見出し画像

プログラムと電子工作・不揮発性メモリ

不揮発性メモリは、M5StickC の電源をオフしても値を保持するメモリ領域です。

プログラムのパラメータなど、多数の小さな値を保存する場合は Preferences オブジェクトを使用すると便利です。


目標

  • 不揮発性メモリの初期化と終了、値を読み出し/書き込みします。

  • 不揮発性メモリの値を全部消去します。

部品・機材

使用する部品は次のとおりです。「Hello, World!」と全く同じです。

電子部品

  • M5StickC Plus 1台

開発用機材

  • PC(Windows10 または 11)、開発環境 Arduino-IDE 導入ずみ

  • USB-A・USB-C ケーブル

開発手順

  1. PC と M5StickC Plus を USBケーブルで接続する。

  2. Arduino-IDE でスケッチ preferences.ino を開く。

  3. 検証・コンパイルする。

  4. M5StickC Plus に書き込む。

スケッチ

references.ino

#include <M5StickCPlus.h>
#include <nvs_flash.h>
#include <Preferences.h>

#define LHEIGHT 15  /*1行の高さ(px)TextSize(3):25、TextSize(2):15*/
#define MY_APP "my_app"  /*名前空間 15文字以内*/

Preferences pref;

bool nvs_erased = false;
uint32_t time_to_wait = millis();

//------------------------------------------------------------------------------
//  setup()
void setup() {
    //  電源ON時に 1回だけ実行する処理をここに書く。
    M5.begin();               /*M5を初期化する*/
    M5.Lcd.setTextSize(2);    /*文字サイズはちょっと小さめ*/
    M5.Lcd.setRotation(3);    /*上スイッチが左になる向き*/

    M5.Lcd.print("preferences");

    M5.Lcd.setCursor(0, LHEIGHT*2);  /*3行目*/
    M5.Lcd.print("btnA to clear nvs.");

    Serial.begin(115200);   /*デバッグ用のシリアル通信を初期化する*/

    //  Preference を Read/Write モードで開く。
    pref.begin(MY_APP, false);

    //  アプリを開くたびに領域を消す。
    //  ※  消したくないので、コメントにしている。
    //pref.clear();

    //  パラメータ "counter" だけを消す。
    //  パラメータ名は 15文字以下。
    //  ※  消したくないので、コメントにしている。
    //pref.remove("counter");

    //  パラメータ "counter" の値を読む。パラメータがないときは 0になる。
    uint16_t counter = pref.getUShort("counter", 0);

    //  パラメータ "counter" の値を LCD に表示する。
    M5.Lcd.setCursor(0, LHEIGHT*1);  /*2行目*/
    M5.Lcd.printf("count: %6u", counter);

    //  counter をインクリメントする。
    counter++;

    Serial.printf("Current counter value: %u\n", counter);

    //  パラメータ "counter" の値を書く。電源を切っても消えない。
    pref.putUShort("counter", counter);

    //  Preference を閉じる。
    pref.end();

    Serial.println("Restarting in 10 seconds...");
}
//------------------------------------------------------------------------------
//  loop()
void loop() {
    //  自動的に繰り返し実行する処理をここに書く。
    //  ボタンの状態を更新する。
    M5.update();

    //  上ボタン(ボタンA)をチェックする。
    if (M5.BtnA.wasPressed() && !nvs_erased) {
        //  --- 上ボタンが押された。
        //  不揮発性メモリの名前空間すべてのメモリを消去する。
        nvs_flash_erase();      // erase the NVS partition and...
        nvs_flash_init();       // initialize the NVS partition.
        nvs_erased = true;

        time_to_wait = millis();

        M5.Lcd.setCursor(0, LHEIGHT*2);  /*3行目*/
        M5.Lcd.print("nvs all cleared.  ");
        Serial.print("nvs was all cleared.\n");
    }

    //  10秒待つ。
    if (10 * 1000 < millis() - time_to_wait) {
        //  --- 10秒経った。
        //  リスタートする。
        ESP.restart();
    }
}
  • setup() 関数で、不揮発性メモリからカウンタの値を読み出し、液晶ディスプレイに値を表示した後、カウンタを +1し、不揮発性メモリに書き込んでいます。

  • loop() 関数で 10秒待ち、リスタートします。

  • 上ボタン(押しボタンA)を押すと、不揮発性メモリの名前空間すべてのメモリを消去します。

結果

  • もし +1した値が保存されていなければ、リスタート後毎回「count:    0」と表示されるはずです。実際は、カウンタの値が前回の値から +1ずつ上がります。(写真1)

  • 上ボタン(押しボタンA)を押すと、カウンタが 0クリアされます。不揮発性メモリのすべての名前空間が消去されます。

写真1 preferences.ino の実行結果

