Assortment of tweaks and fixes

New Features:
GPIO "Run" status output (not standby)
RSSI of STA connection
STA gatewayIP
Only run as "active" controller when changes to fuel mixture etc for a short while.
Timed moderation added for frequently changing JSON vars
Altitude & Humidity via JSON
JSON reboot (mainly for MQTT clients)

Bug Fixes:
Cyclic not enabled when frost start
LVC holdoff added for "starting car" situation
Better handling of string and float NV Storage defaults
FrostRise limits  1-30 now 0-30
Handle spaces in SPIFFS file uploads
This commit is contained in:
Ray Jones 2020-03-23 16:54:15 +11:00
parent 3ca3e633ae
commit 4986a4d741
34 changed files with 737 additions and 137 deletions

View File

@ -1,18 +1,20 @@
Afterburner operation checklist
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Modifications performed:
Pullup on Debug Rx line
Swapped GPIO In#1 with Analogue input
5V via blocking diode to GPIO port
OLED intact
Latest firmware uploaded
SPIFFS uploaded
Check temperature sensor
Keypad functions OK
Indicator LEDs function
Web server functions and serves heater control page
Bluetooth pairs and streams SPP data
Set time
Install battery
GPIO units only
~~~~~~~~~~~~~~~
Check pressure sensor
GPIO functions:
Input x2
Output x2

View File

@ -97,7 +97,7 @@
#include "Protocol/Protocol.h"
#include "Protocol/TxManage.h"
#include "Protocol/SmartError.h"
#include "Utility/helpers.h"
#include "Utility/helpers.h"
#include "Utility/NVStorage.h"
#include "Utility/DebugPort.h"
#include "Utility/macros.h"
@ -124,10 +124,10 @@
#define RX_DATA_TIMOUT 50
const int FirmwareRevision = 31;
const int FirmwareSubRevision = 9;
const int FirmwareMinorRevision = 1;
const char* FirmwareDate = "15 Jan 2020";
const int FirmwareRevision = 32;
const int FirmwareSubRevision = 0;
const int FirmwareMinorRevision = 0;
const char* FirmwareDate = "21 Mar 2020";
#ifdef ESP32
@ -367,9 +367,6 @@ void setup() {
digitalWrite(GPIOout2_pin, LOW);
#endif
nvs_stats_t nvs_stats;
nvs_get_stats(NULL, &nvs_stats);
// initialise TelnetSpy (port 23) as well as Serial to 115200
// Serial is the usual USB connection to a PC
// DO THIS BEFORE WE TRY AND SEND DEBUG INFO!
@ -383,6 +380,11 @@ void setup() {
DebugPort.begin(115200);
DebugPort.println("_______________________________________________________________");
DebugPort.printf("Getting NVS stats\r\n");
nvs_stats_t nvs_stats;
while( nvs_get_stats(NULL, &nvs_stats) == ESP_ERR_NVS_NOT_INITIALIZED);
DebugPort.printf("Reset reason: core0:%d, core1:%d\r\n", rtc_get_reset_reason(0), rtc_get_reset_reason(0));
// DebugPort.printf("Previous user ON = %d\r\n", bUserON); // state flag required for cyclic mode to persist properly after a WD reboot :-)
@ -482,6 +484,9 @@ void setup() {
setupGPIO();
// pinMode(0, OUTPUT);
// digitalWrite(0, LOW);
#if USE_SW_WATCHDOG == 1 && USE_JTAG == 0
// create a high priority FreeRTOS task as a watchdog monitor
TaskHandle_t wdTask;
@ -984,15 +989,18 @@ void manageFrostMode()
if(heaterState == 0) {
RTC_Store.setFrostOn(true);
DebugPort.printf("FROST MODE: Starting heater, < %d`C\r\n", engage);
if(NVstore.getUserSettings().FrostRise == 0)
RTC_Store.setCyclicEngaged(true); // enable cyclic mode if user stop
heaterOn();
}
}
uint8_t rise = NVstore.getUserSettings().FrostRise;
if(rise && (deltaT > rise)) { // if rsie is set to 0, user must shut off heater
if(rise && (deltaT > rise)) { // if rise is set to 0, user must shut off heater
if(RTC_Store.getFrostOn()) {
DebugPort.printf("FROST MODE: Stopping heater, > %d`C\r\n", engage+rise);
heaterOff();
RTC_Store.setFrostOn(false); // cancel active frost mode
RTC_Store.setCyclicEngaged(false); // for cyclic mode
}
}
}
@ -1271,29 +1279,10 @@ float getTemperatureDesired()
float getTemperatureSensor(int source)
{
static long lasttime = millis();
static float tempsave = 0;
long tDelta;
// NVstore always holds primary sensor as index 0
float retval;
TempSensor.getTemperature(source, retval);
return retval;
// switch (source) {
// case 0:
// return FilteredSamples.AmbientTemp.getValue() + NVstore.getHeaterTuning().DS18B20probe[0].offset;
// case 1:
// BMESensor.getTemperature(tempsave);
// /* tDelta = millis() - lasttime;
// if(tDelta >= 0) {
// bme.takeForcedMeasurement();
// tempsave = bme.readTemperature();
// lasttime = millis() + 10000;
// }*/
// return tempsave;
// default:
// return -100;
// }
}
void setPumpMin(float val)
@ -2031,4 +2020,7 @@ CTempSense& getTempSensor()
return TempSensor;
}
void reqHeaterCalUpdate()
{
TxManage.queueSysUpdate();
}

View File

@ -135,6 +135,10 @@ CGPIOInfoScreen::animate()
_drawBitmap(99, 15, threshIconInfo);
_drawBitmap(110, 13, bulbmode ? BulbOnIconInfo : BulbOffIconInfo);
break;
case CGPIOout1::HtrActive:
_drawBitmap(99, 15, onOffIconInfo);
_drawBitmap(110, 13, bulbmode ? BulbOnIconInfo : BulbOffIconInfo);
break;
}
#if USE_JTAG == 0
@ -151,6 +155,9 @@ CGPIOInfoScreen::animate()
_drawBitmap(99, 27, threshIconInfo);
_drawBitmap(110, 26, bulbmode ? BulbOnIconInfo : BulbOffIconInfo);
break;
case CGPIOout2::HtrActive:
_drawBitmap(99, 27, onOffIconInfo);
_drawBitmap(110, 26, bulbmode ? BulbOnIconInfo : BulbOffIconInfo);
}

View File

@ -138,7 +138,7 @@ CGPIOSetupScreen::show()
const char* msgText = NULL;
switch(_GPIOparams.out1Mode) {
case CGPIOout1::Disabled: msgText = "---"; break;
case CGPIOout1::Status: msgText = "Status"; break;
case CGPIOout1::Status: msgText = "stsLED"; break;
case CGPIOout1::User: msgText = "User"; break;
case CGPIOout1::Thresh:
if(_rowSel == 6) {
@ -151,6 +151,7 @@ CGPIOSetupScreen::show()
_printMenuText(Column2, Line3, msg, _rowSel == 8);
}
break;
case CGPIOout1::HtrActive: msgText ="OnSts"; break;
}
if(msgText)
_printMenuText(Column2, Line3, msgText, _rowSel == 6);
@ -173,6 +174,7 @@ CGPIOSetupScreen::show()
_printMenuText(Column2, Line2, msg, _rowSel == 7);
}
break;
case CGPIOout2::HtrActive: msgText ="OnSts"; break;
}
if(msgText)
_printMenuText(Column2, Line2, msgText, _rowSel == 5);
@ -256,6 +258,7 @@ CGPIOSetupScreen::animate()
else
pMsg = " Output 2: Active if under temperature. Hold LEFT to set under. Hold RIGHT to set over. ";
break;
case CGPIOout2::HtrActive: pMsg = " Output 2: Active if heater is running. "; break;
}
if(pMsg)
_scrollMessage(56, pMsg, _scrollChar);
@ -272,6 +275,7 @@ CGPIOSetupScreen::animate()
else
pMsg = " Output 1: Active if under temperature. Hold LEFT to set under. Hold RIGHT to set over. ";
break;
case CGPIOout1::HtrActive: pMsg = " Output 1: Active if heater is running. "; break;
}
if(pMsg)
_scrollMessage(56, pMsg, _scrollChar);
@ -483,13 +487,13 @@ CGPIOSetupScreen::_adjust(int dir)
case 5: // outputs mode
tVal = _GPIOparams.out2Mode;
tVal += dir;
WRAPLIMITS(tVal, 0, 2);
WRAPLIMITS(tVal, 0, 3);
_GPIOparams.out2Mode = (CGPIOout2::Modes)tVal;
break;
case 6: // outputs mode
tVal = _GPIOparams.out1Mode;
tVal += dir;
WRAPLIMITS(tVal, 0, 3);
WRAPLIMITS(tVal, 0, 4);
_GPIOparams.out1Mode = (CGPIOout1::Modes)tVal;
break;
case 7:

