From 99e1b9d6bcaaaa4309d5047d0274287721bf722e Mon Sep 17 00:00:00 2001 From: rljonesau Date: Sun, 23 Sep 2018 09:31:47 +1000 Subject: [PATCH] Initial attempt at injecting messages within a working system --- Arduino/SenderTrial2/.vscode/arduino.json | 6 + .../.vscode/c_cpp_properties.json | 19 ++ Arduino/SenderTrial2/.vscode/settings.json | 6 + Arduino/SenderTrial2/.vscode/tasks.json | 12 + Arduino/SenderTrial2/CFrame.cpp | 161 +++++++++++ Arduino/SenderTrial2/CFrame.h | 128 +++++++++ Arduino/SenderTrial2/SenderTrial2.ino | 267 ++++++++++++++++++ 7 files changed, 599 insertions(+) create mode 100644 Arduino/SenderTrial2/.vscode/arduino.json create mode 100644 Arduino/SenderTrial2/.vscode/c_cpp_properties.json create mode 100644 Arduino/SenderTrial2/.vscode/settings.json create mode 100644 Arduino/SenderTrial2/.vscode/tasks.json create mode 100644 Arduino/SenderTrial2/CFrame.cpp create mode 100644 Arduino/SenderTrial2/CFrame.h create mode 100644 Arduino/SenderTrial2/SenderTrial2.ino diff --git a/Arduino/SenderTrial2/.vscode/arduino.json b/Arduino/SenderTrial2/.vscode/arduino.json new file mode 100644 index 0000000..a6d66f4 --- /dev/null +++ b/Arduino/SenderTrial2/.vscode/arduino.json @@ -0,0 +1,6 @@ +{ + "board": "arduino:sam:arduino_due_x_dbg", + "port": "COM4", + "sketch": "SenderTrial2.ino", + "output": "..\\build" +} \ No newline at end of file diff --git a/Arduino/SenderTrial2/.vscode/c_cpp_properties.json b/Arduino/SenderTrial2/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..93cb9f4 --- /dev/null +++ b/Arduino/SenderTrial2/.vscode/c_cpp_properties.json @@ -0,0 +1,19 @@ +{ + "configurations": [ + { + "name": "Win32", + "includePath": [ + "C:\\Users\\ray\\AppData\\Local\\Arduino15\\packages\\arduino\\tools\\**", + "C:\\Users\\ray\\AppData\\Local\\Arduino15\\packages\\arduino\\hardware\\sam\\1.6.11\\**" + ], + "forcedInclude": [ + "C:\\Users\\ray\\AppData\\Local\\Arduino15\\packages\\arduino\\hardware\\sam\\1.6.11\\cores\\arduino\\Arduino.h" + ], + "intelliSenseMode": "msvc-x64", + "compilerPath": "C:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/Tools/MSVC/14.15.26726/bin/Hostx64/x64/cl.exe", + "cStandard": "c11", + "cppStandard": "c++17" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/Arduino/SenderTrial2/.vscode/settings.json b/Arduino/SenderTrial2/.vscode/settings.json new file mode 100644 index 0000000..7127d0b --- /dev/null +++ b/Arduino/SenderTrial2/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "sketch": "SenderTrial2.ino", + "port": "COM4", + "board": "arduino:sam:arduino_due_x_dbg", + "output": "c:\\Users\\ray\\AppData\\Local\\Arduino\\" + } \ No newline at end of file diff --git a/Arduino/SenderTrial2/.vscode/tasks.json b/Arduino/SenderTrial2/.vscode/tasks.json new file mode 100644 index 0000000..6c28385 --- /dev/null +++ b/Arduino/SenderTrial2/.vscode/tasks.json @@ -0,0 +1,12 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "echo", + "type": "shell", + "command": "echo Hello" + } + ] +} \ No newline at end of file diff --git a/Arduino/SenderTrial2/CFrame.cpp b/Arduino/SenderTrial2/CFrame.cpp new file mode 100644 index 0000000..6e3151b --- /dev/null +++ b/Arduino/SenderTrial2/CFrame.cpp @@ -0,0 +1,161 @@ +#include +#include "CFrame.h" + +unsigned short +CFrame::CalcCRC() +{ + // calculate a CRC-16/MODBUS checksum using the first 22 bytes of the data array + unsigned short wCRCWord = 0xFFFF; + + int wLength = 22; + unsigned char* pData = Data; + while (wLength--) + { + unsigned char nTemp = *pData++ ^ wCRCWord; + wCRCWord >>= 8; + wCRCWord ^= wCRCTable[nTemp]; + } + + return wCRCWord; +} + +void +CFrame::setCRC() +{ + setCRC(CalcCRC()); +} + +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(); // 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/CFrame.h b/Arduino/SenderTrial2/CFrame.h new file mode 100644 index 0000000..94afbe7 --- /dev/null +++ b/Arduino/SenderTrial2/CFrame.h @@ -0,0 +1,128 @@ + +class CFrame { +public: + union { + unsigned char Data[24]; + struct { + unsigned char Byte0; // always 0x76 + unsigned char Len; // always 0x16 == 22 + unsigned char Command; // transient commands: 00: NOP, 0xa0 START, 0x05: STOP + unsigned char ActualTemperature; // 1degC / digit + unsigned char DesiredTemperature; // 1degC / digit + unsigned char MinPumpFreq; // 0.1Hz/digit + unsigned char MaxPumpFreq; // 0.1Hz/digit + unsigned char MinFanRPM_MSB; // 16 bit - big endian MSB + unsigned char MinFanRPM_LSB; // 16 bit - big endian LSB : 1 RPM / digit + unsigned char MaxFanRPM_MSB; // 16 bit - big endian MSB + unsigned char MaxFanRPM_LSB; // 16 bit - big endian LSB : 1 RPM / digit + unsigned char OperatingVoltage; // 120, 240 : 0.1V/digit + unsigned char FanSensor; // SN-1 or SN-2 + unsigned char OperatingMode; // 0x32:Thermostat, 0xCD:Fixed + unsigned char MinTemperature; // Minimum settable temperature + unsigned char MaxTemperature; // Maximum settable temperature + unsigned char MinTempRise; // temp rise to sense running OK + unsigned char Prime; // 00: normal, 0x5A: fuel prime + unsigned char Unknown1_MSB; // always 0x01 + unsigned char Unknown1_LSB; // always 0x2c "300 secs = max run without burn detected"? + unsigned char Unknown2_MSB; // always 0x0d + unsigned char Unknown2_LSB; // always 0xac "3500 ?" + unsigned char CRC_MSB; + unsigned char CRC_LSB; + } Tx; + struct { + unsigned char Byte0; // always 0x76 + unsigned char Len; // always 0x16 == 22 + unsigned char RunState; // operating state + unsigned char OnOff; // 0: OFF, 1: ON + 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 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 ActualPumpFreq; // fuel pump freq.: 0.1Hz / digit + unsigned char ErrorCode; // + unsigned char Unknown1; // always 0x00 + unsigned char FixedPumpFreq; // fixed mode frequency set point: 0.1Hz / digit + unsigned char Unknown2; // always 0x64 "100 ?" + unsigned char Unknown3; // always 0x00 + unsigned char CRC_MSB; + unsigned char CRC_LSB; + } Rx; + }; + static const int TxMode = 1; + const unsigned short wCRCTable[256] = { + 0X0000, 0XC0C1, 0XC181, 0X0140, 0XC301, 0X03C0, 0X0280, 0XC241, + 0XC601, 0X06C0, 0X0780, 0XC741, 0X0500, 0XC5C1, 0XC481, 0X0440, + 0XCC01, 0X0CC0, 0X0D80, 0XCD41, 0X0F00, 0XCFC1, 0XCE81, 0X0E40, + 0X0A00, 0XCAC1, 0XCB81, 0X0B40, 0XC901, 0X09C0, 0X0880, 0XC841, + 0XD801, 0X18C0, 0X1980, 0XD941, 0X1B00, 0XDBC1, 0XDA81, 0X1A40, + 0X1E00, 0XDEC1, 0XDF81, 0X1F40, 0XDD01, 0X1DC0, 0X1C80, 0XDC41, + 0X1400, 0XD4C1, 0XD581, 0X1540, 0XD701, 0X17C0, 0X1680, 0XD641, + 0XD201, 0X12C0, 0X1380, 0XD341, 0X1100, 0XD1C1, 0XD081, 0X1040, + 0XF001, 0X30C0, 0X3180, 0XF141, 0X3300, 0XF3C1, 0XF281, 0X3240, + 0X3600, 0XF6C1, 0XF781, 0X3740, 0XF501, 0X35C0, 0X3480, 0XF441, + 0X3C00, 0XFCC1, 0XFD81, 0X3D40, 0XFF01, 0X3FC0, 0X3E80, 0XFE41, + 0XFA01, 0X3AC0, 0X3B80, 0XFB41, 0X3900, 0XF9C1, 0XF881, 0X3840, + 0X2800, 0XE8C1, 0XE981, 0X2940, 0XEB01, 0X2BC0, 0X2A80, 0XEA41, + 0XEE01, 0X2EC0, 0X2F80, 0XEF41, 0X2D00, 0XEDC1, 0XEC81, 0X2C40, + 0XE401, 0X24C0, 0X2580, 0XE541, 0X2700, 0XE7C1, 0XE681, 0X2640, + 0X2200, 0XE2C1, 0XE381, 0X2340, 0XE101, 0X21C0, 0X2080, 0XE041, + 0XA001, 0X60C0, 0X6180, 0XA141, 0X6300, 0XA3C1, 0XA281, 0X6240, + 0X6600, 0XA6C1, 0XA781, 0X6740, 0XA501, 0X65C0, 0X6480, 0XA441, + 0X6C00, 0XACC1, 0XAD81, 0X6D40, 0XAF01, 0X6FC0, 0X6E80, 0XAE41, + 0XAA01, 0X6AC0, 0X6B80, 0XAB41, 0X6900, 0XA9C1, 0XA881, 0X6840, + 0X7800, 0XB8C1, 0XB981, 0X7940, 0XBB01, 0X7BC0, 0X7A80, 0XBA41, + 0XBE01, 0X7EC0, 0X7F80, 0XBF41, 0X7D00, 0XBDC1, 0XBC81, 0X7C40, + 0XB401, 0X74C0, 0X7580, 0XB541, 0X7700, 0XB7C1, 0XB681, 0X7640, + 0X7200, 0XB2C1, 0XB381, 0X7340, 0XB101, 0X71C0, 0X7080, 0XB041, + 0X5000, 0X90C1, 0X9181, 0X5140, 0X9301, 0X53C0, 0X5280, 0X9241, + 0X9601, 0X56C0, 0X5780, 0X9741, 0X5500, 0X95C1, 0X9481, 0X5440, + 0X9C01, 0X5CC0, 0X5D80, 0X9D41, 0X5F00, 0X9FC1, 0X9E81, 0X5E40, + 0X5A00, 0X9AC1, 0X9B81, 0X5B40, 0X9901, 0X59C0, 0X5880, 0X9841, + 0X8801, 0X48C0, 0X4980, 0X8941, 0X4B00, 0X8BC1, 0X8A81, 0X4A40, + 0X4E00, 0X8EC1, 0X8F81, 0X4F40, 0X8D01, 0X4DC0, 0X4C80, 0X8C41, + 0X4400, 0X84C1, 0X8581, 0X4540, 0X8701, 0X47C0, 0X4680, 0X8641, + 0X8201, 0X42C0, 0X4380, 0X8341, 0X4100, 0X81C1, 0X8081, 0X4040 + }; + +public: + CFrame() { Init(0); }; + CFrame(int TxMode) { Init(TxMode); }; + void Init(int Txmode); + // CRC handlers + unsigned short CalcCRC(); // calculate and set the CRC upon first 22 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 + // 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 + // 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 + // 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 + short getTemperature_HeatExchg(); // temperature of heat exchanger + short getTemperature_Inlet(); // temperature near inlet +}; + diff --git a/Arduino/SenderTrial2/SenderTrial2.ino b/Arduino/SenderTrial2/SenderTrial2.ino new file mode 100644 index 0000000..3d18199 --- /dev/null +++ b/Arduino/SenderTrial2/SenderTrial2.ino @@ -0,0 +1,267 @@ + /* + Chinese Heater Half Duplex Serial Data Send Test Tool + + Connects to the blue wire of a Chinese heater, which is the half duplex serial link. + 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___________ + + 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** + + 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 + 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 + + 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. + + This example code is in the public domain. +*/ + +#include "CFrame.h" + +CFrame Controller(CFrame::TxMode); +CFrame TxFrame(CFrame::TxMode); +CFrame Heater1; +CFrame Heater2; +const int TxEnbPin = 17; +long lastRxTime; // used to calculate inter character delay +long TxEnbTime; // used to reset TxEnb low +bool bOnEvent = false; +bool bOffEvent = false; + +void CheckTx(); + + +void setup() +{ + pinMode(TxEnbPin, OUTPUT); + digitalWrite(TxEnbPin, LOW); + // 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); + + // initialise serial monitor on serial port 0 + Serial.begin(115200); + + // prepare for first long delay detection + lastRxTime = millis(); + +} + +void loop() +{ + static int count = 0; + static unsigned long lastTx = 0; + static bool bAllowTxSlot = false; + static int Stage = -1; + + char str[16]; + + unsigned long timenow = millis(); + + if(Serial.available()) { + char rxval = Serial.read(); + if(rxval == '+') { + bOnEvent = true; + } + if(rxval == '-') { + bOffEvent = true; + } + } + + if((Stage == 4) && (timenow - lastTx) > 10) { + TxEnbTime = timenow; + if(TxEnbTime == 0) + TxEnbTime++; + digitalWrite(TxEnbPin, HIGH); + } + + CheckTx(); + // check serial data has gone quite for a while so we can trample in... + // calc elapsed time since last rxd byte to detect start of frame sequence + /* unsigned long TxSlot = timenow - lastRxTime; + +// if(Slot > 50 && TxEnbTime == 0 && (bOnEvent || bOffEvent)) { + if(bAllowTxSlot && (TxSlot > 50) && (TxEnbTime == 0)) { + TxEnbTime = timenow; + if(TxEnbTime == 0) + TxEnbTime++; + digitalWrite(TxEnbPin, HIGH); + } +*/ + // read from port 1, the "Tx Data" (to heater), send to the serial monitor: + if (Serial1.available()) { + + // calc elapsed time since last rxd byte to detect start of frame sequence + unsigned long diff = timenow - lastRxTime; + lastRxTime = timenow; + + if((Stage == -1) && (diff > 100)) { // this indicates the start of a new frame sequence from the controller + Stage = 0; + count = 0; + } + + int inByte = Serial1.read(); // read hex byte + + if(Stage == 0) { + Controller.Data[count++] = inByte; + if(count == 24) { + Stage = 1; + } + } + + if(Stage == 2) { + Heater1.Data[count++] = inByte; + if(count == 24) { + Stage = 3; + } + } + + if(Stage == 6) { + Heater2.Data[count++] = inByte; + if(count == 24) { + Stage = 7; + } + } + + } + + // dump to PC after capturing all 24 Rx bytes in a frame session + if(Stage == 1) { // filled controller frame, dump + + char str[16]; + sprintf(str, "Ctrl ", lastRxTime); + Serial.print(str); // print timestamp + for(int i=0; i<24; i++) { + + sprintf(str, "%02X ", Controller.Data[i]); // make 2 dig hex values + Serial.print(str); // and print + + } +// Serial.println(); // newline and done + + Stage = 2; + count = 0; + + } // Stage == 1 + + if(Stage == 3) { // filled heater frame, dump + + char str[16]; + sprintf(str, " Htr1 ", lastRxTime); + Serial.print(str); // print timestamp + for(int i=0; i<24; i++) { + + sprintf(str, "%02X ", Heater1.Data[i]); // make 2 dig hex values + Serial.print(str); // and print + + } + Serial.println(); // newline and done + + Stage = 4; + count = 0; + lastTx = timenow; + + } // Stage == 1 + + if(Stage == 7) { // filled heater frame, dump + + char str[16]; + sprintf(str, " Htr2 ", lastRxTime); + Serial.print(str); // print timestamp + for(int i=0; i<24; i++) { + + sprintf(str, "%02X ", Heater2.Data[i]); // make 2 dig hex values + Serial.print(str); // and print + + } + Serial.println(); // newline and done + + Stage = -1; + count = 0; + + } // Stage == 1 + +} // loop + +void CheckTx() +{ + char str[16]; + + if(TxEnbTime) { + long diff = timenow - TxEnbTime; + if(diff >= 12) { + TxEnbTime = 0; + digitalWrite(TxEnbPin, LOW); + Stage = 6; + } + } + + if((Stage == 4) && (timenow - lastTx) > 10) { + + Stage = 5; + + TxFrame.Tx.Byte0 = 0x78; + TxFrame.setTemperature_Desired(35); + TxFrame.setTemperature_Actual(22); + TxFrame.Tx.OperatingVoltage = 240; + TxFrame.setPump_Min(16); + TxFrame.setPump_Max(55); + TxFrame.setFan_Min(1680); + TxFrame.setFan_Max(4500); + + if(bOnEvent) { + bOnEvent = false; + TxFrame.Tx.Command = 0xa0; + } + else if(bOffEvent) { + bOffEvent = false; + TxFrame.Tx.Command = 0x05; + } + else { + TxFrame.Tx.Command = 0x00; + } + + TxFrame.setCRC(); + + // send to serial monitor using ASCII + Serial.print("Us "); // and print ASCII data + for(int i=0; i<24; i++) { + sprintf(str, "%02X ", TxFrame.Data[i]); // make 2 dig hex ASCII values + Serial.print(str); // and print ASCII data + } + + // send to heater - using binary + digitalWrite(TxEnbPin, HIGH); + for(int i=0; i<24; i++) { + Serial1.write(TxFrame.Data[i]); // write native binary values + } + } + +} \ No newline at end of file