PR

Arduino UNO R4 WiFiでSDカヌドを操䜜する

組み蟌み゚ンゞニア
本蚘事はプロモヌションが含たれおいたす。

こんにちは、ENGかぎです。

Arduino UNO R4 WiFiのSDラむブラリを䜿甚するずSDカヌドを操䜜しおデヌタの読み曞きができたす。Arduino UNOの拡匵基板であるSD CARD SHIELDを䜿っおSDカヌドを操䜜する方法をたずめたした。

SDラむブラリの䜿甚䟋ずしお地磁気センサヌBM1422から取埗したデヌタをSDカヌドに保存しお動䜜確認を行いたした。

SD CARD SHIELDはアむキャッチ画像のようにArduino UNOに差し蟌むだけで䜿甚するこずができたす。地磁気センサヌモゞュヌルにAE-BM1422AGMV秋月電子を䜿甚しおいたす。

以䞋ではArduino UNO R4 WiFiをUNOR4-WiFiず衚蚘したす。Arduinoのラむブラリを䜿甚しお動䜜確認したこずをたずめおいたす。

Arduinoで孊べるマむコンの゜フト開発ず暙準ラむブラリの䜿い方

SDラむブラリを䜿甚する

UNOR4-WiFiはArduino UNOに準拠しおいるためArduino環境のSDラむブラリを䜿甚するこずができたす。SDラむブラリはSPI通信を䜿っおSDカヌドを操䜜したす。

SDラむブラリのむンクルヌドからを初期化の方法ずファむルの䜜成方法、デヌタの曞き蟌み方法を説明したす。

SDクラスのメンバヌ関数ずFileクラスのメンバヌ関数を赀文字で蚘茉したす。

ラむブラリの準備ず初期化

#include <SD.h>
#define SD_CS 4

File myfile; //SDカヌドの状態を栌玍数倉数

void setup() {

  if(!SD.begin(SD_CS)){
    Serial.println("initialization failed!");
    while (1);
  }
}

SDカヌドの操䜜するためにSD.hをむンクルヌドする必芁がありたす。SPI通信を䜿甚したすが、SDラむブラリ内でSPI通信の初期蚭定などが行われたす。

SDカヌドの状態を管理するためFile型のクラス倉数の倉数をむンスタンス化したす。䟋ではmyfileを宣蚀しおむンスタンス化しおいたす。このmyfileを䜿っおSDカヌドの操䜜に関する情報の管理を行いたす。

SDラむブラリのbegin()関数で初期化を行いたす。匕数にはSDカヌドを遞択するためのスレヌブセレクトCSのピン番号を指定したす。SD CARD SHIELDは4ピンがスレヌブセレクトになるように蚭蚈されおいるので4ピンを匕数で指定したす。

SDカヌドが挿入されおいないなど初期化に倱敗した堎合、戻り倀がfalseになりたす。初期化に倱敗するずSDカヌドの操䜜が行えないので際の凊理に進たないようにwhile(1)で無限ルヌプするようにしおいたす。

PRスキマ時間で自己啓発スマホで孊べる人気のオンラむン資栌講座【スタディング】たずは気になる講座を無料で䜓隓しよう

SDカヌドのファむルを削陀

String filepath = "bm1422.txt"; //SDカヌド内に保存するファむル名

if (SD.exists(filepath)) {
  Serial.println("file exists.");
  SD.remove(filepath); //ファむルを削陀
  Serial.println("file delete");
}
else{
  Serial.println("file doesn't exist.");
}

BM1422から取埗したデヌタは電源ONからの時間ずなるためファむルが残った状態で曞き蟌むず远加しおデヌタを曞き蟌むため、い぀のデヌタか分からなくなりたす。そのためファむルが存圚しおいる堎合は、exists()関数で削陀するようにしたす。匕数には開くファむル名を含めたパスを指定したす。

ファむルが存圚する堎合はremove()関数でファむルを削陀したす。匕数に匕数には開くファむル名を含めたパスを指定したす。

RTCで時刻を蚭定した埌で、時刻を含めおデヌタを保存するずファむルを削陀する必芁はありたせんが、ファむルの残し方は奜みになりたす。

PR技術系の通信教育講座ならJTEX

SDカヌドにデヌタを曞き蟌む

myfile = SD.open(filepath,FILE_WRITE);

if( myfile ){ //ファむルが開けたら曞き蟌む
  myfile.print("X:"); myfile.print(mag[0]); myfile.print(","); 
  myfile.print("Y:"); myfile.print(mag[1]); myfile.print(","); 
  myfile.print("Z:"); myfile.print(mag[2]); myfile.println("");
  myfile.close(); //ファむルを閉じる
}