View File

@ -229,6 +229,15 @@ CScreenHeader::showWifiIcon()
{
if(isWifiConnected() || isWifiAP()) { // STA or AP mode active
_drawBitmap(X_WIFI_ICON, Y_WIFI_ICON, WifiWideIconInfo, WHITE, BLACK); // wide icon erases annotations!
int8_t RSSI = getWifiRSSI();
if(RSSI < -70) {
_display.fillRect(X_WIFI_ICON, Y_WIFI_ICON, WifiWideIconInfo.width, 6, BLACK);
}
else if(RSSI < -55) {
_display.fillRect(X_WIFI_ICON, Y_WIFI_ICON, WifiWideIconInfo.width, 3, BLACK);
_display.fillRect(X_WIFI_ICON, Y_WIFI_ICON, 1, 4, BLACK);
_display.fillRect(X_WIFI_ICON+WifiIconInfo.width-1, Y_WIFI_ICON, 1, 4, BLACK);
}
int xPos = X_WIFI_ICON + WifiIconInfo.width + 1; // x loaction of upload/download arrows

View File

@ -24,6 +24,7 @@
#include "BasicScreen.h"
#include "PrimingScreen.h"
#include "WiFiScreen.h"
#include "WiFiSTAScreen.h"
#include "FuelMixtureScreen.h"
#include "SetClockScreen.h"
#include "SetTimerScreen.h"
@ -492,6 +493,7 @@ CScreenManager::_loadScreens()
menuloop.push_back(new CHourMeterScreen(*_pDisplay, *this)); // Hour Meter screen
}
menuloop.push_back(new CWiFiScreen(*_pDisplay, *this));
menuloop.push_back(new CWiFiSTAScreen(*_pDisplay, *this));
menuloop.push_back(new CMQTTScreen(*_pDisplay, *this));
menuloop.push_back(new CBTScreen(*_pDisplay, *this));
if(getTempSensor().getBME280().getCount()) {

123
src/OLED/WiFiSTAScreen.cpp Normal file
View File

@ -0,0 +1,123 @@
/*
* This file is part of the "bluetoothheater" distribution
* (https://gitlab.com/mrjones.id.au/bluetoothheater)
*
* Copyright (C) 2018 Ray Jones <ray@mrjones.id.au>
*
* 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 <https://www.gnu.org/licenses/>.
*
*/
#include "WiFiSTAScreen.h"
#include "KeyPad.h"
#include "../Utility/helpers.h"
#include "../Utility/NVStorage.h"
#include "../WiFi/BTCWifi.h"
#include "fonts/Arial.h"
#include "fonts/Icons.h"
///////////////////////////////////////////////////////////////////////////
//
// CWiFiSTAScreen
//
// This screen presents information about the STA connection
//
///////////////////////////////////////////////////////////////////////////
CWiFiSTAScreen::CWiFiSTAScreen(C128x64_OLED& display, CScreenManager& mgr) : CScreen(display, mgr)
{
}
bool
CWiFiSTAScreen::show()
{
CScreen::show();
_display.clearDisplay();
_showTitle("WiFi STA status");
int yPos = 15;
if(NVstore.getUserSettings().wifiMode == 0 || !isWifiSTA()) {
if(NVstore.getUserSettings().wifiMode == 0)
_printMenuText(border, yPos, "DISABLED");
else
_printMenuText(border, yPos, "NOT CONNECTED");
}
else {
_printMenuText(0, yPos, "ADDR:");
_printMenuText(31, yPos, getWifiSTAAddrStr());
yPos += _display.textHeight() + 3;
_printMenuText(0, yPos, " GW:");
_printMenuText(31, yPos, getWifiGatewayAddrStr());
// yPos += _display.textHeight() + 2;
// _printMenuText(0, yPos, " MAC:");
// _printMenuText(31, yPos, getWifiSTAMACStr());
yPos += _display.textHeight() + 3;
_printMenuText(0, yPos, "RSSI:");
int8_t RSSI = getWifiRSSI();
char RSSIstr[16];
sprintf(RSSIstr, "%ddBm", RSSI);
_printMenuText(31, yPos, RSSIstr);
int xPos = 90;
_drawBitmap(xPos, yPos, WifiIconInfo, WHITE, BLACK); // wide icon erases annotations!
if(RSSI < -70) {
_display.fillRect(xPos, yPos, WifiIconInfo.width, 6, BLACK);
}
else if(RSSI < -55) {
_display.fillRect(xPos, yPos, WifiWideIconInfo.width, 3, BLACK);
_display.fillRect(xPos, yPos, 1, 4, BLACK);
_display.fillRect(xPos+WifiIconInfo.width-1, yPos, 1, 4, BLACK);
}
}
_printMenuText(_display.xCentre(), 53, "\021 \020", true, eCentreJustify);
_printMenuText(_display.xCentre(), 53, "Exit", false, eCentreJustify);
return true;
}
bool
CWiFiSTAScreen::keyHandler(uint8_t event)
{
if(event & keyPressed) {
// press LEFT
if(event & key_Left) {
_ScreenManager.prevMenu();
}
// press RIGHT
if(event & key_Right) {
_ScreenManager.nextMenu();
}
// press UP
if(event & key_Up) {
}
// press DOWN
if(event & key_Down) {
}
if(event & key_Centre) {
_ScreenManager.selectMenu(CScreenManager::RootMenuLoop); // force return to main menu
}
_ScreenManager.reqUpdate();
}
return true;
}

38
src/OLED/WiFiSTAScreen.h Normal file
View File

@ -0,0 +1,38 @@
/*
* This file is part of the "bluetoothheater" distribution
* (https://gitlab.com/mrjones.id.au/bluetoothheater)
*
* Copyright (C) 2018 Ray Jones <ray@mrjones.id.au>
*
* 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 <https://www.gnu.org/licenses/>.
*
*/
#ifndef __WIFISTASCREEN_H__
#define __WIFISTASCREEN_H__
#include <stdint.h>
#include "Screen.h"
class C128x64_OLED;
class CScreenManager;
class CWiFiSTAScreen : public CScreen {
public:
CWiFiSTAScreen(C128x64_OLED& display, CScreenManager& mgr);
bool show();
bool keyHandler(uint8_t event);
};
#endif

View File

@ -1301,6 +1301,22 @@ const uint8_t PROGMEM threshIcon[] =
const BITMAP_INFO threshIconInfo(9, 9, threshIcon);
//
// Image data for onOff
//
const uint8_t PROGMEM onOffIcon[] =
{
0x10, // #
0x54, // # # #
0x92, // # # #
0x92, // # # #
0x82, // # #
0x44, // # #
0x38, // ###
};
const BITMAP_INFO onOffIconInfo(7, 7, onOffIcon);
//
// Image data for frost
//

View File

@ -159,5 +159,6 @@ extern const BITMAP_INFO algIconInfo;
extern const BITMAP_INFO passwordIconInfo;
extern const BITMAP_INFO threshIconInfo;
extern const BITMAP_INFO onOffIconInfo;
extern const BITMAP_INFO frostIconInfo;
extern const BITMAP_INFO humidityIconInfo;

View File

@ -238,6 +238,13 @@ CProtocol::setAltitude(float altitude)
Controller.Altitude_LSB = (alt >> 0) & 0xff;
}
int
CProtocol::getAltitude() const
{
int alt = (Controller.Altitude_MSB << 8) | Controller.Altitude_LSB;
return alt;
}
void
CProtocol::Init(int FrameMode)

View File

@ -163,6 +163,7 @@ public:
void setTemperature_HeatExchg(uint16_t degC); // temperature of heat exchanger
// altitude
void setAltitude(float altitude);
int getAltitude() const;
void DebugReport(const char* hdr, const char* ftr);
@ -210,6 +211,7 @@ public:
float getGlow_Current() const { return Heater.getGlowPlug_Current(); };
float getSystemVoltage() const { return Controller.getSystemVoltage(); };
int getGlow_Drive() const { return Controller.getGlowDrive(); };
int getAltitude() const { return Controller.getAltitude(); };
// void setRefTime();
void reportFrames(bool isOEM);

View File

@ -126,6 +126,8 @@ CSmartError::monitor(uint8_t newRunState)
int
CSmartError::checkVolts(float ipVolts, float glowI, bool throwfault)
{
const unsigned long LVCholdoffTime = 10000;
static unsigned long LVCShutdownHoldoff = 0;
// check for low voltage
// Native NV values here are x10 integers
@ -139,27 +141,32 @@ CSmartError::checkVolts(float ipVolts, float glowI, bool throwfault)
// test low voltage cutout
if(ipVolts < threshLVC) {
if(throwfault) { // only throw faults if directed to do so
_Error = 2; // internals error codes are +1 over displayed error code
requestOff(); // shut heater down
if(LVCShutdownHoldoff == 0) {
// initial detection of LVC - introduce a hold off period
DebugPort.println("LVC holdoff enagaged");
LVCShutdownHoldoff = (millis() + LVCholdoffTime) | 1; // ensure non zero!
}
else {
long tDelta = millis() - LVCShutdownHoldoff;
if(tDelta > 0) {
LVCShutdownHoldoff = 0;
if(throwfault) { // only throw faults if directed to do so
DebugPort.println("LVC shutting heater down");
_Error = 2; // internals error codes are +1 over displayed error code
requestOff(); // shut heater down
}
}
}
return 2; // Low voltage return value = 2
}
else {
if(LVCShutdownHoldoff)
DebugPort.println("LVC holdoff cancelled");
LVCShutdownHoldoff = 0; // disable holdoff, voltage now OK
}
// warning threshold
float threshWarn = threshLVC + 0.5; // nominally create a warning threshold, 0.5V over LVC threhsold
/*
float alertlimit; // but always introduce it if below system voltage
if(NVstore.getHeaterTuning().sysVoltage == 120) {
alertlimit = 12.0 - cableComp;
}
else {
alertlimit = 24.0 - cableComp;
}
if(threshWarn < alertlimit) {
threshWarn = alertlimit;
}
*/
if(ipVolts < threshWarn) {
return 1; // LVC OK, but below warning threshold, return code = 1

View File

@ -55,6 +55,7 @@ CTxManage::CTxManage(int TxGatePin, HardwareSerial& serial) :
m_BlueWireSerial(serial),
m_TxFrame(CProtocol::CtrlMode)
{
m_sysUpdate = 0;
m_bOnReq = false;
m_bOffReq = false;
m_bTxPending = false;
@ -107,6 +108,12 @@ CTxManage::queueRawCommand(uint8_t val)
_rawCommand = val;
}
void
CTxManage::queueSysUpdate()
{
m_sysUpdate = 10;
}
void
CTxManage::PrepareFrame(const CProtocol& basisFrame, bool isBTCmaster)
{
@ -139,7 +146,13 @@ CTxManage::PrepareFrame(const CProtocol& basisFrame, bool isBTCmaster)
// 0x78 prevents the controller showing bum information when we parrot the OEM controller
// heater is happy either way, the OEM controller has set the max/min stuff already
if(isBTCmaster) {
m_TxFrame.setActiveMode(); // this allows heater to save the tuning params to EEPROM
if(m_sysUpdate) {
m_sysUpdate--;
m_TxFrame.setActiveMode(); // this allows heater to save the tuning params to EEPROM
}
else {
m_TxFrame.setPassiveMode(); // this prevents the tuning parameters being saved by heater
}
m_TxFrame.setFan_Min(NVstore.getHeaterTuning().Fmin);
m_TxFrame.setFan_Max(NVstore.getHeaterTuning().Fmax);
m_TxFrame.setPump_Min(NVstore.getHeaterTuning().getPmin());

View File

@ -26,6 +26,7 @@ class CTxManage
const int m_nStartDelay = 20;
const int m_nFrameTime = 14;
const int m_nFrontPorch = 0;
int m_sysUpdate;
public:
CTxManage(int TxGatePin, HardwareSerial& serial);
@ -38,6 +39,7 @@ public:
void begin();
const CProtocol& getFrame() const { return m_TxFrame; };
static void GateTerminate();
void queueSysUpdate(); // use to implant NV settings into heater
private:
HardwareSerial& m_BlueWireSerial;

View File

@ -0,0 +1,42 @@
#include "ABpreferences.h"
#include "nvs.h"
const char * nvs_errors2[] = { "OTHER", "NOT_INITIALIZED", "NOT_FOUND", "TYPE_MISMATCH", "READ_ONLY", "NOT_ENOUGH_SPACE", "INVALID_NAME", "INVALID_HANDLE", "REMOVE_FAILED", "KEY_TOO_LONG", "PAGE_FULL", "INVALID_STATE", "INVALID_LENGHT"};
#define nvs_error(e) (((e)>ESP_ERR_NVS_BASE)?nvs_errors2[(e)&~(ESP_ERR_NVS_BASE)]:nvs_errors2[0])
bool
ABpreferences::hasBytes(const char *key)
{
size_t len = 0;
if(!_started || !key){
return false;
}
esp_err_t err = nvs_get_blob(_handle, key, NULL, &len);
if(err == ESP_ERR_NVS_NOT_FOUND) { // NOT FOUND - expected!
return false;
}
if(err) { // remaining errors - print it
log_e("nvs_get_blob len fail: %s %s", key, nvs_error(err));
return false;
}
return len != 0;
}
bool
ABpreferences::hasString(const char *key)
{
size_t len = 0;
if(!_started || !key){
return false;
}
esp_err_t err = nvs_get_str(_handle, key, NULL, &len);
if(err == ESP_ERR_NVS_NOT_FOUND) { // NOT FOUND - expected!
return false;
}
if(err) { // remaining errors - print it
log_e("nvs_get_str len fail: %s %s", key, nvs_error(err));
return false;
}
return len != 0;
}

View File

@ -0,0 +1,9 @@
#include <Preferences.h>
class ABpreferences : public Preferences {
public:
bool hasBytes(const char* key);
bool hasString(const char* key);
};

View File

@ -50,12 +50,14 @@ const char* GPIOout1Names[] = {
"Disabled",
"Status",
"User",
"Thresh"
"Thresh",
"HeaterOn"
};
const char* GPIOout2Names[] = {
"Disabled",
"User",
"Thresh"
"Thresh",
"HeaterOn"
};
const char* GPIOalgNames[] = {
@ -216,7 +218,7 @@ CGPIOin2::_doThermostat(bool active)
}
const char*
CGPIOin2::getExtThermTime()
CGPIOin2:: getExtThermTime()
{
if((_OffHoldoff == 0) || (NVstore.getUserSettings().ThermostatMethod != 3) || (NVstore.getUserSettings().ExtThermoTimeout == 0))
return NULL;
@ -406,6 +408,12 @@ CGPIOoutBase::_doThresh()
}
}
void
CGPIOoutBase::_doActive()
{
int runstate = getHeaterInfo().getRunState(); // raw state, not suspend mode enhanced
digitalWrite(_pin, runstate ? HIGH : LOW); // activates output when heater is not in standby
}
/*********************************************************************************************************
** GPIO out manager
@ -503,7 +511,7 @@ CGPIOout1::begin(int pin, CGPIOout1::Modes mode)
void
CGPIOout1::setMode(CGPIOout1::Modes mode)
{
if(mode >= Disabled && mode <= Thresh)
if(mode >= Disabled && mode <= HtrActive)
_Mode = mode;
_prevState = -1;
if(_getPin())
@ -523,6 +531,7 @@ CGPIOout1::manage()
case CGPIOout1::Status: _doStatus(); break;
case CGPIOout1::User: _doUser(); break;
case CGPIOout1::Thresh: _doThresh(); break;
case CGPIOout1::HtrActive: _doActive(); break;
}
}
@ -579,6 +588,7 @@ CGPIOout1::_doStatus()
ledcAttachPin(pin, 0); // attach PWM to GPIO line
ledcWrite(0, _statusState);
_breatheDelay = millis() + BREATHINTERVAL;
_ledState = 2;
break;
case 2:
ledcDetachPin(pin); // detach PWM from IO line
@ -590,11 +600,13 @@ CGPIOout1::_doStatus()
_statusState = 255;
ledcWrite(0, _statusState);
_breatheDelay = millis() + BREATHINTERVAL;
_ledState = 3;
break;
case 4:
ledcDetachPin(pin); // detach PWM from IO line
_breatheDelay += (FLASHPERIOD - ONFLASHINTERVAL); // extended off
_setPinState(LOW);
_ledState = 4;
break;
}
}
@ -631,7 +643,7 @@ CGPIOout1::_doStopMode() // breath down PWM
_statusState &= 0xff;
ledcWrite(0, _statusState);
}
_ledState = 2;
_ledState = 3;
}
void
@ -657,7 +669,7 @@ CGPIOout1::_doSuspendMode() // brief flash
if(tDelta >= 0)
stretch = 0;
}
_ledState = stretch ? 1 : 0;
_ledState = 4;
}
uint8_t
@ -666,6 +678,7 @@ CGPIOout1::getState()
switch(_Mode) {
case User:
case Thresh:
case HtrActive:
return _getPinState();
case Status:
return _ledState; // special pulse extender for suspend mode
@ -695,7 +708,7 @@ CGPIOout2::begin(int pin, Modes mode)
void
CGPIOout2::setMode(CGPIOout2::Modes mode)
{
if(mode >= Disabled && mode <= Thresh)
if(mode >= Disabled && mode <= HtrActive)
_Mode = mode;
int pin = _getPin();
if(pin)
@ -714,6 +727,7 @@ CGPIOout2::manage()
case CGPIOout2::Disabled: break;
case CGPIOout2::User: _doUser(); break;
case CGPIOout2::Thresh: _doThresh(); break;
case CGPIOout2::HtrActive: _doActive(); break;
}
}
@ -724,6 +738,7 @@ CGPIOout2::getState()
switch (_Mode) {
case CGPIOout2::User:
case CGPIOout2::Thresh:
case CGPIOout2::HtrActive:
return _getPinState();
default:
return 0;

View File

@ -107,6 +107,7 @@ class CGPIOoutBase {
bool _userState;
int _pin;
protected:
void _doActive();
void _doThresh();
void _doUser();
bool _getUserState();
@ -126,7 +127,8 @@ public:
Disabled,
Status,
User,
Thresh
Thresh,
HtrActive
};
CGPIOout1();
void begin(int pin, Modes mode);
@ -152,7 +154,8 @@ public:
enum Modes {
Disabled,
User,
Thresh
Thresh,
HtrActive,
};
CGPIOout2();
void begin(int pin, Modes mode);
@ -207,8 +210,8 @@ struct sGPIOparams {
};
struct sGPIO {
bool outState[2];
bool inState[2];
uint8_t outState[2];
uint8_t inState[2];
int algVal;
sGPIO() {
outState[0] = outState[1] = false;

View File

@ -142,26 +142,26 @@ bool makeJSONString(CModerator& moderator, char* opStr, int len)
float tidyTemp = getTemperatureSensor();
tidyTemp = int(tidyTemp * 10 + 0.5) * 0.1f; // round to 0.1 resolution
if(tidyTemp > -80) {
bSend |= moderator.addJson("TempCurrent", tidyTemp, root);
bSend |= moderator.addJson("TempCurrent", tidyTemp, root, 5000);
}
if(getTempSensor().getNumSensors() > 1) {
getTempSensor().getTemperature(1, tidyTemp);
tidyTemp = int(tidyTemp * 10 + 0.5) * 0.1f; // round to 0.1 resolution
if(tidyTemp > -80) {
bSend |= moderator.addJson("Temp2Current", tidyTemp, root);
bSend |= moderator.addJson("Temp2Current", tidyTemp, root, 5000);
}
if(getTempSensor().getNumSensors() > 2) {
getTempSensor().getTemperature(2, tidyTemp);
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("Temp3Current", tidyTemp, root, 5000);
}
}
if(getTempSensor().getNumSensors() > 3) {
getTempSensor().getTemperature(3, tidyTemp);
tidyTemp = int(tidyTemp * 10 + 0.5) * 0.1f; // round to 0.1 resolution
if(tidyTemp > -80) {
bSend |= moderator.addJson("Temp4Current", tidyTemp, root);
bSend |= moderator.addJson("Temp4Current", tidyTemp, root, 5000);
}
}
}
@ -170,7 +170,7 @@ bool makeJSONString(CModerator& moderator, char* opStr, int len)
if(NVstore.getUserSettings().menuMode < 2) {
bSend |= moderator.addJson("TempMin", getHeaterInfo().getTemperature_Min(), root);
bSend |= moderator.addJson("TempMax", getHeaterInfo().getTemperature_Max(), root);
bSend |= moderator.addJson("TempBody", getHeaterInfo().getTemperature_HeatExchg(), root);
bSend |= moderator.addJson("TempBody", getHeaterInfo().getTemperature_HeatExchg(), root, 5000);
bSend |= moderator.addJson("RunState", getHeaterInfo().getRunStateEx(), root);
bSend |= moderator.addJson("RunString", getHeaterInfo().getRunStateStr(), root); // verbose it up!
bSend |= moderator.addJson("ErrorState", getHeaterInfo().getErrState(), root );
@ -182,13 +182,13 @@ bool makeJSONString(CModerator& moderator, char* opStr, int len)
bSend |= moderator.addJson("PumpActual", getHeaterInfo().getPump_Actual(), root );
bSend |= moderator.addJson("FanMin", getHeaterInfo().getFan_Min(), root );
bSend |= moderator.addJson("FanMax", getHeaterInfo().getFan_Max(), root );
bSend |= moderator.addJson("FanRPM", getFanSpeed(), root );
bSend |= moderator.addJson("FanVoltage", getHeaterInfo().getFan_Voltage(), root );
bSend |= moderator.addJson("FanRPM", getFanSpeed(), root, 2000 );
bSend |= moderator.addJson("FanVoltage", getHeaterInfo().getFan_Voltage(), root, 2500 );
bSend |= moderator.addJson("FanSensor", getHeaterInfo().getFan_Sensor(), root );
bSend |= moderator.addJson("InputVoltage", getBatteryVoltage(false), root );
bSend |= moderator.addJson("InputVoltage", getBatteryVoltage(false), root, 10000 );
bSend |= moderator.addJson("SystemVoltage", getHeaterInfo().getSystemVoltage(), root );
bSend |= moderator.addJson("GlowVoltage", getGlowVolts(), root );
bSend |= moderator.addJson("GlowCurrent", getGlowCurrent(), root );
bSend |= moderator.addJson("GlowVoltage", getGlowVolts(), root, 5000 );
bSend |= moderator.addJson("GlowCurrent", getGlowCurrent(), root, 5000 );
bSend |= moderator.addJson("BluewireStat", getBlueWireStatStr(), root );
}
@ -218,9 +218,12 @@ 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("FrostOn", NVstore.getUserSettings().FrostOn, root); // temp drops below this, auto start - 0 = disable
bSend |= moderator.addJson("FrostRise", NVstore.getUserSettings().FrostRise, root); // temp rise in frost mode till auto off
bSend |= moderator.addJson("PumpCount", RTC_Store.getFuelGauge(), root); // running count of pump strokes
bSend |= moderator.addJson("PumpCount", RTC_Store.getFuelGauge(), root, 10000); // running count of pump strokes
bSend |= moderator.addJson("PumpCal", NVstore.getHeaterTuning().pumpCal, root); // mL/stroke
bSend |= moderator.addJson("LowVoltCutout", NVstore.getHeaterTuning().getLVC(), root); // low voltage cutout
if(getTempSensor().getBME280().getCount()) {
bSend |= moderator.addJson("HumidStart", NVstore.getUserSettings().humidityStart, root); // BME280 ONLY
}
}
bSend |= moderator.addJson("TempOffset", getTempSensor().getOffset(0), root); // degC offset
bSend |= moderator.addJson("TempType", getTempSensor().getID(0), root); // BME280 vs DS18B20
@ -236,6 +239,15 @@ bool makeJSONStringEx(CModerator& moderator, char* opStr, int len)
bSend |= moderator.addJson("Temp4Offset", getTempSensor().getOffset(3), root); // degC offset
bSend |= moderator.addJson("Temp4Type", getTempSensor().getID(3), root); // BME280 vs DS18B20
}
if(getTempSensor().getBME280().getCount()) {
bSend |= moderator.addJson("Altitude", getHeaterInfo().getAltitude(), root, 60000); // BME280 ONLY
float humidity;
getTempSensor().getHumidity(humidity);
humidity = int(humidity * 10 + 0.5) * 0.1f; // round to 0.1 resolution
bSend |= moderator.addJson("Humidity", humidity, root, 30000); // BME280 ONLY
}
if(bSend) {
root.printTo(opStr, len);
}
@ -285,8 +297,8 @@ bool makeJSONStringGPIO(CModerator& moderator, char* opStr, int len)
bSend |= moderator.addJson("GPmodeIn2", GPIOin2Names[info.in2Mode], root);
bSend |= moderator.addJson("GPmodeOut1", GPIOout1Names[info.out1Mode], root);
bSend |= moderator.addJson("GPmodeOut2", GPIOout2Names[info.out2Mode], root);
bSend |= moderator.addJson("GPOutThr1", NVstore.getUserSettings().GPIO.thresh[0], root);
bSend |= moderator.addJson("GPOutThr2", NVstore.getUserSettings().GPIO.thresh[1], root);
bSend |= moderator.addJson("GPoutThr1", NVstore.getUserSettings().GPIO.thresh[0], root);
bSend |= moderator.addJson("GPoutThr2", NVstore.getUserSettings().GPIO.thresh[1], root);
bSend |= moderator.addJson("GPmodeAnlg", GPIOalgNames[info.algMode], root);
bSend |= moderator.addJson("ExtThermoTmout", (uint32_t)NVstore.getUserSettings().ExtThermoTimeout, root);
const char* stop = getExternalThermostatHoldTime();
@ -311,7 +323,8 @@ bool makeJSONStringMQTT(CModerator& moderator, char* opStr, int len)
sMQTTparams info = NVstore.getMQTTinfo();
bSend |= moderator.addJson("MEn", info.enabled, root);
bSend |= moderator.addJson("MOnline", isMQTTconnected(), root);
uint8_t online = isMQTTconnected() ? 1 : 0;
bSend |= moderator.addJson("MOnline", online, root);
bSend |= moderator.addJson("MHost", info.host, root);
bSend |= moderator.addJson("MPort", info.port, root);
bSend |= moderator.addJson("MUser", info.username, root);
@ -375,6 +388,8 @@ bool makeJSONStringIP(CModerator& moderator, char* opStr, int len)
bSend |= moderator.addJson("IP_STA", getWifiSTAAddrStr(), root);
bSend |= moderator.addJson("IP_STAMAC", getWifiSTAMACStr(), root);
bSend |= moderator.addJson("IP_STASSID", getSSID().c_str(), root);
bSend |= moderator.addJson("IP_STAGATEWAY", getWifiGatewayAddrStr(), root);
bSend |= moderator.addJson("IP_STARSSI", getWifiRSSI(), root, 10000);
bSend |= moderator.addJson("IP_OTA", NVstore.getUserSettings().enableOTA, root);
bSend |= moderator.addJson("BT_MAC", getBluetoothClient().getMAC(), root);
@ -518,10 +533,12 @@ void resetAllJSONmoderators()
#else
initJSONTimermoderator();
#endif
initJSONMQTTmoderator();
initJSONIPmoderator();
initJSONSysModerator();
resetJSONMQTTmoderator(); // initJSONMQTTmoderator();
resetJSONIPmoderator(); // initJSONIPmoderator();
resetJSONSysModerator(); // initJSONSysModerator();
GPIOmoderator.reset();
// create and send a validation code (then client knows AB is capable of reboot over JSON)
doJSONreboot(0);
}
void initJSONMQTTmoderator()
@ -575,3 +592,30 @@ void Expand(std::string& str)
}
}
void sendJSONtext(const char* jsonStr)
{
sendWebSocketString( jsonStr );
mqttPublishJSON(jsonStr);
getBluetoothClient().send( jsonStr );
}
void doJSONreboot(uint16_t PIN)
{
char jsonStr[20];
static uint16_t validate = 0;
if(PIN == 0) {
validate = random(1, 10000);
char jsonStr[20];
sprintf(jsonStr, "{\"Reboot\":%04d}", validate);
sendJSONtext( jsonStr );
}
else if(PIN == validate) {
strcpy(jsonStr, "{\"Reboot\":-1}");
sendJSONtext( jsonStr );
delay(1000);
ESP.restart(); // reboot
}
}

View File

@ -40,6 +40,7 @@ void resetJSONIPmoderator();
void resetJSONSysModerator();
void resetJSONMQTTmoderator();
void validateTimer(int ID);
void doJSONreboot(uint16_t code);
template<class T>
const char* createJSON(const char* name, T value)

View File

@ -51,13 +51,42 @@ public:
void reset(const char* name);
};
class sModeratorHoldoff {
unsigned long period;
unsigned long tripTime;
public:
sModeratorHoldoff() {
period = 0;
tripTime = 0;
}
sModeratorHoldoff(const sModeratorHoldoff& rhs) {
period = rhs.period;
tripTime = rhs.tripTime;
}
void set(unsigned long per) {
period = per;
reArm();
}
void reArm() {
tripTime = (millis() + period) | 1;
}
bool expired() {
long tDelta = millis() - tripTime;
return tDelta >= 0;
}
void expire() {
tripTime = millis();
}
};
template <class T>
class TModerator {
std::map<const char*, T> Memory;
std::map<const char*, sModeratorHoldoff> _holdoff;
public:
bool shouldSend(const char* name, T value);
bool addJson(const char* name, T value, JsonObject& root);
void setHoldoff(const char* name, unsigned long period);
bool addJson(const char* name, T value, JsonObject& root, unsigned long holdoff=0);
void reset();
void reset(const char* name);
};
@ -69,7 +98,21 @@ bool TModerator<T>::shouldSend(const char* name, T value)
auto it = Memory.find(name);
if(it != Memory.end()) {
retval = it->second != value;
it->second = value;
if(retval) {
// check if a minimum refresh interval has been defined
auto holdoff = _holdoff.find(name);
if(holdoff != _holdoff.end()) {
if(holdoff->second.expired()) {
holdoff->second.reArm();
}
else {
retval = false;
}
}
}
if(retval) {
it->second = value;
}
}
else {
Memory[name] = value;
@ -78,11 +121,26 @@ bool TModerator<T>::shouldSend(const char* name, T value)
}
template<class T>
bool TModerator<T>::addJson(const char* name, T value, JsonObject& root)
void TModerator<T>::setHoldoff(const char* name, unsigned long period)
{
if(period) {
auto it = _holdoff.find(name);
if(it == _holdoff.end()) {
sModeratorHoldoff holdoff;
holdoff.set(period);
_holdoff[name] = holdoff;
}
}
}
template<class T>
bool TModerator<T>::addJson(const char* name, T value, JsonObject& root, unsigned long holdoff)
{
setHoldoff(name, holdoff);
bool retval = shouldSend(name, value);
if(retval)
if(retval) {
root.set(name, value);
}
return retval;
}
@ -93,15 +151,26 @@ void TModerator<T>::reset()
for(auto it = Memory.begin(); it != Memory.end(); ++it) {
it->second = it->second+100;
}
for(auto it = _holdoff.begin(); it != _holdoff.end(); ++it) {
it->second.expire();
}
}
template<class T>
void TModerator<T>::reset(const char* name)
{
auto it = Memory.find(name);
if(it != Memory.end()) {
DebugPort.printf("Resetting moderator: \"%s\"", name);
it->second = it->second+100;
{
auto it = Memory.find(name);
if(it != Memory.end()) {
DebugPort.printf("Resetting moderator: \"%s\"", name);
it->second = it->second+100;
}
}
{
auto it = _holdoff.find(name);
if(it != _holdoff.end()) {
it->second.expire();
}
}
}
@ -123,8 +192,8 @@ public:
return u32Moderator.addJson(name, value, root);
};
// float values
bool addJson(const char* name, float value, JsonObject& root) {
return fModerator.addJson(name, value, root);
bool addJson(const char* name, float value, JsonObject& root, unsigned long holdoff=0) {
return fModerator.addJson(name, value, root, holdoff);
};
// uint8_t values
bool addJson(const char* name, uint8_t value, JsonObject& root) {

View File

@ -24,6 +24,7 @@
#include "DebugPort.h"
#include <functional>
#include <string.h>
#include <math.h>
#define INBOUNDS(TST, MIN, MAX) (((TST) >= (MIN)) && ((TST) <= (MAX)))
@ -31,20 +32,31 @@
bool
CESP32_NVStorage::validatedLoad(const char* key, char* val, int maxlen, const char* defVal)
{
char probe[128];
bool retval = true;
strcpy(probe, "TestPresence");
int len = preferences.getString(key, probe, 127);
if(len == 0 || strcmp(probe, "TestPresence") == 0) {
if(!preferences.hasString(key)) {
preferences.putString(key, defVal);
DebugPort.printf("CESP32HeaterStorage::validatedLoad<char*> default installed %s=%s", key, defVal);
retval = false;
DebugPort.printf("CESP32HeaterStorage::validatedLoad<char*> default installed %s=%s\r\n", key, defVal);
}
preferences.getString(key, val, maxlen);
val[maxlen] = 0; // ensure null terminated
return retval;
}
bool
CESP32_NVStorage::validatedLoad(const char* key, uint8_t* val, int len)
{
bool retval = true;
if(!preferences.hasBytes(key)) {
DebugPort.printf("CESP32HeaterStorage::validatedLoad<uint8_t*> default installed for %s\r\n", key);
preferences.putBytes(key, val, len);
}
len = preferences.getBytes(key, val, len);
if(len == 0) {
retval = false;
}
return retval;
}
bool
CESP32_NVStorage::validatedLoad(const char* key, uint8_t& val, uint8_t defVal, std::function<bool(uint8_t, uint8_t, uint8_t)> validator, uint8_t min, uint8_t max, uint8_t mask)
{
@ -129,19 +141,42 @@ CESP32_NVStorage::validatedLoad(const char* key, uint32_t& val, uint32_t defVal,
bool
CESP32_NVStorage::validatedLoad(const char* key, float& val, float defVal, float min, float max)
{
val = preferences.getFloat(key, defVal);
// preferences.getFloat() does not do a default value for use
// use some skull duggery via unsigned long to get one installed
unsigned long* pUL = (unsigned long*)&defVal; // point to bytes of float default value as a long
unsigned long ULVal = *pUL; // copy as an unsigned long
unsigned long tmpVal = preferences.getULong(key, ULVal);
// pUL = (unsigned long*)&val; // point to val we exchange, as an usigned long
float* ptmpVal = (float*)&tmpVal; // create a pointer to flaot, that was our UL returned value
float* pVal = &val; // point to our FP exchange value
*pVal = *ptmpVal; // copy as a float
// val = preferences.getFloat(key, defVal);
if(!INBOUNDS(val, min, max)) {
DebugPort.printf("CESP32HeaterStorage::validatedLoad<float> invalid read %s=%f", key, val);
DebugPort.printf(" validator(%f,%f) reset to %f\r\n", min, max, defVal);
val = defVal;
preferences.putFloat(key, val);
// preferences.putFloat(key, val);
pUL = (unsigned long*)&val; // point to bytes of float default value as a long
ULVal = *pUL; // copy as an unsigned long
preferences.putULong(key, ULVal);
return false;
}
return true;
}
size_t
CESP32_NVStorage::saveFloat(const char* key, float val)
{
unsigned long* pUL = (unsigned long*)&val; // point to bytes of float default value as a long
unsigned long ULVal = *pUL; // copy as an unsigned long
return preferences.putULong(key, ULVal);
}
bool finBounds(float test, float minLim, float maxLim)
{
return INBOUNDS(test, minLim, maxLim);

View File

@ -22,7 +22,7 @@
#ifndef __BTC_NV_CORE_H__
#define __BTC_NV_CORE_H__
#include <Preferences.h>
#include "ABpreferences.h"
#include <functional>
@ -46,15 +46,17 @@ class CNVStorage {
class CESP32_NVStorage {
protected:
Preferences preferences;
ABpreferences preferences;
protected:
bool validatedLoad(const char* key, int8_t& val, int8_t defVal, std::function<bool(int8_t, int8_t, int8_t)> validator, int8_t min, int8_t max);
bool validatedLoad(const char* key, uint8_t& val, uint8_t defVal, std::function<bool(uint8_t, uint8_t, uint8_t)> validator, uint8_t min, uint8_t max, uint8_t mask=0xff);
bool validatedLoad(const char* key, uint16_t& val, uint16_t defVal, std::function<bool(uint16_t, uint16_t, uint16_t)> validator, uint16_t min, uint16_t max);
bool validatedLoad(const char* key, long& val, long defVal, long min, long max);
bool validatedLoad(const char* key, char* val, int maxlen, const char* defVal);
bool validatedLoad(const char* key, char* val, int maxlen, const char* defVal); // string
bool validatedLoad(const char* key, uint8_t* val, int len); // bytes
bool validatedLoad(const char* key, float& val, float defVal, float min, float max);
bool validatedLoad(const char* key, uint32_t& val, uint32_t defVal, uint32_t min, uint32_t max);
size_t saveFloat(const char* key, float val);
};

View File

@ -23,7 +23,7 @@
#include "NVStorage.h"
#include "DebugPort.h"
#include <driver/adc.h>
#include <string.h>
bool
sNVStore::valid()
@ -131,6 +131,10 @@ CHeaterStorage::getMQTTinfo() const
void
CHeaterStorage::setMQTTinfo(const sMQTTparams& info)
{
if(_calValues.MQTT != info) {
requestMQTTrestart();
}
_calValues.MQTT = info;
}
@ -268,13 +272,18 @@ sHeaterTuning::load()
validatedLoad("tempOffset0", DS18B20probe[0].offset, 0.0, -10.0, +10.0);
validatedLoad("tempOffset1", DS18B20probe[1].offset, 0.0, -10.0, +10.0);
validatedLoad("tempOffset2", DS18B20probe[2].offset, 0.0, -10.0, +10.0);
preferences.getBytes("probeSerial0", DS18B20probe[0].romCode.bytes, 8);
preferences.getBytes("probeSerial1", DS18B20probe[1].romCode.bytes, 8);
preferences.getBytes("probeSerial2", DS18B20probe[2].romCode.bytes, 8);
memset(DS18B20probe[0].romCode.bytes, 0, 8);
memset(DS18B20probe[1].romCode.bytes, 0, 8);
memset(DS18B20probe[2].romCode.bytes, 0, 8);
validatedLoad("probeSerial0", DS18B20probe[0].romCode.bytes, 8);
validatedLoad("probeSerial1", DS18B20probe[1].romCode.bytes, 8);
validatedLoad("probeSerial2", DS18B20probe[2].romCode.bytes, 8);
validatedLoad("tempOffsetBME", BME280probe.offset, 0.0, -10.0, +10.0);
validatedLoad("probeBMEPrmy", BME280probe.bPrimary, 0, u8inBounds, 0, 1);
preferences.end();
// save();
// for(int i=0; i<3; i++) {
// DebugPort.printf("Rd Probe[%d] %02X:%02X:%02X:%02X:%02X:%02X\r\n",
// i,
@ -291,6 +300,20 @@ sHeaterTuning::load()
void
sHeaterTuning::save()
{
sHeaterTuning currentSettings;
currentSettings.load();
bool requestHeaterUpdate = false;
requestHeaterUpdate |= currentSettings.sysVoltage != this->sysVoltage;
requestHeaterUpdate |= currentSettings.fanSensor != this->fanSensor;
requestHeaterUpdate |= currentSettings.glowDrive != this->glowDrive;
requestHeaterUpdate |= currentSettings.Pmin != this->Pmin;
requestHeaterUpdate |= currentSettings.Pmax != this->Pmax;
requestHeaterUpdate |= currentSettings.Fmin != this->Fmin;
requestHeaterUpdate |= currentSettings.Fmax != this->Fmax;
if(requestHeaterUpdate) {
reqHeaterCalUpdate();
}
// section for heater calibration params
// **** MAX LENGTH is 15 for names ****
preferences.begin("Calibration", false);
@ -302,15 +325,55 @@ sHeaterTuning::save()
preferences.putUChar("fanSensor", fanSensor);
preferences.putUChar("glowDrive", glowDrive);
preferences.putUChar("lowVolts", lowVolts);
preferences.putFloat("pumpCal", pumpCal);
preferences.putFloat("tempOffset0", DS18B20probe[0].offset);
preferences.putFloat("tempOffset1", DS18B20probe[1].offset);
preferences.putFloat("tempOffset2", DS18B20probe[2].offset);
saveFloat("pumpCal", pumpCal);
saveFloat("tempOffset0", DS18B20probe[0].offset);
saveFloat("tempOffset1", DS18B20probe[1].offset);
saveFloat("tempOffset2", DS18B20probe[2].offset);
// preferences.putFloat("pumpCal", pumpCal);
// preferences.putFloat("tempOffset0", DS18B20probe[0].offset);
// preferences.putFloat("tempOffset1", DS18B20probe[1].offset);
// preferences.putFloat("tempOffset2", DS18B20probe[2].offset);
/*// START TESTO
for(int i=0; i<8; i++)
DS18B20probe[0].romCode.bytes[i] = i;
// END TESTO*/
preferences.putBytes("probeSerial0", DS18B20probe[0].romCode.bytes, 8);
preferences.putBytes("probeSerial1", DS18B20probe[1].romCode.bytes, 8);
preferences.putBytes("probeSerial2", DS18B20probe[2].romCode.bytes, 8);
preferences.putFloat("tempOffsetBME", BME280probe.offset);
// preferences.putFloat("tempOffsetBME", BME280probe.offset);
saveFloat("tempOffsetBME", BME280probe.offset);
preferences.putUChar("probeBMEPrmy", BME280probe.bPrimary);
/*// START TESTO
preferences.getBytes("probeSerial0", DS18B20probe[0].romCode.bytes, 8);
DebugPort.printf("getBytes %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X",
DS18B20probe[0].romCode.bytes[0],
DS18B20probe[0].romCode.bytes[1],
DS18B20probe[0].romCode.bytes[2],
DS18B20probe[0].romCode.bytes[3],
DS18B20probe[0].romCode.bytes[4],
DS18B20probe[0].romCode.bytes[5],
DS18B20probe[0].romCode.bytes[6],
DS18B20probe[0].romCode.bytes[7]
);
uint64_t tVal = preferences.getULong64("probeSerial0", 0xffffffffffffffff);
unsigned char* pTst = (unsigned char*)&tVal;
DebugPort.printf("getULong %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X",
pTst[0],
pTst[1],
pTst[2],
pTst[3],
pTst[4],
pTst[5],
pTst[6],
pTst[7]
);
// END TESTO*/
preferences.end();
// for(int i=0; i<3; i++) {
@ -417,8 +480,8 @@ sUserSettings::load()
preferences.putUChar("GPIOout1Mode", GPIO.out1Mode); // set new
preferences.putUChar("GPIOout2Mode", GPIO.out2Mode); // set new
}
validatedLoad("GPIOout1Mode", tVal, 0, u8inBounds, 0, 3); GPIO.out1Mode = (CGPIOout1::Modes)tVal;
validatedLoad("GPIOout2Mode", tVal, 0, u8inBounds, 0, 2); GPIO.out2Mode = (CGPIOout2::Modes)tVal;
validatedLoad("GPIOout1Mode", tVal, 0, u8inBounds, 0, 4); GPIO.out1Mode = (CGPIOout1::Modes)tVal;
validatedLoad("GPIOout2Mode", tVal, 0, u8inBounds, 0, 3); GPIO.out2Mode = (CGPIOout2::Modes)tVal;
validatedLoad("GPIOout1Thresh", GPIO.thresh[0], 0, s8inBounds, -50, 50);
validatedLoad("GPIOout2Thresh", GPIO.thresh[1], 0, s8inBounds, -50, 50);
validatedLoad("GPIOalgMode", tVal, 0, u8inBounds, 0, 2); GPIO.algMode = (CGPIOalg::Modes)tVal;
@ -447,7 +510,8 @@ sUserSettings::save()
preferences.putUChar("thermostat", useThermostat);
preferences.putUChar("degF", degF);
preferences.putUChar("thermoMethod", ThermostatMethod);
preferences.putFloat("thermoWindow", ThermostatWindow);
// preferences.putFloat("thermoWindow", ThermostatWindow);
saveFloat("thermoWindow", ThermostatWindow);
preferences.putUChar("frostOn", FrostOn);
preferences.putUChar("frostRise", FrostRise);
// preferences.putUChar("enableWifi", enableWifi);
@ -484,9 +548,9 @@ sMQTTparams::load()
validatedLoad("enabled", enabled, 0, u8inBounds, 0, 1);
validatedLoad("port", port, 1883, u16inBounds, 0, 0xffff);
validatedLoad("qos", qos, 0, u8inBounds, 0, 2);
validatedLoad("host", host, 127, "");
validatedLoad("username", username, 31, "");
validatedLoad("password", password, 31, "");
validatedLoad("host", host, 127, "broker");
validatedLoad("username", username, 31, "username");
validatedLoad("password", password, 31, "password");
validatedLoad("topic", topicPrefix, 31, "Afterburner");
preferences.end();
}
@ -572,4 +636,4 @@ sHourMeter::load()
validatedLoad("GlowTime", GlowTime, 0, 0, 0xffffffffL);
preferences.end();
DebugPort.printf("Hourmeter NV read: Run=%d, Glow=%d\r\n", RunTime, GlowTime);
}
}

View File

@ -272,6 +272,16 @@ struct sMQTTparams : public CESP32_NVStorage {
topicPrefix[31] = 0;
return *this;
}
bool operator!=(const sMQTTparams& rhs) {
bool retval = false;
retval |= enabled != rhs.enabled;
retval |= port != rhs.port;
retval |= qos != rhs.qos;
retval |= strcmp(host, rhs.host) != 0;
retval |= strcmp(password, rhs.password) != 0;
retval |= strcmp(topicPrefix, rhs.topicPrefix) != 0;
return retval;
}
void load();
void save();
bool valid();

View File

@ -432,7 +432,7 @@ CBME280Sensor::getTemperature(float& tempReading, bool filtered)
}
CSensor::getTemperature(tempReading, filtered);
tempReading += NVstore.getHeaterTuning().BME280probe.offset;;
// tempReading += NVstore.getHeaterTuning().BME280probe.offset;;
return true;
}
@ -559,16 +559,26 @@ CTempSense::setOffset(int usrIdx, float offset)
bool
CTempSense::getTemperature(int usrIdx, float& temperature, bool filtered)
{
bool bRetVal = false;
float offset = 0;
switch(getSensorType(usrIdx)) {
case 0:
return BME280.getTemperature(temperature, filtered);
bRetVal = BME280.getTemperature(temperature, filtered);
offset = getOffset(usrIdx);
break;
case 1:
return DS18B20.getTemperature(usrIdx, temperature, filtered);
bRetVal = DS18B20.getTemperature(usrIdx, temperature, filtered);
offset = getOffset(usrIdx);
break;
case 2:
return DS18B20.getTemperature(usrIdx-1, temperature, filtered);
default:
return false;
bRetVal = DS18B20.getTemperature(usrIdx-1, temperature, filtered);
offset = getOffset(usrIdx-1);
break;
}
if(bRetVal) {
temperature += offset;
}
return bRetVal;
}
int

