ガイド · 読了まで19分

Arduinoがリセットを繰り返す?サーボ・リレー・フリーズの対策

Arduinoがリセットを繰り返す、またはフリーズする場合の対策をステップごとに解説します。サーボやリレーの電流、電圧降下、共通GND、470 µFのコンデンサ、RAMのオーバーフロー、ウォッチドッグまで扱います。

Arduinoがリセットを繰り返す、またはしばらく動いた後にフリーズする場合、ボードが壊れていることはほとんどありません。多くの場合、原因は電源です。サーボが動き出したり、モーターが回り始めたり、リレーが吸着したりした瞬間、急な電流で5 Vのラインが一瞬落ち込み、プロセッサーの低電圧保護(ブラウンアウト)がボードを再起動させます。残りのケースでは、リレーやモーターからの電気的ノイズ、長い配線、RAMの使い切り、忘れられたウォッチドッグ、十分な電流を供給できないパソコンのUSBポートが原因です。

このガイドでは、まずボードが本当にリセットしているのか、それともフリーズしているだけなのかを見分ける方法を説明します。続いて、それぞれの原因の仕組みを実際の電流値と電圧値で解説し、対策をコード付きで紹介します。対策は別電源と共通GND、100〜470 µFのコンデンサ、フライバックダイオード、Stringの代わりに固定サイズのバッファ、そしてウォッチドッグです。さらに、Arduino自身の電源電圧と空きRAMを測る診断用スケッチ、症状から対策までをたどるステップごとのトラブルシューティング表、ESP32とRaspberry Pi Picoでリセット原因を読み取る例も紹介します。

Arduino Uno R3ボード
Arduino Uno R3:USBからの5 Vは500 mAの復帰型ヒューズを通ります。ボードがリセットされると、プログラムはsetup()から再び始まります。RESETボタンとRESETピンも同じ働きをします。

リセットかフリーズか?まず症状を見分ける

リセットしたArduinoは、USBケーブルを抜き差ししたときと同じように、プログラムを最初から実行します。setup()が再び実行され、サーボは開始角度に戻り、LCDには起動時の文字がもう一度表示され、シリアルモニタには起動メッセージが再び現れます。一方、フリーズしたArduinoは何にも反応しません。LEDは最後の状態のまま、シリアル出力は止まり、リセットボタンを押すまでボードはその状態で止まっています。原因も対策も異なるので、まずどちらが起きているのかを確かめましょう。いちばん簡単なのは、setup()にSerial.println("STARTED")の行を追加し、シリアルモニタを開いたままにしておく方法です。

症状リセットフリーズ
setup()が再び実行される?はい、起動メッセージが再び表示されるいいえ
シリアル出力止まった後、起動メッセージから再開する止まったまま戻らない
出力とLED一瞬消えてから初期状態に戻る最後の状態のまま固まる
最も多い原因電圧降下、ノイズ、ウォッチドッグ無限ループ、I2Cバスのハングアップ、RAMの破損
最初に確認すること電源、GND、配線コード、メモリ使用量、センサーの配線

Arduinoはなぜ勝手にリセットするのか?リセットの発生源

Arduino UnoやNanoに載っているATmega328Pプロセッサーを再起動させる発生源は数種類しかなく、それぞれに固有のきっかけがあります。電源電圧が一定のしきい値を下回るとブラウンアウト検出(BOD)が働きます。UnoとNanoでは、ヒューズ設定によりこのしきい値は2.7 Vです。RESETピンをLOWに引くものも、すべてボードを再起動させます。リセットボタン、パソコンのDTR信号、そのピンにつないだ長い配線が拾うノイズなどです。また、リセットに見えて実はリセットではないケースもあります。メモリが壊れるとプログラムがアドレス0にジャンプしてコードが最初から始まりますが、ハードウェアの周辺機能はリセットされないため、ボードがおかしな動きを始めます。

リセットの発生源きっかけ典型的な症状
電源投入(POR)ケーブルの接続、電源が完全に途切れる通常の起動
外部リセット(RESETピン)リセットボタン、シリアルモニタを開く(DTR)、RESETピンのノイズポートを開いたときや配線に触れたときに再起動する
ブラウンアウト(BOD、2.7 V)サーボ、モーター、リレー、LEDテープの突入電流、弱った電池負荷が動いた瞬間にリセットする
ウォッチドッグwdt_reset()が時間内に呼ばれなかった一定間隔でリセットする(例:ちょうど2秒ごと)
ソフトウェアのクラッシュ配列の範囲外アクセス、RAMのオーバーフロー、不正なポインター数分〜数時間後にランダムにリセットまたはフリーズする

サーボ、モーター、リレーの電流:電圧降下がリセットを起こす仕組み

SG90サーボは動作中に100〜250 mAを消費しますが、動き出しやアームに負荷がかかったときは電流が0.5 Aを超えます。パソコンのUSB 2.0ポートが供給できるのは最大500 mAで、UnoのUSBラインにあるヒューズも同じ上限で働きます。サーボ、ボード自身の50 mA、いくつかのモジュールの合計がこの上限を超えると、ポートやヒューズが電流を制限し、5 Vのラインが一瞬落ち込みます。電圧が2.7 Vを下回ればBODがボードをリセットします。もっと厄介なのは、電圧が2.7 Vから約3.8 Vの間にとどまる場合です。ATmega328Pのデータシートによると、このチップが16 MHzで安全に動くにはおよそ3.8 Vが必要です。このグレーゾーンではBODは働きませんが、プロセッサーは速度の限界を超えて動いているため、ボードはリセットする代わりにフリーズしたり、でたらめな値を出したり、I2Cのディスプレイやセンサーが内部で固まったりすることがあります。