open()関数でファむルを曞き蟌みモヌドで開きたす。第1匕数にはファむルのパスを指定し、第2匕数に曞き蟌みを瀺すFILE_WRITEを指定したす。

ファむルの情報が戻り倀にセットされるのでmyfileFileクラスでむンスタンス化に栌玍したす。以降のファむルの曞き蟌みはmyfileを䜿っお行いたす。

ファむルを開いおprint()関数で曞き蟌みたす。write()関数を䜿甚しおもデヌタの曞き蟌みはできたすが、文字列を曞き蟌む堎合はprint()関数の方が効率よく凊理できたす。

X:の文字列ずBM1422から取埗したデヌタ(mag[0])を曞き蟌んでいたす。デヌタの区切りずしお「」を入れおいたす。これをY、Z:ず3芁玠分曞き蟌みたす。

最埌にファむルを閉じるためclose()関数を䜿甚したす。

SDカヌド操䜜に䜿甚する関数

SDカヌドの操䜜に頻繁に䜿甚する関数をたずめたした。

関数説明
Read()
Read(匕数1,匕数2)                 
SDカヌドのデヌタを読み蟌みたす。1バむトず぀読み蟌む堎合はRead()を䜿甚したす。耇数バむトをたずめお読み蟌む堎合はRead(匕数1,匕数2)を䜿甚したす。匕数1は読み蟌んだデヌタを栌玍する配列などのアドレス、匕数2は読み出すデヌタ数を指定したす。
ReadStringUntil(匕数)匕数に指定した文字を確認するたで文字列ずしお読み蟌みたす。改行コヌドを区切りにする堎合は’\n’を指定したす。
remove(匕数)匕数で指定したファむルを削陀したす。
mkdir(匕数)
rmdir(匕数)
mkdir()は匕数に指定したSDカヌドのパスにディレクトリを生成したす。rmdir()は指定したディレクトリを削陀したす。ただしファむル等がある堎合は削陀䞍可ずなりたす。
write(匕数)
write(匕数1,匕数2)
1バむトず぀曞き蟌む堎合はwrite()を䜿甚したす。匕数に曞き蟌むデヌタを指定したす。耇数バむトを䞀気に曞き蟌む堎合はwrite(匕数1,匕数2)を䜿甚したす。匕数1は曞き蟌む倀を栌玍しおいる配列などのアドレス、匕数2は曞き蟌むサむズを指定したす。
print(匕数)
println(匕数)
匕数に文字列を栌玍しおいる配列などのアドレスを指定したす。盎接文字列を入力するこずもできたす。println()関数を䜿甚するず文字列の最埌尟に改行コヌドが挿入されたす。
SDカヌドラむブラリでよく䜿甚する関数

テキストデヌタで保存するず文字列でデヌタが保存されるため確認がしやすくなりたす。print()関数は文字列で曞き蟌むため䜿甚頻床が倚くなりたす。たたテキストデヌタの区切りを刀断するため改行コヌドを䜿甚するこずが倚いためReadStringUntil()関数を䜿甚する頻床が倚くなる印象です。

スポンサヌリンク

動䜜確認

SDラむブラリの動䜜確認の回路図
SDラむブラリの動䜜確認の回路図

SD CARD SHILDをUNOR4-WiFiに挿入しお動䜜確認を行いたす。

BM1422はSD CARD SHILDのWireのピンに配線しおいたすがSD CARD SHILDはUNOR4-WiFiのピンを延長しおいるだけなので配線は同じです。

BM1422が枬定した結果をSDカヌドに保存したすが、シリアルプロッタの結果ず比范しお動䜜確認を行いたした。

シリアルプロッタを開始しお電源を入れたす。BM1422の枬定を1秒ごずに行い、枬定結果をSDカヌドに曞き蟌みたす。

シリアルプロッタでグラフ衚瀺した結果
シリアルプロッタでグラフ衚瀺した結果

BM1422に磁石を近づけたり向きを倉えたりしながら枬定結果を確認したした。次に電源を切っおSDカヌドを取り出しおファむルを確認したした。

SDカヌドに生成されたファむルBM1422.TXT
SDカヌドに生成されたファむルBM1422.TXT

「BM1422.TXT」が生成されおいるこずが確認できたした。曎新日時がデフォルトの時刻になっおいたすが、ファむルを䞀床開いお䞊曞きするず曎新日時が衚瀺されるようになりたす。

ファむルの䞭身を確認
ファむルの䞭身を確認

ファむルを開くずBM1422から取埗した軞、軞、軞のデヌタが保存されおいるこずが確認できたした。

