Added Low Volt Cutout, Temperature probe offset

Tarted up fuel mixture adjust screen
This commit is contained in:
Ray Jones 2019-07-21 21:17:54 +10:00
parent dd5e62c8cb
commit 06e69acc77
42 changed files with 466 additions and 143 deletions

View File

@ -99,6 +99,7 @@
#include "src/Utility/helpers.h"
#include "src/Utility/NVStorage.h"
#include "src/Utility/DebugPort.h"
#include "src/Utility/macros.h"
#include "src/Utility/UtilClasses.h"
#include "src/Utility/BTC_JSON.h"
#include "src/Utility/BTC_GPIO.h"
@ -446,7 +447,11 @@ void setup() {
FilteredSamples.Fan.setRounding(10);
FilteredSamples.Fan.setAlpha(0.7);
FilteredSamples.AmbientTemp.reset(-100.0);
FilteredSamples.FastipVolts.setRounding(0.1);
FilteredSamples.FastipVolts.setAlpha(0.7);
FilteredSamples.FastGlowAmps.setRounding(0.01);
FilteredSamples.FastGlowAmps.setAlpha(0.7);
RTC_Store.begin();
FuelGauge.init(RTC_Store.getFuelGauge());
// demandDegC = RTC_Store.getDesiredTemp();
@ -796,6 +801,9 @@ void loop()
updateFilteredData();
FuelGauge.Integrate(HeaterFrame2.getPump_Actual());
}
if(INBOUNDS(HeaterFrame2.getRunState(), 1, 5)) { // check for Low Voltage Cutout
SmartError.checkVolts(FilteredSamples.FastipVolts.getValue(), FilteredSamples.FastGlowAmps.getValue());
}
updateJSONclients(bReportJSONData);
CommState.set(CommStates::Idle);
NVstore.doSave(); // now is a good time to store to the NV storage, well away from any blue wire activity
@ -822,7 +830,7 @@ void manageCyclicMode()
const sCyclicThermostat& cyclic = NVstore.getUserSettings().cyclic;
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();
float deltaT = getTemperatureSensor() - getDemandDegC();
// DebugPort.printf("Cyclic=%d bUserOn=%d deltaT=%d\r\n", cyclic, bUserON, deltaT);
// ensure we cancel user ON mode if heater throws an error
@ -883,8 +891,10 @@ bool validateFrame(const CProtocol& frame, const char* name)
void requestOn()
{
heaterOn();
RTC_Store.setCyclicEngaged(true); // for cyclic mode
if(SmartError.checkVolts(FilteredSamples.FastipVolts.getValue(), FilteredSamples.FastGlowAmps.getValue())) {
heaterOn();
RTC_Store.setCyclicEngaged(true); // for cyclic mode
}
}
void requestOff()
@ -1041,7 +1051,7 @@ float getTemperatureDesired()
float getTemperatureSensor()
{
return FilteredSamples.AmbientTemp.getValue();
return FilteredSamples.AmbientTemp.getValue() + NVstore.getHeaterTuning().tempOfs;
}
void setPumpMin(float val)
@ -1449,12 +1459,15 @@ void simulateGPIOin(uint8_t newKey)
GPIOin.simulateKey(newKey);
}
float getBatteryVoltage()
float getBatteryVoltage(bool fast)
{
#ifdef RAW_SAMPLES
return getHeaterInfo().getBattVoltage();
#else
return FilteredSamples.ipVolts.getValue();
if(fast)
return FilteredSamples.FastipVolts.getValue();
else
return FilteredSamples.ipVolts.getValue();
#endif
}
@ -1491,6 +1504,8 @@ void updateFilteredData()
FilteredSamples.GlowVolts.update(HeaterFrame2.getGlowPlug_Voltage());
FilteredSamples.GlowAmps.update(HeaterFrame2.getGlowPlug_Current());
FilteredSamples.Fan.update(HeaterFrame2.getFan_Actual());
FilteredSamples.FastipVolts.update(HeaterFrame2.getVoltage_Supply());
FilteredSamples.FastGlowAmps.update(HeaterFrame2.getGlowPlug_Current());
}
int sysUptime()

View File

@ -51,15 +51,15 @@ Working so far:
Web browser upload new binary to controller (AP or STA mode)
Direct discovery and download of updates from internet server (STA mode)
* Factory default menu option
* "Fuel gauge" - Integrates pump frequency, assumes a repeatable dose of fuel per pump stroke.
* Low voltage cut out, definable threshold - auto adjusts for cable voltage drop during start
* Temperature probe offset to correct adverse readings.
To be implemented
--------------------------
* 433MHz Rx stream, 433MHz Tx stream connections. This would allow external timer units for example, or analogue temperature demand (which is still only 1 degree resolution with standard heater thermostat).
* Low voltage cut out
* Temperature probe offset or mapping to correct adverse readings.
* MQTT pub/sub
* "fuel gauge" - Integrate pump frequency, assuming a repeatable dose of fuel per pump cycle...
* Regular Hot Burn cycle (DPF mode!)
* Hour meter - run time, glow time
* list under construction.....

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 B

After

Width:  |  Height:  |  Size: 198 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 B

After

Width:  |  Height:  |  Size: 110 B

BIN
icons/FuelIconSmall.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 B

BIN
icons/MiniThermo.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 B

View File

@ -99,6 +99,7 @@
#include "src/Utility/helpers.h"
#include "src/Utility/NVStorage.h"
#include "src/Utility/DebugPort.h"
#include "src/Utility/macros.h"
#include "src/Utility/UtilClasses.h"
#include "src/Utility/BTC_JSON.h"
#include "src/Utility/BTC_GPIO.h"
@ -446,7 +447,11 @@ void setup() {
FilteredSamples.Fan.setRounding(10);
FilteredSamples.Fan.setAlpha(0.7);
FilteredSamples.AmbientTemp.reset(-100.0);
FilteredSamples.FastipVolts.setRounding(0.1);
FilteredSamples.FastipVolts.setAlpha(0.7);
FilteredSamples.FastGlowAmps.setRounding(0.01);
FilteredSamples.FastGlowAmps.setAlpha(0.7);
RTC_Store.begin();
FuelGauge.init(RTC_Store.getFuelGauge());
// demandDegC = RTC_Store.getDesiredTemp();
@ -796,6 +801,9 @@ void loop()
updateFilteredData();
FuelGauge.Integrate(HeaterFrame2.getPump_Actual());
}
if(INBOUNDS(HeaterFrame2.getRunState(), 1, 5)) { // check for Low Voltage Cutout
SmartError.checkVolts(FilteredSamples.FastipVolts.getValue(), FilteredSamples.FastGlowAmps.getValue());
}
updateJSONclients(bReportJSONData);
CommState.set(CommStates::Idle);
NVstore.doSave(); // now is a good time to store to the NV storage, well away from any blue wire activity
@ -822,7 +830,7 @@ void manageCyclicMode()
const sCyclicThermostat& cyclic = NVstore.getUserSettings().cyclic;
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();
float deltaT = getTemperatureSensor() - getDemandDegC();
// DebugPort.printf("Cyclic=%d bUserOn=%d deltaT=%d\r\n", cyclic, bUserON, deltaT);
// ensure we cancel user ON mode if heater throws an error
@ -883,8 +891,10 @@ bool validateFrame(const CProtocol& frame, const char* name)
void requestOn()
{
heaterOn();
RTC_Store.setCyclicEngaged(true); // for cyclic mode
if(SmartError.checkVolts(FilteredSamples.FastipVolts.getValue(), FilteredSamples.FastGlowAmps.getValue())) {
heaterOn();
RTC_Store.setCyclicEngaged(true); // for cyclic mode
}
}
void requestOff()
@ -1041,7 +1051,7 @@ float getTemperatureDesired()
float getTemperatureSensor()
{
return FilteredSamples.AmbientTemp.getValue();
return FilteredSamples.AmbientTemp.getValue() + NVstore.getHeaterTuning().tempOfs;
}
void setPumpMin(float val)
@ -1449,12 +1459,15 @@ void simulateGPIOin(uint8_t newKey)
GPIOin.simulateKey(newKey);
}
float getBatteryVoltage()
float getBatteryVoltage(bool fast)
{
#ifdef RAW_SAMPLES
return getHeaterInfo().getBattVoltage();
#else
return FilteredSamples.ipVolts.getValue();
if(fast)
return FilteredSamples.FastipVolts.getValue();
else
return FilteredSamples.ipVolts.getValue();
#endif
}
@ -1491,6 +1504,8 @@ void updateFilteredData()
FilteredSamples.GlowVolts.update(HeaterFrame2.getGlowPlug_Voltage());
FilteredSamples.GlowAmps.update(HeaterFrame2.getGlowPlug_Current());
FilteredSamples.Fan.update(HeaterFrame2.getFan_Actual());
FilteredSamples.FastipVolts.update(HeaterFrame2.getVoltage_Supply());
FilteredSamples.FastGlowAmps.update(HeaterFrame2.getGlowPlug_Current());
}
int sysUptime()

View File

