Now using a switch statement for state machine

This commit is contained in:
rljonesau 2018-11-01 07:55:25 +11:00
parent b128e51880
commit c415a1a2c7
3 changed files with 192 additions and 131 deletions

View file

@ -11,6 +11,14 @@
"xtree": "cpp",
"algorithm": "cpp",
"initializer_list": "cpp",
"xutility": "cpp"
"xutility": "cpp",
"istream": "cpp",
"memory": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"utility": "cpp",
"xlocmon": "cpp",
"xlocnum": "cpp",
"xloctime": "cpp"
}
}

View file

@ -4,9 +4,28 @@
//////////////////////////////////////////////////////////////////////////////
// Configure bluetooth options
// ** Recommended to use HC-05 for now **
// If none are selected, will use an abstract class that only reports
// to the debug port what have been sent
// If none are enabled, we'll use an abstract class that only reports
// to the debug port what would have been sent
//
//#define USE_HC05_BLUETOOTH
//#define USE_BLE_BLUETOOTH
//#define USE_CLASSIC_BLUETOOTH
#define USE_HC05_BLUETOOTH 1
#define USE_BLE_BLUETOOTH 0
#define USE_CLASSIC_BLUETOOTH 0
///////////////////////////////////////////////////////////////////////////////
// limit rate of Bluetooth delivery from enthusiastic OEM controllers
//
#define OEM_TO_BLUETOOTH_MODERATION_TIME 700
// show when we did not echo data frames to bluetooth
#define REPORT_SUPPRESSED_OEM_DATA_FRAMES 0
///////////////////////////////////////////////////////////////////////////////
// debug reporting options
//
// true: each frame of data is reported on a new lines
// false: controller, then heater response frmaes are reported on a single line (excel CSV worthy!)
//
#define TERMINATE_OEM_LINE false /* when an OEM controller exists */
#define TERMINATE_BTC_LINE false /* when an OEM controller does not exist */

View file

