diff --git a/Arduino/SenderTrial2/CFrame.cpp b/Arduino/SenderTrial2/CFrame.cpp deleted file mode 100644 index 97b2e6e..0000000 --- a/Arduino/SenderTrial2/CFrame.cpp +++ /dev/null @@ -1,169 +0,0 @@ -#include -#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); - } -} - diff --git a/Arduino/SenderTrial2/Protocol.cpp b/Arduino/SenderTrial2/Protocol.cpp new file mode 100644 index 0000000..1871d52 --- /dev/null +++ b/Arduino/SenderTrial2/Protocol.cpp @@ -0,0 +1,267 @@ +#include +#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); + } +} + diff --git a/Arduino/SenderTrial2/CFrame.h b/Arduino/SenderTrial2/Protocol.h similarity index 63% rename from Arduino/SenderTrial2/CFrame.h rename to Arduino/SenderTrial2/Protocol.h index b077f7e..c44cdb1 100644 --- a/Arduino/SenderTrial2/CFrame.h +++ b/Arduino/SenderTrial2/Protocol.h @@ -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 \ No newline at end of file diff --git a/Arduino/SenderTrial2/SenderTrial2.ino b/Arduino/SenderTrial2/SenderTrial2.ino index 6deb22a..d541715 100644 --- a/Arduino/SenderTrial2/SenderTrial2.ino +++ b/Arduino/SenderTrial2/SenderTrial2.ino @@ -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 } \ No newline at end of file diff --git a/Arduino/SenderTrial2/TxManage.cpp b/Arduino/SenderTrial2/TxManage.cpp index aec134d..262c5e4 100644 --- a/Arduino/SenderTrial2/TxManage.cpp +++ b/Arduino/SenderTrial2/TxManage.cpp @@ -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 diff --git a/Arduino/SenderTrial2/TxManage.h b/Arduino/SenderTrial2/TxManage.h index 8939c3a..2fb1797 100644 --- a/Arduino/SenderTrial2/TxManage.h +++ b/Arduino/SenderTrial2/TxManage.h @@ -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(); };