チャーハンノート

チャーハンの作り方に関する覚書

データロガーの作成 その2

f:id:friedrice_mushroom:20200502173130p:plain:w600
※自作のデータロガーでモバイルバッテリーの性能評価するの図

Arduino互換マイコンでまたデータロガーを作りました。USBチェッカーのロガー付き版という位置づけです。
過去に作ったものは測定対象の電圧を1秒毎に読み取って、SDカードに記録していくものでした。これでゲームボーイの動作時間を測ったりしました。
今回は測定対象をモバイルバッテリーに特化し、新たに電流モニタとRTC(Real Time Clock)を追加して詳細なデータを有用に活用できるようにしました。

要件

作りたかったのは下記のようなもの。

  • 1秒ごとに測定と記録を行う
  • 測定するのは対象の電圧・電流(電力)、電力容量(mWh)
  • 測定電圧は5〜20V、電力は30W程度
  • RTC(Real Time Clock)を内蔵し、測定データやファイル名と紐付く
  • 時刻や測定値、ロガーの動作状況をディスプレイで確認できる
  • 測定可能な時間の制限がない(SDカードの容量が足る限り)
  • コンパクト
  • 低消費電力(モバイルバッテリーで2,3日動作)

主要な使用部品一覧

Arduino(IDE)で使用するライブラリ

必要に応じてArduinoのライブラリマネジャーからインストールしました。

  • Adafruit_SSD1306.h (OLEDディスプレイ使用のため)
    f:id:friedrice_mushroom:20200502163918p:plain:w600
    注意事項として、このライブラリは使用するディスプレイの解像度に応じてライブラリ本体の修正が必要です。今回使用するディスプレイは128x64pxなので、Adafruit_SSD1306.hをエディタで開き下記の選択箇所のコメントを解除し、上書き保存する必要があります。 f:id:friedrice_mushroom:20200502164024p:plain:w600

  • SdFat.h (SDカードモジュール用)
    f:id:friedrice_mushroom:20200502164046p:plain:w600
    SdFat - Arduino Libraries
    SDカードをArduinoで扱うのに一般的なのはSD.hのほうだと思いますが、SDfat.hというものがあり、自分はもっぱらこちらを使用しています。long file nameをサポートしておりファイル名の自由度が高いことが決め手でした。

  • SPI.h (SPI通信用。SdFat.hとセットで必要)
    (なぜかライブラリマネジャーからは見られませんでした)Arduino - SPI

  • RTClib.h (RTCモジュール用)
    f:id:friedrice_mushroom:20200502164141p:plain:w600
    いろんな類似ライブラリがあり、たまたま見つけたものです。

  • Adafruit_INA219.h (電流センサモジュール用)
    f:id:friedrice_mushroom:20200502164715p:plain:w600

フローチャート

データロガーの動作に関して、フローチャートはざっくりと下記の内容です。
記録スイッチをオンにした初回動作として、保存ファイル名の定義(YYYY_MMDD_HHMM_SS.txtの形式)と積算の電力容量や記録カウントをリセットします。
それ以降は1秒経過するごとにセンサ値の読み込みとSDカードへの記録を行います。

f:id:friedrice_mushroom:20200502164740p:plain:w500

回路図

回路図は下記です。配線が太いところは大電流が流れることを想定し、なるべく太い配線を使用しようという意図です。
SDカードへの記録開始/停止を制御するためのスイッチ(logging SW)を設けています。D7がHIGHかLOWかで記録開始/停止を制御するためです。
f:id:friedrice_mushroom:20200502164825p:plain:w600

スケッチ

Arduinoに書き込むスケッチです。

// OLEDディスプレイ使用
#include "Adafruit_SSD1306.h"
#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);

// SDカード用
#include "SPI.h"
#include "SdFat.h"
SdFat SD;
File loggingdatafile; //SDカード認識
const int chipSelect = 10; //SPIのCS端子を定義(D10)

//RTCモジュール用
#define BUFF_MAX 128
#include "RTClib.h"
RTC_DS3231 rtc;

//電力モニタ(INA219)モジュール用
#include "Wire.h"
#include "Adafruit_INA219.h"
Adafruit_INA219 ina219;


// -----------------------------------------------
void setup() {

  //SDカード用
  SD.begin(chipSelect); // SPIのCS端子(D10)

  //OLEDディスプレイ セットアップ
  // ライブラリのAdafruit_SSD1306.hにて、"#define SSD1306_128_64"がコメント解除されているか確認
  //OLEDディスプレイのI2CアドレスはI2CScannerで調べておく
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C); 
  display.display();
  delay(1000); 
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.clearDisplay();

  //RTC用
  Wire.begin();
  // RTCの時刻設定が必要なとき、下記のコメントを解除してコンパイル実行
  // ※ボードに書き込むときにはコメントアウトすること!
//  rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); 

  //電力モニタ用
  ina219.begin();
}

