13.6.DHT-22による温度、湿度計測
プロジェクトの概要
DHT-22温度センサーによる温度計測プロジェクトです。Picoとの通信は専用の1線のシリアル通信です。通信距離は5.1kΩプルアップ時、最大30mです。仕様は以下です。
電源電圧:5V(3.5V~5.5V)
内部ADコンバータ:各16bit
湿度センサ測定範囲:0~99.9%RH・精度:±2% RH(@25℃)
温度センサ測定範囲:-40~+80℃・精度:±0.5℃
出力データ:湿度16bit(分解能:0.1%RH)、温度16bit(分解能:0.1℃)
DHT-22との接続は以下で、電源、ロジックは3.3Vで使用しています。
DHT-22 Pico
DAT GPIO10
VCC 3.3V
GND GND
DHT-22側にプルアップ抵抗4.7KをDATラインに入れています。長距離接続の場合、ノイズに強くなります。
部品リスト
DHT22 温湿度センサーモジュール 1 アマゾン
GROVEの16 x 2 LCD 1 スイッチサイエンス
配線図

ソースリスト
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pico/stdlib.h"
#include "hardware/i2c.h"
#include "hardware/rtc.h"
#include "hardware/adc.h"
//#define PICO_DEFAULT_LED_PIN 25
#define LED_PIN PICO_DEFAULT_LED_PIN
#define I2C_PORT i2c0
#define I2C_SDA 8
#define I2C_SCL 9
#define DHT_PIN 10
//ADc
const float ConversionFactor = 3.3f / (1 << 12);
int WaitTerminalStartup(int timeout_msec) {
int btnin;
uint32_t st = time_us_32();
while(true)
{
if(stdio_usb_connected())
{
return 0;
}
if(timeout_msec != 0){
uint32_t cur = time_us_32();
if((cur - st) > (timeout_msec*1000)) {
return 0;
}
}
gpio_put(LED_PIN, 1);
sleep_ms(200);
gpio_put(LED_PIN, 0);
sleep_ms(200);
}
}
void ScanI2CBus() {
printf("\nI2C Bus Scan\n");
printf(" 0 1 2 3 4 5 6 7 8 9 A B C D E F\n");
for (int addr = 0; addr < (1 << 7); ++addr) {
if (addr % 16 == 0) {
printf("%02x ", addr);
}
int ret;
uint8_t rxdata;
ret = i2c_read_blocking(I2C_PORT, addr, &rxdata, 1, false);
sleep_ms(10);
printf(ret < 0 ? "-" : "*");
printf(addr % 16 == 15 ? "\n" : " ");
}
}
void InitAdc()
{
adc_init();
adc_set_temp_sensor_enabled(true);
}
float ReadOnBoardTemperature()
{
adc_select_input(4);
float tempV = (float)adc_read() * ConversionFactor;
float tempC = 27.0f - (tempV - 0.706f) / 0.001721f;
return tempC;
}
void InitRtc()
{
char datetime_buf[256];
char *datetime_str = &datetime_buf[0];
//Start on 2023/5/9 Tuesday 10:24:00
datetime_t t = {
.year = 2023,
.month = 5,
.day = 9,
.dotw = 2, // 0:Sunday - 5:Friday
.hour = 10,
.min = 24,
.sec = 00
};
rtc_init();
rtc_set_datetime(&t);
sleep_ms(1);
}
//dht22-----------------------------
void WaitFallingEdge() {
while(gpio_get(DHT_PIN) == 0){};
while(gpio_get(DHT_PIN) == 1){};
}
uint32_t getData(){
uint32_t t2;
uint32_t data = 0;
uint32_t t1 = time_us_32();
for (int i = 0; i < 32; i++) {
WaitFallingEdge();
t2 = time_us_32();
data = data << 1;
data = data | ((t2 - t1) > 100);
t1 = t2;
}
return data;
}
uint8_t getCheck(){
uint8_t checksum = 0;
uint32_t t2;
uint32_t t1 = time_us_32();
for (int i = 0; i < 8; i++) {
WaitFallingEdge();
t2 = time_us_32();
checksum = checksum << 1;
checksum = checksum | ((t2 - t1) > 100);
t1 = t2;
}
return checksum;
}
void dhtInitialize(){
gpio_init(DHT_PIN);
gpio_set_dir(DHT_PIN, GPIO_OUT);
gpio_put(DHT_PIN, 1);
sleep_ms(1);
gpio_put(DHT_PIN, 0);
sleep_ms(1);
gpio_set_dir(DHT_PIN, GPIO_IN);
WaitFallingEdge();
WaitFallingEdge();
}
typedef struct {
float temperature;
float humidity;
bool error;
} dhtData;
void dhtRead(dhtData *reading){
dhtInitialize();
uint32_t data = getData();
uint8_t checksum = getCheck();
uint8_t byte1 = data >> 24 & 0xFF;
uint8_t byte2 = data >> 16 & 0xFF;
uint8_t byte3 = data >> 8 & 0xFF;
uint8_t byte4 = data & 0xFF;
reading->error = (checksum != ((byte1 + byte2 + byte3 + byte4) & 0xFF));
reading->humidity = (float)((byte1 << 8) | byte2)/10.0;
int neg = byte3 & 0x80;
byte3 = byte3 & 0x7F;
reading->temperature = (float)((byte3 << 8) | byte4)/10.0;
if(neg > 0){
reading->temperature = -reading->temperature;
}
}
//lcd -------------------------------
#define LCD_ADDR 0x27
// commands
const int LCD_CLEARDISPLAY = 0x01;
const int LCD_RETURNHOME = 0x02;
const int LCD_ENTRYMODESET = 0x04;
const int LCD_DISPLAYCONTROL = 0x08;
const int LCD_CURSORSHIFT = 0x10;
const int LCD_FUNCTIONSET = 0x20;
const int LCD_SETCGRAMADDR = 0x40;
const int LCD_SETDDRAMADDR = 0x80;
//display entry mode
const int LCD_ENTRYSHIFTINCREMENT = 0x01;
const int LCD_ENTRYLEFT = 0x02;
//display and cursor control
const int LCD_BLINKON = 0x01;
const int LCD_CURSORON = 0x02;
const int LCD_DISPLAYON = 0x04;
//display and cursor shift
const int LCD_MOVERIGHT = 0x04;
const int LCD_DISPLAYMOVE = 0x08;
//function set
const int LCD_5x10DOTS = 0x04;
const int LCD_2LINE = 0x08;
const int LCD_8BITMODE = 0x10;
//backlight control
const int LCD_BACKLIGHT = 0x08;
const int LCD_ENABLE_BIT = 0x04;
#define LCD_CHARACTER 1
#define LCD_COMMAND 0
//#define MAX_LINES 2
//#define MAX_CHARS 16
#define MAX_LINES 2
#define MAX_CHARS 16
#define DELAY_US 600
void i2c_write_byte(uint8_t val) {
i2c_write_blocking(I2C_PORT, LCD_ADDR, &val, 1, false);
}
void lcd_toggle_enable(uint8_t val) {
sleep_us(DELAY_US);
i2c_write_byte(val | LCD_ENABLE_BIT);
sleep_us(DELAY_US);
i2c_write_byte(val & ~LCD_ENABLE_BIT);
sleep_us(DELAY_US);
}
void lcd_send_byte(uint8_t val, int mode) {
uint8_t high = mode | (val & 0xF0) | LCD_BACKLIGHT;
uint8_t low = mode | ((val << 4) & 0xF0) | LCD_BACKLIGHT;
i2c_write_byte(high);
lcd_toggle_enable(high);
i2c_write_byte(low);
lcd_toggle_enable(low);
}
void lcd_clear(void) {
lcd_send_byte(LCD_CLEARDISPLAY, LCD_COMMAND);
}
void lcd_set_cursor(int line, int position) {
int addr[4] = {0x80, 0xc0, 0x94, 0xd4};
int val = addr[line] + position;
lcd_send_byte(val, LCD_COMMAND);
}
static void inline lcd_char(char val) {
lcd_send_byte(val, LCD_CHARACTER);
}
void lcd_string(const char *s) {
while(true) {
if(*s == 0) {
break;
}
lcd_char(*s);
s++;
}
}
void lcd_init() {
lcd_send_byte(0x03, LCD_COMMAND);
lcd_send_byte(0x03, LCD_COMMAND);
lcd_send_byte(0x03, LCD_COMMAND);
lcd_send_byte(0x02, LCD_COMMAND);
lcd_send_byte(LCD_ENTRYMODESET | LCD_ENTRYLEFT, LCD_COMMAND);
lcd_send_byte(LCD_FUNCTIONSET | LCD_2LINE, LCD_COMMAND);
lcd_send_byte(LCD_DISPLAYCONTROL | LCD_DISPLAYON, LCD_COMMAND);
lcd_clear();
}
int main()
{
stdio_init_all();
gpio_init(LED_PIN);
gpio_set_dir(LED_PIN, GPIO_OUT);
gpio_put(LED_PIN, 0);
i2c_init(I2C_PORT, 400*1000);
gpio_set_function(I2C_SDA, GPIO_FUNC_I2C);
gpio_set_function(I2C_SCL, GPIO_FUNC_I2C);
gpio_pull_up(I2C_SDA);
gpio_pull_up(I2C_SCL);
WaitTerminalStartup(30*1000);
printf("\nTerminal connected\n");
ScanI2CBus();
printf("I2C Scan completed\n");
lcd_init();
InitAdc();
InitRtc();
char buf[128];
datetime_t nowdt;
int presec = -1;
dhtData readData;
while (1) {
rtc_get_datetime(&nowdt);
if(presec != nowdt.sec)
{
float bTemp = ReadOnBoardTemperature();
dhtRead(&readData);
sprintf(buf, "%02d:%02d:%02d AD:%4.1f", nowdt.hour,nowdt.min, nowdt.sec, bTemp);
lcd_set_cursor(0, 0);
lcd_string(buf);
if(!readData.error) {
sprintf(buf, " %4.1fC %4.1f%%", readData.temperature, readData.humidity);
printf("%4d/%02d/%02d %02d:%02d:%02d DhtTemp:%4.1fC AdcTemp:%4.1fC DhtHumi:%4.1f%%\n",
nowdt.year, nowdt.month, nowdt.day,
nowdt.hour,nowdt.min, nowdt.sec, readData.temperature, bTemp, readData.humidity);
} else {
sprintf(buf, "Checksum error! ");
printf("Checksum error\n");
}
lcd_set_cursor(1, 0);
lcd_string(buf);
}
presec = nowdt.sec;
sleep_ms(200);
}
return 0;
}

温度データは変動が少なく、安定しており、精度は良いと思われます。
コメントをお書きください