Fork of the espurna firmware for `mhsw` switches
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

234 lines
6.3 KiB

// -----------------------------------------------------------------------------
// SenseAir S8 CO2 Sensor
// Uses SoftwareSerial library
// Contribution by Yonsm Guo
// -----------------------------------------------------------------------------
#if SENSOR_SUPPORT && SENSEAIR_SUPPORT
#pragma once
#include <Arduino.h>
#include <SoftwareSerial.h>
#include "BaseSensor.h"
// SenseAir sensor utils
class SenseAir
{
protected:
SoftwareSerial *_serial; // Should initialized by child class
public:
int sendCommand(byte command[]) {
byte recv_buf[7] = {0xff};
byte data_buf[2] = {0xff};
long value = -1;
_serial->write(command, 8); //Send the byte array
delay(50);
// Read answer from sensor
int ByteCounter = 0;
while(_serial->available()) {
recv_buf[ByteCounter] = _serial->read();
ByteCounter++;
}
data_buf[0] = recv_buf[3];
data_buf[1] = recv_buf[4];
value = (data_buf[0] << 8) | (data_buf[1]);
return value;
}
int readCo2(void) {
int co2 = 0;
byte frame[8] = {0};
buildFrame(0xFE, 0x04, 0x03, 1, frame);
co2 = sendCommand(frame);
return co2;
}
private:
// Compute the MODBUS RTU CRC
static unsigned int modRTU_CRC(byte buf[], int len, byte checkSum[2]) {
unsigned int crc = 0xFFFF;
for (int pos = 0; pos < len; pos++) {
crc ^= (unsigned int)buf[pos]; // XOR byte into least sig. byte of crc
for (int i = 8; i != 0; i--) { // Loop over each bit
if ((crc & 0x0001) != 0) { // If the LSB is set
crc >>= 1; // Shift right and XOR 0xA001
crc ^= 0xA001;
}
else // Else LSB is not set
crc >>= 1; // Just shift right
}
}
// Note, this number has low and high bytes swapped, so use it accordingly (or swap bytes)
checkSum[1] = (byte)((crc >> 8) & 0xFF);
checkSum[0] = (byte)(crc & 0xFF);
return crc;
}
static int getBitOfInt(int reg, int pos) {
// Create a mask
int mask = 0x01 << pos;
// Mask the status register
int masked_register = mask & reg;
// Shift the result of masked register back to position 0
int result = masked_register >> pos;
return result;
}
static void buildFrame(byte slaveAddress,
byte functionCode,
short startAddress,
short numberOfRegisters,
byte frame[8]) {
frame[0] = slaveAddress;
frame[1] = functionCode;
frame[2] = (byte)(startAddress >> 8);
frame[3] = (byte)(startAddress);
frame[4] = (byte)(numberOfRegisters >> 8);
frame[5] = (byte)(numberOfRegisters);
// CRC-calculation
byte checkSum[2] = {0};
modRTU_CRC(frame, 6, checkSum);
frame[6] = checkSum[0];
frame[7] = checkSum[1];
}
};
//
class SenseAirSensor : public BaseSensor, SenseAir {
public:
// ---------------------------------------------------------------------
// Public
// ---------------------------------------------------------------------
SenseAirSensor() {
_count = 1;
_co2 = 0;
_lastCo2 = 0;
_serial = NULL;
_sensor_id = SENSOR_SENSEAIR_ID;
}
~SenseAirSensor() {
if (_serial) delete _serial;
_serial = NULL;
}
void setRX(unsigned char pin_rx) {
if (_pin_rx == pin_rx) return;
_pin_rx = pin_rx;
_dirty = true;
}
void setTX(unsigned char pin_tx) {
if (_pin_tx == pin_tx) return;
_pin_tx = pin_tx;
_dirty = true;
}
// ---------------------------------------------------------------------
unsigned char getRX() {
return _pin_rx;
}
unsigned char getTX() {
return _pin_tx;
}
// ---------------------------------------------------------------------
// Sensor API
// ---------------------------------------------------------------------
// Initialization method, must be idempotent
void begin() {
if (!_dirty) return;
if (_serial) delete _serial;
_serial = new SoftwareSerial(_pin_rx, _pin_tx, false, 64);
_serial->enableIntTx(false);
_serial->begin(9600);
_serial->enableRx(true);
_startTime = 0;
_ready = true;
_dirty = false;
}
// Descriptive name of the sensor
String description() {
char buffer[28];
snprintf(buffer, sizeof(buffer), "SenseAir S8 @ SwSerial(%u,%u)", _pin_rx, _pin_tx);
return String(buffer);
}
// Descriptive name of the slot # index
String slot(unsigned char index) {
return description();
}
// Address of the sensor (it could be the GPIO or I2C address)
String address(unsigned char index) {
char buffer[6];
snprintf(buffer, sizeof(buffer), "%u:%u", _pin_rx, _pin_tx);
return String(buffer);
}
// Type for slot # index
unsigned char type(unsigned char index) {
return MAGNITUDE_CO2;
}
void pre() {
if (millis() - _startTime < 20000) {
_error = SENSOR_ERROR_WARM_UP;
return;
}
unsigned int co2 = readCo2();
if (co2 >= 5000 || co2 < 100)
{
_co2 = _lastCo2;
_error = SENSOR_ERROR_OUT_OF_RANGE;
}
else
{
_co2 = (co2 > _lastCo2 + 2000) ? _lastCo2 : co2;
_lastCo2 = co2;
_error = SENSOR_ERROR_OK;
}
}
// Current value for slot # index
double value(unsigned char index) {
return _co2;
}
protected:
unsigned int _pin_rx;
unsigned int _pin_tx;
unsigned long _startTime;
unsigned int _co2;
unsigned int _lastCo2;
};
#endif // SENSOR_SUPPORT && SENSEAIR_SUPPORT