ESP32(ESP32-DevKitC)で取得したデータをmicroSDカードに保存する方法を調べました。

microSDカードモジュール

 ArduinoでmicroSDを使ったときはArduinoの5VをSDカードの3.3Vに変換する回路が搭載されたモジュールを使いました。

 ESP32は3.3Vですのでレベル変換は必要ありません。しかしネットで調べるとデータ線を10kΩ程の抵抗でプルアップする必要があるようです。

 必要な抵抗を搭載したモジュールらしきものがAliExpressであったので入手してみました。 同じようなモジュールがいくつかのショップで売られていました。 日本のAmazon 等ではまだ売られていないようです。

 入手したものはmicroSDのスロットに10kΩの抵抗4つとチップコンデンサ2つがモジュール化されたものです。
 ピンヘッダはついていなかったのではんだ付けして取り付けました。

 回路を起こしてみると次の様になりました(間違っていたらゴメンナサイ)。


 MOSI、CLK、MISOが10kΩでプルアップされていました。もう一つの10kΩは3.3V電源とmicroSDソケットの端子(SPIモードでは使われていない端子)の間につながっています。2つのチップコンデンサ(容量不明)はGNDとの間に並列に接続されていました。

配線

 ESP32(ESP32-DevKitC)との接続は以下の様になります。

ESP32-DevKitCmicroSD モジュール
3.3V3V3
GPIO5(SPI SS)CS
GPIO23(SPI MOSI)MOSI
GPIO18(SPI SCK)CLK
GPIO19(SPI MISO)MISO
GNDGND

スケッチ

サンプルスケッチを試してみる ⇒ ”Error!”

『ファイル』 ⇒ 『スケッチ例』 ⇒ 『SD』 ⇒ 『CardInfo』を開きます。 ESP32用に一部を書き換えます。

  • 36行目”chipSelect”の値を5に変更 const int chipSelect = 5;
  • 40行目 シリアル通信速度を115200に変更 Serial.begin(115200);

 そして書き込みましたが、エラー発生。

 エラーメッセージを読んでみると、

  • ”SD.h”というライブラリが複数ある。
  • ”SPI.h”というライブラリも複数ある。
  • ”SD2Card”が見つからない。

 どうやら、

  • ESP32用に環境をセットアップしたときに”SD.h”、 ”SPI.h”の同名ライブラリがESP32用にインストールされていた
  • ESP32のボード用にコンパイルするときはESP32用の”SD.h”、 ”SPI.h”を使った。
  • Arduino用の”SD.h”には有る”SD2Card”が、ESP32用の”SD.h”には無かったためエラーになった。

 ということらしい。

 つまりArduino用のスケッチは使えないということでしょうか。

ESP32用のサンプルスケッチ

 『ファイル』 ⇒ 『スケッチ例』 ⇒ (”ESP32 Dev Module 用のスケッチ例”の下)『SD(esp32)』 ⇒ 『SD_Test』を開きます。

 このスケッチをそのまま実行すると正常に実行されたように見えました。

 しかし、かろうじてArduino IDEを書けるレベルの私のプログラミング知識では書いてあるコードの理解が追いつきません。

 特に19行目等にある

