Initial attempt at injecting messages within a working system

This commit is contained in:
rljonesau 2018-09-23 09:31:47 +10:00
parent 4fbf3f0d41
commit 99e1b9d6bc
7 changed files with 599 additions and 0 deletions

View file

@ -0,0 +1,6 @@
{
"board": "arduino:sam:arduino_due_x_dbg",
"port": "COM4",
"sketch": "SenderTrial2.ino",
"output": "..\\build"
}

View file

@ -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
}

View file

@ -0,0 +1,6 @@
{
"sketch": "SenderTrial2.ino",
"port": "COM4",
"board": "arduino:sam:arduino_due_x_dbg",
"output": "c:\\Users\\ray\\AppData\\Local\\Arduino\\"
}

12
Arduino/SenderTrial2/.vscode/tasks.json vendored Normal file
View file

@ -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"
}
]
}

View file

@ -0,0 +1,161 @@
#include <Arduino.h>
#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);
}
}

View file

@ -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
};

View file

@ -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
}
}
}