負荷通常の電流ピーク電流Unoの5Vピンから給電できる?
SG90マイクロサーボ100〜250 mA0.5 A以上(動き出し、負荷時)負荷の軽いサーボ1個だけなら可
MG996Rサーボ0.5〜0.9 A最大2.5 A不可、5〜6 Vの別電源が必須
TTギアDCモーター70〜250 mA約1 A(動き出し、拘束時)不可、モータードライバと別電源を使う
5 Vリレーモジュール(1チャンネル)70〜90 mAコイルの通電時に短いパルス1〜2チャンネルなら可、4チャンネル以上は別電源から
WS2812B LED(1個あたり)約20 mA(1色を最大輝度)60 mA(白を最大輝度)不可、30個を白の最大輝度で光らせると1.8 A必要

配線も重要です。ブレッドボードを使ったプロジェクトで使う細いジャンパーワイヤーは一般に26〜28 AWGで、28 AWGの銅線の抵抗は1メートルあたり約0.21 Ωです。20 cmの配線2本(往復0.4 m)とブレッドボードを通してサーボに給電すると、配線で約0.085 Ω、ブレッドボードの接点4か所で合計約0.2 Ωが加わります。オームの法則(V = I × R)により、1 Aのサーボのパルスはこの0.29 Ωで約0.3 Vを失います。細くて長いUSBケーブル、ヒューズ、ボード上の保護部品がさらに約0.5 Ωを加えるため、同じパルスの間にArduinoの5 Vラインは4.5 Vを下回ります。パソコンのポートが電流を制限し始めると、落ち込みはずっと深くなります。自分の回路での電圧降下はオームの法則計算ツールで計算できます。

Unoの5Vピンから給電するのは、負荷の軽いSG90サーボ1個までにしてください。MG996Rのような大きなサーボ、DCモーター、LEDテープは、Arduinoの5Vピンや3.3Vピンに絶対につながないでください。ボードがリセットするだけでなく、長い目で見るとUSBヒューズやレギュレーター、さらにはパソコンのUSBポートまで傷むことがあります。

対策1:別電源と共通GND

  1. サーボやモーター用に別電源を用意します。サーボならサーボ1個あたり1 A以上(MG996Rは2〜3 A)を供給できる5〜6 VのACアダプター、単3(AA)ニッケル水素電池4本(4×AA)のパック、または5〜6 Vに調整したLM2596降圧モジュールが使えます。
  2. 電源のプラスはサーボの赤い線か、モータードライバの電源入力にだけつなぎます。このラインをArduinoの5Vピンにはつながないでください。
  3. 電源のマイナス(GND)は、負荷とArduinoのGNDピンの両方につなぎます。この共通GNDがないと信号ピンの電圧に基準がなくなり、サーボが震えたり、まったく動かなかったりします。
  4. GNDは電源のそばの1点にまとめます(一点アース)。モーターの電流がArduinoやそのGNDパターンを通って戻ってはいけません。そうでないと、モーターのパルスのたびにArduinoのGNDレベルがずれてしまいます。
  5. Arduinoは専用の入力から給電します。USB、DCジャック(7〜12 V)、または安定化した5 Vです。こうすればサーボの電源が落ち込んでもプロセッサーは影響を受けません。
  6. 最後に、サーボが動いている間にArduinoの5VピンとGNDピンの間を測るか、下の診断用スケッチを実行します。電圧が4.75 Vを下回らなければ大丈夫です。
LM2596降圧型電圧レギュレーターモジュール
LM2596降圧モジュール:半固定抵抗(トリマー)で、12 VのACアダプターや2SのLiイオン電池パックのような高い電圧を5〜6 Vに下げられます。Arduinoから独立したサーボ用の電源ラインを作るいちばん手軽な方法です。負荷をつなぐ前に、テスターで出力電圧を設定してください。
共通GNDがなぜ欠かせないのか、一点アースがどう働くのかはGNDとは?で、電池やACアダプターの選び方はArduinoを電池で動かす方法のガイドで説明しています。ルールは簡単です。信号線はArduinoから、電源線は電源から取り、GNDはつなぎます。

対策2:コンデンサ(100〜470 µF)とダイオード

電解コンデンサは、負荷のすぐそばに置いた小さなエネルギーの貯蔵庫のように働きます。サーボが急に電流を求めると、配線や電源が追いつくまで、コンデンサ自身がその電流を供給します。どれだけ効果があるかはΔV = I × Δt / Cの式でわかります。470 µFのコンデンサなら、100 µs続く1 Aのパルスの間の電圧降下はわずか0.21 Vです。同じパルスが1 ms続くと、降下は2.1 Vになります。つまりコンデンサは短いパルスやモーターのブラシから出るノイズは吸収しますが、力不足の電源の代わりにはなりません。サーボのラインには470〜1000 µF、Arduinoの5VとGNDの間には100 µFを入れ、それぞれのそばに高周波ノイズ対策として104と書かれた100 nFのセラミックコンデンサを追加します。表記の読み方はコンデンサの表記ツールで、並列につないだコンデンサの合計はコンデンサの合成容量計算ツールで確認できます。種類ごとの違いはコンデンサとは?で解説しています。

