changed to protocol.h/.cpp

This commit is contained in:
rljonesau 2018-09-23 18:59:19 +10:00
parent 9818b33e47
commit 961c5b8b29
6 changed files with 447 additions and 326 deletions

View file

@ -1,169 +0,0 @@
#include <Arduino.h>
#include "CFrame.h"
unsigned short
CFrame::CalcCRC(int len)
{
// 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;
while (wLength--)
{
unsigned char nTemp = *pData++ ^ wCRCWord;
wCRCWord >>= 8;
wCRCWord ^= wCRCTable[nTemp];
}
return wCRCWord;
}
void
CFrame::setCRC()
{
setCRC(CalcCRC(22));
}
void
CFrame::setCRC(unsigned short CRC)
{
Data[22] = (CRC >> 8) & 0xff; // MSB of CRC in Data[22]
Data[23] = (CRC >> 0) & 0xff; // LSB of CRC in Data[23]
}
CFrame&
CFrame::operator=(CFrame& rhs)
{
memcpy(Data, rhs.Data, 24);
return *this;
}
unsigned short
CFrame::getCRC()
{
unsigned short CRC;
CRC = Data[22]; // MSB of CRC in Data[22]
CRC <<= 8;
CRC |= Data[23]; // LSB of CRC in Data[23]
}
// return true for CRC match
bool
CFrame::verifyCRC()
{
unsigned short CRC = CalcCRC(22); // calculate CRC based on first 22 bytes
return (getCRC() == CRC); // does it match the stored values?
}
void
CFrame::setFan_Min(short Speed)
{
// Minimum speed set
Tx.MinFanRPM_MSB = (Speed >> 8) & 0xff;
Tx.MinFanRPM_LSB = (Speed >> 0) & 0xff;
}
void
CFrame::setFan_Max(short Speed)
{
// Minimum speed set
Tx.MaxFanRPM_MSB = (Speed >> 8) & 0xff;
Tx.MaxFanRPM_LSB = (Speed >> 0) & 0xff;
}
short
CFrame::getFan_Min()
{
short retval;
// Minimum speed get
retval = Tx.MinFanRPM_MSB;
retval <<= 8;
retval |= Tx.MinFanRPM_LSB;
return retval;
}
short
CFrame::getFan_Max()
{
short retval;
// Maximum speed get
retval = Tx.MaxFanRPM_MSB;
retval <<= 8;
retval |= Tx.MaxFanRPM_LSB;
return retval;
}
short
CFrame::getFan_Actual()
{
// Rx side, actual
short retval;
retval = Rx.FanRPM_MSB;
retval <<= 8;
retval |= Rx.FanRPM_LSB;
return retval;
}
short
CFrame::getTemperature_GlowPin() // temperature of glow pin
{
short retval;
retval = Rx.GlowPinTemp_MSB;
retval <<= 8;
retval |= Rx.GlowPinPWMDuty_LSB;
return retval;
}
short
CFrame::getTemperature_HeatExchg() // temperature of heat exchanger
{
short retval;
retval = Rx.HeatExchgTemp_MSB;
retval <<= 8;
retval |= Rx.HeatExchgTemp_LSB;
return retval;
}
short
CFrame::getTemperature_Inlet() // temperature near inlet
{
short retval;
retval = Rx.InletTemp_MSB;
retval <<= 8;
retval |= Rx.InletTemp_LSB;
return retval;
}
void
CFrame::Init(int Txmode)
{
if(Txmode) {
Tx.Byte0 = 0x76;
Tx.Len = 22;
Tx.Command = 0; // NOP
setTemperature_Actual(18); // 1degC / digit
setTemperature_Desired(20); // 1degC / digit
setPump_Min(14); // 0.1Hz/digit
setPump_Max(43); // 0.1Hz/digit
setFan_Min(1450); // 1RPM / digit
setFan_Max(4500); // 1RPM / digit
Tx.OperatingVoltage = 120; // 0.1V/digit
Tx.FanSensor = 1; // SN-1 or SN-2
Tx.OperatingMode = 0x32; // 0x32:Thermostat, 0xCD:Fixed
setTemperature_Min(8); // Minimum settable temperature
setTemperature_Max(35); // Maximum settable temperature
Tx.MinTempRise = 5; // temp rise to sense fuel ignition
Tx.Prime = 0; // 00: normal, 0x5A: fuel prime
Tx.Unknown1_MSB = 0x01; // always 0x01
Tx.Unknown1_LSB = 0x2c; // always 0x2c 16bit: "300 secs = max run without burn detected" ??
Tx.Unknown2_MSB = 0x0d; // always 0x0d
Tx.Unknown2_LSB = 0xac; // always 0xac 16bit: "3500" ??
setCRC();
}
else {
memset(Data, 0, 24);
}
}

