Merge branch 'ESP32Bluetooth' into 'master'
Esp32 bluetooth See merge request mrjones.id.au/bluetoothheater!2
This commit is contained in:
commit
8fd8a314dd
BIN
AppInventor/BluetoothHeater.aia
Normal file
BIN
AppInventor/BluetoothHeater.aia
Normal file
Binary file not shown.
|
@ -3,6 +3,8 @@
|
|||
{
|
||||
"name": "Win32",
|
||||
"includePath": [
|
||||
"C:\\Program Files (x86)\\Arduino\\tools\\**",
|
||||
"C:\\Program Files (x86)\\Arduino\\hardware\\arduino\\avr\\**",
|
||||
"C:\\Users\\ray\\AppData\\Local\\Arduino15\\packages\\esp32\\tools\\**",
|
||||
"C:\\Users\\ray\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.0\\**",
|
||||
"C:\\Users\\ray\\AppData\\Local\\Arduino15\\packages\\arduino\\tools\\**",
|
||||
|
|
12
Arduino/SenderTrial2/.vscode/settings.json
vendored
12
Arduino/SenderTrial2/.vscode/settings.json
vendored
|
@ -2,5 +2,15 @@
|
|||
"sketch": "SenderTrial2.ino",
|
||||
"port": "COM4",
|
||||
"board": "arduino:sam:arduino_due_x_dbg",
|
||||
"output": "c:\\Users\\ray\\AppData\\Local\\Arduino\\"
|
||||
"output": "c:\\Users\\ray\\AppData\\Local\\Arduino\\",
|
||||
"files.associations": {
|
||||
"list": "cpp",
|
||||
"vector": "cpp",
|
||||
"xhash": "cpp",
|
||||
"xstring": "cpp",
|
||||
"xtree": "cpp",
|
||||
"algorithm": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"xutility": "cpp"
|
||||
}
|
||||
}
|
12
Arduino/SenderTrial2/Bluetooth.h
Normal file
12
Arduino/SenderTrial2/Bluetooth.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
#include <Arduino.h>
|
||||
|
||||
class CProtocol;
|
||||
|
||||
void Bluetooth_Init();
|
||||
void Bluetooth_SendFrame(const char* pHdr, const CProtocol& Frame);
|
||||
void Bluetooth_Check();
|
||||
|
||||
extern void Command_Interpret(String line); // decodes received command lines, implemented in main .ino file!
|
||||
|
||||
|
||||
|
218
Arduino/SenderTrial2/BluetoothESP32.cpp
Normal file
218
Arduino/SenderTrial2/BluetoothESP32.cpp
Normal file
|
@ -0,0 +1,218 @@
|
|||
#include "Bluetooth.h"
|
||||
#include "pins.h"
|
||||
#include "Protocol.h"
|
||||
#include "debugport.h"
|
||||
|
||||
#ifdef ESP32
|
||||
|
||||
const int LED = 2;
|
||||
|
||||
// ESP32
|
||||
|
||||
String BluetoothRxLine;
|
||||
|
||||
#ifndef ESP32_USE_BLE_RLJ
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
// CLASSIC BLUETOOTH
|
||||
// |
|
||||
// V
|
||||
|
||||
|
||||
#include "BluetoothSerial.h"
|
||||
|
||||
BluetoothSerial SerialBT;
|
||||
|
||||
void Bluetooth_Init()
|
||||
{
|
||||
pinMode(LED, OUTPUT);
|
||||
|
||||
if(!SerialBT.begin("ESPHEATER")) {
|
||||
DebugPort.println("An error occurred initialising Bluetooth");
|
||||
}
|
||||
}
|
||||
|
||||
void Bluetooth_Check()
|
||||
{
|
||||
if(SerialBT.available()) {
|
||||
char rxVal = SerialBT.read();
|
||||
if(isControl(rxVal)) { // "End of Line"
|
||||
BluetoothRxLine += '\0';
|
||||
Command_Interpret(BluetoothRxLine);
|
||||
BluetoothRxLine = "";
|
||||
}
|
||||
else {
|
||||
BluetoothRxLine += rxVal; // append new char to our Rx buffer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Bluetooth_SendFrame(const char* pHdr, const CProtocol& Frame)
|
||||
{
|
||||
if(SerialBT.hasClient()) {
|
||||
|
||||
if(Frame.verifyCRC()) {
|
||||
digitalWrite(LED, !digitalRead(LED)); // toggle LED
|
||||
SerialBT.print(pHdr);
|
||||
SerialBT.write(Frame.Data, 24);
|
||||
}
|
||||
else {
|
||||
DebugPort.print("Bluetooth data not sent, CRC error ");
|
||||
DebugPort.println(pHdr);
|
||||
}
|
||||
}
|
||||
else {
|
||||
digitalWrite(LED, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// ^
|
||||
// |
|
||||
// CLASSIC BLUETOOTH
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#else // ESP32_USE_BLE_RLJ
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
// BLE
|
||||
// |
|
||||
// V
|
||||
|
||||
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEServer.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <BLE2902.h>
|
||||
|
||||
#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" // UART service UUID
|
||||
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
|
||||
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
|
||||
|
||||
void BLE_Send(std::string Data);
|
||||
|
||||
BLEServer *pServer = NULL;
|
||||
BLECharacteristic* pTxCharacteristic = NULL;
|
||||
volatile bool deviceConnected = false;
|
||||
bool oldDeviceConnected = false;
|
||||
|
||||
class MyServerCallbacks : public BLEServerCallbacks {
|
||||
|
||||
void onConnect(BLEServer* pServer) {
|
||||
deviceConnected = true;
|
||||
}
|
||||
|
||||
void onDisconnect(BLEServer* pServer) {
|
||||
deviceConnected = false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
class MyCallbacks : public BLECharacteristicCallbacks {
|
||||
|
||||
// this callback is called when the ESP WRITE characteristic has been written to by a client
|
||||
// We need to *read* the new information!
|
||||
void onWrite(BLECharacteristic* pCharacteristic) {
|
||||
|
||||
std::string rxValue = pCharacteristic->getValue();
|
||||
|
||||
while(rxValue.length() > 0) {
|
||||
char rxVal = rxValue[0];
|
||||
if(isControl(rxVal)) { // "End of Line"
|
||||
Command_Interpret(BluetoothRxLine);
|
||||
BluetoothRxLine = "";
|
||||
}
|
||||
else {
|
||||
BluetoothRxLine += rxVal; // append new char to our Rx buffer
|
||||
}
|
||||
rxValue.erase(0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
void Bluetooth_Init()
|
||||
{
|
||||
// create the BLE device
|
||||
BLEDevice::init("DieselHeater");
|
||||
|
||||
// create the BLE server
|
||||
pServer = BLEDevice::createServer();
|
||||
pServer->setCallbacks(new MyServerCallbacks);
|
||||
|
||||
// create the BLE service
|
||||
BLEService *pService = pServer->createService(SERVICE_UUID);
|
||||
|
||||
// create a BLE characteristic
|
||||
pTxCharacteristic = pService->createCharacteristic(
|
||||
CHARACTERISTIC_UUID_TX,
|
||||
BLECharacteristic::PROPERTY_NOTIFY
|
||||
);
|
||||
pTxCharacteristic->addDescriptor(new BLE2902());
|
||||
|
||||
|
||||
BLECharacteristic* pRxCharacteristic = pService->createCharacteristic(
|
||||
CHARACTERISTIC_UUID_RX,
|
||||
BLECharacteristic::PROPERTY_WRITE
|
||||
);
|
||||
pRxCharacteristic->setCallbacks(new MyCallbacks/*()*/);
|
||||
|
||||
// start the service
|
||||
pService->start();
|
||||
// start advertising
|
||||
pServer->getAdvertising()->start();
|
||||
DebugPort.println("Awaiting a client to notify...");
|
||||
}
|
||||
|
||||
void Bluetooth_Report(const char* pHdr, const CProtocol& Frame)
|
||||
{
|
||||
if(deviceConnected) {
|
||||
if(Frame.verifyCRC()) {
|
||||
// BLE can only squirt 20 bytes per packet.
|
||||
// build the entire message then divide and conquer
|
||||
std::string txData = pHdr;
|
||||
txData.append((char*)Frame.Data, 24);
|
||||
|
||||
BLE_Send(txData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Bluetooth_Check()
|
||||
{
|
||||
// disconnecting
|
||||
if (!deviceConnected && oldDeviceConnected) {
|
||||
delay(500); // give the bluetooth stack the chance to get things ready
|
||||
pServer->startAdvertising(); // restart advertising
|
||||
DebugPort.println("start advertising");
|
||||
oldDeviceConnected = deviceConnected;
|
||||
}
|
||||
// connecting
|
||||
if (deviceConnected && !oldDeviceConnected) {
|
||||
// do stuff here on connecting
|
||||
oldDeviceConnected = deviceConnected;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// break down supplied string into 20 byte chunks (or less)
|
||||
// BLE can only handle 20 bytes per packet!
|
||||
void BLE_Send(std::string Data)
|
||||
{
|
||||
while(!Data.empty()) {
|
||||
std::string substr = Data.substr(0, 20);
|
||||
int len = substr.length();
|
||||
pTxCharacteristic->setValue((uint8_t*)Data.data(), len);
|
||||
pTxCharacteristic->notify();
|
||||
Data.erase(0, len);
|
||||
}
|
||||
}
|
||||
|
||||
// ^
|
||||
// |
|
||||
// BLE
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#endif // ESP32_USE_BLE_RLJ
|
||||
|
||||
#endif // __ESP32__
|
155
Arduino/SenderTrial2/BluetoothHC05.cpp
Normal file
155
Arduino/SenderTrial2/BluetoothHC05.cpp
Normal file
|
@ -0,0 +1,155 @@
|
|||
#include "Bluetooth.h"
|
||||
#include "pins.h"
|
||||
#include "Protocol.h"
|
||||
#include "debugport.h"
|
||||
|
||||
// Bluetooth access via HC-05 Module, using a UART
|
||||
|
||||
#ifndef ESP32
|
||||
// NOTE: ESP32 uses an entirely different mechanism, please refer to BluetoothESP32.cpp/.h
|
||||
|
||||
#ifdef __arm__
|
||||
// for Arduino Due
|
||||
static UARTClass& Bluetooth(Serial2);
|
||||
#else
|
||||
// for Mega
|
||||
static HardwareSerial& Bluetooth(Serial2); // TODO: make proper ESP32 BT client
|
||||
#endif
|
||||
|
||||
bool Bluetooth_ATCommand(const char* cmd);
|
||||
|
||||
String BluetoothRxData;
|
||||
|
||||
const int BTRates[] = {
|
||||
9600, 38400, 115200, 19200, 57600, 2400, 4800
|
||||
};
|
||||
|
||||
bool bHC05Available = false;
|
||||
|
||||
void Bluetooth_Init()
|
||||
{
|
||||
|
||||
// search for BlueTooth adapter, trying the common baud rates, then less common
|
||||
// as the device cannot be guaranteed to power up with the key pin high
|
||||
// we are at the mercy of the baud rate stored in the module.
|
||||
Bluetooth.begin(9600);
|
||||
digitalWrite(KeyPin, HIGH);
|
||||
delay(500);
|
||||
|
||||
DebugPort.println("\r\n\r\nAttempting to detect HC-05 Bluetooth module...");
|
||||
|
||||
int BTidx = 0;
|
||||
int maxTries = sizeof(BTRates)/sizeof(int);
|
||||
for(BTidx = 0; BTidx < maxTries; BTidx++) {
|
||||
DebugPort.print(" @ ");
|
||||
DebugPort.print(BTRates[BTidx]);
|
||||
DebugPort.print(" baud... ");
|
||||
Bluetooth.begin(BTRates[BTidx]); // open serial port at a certain baud rate
|
||||
Bluetooth.print("\r\n");
|
||||
Bluetooth.setTimeout(50);
|
||||
|
||||
if(Bluetooth_ATCommand("AT\r\n")) {
|
||||
DebugPort.println(" OK.");
|
||||
break;
|
||||
}
|
||||
// failed, try another baud rate
|
||||
DebugPort.println("");
|
||||
Bluetooth.flush();
|
||||
}
|
||||
|
||||
DebugPort.println("");
|
||||
if(BTidx == maxTries) {
|
||||
DebugPort.println("FAILED to detect HC-05 Bluetooth module :-(");
|
||||
}
|
||||
else {
|
||||
if(BTRates[BTidx] == 115200) {
|
||||
DebugPort.println("HC-05 found and already set to 115200 baud, skipping Init.");
|
||||
bHC05Available = true;
|
||||
}
|
||||
else {
|
||||
do {
|
||||
DebugPort.println("HC-05 found");
|
||||
|
||||
DebugPort.print(" Setting Name to \"DieselHeater\"... ");
|
||||
if(!Bluetooth_ATCommand("AT+NAME=\"DieselHeater\"\r\n")) {
|
||||
DebugPort.println("FAILED");
|
||||
break;
|
||||
}
|
||||
DebugPort.println("OK");
|
||||
|
||||
DebugPort.print(" Setting baud rate to 115200N81...");
|
||||
if(!Bluetooth_ATCommand("AT+UART=115200,1,0\r\n")) {
|
||||
DebugPort.println("FAILED");
|
||||
break;
|
||||
};
|
||||
DebugPort.println("OK");
|
||||
|
||||
Bluetooth.begin(115200);
|
||||
bHC05Available = true;
|
||||
|
||||
} while(0);
|
||||
|
||||
}
|
||||
}
|
||||
digitalWrite(KeyPin, LOW); // leave HC-05 command mode
|
||||
|
||||
delay(500);
|
||||
|
||||
if(!bHC05Available)
|
||||
Bluetooth.end(); // close serial port if no module found
|
||||
|
||||
DebugPort.println("");
|
||||
}
|
||||
|
||||
void Bluetooth_Check()
|
||||
{
|
||||
// check for data coming back over Bluetooth
|
||||
if(bHC05Available) {
|
||||
if(Bluetooth.available()) {
|
||||
char rxVal = Bluetooth.read();
|
||||
if(isControl(rxVal)) { // "End of Line"
|
||||
BluetoothRxData += '\0';
|
||||
Command_Interpret(BluetoothRxData);
|
||||
BluetoothRxData = "";
|
||||
}
|
||||
else {
|
||||
BluetoothRxData += rxVal; // append new char to our Rx buffer
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Bluetooth_SendFrame(const char* pHdr, const CProtocol& Frame)
|
||||
{
|
||||
if(bHC05Available) {
|
||||
if(Frame.verifyCRC()) {
|
||||
Bluetooth.print(pHdr);
|
||||
Bluetooth.write(Frame.Data, 24);
|
||||
}
|
||||
else {
|
||||
DebugPort.print("Bluetooth data not sent, CRC error ");
|
||||
DebugPort.println(pHdr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// local function, typically to perform Hayes commands with HC-05
|
||||
bool Bluetooth_ATCommand(const char* cmd)
|
||||
{
|
||||
if(bHC05Available) {
|
||||
Bluetooth.print(cmd);
|
||||
char RxBuffer[16];
|
||||
memset(RxBuffer, 0, 16);
|
||||
int read = Bluetooth.readBytesUntil('\n', RxBuffer, 16); // \n is not included in returned string!
|
||||
if((read == 3) && (0 == strcmp(RxBuffer, "OK\r")) ) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
118
Arduino/SenderTrial2/NVStorage.cpp
Normal file
118
Arduino/SenderTrial2/NVStorage.cpp
Normal file
|
@ -0,0 +1,118 @@
|
|||
#include "Arduino.h"
|
||||
#include "NVStorage.h"
|
||||
|
||||
|
||||
CHeaterStorage::CHeaterStorage()
|
||||
{
|
||||
calValues.Pmin = 14;
|
||||
calValues.Pmax = 40;
|
||||
calValues.Fmin = 1500;
|
||||
calValues.Fmax = 4500;
|
||||
calValues.ThermostatMode = 1;
|
||||
calValues.setTemperature = 22;
|
||||
}
|
||||
|
||||
unsigned char
|
||||
CHeaterStorage::getPmin()
|
||||
{
|
||||
return calValues.Pmin;
|
||||
}
|
||||
|
||||
unsigned char
|
||||
CHeaterStorage::getPmax()
|
||||
{
|
||||
return calValues.Pmax;
|
||||
}
|
||||
|
||||
unsigned short
|
||||
CHeaterStorage::getFmin()
|
||||
{
|
||||
return calValues.Fmin;
|
||||
}
|
||||
|
||||
unsigned short
|
||||
CHeaterStorage::getFmax()
|
||||
{
|
||||
return calValues.Fmax;
|
||||
}
|
||||
|
||||
unsigned char
|
||||
CHeaterStorage::getTemperature()
|
||||
{
|
||||
return calValues.setTemperature;
|
||||
}
|
||||
|
||||
unsigned char
|
||||
CHeaterStorage::getThermostatMode()
|
||||
{
|
||||
return calValues.ThermostatMode;
|
||||
}
|
||||
|
||||
void
|
||||
CHeaterStorage::setPmin(unsigned char val)
|
||||
{
|
||||
calValues.Pmin = val;
|
||||
}
|
||||
|
||||
void
|
||||
CHeaterStorage::setPmax(unsigned char val)
|
||||
{
|
||||
calValues.Pmax = val;
|
||||
}
|
||||
|
||||
void
|
||||
CHeaterStorage::setFmin(unsigned short val)
|
||||
{
|
||||
calValues.Fmin = val;
|
||||
}
|
||||
|
||||
void
|
||||
CHeaterStorage::setFmax(unsigned short val)
|
||||
{
|
||||
calValues.Fmax = val;
|
||||
}
|
||||
|
||||
void
|
||||
CHeaterStorage::setTemperature(unsigned char val)
|
||||
{
|
||||
calValues.setTemperature = val;
|
||||
}
|
||||
|
||||
void
|
||||
CHeaterStorage::setThermostatMode(unsigned char val)
|
||||
{
|
||||
calValues.ThermostatMode = val;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
// ESP32
|
||||
//
|
||||
#ifdef ESP32
|
||||
|
||||
CESP32HeaterStorage::CESP32HeaterStorage()
|
||||
{
|
||||
}
|
||||
|
||||
CESP32HeaterStorage::~CESP32HeaterStorage()
|
||||
{
|
||||
preferences.end();
|
||||
}
|
||||
|
||||
void
|
||||
CESP32HeaterStorage::init()
|
||||
{
|
||||
preferences.begin("dieselheater", false);
|
||||
}
|
||||
|
||||
void CESP32HeaterStorage::load()
|
||||
{
|
||||
preferences.getBytes("calValues", &calValues, sizeof(sNVStore));
|
||||
}
|
||||
|
||||
void CESP32HeaterStorage::save()
|
||||
{
|
||||
preferences.putBytes("calValues", &calValues, sizeof(sNVStore));
|
||||
}
|
||||
|
||||
#endif // ESP32
|
69
Arduino/SenderTrial2/NVStorage.h
Normal file
69
Arduino/SenderTrial2/NVStorage.h
Normal file
|
@ -0,0 +1,69 @@
|
|||
|
||||
struct sNVStore {
|
||||
unsigned char Pmin;
|
||||
unsigned char Pmax;
|
||||
unsigned short Fmin;
|
||||
unsigned short Fmax;
|
||||
unsigned char ThermostatMode;
|
||||
unsigned char setTemperature;
|
||||
};
|
||||
|
||||
class CNVStorage {
|
||||
public:
|
||||
CNVStorage() {};
|
||||
virtual ~CNVStorage() {};
|
||||
virtual void init() = 0;
|
||||
virtual void load() = 0;
|
||||
virtual void save() = 0;
|
||||
};
|
||||
|
||||
|
||||
class CHeaterStorage : public CNVStorage {
|
||||
protected:
|
||||
sNVStore calValues;
|
||||
public:
|
||||
CHeaterStorage();
|
||||
virtual ~CHeaterStorage() {};
|
||||
|
||||
// TODO: These are only here to allow building without fully
|
||||
// fleshing out NV storage for Due, Mega etc
|
||||
// these should be removed once complete (pure virtual)
|
||||
void init() {};
|
||||
void load() {};
|
||||
void save() {};
|
||||
|
||||
|
||||
unsigned char getPmin();
|
||||
unsigned char getPmax();
|
||||
unsigned short getFmin();
|
||||
unsigned short getFmax();
|
||||
unsigned char getTemperature();
|
||||
unsigned char getThermostatMode();
|
||||
|
||||
void setPmin(unsigned char val);
|
||||
void setPmax(unsigned char val);
|
||||
void setFmin(unsigned short val);
|
||||
void setFmax(unsigned short val);
|
||||
void setTemperature(unsigned char val);
|
||||
void setThermostatMode(unsigned char val);
|
||||
};
|
||||
|
||||
|
||||
#ifdef ESP32
|
||||
|
||||
#include <Preferences.h>
|
||||
|
||||
class CESP32HeaterStorage : public CHeaterStorage {
|
||||
Preferences preferences;
|
||||
public:
|
||||
CESP32HeaterStorage();
|
||||
virtual ~CESP32HeaterStorage();
|
||||
void init();
|
||||
void load();
|
||||
void save();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
extern CHeaterStorage* pNVStorage;
|
||||
|
|
@ -1,14 +1,15 @@
|
|||
#include <Arduino.h>
|
||||
#include "Protocol.h"
|
||||
|
||||
#include "debugport.h"
|
||||
|
||||
unsigned short
|
||||
CProtocol::CalcCRC(int len)
|
||||
CProtocol::CalcCRC(int len) const
|
||||
{
|
||||
// calculate a CRC-16/MODBUS checksum using the first 22 bytes of the data array
|
||||
unsigned short wCRCWord = 0xFFFF;
|
||||
|
||||
int wLength = len;
|
||||
unsigned char* pData = Data;
|
||||
const unsigned char* pData = Data;
|
||||
while (wLength--)
|
||||
{
|
||||
unsigned char nTemp = *pData++ ^ wCRCWord;
|
||||
|
@ -34,20 +35,29 @@ CProtocol::setCRC(unsigned short CRC)
|
|||
|
||||
|
||||
unsigned short
|
||||
CProtocol::getCRC()
|
||||
CProtocol::getCRC() const
|
||||
{
|
||||
unsigned short CRC;
|
||||
CRC = Data[22]; // MSB of CRC in Data[22]
|
||||
CRC <<= 8;
|
||||
CRC |= Data[23]; // LSB of CRC in Data[23]
|
||||
return CRC;
|
||||
}
|
||||
|
||||
// return true for CRC match
|
||||
bool
|
||||
CProtocol::verifyCRC()
|
||||
CProtocol::verifyCRC() const
|
||||
{
|
||||
unsigned short CRC = CalcCRC(22); // calculate CRC based on first 22 bytes
|
||||
return (getCRC() == CRC); // does it match the stored values?
|
||||
unsigned short FrameCRC = getCRC();
|
||||
bool bOK = (FrameCRC == CRC);
|
||||
if(!bOK) {
|
||||
DebugPort.print("verifyCRC FAILED: calc:");
|
||||
DebugPort.print(CRC, HEX);
|
||||
DebugPort.print(" data:");
|
||||
DebugPort.println(FrameCRC, HEX);
|
||||
}
|
||||
return bOK; // does it match the stored values?
|
||||
}
|
||||
|
||||
CProtocol&
|
||||
|
@ -57,21 +67,6 @@ CProtocol::operator=(const CProtocol& rhs)
|
|||
return *this;
|
||||
}
|
||||
|
||||
int
|
||||
CProtocol::getCommand()
|
||||
{
|
||||
return Controller.Command;
|
||||
}
|
||||
|
||||
void
|
||||
CProtocol::setCommand(int cmd)
|
||||
{
|
||||
Controller.Command = cmd;
|
||||
}
|
||||
|
||||
/*unsigned char getCommand();
|
||||
void setCommand(int mode);*/
|
||||
|
||||
|
||||
void
|
||||
CProtocol::setFan_Min(short Speed)
|
||||
|
|
|
@ -98,16 +98,24 @@ public:
|
|||
public:
|
||||
CProtocol() { Init(0); };
|
||||
CProtocol(int TxMode) { Init(TxMode); };
|
||||
|
||||
void Init(int Txmode);
|
||||
// CRC handlers
|
||||
unsigned short CalcCRC(int len); // calculate and set the CRC upon len bytes
|
||||
void setCRC(); // calculate and set the CRC in the buffer
|
||||
unsigned short CalcCRC(int len) const; // calculate the CRC upon len bytes
|
||||
void setCRC(); // calculate and set the CRC in the buffer
|
||||
void setCRC(unsigned short CRC); // set the CRC in the buffer
|
||||
unsigned short getCRC(); // extract CRC value from buffer
|
||||
bool verifyCRC(); // return true for CRC match
|
||||
// command
|
||||
int getCommand();
|
||||
void setCommand(int mode);
|
||||
unsigned short getCRC() const; // extract CRC value from buffer
|
||||
bool verifyCRC() 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
|
||||
// command helpers
|
||||
void resetCommand() { setRawCommand(0x00); };
|
||||
void onCommand() { setRawCommand(0xA0); };
|
||||
void offCommand() { setRawCommand(0x05); };
|
||||
// raw command
|
||||
int getRawCommand() { return Controller.Command; };
|
||||
void setRawCommand(int mode) { Controller.Command = mode; };
|
||||
// Run state
|
||||
unsigned char getRunState() { return Heater.RunState; };
|
||||
void setRunState(unsigned char state) { Heater.RunState = state; };
|
||||
|
@ -144,10 +152,13 @@ public:
|
|||
unsigned char getTemperature_Desired() { return Controller.DesiredTemperature; };
|
||||
unsigned char getTemperature_Min() { return Controller.MinTemperature; };
|
||||
unsigned char getTemperature_Max() { return Controller.MaxTemperature; };
|
||||
void setThermostatMode(unsigned on) { Controller.OperatingMode = on ? 0x32 : 0xCD; };
|
||||
// glow plug
|
||||
short getGlowPlug_Current(); // glow plug current
|
||||
short getGlowPlug_Voltage(); // glow plug voltage
|
||||
void setGlowPlug_Current(short ampsx100); // glow plug current
|
||||
void setGlowPlug_Voltage(short voltsx10); // glow plug voltage
|
||||
// heat exchanger
|
||||
short getTemperature_HeatExchg(); // temperature of heat exchanger
|
||||
void setTemperature_HeatExchg(short degC); // temperature of heat exchanger
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
Chinese Heater Half Duplex Serial Data Sending Tool
|
||||
|
||||
Connects to the blue wire of a Chinese heater, which is the half duplex serial link.
|
||||
Sends and receives data from serial port 1.
|
||||
Sends and receives data from hardware serial port 1.
|
||||
|
||||
Terminology: Tx is to the heater unit, Rx is from the heater unit.
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
|||
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.
|
||||
|
||||
If Pin 21 is grounded on the Due, this simple stream will be reported over USB and
|
||||
If Pin 21 is grounded on the Due, this simple stream will be reported over Serial and
|
||||
no control from the Arduino will be allowed.
|
||||
This allows sniffing of the blue wire in a normal system.
|
||||
|
||||
|
@ -20,11 +20,11 @@
|
|||
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
|
||||
data array. These bytes are then reported over USB to the PC in ASCII.
|
||||
data array. These bytes are then reported over Serial to the PC in ASCII.
|
||||
|
||||
It is then expected the heater will respond with it's 24 bytes.
|
||||
Capture those bytes and store them in the Heater1 data array.
|
||||
Once again these bytes are then reported over USB to the PC in ASCII.
|
||||
Once again these bytes are then reported over Serial to the PC in ASCII.
|
||||
|
||||
If no activity is sensed in a second, it is assumed no controller is attached and we
|
||||
have full control over the heater.
|
||||
|
@ -64,18 +64,33 @@
|
|||
|
||||
#include "Protocol.h"
|
||||
#include "TxManage.h"
|
||||
#include "pins.h"
|
||||
#include "NVStorage.h"
|
||||
#include "debugport.h"
|
||||
|
||||
void SerialReport(const char* hdr, const unsigned char* pData, const char* ftr);
|
||||
void BluetoothDetect();
|
||||
bool BlueToothCommand(const char* cmd);
|
||||
void BlueToothReport(const char* pHdr, const unsigned char Data[24]);
|
||||
void BluetoothInterpret();
|
||||
#define BLUETOOTH
|
||||
#define DEBUG_BTRX
|
||||
|
||||
#ifdef BLUETOOTH
|
||||
#include "Bluetooth.h"
|
||||
#endif
|
||||
|
||||
#if defined(__arm__)
|
||||
// Required for Arduino Due, UARTclass is derived from HardwareSerial
|
||||
static UARTClass& BlueWire(Serial1);
|
||||
#else
|
||||
// for ESP32, Mega
|
||||
// HardwareSerial is it for these boards
|
||||
static HardwareSerial& BlueWire(Serial1);
|
||||
#endif
|
||||
|
||||
void DebugReportFrame(const char* hdr, const CProtocol& Frame, const char* ftr);
|
||||
|
||||
class CommStates {
|
||||
public:
|
||||
// comms states
|
||||
enum eCS {
|
||||
Idle, ControllerRx, ControllerReport, HeaterRx1, HeaterReport1, SelfTx, HeaterRx2, HeaterReport2
|
||||
Idle, ControllerRx, ControllerReport, HeaterRx1, HeaterReport1, BTC_Tx, HeaterRx2, HeaterReport2
|
||||
};
|
||||
CommStates() {
|
||||
set(Idle);
|
||||
|
@ -96,123 +111,100 @@ private:
|
|||
int m_Count;
|
||||
};
|
||||
|
||||
#if defined(__arm__)
|
||||
// for Arduino Due
|
||||
UARTClass& USB(Serial);
|
||||
UARTClass& BlueWire(Serial1);
|
||||
UARTClass& BlueTooth(Serial2);
|
||||
#else
|
||||
// for ESP32, Mega
|
||||
HardwareSerial& USB(Serial);
|
||||
HardwareSerial& BlueWire(Serial1);
|
||||
#if defined(__ESP32__)
|
||||
// ESP32
|
||||
HardwareSerial& BlueTooth(Serial2); // TODO: make proper ESP32 BT client
|
||||
#else
|
||||
// Mega
|
||||
HardwareSerial& BlueTooth(Serial2);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__ESP32__)
|
||||
const int TxEnbPin = 22;
|
||||
#else
|
||||
const int TxEnbPin = 20;
|
||||
#endif
|
||||
const int ListenOnlyPin = 21;
|
||||
const int KeyPin = 15;
|
||||
const int Tx1Pin = 18;
|
||||
const int Rx1Pin = 19;
|
||||
const int Tx2Pin = 16;
|
||||
const int Rx2Pin = 17;
|
||||
|
||||
const int BTRates[] = {
|
||||
9600, 38400, 115200, 19200, 57600, 2400, 4800
|
||||
};
|
||||
|
||||
CommStates CommState;
|
||||
CTxManage TxManage(TxEnbPin, Serial1);
|
||||
CProtocol Controller; // most recent data packet received from OEM controller found on blue wire
|
||||
CProtocol Heater1; // data packet received from heater in response to OEM controller packet
|
||||
CProtocol Heater2; // data packet received from heater in response to our packet
|
||||
CProtocol SelfParams(CProtocol::CtrlMode); // holds our local parameters, used in case of no OEM controller
|
||||
CTxManage TxManage(TxEnbPin, BlueWire);
|
||||
CProtocol OEMControllerFrame; // data packet received from heater in response to OEM controller packet
|
||||
CProtocol 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
|
||||
long lastRxTime; // used to observe inter character delays
|
||||
bool bBlueToothAvailable = false;
|
||||
String BluetoothRxData;
|
||||
|
||||
// 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;
|
||||
|
||||
|
||||
void setup()
|
||||
{
|
||||
// initialize listening serial port
|
||||
// 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, special baud rate for Chinese heater controllers
|
||||
// Tx/Rx data to/from heater,
|
||||
// Note special baud rate for Chinese heater controllers
|
||||
pinMode(ListenOnlyPin, INPUT_PULLUP);
|
||||
pinMode(KeyPin, OUTPUT);
|
||||
// pinMode(Tx2Pin, OUTPUT);
|
||||
digitalWrite(KeyPin, LOW);
|
||||
// digitalWrite(Tx2Pin, HIGH);
|
||||
|
||||
#if defined(__arm__) || defined(__AVR__)
|
||||
BlueWire.begin(25000);
|
||||
pinMode(19, INPUT_PULLUP); // required for MUX to work properly
|
||||
#else if defined(__ESP32__)
|
||||
pinMode(Rx1Pin, INPUT_PULLUP); // required for MUX to work properly
|
||||
#elif ESP32
|
||||
// ESP32
|
||||
#define RXD2 16
|
||||
#define TXD2 17
|
||||
BlueWire.begin(25000, SERIAL_8N1, Rx1Pin, Tx1Pin);
|
||||
BlueWire.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
|
||||
|
||||
// initialise serial monitor on serial port 0
|
||||
USB.begin(115200);
|
||||
// this is the usual USB connection to a PC
|
||||
DebugPort.begin(115200);
|
||||
|
||||
// prepare for first long delay detection
|
||||
lastRxTime = millis();
|
||||
|
||||
TxManage.begin(); // ensure Tx enable pin setup
|
||||
TxManage.begin(); // ensure Tx enable pin is setup
|
||||
|
||||
// define defaults should heater controller be missing
|
||||
SelfParams.setTemperature_Desired(23);
|
||||
SelfParams.setTemperature_Actual(22);
|
||||
SelfParams.Controller.OperatingVoltage = 120;
|
||||
SelfParams.setPump_Min(16);
|
||||
SelfParams.setPump_Max(55);
|
||||
SelfParams.setFan_Min(1680);
|
||||
SelfParams.setFan_Max(4500);
|
||||
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);
|
||||
|
||||
BluetoothDetect();
|
||||
#ifdef BLUETOOTH
|
||||
Bluetooth_Init();
|
||||
#endif
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
// main functional loop is based about a state machine approach, waiting for data
|
||||
// to appear upon the blue wire, and marshalling into an appropriate receive buffer
|
||||
// according to the state.
|
||||
|
||||
|
||||
void loop()
|
||||
{
|
||||
unsigned long timenow = millis();
|
||||
|
||||
// check for test commands received from PC Over USB
|
||||
if(USB.available()) {
|
||||
char rxval = USB.read();
|
||||
if(DebugPort.available()) {
|
||||
char rxval = DebugPort.read();
|
||||
if(rxval == '+') {
|
||||
TxManage.RequestOn();
|
||||
TxManage.queueOnRequest();
|
||||
}
|
||||
if(rxval == '-') {
|
||||
TxManage.RequestOff();
|
||||
}
|
||||
}
|
||||
|
||||
// check for data coming back over Bluetooth
|
||||
if(BlueTooth.available()) {
|
||||
char rxVal = BlueTooth.read();
|
||||
if(isControl(rxVal)) { // "End of Line"
|
||||
BluetoothRxData += '\0';
|
||||
BluetoothInterpret();
|
||||
}
|
||||
else {
|
||||
BluetoothRxData += rxVal; // append new char to our Rx buffer
|
||||
TxManage.queueOffRequest();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef BLUETOOTH
|
||||
Bluetooth_Check(); // check for Bluetooth activity
|
||||
#endif
|
||||
|
||||
// Handle time interval where we send data to the blue wire
|
||||
if(CommState.is(CommStates::SelfTx)) {
|
||||
if(CommState.is(CommStates::BTC_Tx)) {
|
||||
lastRxTime = timenow; // we are pumping onto blue wire, track this activity!
|
||||
if(TxManage.CheckTx(timenow) ) { // monitor our data delivery
|
||||
CommState.set(CommStates::HeaterRx2); // then await heater repsonse
|
||||
|
@ -226,25 +218,28 @@ void loop()
|
|||
// check for no rx traffic => no OEM controller
|
||||
if(CommState.is(CommStates::Idle) && (RxTimeElapsed >= 970)) {
|
||||
// have not seen any receive data for a second.
|
||||
// OEM controller probably not connected.
|
||||
// Skip to SelfTx, sending our own settings.
|
||||
CommState.set(CommStates::SelfTx);
|
||||
bool bOurParams = true;
|
||||
TxManage.Start(SelfParams, timenow, bOurParams);
|
||||
BlueToothReport("[BTC]", SelfParams.Data); // BTC => Bluetooth Controller :-)
|
||||
// OEM controller is probably not connected.
|
||||
// Skip state machine immediately to BTC_Tx, sending our own settings.
|
||||
CommState.set(CommStates::BTC_Tx);
|
||||
bool isBTCmaster = true;
|
||||
TxManage.PrepareFrame(DefaultBTCParams, isBTCmaster); // use our parameters, and mix in NV storage values
|
||||
TxManage.Start(timenow);
|
||||
#ifdef BLUETOOTH
|
||||
Bluetooth_SendFrame("[BTC]", TxManage.getFrame()); // BTC => Bluetooth Controller :-)
|
||||
#endif
|
||||
}
|
||||
|
||||
// precautionary action if all 24 bytes were not received whilst expecting them
|
||||
if(RxTimeElapsed > 50) {
|
||||
if( CommState.is(CommStates::ControllerRx) ||
|
||||
CommState.is(CommStates::HeaterRx1) ||
|
||||
CommState.is(CommStates::HeaterRx1) ||
|
||||
CommState.is(CommStates::HeaterRx2) ) {
|
||||
|
||||
CommState.set(CommStates::Idle);
|
||||
}
|
||||
}
|
||||
|
||||
// read from port 1, the "blue wire" (to/from heater), store according to CommState
|
||||
// read data from Serial port 1, the "blue wire" (to/from heater), store according to CommState
|
||||
if (BlueWire.available()) {
|
||||
|
||||
lastRxTime = timenow;
|
||||
|
@ -257,19 +252,19 @@ void loop()
|
|||
int inByte = BlueWire.read(); // read hex byte
|
||||
|
||||
if( CommState.is(CommStates::ControllerRx) ) {
|
||||
if(CommState.saveData(Controller.Data, inByte) ) {
|
||||
if(CommState.saveData(OEMControllerFrame.Data, inByte) ) {
|
||||
CommState.set(CommStates::ControllerReport);
|
||||
}
|
||||
}
|
||||
|
||||
else if( CommState.is(CommStates::HeaterRx1) ) {
|
||||
if( CommState.saveData(Heater1.Data, inByte) ) {
|
||||
if( CommState.saveData(HeaterFrame1.Data, inByte) ) {
|
||||
CommState.set(CommStates::HeaterReport1);
|
||||
}
|
||||
}
|
||||
|
||||
else if( CommState.is(CommStates::HeaterRx2) ) {
|
||||
if( CommState.saveData(Heater2.Data, inByte) ) {
|
||||
if( CommState.saveData(HeaterFrame2.Data, inByte) ) {
|
||||
CommState.set(CommStates::HeaterReport2);
|
||||
}
|
||||
}
|
||||
|
@ -279,174 +274,127 @@ void loop()
|
|||
|
||||
if( CommState.is(CommStates::ControllerReport) ) {
|
||||
// filled controller frame, report
|
||||
BlueToothReport("[OEM]", Controller.Data);
|
||||
SerialReport("Ctrl ", Controller.Data, " ");
|
||||
#ifdef BLUETOOTH
|
||||
// echo received OEM controller frame over Bluetooth, using [OEM] header
|
||||
Bluetooth_SendFrame("[OEM]", OEMControllerFrame);
|
||||
#endif
|
||||
DebugReportFrame("OEM ", OEMControllerFrame, " ");
|
||||
CommState.set(CommStates::HeaterRx1);
|
||||
}
|
||||
|
||||
else if(CommState.is(CommStates::HeaterReport1) ) {
|
||||
// received heater frame (after controller message), report
|
||||
SerialReport("Htr1 ", Heater1.Data, "\r\n");
|
||||
BlueToothReport("[HTR]", Heater1.Data);
|
||||
DebugReportFrame("Htr1 ", HeaterFrame1, "\r\n");
|
||||
#ifdef BLUETOOTH
|
||||
// echo heater reponse data to Bluetooth client
|
||||
Bluetooth_SendFrame("[HTR]", HeaterFrame1);
|
||||
#endif
|
||||
|
||||
if(digitalRead(ListenOnlyPin)) {
|
||||
bool bOurParams = false;
|
||||
TxManage.Start(Controller, timenow, bOurParams);
|
||||
CommState.set(CommStates::SelfTx);
|
||||
bool isBTCmaster = false;
|
||||
TxManage.PrepareFrame(OEMControllerFrame, isBTCmaster); // parrot OEM parameters, but block NV modes
|
||||
TxManage.Start(timenow);
|
||||
CommState.set(CommStates::BTC_Tx);
|
||||
}
|
||||
else {
|
||||
CommState.set(CommStates::Idle); // "Listen Only" input held low, don't send out Tx
|
||||
CommState.set(CommStates::Idle); // "Listen Only" input is held low, don't send out Tx
|
||||
}
|
||||
}
|
||||
|
||||
else if( CommState.is(CommStates::HeaterReport2) ) {
|
||||
// received heater frame (after our control message), report
|
||||
SerialReport("Htr2 ", Heater2.Data, "\r\n");
|
||||
DebugReportFrame("Htr2 ", HeaterFrame2, "\r\n");
|
||||
// if(!digitalRead(ListenOnlyPin)) {
|
||||
BlueToothReport("[HTR]", Heater2.Data); // pin not grounded, suppress duplicate to BT
|
||||
#ifdef BLUETOOTH
|
||||
Bluetooth_SendFrame("[HTR]", HeaterFrame2); // pin not grounded, suppress duplicate to BT
|
||||
#endif
|
||||
// }
|
||||
CommState.set(CommStates::Idle);
|
||||
}
|
||||
|
||||
} // loop
|
||||
|
||||
void SerialReport(const char* hdr, const unsigned char* pData, const char* ftr)
|
||||
void DebugReportFrame(const char* hdr, const CProtocol& Frame, const char* ftr)
|
||||
{
|
||||
USB.print(hdr); // header
|
||||
DebugPort.print(hdr); // header
|
||||
for(int i=0; i<24; i++) {
|
||||
char str[16];
|
||||
sprintf(str, "%02X ", pData[i]); // build 2 dig hex values
|
||||
USB.print(str); // and print
|
||||
sprintf(str, "%02X ", Frame.Data[i]); // build 2 dig hex values
|
||||
DebugPort.print(str); // and print
|
||||
}
|
||||
USB.print(ftr); // footer
|
||||
DebugPort.print(ftr); // footer
|
||||
}
|
||||
|
||||
void BluetoothDetect()
|
||||
void Command_Interpret(String line)
|
||||
{
|
||||
#if defined(__ESP32__)
|
||||
#else
|
||||
// search for BlueTooth adapter, trying the common baud rates, then less common
|
||||
// as the device cannot be guaranteed to power up with the key pin high
|
||||
// we are at the mercy of the baud rate stored in the module.
|
||||
BlueTooth.begin(9600);
|
||||
digitalWrite(KeyPin, HIGH);
|
||||
delay(500);
|
||||
unsigned char cVal;
|
||||
unsigned short sVal;
|
||||
|
||||
#ifdef DEBUG_BTRX
|
||||
DebugPort.println(line);
|
||||
DebugPort.println();
|
||||
#endif
|
||||
|
||||
USB.println("\r\n\r\nAttempting to detect HC-05 Bluetooth module...");
|
||||
|
||||
int BTidx = 0;
|
||||
int maxTries = sizeof(BTRates)/sizeof(int);
|
||||
for(BTidx = 0; BTidx < maxTries; BTidx++) {
|
||||
USB.print(" @ ");
|
||||
USB.print(BTRates[BTidx]);
|
||||
USB.print(" baud... ");
|
||||
BlueTooth.begin(BTRates[BTidx]); // open serial port at a certain baud rate
|
||||
BlueTooth.print("\r\n");
|
||||
BlueTooth.setTimeout(50);
|
||||
|
||||
if(BlueToothCommand("AT\r\n")) {
|
||||
USB.println(" OK.");
|
||||
break;
|
||||
}
|
||||
// failed, try another baud rate
|
||||
USB.println("");
|
||||
BlueTooth.flush();
|
||||
}
|
||||
|
||||
USB.println("");
|
||||
if(BTidx == maxTries) {
|
||||
USB.println("FAILED to detect HC-05 Bluetooth module :-(");
|
||||
}
|
||||
else {
|
||||
if(BTRates[BTidx] == 115200) {
|
||||
USB.println("HC-05 found and already set to 115200 baud, skipping Init.");
|
||||
bBlueToothAvailable = true;
|
||||
}
|
||||
else {
|
||||
do {
|
||||
USB.println("HC-05 found");
|
||||
|
||||
USB.print(" Setting Name to \"DieselHeater\"... ");
|
||||
if(!BlueToothCommand("AT+NAME=\"DieselHeater\"\r\n")) {
|
||||
USB.println("FAILED");
|
||||
break;
|
||||
}
|
||||
USB.println("OK");
|
||||
|
||||
USB.print(" Setting baud rate to 115200N81...");
|
||||
if(!BlueToothCommand("AT+UART=115200,1,0\r\n")) {
|
||||
USB.println("FAILED");
|
||||
break;
|
||||
};
|
||||
USB.println("OK");
|
||||
|
||||
BlueTooth.begin(115200);
|
||||
bBlueToothAvailable = true;
|
||||
|
||||
} while(0);
|
||||
|
||||
}
|
||||
}
|
||||
digitalWrite(KeyPin, LOW); // leave HC-05 command mode
|
||||
|
||||
delay(500);
|
||||
|
||||
if(!bBlueToothAvailable)
|
||||
BlueTooth.end(); // close serial port if no module found
|
||||
|
||||
USB.println("");
|
||||
#endif
|
||||
}
|
||||
|
||||
bool BlueToothCommand(const char* cmd)
|
||||
{
|
||||
if(bBlueToothAvailable) {
|
||||
BlueTooth.print(cmd);
|
||||
char RxBuffer[16];
|
||||
memset(RxBuffer, 0, 16);
|
||||
int read = BlueTooth.readBytesUntil('\n', RxBuffer, 16); // \n is not included in returned string!
|
||||
if((read == 3) && (0 == strcmp(RxBuffer, "OK\r")) ) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void BlueToothReport(const char* pHdr, const unsigned char Data[24])
|
||||
{
|
||||
if(bBlueToothAvailable) {
|
||||
BlueTooth.print(pHdr);
|
||||
BlueTooth.write(Data, 24);
|
||||
}
|
||||
}
|
||||
|
||||
void BluetoothInterpret()
|
||||
{
|
||||
if(BluetoothRxData.startsWith("[CMD]") ) {
|
||||
USB.write("BT command Rx'd: ");
|
||||
if(line.startsWith("[CMD]") ) {
|
||||
DebugPort.write("BT command Rx'd: ");
|
||||
// incoming command from BT app!
|
||||
BluetoothRxData.remove(0, 5); // strip away "[CMD]" header
|
||||
if(BluetoothRxData.startsWith("ON")) {
|
||||
USB.write("ON\n");
|
||||
TxManage.RequestOn();
|
||||
line.remove(0, 5); // strip away "[CMD]" header
|
||||
if(line.startsWith("ON") ) {
|
||||
DebugPort.write("ON\n");
|
||||
TxManage.queueOnRequest();
|
||||
}
|
||||
else if(BluetoothRxData.startsWith("OFF")) {
|
||||
USB.write("OFF\n");
|
||||
TxManage.RequestOff();
|
||||
else if(line.startsWith("OFF")) {
|
||||
DebugPort.write("OFF\n");
|
||||
TxManage.queueOffRequest();
|
||||
}
|
||||
else if(BluetoothRxData.startsWith("Pmin")) {
|
||||
USB.write("Pmin\n");
|
||||
else if(line.startsWith("Pmin")) {
|
||||
line.remove(0, 4);
|
||||
DebugPort.write("Pmin=");
|
||||
cVal = (line.toFloat() * 10) + 0.5;
|
||||
DebugPort.println(cVal);
|
||||
pNVStorage->setPmin(cVal);
|
||||
}
|
||||
else if(BluetoothRxData.startsWith("Pmax")) {
|
||||
USB.write("Pmax\n");
|
||||
else if(line.startsWith("Pmax")) {
|
||||
line.remove(0, 4);
|
||||
DebugPort.write("Pmax=");
|
||||
cVal = (line.toFloat() * 10) + 0.5;
|
||||
DebugPort.println(cVal);
|
||||
pNVStorage->setPmax(cVal);
|
||||
}
|
||||
else if(BluetoothRxData.startsWith("Fmin")) {
|
||||
USB.write("Fmin\n");
|
||||
else if(line.startsWith("Fmin")) {
|
||||
line.remove(0, 4);
|
||||
DebugPort.print("Fmin=");
|
||||
sVal = line.toInt();
|
||||
DebugPort.println(sVal);
|
||||
pNVStorage->setFmin(sVal);
|
||||
}
|
||||
else if(BluetoothRxData.startsWith("Fmax")) {
|
||||
USB.write("Fmax\n");
|
||||
else if(line.startsWith("Fmax")) {
|
||||
line.remove(0, 4);
|
||||
DebugPort.print("Fmax=");
|
||||
sVal = line.toInt();
|
||||
DebugPort.println(sVal);
|
||||
pNVStorage->setFmax(sVal);
|
||||
}
|
||||
else if(line.startsWith("save")) {
|
||||
line.remove(0, 4);
|
||||
DebugPort.write("save\n");
|
||||
pNVStorage->save();
|
||||
}
|
||||
else if(line.startsWith("degC")) {
|
||||
line.remove(0, 4);
|
||||
DebugPort.write("degC=");
|
||||
cVal = line.toInt();
|
||||
DebugPort.println(cVal);
|
||||
pNVStorage->setTemperature(cVal);
|
||||
}
|
||||
else if(line.startsWith("Mode")) {
|
||||
line.remove(0, 4);
|
||||
DebugPort.write("Mode=");
|
||||
cVal = !pNVStorage->getThermostatMode();
|
||||
pNVStorage->setThermostatMode(cVal);
|
||||
DebugPort.println(cVal);
|
||||
}
|
||||
|
||||
}
|
||||
BluetoothRxData = ""; //flush string, ready for new data
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,52 +1,90 @@
|
|||
#include <Arduino.h>
|
||||
#include "TxManage.h"
|
||||
#include "NVStorage.h"
|
||||
|
||||
extern void SerialReport(const char* hdr, const unsigned char* pData, const char* ftr);
|
||||
extern void DebugReportFrame(const char* hdr, const CProtocol&, const char* ftr);
|
||||
|
||||
CTxManage::CTxManage(int TxEnbPin, HardwareSerial& serial) :
|
||||
m_Serial(serial),
|
||||
m_Frame(CProtocol::CtrlMode)
|
||||
CTxManage::CTxManage(int TxGatePin, HardwareSerial& serial) :
|
||||
m_BlueWireSerial(serial),
|
||||
m_TxFrame(CProtocol::CtrlMode)
|
||||
{
|
||||
m_bOnReq = false;
|
||||
m_bOffReq = false;
|
||||
m_bTxPending = false;
|
||||
m_nStartTime = 0;
|
||||
m_nTxEnbPin = TxEnbPin;
|
||||
m_nTxGatePin = TxGatePin;
|
||||
}
|
||||
|
||||
void CTxManage::begin()
|
||||
{
|
||||
pinMode(m_nTxEnbPin, OUTPUT);
|
||||
digitalWrite(m_nTxEnbPin, LOW);
|
||||
pinMode(m_nTxGatePin, OUTPUT);
|
||||
digitalWrite(m_nTxGatePin, LOW);
|
||||
}
|
||||
|
||||
void
|
||||
CTxManage::RequestOn()
|
||||
CTxManage::queueOnRequest()
|
||||
{
|
||||
m_bOnReq = true;
|
||||
}
|
||||
|
||||
void
|
||||
CTxManage::RequestOff()
|
||||
CTxManage::queueOffRequest()
|
||||
{
|
||||
m_bOffReq = true;
|
||||
}
|
||||
|
||||
void
|
||||
CTxManage::Start(const CProtocol& ref, unsigned long timenow, bool self)
|
||||
void
|
||||
CTxManage::PrepareFrame(const CProtocol& basisFrame, bool isBTCmaster)
|
||||
{
|
||||
m_Frame = ref;
|
||||
// copy supplied frame, typically this will be the values an OEM controller delivered
|
||||
// which means we parrot that data by default.
|
||||
// When parroting, we must especially avoid ping ponging "set temperature"!
|
||||
// Otherwise we are supplied with the default params for standalone mode, which we
|
||||
// then instil the NV parameters
|
||||
m_TxFrame = basisFrame;
|
||||
|
||||
// ALWAYS install on/off commands if required
|
||||
m_TxFrame.resetCommand(); // no command upon blue wire initially, unless a request is pending
|
||||
if(m_bOnReq) {
|
||||
m_bOnReq = false;
|
||||
m_TxFrame.onCommand();
|
||||
}
|
||||
if(m_bOffReq) {
|
||||
m_bOffReq = false;
|
||||
m_TxFrame.offCommand();
|
||||
}
|
||||
|
||||
// 0x78 prevents the controller showing bum information when we parrot the OEM controller
|
||||
// heater is happy either way, the OEM controller has set the max/min stuff already
|
||||
m_Frame.Data[0] = self ? 0x76 : 0x78;
|
||||
if(isBTCmaster) {
|
||||
m_TxFrame.setActiveMode(); // this allows heater to svae the tuning params to EEPROM
|
||||
m_TxFrame.setFan_Min(pNVStorage->getFmin());
|
||||
m_TxFrame.setFan_Max(pNVStorage->getFmax());
|
||||
m_TxFrame.setPump_Min(pNVStorage->getPmin());
|
||||
m_TxFrame.setPump_Max(pNVStorage->getPmax());
|
||||
m_TxFrame.setThermostatMode(pNVStorage->getThermostatMode());
|
||||
m_TxFrame.setTemperature_Desired(pNVStorage->getTemperature());
|
||||
}
|
||||
else {
|
||||
m_TxFrame.setPassiveMode(); // this prevents the tuning parameters being saved by heater
|
||||
}
|
||||
|
||||
if(timenow == 0)
|
||||
// ensure CRC valid
|
||||
m_TxFrame.setCRC();
|
||||
}
|
||||
|
||||
void
|
||||
CTxManage::Start(unsigned long timenow)
|
||||
{
|
||||
if(timenow == 0) // avoid a black hole if millis() wraps
|
||||
timenow++;
|
||||
|
||||
m_nStartTime = timenow;
|
||||
m_bTxPending = true;
|
||||
}
|
||||
|
||||
// generate a Tx Gate, then send the TxFrame to the Blue wire
|
||||
// Note the serial data is ISR driven, we need to hold off
|
||||
// for a while to let teh buffewred dat clear before closing the Tx Gate.
|
||||
bool
|
||||
CTxManage::CheckTx(unsigned long timenow)
|
||||
{
|
||||
|
@ -56,51 +94,20 @@ CTxManage::CheckTx(unsigned long timenow)
|
|||
|
||||
if(diff > m_nStartDelay) {
|
||||
// begin front porch of Tx gating pulse
|
||||
digitalWrite(m_nTxEnbPin, HIGH);
|
||||
digitalWrite(m_nTxGatePin, HIGH);
|
||||
}
|
||||
if(m_bTxPending && (diff > (m_nStartDelay + m_nFrontPorch))) {
|
||||
// begin serial transmission
|
||||
m_bTxPending = false;
|
||||
_send();
|
||||
m_BlueWireSerial.write(m_TxFrame.Data, 24); // write native binary values
|
||||
DebugReportFrame("BTC ", m_TxFrame, " "); // report frame to debug port
|
||||
}
|
||||
if(diff > (m_nStartDelay + m_nFrameTime)) {
|
||||
// conclude Tx gating
|
||||
m_nStartTime = 0;
|
||||
digitalWrite(m_nTxEnbPin, LOW);
|
||||
digitalWrite(m_nTxGatePin, LOW);
|
||||
}
|
||||
}
|
||||
return m_nStartTime == 0;
|
||||
}
|
||||
|
||||
void
|
||||
CTxManage::_send()
|
||||
{
|
||||
// install on/off commands if required
|
||||
if(m_bOnReq) {
|
||||
m_bOnReq = false;
|
||||
m_Frame.Controller.Command = 0xa0;
|
||||
}
|
||||
else if(m_bOffReq) {
|
||||
m_bOffReq = false;
|
||||
m_Frame.Controller.Command = 0x05;
|
||||
}
|
||||
else {
|
||||
m_Frame.Controller.Command = 0x00;
|
||||
}
|
||||
|
||||
// ensure CRC valid
|
||||
m_Frame.setCRC();
|
||||
|
||||
// send to heater - using binary
|
||||
for(int i=0; i<24; i++) {
|
||||
m_Serial.write(m_Frame.Data[i]); // write native binary values
|
||||
}
|
||||
|
||||
Report();
|
||||
}
|
||||
|
||||
void
|
||||
CTxManage::Report()
|
||||
{
|
||||
SerialReport("Self ", m_Frame.Data, " ");
|
||||
}
|
||||
|
|
|
@ -8,23 +8,25 @@ class CTxManage
|
|||
const int m_nFrontPorch = 2;
|
||||
|
||||
public:
|
||||
CTxManage(int TxEnbPin, HardwareSerial& serial);
|
||||
void RequestOn();
|
||||
void RequestOff();
|
||||
void Start(const CProtocol& ref, unsigned long timenow, bool self);
|
||||
CTxManage(int TxGatePin, HardwareSerial& serial);
|
||||
void queueOnRequest();
|
||||
void queueOffRequest();
|
||||
void PrepareFrame(const CProtocol& Frame, bool isBTCmaster);
|
||||
void Start(unsigned long timenow);
|
||||
bool CheckTx(unsigned long timenow);
|
||||
void Report();
|
||||
void begin();
|
||||
const CProtocol& getFrame() const { return m_TxFrame; };
|
||||
|
||||
private:
|
||||
CProtocol m_Frame;
|
||||
CProtocol m_TxFrame;
|
||||
bool m_bOnReq;
|
||||
bool m_bOffReq;
|
||||
bool m_bTxPending;
|
||||
int m_nTxEnbPin;
|
||||
int m_nTxGatePin;
|
||||
unsigned long m_nStartTime;
|
||||
HardwareSerial& m_Serial;
|
||||
HardwareSerial& m_BlueWireSerial;
|
||||
|
||||
void _send();
|
||||
};
|
||||
|
||||
extern CTxManage TxManage;
|
||||
|
||||
|
|
8
Arduino/SenderTrial2/debugport.h
Normal file
8
Arduino/SenderTrial2/debugport.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
|
||||
|
||||
#if defined(__arm__)
|
||||
// Typically Arduino Due
|
||||
static UARTClass& DebugPort(Serial);
|
||||
#else
|
||||
static HardwareSerial& DebugPort(Serial); // reference Serial as DebugPort
|
||||
#endif
|
8
Arduino/SenderTrial2/pins.h
Normal file
8
Arduino/SenderTrial2/pins.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
const int TxEnbPin = 22;
|
||||
const int ListenOnlyPin = 21;
|
||||
const int KeyPin = 15;
|
||||
const int Tx1Pin = 18;
|
||||
const int Rx1Pin = 19;
|
||||
const int Tx2Pin = 16;
|
||||
const int Rx2Pin = 17;
|
||||
|
Loading…
Reference in a new issue