場所値理由
サーボの電源ライン、サーボのそば470〜1000 µF / 10〜16 Vの電解コンデンサ + 100 nF動き出しのパルスを和らげる
DCモーターの2つの端子の間100 nFのセラミックコンデンサブラシの火花によるノイズを除去する
Arduinoの5VとGNDの間100 µFの電解コンデンサ + 100 nF短い電圧低下からプロセッサーの電源ラインを守る
WS2812Bテープの入力1000 µF / 10 V突入電流と色が変わるときのパルスを吸収する
センサーやモジュールの電源ピン100 nFのセラミックコンデンサ長い配線が運んでくるノイズを除去する
さまざまな容量の電解コンデンサ
電解コンデンサ:5 Vのサーボラインには470 µF / 16 Vのような値が最適です。本体の帯がマイナス(−)の足を示しています。耐圧はラインの電圧の2倍以上にしてください。

リレーのコイル、モーター、ソレノイドはどれもインダクタです。流れている電流が急に切れると、コイルはその変化に逆らい、電源電圧をはるかに超える数十〜数百ボルトの短い電圧スパイクを発生させます。このスパイクはラインを伝わってArduinoをリセットさせるうえ、駆動用のトランジスタやピンを壊すこともあります。対策は、コイルに並列かつ逆向きにつなぐフライバックダイオードです。1N4007の帯のあるカソード側をプラス側に、もう一方をトランジスタにつながる側につなぎます。市販のリレーモジュールにはこのダイオードが最初から入っています。単体のリレー、ソレノイド、またはトランジスタやMOSFETで駆動するモーターを使う場合は、自分でダイオードを追加する必要があります。PWMで速度制御するモーターには、より高速な1N5819ショットキーダイオードのほうが良い結果になります。詳しくはダイオードとは?とArduinoでMOSFETを使う方法を参照してください。

1N4007整流ダイオード
1N4007ダイオード:本体のグレーの帯がカソード(−)側です。コイルに逆向きにつなぐと、電流が切れたときに生じる電圧スパイクを短絡させて打ち消します。

ノイズ、リレーの接点、長い配線

リレーがオフになるちょうどその瞬間にボードがリセットするなら、問題は電源ではなくノイズです。リレーが水ポンプ、ファン、蛍光灯、電磁接触器をオフにすると、接点の間に小さなアークが生じ、このアークが広帯域の電磁ノイズを放射します。ノイズは近くのセンサー配線、RESETライン、I2Cラインに乗り、プロセッサーをリセットさせたりモジュールを固めたりします。これを防ぐには、フォトカプラ付きのリレーモジュールを使い、JD-VCCのジャンパーを外して、リレーのコイルを別の5 V電源から給電します。こうするとコイルの電流とノイズをArduinoの電源ラインから遠ざけられます。商用電源の誘導性負荷では、接点または負荷に並列にRCスナバ(100 Ω + 100 nF、X2クラス)やバリスタを入れると、アークを大きく抑えられます。配線の詳細はリレーモジュールとは?で、絶縁の考え方はフォトカプラとは?で紹介しています。

  • I2Cは短く:I2Cは回路基板上の短い距離を想定して作られています。SDAとSCLの配線は50 cm未満にしてください。それより長い場合は、プルアップ抵抗を4.7 kΩから2.2 kΩに下げ、速度は100 kHzのままにします。
  • より線を使う:信号線はそれぞれGND線と一緒により合わせます。LANケーブルのツイストペアがこれに最適です。
  • 電源線と信号線を分ける:センサーの配線をモーター、リレー、商用電源の配線と平行に這わせないでください。交差させる必要がある場合は直角に交差させます。
  • RESETピンには何もつながない:RESETピンにつないだ長い配線はアンテナのように働き、触れたときや近くのモーターが動いたときにボードをリセットさせます。外付けのリセットボタンが必要なら、配線を短くしてください。
  • フェライトとケース:モーターの配線に付けたフェライトコアと金属ケースは、高周波ノイズを減らします。
  • タイムアウトを設定する:ノイズでI2Cバスが固まると、Wireライブラリは初期設定のままでは永遠に待ち続け、ボードがフリーズします。下のコードはタイムアウトでこのフリーズを防ぎます。
C++ (Arduino IDE)
1#include <Wire.h>
2
3const uint8_t ADDR = 0x27;   // örnek: I2C LCD adresi / example: I2C LCD address
4
5void setup() {
6  Serial.begin(9600);
7  Wire.begin();
8  Wire.setWireTimeout(25000, true);   // 25 ms; takılırsa I2C birimini sıfırla / reset the I2C unit on timeout
9}
10
11void loop() {
12  Wire.beginTransmission(ADDR);
13  byte err = Wire.endTransmission();  // 0 = cihaz cevap verdi / 0 = device answered
14
15  if (Wire.getWireTimeoutFlag()) {    // hat kilitlendi ama program donmadı / bus hung, sketch did not freeze
16    Serial.println(F("I2C zaman asimi / I2C timeout"));
17    Wire.clearWireTimeoutFlag();
18  } else if (err != 0) {
19    Serial.print(F("I2C hatasi / I2C error: "));
20    Serial.println(err);              // 2 = cihaz yok, 5 = zaman aşımı / 2 = no device, 5 = timeout
21  }
22  delay(1000);
23}

