From 7b7c2c5ba6e10d16c80cf94cf2757aad85ebfc3c Mon Sep 17 00:00:00 2001 From: Daniel Friesel Date: Wed, 20 Oct 2021 22:41:14 +0200 Subject: add mh-z19 test app --- src/app/mhz19/Kconfig | 6 ++ src/app/mhz19/Makefile.inc | 9 +++ src/app/mhz19/main.cc | 173 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 188 insertions(+) create mode 100644 src/app/mhz19/Kconfig create mode 100644 src/app/mhz19/Makefile.inc create mode 100644 src/app/mhz19/main.cc (limited to 'src/app/mhz19') diff --git a/src/app/mhz19/Kconfig b/src/app/mhz19/Kconfig new file mode 100644 index 0000000..473cea9 --- /dev/null +++ b/src/app/mhz19/Kconfig @@ -0,0 +1,6 @@ +# Copyright 2020 Daniel Friesel +# +# SPDX-License-Identifier: CC0-1.0 + +prompt "MH-Z19C reader" +depends on arch_msp430fr5994lp && !meta_driver_stdin && loop && !wakeup diff --git a/src/app/mhz19/Makefile.inc b/src/app/mhz19/Makefile.inc new file mode 100644 index 0000000..0eea52a --- /dev/null +++ b/src/app/mhz19/Makefile.inc @@ -0,0 +1,9 @@ +# vim:ft=make +# +# Copyright 2020 Daniel Friesel +# +# SPDX-License-Identifier: CC0-1.0 + +ifdef app + loop = 1 +endif diff --git a/src/app/mhz19/main.cc b/src/app/mhz19/main.cc new file mode 100644 index 0000000..23c7984 --- /dev/null +++ b/src/app/mhz19/main.cc @@ -0,0 +1,173 @@ +/* + * Copyright 2021 Daniel Friesel + * + * SPDX-License-Identifier: BSD-2-Clause + */ +#include "arch.h" +#include "driver/gpio.h" +#include "driver/stdout.h" + +class SerialOutput : public OutputStream { + private: + SerialOutput(const SerialOutput ©); + + public: + SerialOutput () {} + void setup(); + + virtual void put(char c) override; +}; + +/* + * Baud rate calculation according to datasheet: + * N := f_{BRCLK} / Baudrate = F_CPU / 115200 in our case + * if N <= 16: OS16 = 0, UCBR0 = int(N) + * if N > 16: OS16 = 1, UCBR0 = int(N/16), UCBRF0 = int(((n/16) - int(n/16)) * 16) = int(N)%16 + * Set UCBRS0 according to table 21-4 + */ + +void SerialOutput::setup() +{ + UCA1CTLW0 |= UCSWRST; +#if F_CPU == 16000000UL + // 16M / 9600 == 1666.6667 -> UCOS16 = 1, UCBR0 = 104, UCBRF0 = 2, UCBRS0 = 0xd6 ("0.6667") + UCA1CTLW0 = UCSWRST | UCSSEL__SMCLK; + UCA1MCTLW = UCOS16 | (2<<4) | 0xd600; + UCA1BR0 = 104; +#elif F_CPU == 8000000UL + // 8M / 9600 == 833.3333 -> UCOS16 = 1, UCBR0 = 52, UCBRF0 = 1, UCBRS0 = 0x49 ("0.3335") + UCA1CTLW0 = UCSWRST | UCSSEL__SMCLK; + UCA1MCTLW = UCOS16 | (1<<4) | 0x4900; + UCA1BR0 = 52; +#else +#error Unsupported F_CPU +#endif + + UCA1IRCTL = 0; + UCA1ABCTL = 0; + + P2REN &= ~(BIT5 | BIT6); + P2SEL0 &= ~(BIT5 | BIT6); + P2SEL1 |= BIT5 | BIT6; + + UCA1CTLW0 &= ~UCSWRST; + + //UCA1IE |= UCRXIE; +} + +void SerialOutput::put(char c) +{ + while (!(UCA1IFG & UCTXIFG)); + UCA1TXBUF = c; + + if (c == '\n') { + put('\r'); + } +} + +SerialOutput sout; + +class SerialInput { + private: + SerialInput(const SerialInput ©); + char buffer[64]; + volatile unsigned char write_pos, read_pos; + + public: + SerialInput() : write_pos(0), read_pos(0) {} + void setup(); + bool hasKey(); + char getKey(); + + inline void addKey(char key) { + buffer[write_pos++] = key; + write_pos %= 64; + } +}; + +void SerialInput::setup() +{ + UCA1IE |= UCRXIE; +} + +bool SerialInput::hasKey() +{ + if (write_pos != read_pos) { + return true; + } + return false; +} + +char SerialInput::getKey() +{ + char ret = buffer[read_pos++]; + read_pos %= 64; + return ret; +} + +SerialInput sin; + +__attribute__((interrupt(USCI_A1_VECTOR))) __attribute__((wakeup)) void handle_stdin() +{ + if (UCA1IFG & UCRXIFG) { + sin.addKey(UCA1RXBUF); + } +} + +const unsigned char mhz19c_read[] = {0xff, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79}; + +void loop(void) +{ + static unsigned int interval = 0; + static char buf[9]; + unsigned char i; + unsigned int co2_ppm; + signed int temperature; + unsigned int unknown_status; + unsigned int unknown_cal; + if (interval++ == 2) { + gpio.led_on(0); + for (i = 0; i < sizeof(mhz19c_read); i++) { + sout.put(mhz19c_read[i]); + } + arch.sleep_ms(100); + buf[1] = 0; + for (i = 0; i < 9; i++) { + if (sin.hasKey()) { + buf[i] = sin.getKey(); + } else { + break; + } + } + if (i == 9) { + if (buf[0] == 0xff && buf[1] == 0x86) { + // see https://revspace.nl/MHZ19#Command_0x86_.28read_concentration.29 + co2_ppm = (buf[2] << 8) + buf[3]; + temperature = buf[4] - 40; + unknown_status = buf[5]; + unknown_cal = (buf[6] << 8) + buf[7]; + kout << "CO₂: " << co2_ppm << " ppm @ " << temperature << " °c "; + kout << unknown_status << " / " << unknown_cal << endl; + } else { + kout << "! communication error, expected 0xff 0x86, got" << hex << (int)buf[0] << ' ' << (int)buf[1] << dec << endl; + } + } else { + kout << "! timeout" << endl; + } + gpio.led_off(0); + interval = 0; + } +} + +int main(void) +{ + arch.setup(); + gpio.setup(); + kout.setup(); + sout.setup(); + sin.setup(); + + arch.idle_loop(); + + return 0; +} -- cgit v1.2.3