View File

@ -422,10 +422,22 @@ void DecodeCmd(const char* cmd, String& payload)
else if(strcmp("FrostRise", cmd) == 0) {
sUserSettings us = NVstore.getUserSettings();
us.FrostRise = payload.toInt();
if(INBOUNDS(us.FrostRise, 1, 30)) {
if(INBOUNDS(us.FrostRise, 0, 30)) {
NVstore.setUserSettings(us);
NVstore.save();
}
}
else if(strcmp("HumidStart", cmd) == 0) {
sUserSettings us = NVstore.getUserSettings();
us.humidityStart = payload.toInt();
if((us.humidityStart == 0) || INBOUNDS(us.humidityStart, 50, 100)) {
NVstore.setUserSettings(us);
NVstore.save();
}
}
else if(strcmp("Reboot", cmd) == 0) {
int16_t code = payload.toInt();
doJSONreboot(code);
}
}

View File

@ -90,6 +90,7 @@ extern int sysUptime();
extern void doJSONwatchdog(int topup);
extern void reloadScreens();
extern CTempSense& getTempSensor() ;
extern void reqHeaterCalUpdate();
void setSSID(const char* name);
@ -99,5 +100,6 @@ extern void ShowOTAScreen(int percent=0, eOTAmodes updateType=eOTAnormal);
extern void updateMQTT();
extern void refreshMQTT();
extern void requestMQTTrestart();
#endif

