温度湿度センサーといえば DHT11 ですが、DHT11 は単線で送受信するため配線上は楽なのですが、マイコン上はオープンドレインで送受信を行わねばならず、適切に読み込もうとすると一手間かかります。 DHT11は有名なため、おおくのマイコン用のライブラリが作られています。 rustでembedded-hal を使ったものでライブラリがありましたが、私の所では上手く動作させることは出来ませんでした。
https://crates.io/crates/dht11
オシロスコープで確認する限りは、センサーは正しい値を返していることが確認できていました。
いつかリベンジしたいですが一旦諦めて、通信が楽そうな温湿度センサーを探していました。
SHT31
SHT31というI2C で使える高精度温湿度センサーを見つけました。 秋月でも取り扱いがあります。
自分の場合は、aliexpressのショップで見つけまして購入しました。
https://ja.aliexpress.com/item/1005001626776392.html
さらに、秋月のサイトには日本語のデータシートも掲載されていました。 このデータシートを参考に、周期的連続測定モードで稼働させ、データを読み取ることが出来ました。
以下コードの抜粋です。
// sht31.rs use embedded_hal::i2c::{blocking::I2c, Error}; const _SHT31_ADDRESS: u8 = 0x44; const _SETUP: &[u8] = &[0x22, 0x36]; pub struct SHT31 {} impl SHT31 { pub fn new() -> Self { Self {} } /** * 周期的連続測定モードの開始 */ pub fn setup<T: I2c>(&mut self, i2c_conn: &mut T) -> Result<(), ()> { if let Err(e) = i2c_conn.write(_SHT31_ADDRESS, _SETUP) { println!("sht31 i2c error: {}", e.kind()); return Err(()); } return Ok(()); } /** * 計測 */ pub fn read<T: I2c>(&mut self, i2c_conn: &mut T) -> Result<[f32; 2], ()> { let mut buf = &mut [0u8; 6]; if let Err(e) = i2c_conn.read(_SHT31_ADDRESS, buf) { println!("sht31 i2c error: {}", e.kind()); return Err(()); } // TODO: チェックサム let measured_temp = u16::from(buf[0]) << 8 | u16::from(buf[1]); let real_temp = f32::from(measured_temp) * 175. / (((1 << 16) - 1) as f32) - 45.; let measured_hum = u16::from(buf[3]) << 8 | u16::from(buf[4]); let real_hum = f32::from(measured_hum) * 100. / (((1 << 16) - 1) as f32); Ok([real_temp, real_hum]) } }
// main.rs use esp_idf_hal::{i2c, prelude::Peripherals} use std::thread::sleep; use std::time::Duration; mod sht31; fn main() -> std::io::Result<()> esp_idf_sys::link_patches(); let peripherals = Peripherals::take().unwrap(); let mut i2c_conn = setup_i2c( peripherals.i2c0, peripherals.pins.gpio5, peripherals.pins.gpio6, ) .unwrap() let mut sht31_module = sht31::SHT31::new(); sht31_module.setup(&mut i2c_conn); loop { let r = sht31_module.read(&mut i2c_conn); match r { Ok(v) => { println!("{:>2.1}C {:>3.1}%", v[0], v[1]); } Err(e) => println!("sht11 error"), } sleep(Duration::from_secs(3)); } }
動かしてみて
良好に読み取れていそうで良かったです。
一旦 DHT11 を諦めた経緯
本来であれば、マルチスレッドで動かす物ではないため、GPIOで読み取る時のμs単位ディレイに CPU クロックが使えれば正確で良いのです。 今回利用した ESP32-C3 で通信な可能な形で embedded-hal を実装している esp-idf-hal で使えるディレイが、 Espressif Task Scheduler ベースのもの(esp_idf_hal::delay::Ets)であり、単純にそれを使って既存の dht11 ライブラリが動作しなかったため、これは嵌まると大きそうだと感じたためです。