@ -50,7 +50,7 @@ CBasicScreen::CBasicScreen(C128x64_OLED& display, CScreenManager& mgr) : CScreen
bool
CBasicScreen::show()
{
CScreenHeader::show();
CScreenHeader::show(false);
char msg[20];
int xPos, yPos;

View File

@ -43,10 +43,11 @@ CClockScreen::CClockScreen(C128x64_OLED& display, CScreenManager& mgr) : CScreen
}
bool
CClockScreen::show()
{
CScreenHeader::show();
CScreenHeader::show(false);
const BTCDateTime& now = Clock.get();

View File

@ -82,7 +82,7 @@ CDetailedScreen::CDetailedScreen(C128x64_OLED& display, CScreenManager& mgr) : C
bool
CDetailedScreen::show()
{
CScreenHeader::show();
CScreenHeader::show(false);
int runstate = getHeaterInfo().getRunStateEx();
int errstate = getHeaterInfo().getErrState();
@ -133,7 +133,7 @@ CDetailedScreen::show()
}
if(!bGlowActive) {
showBowser(FuelGauge.Used_ml());
showBowser(FuelGauge.Used_mL());
}
showRunState(runstate, errstate);
return true;

View File

@ -0,0 +1,48 @@
/*
* 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 __FUELCALSCREEN_H__
#define __FUELCALSCREEN_H__
#include <stdint.h>
#include "PasswordScreen.h"
class C128x64_OLED;
class CScreenManager;
class CFuelCalScreen : public CPasswordScreen
{
int _rowSel;
void _adjust(int dir);
float _mlPerStroke;
float _tOfs;
uint8_t _LVC;
int _animateCount;
void _initUI();
public:
CFuelCalScreen(C128x64_OLED& display, CScreenManager& mgr);
bool show();
bool animate();
bool keyHandler(uint8_t event);
void onSelect();
};
#endif

View File

@ -34,6 +34,7 @@
#include "../Utility/DebugPort.h"
#include "../Utility/macros.h"
#include "../Protocol/Protocol.h"
#include "fonts/Icons.h"
CFuelMixtureScreen::CFuelMixtureScreen(C128x64_OLED& display, CScreenManager& mgr) : CPasswordScreen(display, mgr)
@ -47,6 +48,12 @@ CFuelMixtureScreen::onSelect()
CPasswordScreen::onSelect();
_initUI();
_load();
}
void
CFuelMixtureScreen::_load()
{
adjPump[0] = getHeaterInfo().getPump_Min();
adjPump[1] = getHeaterInfo().getPump_Max();
adjFan[0] = getHeaterInfo().getFan_Min();
@ -58,6 +65,7 @@ CFuelMixtureScreen::_initUI()
{
_rowSel = 0;
_colSel = 0;
_animateCount = 0;
}
bool
@ -67,34 +75,40 @@ CFuelMixtureScreen::show()
int xPos, yPos;
const int col3 = _display.width() - border;
_display.clearDisplay();
_display.fillRect(70, 0, 58, 64, BLACK); // scrub variables
_display.fillRect(0, 50, 128, 14, BLACK); // scrub footer
if(!CPasswordScreen::show()) {
switch(_rowSel) {
case 0:
case 1:
case 2:
case 3:
case 4:
if(_animateCount == -1) {
_animateCount = 0;
_display.clearDisplay();
}
// Pump Minimum adjustment
yPos = border + 36;
_printMenuText(80, yPos, "Min", false, eRightJustify);
sprintf(str, "%.1f", adjPump[0]);
_printMenuText(65, yPos, "Min", false, eRightJustify);
sprintf(str, "%.1f Hz", adjPump[0]);
_printMenuText(col3, yPos, str, _rowSel == 1, eRightJustify);
// Pump Maximum adjustment
yPos = border + 24;
_printMenuText(80, yPos, "Pump Hz Max", false, eRightJustify);
sprintf(str, "%.1f", adjPump[1]);
_printMenuText(65, yPos, "Max", false, eRightJustify);
sprintf(str, "%.1f Hz", adjPump[1]);
_printMenuText(col3, yPos, str, _rowSel == 2, eRightJustify);
// Fan Minimum adjustment
yPos = border + 12;
_printMenuText(80, yPos, "Min", false, eRightJustify);
sprintf(str, "%d", adjFan[0]);
_printMenuText(65, yPos, "Min", false, eRightJustify);
sprintf(str, "%d RPM", adjFan[0]);
_printMenuText(col3, yPos, str, _rowSel == 3, eRightJustify);
// Fan Maximum adjustment
yPos = border;
_printMenuText(80, yPos, "Fan RPM Max", false, eRightJustify);
sprintf(str, "%d", adjFan[1]);
_printMenuText(65, yPos, "Max", false, eRightJustify);
sprintf(str, "%d RPM", adjFan[1]);
_printMenuText(col3, yPos, str, _rowSel == 4, eRightJustify);
// navigation line
yPos = 53;
@ -128,6 +142,29 @@ CFuelMixtureScreen::show()
return true;
}
bool
CFuelMixtureScreen::animate()
{
if(_animateCount >= 0) {
int xPos = 20;
int yPos = 5;
int yFuel = 30;
_display.fillRect(xPos, yPos, FanIcon1Info.width, FanIcon1Info.height, BLACK);
_display.fillRect(xPos+5, yFuel, FuelIconInfo.width, FuelIconInfo.height + 4, BLACK);
_drawBitmap(xPos+5, yFuel+_animateCount, FuelIconInfo);
switch(_animateCount) {
case 0: _drawBitmap(xPos, yPos, FanIcon1Info); break;
case 1: _drawBitmap(xPos, yPos, FanIcon2Info); break;
case 2: _drawBitmap(xPos, yPos, FanIcon3Info); break;
case 3: _drawBitmap(xPos, yPos, FanIcon4Info); break;
}
_animateCount++;
WRAPUPPERLIMIT(_animateCount, 3, 0);
}
return true;
}
bool
CFuelMixtureScreen::keyHandler(uint8_t event)
@ -143,6 +180,8 @@ CFuelMixtureScreen::keyHandler(uint8_t event)
case 2:
case 3:
case 4:
_animateCount = -1;
_display.clearDisplay();
_rowSel = 5; // enter save confirm mode
break;
case 5:
@ -204,6 +243,8 @@ CFuelMixtureScreen::keyHandler(uint8_t event)
UPPERLIMIT(_rowSel, 4);
break;
case 5:
_display.clearDisplay();
_animateCount = -1;
_showStoringMessage();
setPumpMin(adjPump[0]);
setPumpMax(adjPump[1]);
@ -233,7 +274,10 @@ CFuelMixtureScreen::keyHandler(uint8_t event)
_ScreenManager.reqUpdate();
}
if(_rowSel == 0) {
_load(); // dispose of any changes, re-obtain current settings
}
if(event & keyRepeat) {
switch(_rowSel) {
case 1:

View File

@ -33,12 +33,15 @@ class CFuelMixtureScreen : public CPasswordScreen {
uint16_t adjFan[2];
int _rowSel;
int _colSel;
int _animateCount;
void _adjustSetting(int dir);
void _initUI();
void _load();
public:
CFuelMixtureScreen(C128x64_OLED& display, CScreenManager& mgr);
bool show();
bool animate();
bool keyHandler(uint8_t event);
void onSelect();
};

View File

@ -291,12 +291,6 @@ CGPIOInfoScreen::CGPIOInfoScreen(C128x64_OLED& display, CScreenManager& mgr) : C
_keyRepeatCount = -1;
}
void
CGPIOInfoScreen::onSelect()
{
CScreenHeader::onSelect();
}
void
CGPIOInfoScreen::_initUI()
{
@ -305,7 +299,7 @@ CGPIOInfoScreen::_initUI()
bool
CGPIOInfoScreen::show()
{
CScreenHeader::show();
CScreenHeader::show(false);
char msg[16];
_display.writeFillRect(49, 18, 30, 12, WHITE);

View File

@ -53,7 +53,6 @@ public:
CGPIOInfoScreen(C128x64_OLED& display, CScreenManager& mgr);
bool show();
bool keyHandler(uint8_t event);
void onSelect();
};
#endif

View File

@ -21,11 +21,13 @@
#include "128x64OLED.h"
#include "HeaterSettingsScreen.h"
#include "FuelCalScreen.h"
#include "KeyPad.h"
#include "../Utility/helpers.h"
#include "../Utility/macros.h"
#include "../Utility/NVStorage.h"
#include "../Protocol/Protocol.h"
#include "fonts/Icons.h"
///////////////////////////////////////////////////////////////////////////
//
@ -264,6 +266,8 @@ CFuelCalScreen::CFuelCalScreen(C128x64_OLED& display, CScreenManager& mgr) : CPa
{
_initUI();
_mlPerStroke = 0.02;
_LVC = 115;
_tOfs = 0;
}
void
@ -272,6 +276,8 @@ CFuelCalScreen::onSelect()
CPasswordScreen::onSelect();
_initUI();
_mlPerStroke = NVstore.getHeaterTuning().pumpCal;
_LVC = NVstore.getHeaterTuning().lowVolts;
_tOfs = NVstore.getHeaterTuning().tempOfs;
}
void
@ -285,7 +291,10 @@ bool
CFuelCalScreen::show()
{
char msg[20];
_display.clearDisplay();
const int col = 90;
_display.fillRect(0, 50, 128, 14, BLACK);
_display.fillRect(col-border, Line3-border, 128-(col-1), 64-Line3-border, BLACK);
if(!CPasswordScreen::show()) { // for showing "saving settings"
@ -295,13 +304,31 @@ CFuelCalScreen::show()
_printMenuText(_display.xCentre(), 43, "confirm save", false, eCentreJustify);
}
else {
int col = 80;
_printInverted(_display.xCentre(), 0, " Fuel Calibration ", true, eCentreJustify);
_printMenuText(col, Line1, "ml / stroke:", false, eRightJustify);
if(_animateCount < 0) {
_display.clearDisplay();
_animateCount = 0;
}
_printInverted(_display.xCentre(), 0, " Special Features ", true, eCentreJustify);
// fuel calibration
int yPos = Line1;
_printMenuText(col, yPos, "mL/stroke : ", false, eRightJustify);
sprintf(msg, "%.03f", _mlPerStroke);
_printMenuText(col+1, Line1, msg, _rowSel == 1);
_printMenuText(col, yPos, msg, _rowSel == 1);
// low voltage cutout
yPos = Line2;
_printMenuText(col, yPos, "L.V.C. < ", false, eRightJustify);
if(_LVC)
sprintf(msg, "%.1fV", float(_LVC) * 0.1);
else
strcpy(msg, "OFF");
_printMenuText(col, yPos, msg, _rowSel == 2);
// temp offset
yPos = Line3;
_printMenuText(col, yPos, "\367C offset : ", false, eRightJustify);
sprintf(msg, "%+.1f", _tOfs);
_printMenuText(col, yPos, msg, _rowSel == 3);
// navigation line
int yPos = 53;
yPos = 53;
int xPos = _display.xCentre();
switch(_rowSel) {
@ -324,43 +351,44 @@ CFuelCalScreen::show()
bool
CFuelCalScreen::animate()
{
/* char msg[16];
if(_animateCount >= 0) {
switch(_animateCount) {
case 0:
_display.fillRect(0, Line3-4, BatteryIconInfo.width, 40, BLACK);
_drawBitmap(6, Line1-3, FuelIconSmallInfo);
_drawBitmap(0, Line2-1 , BatteryIconInfo);
_drawBitmap(5, Line3-4, miniThermoIconInfo);
break;
case 2:
_display.fillRect(6, Line1-3, FuelIconSmallInfo.width, FuelIconSmallInfo.height, BLACK); // scrub prior drip
_drawBitmap(6, Line1-2, FuelIconSmallInfo); // drip fuel
_display.fillRect(BatteryIconInfo.width - 4, Line2+2, 2, 5, BLACK); // deplete battery
_display.fillRect(7, Line3+2, 2, 2, WHITE); // grow thermometer
break;
case 4:
_display.fillRect(6, Line1-2, FuelIconSmallInfo.width, FuelIconSmallInfo.height, BLACK); // scrub prior drip
_drawBitmap(6, Line1-1, FuelIconSmallInfo); // drip fuel
_display.fillRect(BatteryIconInfo.width - 7, Line2+2, 2, 5, BLACK); // deplete battery
_display.fillRect(7, Line3+1, 2, 1, WHITE); // grow thermometer
break;
case 6:
_display.fillRect(6, Line1-1, FuelIconSmallInfo.width, FuelIconSmallInfo.height, BLACK); // scrub prior drip
_drawBitmap(6, Line1, FuelIconSmallInfo); // drip fuel
_display.fillRect(BatteryIconInfo.width - 10, Line2+2, 2, 5, BLACK); // deplete battery
_display.fillRect(7, Line3, 2, 1, WHITE); // grow thermometer
break;
case 8:
_display.fillRect(6, Line1, FuelIconSmallInfo.width, FuelIconSmallInfo.height, BLACK); // scrub prior drip
_drawBitmap(6, Line1+1, FuelIconSmallInfo); // drip fuel
_display.fillRect(BatteryIconInfo.width - 13, Line2+2, 2, 5, BLACK); // deplete battery
_display.fillRect(7, Line3-1, 2, 1, WHITE); // grow thermometer
break;
}
if(isPasswordBusy() || (_rowSel == 4)) { // Password screen activity
_printMenuText(Column, Line2, " ");
_printMenuText(Column, Line1, " ");
if(_rowSel == 4)
_printMenuText(_display.xCentre(), 43, "Confirm save", false, eCentreJustify);
}
else {
_animateCount++;
WRAPUPPERLIMIT(_animateCount, 9, 0);
}
if(_rowSel == 1) {
_display.drawRect(Column-border, Line1-border, 34, 8+2*border, BLACK);
_display.drawRoundRect(Column-border, Line1-border, 34, 8+2*border, radius, WHITE);
}
else {
_printMenuText(Column, Line1, " ");
}
if(_animateCount < 4)
sprintf(msg, "PF-%d ", _glowDrive);
else
sprintf(msg, "(%dW)", plugPowers[_glowDrive-1]);
_printMenuText(Column, Line1, msg);
int xPos = Column;
_printMenuText(xPos, Line2, " ", _rowSel == 2); // erase, but create selection loop
if(_animateCount < 4) {
sprintf(msg, "SN-%d", _fanSensor);
_printMenuText(Column, Line2, msg);
}
else {
sprintf(msg, "(\365%d)", _fanSensor); // \365 is division character
_printMenuText(xPos, Line2, msg);
}
}*/
return true;
}
@ -370,6 +398,15 @@ CFuelCalScreen::keyHandler(uint8_t event)
{
sHeaterTuning tuning;
if(event & keyRepeat) {
if(event & key_Left) {
_adjust(-1);
}
if(event & key_Right) {
_adjust(+1);
}
}
if(event & keyPressed) {
// press LEFT to select previous screen
if(event & key_Left) {
@ -418,9 +455,13 @@ CFuelCalScreen::keyHandler(uint8_t event)
UPPERLIMIT(_rowSel, 3);
break;
case 4: // confirmed save
_display.clearDisplay();
_animateCount = -1;
_showStoringMessage();
tuning = NVstore.getHeaterTuning();
tuning.pumpCal = _mlPerStroke;
tuning.lowVolts = _LVC;
tuning.tempOfs = _tOfs;
NVstore.setHeaterTuning(tuning);
saveNV();
_rowSel = 0;
@ -436,6 +477,8 @@ CFuelCalScreen::keyHandler(uint8_t event)
case 1:
case 2:
case 3:
_animateCount = -1;
_display.clearDisplay();
_rowSel = 4;
break;
}
@ -452,10 +495,34 @@ CFuelCalScreen::_adjust(int dir)
switch(_rowSel) {
case 1:
_mlPerStroke += dir * 0.001;
BOUNDSLIMIT(_mlPerStroke, 0.001, 1);
break;
case 2:
case 2:
if(_LVC == 0) {
if(NVstore.getHeaterTuning().sysVoltage == 120)
_LVC = dir > 0 ? 115 : 0;
else
_LVC = dir > 0 ? 230 : 0;
}
else {
_LVC += dir;
if(NVstore.getHeaterTuning().sysVoltage == 120) {
if(_LVC < 100)
_LVC = 0;
else
UPPERLIMIT(_LVC, 125);
}
else {
if(_LVC < 200)
_LVC = 0;
else
UPPERLIMIT(_LVC, 250);
}
}
break;
case 3:
case 3:
_tOfs += dir * 0.1;
BOUNDSLIMIT(_tOfs, -10, 10);
break;
}
}

View File

@ -45,19 +45,4 @@ public:
void onSelect();
};
class CFuelCalScreen : public CPasswordScreen
{
int _rowSel;
void _adjust(int dir);
float _mlPerStroke;
int _animateCount;
void _initUI();
public:
CFuelCalScreen(C128x64_OLED& display, CScreenManager& mgr);
bool show();
bool animate();
bool keyHandler(uint8_t event);
void onSelect();
};
#endif