注意

  • 不揮発性メモリは、電源をオフすると消えるメモリとは異なり、書き込み回数に制限があるので、頻繁に書換えるような使用は避けてください。(まあ少々のことでは制限を超えることはないでしょうが...)

参考

  • ArduinoーエSP32 ESPRESSIF Libraries--APIs--Preferences

/*
 *  Prefereces クラスの使用方法:
 *  
 *  1. 初期化、終了
 *      #include <Preferences.h>
 *      Preferences pref;
 *
 *      bool pref.begin(const char* my_namespace, bool readOnly = false, const char* partition_label = nullptr);
 *      //  const char* my_namespace: 15文字以下、アプリケーションごとにユニークな名称。衝突しないように付ける。
 *      //  bool readOnly: false 読書きモード、true 読出しのみ
 *      void pref.end();
 *
 *  2. 消去
 *      bool pref.clear();  //名前空間の中身をすべて消去する。名前空間は残る。
 *      bool pref.remove(const char* key_name);  //キーと値のペアを削除する。
 *      //  const char* key_name: 15文字以下、フラッシュメモリに格納するパラメータの名称、名前空間内でユニーク。
 *
 *  3. 読み書き
 *      size_t pref.putChar(const char* key_name, int8_t value);
 *      size_t pref.putUChar(const char* key_name, uint8_t value);
 *      size_t pref.putShort(const char* key_name, int16_t value);
 *      size_t pref.putUShort(const char* key_name, uint16_t value);
 *      size_t pref.putInt(const char* key_name, int32_t value);
 *      size_t pref.putUInt(const char* key_name, uint32_t value);
 *      size_t pref.putLong(const char* key_name, int32_t value);
 *      size_t pref.putULong(const char* key_name, uint32_t value);
 *      size_t pref.putLong64(const char* key_name, int64_t value);
 *      size_t pref.putULong64(const char* key_name, uint64_t value);
 *      size_t pref.putFloat(const char* key_name, float_t value);
 *      size_t pref.putDouble(const char* key_name, double_t value);
 *      size_t pref.putBool(const char* key_name, bool value);
 *      size_t pref.putString(const char* key_name, const char* value);
 *      size_t pref.putString(const char* key_name, String value);
 *      size_t pref.putBytes(const char* key_name, const void* value, size_t len);
 *
 *      int8_t pref.getChar(const char* key_name, int8_t defaultValue = 0);
 *      uint8_t pref.getUChar(const char* key_name, uint8_t defaultValue = 0);
 *      int16_t pref.getShort(const char* key_name, int16_t defaultValue = 0);
 *      uint16_t pref.getUShort(const char* key_name, uint16_t defaultValue = 0);
 *      int32_t pref.getInt(const char* key_name, int32_t defaultValue = 0);
 *      uint32_t pref.getUInt(const char* key_name, uint32_t defaultValue = 0);
 *      int32_t pref.getLong(const char* key_name, int32_t defaultValue = 0);
 *      uint32_t pref.getULong(const char* key_name, uint32_t defaultValue = 0);
 *      int64_t pref.getLong64(const char* key_name, int64_t defaultValue = 0);
 *      uint64_t pref.getULong64(const char* key_name, uint64_t defaultValue = 0);
 *      float_t pref.getFloat(const char* key_name, float_t defaultValue = NAN);
 *      double_t pref.getDouble(const char* key_name, double_t defaultValue = NAN);
 *      bool pref.getBool(const char* key_name, bool defaultValue = false);
 *      size_t pref.getString(const char* key_name, char* value, size_t maxLen);
 *      String pref.getString(const char* key_name, String defaultValue = String());
 *      size_t pref.getBytesLength(const char* key_name);
 *      size_t pref.getBytes(const char* key_name, void * buf, size_t maxLen);
 *
 *  4. その他
 *      size_t pref.getBytesLength(const char* key_name);  //キーに対する値のバイト数を取得する。
 *      PreferenceType pref.getType(const char* key_name);  //キーに対する値のデータ型を取得する。
 *      //  PreferenceType: PT_I8 Char; PT_U8 UChar, Bool; PT_I16 Short;
 *      //                  PT_U16 UShort; PT_I32 Int, Long; PT_U32 UInt, ULong;
 *      //                  PT_I64 Long64; PT_U64 ULong64; PT_STR String;
 *      //                  PT_BLOB Double, Float, Bytes; PT_INVALID エラー
 *      size_t pref.freeEntries();  //使用可能な空き数を取得する。
 *      bool pref.isKey(const char* key_name);  //キーが存在するか確認する。
 */
  • 不揮発性メモリの名前空間すべてのメモリを消去する

ライセンス

このページのソースコードは、複製・改変・配布が自由です。営利目的にも使用してかまいませんが、何ら責任を負いません。


この記事が気に入ったらサポートをしてみませんか?