Add SoftwareSerial to CSE7766 driver

Add SoftwareSerial to CSE7766 driver allowing different GPIOs (#7563)
This commit is contained in:
Theo Arends 2020-01-21 16:38:55 +01:00
parent 760f4e49ac
commit a61c028b66
3 changed files with 77 additions and 43 deletions

View File

@ -80,3 +80,5 @@ The following binary downloads have been compiled with ESP8266/Arduino library c
- Add support to BMP driver to enter reset state (sleep enable) when deep sleep is used in Tasmota - Add support to BMP driver to enter reset state (sleep enable) when deep sleep is used in Tasmota
- Add support for gzipped binaries - Add support for gzipped binaries
- Add web page sliders when ``SetOption37 128`` is active allowing control of white(s) - Add web page sliders when ``SetOption37 128`` is active allowing control of white(s)
- Add most SetOptions as defines to my_user_config.h
- Add SoftwareSerial to CSE7766 driver allowing different GPIOs (#7563)

View File

@ -6,6 +6,8 @@
- Fix OTA minimal gzipped detection regression from 8.1.0.3 - Fix OTA minimal gzipped detection regression from 8.1.0.3
- Add web page sliders when ``SetOption37 128`` is active allowing control of white(s) - Add web page sliders when ``SetOption37 128`` is active allowing control of white(s)
- Add Zigbee persistence and friendly names - Add Zigbee persistence and friendly names
- Add most SetOptions as defines to my_user_config.h
- Add SoftwareSerial to CSE7766 driver allowing different GPIOs (#7563)
### 8.1.0.3 20200106 ### 8.1.0.3 20200106

View File

@ -20,7 +20,7 @@
#ifdef USE_ENERGY_SENSOR #ifdef USE_ENERGY_SENSOR
#ifdef USE_CSE7766 #ifdef USE_CSE7766
/*********************************************************************************************\ /*********************************************************************************************\
* CSE7766 - Energy (Sonoff S31 and Sonoff Pow R2) * CSE7759 and CSE7766 - Energy (Sonoff S31 and Sonoff Pow R2)
* HLW8032 - Energy (Blitzwolf SHP5) * HLW8032 - Energy (Blitzwolf SHP5)
* *
* Based on datasheet from http://www.chipsea.com/UploadFiles/2017/08/11144342F01B5662.pdf * Based on datasheet from http://www.chipsea.com/UploadFiles/2017/08/11144342F01B5662.pdf
@ -37,6 +37,12 @@
#define CSE_PREF 1000 #define CSE_PREF 1000
#define CSE_UREF 100 #define CSE_UREF 100
#define CSE_BUFFER_SIZE 25
#include <TasmotaSerial.h>
TasmotaSerial *CseSerial = nullptr;
struct CSE { struct CSE {
long voltage_cycle = 0; long voltage_cycle = 0;
long current_cycle = 0; long current_cycle = 0;
@ -45,6 +51,8 @@ struct CSE {
long cf_pulses = 0; long cf_pulses = 0;
long cf_pulses_last_time = CSE_PULSES_NOT_INITIALIZED; long cf_pulses_last_time = CSE_PULSES_NOT_INITIALIZED;
int byte_counter = 0;
uint8_t *rx_buffer = nullptr;
uint8_t power_invalid = 0; uint8_t power_invalid = 0;
bool received = false; bool received = false;
} Cse; } Cse;
@ -57,7 +65,7 @@ void CseReceived(void)
// 55 5A 02 F7 60 00 03 AB 00 40 10 02 60 5D 51 A6 58 03 E9 EF 71 0B 7A 36 - 55 = Ok, 71 = Ok // 55 5A 02 F7 60 00 03 AB 00 40 10 02 60 5D 51 A6 58 03 E9 EF 71 0B 7A 36 - 55 = Ok, 71 = Ok
// Hd Id VCal---- Voltage- ICal---- Current- PCal---- Power--- Ad CF--- Ck // Hd Id VCal---- Voltage- ICal---- Current- PCal---- Power--- Ad CF--- Ck
uint8_t header = serial_in_buffer[0]; uint8_t header = Cse.rx_buffer[0];
if ((header & 0xFC) == 0xFC) { if ((header & 0xFC) == 0xFC) {
AddLog_P(LOG_LEVEL_DEBUG, PSTR("CSE: Abnormal hardware")); AddLog_P(LOG_LEVEL_DEBUG, PSTR("CSE: Abnormal hardware"));
return; return;
@ -67,30 +75,30 @@ void CseReceived(void)
if (HLW_UREF_PULSE == Settings.energy_voltage_calibration) { if (HLW_UREF_PULSE == Settings.energy_voltage_calibration) {
long voltage_coefficient = 191200; // uSec long voltage_coefficient = 191200; // uSec
if (CSE_NOT_CALIBRATED != header) { if (CSE_NOT_CALIBRATED != header) {
voltage_coefficient = serial_in_buffer[2] << 16 | serial_in_buffer[3] << 8 | serial_in_buffer[4]; voltage_coefficient = Cse.rx_buffer[2] << 16 | Cse.rx_buffer[3] << 8 | Cse.rx_buffer[4];
} }
Settings.energy_voltage_calibration = voltage_coefficient / CSE_UREF; Settings.energy_voltage_calibration = voltage_coefficient / CSE_UREF;
} }
if (HLW_IREF_PULSE == Settings.energy_current_calibration) { if (HLW_IREF_PULSE == Settings.energy_current_calibration) {
long current_coefficient = 16140; // uSec long current_coefficient = 16140; // uSec
if (CSE_NOT_CALIBRATED != header) { if (CSE_NOT_CALIBRATED != header) {
current_coefficient = serial_in_buffer[8] << 16 | serial_in_buffer[9] << 8 | serial_in_buffer[10]; current_coefficient = Cse.rx_buffer[8] << 16 | Cse.rx_buffer[9] << 8 | Cse.rx_buffer[10];
} }
Settings.energy_current_calibration = current_coefficient; Settings.energy_current_calibration = current_coefficient;
} }
if (HLW_PREF_PULSE == Settings.energy_power_calibration) { if (HLW_PREF_PULSE == Settings.energy_power_calibration) {
long power_coefficient = 5364000; // uSec long power_coefficient = 5364000; // uSec
if (CSE_NOT_CALIBRATED != header) { if (CSE_NOT_CALIBRATED != header) {
power_coefficient = serial_in_buffer[14] << 16 | serial_in_buffer[15] << 8 | serial_in_buffer[16]; power_coefficient = Cse.rx_buffer[14] << 16 | Cse.rx_buffer[15] << 8 | Cse.rx_buffer[16];
} }
Settings.energy_power_calibration = power_coefficient / CSE_PREF; Settings.energy_power_calibration = power_coefficient / CSE_PREF;
} }
uint8_t adjustement = serial_in_buffer[20]; uint8_t adjustement = Cse.rx_buffer[20];
Cse.voltage_cycle = serial_in_buffer[5] << 16 | serial_in_buffer[6] << 8 | serial_in_buffer[7]; Cse.voltage_cycle = Cse.rx_buffer[5] << 16 | Cse.rx_buffer[6] << 8 | Cse.rx_buffer[7];
Cse.current_cycle = serial_in_buffer[11] << 16 | serial_in_buffer[12] << 8 | serial_in_buffer[13]; Cse.current_cycle = Cse.rx_buffer[11] << 16 | Cse.rx_buffer[12] << 8 | Cse.rx_buffer[13];
Cse.power_cycle = serial_in_buffer[17] << 16 | serial_in_buffer[18] << 8 | serial_in_buffer[19]; Cse.power_cycle = Cse.rx_buffer[17] << 16 | Cse.rx_buffer[18] << 8 | Cse.rx_buffer[19];
Cse.cf_pulses = serial_in_buffer[21] << 8 | serial_in_buffer[22]; Cse.cf_pulses = Cse.rx_buffer[21] << 8 | Cse.rx_buffer[22];
if (Energy.power_on) { // Powered on if (Energy.power_on) { // Powered on
if (adjustement & 0x40) { // Voltage valid if (adjustement & 0x40) { // Voltage valid
@ -134,41 +142,44 @@ void CseReceived(void)
bool CseSerialInput(void) bool CseSerialInput(void)
{ {
if (Cse.received) { while (CseSerial->available()) {
serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; yield();
if (24 == serial_in_byte_counter) { uint8_t serial_in_byte = CseSerial->read();
AddLogSerial(LOG_LEVEL_DEBUG_MORE); if (Cse.received) {
Cse.rx_buffer[Cse.byte_counter++] = serial_in_byte;
if (24 == Cse.byte_counter) {
uint8_t checksum = 0; AddLogBuffer(LOG_LEVEL_DEBUG_MORE, Cse.rx_buffer, 24);
for (uint32_t i = 2; i < 23; i++) { checksum += serial_in_buffer[i]; }
if (checksum == serial_in_buffer[23]) { uint8_t checksum = 0;
Energy.data_valid[0] = 0; for (uint32_t i = 2; i < 23; i++) { checksum += Cse.rx_buffer[i]; }
CseReceived(); if (checksum == Cse.rx_buffer[23]) {
Cse.received = false; Energy.data_valid[0] = 0;
return true; CseReceived();
} else {
AddLog_P(LOG_LEVEL_DEBUG, PSTR("CSE: " D_CHECKSUM_FAILURE));
do { // Sync buffer with data (issue #1907 and #3425)
memmove(serial_in_buffer, serial_in_buffer +1, 24);
serial_in_byte_counter--;
} while ((serial_in_byte_counter > 2) && (0x5A != serial_in_buffer[1]));
if (0x5A != serial_in_buffer[1]) {
Cse.received = false; Cse.received = false;
serial_in_byte_counter = 0; return true;
} else {
AddLog_P(LOG_LEVEL_DEBUG, PSTR("CSE: " D_CHECKSUM_FAILURE));
do { // Sync buffer with data (issue #1907 and #3425)
memmove(Cse.rx_buffer, Cse.rx_buffer +1, 24);
Cse.byte_counter--;
} while ((Cse.byte_counter > 2) && (0x5A != Cse.rx_buffer[1]));
if (0x5A != Cse.rx_buffer[1]) {
Cse.received = false;
Cse.byte_counter = 0;
}
} }
} }
}
} else {
if ((0x5A == serial_in_byte) && (1 == serial_in_byte_counter)) { // 0x5A - Packet header 2
Cse.received = true;
} else { } else {
serial_in_byte_counter = 0; if ((0x5A == serial_in_byte) && (1 == Cse.byte_counter)) { // 0x5A - Packet header 2
Cse.received = true;
} else {
Cse.byte_counter = 0;
}
Cse.rx_buffer[Cse.byte_counter++] = serial_in_byte;
} }
serial_in_buffer[serial_in_byte_counter++] = serial_in_byte;
} }
serial_in_byte = 0; // Discard
return false;
} }
/********************************************************************************************/ /********************************************************************************************/
@ -209,15 +220,31 @@ void CseEverySecond(void)
} }
} }
void CseDrvInit(void) void CseSnsInit(void)
{ {
if ((3 == pin[GPIO_CSE7766_RX]) && (1 == pin[GPIO_CSE7766_TX])) { // As it uses 8E1 currently only hardware serial is supported // Software serial init needs to be done here as earlier (serial) interrupts may lead to Exceptions
SetSerial(4800, TS_SERIAL_8E1); CseSerial = new TasmotaSerial(pin[GPIO_CSE7766_RX], pin[GPIO_CSE7766_TX], 1);
if (CseSerial->begin(4800, 2)) { // Fake Software Serial 8E1 by using two stop bits
if (CseSerial->hardwareSerial()) {
SetSerial(4800, TS_SERIAL_8E1);
ClaimSerial();
}
if (0 == Settings.param[P_CSE7766_INVALID_POWER]) { if (0 == Settings.param[P_CSE7766_INVALID_POWER]) {
Settings.param[P_CSE7766_INVALID_POWER] = CSE_MAX_INVALID_POWER; // SetOption39 1..255 Settings.param[P_CSE7766_INVALID_POWER] = CSE_MAX_INVALID_POWER; // SetOption39 1..255
} }
Cse.power_invalid = Settings.param[P_CSE7766_INVALID_POWER]; Cse.power_invalid = Settings.param[P_CSE7766_INVALID_POWER];
energy_flg = XNRG_02; } else {
energy_flg = ENERGY_NONE;
}
}
void CseDrvInit(void)
{
Cse.rx_buffer = (uint8_t*)(malloc(CSE_BUFFER_SIZE));
if (Cse.rx_buffer != nullptr) {
if ((pin[GPIO_CSE7766_RX] < 99) && (pin[GPIO_CSE7766_TX] < 99)) {
energy_flg = XNRG_02;
}
} }
} }
@ -254,8 +281,8 @@ bool Xnrg02(uint8_t function)
bool result = false; bool result = false;
switch (function) { switch (function) {
case FUNC_SERIAL: case FUNC_LOOP:
result = CseSerialInput(); if (CseSerial) { CseSerialInput(); }
break; break;
case FUNC_ENERGY_EVERY_SECOND: case FUNC_ENERGY_EVERY_SECOND:
CseEverySecond(); CseEverySecond();
@ -263,6 +290,9 @@ bool Xnrg02(uint8_t function)
case FUNC_COMMAND: case FUNC_COMMAND:
result = CseCommand(); result = CseCommand();
break; break;
case FUNC_INIT:
CseSnsInit();
break;
case FUNC_PRE_INIT: case FUNC_PRE_INIT:
CseDrvInit(); CseDrvInit();
break; break;