View file

@ -0,0 +1,267 @@
#include <Arduino.h>
#include "Protocol.h"
unsigned short
CFrame::CalcCRC(int len)
{
// 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;
while (wLength--)
{
unsigned char nTemp = *pData++ ^ wCRCWord;
wCRCWord >>= 8;
wCRCWord ^= wCRCTable[nTemp];
}
return wCRCWord;
}
void
CFrame::setCRC()
{
setCRC(CalcCRC(22));
}
void
CFrame::setCRC(unsigned short CRC)
{
Data[22] = (CRC >> 8) & 0xff; // MSB of CRC in Data[22]
Data[23] = (CRC >> 0) & 0xff; // LSB of CRC in Data[23]
}
unsigned short
CFrame::getCRC()
{
unsigned short CRC;
CRC = Data[22]; // MSB of CRC in Data[22]
CRC <<= 8;
CRC |= Data[23]; // LSB of CRC in Data[23]
}
// return true for CRC match
bool
CFrame::verifyCRC()
{
unsigned short CRC = CalcCRC(22); // calculate CRC based on first 22 bytes
return (getCRC() == CRC); // does it match the stored values?
}
CFrame&
CFrame::operator=(const CFrame& rhs)
{
memcpy(Data, rhs.Data, 24);
return *this;
}
int
CFrame::getCommand()
{
return Controller.Command;
}
void
CFrame::setCommand(int cmd)
{
Controller.Command = cmd;
}
/*unsigned char getCommand();
void setCommand(int mode);*/
void
CFrame::setFan_Min(short Speed)
{
// Minimum speed set
Controller.MinFanRPM_MSB = (Speed >> 8) & 0xff;
Controller.MinFanRPM_LSB = (Speed >> 0) & 0xff;
}
void
CFrame::setFan_Max(short Speed)
{
// Minimum speed set
Controller.MaxFanRPM_MSB = (Speed >> 8) & 0xff;
Controller.MaxFanRPM_LSB = (Speed >> 0) & 0xff;
}
short
CFrame::getFan_Min()
{
short retval;
// Minimum speed get
retval = Controller.MinFanRPM_MSB;
retval <<= 8;
retval |= Controller.MinFanRPM_LSB;
return retval;
}
short
CFrame::getFan_Max()
{
short retval;
// Maximum speed get
retval = Controller.MaxFanRPM_MSB;
retval <<= 8;
retval |= Controller.MaxFanRPM_LSB;
return retval;
}
short
CFrame::getFan_Actual()
{
// Rx side, actual
short retval;
retval = Heater.FanRPM_MSB;
retval <<= 8;
retval |= Heater.FanRPM_LSB;
return retval;
}
void
CFrame::setFan_Actual(short Speed) // Heater side, actual
{
// actual speed set
Heater.FanRPM_MSB = (Speed >> 8) & 0xff;
Heater.FanRPM_LSB = (Speed >> 0) & 0xff;
}
short
CFrame::getGlowPlug_Current() // glow plug current
{
short retval;
retval = Heater.GlowPlugCurrent_MSB;
retval <<= 8;
retval |= Heater.GlowPlugCurrent_LSB;
return retval;
}
void
CFrame::setGlowPlug_Current(short ampsx100) // glow plug current
{
Heater.GlowPlugCurrent_MSB = (ampsx100 >> 8) & 0xff;
Heater.GlowPlugCurrent_LSB = (ampsx100 >> 0) & 0xff;
}
short
CFrame::getGlowPlug_Voltage() // glow plug voltage
{
short retval;
retval = Heater.GlowPlugVoltage_MSB;
retval <<= 8;
retval |= Heater.GlowPlugVoltage_LSB;
return retval;
}
void
CFrame::setGlowPlug_Voltage(short voltsx10) // glow plug voltage
{
Heater.GlowPlugVoltage_MSB = (voltsx10 >> 8) & 0xff;
Heater.GlowPlugVoltage_LSB = (voltsx10 >> 0) & 0xff;
}
short
CFrame::getTemperature_HeatExchg() // temperature of heat exchanger
{
short retval;
retval = Heater.HeatExchgTemp_MSB;
retval <<= 8;
retval |= Heater.HeatExchgTemp_LSB;
return retval;
}
void
CFrame::setTemperature_HeatExchg(short degC) // temperature of heat exchanger
{
Heater.HeatExchgTemp_MSB = (degC >> 8) & 0xff;
Heater.HeatExchgTemp_LSB = (degC >> 0) & 0xff;
}
short
CFrame::getFan_Voltage() // temperature near inlet
{
short retval;
retval = Heater.FanVoltage_MSB;
retval <<= 8;
retval |= Heater.FanVoltage_LSB;
return retval;
}
void
CFrame::setFan_Voltage(short voltsx10) // temperature near inlet
{
Heater.FanVoltage_MSB = (voltsx10 >> 8) & 0xff;
Heater.FanVoltage_LSB = (voltsx10 >> 0) & 0xff;
}
void
CFrame::setVoltage_Supply(short voltsx10)
{
Heater.SupplyV_MSB = (voltsx10 >> 8) & 0xff;
Heater.SupplyV_LSB = (voltsx10 >> 0) & 0xff;
}
short
CFrame::getVoltage_Supply()
{
short retval = 0;
retval = Heater.SupplyV_MSB & 0xff;
retval <<= 8;
retval |= Heater.SupplyV_LSB & 0xff;
}
void
CFrame::Init(int FrameMode)
{
if(FrameMode == CtrlMode) {
Controller.Byte0 = 0x76;
Controller.Len = 22;
Controller.Command = 0; // NOP
setTemperature_Actual(18); // 1degC / digit
setTemperature_Desired(20); // 1degC / digit
setPump_Min(14); // 0.1Hz/digit
setPump_Max(43); // 0.1Hz/digit
setFan_Min(1450); // 1RPM / digit
setFan_Max(4500); // 1RPM / digit
Controller.OperatingVoltage = 120; // 0.1V/digit
Controller.FanSensor = 1; // SN-1 or SN-2
Controller.OperatingMode = 0x32; // 0x32:Thermostat, 0xCD:Fixed
setTemperature_Min(8); // Minimum settable temperature
setTemperature_Max(35); // Maximum settable temperature
Controller.MinTempRise = 5; // temp rise to sense fuel ignition
Controller.Prime = 0; // 00: normal, 0x5A: fuel prime
Controller.Unknown1_MSB = 0x01; // always 0x01
Controller.Unknown1_LSB = 0x2c; // always 0x2c 16bit: "300 secs = max run without burn detected" ??
Controller.Unknown2_MSB = 0x0d; // always 0x0d
Controller.Unknown2_LSB = 0xac; // always 0xac 16bit: "3500" ??
setCRC();
}
else if(FrameMode == HeatMode){
Heater.Byte0 = 0x76;
Heater.Len = 22;
setRunState(0);
setErrState(0);
setVoltage_Supply(133);
setFan_Actual(0);
setFan_Voltage(0);
setTemperature_HeatExchg(18);
setGlowPlug_Voltage(0);
setGlowPlug_Current(0);
Heater.ActualPumpFreq = 0; // fuel pump freq.: 0.1Hz / digit
Heater.ErrorCode = 0; //
Heater.Unknown1 = 0; // always 0x00
Heater.FixedPumpFreq = 23; // fixed mode frequency set point: 0.1Hz / digit
Heater.Unknown2 = 100; // always 0x64 "100 ?"
Heater.Unknown3 = 0; // always 0x00
setCRC();
}
else {
memset(Data, 0, 24);
}
}

