2018-10-30 08:00:19 +00:00
|
|
|
//
|
2018-10-27 09:03:10 +00:00
|
|
|
/*
|
2018-09-23 08:59:19 +00:00
|
|
|
Chinese Heater Half Duplex Serial Data Sending Tool
|
2018-09-22 23:31:47 +00:00
|
|
|
|
|
|
|
Connects to the blue wire of a Chinese heater, which is the half duplex serial link.
|
2018-10-20 07:11:23 +00:00
|
|
|
Sends and receives data from hardware serial port 1.
|
2018-09-23 08:59:19 +00:00
|
|
|
|
2018-09-22 23:31:47 +00:00
|
|
|
Terminology: Tx is to the heater unit, Rx is from the heater unit.
|
|
|
|
|
2018-09-23 08:59:19 +00:00
|
|
|
Typical data frame timing on the blue wire is:
|
|
|
|
__Tx_Rx____________________________Tx_Rx____________________________Tx_Rx___________
|
|
|
|
|
|
|
|
This software can connect to the blue wire in a normal OEM system, detecting the
|
|
|
|
OEM controller and allowing extraction of the data or injecting on/off commands.
|
2018-09-24 10:56:37 +00:00
|
|
|
|
2018-10-20 07:11:23 +00:00
|
|
|
If Pin 21 is grounded on the Due, this simple stream will be reported over Serial and
|
2018-09-24 10:56:37 +00:00
|
|
|
no control from the Arduino will be allowed.
|
2018-10-31 18:57:21 +00:00
|
|
|
This allows passive sniffing of the blue wire in a normal system.
|
2018-09-23 08:59:19 +00:00
|
|
|
|
2018-09-22 23:31:47 +00:00
|
|
|
The binary data is received from the line.
|
2018-09-23 08:59:19 +00:00
|
|
|
If it has been > 100ms since the last blue wire activity this indicates a new frame
|
|
|
|
sequence is starting from the OEM controller.
|
|
|
|
Synchronise as such then count off the next 24 bytes storing them in the Controller's
|
2018-10-20 07:11:23 +00:00
|
|
|
data array. These bytes are then reported over Serial to the PC in ASCII.
|
2018-09-22 23:31:47 +00:00
|
|
|
|
2018-09-24 10:56:37 +00:00
|
|
|
It is then expected the heater will respond with it's 24 bytes.
|
2018-09-23 08:59:19 +00:00
|
|
|
Capture those bytes and store them in the Heater1 data array.
|
2018-10-20 07:11:23 +00:00
|
|
|
Once again these bytes are then reported over Serial to the PC in ASCII.
|
2018-09-22 23:31:47 +00:00
|
|
|
|
2018-10-31 18:57:21 +00:00
|
|
|
If no activity is sensed in a second, it is assumed no OEM controller is attached and we
|
2018-09-23 08:59:19 +00:00
|
|
|
have full control over the heater.
|
|
|
|
|
|
|
|
Either way we can now inject a message onto the blue wire allowing our custom
|
|
|
|
on/off control.
|
2018-10-31 18:57:21 +00:00
|
|
|
We must remain synchronous with an OEM controller if it exists otherwise E-07
|
2018-09-23 08:59:19 +00:00
|
|
|
faults will be caused.
|
|
|
|
|
|
|
|
Typical data frame timing on the blue wire is then:
|
|
|
|
__OEMTx_HtrRx__OurTx_HtrRx____________OEMTx_HtrRx__OurTx_HtrRx____________OEMTx_HtrRx__OurTx_HtrRx_________
|
2018-09-22 23:31:47 +00:00
|
|
|
|
2018-09-23 08:59:19 +00:00
|
|
|
The second HtrRx to the next OEMTx delay is always > 100ms and is paced by the OEM controller.
|
|
|
|
The delay before seeing Heater Rx data after any Tx is usually much less than 10ms.
|
|
|
|
But this does rise if new max/min or voltage settings are sent.
|
|
|
|
**The heater only ever sends Rx data in response to a data frame from a controller**
|
2018-09-25 10:56:32 +00:00
|
|
|
|
2018-10-31 18:57:21 +00:00
|
|
|
For Bluetooth connectivity, a HC-05 Bluetooth module is attached to Serial2:
|
2018-09-25 10:56:32 +00:00
|
|
|
TXD -> Rx2 (pin 17)
|
|
|
|
RXD -> Tx2 (pin 16)
|
|
|
|
EN(key) -> pin 15
|
2018-10-31 18:57:21 +00:00
|
|
|
STATE -> pin 4
|
2018-09-22 23:31:47 +00:00
|
|
|
|
2018-09-23 08:59:19 +00:00
|
|
|
|
|
|
|
This code only works with boards that have more than one hardware serial port like Arduino
|
2018-10-31 18:57:21 +00:00
|
|
|
Mega, Due, Zero, ESP32 etc.
|
2018-09-22 23:31:47 +00:00
|
|
|
|
|
|
|
|
2018-09-23 08:59:19 +00:00
|
|
|
The circuit:
|
|
|
|
- a Tx Rx multiplexer is required to combine the Arduino's Tx1 And Rx1 pins onto the blue wire.
|
2018-10-30 11:05:29 +00:00
|
|
|
- a Tx Enable signal from pin 22 controls the multiplexer, high for Tx, low for Rx
|
2018-09-23 08:59:19 +00:00
|
|
|
- Serial logging software on Serial0 via USB link
|
2018-09-22 23:31:47 +00:00
|
|
|
|
2018-09-23 08:59:19 +00:00
|
|
|
created 23 Sep 2018 by Ray Jones
|
2018-09-22 23:31:47 +00:00
|
|
|
|
|
|
|
This example code is in the public domain.
|
|
|
|
*/
|
|
|
|
|
2018-09-23 08:59:19 +00:00
|
|
|
#include "Protocol.h"
|
2018-09-23 06:31:09 +00:00
|
|
|
#include "TxManage.h"
|
2018-10-18 09:49:14 +00:00
|
|
|
#include "pins.h"
|
2018-10-20 07:11:23 +00:00
|
|
|
#include "NVStorage.h"
|
|
|
|
#include "debugport.h"
|
2018-10-27 21:21:35 +00:00
|
|
|
#include "SmartError.h"
|
2018-10-27 23:14:34 +00:00
|
|
|
#include "BTCWifi.h"
|
2018-10-30 11:05:29 +00:00
|
|
|
#include "BTCConfig.h"
|
|
|
|
|
2018-10-28 03:03:44 +00:00
|
|
|
#define HOST_NAME "remotedebug-sample"
|
|
|
|
#define TRIGGER_PIN 0
|
2018-10-18 09:49:14 +00:00
|
|
|
|
2018-10-28 08:59:34 +00:00
|
|
|
#define FAILEDSSID "BTCESP32"
|
|
|
|
#define FAILEDPASSWORD "thereisnospoon"
|
2018-10-28 03:03:44 +00:00
|
|
|
|
|
|
|
//comment this out to remove TELNET
|
|
|
|
|
2018-10-30 07:23:22 +00:00
|
|
|
//#define TELNET
|
2018-10-28 03:03:44 +00:00
|
|
|
|
|
|
|
#ifdef TELNET
|
2018-10-30 07:23:22 +00:00
|
|
|
#define DebugPort Debug
|
2018-10-28 03:03:44 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef TELNET
|
2018-10-30 07:23:22 +00:00
|
|
|
#define DebugPort DebugPort
|
2018-10-28 03:03:44 +00:00
|
|
|
#endif
|
2018-10-27 23:14:34 +00:00
|
|
|
|
2018-10-30 11:05:29 +00:00
|
|
|
#ifdef ESP32
|
|
|
|
#include "BluetoothESP32.h"
|
|
|
|
#else
|
|
|
|
#include "BluetoothHC05.h"
|
|
|
|
#endif
|
2018-10-18 10:09:18 +00:00
|
|
|
|
2018-10-18 09:49:14 +00:00
|
|
|
#if defined(__arm__)
|
2018-10-20 07:11:23 +00:00
|
|
|
// Required for Arduino Due, UARTclass is derived from HardwareSerial
|
2018-10-27 10:30:01 +00:00
|
|
|
static UARTClass& BlueWireSerial(Serial1);
|
2018-10-18 09:49:14 +00:00
|
|
|
#else
|
|
|
|
// for ESP32, Mega
|
2018-10-20 07:11:23 +00:00
|
|
|
// HardwareSerial is it for these boards
|
2018-10-27 10:30:01 +00:00
|
|
|
static HardwareSerial& BlueWireSerial(Serial1);
|
2018-10-18 09:49:14 +00:00
|
|
|
#endif
|
2018-09-22 23:31:47 +00:00
|
|
|
|
2018-09-23 06:31:09 +00:00
|
|
|
class CommStates {
|
|
|
|
public:
|
2018-09-23 08:59:19 +00:00
|
|
|
// comms states
|
2018-09-23 06:31:09 +00:00
|
|
|
enum eCS {
|
2018-10-27 06:35:17 +00:00
|
|
|
Idle, OEMCtrlRx, OEMCtrlReport, HeaterRx1, HeaterReport1, BTC_Tx, HeaterRx2, HeaterReport2
|
2018-09-23 06:31:09 +00:00
|
|
|
};
|
|
|
|
CommStates() {
|
|
|
|
set(Idle);
|
|
|
|
}
|
|
|
|
void set(eCS eState) {
|
|
|
|
m_State = eState;
|
|
|
|
m_Count = 0;
|
|
|
|
}
|
2018-10-31 20:55:25 +00:00
|
|
|
eCS get() {
|
|
|
|
return m_State;
|
|
|
|
}
|
2018-09-23 06:31:09 +00:00
|
|
|
bool is(eCS eState) {
|
|
|
|
return m_State == eState;
|
|
|
|
}
|
2018-10-27 06:35:17 +00:00
|
|
|
bool collectData(CProtocol& Frame, unsigned char val, int limit = 24) { // returns true when buffer filled
|
|
|
|
Frame.Data[m_Count++] = val;
|
2018-09-23 06:31:09 +00:00
|
|
|
return m_Count == limit;
|
|
|
|
}
|
|
|
|
private:
|
2018-10-31 20:55:25 +00:00
|
|
|
eCS m_State;
|
2018-09-23 06:31:09 +00:00
|
|
|
int m_Count;
|
|
|
|
};
|
|
|
|
|
2018-10-27 10:30:01 +00:00
|
|
|
// a class to collect a new data byte from the blue wire
|
|
|
|
class sRxData {
|
|
|
|
bool newData;
|
|
|
|
int Value;
|
|
|
|
public:
|
|
|
|
sRxData() {
|
|
|
|
reset();
|
|
|
|
}
|
|
|
|
void reset() {
|
|
|
|
newData = false;
|
|
|
|
}
|
|
|
|
void setValue(int value) {
|
|
|
|
newData = true;
|
|
|
|
Value = value;
|
|
|
|
}
|
|
|
|
bool available() {
|
|
|
|
return newData;
|
|
|
|
}
|
|
|
|
int getValue() {
|
|
|
|
return Value;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-10-28 07:34:16 +00:00
|
|
|
class CModeratedFrame : public CProtocol {
|
|
|
|
unsigned long lastTime;
|
|
|
|
public:
|
|
|
|
CModeratedFrame() { lastTime = 0; };
|
|
|
|
void setTime() { lastTime = millis(); };
|
|
|
|
unsigned long elapsedTime() { return millis() - lastTime; };
|
|
|
|
};
|
2018-09-25 10:56:32 +00:00
|
|
|
|
2018-09-23 06:31:09 +00:00
|
|
|
CommStates CommState;
|
2018-10-27 10:30:01 +00:00
|
|
|
CTxManage TxManage(TxEnbPin, BlueWireSerial);
|
2018-10-28 07:34:16 +00:00
|
|
|
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
|
2018-10-20 07:11:23 +00:00
|
|
|
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
|
2018-10-27 21:21:35 +00:00
|
|
|
CSmartError SmartError;
|
2018-10-30 11:05:29 +00:00
|
|
|
|
2018-10-31 18:57:21 +00:00
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
2018-10-31 20:55:25 +00:00
|
|
|
// Bluetooth instantiation
|
|
|
|
//
|
2018-10-30 11:05:29 +00:00
|
|
|
#ifdef ESP32
|
2018-10-31 20:55:25 +00:00
|
|
|
|
2018-10-31 18:57:21 +00:00
|
|
|
// Bluetooth options for ESP32
|
2018-10-31 20:55:25 +00:00
|
|
|
#if USE_HC05_BLUETOOTH == 1
|
2018-10-31 18:57:21 +00:00
|
|
|
CBluetoothESP32HC05 Bluetooth(HC05_KeyPin, HC05_SensePin, Rx2Pin, Tx2Pin); // Instantiate ESP32 using a HC-05
|
2018-10-31 20:55:25 +00:00
|
|
|
#elif USE_BLE_BLUETOOTH == 1
|
2018-10-31 18:57:21 +00:00
|
|
|
CBluetoothESP32BLE Bluetooth; // Instantiate ESP32 BLE server
|
2018-10-31 20:55:25 +00:00
|
|
|
#elif USE_CLASSIC_BLUETOOTH == 1
|
2018-10-31 18:57:21 +00:00
|
|
|
CBluetoothESP32Classic Bluetooth; // Instantiate ESP32 Classic Bluetooth server
|
2018-10-31 20:55:25 +00:00
|
|
|
#else // none selected
|
2018-10-31 18:57:21 +00:00
|
|
|
CBluetoothAbstract Bluetooth; // default no bluetooth support - empty shell
|
|
|
|
#endif
|
2018-10-31 20:55:25 +00:00
|
|
|
|
2018-10-31 18:57:21 +00:00
|
|
|
#else // !ESP32
|
2018-10-31 20:55:25 +00:00
|
|
|
|
2018-10-31 18:57:21 +00:00
|
|
|
// Bluetooth for others
|
2018-10-31 20:55:25 +00:00
|
|
|
#if USE_HC05_BLUETOOTH == 1
|
2018-10-31 18:57:21 +00:00
|
|
|
CBluetoothHC05 Bluetooth(HC05_KeyPin, HC05_SensePin); // Instantiate a HC-05
|
2018-10-31 20:55:25 +00:00
|
|
|
#else // none selected
|
2018-10-31 18:57:21 +00:00
|
|
|
CBluetoothAbstract Bluetooth; // default no bluetooth support - empty shell
|
|
|
|
#endif // closing USE_HC05_BLUETOOTH
|
2018-10-31 20:55:25 +00:00
|
|
|
|
2018-10-31 18:57:21 +00:00
|
|
|
#endif // closing ESP32
|
2018-10-31 20:55:25 +00:00
|
|
|
//
|
|
|
|
// END Bluetooth instantiation
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
2018-10-30 11:05:29 +00:00
|
|
|
|
2018-09-23 08:59:19 +00:00
|
|
|
long lastRxTime; // used to observe inter character delays
|
2018-10-27 09:03:10 +00:00
|
|
|
bool hasOEMController = false;
|
2018-09-22 23:31:47 +00:00
|
|
|
|
2018-10-20 07:11:23 +00:00
|
|
|
// setup Non Volatile storage
|
|
|
|
// this is very much hardware dependent, we can use the ESP32's FLASH
|
|
|
|
#ifdef ESP32
|
|
|
|
CESP32HeaterStorage NVStorage;
|
|
|
|
#else
|
|
|
|
CHeaterStorage NVStorage; // dummy, for now
|
|
|
|
#endif
|
|
|
|
CHeaterStorage* pNVStorage = NULL;
|
|
|
|
|
2018-10-27 06:35:17 +00:00
|
|
|
|
2018-10-30 11:05:29 +00:00
|
|
|
void setup() {
|
2018-10-28 08:59:34 +00:00
|
|
|
initWifi(TRIGGER_PIN, FAILEDSSID, FAILEDPASSWORD);
|
2018-10-30 07:23:22 +00:00
|
|
|
|
2018-10-27 06:35:17 +00:00
|
|
|
pinMode(Tx2Pin, OUTPUT);
|
|
|
|
digitalWrite(Tx2Pin, HIGH);
|
|
|
|
pinMode(Rx2Pin, INPUT_PULLUP);
|
2018-09-24 10:56:37 +00:00
|
|
|
pinMode(ListenOnlyPin, INPUT_PULLUP);
|
2018-10-30 11:05:29 +00:00
|
|
|
pinMode(HC05_KeyPin, OUTPUT);
|
|
|
|
digitalWrite(HC05_KeyPin, LOW);
|
2018-09-24 10:56:37 +00:00
|
|
|
|
2018-10-27 21:21:35 +00:00
|
|
|
// 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
|
2018-10-15 07:14:12 +00:00
|
|
|
#if defined(__arm__) || defined(__AVR__)
|
2018-10-27 10:30:01 +00:00
|
|
|
BlueWireSerial.begin(25000);
|
2018-10-18 10:09:18 +00:00
|
|
|
pinMode(Rx1Pin, INPUT_PULLUP); // required for MUX to work properly
|
2018-10-20 07:11:23 +00:00
|
|
|
#elif ESP32
|
2018-10-15 07:14:12 +00:00
|
|
|
// ESP32
|
2018-10-27 10:30:01 +00:00
|
|
|
BlueWireSerial.begin(25000, SERIAL_8N1, Rx1Pin, Tx1Pin); // need to explicitly specify pins for pin multiplexer!
|
2018-10-18 10:09:18 +00:00
|
|
|
pinMode(Rx1Pin, INPUT_PULLUP); // required for MUX to work properly
|
2018-10-15 07:14:12 +00:00
|
|
|
#endif
|
2018-09-22 23:31:47 +00:00
|
|
|
|
|
|
|
// initialise serial monitor on serial port 0
|
2018-10-20 07:11:23 +00:00
|
|
|
// this is the usual USB connection to a PC
|
|
|
|
DebugPort.begin(115200);
|
2018-09-22 23:31:47 +00:00
|
|
|
|
|
|
|
// prepare for first long delay detection
|
|
|
|
lastRxTime = millis();
|
|
|
|
|
2018-10-20 07:11:23 +00:00
|
|
|
TxManage.begin(); // ensure Tx enable pin is setup
|
2018-09-23 08:59:19 +00:00
|
|
|
|
2018-10-27 21:21:35 +00:00
|
|
|
// define defaults should OEM controller be missing
|
2018-10-20 07:11:23 +00:00
|
|
|
DefaultBTCParams.setTemperature_Desired(23);
|
|
|
|
DefaultBTCParams.setTemperature_Actual(22);
|
|
|
|
DefaultBTCParams.Controller.OperatingVoltage = 120;
|
|
|
|
DefaultBTCParams.setPump_Min(16);
|
|
|
|
DefaultBTCParams.setPump_Max(55);
|
|
|
|
DefaultBTCParams.setFan_Min(1680);
|
|
|
|
DefaultBTCParams.setFan_Max(4500);
|
2018-10-18 09:49:14 +00:00
|
|
|
|
2018-10-31 18:57:21 +00:00
|
|
|
Bluetooth.init();
|
2018-10-20 07:11:23 +00:00
|
|
|
|
|
|
|
// create pointer to CHeaterStorage
|
|
|
|
// via the magic of polymorphism we can use this to access whatever
|
|
|
|
// storage is required for a specifc platform in a uniform way
|
|
|
|
pNVStorage = &NVStorage;
|
|
|
|
pNVStorage->init();
|
|
|
|
pNVStorage->load();
|
2018-09-22 23:31:47 +00:00
|
|
|
}
|
|
|
|
|
2018-10-31 20:55:25 +00:00
|
|
|
|
|
|
|
|
2018-10-20 07:11:23 +00:00
|
|
|
// main functional loop is based about a state machine approach, waiting for data
|
2018-10-31 20:55:25 +00:00
|
|
|
// to appear upon the blue wire, and marshalling into an appropriate receive buffers
|
2018-10-20 07:11:23 +00:00
|
|
|
// according to the state.
|
|
|
|
|
2018-09-22 23:31:47 +00:00
|
|
|
void loop()
|
|
|
|
{
|
|
|
|
unsigned long timenow = millis();
|
2018-10-27 23:14:34 +00:00
|
|
|
doWiFiManager();
|
2018-09-22 23:31:47 +00:00
|
|
|
|
2018-09-23 06:31:09 +00:00
|
|
|
// check for test commands received from PC Over USB
|
2018-10-28 03:03:44 +00:00
|
|
|
|
2018-10-20 07:11:23 +00:00
|
|
|
if(DebugPort.available()) {
|
|
|
|
char rxval = DebugPort.read();
|
2018-09-22 23:31:47 +00:00
|
|
|
if(rxval == '+') {
|
2018-10-20 07:11:23 +00:00
|
|
|
TxManage.queueOnRequest();
|
2018-09-22 23:31:47 +00:00
|
|
|
}
|
|
|
|
if(rxval == '-') {
|
2018-10-20 07:11:23 +00:00
|
|
|
TxManage.queueOffRequest();
|
2018-09-22 23:31:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-31 18:57:21 +00:00
|
|
|
Bluetooth.check(); // check for Bluetooth activity
|
2018-09-24 10:56:37 +00:00
|
|
|
|
2018-10-27 10:30:01 +00:00
|
|
|
// calc elapsed time since last rxd byte
|
|
|
|
// used to detect no OEM controller, or the start of an OEM frame sequence
|
2018-09-23 06:31:09 +00:00
|
|
|
unsigned long RxTimeElapsed = timenow - lastRxTime;
|
|
|
|
|
2018-10-27 10:30:01 +00:00
|
|
|
// precautionary state machine action if all 24 bytes were not received
|
|
|
|
// whilst expecting a frame from the blue wire
|
2018-09-24 10:56:37 +00:00
|
|
|
if(RxTimeElapsed > 50) {
|
2018-10-27 06:35:17 +00:00
|
|
|
if( CommState.is(CommStates::OEMCtrlRx) ||
|
2018-10-20 07:11:23 +00:00
|
|
|
CommState.is(CommStates::HeaterRx1) ||
|
2018-09-23 08:59:19 +00:00
|
|
|
CommState.is(CommStates::HeaterRx2) ) {
|
2018-09-23 06:31:09 +00:00
|
|
|
|
2018-10-28 04:05:23 +00:00
|
|
|
if(CommState.is(CommStates::OEMCtrlRx)) {
|
|
|
|
DebugPort.println("Timeout collecting OEM controller data, returning to Idle State");
|
|
|
|
}
|
|
|
|
else if(CommState.is(CommStates::HeaterRx1)) {
|
|
|
|
DebugPort.println("Timeout collecting OEM heater response data, returning to Idle State");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
DebugPort.println("Timeout collecting BTC heater response data, returning to Idle State");
|
|
|
|
}
|
|
|
|
|
2018-10-27 10:30:01 +00:00
|
|
|
CommState.set(CommStates::Idle); // revert to idle mode
|
2018-09-23 06:31:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-27 06:35:17 +00:00
|
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// 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
|
2018-10-31 20:55:25 +00:00
|
|
|
// Note that the data is read now, then held for later use in the state machine
|
2018-10-27 06:35:17 +00:00
|
|
|
//
|
2018-10-27 10:30:01 +00:00
|
|
|
sRxData BlueWireData;
|
|
|
|
|
|
|
|
if (BlueWireSerial.available()) {
|
|
|
|
// Data is avaialable, 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!
|
2018-09-22 23:31:47 +00:00
|
|
|
|
2018-10-27 10:30:01 +00:00
|
|
|
lastRxTime = timenow; // tickle last rx time, for rx data timeout purposes
|
|
|
|
BlueWireData.setValue(BlueWireSerial.read()); // read hex byte, store for later use
|
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// do our state machine to track the reception and delivery of blue wire data
|
2018-09-23 06:31:09 +00:00
|
|
|
|
2018-10-31 20:55:25 +00:00
|
|
|
switch(CommState.get()) {
|
|
|
|
|
|
|
|
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)) {
|
2018-10-31 18:57:21 +00:00
|
|
|
#ifdef REPORT_OEM_RESYNC
|
2018-10-31 20:55:25 +00:00
|
|
|
DebugPort.print("Re-sync'd with OEM Controller. ");
|
|
|
|
DebugPort.print(RxTimeElapsed);
|
|
|
|
DebugPort.println("ms Idle time.");
|
2018-10-31 18:57:21 +00:00
|
|
|
#endif
|
2018-10-31 20:55:25 +00:00
|
|
|
hasOEMController = true;
|
|
|
|
CommState.set(CommStates::OEMCtrlRx); // we must add this new byte!
|
|
|
|
//
|
|
|
|
// ** IMPORTANT - we must drop through to OEMCtrlRx *NOW* (skip break) **
|
|
|
|
//
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
break; // only break if we failed all the Idle tests
|
|
|
|
}
|
2018-09-22 23:31:47 +00:00
|
|
|
|
|
|
|
|
2018-10-31 20:55:25 +00:00
|
|
|
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;
|
|
|
|
|
2018-09-22 23:31:47 +00:00
|
|
|
|
2018-10-31 20:55:25 +00:00
|
|
|
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();
|
2018-09-22 23:31:47 +00:00
|
|
|
}
|
2018-10-31 20:55:25 +00:00
|
|
|
else {
|
|
|
|
#if REPORT_SUPPRESSED_OEM_DATA_FRAMES != 0
|
|
|
|
DebugPort.println("Suppressed delivery of OEM frame");
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
CommState.set(CommStates::HeaterRx1);
|
|
|
|
break;
|
2018-09-22 23:31:47 +00:00
|
|
|
|
2018-10-27 10:30:01 +00:00
|
|
|
|
2018-10-31 20:55:25 +00:00
|
|
|
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);
|
|
|
|
}
|
2018-10-27 10:30:01 +00:00
|
|
|
}
|
2018-10-31 20:55:25 +00:00
|
|
|
break;
|
|
|
|
|
2018-10-27 10:30:01 +00:00
|
|
|
|
2018-10-31 20:55:25 +00:00
|
|
|
case CommStates::HeaterReport1:
|
|
|
|
// received heater frame (after controller message), report
|
2018-10-27 21:21:35 +00:00
|
|
|
|
2018-10-31 20:55:25 +00:00
|
|
|
// 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
|
|
|
|
// 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
|
|
|
|
}
|
2018-09-22 23:31:47 +00:00
|
|
|
|
2018-10-31 20:55:25 +00:00
|
|
|
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;
|
2018-09-22 23:31:47 +00:00
|
|
|
|
2018-10-27 10:30:01 +00:00
|
|
|
|
2018-10-31 20:55:25 +00:00
|
|
|
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
|
2018-10-27 10:30:01 +00:00
|
|
|
}
|
2018-10-31 20:55:25 +00:00
|
|
|
break;
|
2018-10-27 10:30:01 +00:00
|
|
|
|
2018-10-27 21:21:35 +00:00
|
|
|
|
2018-10-31 20:55:25 +00:00
|
|
|
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;
|
2018-10-27 21:21:35 +00:00
|
|
|
|
2018-10-31 20:55:25 +00:00
|
|
|
|
|
|
|
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);
|
2018-10-27 06:35:17 +00:00
|
|
|
|
2018-10-27 09:03:10 +00:00
|
|
|
#ifdef SHOW_HEAP
|
2018-10-31 20:55:25 +00:00
|
|
|
Serial.print("Free heap ");
|
|
|
|
Serial.println(ESP.getFreeHeap());
|
2018-10-27 09:03:10 +00:00
|
|
|
#endif
|
2018-10-31 20:55:25 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
} // switch(CommState)
|
2018-09-22 23:31:47 +00:00
|
|
|
|
2018-09-23 06:31:09 +00:00
|
|
|
} // loop
|
2018-09-22 23:31:47 +00:00
|
|
|
|
2018-10-20 07:11:23 +00:00
|
|
|
void DebugReportFrame(const char* hdr, const CProtocol& Frame, const char* ftr)
|
2018-09-23 06:31:09 +00:00
|
|
|
{
|
2018-10-20 07:11:23 +00:00
|
|
|
DebugPort.print(hdr); // header
|
2018-09-23 06:31:09 +00:00
|
|
|
for(int i=0; i<24; i++) {
|
|
|
|
char str[16];
|
2018-10-20 11:28:32 +00:00
|
|
|
sprintf(str, " %02X", Frame.Data[i]); // build 2 dig hex values
|
2018-10-20 07:11:23 +00:00
|
|
|
DebugPort.print(str); // and print
|
|
|
|
}
|
|
|
|
DebugPort.print(ftr); // footer
|
|
|
|
}
|
|
|
|
|
2018-10-27 06:35:17 +00:00
|
|
|
|
2018-10-20 11:28:32 +00:00
|
|
|
void Command_Interpret(const char* pLine)
|
2018-10-20 07:11:23 +00:00
|
|
|
{
|
|
|
|
unsigned char cVal;
|
|
|
|
unsigned short sVal;
|
2018-10-20 11:28:32 +00:00
|
|
|
|
|
|
|
if(strlen(pLine) == 0)
|
|
|
|
return;
|
2018-10-20 07:11:23 +00:00
|
|
|
|
|
|
|
#ifdef DEBUG_BTRX
|
2018-10-20 11:28:32 +00:00
|
|
|
DebugPort.println(pLine);
|
2018-10-20 07:11:23 +00:00
|
|
|
#endif
|
|
|
|
|
2018-10-20 11:28:32 +00:00
|
|
|
if(strncmp(pLine, "[CMD]", 5) == 0) {
|
2018-10-20 07:11:23 +00:00
|
|
|
// incoming command from BT app!
|
2018-10-20 11:28:32 +00:00
|
|
|
DebugPort.write(" Command decode: ");
|
|
|
|
|
|
|
|
pLine += 5; // skip past "[CMD]" header
|
|
|
|
if(strncmp(pLine, "ON", 2) == 0) {
|
2018-10-20 07:11:23 +00:00
|
|
|
TxManage.queueOnRequest();
|
2018-10-20 11:28:32 +00:00
|
|
|
DebugPort.println("Heater ON");
|
2018-10-27 21:21:35 +00:00
|
|
|
SmartError.reset();
|
2018-10-20 07:11:23 +00:00
|
|
|
}
|
2018-10-20 11:28:32 +00:00
|
|
|
else if(strncmp(pLine, "OFF", 3) == 0) {
|
2018-10-20 07:11:23 +00:00
|
|
|
TxManage.queueOffRequest();
|
2018-10-20 11:28:32 +00:00
|
|
|
DebugPort.println("Heater OFF");
|
2018-10-27 21:21:35 +00:00
|
|
|
SmartError.inhibit();
|
2018-10-20 07:11:23 +00:00
|
|
|
}
|
2018-10-20 11:28:32 +00:00
|
|
|
else if(strncmp(pLine, "Pmin", 4) == 0) {
|
|
|
|
pLine += 4;
|
|
|
|
cVal = (unsigned char)((atof(pLine) * 10.0) + 0.5);
|
2018-10-20 07:11:23 +00:00
|
|
|
pNVStorage->setPmin(cVal);
|
2018-10-20 11:28:32 +00:00
|
|
|
DebugPort.print("Pump min = ");
|
2018-10-20 07:11:23 +00:00
|
|
|
DebugPort.println(cVal);
|
2018-10-20 11:28:32 +00:00
|
|
|
}
|
|
|
|
else if(strncmp(pLine, "Pmax", 4) == 0) {
|
|
|
|
pLine += 4;
|
|
|
|
cVal = (unsigned char)((atof(pLine) * 10.0) + 0.5);
|
2018-10-20 07:11:23 +00:00
|
|
|
pNVStorage->setPmax(cVal);
|
2018-10-20 11:28:32 +00:00
|
|
|
DebugPort.print("Pump max = ");
|
|
|
|
DebugPort.println(cVal);
|
2018-10-20 07:11:23 +00:00
|
|
|
}
|
2018-10-20 11:28:32 +00:00
|
|
|
else if(strncmp(pLine, "Fmin", 4) == 0) {
|
|
|
|
pLine += 4;
|
|
|
|
sVal = atoi(pLine);
|
2018-10-20 07:11:23 +00:00
|
|
|
pNVStorage->setFmin(sVal);
|
2018-10-20 11:28:32 +00:00
|
|
|
DebugPort.print("Fan min = ");
|
2018-10-20 07:11:23 +00:00
|
|
|
DebugPort.println(sVal);
|
2018-10-20 11:28:32 +00:00
|
|
|
}
|
|
|
|
else if(strncmp(pLine, "Fmax", 4) == 0) {
|
|
|
|
pLine += 4;
|
|
|
|
sVal = atoi(pLine);
|
2018-10-20 07:11:23 +00:00
|
|
|
pNVStorage->setFmax(sVal);
|
2018-10-20 11:28:32 +00:00
|
|
|
DebugPort.print("Fan max = ");
|
|
|
|
DebugPort.println(int(sVal));
|
2018-10-20 07:11:23 +00:00
|
|
|
}
|
2018-10-20 11:28:32 +00:00
|
|
|
else if(strncmp(pLine, "save", 4) == 0) {
|
2018-10-20 07:11:23 +00:00
|
|
|
pNVStorage->save();
|
2018-10-20 11:28:32 +00:00
|
|
|
DebugPort.println("NV save");
|
2018-10-20 07:11:23 +00:00
|
|
|
}
|
2018-10-20 11:28:32 +00:00
|
|
|
else if(strncmp(pLine, "degC", 4) == 0) {
|
|
|
|
pLine += 4;
|
|
|
|
cVal = atoi(pLine);
|
2018-10-20 07:11:23 +00:00
|
|
|
pNVStorage->setTemperature(cVal);
|
2018-10-20 11:28:32 +00:00
|
|
|
DebugPort.print("degC = ");
|
|
|
|
DebugPort.println(cVal);
|
2018-10-20 07:11:23 +00:00
|
|
|
}
|
2018-10-20 11:28:32 +00:00
|
|
|
else if(strncmp(pLine, "Mode", 4) == 0) {
|
|
|
|
pLine += 4;
|
2018-10-20 07:11:23 +00:00
|
|
|
cVal = !pNVStorage->getThermostatMode();
|
|
|
|
pNVStorage->setThermostatMode(cVal);
|
2018-10-27 06:35:17 +00:00
|
|
|
DebugPort.print("Mode now ");
|
2018-10-20 11:28:32 +00:00
|
|
|
DebugPort.println(cVal ? "Thermostat" : "Fixed Hz");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
DebugPort.print(pLine);
|
|
|
|
DebugPort.println(" ????");
|
2018-10-20 07:11:23 +00:00
|
|
|
}
|
|
|
|
|
2018-09-22 23:31:47 +00:00
|
|
|
}
|
2018-09-25 10:56:32 +00:00
|
|
|
}
|