From 8fb1981552f454aaadd37cb5bcd8b3c71875b045 Mon Sep 17 00:00:00 2001 From: Ray Jones Date: Sun, 22 Sep 2019 09:03:42 +1000 Subject: [PATCH] Added Temp Probe selection screen, mapping sensors to user preferences --- src/Afterburner.cpp | 11 +- src/OLED/DS18B20Screen.cpp | 371 ++++++++++++++++++++++++++++++++++++ src/OLED/DS18B20Screen.h | 53 ++++++ src/OLED/FuelCalScreen.cpp | 4 +- src/OLED/ScreenManager.cpp | 2 + src/OLED/fonts/MiniFont.c | 14 +- src/Utility/BTC_JSON.cpp | 27 ++- src/Utility/NVStorage.cpp | 38 +++- src/Utility/NVStorage.h | 25 ++- src/Utility/TempSense.cpp | 240 ++++++++++++++++++----- src/Utility/TempSense.h | 22 ++- src/Utility/UtilClasses.cpp | 18 +- 12 files changed, 762 insertions(+), 63 deletions(-) create mode 100644 src/OLED/DS18B20Screen.cpp create mode 100644 src/OLED/DS18B20Screen.h diff --git a/src/Afterburner.cpp b/src/Afterburner.cpp index e5d7557..69b9110 100644 --- a/src/Afterburner.cpp +++ b/src/Afterburner.cpp @@ -482,6 +482,10 @@ void setup() { pHourMeter->init(bESP32PowerUpInit || RTC_Store.getBootInit()); // ensure persistent memory variable are reset after powerup, or OTA update RTC_Store.setBootInit(false); + TempSensor.mapSensor(0, NVstore.getHeaterTuning().tempProbe[0].romCode); + TempSensor.mapSensor(1, NVstore.getHeaterTuning().tempProbe[1].romCode); + TempSensor.mapSensor(2, NVstore.getHeaterTuning().tempProbe[2].romCode); + delay(1000); // just to hold the splash screeen for while } @@ -799,7 +803,9 @@ void loop() if(tDelta > MIN_TEMPERATURE_INTERVAL) { // maintain a minimum holdoff period lastTemperatureTime = millis(); // reset time to observe temeprature - if(TempSensor.readTemperature(fTemperature)) { + TempSensor.readSensors(); +// TempSensor.checkNumSensors(); + if(TempSensor.getTemperature(fTemperature)) { // get Primary sensor temeprature if(DS18B20holdoff) { DS18B20holdoff--; DebugPort.printf("Skipped initial DS18B20 reading: %f\r\n", fTemperature); @@ -1125,7 +1131,8 @@ float getTemperatureDesired() float getTemperatureSensor() { - return FilteredSamples.AmbientTemp.getValue() + NVstore.getHeaterTuning().tempOfs; + // NVstore always holds primary sensor as index 0 + return FilteredSamples.AmbientTemp.getValue() + NVstore.getHeaterTuning().tempProbe[0].offset; } void setPumpMin(float val) diff --git a/src/OLED/DS18B20Screen.cpp b/src/OLED/DS18B20Screen.cpp new file mode 100644 index 0000000..d4c85b6 --- /dev/null +++ b/src/OLED/DS18B20Screen.cpp @@ -0,0 +1,371 @@ +/* + * This file is part of the "bluetoothheater" distribution + * (https://gitlab.com/mrjones.id.au/bluetoothheater) + * + * Copyright (C) 2019 Ray Jones + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "128x64OLED.h" +#include "DS18B20Screen.h" +#include "KeyPad.h" +#include "fonts/Icons.h" +#include "fonts/MiniFont.h" +#include "Utility/TempSense.h" + +extern CTempSense TempSensor; + + +CDS18B20Screen::CDS18B20Screen(C128x64_OLED& display, CScreenManager& mgr) : CPasswordScreen(display, mgr) +{ + _nNumSensors = 0; + for(int i=0; i<3; i++) { + _sensorRole[i] = -1; + _Offset[i] = 0; + } + _initUI(); +} + +void +CDS18B20Screen::onSelect() +{ + CScreenHeader::onSelect(); + _initUI(); + _nNumSensors = TempSensor.getNumSensors(); + _readNV(); +} + +void +CDS18B20Screen::_initUI() +{ + _rowSel = 0; + _colSel = 0; + _keyHold = -1; + _scrollChar = 0; +} + +bool +CDS18B20Screen::show() +{ + char msg[32]; + + _display.clearDisplay(); + + if(!CPasswordScreen::show()) { // for showing "saving settings" + + if(_rowSel == SaveConfirm) { + _showConfirmMessage(); + } + else { + if(_colSel == 0) + _showTitle("Temp Sensor Role"); + else + _showTitle("Temp Sensor Offset"); + + int baseLine = 40; + switch(_nNumSensors) { + case 1: baseLine = 18; break; + case 2: baseLine = 30; break; + } + for(int i = 0; i<_nNumSensors; i++) { + + switch(_sensorRole[i]) { + case 0: strcpy(msg, "Pri"); break; + case 1: strcpy(msg, "Sec"); break; + case 2: strcpy(msg, "Ter"); break; + default: strcpy(msg, " ? "); break; + } + _printMenuText(border, baseLine-i*12, msg, _rowSel == (i+1) && _colSel == 0); + + OneWireBus_ROMCode romCode; + if(!TempSensor.getRomCodeIdx(romCode, i)) { + strcpy(msg, "missing?") ; + } + else { + char* buffer = msg; + for (int j = 5; j >= 0; j--) { + sprintf(buffer, "%02X%s", romCode.fields.serial_number[j], j ? ":" : ""); + buffer += 3; + } + } + { + CTransientFont AF(_display, &miniFontInfo); + _printMenuText(27, baseLine+2-i*12, msg); + } + + if(_colSel == 0) { + float temperature; + TempSensor.getTemperatureIdx(temperature, i); + sprintf(msg, "%.01fC", temperature + _Offset[i]); + } + else { + sprintf(msg, "%+.01f", _Offset[i]); + } + _printMenuText(90, baseLine-i*12, msg, _rowSel == (i+1) && _colSel == 1); + } + + } + } + return true; +} + +bool +CDS18B20Screen::animate() +{ + if(!CPasswordScreen::_busy() && !CPasswordScreen::isPasswordBusy()) { + if(_rowSel != SaveConfirm) { + const char* pMsg = NULL; + switch(_rowSel) { + case 0: + _printMenuText(_display.xCentre(), 52, " \021 \030Edit Exit \020 ", true, eCentreJustify); + break; + case 1: + case 2: + case 3: + if(_colSel == 0) + pMsg = " Hold Right to adjust probe offset. "; + else + pMsg = " Hold Left to select probe's role. "; + break; + } + if(pMsg != NULL) { + _display.drawFastHLine(0, 52, 128, WHITE); + _scrollMessage(56, pMsg, _scrollChar); + } + return true; + } + } + return false; +} + + +bool +CDS18B20Screen::keyHandler(uint8_t event) +{ + if(CPasswordScreen::keyHandler(event)) { + if(_isPasswordOK()) { + _rowSel = 1; + _keyHold = -1; + } + } + + else { + sUserSettings us; + if(event & keyPressed) { + _keyHold = 0; + // UP press + if(event & key_Up) { + if(_rowSel == SaveConfirm) { + _enableStoringMessage(); + _saveNV(); + NVstore.save(); + TempSensor.mapSensor(-1); // reset existing mapping + TempSensor.mapSensor(0, NVstore.getHeaterTuning().tempProbe[0].romCode); + TempSensor.mapSensor(1, NVstore.getHeaterTuning().tempProbe[1].romCode); + TempSensor.mapSensor(2, NVstore.getHeaterTuning().tempProbe[2].romCode); + TempSensor.mapSensor(-2); // report mapping + _rowSel = 0; + } + else { + if(_rowSel == 0) { + _getPassword(); + if(_isPasswordOK()) { + _rowSel = 1; + } + } + else { + _testCancel(); + _rowSel++; + UPPERLIMIT(_rowSel, 3); + } + } + } + // DOWN press + if(event & key_Down) { + _testCancel(); + if(_rowSel == SaveConfirm) + _rowSel = 0; + _rowSel--; + LOWERLIMIT(_rowSel, 0); + } + } + + + if(event & keyRepeat) { + if(_keyHold >= 0) { + _keyHold++; + if(_keyHold == 2) { + if(event & key_Left) { + _colSel = 0; + _scrollChar = 0; + } + if(event & key_Right) { + _colSel = 1; + _scrollChar = 0; + } + if(event & key_Centre) { + if(_colSel == 0) + _sensorRole[_rowSel-1] = -1; + else + _Offset[_rowSel-1] = 0; + } + _keyHold = -1; + } + } + } + + + if(event & keyReleased) { + if(_keyHold == 0) { + // LEFT press + if(event & key_Left) { + if(_rowSel == 0) + _ScreenManager.prevMenu(); + else + adjust(-1); + } + // RIGHT press + if(event & key_Right) { + if(_rowSel == 0) + _ScreenManager.nextMenu(); + else + adjust(+1); + } + // CENTRE press + if(event & key_Centre) { + if(_rowSel == 0) { + _ScreenManager.selectMenu(CScreenManager::RootMenuLoop); // force return to main menu + } + else { + _rowSel = SaveConfirm; + } + } + } + _keyHold = -1; + } + + _ScreenManager.reqUpdate(); + } + + return true; +} + +void +CDS18B20Screen::adjust(int dir) +{ + switch(_rowSel) { + case 1: + if(_colSel == 0) { + _sensorRole[0] += dir; + WRAPLIMITS(_sensorRole[0], 0, _nNumSensors-1); + if(_nNumSensors == 2) + _sensorRole[1] = _sensorRole[0] == 0 ? 1 : 0; + } + else { + _Offset[0] += dir * 0.1; + BOUNDSLIMIT(_Offset[0], -10, +10); + } + break; + case 2: + if(_colSel == 0) { + _sensorRole[1] += dir; + WRAPLIMITS(_sensorRole[1], 0, _nNumSensors-1); + if(_nNumSensors == 2) + _sensorRole[0] = _sensorRole[1] == 0 ? 1 : 0; + } + else { + _Offset[1] += dir * 0.1; + BOUNDSLIMIT(_Offset[1], -10, +10); + } + break; + case 3: + if(_colSel == 0) { + _sensorRole[2] += dir; + WRAPLIMITS(_sensorRole[2], 0, _nNumSensors-1); + } + else { + _Offset[2] += dir * 0.1; + BOUNDSLIMIT(_Offset[2], -10, +10); + } + break; + } +} + +void +CDS18B20Screen::_testCancel() +{ + switch(_rowSel) { + case 1: + if(_sensorRole[1] == _sensorRole[0]) _sensorRole[1] = -1; + if(_sensorRole[2] == _sensorRole[0]) _sensorRole[2] = -1; + break; + case 2: + if(_sensorRole[0] == _sensorRole[1]) _sensorRole[0] = -1; + if(_sensorRole[2] == _sensorRole[1]) _sensorRole[2] = -1; + break; + case 3: + if(_sensorRole[0] == _sensorRole[2]) _sensorRole[0] = -1; + if(_sensorRole[1] == _sensorRole[2]) _sensorRole[1] = -1; + break; + } +} + + +void +CDS18B20Screen::_readNV() +{ + _sensorRole[0] = _sensorRole[1] = _sensorRole[2] = -1; + + const sHeaterTuning& tuning = NVstore.getHeaterTuning(); + + for(int sensor = 0; sensor < TempSensor.getNumSensors(); sensor++) { + OneWireBus_ROMCode romCode; + TempSensor.getRomCodeIdx(romCode, sensor); // get rom code of each attached sensor + // NV storage indices are the sensor role + // ie 0 is normal thermostat + // scan the NV store and match the stored romCodes + // if not matched, the sensor remains unmapped + for(int i=0; i< 3; i++) { + if(memcmp(tuning.tempProbe[i].romCode.bytes, romCode.bytes, 8) == 0) { + _sensorRole[sensor] = i; // assign role to sensor according to NV placement + _Offset[sensor] = tuning.tempProbe[i].offset; + continue; + } + } + } + DebugPort.printf("Sensor roles: %d, %d, %d\r\n", _sensorRole[0], _sensorRole[1], _sensorRole[2]); +} + +void +CDS18B20Screen::_saveNV() +{ + sHeaterTuning tuning = NVstore.getHeaterTuning(); + for(int i=0; i<3; i++) { + memset(tuning.tempProbe[i].romCode.bytes, 0, 8); + tuning.tempProbe[i].offset = 0; + } + + for(int sensor = 0; sensor < TempSensor.getNumSensors(); sensor++) { + int role = _sensorRole[sensor]; // role of probe determines placement in NV storage + if(role != -1) { + tuning.tempProbe[role].offset = _Offset[sensor]; + OneWireBus_ROMCode romCode; + TempSensor.getRomCodeIdx(romCode, sensor); // get rom code of indexed sensor + memcpy(tuning.tempProbe[role].romCode.bytes, romCode.bytes, 8); + } + } + NVstore.setHeaterTuning(tuning); +} \ No newline at end of file diff --git a/src/OLED/DS18B20Screen.h b/src/OLED/DS18B20Screen.h new file mode 100644 index 0000000..6e0f9a3 --- /dev/null +++ b/src/OLED/DS18B20Screen.h @@ -0,0 +1,53 @@ +/* + * This file is part of the "bluetoothheater" distribution + * (https://gitlab.com/mrjones.id.au/bluetoothheater) + * + * Copyright (C) 2019 Ray Jones + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef __DS18B20SCREEN_H__ +#define __DS18B20SCREEN_H__ + +#include +#include "PasswordScreen.h" +#include "../Utility/NVStorage.h" + +class C128x64_OLED; +class CScreenManager; + +class CDS18B20Screen : public CPasswordScreen +{ + int _rowSel, _colSel; + int _keyHold; + int _scrollChar; + int _nNumSensors; + int _sensorRole[3]; + float _Offset[3]; + void _initUI(); + void _testCancel(); + void _readNV(); + void _saveNV(); +public: + CDS18B20Screen(C128x64_OLED& display, CScreenManager& mgr); + bool show(); + bool animate(); + bool keyHandler(uint8_t event); + void onSelect(); + void adjust(int dir); +}; + +#endif diff --git a/src/OLED/FuelCalScreen.cpp b/src/OLED/FuelCalScreen.cpp index e5fff73..9a12db2 100644 --- a/src/OLED/FuelCalScreen.cpp +++ b/src/OLED/FuelCalScreen.cpp @@ -48,7 +48,7 @@ CFuelCalScreen::onSelect() _initUI(); _mlPerStroke = NVstore.getHeaterTuning().pumpCal; _LVC = NVstore.getHeaterTuning().lowVolts; - _tOfs = NVstore.getHeaterTuning().tempOfs; + _tOfs = NVstore.getHeaterTuning().tempProbe[0].offset; } void @@ -231,7 +231,7 @@ CFuelCalScreen::keyHandler(uint8_t event) tuning = NVstore.getHeaterTuning(); tuning.pumpCal = _mlPerStroke; tuning.lowVolts = _LVC; - tuning.tempOfs = _tOfs; + tuning.tempProbe[0].offset = _tOfs; NVstore.setHeaterTuning(tuning); saveNV(); _rowSel = 0; diff --git a/src/OLED/ScreenManager.cpp b/src/OLED/ScreenManager.cpp index 3f7e16e..e680087 100644 --- a/src/OLED/ScreenManager.cpp +++ b/src/OLED/ScreenManager.cpp @@ -46,6 +46,7 @@ #include "BTScreen.h" #include "MenuTrunkScreen.h" #include "MQTTScreen.h" +#include "DS18B20Screen.h" #include #include "../cfg/pins.h" #include "../cfg/BTCConfig.h" @@ -486,6 +487,7 @@ CScreenManager::_loadScreens() menuloop.push_back(new CWiFiScreen(*_pDisplay, *this)); menuloop.push_back(new CMQTTScreen(*_pDisplay, *this)); menuloop.push_back(new CBTScreen(*_pDisplay, *this)); + menuloop.push_back(new CDS18B20Screen(*_pDisplay, *this)); _Screens.push_back(menuloop); } diff --git a/src/OLED/fonts/MiniFont.c b/src/OLED/fonts/MiniFont.c index fac3c23..8836ce4 100644 --- a/src/OLED/fonts/MiniFont.c +++ b/src/OLED/fonts/MiniFont.c @@ -168,6 +168,16 @@ const uint8_t miniFontBitmaps[] PROGMEM = // @81 ':' (1 pixel wide) 0x50, // # # + // @82 'b' (3 pixels wide) + 0x38, // ### + 0x28, // # # + 0xf8, // ##### + + // @85 'd' (3 pixels wide) + 0xf8, // ##### + 0x28, // # # + 0x38, // ### + }; // Character descriptors for a 3x5 font @@ -194,9 +204,9 @@ const FONT_CHAR_INFO miniFontDescriptors[] PROGMEM = {0, 0, 0}, // '?' {0, 0, 0}, // '@' {3, 5, 33}, // 'A' - {0, 0, 0}, // 'B' + {3, 5, 82}, // 'B' {3, 5, 36}, // 'C' - {0, 0, 0}, // 'D' + {3, 5, 85}, // 'D' {3, 5, 72}, // 'E' {3, 5, 39}, // 'F' {3, 5, 42}, // 'G' diff --git a/src/Utility/BTC_JSON.cpp b/src/Utility/BTC_JSON.cpp index 432ba36..cd2eaf4 100644 --- a/src/Utility/BTC_JSON.cpp +++ b/src/Utility/BTC_JSON.cpp @@ -36,7 +36,9 @@ #include "../Protocol/Protocol.h" #include #include "HourMeter.h" +#include "Utility/TempSense.h" +extern CTempSense TempSensor; extern CModerator MQTTmoderator; char defaultJSONstr[64]; @@ -143,6 +145,22 @@ bool makeJSONString(CModerator& moderator, char* opStr, int len) if(tidyTemp > -80) { bSend |= moderator.addJson("TempCurrent", tidyTemp, root); } + if(TempSensor.getNumSensors() > 1) { + TempSensor.getTemperature(tidyTemp, 1); + tidyTemp += NVstore.getHeaterTuning().tempProbe[1].offset; + tidyTemp = int(tidyTemp * 10 + 0.5) * 0.1f; // round to 0.1 resolution + if(tidyTemp > -80) { + bSend |= moderator.addJson("Temp2Current", tidyTemp, root); + } + if(TempSensor.getNumSensors() > 2) { + TempSensor.getTemperature(tidyTemp, 2); + tidyTemp += NVstore.getHeaterTuning().tempProbe[2].offset; + tidyTemp = int(tidyTemp * 10 + 0.5) * 0.1f; // round to 0.1 resolution + if(tidyTemp > -80) { + bSend |= moderator.addJson("Temp3Current", tidyTemp, root); + } + } + } bSend |= moderator.addJson("TempDesired", getTemperatureDesired(), root); bSend |= moderator.addJson("TempMode", NVstore.getUserSettings().degF, root); if(NVstore.getUserSettings().menuMode < 2) { @@ -196,9 +214,14 @@ bool makeJSONStringEx(CModerator& moderator, char* opStr, int len) bSend |= moderator.addJson("CyclicOn", NVstore.getUserSettings().cyclic.Start, root); // threshold of under temp for cyclic mode bSend |= moderator.addJson("PumpCount", RTC_Store.getFuelGauge(), root); // running count of pump strokes bSend |= moderator.addJson("PumpCal", NVstore.getHeaterTuning().pumpCal, root); // mL/stroke - bSend |= moderator.addJson("LowVoltCutout", NVstore.getHeaterTuning().getLVC(), root); // low volatge cutout + bSend |= moderator.addJson("LowVoltCutout", NVstore.getHeaterTuning().getLVC(), root); // low voltage cutout + } + bSend |= moderator.addJson("TempOffset", NVstore.getHeaterTuning().tempProbe[0].offset, root); // degC offset + if(TempSensor.getNumSensors() > 1) { + bSend |= moderator.addJson("Temp2Offset", NVstore.getHeaterTuning().tempProbe[1].offset, root); // degC offset + if(TempSensor.getNumSensors() > 2) + bSend |= moderator.addJson("Temp3Offset", NVstore.getHeaterTuning().tempProbe[2].offset, root); // degC offset } - bSend |= moderator.addJson("TempOffset", NVstore.getHeaterTuning().tempOfs, root); // degC offset if(bSend) { root.printTo(opStr, len); diff --git a/src/Utility/NVStorage.cpp b/src/Utility/NVStorage.cpp index 568a569..67b1e1a 100644 --- a/src/Utility/NVStorage.cpp +++ b/src/Utility/NVStorage.cpp @@ -265,8 +265,25 @@ sHeaterTuning::load() else validatedLoad("lowVolts", lowVolts, 230, u8inBoundsOrZero, 200, 250); validatedLoad("pumpCal", pumpCal, 0.02, 0.001, 1); - validatedLoad("tempOfs", tempOfs, 0.0, -10.0, +10.0); + validatedLoad("tempOffset0", tempProbe[0].offset, 0.0, -10.0, +10.0); + validatedLoad("tempOffset1", tempProbe[1].offset, 0.0, -10.0, +10.0); + validatedLoad("tempOffset2", tempProbe[2].offset, 0.0, -10.0, +10.0); + preferences.getBytes("probeSerial0", tempProbe[0].romCode.bytes, 8); + preferences.getBytes("probeSerial1", tempProbe[1].romCode.bytes, 8); + preferences.getBytes("probeSerial2", tempProbe[2].romCode.bytes, 8); preferences.end(); + + for(int i=0; i<3; i++) { + DebugPort.printf("Rd Probe[%d] %02X:%02X:%02X:%02X:%02X:%02X\r\n", + i, + tempProbe[i].romCode.fields.serial_number[5], + tempProbe[i].romCode.fields.serial_number[4], + tempProbe[i].romCode.fields.serial_number[3], + tempProbe[i].romCode.fields.serial_number[2], + tempProbe[i].romCode.fields.serial_number[1], + tempProbe[i].romCode.fields.serial_number[0] + ); + } } void @@ -284,8 +301,25 @@ sHeaterTuning::save() preferences.putUChar("glowDrive", glowDrive); preferences.putUChar("lowVolts", lowVolts); preferences.putFloat("pumpCal", pumpCal); - preferences.putFloat("tempOfs", tempOfs); + preferences.putFloat("tempOffset0", tempProbe[0].offset); + preferences.putFloat("tempOffset1", tempProbe[1].offset); + preferences.putFloat("tempOffset2", tempProbe[2].offset); + preferences.putBytes("probeSerial0", tempProbe[0].romCode.bytes, 8); + preferences.putBytes("probeSerial1", tempProbe[1].romCode.bytes, 8); + preferences.putBytes("probeSerial2", tempProbe[2].romCode.bytes, 8); preferences.end(); + + for(int i=0; i<3; i++) { + DebugPort.printf("Wr Probe[%d] %02X:%02X:%02X:%02X:%02X:%02X\r\n", + i, + tempProbe[i].romCode.fields.serial_number[5], + tempProbe[i].romCode.fields.serial_number[4], + tempProbe[i].romCode.fields.serial_number[3], + tempProbe[i].romCode.fields.serial_number[2], + tempProbe[i].romCode.fields.serial_number[1], + tempProbe[i].romCode.fields.serial_number[0] + ); + } } void diff --git a/src/Utility/NVStorage.h b/src/Utility/NVStorage.h index b2ec950..78405f1 100644 --- a/src/Utility/NVStorage.h +++ b/src/Utility/NVStorage.h @@ -25,12 +25,17 @@ #include "BTC_GPIO.h" #include "NVCore.h" #include "../Utility/helpers.h" +#include "Utility/TempSense.h" #include "../RTC/Timers.h" // for sTimer void toggle(bool& ref); void toggle(uint8_t& ref); +struct sProbeTuning { + float offset; + OneWireBus_ROMCode romCode; +}; struct sHeaterTuning : public CESP32_NVStorage { uint8_t Pmin; @@ -42,7 +47,7 @@ struct sHeaterTuning : public CESP32_NVStorage { uint8_t glowDrive; uint8_t lowVolts; // x10 float pumpCal; - float tempOfs; + sProbeTuning tempProbe[3]; // [0],[1],[2] - Primary, Secondary, Tertiary bool valid() { bool retval = true; @@ -58,7 +63,9 @@ struct sHeaterTuning : public CESP32_NVStorage { retval &= INBOUNDS(lowVolts, 100, 125) || (lowVolts == 0); else retval &= INBOUNDS(lowVolts, 200, 250 || (lowVolts == 0)); - retval &= INBOUNDS(tempOfs, -10, +10); + retval &= INBOUNDS(tempProbe[0].offset, -10, +10); + retval &= INBOUNDS(tempProbe[1].offset, -10, +10); + retval &= INBOUNDS(tempProbe[2].offset, -10, +10); return retval; }; void init() { @@ -71,7 +78,12 @@ struct sHeaterTuning : public CESP32_NVStorage { glowDrive = 5; pumpCal = 0.02; lowVolts = 115; - tempOfs = 0; + tempProbe[0].offset = 0; + tempProbe[1].offset = 0; + tempProbe[2].offset = 0; + memset(tempProbe[0].romCode.bytes, 0, sizeof(tempProbe[0].romCode)); + memset(tempProbe[1].romCode.bytes, 0, sizeof(tempProbe[1].romCode)); + memset(tempProbe[2].romCode.bytes, 0, sizeof(tempProbe[1].romCode)); }; void load(); void save(); @@ -85,7 +97,12 @@ struct sHeaterTuning : public CESP32_NVStorage { glowDrive = rhs.glowDrive; pumpCal = rhs.pumpCal; lowVolts = rhs.lowVolts; - tempOfs = rhs.tempOfs; + tempProbe[0].offset = rhs.tempProbe[0].offset; + tempProbe[1].offset = rhs.tempProbe[1].offset; + tempProbe[2].offset = rhs.tempProbe[2].offset; + memcpy(tempProbe[0].romCode.bytes, rhs.tempProbe[0].romCode.bytes, 8); + memcpy(tempProbe[1].romCode.bytes, rhs.tempProbe[1].romCode.bytes, 8); + memcpy(tempProbe[2].romCode.bytes, rhs.tempProbe[2].romCode.bytes, 8); return *this; } float getPmin() const; diff --git a/src/Utility/TempSense.cpp b/src/Utility/TempSense.cpp index 5ec2d18..3adec6c 100644 --- a/src/Utility/TempSense.cpp +++ b/src/Utility/TempSense.cpp @@ -23,13 +23,19 @@ #include "TempSense.h" #include "DebugPort.h" +#include "macros.h" CTempSense::CTempSense() { - _TempSensor = NULL; _owb = NULL; + _nNumSensors = 0; + for(int i=0; i< MAX_DS18B20_DEVICES; i++) + _Sensors[i] = NULL; + for(int i=0; i<3; i++) + _sensorMap[i] = -1; } +#ifdef SINGLE_DS18B20_SENSOR void CTempSense::begin(int pin) { // initialise DS18B20 sensor interface @@ -48,27 +54,24 @@ void CTempSense::begin(int pin) attach(); } } -/*void CTempSense::begin(int pin) +#else +void CTempSense::begin(int pin) { // initialise DS18B20 sensor interface + // create one wire bus interface, using RMT peripheral _owb = owb_rmt_initialize(&_rmt_driver_info, pin, RMT_CHANNEL_1, RMT_CHANNEL_0); owb_use_crc(_owb, true); // enable CRC check for ROM code - bool found = find(); - - readROMcode(); - - // Create DS18B20 device on the 1-Wire bus - if(found) { - attach(); - } + find(); } -*/ +#endif + +#ifdef SINGLE_DS18B20_SENSOR bool -CTempSense::readTemperature(float& tempReading) +CTempSense::readSensors(float& tempReading) { - if(_TempSensor == NULL) { + if(_Sensors[0] == NULL) { // bool found = find(); bool found = readROMcode(); @@ -85,8 +88,8 @@ CTempSense::readTemperature(float& tempReading) } } - if(_TempSensor != NULL) { - DS18B20_ERROR error = ds18b20_read_temp(_TempSensor, &tempReading); + if(_Sensors[0] != NULL) { + DS18B20_ERROR error = ds18b20_read_temp(_Sensors[0], &tempReading); // DebugPort.printf(">>>> DS18B20 = %f, error=%d\r\n", fTemperature, error); if(error == DS18B20_OK) { @@ -94,63 +97,134 @@ CTempSense::readTemperature(float& tempReading) } else { DebugPort.println("\007DS18B20 sensor removed?"); - ds18b20_free(&_TempSensor); + ds18b20_free(&_Sensors[0]); } } return false; } -/*bool -CTempSense::readTemperature(float& tempReading) +#else +bool +CTempSense::readSensors() { - if(_TempSensor == NULL) { + bool retval = false; + + if(_nNumSensors == 0) { bool found = find(); if(found) { - DebugPort.println("Found DS18B20 device"); - - readROMcode(); - - attach(); + DebugPort.println("Found DS18B20 device(s)"); startConvert(); // request a new conversion, waitConvertDone(); } } - if(_TempSensor != NULL) { - DS18B20_ERROR error = ds18b20_read_temp(_TempSensor, &tempReading); -// DebugPort.printf(">>>> DS18B20 = %f, error=%d\r\n", fTemperature, error); - - if(error == DS18B20_OK) { - return true; + if(_nNumSensors) { + for (int i = 0; i < MAX_DS18B20_DEVICES; ++i) { + _Errors[i] = DS18B20_ERROR_UNKNOWN; } - else { - DebugPort.println("\007DS18B20 sensor removed?"); - ds18b20_free(&_TempSensor); + + for (int i = 0; i < _nNumSensors; ++i) { + _Errors[i] = ds18b20_read_temp(_Sensors[i], &_Readings[i]); + } + +#ifdef REPORT_READINGS + DebugPort.println("\nTemperature readings (degrees C)"); +#endif + for (int i = 0; i < _nNumSensors; ++i) { + if(_Errors[i] == DS18B20_OK) { +#ifdef REPORT_READINGS + DebugPort.printf(" %d: %.1f OK\r\n", i, _Readings[i]); +#endif + retval = true; // at least one sensor read OK + } + else { +#ifdef REPORT_READINGS + DebugPort.printf("\007 %d: DS18B20 sensor removed?\r\n", i); +#endif + } } } - return false; + return retval; } -*/ +#endif + +#ifdef SINGLE_DS18B20_SENSOR bool CTempSense::find() { // Find all connected devices // DebugPort.printf("Finding one wire bus devices..."); + _nNumSensors = 0; OneWireBus_SearchState search_state = {0}; bool found = false; owb_search_first(_owb, &search_state, &found); - if(found) + if(found) { + _nNumSensors = 1; DebugPort.println("Found a one wire device"); + } else DebugPort.println("No one wire devices found!!"); return found; } +#else +bool +CTempSense::find() +{ + // Find all connected devices + DebugPort.println("Finding one wire bus devices..."); + memset(_device_rom_codes, 0, sizeof(_device_rom_codes)); + _nNumSensors = 0; + OneWireBus_SearchState search_state = {0}; + bool found = false; + owb_search_first(_owb, &search_state, &found); + while(found) { + char rom_code_s[17]; + owb_string_from_rom_code(search_state.rom_code, rom_code_s, sizeof(rom_code_s)); + DebugPort.printf(" %d : %s\r\n", _nNumSensors, rom_code_s); + + _device_rom_codes[_nNumSensors] = search_state.rom_code; + _nNumSensors++; + owb_search_next(_owb, &search_state, &found); + } + DebugPort.printf("Found %d device%s\r\n", _nNumSensors, _nNumSensors==1 ? "" : "s"); + + // Create DS18B20 devices on the 1-Wire bus + for (int i = 0; i < MAX_DS18B20_DEVICES; ++i) { + if(_Sensors[i]) { + ds18b20_free(&_Sensors[i]); + } + _Sensors[i] = NULL; + } + for (int i = 0; i < _nNumSensors; ++i) + { + DS18B20_Info * ds18b20_info = ds18b20_malloc(); // heap allocation + _Sensors[i] = ds18b20_info; + + if (_nNumSensors == 1) + { + printf("Single device optimisations enabled\n"); + ds18b20_init_solo(ds18b20_info, _owb); // only one device on bus + } + else + { + ds18b20_init(ds18b20_info, _owb, _device_rom_codes[i]); // associate with bus and device + } + ds18b20_use_crc(ds18b20_info, true); // enable CRC check for temperature readings + ds18b20_set_resolution(ds18b20_info, DS18B20_RESOLUTION_12_BIT); + } + + + return found; +} +#endif + +#ifdef SINGLE_DS18B20_SENSOR bool CTempSense::readROMcode() { @@ -174,28 +248,104 @@ CTempSense::readROMcode() bool CTempSense::attach() { - if(_TempSensor == NULL) { - _TempSensor = ds18b20_malloc(); // heap allocation + if(_Sensors[0] == NULL) { + _Sensors[0] = ds18b20_malloc(); // heap allocation DebugPort.printf("Single device optimisations enabled\r\n"); - ds18b20_init_solo(_TempSensor, _owb); // only one device on bus - ds18b20_use_crc(_TempSensor, true); // enable CRC check for temperature readings - ds18b20_set_resolution(_TempSensor, DS18B20_RESOLUTION_12_BIT); + ds18b20_init_solo(_Sensors[0], _owb); // only one device on bus + ds18b20_use_crc(_Sensors[0], true); // enable CRC check for temperature readings + ds18b20_set_resolution(_Sensors[0], DS18B20_RESOLUTION_12_BIT); } return true; } +#endif void CTempSense::startConvert() { // kick off the initial temperature conversion - if(_TempSensor) - ds18b20_convert(_TempSensor); + if(_Sensors[0]) + ds18b20_convert_all(_owb); } void CTempSense::waitConvertDone() { - if(_TempSensor) - ds18b20_wait_for_conversion(_TempSensor); + if(_Sensors[0]) + ds18b20_wait_for_conversion(_Sensors[0]); } + +int +CTempSense::checkNumSensors() const +{ + long start = millis(); + bool found = false; + int numSensors = 0; + OneWireBus_SearchState search_state = {0}; + owb_search_first(_owb, &search_state, &found); + while(found) { + numSensors++; + owb_search_next(_owb, &search_state, &found); + } + DebugPort.printf("Found %d one-wire device%s\r\n", numSensors, numSensors==1 ? "" : "s"); + long tDelta = millis() - start; + DebugPort.printf("checkNumSensors: %ldms\r\n", tDelta); + return numSensors; +} + +bool +CTempSense::mapSensor(int idx, OneWireBus_ROMCode romCode) +{ + if(idx == -1) { + _sensorMap[0] = _sensorMap[1] = _sensorMap[2] = -1; + return false; + } + if(idx == -2) { + DebugPort.printf("Sensor Map: %d %d %d\r\n", + _sensorMap[0], _sensorMap[1], _sensorMap[2]); + return false; + } + + if(!INBOUNDS(idx, 0, 2)) + return false; + + for(int i = 0; i < _nNumSensors; i++) { + if(memcmp(_Sensors[i]->rom_code.bytes, romCode.bytes, 8) == 0) { + _sensorMap[idx] = i; + DebugPort.printf("Mapped DS18B20 %02X:%02X:%02X:%02X:%02X:%02X as role %d\r\n", + romCode.fields.serial_number[5], romCode.fields.serial_number[4], romCode.fields.serial_number[3], + romCode.fields.serial_number[2], romCode.fields.serial_number[1], romCode.fields.serial_number[0], + idx); + return true; + } + } + return false; +} + +bool +CTempSense::getTemperature(float& temperature, int mapIdx) +{ + int idx = _sensorMap[mapIdx]; + if(idx < 0) + return getTemperatureIdx(temperature, 0); // default to sensor 0 if not mapped + // temperature = _Readings[idx]; + // return _Errors[idx] == DS18B20_OK; + return getTemperatureIdx(temperature, idx); +} + +bool +CTempSense::getTemperatureIdx(float& temperature, int idx) +{ + temperature = _Readings[idx]; + return _Errors[idx] == DS18B20_OK; +} + +bool +CTempSense::getRomCodeIdx(OneWireBus_ROMCode& romCode, int idx) +{ + if(idx >= _nNumSensors) + return false; + romCode = _Sensors[idx]->rom_code; + return true; +} + diff --git a/src/Utility/TempSense.h b/src/Utility/TempSense.h index 0f5a06d..e78adca 100644 --- a/src/Utility/TempSense.h +++ b/src/Utility/TempSense.h @@ -24,22 +24,40 @@ #include "../../lib/esp32-ds18b20/ds18b20.h" +//#define SINGLE_DS18B20_SENSOR + +const int MAX_DS18B20_DEVICES = 3; + class CTempSense { OneWireBus * _owb; owb_rmt_driver_info _rmt_driver_info; - DS18B20_Info * _TempSensor = NULL; + DS18B20_Info * _Sensors[MAX_DS18B20_DEVICES]; + OneWireBus_ROMCode _device_rom_codes[MAX_DS18B20_DEVICES]; + int _nNumSensors; + + float _Readings[MAX_DS18B20_DEVICES]; + DS18B20_ERROR _Errors[MAX_DS18B20_DEVICES]; bool _discover(); + int _sensorMap[3]; public: CTempSense(); void begin(int pin); bool find(); +#ifdef SINGLE_DS18B20_SENSOR bool readROMcode(); bool attach(); - bool readTemperature(float& tempReading); +#endif + bool readSensors(); void startConvert(); void waitConvertDone(); + bool getTemperature(float& tempReading, int mapIdx=0); // indexed as mapped by user + bool getTemperatureIdx(float& tempReading, int selSensor=0); // index is discovery order on one-wire bus + bool getRomCodeIdx(OneWireBus_ROMCode& romCode, int selSensor=0); // index is discovery order on one-wire bus + int checkNumSensors() const; + int getNumSensors() const { return _nNumSensors; }; + bool mapSensor(int idx, OneWireBus_ROMCode romCode = { 0 } ); }; #endif diff --git a/src/Utility/UtilClasses.cpp b/src/Utility/UtilClasses.cpp index a64afdc..232edd3 100644 --- a/src/Utility/UtilClasses.cpp +++ b/src/Utility/UtilClasses.cpp @@ -340,8 +340,22 @@ void DecodeCmd(const char* cmd, String& payload) } else if(strcmp("TempOffset", cmd) == 0) { sHeaterTuning ht = NVstore.getHeaterTuning(); - ht.tempOfs = payload.toFloat(); - if(INBOUNDS(ht.tempOfs, -10.0, +10.0)) { + ht.tempProbe[0].offset = payload.toFloat(); + if(INBOUNDS(ht.tempProbe[0].offset, -10.0, +10.0)) { + NVstore.setHeaterTuning(ht); + } + } + else if(strcmp("Temp2Offset", cmd) == 0) { + sHeaterTuning ht = NVstore.getHeaterTuning(); + ht.tempProbe[1].offset = payload.toFloat(); + if(INBOUNDS(ht.tempProbe[1].offset, -10.0, +10.0)) { + NVstore.setHeaterTuning(ht); + } + } + else if(strcmp("Temp3Offset", cmd) == 0) { + sHeaterTuning ht = NVstore.getHeaterTuning(); + ht.tempProbe[2].offset = payload.toFloat(); + if(INBOUNDS(ht.tempProbe[2].offset, -10.0, +10.0)) { NVstore.setHeaterTuning(ht); } }