void listDir(fs::FS &fs, const char * dirname, uint8_t levels){

 の fs::FS &fs の部分ですが、なんとなくやっていることは分かるのですが、明確に説明できずもやもやした感じが残っています。

 とりあえずこのサンプルスケッチを書き替えて色々試してみました。

書き込み

 書き込みのスケッチは次のようにしました。
 サンプルスケッチからファイル書き込みに関するコードだけ抜き出したものです。

#include "FS.h"
#include "SD.h"
#include "SPI.h"

void writeFile(fs::FS &fs, const char * path, const char * message){
    Serial.printf("Writing file: %s\n", path);

    File file = fs.open(path, FILE_WRITE);
    if(!file){
        Serial.println("Failed to open file for writing");
        return;
    }
    if(file.print(message)){
        Serial.println("File written");
    } else {
        Serial.println("Write failed");
    }
    file.close();
}

void appendFile(fs::FS &fs, const char * path, const char * message){
    Serial.printf("Appending to file: %s\n", path);

    File file = fs.open(path, FILE_APPEND);
    if(!file){
        Serial.println("Failed to open file for appending");
        return;
    }
    if(file.print(message)){
        Serial.println("Message appended");
    } else {
        Serial.println("Append failed");
    }
    file.close();
}

void setup() {
    Serial.begin(115200);
    while (!Serial) {
    }

    if(!SD.begin()){
        Serial.println("Card Mount Failed");
        return;
    }
    writeFile(SD, "/hello.txt", "hello ");
    appendFile(SD, "/hello.txt", "World!\n");
}

void loop() {

}

 microSDのルートフォルダにあるファイルを指定するときArduinoでは”hello.txt”のようにフィアル名だけを指定して開くことができましたが、esp32ではルートを示す”/”をつけて”/hello.txt”としなければファイルを開けません。
 ファイルを新しく作って書き込むときは”writeFile()”関数を、既存のファイルに追記するときは”appendFile()”関数と使い分けます。
 但し、ファイルの存在しない状態で”appendFile()”関数を実行すると新しくファイルが作られて書き込まれました。

読み出し

 ファイルの読み出しのスケッチは以下の通りです。
 これもサンプルスケッチから読み出しのコードだけ抜き出したものです。

#include "FS.h"
#include "SD.h"
#include "SPI.h"

void readFile(fs::FS &fs, const char * path){
    Serial.printf("Reading file: %s\n", path);

    File file = fs.open(path);
    if(!file){
        Serial.println("Failed to open file for reading");
        return;
    }

    Serial.print("Read from file: ");
    while(file.available()){
        Serial.write(file.read());
    }
    file.close();
}

void setup() {
    Serial.begin(115200);
    while (!Serial) {
    }

    if(!SD.begin()){
        Serial.println("Card Mount Failed");
        return;
    }

    readFile(SD, "/hello.txt");   
}

void loop() {

}

 読み出しでも同様にルートであることを”/”で明示する必要があります。

タブ(ファイル)を分ける

 esp32用の ”SD.h” でのスケッチは各操作各々につき関数を用意します。 スケッチの先頭に何種類もの関数があると、”setup()” 関数や”loop()” 関数をすぐに見つけられなくなり、可読性が下がります。

 Arduino IDE では新しいタブを追加して、そちらに関数等スケッチの一部を移すことができます(”#include ・・・” はメインタブから移すことはできないようです)。
 別タブにすると、スケッチは同じフォルダの別ファイルとして保存されます。

  • Arduino IDE右上の下矢印をクリックして、出てきたメニューから『新規タブ』をクリック。
  • 下に”新規フィアルの名前:”の項が出るので、ファイル(タブ)名を入力して[OK]をクリックする。
  • 新しいタブが出来るので、移動させたい関数をカット&ペーストで持ってくる(元のタブに関数が残っているとエラーになります)。
  • とりあえずサンプルスケッチ ”SD_Test” の ”listDir” 関数から ”testFileIO” 関数までを ”DS_Functions” をいうタブ(ファイル)に分けてみました。
  • 保存すると新しいファイルができている。
広告

 

温湿度のデータをmicroSDに保存

 ”温湿度ロガーの作成”で作ったものをESP32 で作成しました。

配線

 温湿度を測定する為の温湿度センサー、データを保存する為のmicro SD モジュール、測定開始・終了させるためのプッシュボタン、測定中を示すLED 及びLED 用の電流制限抵抗(220Ω)。 そしてブレッドボードとジャンプワイヤー、ESP32(ESP32-DevKitC)を用意しました。

 配線は下図の通りです。

スケッチ

 Arduinoで作ったときは ”MsTimer2” というライブラリを使いましたが、ESP32 では動作しませんでした。
 そこで ”Ticker” というライブラリを使いました。 ”MsTimer2” と同様に設定した時間毎に割り込み処理を行うことができます。 精度が少し劣るということらしいですが、そんなに正確な動作が必要なわけではありません。
 ”Timer” を使って割り込みを行うこともできて精度も高いらしいのですが、私の手には負えませんでした。 割り込み先の関数で温湿度のデータを取得して文字列型に変換する作業を行うとESP32 が落ちてしまい、原因も対策も見当つきませんでした。

#include <FS.h>
#include <SD.h>
#include <SPI.h>
#include <DHT.h>
#include <DHT_U.h>
#include <Ticker.h>

#define DHTPIN 16 // 温湿度センサーのピン番号
#define DHTTYPE DHT11 // 温湿度センサーの型番

volatile bool  meas = 0; // 測定が有効か否か
DHT dht(DHTPIN, DHTTYPE); // 温湿度センサの設定

Ticker ticker;

// SDカードに追記
void appendFile(fs::FS &fs, const char * path, String message){
  // 三番目の引数"message"はString型にするとポインタ渡しが出来なかったので値渡しにした
    Serial.printf("Appending to file: %s\n", path);

    File file = fs.open(path, FILE_APPEND);
    if(!file){
        Serial.println("Failed to open file for appending");
        return;
    }
    if(file.print(message)){
        Serial.println("Message appended");
    } else {
        Serial.println("Append failed");
    }
    file.close();
}

// 温湿度測定
void measure() {
  // "meas"が1なら温湿度を測定してSDカードに保存
  if (meas == 1) {
    String dataString = "";
    dataString += String(millis() / 1000); // 起動後の経過時間を秒に変換
    dataString += ",";
    float t = dht.readTemperature(); // 温度測定(℃)
    if(!isnan(t)){
      dataString += String(t);
    }else{
      dataString += " ";
    }
    dataString += ",";
    float h = dht.readHumidity(); // 湿度測定(%))
    if(!isnan(h)){
      dataString += String(h);
    }else{
      dataString += " ";
    }
    dataString += "\n";

    // ファイルにデータを追記する
    appendFile(SD, "/LOGGING.CSV", dataString);
    Serial.println(dataString);
    }
}

void setup() {
  Serial.begin(115200);
  while (!Serial) {
  }

  Serial.println("Initializing SD card...");

  // SD カード初期化
  if(!SD.begin()){
      Serial.println("Card Mount Failed");
      return;
  }
  Serial.println("card initialized.");

  pinMode(4, OUTPUT); // LED出力
  pinMode(2, INPUT_PULLUP); // プッシュボタン入力

  dht.begin(); // 温湿度センサー開始
  
  ticker.attach_ms(60000, measure); // 60秒毎に”measure”関数を割り込みで呼び出す
}

void loop() {
  // プッシュボタンが押されたとき、”meas”が"0"なら"1"にしてLED点灯
  // "1"なら"0"にしてLED消灯         
  if (digitalRead(2) == LOW) {
    if (meas == 0) {
      meas = 1;
      digitalWrite(4 ,HIGH);
    }
    else  {
      meas = 0;
      digitalWrite(4 ,LOW);
    }
    Serial.println(meas);
    delay(1000);
  }
}

動作確認

 配線を行ったArduino にスケッチを書きこんで、温湿度測定を行いました。

広告