View File

@ -57,7 +57,7 @@ CInheritSettingsScreen::_initUI()
bool
CInheritSettingsScreen::show()
{
CScreenHeader::show();
CScreenHeader::show(false);
_display.writeFillRect(0, 16, 96, 12, WHITE);
_printInverted(3, 18, "Inherit Settings", true);

View File

@ -41,6 +41,7 @@ CPasswordScreen::CPasswordScreen(C128x64_OLED& display, CScreenManager& mgr) : C
void
CPasswordScreen::onSelect()
{
CScreenHeader::onSelect();
_initUI();
}

View File

@ -46,6 +46,7 @@ CPrimingScreen::CPrimingScreen(C128x64_OLED& display, CScreenManager& mgr) : CSc
void
CPrimingScreen::onSelect()
{
CScreenHeader::onSelect();
_stopPump();
_initUI();
}
@ -69,7 +70,7 @@ CPrimingScreen::_initUI()
bool
CPrimingScreen::show()
{
CScreenHeader::show();
CScreenHeader::show(false);
CRect extents;
@ -200,7 +201,7 @@ CPrimingScreen::show()
}
else {
char msg[16];
sprintf(msg, "%.02fL", FuelGauge.Used_ml() * 0.001);
sprintf(msg, "%.02fL", FuelGauge.Used_mL() * 0.001);
_printMenuText(loc.xPos+1, midline+3, msg);
}

View File

@ -63,6 +63,7 @@ CScreen::show()
void
CScreen::onSelect()
{
_display.clearDisplay();
}
void

View File

@ -31,6 +31,8 @@
#include "fonts/Icons.h"
#include "fonts/MiniFont.h"
#include "../RTC/TimerManager.h"
#include "../Protocol/SmartError.h"
#include "../Utility/DataFilter.h"
#define MINIFONT miniFontInfo
@ -65,9 +67,15 @@ CScreenHeader::CScreenHeader(C128x64_OLED& disp, CScreenManager& mgr) : CScreen(
}
bool
CScreenHeader::show()
CScreenHeader::show(bool erase)
{
_display.clearDisplay();
if(erase)
_display.clearDisplay(); // erase everything
else {
_display.fillRect(0, 17, 128, 47, BLACK); // only erase below the header
_display.fillRect(119, 0, 9, 17, BLACK); // erase top of body thermo
_display.fillRect(0, 0, 9, 17, BLACK); // erase top of ambient thermo
}
// standard header items
// Bluetooth
@ -75,8 +83,7 @@ CScreenHeader::show()
// WiFi icon is updated in animate()
// battery
showBatteryIcon(getBatteryVoltage());
// Battery is updated in animate
// clock
showTime();
@ -104,7 +111,9 @@ CScreenHeader::animate()
// animate timer icon,
// inserting an update icon if new firmware available from internet web server
_animateCount++;
WRAPUPPERLIMIT(_animateCount, 10, 0);
_batteryCount++;
WRAPUPPERLIMIT(_animateCount, 9, 0);
WRAPUPPERLIMIT(_batteryCount, 5, 0);
if(isUpdateAvailable(true)) {
int xPos = X_TIMER_ICON - 3;
int yPos = Y_TIMER_ICON;
@ -122,7 +131,22 @@ CScreenHeader::animate()
}
}
else {
int xPos = X_BATT_ICON;
int yPos = Y_BATT_ICON;
showTimers();
switch(_batteryCount) {
case 3:
if(SmartError.checkVolts(FilteredSamples.FastipVolts.getValue(), FilteredSamples.FastGlowAmps.getValue())) {
showBatteryIcon(getBatteryVoltage(true));
}
else {
_display.fillRect(xPos, yPos, BatteryIconInfo.width, BatteryIconInfo.height, BLACK);
}
break;
case 0:
showBatteryIcon(getBatteryVoltage(true));
break;
}
}
showWifiIcon();
@ -285,6 +309,7 @@ CScreenHeader::showTime()
int xPos = X_WIFI_ICON + WifiIconInfo.width + WifiInIconInfo.width; // rhs of wifi conglomeration
if(isWifiAP()) xPos += 4; // add more if an Access Point
_display.fillRect(xPos - 15, Y_CLOCK, 30, arial_8ptFontInfo.nBitsPerLine, BLACK);
_printMenuText(X_CLOCK, Y_CLOCK, msg);
}
}