RAMのオーバーフローとStringの使用

Arduino Unoには2 KBのSRAMしかなく、この小さな領域をグローバル変数、動的メモリ(ヒープ)、関数呼び出しで使うスタックが共有しています。コンパイルの最後にLow memory available, stability problems may occur.(メモリ不足で動作が不安定になる可能性がある)という警告が出たら、ボードはしばらくするとクラッシュする可能性が高いです。いちばん厄介な原因はStringクラスです。連結するたびにヒープに新しい領域が確保され、古い領域が解放されるため、時間とともにメモリは穴だらけになります。ある日スタックがヒープとぶつかり、プログラムがランダムな瞬間にリセットしたりフリーズしたりします。たいてい数分〜数時間後に現れるので、電源の問題とは簡単に見分けられます。固定の文字列はF()マクロでフラッシュに置き、Stringの代わりに固定サイズのchar配列とsnprintf()を使い、関数の中で大きなローカル配列を宣言しないようにしましょう。下のコードは空きRAMを1秒ごとに表示します。値が下がり続けるなら、メモリリークがあります。詳しくはArduinoのメモリ不足で解説しています。

C++ (Arduino IDE)
1extern int __heap_start, *__brkval;
2
3// Heap ile stack arasındaki boş RAM / free RAM between heap and stack
4int freeRam() {
5  int v;
6  return (int)&v - (__brkval == 0 ? (int)&__heap_start : (int)__brkval);
7}
8
9char line[32];             // sabit tampon, bir kez ayrılır / fixed buffer, allocated once
10unsigned long counter = 0;
11
12void setup() {
13  Serial.begin(9600);
14}
15
16void loop() {
17  counter++;
18  // Kaçın / avoid: String s = "Sayac: " + String(counter) + " sn";
19  snprintf(line, sizeof(line), "Sayac: %lu sn", counter);  // heap kullanmaz / no heap use
20  Serial.println(line);
21
22  Serial.print(F("Bos RAM / free RAM: "));  // F(): metin flaşta kalır / text stays in flash
23  Serial.println(freeRam());                // sürekli azalıyorsa sızıntı var / steady drop = leak
24  delay(1000);
25}

ウォッチドッグ:フリーズに備える安全ベルト

ウォッチドッグタイマーは、専用の128 kHz発振器で独立して動くプロセッサー内部のカウンターです。プログラムはwdt_reset()で定期的にこれをリセットする必要があり、設定した時間(15 ms〜8秒)以内にリセットされないと、ウォッチドッグがボードをリセットします。こうすれば、固まったI2Cバス、無限ループにはまった関数、ノイズで壊れたセンサーの読み取りがあっても、ボードが永遠にフリーズすることはなく、数秒で復帰します。ただし、ウォッチドッグは安全ベルトであって修理ではないことを覚えておいてください。電源やメモリの問題を直すわけではなく、現場で動くプロジェクトが自力で復帰できるようにするだけです。タイムアウトは、コードの中でいちばん長い処理よりはっきり長く設定します。delay(3000)を含むコードで2秒のウォッチドッグを使うと、ボードは何度もリセットを繰り返します。

C++ (Arduino IDE)
1#include <avr/wdt.h>
2
3const int LED_PIN = 13;
4
5void setup() {
6  MCUSR = 0;          // reset bayraklarını temizle / clear reset flags
7  wdt_disable();      // açılışta watchdog kapalı / watchdog off during startup
8  Serial.begin(9600);
9  pinMode(LED_PIN, OUTPUT);
10  Serial.println(F("Basladi / started"));
11  delay(500);
12  wdt_enable(WDTO_2S);  // 2 sn içinde beslenmezse reset / reset if not fed within 2 s
13}
14
15void loop() {
16  digitalWrite(LED_PIN, !digitalRead(LED_PIN));
17  delay(250);
18
19  // Seri Monitör'den 'x' gönder: takılmayı taklit eder / send 'x' to simulate a hang
20  if (Serial.available() && Serial.read() == 'x') {
21    Serial.println(F("Sonsuz donguye girildi / stuck in a loop"));
22    while (true) {}     // wdt_reset() yok: 2 sn sonra reset / no wdt_reset(): reset after 2 s
23  }
24
25  wdt_reset();          // her turda besle / feed it every loop
26}
古いブートローダーを積んだNanoの互換品では、ウォッチドッグは危険です。このブートローダーはウォッチドッグによるリセットの後にタイマーを止めないため、タイマーは最短の周期(約16 ms)で動き続け、ブートローダーはその時間内に処理を終えられません。その結果、コードが一度も始まらない無限リセットループに陥ります。ボードはUSBケーブルを抜き差ししたときだけ復帰し、ウォッチドッグが最初に作動した時点でまたループが始まります。「ツール」>「プロセッサ」でATmega328P (Old Bootloader)を選ぶ必要があるボードなら、ウォッチドッグを使う前にOptibootブートローダーを書き込んでください。方法はArduinoに書き込めないときで説明しています。UnoのOptibootブートローダーにはこの問題はありません。