// -----------------------------------------------
void loop() {
  
  boolean logging_sw; // 記録スイッチの状態
  static boolean logging_state = false; //データロガー動作状態(true/false)
  static unsigned int count=0; // ロギング開始からの経過時間(秒)

  // RTCの時刻を読み取る
  DateTime now = rtc.now();

  //RTCの秒だけ読み込むバッファ(秒単位でdelayを解除させるために使用)
  int t_sec=now.second();
  static int t_sec_past; 
  float temp; // 温度用(RTCモジュール内蔵)

  
  if (t_sec_past == t_sec){delay(100);}
  // t_sec_past == t_secではなくなったとき1秒経過したとみなしてdelayを解除、下記を実行する
  else{
    
    //最新の時刻(秒)を読み込む
    t_sec_past=t_sec;
    
    // RTCモジュールから読み取った年月日と時刻を記録するバッファ
    char date_buff[BUFF_MAX];
    char time_buff[BUFF_MAX];
    static char filename[BUFF_MAX];
    
    // OLEDに表示する年月日と時刻を一続きの文字列として読み込む
    snprintf(date_buff, BUFF_MAX, "%d/%02d/%02d", now.year(),now.month(), now.day());               
    snprintf(time_buff, BUFF_MAX, "%02d:%02d:%02d", now.hour(), now.minute(), now.second());
  
    // 電力モニタから読み取った値を格納
    float shuntvoltage = ina219.getShuntVoltage_mV();
    float busvoltage = ina219.getBusVoltage_V();
    float current_mA = ina219.getCurrent_mA();
    float power_mW = ina219.getPower_mW();
    float loadvoltage = busvoltage + (shuntvoltage / 1000);
    float loadresistance = loadvoltage / current_mA *1000;
    static float capacity = 0; //電力容量(mWh)を記録する用(前回Loop時の値を蓄積するためstaticで宣言)
    
// OLEDディスプレイ操作関連-----------------------------------------------
    display.clearDisplay();

    // 日付を表示
    display.setCursor(0,0);
    display.print(date_buff);display.print(" ");display.print(time_buff);

    // 電力モニタで読み取った値を表示
    // loadvoltage
    display.setCursor(0,16);    
    display.print("V:");
    display.print(loadvoltage,2);
    display.print(" V");

    // current_mA
    display.setCursor(64,16);    
    display.print("I:");
    display.print(current_mA,1);
    display.print("mA");

    // power_mW 
    display.setCursor(0,24);    
    display.print("P:");
    display.print(power_mW,0);
    display.print("mW");
    
    // 温度を表示(RTCモジュール内蔵)
    temp=rtc.getTemperature();        
    display.setCursor(64,24);    
    display.print("T:");
    display.print(temp,1);
    display.print(" C");

    // SDカード記録開始時からのカウント(秒数)を表示
    // (オマケで経過日数と経過時間も続けて表示("0h0d"))
    display.setCursor(0,48);    
    display.print("Count:");
    display.print(count);
    display.print(" ");
    display.print(count/86400);
    display.print("d");
    display.print(count%86400/3600);
    display.print("h");

    // SDカード記録開始時からの放電容量(mWh)を積算して表示
    // 見やすいように1000mWh以上は桁区切りするようにした
    display.setCursor(0,56);    
    display.print("Capacity:");
    if(capacity > 1000){
      display.print(capacity/1000,0);
      display.print(",");
      display.print((int)capacity%1000/100);
      display.print((int)capacity%100/10);
      display.print((int)capacity%10);

    }
    else{
      display.print(capacity,1);
    }
    display.print("mWh");
    
    display.display();

// ロガーのステータス制御とSDカード記録関連--------------------------------------------

    // ロガーのステータス(Start, Logging, Stop)をディスプレイに表示する位置
    display.setCursor(0,36);

    logging_sw = digitalRead(7); //D7ピンの値を読み込む

    // 記録スイッチ(logging_sw)"ON"のとき
    if(logging_sw == HIGH){

      // 記録状態(logging_state) 初回はfalseのため、
      // 以下の処理を実行したあとにtrueに書き換える。
      if(logging_state == false){
        display.print("Start");// 初回だけ"Start"表示
        
        // カウントと容量値のリセット(0にする)
        capacity = 0;
        count = 0;

        // 保存ファイル名もここで定義する
        // 例 --> 2020_0423_0846_06.txt (2020年4月23日8:46 06秒のとき)        
        snprintf(filename, BUFF_MAX, "%d_%02d%02d_%02d%02d_%02d.txt", now.year(),
               now.month(), now.day(), now.hour(), now.minute(), now.second());

        // ログデータ(CSV)の項目名の生成
        // timestampのファイル作成日時もここで付与する
        loggingdatafile = SD.open(filename, FILE_WRITE);
        //SDカードが読み込めたとき --> SDカードへ書き込み開始
        if (loggingdatafile) {
          loggingdatafile.println("Date,Time,Count,Load Voltage_V,Current_mA,Power_mW,Capacity_mWh,Temp_C,Bus Voltage_V,Shunt Voltage_mV");
          loggingdatafile.timestamp(T_CREATE, now.year(),now.month(), now.day(), now.hour(), now.minute(), now.second());
          loggingdatafile.close();          
        }
      logging_state=true;
        
      // 記録状態(logging_state) true ⇒ "Logging"と表示
      }else{display.print("Logging ");}

    //
    loggingdatafile = SD.open(filename, FILE_WRITE);
      //SDカードが読み込めたとき ⇒ SDカードへ書き込み開始
      if (loggingdatafile) {
        
        // 書き込み前にLED点灯
        digitalWrite(30,LOW);

        count+=1;
        capacity = capacity + power_mW/3600;
        
        // "Date,Time,Count,Load Voltage_V,Current_mA,Power_mW,Capacity_mWh,Temp_C,Bus Voltage_V,Shunt Voltage_mV"
        loggingdatafile.print(date_buff);loggingdatafile.print(",");
        loggingdatafile.print(time_buff);loggingdatafile.print(",");
        loggingdatafile.print(count);loggingdatafile.print(",");
        loggingdatafile.print(loadvoltage);loggingdatafile.print(",");
        loggingdatafile.print(current_mA);loggingdatafile.print(",");
        loggingdatafile.print(power_mW);loggingdatafile.print(",");
        loggingdatafile.print(capacity);loggingdatafile.print(",");
        loggingdatafile.print(temp);loggingdatafile.print(",");
        loggingdatafile.print(busvoltage);loggingdatafile.print(",");
        loggingdatafile.print(shuntvoltage);
        loggingdatafile.println();
        loggingdatafile.timestamp(T_WRITE, now.year(),now.month(), now.day(), now.hour(), now.minute(), now.second());
        loggingdatafile.close();

        // 書き込み後にLED消灯"
        delay(50);
        digitalWrite(30,HIGH);

    //SetupでSDカードが読み込めなかったとき ⇒ "ERROR"と表示
      }else{display.print(" ERROR");}


    // 記録スイッチ(logging_sw)"OFF"のとき
    }else{
      // 記録状態(logging_state)をfalseに、さらに"stop"と表示
      logging_state=false;
      display.print("Stop");
    }
  }

  // loopの最後、OLEDディスプレイの更新を指示
  display.display();
}

