Arduino 入門
番外編 13
【スリープモードによる消費電力の低減】
こんにちは管理人のomoroyaです。
本記事は、「Arduino 入門 番外編 13 【スリープモードによる消費電力の低減】」です。
arduino 入門 番外編はarduinoの基本的なことを書いていく方向性にしたいと考えています。
Arduinoで遊んでいるとわかりますが、Arduinoには終了という概念がありません。
入門編にあるサンプルコードを見てみてください。
ひたすらループ内を実行しているだけです。
では、終了させるためにはどうしたら良いか?
電源を落とすしかないわけです・・・。
しかし、このままではバッテリー駆動している場合など困ります。
必要な場合のみ、電力を消費するようにしたいと思ったときに電源を落とす、入れるという動作を外部にさせる必要が生じます。
それを外部にさせようと思うと結構手間ですよね・・・。
そんな手間を省くために、スリープモード機能を利用するという手があります。
完全にOFFさせてしまうと、復帰が難しい。
そこで、低消費電力状態を作り出し、復帰は自分自身でできる状態にするといった具合です。
ということで本記事では、Arduinoのスリープモードによる消費電力低減について解説していきます。
Arduinoに供給される、電圧、電流、電力の確認にはUSBテスターを使用しています。
Arduinoをこれから始めたい、という方は下記の入門編からお読みください。
Lessonの入門編で使用しているArduinoは互換品です。
互換品とは言え、Arduinoはオープンソースであり複製して販売するのもライセンス的に問題なし。
そのため互換品の品質も悪くなく、それでいて値段は安いです。
正規品本体の値段で豊富な部品が多数ついています。
Arduino スリープ機能ライブラリ
Arduinoのスリープ機能を使用するためにはライブラリを使用する必要があります。
スリープに関する主なライブラリは下記2つのようです。
- avr/sleep.h
- Narcoleptic.h
項目1は標準で実装されていますので#includeすれば使えます。
項目2は標準で実装されていませんので、ライブラリをインストールする必要があります。
本記事はで、項目1の標準実装されている「avr/sleep.h」について解説します。
Narcoleptic.hについては、別記事にて解説予定です。
上記以外にも消費電力を低減できるライブラリ、方法があります。
こちらに関しては、私も勉強中のため学習しながら記事をアップしていきます。
Arduino スリープ機能について
スリープ機能はおおまかに以下3つが考えられます。
・トリガーを利用したスリープ
例えば、「トリガー発生までスリープ⇒処理」を繰り返すことを想定。
・一定時間を指定したスリープ
例えば、「指定した時間スリープ⇒処理」を繰り返すことを想定。
・トリガースリープと一定時間スリープの併用
使用するライブラリによってできることが異なります。
必要に応じたライブラリを使用する必要があります。
sleep.h ライブラリの機能について
コード内での使い方は以下となります。
modeの指定には以下の機能があります。
モード名 mode |
内容 |
---|---|
アイドル SLEEP_MODE_IDLE |
システムクロックは停止。 周辺回路へのクロックは供給。 復帰方法: 外部割込、ウオッチドックタイマー、ADC変化、RESETピン |
ADCノイズ低減 SLEEP_MODE_ADC |
ADC変換ノイズ低減。 |
パワーダウン SLEEP_MODE_PWR_DOWN |
外部発振器停止。 復帰方法: 外部割込、ウオッチドックタイマー、RESETピン ※もっとも消費電力が少ないモード |
パワーセーブ SLEEP_MODE_PWR_SAVE |
タイマー用の外部発振器は動作。 復帰方法: 外部割込、ウオッチドックタイマー、RESETピン |
スタンバイ SLEEP_MODE_STANDBY |
外部発振器が動作。 上記以外はパワーダウンと同じ。 |
パワーダウン +外部割込み復帰 SLEEP_MODE_EXT_STANDBY |
外部発振器が動作。 上記以外はパワーセーブと同じ。 本記事では割愛します。 |
より、詳細なスリープ機能と復帰条件がAVRマイコンのデータシートに記載されています。
データシートは下記サイトにて確認することができます。
スリープモード コマンド
#include <avr/sleep.h>で使用できるコマンドを解説します。
コマンド名 | 説明 |
---|---|
set_sleep_mode(mode) | スリープモードの選択。 SLEEP_MODE_IDLE※下記注意点参照 SLEEP_MODE_ADC SLEEP_MODE_PWR_DOWN SLEEP_MODE_PWR_SAVE SLEEP_MODE_STANDBY SLEEP_MODE_EXT_STANDBY |
sleep_enable() | スリープモードの許可 |
sleep_disable() | スリープモード禁止 |
sleep_cpu() | スリープモード実行 |
cli() | 割込み禁止 |
sei() | 割込み許可 |
sleep_mode() | sleep_enable() sleep_disable() sleep_cpu() を実行する。 |
SLEEP_MODE_IDLEだけ機能しない!
これに気づくのに、かなりの時間を要しました。
8bitタイマーを止める方法を以下に示します。
sleepモードに入る前にPRRを設定することでタイマーを止めることができます。
PRR = PRR | 0b00100000; //8bit タイマーの停止 sleep_mode(); //スリープモードの設定
PRRは「Power Reduction Register」と呼ばれているものです。
Arduinoの公式ページにも下記の記載があります。
When using SLEEP_MODE_IDLE, care must be taken to ensure that the 8-bit timer is disabled if you’re using the arduino layer. The timer can be disabled before entering sleep using the PRR = PRR | 0b00100000; statement and subsequently re-enabled once out of sleep using PRR = PRR & 0b00000000; .
https://playground.arduino.cc/Learning/ArduinoSleepCode/
より引用
Arduino スリープからの復帰について
Arduinoのスリープからの復帰について解説します。
復帰するためにはsleepライブラリの説明にある復帰方法を実行する必要があります。
例えば、外部割込、ウオッチドックタイマー、ADC変化、RESETピンなどです。
何も考えないで済むのは「RESETピン」です。
Arduinoの「RESETピン」を押せば復帰します。
それだと、あまりにも安直ですし、Arduinoで遊んでいる感じがないですよね・・・。
ということで、外部割込を使ってコードで対応します。
そのコマンドが下記の2つ。
外部割込みが発生した時に実行する関数を指定。
指定した割り込みを停止。
引数パラメータは以下となります。
function:割込み発生時に呼び出す関数
mode:割込みを発生させるトリガ
ピンの割り当てはボードによって異なります。
UNOに割り当てられている有効な割込み番号は以下となります。
デジタルピン3(int.1)
modeに割り当てられるトリガは以下となります。
CHANGE:ピンの状態が変化した時に発生
RISING:ピンの状態がLOWからHIGHに変わったとに発生
FALLING:ピンの状態がHIGHからLOWに変わったときに発生
では、これらをふまえてさっそく回路とコード作成です。
スリープモード サンプル回路図
回路はGNDとデジタルの2番ピン、プッシュボタンスイッチのみの単純なものです。
プッシュボタンスイッチを押すと、2番ピンがGNDに接続されるようにします。
回路図がこちら。
ブレッドボード図がこちら。
スリープモード サンプルコード
次に、スリープモードを実行するためのサンプルコードを作成していきます。
今回はスリープモードのテストのため、動作中は特に電子部品をつないで動作させていません。
割込みのためのデジタル入力の2番ピンはプルアップ指定をしておきます。
Switchを押すことで「GND」、スイッチが押されていないときは「VDD」となります。
//Arduino 番外編13 スリープモード編 //https://omoroya.com #include <avr/sleep.h> int wakePin = 2; //割り込み用のピン番号 void setup() { pinMode(wakePin, INPUT_PULLUP); //割込み用のピンを入力のプルアップあり指定 Serial.begin(9600); //シリアルポートの指定 Serial.println("-- Sleep Test Start --"); } void loop() { if (Serial.available()) { //シリアルモニターからの入力の受け取り int val = Serial.read(); //val変数に格納 if (val == 'i') { //"i"が入力された場合 SLEEP_MODE_IDLE Serial.println(" MODE <- IDLE"); //モニタに表示 standBy(); //スタンバイ突入調整関数読み出し set_sleep_mode(SLEEP_MODE_IDLE); //スリープモードの設定 interrupt(); //割込み関数の呼び出し } if (val == 'a') { //"a"が入力された場合 SLEEP_MODE_ADC Serial.println(" MODE <- ADC"); //モニタに表示 standBy(); //スタンバイ突入調整関数読み出し set_sleep_mode(SLEEP_MODE_ADC); //スリープモードの設定 interrupt(); //割込み関数の呼び出し } if (val == 'd') { //"d"が入力された場合 SLEEP_MODE_PWR_DOWN Serial.println(" MODE <- DOWN"); //モニタに表示 standBy(); //スタンバイ突入調整関数読み出し set_sleep_mode(SLEEP_MODE_PWR_DOWN); //スリープモードの設定 interrupt(); //割込み関数の呼び出し } if (val == 's') { //"s"が入力された場合 SLEEP_MODE_PWR_SAVE Serial.println(" MODE <- SAVE"); //モニタに表示 standBy(); //スタンバイ突入調整関数読み出し set_sleep_mode(SLEEP_MODE_PWR_SAVE); //スリープモードの設定 interrupt(); //割込み関数の呼び出し } if (val == 'b') { //"b"が入力された場合 SLEEP_MODE_STANDBY Serial.println(" MODE <- STANDBY"); //モニタに表示 standBy(); //スタンバイ突入調整関数読み出し set_sleep_mode(SLEEP_MODE_STANDBY); //スリープモードの設定 interrupt(); //割込み関数の呼び出し } } Serial.println("In operation."); //モニタに動作中を表示 delay(500); //何か動作させたい場合はここにコードを追加 //例えばLEDを点灯させるなど。 //setup関数での設定もお忘れなく! } //スタンバイ突入調整関数 void standBy(){ Serial.println("Arduino is standby."); //モニタ表示 delay(100); //時間調整 } //割込み関数 void interrupt(){ attachInterrupt(0,wakeUp, LOW); //割り込み処理の設定、2番ピンがLowで復帰 PRR = PRR | 0b00100000; //8bitタイマーdisable sleep_mode(); //スリープモードを有効にする detachInterrupt(0); //割り込み処理を解除する } void wakeUp(){ Serial.println("Wake up from standby."); //復帰時にシリアルモニターに表示 PRR = PRR & 0b00000000; //8bitタイマーenable delay(30000); //時間調整 }
では、さっそく動かしてみましょう!
スリープモード 動作確認
全体回路がこちらとなります。
実際にコードを書き込んで動かしてみた結果がこちら。
mode | 電流値 |
---|---|
動作時 | |
SLEEP_MODE_IDLE | |
SLEEP_MODE_ADC | |
SLEEP_MODE_PWR_DOWN | |
SLEEP_MODE_PWR_SAVE | |
SLEEP_MODE_STANDBY |
仕様書によるとSLEEP_MODE_PWR_DOWNが一番低電流になると見込んでいました。
一応、その通りとなりました。
しかし、思ったほど低電流となりません・・・。
他にも、色々と止める必要があったり、本記事では触れていないライブラリを使ったりすることでさらに低減できると考えます。
さらに学習して、低消費電力を目指したいと考えます。
まとめ
「Arduino 入門 番外編 13 【スリープモードによる消費電力の低減】」
いかがだったでしょうか?
バッテリーなどを使って、Arduinoを動かしたいと考えると動作していないときの待機電力を抑えたいものです。
本記事だけでは、まだまだ勉強不足だなと感じました。
まだ、学習していない低電力モードにも挑戦したいと思います。
冒頭でもお伝えしましたが、Arduinoに供給される電圧、電流、電力のモニターにはUSBテスターが便利です。
次回の番外編も、Arduinoの基本的なことについて情報を発信したいと考えています。
最後に
疑問点、質問などありましたら気軽にコメントください。
この電子部品の解説をしてほしい!などなどなんでもOKです。
リンク切れ、間違いなどあればコメントいただけると助かります。
Arduino入門編、番外編、お役立ち情報などなどサイトマップで記事一覧をぜひご確認ください。
Arduino入門編、Arduino入門編2で使用しているUNOはAmazonにて購入可能です。
Arduino入門編では互換品を使用。
Arduinoはオープンソース。
複製して販売するのもライセンス的に問題なし。
そのため互換品の品質も悪くなく、それでいて値段は安いです。
正規品本体の値段程度で豊富な部品が多数ついています。
学習用、遊び用、お試し用には安価な互換品がおすすめです。
ELEGOO UNO キット レベルアップ チュートリアル付 uno mega2560 r3 nanoと互換 Arduino用
上記のものでも十分に多数の部品が入っていますが、最初からもっと多数の部品が入っているこちらもお勧めです。
Arduino入門編2では「Arduino UNO R4 Minima」「Arduino UNO R4 WIFI」にて遊ぶため今のところは正規品を使用。(まだ互換品が・・・ほぼない)
Amazonでお得に買う方法
Amazonでお得に購入するならAmazon Mastercard、Amazonギフト券がおすすめです。
時期により異なりますが、様々なキャンペーンを実施しています。
\Amazonギフト券/
Amazonギフトカード キャンペーン
\Amazon Mastercard お申込み/
Amazon Mastercard 申し込み
いずれの場合もプライム会員である方がお得!!
\Amazon Prime 30日間の無料会員を試す/
無料会員登録
コメント
はじめまして。
ボード上のLEDの消費電流が結構大きいので、スリープの実験で消費電流の変化を見るならマイコンチップのATMEGA328Pと16MHzの水晶、水晶用のコンデンサ2個の最小構成で試した方がいいと思います。
コメントありがとうございます。
消費電流の低減について深堀ができていないので、時間をみつけて色々と試してみたいと思います。
こんにちは
ウォッチドッグタイマー等を使用した,スリープ時間を制御できる方法が知りたいです。
コメントありがとうございます。
すみません、色々と立て込んでおりまして時間がありましたら検討します。
こんにちは。いつも記事拝見させていただいております。
ウォッチドックタイマー等を使って,スリープ時間を制御できるようなプログラムについての解説が見たいです。
バッテリ駆動のディスペンサーを作っているのですが、省エネしたく、貴殿のスケッチを参考にして作ったのですが、どうしてもstandByでエラーになります。’standBy’ was not declared in this scope と出ます。このstandByというのは?なにものでしょう。対策ありませんでしょうか?
//Arduino 番外編13 スリープモード編
//https://omoroya.com
#include
Servo myservo1;
const int IR_Sensor =2;
int IR=0;
#include
#include
void setup()
{
pinMode(IR_Sensor,INPUT);
myservo1.attach(3);
}
void loop()
{
IR=digitalRead(IR_Sensor);
myservo1.write(0);
if (IR_Sensor == 0) { //”d”が入力された場合 SLEEP_MODE_PWR_DOWN
SLEEP_MODE_PWR_DOWN
standBy(); //スタンバイ突入調整関数読み出し
set_sleep_mode(SLEEP_MODE_PWR_DOWN); //スリープモードの設定
interrupt(); //割込み関数の呼び出し
}
}
delay(500);
//何か動作させたい場合はここにコードを追加
//例えばLEDを点灯させるなど。
//setup関数での設定もお忘れなく!
myservo1.write(110);
delay(1000);
myservo1.write(0);
}
//スタンバイ突入調整関数
void standBy(){
delay(100); //時間調整
}
//割込み関数
void interrupt(){
attachInterrupt(0,wakeUp, 0); //割り込み処理の設定、2番ピンがLowで復帰
PRR = PRR | 0b00100000; //8bitタイマーdisable
sleep_mode(); //スリープモードを有効にする
detachInterrupt(0); //割り込み処理を解除する
}
void wakeUp(){
PRR = PRR & 0b00000000; //8bitタイマーenable
delay(30000); //時間調整
}
コメントありがとうございます。
void loop(){
}
の{}の数がおかしいためと考えます。
{}が対になっているか確認ください。
下記1つ余計では?
}
} ← いらない。
delay(500);
上記を消して、defineなどが定義されていればエラーは出ませんでした。