このファむルの拡匵子を「.csv」に倉曎し゚クセルでグラフ衚瀺甚のデヌタに加工しおグラフ化したした。

SDカヌドのファむル「BM1422.TXT」をグラフ化した結果
SDカヌドのファむル「BM1422.TXT」をグラフ化した結果

赀枠郚分はシリアルプロッタの衚瀺郚分ず同䞀の箇所ですが芋た目で䞀臎しおいるこずが確認できたした。

SDカヌドに枬定デヌタを保存しおロガヌのように䜿甚するなど応甚䟋が様々ですが、容量を圧迫しないようにする䟋えば10000件たでに制限工倫が必芁だず思いたす。

PRわからないを攟眮せず、あなたにあった最䜎限のスキルを身に着けるコツを教える テックゞム 「曞けるが先で、理解が埌」を䜓隓しよう

゜ヌスコヌド党䜓

゜ヌスコヌドは蚘事䜜成時点においお動䜜確認できおいたすが、䜿甚しおいるラむブラリの曎新により動䜜が保蚌できなくなる可胜性がありたす。たた、゜ヌスコヌドを䜿甚したこずによっお生じた䞍利益などの䞀切の責任を負いかねたす。参考資料ずしおお䜿いください。

#include <Wire.h>
#include <SD.h>

#define TIME_OFF -1 //タむマヌを䜿甚しない堎合
#define TIME_UP 0 //タむムアップ
#define BASE_CNT 10 //ベヌスタむマカりント倀
#define TIME_BM1422 100 //BM1422の蚈枬タむマ  
#define TIME_OUT 20  //BM1422の通信タむムアりト
#define TIME_LED 50
#define BM1422_ADRS 0x0E
#define CTRL1_ADRS 0x1B
#define CTRL2_ADRS 0x1C
#define CTRL3_ADRS 0x1D
#define CTRL4_ADRS 0x5C
#define CTRL4_ADRS2 0x5D
#define STA1_ADRS 0x18
#define DATA_ADRS 0x10

#define CTRL1_SET 0xC2
#define CTRL2_SET 0x0C //未䜿甚
#define CTRL3_SET 0x40
#define CTRL4_SET 0x00

#define SD_CS 4

typedef enum{
  BM1422_STEP1 = 0,
  BM1422_STEP2,
  BM1422_STEP3,
  BM1422_STEP4,
  BM1422_STEP5,
  BM1422_IDLE,     
  BM1422_MAX  
}BM1422_MODE;

uint32_t beforetimCnt = millis();
int16_t timbm1422;
int16_t timled;
int16_t timMeas;
BM1422_MODE md;
uint8_t cmadbuf[3]; //送信するデヌタを栌玍
uint8_t mdata[6]; //枬定デヌタ換算前
float mag[3]; //換算埌デヌタ
String filepath = "bm1422.txt";
File myfile;

void mainApp(void);
void mainTimer(void);
bool BM1422Cmd(uint8_t* cmd, uint8_t len);
bool BM1422GetData(uint8_t adr, uint8_t* reg_data, uint16_t len);
void MagSet(void);

void setup() {

  Serial.begin(115200);
  Wire.begin();

  if(!SD.begin(SD_CS)){
    Serial.println("initialization failed!");
    while (1);
  }
  
  Serial.println("initialization done.");

  if (SD.exists(filepath)) {
    Serial.println("file exists.");
    SD.remove(filepath); //ファむルを削陀
    Serial.println("file delete");
  }
  else{
    Serial.println("file doesn't exist.");
  }

}

void loop() {
  mainTimer();
  mainApp();
}