動作確認

ブレッドボードに組んだ様子が下の写真です。
記録スイッチは省略していて、D7の配線を手動でGND←→VCC(3.3V)を切り替えます。
USB入力側にはてきとうなUSB電源を延長ケーブルで接続し、USB出力側には以前アマゾンで購入した電子負荷を接続しました。
f:id:friedrice_mushroom:20200502164943p:plain:w400
電子負荷はこんなやつです。
Amazon.co.jp: アマチュアUSBポートのための調整可能な定電流電子負荷 モバイルバッテリのための直流3.7-13V老化テスター放電 電気テスト計器 連続15W電気放電USB ファン付き放電器: 産業・研究開発用品

動作チェックです。
待機状態のディスプレイ表示内容です。”stop”と表示されています。
f:id:friedrice_mushroom:20200502165027p:plain:w300

こちらがSDカード記録中の表示です。
”Logging”と表示されているのと、記録開始してからのカウント(積算秒数)と積算電力容量がmWhで表示されています。
f:id:friedrice_mushroom:20200502165102p:plain:w300

SDカードにも正常にデータが保存されていることがわかったので、動作確認としてはOKです。
最後に、それぞれのモジュールをユニバーサル基板に実装しなおしました。裏側の配線はとてもお見せできません。 f:id:friedrice_mushroom:20200502165203p:plain:w600

基板に実装したときに新たに追加したのは下記の4つです。
抜き差しの時のマイコン側への負荷を軽減させるため電源用のUSB端子を設けてON/OFFスイッチを追加しました。デバッグするときだけマイコン側のUSBに接続するイメージです。USB端子はマルツで購入しました。
USBコネクター変換基板 CK-37|電子部品・半導体通販のマルツ

SDカードへの記録を制御するスイッチとしてオルタネイト型のPushボタンをつけました。これは秋月で購入しました。
基板用押ボタンスイッチ(黒・オルタネート) PS-70S: パーツ一般 秋月電子通商-電子部品・ネット通販

Resetボタンはマイコンにあるのと同機能です。スペースが空いたのでつけただけです。 f:id:friedrice_mushroom:20200502165245p:plain:w600

ここまでがデータロガー(USBチェッカー)を作成したことのまとめです。
これを使って実際にモバイルバッテリーの充放電時の容量を測定してみたのですが(これが本題だった)、長くなってしまったので次回です・・・。