Skip to content

Add additional C++ Example for AM2302-Sensor #450

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
253 changes: 253 additions & 0 deletions gpio/AM2302_sensor/AM2302-Sensor.cpp
Original file line number Diff line number Diff line change
@@ -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");
}
63 changes: 63 additions & 0 deletions gpio/AM2302_sensor/AM2302-Sensor.hpp
Original file line number Diff line number Diff line change
@@ -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
63 changes: 63 additions & 0 deletions gpio/AM2302_sensor/AM2302.cpp
Original file line number Diff line number Diff line change
@@ -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);
}
}
8 changes: 8 additions & 0 deletions gpio/AM2302_sensor/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
1 change: 1 addition & 0 deletions gpio/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
Expand Down