Knurling Sessions 2020Q4 をやる (3) 〜CO2センサドライバ〜
はじめに
knurling-rs では Ferrous Systems が、スポンサー向けに組込み Rust の教材 knurling-session を提供しています。
knurling-session では、3ヶ月ごとに1つのテーマを取り上げています。 最新のもの以外は、公開されています。スポンサーになると最新のものも見れます。 また、スポンサーが増えると、knurling-rs に割く時間が増やせるため、ツールの改善や教材の整備が増えることが期待できます。 興味を持ったらスポンサーしましょう!
現在公開されている knurling-session の 2020 Q4 をやっていきます。 2020 Q4 は、nRF52840-DK と CO2 センサーを使ったプロジェクトです。
2020 Q4 のレンダリングされているドキュメントはこちら。
session20q4.ferrous-systems.com
前回、Lチカ終わらせたので、いよいよ CO2 センサ (SCD30) ドライバを書いていきます。
tomo-wait-for-it-yuki.hatenablog.com
Hello, Sensor!
まずは、CO2 センサの配線です。スルーホール用のテストワイヤ使ってみました。
SCD30 は、I2C インタフェースで制御します。 I2C ドライバのオブジェクトを作成して、SCD30 ドライバのオブジェクトを初期化します。
let scl = pins.p0_30.degrade().into_floating_input(); let sda = pins.p0_31.degrade().into_floating_input(); let twi_pins = twim::Pins{ scl, sda }; let i2c = Twim::new(board.TWIM0, twi_pins, twim::Frequency::K100); let mut scd30 = SCD30::init(i2c);
SCD30 ドライバも自作します。 データシートを見て、firmware version を取得するコードを書きます。
pub struct SCD30<T: Instance>(Twim<T>); impl<T> SCD30<T> where T: Instance { pub fn init(i2c: Twim<T>) -> Self { SCD30(i2c) } pub fn get_firmware_version(&mut self) -> Result<[u8; 2], Error> { let command: [u8; 2] = [0xd1, 0x00]; let mut rd_buffer = [0u8; 2]; self.0.write(DEFAULT_ADDRESS, &command)?; self.0.read(DEFAULT_ADDRESS, &mut rd_buffer)?; let major = u8::from_be(rd_buffer[0]); let minor = u8::from_be(rd_buffer[1]); Ok([major, minor]) } }
これで firmware version が取得できるようになります。
0 INFO Firmware Version: 3.66
Start Measuring
データシートを見ながら、SCD30 ドライバにメソッドを追加していきます。 今回作成する SCD30 ドライバは、70 行くらいで、それほど複雑ではありません。
impl<T> SCD30<T> where T: Instance { pub fn start_continuous_measurement(&mut self, pressure: u16) -> Result<(), Error> { let mut command: [u8; 5] = [0x00, 0x10, 0x00, 0x00, 0x00]; let argument_bytes = &pressure.to_be_bytes(); command[2] = argument_bytes[0]; command[3] = argument_bytes[1]; let mut crc = Crc::<u8>::new(0x31, 8, 0xff, 0x00, false); crc.update(&pressure.to_be_bytes()); command[4] = crc.finish(); self.0.write(DEFAULT_ADDRESS, &command)?; Ok(()) } pub fn data_ready(&mut self) -> Result<bool, Error> { let command: [u8; 2] = [0x02, 0x02]; let mut rd_buffer = [0u8; 3]; self.0.write(DEFAULT_ADDRESS, &command)?; self.0.read(DEFAULT_ADDRESS, &mut rd_buffer)?; Ok(u16::from_be_bytes([rd_buffer[0], rd_buffer[1]]) == 1) } pub fn read_measurement(&mut self) -> Result<SensorData, Error> { let command: [u8; 2] = [0x03, 0x00]; let mut rd_buffer = [0u8; 18]; self.0.write(DEFAULT_ADDRESS, &command)?; self.0.read(DEFAULT_ADDRESS, &mut rd_buffer)?; let co2 = f32::from_bits(u32::from_be_bytes([rd_buffer[0], rd_buffer[1], rd_buffer[3], rd_buffer[4]])); let temperature = f32::from_bits(u32::from_be_bytes([rd_buffer[6], rd_buffer[7], rd_buffer[9], rd_buffer[10]])); let humidity = f32::from_bits(u32::from_be_bytes([rd_buffer[12], rd_buffer[13], rd_buffer[15], rd_buffer[16]])); let data = SensorData{ co2, temperature, humidity }; Ok(data) }
ここまでできると、defmt 経由で計測結果を出力します。
Red Alert!
CO2 センサ、RGB LED、ブザーを組み合わせてプログラムを作成します。