mirror of https://github.com/arendst/Tasmota.git
Fix sensor MHZ-19 vanishing data over time
5.13.1a * Fix sensor MHZ-19 vanishing data over time (#2659)
This commit is contained in:
parent
8ff8e6e69b
commit
b7b3f9b0d5
|
@ -29,51 +29,51 @@ extern "C" {
|
|||
|
||||
// As the Arduino attachInterrupt has no parameter, lists of objects
|
||||
// and callbacks corresponding to each possible GPIO pins have to be defined
|
||||
TasmotaSerial *ObjList[16];
|
||||
TasmotaSerial *tms_obj_list[16];
|
||||
|
||||
#ifdef TM_SERIAL_USE_IRAM
|
||||
void ICACHE_RAM_ATTR sws_isr_0() { ObjList[0]->rxRead(); };
|
||||
void ICACHE_RAM_ATTR sws_isr_1() { ObjList[1]->rxRead(); };
|
||||
void ICACHE_RAM_ATTR sws_isr_2() { ObjList[2]->rxRead(); };
|
||||
void ICACHE_RAM_ATTR sws_isr_3() { ObjList[3]->rxRead(); };
|
||||
void ICACHE_RAM_ATTR sws_isr_4() { ObjList[4]->rxRead(); };
|
||||
void ICACHE_RAM_ATTR sws_isr_5() { ObjList[5]->rxRead(); };
|
||||
void ICACHE_RAM_ATTR tms_isr_0() { tms_obj_list[0]->rxRead(); };
|
||||
void ICACHE_RAM_ATTR tms_isr_1() { tms_obj_list[1]->rxRead(); };
|
||||
void ICACHE_RAM_ATTR tms_isr_2() { tms_obj_list[2]->rxRead(); };
|
||||
void ICACHE_RAM_ATTR tms_isr_3() { tms_obj_list[3]->rxRead(); };
|
||||
void ICACHE_RAM_ATTR tms_isr_4() { tms_obj_list[4]->rxRead(); };
|
||||
void ICACHE_RAM_ATTR tms_isr_5() { tms_obj_list[5]->rxRead(); };
|
||||
// Pin 6 to 11 can not be used
|
||||
void ICACHE_RAM_ATTR sws_isr_12() { ObjList[12]->rxRead(); };
|
||||
void ICACHE_RAM_ATTR sws_isr_13() { ObjList[13]->rxRead(); };
|
||||
void ICACHE_RAM_ATTR sws_isr_14() { ObjList[14]->rxRead(); };
|
||||
void ICACHE_RAM_ATTR sws_isr_15() { ObjList[15]->rxRead(); };
|
||||
void ICACHE_RAM_ATTR tms_isr_12() { tms_obj_list[12]->rxRead(); };
|
||||
void ICACHE_RAM_ATTR tms_isr_13() { tms_obj_list[13]->rxRead(); };
|
||||
void ICACHE_RAM_ATTR tms_isr_14() { tms_obj_list[14]->rxRead(); };
|
||||
void ICACHE_RAM_ATTR tms_isr_15() { tms_obj_list[15]->rxRead(); };
|
||||
#else
|
||||
void sws_isr_0() { ObjList[0]->rxRead(); };
|
||||
void sws_isr_1() { ObjList[1]->rxRead(); };
|
||||
void sws_isr_2() { ObjList[2]->rxRead(); };
|
||||
void sws_isr_3() { ObjList[3]->rxRead(); };
|
||||
void sws_isr_4() { ObjList[4]->rxRead(); };
|
||||
void sws_isr_5() { ObjList[5]->rxRead(); };
|
||||
void tms_isr_0() { tms_obj_list[0]->rxRead(); };
|
||||
void tms_isr_1() { tms_obj_list[1]->rxRead(); };
|
||||
void tms_isr_2() { tms_obj_list[2]->rxRead(); };
|
||||
void tms_isr_3() { tms_obj_list[3]->rxRead(); };
|
||||
void tms_isr_4() { tms_obj_list[4]->rxRead(); };
|
||||
void tms_isr_5() { tms_obj_list[5]->rxRead(); };
|
||||
// Pin 6 to 11 can not be used
|
||||
void sws_isr_12() { ObjList[12]->rxRead(); };
|
||||
void sws_isr_13() { ObjList[13]->rxRead(); };
|
||||
void sws_isr_14() { ObjList[14]->rxRead(); };
|
||||
void sws_isr_15() { ObjList[15]->rxRead(); };
|
||||
void tms_isr_12() { tms_obj_list[12]->rxRead(); };
|
||||
void tms_isr_13() { tms_obj_list[13]->rxRead(); };
|
||||
void tms_isr_14() { tms_obj_list[14]->rxRead(); };
|
||||
void tms_isr_15() { tms_obj_list[15]->rxRead(); };
|
||||
#endif // TM_SERIAL_USE_IRAM
|
||||
|
||||
static void (*ISRList[16])() = {
|
||||
sws_isr_0,
|
||||
sws_isr_1,
|
||||
sws_isr_2,
|
||||
sws_isr_3,
|
||||
sws_isr_4,
|
||||
sws_isr_5,
|
||||
tms_isr_0,
|
||||
tms_isr_1,
|
||||
tms_isr_2,
|
||||
tms_isr_3,
|
||||
tms_isr_4,
|
||||
tms_isr_5,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
sws_isr_12,
|
||||
sws_isr_13,
|
||||
sws_isr_14,
|
||||
sws_isr_15
|
||||
tms_isr_12,
|
||||
tms_isr_13,
|
||||
tms_isr_14,
|
||||
tms_isr_15
|
||||
};
|
||||
|
||||
TasmotaSerial::TasmotaSerial(int receive_pin, int transmit_pin)
|
||||
|
@ -91,7 +91,7 @@ TasmotaSerial::TasmotaSerial(int receive_pin, int transmit_pin)
|
|||
// Use getCycleCount() loop to get as exact timing as possible
|
||||
m_bit_time = ESP.getCpuFreqMHz() *1000000 /TM_SERIAL_BAUDRATE;
|
||||
pinMode(m_rx_pin, INPUT);
|
||||
ObjList[m_rx_pin] = this;
|
||||
tms_obj_list[m_rx_pin] = this;
|
||||
attachInterrupt(m_rx_pin, ISRList[m_rx_pin], FALLING);
|
||||
}
|
||||
if (m_tx_pin > -1) {
|
|
@ -5,6 +5,7 @@
|
|||
* Fix rule power trigger when no backlog command is used (#2613)
|
||||
* Fix several timer data input and output errors (#2597, #2620)
|
||||
* Fix KNX config error (#2628)
|
||||
* Fix sensor MHZ-19 vanishing data over time (#2659)
|
||||
* Add Portuguese in Brazil language file
|
||||
* Add rule state test for On/Off in addition to 0/1 (#2613)
|
||||
* Updated Italian language file (#2618)
|
||||
|
|
|
@ -90,6 +90,7 @@
|
|||
#define D_JSON_PRESSUREATSEALEVEL "SeaPressure"
|
||||
#define D_JSON_PROGRAMFLASHSIZE "ProgramFlashSize"
|
||||
#define D_JSON_PROGRAMSIZE "ProgramSize"
|
||||
#define D_JSON_RESET "Reset"
|
||||
#define D_JSON_RESTARTING "Restarting"
|
||||
#define D_JSON_RESTARTREASON "RestartReason"
|
||||
#define D_JSON_RSSI "RSSI"
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
/*********************************************************************************************\
|
||||
* MH-Z19 - CO2 sensor
|
||||
*
|
||||
* Adapted from EspEasy plugin P049 by Dmitry (rel22 ___ inbox.ru)
|
||||
* Adapted from EspEasy plugin P049 by Dmitry (rel22 ___ inbox.ru)
|
||||
**********************************************************************************************
|
||||
* Filter usage
|
||||
*
|
||||
|
@ -64,17 +64,20 @@ enum MhzFilterOptions {MHZ19_FILTER_OFF, MHZ19_FILTER_OFF_ALLSAMPLES, MHZ19_FILT
|
|||
#define CO2_HIGH 1200 // Above this CO2 value show red light
|
||||
#endif
|
||||
|
||||
#define MHZ19_READ_TIMEOUT 500 // Must be way less than 1000
|
||||
#define MHZ19_READ_TIMEOUT 400 // Must be way less than 1000 but enough to read 9 bytes at 9600 bps
|
||||
#define MHZ19_RETRY_COUNT 8
|
||||
|
||||
TasmotaSerial *MhzSerial;
|
||||
|
||||
const char kMhzTypes[] PROGMEM = "MHZ19|MHZ19B";
|
||||
|
||||
const uint8_t mhz_cmnd_read_ppm[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79};
|
||||
const uint8_t mhz_cmnd_abc_enable[9] = {0xFF, 0x01, 0x79, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xE6};
|
||||
const uint8_t mhz_cmnd_abc_disable[9] = {0xFF, 0x01, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86};
|
||||
const uint8_t mhz_cmnd_zeropoint[9] = {0xff, 0x01, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78};
|
||||
enum MhzCommands { MHZ_CMND_READPPM, MHZ_CMND_ABCENABLE, MHZ_CMND_ABCDISABLE, MHZ_CMND_ZEROPOINT, MHZ_CMND_RESET };
|
||||
const uint8_t kMhzCommands[][2] PROGMEM = {
|
||||
{0x86,0x00}, // mhz_cmnd_read_ppm
|
||||
{0x79,0xA0}, // mhz_cmnd_abc_enable
|
||||
{0x79,0x00}, // mhz_cmnd_abc_disable
|
||||
{0x87,0x00}, // mhz_cmnd_zeropoint
|
||||
{0x8D,0x00}}; // mhz_cmnd_reset
|
||||
|
||||
uint8_t mhz_type = 1;
|
||||
uint16_t mhz_last_ppm = 0;
|
||||
|
@ -84,13 +87,41 @@ bool mhz_abc_must_apply = false;
|
|||
char mhz_types[7];
|
||||
|
||||
float mhz_temperature = 0;
|
||||
uint8_t mhz_timer = 0;
|
||||
uint8_t mhz_retry = MHZ19_RETRY_COUNT;
|
||||
|
||||
uint8_t mhz_received = 0;
|
||||
uint8_t mhz_state = 0;
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
byte MhzCalculateChecksum(byte *array)
|
||||
{
|
||||
byte checksum = 0;
|
||||
for (byte i = 1; i < 8; i++) {
|
||||
checksum += array[i];
|
||||
}
|
||||
checksum = 255 - checksum;
|
||||
return (checksum +1);
|
||||
}
|
||||
|
||||
size_t MhzSendCmd(byte command_id)
|
||||
{
|
||||
uint8_t mhz_send[9] = { 0 };
|
||||
|
||||
mhz_send[0] = 0xFF; // Start byte, fixed
|
||||
mhz_send[1] = 0x01; // Sensor number, 0x01 by default
|
||||
memcpy_P(&mhz_send[2], kMhzCommands[command_id], sizeof(kMhzCommands[0]));
|
||||
/*
|
||||
mhz_send[4] = 0x00;
|
||||
mhz_send[5] = 0x00;
|
||||
mhz_send[6] = 0x00;
|
||||
mhz_send[7] = 0x00;
|
||||
*/
|
||||
mhz_send[8] = MhzCalculateChecksum(mhz_send);
|
||||
return MhzSerial->write(mhz_send, sizeof(mhz_send));
|
||||
}
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
bool MhzCheckAndApplyFilter(uint16_t ppm, uint8_t s)
|
||||
{
|
||||
if (1 == s) {
|
||||
|
@ -126,89 +157,84 @@ bool MhzCheckAndApplyFilter(uint16_t ppm, uint8_t s)
|
|||
return true;
|
||||
}
|
||||
|
||||
void Mhz50ms()
|
||||
void MhzEverySecond()
|
||||
{
|
||||
mhz_state++;
|
||||
if (4 == mhz_state) { // Every 200 mSec
|
||||
if (8 == mhz_state) { // Every 8 sec start a MH-Z19 measuring cycle (which takes 1005 +5% ms)
|
||||
mhz_state = 0;
|
||||
|
||||
uint8_t mhz_response[9];
|
||||
|
||||
mhz_timer++;
|
||||
if (6 == mhz_timer) { // MH-Z19 measuring cycle takes 1005 +5% ms
|
||||
mhz_timer = 0;
|
||||
|
||||
MhzSerial->flush();
|
||||
MhzSerial->write(mhz_cmnd_read_ppm, 9);
|
||||
if (mhz_retry) {
|
||||
mhz_retry--;
|
||||
if (!mhz_retry) {
|
||||
mhz_last_ppm = 0;
|
||||
mhz_temperature = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (1 == mhz_timer) {
|
||||
if (mhz_retry) {
|
||||
mhz_retry--;
|
||||
if (!mhz_retry) {
|
||||
mhz_last_ppm = 0;
|
||||
mhz_temperature = 0;
|
||||
}
|
||||
}
|
||||
MhzSerial->flush(); // Sync reception
|
||||
MhzSendCmd(MHZ_CMND_READPPM);
|
||||
mhz_received = 0;
|
||||
}
|
||||
|
||||
unsigned long start = millis();
|
||||
uint8_t counter = 0;
|
||||
while (((millis() - start) < MHZ19_READ_TIMEOUT) && (counter < 9)) {
|
||||
if (MhzSerial->available() > 0) {
|
||||
mhz_response[counter++] = MhzSerial->read();
|
||||
}
|
||||
}
|
||||
if ((mhz_state > 2) && !mhz_received) { // Start reading response after 3 seconds every second until received
|
||||
uint8_t mhz_response[9];
|
||||
|
||||
AddLogSerial(LOG_LEVEL_DEBUG_MORE, mhz_response, counter);
|
||||
|
||||
if (counter < 9) {
|
||||
// AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "MH-Z19 comms timeout"));
|
||||
return;
|
||||
}
|
||||
|
||||
byte crc = 0;
|
||||
for (uint8_t i = 1; i < 8; i++) {
|
||||
crc += mhz_response[i];
|
||||
}
|
||||
crc = 255 - crc;
|
||||
crc++;
|
||||
if (mhz_response[8] != crc) {
|
||||
// AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "MH-Z19 crc error"));
|
||||
return;
|
||||
}
|
||||
if (0xFF != mhz_response[0] || 0x86 != mhz_response[1]) {
|
||||
// AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "MH-Z19 bad response"));
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t u = (mhz_response[6] << 8) | mhz_response[7];
|
||||
if (15000 == u) { // During (and only ever at) sensor boot, 'u' is reported as 15000
|
||||
if (!mhz_abc_enable) {
|
||||
// After bootup of the sensor the ABC will be enabled.
|
||||
// Thus only actively disable after bootup.
|
||||
mhz_abc_must_apply = true;
|
||||
}
|
||||
unsigned long start = millis();
|
||||
uint8_t counter = 0;
|
||||
while (((millis() - start) < MHZ19_READ_TIMEOUT) && (counter < 9)) {
|
||||
if (MhzSerial->available() > 0) {
|
||||
mhz_response[counter++] = MhzSerial->read();
|
||||
} else {
|
||||
uint16_t ppm = (mhz_response[2] << 8) | mhz_response[3];
|
||||
mhz_temperature = ConvertTemp((float)mhz_response[4] - 40);
|
||||
uint8_t s = mhz_response[5];
|
||||
mhz_type = (s) ? 1 : 2;
|
||||
if (MhzCheckAndApplyFilter(ppm, s)) {
|
||||
mhz_retry = MHZ19_RETRY_COUNT;
|
||||
LightSetSignal(CO2_LOW, CO2_HIGH, mhz_last_ppm);
|
||||
delay(5);
|
||||
}
|
||||
}
|
||||
|
||||
if (0 == s || 64 == s) { // Reading is stable.
|
||||
if (mhz_abc_must_apply) {
|
||||
mhz_abc_must_apply = false;
|
||||
if (mhz_abc_enable) {
|
||||
MhzSerial->write(mhz_cmnd_abc_enable, 9); // Sent sensor ABC Enable
|
||||
} else {
|
||||
MhzSerial->write(mhz_cmnd_abc_disable, 9); // Sent sensor ABC Disable
|
||||
}
|
||||
AddLogSerial(LOG_LEVEL_DEBUG_MORE, mhz_response, counter);
|
||||
|
||||
if (counter < 9) {
|
||||
// AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "MH-Z19 comms timeout"));
|
||||
return;
|
||||
}
|
||||
|
||||
byte crc = MhzCalculateChecksum(mhz_response);
|
||||
if (mhz_response[8] != crc) {
|
||||
// AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "MH-Z19 crc error"));
|
||||
return;
|
||||
}
|
||||
if (0xFF != mhz_response[0] || 0x86 != mhz_response[1]) {
|
||||
// AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "MH-Z19 bad response"));
|
||||
return;
|
||||
}
|
||||
|
||||
mhz_received = 1;
|
||||
|
||||
uint16_t u = (mhz_response[6] << 8) | mhz_response[7];
|
||||
if (15000 == u) { // During (and only ever at) sensor boot, 'u' is reported as 15000
|
||||
if (!mhz_abc_enable) {
|
||||
// After bootup of the sensor the ABC will be enabled.
|
||||
// Thus only actively disable after bootup.
|
||||
mhz_abc_must_apply = true;
|
||||
}
|
||||
} else {
|
||||
uint16_t ppm = (mhz_response[2] << 8) | mhz_response[3];
|
||||
mhz_temperature = ConvertTemp((float)mhz_response[4] - 40);
|
||||
uint8_t s = mhz_response[5];
|
||||
mhz_type = (s) ? 1 : 2;
|
||||
if (MhzCheckAndApplyFilter(ppm, s)) {
|
||||
mhz_retry = MHZ19_RETRY_COUNT;
|
||||
LightSetSignal(CO2_LOW, CO2_HIGH, mhz_last_ppm);
|
||||
|
||||
if (0 == s || 64 == s) { // Reading is stable.
|
||||
if (mhz_abc_must_apply) {
|
||||
mhz_abc_must_apply = false;
|
||||
if (mhz_abc_enable) {
|
||||
MhzSendCmd(MHZ_CMND_ABCENABLE);
|
||||
} else {
|
||||
MhzSendCmd(MHZ_CMND_ABCDISABLE);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -225,6 +251,8 @@ void Mhz50ms()
|
|||
2 - Manual start = ABC Off
|
||||
|
||||
3 - Optional filter settings
|
||||
|
||||
9 - Reset
|
||||
*/
|
||||
|
||||
bool MhzCommandSensor()
|
||||
|
@ -233,9 +261,13 @@ bool MhzCommandSensor()
|
|||
|
||||
switch (XdrvMailbox.payload) {
|
||||
case 2:
|
||||
MhzSerial->write(mhz_cmnd_zeropoint, 9);
|
||||
MhzSendCmd(MHZ_CMND_ZEROPOINT);
|
||||
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, D_JSON_ZERO_POINT_CALIBRATION);
|
||||
break;
|
||||
case 9:
|
||||
MhzSendCmd(MHZ_CMND_RESET);
|
||||
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, D_JSON_RESET);
|
||||
break;
|
||||
default:
|
||||
serviced = false;
|
||||
}
|
||||
|
@ -250,7 +282,7 @@ void MhzInit()
|
|||
mhz_type = 0;
|
||||
if ((pin[GPIO_MHZ_RXD] < 99) && (pin[GPIO_MHZ_TXD] < 99)) {
|
||||
MhzSerial = new TasmotaSerial(pin[GPIO_MHZ_RXD], pin[GPIO_MHZ_TXD]);
|
||||
if (MhzSerial->begin()) {
|
||||
if (MhzSerial->begin(9600)) {
|
||||
mhz_type = 1;
|
||||
}
|
||||
}
|
||||
|
@ -288,8 +320,8 @@ boolean Xsns15(byte function)
|
|||
case FUNC_INIT:
|
||||
MhzInit();
|
||||
break;
|
||||
case FUNC_EVERY_50_MSECOND:
|
||||
Mhz50ms();
|
||||
case FUNC_EVERY_SECOND:
|
||||
MhzEverySecond();
|
||||
break;
|
||||
case FUNC_COMMAND:
|
||||
if (XSNS_15 == XdrvMailbox.index) {
|
||||
|
|
Loading…
Reference in New Issue