diff --git a/gpio/AM2302_sensor/AM2302-Sensor.cpp b/gpio/AM2302_sensor/AM2302-Sensor.cpp new file mode 100644 index 000000000..1f457e164 --- /dev/null +++ b/gpio/AM2302_sensor/AM2302-Sensor.cpp @@ -0,0 +1,253 @@ +/* +* AM2302-Sensor.cpp +* +* Author: Frank Häfele +* Date: 25.04.2024 +* +* Objective: AM2302-Sensor class +* +* SPDX-License-Identifier: BSD-3-Clause +*/ + +#include "AM2302-Sensor.hpp" + +/** + * @brief Construct a new am2302::am2302 sensor::am2302 sensor object + * + * @param pin Pin for AM2302 sensor + */ +AM2302::AM2302_Sensor::AM2302_Sensor(uint8_t pin) : _us_last_read{0}, _pin{pin} +{} + +/** + * @brief begin function setup pin and run sensor check. + * + * @return true if sensor check is successful. + * @return false if sensor check failed. + */ +bool AM2302::AM2302_Sensor::begin() { + gpio_init(_pin); + gpio_pull_up(_pin); + // required delay() for a secure sensor check, + // if you reset the mcu very fast one after another + auto tic{time_us_64()}; + while ( time_us_64() - tic < READ_FREQUENCY * 1000U ) { + sleep_ms(1U); + } + auto status{read()}; + _us_last_read = time_us_64(); + if (status == AM2302_READ_OK) { + return true; + } + else { + return false; + } +} + +/** + * @brief read functionality + * + * @return sensor status +*/ +int8_t AM2302::AM2302_Sensor::read() { + auto status{read_sensor()}; + + if (status == AM2302_READ_OK) { + // return status immediately + return status; + } + else if (status == AM2302_ERROR_READ_FREQ) { + return status; + } + else if (status == AM2302_ERROR_TIMEOUT) { + resetData(); + return status; + } + else if (status == AM2302_ERROR_CHECKSUM) { + // nothing to do + return status; + } +} + +/** + * @brief initiate start sequence and read sensor data + * + * @return sensor status +*/ +int8_t AM2302::AM2302_Sensor::read_sensor() { + // check read frequency + if ( time_us_64() - _us_last_read < READ_FREQUENCY * 1000U) { + return AM2302_ERROR_READ_FREQ; + } + _us_last_read = time_us_64(); + // ***************************** + // === send start sequence === + // **************************** + // start from HIGH ==> switch to LOW for min. of 1 ms + // Set pin to Output + gpio_set_dir(_pin, GPIO_OUT); + // set Pin to LOW + gpio_put(_pin, 0); + // wait min. 1,0 ms + sleep_us(1200U); + // Set port to HIGH ==> INPUT_PULLUP with PullUp + gpio_put(_pin, 1); + gpio_set_dir(_pin, GPIO_IN); + // delay_us(30.0); not needed + + // ****************************** + // === wait for Acknowledge === + // ****************************** + // Acknowledge Sequence 80us LOW 80 us HIGH + // wait for LOW (80 µs) + await_state(0); + // wait for HIGH (80 µs) + await_state(1); + + // ***************************** + // ==== Read Sensor Data ==== + // ***************************** + // ==> START of time critical code <== + // read 40 bits from sensor into the buffer: + // ==> HIGH state is 70 µs + // ==> LOW state is 28 µs + uint8_t _data[5U] = {0}; + if (read_sensor_data(_data, 5U) == AM2302_ERROR_TIMEOUT) { + return AM2302_ERROR_TIMEOUT; + } + // ==> END of time critical code <== + + // check checksum + _checksum_ok = (_data[4] == ( (_data[0] + _data[1] + _data[2] + _data[3]) & 0xFF) ); + + /* + // Code part to check the checksum + // Due to older sensors have an bug an deliver wrong data + auto d4 = _data[4]; + auto cs = ( (_data[0] + _data[1] + _data[2] + _data[3]) & 0xFF) ; + Serial.print("delivered Checksum: "); + AM2302_Tools::print_byte_as_bit(d4); + Serial.print("calculated Checksum: "); + AM2302_Tools::print_byte_as_bit(cs); + */ + + if (_checksum_ok) { + _hum = static_cast<uint16_t>((_data[0] << 8) | _data[1]); + if (_data[2] & 0x80) { + // negative temperature detected + _data[2] &= 0x7f; + _temp = -static_cast<int16_t>((_data[2] << 8) | _data[3]); + } + else { + _temp = static_cast<int16_t>((_data[2] << 8) | _data[3]); + } + return AM2302_READ_OK; + } + else { + return AM2302_ERROR_CHECKSUM; + } +} + +/** + * @brief wait for a specific pin state + * + * @param state state to wait for + * @return int8_t status + */ +int8_t AM2302::AM2302_Sensor::await_state(uint8_t state) { + uint8_t wait_counter{0}, state_counter{0}; + // count wait for state time + while ( (gpio_get(_pin) != state) ) { + ++wait_counter; + sleep_us(1U); + if (wait_counter >= READ_TIMEOUT) { + return AM2302_ERROR_TIMEOUT; + } + } + // count state time + while ( (gpio_get(_pin) == state) ) { + ++state_counter; + sleep_us(1U); + if (state_counter >= READ_TIMEOUT) { + return AM2302_ERROR_TIMEOUT; + } + } + return (state_counter > wait_counter); +} + +/** + * @brief read sensor data + * + * @param buffer data buffer of 40 bit + * @param size of buffer => 40 + * @return int8_t + */ +int8_t AM2302::AM2302_Sensor::read_sensor_data(uint8_t *buffer, uint8_t size) { + for (uint8_t i = 0; i < size; ++i) { + for (uint8_t bit = 0; bit < 8U; ++bit) { + uint8_t wait_counter{0}, state_counter{0}; + // count wait for state time + while ( !gpio_get(_pin) ) { + ++wait_counter; + sleep_us(1U); + if (wait_counter >= READ_TIMEOUT) { + return AM2302_ERROR_TIMEOUT; + } + } + // count state time + while ( gpio_get(_pin) ) { + ++state_counter; + sleep_us(1U); + if (state_counter >= READ_TIMEOUT) { + return AM2302_ERROR_TIMEOUT; + } + } + buffer[i] <<= 1; + buffer[i] |= (state_counter > wait_counter); + } + } + return AM2302_READ_OK; +} + +/** + * @brief get Sensor State in human readable manner + * + * @return sensor state +*/ +const char * AM2302::AM2302_Sensor::get_sensorState(int8_t state) { + if(state == AM2302_READ_OK) { + return AM2302_STATE_OK; + } + else if(state == AM2302_ERROR_CHECKSUM) { + return AM2302_STATE_ERR_CKSUM; + } + else if(state == AM2302_ERROR_TIMEOUT) { + return AM2302_STATE_ERR_TIMEOUT; + } + else if(state == AM2302_ERROR_READ_FREQ) { + return AM2302_STATE_ERR_READ_FREQ; + } +} + +/** + * @brief reset temperature and humidity data + * + */ +void AM2302::AM2302_Sensor::resetData() { + // reset tem to -255 and hum to 0 as indication + _temp = -2550; + _hum = 0; +} + +/** + * @brief helper function to print byte as bit + * + * @param value byte with 8 bits + */ +void AM2302_Tools::print_byte_as_bit(char value) { + for (int i = 7; i >= 0; --i) { + char c = (value & (1 << i)) ? '1' : '0'; + printf("%c", c); + } + printf("\n"); +} \ No newline at end of file diff --git a/gpio/AM2302_sensor/AM2302-Sensor.hpp b/gpio/AM2302_sensor/AM2302-Sensor.hpp new file mode 100644 index 000000000..f80a1b570 --- /dev/null +++ b/gpio/AM2302_sensor/AM2302-Sensor.hpp @@ -0,0 +1,63 @@ +/* +* AM2302-Sensor.hpp +* +* Author: Frank Häfele +* Date: 25.04.2024 +* +* Objective: AM2302-Sensor class +* +* SPDX-License-Identifier: BSD-3-Clause +*/ + +#ifndef __AM2302_SENSOR_H__ +#define __AM2302_SENSOR_H__ +#include <cstdint> +#include <cstdio> +#include "pico/stdlib.h" +#include "hardware/gpio.h" + +namespace AM2302 { + + constexpr const char * AM2302_STATE_OK{"OK"}; + constexpr const char * AM2302_STATE_ERR_CKSUM{"Error: Checksum"}; + constexpr const char * AM2302_STATE_ERR_TIMEOUT{"Error: Timeout"}; + constexpr const char * AM2302_STATE_ERR_READ_FREQ{"Error: Read Frequency"}; + + constexpr int8_t AM2302_READ_OK {0}; + constexpr int8_t AM2302_ERROR_CHECKSUM {-1}; + constexpr int8_t AM2302_ERROR_TIMEOUT {-2}; + constexpr int8_t AM2302_ERROR_READ_FREQ {-3}; + + // define timeout in 100 µs + constexpr uint8_t READ_TIMEOUT {100U}; + // read cycle max every 2 s + constexpr uint16_t READ_FREQUENCY {2000U}; + + class AM2302_Sensor { + + public: + explicit AM2302_Sensor(uint8_t pin); + bool begin(); + int8_t read(); + float get_Temperature() const {return _temp * 0.1F;} + float get_Humidity() const {return _hum * 0.1F;} + static const char * get_sensorState(int8_t state); + + private: + unsigned long _us_last_read; + uint16_t _hum {0}; + int16_t _temp {0}; + uint8_t _pin; + bool _checksum_ok {false}; + int8_t await_state(uint8_t state); + int8_t read_sensor(); + int8_t read_sensor_data(uint8_t *buffer, uint8_t const size); + void resetData(); + }; +} + +namespace AM2302_Tools { + void print_byte_as_bit(char value); +} + +#endif diff --git a/gpio/AM2302_sensor/AM2302.cpp b/gpio/AM2302_sensor/AM2302.cpp new file mode 100644 index 000000000..c2526f490 --- /dev/null +++ b/gpio/AM2302_sensor/AM2302.cpp @@ -0,0 +1,63 @@ +/** + * AM2302.cpp + * + * Author: Frank Häfele + * Date: 25.04.2024 + * + * Objective: Read one or multiple AM2302-Sensors + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <cstdio> +#include "pico/stdlib.h" +#include "AM2302-Sensor.hpp" + +const uint LED_PIN = PICO_DEFAULT_LED_PIN; + +// DHT Pin +const uint8_t SIZE = 3U; +const uint8_t PINS[SIZE] = {13U, 14U, 15U}; + +// create am2302 object +AM2302::AM2302_Sensor am2302[SIZE] = { + AM2302::AM2302_Sensor{PINS[0]}, + AM2302::AM2302_Sensor{PINS[1]}, + AM2302::AM2302_Sensor{PINS[2]} +}; + +int main() { + stdio_init_all(); + + gpio_init(LED_PIN); + gpio_set_dir(LED_PIN, GPIO_OUT); + + printf("\n\n === Pi Pico C++ Example - Read AM2302-Sensor === \n\n"); + // setup pin and do a sensor check + + for (size_t i = 0; i < SIZE; ++i) { + printf("Sensor available : %d\n", am2302[i].begin()); + } + printf("\n"); + sleep_ms(3000); + while (true) { + + gpio_put(LED_PIN, 1); + printf("\n\tStatus :"); + for (size_t i = 0; i < SIZE; ++i) { + printf("\t%s", AM2302::AM2302_Sensor::get_sensorState(am2302[i].read())); + } + printf("\n\tTemperature :"); + gpio_put(LED_PIN, 0); + + for (size_t i = 0; i < SIZE; ++i) { + printf("\t%5.2f", am2302[i].get_Temperature()); + } + printf("\n\tHumidity :"); + for (size_t i = 0; i < SIZE; ++i) { + printf("\t%5.2f", am2302[i].get_Humidity()); + } + printf("\n\n"); + sleep_ms(10000); + } +} \ No newline at end of file diff --git a/gpio/AM2302_sensor/CMakeLists.txt b/gpio/AM2302_sensor/CMakeLists.txt new file mode 100644 index 000000000..8ed89e4f3 --- /dev/null +++ b/gpio/AM2302_sensor/CMakeLists.txt @@ -0,0 +1,8 @@ +add_executable(AM2302 AM2302.cpp AM2302-Sensor.cpp) + +target_link_libraries(AM2302 pico_stdlib) + +pico_add_extra_outputs(AM2302) + +# add url via pico_set_program_url +example_auto_set_url(AM2302) \ No newline at end of file diff --git a/gpio/CMakeLists.txt b/gpio/CMakeLists.txt index f06bf41f6..66de9dec9 100644 --- a/gpio/CMakeLists.txt +++ b/gpio/CMakeLists.txt @@ -1,4 +1,5 @@ if (NOT PICO_NO_HARDWARE) + add_subdirectory(AM2302_sensor) add_subdirectory(dht_sensor) add_subdirectory(hello_7segment) add_subdirectory(hello_gpio_irq)