View file

@ -30,24 +30,24 @@ public:
unsigned char Unknown2_LSB; // [21] always 0xac "3500 ?"
unsigned char CRC_MSB; // [22]
unsigned char CRC_LSB; // [23]
} Tx;
} Controller;
struct {
unsigned char Byte0; // always 0x76
unsigned char Len; // always 0x16 == 22
unsigned char Len; // always 0x16 == 22 bytes
unsigned char RunState; // operating state
unsigned char OnOff; // 0: OFF, 1: ON
unsigned char ErrState; // 0: OFF, 1: ON, 2+ (E-0n + 1)
unsigned char SupplyV_MSB; // 16 bit - big endian MSB
unsigned char SupplyV_LSB; // 16 bit - big endian MSB : 0.1V / digit
unsigned char FanRPM_MSB; // 16 bit - big endian MSB
unsigned char FanRPM_LSB; // 16 bit - big endian LSB : 1 RPM / digit
unsigned char InletTemp_MSB; // 16 bit - big endian MSB
unsigned char InletTemp_LSB; // 16 bit - big endian LSB : 1 degC / digit
unsigned char FanVoltage_MSB; // 16 bit - big endian MSB
unsigned char FanVoltage_LSB; // 16 bit - big endian LSB : 0.1V / digit
unsigned char HeatExchgTemp_MSB; // 16 bit - big endian MSB
unsigned char HeatExchgTemp_LSB; // 16 bit - big endian LSB : 1 degC / digit
unsigned char GlowPinPWMDuty_MSB; // 16 bit - big endian MSB
unsigned char GlowPinPWMDuty_LSB; // 16 bit - big endian LSB : 1% / digit
unsigned char GlowPinTemp_MSB; // 16 bit - big endian MSB
unsigned char GlowPinTemp_LSB; // 16 bit - big endian LSB : 1 degC / digit
unsigned char GlowPlugVoltage_MSB; // 16 bit - big endian MSB
unsigned char GlowPlugVoltage_LSB; // 16 bit - big endian LSB : 0.1V / digit
unsigned char GlowPlugCurrent_MSB; // 16 bit - big endian MSB
unsigned char GlowPlugCurrent_LSB; // 16 bit - big endian LSB : 10mA / digit
unsigned char ActualPumpFreq; // fuel pump freq.: 0.1Hz / digit
unsigned char ErrorCode; //
unsigned char Unknown1; // always 0x00
@ -56,9 +56,10 @@ public:
unsigned char Unknown3; // always 0x00
unsigned char CRC_MSB;
unsigned char CRC_LSB;
} Rx;
} Heater;
};
static const int TxMode = 1;
static const int CtrlMode = 1;
static const int HeatMode = 2;
const unsigned short wCRCTable[256] = {
0X0000, 0XC0C1, 0XC181, 0X0140, 0XC301, 0X03C0, 0X0280, 0XC241,
0XC601, 0X06C0, 0X0780, 0XC741, 0X0500, 0XC5C1, 0XC481, 0X0440,
@ -99,35 +100,59 @@ public:
CFrame(int TxMode) { Init(TxMode); };
void Init(int Txmode);
// CRC handlers
unsigned short CalcCRC(int len); // calculate and set the CRC upon first 22 bytes
unsigned short CalcCRC(int len); // calculate and set 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);
// Run state
unsigned char getRunState() { return Heater.RunState; };
void setRunState(unsigned char state) { Heater.RunState = state; };
unsigned char getErrState() { return Heater.ErrState; };
void setErrState(unsigned char state) { Heater.ErrState = state; };
//
short getVoltage_Supply();
void setVoltage_Supply(short voltsx10);
// fan set/get
short getFan_Actual(); // Rx side, actual
short getFan_Min(); // Tx side, define min fan speed
short getFan_Max(); // Tx side, define max fan speed
void setFan_Min(short speed); // Tx side, define min fan speed
void setFan_Max(short speed); // Tx side, define max fan speed
short getFan_Actual(); // Heater side, actual
short getFan_Min(); // Controller side, define min fan speed
short getFan_Max(); // Controller side, define max fan speed
void setFan_Actual(short speed); // Heater side, actual
void setFan_Min(short speed); // Controller side, define min fan speed
void setFan_Max(short speed); // Controller side, define max fan speed
short getFan_Voltage(); // fan voltage
void setFan_Voltage(short voltsx10); // fan voltage
// pump set/get
void setPump_Min(unsigned short Freq) { Tx.MinPumpFreq = Freq; };
void setPump_Max(unsigned short Freq) { Tx.MaxPumpFreq = Freq; };
unsigned char getPump_Min() { return Tx.MinPumpFreq; }; // Tx side, min pump freq
unsigned char getPump_Max() { return Tx.MaxPumpFreq; }; // Tx side, max pump freq
unsigned char getPump_Actual() { return Rx.ActualPumpFreq; }; // Rx style, actual
unsigned char getPump_Fixed() { return Rx.FixedPumpFreq; }; // Fixed mode pump frequency
void setPump_Min(unsigned short Freq) { Controller.MinPumpFreq = Freq; };
void setPump_Max(unsigned short Freq) { Controller.MaxPumpFreq = Freq; };
void setPump_Actual(unsigned char Freq) { Heater.ActualPumpFreq = Freq; };
void setPump_Fixed(unsigned char Freq) { Heater.FixedPumpFreq = Freq; };
unsigned char getPump_Min() { return Controller.MinPumpFreq; }; // Tx side, min pump freq
unsigned char getPump_Max() { return Controller.MaxPumpFreq; }; // Tx side, max pump freq
unsigned char getPump_Actual() { return Heater.ActualPumpFreq; }; // Rx style, actual
unsigned char getPump_Fixed() { return Heater.FixedPumpFreq; }; // Fixed mode pump frequency
// temperature set/get
void setTemperature_Desired(unsigned char degC) { Tx.DesiredTemperature = degC; };
void setTemperature_Min(unsigned char degC) { Tx.MinTemperature = degC; };
void setTemperature_Max(unsigned char degC) { Tx.MaxTemperature = degC; };
void setTemperature_Actual(unsigned char degC) { Tx.ActualTemperature = degC; };
unsigned char getTemperature_Desired() { return Tx.DesiredTemperature; };
short getTemperature_GlowPin(); // temperature of glow pin
void setTemperature_Desired(unsigned char degC) { Controller.DesiredTemperature = degC; };
void setTemperature_Min(unsigned char degC) { Controller.MinTemperature = degC; };
void setTemperature_Max(unsigned char degC) { Controller.MaxTemperature = degC; };
void setTemperature_Actual(unsigned char degC) { Controller.ActualTemperature = degC; };
unsigned char getTemperature_Desired() { return Controller.DesiredTemperature; };
unsigned char getTemperature_Min() { return Controller.MinTemperature; };
unsigned char getTemperature_Max() { return Controller.MaxTemperature; };
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
short getTemperature_HeatExchg(); // temperature of heat exchanger
short getTemperature_Inlet(); // temperature near inlet
void setTemperature_HeatExchg(short degC); // temperature of heat exchanger
CFrame& operator=(CFrame& rhs);
CFrame& operator=(const CFrame& rhs);
};
#endif

