WioTerminal と DHT11 で温湿度計 その2

前回記事であまりにも、ちゃちゃっと済ましてしまったので、もう少し、時間をかけてみた。
f:id:manpukukoji:20200821161526j:plain
簡単にできることは、簡単に済ましてしまえば、良いと思いますが、まぁ、勉強も兼ねて、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);

}