Rewrite Sonoff SPM state machine

- Rewrite Sonoff SPM state machine for easier maintenance
- Fix handling user output when initiating a scan
This commit is contained in:
Theo Arends 2021-11-20 17:29:10 +01:00
parent 25f23b2406
commit efb77a6547
1 changed files with 89 additions and 65 deletions

View File

@ -30,7 +30,7 @@
* Bulk of the action is handled by ARM processors present in every unit communicating over modbus RS-485.
* Each SPM-4Relay has 4 bistable relays with their own CSE7761 energy monitoring device handled by an ARM processor.
* Green led is controlled by ARM processor indicating SD-Card access.
* ESP32 is used as interface between Welink and ARM processor in SPM-Main unit communicating over proprietary serial protocol.
* ESP32 is used as interface between eWelink and ARM processor in SPM-Main unit communicating over proprietary serial protocol.
* Inductive/Capacitive loads are not reported correctly.
* Power on sequence for two SPM-4Relay modules is 00-00-15-10-(0F)-(13)-(13)-(19)-0C-09-04-09-04-0B-0B
*
@ -124,10 +124,19 @@
#define SSPM_MODULE_NAME_SIZE 12
enum SspmInitSteps { SPM_NONE, SPM_WAIT, SPM_RESET, SPM_POLL, SPM_POLL_ACK, SPM_SEND_FUNC_UNITS, SPM_STEP_WAIT2,
SPM_DEVICES_FOUND,
SPM_GET_ENERGY_TOTALS,
SPM_ALLOW_LOOP};
enum SspmMachineStates { SPM_NONE, // Do nothing
SPM_WAIT, // Wait 100ms
SPM_RESET, // Toggle ARM reset pin
SPM_POLL_ARM, // Wait for first acknowledge from ARM after reset
SPM_POLL_ARM_2, // Wait for second acknowledge from ARM after reset
SPM_SEND_FUNC_UNITS, // Get number of units
SPM_START_SCAN, // Start module scan sequence
SPM_WAIT_FOR_SCAN, // Wait for scan sequence to complete
SPM_SCAN_COMPLETE, // Scan complete
SPM_GET_ENERGY_TOTALS, // Init available Energy totals registers
SPM_UPDATE_CHANNELS, // Update Energy for powered on channels
SPM_UPDATE_TOTALS // Update Energy totals for powered on channels
};
#include <TasmotaSerial.h>
TasmotaSerial *SspmSerial;
@ -156,7 +165,7 @@ typedef struct {
uint8_t counter;
uint8_t command_sequence;
uint8_t loop_step;
uint8_t init_step;
uint8_t mstate;
uint8_t last_button;
bool discovery_triggered;
} TSspm;
@ -528,7 +537,7 @@ void SSPMHandleReceivedData(void) {
|Er| |St|
*/
if ((1 == Sspm->expected_bytes) && (0 == SspmBuffer[19])) {
Sspm->init_step++;
Sspm->mstate++; // Cycle to
}
break;
case SSPM_FUNC_GET_OPS:
@ -591,15 +600,14 @@ void SSPMHandleReceivedData(void) {
} else {
AddLog(LOG_LEVEL_DEBUG, PSTR("SPM: Relay scan done"));
Sspm->init_step = SPM_DEVICES_FOUND;
// Sspm->get_energy_relay = 1;
// Sspm->allow_updates = 1; // Enable requests from 100mSec loop
Sspm->mstate = SPM_SCAN_COMPLETE;
}
break;
case SSPM_FUNC_SET_TIME:
/* 0x0C
AA 55 01 00 00 00 00 00 00 00 00 00 00 00 00 80 0c 00 01 00 04 3e 62
*/
TasmotaGlobal.devices_present = 0;
SSPMSendGetModuleState(Sspm->module_selected -1);
break;
case SSPM_FUNC_INIT_SCAN:
@ -607,7 +615,6 @@ void SSPMHandleReceivedData(void) {
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
AA 55 01 ff ff ff ff ff ff ff ff ff ff ff ff 80 10 00 01 00 02 e5 03
*/
Sspm->module_max = 0;
break;
case SSPM_FUNC_UNITS:
/* 0x15
@ -615,7 +622,7 @@ void SSPMHandleReceivedData(void) {
AA 55 01 00 00 00 00 00 00 00 00 00 00 00 00 80 15 00 04 00 01 00 00 01 81 b1
|?? ?? ?? ??|
*/
SSPMSendInitScan();
Sspm->mstate = SPM_START_SCAN;
break;
case SSPM_FUNC_GET_ENERGY_TOTAL:
/* 0x16
@ -746,7 +753,6 @@ void SSPMHandleReceivedData(void) {
/* 0x0F
AA 55 01 00 00 00 00 00 00 00 00 00 00 00 00 00 0f 00 01 02 01 9d f8
*/
// Sspm->module_max = 0;
SSPMSendAck(command_sequence);
break;
case SSPM_FUNC_SCAN_RESULT:
@ -851,7 +857,7 @@ void SSPMInit(void) {
}
Sspm->old_power = TasmotaGlobal.power;
Sspm->init_step = SPM_WAIT; // Start init sequence
Sspm->mstate = SPM_WAIT; // Start init sequence
}
void SSPMEvery100ms(void) {
@ -865,17 +871,18 @@ void SSPMEvery100ms(void) {
}
// Fix race condition if the ARM doesn't respond
if ((Sspm->init_step > SPM_NONE) && (Sspm->init_step < SPM_SEND_FUNC_UNITS)) {
if ((Sspm->mstate > SPM_NONE) && (Sspm->mstate < SPM_SEND_FUNC_UNITS)) {
Sspm->counter++;
if (Sspm->counter > 20) {
Sspm->init_step = SPM_NONE;
Sspm->mstate = SPM_NONE;
}
}
switch (Sspm->init_step) {
switch (Sspm->mstate) {
case SPM_NONE:
return;
case SPM_WAIT:
Sspm->init_step++;
// 100ms wait
Sspm->mstate = SPM_RESET;
break;
case SPM_RESET:
// Reset ARM
@ -883,22 +890,34 @@ void SSPMEvery100ms(void) {
delay(18);
digitalWrite(SSPM_GPIO_ARM_RESET, 1);
delay(18);
Sspm->init_step++;
case SPM_POLL:
Sspm->mstate = SPM_POLL_ARM;
case SPM_POLL_ARM:
// Wait for first acknowledge from ARM after reset
SSPMSendCmnd(SSPM_FUNC_FIND);
break;
case SPM_POLL_ACK:
case SPM_POLL_ARM_2:
// Wait for second acknowledge from ARM after reset
SSPMSendCmnd(SSPM_FUNC_FIND);
break;
case SPM_SEND_FUNC_UNITS:
Sspm->init_step++;
// Get number of units
SSPMSendCmnd(SSPM_FUNC_UNITS);
break;
case SPM_DEVICES_FOUND:
TasmotaGlobal.discovery_counter = 1; // force TasDiscovery()
case SPM_START_SCAN:
// Start scan module sequence
Sspm->module_max = 0;
SSPMSendInitScan();
Sspm->mstate = SPM_WAIT_FOR_SCAN;
break;
case SPM_WAIT_FOR_SCAN:
// Wait for scan sequence to complete
break;
case SPM_SCAN_COMPLETE:
// Scan sequence finished
TasmotaGlobal.discovery_counter = 1; // Force TasDiscovery()
Sspm->get_energy_relay = 1;
Sspm->allow_updates = 1; // Enable requests from 100mSec loop
Sspm->init_step++;
Sspm->allow_updates = 1; // Enable requests from 100mSec loop
Sspm->mstate = SPM_GET_ENERGY_TOTALS;
break;
case SPM_GET_ENERGY_TOTALS:
// Retrieve Energy total status from up to 128 relays
@ -908,47 +927,54 @@ void SSPMEvery100ms(void) {
Sspm->get_energy_relay++;
if (Sspm->get_energy_relay > TasmotaGlobal.devices_present) {
Sspm->get_energy_relay = 1;
Sspm->init_step++;
Sspm->mstate = SPM_UPDATE_CHANNELS;
}
}
break;
case SPM_ALLOW_LOOP:
// Retrieve Energy status from up to 128 relays
case SPM_UPDATE_CHANNELS:
// Retrieve Energy status from up to 128 powered on relays
if (Sspm->allow_updates && (Sspm->get_energy_relay > 0)) {
power_t powered_on = TasmotaGlobal.power >> (Sspm->get_energy_relay -1);
if (0 == Sspm->loop_step) {
// Get energy total only once in any 256 requests to safe comms
if (powered_on &1) {
SSPMSetLock(4);
SSPMSendGetEnergyTotal(Sspm->get_energy_relay -1);
}
Sspm->get_energy_relay++;
if (Sspm->get_energy_relay > TasmotaGlobal.devices_present) {
Sspm->get_energy_relay = 1;
Sspm->loop_step++;
}
if (powered_on &1) {
SSPMSetLock(4);
SSPMSendGetEnergy(Sspm->get_energy_relay -1);
} else {
if (powered_on &1) {
SSPMSetLock(4);
SSPMSendGetEnergy(Sspm->get_energy_relay -1);
} else {
uint32_t relay_set = (Sspm->get_energy_relay -1) >> 2;
uint32_t relay_num = (Sspm->get_energy_relay -1) &3;
if (Sspm->voltage[relay_set][relay_num]) {
Sspm->voltage[relay_set][relay_num] = 0;
Sspm->current[relay_set][relay_num] = 0;
Sspm->active_power[relay_set][relay_num] = 0;
Sspm->apparent_power[relay_set][relay_num] = 0;
Sspm->reactive_power[relay_set][relay_num] = 0;
Sspm->power_factor[relay_set][relay_num] = 0;
}
}
Sspm->loop_step++; // Rolls over after 256 so allows for scanning at least all relays twice
Sspm->get_energy_relay++;
if ((Sspm->get_energy_relay > TasmotaGlobal.devices_present) || !Sspm->loop_step) {
Sspm->get_energy_relay = 1;
uint32_t relay_set = (Sspm->get_energy_relay -1) >> 2;
uint32_t relay_num = (Sspm->get_energy_relay -1) &3;
if (Sspm->voltage[relay_set][relay_num]) {
Sspm->voltage[relay_set][relay_num] = 0;
Sspm->current[relay_set][relay_num] = 0;
Sspm->active_power[relay_set][relay_num] = 0;
Sspm->apparent_power[relay_set][relay_num] = 0;
Sspm->reactive_power[relay_set][relay_num] = 0;
Sspm->power_factor[relay_set][relay_num] = 0;
}
}
Sspm->get_energy_relay++;
if (Sspm->get_energy_relay > TasmotaGlobal.devices_present) {
Sspm->get_energy_relay = 1;
}
Sspm->loop_step++; // Rolls over after 256 so allows for scanning at least all relays twice
if (!Sspm->loop_step) {
Sspm->get_energy_relay = 1;
Sspm->mstate = SPM_UPDATE_TOTALS;
}
}
break;
case SPM_UPDATE_TOTALS:
// Retrieve Energy totals from up to 128 powered on relays
if (Sspm->allow_updates && (Sspm->get_energy_relay > 0)) {
power_t powered_on = TasmotaGlobal.power >> (Sspm->get_energy_relay -1);
// Get energy total only once in any 256 requests to safe comms
if (powered_on &1) {
SSPMSetLock(4);
SSPMSendGetEnergyTotal(Sspm->get_energy_relay -1);
}
Sspm->get_energy_relay++;
if (Sspm->get_energy_relay > TasmotaGlobal.devices_present) {
Sspm->get_energy_relay = 1;
Sspm->mstate = SPM_UPDATE_CHANNELS;
}
}
break;
}
@ -973,7 +999,7 @@ bool SSPMButton(void) {
bool result = false;
uint32_t button = XdrvMailbox.payload;
if ((PRESSED == button) && (NOT_PRESSED == Sspm->last_button)) { // Button pressed
SSPMSendInitScan();
Sspm->mstate = SPM_START_SCAN;
result = true; // Disable further button processing
}
Sspm->last_button = button;
@ -1025,10 +1051,9 @@ void SSPMEnergyShow(bool json) {
ResponseAppend_P(PSTR("]}"));
} else {
Sspm->rotate++;
if ((Sspm->rotate >> 2) == Sspm->module_max) {
if (Sspm->rotate >= TasmotaGlobal.devices_present) {
Sspm->rotate = 0;
}
uint32_t module = Sspm->rotate >> 2;
uint32_t relay_base = module * 4;
WSContentSend_P(PSTR("{s}" D_SENSOR_RELAY "{m}L%02d / L%02d / L%02d / L%02d{e}"), relay_base +1, relay_base +2, relay_base +3, relay_base +4);
@ -1077,9 +1102,8 @@ void CmndSSPMEnergyHistory(void) {
}
void CmndSSPMScan(void) {
SSPMSendInitScan();
ResponseCmndDone();
Sspm->mstate = SPM_START_SCAN;
ResponseCmndChar(PSTR(D_JSON_STARTED));
}
/*********************************************************************************************\