Moved Blue Wire Comms to a separate task
This commit is contained in:
parent
28bfb28ff6
commit
5cdc5c95a5
|
@ -120,12 +120,13 @@
|
|||
#include "RTC/TimerManager.h"
|
||||
#include "Utility/GetLine.h"
|
||||
#include "Utility/DemandManager.h"
|
||||
#include "Protocol/BlueWireTask.h"
|
||||
|
||||
// SSID & password now stored in NV storage - these are still the default values.
|
||||
//#define AP_SSID "Afterburner"
|
||||
//#define AP_PASSWORD "thereisnospoon"
|
||||
|
||||
#define RX_DATA_TIMOUT 50
|
||||
// #define RX_DATA_TIMOUT 50
|
||||
|
||||
const int FirmwareRevision = 32;
|
||||
const int FirmwareSubRevision = 0;
|
||||
|
@ -139,17 +140,6 @@ const char* FirmwareDate = "11 Apr 2020";
|
|||
#include "Bluetooth/BluetoothHC05.h"
|
||||
#endif
|
||||
|
||||
// Setup Serial Port Definitions
|
||||
#if defined(__arm__)
|
||||
// Required for Arduino Due, UARTclass is derived from HardwareSerial
|
||||
static UARTClass& BlueWireSerial(Serial1);
|
||||
#else
|
||||
// for ESP32, Mega
|
||||
// HardwareSerial is it for these boards
|
||||
static HardwareSerial& BlueWireSerial(Serial1);
|
||||
#endif
|
||||
|
||||
void initBlueWireSerial();
|
||||
bool validateFrame(const CProtocol& frame, const char* name);
|
||||
void checkDisplayUpdate();
|
||||
void checkDebugCommands();
|
||||
|
@ -159,7 +149,7 @@ void manageHumidity();
|
|||
void doStreaming();
|
||||
void heaterOn();
|
||||
void heaterOff();
|
||||
void updateFilteredData();
|
||||
void updateFilteredData(CProtocol& HeaterInfo);
|
||||
bool HandleMQTTsetup(char rxVal);
|
||||
void showMainmenu();
|
||||
|
||||
|
@ -178,16 +168,11 @@ bool bReportStack = false;
|
|||
unsigned long lastAnimationTime; // used to sequence updates to LCD for animation
|
||||
|
||||
sFilteredData FilteredSamples;
|
||||
CommStates CommState;
|
||||
CTxManage TxManage(TxEnbPin, BlueWireSerial);
|
||||
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
|
||||
CProtocol DefaultBTCParams(CProtocol::CtrlMode); // defines the default parameters, used in case of no OEM controller
|
||||
CSmartError SmartError;
|
||||
CKeyPad KeyPad;
|
||||
CScreenManager ScreenManager;
|
||||
ABTelnetSpy DebugPort;
|
||||
|
||||
#if USE_JTAG == 0
|
||||
//CANNOT USE GPIO WITH JTAG DEBUG
|
||||
CGPIOin GPIOin;
|
||||
|
@ -199,11 +184,6 @@ CMQTTsetup MQTTmenu;
|
|||
|
||||
|
||||
|
||||
long lastRxTime; // used to observe inter character delays
|
||||
bool bHasOEMController = false;
|
||||
bool bHasOEMLCDController = false;
|
||||
bool bHasHtrData = false;
|
||||
|
||||
// these variables will persist over a soft reboot.
|
||||
__NOINIT_ATTR float persistentRunTime;
|
||||
__NOINIT_ATTR float persistentGlowTime;
|
||||
|
@ -217,10 +197,10 @@ bool bReportJSONData = REPORT_JSON_TRANSMIT;
|
|||
bool bReportRecyleEvents = REPORT_BLUEWIRE_RECYCLES;
|
||||
bool bReportOEMresync = REPORT_OEM_RESYNC;
|
||||
|
||||
CProtocolPackage reportHeaterData;
|
||||
CProtocolPackage primaryHeaterData;
|
||||
CProtocol BlueWireRxData;
|
||||
CProtocol BlueWireTxData;
|
||||
CProtocolPackage BlueWireData;
|
||||
|
||||
unsigned long moderator;
|
||||
bool bUpdateDisplay = false;
|
||||
bool bHaveWebClient = false;
|
||||
bool bBTconnected = false;
|
||||
|
@ -281,6 +261,14 @@ CBluetoothAbstract& getBluetoothClient()
|
|||
return Bluetooth;
|
||||
}
|
||||
|
||||
// collect and report any debug messages from the blue wire task
|
||||
char taskMsg[BLUEWIRE_MSGQUEUESIZE];
|
||||
void checkBlueWireDebugMsgs()
|
||||
{
|
||||
if(BlueWireMsgBuf && xQueueReceive(BlueWireMsgBuf, taskMsg, 0))
|
||||
DebugPort.print(taskMsg);
|
||||
}
|
||||
|
||||
// callback function for Keypad events.
|
||||
// must be an absolute function, cannot be a class member due the "this" element!
|
||||
void parentKeyHandler(uint8_t event)
|
||||
|
@ -462,31 +450,11 @@ void setup() {
|
|||
pinMode(LED_Pin, OUTPUT); // On board LED indicator
|
||||
digitalWrite(LED_Pin, LOW);
|
||||
|
||||
initBlueWireSerial();
|
||||
|
||||
// prepare for first long delay detection
|
||||
lastRxTime = millis();
|
||||
|
||||
TxManage.begin(); // ensure Tx enable pin is setup
|
||||
|
||||
// define defaults should OEM controller be missing
|
||||
DefaultBTCParams.setHeaterDemand(23);
|
||||
DefaultBTCParams.setTemperature_Actual(22);
|
||||
DefaultBTCParams.setSystemVoltage(12.0);
|
||||
DefaultBTCParams.setPump_Min(1.6f);
|
||||
DefaultBTCParams.setPump_Max(5.5f);
|
||||
DefaultBTCParams.setFan_Min(1680);
|
||||
DefaultBTCParams.setFan_Max(4500);
|
||||
DefaultBTCParams.Controller.FanSensor = 1;
|
||||
|
||||
bBTconnected = false;
|
||||
Bluetooth.begin();
|
||||
|
||||
setupGPIO();
|
||||
|
||||
// pinMode(0, OUTPUT);
|
||||
// digitalWrite(0, LOW);
|
||||
|
||||
#if USE_SW_WATCHDOG == 1 && USE_JTAG == 0
|
||||
// create a high priority FreeRTOS task as a watchdog monitor
|
||||
TaskHandle_t wdTask;
|
||||
|
@ -553,6 +521,15 @@ void setup() {
|
|||
TempSensor.getDS18B20().mapSensor(1, NVstore.getHeaterTuning().DS18B20probe[1].romCode);
|
||||
TempSensor.getDS18B20().mapSensor(2, NVstore.getHeaterTuning().DS18B20probe[2].romCode);
|
||||
|
||||
// create task to run blue wire interface
|
||||
TaskHandle_t bwTask;
|
||||
xTaskCreate(BlueWireTask,
|
||||
"BlueWireTask",
|
||||
2000,
|
||||
NULL,
|
||||
3,
|
||||
&bwTask);
|
||||
|
||||
delay(1000); // just to hold the splash screeen for while
|
||||
}
|
||||
|
||||
|
@ -570,305 +547,17 @@ void loop()
|
|||
|
||||
// DebugPort.handle(); // keep telnet spy alive
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// 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;
|
||||
|
||||
// calc elapsed time since last rxd byte
|
||||
// used to detect no OEM controller, or the start of an OEM frame sequence
|
||||
unsigned long RxTimeElapsed = timenow - lastRxTime;
|
||||
|
||||
if (BlueWireSerial.available()) {
|
||||
// Data is available, read and store it now, use it later
|
||||
// Note that if not in a recognised data receive frame state, the data
|
||||
// will be deliberately lost!
|
||||
BlueWireData.setValue(BlueWireSerial.read()); // read hex byte, store for later use
|
||||
|
||||
lastRxTime = timenow; // tickle last rx time, for rx data timeout purposes
|
||||
}
|
||||
|
||||
|
||||
// precautionary state machine action if all 24 bytes were not received
|
||||
// whilst expecting a frame from the blue wire
|
||||
if(RxTimeElapsed > RX_DATA_TIMOUT) {
|
||||
|
||||
if(NVstore.getUserSettings().menuMode == 2)
|
||||
bReportRecyleEvents = false;
|
||||
|
||||
if( CommState.is(CommStates::OEMCtrlRx) ||
|
||||
CommState.is(CommStates::HeaterRx1) ||
|
||||
CommState.is(CommStates::HeaterRx2) ) {
|
||||
|
||||
if(RxTimeElapsed >= moderator) {
|
||||
moderator += 10;
|
||||
if(bReportRecyleEvents) {
|
||||
DebugPort.printf("%ldms - ", RxTimeElapsed);
|
||||
}
|
||||
if(CommState.is(CommStates::OEMCtrlRx)) {
|
||||
bHasOEMController = false;
|
||||
bHasOEMLCDController = false;
|
||||
if(bReportRecyleEvents)
|
||||
DebugPort.println("Timeout collecting OEM controller data, returning to Idle State");
|
||||
}
|
||||
else if(CommState.is(CommStates::HeaterRx1)) {
|
||||
bHasHtrData = false;
|
||||
if(bReportRecyleEvents)
|
||||
DebugPort.println("Timeout collecting OEM heater response data, returning to Idle State");
|
||||
}
|
||||
else {
|
||||
bHasHtrData = false;
|
||||
if(bReportRecyleEvents)
|
||||
DebugPort.println("Timeout collecting BTC heater response data, returning to Idle State");
|
||||
}
|
||||
}
|
||||
|
||||
if(bReportRecyleEvents)
|
||||
DebugPort.println("Recycling blue wire serial interface");
|
||||
initBlueWireSerial();
|
||||
CommState.set(CommStates::TemperatureRead); // revert to idle mode, after passing thru temperature mode
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// do our state machine to track the reception and delivery of blue wire data
|
||||
|
||||
long tDelta;
|
||||
switch(CommState.get()) {
|
||||
|
||||
case CommStates::Idle:
|
||||
// report any debug messages from the blue wire task
|
||||
checkBlueWireDebugMsgs();
|
||||
|
||||
feedWatchdog(); // feed watchdog
|
||||
|
||||
doStreaming(); // do wifi, BT tx etc when NOT in midst of handling blue wire
|
||||
// this especially avoids E-07 faults due to larger data transfers
|
||||
doStreaming(); // do wifi, BT tx etc
|
||||
|
||||
moderator = 50;
|
||||
|
||||
#if RX_LED == 1
|
||||
digitalWrite(LED_Pin, LOW);
|
||||
#endif
|
||||
// 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 >= (NVstore.getUserSettings().FrameRate - 60)) { // compensate for the time spent just doing things in this state machine
|
||||
// have not seen any receive data for a second.
|
||||
// OEM controller is probably not connected.
|
||||
// Skip state machine immediately to BTC_Tx, sending our own settings.
|
||||
bHasHtrData = false;
|
||||
bHasOEMController = false;
|
||||
bHasOEMLCDController = 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 SUPPORT_OEM_CONTROLLER == 1
|
||||
if(BlueWireData.available() && (RxTimeElapsed > (RX_DATA_TIMOUT+10))) {
|
||||
|
||||
if(bReportOEMresync) {
|
||||
DebugPort.printf("Re-sync'd with OEM Controller. %ldms Idle time.\r\n", RxTimeElapsed);
|
||||
}
|
||||
|
||||
bHasHtrData = false;
|
||||
bHasOEMController = true;
|
||||
CommState.set(CommStates::OEMCtrlRx); // we must add this new byte!
|
||||
//
|
||||
// ** IMPORTANT - we must drop through to OEMCtrlRx *NOW* (skipping break) **
|
||||
// ** otherwise the first byte will be lost! **
|
||||
//
|
||||
}
|
||||
else {
|
||||
Clock.update();
|
||||
checkDisplayUpdate();
|
||||
break; // only break if we fail all Idle state tests
|
||||
}
|
||||
#else
|
||||
Clock.update();
|
||||
checkDisplayUpdate();
|
||||
break;
|
||||
#endif
|
||||
|
||||
|
||||
case CommStates::OEMCtrlRx:
|
||||
|
||||
#if RX_LED == 1
|
||||
digitalWrite(LED_Pin, HIGH);
|
||||
#endif
|
||||
// collect OEM controller frame
|
||||
if(BlueWireData.available()) {
|
||||
if(CommState.collectData(OEMCtrlFrame, BlueWireData.getValue()) ) {
|
||||
CommState.set(CommStates::OEMCtrlValidate); // collected 24 bytes, move on!
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case CommStates::OEMCtrlValidate:
|
||||
#if RX_LED == 1
|
||||
digitalWrite(LED_Pin, LOW);
|
||||
#endif
|
||||
// test for valid CRC, abort and restarts Serial1 if invalid
|
||||
if(!validateFrame(OEMCtrlFrame, "OEM")) {
|
||||
break;
|
||||
}
|
||||
|
||||
// filled OEM controller frame
|
||||
OEMCtrlFrame.setTime();
|
||||
// LCD controllers use 0x76 as first byte, rotary knobs use 0x78
|
||||
bHasOEMLCDController = (OEMCtrlFrame.Controller.Byte0 != 0x78);
|
||||
|
||||
CommState.set(CommStates::HeaterRx1);
|
||||
break;
|
||||
|
||||
|
||||
case CommStates::HeaterRx1:
|
||||
#if RX_LED == 1
|
||||
digitalWrite(LED_Pin, HIGH);
|
||||
#endif
|
||||
// collect heater frame, always in response to an OEM controller frame
|
||||
if(BlueWireData.available()) {
|
||||
if( CommState.collectData(HeaterFrame1, BlueWireData.getValue()) ) {
|
||||
CommState.set(CommStates::HeaterValidate1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case CommStates::HeaterValidate1:
|
||||
#if RX_LED == 1
|
||||
digitalWrite(LED_Pin, LOW);
|
||||
#endif
|
||||
|
||||
// test for valid CRC, abort and restarts Serial1 if invalid
|
||||
if(!validateFrame(HeaterFrame1, "RX1")) {
|
||||
bHasHtrData = false;
|
||||
break;
|
||||
}
|
||||
bHasHtrData = true;
|
||||
|
||||
// received heater frame (after controller message), report
|
||||
|
||||
// do some monitoring of the heater state variable
|
||||
// if abnormal transitions, introduce a smart error!
|
||||
// This routine also cancels ON/OFF requests if runstate in startup/shutdown periods
|
||||
SmartError.monitor(HeaterFrame1);
|
||||
|
||||
HeaterFrame1.setTime();
|
||||
|
||||
while(BlueWireSerial.available()) {
|
||||
DebugPort.println("DUMPED ROGUE RX DATA");
|
||||
BlueWireSerial.read();
|
||||
}
|
||||
BlueWireSerial.flush();
|
||||
|
||||
primaryHeaterData.set(HeaterFrame1, OEMCtrlFrame); // OEM is always *the* controller
|
||||
if(bReportBlueWireData) {
|
||||
primaryHeaterData.reportFrames(true);
|
||||
CommState.setDelay(20); // let serial get sent before we send blue wire
|
||||
}
|
||||
else {
|
||||
CommState.setDelay(0);
|
||||
}
|
||||
CommState.set(CommStates::HeaterReport1);
|
||||
break;
|
||||
|
||||
|
||||
case CommStates::HeaterReport1:
|
||||
if(CommState.delayExpired()) {
|
||||
bool isBTCmaster = false;
|
||||
TxManage.PrepareFrame(OEMCtrlFrame, isBTCmaster); // parrot OEM parameters, but block NV modes
|
||||
TxManage.Start(timenow);
|
||||
CommState.set(CommStates::BTC_Tx);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
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
|
||||
CommState.set(CommStates::HeaterRx2); // then await heater repsonse
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case CommStates::HeaterRx2:
|
||||
#if RX_LED == 1
|
||||
digitalWrite(LED_Pin, HIGH);
|
||||
#endif
|
||||
// collect heater frame, in response to our control frame
|
||||
if(BlueWireData.available()) {
|
||||
#ifdef BADSTARTCHECK
|
||||
if(!CommState.checkValidStart(BlueWireData.getValue())) {
|
||||
DebugPort.println("***** Invalid start of frame - restarting Serial port *****");
|
||||
initBlueWireSerial();
|
||||
CommState.set(CommStates::Idle);
|
||||
}
|
||||
else {
|
||||
if( CommState.collectData(HeaterFrame2, BlueWireData.getValue()) ) {
|
||||
CommState.set(CommStates::HeaterValidate2);
|
||||
}
|
||||
}
|
||||
#else
|
||||
if( CommState.collectData(HeaterFrame2, BlueWireData.getValue()) ) {
|
||||
CommState.set(CommStates::HeaterValidate2);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case CommStates::HeaterValidate2:
|
||||
#if RX_LED == 1
|
||||
digitalWrite(LED_Pin, LOW);
|
||||
#endif
|
||||
|
||||
// test for valid CRC, abort and restart Serial1 if invalid
|
||||
if(!validateFrame(HeaterFrame2, "RX2")) {
|
||||
bHasHtrData = false;
|
||||
break;
|
||||
}
|
||||
bHasHtrData = true;
|
||||
|
||||
// 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);
|
||||
|
||||
if(!bHasOEMController) // no OEM controller - BTC is *the* controller
|
||||
primaryHeaterData.set(HeaterFrame2, TxManage.getFrame());
|
||||
|
||||
if(bReportBlueWireData) {
|
||||
reportHeaterData.set(HeaterFrame2, TxManage.getFrame());
|
||||
reportHeaterData.reportFrames(false);
|
||||
CommState.setDelay(20); // let serial get sent before we send blue wire
|
||||
}
|
||||
else {
|
||||
CommState.setDelay(0);
|
||||
}
|
||||
CommState.set(CommStates::HeaterReport2);
|
||||
break;
|
||||
|
||||
|
||||
case CommStates::HeaterReport2:
|
||||
if(CommState.delayExpired()) {
|
||||
CommState.set(CommStates::TemperatureRead);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case CommStates::TemperatureRead:
|
||||
// update temperature reading,
|
||||
// synchronised with serial reception as interrupts do get disabled in the OneWire library
|
||||
tDelta = timenow - lastTemperatureTime;
|
||||
long tDelta = timenow - lastTemperatureTime;
|
||||
if(tDelta > MIN_TEMPERATURE_INTERVAL) { // maintain a minimum holdoff period
|
||||
lastTemperatureTime = millis(); // reset time to observe temeprature
|
||||
|
||||
|
@ -902,54 +591,45 @@ void loop()
|
|||
ScreenManager.reqUpdate();
|
||||
}
|
||||
|
||||
if(bHasHtrData) {
|
||||
// apply exponential mean to the anlogue readings for some smoothing
|
||||
updateFilteredData();
|
||||
if(BlueWireSemaphore && xSemaphoreTake(BlueWireSemaphore, 0)) {
|
||||
updateJSONclients(bReportJSONData);
|
||||
updateMQTT();
|
||||
NVstore.doSave(); // now is a good time to store to the NV storage, well away from any blue wire activity
|
||||
}
|
||||
|
||||
// integrate fuel pump activity for fuel gauge
|
||||
FuelGauge.Integrate(getHeaterInfo().getPump_Actual());
|
||||
// collect transmitted heater data from blue wire task
|
||||
if(BlueWireTxQueue && xQueueReceive(BlueWireTxQueue, BlueWireTxData.Data, 0)) {
|
||||
}
|
||||
|
||||
// test for low volts shutdown during normal run
|
||||
if(INBOUNDS(getHeaterInfo().getRunState(), 1, 5)) { // check for Low Voltage Cutout
|
||||
// collect and process received heater data from blue wire task
|
||||
if(BlueWireRxQueue && xQueueReceive(BlueWireRxQueue, BlueWireRxData.Data, 0)) {
|
||||
BlueWireData.set(BlueWireRxData, BlueWireTxData);
|
||||
SmartError.monitor(BlueWireRxData);
|
||||
|
||||
updateFilteredData(BlueWireRxData);
|
||||
|
||||
FuelGauge.Integrate(BlueWireRxData.getPump_Actual());
|
||||
|
||||
if(INBOUNDS(BlueWireRxData.getRunState(), 1, 5)) { // check for Low Voltage Cutout
|
||||
SmartError.checkVolts(FilteredSamples.FastipVolts.getValue(), FilteredSamples.FastGlowAmps.getValue());
|
||||
SmartError.checkfuelUsage();
|
||||
}
|
||||
|
||||
// trap being in state 0 with a heater error - cancel user on memory to avoid unexpected cyclic restarts
|
||||
if(RTC_Store.getCyclicEngaged() && (getHeaterInfo().getRunState() == 0) && (getHeaterInfo().getErrState() > 1)) {
|
||||
DebugPort.println("Forcing cyclic cancel due to error induced shutdown");
|
||||
if(RTC_Store.getCyclicEngaged() && (BlueWireRxData.getRunState() == 0) && (BlueWireRxData.getErrState() > 1)) {
|
||||
const char* msg = "Forcing cyclic cancel due to error induced shutdown\r\n";
|
||||
xQueueSend(BlueWireMsgBuf, msg, 0);
|
||||
// DebugPort.println("Forcing cyclic cancel due to error induced shutdown");
|
||||
RTC_Store.setCyclicEngaged(false);
|
||||
}
|
||||
|
||||
pHourMeter->monitor(HeaterFrame2);
|
||||
}
|
||||
updateJSONclients(bReportJSONData);
|
||||
updateMQTT();
|
||||
CommState.set(CommStates::Idle);
|
||||
NVstore.doSave(); // now is a good time to store to the NV storage, well away from any blue wire activity
|
||||
break;
|
||||
} // switch(CommState)
|
||||
pHourMeter->monitor(BlueWireRxData);
|
||||
|
||||
BlueWireData.reset(); // ensure we flush any used data
|
||||
|
||||
// 21/11/19 vTaskDelay() causes E-07 errors when OEM controller is attached.
|
||||
// may look at a specific freertos task to handle the blue wire....
|
||||
if(!bHasOEMController) {
|
||||
vTaskDelay(1); // give up for now - allow power lowering...
|
||||
}
|
||||
|
||||
|
||||
} // loop
|
||||
|
||||
void DebugReportFrame(const char* hdr, const CProtocol& Frame, const char* ftr)
|
||||
{
|
||||
DebugPort.print(hdr); // header
|
||||
for(int i=0; i<24; i++) {
|
||||
char str[16];
|
||||
sprintf(str, " %02X", Frame.Data[i]); // build 2 dig hex values
|
||||
DebugPort.print(str); // and print
|
||||
}
|
||||
DebugPort.print(ftr); // footer
|
||||
}
|
||||
|
||||
void manageCyclicMode()
|
||||
{
|
||||
|
@ -1029,36 +709,6 @@ void manageHumidity()
|
|||
}
|
||||
|
||||
|
||||
void initBlueWireSerial()
|
||||
{
|
||||
// initialize serial port to interact with the "blue wire"
|
||||
// 25000 baud, Tx and Rx channels of Chinese heater comms interface:
|
||||
// Tx/Rx data to/from heater,
|
||||
// Note special baud rate for Chinese heater controllers
|
||||
#if defined(__arm__) || defined(__AVR__)
|
||||
BlueWireSerial.begin(25000);
|
||||
pinMode(Rx1Pin, INPUT_PULLUP); // required for MUX to work properly
|
||||
#elif ESP32
|
||||
// ESP32
|
||||
BlueWireSerial.begin(25000, SERIAL_8N1, Rx1Pin, Tx1Pin); // need to explicitly specify pins for pin multiplexer!
|
||||
pinMode(Rx1Pin, INPUT_PULLUP); // required for MUX to work properly
|
||||
#endif
|
||||
}
|
||||
|
||||
bool validateFrame(const CProtocol& frame, const char* name)
|
||||
{
|
||||
if(!frame.verifyCRC()) {
|
||||
// Bad CRC - restart blue wire Serial port
|
||||
DebugPort.printf("\007Bad CRC detected for %s frame - restarting blue wire's serial port\r\n", name);
|
||||
DebugReportFrame("BAD CRC:", frame, "\r\n");
|
||||
initBlueWireSerial();
|
||||
CommState.set(CommStates::TemperatureRead);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
CDemandManager::eStartCode
|
||||
requestOn()
|
||||
{
|
||||
|
@ -1068,7 +718,7 @@ requestOn()
|
|||
return CDemandManager::eStartLowFuel;
|
||||
}
|
||||
bool LVCOK = 2 != SmartError.checkVolts(FilteredSamples.FastipVolts.getValue(), FilteredSamples.FastGlowAmps.getValue());
|
||||
if(bHasHtrData && LVCOK) {
|
||||
if(hasHtrData() && LVCOK) {
|
||||
RTC_Store.setCyclicEngaged(true); // for cyclic mode
|
||||
RTC_Store.setFrostOn(false); // cancel frost mode
|
||||
// only start if below appropriate temperature threshold, raised for cyclic mode
|
||||
|
@ -1130,11 +780,6 @@ void checkDisplayUpdate()
|
|||
}
|
||||
}
|
||||
|
||||
void reqPumpPrime(bool on)
|
||||
{
|
||||
DefaultBTCParams.setPump_Prime(on);
|
||||
}
|
||||
|
||||
void forceBootInit()
|
||||
{
|
||||
RTC_Store.setBootInit();
|
||||
|
@ -1150,11 +795,6 @@ float getTemperatureSensor(int source)
|
|||
}
|
||||
|
||||
|
||||
const CProtocolPackage& getHeaterInfo()
|
||||
{
|
||||
return primaryHeaterData;
|
||||
}
|
||||
|
||||
bool isWebClientConnected()
|
||||
{
|
||||
return bHaveWebClient;
|
||||
|
@ -1199,10 +839,22 @@ void checkDebugCommands()
|
|||
}
|
||||
switch(nGetConf) {
|
||||
case 1:
|
||||
setSSID(line.getString());
|
||||
setName(line.getString(), 0);
|
||||
break;
|
||||
case 2:
|
||||
setAPpassword(pw2.c_str());
|
||||
setPassword(pw2.c_str(), 0);
|
||||
break;
|
||||
case 3:
|
||||
setName(line.getString(), 1);
|
||||
break;
|
||||
case 4:
|
||||
setPassword(pw2.c_str(), 1);
|
||||
break;
|
||||
case 5:
|
||||
setName(line.getString(), 2);
|
||||
break;
|
||||
case 6:
|
||||
setPassword(pw2.c_str(), 2);
|
||||
break;
|
||||
}
|
||||
nGetConf = 0;
|
||||
|
@ -1276,6 +928,122 @@ void checkDebugCommands()
|
|||
}
|
||||
nGetString = 0;
|
||||
return;
|
||||
case 10:
|
||||
if(line.getLen() <= 31) {
|
||||
nGetConf = 3;
|
||||
DebugPort.printf("\r\nSet Web page username to %s? (y/n) - ", line.getString());
|
||||
}
|
||||
else {
|
||||
DebugPort.println("\r\nNew username is longer than 31 characters - ABORTING");
|
||||
}
|
||||
nGetString = 0;
|
||||
return;
|
||||
case 11:
|
||||
pw1 = line.getString();
|
||||
pw2 = NVstore.getCredentials().webPassword;
|
||||
if(pw1 != pw2) {
|
||||
DebugPort.println("\r\nPassword does not match existing - ABORTING");
|
||||
nGetString = 0;
|
||||
}
|
||||
else {
|
||||
nGetString = 12;
|
||||
DebugPort.print("\r\nPlease enter new password - ");
|
||||
DebugPort.enable(false); // block other debug msgs whilst we get the password
|
||||
}
|
||||
line.reset();
|
||||
line.maskEntry();
|
||||
return;
|
||||
case 12:
|
||||
pw1 = line.getString();
|
||||
if(line.getLen() < 8) {
|
||||
// ABORT - too short
|
||||
DebugPort.println("\r\nNew password must be at least 8 characters - ABORTING");
|
||||
nGetString = 0;
|
||||
}
|
||||
else if(line.getLen() > 31) {
|
||||
// ABORT - too long!
|
||||
DebugPort.println("\r\nNew password is longer than 31 characters - ABORTING");
|
||||
nGetString = 0;
|
||||
}
|
||||
else {
|
||||
nGetString = 13;
|
||||
DebugPort.print("\r\nPlease confirm new password - ");
|
||||
DebugPort.enable(false); // block other debug msgs whilst we get the password
|
||||
}
|
||||
line.reset();
|
||||
line.maskEntry();
|
||||
return;
|
||||
case 13:
|
||||
pw2 = line.getString();
|
||||
line.reset();
|
||||
if(pw1 != pw2) {
|
||||
DebugPort.println("\r\nNew passwords do not match - ABORTING");
|
||||
}
|
||||
else {
|
||||
nGetConf = 4;
|
||||
DebugPort.print("\r\nSet new password (y/n) - ");
|
||||
}
|
||||
nGetString = 0;
|
||||
return;
|
||||
|
||||
case 20:
|
||||
if(line.getLen() <= 31) {
|
||||
nGetConf = 5;
|
||||
DebugPort.printf("\r\nSet Web /update username to %s? (y/n) - ", line.getString());
|
||||
}
|
||||
else {
|
||||
DebugPort.println("\r\nNew username is longer than 31 characters - ABORTING");
|
||||
}
|
||||
nGetString = 0;
|
||||
return;
|
||||
|
||||
case 21:
|
||||
pw1 = line.getString();
|
||||
pw2 = NVstore.getCredentials().webUpdatePassword;
|
||||
if(pw1 != pw2) {
|
||||
DebugPort.println("\r\nPassword does not match existing - ABORTING");
|
||||
nGetString = 0;
|
||||
}
|
||||
else {
|
||||
nGetString = 22;
|
||||
DebugPort.print("\r\nPlease enter new password - ");
|
||||
DebugPort.enable(false); // block other debug msgs whilst we get the password
|
||||
}
|
||||
line.reset();
|
||||
line.maskEntry();
|
||||
return;
|
||||
case 22:
|
||||
pw1 = line.getString();
|
||||
if(line.getLen() < 8) {
|
||||
// ABORT - too short
|
||||
DebugPort.println("\r\nNew password must be at least 8 characters - ABORTING");
|
||||
nGetString = 0;
|
||||
}
|
||||
else if(line.getLen() > 31) {
|
||||
// ABORT - too long!
|
||||
DebugPort.println("\r\nNew password is longer than 31 characters - ABORTING");
|
||||
nGetString = 0;
|
||||
}
|
||||
else {
|
||||
nGetString = 23;
|
||||
DebugPort.print("\r\nPlease confirm new password - ");
|
||||
DebugPort.enable(false); // block other debug msgs whilst we get the password
|
||||
}
|
||||
line.reset();
|
||||
line.maskEntry();
|
||||
return;
|
||||
case 23:
|
||||
pw2 = line.getString();
|
||||
line.reset();
|
||||
if(pw1 != pw2) {
|
||||
DebugPort.println("\r\nNew passwords do not match - ABORTING");
|
||||
}
|
||||
else {
|
||||
nGetConf = 6;
|
||||
DebugPort.print("\r\nSet new password (y/n) - ");
|
||||
}
|
||||
nGetString = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
DebugPort.enable(false);
|
||||
|
@ -1347,8 +1115,10 @@ void checkDebugCommands()
|
|||
bReportJSONData = !bReportJSONData;
|
||||
DebugPort.printf("Toggled JSON data reporting %s\r\n", bReportJSONData ? "ON" : "OFF");
|
||||
}
|
||||
else if(rxVal == 'w') {
|
||||
else if(rxVal == ('w' & 0x1f)) {
|
||||
bReportRecyleEvents = !bReportRecyleEvents;
|
||||
if(NVstore.getUserSettings().menuMode == 2)
|
||||
bReportRecyleEvents = false;
|
||||
DebugPort.printf("Toggled blue wire recycling event reporting %s\r\n", bReportRecyleEvents ? "ON" : "OFF");
|
||||
}
|
||||
else if(rxVal == 'n') {
|
||||
|
@ -1360,7 +1130,7 @@ void checkDebugCommands()
|
|||
else if(rxVal == 'm') {
|
||||
MQTTmenu.setActive();
|
||||
}
|
||||
else if(rxVal == 'o') {
|
||||
else if(rxVal == ('o' & 0x1f)) {
|
||||
bReportOEMresync = !bReportOEMresync;
|
||||
DebugPort.printf("Toggled OEM resync event reporting %s\r\n", bReportOEMresync ? "ON" : "OFF");
|
||||
}
|
||||
|
@ -1371,7 +1141,31 @@ void checkDebugCommands()
|
|||
nGetString = 2;
|
||||
DebugPort.enable(false); // block other debug msgs whilst we get strings
|
||||
}
|
||||
else if(rxVal == 's') {
|
||||
else if(rxVal == 'u') {
|
||||
DebugPort.print("Please enter username for Web page access (CTRL-X to disable) - ");
|
||||
line.reset();
|
||||
nGetString = 10;
|
||||
DebugPort.enable(false); // block other debug msgs whilst we get strings
|
||||
}
|
||||
else if(rxVal == 'w') {
|
||||
DebugPort.print("Please enter current Web page password - ");
|
||||
line.reset();
|
||||
nGetString = 11;
|
||||
DebugPort.enable(false); // block other debug msgs whilst we get strings
|
||||
}
|
||||
else if(rxVal == 'y') {
|
||||
DebugPort.print("Please enter username for /update Web page access - ");
|
||||
line.reset();
|
||||
nGetString = 20;
|
||||
DebugPort.enable(false); // block other debug msgs whilst we get strings
|
||||
}
|
||||
else if(rxVal == 'z') {
|
||||
DebugPort.print("Please enter current /update Web page password - ");
|
||||
line.reset();
|
||||
nGetString = 21;
|
||||
DebugPort.enable(false); // block other debug msgs whilst we get strings
|
||||
}
|
||||
else if(rxVal == ('c' & 0x1f)) {
|
||||
CommState.toggleReporting();
|
||||
}
|
||||
else if(rxVal == '+') {
|
||||
|
@ -1393,7 +1187,7 @@ void checkDebugCommands()
|
|||
else if(rxVal == ('r' & 0x1f)) { // CTRL-R reboot
|
||||
ESP.restart(); // reset the esp
|
||||
}
|
||||
else if(rxVal == ('s' & 0x1f)) { // CTRL-B Test Mode: bluetooth module route
|
||||
else if(rxVal == ('s' & 0x1f)) { // CTRL-S Test Mode: bluetooth module route
|
||||
bReportStack = !bReportStack;
|
||||
}
|
||||
}
|
||||
|
@ -1422,38 +1216,6 @@ void checkDebugCommands()
|
|||
}
|
||||
}
|
||||
|
||||
// 0x00 - Normal: BTC, with heater responding
|
||||
// 0x01 - Error: BTC, heater not responding
|
||||
// 0x02 - Special: OEM controller & heater responding
|
||||
// 0x03 - Error: OEM controller, heater not responding
|
||||
int getBlueWireStat()
|
||||
{
|
||||
int stat = 0;
|
||||
if(!bHasHtrData) {
|
||||
stat |= 0x01;
|
||||
}
|
||||
if(bHasOEMController) {
|
||||
stat |= 0x02;
|
||||
}
|
||||
return stat;
|
||||
}
|
||||
|
||||
const char* getBlueWireStatStr()
|
||||
{
|
||||
static const char* BlueWireStates[] = { "BTC,Htr", "BTC", "OEM,Htr", "OEM" };
|
||||
|
||||
return BlueWireStates[getBlueWireStat()];
|
||||
}
|
||||
|
||||
bool hasOEMcontroller()
|
||||
{
|
||||
return bHasOEMController;
|
||||
}
|
||||
|
||||
bool hasOEMLCDcontroller()
|
||||
{
|
||||
return bHasOEMLCDController;
|
||||
}
|
||||
|
||||
int getSmartError()
|
||||
{
|
||||
|
@ -1756,14 +1518,14 @@ int getFanSpeed()
|
|||
#endif
|
||||
}
|
||||
|
||||
void updateFilteredData()
|
||||
void updateFilteredData(CProtocol& HeaterInfo)
|
||||
{
|
||||
FilteredSamples.ipVolts.update(getHeaterInfo().getBattVoltage());
|
||||
FilteredSamples.GlowVolts.update(getHeaterInfo().getGlow_Voltage());
|
||||
FilteredSamples.GlowAmps.update(getHeaterInfo().getGlow_Current());
|
||||
FilteredSamples.Fan.update(getHeaterInfo().getFan_Actual());
|
||||
FilteredSamples.FastipVolts.update(getHeaterInfo().getBattVoltage());
|
||||
FilteredSamples.FastGlowAmps.update(getHeaterInfo().getGlow_Current());
|
||||
FilteredSamples.ipVolts.update(HeaterInfo.getVoltage_Supply());
|
||||
FilteredSamples.GlowVolts.update(HeaterInfo.getGlowPlug_Voltage());
|
||||
FilteredSamples.GlowAmps.update(HeaterInfo.getGlowPlug_Current());
|
||||
FilteredSamples.Fan.update(HeaterInfo.getFan_Actual());
|
||||
FilteredSamples.FastipVolts.update(HeaterInfo.getVoltage_Supply());
|
||||
FilteredSamples.FastGlowAmps.update(HeaterInfo.getGlowPlug_Current());
|
||||
}
|
||||
|
||||
int sysUptime()
|
||||
|
@ -1776,32 +1538,72 @@ void resetFuelGauge()
|
|||
FuelGauge.reset();
|
||||
}
|
||||
|
||||
void setSSID(const char* name)
|
||||
void setName(const char* name, int type)
|
||||
{
|
||||
sCredentials creds = NVstore.getCredentials();
|
||||
strncpy(creds.APSSID, name, 31);
|
||||
creds.APSSID[31] = 0;
|
||||
char* pDest = NULL;
|
||||
switch (type) {
|
||||
case 0: pDest = creds.APSSID; break;
|
||||
case 1: pDest = creds.webUsername; break;
|
||||
case 2: pDest = creds.webUpdateUsername; break;
|
||||
}
|
||||
if(pDest) {
|
||||
strncpy(pDest, name, 31);
|
||||
pDest[31] = 0;
|
||||
}
|
||||
NVstore.setCredentials(creds);
|
||||
NVstore.save();
|
||||
NVstore.doSave(); // ensure NV storage
|
||||
if(type == 0) {
|
||||
DebugPort.println("Restarting ESP to invoke new network credentials");
|
||||
DebugPort.handle();
|
||||
delay(1000);
|
||||
ESP.restart();
|
||||
}
|
||||
}
|
||||
|
||||
void setAPpassword(const char* name)
|
||||
void setPassword(const char* name, int type)
|
||||
{
|
||||
sCredentials creds = NVstore.getCredentials();
|
||||
strncpy(creds.APpassword, name, 31);
|
||||
creds.APpassword[31] = 0;
|
||||
char* pDest = NULL;
|
||||
switch (type) {
|
||||
case 0: pDest = creds.APpassword; break;
|
||||
case 1: pDest = creds.webPassword; break;
|
||||
case 2: pDest = creds.webUpdatePassword; break;
|
||||
}
|
||||
if(pDest) {
|
||||
strncpy(pDest, name, 31);
|
||||
pDest[31] = 0;
|
||||
}
|
||||
NVstore.setCredentials(creds);
|
||||
NVstore.save();
|
||||
NVstore.doSave(); // ensure NV storage
|
||||
if(type == 0) {
|
||||
DebugPort.println("Restarting ESP to invoke new network credentials");
|
||||
DebugPort.handle();
|
||||
delay(1000);
|
||||
ESP.restart();
|
||||
}
|
||||
}
|
||||
|
||||
void setWebUsername(const char* name)
|
||||
{
|
||||
sCredentials creds = NVstore.getCredentials();
|
||||
strncpy(creds.webUsername, name, 31);
|
||||
creds.webUsername[31] = 0;
|
||||
NVstore.setCredentials(creds);
|
||||
NVstore.save();
|
||||
NVstore.doSave(); // ensure NV storage
|
||||
}
|
||||
|
||||
void setWebPassword(const char* name)
|
||||
{
|
||||
sCredentials creds = NVstore.getCredentials();
|
||||
strncpy(creds.webPassword, name, 31);
|
||||
creds.webPassword[31] = 0;
|
||||
NVstore.setCredentials(creds);
|
||||
NVstore.save();
|
||||
NVstore.doSave(); // ensure NV storage
|
||||
}
|
||||
|
||||
|
||||
|
@ -1812,15 +1614,19 @@ void showMainmenu()
|
|||
DebugPort.println("");
|
||||
DebugPort.printf(" <B> - toggle raw blue wire data reporting, currently %s\r\n", bReportBlueWireData ? "ON" : "OFF");
|
||||
DebugPort.printf(" <J> - toggle output JSON reporting, currently %s\r\n", bReportJSONData ? "ON" : "OFF");
|
||||
DebugPort.printf(" <W> - toggle reporting of blue wire timeout/recycling event, currently %s\r\n", bReportRecyleEvents ? "ON" : "OFF");
|
||||
DebugPort.printf(" <O> - toggle reporting of OEM resync event, currently %s\r\n", bReportOEMresync ? "ON" : "OFF");
|
||||
DebugPort.printf(" <S> - toggle reporting of state machine transits %s\r\n", CommState.isReporting() ? "ON" : "OFF");
|
||||
DebugPort.printf(" <N> - change AP SSID, currently \"%s\"\r\n", NVstore.getCredentials().APSSID);
|
||||
DebugPort.println(" <P> - change AP password");
|
||||
DebugPort.println(" <M> - configure MQTT");
|
||||
DebugPort.println(" <U> - change Web page username");
|
||||
DebugPort.println(" <W> - change Web page password");
|
||||
DebugPort.println(" <Y> - change Web /update username");
|
||||
DebugPort.println(" <Z> - change Web /update password");
|
||||
DebugPort.println(" <+> - request heater turns ON");
|
||||
DebugPort.println(" <-> - request heater turns OFF");
|
||||
DebugPort.println(" <R> - restart the ESP");
|
||||
DebugPort.println(" <CTRL-R> - restart the ESP");
|
||||
DebugPort.printf(" <CTRL-C> - toggle reporting of state machine transits %s\r\n", CommState.isReporting() ? "ON" : "OFF");
|
||||
DebugPort.printf(" <CTRL-O> - toggle reporting of OEM resync event, currently %s\r\n", bReportOEMresync ? "ON" : "OFF");
|
||||
DebugPort.printf(" <CTRL-W> - toggle reporting of blue wire timeout/recycling event, currently %s\r\n", bReportRecyleEvents ? "ON" : "OFF");
|
||||
DebugPort.println("");
|
||||
DebugPort.println("");
|
||||
DebugPort.println("");
|
||||
|
@ -1844,3 +1650,9 @@ void reqHeaterCalUpdate()
|
|||
{
|
||||
TxManage.queueSysUpdate();
|
||||
}
|
||||
|
||||
const CProtocolPackage& getHeaterInfo()
|
||||
{
|
||||
return BlueWireData;
|
||||
}
|
||||
|
||||
|
|
478
src/Protocol/BlueWireTask.cpp
Normal file
478
src/Protocol/BlueWireTask.cpp
Normal file
|
@ -0,0 +1,478 @@
|
|||
/*
|
||||
* This file is part of the "bluetoothheater" distribution
|
||||
* (https://gitlab.com/mrjones.id.au/bluetoothheater)
|
||||
*
|
||||
* Copyright (C) 2018 Ray Jones <ray@mrjones.id.au>
|
||||
* Copyright (C) 2018 James Clark
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "BlueWireTask.h"
|
||||
#include "../cfg/BTCConfig.h"
|
||||
#include "../cfg/pins.h"
|
||||
#include "Protocol.h"
|
||||
#include "TxManage.h"
|
||||
// #include "SmartError.h"
|
||||
#include "../Utility/UtilClasses.h"
|
||||
#include "../Utility/DataFilter.h"
|
||||
#include "../Utility/FuelGauge.h"
|
||||
#include "../Utility/HourMeter.h"
|
||||
#include "../Utility/macros.h"
|
||||
|
||||
// Setup Serial Port Definitions
|
||||
#if defined(__arm__)
|
||||
// Required for Arduino Due, UARTclass is derived from HardwareSerial
|
||||
static UARTClass& BlueWireSerial(Serial1);
|
||||
#else
|
||||
// for ESP32, Mega
|
||||
// HardwareSerial is it for these boards
|
||||
static HardwareSerial& BlueWireSerial(Serial1);
|
||||
#endif
|
||||
|
||||
#define RX_DATA_TIMOUT 50
|
||||
|
||||
CommStates CommState;
|
||||
CTxManage TxManage(TxEnbPin, BlueWireSerial);
|
||||
CProtocol DefaultBTCParams(CProtocol::CtrlMode); // defines the default parameters, used in case of no OEM controller
|
||||
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
|
||||
// CSmartError SmartError;
|
||||
CProtocolPackage reportHeaterData;
|
||||
CProtocolPackage primaryHeaterData;
|
||||
|
||||
static bool bHasOEMController = false;
|
||||
static bool bHasOEMLCDController = false;
|
||||
static bool bHasHtrData = false;
|
||||
|
||||
extern bool bReportRecyleEvents;
|
||||
extern bool bReportOEMresync;
|
||||
extern bool bReportBlueWireData;
|
||||
extern sFilteredData FilteredSamples;
|
||||
|
||||
QueueHandle_t BlueWireMsgBuf = NULL; // cannot use general Serial.print etc from this task without causing conflicts
|
||||
QueueHandle_t BlueWireRxQueue = NULL; // queue to pass down heater receive data
|
||||
QueueHandle_t BlueWireTxQueue = NULL; // queue to pass down heater transmit data
|
||||
SemaphoreHandle_t BlueWireSemaphore = NULL; // flag to indicate completion of heater data exchange
|
||||
|
||||
bool validateFrame(const CProtocol& frame, const char* name);
|
||||
void DebugReportFrame(const char* hdr, const CProtocol& Frame, const char* ftr, char* msg);
|
||||
// void updateFilteredData();
|
||||
void initBlueWireSerial();
|
||||
|
||||
void pushDebugMsg(const char* msg) {
|
||||
if(BlueWireMsgBuf)
|
||||
xQueueSend(BlueWireMsgBuf, msg, 0);
|
||||
}
|
||||
|
||||
void BlueWireTask(void*) {
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// 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
|
||||
//
|
||||
static unsigned long lastRxTime = 0; // used to observe inter character delays
|
||||
static unsigned long moderator = 50;
|
||||
bool isBTCmaster = false;
|
||||
|
||||
BlueWireMsgBuf = xQueueCreate(4, BLUEWIRE_MSGQUEUESIZE);
|
||||
BlueWireRxQueue = xQueueCreate(4, BLUEWIRE_DATAQUEUESIZE);
|
||||
BlueWireTxQueue = xQueueCreate(4, BLUEWIRE_DATAQUEUESIZE);
|
||||
BlueWireSemaphore = xSemaphoreCreateBinary();
|
||||
|
||||
TxManage.begin(); // ensure Tx enable pin is setup
|
||||
|
||||
// define defaults should OEM controller be missing
|
||||
DefaultBTCParams.setHeaterDemand(23);
|
||||
DefaultBTCParams.setTemperature_Actual(22);
|
||||
DefaultBTCParams.setSystemVoltage(12.0);
|
||||
DefaultBTCParams.setPump_Min(1.6f);
|
||||
DefaultBTCParams.setPump_Max(5.5f);
|
||||
DefaultBTCParams.setFan_Min(1680);
|
||||
DefaultBTCParams.setFan_Max(4500);
|
||||
DefaultBTCParams.Controller.FanSensor = 1;
|
||||
|
||||
initBlueWireSerial();
|
||||
|
||||
CommState.setCallback(pushDebugMsg);
|
||||
TxManage.setCallback(pushDebugMsg);
|
||||
|
||||
for(;;) {
|
||||
|
||||
sRxData BlueWireRxData;
|
||||
unsigned long timenow = millis();
|
||||
|
||||
// calc elapsed time since last rxd byte
|
||||
// used to detect no OEM controller, or the start of an OEM frame sequence
|
||||
unsigned long RxTimeElapsed = timenow - lastRxTime;
|
||||
|
||||
if (BlueWireSerial.available()) {
|
||||
// Data is available, read and store it now, use it later
|
||||
// Note that if not in a recognised data receive frame state, the data
|
||||
// will be deliberately lost!
|
||||
BlueWireRxData.setValue(BlueWireSerial.read()); // read hex byte, store for later use
|
||||
|
||||
lastRxTime = timenow; // tickle last rx time, for rx data timeout purposes
|
||||
}
|
||||
|
||||
|
||||
// precautionary state machine action if all 24 bytes were not received
|
||||
// whilst expecting a frame from the blue wire
|
||||
if(RxTimeElapsed > RX_DATA_TIMOUT) {
|
||||
|
||||
|
||||
if( CommState.is(CommStates::OEMCtrlRx) ||
|
||||
CommState.is(CommStates::HeaterRx1) ||
|
||||
CommState.is(CommStates::HeaterRx2) ) {
|
||||
|
||||
if(RxTimeElapsed >= moderator) {
|
||||
moderator += 10;
|
||||
if(bReportRecyleEvents) {
|
||||
char msg[32];
|
||||
sprintf(msg, "%ldms - ", RxTimeElapsed);
|
||||
pushDebugMsg(msg);
|
||||
}
|
||||
if(CommState.is(CommStates::OEMCtrlRx)) {
|
||||
bHasOEMController = false;
|
||||
bHasOEMLCDController = false;
|
||||
if(bReportRecyleEvents) {
|
||||
pushDebugMsg("Timeout collecting OEM controller data, returning to Idle State\r\n");
|
||||
}
|
||||
}
|
||||
else if(CommState.is(CommStates::HeaterRx1)) {
|
||||
bHasHtrData = false;
|
||||
if(bReportRecyleEvents) {
|
||||
pushDebugMsg("Timeout collecting OEM heater response data, returning to Idle State\r\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
bHasHtrData = false;
|
||||
if(bReportRecyleEvents) {
|
||||
pushDebugMsg("Timeout collecting BTC heater response data, returning to Idle State\r\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(bReportRecyleEvents) {
|
||||
pushDebugMsg("Recycling blue wire serial interface\r\n");
|
||||
}
|
||||
#ifdef REBOOT_BLUEWIRE
|
||||
initBlueWireSerial();
|
||||
#endif
|
||||
CommState.set(CommStates::ExchangeComplete); // revert to idle mode, after passing thru exchange complete mode
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// do our state machine to track the reception and delivery of blue wire data
|
||||
|
||||
switch(CommState.get()) {
|
||||
|
||||
case CommStates::Idle:
|
||||
|
||||
moderator = 50;
|
||||
|
||||
digitalWrite(LED_Pin, LOW);
|
||||
// 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 >= (NVstore.getUserSettings().FrameRate - 60)) { // compensate for the time spent just doing things in this state machine
|
||||
// have not seen any receive data for a second (typ.).
|
||||
// OEM controller is probably not connected.
|
||||
// Skip state machine immediately to BTC_Tx, sending our own settings.
|
||||
bHasHtrData = false;
|
||||
bHasOEMController = false;
|
||||
bHasOEMLCDController = false;
|
||||
isBTCmaster = true;
|
||||
TxManage.PrepareFrame(DefaultBTCParams, isBTCmaster); // use our parameters, and mix in NV storage values
|
||||
TxManage.Start(timenow);
|
||||
CommState.set(CommStates::TxStart);
|
||||
break;
|
||||
}
|
||||
|
||||
if(BlueWireRxData.available() && (RxTimeElapsed > (RX_DATA_TIMOUT+10))) {
|
||||
|
||||
if(bReportOEMresync) {
|
||||
char msg[64];
|
||||
sprintf(msg, "Re-sync'd with OEM Controller. %ldms Idle time.\r\n", RxTimeElapsed);
|
||||
pushDebugMsg(msg);
|
||||
}
|
||||
|
||||
bHasHtrData = false;
|
||||
bHasOEMController = true;
|
||||
CommState.set(CommStates::OEMCtrlRx); // we must add this new byte!
|
||||
//
|
||||
// ** IMPORTANT - we must drop through to OEMCtrlRx *NOW* (skipping break) **
|
||||
// ** otherwise the first byte will be lost! **
|
||||
//
|
||||
}
|
||||
else {
|
||||
break; // only break if we fail all Idle state tests
|
||||
}
|
||||
|
||||
|
||||
case CommStates::OEMCtrlRx:
|
||||
|
||||
digitalWrite(LED_Pin, HIGH);
|
||||
|
||||
// collect OEM controller frame
|
||||
if(BlueWireRxData.available()) {
|
||||
if(CommState.collectData(OEMCtrlFrame, BlueWireRxData.getValue()) ) {
|
||||
CommState.set(CommStates::OEMCtrlValidate); // collected 24 bytes, move on!
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case CommStates::OEMCtrlValidate:
|
||||
|
||||
digitalWrite(LED_Pin, LOW);
|
||||
|
||||
// test for valid CRC, abort and restarts Serial1 if invalid
|
||||
if(!validateFrame(OEMCtrlFrame, "OEM")) {
|
||||
break;
|
||||
}
|
||||
|
||||
// filled OEM controller frame
|
||||
OEMCtrlFrame.setTime();
|
||||
// LCD controllers use 0x76 as first byte, rotary knobs use 0x78
|
||||
bHasOEMLCDController = (OEMCtrlFrame.Controller.Byte0 != 0x78);
|
||||
|
||||
// xQueueSend(BlueWireTxQueue, OEMCtrlFrame.Data, 0);
|
||||
|
||||
CommState.set(CommStates::HeaterRx1);
|
||||
break;
|
||||
|
||||
|
||||
case CommStates::HeaterRx1:
|
||||
|
||||
digitalWrite(LED_Pin, HIGH);
|
||||
|
||||
// collect heater frame, always in response to an OEM controller frame
|
||||
if(BlueWireRxData.available()) {
|
||||
if( CommState.collectData(HeaterFrame1, BlueWireRxData.getValue()) ) {
|
||||
CommState.set(CommStates::HeaterValidate1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case CommStates::HeaterValidate1:
|
||||
|
||||
digitalWrite(LED_Pin, LOW);
|
||||
|
||||
// test for valid CRC, abort and restarts Serial1 if invalid
|
||||
if(!validateFrame(HeaterFrame1, "RX1")) {
|
||||
bHasHtrData = false;
|
||||
break;
|
||||
}
|
||||
bHasHtrData = true;
|
||||
|
||||
HeaterFrame1.setTime();
|
||||
|
||||
while(BlueWireSerial.available()) {
|
||||
pushDebugMsg("DUMPED ROGUE RX DATA\r\n");
|
||||
BlueWireSerial.read();
|
||||
}
|
||||
BlueWireSerial.flush();
|
||||
|
||||
// received heater frame (after controller message), report
|
||||
primaryHeaterData.set(HeaterFrame1, OEMCtrlFrame); // OEM is always *the* controller
|
||||
if(bReportBlueWireData) {
|
||||
primaryHeaterData.reportFrames(true, pushDebugMsg);
|
||||
}
|
||||
isBTCmaster = false;
|
||||
TxManage.PrepareFrame(OEMCtrlFrame, isBTCmaster); // parrot OEM parameters, but block NV modes
|
||||
CommState.set(CommStates::TxStart);
|
||||
break;
|
||||
|
||||
|
||||
case CommStates::TxStart:
|
||||
xQueueSend(BlueWireTxQueue, TxManage.getFrame().Data, 0);
|
||||
TxManage.Start(timenow);
|
||||
CommState.set(CommStates::TxInterval);
|
||||
break;
|
||||
|
||||
|
||||
case CommStates::TxInterval:
|
||||
// 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
|
||||
CommState.set(CommStates::HeaterRx2); // then await heater repsonse
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case CommStates::HeaterRx2:
|
||||
|
||||
digitalWrite(LED_Pin, HIGH);
|
||||
|
||||
// collect heater frame, in response to our control frame
|
||||
if(BlueWireRxData.available()) {
|
||||
#ifdef BADSTARTCHECK
|
||||
if(!CommState.checkValidStart(BlueWireData.getValue())) {
|
||||
DebugPort.println("***** Invalid start of frame - restarting Serial port *****");
|
||||
initBlueWireSerial();
|
||||
CommState.set(CommStates::Idle);
|
||||
}
|
||||
else {
|
||||
if( CommState.collectData(HeaterFrame2, BlueWireData.getValue()) ) {
|
||||
CommState.set(CommStates::HeaterValidate2);
|
||||
}
|
||||
}
|
||||
#else
|
||||
if( CommState.collectData(HeaterFrame2, BlueWireRxData.getValue()) ) {
|
||||
CommState.set(CommStates::HeaterValidate2);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case CommStates::HeaterValidate2:
|
||||
|
||||
digitalWrite(LED_Pin, LOW);
|
||||
|
||||
// test for valid CRC, abort and restart Serial1 if invalid
|
||||
if(!validateFrame(HeaterFrame2, "RX2")) {
|
||||
bHasHtrData = false;
|
||||
break;
|
||||
}
|
||||
bHasHtrData = true;
|
||||
|
||||
// received heater frame (after our control message), report
|
||||
|
||||
xQueueSend(BlueWireRxQueue, HeaterFrame2.Data, 0);
|
||||
|
||||
// do some monitoring of the heater state variables
|
||||
// if abnormal transitions, introduce a smart error!
|
||||
// SmartError.monitor(HeaterFrame2);
|
||||
|
||||
if(!bHasOEMController) // no OEM controller - BTC is *the* controller
|
||||
primaryHeaterData.set(HeaterFrame2, TxManage.getFrame());
|
||||
|
||||
if(bReportBlueWireData) { // debug or investigation purposes
|
||||
reportHeaterData.set(HeaterFrame2, TxManage.getFrame());
|
||||
reportHeaterData.reportFrames(false, pushDebugMsg);
|
||||
}
|
||||
CommState.set(CommStates::ExchangeComplete);
|
||||
break;
|
||||
|
||||
|
||||
case CommStates::ExchangeComplete:
|
||||
xSemaphoreGive(BlueWireSemaphore);
|
||||
CommState.set(CommStates::Idle);
|
||||
break;
|
||||
} // switch(CommState)
|
||||
|
||||
vTaskDelay(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool validateFrame(const CProtocol& frame, const char* name)
|
||||
{
|
||||
if(!frame.verifyCRC(pushDebugMsg)) {
|
||||
// Bad CRC - restart blue wire Serial port
|
||||
char msg[128];
|
||||
sprintf(msg, "\007Bad CRC detected for %s frame - restarting blue wire's serial port\r\n", name);
|
||||
pushDebugMsg(msg);
|
||||
msg[0] = 0; // empty string
|
||||
DebugReportFrame("BAD CRC:", frame, "\r\n", msg);
|
||||
pushDebugMsg(msg);
|
||||
#ifdef REBOOT_BLUEWIRE
|
||||
initBlueWireSerial();
|
||||
#endif
|
||||
CommState.set(CommStates::ExchangeComplete);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void DebugReportFrame(const char* hdr, const CProtocol& Frame, const char* ftr, char* msg)
|
||||
{
|
||||
strcat(msg, hdr); // header
|
||||
for(int i=0; i<24; i++) {
|
||||
char str[16];
|
||||
sprintf(str, " %02X", Frame.Data[i]); // build 2 dig hex values
|
||||
strcat(msg, str); // and print
|
||||
}
|
||||
strcat(msg, ftr); // footer
|
||||
}
|
||||
|
||||
bool hasOEMcontroller()
|
||||
{
|
||||
return bHasOEMController;
|
||||
}
|
||||
|
||||
bool hasOEMLCDcontroller()
|
||||
{
|
||||
return bHasOEMLCDController;
|
||||
}
|
||||
|
||||
bool hasHtrData()
|
||||
{
|
||||
return bHasHtrData;
|
||||
}
|
||||
|
||||
void initBlueWireSerial()
|
||||
{
|
||||
// initialize serial port to interact with the "blue wire"
|
||||
// 25000 baud, Tx and Rx channels of Chinese heater comms interface:
|
||||
// Tx/Rx data to/from heater,
|
||||
// Note special baud rate for Chinese heater controllers
|
||||
#if defined(__arm__) || defined(__AVR__)
|
||||
BlueWireSerial.begin(25000);
|
||||
pinMode(Rx1Pin, INPUT_PULLUP); // required for MUX to work properly
|
||||
#elif ESP32
|
||||
// ESP32
|
||||
BlueWireSerial.begin(25000, SERIAL_8N1, Rx1Pin, Tx1Pin); // need to explicitly specify pins for pin multiplexer!
|
||||
pinMode(Rx1Pin, INPUT_PULLUP); // required for MUX to work properly
|
||||
#endif
|
||||
}
|
||||
|
||||
// 0x00 - Normal: BTC, with heater responding
|
||||
// 0x01 - Error: BTC, heater not responding
|
||||
// 0x02 - Special: OEM controller & heater responding
|
||||
// 0x03 - Error: OEM controller, heater not responding
|
||||
int getBlueWireStat()
|
||||
{
|
||||
int stat = 0;
|
||||
if(!bHasHtrData) {
|
||||
stat |= 0x01;
|
||||
}
|
||||
if(bHasOEMController) {
|
||||
stat |= 0x02;
|
||||
}
|
||||
return stat;
|
||||
}
|
||||
|
||||
const char* getBlueWireStatStr()
|
||||
{
|
||||
static const char* BlueWireStates[] = { "BTC,Htr", "BTC", "OEM,Htr", "OEM" };
|
||||
|
||||
return BlueWireStates[getBlueWireStat()];
|
||||
}
|
||||
|
||||
void reqPumpPrime(bool on)
|
||||
{
|
||||
DefaultBTCParams.setPump_Prime(on);
|
||||
}
|
||||
|
||||
|
40
src/Protocol/BlueWireTask.h
Normal file
40
src/Protocol/BlueWireTask.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* This file is part of the "bluetoothheater" distribution
|
||||
* (https://gitlab.com/mrjones.id.au/bluetoothheater)
|
||||
*
|
||||
* Copyright (C) 2018 Ray Jones <ray@mrjones.id.au>
|
||||
* Copyright (C) 2018 James Clark
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#ifndef __BLUEWIRETASK_H__
|
||||
#define __BLUEWIRETASK_H__
|
||||
|
||||
#include <FreeRTOS.h>
|
||||
#include "../Utility/UtilClasses.h"
|
||||
|
||||
extern QueueHandle_t BlueWireMsgBuf; // cannot use general Serial.print etc from this task without causing conflicts
|
||||
extern QueueHandle_t BlueWireRxQueue; // queue to pass down heater receive data
|
||||
extern QueueHandle_t BlueWireTxQueue; // queue to pass down heater transmit data
|
||||
extern SemaphoreHandle_t BlueWireSemaphore; // flag to indicate completion of heater data exchange
|
||||
|
||||
const int BLUEWIRE_MSGQUEUESIZE = 192;
|
||||
const int BLUEWIRE_DATAQUEUESIZE = 24;
|
||||
|
||||
extern void BlueWireTask(void*);
|
||||
extern CommStates CommState;
|
||||
|
||||
|
||||
#endif
|
|
@ -54,15 +54,17 @@ CProtocol::getCRC() const
|
|||
|
||||
// return true for CRC match
|
||||
bool
|
||||
CProtocol::verifyCRC(bool bSilent) const
|
||||
CProtocol::verifyCRC(std::function<void(const char*)> pushMsg) const
|
||||
{
|
||||
char errmsg[32];
|
||||
CModBusCRC16 CRCengine;
|
||||
uint16_t CRC = CRCengine.process(22, Data); // calculate CRC based on first 22 bytes of our data buffer
|
||||
|
||||
uint16_t FrameCRC = getCRC();
|
||||
bool bOK = (FrameCRC == CRC);
|
||||
if(!bOK && !bSilent) {
|
||||
DebugPort.printf("verifyCRC FAILED: calc: %04X data: %04X\r\n", CRC, FrameCRC);
|
||||
if(!bOK) {
|
||||
sprintf(errmsg, "verifyCRC FAILED: calc: %04X data: %04X\r\n", CRC, FrameCRC);
|
||||
pushMsg(errmsg);
|
||||
}
|
||||
return bOK; // does it match the stored values?
|
||||
}
|
||||
|
@ -430,16 +432,19 @@ CProtocolPackage::setRefTime()
|
|||
}*/
|
||||
|
||||
void
|
||||
CProtocolPackage::reportFrames(bool isOEM)
|
||||
CProtocolPackage::reportFrames(bool isOEM, std::function<void(const char*)> pushMsg)
|
||||
{
|
||||
_timeStamp.report(); // absolute time
|
||||
char msg[192];
|
||||
msg[0] = 0;
|
||||
_timeStamp.report(msg); // absolute time
|
||||
if(isOEM) {
|
||||
DebugReportFrame("OEM:", Controller, TERMINATE_OEM_LINE ? "\r\n" : " ");
|
||||
DebugReportFrame("OEM:", Controller, TERMINATE_OEM_LINE ? "\r\n" : " ", msg);
|
||||
}
|
||||
else {
|
||||
DebugReportFrame("BTC:", Controller, TERMINATE_BTC_LINE ? "\r\n" : " ");
|
||||
DebugReportFrame("BTC:", Controller, TERMINATE_BTC_LINE ? "\r\n" : " ", msg);
|
||||
}
|
||||
DebugReportFrame("HTR:", Heater, "\r\n");
|
||||
DebugReportFrame("HTR:", Heater, "\r\n", msg);
|
||||
pushMsg(msg);
|
||||
}
|
||||
|
||||
int
|
||||
|
|
|
@ -95,7 +95,7 @@ public:
|
|||
void setCRC(); // calculate and set the CRC in the buffer
|
||||
void setCRC(uint16_t CRC); // set the CRC in the buffer
|
||||
uint16_t getCRC() const; // extract CRC value from buffer
|
||||
bool verifyCRC(bool silent=false) const; // return true for CRC match
|
||||
bool verifyCRC(std::function<void(const char*)> pushMsg) const; // return true for CRC match
|
||||
|
||||
void setActiveMode() { Controller.Byte0 = 0x76; }; // this allows heater to save tuning params to EEPROM
|
||||
void setPassiveMode() { Controller.Byte0 = 0x78; }; // this prevents heater saving tuning params to EEPROM
|
||||
|
@ -214,7 +214,7 @@ public:
|
|||
int getAltitude() const { return Controller.getAltitude(); };
|
||||
|
||||
// void setRefTime();
|
||||
void reportFrames(bool isOEM);
|
||||
void reportFrames(bool isOEM, std::function<void(const char*)> pushMsg);
|
||||
};
|
||||
|
||||
extern const CProtocolPackage& getHeaterInfo();
|
||||
|
|
|
@ -59,8 +59,7 @@ CSmartError::inhibit(bool reseterror)
|
|||
void
|
||||
CSmartError::monitor(const CProtocol& heaterFrame)
|
||||
{
|
||||
bool bSilent = true;
|
||||
if(heaterFrame.verifyCRC(bSilent)) { // check but don't report dodgy frames to debug
|
||||
if(heaterFrame.verifyCRC(NULL)) { // check but don't report dodgy frames to debug
|
||||
// only accept valid heater frames!
|
||||
monitor(heaterFrame.getRunState());
|
||||
}
|
||||
|
|
|
@ -28,8 +28,6 @@
|
|||
//#define DEBUG_THERMOSTAT
|
||||
|
||||
|
||||
extern void DebugReportFrame(const char* hdr, const CProtocol&, const char* ftr);
|
||||
|
||||
// CTxManage is used to send a data frame to the blue wire
|
||||
//
|
||||
// As the blue wire is bidirectional, we need to only allow our transmit data
|
||||
|
@ -71,6 +69,7 @@ CTxManage::CTxManage(int TxGatePin, HardwareSerial& serial) :
|
|||
m_nTxGatePin = TxGatePin;
|
||||
_rawCommand = 0;
|
||||
m_HWTimer = NULL;
|
||||
_callback = NULL;
|
||||
}
|
||||
|
||||
// static function used for the tx gate termination
|
||||
|
@ -198,7 +197,11 @@ CTxManage::PrepareFrame(const CProtocol& basisFrame, bool isBTCmaster)
|
|||
float tDelta = tCurrent - tDesired;
|
||||
float fTemp;
|
||||
#ifdef DEBUG_THERMOSTAT
|
||||
DebugPort.printf("Window=%.1f tCurrent=%.1f tDesired=%.1f tDelta=%.1f\r\n", Window, tCurrent, tDesired, tDelta);
|
||||
if(_callback) {
|
||||
char msg[80];
|
||||
sprintf(msg, "Window=%.1f tCurrent=%.1f tDesired=%.1f tDelta=%.1f\r\n", Window, tCurrent, tDesired, tDelta);
|
||||
_callback(msg);
|
||||
}
|
||||
#endif
|
||||
Window /= 2;
|
||||
switch(ThermoMode) {
|
||||
|
@ -224,7 +227,11 @@ CTxManage::PrepareFrame(const CProtocol& basisFrame, bool isBTCmaster)
|
|||
s8Temp = (int8_t)(tActual + 0.5);
|
||||
m_TxFrame.setTemperature_Actual(s8Temp);
|
||||
#ifdef DEBUG_THERMOSTAT
|
||||
DebugPort.printf("Conventional thermostat mode: tActual = %d\r\n", u8Temp);
|
||||
if(_callback) {
|
||||
char msg[80];
|
||||
sprintf(msg, "Conventional thermostat mode: tActual = %d\r\n", u8Temp);
|
||||
_callback(msg);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
|
@ -241,7 +248,11 @@ CTxManage::PrepareFrame(const CProtocol& basisFrame, bool isBTCmaster)
|
|||
}
|
||||
m_TxFrame.setTemperature_Actual(s8Temp);
|
||||
#ifdef DEBUG_THERMOSTAT
|
||||
DebugPort.printf("Heater controlled windowed thermostat mode: tActual=%d\r\n", u8Temp);
|
||||
if(_callback) {
|
||||
char msg[80];
|
||||
sprintf(msg, "Heater controlled windowed thermostat mode: tActual=%d\r\n", u8Temp);
|
||||
_callback(msg);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
|
@ -251,7 +262,11 @@ CTxManage::PrepareFrame(const CProtocol& basisFrame, bool isBTCmaster)
|
|||
// so create a desired "temp" according the the current hystersis
|
||||
tDelta /= Window; // convert tDelta to fraction of window (CAUTION - may be > +-1 !)
|
||||
#ifdef DEBUG_THERMOSTAT
|
||||
if(_callback) {
|
||||
char msg[80];
|
||||
DebugPort.printf("Linear window thermostat mode: Fraction=%f", tDelta);
|
||||
_callback(msg);
|
||||
}
|
||||
#endif
|
||||
fTemp = (m_TxFrame.getTemperature_Max() + m_TxFrame.getTemperature_Min()) * 0.5; // midpoint - tDelta = 0 hinges here
|
||||
tDelta *= (m_TxFrame.getTemperature_Max() - fTemp); // linear offset from setpoint
|
||||
|
@ -265,7 +280,11 @@ CTxManage::PrepareFrame(const CProtocol& basisFrame, bool isBTCmaster)
|
|||
m_TxFrame.setThermostatModeProtocol(0); // direct heater to use Hz Mode
|
||||
m_TxFrame.setTemperature_Actual(0); // must force actual to 0 for Hz mode
|
||||
#ifdef DEBUG_THERMOSTAT
|
||||
DebugPort.printf(" tDesired (pseudo Hz demand) = %d\r\n", u8Temp);
|
||||
if(_callback) {
|
||||
char msg[80];
|
||||
sprintf(msg, " tDesired (pseudo Hz demand) = %d\r\n", u8Temp);
|
||||
_callback(msg);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ class CTxManage
|
|||
const int m_nFrameTime = 14;
|
||||
const int m_nFrontPorch = 0;
|
||||
int m_sysUpdate;
|
||||
std::function<void(const char*)> _callback;
|
||||
|
||||
public:
|
||||
CTxManage(int TxGatePin, HardwareSerial& serial);
|
||||
|
@ -40,6 +41,7 @@ public:
|
|||
const CProtocol& getFrame() const { return m_TxFrame; };
|
||||
static void IRAM_ATTR callbackGateTerminate();
|
||||
void queueSysUpdate(); // use to implant NV settings into heater
|
||||
void setCallback(std::function<void(const char*)> fn) { _callback = fn; };
|
||||
|
||||
private:
|
||||
HardwareSerial& m_BlueWireSerial;
|
||||
|
|
|
@ -30,6 +30,6 @@ class CProtocol;
|
|||
|
||||
extern ABTelnetSpy DebugPort;
|
||||
|
||||
void DebugReportFrame(const char* hdr, const CProtocol& Frame, const char* ftr);
|
||||
void DebugReportFrame(const char* hdr, const CProtocol& Frame, const char* ftr, char* msg);
|
||||
|
||||
#endif // __DEBUGPORT_H__
|
||||
|
|
|
@ -438,16 +438,22 @@ CBME280Sensor::getTemperature(float& tempReading, bool filtered)
|
|||
}
|
||||
|
||||
bool
|
||||
CBME280Sensor::getAltitude(float& reading)
|
||||
CBME280Sensor::getAltitude(float& reading, bool fresh)
|
||||
{
|
||||
reading = _bme.readAltitude(1013.25); //use standard atmosphere as reference
|
||||
if(fresh) {
|
||||
_fAltitude = _bme.readAltitude(1013.25); //use standard atmosphere as reference
|
||||
}
|
||||
reading = _fAltitude;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CBME280Sensor::getHumidity(float& reading)
|
||||
CBME280Sensor::getHumidity(float& reading, bool fresh)
|
||||
{
|
||||
reading = _bme.readHumidity();
|
||||
if(fresh) {
|
||||
_fHumidity = _bme.readHumidity();
|
||||
}
|
||||
reading = _fHumidity;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -489,6 +495,10 @@ CTempSense::startConvert()
|
|||
bool
|
||||
CTempSense::readSensors()
|
||||
{
|
||||
float fDummy;
|
||||
getAltitude(fDummy, true);
|
||||
getHumidity(fDummy, true);
|
||||
|
||||
return DS18B20.readSensors();
|
||||
}
|
||||
|
||||
|
@ -628,19 +638,19 @@ CTempSense::getTemperatureBME280(float& reading)
|
|||
}
|
||||
|
||||
bool
|
||||
CTempSense::getAltitude(float& reading)
|
||||
CTempSense::getAltitude(float& reading, bool fresh)
|
||||
{
|
||||
if(BME280.getCount())
|
||||
return BME280.getAltitude(reading);
|
||||
return BME280.getAltitude(reading, fresh);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
CTempSense::getHumidity(float& reading)
|
||||
CTempSense::getHumidity(float& reading, bool fresh)
|
||||
{
|
||||
if(BME280.getCount())
|
||||
return BME280.getHumidity(reading);
|
||||
return BME280.getHumidity(reading, fresh);
|
||||
else
|
||||
return false;
|
||||
}
|
|
@ -89,13 +89,15 @@ public:
|
|||
class CBME280Sensor : public CSensor {
|
||||
Adafruit_BME280 _bme; // I2C
|
||||
long _lastSampleTime;
|
||||
float _fAltitude;
|
||||
float _fHumidity;
|
||||
int _count;
|
||||
public:
|
||||
CBME280Sensor();
|
||||
bool begin(int ID);
|
||||
bool getTemperature(float& tempReading, bool filtered) ;
|
||||
bool getAltitude(float& reading);
|
||||
bool getHumidity(float& reading);
|
||||
bool getAltitude(float& reading, bool fresh=false);
|
||||
bool getHumidity(float& reading, bool fresh=false);
|
||||
const char* getID();
|
||||
int getCount() const { return _count; };
|
||||
};
|
||||
|
@ -119,8 +121,8 @@ public:
|
|||
bool getTemperatureBME280(float& tempReading) ; // index is sensor discovery order on one-wire bus
|
||||
bool getTemperatureDS18B20Idx(int sensIdx, float& tempReading) ; // index is sensor discovery order on one-wire bus
|
||||
int getNumSensors() const;
|
||||
bool getAltitude(float& reading);
|
||||
bool getHumidity(float& reading);
|
||||
bool getAltitude(float& reading, bool fresh=false);
|
||||
bool getHumidity(float& reading, bool fresh=false);
|
||||
CBME280Sensor& getBME280() { return BME280; };
|
||||
CDS18B20SensorSet& getDS18B20() { return DS18B20; };
|
||||
static void format(char* msg, float fTemp);
|
||||
|
|
|
@ -38,13 +38,16 @@ CommStates::set(eCS eState)
|
|||
{
|
||||
_State = eState;
|
||||
_Count = 0;
|
||||
if(_report) {
|
||||
if(_report && _callback != NULL) {
|
||||
static const char* stateNames[] = {
|
||||
"Idle", "OEMCtrlRx", "OEMCtrlValidate", "HeaterRx1", "HeaterValidate1", "HeaterReport1",
|
||||
"BTC_Tx", "HeaterRx2", "HeaterValidate2", "HeaterReport2", "TemperatureRead"
|
||||
"Idle", "OEMCtrlRx", "OEMCtrlValidate", "HeaterRx1", "HeaterValidate1", "TxStart",
|
||||
"TxInterval", "HeaterRx2", "HeaterValidate2", "ExchangeComplete"
|
||||
};
|
||||
if(_State == Idle) DebugPort.println(""); // clear screen
|
||||
DebugPort.printf("State: %s\r\n", stateNames[_State]);
|
||||
if(_State == Idle)
|
||||
_callback("\r\n");
|
||||
char msg[32];
|
||||
sprintf(msg, "State: %s\r\n", stateNames[_State]);
|
||||
_callback(msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,17 +57,6 @@ CommStates::collectData(CProtocol& Frame, uint8_t val, int limit) { // returns
|
|||
return _Count >= limit;
|
||||
}
|
||||
|
||||
bool
|
||||
CommStates::collectDataEx(CProtocol& Frame, uint8_t val, int limit) { // returns true when buffer filled
|
||||
// guarding against rogue rx kernel buffer stutters....
|
||||
if((_Count == 0) && (val != 0x76)) {
|
||||
DebugPort.println("First heater byte not 0x76 - SKIPPING");
|
||||
return false;
|
||||
}
|
||||
Frame.Data[_Count++] = val;
|
||||
return _Count >= limit;
|
||||
}
|
||||
|
||||
bool
|
||||
CommStates::checkValidStart(uint8_t val)
|
||||
{
|
||||
|
@ -87,6 +79,8 @@ CommStates::delayExpired()
|
|||
return(test >= 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
CProfile::CProfile()
|
||||
{
|
||||
tStart = millis();
|
||||
|
@ -103,6 +97,8 @@ CProfile::elapsed(bool reset/* = false*/)
|
|||
return retval;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void DecodeCmd(const char* cmd, String& payload)
|
||||
{
|
||||
int val;
|
||||
|
|
|
@ -29,11 +29,14 @@
|
|||
class CProtocol;
|
||||
|
||||
// a class to track the blue wire receive / transmit states
|
||||
|
||||
#define COMMSTATES_CALLBACK_SIGNATURE std::function<void(char*)> CScallback
|
||||
|
||||
class CommStates {
|
||||
public:
|
||||
// comms states
|
||||
enum eCS {
|
||||
Idle, OEMCtrlRx, OEMCtrlValidate, HeaterRx1, HeaterValidate1, HeaterReport1, BTC_Tx, HeaterRx2, HeaterValidate2, HeaterReport2,TemperatureRead
|
||||
Idle, OEMCtrlRx, OEMCtrlValidate, HeaterRx1, HeaterValidate1, TxStart, TxInterval, HeaterRx2, HeaterValidate2, ExchangeComplete
|
||||
};
|
||||
|
||||
private:
|
||||
|
@ -41,12 +44,14 @@ private:
|
|||
int _Count;
|
||||
unsigned long _delay;
|
||||
bool _report;
|
||||
std::function<void(const char*)> _callback;
|
||||
public:
|
||||
CommStates() {
|
||||
_State = Idle;
|
||||
_Count = 0;
|
||||
_delay = millis();
|
||||
_report = REPORT_STATE_MACHINE_TRANSITIONS;
|
||||
_report = false;
|
||||
_callback = NULL;
|
||||
}
|
||||
void set(eCS eState);
|
||||
eCS get() {
|
||||
|
@ -56,7 +61,6 @@ public:
|
|||
return _State == eState;
|
||||
}
|
||||
bool collectData(CProtocol& Frame, uint8_t val, int limit = 24);
|
||||
bool collectDataEx(CProtocol& Frame, uint8_t val, int limit = 24);
|
||||
bool checkValidStart(uint8_t val);
|
||||
void setDelay(int ms);
|
||||
bool delayExpired();
|
||||
|
@ -67,6 +71,7 @@ public:
|
|||
bool isReporting() {
|
||||
return _report != 0;
|
||||
};
|
||||
void setCallback(std::function<void(const char*)> fn) { _callback = fn; };
|
||||
};
|
||||
|
||||
|
||||
|
@ -128,18 +133,28 @@ public:
|
|||
void setRefTime() {
|
||||
refTime = millis();
|
||||
};
|
||||
void report(bool isDelta) {
|
||||
void report(bool isDelta, char* msg=NULL) {
|
||||
if(isDelta) {
|
||||
long delta = millis() - prevTime;
|
||||
if(msg)
|
||||
sprintf(msg, "%+8ldms ", delta);
|
||||
else
|
||||
DebugPort.printf("%+8ldms ", delta);
|
||||
}
|
||||
else {
|
||||
prevTime = millis();
|
||||
if(msg)
|
||||
sprintf(msg, "%8ldms ", prevTime - refTime);
|
||||
else
|
||||
DebugPort.printf("%8ldms ", prevTime - refTime);
|
||||
}
|
||||
};
|
||||
void report() {
|
||||
void report(char* msg = NULL) {
|
||||
prevTime = millis();
|
||||
if(msg) {
|
||||
sprintf(msg, "%8ldms ", prevTime - refTime);
|
||||
}
|
||||
else
|
||||
DebugPort.printf("%8ldms ", prevTime - refTime);
|
||||
};
|
||||
};
|
||||
|
|
|
@ -45,6 +45,7 @@ extern void resetFuelGauge();
|
|||
extern const char* getBlueWireStatStr();
|
||||
extern bool hasOEMcontroller();
|
||||
extern bool hasOEMLCDcontroller();
|
||||
extern bool hasHtrData();
|
||||
extern int getBlueWireStat();
|
||||
extern int getSmartError();
|
||||
extern bool isCyclicActive();
|
||||
|
@ -73,8 +74,8 @@ extern CTempSense& getTempSensor() ;
|
|||
extern void reqHeaterCalUpdate();
|
||||
|
||||
|
||||
void setSSID(const char* name);
|
||||
void setAPpassword(const char* name);
|
||||
void setName(const char* name, int type);
|
||||
void setPassword(const char* name, int type);
|
||||
|
||||
extern void ShowOTAScreen(int percent=0, eOTAmodes updateType=eOTAnormal);
|
||||
|
||||
|
|
|
@ -150,7 +150,7 @@ bool initWifi()
|
|||
}
|
||||
|
||||
// WiFi.setTxPower(WIFI_POWER_MINUS_1dBm);
|
||||
// WiFi.setTxPower(WIFI_POWER_19_5dBm);
|
||||
WiFi.setTxPower(WIFI_POWER_19_5dBm);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
@ -118,9 +118,6 @@ sBrowserUpload::fragment(HTTPUpload& upload)
|
|||
::SPIFFS.remove(SrcFile.name.c_str()); // remove the bad file from SPIFFS
|
||||
return -2;
|
||||
}
|
||||
#ifdef SSL_SERVER
|
||||
upload.totalSize += upload.currentSize;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -130,9 +127,6 @@ sBrowserUpload::fragment(HTTPUpload& upload)
|
|||
Update.printError(DebugPort);
|
||||
return -3;
|
||||
}
|
||||
#ifdef SSL_SERVER
|
||||
upload.totalSize += upload.currentSize;
|
||||
#endif
|
||||
}
|
||||
return upload.totalSize;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue