DHT11 から、温度と湿度の測定値を読み出すことができましたので、温湿度計を作ってみることにしましょう。
測定値は、温度と湿度をそれぞれ 2 桁の 7 セグメント LED 表示器に表示させることにします。
温度の測定範囲が -20〜60℃ ですが、室温が氷点下になることはまずないので、最低値は 0℃ にして、マイナス表示はしません。湿度は 5〜95%RH なので、問題ないです。
回路図
回路図です。なんだか、見慣れた回路図、になってきちゃいました。
今回も LED ドライバ IC TM1630 を使って 7 セグメント LED を駆動します。TM1630 は 4 桁表示モードですが、2 桁ずつを個別に表示させます。そんな使い方も、簡単にできちゃうんですね。そのあたりはスケッチの解説で。
ちなみに、DHT11 の電源は Arduino の 5V 出力を使っています。TM1630 はわりとノイズが出ますので、影響を避けるためです。
スケッチ
DHT11 からデータを読み出すスケッチと、TM1630 を駆動するスケッチを並べただけです。詳細は、これまでの記事を参照して下さい。
なお、シリアル通信の部分はデータの確認用ですので、取っ払っちゃってかまいません。
- // Temperature Humidity Meter 2022.05.31 by meyon
- // DHT11 Definition
- const byte pinDHT11 = 7;
- // 7-SD Driver TM1630 Definition
- #define DISPLAY_MODE 0x00
- #define WRITE_REG_AUTO_ADDR 0x40
- #define SET_DISPLAY_ADDR 0xC0
- #define DISPLAY_ON 0x88 | 0x84
- #define STB_ENABLE LOW
- #define STB_DISABLE HIGH
- #define DOT_POSITION -1
- const byte dioPin = 10;
- const byte clkPin = 11;
- const byte stbPin = 12;
- const byte numberOfDigits = 2;
- byte gridData[numberOfDigits] = {0};
- const byte digit[] = {
- 0b01111110, // 0
- 0b00001100, // 1
- 0b10110110, // 2
- 0b10011110, // 3
- 0b11001100, // 4
- 0b11011010, // 5
- 0b11111010, // 6
- 0b01001110, // 7
- 0b11111110, // 8
- 0b11011110, // 9
- 0b00000000, // 10:blank
- 0b00100000, // 11:dot
- };
- const byte blank = 10;
- const byte dot = 11;
- void setup() {
- // 7-SD Driver TM1630 Setup
- pinMode(dioPin, OUTPUT);
- pinMode(clkPin, OUTPUT);
- pinMode(stbPin, OUTPUT);
- Serial.begin(9600);
- }
- // Read Data from DHT11
- void readDHT11(byte *temp, byte *hmd) {
- const int timeout = 500; // us
- byte startResponse = 0;
- byte detectedValue[5] = {0};
- byte checkDigit = 0;
- pinMode(pinDHT11, OUTPUT);
- delay(20);
- pinMode(pinDHT11, INPUT);
- startResponse = pulseIn(pinDHT11, HIGH, timeout);
- if(startResponse) {
- for(byte i=0; i<5; i++) {
- for(byte j=0; j<8; j++) {
- byte duration = pulseIn(pinDHT11, HIGH, timeout);
- detectedValue[i] *= 2;
- if(54 < duration) detectedValue[i] |= 1;
- }
- }
- for(byte i = 0; i < 4; i++) {
- checkDigit += detectedValue[i];
- }
- if((detectedValue[4] == checkDigit) && checkDigit) {
- *hmd = detectedValue[0];
- *temp = detectedValue[2] + (0x04 < detectedValue[3] ? 1 : 0);
- if(0x7F < detectedValue[3]) *temp = 0;
- }
- for(byte i=0; i<5; i++) {
- Serial.print(detectedValue[i]);
- Serial.print(" ");
- }
- }
- }
- // 7-Segment Display Drive
- void displayNumbers(int n, byte addr) {
- for (byte i = 0; i < numberOfDigits; i++) {
- int exponentialInDecimal = pow(10, i) + 0.5;
- bool zeroSuppression = (0 != i) && (DOT_POSITION < i) && (exponentialInDecimal > n);
- gridData[i] = zeroSuppression ? blank : n / exponentialInDecimal % 10 ;
- }
- digitalWrite(stbPin, STB_ENABLE);
- shiftOut(dioPin, clkPin, LSBFIRST, DISPLAY_MODE);
- digitalWrite(stbPin, STB_DISABLE);
- digitalWrite(stbPin, STB_ENABLE);
- shiftOut(dioPin, clkPin, LSBFIRST, WRITE_REG_AUTO_ADDR);
- digitalWrite(stbPin, STB_DISABLE);
- digitalWrite(stbPin,STB_ENABLE);
- shiftOut(dioPin, clkPin, LSBFIRST, SET_DISPLAY_ADDR | addr);
- for (byte i = 0; i < numberOfDigits; i++) {
- shiftOut(dioPin, clkPin, LSBFIRST, digit[gridData[i]]);
- shiftOut(dioPin, clkPin, LSBFIRST, DOT_POSITION == i ? digit[dot] : 0);
- }
- digitalWrite(stbPin, STB_DISABLE);
- digitalWrite(stbPin, STB_ENABLE);
- shiftOut(dioPin, clkPin, LSBFIRST, DISPLAY_ON);
- digitalWrite(stbPin, STB_DISABLE);
- }
- void loop() {
- static const int interval = 2500; // ms
- static unsigned long previousMillis = 0;
- static byte humidity = 0;
- static byte temperature = 0;
- if(interval < millis() - previousMillis) {
- readDHT11(&temperature, &humidity);
- displayNumbers(temperature, 0x04);
- displayNumbers(humidity, 0x00);
- Serial.print("/ ");
- Serial.print(temperature);
- Serial.print("C ");
- Serial.print(humidity);
- Serial.print("%\n");
- previousMillis = millis();
- }
- }
4 桁表示モードの TM1630 で、温度と湿度をそれぞれ 2 桁ずつ表示させるための変更点だけを記しておきます。
7 セグメント LED の何桁目に表示させるかは、左の表の表示レジスタアドレスによります。
1〜4 桁目は GRID1〜GRID4 に対応しています。
温度は GRID3 と GRID4 に、湿度は GRID1 と GRID2 に表示させることにしましょう。
アドレスはデータの数だけ自動インクリメントするようにしていますので、最初のアドレスだけを指定すればよいです。したがって、温度は 04H 、湿度は 00H ということになります。
- shiftOut(dioPin, clkPin, LSBFIRST, SET_DISPLAY_ADDR | addr);
106 行目。表示レジスタのアドレスを指定するコマンドです。
アドレス指定コマンド SET_DISPLAY_ADDR の値は 0xC0 です。これにアドレスとして、温度は 0x04 を、湿度は 0x00 を加えて、アドレスの指定を行ないます。
- displayNumbers(temperature, 0x04);
- displayNumbers(humidity, 0x00);
アドレスは引数として渡すことにします。
- const byte numberOfDigits = 2;
もう一点。19 行目の表示桁数を 2 に変更します。
以上で、2 桁ずつ独立した表示が可能になりました。
ブレッドボード
毎度おなじみの、ブレッドボードです。
一番上はいつもの電源。5V と 9V を供給しています。
7 セグメント LED の 4 桁を常に表示しているので、5V のレギュレータが少し暖かくなります。
次が 7 セグメント LED と ドライバ IC TM1630 。
4 桁の LED をそのまま使ってますが、2 桁を 2 個にするのが良いと思います。
3 つ目のブレッドボードに DHT11 が載ってます。
一番下は、言わずと知れた Arduino UNO です。
これ、きちんと製作しようかしらん (^_^;)
製作後記
まぁなかなか良くできたんじゃね?って思ってます。
気に入らないのは、起始信号の 20ms が長げーよ、ってこと。ローエンドの機種なので仕方ないケド。
20ms の間に他の処理ができるようなスケッチも、じつは書いてみたんだけど、時間が不安定になっちゃって、ビットの読み取りエラーが頻発してしまう。ライブラリではどうしているんだろうと、つらつら眺めてみたんですが、ほとんど同じことやってました (^_^;)
DHT11 読み取りの関数を呼び出したら、20ms の起始信号に続いて、データの読み取りに約 4ms 必要です。この間、他の処理ができなくなるのは、仕様です (;´Д`)
なので、7 セグメント LED 点灯させるには TM1630 とか使いたくなる。
ただ、TM1630 へコマンドを送るのも 2ms x 2 回必要なので、リアルタイムに何かやるのは無理っぽい。TM1630 に関しては、SPI 使えば速くできると思いますが、その価値あるかどうかは考え方次第です。