View File

@ -44,7 +44,8 @@ class CScreenHeader : public CScreen {
sScreenholdoff _UpAnnotation;
sScreenholdoff _DnAnnotation;
bool _colon;
int _animateCount;
uint8_t _animateCount;
uint8_t _batteryCount;
protected:
void showBTicon();
void showWifiIcon();
@ -53,7 +54,7 @@ protected:
virtual void showTime(); // x location depends upon how many timers are active
public:
CScreenHeader(C128x64_OLED& disp, CScreenManager& mgr);
bool show();
bool show(bool erase);
bool animate();
};

View File

@ -30,6 +30,7 @@
#include "ClockScreen.h"
#include "RebootScreen.h"
#include "HeaterSettingsScreen.h"
#include "FuelCalScreen.h"
#include "SettingsScreen.h"
#include "ThermostatModeScreen.h"
#include "FontDumpScreen.h"

View File

@ -43,6 +43,7 @@ CSetClockScreen::CSetClockScreen(C128x64_OLED& display, CScreenManager& mgr) : C
void
CSetClockScreen::onSelect()
{
CScreenHeader::onSelect();
_initUI();
}
@ -67,7 +68,7 @@ CSetClockScreen::show()
if(deltaT >= 0) {
_nextT += 1000;
CScreenHeader::show();
CScreenHeader::show(false);
char str[16];
int xPos, yPos;

View File

@ -47,6 +47,7 @@ CSetTimerScreen::CSetTimerScreen(C128x64_OLED& display, CScreenManager& mgr, int
void
CSetTimerScreen::onSelect()
{
CScreenHeader::onSelect();
_initUI();
NVstore.getTimerInfo(_timerID, _timerInfo);
}
@ -62,7 +63,7 @@ CSetTimerScreen::_initUI()
bool
CSetTimerScreen::show()
{
CScreenHeader::show();
CScreenHeader::show(false);
char str[20];
int xPos, yPos;

View File

@ -66,7 +66,7 @@ CSettingsScreen::show()
{
char str[16];
CScreenHeader::show();
CScreenHeader::show(false);
_display.writeFillRect(0, 16, 84, 12, WHITE);
_printInverted(3, 18, "Heater Tuning", true);

View File

@ -48,6 +48,7 @@ CWiFiScreen::CWiFiScreen(C128x64_OLED& display, CScreenManager& mgr) : CScreenHe
void
CWiFiScreen::onSelect()
{
CScreenHeader::onSelect();
_initUI();
}
@ -87,7 +88,7 @@ CWiFiScreen::_initUI()
bool
CWiFiScreen::show()
{
CScreenHeader::show();
CScreenHeader::show(false);
int yPos = 18;

View File

@ -662,11 +662,19 @@ const uint8_t PROGMEM arial_8ptBitmaps [] =
0x01, 0x00, // #
// @870 '~' (5 pixels wide)
0x03, 0x00, // ##
0x01, 0x00, // #
0x02, 0x00, // #
0x03, 0x00, // ##
0x01, 0x00, // #
0x02, 0x00, // #
// @880 '1' (5 pixels wide)
0x00, 0x00, //
0x04, 0x00, // #
0x08, 0x00, // #
0x1F, 0xE0, // ########
0x00, 0x00, //
};
// Character descriptors for Arial 8pt
@ -690,7 +698,7 @@ const FONT_CHAR_INFO PROGMEM arial_8ptDescriptors[] =
{1, 14, 100}, // '.'
{3, 14, 102}, // '/'
{5, 14, 108}, // '0'
{3, 14, 118}, // '1'
{5, 14, 880}, // '1'
{5, 14, 124}, // '2'
{5, 14, 134}, // '3'
{5, 14, 144}, // '4'

View File

@ -375,6 +375,25 @@ const uint8_t FuelIcon [] PROGMEM = {
};
const BITMAP_INFO FuelIconInfo(7, 12, FuelIcon);
//
// Image data for FuelIconSmall
//
const uint8_t PROGMEM FuelIconSmall[] =
{
0x20, // #
0x20, // #
0x70, // ###
0x70, // ###
0xF8, // #####
0xF8, // #####
0xF8, // #####
0xF8, // #####
0x70, // ###
};
const BITMAP_INFO FuelIconSmallInfo(5, 9, FuelIconSmall);
// 'Target', 13x13px
const uint8_t TargetIcon [] PROGMEM = {
0x0f, 0x80, // #####
@ -1083,3 +1102,25 @@ const uint8_t PROGMEM resetIcon[] =
};
const BITMAP_INFO resetIconInfo(17, 10, resetIcon);
//
// Image data for miniThermo
//
const uint8_t PROGMEM miniThermoIcon[] =
{
0x30, // ##
0x48, // # #
0x48, // # #
0x48, // # #
0x48, // # #
0x48, // # #
0x48, // # #
0x48, // # #
0x78, // ####
0xFC, // ######
0xFC, // ######
0xFC, // ######
0x78, // ####
};
const BITMAP_INFO miniThermoIconInfo(6, 13, miniThermoIcon);

View File

@ -65,8 +65,8 @@ extern const BITMAP_INFO FanIcon4Info;
// 'FuelIcon', 7x12px
extern const BITMAP_INFO FuelIconInfo;
extern const BITMAP_INFO FuelIconSmallInfo;
// 'Target', 13x13px
extern const BITMAP_INFO TargetIconInfo;
extern const BITMAP_INFO RepeatIconInfo;
@ -77,7 +77,8 @@ extern const BITMAP_INFO LargeTimerIconInfo;
extern const BITMAP_INFO VerticalRepeatIconInfo;
extern const BITMAP_INFO CrossLgIconInfo;
extern const BITMAP_INFO CrossIconInfo;
extern const BITMAP_INFO CrossIconInfo
;
// Bitmap for open
extern const BITMAP_INFO OpenIconInfo;
@ -137,3 +138,4 @@ extern const BITMAP_INFO ThermostatDegCIconInfo;
extern const BITMAP_INFO ThermostatDegFIconInfo;
extern const BITMAP_INFO ThermostatHzIconInfo;
extern const BITMAP_INFO resetIconInfo;
extern const BITMAP_INFO miniThermoIconInfo;

View File

@ -368,7 +368,7 @@ const char* Errstates [] PROGMEM = {
"Flame out", // [9] E-08
"Temp sense", // [10] E-09
"Ignition fail", // [11] E-10 SmartError manufactured state - sensing runstate 2 -> >5
"Failed 1st ignition attempt", // [12] E-11 SmartError manufactured state - sensing runstate 2 -> 3
"Failed 1st ignite", // [12] E-11 SmartError manufactured state - sensing runstate 2 -> 3
"Unknown error?" // mystery code!
};
@ -385,7 +385,7 @@ const char* ErrstatesEx [] PROGMEM = {
"E-08: Flame out", // [9] E-08
"E-09: Temp sense", // [10] E-09
"E-10: Ignition fail", // [11] E-10 SmartError manufactured state - sensing runstate 2 -> >5
"E-11: Failed 1st ignition attempt", // [12] E-11 SmartError manufactured state - sensing runstate 2 -> 3
"E-11: Failed 1st ignite", // [12] E-11 SmartError manufactured state - sensing runstate 2 -> 3
"Unknown error?" // mystery code!
};

View File

@ -2,7 +2,7 @@
* This file is part of the "bluetoothheater" distribution
* (https://gitlab.com/mrjones.id.au/bluetoothheater)
*
* Copyright (C) 2018 Ray Jones <ray@mrjones.id.au>
* Copyright (C) 2019 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
@ -21,6 +21,10 @@
#include "SmartError.h"
#include "TxManage.h"
#include "../Utility/helpers.h"
#include "../Utility/macros.h"
#include "../Utility/NVStorage.h"
#include "../Utility/DataFilter.h"
CSmartError::CSmartError()
{
@ -83,12 +87,12 @@ CSmartError::monitor(uint8_t newRunState)
else if(newRunState > 5) {
// transitioned from preheat to post glow
// - second ignition attempt failed, heater is shutting down
m_Error = 11;
m_Error = 11; // +1 over displayed error code
}
else if(newRunState == 3) {
// transitioned from preheat to retry
// - first ignition attempt failed, heater will retry
m_Error = 12;
m_Error = 12; // +1 over displayed error code
}
}
}
@ -106,7 +110,24 @@ CSmartError::monitor(uint8_t newRunState)
}
}
m_prevRunState = newRunState;
m_prevRunState = newRunState;
}
bool
CSmartError::checkVolts(float ipVolts, float glowI)
{
// check for low voltage
// values here are x10 integers
if(NVstore.getHeaterTuning().lowVolts) { // only if enabled
float cableComp = glowI * 0.1; // allow 1V drop for 10A current (bit naive but better than no compensation)
float Thresh = NVstore.getHeaterTuning().getLVC() - cableComp; // NVstore
if(ipVolts < Thresh) {
m_Error = 2; // +1 over displayed error code
requestOff();
return false;
}
}
return true;
}
// return our smart error, if it exists, as the registered code

View File

@ -31,6 +31,7 @@ public:
void inhibit();
void monitor(const CProtocol& heaterFrame);
void monitor(uint8_t runstate);
bool checkVolts(float volts, float plugI);
uint8_t getError();
};