View file

@ -1,55 +1,68 @@
/*
Chinese Heater Half Duplex Serial Data Send Test Tool
Chinese Heater Half Duplex Serial Data Sending Tool
Connects to the blue wire of a Chinese heater, which is the half duplex serial link.
Receives data from serial port 1.
Sends and receives data from serial port 1.
Terminology: Tx is to the heater unit, Rx is from the heater unit.
The binary data is received from the line.
If it has been > 100ms since the last activity this indicates a new frame
sequence is starting, synchronise as such then count off the next 48 bytes
storing them in the Data array.
The "outer loop" then detects the count of 48 and packages the data to be sent
over Serial to the USB attached PC.
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.
The binary data is received from the line.
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.
It is then expected the heater wil lrespond 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.
If no activity is sensed in a second, it is assumed no controller is attached and we
have full control over the heater.
Either way we can now inject a message onto the blue wire allowing our custom
on/off control.
We must remain synchronous with the OEM controller if it exists otherwise E-07
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_________
Rx to next Tx delay is always > 100ms and is paced by the controller.
The delay before seeing Rx data after Tx is usually much less than 10ms.
**The heater only ever sends Rx data in response to a data frame from the controller**
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**
Resultant data is tagged and sent out on serial port 0 (the default debug port),
along with a timestamp for relative timing.
This example works only with boards with more than one serial port like Arduino
This code only works with boards that have more than one hardware serial port like Arduino
Mega, Due, Zero etc.
The circuit:
- Blue wire connected to Serial 1 Rx input - preferably with series 680ohm resistor.
- Serial logging software on Serial port 0 via USB link
- a Tx Rx multiplexer is required to combine the Arduino's Tx1 And Rx1 pins onto the blue wire.
- a Tx Enable signal from pin 20 controls the multiplexer
- Serial logging software on Serial0 via USB link
created 24 Aug 2018 by Ray Jones
modified 25 Aug by Ray Jones
- simplifed to read 48 bytes, synchronised by observing a long pause
between characters. The heater only sends when prompted.
No longer need to discrimate which packet of data would be present.
created 23 Sep 2018 by Ray Jones
This example code is in the public domain.
*/
#include "CFrame.h"
#include "Protocol.h"
#include "TxManage.h"
void SerialReport(const char* hdr, const unsigned char* pData, const char* ftr);
class CommStates {
public:
// comms states
enum eCS {
Idle, CtrlRx, CtrlRpt, HtrRx1, HtrRpt1, SelfTx, HtrRx2, HtrRpt2
Idle, ControllerRx, ControllerReport, HeaterRx1, HeaterReport1, SelfTx, HeaterRx2, HeaterReport2
};
CommStates() {
set(Idle);
@ -61,7 +74,7 @@ class CommStates {
bool is(eCS eState) {
return m_State == eState;
}
bool rxData(unsigned char* pData, unsigned char val, int limit = 24) { // return true if buffer filled
bool saveData(unsigned char* pData, unsigned char val, int limit = 24) { // returns true when buffer filled
pData[m_Count++] = val;
return m_Count == limit;
}
@ -70,17 +83,19 @@ private:
int m_Count;
};
const int TxEnbPin = 17;
UARTClass& USB(Serial);
UARTClass& BlueWire(Serial1);
UARTClass& BlueTooth(Serial2);
const int TxEnbPin = 20;
CommStates CommState;
CFrame Controller(CFrame::TxMode);
CFrame TxFrame(CFrame::TxMode);
CTxManage TxManage(TxEnbPin, Serial1);
CFrame Heater1;
CFrame Heater2;
long lastRxTime; // used to calculate inter character delay
long TxEnbTime; // used to reset TxEnb low
bool bOnEvent = false;
bool bOffEvent = false;
CFrame Controller; // most recent data packet received from OEM controller found on blue wire
CFrame Heater1; // data packet received from heater in response to OEM controller packet
CFrame Heater2; // data packet received from heater in response to our packet
CFrame SelfParams(CFrame::CtrlMode); // holds our local parameters, used in case on no OEM controller
long lastRxTime; // used to observe inter character delays
void setup()
@ -88,16 +103,25 @@ void setup()
// initialize listening serial port
// 25000 baud, Tx and Rx channels of Chinese heater comms interface:
// Tx/Rx data to/from heater, special baud rate for Chinese heater controllers
Serial1.begin(25000);
pinMode(19, INPUT_PULLUP);
BlueWire.begin(25000);
pinMode(19, INPUT_PULLUP); // required for MUX to work properly
// initialise serial monitor on serial port 0
Serial.begin(115200);
USB.begin(115200);
// prepare for first long delay detection
lastRxTime = millis();
TxManage.begin();
TxManage.begin(); // ensure Tx enable pin 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);
}
@ -106,8 +130,8 @@ void loop()
unsigned long timenow = millis();
// check for test commands received from PC Over USB
if(Serial.available()) {
char rxval = Serial.read();
if(USB.available()) {
char rxval = USB.read();
if(rxval == '+') {
TxManage.RequestOn();
}
@ -116,12 +140,11 @@ void loop()
}
}
// Handle time interval where we send data to the blue wire
if(CommState.is(CommStates::SelfTx)) {
// Interval where we should send data to the blue wire
lastRxTime = timenow; // not expecting rx data, but we are pumping onto blue wire!
TxManage.Tick(timenow); // keep trying to send our data
if(!TxManage.isBusy()) { // until completed
CommState.set(CommStates::HtrRx2); // then await heater repsonse
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
}
}
@ -135,72 +158,71 @@ void loop()
// OEM controller probably not connected.
// Skip to SelfTx, sending our own settings.
CommState.set(CommStates::SelfTx);
bool bSelfParams = true;
TxManage.Send(timenow, bSelfParams);
bool bOurParams = true;
TxManage.Start(SelfParams, timenow, bOurParams);
}
// precaution if 24 bytes were not received whilst expecting them
if(RxTimeElapsed > 50) {
if( CommState.is(CommStates::CtrlRx) ||
CommState.is(CommStates::HtrRx1) ||
CommState.is(CommStates::HtrRx2) ) {
// precaution action if all 24 bytes were not received whilst expecting them
if(RxTimeElapsed > 150) {
if( CommState.is(CommStates::ControllerRx) ||
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
if (Serial1.available()) {
if (BlueWire.available()) {
lastRxTime = timenow;
if( CommState.is(CommStates::Idle) && (RxTimeElapsed > 100)) { // this indicates the start of a new frame sequence from another controller
CommState.set(CommStates::CtrlRx);
// detect start of a new frame sequence from OEM controller
if( CommState.is(CommStates::Idle) && (RxTimeElapsed > 100)) {
CommState.set(CommStates::ControllerRx);
}
int inByte = Serial1.read(); // read hex byte
int inByte = BlueWire.read(); // read hex byte
if( CommState.is(CommStates::CtrlRx) ) {
if(CommState.rxData(Controller.Data, inByte) ) {
CommState.set(CommStates::CtrlRpt);
if( CommState.is(CommStates::ControllerRx) ) {
if(CommState.saveData(Controller.Data, inByte) ) {
CommState.set(CommStates::ControllerReport);
}
}
else if( CommState.is(CommStates::HtrRx1) ) {
if( CommState.rxData(Heater1.Data, inByte) ) {
CommState.set(CommStates::HtrRpt1);
else if( CommState.is(CommStates::HeaterRx1) ) {
if( CommState.saveData(Heater1.Data, inByte) ) {
CommState.set(CommStates::HeaterReport1);
}
}
else if( CommState.is(CommStates::HtrRx2) ) {
if( CommState.rxData(Heater2.Data, inByte) ) {
CommState.set(CommStates::HtrRpt2);
else if( CommState.is(CommStates::HeaterRx2) ) {
if( CommState.saveData(Heater2.Data, inByte) ) {
CommState.set(CommStates::HeaterReport2);
}
}
} // Serial1.available
} // BlueWire.available
if( CommState.is(CommStates::CtrlRpt) ) {
if( CommState.is(CommStates::ControllerReport) ) {
// filled controller frame, report
SerialReport("Ctrl ", Controller.Data, " ");
CommState.set(CommStates::HtrRx1);
CommState.set(CommStates::HeaterRx1);
}
else if(CommState.is(CommStates::HtrRpt1) ) {
else if(CommState.is(CommStates::HeaterReport1) ) {
// received heater frame (after controller message), report
SerialReport("Htr1 ", Heater1.Data, "\r\n");
TxManage.Copy(Controller); // replicate last obtained controller data
TxManage.Send(timenow, false);
bool bOurParams = false;
TxManage.Start(Controller, timenow, bOurParams);
CommState.set(CommStates::SelfTx);
}
else if( CommState.is(CommStates::HtrRpt2) ) {
else if( CommState.is(CommStates::HeaterReport2) ) {
// received heater frame (after our control message), report
SerialReport("Htr2 ", Heater2.Data, "\r\n");
CommState.set(CommStates::Idle);
}
@ -208,11 +230,11 @@ void loop()
void SerialReport(const char* hdr, const unsigned char* pData, const char* ftr)
{
Serial.print(hdr); // header
USB.print(hdr); // header
for(int i=0; i<24; i++) {
char str[16];
sprintf(str, "%02X ", pData[i]); // build 2 dig hex values
Serial.print(str); // and print
sprintf(str, "%02X ", pData[i]); // build 2 dig hex values
USB.print(str); // and print
}
Serial.print(ftr); // footer
USB.print(ftr); // footer
}

View file

@ -5,12 +5,11 @@ extern void SerialReport(const char* hdr, const unsigned char* pData, const char
CTxManage::CTxManage(int TxEnbPin, USARTClass& serial) :
m_Serial(serial),
m_Frame(CFrame::TxMode)
m_Frame(CFrame::CtrlMode)
{
m_bOnReq = false;
m_bOffReq = false;
m_bTxPending = false;
m_bSelf = true;
m_nStartTime = 0;
m_nTxEnbPin = TxEnbPin;
}
@ -33,31 +32,23 @@ CTxManage::RequestOff()
m_bOffReq = true;
}
void
CTxManage::Copy(CFrame& ref)
void
CTxManage::Start(const CFrame& ref, unsigned long timenow, bool self)
{
m_Frame = ref;
}
// 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;
bool
CTxManage::isBusy()
{
return m_nStartTime != 0;
}
void
CTxManage::Send(unsigned long timenow, bool self)
{
if(timenow == 0)
timenow++;
m_nStartTime = timenow;
m_bSelf = self;
m_bTxPending = true;
}
void
CTxManage::Tick(unsigned long timenow)
bool
CTxManage::CheckTx(unsigned long timenow)
{
if(m_nStartTime) {
@ -78,38 +69,26 @@ CTxManage::Tick(unsigned long timenow)
digitalWrite(m_nTxEnbPin, LOW);
}
}
return m_nStartTime == 0;
}
void
CTxManage::_send()
{
if(m_bSelf) {
m_Frame.Data[0] = 0x76; // required for heater to use the max min information
m_Frame.Data[1] = 0x16;
m_Frame.setTemperature_Desired(35);
m_Frame.setTemperature_Actual(22);
m_Frame.Tx.OperatingVoltage = 120;
m_Frame.setPump_Min(16);
m_Frame.setPump_Max(55);
m_Frame.setFan_Min(1680);
m_Frame.setFan_Max(4500);
}
else {
m_Frame.Data[0] = 0x78; // this prevents the controller trying to show bum information, heater uses controller max/min settings
}
// install on/off commands if required
if(m_bOnReq) {
m_bOnReq = false;
m_Frame.Tx.Command = 0xa0;
m_Frame.Controller.Command = 0xa0;
}
else if(m_bOffReq) {
m_bOffReq = false;
m_Frame.Tx.Command = 0x05;
m_Frame.Controller.Command = 0x05;
}
else {
m_Frame.Tx.Command = 0x00;
m_Frame.Controller.Command = 0x00;
}
// ensure CRC valid
m_Frame.setCRC();
// send to heater - using binary

View file

@ -3,14 +3,16 @@
class CTxManage
{
const int m_nStartDelay = 20;
const int m_nFrameTime = 14;
const int m_nFrontPorch = 2;
public:
CTxManage(int TxEnbPin, USARTClass& serial);
void RequestOn();
void RequestOff();
void Copy(CFrame& ref);
bool isBusy();
void Tick(unsigned long timenow);
void Send(unsigned long timenow, bool self);
void Start(const CFrame& ref, unsigned long timenow, bool self);
bool CheckTx(unsigned long timenow);
void Report();
void begin();
@ -19,15 +21,10 @@ private:
bool m_bOnReq;
bool m_bOffReq;
bool m_bTxPending;
bool m_bSelf;
int m_nTxEnbPin;
unsigned long m_nStartTime;
USARTClass& m_Serial;
const int m_nStartDelay = 20;
const int m_nFrameTime = 14;
const int m_nFrontPorch = 2;
void _send();
};