前回記事であまりにも、ちゃちゃっと済ましてしまったので、もう少し、時間をかけてみた。
簡単にできることは、簡単に済ましてしまえば、良いと思いますが、まぁ、勉強も兼ねて、DHT11のセンサのデータシート片手にプログラムやり直してみました。
ほとんど、自己満足の世界です。
せっかくなので、表示画面ももう少しそれっぽく改良?
画面は、LovyanGFX のライブラリを使って、ライブラリのサンプルのメータを参考にさせていただいています。
DHT11
DHT11は、Arduino用のライブラリもあり、比較的安価なので、広く利用されているようで、ラズパイの例も含めて、使用例のサイトも沢山見かけます。
秋月の販売サイトからデータシートが見れたので、これをもとに自前で通信を試みてみました。
と、言ってもたいしたことではなく、通信ピンのHIGH / LOW の時間をみて、ビットの 0/1 を決めるだけです。
データシートによると、
約20msecの間、マイコン側でLOWにしてやると、センサからレスポンスがあった後、40ビットのデータが返ってきます。
40ビットは、8ビットずつの5つのデータに分けられていて、それぞれ、
湿度データHigh + 湿度データLow + 温度データHigh + 温度データLow + チェックサム
の構成で、データシートには、以下のように
8bit humidity integer data + 8bit humidity decimal data + 8bit temperature integer data + 8bit temperature decimal data + 8bit check bit.
と書いてあったので、Low のデータには、小数部分がくるのかと思ったのですが、
どうも、ここは、常にゼロのようで、このセンサでは、温度も、湿度も、小数部分は、測定できないのではないかと思います。
温度データLowの部分は、氷点下の場合に何か、ビットが立つようなことも書いてありましたが、氷点下のテストは、やっていませんので、定かではありません。
データビットの判定は、High の時間が、23マイクロ秒から27マイクロ秒のときは、ゼロ。
Highの時間が、68マイクロ秒から74マイクロ秒のときが、1 になります。
が、チェックサムエラーが何度か起きたので、調べてみたら、”1” なのに、67マイクロ秒とか、66マイクロ秒のときがあったので、”1” の判定の範囲を少し広めにして、エラーを回避しています。
表示部分
今回もLovyanGFXのライブラリにお世話になっています。
ライブラリのサンプルにメータがあったので、それを参考に(結構コピペしてます)温度、湿度の表示をしてみました。
センサからは、整数値が2つ出てくるだけですが、表示部をそれなりに手間をかけてやると、少ない情報量でもそれっぽくなりますね。
と、言うわけで、以下スケッチです。
#include <LovyanGFX.hpp> static LGFX lcd; static LGFX_Sprite canvas(&lcd); // オフスクリーン描画用バッファ static LGFX_Sprite base(&canvas); // 文字盤パーツ static LGFX_Sprite needle(&canvas); // 針パーツ static auto transpalette = 0; // 透過色パレット番号 const int PIN_DHT = D2; const char c=0xF7; unsigned long p_millis, p_micros; unsigned long dT = 0; int bt = 0; int data[40]; int humid_H, humid_L, temp_H, temp_L, chk; int mp[] = {128, 64, 32, 16, 8, 4, 2, 1}; int tm[90]; void read_DHT11() { pinMode(D2, OUTPUT); digitalWrite(D2,LOW); p_millis = millis(); while((millis() - p_millis)<20) {;} tm[0] = millis()-p_millis; digitalWrite(D2,HIGH); pinMode(D2,INPUT); p_micros=micros(); while(digitalRead(D2) == HIGH) {;} tm[1]=micros()-p_micros; p_micros=micros(); while(digitalRead(D2) == LOW) {;} tm[2]=micros()-p_micros; p_micros=micros(); while(digitalRead(D2) == HIGH) {;} tm[3]=micros()-p_micros; for(bt = 0;bt < 40;bt++){ p_micros=micros(); while(digitalRead(D2) == LOW) {;} tm[bt*2+4]=micros()-p_micros; p_micros = micros(); while(digitalRead(D2) == HIGH) {;} dT = micros() - p_micros; tm[bt*2+5]=dT; if ((dT > 65) and (dT < 75)) { data[bt] = 1; } else { data[bt] = 0; } } while(digitalRead(D2) == LOW) {;} humid_H = 0; humid_L = 0; temp_H = 0; temp_L = 0; chk = 0; for (int i=0; i<8; i++) { humid_H += data[i]*mp[i]; humid_L += data[i+8]*mp[i]; temp_H += data[i+16]*mp[i]; temp_L += data[i+24]*mp[i]; chk += data[i+32]*mp[i]; } Serial.println(humid_H); Serial.println(humid_L); Serial.println(temp_H); Serial.println(temp_L); if ((humid_H + humid_L + temp_H + temp_L) != chk ) Serial.println("Parity Error"); /* for(bt = 0;bt < 40;bt++){ Serial.print(data[bt]); //Serial.print(" / "); } Serial.println(" "); for(bt = 0;bt < 40;bt++){ Serial.print(tm[bt*2+4]); Serial.print(" / "); Serial.print(tm[bt*2+5]); Serial.print(" / "); } Serial.println(" "); */ } void setup() { pinMode(D2, INPUT); Serial.begin(115200); Serial.println("DHT11"); float rad; float ty; float tx; lcd.init(); lcd.setRotation(1); lcd.setBrightness(128); lcd.setColorDepth(16); lcd.clear(); lcd.setPivot(159, 119); lcd.setColorDepth(24); for (int i = 0; i < 180; i+=2) { // 外周を描画する lcd.setColor(lcd.color888(i*1.4,i*1.4+2,i*1.4+4)); lcd.fillArc(160, 120, 120 , 120-3, 90+i, 92+i); lcd.fillArc(160, 120, 120 , 120-3, 88-i, 90-i); } for (int i = 0; i < 180; i+=2) { // 外周を描画する lcd.setColor(lcd.color888(i*1.4,i*1.4+2,i*1.4+4)); lcd.fillArc(160, 120, 120-4, 120-7, 270+i, 272+i); lcd.fillArc(160, 120, 120-4, 120-7, 268-i, 270-i); } lcd.setColorDepth(16); canvas.setColorDepth(lgfx::palette_4bit); base .setColorDepth(lgfx::palette_4bit); needle.setColorDepth(lgfx::palette_2bit); canvas.createSprite(240, 240); // メモリ確保 base .createSprite(240, 240); needle.createSprite(11, 109); //base.setFont(&fonts::Orbitron_Light_24); base.setTextSize(1.5); base.setTextDatum(lgfx::middle_center); base.fillCircle(120, 120, 120 - 8, 1); base.drawCircle(120, 120, 120 - 10, 3); base.setTextColor(4); for (int i=10; i<100; i+=10) { //湿度計部分 if ( (i%10) == 0 ) { base.fillArc(120, 120, 120 - 10, 120 - 24, -0.2 + i*1.8, 0.2 + i*1.8, 3); } rad = i * 1.8 * 0.0174532925; ty = sin(rad) * (120 * 10 / 15); tx = cos(rad) * (120 * 10 / 17); base.drawNumber(i, 120 + tx, 120 + ty); // 数値描画 } for (int i=0; i<51; i+=5) { //温度計部分 base.fillArc(120, 120, 120 - 10, 120 - 24, 195-0.2 + i*3, 195+0.2 + i*3, 3); if ( (i%10) == 0 ) { rad = (i+5) * 3 * 0.0174532925; ty = -sin(rad) * (125 * 10 / 15); tx = -cos(rad) * (125 * 10 / 15); base.drawNumber(i, 120 + tx, 120 + ty); // 数値描画 } } needle.setPivot(5, 100); // 針パーツの回転中心座標を設定する needle.fillRect(3, 20, 5, 80, 2); needle.fillCircle(5, 100, 4, 1); needle.drawCircle(5, 100, 4, 3); canvas.setPaletteColor(1, 0, 0, 15); canvas.setPaletteColor(2, 255, 31, 31); canvas.setPaletteColor(3, 255, 255, 191); canvas.setPaletteColor(4, 255, 255, 0); canvas.setPaletteColor(5, 0, 0, 255); draw(0,0); } void draw(int value_H,int value_T) { float angle_H = 90+1.8*value_H; float angle_T = 285+3*value_T; base.setTextSize(2.5); base.fillRect(80,75,80,40,1); base.drawString(String(value_T)+c+'C',120,90); base.fillRect(80,135,80,40,1); base.drawString(String(value_H)+'%',120,150); base.fillArc(120,120,109,100,0,180,1); for (int i=10; i<100; i+=10) { base.fillArc(120, 120, 120 - 10, 120 - 24, -0.2 + i*1.8, 0.2 + i*1.8, 3); } base.fillArc(120,120,109,100,0,1.8*value_H,5); base.pushSprite(0, 0); // 描画用バッファに盤の画像を上書き //Serial.println(angle_H); //needle.pushRotateZoom( angle_H, 1.0, 1.0, transpalette); // 針をバッファに描画する //Serial.println(angle_T); needle.pushRotateZoom( angle_T, 1.0, 1.0, transpalette); // 針をバッファに描画する canvas.pushRotateZoom(0, 1.0, 1.0, transpalette); // 完了した盤をLCDに描画する } void loop() { delay(3000); read_DHT11(); draw(humid_H,temp_H); }