View File

@ -277,7 +277,29 @@ void interpretJsonCommand(char* pLine)
sHeaterTuning ht = NVstore.getHeaterTuning();
ht.pumpCal = fCal;
NVstore.setHeaterTuning(ht);
NVstore.save();
}
}
else if(strcmp("TempOffset", it->key) == 0) {
float fCal = it->value.as<float>();
if(INBOUNDS(fCal, -10.0, +10.0)) {
sHeaterTuning ht = NVstore.getHeaterTuning();
ht.tempOfs = fCal;
NVstore.setHeaterTuning(ht);
}
}
else if(strcmp("LowVoltCutout", it->key) == 0) {
float fCal = it->value.as<float>();
if(fCal != 0) {
bool bOK = false;
if(NVstore.getHeaterTuning().sysVoltage == 120)
bOK = INBOUNDS(fCal, 10.0, 12.5);
else
bOK = INBOUNDS(fCal, 20.0, 25.0);
if(bOK) {
sHeaterTuning ht = NVstore.getHeaterTuning();
ht.lowVolts = fCal;
NVstore.setHeaterTuning(ht);
}
}
}
}
@ -325,7 +347,7 @@ bool makeJSONString(CModerator& moderator, char* opStr, int len)
bSend |= moderator.addJson("FanRPM", getFanSpeed(), root );
bSend |= moderator.addJson("FanVoltage", getHeaterInfo().getFan_Voltage(), root );
bSend |= moderator.addJson("FanSensor", getHeaterInfo().getFan_Sensor(), root );
bSend |= moderator.addJson("InputVoltage", getBatteryVoltage(), root );
bSend |= moderator.addJson("InputVoltage", getBatteryVoltage(false), root );
bSend |= moderator.addJson("SystemVoltage", getHeaterInfo().getSystemVoltage(), root );
bSend |= moderator.addJson("GlowVoltage", getGlowVolts(), root );
bSend |= moderator.addJson("GlowCurrent", getGlowCurrent(), root );
@ -354,9 +376,11 @@ bool makeJSONStringEx(CModerator& moderator, char* opStr, int len)
bSend |= moderator.addJson("ThermostatUndertemp", NVstore.getUserSettings().cyclic.Start, root);
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
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("TempOffset", NVstore.getHeaterTuning().tempOfs, root); // degC offset
bSend |= moderator.addJson("LowVoltCutout", NVstore.getHeaterTuning().lowVolts, root); // low volatge cutout
if(bSend) {
root.printTo(opStr, len);

View File

@ -2,7 +2,7 @@
* This file is part of the "bluetoothheater" distribution
* (https://gitlab.com/mrjones.id.au/bluetoothheater)
*
* Copyright (C) 2018 Ray Jones <ray@mrjones.id.au>
* Copyright (C) 2019 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
@ -44,6 +44,11 @@ struct sFilteredData {
CExpMean GlowAmps;
CExpMean Fan;
CExpMean AmbientTemp;
CExpMean FastipVolts;
CExpMean FastGlowAmps;
};
extern sFilteredData FilteredSamples;
#endif

View File

@ -71,7 +71,7 @@ CFuelGauge::Integrate(float Hz)
}
float
CFuelGauge::Used_ml()
CFuelGauge::Used_mL()
{
return _pumpStrokes * _pumpCal; // strokes * millilitre / stroke
}

View File

@ -34,7 +34,7 @@ public:
void init(float fuelUsed = 0);
void reset();
void Integrate(float Hz);
float Used_ml();
float Used_mL();
float Used_strokes();
};

