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

View File

@ -30,7 +30,7 @@
* Bulk of the action is handled by ARM processors present in every unit communicating over modbus RS-485. * 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. * 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. * 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. * 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 * 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 #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, enum SspmMachineStates { SPM_NONE, // Do nothing
SPM_DEVICES_FOUND, SPM_WAIT, // Wait 100ms
SPM_GET_ENERGY_TOTALS, SPM_RESET, // Toggle ARM reset pin
SPM_ALLOW_LOOP}; 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> #include <TasmotaSerial.h>
TasmotaSerial *SspmSerial; TasmotaSerial *SspmSerial;
@ -156,7 +165,7 @@ typedef struct {
uint8_t counter; uint8_t counter;
uint8_t command_sequence; uint8_t command_sequence;
uint8_t loop_step; uint8_t loop_step;
uint8_t init_step; uint8_t mstate;
uint8_t last_button; uint8_t last_button;
bool discovery_triggered; bool discovery_triggered;
} TSspm; } TSspm;
@ -528,7 +537,7 @@ void SSPMHandleReceivedData(void) {
|Er| |St| |Er| |St|
*/ */
if ((1 == Sspm->expected_bytes) && (0 == SspmBuffer[19])) { if ((1 == Sspm->expected_bytes) && (0 == SspmBuffer[19])) {
Sspm->init_step++; Sspm->mstate++; // Cycle to
} }
break; break;
case SSPM_FUNC_GET_OPS: case SSPM_FUNC_GET_OPS:
@ -591,15 +600,14 @@ void SSPMHandleReceivedData(void) {
} else { } else {
AddLog(LOG_LEVEL_DEBUG, PSTR("SPM: Relay scan done")); AddLog(LOG_LEVEL_DEBUG, PSTR("SPM: Relay scan done"));
Sspm->init_step = SPM_DEVICES_FOUND; Sspm->mstate = SPM_SCAN_COMPLETE;
// Sspm->get_energy_relay = 1;
// Sspm->allow_updates = 1; // Enable requests from 100mSec loop
} }
break; break;
case SSPM_FUNC_SET_TIME: case SSPM_FUNC_SET_TIME:
/* 0x0C /* 0x0C
AA 55 01 00 00 00 00 00 00 00 00 00 00 00 00 80 0c 00 01 00 04 3e 62 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); SSPMSendGetModuleState(Sspm->module_selected -1);
break; break;
case SSPM_FUNC_INIT_SCAN: 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 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 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; break;
case SSPM_FUNC_UNITS: case SSPM_FUNC_UNITS:
/* 0x15 /* 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 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; break;
case SSPM_FUNC_GET_ENERGY_TOTAL: case SSPM_FUNC_GET_ENERGY_TOTAL:
/* 0x16 /* 0x16
@ -746,7 +753,6 @@ void SSPMHandleReceivedData(void) {
/* 0x0F /* 0x0F
AA 55 01 00 00 00 00 00 00 00 00 00 00 00 00 00 0f 00 01 02 01 9d f8 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); SSPMSendAck(command_sequence);
break; break;
case SSPM_FUNC_SCAN_RESULT: case SSPM_FUNC_SCAN_RESULT:
@ -851,7 +857,7 @@ void SSPMInit(void) {
} }
Sspm->old_power = TasmotaGlobal.power; Sspm->old_power = TasmotaGlobal.power;
Sspm->init_step = SPM_WAIT; // Start init sequence Sspm->mstate = SPM_WAIT; // Start init sequence
} }
void SSPMEvery100ms(void) { void SSPMEvery100ms(void) {
@ -865,17 +871,18 @@ void SSPMEvery100ms(void) {
} }
// Fix race condition if the ARM doesn't respond // 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++; Sspm->counter++;
if (Sspm->counter > 20) { if (Sspm->counter > 20) {
Sspm->init_step = SPM_NONE; Sspm->mstate = SPM_NONE;
} }
} }
switch (Sspm->init_step) { switch (Sspm->mstate) {
case SPM_NONE: case SPM_NONE:
return; return;
case SPM_WAIT: case SPM_WAIT:
Sspm->init_step++; // 100ms wait
Sspm->mstate = SPM_RESET;
break; break;
case SPM_RESET: case SPM_RESET:
// Reset ARM // Reset ARM
@ -883,22 +890,34 @@ void SSPMEvery100ms(void) {
delay(18); delay(18);
digitalWrite(SSPM_GPIO_ARM_RESET, 1); digitalWrite(SSPM_GPIO_ARM_RESET, 1);
delay(18); delay(18);
Sspm->init_step++; Sspm->mstate = SPM_POLL_ARM;
case SPM_POLL: case SPM_POLL_ARM:
// Wait for first acknowledge from ARM after reset
SSPMSendCmnd(SSPM_FUNC_FIND); SSPMSendCmnd(SSPM_FUNC_FIND);
break; break;
case SPM_POLL_ACK: case SPM_POLL_ARM_2:
// Wait for second acknowledge from ARM after reset
SSPMSendCmnd(SSPM_FUNC_FIND); SSPMSendCmnd(SSPM_FUNC_FIND);
break; break;
case SPM_SEND_FUNC_UNITS: case SPM_SEND_FUNC_UNITS:
Sspm->init_step++; // Get number of units
SSPMSendCmnd(SSPM_FUNC_UNITS); SSPMSendCmnd(SSPM_FUNC_UNITS);
break; break;
case SPM_DEVICES_FOUND: case SPM_START_SCAN:
TasmotaGlobal.discovery_counter = 1; // force TasDiscovery() // 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->get_energy_relay = 1;
Sspm->allow_updates = 1; // Enable requests from 100mSec loop Sspm->allow_updates = 1; // Enable requests from 100mSec loop
Sspm->init_step++; Sspm->mstate = SPM_GET_ENERGY_TOTALS;
break; break;
case SPM_GET_ENERGY_TOTALS: case SPM_GET_ENERGY_TOTALS:
// Retrieve Energy total status from up to 128 relays // Retrieve Energy total status from up to 128 relays
@ -908,26 +927,14 @@ void SSPMEvery100ms(void) {
Sspm->get_energy_relay++; Sspm->get_energy_relay++;
if (Sspm->get_energy_relay > TasmotaGlobal.devices_present) { if (Sspm->get_energy_relay > TasmotaGlobal.devices_present) {
Sspm->get_energy_relay = 1; Sspm->get_energy_relay = 1;
Sspm->init_step++; Sspm->mstate = SPM_UPDATE_CHANNELS;
} }
} }
break; break;
case SPM_ALLOW_LOOP: case SPM_UPDATE_CHANNELS:
// Retrieve Energy status from up to 128 relays // Retrieve Energy status from up to 128 powered on relays
if (Sspm->allow_updates && (Sspm->get_energy_relay > 0)) { if (Sspm->allow_updates && (Sspm->get_energy_relay > 0)) {
power_t powered_on = TasmotaGlobal.power >> (Sspm->get_energy_relay -1); 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++;
}
} else {
if (powered_on &1) { if (powered_on &1) {
SSPMSetLock(4); SSPMSetLock(4);
SSPMSendGetEnergy(Sspm->get_energy_relay -1); SSPMSendGetEnergy(Sspm->get_energy_relay -1);
@ -943,11 +950,30 @@ void SSPMEvery100ms(void) {
Sspm->power_factor[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++; Sspm->get_energy_relay++;
if ((Sspm->get_energy_relay > TasmotaGlobal.devices_present) || !Sspm->loop_step) { if (Sspm->get_energy_relay > TasmotaGlobal.devices_present) {
Sspm->get_energy_relay = 1; 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; break;
@ -973,7 +999,7 @@ bool SSPMButton(void) {
bool result = false; bool result = false;
uint32_t button = XdrvMailbox.payload; uint32_t button = XdrvMailbox.payload;
if ((PRESSED == button) && (NOT_PRESSED == Sspm->last_button)) { // Button pressed if ((PRESSED == button) && (NOT_PRESSED == Sspm->last_button)) { // Button pressed
SSPMSendInitScan(); Sspm->mstate = SPM_START_SCAN;
result = true; // Disable further button processing result = true; // Disable further button processing
} }
Sspm->last_button = button; Sspm->last_button = button;
@ -1025,10 +1051,9 @@ void SSPMEnergyShow(bool json) {
ResponseAppend_P(PSTR("]}")); ResponseAppend_P(PSTR("]}"));
} else { } else {
Sspm->rotate++; Sspm->rotate++;
if ((Sspm->rotate >> 2) == Sspm->module_max) { if (Sspm->rotate >= TasmotaGlobal.devices_present) {
Sspm->rotate = 0; Sspm->rotate = 0;
} }
uint32_t module = Sspm->rotate >> 2; uint32_t module = Sspm->rotate >> 2;
uint32_t relay_base = module * 4; 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); 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) { void CmndSSPMScan(void) {
SSPMSendInitScan(); Sspm->mstate = SPM_START_SCAN;
ResponseCmndChar(PSTR(D_JSON_STARTED));
ResponseCmndDone();
} }
/*********************************************************************************************\ /*********************************************************************************************\