diff --git a/Build-instructions-Bestway-WiFi-remote.pdf b/Build-instructions-Bestway-WiFi-remote.pdf index 1636fc4e..599b8d0d 100644 Binary files a/Build-instructions-Bestway-WiFi-remote.pdf and b/Build-instructions-Bestway-WiFi-remote.pdf differ diff --git a/Code/4-wire-version/data/WebSocket.js b/Code/4-wire-version/data/WebSocket.js index b02f01b9..ecbb93b6 100644 --- a/Code/4-wire-version/data/WebSocket.js +++ b/Code/4-wire-version/data/WebSocket.js @@ -99,10 +99,14 @@ function handlemsg(e) ] document.getElementById('mqtt').innerHTML = "MQTT: " + mqtt_states[msgobj.MQTT + 4]; - // hydro jets available - document.getElementById('jetstitle').style.display = (msgobj.HASJETS ? 'table-row' : 'none'); - document.getElementById('jetsbutton').style.display = (msgobj.HASJETS ? 'table-row' : 'none'); + + document.getElementById('jetstitle').style.display = (msgobj.HASJETS ? 'inherit' : 'none'); + document.getElementById('jetsbutton').style.display = (msgobj.HASJETS ? 'inherit' : 'none'); document.getElementById('jetstotals').style.display = (msgobj.HASJETS ? 'table-row' : 'none'); + + document.getElementById('airtitle').style.display = (msgobj.HASAIR ? 'inherit' : 'none'); + document.getElementById('airbutton').style.display = (msgobj.HASAIR ? 'inherit' : 'none'); + document.getElementById('airtotals').style.display = (msgobj.HASAIR ? 'table-row' : 'none'); document.getElementById('ciotx').innerHTML = 'CIO TX: ' + (msgobj.CIOTX ? 'Active' : 'Dead'); document.getElementById('dsptx').innerHTML = 'DSP TX: ' + (msgobj.DSPTX ? 'Active' : 'Dead'); diff --git a/Code/4-wire-version/data/config.html b/Code/4-wire-version/data/config.html index b4a016bd..98f7d4ab 100644 --- a/Code/4-wire-version/data/config.html +++ b/Code/4-wire-version/data/config.html @@ -80,10 +80,9 @@ - + @@ -129,7 +128,9 @@

Command queue

