温度(湿度)センサを使ってみた(2)
RaspberryPi/pigpioでDHT11
どもです。
前回は、DHT11をAdrudinoにつないで温度/湿度を測定してみました。
そこで今回は、Arduinoの代わりにRaspberryPiで同様に温度/湿度を測定してみました。
1.開発環境
本エントリでは、以下の環境で開発・実験を行いました。
OS | Windows10 Pro (バージョン:1903) |
---|---|
CPU | Intel Core i7-8700 |
メモリ | 16.0 GB |
pigpio (RaspberryPiのライブラリ) |
Ver.71 |
なお、クロスコンパイラ/クロス環境の構築手順については、こちらを参照して下さい。
コンパイラのバージョンは少し古いですが、基本的な手順は変わっていません。
2.配線
まず、ブレッドボード上の配線です。
配線図は、下記の通りです。
基本的には、前回と同じです。
ただし、RaspberryPiでは波形のノイズの解消のための、コンデンサを追加しています。
使用したコンデンサは、積層セラミックコンデンサ(0.1μF)です。
3.実装
今回の実装では、Arduinoの際に使用したライブラリのコード、およびDHT11のデータシートを元に作成しています。
コードは下記の通りです。
まずmain()です。
#include <iostream>
#include "Sensor/CDHT11.h"
#include "CGpio/CGpio.h"
using namespace std;
#define DHT11_DATA (21)
int main() {
CDHT11 dht11;
dht11.setupGpioPin(DHT11_DATA);
while (1) {
if (CDHT11::CDHT11_ERROR_OK == dht11.read()) {
printf("Temperature = %d.%d[C] ",
dht11.getTemperature() / 256,
abs(dht11.getTemperature()) % 256);
printf("humidity = %d.%d[%%]\n",
dht11.getHumidity() / 256,
abs(dht11.getHumidity()) % 256);
}
}
return 0;
}
次に、CDHT11クラスのデータ読み出し処理です。
なお、全部を載せると結構な量になってしまいますので、重要と思われる部分のみ抜粋します。
/**
* @brief Read sensor value.
* @return Returns 0 if reading sensor value finished succeeded, otherwise
* returns none 0 value.
*/
int CDHT11::read() {
if (false == this->isScanIntervalPassed()) {
return CDHT11_ERROR_SENSOR_NO_UPDATA;
}
int sequenceResult = this->readSequence();
if (CDHT11_ERROR_OK != sequenceResult) {
CLog::Error("Reading sensor failed.");
} else {
//Nothing to do.
}
this->updateInterval();
return sequenceResult;
}
/*
* A wait time to set data signal level keeping low to send start signal.
* From the data sheet, the time should be larger than 18 msec, no more than
* 30 msec.
*/
//#define DHT11_START_SIGNAL_LOW_TIME_MILLISEC (18)
//A little bit longer than the value in data sheet...
#define DHT11_START_SIGNAL_LOW_TIME_MILLISEC (20)
#define DHT11_START_SIGNAL_HIGH_TIME_MILLISEC (80)
#define MILLI2MICRO_SEC(milli_sec) ((milli_sec) * 1000)
#define DHT11_SENSOR_READY_TO_OUTPUT_SIGNAL_TIME_MICROSEC (80)
#define DHT11_DATA_START_LOW_BIT_WAIT_TIME (50)
#define DHT11_DATA_FOLLOW_HIGH_BIT_WAIT_TIME (70)
/**
* @brief Run sequence to read the data from sensor, DHT11.
* A sequence run in this method is on the data sheet.
* @return Returns true if the sequence finished succesfully, otherwise
* retunrs none 0 value.
*/
int CDHT11::readSequence() {
CGpio* gpio = CGpio::getInstance();
uint32_t cycleBuff[80] = { 0 };
//Sending start signal.
//Step1:Sending start signals.
gpio->SetMode(this->m_pin, CGpio::CGPIO_PIN_IN_OUT_OUTPUT);
gpio->SetPullUpDownMode(this->m_pin, CGpio::CGPIO_PULL_UP_DOWN_OFF);
gpio->Write(this->m_pin, CGpio::CGPIO_PIN_LEVEL_LOW);
gpio->DelayMilli(DHT11_START_SIGNAL_LOW_TIME_MILLISEC);
//Step2:Waiting for response signal by setting GPIO pin pulled up.
gpio->SetMode(this->m_pin, CGpio::CGPIO_PIN_IN_OUT_INPUT);
gpio->SetPullUpDownMode(this->m_pin, CGpio::CGPIO_PULL_UP_DOWN_UP);
this->waitForPulse(CGpio::CGPIO_PIN_LEVEL_LOW,
MILLI2MICRO_SEC(DHT11_START_SIGNAL_HIGH_TIME_MILLISEC));
if (WAIT_SIGNAL_TIMEOUT == this->waitForPulse(
CGpio::CGPIO_PIN_LEVEL_HIGH,
DHT11_START_SIGNAL_HIGH_TIME_MILLISEC))
{
CLog::Warn("Response to pulled up signal can not be detected.");
return CDHT11_ERROR_PULLED_UP_TIMEOUT;
}
/*
* Step3:Waiting for response signal sensor send to notify host,
* raspberry-pi, to ready to output data.
*/
uint32_t startSignalWaitTime_Low = this->waitForPulse(
CGpio::CGPIO_PIN_LEVEL_LOW,
DHT11_SENSOR_READY_TO_OUTPUT_SIGNAL_TIME_MICROSEC);
if (WAIT_SIGNAL_TIMEOUT == startSignalWaitTime_Low)
{
CLog::Warn("Response to start signal of low can not be detected.");
return CDHT11_ERROR_SENSOR_PULL_LOW_TIMEOUT;
}
uint32_t startSignalWaitTime_high = this->waitForPulse(
CGpio::CGPIO_PIN_LEVEL_HIGH,
DHT11_SENSOR_READY_TO_OUTPUT_SIGNAL_TIME_MICROSEC);
if (WAIT_SIGNAL_TIMEOUT == startSignalWaitTime_high)
{
CLog::Warn("Response to start signal of high can not be detected.");
return CDHT11_ERROR_SENSOR_PULL_HIGH_TIMEOUT;
}
//Step4:Read signal sensor sends.
for (int buffIndex = 0; buffIndex < 40; buffIndex++) {
cycleBuff[buffIndex * 2] = this->waitForPulse(
(const unsigned int)CGpio::CGPIO_PIN_LEVEL_LOW,
(const unsigned int)DHT11_DATA_START_LOW_BIT_WAIT_TIME);
cycleBuff[(buffIndex * 2) + 1] = this->waitForPulse(
(const unsigned int)CGpio::CGPIO_PIN_LEVEL_HIGH,
(const unsigned int)DHT11_DATA_FOLLOW_HIGH_BIT_WAIT_TIME);
}
// for (int buffIndex = 0; buffIndex < 40; buffIndex++) {
// printf("cycleBuff[%2d] = %7d, cycleBuff[%2d] = %7d\n",
// buffIndex * 2,
// cycleBuff[buffIndex * 2],
// (buffIndex * 2) + 1,
// cycleBuff[(buffIndex * 2) + 1]);
// }
//Step5:Convert Low-High time to bit data.
this->InitDataBuff();
for (int index = 0; index < 40; index++) {
uint32_t startBit = cycleBuff[index * 2];
uint32_t followBit = cycleBuff[index * 2 + 1];
if ((CDHT11::WAIT_SIGNAL_TIMEOUT == startBit) ||
(CDHT11::WAIT_SIGNAL_TIMEOUT == followBit))
{
CLog::Warn("Reading data failed.");
return CDHT11_ERROR_SENSOR_READ_DATA_TIMEOUT;;
}
this->m_dataBuff[index / 8] <<= 1;
if (startBit < followBit) {
this->m_dataBuff[index / 8] |= 1;
}
}
// this->ShowBuff();
if (false == this->validateCheckSum()) {
CLog::Error("Receive data invalid.");
return CDHT11_ERROR_SENSOR_READ_DATA_INVALID;
} else {
CLog::Debug("Receive data valid.");
return CDHT11_ERROR_OK;
}
}
/**
* Wait while the pin level is kept.
*
* @param level The pin level to wait while.
* @param time Max time to wait for, specified by micro seconds.
* @return Returns wait time. If timeout occurred, returns 0xFFFFFFFF, meaning
* -1.
*/
uint32_t CDHT11::waitForPulse(
const unsigned int level,
const unsigned int time)
{
CGpio* instance = CGpio::getInstance();
unsigned int readLevel = 0;
instance->Read(this->m_pin, &readLevel);
uint32_t passedTime = 0;
while (level == readLevel) {
if ((time) < passedTime) {
//Time out!
return CDHT11::WAIT_SIGNAL_TIMEOUT;
}
instance->Read(this->m_pin, &readLevel);
passedTime += instance->DelayMicro(1);
}
//CLog::Debug("Wait for sensor start signal : OK");
return passedTime;
}
4.解説
上記コード、特にデータの読み出しを行っているCDHT11::readSequence()について解説します。
4.1.開始信号の送信
RaspberryPiでDATAピンの電位を操作します。
DHT11では、データの送信開始を要求するために、以下のようにDATAピンの電位を変化させます。
「Step1」~「Step2」のコードが該当します。
なお、RaspberryPiのGPIOのプルアップ/ダウンの変更は、入力/出力の設定後に行う必要があるようです。
プルアップ/ダウンを変更させてから入力/出力を変更した場合、プルアップ/ダウンの変更が期待通りの設定になりません。
処理の実行手順に注意する必要があります。
4.2.DHT11の開始待ち
DATAピンの電位を一定時間LOWに維持すると、次にDHT11がDATAの電位のHIGH/LOWを動作させます。
その際の仕様は、下記の通りです。
これを検知するのが、「Step3」の処理に該当します。
4.3.温度/湿度データの読み出し
DHT11では、DATAピンの電位がHIGHになっている時間が、ビットの0/1に対応しています。
データシートによれば、HIGHで保持されている時間とビットの0/1の対応は下記の通りです。
ここに記載されているHIGHからLOWへの変化を割り込みで検知、時間を計測して0/1を判定する、という方法もアリかもしれません。
しかしここでは、違う方法を採用します。
(ていうか、Arduino向けライブラリでは違う方法を採用しているので、それに倣います。)
即ち、LOW/HIGHに保持されていた時間を保持しておき、その長さを比較することでビットの0/1を判定する、という方法です。
この「LOW/HIGHに保持されていた時間を保持」しているのがStep4、「長さを比較することでビットの0/1を判定」しているのが「Step5」となります。
4.4.整合性確認
最後に整合性の確認です。
DHT11は、全部で5バイトのデータを送信します。
受信したビットデータの前半4バイトの総和と5バイト目の値が一致していれば、受信したデータは正常と判断できます。
一致しない場合には、不正なデータと判断します。
なお、総和は16ビットのデータになる可能性がありますが、上位8ビットのデータは無視します。
4.5.測定結果
作成したアプリケーションの実行すると、下記のようになります。
結果は…前回と同様ですね。
5.まとめ
前回に引き続き、今回もDHT11を使ってみました。
ネットで調べてみると、RaspberryPi上ではpythonで動かした例はたくさん見つかります。
しかし、C言語またはC++とpigpioで動作させた例は殆ど見つかりません。
今回のエントリで、C言語またはC++とpigpioでDHT11を操作/動作させる際に困っているヒトの一助にでもなれば、うれしいなぁ…。
ではっ!
追記:公開しています
DHT11を使用するためのソースコードは、GitHubで公開しています。
ArduinoとRaspberryPiの両方を公開しているので、必要に応じて、それぞれ参照してみてください。
ディスカッション
コメント一覧
まだ、コメントがありません