This commit is contained in:
Ray Jones 2019-07-20 06:53:12 +10:00
parent a543ba0748
commit 04ae988d2d
19 changed files with 349 additions and 343 deletions

View File

@ -183,10 +183,14 @@ bool bHasHtrData = false;
// these variables will persist over a soft reboot.
__NOINIT_ATTR bool bForceInit; // = false;
__NOINIT_ATTR bool bUserON; // = false;
__NOINIT_ATTR uint8_t demandDegC;
__NOINIT_ATTR uint8_t demandPump;
//__NOINIT_ATTR bool bUserON; // = false;
//__NOINIT_ATTR uint8_t demandDegC;
//__NOINIT_ATTR uint8_t demandPump;
//uint8_t demandDegC;
//uint8_t demandPump;
//bool bCyclicEngaged;
CFuelGauge FuelGauge;
CRTC_Store RTC_Store;
bool bReportBlueWireData = REPORT_RAW_DATA;
bool bReportJSONData = REPORT_JSON_TRANSMIT;
@ -305,13 +309,11 @@ extern "C" unsigned long __wrap_millis() {
void setup() {
// ensure cyclic mode is disabled after power on
bool bPowerUpInit = false;
if(rtc_get_reset_reason(0) == 1/* || bForceInit*/) {
bPowerUpInit = true;
bForceInit = false;
bUserON = false;
demandPump = demandDegC = 22;
}
// bool bPowerUpInit = false;
// if(rtc_get_reset_reason(0) == 1/* || bForceInit*/) {
// bPowerUpInit = true;
// bForceInit = false;
// }
// initially, ensure the GPIO outputs are not activated during startup
// (GPIO2 tends to be one with default chip startup)
@ -334,7 +336,7 @@ void setup() {
DebugPort.println("_______________________________________________________________");
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 :-)
// DebugPort.printf("Previous user ON = %d\r\n", bUserON); // state flag required for cyclic mode to persist properly after a WD reboot :-)
// initialise DS18B20 sensor interface
TempSensor.begin(DS18B20_Pin);
@ -383,6 +385,9 @@ void setup() {
BootTime = Clock.get().secondstime();
ScreenManager.begin(bNoClock);
if(!bNoClock && Clock.lostPower()) {
ScreenManager.selectMenu(CScreenManager::BranchMenu, CScreenManager::SetClockUI);
}
#if USE_WIFI == 1
@ -442,9 +447,12 @@ void setup() {
FilteredSamples.Fan.setAlpha(0.7);
FilteredSamples.AmbientTemp.reset(-100.0);
// if(bPowerUpInit) {
FuelGauge.init();
// }
RTC_Store.begin();
FuelGauge.init(RTC_Store.getFuelGauge());
// demandDegC = RTC_Store.getDesiredTemp();
// demandPump = RTC_Store.getDesiredPump();
// bCyclicEngaged = RTC_Store.getCyclicEngaged();
DebugPort.printf("Previous cyclic active = %d\r\n", RTC_Store.getCyclicEngaged()); // state flag required for cyclic mode to persist properly after a WD reboot :-)
delay(1000); // just to hold the splash screeen for while
}
@ -812,7 +820,7 @@ void DebugReportFrame(const char* hdr, const CProtocol& Frame, const char* ftr)
void manageCyclicMode()
{
const sCyclicThermostat& cyclic = NVstore.getUserSettings().cyclic;
if(cyclic.Stop && bUserON) { // cyclic mode enabled, and user has started heater
if(cyclic.Stop && RTC_Store.getCyclicEngaged()) { // cyclic mode enabled, and user has started heater
int stopDeltaT = cyclic.Stop + 1; // bump up by 1 degree - no point invoking at 1 deg over!
float deltaT = FilteredSamples.AmbientTemp.getValue() - getDemandDegC();
// DebugPort.printf("Cyclic=%d bUserOn=%d deltaT=%d\r\n", cyclic, bUserON, deltaT);
@ -876,13 +884,13 @@ bool validateFrame(const CProtocol& frame, const char* name)
void requestOn()
{
heaterOn();
bUserON = true; // for cyclic mode
RTC_Store.setCyclicEngaged(true); // for cyclic mode
}
void requestOff()
{
heaterOff();
bUserON = false; // for cyclic mode
RTC_Store.setCyclicEngaged(false); // for cyclic mode
}
void heaterOn()
@ -898,38 +906,42 @@ void heaterOff()
}
bool reqTemp(uint8_t newTemp, bool save)
bool reqDemand(uint8_t newDemand, bool save)
{
if(bHasOEMController)
return false;
uint8_t max = DefaultBTCParams.getTemperature_Max();
uint8_t min = DefaultBTCParams.getTemperature_Min();
if(newTemp >= max)
newTemp = max;
if(newTemp <= min)
newTemp = min;
if(newDemand >= max)
newDemand = max;
if(newDemand <= min)
newDemand = min;
// set and save the demand to NV storage
// note that we now maintain fixed Hz and Thermostat set points seperately
if(getThermostatModeActive())
demandDegC = newTemp;
else
demandPump = newTemp;
if(getThermostatModeActive()) {
RTC_Store.setDesiredTemp(newDemand);
}
else {
RTC_Store.setDesiredPump(newDemand);
}
ScreenManager.reqUpdate();
return true;
}
bool reqTempDelta(int delta)
bool reqDemandDelta(int delta)
{
uint8_t newTemp;
if(getThermostatModeActive())
newTemp = demandDegC + delta;
else
newTemp = demandPump + delta;
uint8_t newDemand;
if(getThermostatModeActive()) {
newDemand = RTC_Store.getDesiredTemp() + delta;
}
else {
newDemand = RTC_Store.getDesiredPump() + delta;
}
return reqTemp(newTemp);
return reqDemand(newDemand);
}
bool reqThermoToggle()
@ -997,7 +1009,7 @@ void forceBootInit()
uint8_t getDemandDegC()
{
return demandDegC;
return RTC_Store.getDesiredTemp();
}
void setDemandDegC(uint8_t val)
@ -1005,12 +1017,12 @@ void setDemandDegC(uint8_t val)
uint8_t max = DefaultBTCParams.getTemperature_Max();
uint8_t min = DefaultBTCParams.getTemperature_Min();
BOUNDSLIMIT(val, min, max);
demandDegC = val;
RTC_Store.setDesiredTemp(val);
}
uint8_t getDemandPump()
{
return demandPump;
return RTC_Store.getDesiredPump();
}
@ -1021,9 +1033,9 @@ float getTemperatureDesired()
}
else {
if(getThermostatModeActive())
return demandDegC;
return RTC_Store.getDesiredTemp();
else
return demandPump;
return RTC_Store.getDesiredPump();
}
}
@ -1273,7 +1285,7 @@ int getSmartError()
bool isCyclicActive()
{
return bUserON && NVstore.getUserSettings().cyclic.isEnabled();
return RTC_Store.getCyclicEngaged() && NVstore.getUserSettings().cyclic.isEnabled();
}
void setupGPIO()

View File

@ -1,53 +0,0 @@
Hi there,
I've been somewhat overwhelmed with requests and actually getting units built.
At the moment all PCBs I have built for the cased variety are depleted, but I will be building more PCBs over the coming days.
I have the parts, but they lack a touch of solder in the right places :-)
Please be aware this is part time affair after a day's work and currently this is consuming all my spare time!
An important question for the sale of the early units is does the prospect of reflashing firmware concern you?
There are still some firmware aspects I'm working on, and the web pages still need a fair bit of work. I would hate for units to become stale with old out dated firmware!!
This requires the Arduino environment to be set up for an ESP32, and in cases of dire emergency a USB to serial adapter.
Over The Air programming using WiFi is also available via batch file and a pre-compiled binary which makes this a lot easier for the firmware, but web content presently must be via the Arduino environment.
I have two possibilities:
1/ the controller as you can currently view in the GitLab repo.
This would be supplied as is, without a case, but I have produced a 3D model you can print.
GitLab repo: https://gitlab.com/mrjones.id.au/bluetoothheater/wikis/home
2/ a newer version that is housed in a case.
It also provides a few conditioned I/O expansion lines:
2 digital inputs, 2 digital outputs, 1 analogue input.
The software is still yet to be fully dealt with for this aspect, but you can presently start and stop the heater, and turn outputs on or off upon command.
Pricing:
Option 1: 50AUD + post and handling
Option 2: 75AUD + post and handling
For domestic customers in Australia, P&H is 10AUD
For international customers P&H tends to be around 30-36AUD depending upon which region you reside.
Payment is accepted via PayPal.
Also please confirm your present controller is a digital style.
Please refer to my wiki at the GitLab repo: https://gitlab.com/mrjones.id.au/bluetoothheater/wikis/home
Another criteria here is what style connector you presently have.
Most have a triangular 3 pin plug, but newer units are coming with a smaller round 3 pin connector.
Please advise.
Option 2 with the case, has a range of colours available.
Black or White case.
Black, Grey, Blue or Green buttons.
Case for Option 1 https://www.thingiverse.com/thing:3398068
Rest assured the pricing is purely for the hardware.
The software will always be open source and freely available for upgrades and bug fixes.
And as mentioned, you can expect extra features to appear as time goes on.
An example of things in the todo list are Low Voltage shutdown, and a "de-coke burn" which will be full power, but a touch leaner than usual.
I'm also yet to explore a 433MHz Receive option using the garage style remote controls.
Thanks again for your interest
Cheers, Ray

Binary file not shown.

Binary file not shown.

View File

@ -1,23 +0,0 @@
To:
Luke Girard,
3494 Cedar Hill Road,
Victoria,
British Columbia,
V8P 3Z1,
CANADA
From:
Ray Jones,
31 Wiggins Place,
Wallan,
Victoria, 3756,
AUSTRALIA

View File

@ -1 +0,0 @@
C:\Users\ray\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.1/tools/espota.exe -i 192.168.20.40 -p 3232 --auth= -f C:\Users\ray\AppData\Local\Temp\arduino_build_241840/BTCDieselHeater.ino.bin

View File

@ -1 +0,0 @@
C:\Users\ray\AppData\Local\Arduino15\packages\esp32\tools\esptool_py\2.6.0/esptool.exe --chip esp32 --port COM11 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size detect 0xe000 C:\Users\ray\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.1/tools/partitions/boot_app0.bin 0x1000 C:\Users\ray\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.1/tools/sdk/bin/bootloader_qio_80m.bin 0x10000 C:\Users\ray\AppData\Local\Temp\arduino_build_241840/BTCDieselHeater.ino.bin 0x8000 C:\Users\ray\AppData\Local\Temp\arduino_build_241840/BTCDieselHeater.ino.partitions.bin

View File

@ -1,28 +0,0 @@
Thank you ??? for your interest.
Yes I do have some units ready to sell.
I have two possibilities:
1/ the controller as you can currently view in the GitLab repo.
This would be supplied as is without a case.
2/ a newer version that will be housed in a case.
It will also provide a few conditioned I/O expansion lines:
2 digital inputs, 2 digital outputs, 1 analogue input.
The software is still yet to be fully dealt with for this aspect.
Pricing:
Option 1: 50AUD + post and handling
Option 2: 65AUD + post and handling
Post and handling to ??? would be ???AUD
An important question for the sale of the early units is does the prospect of reflashing firmware concern you?
As you can see there are still some aspects I'm working on, and the web pages need a fair bit of work still.
This requires the Arduino environment to be set up for an ESP32, and a USB to serial adapter.
Over The Air programming using WiFi is also available which makes this a lot easier.
Rest assured the pricing is purely for the hardware.
The software will always be open source and freely available for upgrades and bug fixes.
Thanks again for your interest
Cheers, Ray

View File

@ -1,55 +0,0 @@
Hi there,
An important question for the sale of the early units is does the prospect of re-flashing firmware concern you?
There are still some firmware aspects I'm working on, and the web pages do still need a fair bit of work.
I would hate for units to become stale with old out dated firmware!!
In extreme cases this may require the Arduino environment to be set up for an ESP32, and a USB to serial adapter.
The most recent firmware though now has the ability to be readily updated via web browser, or indeed directly from my web server if connected to a wifi network when new firmware is released (only upon user authorisation).
At an intermediate level, Over The Air programming using WiFi is also available via batch file and a pre-compiled binary which is also quite straight forward.
Web page content can be updated via the web browser interface, or the Arduino environment.
For the unit itself I have two possibilities:
1/ the Mk1 controller as you can currently view in the GitLab repo: https://gitlab.com/mrjones.id.au/bluetoothheater/wikis/home
This would be supplied as is, without a case, but I have produced a 3D model you can print: https://www.thingiverse.com/thing:3398068
2/ a newer Mk2 version that is housed in a case.
It also provides a few conditioned I/O expansion lines:
2 digital inputs, 2 digital outputs, 1 analogue input.
The software is still yet to be fully dealt with for this aspect, but you can presently start and stop the heater, and turn outputs on or off upon command.
Please visit my webpage http://www.mrjones.id.au/afterburner for examples of the unit in a case, and the full capabilities of the unit.
There you can also find the user manual.
Pricing:
Option 1: 50AUD + post and handling
Option 2: 75AUD + post and handling
For domestic customers in Australia, P&H is 10AUD
For international customers P&H tends to be around 30-36AUD depending upon which region you reside.
Payment is accepted via PayPal.
Also please confirm your present controller is a digital style.
Please refer to my wiki at the GitLab repo: https://gitlab.com/mrjones.id.au/bluetoothheater/wikis/home or the user manual from my website.
Another important criteria here is what style connector you presently have.
Most have a triangular 3 pin plug, but newer units are coming with a smaller round 3 pin connector.
Please advise, i will supply the appropriate style connector with the unit.
Option 2 with the case, has a range of colours available.
Black or White case.
Black, Grey, Blue or Green buttons.
Please note there is typically a week delay as I do this in my "spare time" and demand can be high at times.
Rest assured the pricing is purely for the hardware and my time to build.
The software will always be open source and freely available for upgrades and bug fixes.
And as mentioned, you can expect extra features to appear as time goes on.
An example of things in the todo list are Low Voltage shutdown, and a "de-coke burn" which will be full power, but a touch leaner than usual.
I'm also yet to explore a 433MHz Receive option using the garage style remote controls.
Thanks again for your interest
Cheers, Ray

View File

@ -183,10 +183,14 @@ bool bHasHtrData = false;
// these variables will persist over a soft reboot.
__NOINIT_ATTR bool bForceInit; // = false;
__NOINIT_ATTR bool bUserON; // = false;
__NOINIT_ATTR uint8_t demandDegC;
__NOINIT_ATTR uint8_t demandPump;
//__NOINIT_ATTR bool bUserON; // = false;
//__NOINIT_ATTR uint8_t demandDegC;
//__NOINIT_ATTR uint8_t demandPump;
//uint8_t demandDegC;
//uint8_t demandPump;
//bool bCyclicEngaged;
CFuelGauge FuelGauge;
CRTC_Store RTC_Store;
bool bReportBlueWireData = REPORT_RAW_DATA;
bool bReportJSONData = REPORT_JSON_TRANSMIT;
@ -305,13 +309,11 @@ extern "C" unsigned long __wrap_millis() {
void setup() {
// ensure cyclic mode is disabled after power on
bool bPowerUpInit = false;
if(rtc_get_reset_reason(0) == 1/* || bForceInit*/) {
bPowerUpInit = true;
bForceInit = false;
bUserON = false;
demandPump = demandDegC = 22;
}
// bool bPowerUpInit = false;
// if(rtc_get_reset_reason(0) == 1/* || bForceInit*/) {
// bPowerUpInit = true;
// bForceInit = false;
// }
// initially, ensure the GPIO outputs are not activated during startup
// (GPIO2 tends to be one with default chip startup)
@ -334,7 +336,7 @@ void setup() {
DebugPort.println("_______________________________________________________________");
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 :-)
// DebugPort.printf("Previous user ON = %d\r\n", bUserON); // state flag required for cyclic mode to persist properly after a WD reboot :-)
// initialise DS18B20 sensor interface
TempSensor.begin(DS18B20_Pin);
@ -383,6 +385,9 @@ void setup() {
BootTime = Clock.get().secondstime();
ScreenManager.begin(bNoClock);
if(!bNoClock && Clock.lostPower()) {
ScreenManager.selectMenu(CScreenManager::BranchMenu, CScreenManager::SetClockUI);
}
#if USE_WIFI == 1
@ -442,9 +447,12 @@ void setup() {
FilteredSamples.Fan.setAlpha(0.7);
FilteredSamples.AmbientTemp.reset(-100.0);
// if(bPowerUpInit) {
FuelGauge.init();
// }
RTC_Store.begin();
FuelGauge.init(RTC_Store.getFuelGauge());
// demandDegC = RTC_Store.getDesiredTemp();
// demandPump = RTC_Store.getDesiredPump();
// bCyclicEngaged = RTC_Store.getCyclicEngaged();
DebugPort.printf("Previous cyclic active = %d\r\n", RTC_Store.getCyclicEngaged()); // state flag required for cyclic mode to persist properly after a WD reboot :-)
delay(1000); // just to hold the splash screeen for while
}
@ -812,7 +820,7 @@ void DebugReportFrame(const char* hdr, const CProtocol& Frame, const char* ftr)
void manageCyclicMode()
{
const sCyclicThermostat& cyclic = NVstore.getUserSettings().cyclic;
if(cyclic.Stop && bUserON) { // cyclic mode enabled, and user has started heater
if(cyclic.Stop && RTC_Store.getCyclicEngaged()) { // cyclic mode enabled, and user has started heater
int stopDeltaT = cyclic.Stop + 1; // bump up by 1 degree - no point invoking at 1 deg over!
float deltaT = FilteredSamples.AmbientTemp.getValue() - getDemandDegC();
// DebugPort.printf("Cyclic=%d bUserOn=%d deltaT=%d\r\n", cyclic, bUserON, deltaT);
@ -876,13 +884,13 @@ bool validateFrame(const CProtocol& frame, const char* name)
void requestOn()
{
heaterOn();
bUserON = true; // for cyclic mode
RTC_Store.setCyclicEngaged(true); // for cyclic mode
}
void requestOff()
{
heaterOff();
bUserON = false; // for cyclic mode
RTC_Store.setCyclicEngaged(false); // for cyclic mode
}
void heaterOn()
@ -898,38 +906,42 @@ void heaterOff()
}
bool reqTemp(uint8_t newTemp, bool save)
bool reqDemand(uint8_t newDemand, bool save)
{
if(bHasOEMController)
return false;
uint8_t max = DefaultBTCParams.getTemperature_Max();
uint8_t min = DefaultBTCParams.getTemperature_Min();
if(newTemp >= max)
newTemp = max;
if(newTemp <= min)
newTemp = min;
if(newDemand >= max)
newDemand = max;
if(newDemand <= min)
newDemand = min;
// set and save the demand to NV storage
// note that we now maintain fixed Hz and Thermostat set points seperately
if(getThermostatModeActive())
demandDegC = newTemp;
else
demandPump = newTemp;
if(getThermostatModeActive()) {
RTC_Store.setDesiredTemp(newDemand);
}
else {
RTC_Store.setDesiredPump(newDemand);
}
ScreenManager.reqUpdate();
return true;
}
bool reqTempDelta(int delta)
bool reqDemandDelta(int delta)
{
uint8_t newTemp;
if(getThermostatModeActive())
newTemp = demandDegC + delta;
else
newTemp = demandPump + delta;
uint8_t newDemand;
if(getThermostatModeActive()) {
newDemand = RTC_Store.getDesiredTemp() + delta;
}
else {
newDemand = RTC_Store.getDesiredPump() + delta;
}
return reqTemp(newTemp);
return reqDemand(newDemand);
}
bool reqThermoToggle()
@ -997,7 +1009,7 @@ void forceBootInit()
uint8_t getDemandDegC()
{
return demandDegC;
return RTC_Store.getDesiredTemp();
}
void setDemandDegC(uint8_t val)
@ -1005,12 +1017,12 @@ void setDemandDegC(uint8_t val)
uint8_t max = DefaultBTCParams.getTemperature_Max();
uint8_t min = DefaultBTCParams.getTemperature_Min();
BOUNDSLIMIT(val, min, max);
demandDegC = val;
RTC_Store.setDesiredTemp(val);
}
uint8_t getDemandPump()
{
return demandPump;
return RTC_Store.getDesiredPump();
}
@ -1021,9 +1033,9 @@ float getTemperatureDesired()
}
else {
if(getThermostatModeActive())
return demandDegC;
return RTC_Store.getDesiredTemp();
else
return demandPump;
return RTC_Store.getDesiredPump();
}
}
@ -1273,7 +1285,7 @@ int getSmartError()
bool isCyclicActive()
{
return bUserON && NVstore.getUserSettings().cyclic.isEnabled();
return RTC_Store.getCyclicEngaged() && NVstore.getUserSettings().cyclic.isEnabled();
}
void setupGPIO()

View File

@ -231,7 +231,7 @@ CBasicScreen::keyHandler(uint8_t event)
if(!_showModeTime) {
// release DOWN key to reduce set demand, provided we are not in mode select
if(event & key_Down) {
if(reqTempDelta(-1)) {
if(reqDemandDelta(-1)) {
_showSetModeTime = millis() + 2000;
_feedbackType = 0;
_ScreenManager.reqUpdate();
@ -241,7 +241,7 @@ CBasicScreen::keyHandler(uint8_t event)
}
// release UP key to increase set demand, provided we are not in mode select
if(event & key_Up) {
if(reqTempDelta(+1)) {
if(reqDemandDelta(+1)) {
_showSetModeTime = millis() + 2000;
_feedbackType = 0;
_ScreenManager.reqUpdate();

View File

@ -251,11 +251,11 @@ CDetailedScreen::keyHandler(uint8_t event)
if(event & keyReleased) {
if(_keyRepeatCount == 0) { // short Up press - lower target
if(event & key_Up) {
if(reqTempDelta(+1)) _showTarget = millis() + 3500;
if(reqDemandDelta(+1)) _showTarget = millis() + 3500;
else _reqOEMWarning();
}
if(event & key_Down) { // short Down press - lower target
if(reqTempDelta(-1)) _showTarget = millis() + 3500;
if(reqDemandDelta(-1)) _showTarget = millis() + 3500;
else _reqOEMWarning();
}
if(event & key_Centre) { // short Centre press - show target

View File

@ -385,7 +385,7 @@ CScreenManager::checkUpdate()
if(runState > 0 && prevRunState == 0) {
// heater has started
uint8_t userStartMenu = NVstore.getUserSettings().HomeMenu.onStart;
if(userStartMenu && userStartMenu <= 3) { // allow user to override defualt screen
if(userStartMenu && userStartMenu <= 3) { // allow user to override default screen
userStartMenu--;
DebugPort.print("Screen Manager: Heater start detected, switching to user preferred screen: ");
switch(userStartMenu) {
@ -397,10 +397,10 @@ CScreenManager::checkUpdate()
_enterScreen();
}
}
if(runState == 0 && prevRunState != 0) {
if(runState == 0 && prevRunState > 0) {
// heater has stopped
uint8_t userStopMenu = NVstore.getUserSettings().HomeMenu.onStop;
if(userStopMenu && userStopMenu <= 3) { // allow user to override defualt screen
if(userStopMenu && userStopMenu <= 3) { // allow user to override default screen
userStopMenu--;
DebugPort.print("Screen Manager: Heater stop detected, switching to user preferred screen: ");
switch(userStopMenu) {
@ -544,6 +544,7 @@ CScreenManager::selectMenu(eUIMenuSets menuSet, int specific)
// targetting a specific menu
_subMenu = specific;
UPPERLIMIT(_subMenu, _Screens[_menu].size()-1); // check bounds!
DebugPort.printf("selectMenu %d %d\r\n", _menu, _subMenu);
}
else {
// default sub menu behaviour
@ -574,7 +575,7 @@ CScreenManager::showOTAMessage(int percent, eOTAmodes updateType)
static long prevTime = millis();
long tDelta = millis() - prevTime;
if(percent != prevPercent && tDelta > 500) {
if(percent != prevPercent/* && tDelta > 500*/) {
prevTime = millis();
_pDisplay->clearDisplay();
_pDisplay->setCursor(64,22);

View File

@ -117,6 +117,17 @@ CClock::readData(uint8_t* pData, int len, int ofs)
_rtc.readData(pData, len, ofs);
}
bool
CClock::lostPower()
{
return _rtc.lostPower();
}
void
CClock::resetLostPower()
{
_rtc.resetLostPower();
}
void setDateTime(const char* newTime)
{
@ -153,7 +164,8 @@ void setTime(const char* newTime)
#define _I2C_WRITE write
#define _I2C_READ read
void RTC_DS3231Ex::writeData(uint8_t* pData, int len, int ofs) {
void
RTC_DS3231Ex::writeData(uint8_t* pData, int len, int ofs) {
Wire.beginTransmission(DS3231_ADDRESS);
Wire._I2C_WRITE((byte)(7+ofs)); // start at alarm bytes
for(int i=0; i<len; i++) {
@ -162,7 +174,8 @@ void RTC_DS3231Ex::writeData(uint8_t* pData, int len, int ofs) {
Wire.endTransmission();
}
void RTC_DS3231Ex::readData(uint8_t* pData, int len, int ofs) {
void
RTC_DS3231Ex::readData(uint8_t* pData, int len, int ofs) {
Wire.beginTransmission(DS3231_ADDRESS);
Wire._I2C_WRITE((byte)(7+ofs)); // start at alarm bytes
Wire.endTransmission();
@ -174,32 +187,171 @@ void RTC_DS3231Ex::readData(uint8_t* pData, int len, int ofs) {
Wire.endTransmission();
}
void storeFuelGauge(float val)
bool
RTC_DS3231Ex::resetLostPower()
{
Wire.beginTransmission(DS3231_ADDRESS);
Wire._I2C_WRITE(DS3231_STATUSREG);
Wire.endTransmission();
Wire.requestFrom(DS3231_ADDRESS, 1);
uint8_t sts = Wire._I2C_READ();
sts &= 0x7f; // clear power loss flag
Wire.beginTransmission(DS3231_ADDRESS);
Wire._I2C_WRITE(DS3231_STATUSREG);
Wire._I2C_WRITE(sts);
Wire.endTransmission();
}
// RTC storage, using alarm registers as GP storage
// MAXIMUM OF 7 BYTES
//
// [0..3] float fuelGauge strokes
// [4] uint8_t DesiredTemp (typ. 8-35)
// [5] uint8_t DesiredPump (typ. 8-35)
// [6] uint8_t spare
//
// ____________________________________________________
// | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 |
// |---------------|------|-----------------------------|
// Byte[4]: | CyclicEngaged | bit6 | Desired Deg Celcius |
// |---------------|------|-----------------------------|
// Byte[5]: | | | Desired Pump Speed |
// ----------------------------------------------------
CRTC_Store::CRTC_Store()
{
_accessed[0] = false;
_accessed[1] = false;
_accessed[2] = false;
_accessed[3] = false;
_fuelgauge = 0;
_demandDegC = 22;
_demandPump = 22;
_CyclicEngaged = false;
}
void
CRTC_Store::begin()
{
if(Clock.lostPower()) {
// RTC lost power - reset internal NV values to defaults
DebugPort.println("CRTC_Store::begin() RTC lost power, re-initialising NV aspect");
_demandPump = _demandDegC = 22;
_CyclicEngaged = false;
setFuelGauge(0);
setDesiredTemp(_demandDegC);
setDesiredPump(_demandPump);
Clock.resetLostPower();
}
getFuelGauge();
getDesiredTemp();
getDesiredPump();
}
void
CRTC_Store::setFuelGauge(float val)
{
_accessed[0] = true;
_fuelgauge = val;
Clock.saveData((uint8_t*)&val, 4, 0);
}
void getStoredFuelGauge(float& val)
float
CRTC_Store::getFuelGauge()
{
Clock.readData((uint8_t*)&val, 4, 0);
if(!_accessed[0]) {
float NVval;
Clock.readData((uint8_t*)&NVval, 4, 0);
_fuelgauge = NVval;
_accessed[0] = true;
DebugPort.printf("RTC_Store - read fuel gauge %.2f\r\n", _fuelgauge);
}
return _fuelgauge;
}
void storeDesiredTemp(uint8_t val)
void
CRTC_Store::setDesiredTemp(uint8_t val)
{
Clock.saveData((uint8_t*)&val, 1, 4);
_demandDegC = val;
_PackAndSaveByte4();
}
void getStoredDesiredTemp(uint8_t& val)
uint8_t
CRTC_Store::getDesiredTemp()
{
Clock.readData((uint8_t*)&val, 1, 4);
_ReadAndUnpackByte4();
return _demandDegC;
}
void storeDesiredPump(uint8_t val)
bool
CRTC_Store::getCyclicEngaged()
{
Clock.saveData((uint8_t*)&val, 1, 5);
_ReadAndUnpackByte4();
return _CyclicEngaged;
}
void getStoredDesiredPump(uint8_t& val)
void
CRTC_Store::setCyclicEngaged(bool active)
{
Clock.readData((uint8_t*)&val, 1, 5);
_CyclicEngaged = active;
_PackAndSaveByte4();
}
void
CRTC_Store::setDesiredPump(uint8_t val)
{
_demandPump = val;
_PackAndSaveByte5();
}
uint8_t
CRTC_Store::getDesiredPump()
{
_ReadAndUnpackByte5();
return _demandPump;
}
void
CRTC_Store::_ReadAndUnpackByte4()
{
if(!_accessed[1]) {
uint8_t NVval = 0;
Clock.readData((uint8_t*)&NVval, 1, 4);
_demandDegC = NVval & 0x3f;
_CyclicEngaged = (NVval & 0x80) != 0;
_bit6 = (NVval & 0x40) != 0;
_accessed[1] = true;
DebugPort.printf("RTC_Store - read byte4: degC=%d, CyclicOn=%d, bit6=%d\r\n", _demandDegC, _CyclicEngaged, _bit6);
}
}
void
CRTC_Store::_PackAndSaveByte4()
{
uint8_t NVval = (_CyclicEngaged ? 0x80 : 0x00)
| (_bit6 ? 0x40 : 0x00)
| (_demandDegC & 0x3f);
Clock.saveData((uint8_t*)&NVval, 1, 4);
}
void
CRTC_Store::_ReadAndUnpackByte5()
{
if(!_accessed[2]) {
uint8_t NVval = 0;
Clock.readData((uint8_t*)&NVval, 1, 5);
_demandPump = NVval & 0x3f;
_accessed[2] = true;
DebugPort.printf("RTC_Store - read byte5: pump=%d\r\n", _demandPump);
}
}
void
CRTC_Store::_PackAndSaveByte5()
{
uint8_t NVval = (_demandPump & 0x3f);
Clock.saveData((uint8_t*)&NVval, 1, 5);
}

View File

@ -30,6 +30,7 @@ class RTC_DS3231Ex : public RTC_DS3231 {
public:
void writeData(uint8_t* pData, int len, int ofs=0);
void readData(uint8_t* pData, int len, int ofs=0);
bool resetLostPower();
};
@ -68,8 +69,35 @@ public:
void set(const DateTime& newTime);
void saveData(uint8_t* pData, int len, int ofs);
void readData(uint8_t* pData, int len, int ofs);
bool lostPower();
void resetLostPower();
};
class CRTC_Store {
bool _accessed[4]; // [0] - bytes 0..3, [1] byte 4, [2] byte 5, [3] byte 6
float _fuelgauge;
uint8_t _demandDegC;
uint8_t _demandPump;
bool _CyclicEngaged;
bool _bit6;
void _ReadAndUnpackByte4();
void _PackAndSaveByte4();
void _ReadAndUnpackByte5();
void _PackAndSaveByte5();
public:
CRTC_Store();
void begin();
void setFuelGauge(float val);
void setDesiredTemp(uint8_t val);
void setDesiredPump(uint8_t val);
void setCyclicEngaged(bool _CyclicEngaged);
float getFuelGauge();
uint8_t getDesiredTemp();
uint8_t getDesiredPump();
bool getCyclicEngaged();
};
extern CClock Clock;
extern CRTC_Store RTC_Store;
#endif // __BTC_TIMERS_H__

View File

@ -73,7 +73,7 @@ void interpretJsonCommand(char* pLine)
for(it = obj.begin(); it != obj.end(); ++it) {
if(strcmp("TempDesired", it->key) == 0) {
if( !reqTemp(it->value.as<uint8_t>(), false) ) { // this request is blocked if OEM controller active
if( !reqDemand(it->value.as<uint8_t>(), false) ) { // this request is blocked if OEM controller active
JSONmoderator.reset("TempDesired");
}
}
@ -265,6 +265,21 @@ void interpretJsonCommand(char* pLine)
NVstore.setUserSettings(us);
NVstore.save();
}
else if(strcmp("PumpCount", it->key) == 0) { // reset fuel gauge
int Count = it->value.as<int>();
if(Count == 0) {
RTC_Store.setFuelGauge(0);
}
}
else if(strcmp("PumpCal", it->key) == 0) {
float fCal = it->value.as<float>();
if(INBOUNDS(fCal, 0.001, 1)) {
sHeaterTuning ht = NVstore.getHeaterTuning();
ht.pumpCal = fCal;
NVstore.setHeaterTuning(ht);
NVstore.save();
}
}
}
}
@ -340,6 +355,8 @@ bool makeJSONStringEx(CModerator& moderator, char* opStr, int len)
bSend |= moderator.addJson("CyclicTemp", getDemandDegC(), root); // actual pivot point for cyclic mode
bSend |= moderator.addJson("CyclicOff", stop, root); // threshold of over temp for cyclic mode
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
if(bSend) {
root.printTo(opStr, len);

View File

@ -19,78 +19,28 @@
*
*/
//
// We need to identify the PCB the firmware is running upon for 2 reasons related to GPIO functions
//
// 1: Digital Inputs
// To the outside world, the digital inputs are always treated as contact closures to ground.
// V1.0 PCBs expose the bare ESP inputs for GPIO, they are normally pulled HIGH.
// V2.0+ PCBs use an input conditioning transistor that inverts the sense state.
// Inactive state for V1.0 is HIGH
// Inactive state for V2.0+ is LOW
//
// 2: Analogue input
// Unfortunately the pin originally chosen for the analogue input on the V2.0 PCB goes to
// an ADC2 channel of the ESP32.
// It turns out NONE of the 10 ADC2 channels can be used if Wifi is enabled!
// The remedy on V2.0 PCBs is to cut the traces leading from Digital input 1 and the Analogue input.
// The signals are then tranposed.
// This then presents Digital Input #1 to GPIO26, and analogue to GPIO33.
// As GPIO33 uses an ADC1 channel no issue is present reading analogue values with wifi enabled.
//
// Board Detection
// Fortunately due to the use of the digital input transistors on V2.0+ PCBs, a logical
// determination of the board configuration can be made.
// By setting the pins as digital inputs with pull ups enabled, the logic level presented
// can be read and thus the input signal paths can be determined.
// Due to the input conditioning transistors, V2.0 PCBs will hold the inputs to the ESP32
// LOW when inactive, V1.0 PCBs will pull HIGH.
// Likewise, the analogue input is left to float, so it will always be pulled HIGH.
// NOTE: a 100nF capacitor exists on the analogue input so a delay is required to ensure
// a reliable read.
//
// Input state truth table
// GPIO26 GPIO33
// ------ ------
// V1.0 HIGH HIGH
// unmodified V2.0 HIGH LOW
// modified V2.0 LOW HIGH
// V2.1 LOW HIGH
//
//
// ****************************************************************************************
// This test only needs to be performed upon the very first firmware execution.
// Once the board has been identified, the result is saved to non volatile memory
// If a valid value is detected, the test is bypassed.
// This avoids future issues should the GPIO inputs be legitimately connected to
// extension hardware that may distort the test results when the system is repowered.
// ****************************************************************************************
//
#include "FuelGauge.h"
#include "NVStorage.h"
#include "DebugPort.h"
#include "../RTC/Clock.h"
CFuelGauge::CFuelGauge()
{
_tank_mL = 0;
_pumpStrokes = 0;
_pumpCal = 0.02;
record.lastsave = millis();
record.storedval = _tank_mL;
_lastStoredVal = _pumpStrokes;
DebugPort.println("CFuelGauge::CFuelGauge");
}
void
CFuelGauge::init()
CFuelGauge::init(float fuelUsed)
{
_pumpCal = NVstore.getHeaterTuning().pumpCal;
float testVal;
getStoredFuelGauge(testVal); // RTC registers used to store this
if(INBOUNDS(testVal, 0, 200000)) {
DebugPort.printf("Initialising fuel gauge with %.2fmL\r\n", testVal);
_tank_mL = testVal;
record.storedval = _tank_mL;
}
_pumpStrokes = fuelUsed;
DebugPort.printf("Initialising fuel gauge with %.2f strokes\r\n", _pumpStrokes);
_lastStoredVal = _pumpStrokes;
}
@ -101,16 +51,14 @@ CFuelGauge::Integrate(float Hz)
long tSample = timenow - _lasttime;
_lasttime = timenow;
_tank_mL += Hz * tSample * 0.001 * _pumpCal; // Hz * seconds * mL / stroke
_pumpStrokes += Hz * tSample * 0.001; // Hz * seconds
long tDiff = millis() - record.lastsave;
float fuelDelta = _tank_mL - record.storedval;
bool bStoppedSave = (Hz == 0) && (_tank_mL != record.storedval);
if(tDiff > 600000 || fuelDelta > 1 || bStoppedSave) { // record fuel usage every 10 minutes, or every 5mL used
DebugPort.printf("Storing fuel gauge: %.2fmL\r\n", _tank_mL);
storeFuelGauge(_tank_mL); // uses RTC registers to store this
record.lastsave = millis();
record.storedval = _tank_mL;
float fuelDelta = _pumpStrokes - _lastStoredVal;
bool bStoppedSave = (Hz == 0) && (_pumpStrokes != _lastStoredVal);
if(fuelDelta > 10 || bStoppedSave) { // record fuel usage every 10 minutes, or every 10 strokes
DebugPort.printf("Storing fuel gauge: %.2f strokes\r\n", _pumpStrokes);
RTC_Store.setFuelGauge(_pumpStrokes); // uses RTC registers to store this
_lastStoredVal = _pumpStrokes;
}
}
@ -118,5 +66,5 @@ CFuelGauge::Integrate(float Hz)
float
CFuelGauge::Used_mL()
{
return _tank_mL;
return _pumpStrokes * _pumpCal; // strokes * mL / stroke
}

View File

@ -25,16 +25,13 @@
#include <stdint.h>
class CFuelGauge {
float _tank_mL;
float _pumpStrokes;
unsigned long _lasttime;
float _pumpCal;
struct {
unsigned long lastsave;
float storedval;
} record;
float _lastStoredVal;
public:
CFuelGauge();
void init();
void init(float fuelUsed = 0);
void Integrate(float Hz);
float Used_mL();
};

View File

@ -31,8 +31,8 @@ extern void forceBootInit();
extern void requestOn();
extern void requestOff();
extern bool reqTempDelta(int delta);
extern bool reqTemp(uint8_t newTemp, bool save=true);
extern bool reqDemandDelta(int delta);
extern bool reqDemand(uint8_t newTemp, bool save=true);
extern bool reqThermoToggle();
extern bool setThermostatMode(uint8_t);
extern bool getThermostatModeActive(); // OEM: actual mode from blue wire, BTC: or our NV
@ -84,12 +84,12 @@ extern float getGlowCurrent();
extern float getFanSpeed();
extern int sysUptime();
extern void storeFuelGauge(float val);
extern void getStoredFuelGauge(float& val);
extern void storeDesiredTemp(uint8_t val);
extern void getStoredDesiredTemp(uint8_t& val);
extern void storeDesiredPump(uint8_t val);
extern void getStoredDesiredPump(uint8_t& val);
/* extern void setFuelGauge_RTC(float val);
extern void getFuelGauge_RTC(float& val);
extern void setDesiredTemp_RTC(uint8_t val);
extern void getDesiredTemp_RTC(uint8_t& val);
extern void setDesiredPump_RTC(uint8_t val);
extern void getDesiredPump_RTC(uint8_t& val);*/
extern void ShowOTAScreen(int percent=0, eOTAmodes updateType=eOTAnormal);