const rebootesp = 6; const gettarget = 7; const setjets = 11; -const commandlist = ["Set target", "Set unit", "Set bubbles", "Set heat", "Set pump", "Reset queue", "Reboot ESP", "Internal cmd", "Set jets"]; +const setgodmode = 12; +const commandlist = ["Set target", "Set unit", "Set bubbles", "Set heat", "Set pump", "Reset queue", "Reboot ESP", "Internal cmd", "resetTotals:", "resetTimerChlorine", "resetTimerFilter", "Set jets", "Take Control"]; + function loadconfig(){ const Http = new XMLHttpRequest(); @@ -195,7 +196,7 @@

Command queue

"FINT":parseInt(document.getElementById("finterval").value), "CLINT":parseInt(document.getElementById("clinterval").value), "AUDIO":document.getElementById("audio").checked//, - //"RESTORE":document.getElementById("restore").value + //"RESTORE":parseInt(document.getElementById("restore").value) } Http.send(JSON.stringify(msgobj)); console.log(msgobj); diff --git a/Code/4-wire-version/data/index.html b/Code/4-wire-version/data/index.html index 8b0e20f8..afe22577 100644 --- a/Code/4-wire-version/data/index.html +++ b/Code/4-wire-version/data/index.html @@ -67,12 +67,14 @@

Control:

- + - + diff --git a/Code/4-wire-version/lib/BWC4W/BWC_8266_4w.cpp b/Code/4-wire-version/lib/BWC4W/BWC_8266_4w.cpp index d9ee3a2c..1d446e17 100644 --- a/Code/4-wire-version/lib/BWC4W/BWC_8266_4w.cpp +++ b/Code/4-wire-version/lib/BWC4W/BWC_8266_4w.cpp @@ -31,11 +31,13 @@ void CIO::loop(void) { { msglen = cio_serial.readBytes(from_CIO_buf, PAYLOADSIZE); //copy from_CIO_buf -> to_DSP_buf - if(msglen == PAYLOADSIZE){ + if(msglen == PAYLOADSIZE) + { //discard message if checksum is wrong uint8_t calculatedChecksum; calculatedChecksum = from_CIO_buf[1]+from_CIO_buf[2]+from_CIO_buf[3]+from_CIO_buf[4]; - if(from_CIO_buf[CIO_CHECKSUMINDEX] == calculatedChecksum){ + if(from_CIO_buf[CIO_CHECKSUMINDEX] == calculatedChecksum) + { for(int i = 0; i < PAYLOADSIZE; i++){ if(to_DSP_buf[i] != from_CIO_buf[i]) dataAvailable = true; to_DSP_buf[i] = from_CIO_buf[i]; @@ -48,7 +50,8 @@ void CIO::loop(void) { states[CHAR1] = ' '; states[CHAR2] = ' '; states[CHAR3] = ' '; - if(states[ERROR]){ + if(states[ERROR]) + { to_CIO_buf[COMMANDINDEX] = 0; //clear any commands GODMODE = false; states[CHAR1] = 'E'; @@ -83,7 +86,8 @@ void CIO::loop(void) { { msglen = dsp_serial.readBytes(from_DSP_buf, PAYLOADSIZE); //copy from_DSP_buf -> to_CIO_buf - if(msglen == PAYLOADSIZE){ + if(msglen == PAYLOADSIZE) + { //discard message if checksum is wrong uint8_t calculatedChecksum; calculatedChecksum = from_DSP_buf[1]+from_DSP_buf[2]+from_DSP_buf[3]+from_DSP_buf[4]; diff --git a/Code/4-wire-version/lib/BWC4W/BWC_8266_4w_globals.h b/Code/4-wire-version/lib/BWC4W/BWC_8266_4w_globals.h index fa3fa0d8..9cdd6397 100644 --- a/Code/4-wire-version/lib/BWC4W/BWC_8266_4w_globals.h +++ b/Code/4-wire-version/lib/BWC4W/BWC_8266_4w_globals.h @@ -255,4 +255,52 @@ const bool HASJETS = false; const String MYMODEL = "NO54154"; #endif +#ifdef NO54144 + +//what row in allowedstates to go to when pressing Bubbles, Jets, Pump, Heat (columns in that order) +//Example: We are in state zero (first row). If we press Bubbles (first column) then there is a 6 +//meaning current state (row) is now 6. According to ALLOWEDSTATES table, we turn on Bubbles and keep +//everything else off. (1,0,0,0) +const uint8_t JUMPTABLE[][4] = { +// b,j,p,h + {1,2,3,4}, + {0,2,3,4}, + {1,0,3,4}, + {1,2,0,4}, + {1,2,0,3} +}; +//Bubbles, Jets, Pump, Heat +const uint8_t ALLOWEDSTATES[][4] = { + {0,0,0,0}, + {1,0,0,0}, + {0,1,0,0}, + {0,0,1,0}, + {0,0,1,2} //the "2" means both heater elements +}; + +//cio +const uint8_t TEMPINDEX = 2; +const uint8_t ERRORINDEX = 3; +const uint8_t CIO_CHECKSUMINDEX = 5; +//dsp +const uint8_t COMMANDINDEX = 2; +const uint8_t DSP_CHECKSUMINDEX = 5; + +const uint8_t PAYLOADSIZE = 7; + +const uint8_t PUMPBITMASK = B00000101; //5 +const uint8_t BUBBLESBITMASK = B00000010; //2 +const uint8_t JETSBITMASK = B00001000; //8 +// const uint8_t HEATBITMASK1 = B00000000; //0 heater stage 1 = off +// const uint8_t HEATBITMASK2 = B00110000; //48 heater stage 2 = on +//lines below should be tested. It would be consistent with 54173 model. +//If heating is slow this is probably the cause but I don't want to change it before someone tests it. +const uint8_t HEATBITMASK1 = B00110000; //48 heater stage 1 = 50% +const uint8_t HEATBITMASK2 = B01000000; //64 heater stage 2 = 100% +const uint8_t POWERBITMASK = B10000000; //128 +const bool HASJETS = true; +const bool HASAIR = false; +const String MYMODEL = "NO54144"; +#endif + #endif \ No newline at end of file diff --git a/Code/4-wire-version/lib/BWC4W/model.h b/Code/4-wire-version/lib/BWC4W/model.h index ffd99a54..11daf852 100644 --- a/Code/4-wire-version/lib/BWC4W/model.h +++ b/Code/4-wire-version/lib/BWC4W/model.h @@ -4,11 +4,16 @@ //#define NO54173 //this is same as 54138 but can run heater on 50% when bubbles are on. //#define NO54123 //not tested. ref https://github.com/mrQ000/layz-rc link to presentation slides //#define NO54154 //no jets. Works for Palm Springs 54129 +//#define NO54144 //with jets, but no bubbles. Palm Springs 54144 //WARNING: DEVICES HAVE DIFFERENT PINOUTS!!! CHECK BEFORE USING //The 54123 should work also for 54112 judging from a comment from @jenswalit in the forum //If using/testing the new PCB choose PCB_V2 -#define PCB_V1 -//#define PCB_V2 //The PCB with rounded corners \ No newline at end of file +//#define PCB_V1 +#define PCB_V2 //The PCB with rounded corners + +#ifdef PCB_V2 +#warning "USING PINOUT FOR PCB V2. EDIT lib/BWC/model.h IF USING OTHER PCB" +#endif \ No newline at end of file diff --git a/Code/4-wire-version/platformio.ini b/Code/4-wire-version/platformio.ini index f8a9c840..cbe243bd 100644 --- a/Code/4-wire-version/platformio.ini +++ b/Code/4-wire-version/platformio.ini @@ -23,12 +23,14 @@ lib_deps = knolleary/PubSubClient@^2.8 tzapu/WiFiManager@^0.16 plerup/EspSoftwareSerial@^6.15.2 + me-no-dev/ESPAsyncTCP board_build.filesystem = littlefs monitor_speed = 115200 ;Set upload speed to 115200 if you get transfer errors ;upload_speed = 921600 ;board_build.f_cpu = 160000000L - +build_flags = + -DWEBSOCKETS_NETWORK_TYPE="NETWORK_ESP8266_ASYNC" ; Uncomment the lines below by removing semicolons and edit IP for OTA upload. ; You have to upload via USB cable the first time. (upload_protocol = esptool) ; Make sure to use a data-USB cable. There is power only cables that wont work. diff --git a/Code/4-wire-version/src/config.h b/Code/4-wire-version/src/config.h index 2163f78a..6ab63646 100644 --- a/Code/4-wire-version/src/config.h +++ b/Code/4-wire-version/src/config.h @@ -2,7 +2,7 @@ #include #define LEGACY_NAME "layzspa" -#define FW_VERSION "4W_2022-05-20" +#define FW_VERSION "4W_2022-06-27" /* * Miscellaneous diff --git a/Code/4-wire-version/src/main.cpp b/Code/4-wire-version/src/main.cpp index 5477f3f4..8fa80a0f 100644 --- a/Code/4-wire-version/src/main.cpp +++ b/Code/4-wire-version/src/main.cpp @@ -47,6 +47,17 @@ void setup() void loop() { + // calc looptime + static bool firstloopdone = false; + unsigned long ms = millis(); + unsigned long looptime; + if(ms>prevlooptime && firstloopdone){ + looptime = ms - prevlooptime; + looptime_min = min(looptime, looptime_min); + looptime_max = max(looptime, looptime_max); + } + prevlooptime = ms; + firstloopdone = true; // We need this self-destructing info several times, so save it locally bool newData = bwc.newData(); // Fiddle with the pump computer @@ -125,9 +136,6 @@ void loop() } if (WiFi.status() == WL_CONNECTED) { - // could be interesting to display the IP - //bwc.print(WiFi.localIP().toString()); - if (!DateTime.isTimeValid()) { Serial.println(F("NTP > Start synchronisation")); @@ -208,14 +216,18 @@ String getOtherInfo() String json = ""; // Set the values in the document doc["CONTENT"] = "OTHER"; + doc["MQTT"] = mqttClient.state(); doc["CIOTX"] = bwc.cio_tx_ok; doc["DSPTX"] = bwc.dsp_tx_ok; doc["HASJETS"] = HASJETS; + doc["HASAIR"] = HASAIR; doc["RSSI"] = WiFi.RSSI(); doc["IP"] = WiFi.localIP().toString(); doc["SSID"] = WiFi.SSID(); doc["FW"] = FW_VERSION; doc["MODEL"] = MYMODEL; + doc["LOOPMAX"] = looptime_max; + doc["LOOPMIN"] = looptime_min; // Serialize JSON to string if (serializeJson(doc, json) == 0) @@ -258,6 +270,18 @@ void sendMQTT() { //Serial.println(F("MQTT > times not published")); } + + + //send other info + json = getOtherInfo(); + if (mqttClient.publish((String(mqttBaseTopic) + "/other").c_str(), String(json).c_str(), true)) + { + //Serial.println(F("MQTT > other published")); + } + else + { + //Serial.println(F("MQTT > other not published")); + } } @@ -1016,10 +1040,10 @@ void handleDir() while (root.next()) { Serial.println(root.fileName()); - mydir += root.fileName() + F(" \t Size: "); - mydir += String(root.fileSize()) + F(" Bytes\r\n"); + mydir += "" + root.fileName() + "" + F(" \t Size: "); + mydir += String(root.fileSize()) + F(" Bytes
"); } - server.send(200, "text/plain", mydir); + server.send(200, "text/html", mydir); } /** @@ -1244,6 +1268,12 @@ void mqttConnect() // Watch the 'command' topic for incoming MQTT messages mqttClient.subscribe((String(mqttBaseTopic) + "/command").c_str()); mqttClient.loop(); + + mqttClient.publish((String(mqttBaseTopic) + "/reboot_time").c_str(), DateTime.format(DateFormatter::ISO8601).c_str(), true); + mqttClient.publish((String(mqttBaseTopic) + "/reboot_reason").c_str(), ESP.getResetReason().c_str(), true); + mqttClient.loop(); + sendMQTT(); + //setupHA(); //Setup MQTT Device } else { diff --git a/Code/4-wire-version/src/main.h b/Code/4-wire-version/src/main.h index df5c7664..170828cf 100644 --- a/Code/4-wire-version/src/main.h +++ b/Code/4-wire-version/src/main.h @@ -61,7 +61,8 @@ bool runonce = true; const int solarpin = D0; /** pulled to GND. Boot fails if pulled HIGH. */ const int myoutputpin = D8; - +/** track loop times */ +unsigned long prevlooptime, looptime_max, looptime_min; void handleAUX(); diff --git a/Code/6-wire-version/data/config.html b/Code/6-wire-version/data/config.html index cd334797..73f9ed43 100644 --- a/Code/6-wire-version/data/config.html +++ b/Code/6-wire-version/data/config.html @@ -205,7 +205,7 @@

Command queue

"FINT":parseInt(document.getElementById("finterval").value), "CLINT":parseInt(document.getElementById("clinterval").value), "AUDIO":document.getElementById("audio").checked, - "RESTORE":document.getElementById("restore").value + "RESTORE":parseInt(document.getElementById("restore").value) } Http.send(JSON.stringify(msgobj)); console.log(msgobj); diff --git a/Code/6-wire-version/lib/BWC/BWC_6w_type1.cpp b/Code/6-wire-version/lib/BWC/BWC_6w_type1.cpp index 009aa2a0..abdd21ca 100644 --- a/Code/6-wire-version/lib/BWC/BWC_6w_type1.cpp +++ b/Code/6-wire-version/lib/BWC/BWC_6w_type1.cpp @@ -30,78 +30,90 @@ void CIO::stop(){ void CIO::loop(void) { //newdata is true when a data packet has arrived from cio - if(newData) { + if(!newData) return; + if(_packet_error) + { + _packet_error = false; newData = false; - static int capturePhase = 0; - static uint32_t buttonReleaseTime; + return; + } + newData = false; + static uint32_t buttonReleaseTime; + enum Readmode: int {readtemperature, uncertain, readtarget}; + static int capturePhase = readtemperature; - //capture TARGET after UP/DOWN has been pressed... - if ((button == ButtonCodes[UP]) || (button == ButtonCodes[DOWN])) - { - buttonReleaseTime = millis(); - capturePhase = 1; - } - //require two consecutive messages to be equal before registering - static uint8_t prev_checksum = 0; - uint8_t checksum = 0; - for(int i = 0; i < 11; i++){ - checksum += _payload[i]; - } - if(checksum != prev_checksum) { - prev_checksum = checksum; - return; - } - - //copy private array to public array - for(unsigned int i = 0; i < sizeof(payload); i++){ - payload[i] = _payload[i]; - } + //capture TARGET after UP/DOWN has been pressed... + if ((button == ButtonCodes[UP]) || (button == ButtonCodes[DOWN])) + { + buttonReleaseTime = millis(); //updated as long as buttons are pressed + capturePhase = readtarget; + } + //require two consecutive messages to be equal before registering + static uint8_t prev_checksum = 0; + uint8_t checksum = 0; + for(int i = 0; i < 11; i++){ + checksum += _payload[i]; + } + if(checksum != prev_checksum) { + prev_checksum = checksum; + return; + } + + //copy private array to public array + for(unsigned int i = 0; i < sizeof(payload); i++){ + payload[i] = _payload[i]; + } - //determine if anything changed, so we can update webclients - for(unsigned int i = 0; i < sizeof(payload); i++){ - if (payload[i] != _prevPayload[i]) dataAvailable = true; - _prevPayload[i] = payload[i]; - } + //determine if anything changed, so we can update webclients + for(unsigned int i = 0; i < sizeof(payload); i++){ + if (payload[i] != _prevPayload[i]) dataAvailable = true; + _prevPayload[i] = payload[i]; + } - brightness = _brightness & 7; //extract only the brightness bits (0-7) - //extract information from payload to a better format - states[LOCKEDSTATE] = (payload[LCK_IDX] & (1 << LCK_BIT)) > 0; - states[POWERSTATE] = (payload[PWR_IDX] & (1 << PWR_BIT)) > 0; - states[UNITSTATE] = (payload[C_IDX] & (1 << C_BIT)) > 0; - states[BUBBLESSTATE] = (payload[AIR_IDX] & (1 << AIR_BIT)) > 0; - states[HEATGRNSTATE] = (payload[GRNHTR_IDX] & (1 << GRNHTR_BIT)) > 0; - states[HEATREDSTATE] = (payload[REDHTR_IDX] & (1 << REDHTR_BIT)) > 0; - states[HEATSTATE] = states[HEATGRNSTATE] || states[HEATREDSTATE]; - states[PUMPSTATE] = (payload[FLT_IDX] & (1 << FLT_BIT)) > 0; - states[CHAR1] = (uint8_t)_getChar(payload[DGT1_IDX]); - states[CHAR2] = (uint8_t)_getChar(payload[DGT2_IDX]); - states[CHAR3] = (uint8_t)_getChar(payload[DGT3_IDX]); - if(HASJETS) states[JETSSTATE] = (payload[HJT_IDX] & (1 << HJT_BIT)) > 0; - else states[JETSSTATE] = 0; - //Determine if display is showing target temp or actual temp or anything else. - //...until 4 seconds after UP/DOWN released - if((millis()-buttonReleaseTime) > 2000) capturePhase = 0; - //convert text on display to a value if the chars are recognized - if(states[CHAR1] == '*' || states[CHAR2] == '*' || states[CHAR3] == '*') return; - String tempstring = String((char)states[CHAR1])+String((char)states[CHAR2])+String((char)states[CHAR3]); - uint8_t tmpTemp = tempstring.toInt(); - //capture only if showing plausible values (not blank screen while blinking) - if( (capturePhase == 1) && (tmpTemp > 19) ) { - states[TARGET] = tmpTemp; - } - //wait 6 seconds after UP/DOWN is released to be sure that actual temp is shown - if( (capturePhase == 0) && (states[CHAR3]!='H') && (states[CHAR3]!=' ') && ((millis()-buttonReleaseTime) > 6000) ) - { - if(states[TEMPERATURE] != tmpTemp) dataAvailable = true; - states[TEMPERATURE] = tmpTemp; - } + brightness = _brightness & 7; //extract only the brightness bits (0-7) + //extract information from payload to a better format + states[LOCKEDSTATE] = (payload[LCK_IDX] & (1 << LCK_BIT)) > 0; + states[POWERSTATE] = (payload[PWR_IDX] & (1 << PWR_BIT)) > 0; + states[UNITSTATE] = (payload[C_IDX] & (1 << C_BIT)) > 0; + states[BUBBLESSTATE] = (payload[AIR_IDX] & (1 << AIR_BIT)) > 0; + states[HEATGRNSTATE] = (payload[GRNHTR_IDX] & (1 << GRNHTR_BIT)) > 0; + states[HEATREDSTATE] = (payload[REDHTR_IDX] & (1 << REDHTR_BIT)) > 0; + states[HEATSTATE] = states[HEATGRNSTATE] || states[HEATREDSTATE]; + states[PUMPSTATE] = (payload[FLT_IDX] & (1 << FLT_BIT)) > 0; + states[CHAR1] = (uint8_t)_getChar(payload[DGT1_IDX]); + states[CHAR2] = (uint8_t)_getChar(payload[DGT2_IDX]); + states[CHAR3] = (uint8_t)_getChar(payload[DGT3_IDX]); + if(HASJETS) states[JETSSTATE] = (payload[HJT_IDX] & (1 << HJT_BIT)) > 0; + else states[JETSSTATE] = 0; + //Determine if display is showing target temp or actual temp or anything else. + //Unreadable characters - exit + if(states[CHAR1] == '*' || states[CHAR2] == '*' || states[CHAR3] == '*') return; + //Error or user plays with timer button - exit (error notification can be dealt with in main.cpp or elsewhere) + if(states[CHAR1] == 'E' || states[CHAR3] == 'H' || states[CHAR3] == ' ') return; + + //Stop expecting target temp after timeout + if((millis()-buttonReleaseTime) > 2000) capturePhase = uncertain; + if((millis()-buttonReleaseTime) > 6000) capturePhase = readtemperature; + //convert text on display to a value if the chars are recognized + String tempstring = String((char)states[CHAR1])+String((char)states[CHAR2])+String((char)states[CHAR3]); + uint8_t parsedValue = tempstring.toInt(); + //capture target temperature only if showing plausible values (not blank screen while blinking) + if( (capturePhase == readtarget) && (parsedValue > 19) ) { + states[TARGET] = parsedValue; + } + //wait 6 seconds after UP/DOWN is released to be sure that actual temp is shown + if(capturePhase == readtemperature) + { + if(states[TEMPERATURE] != parsedValue) dataAvailable = true; + states[TEMPERATURE] = parsedValue; + } - if(states[UNITSTATE] != _prevUNT || states[HEATSTATE] != _prevHTR || states[PUMPSTATE] != _prevFLT) { - stateChanged = true; - _prevUNT = states[UNITSTATE]; - _prevHTR = states[HEATSTATE]; - _prevFLT = states[PUMPSTATE]; - } + //If any of these states changes, we need to set a flag to save states. Used to restore them after reboot. + if(states[UNITSTATE] != _prevUNT || states[HEATSTATE] != _prevHTR || states[PUMPSTATE] != _prevFLT) { + stateChanged = true; + _prevUNT = states[UNITSTATE]; + _prevHTR = states[HEATSTATE]; + _prevFLT = states[PUMPSTATE]; } } @@ -110,6 +122,8 @@ void IRAM_ATTR CIO::eopHandler(void) { //process latest data and enter corresponding mode (like listen for DSP_STS or send BTN_OUT) //pinMode(_DATA_PIN, INPUT); WRITE_PERI_REG( PIN_DIR_INPUT, 1 << _DATA_PIN); + if(_byteCount != 11 && _byteCount != 0) _packet_error = true; + if(_bitCount != 0) _packet_error = true; _byteCount = 0; _bitCount = 0; uint8_t msg = _receivedByte; @@ -141,6 +155,7 @@ void IRAM_ATTR CIO::eopHandler(void) { //CIO comm //packet start //arduino core 3.0.1+ should work with digitalWrite() now. +//CS line toggles void IRAM_ATTR CIO::packetHandler(void) { if (!(READ_PERI_REG(PIN_IN) & (1 << _CS_PIN))) { //packet start @@ -156,6 +171,7 @@ void IRAM_ATTR CIO::packetHandler(void) { //CIO comm //Read incoming bits, and take action after a complete byte +//CLK line toggles void IRAM_ATTR CIO::clkHandler(void) { //sanity check on clock signal static uint32_t prev_us = 0; @@ -195,11 +211,22 @@ void IRAM_ATTR CIO::clkHandler(void) { _bitCount++; if (_bitCount == 8) { _bitCount = 0; - if (_CIO_cmd_matches == 2) { //meaning we have received the header for 11 data bytes to come - _payload[_byteCount] = _receivedByte; - _byteCount++; + //We have received the header for 11 data bytes to come + if (_CIO_cmd_matches == 2) + { + if(_byteCount < 11) + { + _payload[_byteCount] = _receivedByte; + _byteCount++; + } + else + { + _packet_error = true; + } } - else if (_receivedByte == DSP_CMD2_DATAREAD) { + //We have received request for button pressed + else if (_receivedByte == DSP_CMD2_DATAREAD) + { _sendBit = 8; _dataIsOutput = true; //pinMode(_DATA_PIN, OUTPUT); diff --git a/Code/6-wire-version/lib/BWC/BWC_6w_type1.h b/Code/6-wire-version/lib/BWC/BWC_6w_type1.h index 9db34c57..f4b57bb7 100644 --- a/Code/6-wire-version/lib/BWC/BWC_6w_type1.h +++ b/Code/6-wire-version/lib/BWC/BWC_6w_type1.h @@ -128,6 +128,7 @@ class CIO { bool _prevUNT; bool _prevHTR; bool _prevFLT; + volatile bool _packet_error = false; char _getChar(uint8_t value); }; diff --git a/Code/6-wire-version/lib/BWC/BWC_6w_type2.cpp b/Code/6-wire-version/lib/BWC/BWC_6w_type2.cpp index ec0ceebc..857d8f6b 100644 --- a/Code/6-wire-version/lib/BWC/BWC_6w_type2.cpp +++ b/Code/6-wire-version/lib/BWC/BWC_6w_type2.cpp @@ -31,85 +31,80 @@ void CIO::stop(){ void CIO::loop(void) { //newdata is true when a data packet has arrived from cio - if(newData) { - newData = false; - static int capturePhase = 0; - static uint32_t buttonReleaseTime; - - //capture TARGET after UP/DOWN has been pressed... - if ((button == ButtonCodes[UP]) || (button == ButtonCodes[DOWN])) - { - buttonReleaseTime = millis(); - capturePhase = 1; - } - - /* - * This model is only sending messages when something updated - * so this section is not useful - */ - /* - //require two consecutive messages to be equal before registering - static uint8_t prev_checksum = 0; - uint8_t checksum = 0; - for(unsigned int i = 0; i < sizeof(payload); i++){ - checksum += _payload[i]; - } - if(checksum != prev_checksum) { - prev_checksum = checksum; - return; - } - */ - - //copy private array to public array - for(unsigned int i = 0; i < sizeof(payload); i++){ - payload[i] = _payload[i]; - } + if(!newData) return; + newData = false; + static uint32_t buttonReleaseTime; + enum Readmode: int {readtemperature, uncertain, readtarget}; + static int capturePhase = readtemperature; + + //capture TARGET after UP/DOWN has been pressed... + if ((button == ButtonCodes[UP]) || (button == ButtonCodes[DOWN])) + { + buttonReleaseTime = millis(); //updated as long as buttons are pressed + capturePhase = readtarget; + } + + /* + * This model is only sending messages when something updated + * so this section is not useful + */ + //require two consecutive messages to be equal before registering + + //copy private array to public array + for(unsigned int i = 0; i < sizeof(payload); i++){ + payload[i] = _payload[i]; + } - //determine if anything changed, so we can update webclients - for(unsigned int i = 0; i < sizeof(payload); i++){ - if (payload[i] != _prevPayload[i]) dataAvailable = true; - _prevPayload[i] = payload[i]; - } + //determine if anything changed, so we can update webclients + for(unsigned int i = 0; i < sizeof(payload); i++){ + if (payload[i] != _prevPayload[i]) dataAvailable = true; + _prevPayload[i] = payload[i]; + } - //brightness = _brightness & 7; //extract only the brightness bits (0-7) - //extract information from payload to a better format - states[LOCKEDSTATE] = (payload[LCK_IDX] & (1 << LCK_BIT)) > 0; - states[POWERSTATE] = 1; //(payload[PWR_IDX] & (1 << PWR_BIT)) > 0; - states[UNITSTATE] = (payload[C_IDX] & (1 << C_BIT)) > 0; - states[BUBBLESSTATE] = (payload[AIR_IDX] & (1 << AIR_BIT)) > 0; - states[HEATGRNSTATE] = (payload[GRNHTR_IDX] & (1 << GRNHTR_BIT)) > 0; - states[HEATREDSTATE] = (payload[REDHTR_IDX] & (1 << REDHTR_BIT)) > 0; - states[HEATSTATE] = states[HEATGRNSTATE] || states[HEATREDSTATE]; - states[PUMPSTATE] = (payload[FLT_IDX] & (1 << FLT_BIT)) > 0; - states[CHAR1] = (uint8_t)_getChar(payload[DGT1_IDX]); - states[CHAR2] = (uint8_t)_getChar(payload[DGT2_IDX]); - states[CHAR3] = (uint8_t)_getChar(payload[DGT3_IDX]); - if(HASJETS) states[JETSSTATE] = (payload[HJT_IDX] & (1 << HJT_BIT)) > 0; - else states[JETSSTATE] = 0; - //Determine if display is showing target temp or actual temp or anything else. - //...until 4 seconds after UP/DOWN released - if((millis()-buttonReleaseTime) > 2000) capturePhase = 0; - //convert text on display to a value if the chars are recognized - if(states[CHAR1] == '*' || states[CHAR2] == '*' || states[CHAR3] == '*') return; - String tempstring = String((char)states[CHAR1])+String((char)states[CHAR2])+String((char)states[CHAR3]); - uint8_t tmpTemp = tempstring.toInt(); - //capture only if showing plausible values (not blank screen while blinking) - if( (capturePhase == 1) && (tmpTemp > 19) ) { - states[TARGET] = tmpTemp; - } - //wait 6 seconds after UP/DOWN is released to be sure that actual temp is shown - if( (capturePhase == 0) && (states[CHAR3]!='H') && (states[CHAR3]!=' ') && ((millis()-buttonReleaseTime) > 6000) ) - { - if(states[TEMPERATURE] != tmpTemp) dataAvailable = true; - states[TEMPERATURE] = tmpTemp; - } + //brightness = _brightness & 7; //extract only the brightness bits (0-7) + //extract information from payload to a better format + states[LOCKEDSTATE] = (payload[LCK_IDX] & (1 << LCK_BIT)) > 0; + states[POWERSTATE] = 1; //(payload[PWR_IDX] & (1 << PWR_BIT)) > 0; + states[UNITSTATE] = (payload[C_IDX] & (1 << C_BIT)) > 0; + states[BUBBLESSTATE] = (payload[AIR_IDX] & (1 << AIR_BIT)) > 0; + states[HEATGRNSTATE] = (payload[GRNHTR_IDX] & (1 << GRNHTR_BIT)) > 0; + states[HEATREDSTATE] = (payload[REDHTR_IDX] & (1 << REDHTR_BIT)) > 0; + states[HEATSTATE] = states[HEATGRNSTATE] || states[HEATREDSTATE]; + states[PUMPSTATE] = (payload[FLT_IDX] & (1 << FLT_BIT)) > 0; + states[CHAR1] = (uint8_t)_getChar(payload[DGT1_IDX]); + states[CHAR2] = (uint8_t)_getChar(payload[DGT2_IDX]); + states[CHAR3] = (uint8_t)_getChar(payload[DGT3_IDX]); + if(HASJETS) states[JETSSTATE] = (payload[HJT_IDX] & (1 << HJT_BIT)) > 0; + else states[JETSSTATE] = 0; + //Determine if display is showing target temp or actual temp or anything else. + //Unreadable characters - exit + if(states[CHAR1] == '*' || states[CHAR2] == '*' || states[CHAR3] == '*') return; + //Error or user plays with timer button - exit (error notification can be dealt with in main.cpp or elsewhere) + if(states[CHAR1] == 'E' || states[CHAR3] == 'H' || states[CHAR3] == ' ') return; + + //Stop expecting target temp after timeout + if((millis()-buttonReleaseTime) > 2000) capturePhase = uncertain; + if((millis()-buttonReleaseTime) > 6000) capturePhase = readtemperature; + //convert text on display to a value if the chars are recognized + String tempstring = String((char)states[CHAR1])+String((char)states[CHAR2])+String((char)states[CHAR3]); + uint8_t parsedValue = tempstring.toInt(); + //capture target temperature only if showing plausible values (not blank screen while blinking) + if( (capturePhase == readtarget) && (parsedValue > 19) ) { + states[TARGET] = parsedValue; + } + //wait 6 seconds after UP/DOWN is released to be sure that actual temp is shown + if(capturePhase == readtemperature) + { + if(states[TEMPERATURE] != parsedValue) dataAvailable = true; + states[TEMPERATURE] = parsedValue; + } - if(states[UNITSTATE] != _prevUNT || states[HEATSTATE] != _prevHTR || states[PUMPSTATE] != _prevFLT) { - stateChanged = true; - _prevUNT = states[UNITSTATE]; - _prevHTR = states[HEATSTATE]; - _prevFLT = states[PUMPSTATE]; - } + //If any of these states changes, we need to set a flag to save states. Used to restore them after reboot. + if(states[UNITSTATE] != _prevUNT || states[HEATSTATE] != _prevHTR || states[PUMPSTATE] != _prevFLT) { + stateChanged = true; + _prevUNT = states[UNITSTATE]; + _prevHTR = states[HEATSTATE]; + _prevFLT = states[PUMPSTATE]; } } diff --git a/Code/6-wire-version/lib/BWC/BWC_common.cpp b/Code/6-wire-version/lib/BWC/BWC_common.cpp index 2956221c..7d50ddda 100644 --- a/Code/6-wire-version/lib/BWC/BWC_common.cpp +++ b/Code/6-wire-version/lib/BWC/BWC_common.cpp @@ -101,7 +101,6 @@ void BWC::loop(){ //queue overrides real buttons _handleButtonQ(); if(_saveEventlogNeeded) saveEventlog(); - if(_saveCmdqNeeded) _saveCommandQueue(); if(_saveSettingsNeeded) saveSettings(); if(_cio.stateChanged) { _saveStatesNeeded = true; @@ -183,7 +182,6 @@ void BWC::_handleButtonQ(void) { //check if state is as desired, or duration is up. If so - remove row. Else set BTNCODE if( (_cio.states[_buttonQ[0][1]] == _buttonQ[0][2]) || (_buttonQ[0][3] <= 0) ) { - if(_buttonQ[0][0] == UP || _buttonQ[0][0] == DOWN) maxeffort = false; //remove row for(int i = 0; i < _qButtonLen-1; i++){ _buttonQ[i][0] = _buttonQ[i+1][0]; @@ -196,7 +194,6 @@ void BWC::_handleButtonQ(void) { } else { - if(_buttonQ[0][0] == UP || _buttonQ[0][0] == DOWN) maxeffort = true; //set buttoncode _cio.button = ButtonCodes[_buttonQ[0][0]]; } @@ -531,7 +528,6 @@ String BWC::getJSONCommandQueue(){ } bool BWC::newData(){ - if(maxeffort) return false; bool result = _cio.dataAvailable; _cio.dataAvailable = false; if (result && _audio) _dsp.beep(); @@ -596,10 +592,6 @@ void BWC::saveSettingsFlag(){ } void BWC::saveSettings(){ - if(maxeffort) { - _saveSettingsNeeded = true; - return; - } //kill the dog ESP.wdtDisable(); _saveSettingsNeeded = false; @@ -673,14 +665,8 @@ void BWC::_loadCommandQueue(){ } void BWC::_saveCommandQueue(){ - if(maxeffort) { - _saveCmdqNeeded = true; - return; - } //kill the dog ESP.wdtDisable(); - - _saveCmdqNeeded = false; File file = LittleFS.open("cmdq.txt", "w"); if (!file) { Serial.println(F("Failed to save cmdq.txt")); @@ -719,10 +705,6 @@ void BWC::reloadSettings(){ } void BWC::_saveStates() { - if(maxeffort) { - _saveStatesNeeded = true; - return; - } //kill the dog ESP.wdtDisable(); @@ -777,11 +759,6 @@ void BWC::_restoreStates() { } void BWC::saveEventlog(){ - if(maxeffort) { - _saveEventlogNeeded = true; - return; - } - _saveEventlogNeeded = false; //kill the dog ESP.wdtDisable(); File file = LittleFS.open("eventlog.txt", "a"); diff --git a/Code/6-wire-version/lib/BWC/model.h b/Code/6-wire-version/lib/BWC/model.h index 2ddf7ae6..a701571b 100644 --- a/Code/6-wire-version/lib/BWC/model.h +++ b/Code/6-wire-version/lib/BWC/model.h @@ -6,5 +6,7 @@ //#define MALDIVES2021 //hydrojets //If using/testing the new PCB choose PCB_V2 -#define PCB_V1 -//#define PCB_V2 //The PCB with rounded corners \ No newline at end of file +//#define PCB_V1 +#define PCB_V2 //The PCB with rounded corners + +#warning "USING PINOUT FOR PCB V2 (PCB WITH ROUND CORNERS). EDIT lib/BWC/model.h IF USING OTHER PCB!" \ No newline at end of file diff --git a/Code/6-wire-version/platformio.ini b/Code/6-wire-version/platformio.ini index 9c6e05bc..da1f8437 100644 --- a/Code/6-wire-version/platformio.ini +++ b/Code/6-wire-version/platformio.ini @@ -22,11 +22,14 @@ lib_deps = links2004/WebSockets@^2.3.3 knolleary/PubSubClient@^2.8 tzapu/WiFiManager@^0.16.0 + me-no-dev/ESPAsyncTCP board_build.filesystem = littlefs monitor_speed = 115200 ; Set upload speed to 115200 if you get transfer errors -upload_speed = 921600 +; upload_speed = 921600 ; board_build.f_cpu = 160000000L +build_flags = + -DWEBSOCKETS_NETWORK_TYPE="NETWORK_ESP8266_ASYNC" ; build_type = debug ; monitor_filters = esp8266_exception_decoder, default diff --git a/Code/6-wire-version/src/config.h b/Code/6-wire-version/src/config.h index 37c09330..444f56aa 100644 --- a/Code/6-wire-version/src/config.h +++ b/Code/6-wire-version/src/config.h @@ -2,7 +2,7 @@ #include #define DEVICE_NAME "layzspa" -#define FW_VERSION "2022-05-24" +#define FW_VERSION "2022-06-29" #define HA_PREFIX "homeassistant" /* diff --git a/Code/6-wire-version/src/main.cpp b/Code/6-wire-version/src/main.cpp index e3ce4223..946ed935 100644 --- a/Code/6-wire-version/src/main.cpp +++ b/Code/6-wire-version/src/main.cpp @@ -67,7 +67,7 @@ void loop() if (WiFi.status() == WL_CONNECTED) { // listen for websocket events - webSocket.loop(); + // webSocket.loop(); // listen for webserver events server.handleClient(); // listen for OTA events @@ -434,14 +434,18 @@ void startOTA() void stopall() { bwc.stop(); + updateMqttTimer.detach(); periodicTimer.detach(); updateWSTimer.detach(); LittleFS.end(); server.stop(); webSocket.close(); mqttClient.disconnect(); + bwc.saveSettings(); } + + /** * start a web socket server */ @@ -1068,10 +1072,10 @@ void handleDir() while (root.next()) { Serial.println(root.fileName()); - mydir += root.fileName() + F(" \t Size: "); - mydir += String(root.fileSize()) + F(" Bytes\r\n"); + mydir += "" + root.fileName() + "" + F(" \t Size: "); + mydir += String(root.fileSize()) + F(" Bytes
"); } - server.send(200, "text/plain", mydir); + server.send(200, "text/html", mydir); } /** @@ -1186,14 +1190,11 @@ void handleRestart() server.send(303); delay(1000); - periodicTimer.detach(); - updateMqttTimer.detach(); - updateWSTimer.detach(); - bwc.stop(); - bwc.saveSettings(); - + stopall(); + delay(1000); Serial.println(F("ESP restart ...")); ESP.restart(); + delay(3000); }
Bubbles
Bubbles
- +
+ +
Heater @@ -156,7 +158,7 @@

Totals:

Heating: n/a
Air: n/a