View File

@ -97,6 +97,11 @@ sHeaterTuning::setSysVoltage(float fVal)
}
}
float
sHeaterTuning::getLVC() const
{
return lowVolts * 0.1;
}
void
CHeaterStorage::getTimerInfo(int idx, sTimer& timerInfo)
@ -235,7 +240,12 @@ sHeaterTuning::load()
validatedLoad("systemVoltage", sysVoltage, 120, u8Match2, 120, 240);
validatedLoad("fanSensor", fanSensor, 1, u8inBounds, 1, 2);
validatedLoad("glowDrive", glowDrive, 5, u8inBounds, 1, 6);
if(sysVoltage == 120)
validatedLoad("lowVolts", lowVolts, 115, u8inBounds, 100, 125);
else
validatedLoad("lowVolts", lowVolts, 230, u8inBounds, 200, 250);
validatedLoad("pumpCal", pumpCal, 0.02, 0.001, 1);
validatedLoad("tempOfs", tempOfs, 0.0, -10.0, +10.0);
preferences.end();
}
@ -252,7 +262,9 @@ sHeaterTuning::save()
preferences.putUChar("systemVoltage", sysVoltage);
preferences.putUChar("fanSensor", fanSensor);
preferences.putUChar("glowDrive", glowDrive);
preferences.putUChar("lowVolts", lowVolts);
preferences.putFloat("pumpCal", pumpCal);
preferences.putFloat("tempOfs", tempOfs);
preferences.end();
}