診断用スケッチ:Arduino自身に電圧とRAMを測らせる

ミリ秒単位の電圧低下を見るには、テスターでは遅すぎます。代わりにArduino自身を簡単な測定器として使えます。下のコードは、ADCでチップ内部の1.1 V基準電圧を測り、そこから電源電圧を1秒間に約9000回計算して、1秒ごとの最低値を表示します。さらに起動のたびにEEPROMのカウンターを1つ増やすので、後からシリアルモニタを開いても、ボードが何回再起動したかがわかります。テスト用の負荷として、D9のサーボを2秒ごとに0度と180度の間で動かします。自分のプロジェクトがあるなら、測定部分をそのコードに追加しても構いません。シリアルモニタは115200ボーで開いてください。

C++ (Arduino IDE)
1// Reset teşhis kodu (Uno, Nano) / reset diagnostic sketch (Uno, Nano)
2#include <EEPROM.h>
3#include <Servo.h>
4
5Servo servo;
6extern int __heap_start, *__brkval;
7
8int freeRam() {
9  int v;
10  return (int)&v - (__brkval == 0 ? (int)&__heap_start : (int)__brkval);
11}
12
13// Besleme gerilimini dahili 1,1 V referansla ölç / measure supply voltage against the internal 1.1 V reference
14long readVcc() {
15  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
16  ADCSRA |= _BV(ADSC);                  // ölçümü başlat / start a conversion
17  while (bit_is_set(ADCSRA, ADSC)) {}   // bitmesini bekle / wait until it is done
18  return 1125300L / ADC;                // 1100 mV × 1023 / okuma → mV / reading → mV
19}
20
21long minVcc = 9999;
22unsigned long lastPrint = 0, lastMove = 0;
23bool sideA = false;
24
25void setup() {
26  Serial.begin(115200);
27  uint16_t boots;
28  EEPROM.get(0, boots);                 // açılış sayacı / boot counter
29  if (boots == 0xFFFF) boots = 0;       // boş EEPROM / blank EEPROM
30  EEPROM.put(0, ++boots);
31  Serial.print(F("BASLADI / STARTED  acilis no / boot #"));
32  Serial.println(boots);
33
34  servo.attach(9);                      // test yükü: servo D9'da / test load: servo on D9
35  readVcc();                            // ilk ölçümü at / discard the first reading
36  delay(5);
37}
38
39void loop() {
40  long v = readVcc();                   // saniyede ~9000 ölçüm / ~9000 samples per second
41  if (v < minVcc) minVcc = v;
42
43  if (millis() - lastMove >= 2000) {    // 2 sn'de bir servoyu oynat / move the servo every 2 s
44    lastMove = millis();
45    sideA = !sideA;
46    servo.write(sideA ? 180 : 0);
47  }
48
49  if (millis() - lastPrint >= 1000) {
50    lastPrint = millis();
51    Serial.print(F("sn/s: "));
52    Serial.print(millis() / 1000);
53    Serial.print(F("  min Vcc (mV): "));
54    Serial.print(minVcc);
55    Serial.print(F("  bos RAM / free RAM: "));
56    Serial.println(freeRam());
57    minVcc = 9999;                      // her saniye sıfırla / reset every second
58  }
59}

結果は次のように読みます。USBから給電した正常なUnoでは、最低値は4.6〜5.1 Vの間に収まります。サーボが動くたびに値が4.5 Vを下回り、リセットがそのタイミングと一致するなら、問題は電源です。電圧が安定しているのに起動回数が増え続けるなら、ノイズかソフトウェアの原因を探してください。同時に空きRAMが減り続けているなら、メモリリークがあります。内部の1.1 V基準電圧はチップによって約10%ずれることがあるので、一度テスターの値と比べてください。テスターが4.95 Vを示し、コードが5.10 Vを表示するなら、定数1125300を1125300 × 4.95 / 5.10 ≈ 1092200に変更します。作業が終わったら、EEPROMのカウンターはコードから外してください。EEPROMのセルの書き込み寿命は約10万回です。

ステップごとのトラブルシューティング表:症状、原因、対策

表は上から下へ読んでください。上の行ほどよくある原因です。一度に変えるのは1か所だけにして、そのたびに診断用スケッチを実行し直します。3か所を同時に変えると、どれで問題が解決したのかわからず、次のプロジェクトで同じ不具合がまた起きてしまいます。

