以前、温湿度センサ DHT11 を使った温湿度計を作りました。
一連の記事は 温湿度センサ DHT11 を使ってみる (3) (2) (1) です。
これは、Arduino NANO を使い、DHT11 からのデータを受け取って 7 セグメント LED に温度と湿度を表示させるものです。
そいつをユニバーサル基板に組み、現在は写真のように、簡単なカバーも付けて、室温管理に利用しています。センサ DHT11 は、基板近くに置くと発熱の影響を受けるので、アンテナのように上に突き立ててあります。
暑い夏。節電の夏。エアコンの温度管理は大切です (^_^;)
Arduino Nano Every では動かない
今回、こいつを Arduino Nano Every に交換してみました。が、温度、湿度ともに表示が「0」になって、計測ができません。ついに Arduino NANO と互換できない事例にであったようです。
原因を調べてみると、どうやら DHT11 からのデータをうまく受けとれていないようです。
スケッチでは、DHT11 のライブラリは利用せずに、pulseIn() 関数で、送られてくるデータビットの HIGH の時間を取得しています。
図 1 は、Arduino NANO の場合の DHT11 からのデータの受信状況です。
横方向に、40 ビット分の HIGH の時間を取得しています。この時間が 24μs ほどならば “0” 、71μs ほどならば “1” の値となります。
Arduino NANO では、正常に受け取れていることがわかります。
一方、Arduino Nano Every の場合が、図 2 。データの取得が飛び飛びで、全体の 4 割以下しか受け取れていません。
HIGH の時間を取得することはできるようですが、連続してデータを受けることができていません。
どうやら、Arduino Nano Every では、pulseIn() が意図したように動作しないようです。
DHT センサライブラリを使う
pulseIn() がうまくいかないなら別な方法で……、なんて労力は、ないです。
DHT11 からのデータ取得方法については、すでに十分理解できましたので、ライブラリを使うことにしましょう。リファレンスには、
This library is compatible with all architectures so you should be able to use it on all the Arduino boards.
https://www.arduino.cc/reference/en/libraries/dht-sensor-library/
と書かれていますので、Arduino Nano Every でも使えるはずです。
ライブラリのインストール
ライブラリをインストールしましょう。難しくないです。
Arduino IDE を起動し、スケッチ – ライブラリをインクルード – ライブラリを管理 をクリックすると、ライブラリマネージャが開きます。
「検索をフィルタ…」欄に「DHT11」と入力すると、関連のライブラリが表示されますので、「DHT sensor library」をインストール。必要なライブラリを一緒にインストールするかい?と訊かれるので、全部やって、と答えましょう。
以上、完了。
DHTtester
ライブラリの動作を確認してみます。
カスタムライブラリのスケッチ例に「DHTtester」が追加されていますので、これを、DHT11 をつないだ Arduino に書き込みます。DHTPIN と DHTTYPE の変更をお忘れなく。
DHT11 から読み取った温度と湿度が、シリアルモニタに表示されれば OK です。
結果、Arduino NANO 、Arduino Nano Every ともに、問題なく動作しました。
FYI
以下のサイトをご参照下さい。
スケッチ
Arduino Nano Every で動作するように、スケッチを変更しました。もちろん Arduino NANO でも、これまで同様に利用できます。回路も変更ありません。
この機会に、ということで、クラスにしてみました。
- // Temperature Humidity Meter v.2.1 2022.07.11 by meyon
- // Arduino NANO / Nano Every
- //#define DEBUG_ON
- #include "DHT.h"
- DHT dht(7, DHT11);
- class Sense {
- public:
- Sense() {
- dht.begin();
- }
- void readDHT(byte *temp, byte *hmd) {
- float tm = dht.readTemperature();
- float hm = dht.readHumidity();
- #ifdef DEBUG_ON
- Serial.print(tm);
- Serial.print(" ");
- Serial.print(hm);
- Serial.print(" ");
- #endif
- if(0 > tm) tm = 0;
- *temp = (byte)(tm + 0.5);
- *hmd = (byte)hm;
- }
- };
DHT11 からデータを取得するクラス定義です。
以前のスケッチと同様に、温度は小数点以下を丸めています。氷点下の場合は「0」を表示します。
- class Display {
- const byte DISPLAY_MODE = 0x00;
- const byte WRITE_REG_AUTO_ADDR = 0x40;
- const byte SET_DISPLAY_ADDR = 0xC0;
- const byte DISPLAY_ON = 0x88 | 0x84;
- const byte STB_ENABLE = LOW;
- const byte STB_DISABLE = HIGH;
- const int DOT_POSITION = -1;
- const byte dioPin = 10;
- const byte clkPin = 11;
- const byte stbPin = 12;
- static const byte numberOfDigits = 2;
- const byte digit[12] = {
- 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;
- byte gridData[numberOfDigits] = {0};
- public:
- Display() {
- pinMode(dioPin, OUTPUT);
- pinMode(clkPin, OUTPUT);
- pinMode(stbPin, OUTPUT);
- }
- 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);
- }
- };
TM1630 を使用して 7 セグメント LED を表示させるクラス定義です。
メンバ変数の初期化とか、よくわからないことが多々あるのですが、以前のスケッチで作った関数をほぼそのまま移行しています。これでまぁ正常に動いてますので、結果オーライってことで。
- Sense sensor;
- Display ssd;
- void setup() {
- #ifdef DEBUG_ON
- Serial.begin(9600);
- #endif
- }
- void loop() {
- static const unsigned long interval = 10000; // ms
- static unsigned long previousMillis = 0;
- static byte humidity = 0;
- static byte temperature = 0;
- if(interval < millis() - previousMillis) {
- sensor.readDHT(&temperature, &humidity);
- ssd.displayNumbers(temperature, 0x04);
- ssd.displayNumbers(humidity, 0x00);
- #ifdef DEBUG_ON
- Serial.print("/ ");
- Serial.print(temperature);
- Serial.print("°C ");
- Serial.print(humidity);
- Serial.print("%\n");
- #endif
- previousMillis = millis();
- }
- }
10秒毎に DHT11 の出力を取得し、TM1630 へ送っています。
4 行目のコメントを外すと、シリアルモニタで、取得したデータと出力するデータを表示できます。動作の確認用です。
後記
プログラマの息子と、オブジェクト指向プログラミング (OOP) とかクラスとかについて話す機会がありました。
OOP ってけっきょく何なんだ。クラスでプログラムを書くのは何故なんだ? それは、初学者の教科書ではわからないし、通常は初学者には教えない、と息子はいいます。中級者以上の、もっと難しい論ででてくる話だと。
だけど、そこんところが理解できないと、やり方や形ばかりが先行して本来の目的が果たせない気がする。
いまいちスッキリしなかったメンバ変数の初期化について、息子に基本的な考え方を教えてもらいました。
コンストラクタで初期化する、ということは、外部要素のインジェクション、つまりオブジェクトを生成する時にそのインスタンスに必要な値を与える、ということ。だから、そのインスタンス内でしか使わない値は、メンバ変数を宣言する時に初期化して構わない。
うぅ、言葉がイチイチ面倒くさい (;´Д`)
でもまぁ、いろんなやり方ができるんで、自分なりのルールでいいんじゃね? ってことでした。
これからもっといろいろスケッチを書くうちに、きっと、もっと、すっきりした形で書けるようになっていくと思います。