View File

@ -51,7 +51,8 @@ extern void DecodeCmd(const char* cmd, String& payload);
AsyncMqttClient MQTTclient;
char topicnameJSONin[128];
char topicnameCmd[128];
CModerator MQTTmoderator;
CModerator MQTTmoderator; // for basic MQTT interface
unsigned long MQTTrestart = 0;
void subscribe(const char* topic);
@ -100,14 +101,10 @@ void onMqttConnect(bool sessionPresent)
sprintf(statusTopic, "%s/status", NVstore.getMQTTinfo().topicPrefix);
sprintf(topicnameJSONin, "%s/JSONin", NVstore.getMQTTinfo().topicPrefix);
sprintf(topicnameCmd, "%s/cmd/#", NVstore.getMQTTinfo().topicPrefix);
// subscribe to that topic
// DebugPort.printf("MQTT: Subscribing to \"%s\"\r\n", topicnameJSONin);
// MQTTclient.subscribe(topicnameJSONin, NVstore.getMQTTinfo().qos);
// MQTTclient.subscribe(topicnameCmd, NVstore.getMQTTinfo().qos);
// MQTTclient.subscribe(statusTopic, NVstore.getMQTTinfo().qos);
subscribe(topicnameJSONin);
subscribe(topicnameCmd);
subscribe(statusTopic);
subscribe(topicnameJSONin); // subscribe to the JSONin topic
subscribe(topicnameCmd); // subscribe to the basic command topic
subscribe(statusTopic); // subscribe to the status topic
// spit out an "I'm here" message
MQTTclient.publish(statusTopic, NVstore.getMQTTinfo().qos, true, "online");
@ -152,7 +149,7 @@ void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties
}
else if(strcmp(topic, statusTopic) == 0) { // check if incoming topic is our general status
if(strcmp(szPayload, "1") == 0) {
MQTTmoderator.reset();
// MQTTmoderator.reset();
MQTTclient.publish(statusTopic, NVstore.getMQTTinfo().qos, true, "online");
}
}
@ -199,9 +196,11 @@ bool mqttInit()
#else
mqttReconnect = 0;
#endif
MQTTrestart = 0;
memset(topicnameJSONin, 0, sizeof(topicnameJSONin));
DebugPort.println("MQTT: Initialising...");
MQTTclient.disconnect(true);
long escape = millis() + 10000;
while(MQTTclient.connected()) {
@ -274,6 +273,16 @@ void kickMQTT() {
void doMQTT()
{
// manage restart of MQTT
if(MQTTrestart) {
long tDelta = millis() - MQTTrestart;
if(tDelta > 0) {
MQTTrestart = 0;
mqttInit();
// connectToMqtt();
}
}
// most MQTT is managed via callbacks!!!
if(NVstore.getMQTTinfo().enabled) {
#ifndef USE_RTOS_MQTTTIMER
@ -411,4 +420,9 @@ void subscribe(const char* topic)
MQTTclient.subscribe(topic, NVstore.getMQTTinfo().qos);
}
void requestMQTTrestart()
{
MQTTrestart = (millis() + 1000) | 1;
}
#endif

View File

@ -162,6 +162,7 @@ String getContentType(String filename) { // convert the file extension to the MI
bool handleFileRead(String path) { // send the right file to the client (if it exists)
DebugPort.println("handleFileRead: " + path);
if (path.endsWith("/")) path += "index.html"; // If a folder is requested, send the index file
path.replace("%20", " "); // convert HTML spaces to normal spaces
String contentType = getContentType(path); // Get the MIME type
String pathWithGz = path + ".gz";
if(SPIFFS.exists(pathWithGz) || SPIFFS.exists(path)) { // If the file exists as a compressed archive, or normal
@ -622,8 +623,10 @@ void listSPIFFS(const char * dirname, uint8_t levels, String& HTMLreport, int wi
String ers;
String rename;
if(withHTMLanchors == 2) {
rename = "<button class='rename' onClick=onRename('" + fn + "')>Rename</button>";
ers = "<input class='del' type='button' value='X' onClick=onErase('" + fn + "')>";
String htmlNm = fn;
htmlNm.replace(" ", "%20");
rename = "<button class='rename' onClick=onRename('" + htmlNm + "')>Rename</button>";
ers = "<input class='del' type='button' value='X' onClick=onErase('" + htmlNm + "')>";
}
if(withHTMLanchors) {
String fn2;
@ -638,6 +641,7 @@ void listSPIFFS(const char * dirname, uint8_t levels, String& HTMLreport, int wi
fn2.remove(fn2.length()-3, 3); // strip trailing ".gz"
}
if(fn2.length() != 0) {
fn2.replace(" ", "%20");
// create hyperlink if web page file
fn = "<a href=\"" + fn2 + "\">" + file.name() + "</a>";
}
@ -678,6 +682,7 @@ void addTableData(String& HTML, String dta)
void onErase()
{
String filename = server.arg("filename"); // get request argument value by name
filename.replace("%20", " "); // convert HTML spaces to real spaces
if(filename.length() != 0) {
DebugPort.printf("onErase: %s ", filename.c_str());
@ -986,6 +991,8 @@ void onRename()
DebugPort.println("WEB: POST /reboot");
String oldname = server.arg("oldname"); // get request argument value by name
String newname = server.arg("newname"); // get request argument value by name
newname.replace("%20", " "); // convert html spaces to real spaces
oldname.replace("%20", " ");
if(oldname != "" && newname != "") {
DebugPort.printf("Renaming %s to %s\r\n", oldname.c_str(), newname.c_str());
SPIFFS.rename(oldname.c_str(), newname.c_str());

View File

@ -279,6 +279,38 @@ const char* getWifiSTAAddrStr()
else
return "";
}
const char* getWifiGatewayAddrStr()
{
if(NVstore.getUserSettings().wifiMode) {
IPAddress IPaddr = WiFi.gatewayIP(); // use stepping stone - function returns an automatic stack var - LAME!
static char GWIPaddr[16];
sprintf(GWIPaddr, "%d.%d.%d.%d", IPaddr[0], IPaddr[1], IPaddr[2], IPaddr[3]);
return GWIPaddr;
}
else
return "";
}
int8_t getWifiRSSI()
{
if(NVstore.getUserSettings().wifiMode) {
static unsigned long updateRSSI = millis() + 2500;
static int8_t RSSI = 0;
long tDelta = millis() - updateRSSI;
if(tDelta > 0) {
updateRSSI = millis() + 2500;
RSSI = WiFi.RSSI();
}
return RSSI;
}
else {
return 0;
}
}
const char* getWifiAPMACStr()
{

View File

@ -28,8 +28,10 @@ void doWiFiManager();
bool initWifi(const char *failedssid, const char *failedpassword);
const char* getWifiAPAddrStr();
const char* getWifiSTAAddrStr();
const char* getWifiGatewayAddrStr();
const char* getWifiAPMACStr();
const char* getWifiSTAMACStr();
int8_t getWifiRSSI();
String getSSID();
bool isWifiConnected();

View File

@ -63,6 +63,8 @@ sBrowserUpload::begin(String& filename, int filesize)
}
}
else {
SrcFile.name.replace("%20", " "); // convert HTML spaces to real spaces
// SPIFFS UPLOAD START
DebugPort.printf("Starting SPIFFS upload: %s\r\n", SrcFile.name.c_str());