Arduino Unoで内蔵メモリEEPROMを使って状態を保存する方法

Arduino Unoで内蔵メモリEEPROMを使って状態を保存する方法
Arduino Unoで内蔵メモリEEPROMを使って状態を保存する方法

Arduino Unoでデータを保存するには、以前に紹介した SDカードを使った外部記憶装置の利用 以外にも、内部のEEPROMへ保存する方法があります。何か状態の保存をする程度であればEEPROMで十分かもしれません。この記事ではEEPROMの使い方を解説するとともに、スイッチでLチカした状態を保存してみましたのでご参考ください。

Arduino Uno R3のEEPROM

EEPROM(Electrically Erasable Programmable Read-Only Memory)は電気的に消去でき、プログラムで書き換え可能な不揮発性メモリです。Arduino Uno R3で1024バイトのEEPROMが内蔵されています。わずか1kB程度のメモリではありますが、プリセット値やちょっとした状態保存であれば十分使えます。ちなみにArduino Uno R4ではEEPROMの値は8kBへ拡張されてます。

EEPROMの使い方

Arduino IDEではEEPROMが簡単に使えるようにライブラリが内蔵されています。スケッチでEEPROMを使う場合は下記ヘッダをインクルードします。

cpp
#include <EEPROM.h>

次のようにしてEEPROMのサイズを取得できます。

cpp
unsigned int eeprom_size = EEPROM.length();
Serial.println(eeprom_size);

データを読み込むread()

read() 関数を使ってEEPROMから保存されている値を読み込みます。address には0〜1023の範囲で値を指定します。
cpp
int value = EEPROM.read(address)

データを書き込むwrite()

EEPROMへデータを書き込むには write() 関数を使います。

cpp
int value = 16
EEPROM.write(address, value)

ただしArduino Uno R3は8ビットマイコンですから、write()で書き込める値 value は8ビットになります。つまり0〜255までの値となります。それ以上大きい値を書き込む場合は分割してメモリへ保存するなどの工夫が必要となります。

他にもEEPROMライブラリには update() get() put() といった関数が存在しますが、ここで紹介した read()write() の2つを使えば十分事足りります。

EEPROM Library | Arduino Documentation

【実践】2つのLEDの状態を保存する

2進数を使って2つのLEDの状態を記録する

LEDの状態保存には次のように2進数を使うと便利です。今後、状態管理が複数に増えたときもプログラムを完結に書くことができます。

10進数二進数LED1LED2
00x00オフオフ
10x01オフオン
20x10オンオフ
30x11オンオン

Arduinoとタクトスイッチ、LEDの配線

ここでは実際にArduino Unoを使って、タクトスイッチでLEDを点灯させ、その状態をEEPROMに書き込んで保存してみます。Arduinoとタクトスイッチ、LEDは次のように配線しました。抵抗は220Ω〜1kΩ程度で構いません。タクトスイッチの片側はGNDに接地し、もう一方をArduinoのデジタルピンへ接続させます。

Arduinoとタクトスイッチ、LEDの配線
Arduinoとタクトスイッチ、LEDの配線

2つのLEDの状態を保存するスケッチ

こちらが2つのLEDの状態を保存するソースコードになります。2進数を使うことでEEPROMの1つのアドレスに2つのLEDの状態を保存することができました。

ledx2.ino
/**
 * @file ledx2.ino
 * @author Toshihiko Arai ( i.officearai@gmail.com)
 * @brief Arduino Uno R3のEEPROMを使って、2つのLEDの状態を記憶させるデモ
 * @version 0.1
 * @date 2023-07-24
 * 
 * @copyright Copyright (c) 2023
 * 
 */
#include <Arduino.h>
#include <EEPROM.h>

const uint8_t LED_PINS[] = {2, 3};
const uint8_t SWITCH_PINS[] = {4, 5};
const uint8_t BIT_SIZE = 2;

uint8_t ledStates = 0b00;

void setup() {
    Serial.begin(115200);

    for(uint8_t i = 0; i < BIT_SIZE; i++) {
        pinMode(LED_PINS[i], OUTPUT);
        pinMode(SWITCH_PINS[i], INPUT_PULLUP);
    }
    ledStates = EEPROM.read(0);

    blinkLed(ledStates);

}

void loop() {
    for(uint8_t i = 0; i < BIT_SIZE; i++) {

        if (digitalRead(SWITCH_PINS[i]) == LOW) {
            
            
            if(bitRead(ledStates, i)) {
                bitClear(ledStates, i);
            } else {
                bitSet(ledStates, i);
            }
            blinkLed(ledStates);
            saveStates(ledStates);
            Serial.println(ledStates, BIN);
            delay(200); // チャタリング防止用
        }
    }

    delay(100); // チャタリング防止用
}

void blinkLed(uint8_t state) {
    for(uint8_t i = 0; i < BIT_SIZE; i++) {        
        bool isOn = bitRead(state, i);
        digitalWrite(LED_PINS[i], isOn);
    }

}

void saveStates(uint8_t state) {
    EEPROM.write(0, state);
}

▼ 実際にArduinoで動作させている様子を動画にしました。

Arduinoでビット演算の扱い

Arduino言語はC++ベースですので、2進数のビット演算は論理演算で書くこともできます。が、Arduino言語にはとても便利な関数が用意されています。それが bitRead(x, n) bitClear(x, n) bitSet(x, n) です。x は処理したい2進数の変数を入れ、n で何ビット目かを指定します。bitRead(x, n) では n ビット目の値を取り出すことができます。bitClear(x, n) では特定の n ビットの値のみを0へ書き換えることができます。bitSet(x, n) ではその逆に特定のビットを1へ書き換えます。

タクトスイッチの使い方

タクトスイッチを使うため INPUT_PULLUP で内蔵プルアップを設定しておきます。タクトスイッチを押すことでGNDとショートし、digitalReadLOW になります。ただしタクトスイッチはそのままだとチャッタリング生じるため、プログラムでは短い間に何十回もボタンを押したと判断してしまいます。そこで delay 関数でタイムラグを設けることでチャタリング時間を排除させてます。簡易的な方法ではありますが、結構有効です。 他にも attachInterrupt 関数を使って割り込み処理でタクトスイッチのオンオフをハンドリングする方法もあります。ただし attachInterrupt で呼ばれるイベント内では、変数の扱いに注意が必要です。メインスレッドの変数をそのまま参照できないため、処理を工夫する必要がありプログラムが複雑になります。詳しくは volatileISR あたりを調べてみてください。 スイッチ判定を割り込み処理で行ってた時もありますが、トラブルが多く管理が複雑になるためやめました。上記のプログラムソースのように loop() イベント内でスイッチ判定させるようにしてます。ちなみにこういった処理方法をポーリングと呼びます。

関連記事

最後までご覧いただきありがとうございます!

▼ 記事に関するご質問やお仕事のご相談は以下よりお願いいたします。
お問い合わせフォーム

人気のArduino互換機
Arduinoで人気の周辺パーツ
あると便利な道具
Arduinoのオススメ参考書

▼ Arduino初心者向きの内容です。ほかのArduino書籍と比べて図や説明がとてもていねいで読みやすいです。Arduinoで一通りのセンサーが扱えるようになります。

▼ 外国人が書いた本を翻訳したものです。この手の書籍は、目からうろこな発見をすることが多いです。

▼ Arduinoの入門書を既に読んでいる方で、次のステップを目指したい人向きの本です。C言語のプログラミングの内容が中心です。ESP32だけでなく、ふつうのArduinoにも役立つ内容でした。

関連記事