症状考えられる原因確認方法対策
サーボやモーターが動くとリセットする突入電流で5 Vのラインが落ち込む診断用スケッチの最低電圧が4.5 Vを下回る5〜6 Vの別電源、共通GND、470〜1000 µF
リレーが吸着するときではなく、離れるときにリセットする接点のアークとコイルのスパイク(ノイズ)リレーの負荷を外すと問題が消えるJD-VCCで別電源、スナバまたはバリスタ、フライバックダイオード
シリアルモニタを開くと最初から始まるDTRによる自動リセット(正常な動作)ポートを開くたびにSTARTEDが表示される故障ではない。必要ならRESETとGNDの間に10 µF
パソコンでだけリセットし、ACアダプターでは問題ないUSBポートやハブが十分な電流を供給できない別のポートと短く太いケーブルで直る背面のポートかセルフパワーのハブを使い、負荷は別に給電する
9V電池で動かすと、動作時にリセットする内部抵抗が大きく、電圧が落ち込む動作中にVinが7 Vを下回る単3電池6本(6×AA)か2Sの18650パック
数分〜数時間後にリセットまたはフリーズするRAMのオーバーフロー、Stringによる断片化空きRAMが減り続けるF()、char配列、snprintf、小さなローカル配列
正確に一定の間隔でリセットするウォッチドッグが時間内にリセットされていないリセットの間隔がウォッチドッグのタイムアウトと同じwdt_reset()を追加するかタイムアウトを延ばし、長いdelay()を避ける
Nanoでウォッチドッグによるリセットの後、コードが始まらない古いブートローダーのウォッチドッグの不具合Old Bootloaderの設定でしか書き込めないOptibootブートローダーを書き込む
長いセンサーケーブルをつなぐとランダムにフリーズするノイズでI2Cバスが固まるWireのタイムアウトフラグが立っている短いより線、2.2 kΩのプルアップ、setWireTimeout、ウォッチドッグ
手を近づけたり触れたりするとリセットするRESETピンの長い配線、静電気配線を外すと直るRESETラインを短くし、回路をケースに入れる
レギュレーターがとても熱くなった後、リセットするジャックに12 Vで大電流、レギュレーターの過熱保護レギュレーターが触れないほど熱い7〜9 VのACアダプターか5 Vの降圧モジュール
ボードの電源が入らない、またはポートが消えるショート、USBヒューズの作動モジュールを外すとボードの電源が入るショートの箇所を見つけ、ボードが壊れていないか確認する

USBポートの電流不足とシリアルモニタによるリセット

パソコンのUSB 2.0ポートは500 mA、USB 3.0ポートは900 mAを供給します。ただしUnoのUSBヒューズが500 mAで制限するので、USB 3.0ポートを使っても電流は増えません。セルフパワーでないUSBハブは1つのポートの電流をすべての機器で分け合い、前面のポートは内部の細いケーブルで電圧が下がり、バッテリーで動いているノートパソコンは省電力モードでポートへの給電が弱くなることがあります。ボードは短くて品質の良いケーブルで、背面のポートに直接つないでください。また、故障ではないリセットもあります。UnoとNanoは、シリアルモニタやPythonのプログラムがポートを開くと、DTR信号で自分自身をリセットします。これこそが書き込みを可能にしている機能です。Pythonから通信する場合は、ポートを開いた後に2秒待ってください(ArduinoとPythonのシリアル通信)。リセットを完全に無効にしたい場合は、RESETとGNDの間に10 µFのコンデンサを入れ、書き込むときは外します。シリアルポートの詳細はArduinoのシリアルモニタで解説しています。

ESP32がリセットを繰り返す:リセット原因を読み取る

ESP32は3.3 Vで動作し、Wi-Fiの送信中は約240 mA、短いピークではそれ以上を消費するため、同じ問題にさらに敏感です。Espressifは少なくとも500 mAを供給できる電源を推奨しています。うれしいことに、ESP32はなぜ再起動したのかを自分で教えてくれます。シリアルモニタにBrownout detector was triggered(ブラウンアウト検出が作動)というメッセージが出たら電圧降下を意味し、起動時のrst:0xf (RTCWDT_BROWN_OUT_RESET)やGuru Meditation Errorの行がリセット原因を示しています。下のコードは同じ情報をesp_reset_reason()で読み取り、読みやすい文字列に変換します。ESP32ではENボタンがチップの電源を完全に切ってから入れ直すため、このボタンによるリセットも電源投入として表示されます。すべてのメッセージとその対策はESP32のブラウンアウトとGuru Meditationを参照してください。

C++ (Arduino IDE)
1#include <esp_system.h>
2
3// Reset sebebini yazıya çevir / turn the reset reason into text
4const char* reasonText(esp_reset_reason_t r) {
5  switch (r) {
6    case ESP_RST_POWERON:   return "Guc acilisi ya da EN tusu / power-on or EN button";
7    case ESP_RST_SW:        return "Yazilimla (ESP.restart) / software restart";
8    case ESP_RST_PANIC:     return "Cokme (Guru Meditation) / panic";
9    case ESP_RST_INT_WDT:   return "Kesme watchdog'u / interrupt watchdog";
10    case ESP_RST_TASK_WDT:  return "Gorev watchdog'u / task watchdog";
11    case ESP_RST_WDT:       return "Diger watchdog / other watchdog";
12    case ESP_RST_DEEPSLEEP: return "Derin uykudan uyanma / deep sleep wake-up";
13    case ESP_RST_BROWNOUT:  return "Gerilim dusmesi (brownout) / brownout";
14    default:                return "Bilinmeyen / unknown";
15  }
16}
17
18void setup() {
19  Serial.begin(115200);
20  delay(500);
21  esp_reset_reason_t r = esp_reset_reason();
22  Serial.print("Reset nedeni / reset reason: ");
23  Serial.println(reasonText(r));
24}
25
26void loop() {
27  Serial.print("Calisma suresi / uptime (s): ");
28  Serial.println(millis() / 1000);
29  delay(5000);
30}

Raspberry Pi Pico:リセット原因とウォッチドッグ(MicroPython)

