/* SDカードロガー、液晶表示付き アナログポート(A0 - 3)の電圧、cpu温度、電源電圧を読んでSDカードに保存。 記録条件は LogConf.txt の先頭4行で タイトル、ログファイル名、記録間隔、データラベル の順で指定する。下記は設定例。5行目以降は無視されるのでメモなどを記入してもOK SD logger test 2017 / 09 / 29 Log00010.csv 3 N, sec, ch0, ch1, ch2, ch3, cpuTemp(C), cpuVcc(V) 2018/10/2 ラジオペンチ http: //radiopench.blog96.fc2.com/ */ #include #include #include // http://n.mtng.org/ele/arduino/i2c.html #include #include const int chipSelect = 4; // SDカードのChip Selectピン const int logSW = 5; // ログ記録スイッチ const int ledPin = 6; // LEDピン unsigned long IC = 0; // インターバルカウンタ unsigned long LC = 0; // 行カウンタ(LineCounter) unsigned long TC = 0; // 秒カウンタ(TimeCounter) unsigned long logInterval; volatile int sampleFlag = 0; String readBuff; // ファイルのラインリードバッファ String logTitle; // ログのタイトル情報 String logFileName; // ログファイル名 String colName; // データー名(先頭行に出力される) String logData; float V[4]; // 電圧測定結果 (ch0〜ch3) float Temp, Vcc; // CPU温度、Vcc測定結果 File myFile; // SD読み書き用 I2CLiquidCrystal lcd(30, true); // コントラスト(0-63),液晶電源(true=5V, false=3.3V) void setup() { pinMode(logSW, INPUT_PULLUP); // 記録スイッチ pinMode(ledPin, OUTPUT); // 動作表示LED pinMode(chipSelect, OUTPUT); // SDのchip selectピン pinMode(A3, INPUT_PULLUP); // フォトトラのコレクタ抵抗代わりにプルアップ Serial.begin(115200); lcd.begin(16, 2); lcd.print("Starting..."); delay(1000); Serial.print("Reading SD card..."); if (!SD.begin(chipSelect)) { Serial.println("SD failed!"); errorStop(); // エラー表示して停止 } digitalWrite(ledPin, HIGH); Serial.println("initialization done."); Serial.println(); readConfig(); // ログ条件設定ファイル読み出し Serial.println("Log settings (LogConf.txt)"); Serial.print("Data title = "); Serial.println(logTitle); Serial.print("File name = "); Serial.println(logFileName); Serial.print("Rec.interval = "); Serial.print(logInterval); Serial.println("sec"); Serial.print("Data lavel = "); Serial.println(colName); lcdDispConf(); // ログ条件を液晶に表示 digitalWrite(ledPin, LOW); delay(1000); } void loop() { digitalWrite(ledPin, LOW); while (digitalRead(logSW) == HIGH) { // 記録スイッチがONになるまで待つ } myFile = SD.open(logFileName, FILE_WRITE); // ログファイルを開く if (myFile == 0) { // エラー処理 Serial.println("Cant open log file."); errorStop(); // LED点滅 } Serial.println(); myFile.println(); // ログパラメーターを出力 Serial.println(logTitle); myFile.println(logTitle); // タイトル Serial.println(colName); myFile.println(colName); // データーラベル myFile.close(); LC = 0; // 行数カウンタ初期化 TC = 0; // タイムカウンタ初期化 IC = 0; // インターバルカウンタ初期化 MsTimer2::set(1000, sensInterval); // 1000ms間隔でMsTimer2割込み MsTimer2::start(); while (digitalRead(logSW) == LOW) { // 記録スイッチがONの間は以下を繰り返し実行 digitalWrite(ledPin, HIGH); while (sampleFlag == 0) { // MsTimer2の割込みが入るまで待つ } sampleFlag = 0; digitalWrite(ledPin, LOW); // 処理中表示のためLEDを消灯 sensData(); // データー取得して記録文字列に変換 lcdDisp1(); // 液晶の1行目表示(記録行数、温度など) lcdDisp2(); // 液晶の2行目表示(データ内容) if (IC == 0) { // インターバルカウンタがゼロだったらデータ書き込み LC++; // 行カウンタをインクリメント File myFile = SD.open(logFileName, FILE_WRITE); // ログファイルを開く if (myFile == 0) { Serial.println("Cant open log file."); errorStop(); } Serial.println(logData); myFile.println(logData); // ファイルにログデーター書き込み myFile.close(); delay(200); // LEDを長めに消灯させるためにちょっと待つ } TC++; // 累積秒数カウンタインクリメント IC++; if (IC == logInterval) { // インターバルカウンタが設定値になっていたら IC = 0; // 次回のためにリセット } digitalWrite(ledPin, HIGH); // 処理が終わったのでLED点灯 } // 記録スイッチONの時のループの末尾 lcdDispConf(); // 液晶をログ条件表示に変更 MsTimer2::stop(); // 記録スイッチがOFFになったので割込みタイマー停止 } void readConfig() { // ログ条件ファイルを読む myFile = SD.open("LogConf.txt", FILE_READ); // 設定ファイルを開く if (myFile) { lineRead(); // 1行目 logTitle = readBuff; lineRead(); // 2行目 logFileName = readBuff; lineRead(); // 3行目 logInterval = readBuff.toInt(); // ログ取得周期 値は整数に変換 lineRead(); // 4行目 colName = readBuff; myFile.close(); } } void lineRead() { // ファイルから1行読んで readBuffに格納する // 行末コード win:0x0D,0x0A unix:0x0A mac:0x0D char s; int x = 0; readBuff = ""; // String型 for (;;) { s = myFile.read(); // ファイルから1文字読む if (s == 0x0A) { // LF(0x0A)なら行末とみなして break; // ループを抜ける } if (s != 0x0D) { // CR(0x0D)以外だったら、つまり0x0Dは無視 readBuff += s; // 文字追加。Stringなので末尾には0x00が自動挿入される x++; } } } void sensData() { // 測定と記録用文字列の作成 int x; float voltage; logData = ""; logData += String(LC, DEC); // 1列目は行番号 logData += ", "; logData += String(TC, DEC); // 2列目は経過時刻(秒) for (int n = 0; n <= 3; n++) { // 0から3の範囲の logData += ", "; // (csvデリミッタ) x = analogRead(n); // アナログポートの値を読んで voltage = 5.0 * x / 1023.0; // 浮動小数点形式の電圧に換算して V[n] = voltage; // 配列に値を記録(液晶表示用) logData += String(voltage, 2); // 文字列に変換して追加(小数点以下2桁まで) } logData += ", "; logData += String(Temp, 1); // 温度(値は1行目の表示で測定済) logData += ", "; logData += String(Vcc, 2); // 電圧 } void lcdDispConf() { lcd.clear(); lcd.print(logTitle.substring(0, 16)); lcd.setCursor(0, 1); lcd.print(logFileName); lcd.print(" "); lcd.print(logInterval % 1000); // 下3桁だけ表示 } void lcdDisp1() { // 1行目の表示(毎秒更新) unsigned long x; x = (logInterval - IC) % 1000; // ログインターバルの残り時間(表示のmaxは999) lcd.setCursor(0, 0); if (x < 100) { // ゼロサプレス処理 lcd.print(" "); } if (x < 10) { lcd.print(" "); } lcd.print(x); lcd.print(" "); // インターバル残り時間表示 x = LC % 100000UL; // 記録行数表示(表示のmaxは99999) if (x < 10000) { // ゼロサプレス処理 lcd.print(" "); } if (x < 1000) { lcd.print(" "); } if (x < 100) { lcd.print(" "); } if (x < 10) { lcd.print(" "); } lcd.print(x); lcd.print("L"); // 記録行数表示 lcd.print(" "); Temp = cpuTemp(); // CPU温度測定 Vcc = cpuVcc(); // CPU電源電圧測定 lcd.print(Temp, 1); lcd.print("c "); // 温度表示する場合 // lcd.print(Vcc, 2); lcd.print("V "); // 電圧表示する場合 } void lcdDisp2() { // 2行目表示(SDでログデータ記録字更新) lcd.setCursor(0, 1); // 2行目の先頭から、 for (int n = 0; n <= 3; n++) { lcd.print(V[n], 1); lcd.print(" "); // 電圧表示 } lcd.setCursor(15, 1); lcd.print("V"); // 末尾にVを表示 } // CPU温度・電圧測定(流用する場合は以下をコピーする) float cpuTemp() { // CPU温度測定関数 long sum = 0; adcSetup(0xC8); // Vref=1.1V, input=ch8 for (int n = 0; n < 100; n++) { sum = sum + adc(); // adcの値を読んで積分 } return (sum * 1.1 / 102.4) - 347.5; // 温度を計算して戻り値にする。-347.5は要調整 } float cpuVcc() { // 電源電圧(AVCC)測定関数 long sum = 0; adcSetup(0x4E); // Vref=AVcc, input=internal1.1V for (int n = 0; n < 10; n++) { sum = sum + adc(); // adcの値を読んで積分 } return (1.1 * 10240.0) / sum; // 電圧を計算して戻り値にする } void adcSetup(byte data) { // ADコンバーターの設定 ADMUX = data; // ADC Multiplexer Select Reg. ADCSRA |= ( 1 << ADEN); // ADC イネーブル ADCSRA |= 0x07; // AD変換クロック CK/128 delay(10); // 安定するまで待つ } unsigned int adc() { // ADCの値を読む unsigned int dL, dH; ADCSRA |= ( 1 << ADSC); // AD変換開始 while (ADCSRA & ( 1 << ADSC) ) { // 変換完了待ち } dL = ADCL; // LSB側読み出し dH = ADCH; // MSB側 return dL | (dH << 8); // 10ビットに合成した値を返す } // CPU温度・電圧測定 ------ ここまでコピー ------------- void errorStop() { // エラー表示して停止 lcd.clear(); lcd.print("Error"); for (;;) { // 無限ループでLED点滅 digitalWrite(ledPin, HIGH); delay(20); digitalWrite(ledPin, LOW); delay(150); } } void sensInterval() { // Mstimet2の割り込み処理 sampleFlag = 1; // サンプリングフラグ立てる }