Ze08-CH2O
#include <SoftwareSerial.h>
// Activate debug messages output
#define DEBUG_ON
#define WINSEN_ZE08_CH2O_UART_TX_PIN (2)
#define WINSEN_ZE08_CH2O_UART_SPEED (9600)
#define WINSEN_ZE08_CH2O_UART_DEFAULT_READ_TIMEOUT (2500UL)
#define WINSEN_ZE08_CH2O_UART_START_BYTE (0xFF)
#define WINSEN_ZE08_CH2O_UART_GAS_NAME (0x17)
#define WINSEN_ZE08_CH2O_UART_GAS_UNIT (0x04)
// We can use 0xFFFF as error code because sensor's ppb range is 0x00...0x1388 (0...5000)
#define WINSEN_ZE08_CH2O_READ_ERROR_CODE (0xFFFF)
// Store all metric's value in the pretty struct
typedef struct {
uint8_t gasName;
uint8_t gasUnit;
uint8_t noDecimalByte;
uint8_t concentrationHighByte, concentrationLowByte;
uint8_t fullRangeHighByte, fullRangeLowByte;
uint8_t checkSum;
} Ze08Ch2OData_t;
uint16_t getZe08Ch2OMetric(const uint8_t _rxPin, const uint8_t _txPin) {
uint8_t headerDetected = false,
writePos = 0x00,
checkSum = 0x00;
uint16_t rc = WINSEN_ZE08_CH2O_READ_ERROR_CODE;
uint32_t readStartTime;;
Ze08Ch2OData_t Ze08Ch2OData;
uint8_t* ptrRawBuffer = (uint8_t*) &Ze08Ch2OData;
SoftwareSerial swSerial(_rxPin, _txPin);
swSerial.begin(WINSEN_ZE08_CH2O_UART_SPEED);
readStartTime = millis();
while ((sizeof(Ze08Ch2OData) > writePos) && (WINSEN_ZE08_CH2O_UART_DEFAULT_READ_TIMEOUT > millis() - readStartTime)) {
if (!swSerial.available()) {
continue;
}
ptrRawBuffer[writePos] = swSerial.read();
#if defined(DEBUG_ON)
//Serial.print("currentChar (# "); Serial.print(writePos); Serial.print(") => 0x"); Serial.println((byte) ptrRawBuffer[writePos], HEX);
#endif
if (!headerDetected) {
headerDetected = (WINSEN_ZE08_CH2O_UART_START_BYTE == ptrRawBuffer[0x00]);
} else {
writePos++;
}
}
// Reading is not finished sucessfully: not all bytes recieved or wrong Gas ID / Unit ID contained in the packet
if (writePos < sizeof(Ze08Ch2OData) ||
WINSEN_ZE08_CH2O_UART_GAS_NAME != Ze08Ch2OData.gasName ||
WINSEN_ZE08_CH2O_UART_GAS_UNIT != Ze08Ch2OData.gasUnit) {
goto finish;
}
// Calculate checksum.
//Start byte & recieved checksum is not taken in account. The first one is dropped in the read procedure and the second one just will skipped in calculation
for (uint8_t i = 0x00; i < sizeof(Ze08Ch2OData) - 1; i++) {
checkSum += ptrRawBuffer[i];
#if defined(DEBUG_ON)
Serial.print(" 0x"); Serial.print(ptrRawBuffer[i], HEX);
#endif
}
checkSum = (~checkSum) + 1;
#if defined(DEBUG_ON)
Serial.print("\nRecieved / calculated checksum: 0x");
Serial.print(Ze08Ch2OData.checkSum, HEX); Serial.print(" / 0x"); Serial.println(checkSum, HEX);
#endif
if (checkSum == Ze08Ch2OData.checkSum) {
#if defined(DEBUG_ON)
// rc = (Ze08Ch2OData.fullRangeHighByte << 0x08) + Ze08Ch2OData.fullRangeLowByte; Serial.print("Full range (ppb): "); Serial.println(rc);
#endif
rc = (Ze08Ch2OData.concentrationHighByte << 0x08) + Ze08Ch2OData.concentrationLowByte;
}
finish:
swSerial.~SoftwareSerial();
return rc;
}
void setup() {
Serial.begin(115200);
Serial.println("Winsen ZE08-CH2O sensor reading demo (UART, active mode)"); ;
}
void loop() {
delay(2000);
uint16_t ch2O = getZe08Ch2OMetric(WINSEN_ZE08_CH2O_UART_TX_PIN, WINSEN_ZE08_CH2O_UART_TX_PIN);
Serial.print("["); Serial.print(millis()); Serial.print("] CH2O: ");
if (WINSEN_ZE08_CH2O_READ_ERROR_CODE != ch2O) {
Serial.print(ch2O); Serial.print(" ppb, \t"); Serial.print(ch2O / 1000.0, 3); Serial.println(" ppm");
} else {
Serial.println("read error");
}
}