From 9d79c107c5cbe5e02974b9501c80852dbd02a59e Mon Sep 17 00:00:00 2001 From: rljonesau Date: Sat, 17 Nov 2018 13:08:22 +1100 Subject: [PATCH] Fine tuning OLED presentation, adding mini font --- Arduino/BTCDieselHeater/BTCDieselHeater.ino | 81 ++++- Arduino/BTCDieselHeater/OLEDconsts.h | 169 ++++++++++ Arduino/BTCDieselHeater/Protocol.h | 1 + Arduino/BTCDieselHeater/UtilClasses.h | 12 +- Arduino/BTCDieselHeater/display.cpp | 319 ++++++++++++++++++ Arduino/BTCDieselHeater/display.h | 12 + .../issues/BlueWireRxSlippage.txt | 102 ++++++ Arduino/BTCDieselHeater/issues/lostsync.txt | 46 +++ 8 files changed, 730 insertions(+), 12 deletions(-) create mode 100644 Arduino/BTCDieselHeater/OLEDconsts.h create mode 100644 Arduino/BTCDieselHeater/display.cpp create mode 100644 Arduino/BTCDieselHeater/display.h create mode 100644 Arduino/BTCDieselHeater/issues/BlueWireRxSlippage.txt create mode 100644 Arduino/BTCDieselHeater/issues/lostsync.txt diff --git a/Arduino/BTCDieselHeater/BTCDieselHeater.ino b/Arduino/BTCDieselHeater/BTCDieselHeater.ino index 88dc7fb..38c9314 100644 --- a/Arduino/BTCDieselHeater/BTCDieselHeater.ino +++ b/Arduino/BTCDieselHeater/BTCDieselHeater.ino @@ -113,6 +113,7 @@ static HardwareSerial& BlueWireSerial(Serial1); OneWire ds(DS18B20_Pin); // on pin 5 (a 4.7K resistor is necessary) DallasTemperature TempSensor(&ds); long lastTemperatureTime; // used to moderate DS18B20 access +unsigned long lastAnimationTime; float fFilteredTemperature = 0; const float fAlpha = 0.995; @@ -126,6 +127,7 @@ CSmartError SmartError; sRxLine PCline; long lastRxTime; // used to observe inter character delays bool hasOEMController = false; +bool stateDebug = false; //////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -191,6 +193,7 @@ void setup() { TempSensor.setWaitForConversion(false); TempSensor.requestTemperatures(); lastTemperatureTime = millis(); + lastAnimationTime = millis(); initOLED(); @@ -314,6 +317,9 @@ void loop() val--; bSendVal = true; } + else if(rxVal == 's') { + stateDebug = !stateDebug; + } } if(bSendVal) { switch(mode) { @@ -331,6 +337,7 @@ void loop() } } + Bluetooth.check(); // check for Bluetooth activity // calc elapsed time since last rxd byte @@ -363,24 +370,44 @@ void loop() // Reads data from the "blue wire" Serial port, (to/from heater) // If an OEM controller exists we will also see it's data frames // Note that the data is read now, then held for later use in the state machine - // + //s sRxData BlueWireData; if (BlueWireSerial.available()) { // Data is avaialable, read and store it now, use it later // Note that if not in a recognised data receive frame state, the data // will be deliberately lost! - + unsigned long dT = timenow - lastRxTime; lastRxTime = timenow; // tickle last rx time, for rx data timeout purposes BlueWireData.setValue(BlueWireSerial.read()); // read hex byte, store for later use + + if(stateDebug) { + Serial.print("dT{"); + Serial.print(dT); + Serial.print("}"); + } + } /////////////////////////////////////////////////////////////////////////////////////////// // do our state machine to track the reception and delivery of blue wire data + unsigned long tDelta; switch(CommState.get()) { case CommStates::Idle: + + if(stateDebug) { + Serial.print(":0"); + } + + // only update OLED when not processing blue wire + tDelta = timenow - lastAnimationTime; + if(tDelta >= 250) { + lastAnimationTime += 250; + animateOLED(); + } + #if RX_LED == 1 digitalWrite(LED_Pin, LOW); #endif @@ -408,7 +435,7 @@ void loop() hasOEMController = true; CommState.set(CommStates::OEMCtrlRx); // we must add this new byte! // - // ** IMPORTANT - we must drop through to OEMCtrlRx *NOW* (skip break) ** + // ** IMPORTANT - we must drop through to OEMCtrlRx *NOW* (skipping break) ** // } else { @@ -417,11 +444,18 @@ void loop() case CommStates::OEMCtrlRx: + if(stateDebug) { + Serial.print(":1"); + } + #if RX_LED == 1 digitalWrite(LED_Pin, HIGH); #endif // collect OEM controller frame if(BlueWireData.available()) { + if(stateDebug) { + Serial.print(",RD1"); + } if(CommState.collectData(OEMCtrlFrame, BlueWireData.getValue()) ) { CommState.set(CommStates::OEMCtrlReport); // collected 24 bytes, move on! } @@ -430,6 +464,9 @@ void loop() case CommStates::OEMCtrlReport: + if(stateDebug) { + Serial.print(":2"); + } #if RX_LED == 1 digitalWrite(LED_Pin, LOW); #endif @@ -451,11 +488,17 @@ void loop() case CommStates::HeaterRx1: + if(stateDebug) { + Serial.print(":3"); + } #if RX_LED == 1 digitalWrite(LED_Pin, HIGH); #endif // collect heater frame, always in response to an OEM controller frame if(BlueWireData.available()) { + if(stateDebug) { + Serial.print(",RD2"); + } if( CommState.collectData(HeaterFrame1, BlueWireData.getValue()) ) { CommState.set(CommStates::HeaterReport1); } @@ -464,6 +507,9 @@ void loop() case CommStates::HeaterReport1: + if(stateDebug) { + Serial.print(":4"); + } #if RX_LED == 1 digitalWrite(LED_Pin, LOW); #endif @@ -505,6 +551,9 @@ void loop() case CommStates::BTC_Tx: + if(stateDebug) { + Serial.print(":5"); + } // Handle time interval where we send data to the blue wire lastRxTime = timenow; // *we* are pumping onto blue wire, track this activity! if(TxManage.CheckTx(timenow) ) { // monitor progress of our data delivery @@ -518,12 +567,20 @@ void loop() case CommStates::HeaterRx2: + if(stateDebug) { + Serial.print(":6"); + } #if RX_LED == 1 digitalWrite(LED_Pin, HIGH); #endif // collect heater frame, in response to our control frame if(BlueWireData.available()) { - if( CommState.collectData(HeaterFrame2, BlueWireData.getValue()) ) { + if(stateDebug) { + Serial.print(",RD("); + Serial.print(BlueWireData.getValue(), HEX); + Serial.print(")"); + } + if( CommState.collectDataEx(HeaterFrame2, BlueWireData.getValue()) ) { CommState.set(CommStates::HeaterReport2); } } @@ -531,6 +588,9 @@ void loop() case CommStates::HeaterReport2: + if(stateDebug) { + Serial.print(":7"); + } #if RX_LED == 1 digitalWrite(LED_Pin, LOW); #endif @@ -550,6 +610,9 @@ void loop() break; case CommStates::TemperatureRead: + if(stateDebug) { + Serial.print(":8"); + } // update temperature reading, // synchronised with serial reception as interrupts do get disabled in the OneWire library unsigned Tdelta = timenow - lastTemperatureTime; @@ -562,17 +625,13 @@ void loop() TempSensor.requestTemperatures(); // prep sensor for future reading updateOLED(TxManage.getFrame(), HeaterFrame2); -/* display.clearDisplay(); - showThermometer(TxManage.getFrame().getTemperature_Desired(), // read values from most recently sent [BTC] frame - TxManage.getFrame().getTemperature_Actual()); - showBodyThermometer(HeaterFrame2.getTemperature_HeatExchg()); - showBTicon(); - showBatteryIcon(); - display.display();*/ + } CommState.set(CommStates::Idle); break; } // switch(CommState) + + BlueWireData.reset(); // ensure we flush any used data } // loop diff --git a/Arduino/BTCDieselHeater/OLEDconsts.h b/Arduino/BTCDieselHeater/OLEDconsts.h new file mode 100644 index 0000000..8b50ab6 --- /dev/null +++ b/Arduino/BTCDieselHeater/OLEDconsts.h @@ -0,0 +1,169 @@ + +// 'Thermometer', 8x50px +const unsigned char thermometerBitmap [] PROGMEM = { + 0x00, 0x18, 0x24, 0x24, 0x24, 0x24, 0x24, 0x26, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, + 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x26, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, + 0x24, 0x24, 0x24, 0x24, 0x24, 0x26, 0x24, 0x24, 0x24, 0x24, 0x3c, 0x7e, 0xff, 0xff, 0xff, 0xff, + 0x7e, 0x3c +}; + +// 'ThermoPtr', 3x5px +const unsigned char thermoPtr [] PROGMEM = { + 0x80, 0xc0, 0xe0, 0xc0, 0x80 +}; + +// 'Bluetooth icon', 6x11px +const unsigned char BTicon [] PROGMEM = { + 0x20, 0x30, 0x28, 0xa4, 0x68, 0x30, 0x68, 0xa4, 0x28, 0x30, 0x20, +}; + +// 'BatteryIcon', 15x10px +const unsigned char BatteryIcon [] PROGMEM = { + 0x30, 0x18, 0xff, 0xfe, 0x80, 0x02, 0xb6, 0xda, 0xb6, 0xda, 0xb6, 0xda, 0xb6, 0xda, 0xb6, 0xda, + 0x80, 0x02, 0xff, 0xfe +}; + +// 'GlowPlugIcon', 16x10px +const unsigned char GlowPlugIcon [] PROGMEM = { + 0x00, 0x00, 0xe3, 0x8e, 0x1c, 0x70, 0x28, 0x28, 0x24, 0x48, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, + 0x14, 0x50, 0x1c, 0x70 +}; + +// 'Fan3_1a', 16x16px +const unsigned char FanIcon1 [] PROGMEM = { + 0x03, 0xc0, 0x04, 0x20, 0x04, 0x20, 0x04, 0x20, 0x04, 0x20, 0x03, 0xc0, 0x07, 0xe0, 0x06, 0x60, + 0x7e, 0x7e, 0x87, 0xe1, 0x87, 0xe1, 0x84, 0x21, 0x84, 0x21, 0x78, 0x1e, 0x00, 0x00, 0x00, 0x00 +}; +// 'Fan3_2a', 16x16px +const unsigned char FanIcon2 [] PROGMEM = { + 0x00, 0x78, 0x00, 0x84, 0x00, 0x84, 0x00, 0x84, 0x00, 0x84, 0x7b, 0xf8, 0x87, 0xe0, 0x86, 0x60, + 0x86, 0x60, 0x87, 0xe0, 0x7b, 0xf8, 0x00, 0x84, 0x00, 0x84, 0x00, 0x84, 0x00, 0x84, 0x00, 0x78 +}; +// 'Fan3_3a', 16x16px +const unsigned char FanIcon3 [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x78, 0x1e, 0x84, 0x21, 0x84, 0x21, 0x87, 0xe1, 0x87, 0xe1, 0x7e, 0x7e, + 0x06, 0x60, 0x07, 0xe0, 0x03, 0xc0, 0x04, 0x20, 0x04, 0x20, 0x04, 0x20, 0x04, 0x20, 0x03, 0xc0 +}; +// 'Fan3_4a', 16x16px +const unsigned char FanIcon4 [] PROGMEM = { + 0x1e, 0x00, 0x21, 0x00, 0x21, 0x00, 0x21, 0x00, 0x21, 0x00, 0x1f, 0xde, 0x07, 0xe1, 0x06, 0x61, + 0x06, 0x61, 0x07, 0xe1, 0x1f, 0xde, 0x21, 0x00, 0x21, 0x00, 0x21, 0x00, 0x21, 0x00, 0x1e, 0x00 +}; + + +// 'FuelIcon', 7x12px +const unsigned char FuelIcon [] PROGMEM = { + 0x10, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0xfe, 0xfe, 0x7c, 0x38 +}; + +// 'Target', 13x13px +const unsigned char TargetIcon [] PROGMEM = { + 0x0f, 0x80, 0x10, 0x40, 0x20, 0x20, 0x47, 0x10, 0x88, 0x88, 0x92, 0x48, 0x97, 0x48, 0x92, 0x48, + 0x88, 0x88, 0x47, 0x10, 0x20, 0x20, 0x10, 0x40, 0x0f, 0x80 +}; + +// '0', 3x5px +const unsigned char Mini0 [] PROGMEM = { + 0xe0, 0xb0, 0xb0, 0xb0, 0xe0 +}; + +// '1', 3x5px +const unsigned char Mini1 [] PROGMEM = { + 0x40, 0xc0, 0x40, 0x40, 0x40 +}; + +// '2', 3x5px +const unsigned char Mini2 [] PROGMEM = { + 0xe0, 0x20, 0xe0, 0x80, 0xe0 +}; + +// '3', 3x5px +const unsigned char Mini3 [] PROGMEM = { + 0xe0, 0x20, 0x60, 0x20, 0xe0 +}; + +// '4', 3x5px +const unsigned char Mini4 [] PROGMEM = { + 0x80, 0xb0, 0xe0, 0x20, 0x20 +}; + +// '5', 3x5px +const unsigned char Mini5 [] PROGMEM = { + 0xe0, 0x80, 0xe0, 0x20, 0xe0 +}; + +// '6', 3x5px +const unsigned char Mini6 [] PROGMEM = { + 0xe0, 0x80, 0xe0, 0xb0, 0xe0 +}; + +// '7', 3x5px +const unsigned char Mini7 [] PROGMEM = { + 0xe0, 0x20, 0x20, 0x20, 0x20 +}; + +// '8', 3x5px +const unsigned char Mini8 [] PROGMEM = { + 0xe0, 0xb0, 0xe0, 0xb0, 0xe0 +}; + +// '9', 3x5px +const unsigned char Mini9 [] PROGMEM = { + 0xe0, 0xb0, 0xe0, 0x20, 0xe0 +}; + +// 'DP', 3x5px +const unsigned char MiniDP [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x40 +}; + +// 'Deg', 3x5px +const unsigned char MiniDeg [] PROGMEM = { + 0xc0, 0xc0, 0x00, 0x00, 0x00 +}; + +// 'C', 3x5px +const unsigned char MiniC [] PROGMEM = { + 0xe0, 0x80, 0x80, 0x80, 0xe0 +}; + +// 'H', 3x5px +const unsigned char MiniH [] PROGMEM = { + 0xb0, 0xb0, 0xe0, 0xb0, 0xb0 +}; + +// 'z', 3x5px +const unsigned char Miniz [] PROGMEM = { + 0x00, 0x00, 0xe0, 0x40, 0xe0 +}; + +// ' ', 3x5px +const unsigned char MiniSpc [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +const char* Runstates [] PROGMEM = { + " Stopped/Ready ", + "Starting...", + "Igniting...", + " Ignition retry ", + "Ignited", + "Running", + "Stopping", + "Shutdown", + "Cooling" +}; + +const char* Errstates [] PROGMEM = { + "Low voltage", // E-01 + "High voltage", // E-02 + "Glow plug fault", // E-03 + "Pump fault", // E-04 + "Overheat", // E-05 + "Motor fault", // E-06 + "Comms fault", // E-07 + "Flame out", // E-08 + "Temp sense", // E-09 + "Ignition fail", // E-10 + "Unknown error?" // mystery code! +}; diff --git a/Arduino/BTCDieselHeater/Protocol.h b/Arduino/BTCDieselHeater/Protocol.h index 9310905..ede8a98 100644 --- a/Arduino/BTCDieselHeater/Protocol.h +++ b/Arduino/BTCDieselHeater/Protocol.h @@ -158,6 +158,7 @@ public: unsigned char getTemperature_Max() const { return Controller.MaxTemperature; }; unsigned char getTemperature_Actual() const { return Controller.ActualTemperature; }; void setThermostatMode(unsigned on) { Controller.OperatingMode = on ? 0x32 : 0xCD; }; + bool isThermostat() const { return Controller.OperatingMode == 0x32; }; // glow plug short getGlowPlug_Current() const; // glow plug current short getGlowPlug_Voltage() const; // glow plug voltage diff --git a/Arduino/BTCDieselHeater/UtilClasses.h b/Arduino/BTCDieselHeater/UtilClasses.h index fe306b2..6ff23b6 100644 --- a/Arduino/BTCDieselHeater/UtilClasses.h +++ b/Arduino/BTCDieselHeater/UtilClasses.h @@ -30,8 +30,18 @@ public: } bool collectData(CProtocol& Frame, unsigned char val, int limit = 24) { // returns true when buffer filled Frame.Data[m_Count++] = val; - return m_Count == limit; + return m_Count >= limit; } + bool collectDataEx(CProtocol& Frame, unsigned char val, int limit = 24) { // returns true when buffer filled + // guarding against rogue rx kernel buffer stutters.... + if((m_Count == 0) && (val != 0x76)) { + Serial.println("First heater byte not 0x76 - SKIPPING"); + return false; + } + Frame.Data[m_Count++] = val; + return m_Count >= limit; + } + private: eCS m_State; int m_Count; diff --git a/Arduino/BTCDieselHeater/display.cpp b/Arduino/BTCDieselHeater/display.cpp new file mode 100644 index 0000000..d159d54 --- /dev/null +++ b/Arduino/BTCDieselHeater/display.cpp @@ -0,0 +1,319 @@ +#include +#include "AdaFruit_SH1106.h" +#include "protocol.h" +#include "display.h" +#include "pins.h" +#include "BluetoothAbstract.h" +#include "OLEDconsts.h" + +#define X_FANICON 60 //46 +#define Y_FANICON 39 +#define X_FUELICON 90 // 80 +#define Y_FUELICON 39 +#define X_TARGETICON 30 +#define Y_TARGETICON 39 +#define Y_BASELINE 57 + +// +// **** NOTE: There are two very lame libaries conspiring to make life difficult **** +// A/ The ESP32 SPI.cpp library instatiates an instance of SPI, using the VSPI port (and pins) +// B/ The Adfruit_SH1106 library has hard coded "SPI" as the SPI port instance +// +// As an ESP32 has a pin multiplexer, this is very bad form. +// The design uses the defacto HSPI pins (and port), +// You **MUST comment out the SPIClass SPI(VSPI);** at the end of the ESP32 SPI library +// then we declare "SPI" here, which will use HSPI!!!! + +// 128 x 64 OLED support +SPIClass SPI; // default constructor opens HSPI on pins +Adafruit_SH1106 display(LCDpin_DC, -1, LCDpin_CS); + +int prevPump; +int prevRPM; + + +extern float fFilteredTemperature; +extern CBluetoothAbstract& getBluetoothClient(); + +void showThermometer(float desired, float actual); +void showBodyThermometer(int actual); +void showBTicon(); +void showBatteryIcon(float voltage); +void showGlowPlug(int power); +void showFan(int RPM); +void showFuel(float rate); +void showRunState(int state, int errstate); +void printMiniNumericString(int xPos, int yPos, const char* str); + +void initOLED() +{ + SPI.setFrequency(8000000); + // SH1106_SWITCHCAPVCC = generate display voltage from 3.3V internally + display.begin(SH1106_SWITCHCAPVCC, 0, false); + + // Show initial display buffer contents on the screen -- + // the library initializes this with an Adafruit splash screen. + display.display(); +} + +void updateOLED(const CProtocol& CtlFrame, const CProtocol& HtrFrame) +{ + int runstate = HtrFrame.getRunState(); + int errstate = HtrFrame.getErrState(); + if(errstate) errstate--; // correct for +1 biased return value + + display.clearDisplay(); + + float desiredT = 0; + if(runstate) { + if(CtlFrame.isThermostat()) + desiredT = CtlFrame.getTemperature_Desired(); + else + desiredT = -HtrFrame.getPump_Fixed() * 0.1f; + } + + showThermometer(desiredT, // read values from most recently sent [BTC] frame + fFilteredTemperature); + + if(getBluetoothClient().isConnected()) + showBTicon(); + + float voltage = HtrFrame.getVoltage_Supply() * 0.1f; + showBatteryIcon(voltage); + + if(runstate) { + float power = HtrFrame.getGlowPlug_Current() * 0.01 * HtrFrame.getGlowPlug_Voltage() * 0.1; + if(power > 1) + showGlowPlug(int(power)); + + showFan(HtrFrame.getFan_Actual()); + + showFuel(HtrFrame.getPump_Actual() * 0.1f); + + showBodyThermometer(HtrFrame.getTemperature_HeatExchg()); + } + else { + prevRPM = 0; + prevPump = 0; + } + + showRunState(runstate, errstate); + +} + + +void animateOLED() +{ + static int fan = 0; + static int drip = 0; + + if(prevPump || prevRPM) { + + if(prevPump) { + // erase region of fuel icon + display.fillRect(X_FUELICON, Y_FUELICON, 7, 16, BLACK); + display.drawBitmap(X_FUELICON, Y_FUELICON+drip, FuelIcon, 7, 12, WHITE); + drip++; + drip &= 0x03; + } + + if(prevRPM) { + // erase region of fuel icon + display.fillRect(X_FANICON, Y_FANICON, 16, 16, BLACK); + switch(fan) { + case 0: display.drawBitmap(X_FANICON, Y_FANICON, FanIcon1, 16, 16, WHITE); break; + case 1: display.drawBitmap(X_FANICON, Y_FANICON, FanIcon2, 16, 16, WHITE); break; + case 2: display.drawBitmap(X_FANICON, Y_FANICON, FanIcon3, 16, 16, WHITE); break; + case 3: display.drawBitmap(X_FANICON, Y_FANICON, FanIcon4, 16, 16, WHITE); break; + } + fan++; + fan &= 0x03; + } + } + display.display(); +} + + +#define TEMP_YPOS(A) ((((20 - A) * 3) / 2) + 22) +#define BULB_X 1 // >= 1 +#define BULB_Y 4 +void showThermometer(float desired, float actual) +{ + display.clearDisplay(); + // draw bulb design + display.drawBitmap(BULB_X, BULB_Y, thermometerBitmap, 8, 50, WHITE); + // draw mercury + int yPos = BULB_Y + TEMP_YPOS(actual); + display.drawLine(BULB_X + 3, yPos, BULB_X + 3, BULB_Y + 42, WHITE); + display.drawLine(BULB_X + 4, yPos, BULB_X + 4, BULB_Y + 42, WHITE); + // print actual temperature + display.setTextColor(WHITE); + display.setCursor(0, Y_BASELINE); + display.print(actual, 1); + // draw set point + + if(desired) { + char msg[16]; + int desY = 19; + display.drawBitmap(X_TARGETICON, Y_TARGETICON, TargetIcon, 13, 13, WHITE); // set indicator against bulb + desY += 14; + if(desired > 0) { + int yPos = BULB_Y + TEMP_YPOS(desired) - 2; + display.drawBitmap(BULB_X-1, yPos, thermoPtr, 3, 5, WHITE); // set indicator against bulb + sprintf(msg, "%.0fC", desired); + } + else { + sprintf(msg, "%.1fHz", -desired); + } + int xPos = X_TARGETICON + 6 - ( strlen(msg) * 3); // 3 = 1/2 width font + display.setCursor(xPos, Y_BASELINE); + display.print(msg); + } +} + +#define BODYBULB_X 119 +#define BODY_YPOS(A) ((((100 - A) * 3) / 16) + 22) // 100degC centre - ticks +- 80C +void showBodyThermometer(int actual) +{ + // draw bulb design + display.drawBitmap(BODYBULB_X, BULB_Y, thermometerBitmap, 8, 50, WHITE); + // draw mercury + int yPos = BULB_Y + BODY_YPOS(actual); + display.drawLine(BODYBULB_X + 3, yPos, BODYBULB_X + 3, BULB_Y + 42, WHITE); + display.drawLine(BODYBULB_X + 4, yPos, BODYBULB_X + 4, BULB_Y + 42, WHITE); + // print actual temperature + display.setTextColor(WHITE); + char label[16]; + sprintf(label, "%d", actual); + // determine width and position right justified + int width = strlen(label) * 6; + display.setCursor(127-width, Y_BASELINE); + display.print(label); +} + +void showBTicon() +{ + display.drawBitmap(12, 0, BTicon, 6, 11, WHITE); +} + +void showBatteryIcon(float voltage) +{ + display.drawBitmap(95, 0, BatteryIcon, 15, 10, WHITE); + display.setCursor(85, 12); + display.setTextColor(WHITE); + display.print(voltage, 1); + display.print("V"); + + // nominal 10.5 -> 13.5V bargraph + int Capacity = int((voltage - 11.0) * 4.5); + if(Capacity < 0) Capacity = 0; + if(Capacity > 11) Capacity = 11; + display.fillRect(97 + Capacity, 2, 11-Capacity, 6, BLACK); +} + +#define XPOS_GLOW 35 +#define YPOS_GLOW 0 +void showGlowPlug(int power) +{ + display.drawBitmap(XPOS_GLOW, YPOS_GLOW, GlowPlugIcon, 16, 10, WHITE); + display.setCursor(XPOS_GLOW, YPOS_GLOW+12); + display.setTextColor(WHITE); + display.print(power); + display.print("W"); +} + +void showFan(int RPM) +{ + // NOTE: fan rotation animation performed in animateOLED + prevRPM = RPM; // used by animation routine + + display.setTextColor(WHITE); + char msg[16]; + sprintf(msg, "%d", RPM); + int xPos = X_FANICON + 8 - ( strlen(msg) * 3); // 3 = 1/2 width font + display.setCursor(xPos, Y_BASELINE); + display.print(msg); +} + +void showFuel(float rate) +{ + // NOTE: fuel drop animation performed in animateOLED + prevPump = rate > 0; // used by animation routine + if(rate) { + char msg[16]; + sprintf(msg, "%.1f", rate); + int xPos = X_FUELICON + 3 - ( strlen(msg) * 3); // 3 = 1/2 width font + display.setCursor(xPos, Y_BASELINE); + display.setTextColor(WHITE); + display.print(rate, 1); + } +} + +void showRunState(int runstate, int errstate) +{ + static bool toggle = false; + const char* toPrint = NULL; + int yPos = 25; + display.setTextColor(WHITE, BLACK); + if(runstate >= 0 && runstate <= 8) { + if(runstate == 0 && errstate) { + // create an "E-XX" message to display + char msg[16]; + sprintf(msg, "E-%02d", errstate); + int xPos = 64 - ((strlen(msg)/2) * 6); + display.setCursor(xPos, yPos); + yPos += 8; + // flash error code + toggle = !toggle; + if(toggle) + display.print(msg); + else { + display.print(" "); + } + // bounds limit error and gather message + if(errstate > 10) errstate = 11; + toPrint = Errstates[errstate-1]; + } + else { + toPrint = Runstates[runstate]; + } + } + if(toPrint) { + int width = strlen(toPrint); + int xPos = 64 - ((width/2) * 6); + display.setCursor(xPos, yPos); + display.print(toPrint); + } +} + + +void printMiniNumericString(int xPos, int yPos, const char* str) +{ + const char* pNext = str; + while(*pNext) { + const uint8_t* pBmp = NULL; + switch(*pNext) { + case '0': pBmp = Mini0; break; + case '1': pBmp = Mini1; break; + case '2': pBmp = Mini2; break; + case '3': pBmp = Mini3; break; + case '4': pBmp = Mini4; break; + case '5': pBmp = Mini5; break; + case '6': pBmp = Mini6; break; + case '7': pBmp = Mini7; break; + case '8': pBmp = Mini8; break; + case '9': pBmp = Mini9; break; + case '.': pBmp = MiniDP; break; + case '`': pBmp = MiniDeg; break; + case 'C': pBmp = MiniC; break; + case 'H': pBmp = MiniH; break; + case 'z': pBmp = Miniz; break; + case ' ': pBmp = MiniSpc; break; + } + if(pBmp) { + display.drawBitmap(xPos, yPos, pBmp, 3, 5, WHITE); + } + xPos += 4; + pNext++; + } +} \ No newline at end of file diff --git a/Arduino/BTCDieselHeater/display.h b/Arduino/BTCDieselHeater/display.h new file mode 100644 index 0000000..32accd9 --- /dev/null +++ b/Arduino/BTCDieselHeater/display.h @@ -0,0 +1,12 @@ +#ifndef __DISPLAY_H__ +#define __DISPLAY_H__ + +#include + +class CProtocol; + +void initOLED(); +void updateOLED(const CProtocol& CtlFrame, const CProtocol& HtrFrame); +void animateOLED(); + +#endif // __DISPLAY_H__ diff --git a/Arduino/BTCDieselHeater/issues/BlueWireRxSlippage.txt b/Arduino/BTCDieselHeater/issues/BlueWireRxSlippage.txt new file mode 100644 index 0000000..0b00ea8 --- /dev/null +++ b/Arduino/BTCDieselHeater/issues/BlueWireRxSlippage.txt @@ -0,0 +1,102 @@ + 1249177ms [BTC] 76 16 00 11 15 0E 28 05 DC 11 94 78 01 32 08 23 05 00 01 2C 0D AC 13 74 + +21ms [HTR] 8B 1D 76 16 00 00 00 84 00 00 00 84 00 2F 00 00 00 00 00 08 00 1A 64 00 verifyCRC FAILED: calc:E388 data:6400 +Bluetooth data not sent, CRC error + 1250198ms [BTC] 76 16 00 11 15 0E 28 05 DC 11 94 78 01 32 08 23 05 00 01 2C 0D AC 13 74 + +23ms [HTR] 8B 1D 76 16 00 00 00 84 00 00 00 84 00 2F 00 00 00 00 00 08 00 1A 64 00 verifyCRC FAILED: calc:E388 data:6400 +Bluetooth data not sent, CRC error + +*** SWITCHED INTO VERBOSE MODE **** + +:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 +:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 +:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 +:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 +:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 +:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5 +1251221ms [BTC] 76 16 00 11 15 0E 28 05 DC 11 94 78 01 32 08 23 05 00 01 2C 0D AC 13 74 :6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6 +dT{16}:6,RD(8B) +dT{1}:6,RD(1D) +dT{1}:6,RD(76) <<<<<<<<<<<<<<<<<<<<<<<<<< THIS SHOULD BE FIRST RX BYTE!!!! +dT{1}:6,RD(16) +dT{1}:6,RD(0) +dT{2}:6,RD(0) +dT{1}:6,RD(0) +dT{1}:6,RD(84) +dT{1}:6,RD(0) +dT{1}:6,RD(0) +dT{1}:6,RD(0) +dT{1}:6,RD(84) +dT{2}:6,RD(0) +dT{1}:6,RD(2F) +dT{1}:6,RD(0) +dT{1}:6,RD(0) +dT{1}:6,RD(0) +dT{1}:6,RD(0) +dT{2}:6,RD(0) +dT{1}:6,RD(8) +dT{1}:6,RD(0) +dT{1}:6,RD(1A) +dT{1}:6,RD(64) +dT{1}:6,RD(0) +:7 +48ms [HTR] 8B 1D 76 16 00 00 00 84 00 00 00 84 00 2F 00 00 00 00 00 08 00 1A 64 00 verifyCRC FAILED: calc:E388 data:6400 +Bluetooth data not sent, CRC error +:8 +:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 +:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 +:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 +:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 +:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 +:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 +:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 +:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 + +8< (lots of state :0 snipped) + +:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 +:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 +:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 +:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 +:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 +:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 +:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 +:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 +:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 +:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 +:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5 +:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5 +:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5:5 +:5:5:5:5:5:5:5:5:5:5 + 1252268ms [BTC] 76 16 00 11 15 0E 28 05 DC 11 94 78 01 32 08 23 05 00 01 2C 0D AC 13 74 +:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6:6 +dT{16}:6,RD(8B) +dT{1}:6,RD(1D) +dT{1}:6,RD(76) <<<<<<<<<<<<<<<<<<<<<<<<<< THIS SHOULD BE FIRST RX BYTE!!!! +dT{2}:6,RD(16) +dT{1}:6,RD(0) +dT{1}:6,RD(0) +dT{1}:6,RD(0) +dT{1}:6,RD(84) +dT{1}:6,RD(0) +dT{2}:6,RD(0) +dT{1}:6,RD(0) +dT{1}:6,RD(84) +dT{1}:6,RD(0) +dT{1}:6,RD(2F) +dT{1}:6,RD(0) +dT{2}:6,RD(0) +dT{1}:6,RD(0) +dT{1}:6,RD(0) +dT{1}:6,RD(0) +dT{1}:6,RD(8) +dT{1}:6,RD(0) +dT{1}:6,RD(1A) +dT{2}:6,RD(64) +dT{1}:6,RD(0):7 + +49ms [HTR] 8B 1D 76 16 00 00 00 84 00 00 00 84 00 2F 00 00 00 00 00 08 00 1A 64 00 verifyCRC FAILED: calc:E388 data:6400 +Bluetooth data not sent, CRC error +:8 +:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 +:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 +:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 +:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 +:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 diff --git a/Arduino/BTCDieselHeater/issues/lostsync.txt b/Arduino/BTCDieselHeater/issues/lostsync.txt new file mode 100644 index 0000000..60bf71c --- /dev/null +++ b/Arduino/BTCDieselHeater/issues/lostsync.txt @@ -0,0 +1,46 @@ + 1174404ms [BTC] 76 16 00 12 15 0E 28 05 DC 11 94 78 01 32 08 23 05 00 01 2C 0D AC 57 30 + +22ms [HTR] 76 16 00 00 00 85 00 00 00 85 00 2F 00 00 00 00 00 08 00 1A 64 00 9A 4E + 1175426ms [BTC] 76 16 00 12 15 0E 28 05 DC 11 94 78 01 32 08 23 05 00 01 2C 0D AC 57 30 + +22ms [HTR] 76 16 00 00 00 84 00 00 00 84 00 2F 00 00 00 00 00 08 00 1A 64 00 8B 1D + 1176448ms [BTC] 76 16 00 12 15 0E 28 05 DC 11 94 78 01 32 08 23 05 00 01 2C 0D AC 57 30 + +23ms [HTR] 76 16 00 00 00 84 00 00 00 84 00 2F 00 00 00 00 00 08 00 1A 64 00 8B 1D + 1177471ms [BTC] 76 16 00 12 15 0E 28 05 DC 11 94 78 01 32 08 23 05 00 01 2C 0D AC 57 30 + +22ms [HTR] 76 16 00 00 00 85 00 00 00 85 00 2F 00 00 00 00 00 08 00 1A 64 00 9A 4E + 1178493ms [BTC] 76 16 00 12 15 0E 28 05 DC 11 94 78 01 32 08 23 05 00 01 2C 0D AC 57 30 + +21ms [HTR] 76 16 00 00 00 85 00 00 00 85 00 2F 00 00 00 00 00 08 00 1A 64 00 9A 4E + 1179515ms [BTC] 76 16 00 12 15 0E 28 05 DC 11 94 78 01 32 08 23 05 00 01 2C 0D AC 57 30 + +22ms [HTR] 76 16 00 00 00 84 00 00 00 84 00 2F 00 00 00 00 00 08 00 1A 64 00 8B 1D + 1180537ms [BTC] 76 16 00 12 15 0E 28 05 DC 11 94 78 01 32 08 23 05 00 01 2C 0D AC 57 30 + +22ms [HTR] 76 16 00 00 00 85 00 00 00 85 00 2F 00 00 00 00 00 08 00 1A 64 00 9A 4E + 1181559ms [BTC] 76 16 00 12 15 0E 28 05 DC 11 94 78 01 32 08 23 05 00 01 2C 0D AC 57 30 + +23ms [HTR] 76 16 00 00 00 84 00 00 00 84 00 2F 00 00 00 00 00 08 00 1A 64 00 8B 1D + 1182583ms [BTC] 76 16 00 12 15 0E 28 05 DC 11 94 78 01 32 08 23 05 00 01 2C 0D AC 57 30 Timeout collecting BTC heater response data, returning to Idle State + 1183588ms [BTC] 76 16 00 12 15 0E 28 05 DC 11 94 78 01 32 08 23 05 00 01 2C 0D AC 57 30 + +22ms [HTR] 76 16 00 00 00 84 00 00 00 84 00 2F 00 00 00 00 00 08 00 1A 64 00 8B 1D + 1184611ms [BTC] 76 16 00 12 15 0E 28 05 DC 11 94 78 01 32 08 23 05 00 01 2C 0D AC 57 30 + +21ms [HTR] 76 16 00 00 00 85 00 00 00 85 00 2F 00 00 00 00 00 08 00 1A 64 00 9A 4E + 1185632ms [BTC] 76 16 00 12 15 0E 28 05 DC 11 94 78 01 32 08 23 05 00 01 2C 0D AC 57 30 + +22ms [HTR] 76 16 00 00 00 84 00 00 00 84 00 2F 00 00 00 00 00 08 00 1A 64 00 8B 1D + 1186654ms [BTC] 76 16 00 12 15 0E 28 05 DC 11 94 78 01 32 08 23 05 00 01 2C 0D AC 57 30 Timeout collecting BTC heater response data, returning to Idle State + 1187676ms [BTC] 76 16 00 12 15 0E 28 05 DC 11 94 78 01 32 08 23 05 00 01 2C 0D AC 57 30 + +22ms [HTR] 1D 76 16 00 00 00 84 00 00 00 84 00 2F 00 00 00 00 00 08 00 1A 64 00 8B verifyCRC FAILED: calc:58A8 data:8B +Bluetooth data not sent, CRC error + 1188699ms [BTC] 76 16 00 12 15 0E 28 05 DC 11 94 78 01 32 08 23 05 00 01 2C 0D AC 57 30 + +22ms [HTR] 1D 76 16 00 00 00 85 00 00 00 85 00 2F 00 00 00 00 00 08 00 1A 64 00 9A verifyCRC FAILED: calc:B94 data:9A +Bluetooth data not sent, CRC error + 1189721ms [BTC] 76 16 00 12 15 0E 28 05 DC 11 94 78 01 32 08 23 05 00 01 2C 0D AC 57 30 + +22ms [HTR] 4E 76 16 00 00 00 85 00 00 00 85 00 2F 00 00 00 00 00 08 00 1A 64 00 9A verifyCRC FAILED: calc:27B2 data:9A +Bluetooth data not sent, CRC error + 1190743ms [BTC] 76 16 00 12 15 0E 28 05 DC 11 94 78 01 32 08 23 05 00 01 2C 0D AC 57 30 + +22ms [HTR] 4E 76 16 00 00 00 84 00 00 00 84 00 2F 00 00 00 00 00 08 00 1A 64 00 8B verifyCRC FAILED: calc:748E data:8B +Bluetooth data not sent, CRC error + 1191765ms [BTC] 76 16 00 12 15 0E 28 05 DC 11 94 78 01 32 08 23 05 00 01 2C 0D AC 57 30 + +22ms [HTR] 1D 76 16 00 00 00 84 00 00 00 84 00 2F 00 00 00 00 00 08 00 1A 64 00 8B verifyCRC FAILED: calc:58A8 data:8B +Bluetooth data not sent, CRC error + 1192788ms [BTC] 76 16 00 12 15 0E 28 05 DC 11 94 78 01 32 08 23 05 00 01 2C 0D AC 57 30 + +21ms [HTR] 1D 76 16 00 00 00 85 00 00 00 85 00 2F 00 00 00 00 00 08 00 1A 64 00 9A verifyCRC FAILED: calc:B94 data:9A +Bluetooth data not sent, CRC error + 1193810ms [BTC] 76 16 00 12 15 0E 28 05 DC 11 94 78 01 32 08 23 05 00 01 2C 0D AC 57 30 + +23ms [HTR] 4E 76 16 00 00 00 84 00 00 00 84 00 2F 00 00 00 00 00 08 00 1A 64 00 8B verifyCRC FAILED: calc:748E data:8B +Bluetooth data not sent, CRC error + 1194833ms [BTC] 76 16 00 12 15 0E 28 05 DC 11 94 78 01 32 08 23 05 00 01 2C 0D AC 57 30 Timeout collecting BTC heater response data, returning to Idle State