Arduino 8) EEPROMを使って電源を切っても変数が消えないようにする
2019.02.15
プログラムを作るに当たって、電源を切ったらデータが消えてしまうというのは
考えられません。あってはならないことです。
Windowsはデータベースに保存したり、iniファイルを使用したりと様々な方法で
環境の保存を行っております。
Arduinoは頻繁に電源を入り切りするコンピュータです。
Windowsのようにアプリの終了やシャットダウンで環境保存することはできません。
そこでプログラム動作中に変数を退避する必要が出てくるわけですが、
それを実現するのがEEPROMに記憶するという方法です。

EEPROMのエリアは基本0~255の256個(256Byte)となってますが、
Unoの場合1KB(1024Byte)Megaの場合4KBまで使用可能です。
一般的には10万回の書き込みに耐えられるとされていますが、
実際にテストを行った方が居て、300万回は耐えたという情報もあります。

1秒毎にカウントして記憶するプログラムを組んでみました。
EEPROM.hを覆っている<>はhtmlの都合上全角になっております。

#include <EEPROM.h>
int Access_count;

void setup() {
Serial.begin( 9600 ); //シリアルモニタを使うときの儀式
pinMode(13, OUTPUT); //ボードのLEDを使用する
Access_count = EEPROM.read(0); //0番地にEEPROMから値を読み込む
}


void loop() {
digitalWrite(13, HIGH); //LEDを光らせる
delay(1000); //1秒
Access_count = Access_count + 1; //アクセスカウンターを追加
Serial.println(Access_count); //シリアルモニタに値を出力
EEPROM.write(0, Access_count); //0番地からEEPROMに値を書き込む。
}


シンプルにまとめてみました。
正常にEEPROMに書き込まれたかは電源入れた都度
シリアルモニタで確認できるようにしています。
このプログラムは1秒毎にカウントしていくものですが、
1Byteを超える数字256以上まで進んだ場合は正常に保存できません。
255より大きい数字は自分で分割して保存エリアを分ける必要があります。
それではちょっと勝手が悪いので、別の方法を模索していると、
ちょうどいい命令を見つけました。

EEPROM.put (番地, データ);  //保存
EEPROM.get (番地, データ); //読込

INT型であれば、-32768から32767まで扱うことができます。
これは2byte使うので0番地で保存した場合同時に1番地も使用しているとみた方がいいです。
他の変数も保存する場合はぶつからないように2番地から使うと行った配慮が必要になるかと思われます。
ためしに以下のようなプログラムを作って検証してみました。

#include <EEPROM.h>
int TestNum = 12345;

void setup() {
Serial.begin( 9600 ); //シリアルモニタを使うときの儀式
EEPROM.put(0,0);
EEPROM.put(1,0);
EEPROM.put(2,0);
EEPROM.put(3,0);
EEPROM.put(0,TestNum);
Serial.println(EEPROM.read(0));
Serial.println(EEPROM.read(1));
Serial.println(EEPROM.read(2));
Serial.println(EEPROM.read(3));
Serial.println(EEPROM.get(0,TestNum));
}


結果は以下の通り



0番地に57(2進数00111001)
1番地に48(2進数00110000)
連結すると0011000000111001 = 12345
計算は合ってます。
想像通り自動的に2byte使ってますね。
これは大変便利な機能ですが大きいプログラムを組むときは
アドレスマップを作らないと危険ですね。

では次の疑問に行きたいと思います。
intをlong(-2147483684から2147483647)の変数に変えた場合はどうなるのか。
上の経験を踏まえれば32bitなので4byte使われると思いますが。

ソースを以下のように変更して実行してみます。

#include <EEPROM.h>
long TestNum = 1234567890;

void setup() {
Serial.begin( 9600 ); //シリアルモニタを使うときの儀式
EEPROM.put(0,0);
EEPROM.put(1,0);
EEPROM.put(2,0);
EEPROM.put(3,0);
EEPROM.put(4,0);
EEPROM.put(5,0);
EEPROM.put(0,TestNum);
Serial.println(EEPROM.read(0));
Serial.println(EEPROM.read(1));
Serial.println(EEPROM.read(2));
Serial.println(EEPROM.read(3));
Serial.println(EEPROM.read(4));
Serial.println(EEPROM.read(5));
Serial.println(EEPROM.get(0,TestNum));
}


結果は想像したとおりになりました。



では事前に0番地から5番地に255を入れ込んでおき、
long TestNum = 123;
とした場合はどうなるのでしょうか。

#include <EEPROM.h>
long TestNum = 123;

void setup() {
Serial.begin( 9600 ); //シリアルモニタを使うときの儀式
EEPROM.put(0,255);
EEPROM.put(1,255);
EEPROM.put(2,255);
EEPROM.put(3,255);
EEPROM.put(4,255);
EEPROM.put(5,255);
EEPROM.put(0,TestNum);
Serial.println(EEPROM.read(0));
Serial.println(EEPROM.read(1));
Serial.println(EEPROM.read(2));
Serial.println(EEPROM.read(3));
Serial.println(EEPROM.read(4));
Serial.println(EEPROM.read(5));
Serial.println(EEPROM.get(0,TestNum));
}


値は1Byte、変数は4Byteのもの。
命令が変数のサイズで反応しているなら5番地と6番地は255のまま。
値のサイズで反応しているなら0番地以外全て255のままとなります。
結果は・・



つまり値ではなく、変数の種類で書き込み場所が決まるようです。
これはとても重要なことですよ。ぜひ頭に焼き付けて置くべき事柄です。
実験してよかった(*´ω`*)

あと問題があるとすればEEPROMの書き込み寿命ですかね。
今、F-RAMっていうのが登場しているようですが、
F-RAMはCypressのFM25W256を使うと、ほぼ制限を気にすることなく書き込みができるという記事があります。
https://www.switch-science.com/catalog/1406/
結構いい値段がしますね。庶民にはまだまだ手が出ないかなぁ。

- CafeNote -