View File

@ -37,10 +37,12 @@ struct sHeaterTuning : public CESP32_NVStorage {
uint8_t Pmax;
uint16_t Fmin;
uint16_t Fmax;
uint8_t sysVoltage;
uint8_t sysVoltage; // x10
uint8_t fanSensor;
uint8_t glowDrive;
uint8_t lowVolts; // x10
float pumpCal;
float tempOfs;
bool valid() {
bool retval = true;
@ -52,6 +54,11 @@ struct sHeaterTuning : public CESP32_NVStorage {
retval &= fanSensor == 1 || fanSensor == 2;
retval &= INBOUNDS(glowDrive, 1, 6);
retval &= INBOUNDS(pumpCal, 0.001, 1.0);
if(sysVoltage == 120)
retval &= INBOUNDS(lowVolts, 100, 125);
else
retval &= INBOUNDS(lowVolts, 200, 250);
retval &= INBOUNDS(tempOfs, -10, +10);
return retval;
};
void init() {
@ -63,6 +70,8 @@ struct sHeaterTuning : public CESP32_NVStorage {
fanSensor = 1;
glowDrive = 5;
pumpCal = 0.02;
lowVolts = 115;
tempOfs = 0;
};
void load();
void save();
@ -75,6 +84,8 @@ struct sHeaterTuning : public CESP32_NVStorage {
fanSensor = rhs.fanSensor;
glowDrive = rhs.glowDrive;
pumpCal = rhs.pumpCal;
lowVolts = rhs.lowVolts;
tempOfs = rhs.tempOfs;
return *this;
}
float getPmin() const;
@ -82,6 +93,7 @@ struct sHeaterTuning : public CESP32_NVStorage {
void setPmin(float val);
void setPmax(float val);
void setSysVoltage(float val);
float getLVC() const;
};
struct sHomeMenuActions {

View File

@ -78,19 +78,12 @@ extern void setUploadSize(long val);
extern void getGPIOinfo(sGPIO& info);
extern void simulateGPIOin(uint8_t newKey);
extern void setDegFMode(bool state);
extern float getBatteryVoltage();
extern float getBatteryVoltage(bool fast);
extern float getGlowVolts();
extern float getGlowCurrent();
extern float getFanSpeed();
extern int sysUptime();
/* 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);

View File

@ -2,7 +2,7 @@
* This file is part of the "bluetoothheater" distribution
* (https://gitlab.com/mrjones.id.au/bluetoothheater)
*
* Copyright (C) 2018 Ray Jones <ray@mrjones.id.au>
* Copyright (C) 2019 Ray Jones <ray@mrjones.id.au>
* Copyright (C) 2018 James Clark
*
* This program is free software: you can redistribute it and/or modify
@ -23,12 +23,12 @@
#ifndef __MACROS_H__
#define __MACROS_H__
#define LOWERLIMIT(A, B) if((A) < (B)) (A) = (B)
#define UPPERLIMIT(A, B) if((A) > (B)) (A) = (B)
#define WRAPUPPERLIMIT(A, B, C) if((A) > (B)) (A) = (C)
#define WRAPLOWERLIMIT(A, B, C) if((A) < (B)) (A) = (C)
#define WRAPLIMITS(A, B, C) if((A) < (B)) (A) = (C) ; if((A) > (C)) (A) = (B)
#define LOWERLIMIT(A, B) { if((A) < (B)) (A) = (B); }
#define UPPERLIMIT(A, B) { if((A) > (B)) (A) = (B); }
#define WRAPUPPERLIMIT(A, B, C) { if((A) > (B)) (A) = (C); }
#define WRAPLOWERLIMIT(A, B, C) { if((A) < (B)) (A) = (C); }
#define WRAPLIMITS(A, B, C) { if((A) < (B)) (A) = (C) ; if((A) > (C)) (A) = (B); }
#define INBOUNDS(TST, MIN, MAX) (((TST) >= (MIN)) && ((TST) <= (MAX)))
#define BOUNDSLIMIT(A, B, C) if((A) < (B)) (A) = (B); if((A) > (C)) (A) = (C)
#define BOUNDSLIMIT(A, B, C) { if((A) < (B)) (A) = (B); if((A) > (C)) (A) = (C); }
#endif