@ -73,8 +73,6 @@
#include "BTCWifi.h"
#include "BTCConfig.h"
#define OEM_MODERATION_TIME 200 // 700
#define HOST_NAME "remotedebug-sample"
#define TRIGGER_PIN 0
@ -121,6 +119,9 @@ class CommStates {
m_State = eState;
m_Count = 0;
}
eCS get() {
return m_State;
}
bool is(eCS eState) {
return m_State == eState;
}
@ -129,7 +130,7 @@ class CommStates {
return m_Count == limit;
}
private:
int m_State;
eCS m_State;
int m_Count;
};
@ -166,8 +167,6 @@ public:
CommStates CommState;
CTxManage TxManage(TxEnbPin, BlueWireSerial);
//CProtocol OEMCtrlFrame; // data packet received from heater in response to OEM controller packet
//CProtocol HeaterFrame1; // data packet received from heater in response to OEM controller packet
CModeratedFrame OEMCtrlFrame; // data packet received from heater in response to OEM controller packet
CModeratedFrame HeaterFrame1; // data packet received from heater in response to OEM controller packet
CProtocol HeaterFrame2; // data packet received from heater in response to our packet
@ -175,26 +174,34 @@ CProtocol DefaultBTCParams(CProtocol::CtrlMode); // defines the default paramet
CSmartError SmartError;
////////////////////////////////////////////////////////////////////////////////////////////////////////
// Bluetooth instantiation
// Bluetooth instantiation
//
#ifdef ESP32
// Bluetooth options for ESP32
#ifdef USE_HC05_BLUETOOTH
#if USE_HC05_BLUETOOTH == 1
CBluetoothESP32HC05 Bluetooth(HC05_KeyPin, HC05_SensePin, Rx2Pin, Tx2Pin); // Instantiate ESP32 using a HC-05
#elif defined USE_BLE_BLUETOOTH
#elif USE_BLE_BLUETOOTH == 1
CBluetoothESP32BLE Bluetooth; // Instantiate ESP32 BLE server
#elif defined USE_CLASSIC_BLUETOOTH
#elif USE_CLASSIC_BLUETOOTH == 1
CBluetoothESP32Classic Bluetooth; // Instantiate ESP32 Classic Bluetooth server
#else
#else // none selected
CBluetoothAbstract Bluetooth; // default no bluetooth support - empty shell
#endif
#else // !ESP32
// Bluetooth for others
#ifdef USE_HC05_BLUETOOTH
#if USE_HC05_BLUETOOTH == 1
CBluetoothHC05 Bluetooth(HC05_KeyPin, HC05_SensePin); // Instantiate a HC-05
#else // !USE_HC05_BLUETOOTH
#else // none selected
CBluetoothAbstract Bluetooth; // default no bluetooth support - empty shell
#endif // closing USE_HC05_BLUETOOTH
#endif // closing ESP32
//
// END Bluetooth instantiation
////////////////////////////////////////////////////////////////////////////////////////////////////////
long lastRxTime; // used to observe inter character delays
bool hasOEMController = false;
@ -208,7 +215,6 @@ CHeaterStorage NVStorage; // dummy, for now
#endif
CHeaterStorage* pNVStorage = NULL;
void PrepareTxFrame(const CProtocol& basisFrame, CProtocol& TxFrame, bool isBTCmaster);
void setup() {
initWifi(TRIGGER_PIN, FAILEDSSID, FAILEDPASSWORD);
@ -261,11 +267,12 @@ void setup() {
pNVStorage->load();
}
// main functional loop is based about a state machine approach, waiting for data
// to appear upon the blue wire, and marshalling into an appropriate receive buffer
// according to the state.
// main functional loop is based about a state machine approach, waiting for data
// to appear upon the blue wire, and marshalling into an appropriate receive buffers
// according to the state.
void loop()
{
unsigned long timenow = millis();
@ -314,6 +321,7 @@ void loop()
// Blue wire data reception
// Reads data from the "blue wire" Serial port, (to/from heater)
// If an OEM controller exists we will also see it's data frames
// Note that the data is read now, then held for later use in the state machine
//
sRxData BlueWireData;
@ -329,130 +337,156 @@ void loop()
///////////////////////////////////////////////////////////////////////////////////////////
// do our state machine to track the reception and delivery of blue wire data
// we may need to transit to a OEMCtrlRx if we captured a new byte, so stand alone if here!
if( CommState.is(CommStates::Idle)) {
switch(CommState.get()) {
// Detect the possible start of a new frame sequence from an OEM controller
// This will be the first activity for considerable period on the blue wire
// The heater always responds to a controller frame, but otherwise never by itself
if(BlueWireData.available() && (RxTimeElapsed > 100)) {
case CommStates::Idle:
// Detect the possible start of a new frame sequence from an OEM controller
// This will be the first activity for considerable period on the blue wire
// The heater always responds to a controller frame, but otherwise never by itself
if(RxTimeElapsed >= 970) {
// have not seen any receive data for a second.
// OEM controller is probably not connected. 6
// Skip state machine immediately to BTC_Tx, sending our own settings.
hasOEMController = false;
bool isBTCmaster = true;
TxManage.PrepareFrame(DefaultBTCParams, isBTCmaster); // use our parameters, and mix in NV storage values
TxManage.Start(timenow);
CommState.set(CommStates::BTC_Tx);
break;
}
if(BlueWireData.available() && (RxTimeElapsed > 100)) {
#ifdef REPORT_OEM_RESYNC
DebugPort.print("Re-sync'd with OEM Controller. ");
DebugPort.print(RxTimeElapsed);
DebugPort.println("ms Idle time.");
DebugPort.print("Re-sync'd with OEM Controller. ");
DebugPort.print(RxTimeElapsed);
DebugPort.println("ms Idle time.");
#endif
hasOEMController = true;
CommState.set(CommStates::OEMCtrlRx); // we must add this new byte!
}
if(RxTimeElapsed >= 970) {
// have not seen any receive data for a second.
// OEM controller is probably not connected. 6
// Skip state machine immediately to BTC_Tx, sending our own settings.
hasOEMController = false;
CommState.set(CommStates::BTC_Tx);
bool isBTCmaster = true;
TxManage.PrepareFrame(DefaultBTCParams, isBTCmaster); // use our parameters, and mix in NV storage values
TxManage.Start(timenow);
}
} // CommState::Idle
if( CommState.is(CommStates::OEMCtrlRx) ) {
// collect OEM controller frame
if(BlueWireData.available()) {
if(CommState.collectData(OEMCtrlFrame, BlueWireData.getValue()) ) {
CommState.set(CommStates::OEMCtrlReport); // collected 24 bytes, move on!
hasOEMController = true;
CommState.set(CommStates::OEMCtrlRx); // we must add this new byte!
//
// ** IMPORTANT - we must drop through to OEMCtrlRx *NOW* (skip break) **
//
}
}
}
else if( CommState.is(CommStates::OEMCtrlReport) ) {
// filled OEM controller frame, report
// echo received OEM controller frame over Bluetooth, using [OEM] header
if(OEMCtrlFrame.elapsedTime() > OEM_MODERATION_TIME) {
Bluetooth.sendFrame("[OEM]", OEMCtrlFrame, false);
OEMCtrlFrame.setTime();
}
else {
DebugPort.println("Suppressed delivery of OEM frame");
}
CommState.set(CommStates::HeaterRx1);
}
else if( CommState.is(CommStates::HeaterRx1) ) {
// collect heater frame, always in response to an OEM controller frame
if(BlueWireData.available()) {
if( CommState.collectData(HeaterFrame1, BlueWireData.getValue()) ) {
CommState.set(CommStates::HeaterReport1);
else {
break; // only break if we failed all the Idle tests
}
}
}
else if(CommState.is(CommStates::HeaterReport1) ) {
// received heater frame (after controller message), report
case CommStates::OEMCtrlRx:
// collect OEM controller frame
if(BlueWireData.available()) {
if(CommState.collectData(OEMCtrlFrame, BlueWireData.getValue()) ) {
CommState.set(CommStates::OEMCtrlReport); // collected 24 bytes, move on!
}
}
break;
case CommStates::OEMCtrlReport:
// filled OEM controller frame, report
// echo received OEM controller frame over Bluetooth, using [OEM] header
// note that Rotary Knob and LED OEM controllers can flood the Bluetooth
// handling at the client side, moderate OEM Bluetooth delivery
if(OEMCtrlFrame.elapsedTime() > OEM_TO_BLUETOOTH_MODERATION_TIME) {
Bluetooth.sendFrame("[OEM]", OEMCtrlFrame, TERMINATE_OEM_LINE);
OEMCtrlFrame.setTime();
}
else {
#if REPORT_SUPPRESSED_OEM_DATA_FRAMES != 0
DebugPort.println("Suppressed delivery of OEM frame");
#endif
}
CommState.set(CommStates::HeaterRx1);
break;
case CommStates::HeaterRx1:
// collect heater frame, always in response to an OEM controller frame
if(BlueWireData.available()) {
if( CommState.collectData(HeaterFrame1, BlueWireData.getValue()) ) {
CommState.set(CommStates::HeaterReport1);
}
}
break;
case CommStates::HeaterReport1:
// received heater frame (after controller message), report
// do some monitoring of the heater state variable
// if suspicious transitions, introduce a smart error!
SmartError.monitor(HeaterFrame1);
// do some monitoring of the heater state variable
// if abnormal transitions, introduce a smart error!
SmartError.monitor(HeaterFrame1);
// echo heater reponse data to Bluetooth client
if(HeaterFrame1.elapsedTime() > OEM_MODERATION_TIME) {
Bluetooth.sendFrame("[HTR]", HeaterFrame1, true);
HeaterFrame1.setTime();
}
else {
DebugPort.println("Suppressed delivery of OEM heater response frame");
}
if(digitalRead(ListenOnlyPin)) {
bool isBTCmaster = false;
TxManage.PrepareFrame(OEMCtrlFrame, isBTCmaster); // parrot OEM parameters, but block NV modes
TxManage.Start(timenow);
CommState.set(CommStates::BTC_Tx);
}
else {
CommState.set(CommStates::Idle); // "Listen Only" input is held low, don't send out Tx
}
}
// Handle time interval where we send data to the blue wire
else if(CommState.is(CommStates::BTC_Tx)) {
lastRxTime = timenow; // *we* are pumping onto blue wire, track this activity!
if(TxManage.CheckTx(timenow) ) { // monitor progress of our data delivery
CommState.set(CommStates::HeaterRx2); // then await heater repsonse
}
}
else if( CommState.is(CommStates::HeaterRx2) ) {
// collect heater frame, in response to our control frame
if(BlueWireData.available()) {
if( CommState.collectData(HeaterFrame2, BlueWireData.getValue()) ) {
CommState.set(CommStates::HeaterReport2);
// echo heater reponse data to Bluetooth client
// note that Rotary Knob and LED OEM controllers can flood the Bluetooth
// handling at the client side, moderate OEM Bluetooth delivery
if(HeaterFrame1.elapsedTime() > OEM_TO_BLUETOOTH_MODERATION_TIME) {
Bluetooth.sendFrame("[HTR]", HeaterFrame1, true);
HeaterFrame1.setTime();
}
else {
#if REPORT_SUPPRESSED_OEM_DATA_FRAMES != 0
DebugPort.println("Suppressed delivery of OEM heater response frame");
#endif
}
}
}
else if( CommState.is(CommStates::HeaterReport2) ) {
// received heater frame (after our control message), report
if(digitalRead(ListenOnlyPin)) {
bool isBTCmaster = false;
TxManage.PrepareFrame(OEMCtrlFrame, isBTCmaster); // parrot OEM parameters, but block NV modes
TxManage.Start(timenow);
CommState.set(CommStates::BTC_Tx);
}
else {
CommState.set(CommStates::Idle); // "Listen Only" input is held low, don't send out Tx
}
break;
// do some monitoring of the heater state variable
// if suspicious transitions, introduce a smart error!
SmartError.monitor(HeaterFrame2);
case CommStates::BTC_Tx:
// Handle time interval where we send data to the blue wire
lastRxTime = timenow; // *we* are pumping onto blue wire, track this activity!
if(TxManage.CheckTx(timenow) ) { // monitor progress of our data delivery
if(!hasOEMController) {
// only convey this frames to Bluetooth when NOT using an OEM controller!
Bluetooth.sendFrame("[BTC]", TxManage.getFrame(), TERMINATE_BTC_LINE); // BTC => Bluetooth Controller :-)
}
CommState.set(CommStates::HeaterRx2); // then await heater repsonse
}
break;
delay(5);
if(!hasOEMController) {
// only convey these frames to Bluetooth when NOT using an OEM controller!
Bluetooth.sendFrame("[BTC]", TxManage.getFrame(), true); // BTC => Bluetooth Controller :-)
Bluetooth.sendFrame("[HTR]", HeaterFrame2, true); // pin not grounded, suppress duplicate to BT
}
CommState.set(CommStates::Idle);
case CommStates::HeaterRx2:
// collect heater frame, in response to our control frame
if(BlueWireData.available()) {
if( CommState.collectData(HeaterFrame2, BlueWireData.getValue()) ) {
CommState.set(CommStates::HeaterReport2);
}
}
break;
case CommStates::HeaterReport2:
// received heater frame (after our control message), report
// do some monitoring of the heater state variables
// if abnormal transitions, introduce a smart error!
SmartError.monitor(HeaterFrame2);
delay(5);
if(!hasOEMController) {
// only convey these frames to Bluetooth when NOT using an OEM controller!
// Bluetooth.sendFrame("[BTC]", TxManage.getFrame(), TERMINATE_BTC_LINE); // BTC => Bluetooth Controller :-)
Bluetooth.sendFrame("[HTR]", HeaterFrame2, true); // pin not grounded, suppress duplicate to BT
}
CommState.set(CommStates::Idle);
#ifdef SHOW_HEAP
Serial.print("Free heap ");
Serial.println(ESP.getFreeHeap());
Serial.print("Free heap ");
Serial.println(ESP.getFreeHeap());
#endif
}
break;
} // switch(CommState)
} // loop