SDカード利用(2)
今回は温度湿度センサーDHT11を例にして、一定時間ごとの「データ自動蓄積」をやってみます。見た目がアレですけどとりあえず必要なモジュールをつなげてみました。
配線について
(VCC,GNDは省略:それぞれ+5VとGND)
・温度湿度センサー DHT11
信号線を ANALOGの0に接続
・SDカードモジュール
MOSI(SDO) pin11、MISO(SDI) pin12、SCK(CLK)pin13、SS(CS) pin10
・RTCモジュール DS1302
CLK(SCLK) pin6、DAT(IO)pin7、RST(CE)pin8
・あと、カードへの書き込み中に電源を切ったりしてしまわないようにアクセスランプ用のLED一つつけてあります。pin 3
なお、SDカードモジュールについては前回記事
RTCモジュールについては過去記事
も参照してください。
SDカードへのデータ保存
SDカードへのデータの保存は、Arduino公式サイトから
https://www.arduino.cc/en/Tutorial/LibraryExamples/Datalogger
のサンプルコードを見て概要把握しました。
CSにつないだピン番号を
const int chipSelect = 10;
で指定。Setupで
SD.begin(chipSelect);
して、loop関数内で、データ文字列String dataString に保存したい文字列を設定。保存したいファイル名を指定してopen
File dataFile = SD.open("datalog.txt", FILE_WRITE);
if (dataFile) {
dataFile.println(dataString);
dataFile.close();
}
これを一定時間ごとに繰り返していけばよいようです。
温度湿度センサー(DHT11)
ライブラリマネージャから「DHTStable」をインストール。
#include <dht.h>
#define dht_pin A0 //ピン
dht DHT;
/*
あとは読み出すところで
DHT.read11(dht_pin);
とやれば
DHT.humidity で湿度
DHT.temperature で温度が取り出せるようです。
*/
統合
下記のコードでとりあえず目的通りの動作をさせることができました。SDカードのアクセス中に触らないよう、LEDを点けて5秒後に書き込み開始、書き込み終了後LEDを消灯して、25秒待つ、つまり、約30秒に一回時刻と温湿度を記録するようにしました。抜く瞬間にLEDが点灯しても5秒は余裕があるので緊張せずにカードを抜けます。
//SDCard
#include <SPI.h>
#include <SD.h>
const int chipSelect = 10;
//DHT11
#include <dht.h>
#define dht_pin A0
dht DHT;
//LED
#define LED 3
//RTC
#define SCLK 6 //(基板上CLK⇒6番ピン)
#define IO 7 //(基板上DAT⇒7番ピン)
#define CE 8 //(基板上RST⇒8番ピン)
//RTC用構造体
struct DateTime{
int Seconds;
boolean CH; //1でClockHalt(停止)だが、0を入れてこの機能使わない前提
int Minutes;
int Hour;
boolean Mode12; //1で12時間表示だが、0を入れて24時間表示で使う前提
int Date;
int Month;
int Day;
int Year;
boolean WP; //1でWriteProtectだが0を入れてこの機能使わない前提
};
//記録用文字列
String timestamp;
String temphumid;
void setup() {
//シリアル通信準備
//Serial.begin(9600);
pinMode(CE, OUTPUT);
pinMode(SCLK, OUTPUT);
//SDカード
SD.begin(chipSelect);
};
void loop() {
//5秒余裕をもたせてアクセスランプを付ける
digitalWrite(LED,HIGH);
delay(5000);
settimestamp();
//Serial.print(timestamp);Serial.print(",");
settemphumid();
//Serial.println(temphumid);
File dataFile = SD.open("datalog.txt", FILE_WRITE);
if (dataFile) {
dataFile.print(timestamp);
dataFile.print(",");
dataFile.println(temphumid);
dataFile.close();
}
digitalWrite(LED,LOW);
delay(25000);//25秒(25000ミリ秒)待ち。
}
//dht11用コード
void settemphumid(){
DHT.read11(dht_pin);
temphumid=String(DHT.temperature)+","+String(DHT.humidity);
}
/////////////////////////////
//以下はRTC操作用のコード
/////////////////////////////
void settimestamp(){
byte RTC_clock_image[8];//クロックデータの読み込み用。1バイト×8の配列
DateTime myDateTime;
read_rtc_byte(RTC_clock_image);//読みだして格納
extract_datetime(RTC_clock_image,myDateTime);
timestamp =String(myDateTime.Year)+","+String(myDateTime.Month)+",";
timestamp += String(myDateTime.Date)+","+String(myDateTime.Hour)+":";
timestamp += String(myDateTime.Minutes)+":"+String(myDateTime.Seconds);
}
//RTCからクロックデータをまるごと読みだし、
byte read_rtc_byte(byte image[]){
pinMode(IO, OUTPUT);//IOのpinmodeをまず送信用で準備
digitalWrite(CE, LOW);wait();//最初はCE下げた状態
digitalWrite(CE, HIGH);wait();//開始(CE 上げ)
write_1byte(0xbf);//バーストモードのコマンド0xbf送信
//読み出しに移るのでIOのピンモード切り替える。SCLK上げのまま
pinMode(IO, INPUT);
//8byte分続けて読みbyte配列のimageに入れる
for (int i=0; i<=7; i++){
read_1byte(image[i]);
}
//CE下げる
digitalWrite(CE, LOW); wait();
}
void write_1byte(byte data){
//コマンド等送信 1ビットずつ繰り返し8ビット(1バイト)分送る
for (int i=0; i <= 7; i++){
digitalWrite(SCLK, LOW);wait(); //LOW確認
digitalWrite( IO, bitRead(data, i));wait();
digitalWrite(SCLK, HIGH);wait(); //クロック(LOWからHIGH)で書き込む
//クロック上げたままで関数を抜ける。
}
}
void read_1byte(byte &data){
//データ受信 1ビットずつ繰り返し8ビット(1バイト)分読みだす
data = 0;
for (int i=0; i<=7; i++){
digitalWrite(SCLK, HIGH);wait();//clock上げ。(上げたまま引き継いだ時はそのまま)
digitalWrite(SCLK, LOW);wait();//clock下げて、
bitWrite( data,i, digitalRead(IO));//読む
//クロック下げた状態で関数を抜ける
}
}
void setImage(byte command, byte image){ //コマンド、書き込みデータ
pinMode(IO, OUTPUT);//IOのpinmodeを送信用で準備
digitalWrite(CE, LOW);wait();//最初はCE下げた状態
digitalWrite(CE, HIGH);wait();//開始(CE 上げ)
write_1byte(command);
write_1byte(image);
digitalWrite(SCLK, LOW);wait();//clock下げて、
digitalWrite(CE, LOW);wait();//最後はCE下げる
}
void getImage(byte command, byte &image){
pinMode(IO, OUTPUT);//IOのpinmodeを送信用で準備
digitalWrite(CE, LOW);wait();//最初はCE下げた状態
digitalWrite(CE, HIGH);wait();//開始(CE 上げ)
write_1byte(command);
pinMode(IO, INPUT);//IOのpinmodeを受信用に切り替え
read_1byte(image);
digitalWrite(CE, LOW);wait();//最後はCE下げる
}
void wait(){
//Vcc5Vなのでデータシート11ページから待ち時間は1μ秒分とっておけばよい。
delayMicroseconds(4);
}
//byte配列からの情報抽出
void extract_datetime(byte image[], DateTime &TempDateTime){
int data10, data1;
//秒
data10 = (image[0]>>4)&7; //上位4ビットを0111でマスクする。
data1 = image[0] & 15; //下位4ビットを1111でマスクする。
TempDateTime.Seconds = (data10 * 10) + data1;
TempDateTime.CH = image[0]>>7;
//分
data10 = (image[1]>>4)&7; //上位4ビットを0111でマスクする。
data1 = image[1] & 15; //下位4ビットを1111でマスクする。
TempDateTime.Minutes = (data10 * 10) + data1;
//時
data10 = (image[2]>>4)&3; //上位4ビットを0011でマスクする。
data1 = image[2] & 15; //下位4ビットを1111でマスクする。
TempDateTime.Hour = (data10 * 10) + data1;
TempDateTime.Mode12 = image[2]>>7; //12時間表示モード。0にして使わない。
//日
data10 = (image[3]>>4)&3; //上位4ビットを0011でマスクする。
data1 = image[3] & 15; //下位4ビットを1111でマスクする。
TempDateTime.Date = (data10 * 10) + data1;
//月
data10 = (image[4]>>4)&1; //上位4ビットを0001でマスクする。
data1 = image[4] & 15; //下位4ビットを1111でマスクする。
TempDateTime.Month = (data10 * 10) + data1;
//曜日
TempDateTime.Day =image[5];
//年
data10 = (image[6]>>4) & 15; //上位4ビットを1111でマスクする。
data1 = image[6] & 15; //下位4ビットを1111でマスクする。
TempDateTime.Year = (data10 * 10) + data1 + 2000;
//WP
TempDateTime.WP =image[7]>>7;
}
動作外観
こんな感じで動いています。終了時はアクセスランプが消えているのを見計らってカードを抜けばよいでしょう。
測定例
温度湿度の測定を試してみました。
2021,1,10,22:32:28,21.00,34.00
2021,1,10,22:32:58,21.00,33.00
2021,1,10,22:33:28,21.00,34.00
2021,1,10,22:33:58,21.00,34.00
2021,1,10,22:34:28,21.00,34.00
2021,1,10,22:34:58,21.00,34.00
:
(中略)
:
2021,1,11,16:24:5,20.00,35.00
2021,1,11,16:24:35,19.00,35.00
2021,1,11,16:25:5,20.00,35.00
2021,1,11,16:25:36,19.00,35.00
2021年1月10日の22時32分28秒から開始し、約30秒ずつ測定を繰り返し、2021年1月11日16時25分36秒まで続けた、という次第。全2132点のデータが取れていました。これをグラフにしてみると
あまり面白くないグラフですが・・・一応データ蓄積はできていました。深夜は気温が下がって、湿度は反対にちょっと上がっているようです。湿度は一定周期で異常値も出ていますね。朝方、急に気温が上がっているのは暖房付けたからです。
問題点
まあ、出来たところを書いたので、いかにも問題なく動いているみたいですが、正直に言いますと、何度も試行錯誤しました。どうも不安定で気持ちの悪い部分がありました。
・長時間測定後、データとれていると思って、開けてみたら何にも入っていなかったり・・
・ファイルが入っていたものの文字化けして、結局読めなかったり・・
なんてこともたびたび。
今のところ上手くいったのは、
・PCのUSB端子から給電し
・SDカードをモジュールに刺してすぐArduinoUNOを本体のリセットボタンを押して再起動している場合
です。PCから切り離してモバイルバッテリーにつないでUNO単独でデータロギング、というのをめざしているんですが、なぜかこれだと、SDにデータファイルが作成されないんですよね。このままでは運頼みで、実用にならないので、今後さらに検討してみたいと思います。
小学生の夏休みの自由研究か何かで長期の温湿度測定をやって、最終日にデータをグラフにしようかとしたら、ファイルが無かった・・なんてことになっては悲劇です(^_^;) どうにも中途半端な内容ですみません。