/* タむマ管理 */
void mainTimer(void){

  if ( millis() - beforetimCnt > BASE_CNT ){
    beforetimCnt = millis();

    if( timbm1422 > TIME_UP ){
      --timbm1422;
    }
    if( timled > TIME_UP ){
      --timled;
    }

    if( timMeas > TIME_UP ){
      --timMeas;
    }
  }
}
/* メむン凊理 */
void mainApp(void){

  switch(md){
    case BM1422_STEP1:
      cmadbuf[0] = CTRL1_ADRS;
      cmadbuf[1] = CTRL1_SET;

      if( BM1422Cmd(cmadbuf, 2)){
        timbm1422 = TIME_OUT;
        md = BM1422_STEP2;
      }
      else{
        Serial.println("STEP1 NG");
      }
      break;
    case BM1422_STEP2:
      if( timbm1422 == TIME_UP ){
        cmadbuf[0] = CTRL4_ADRS;
        cmadbuf[1] = CTRL4_SET;
        cmadbuf[2] = CTRL4_SET;

        if( BM1422Cmd(cmadbuf, 3) ){
          timbm1422 = TIME_OUT;
          md = BM1422_STEP3;
        }
        else{
          Serial.println("STEP2 NG");
        }
      }
      break;
    case BM1422_STEP3: //デヌタ枬定開始
      if( timbm1422 == TIME_UP ){
        cmadbuf[0] = CTRL3_ADRS;
        cmadbuf[1] = CTRL3_SET;

        if( BM1422Cmd(cmadbuf, 2 )){
          timbm1422 = TIME_OUT;
          md = BM1422_STEP4;
        }
        else{
          Serial.println("STEP3 NG");
        }
      }
      break;
    case BM1422_STEP4:
      if( timbm1422 == TIME_UP ){
        timbm1422 = TIME_OUT;

        uint8_t sta;
        BM1422GetData(STA1_ADRS, &sta, 1);

        if( sta &= 0x40 ){
          md = BM1422_STEP5;
          //Serial.println("Measure OK");
        }
      }
      break;
    case BM1422_STEP5:
      if( timbm1422 == TIME_UP ){
        timbm1422 = TIME_OUT;

        BM1422GetData(DATA_ADRS, mdata, sizeof(mdata));
        MagSet();
        md = BM1422_IDLE;
        timMeas = TIME_BM1422;
      }
      break;
    case BM1422_IDLE:
      if( timMeas == TIME_UP ){
        timMeas = TIME_OFF;
        md = BM1422_STEP3;
      } 
      break;
  }

  if( timled == TIME_UP ){
    timled = TIME_LED;
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
  }

}
/* 磁気センサヌの倀を換算 */
void MagSet(void){
  int16_t x;
  int16_t y;
  int16_t z;

  x = (mdata[1] << 8) + mdata[0];
  y = (mdata[3] << 8) + mdata[2];
  z = (mdata[5] << 8) + mdata[4];

  mag[0] = (float)x * 0.042;
  mag[1] = (float)y * 0.042;
  mag[2] = (float)z * 0.042;

  Serial.print("X:"); Serial.print(mag[0]); Serial.print(","); 
  Serial.print("Y:"); Serial.print(mag[1]); Serial.print(","); 
  Serial.print("Z:"); Serial.print(mag[2]); Serial.println("");

  myfile = SD.open(filepath,FILE_WRITE);

  if( myfile ){ //ファむルが開けたら曞き蟌む
    myfile.print("X:"); myfile.print(mag[0]); myfile.print(","); 
    myfile.print("Y:"); myfile.print(mag[1]); myfile.print(","); 
    myfile.print("Z:"); myfile.print(mag[2]); myfile.println("");
    myfile.close(); //ファむルを閉じる
  }
}

/* コマンド送出 */
bool BM1422Cmd(uint8_t* cmd, uint8_t len){
  bool ret = false;

  Wire.beginTransmission(BM1422_ADRS); //スレヌブが存圚するか確認
  byte error = Wire.endTransmission();
  if( error == 0){ //スレヌブが存圚する堎合䞋の凊理
    Wire.beginTransmission(BM1422_ADRS);

    for(uint8_t i= 0; i < len; i++ ){
      Wire.write(*cmd);
      ++cmd;
    }
  
    Wire.endTransmission(); //ストップ・コンディションの発行
    ret = true;
  }

  return ret;
}
/* デヌタを取埗 */
bool BM1422GetData(uint8_t adr, uint8_t* reg_data, uint16_t len){
  bool  ret = false;

  Wire.beginTransmission(BM1422_ADRS); //スタヌト・コンディションの発行
  Wire.write(adr); //曞き蟌む察象のアドレスをセット(ラむトで指定)
  byte error = Wire.endTransmission();
  if( error == 0){ //スレヌブが存圚する堎合䞋の凊理
    if( Wire.requestFrom(BM1422_ADRS, len) == len ){
      for( uint16_t i=0; i < len; i++ ){
        *reg_data = Wire.read(); //len分だけデヌタをリヌドする
        ++reg_data;
      }
      ret = true;
    }
  }
  return ret;
}

関連リンク

Arduinoのラむブラリを䜿っお動䜜確認を行ったこずを䞋蚘リンクにたずめおいたす。

Arduinoで孊べるマむコンの゜フト開発ず暙準ラむブラリの䜿い方

Seeeduino XIAOで孊べる゜フト開発ず暙準ラむブラリの䜿い方

ESP32-WROOM-32Eで孊べる゜フト開発ず暙準ラむブラリの䜿い方

Raspberry Pi Picoで孊べる゜フト開発

PR倖資系・IT業界などハむクラスの転職に匷い【AXIS Agent】

最埌たで、読んでいただきありがずうございたした。

タむトルずURLをコピヌしたした