Raspberry Pi Picoは昇降圧(バックブースト)レギュレーターのおかげで、VSYSピンの電圧が1.8〜5.5 Vの範囲ならどの電圧でも動くため、Arduinoより電圧の落ち込みに強いです。それでも、USBの5 Vが来ているVBUSピンにサーボをつなぐと、電流はパソコンのポートから直接引かれ、ポートの電圧が落ち込めばPicoも止まります。また、3V3ピンから取り出す電流は合計300 mAを超えないようにしてください。PicoのリセットピンRUNは、チップ内蔵の約50 kΩの抵抗でプルアップされています。ここに長い配線をつなぐと、ArduinoのRESETピンと同じようにノイズの入り口になります。MicroPythonでは、Picoのmachine.reset_cause()は2つの値しか返しません。電源投入またはRUNピンならPWRON_RESET、ウォッチドッグまたはmachine.reset()ならWDT_RESETです。ウォッチドッグは最大8388 msまで設定でき、一度始めると止められません。Thonnyでプログラムを止めても、ボードをリセットし続けます。そのため、コードの最初に3秒の待ち時間を入れておきましょう。

MicroPython
1import machine, time
2from machine import Pin
3
4time.sleep(3)   # Thonny durdurabilsin diye bekle / wait so Thonny can stop the script
5
6neden = machine.reset_cause()
7if neden == machine.WDT_RESET:
8    print("Watchdog ya da machine.reset() / watchdog or machine.reset()")
9else:
10    print("Guc acilisi ya da RUN pini / power-on or RUN pin")
11
12led = Pin("LED", Pin.OUT)          # Pico ve Pico W dahili LED / onboard LED
13wdt = machine.WDT(timeout=5000)    # 5 sn; RP2040'ta en fazla 8388 ms / max 8388 ms on RP2040
14
15sayac = 0
16while True:
17    led.toggle()
18    sayac += 1
19    print("Calisiyor / running", sayac)
20    wdt.feed()                     # her turda besle / feed it every loop
21    time.sleep(1)

プロジェクトに合った電源の構成

  • ロボットカー:モーターは2Sの18650パックからL298NかL9110Sを通して給電し、モーターの端子間に100 nFのコンデンサを入れます。作り方はArduinoロボットカーの作り方で紹介しています。
  • サーボのロボットアーム:サーボが4個以上なら、3〜5 A以上を供給できる5〜6 Vの電源を使い、サーボのラインに1000 µFを追加します。部品選びはMG996RサーボモーターとArduinoのロボットアームを参照してください。
  • ポンプとリレーを使った水やりシステム:ポンプは別電源から給電し、JD-VCCでリレーを絶縁して、長いセンサー配線はポンプのケーブルから離します(土壌水分センサー)。
  • LEDテープとNeoPixel:LED30個ごとに約2 Aを見込み、テープの入力に1000 µF、データ線に300〜500 Ωの抵抗を入れます(WS2812B NeoPixel)。
  • DCモーターの駆動:トランジスタやMOSFETで駆動するモーターには、1つ残らずフライバックダイオードを付けてください。市販のドライバボードについてはL298NでDCモーターを制御する方法を参照してください。
  • 屋外のセンサーステーション:ウォッチドッグを有効にし、I2Cのタイムアウトを設定して、LM2596降圧コンバーターのような効率の良い電源から給電します。

勝手にリセットするArduinoは厄介ですが、原因はほぼ必ず測定でき、直せるものです。まず症状を見分け、次に診断用スケッチで電圧とRAMを測り、それから電源を分け、GNDをつなぎ、コンデンサとダイオードを追加して、必要ならウォッチドッグでプロジェクトを守りましょう。この構成を整えれば、サーボ、モーター、リレーを使ったプロジェクトが何日も問題なく動きます。BlueGraysアプリには、サーボモーターの使い方、L298Nで作るBluetooth操作の車、障害物回避ロボット、220V電球の点灯・消灯(日本の商用電源は100V(50/60Hz)です)、スマート水やりステーションがあり、正しい電源の接続を示す回路図とステップごとのコードが付いています。

よくある質問

Arduinoがリセットを繰り返すのはなぜですか?

最も多い原因は、サーボ、モーター、リレーの急な電流で5 Vのラインが一瞬下がり、プロセッサーの2.7 Vの低電圧保護がボードを再起動させることです。リレーの接点からのノイズ、RAMのオーバーフロー、リセットされないウォッチドッグ、十分な電流を供給できないUSBポートもリセットの原因になります。ほとんどの場合、サーボやモーターを別電源から給電し、GNDをつなげば解決します。

サーボが動くとArduinoがリセットするのはなぜですか?

小さなSG90サーボでも動き出しには0.5 Aを超える電流が流れ、ボードの他の負荷と合わせてUSBポートとUnoの500 mAヒューズの上限を超えてしまいます。電圧が落ち込むと、ボードはリセットします。サーボは5〜6 Vの別電源から給電し、その電源のGNDをArduinoのGNDにつなぎ、サーボのラインに470〜1000 µFのコンデンサを追加してください。

リレーが切り替わるとArduinoがリセットするのはなぜですか?

リレーのコイルは通電するときに急な電流を流し、離れるときにはコイルと接点がノイズを出します。ポンプ、ファン、蛍光灯のような誘導性負荷をオフにしたときに生じるアークは、Arduinoをリセットさせることがあります。フォトカプラ付きのモジュールならJD-VCCのジャンパーを外してリレーを別電源から給電し、負荷に並列にスナバかバリスタを入れ、センサーの配線を負荷の配線から離してください。

しばらくするとArduinoがフリーズするのはなぜですか?

数分〜数時間後に現れるフリーズの原因として最も多いのは、RAMがいっぱいになることと、Stringの連結でメモリが断片化することです。ノイズによるI2Cバスのハングアップや、無限ループで待ち続けるコードもボードをフリーズさせます。空きRAMを測り、Stringの代わりにchar配列を使い、Wire.setWireTimeout()でタイムアウトを設定し、安全策としてウォッチドッグを有効にしてください。

Arduinoの回路にはどのコンデンサを入れればいいですか?

サーボやモーターの電源ラインに470〜1000 µF、Arduinoの5VとGNDの間に100 µFの電解コンデンサを入れると効果的です。それぞれのそばに100 nF(104)のセラミックコンデンサを追加すると、高周波ノイズも除去できます。電解コンデンサの耐圧はラインの電圧の2倍以上にし、帯のあるマイナスの足をGNDにつなぎます。

シリアルモニタを開くとArduinoがリセットするのはなぜですか?

UnoとNanoはシリアルポートが開かれると、DTR信号で自分自身をリセットします。これは故障ではなく、書き込みを可能にしている自動リセット機能です。Pythonなどのプログラムから通信する場合は、ポートを開いた後に2秒待ってください。リセットを無効にするには、RESETとGNDの間に10 µFのコンデンサを入れ、書き込む前に外します。

全プロジェクトを見る
サーボモーターの使い方
#35初級
プレミアム

サーボモーターの使い方

小型サーボモーターのアームが、一方の端、中央、もう一方の端の3つの位置へ順番に回り、それぞれの位置で2秒間止まります。Arduinoが保持する角度をモーターに指示すると、モーターはその位置まで動いて止まります。ロボットアームや自動開閉するふたが動く仕組みを学べます。

4.7(48)
67
ボタンを押している間だけ220V電球を点灯
#19上級
プレミアム

ボタンを押している間だけ220V電球を点灯

ボタンを押している間は実際の220ボルトの電球が点灯し、離すとすぐに消灯します。Arduinoの小さな信号では電球を直接点灯できないため、電気で動くスイッチであるリレーを間に入れ、電球の回路を開閉します。ボードで家庭の照明を操作するための第一歩です。家庭用電源を扱う際は十分に注意し、不安がある場合は詳しい人に手伝ってもらってください。

4.4(30)
38
L298Nで作るBluetooth操作の車
#80上級
プレミアム

L298Nで作るBluetooth操作の車

スマートフォンをリモコンにして、自作のロボットカーを操作できます。ArduinoはBluetoothモジュールを通じてスマートフォンのリモコンアプリから指令を受け取り、モータードライバーで車輪を回します。前進、後退、左右や斜めへの移動ができ、速度もスマートフォンから調整できます。停止ボタンを押すと、短いブレーキ動作でモーターがすぐに止まります。

5.0(38)
65
障害物回避ロボット
#81上級
プレミアム

障害物回避ロボット

自分で進む道を見つける、賢い車を作ってみませんか?障害物回避ロボットは、前面の超音波センサーで周囲を常に調べます。障害物を見つけると停止し、サーボモーターで左右を確認して、最も障害物の少ない方向を選んで進みます。自動運転の基礎を学ぶのにぴったりの入門プロジェクトです。

5.0(38)
49
スマート水やりステーション
#83上級
プレミアム

スマート水やりステーション

旅行中も植物が枯れないよう、自動で水やりをするシステムです。土壌水分センサーで土の湿り具合を測定し、乾燥するとArduinoが給水ポンプを数秒間動かして、青いライトを点灯させます。水分が適切なときは緑のライトが点灯します。土がひどく乾燥すると、赤いライトが点滅し、ブザーが鳴って知らせます。

5.0(42)
57
サーボの円形ダンス — 32個のサーボで動く模様
#102上級
プレミアム

サーボの円形ダンス — 32個のサーボで動く模様

Arduino Megaで、円形に並べた32個のサーボモーターを動かすビジュアルアニメーションのプロジェクトです。サーボは1つの配列で管理し、Megaの22–53番ピンに接続します。ランダムな角度でばらばらに振動する動き、全体が同時に動く同期スイープ、円周を滑らかに巡る波、左右に揺れる方位磁針の針という4種類の動きを見せます。回転する波の効果は、各サーボと動作中のサーボとの関係によって生まれます。

4.5(40)
62
ESP32サーボモーター(ポテンショメーター制御)
#118中級
プレミアム

ESP32サーボモーター(ポテンショメーター制御)

このプロジェクトでは、ポテンショメーターでSG90サーボモーターを制御します。ポテンショメーターから読み取ったアナログ値を0~180度の範囲に変換し、サーボの角度を調整します。ESP32Servoライブラリは、ESP32の出力に対応した適切なGPIOでサーボ信号を生成します。このプロジェクトではGPIO 18を使います。ロボットアームやカメラ用ジンバル、自動ドアの機構に使われる基本的な制御方法です。

4.3(29)
42