ESP32_ChinaDieselHeater_Con.../src/OLED/ScreenManager.cpp

863 lines
38 KiB
C++

/*
* This file is part of the "bluetoothheater" distribution
* (https://gitlab.com/mrjones.id.au/bluetoothheater)
*
* 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
* 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 "ScreenManager.h"
#include "DetailedScreen.h"
#include "BasicScreen.h"
#include "PrimingScreen.h"
#include "WiFiScreen.h"
#include "WiFiSTAScreen.h"
#include "FuelMixtureScreen.h"
#include "SetClockScreen.h"
#include "SetTimerScreen.h"
#include "ClockScreen.h"
#include "RebootScreen.h"
#include "HeaterSettingsScreen.h"
#include "FuelCalScreen.h"
#include "SettingsScreen.h"
#include "ThermostatModeScreen.h"
#include "TimerChartScreen.h"
#include "InheritSettingsScreen.h"
#include "GPIOInfoScreen.h"
#include "GPIOSetupScreen.h"
#include "VersionInfoScreen.h"
#include "HomeMenuSelScreen.h"
#include "MenuSelScreen.h"
#include "TimeoutsScreen.h"
#include "HourMeterScreen.h"
#include "BTScreen.h"
#include "MenuTrunkScreen.h"
#include "MQTTScreen.h"
#include "DS18B20Screen.h"
#include "BME280Screen.h"
#include "TempSensorScreen.h"
#include "FrostScreen.h"
#include "HumidityScreen.h"
#include "WebPageUpdateScreen.h"
#include "LVCScreen.h"
#include <Wire.h>
#include "../cfg/pins.h"
#include "../cfg/BTCConfig.h"
#include "KeyPad.h"
#include "fonts/Icons.h"
#include "fonts/MiniFont.h"
#include "fonts/MidiFont.h"
#include "../Protocol/Protocol.h"
#include "fonts/Arial.h"
#include <SPIFFS.h>
#include "../Utility/BoardDetect.h"
#pragma pack ( push, 1)
struct sBMPhdr {
char b0;
char b1;
uint32_t filesize;
uint16_t resv1;
uint16_t resv2;
uint32_t startofs;
uint32_t hdrsize;
uint32_t width;
uint32_t height;
uint16_t numcolorplanes;
uint16_t bitsperpixel;
uint32_t compmethod;
uint32_t imagesize;
uint32_t hRes;
uint32_t vRes;
uint32_t numColorsPalette;
uint32_t numImportantColors;
};
#pragma pack (pop)
extern CScreenManager ScreenManager;
////////////////////////////////////////////////////////////////////////////////////////////////
// splash creen created using image2cpp http://javl.github.io/image2cpp/
// Settings:
// Black background
// Invert [X]
// Arduino code, single bitmap
// Identifier: DieselSplash
// Draw Mode: Horizontal
//
const uint8_t DieselSplash [] PROGMEM =
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // #
0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ####### ##
0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // # # # #
0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x08, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // # # # # #
0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0xC4, 0x00, 0x03, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // # ##### # ## #
0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x20, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // # # ##
0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x03, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // # # ## #
0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ### # # #
0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x40, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // # # # #
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ##
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // # #
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // #
0x00, 0x30, 0x03, 0x03, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ## ## ## #
0x00, 0x30, 0x07, 0x86, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ## #### ## ##
0x00, 0x78, 0x0E, 0xC6, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // #### ### ## ## ##
0x00, 0x78, 0x0C, 0xC6, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // #### ## ## ## ##
0x00, 0xD8, 0x0C, 0x0C, 0x0E, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x0E, 0x00, 0x1C, // ## ## ## ## ### ## ## ### ### ###
0x00, 0xCC, 0x18, 0x0C, 0x1F, 0x06, 0x3B, 0x1C, 0x00, 0x30, 0x31, 0xCC, 0x00, 0x1F, 0x06, 0x38, // ## ## ## ## ##### ## ### ## ### ## ## ### ## ##### ## ###
0x01, 0x8C, 0x18, 0xFF, 0xF3, 0x06, 0x73, 0x3E, 0x18, 0x30, 0x33, 0x8C, 0xF0, 0x33, 0x06, 0x70, // ## ## ## ############ ## ## ### ## ##### ## ## ## ### ## #### ## ## ## ###
0x03, 0x8C, 0x3F, 0x8C, 0x33, 0x0E, 0xC3, 0x66, 0x38, 0x30, 0x76, 0x0D, 0xF8, 0x33, 0x0E, 0xC0, // ### ## ####### ## ## ## ### ## ## ## ## ### ## ### ## ## ###### ## ## ### ##
0x03, 0x0C, 0x3E, 0x0C, 0x36, 0x17, 0x83, 0xC6, 0x58, 0x70, 0xBC, 0x0F, 0xB8, 0x76, 0x17, 0x80, // ## ## ##### ## ## ## # #### #### ## # ## ### # #### ##### ### ### ## # ####
0x07, 0xFE, 0x18, 0x0C, 0x3C, 0x27, 0x03, 0x86, 0x98, 0x71, 0x38, 0x0F, 0x18, 0xBC, 0x27, 0x00, // ########## ## ## #### # ### ### ## # ## ### # ### #### ## # #### # ###
0x0F, 0xCE, 0x18, 0x18, 0x30, 0x47, 0x03, 0x8F, 0x18, 0xDE, 0x38, 0x0E, 0x19, 0x30, 0x47, 0x00, // ###### ### ## ## ## # ### ### #### ## ## #### ### ### ## # ## # ###
0x1C, 0x07, 0x18, 0x18, 0x31, 0x86, 0x03, 0x1E, 0x19, 0x8C, 0x30, 0x0E, 0x1E, 0x31, 0x86, 0x00, // ### ### ## ## ## ## ## ## #### ## ## ## ## ### #### ## ## ##
0x38, 0x03, 0x18, 0x18, 0x3F, 0x06, 0x03, 0x38, 0x1F, 0x00, 0x30, 0x0C, 0x0C, 0x3F, 0x06, 0x00, // ### ## ## ## ###### ## ## ### ##### ## ## ## ###### ##
0x30, 0x01, 0x98, 0x18, 0x1E, 0x04, 0x01, 0xF0, 0x0C, 0x00, 0x20, 0x00, 0x00, 0x1E, 0x04, 0x00, // ## ## ## ## #### # ##### ## # #### #
0x20, 0x01, 0xB0, 0x30, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // # ## ## ## ##
0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ##
0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ###
0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x21, 0x24, 0x00, // ### ## # # # # #
0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x20, 0x24, 0x00, // ## # # # # #
0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x19, 0x28, 0xCA, 0x30, 0xE1, 0xC9, 0x06, 0x39, 0x25, 0x20, // ## ## # # # ## # # ## ### ### # # ## ### # # # # #
0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x25, 0x69, 0x2D, 0x49, 0x21, 0x29, 0x09, 0x29, 0x49, 0x20, // # # # # # ## # # # ## # # # # # # # # # # # # # # # # # #
0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x45, 0xAB, 0xC8, 0xF2, 0x22, 0x29, 0x10, 0x4A, 0x49, 0x20, // # # # # ## # # #### # #### # # # # # # # # # # # # # #
0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x45, 0xB2, 0x08, 0x82, 0x22, 0x2A, 0x10, 0x4A, 0x49, 0x40, // # # # # ## ## # # # # # # # # # # # # # # # # #
0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x49, 0x22, 0x50, 0x92, 0x42, 0x44, 0x11, 0x4A, 0x48, 0x80, // # # # # # # # # # # # # # # # # # # # # # # # #
0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x31, 0x21, 0x90, 0x61, 0x81, 0x84, 0x0E, 0x4A, 0x6C, 0x80, // ### ## # # ## # ## ## ## # ### # # # ## ## #
0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, // # # #
0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ##
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
};
void storeSplashScreen(const uint8_t* image)
{
Preferences preferences;
DebugPort.println("Storing new splash screen");
preferences.begin("splashscreen", false);
preferences.putBytes("image", image, 1024);
preferences.end();
}
void loadSplashScreen(uint8_t* image)
{
Preferences preferences;
preferences.begin("splashscreen", false);
int size = preferences.getBytes("image", image, 1024);
preferences.end();
if(size == 0) {
storeSplashScreen(DieselSplash);
memcpy(image, DieselSplash, 1024);
}
}
void checkSplashScreenUpdate()
{
if(SPIFFS.exists("/splash.bmp")) { // If a splash.bmp file was uploaded
File file = SPIFFS.open("/splash.bmp", "rb"); // Open it
sBMPhdr header;
file.readBytes((char*)&header, 0x3e);
do {
if(header.b0 != 'B' || header.b1 != 'M') {
DebugPort.println("Bad BMP header");
break;
}
if(header.width != 128 || header.height != 64) {
DebugPort.println("Bad BMP size");
break;
}
file.seek(header.startofs);
bool bOK = true;
uint8_t image[1024];
uint8_t line[128];
switch(header.bitsperpixel) {
case 1:
DebugPort.println("Reading monochrome bitmap file for splash screen");
memset(image, 0, 1024);
for(int i=0; i<64; i++) {
file.readBytes((char*)line, 16);
for(int j=0; j < 16; j++)
line[j] ^= 0xff; // invert black/white
memcpy(&image[(63-i)*16], line, 16);
}
break;
case 4:
DebugPort.println("Reading 16 color bitmap file for splash screen");
memset(image, 0, 1024);
for(int i=0; i<64; i++) {
file.readBytes((char*)line, 64);
for(int j=0; j < 16; j++) {
uint8_t packed = 0;
for(int k=0; k<4; k++) {
packed <<= 2;
uint8_t hold = line[k+j*4];
if((hold & 0xf0) == 0)
packed |= 0x02;
if((hold & 0x0f) == 0)
packed |= 0x01;
}
line[j] = packed;
}
memcpy(&image[(63-i)*16], line, 16);
}
break;
case 8:
DebugPort.println("Reading 256 color bitmap file for splash screen");
memset(image, 0, 1024);
for(int i=0; i<64; i++) {
file.readBytes((char*)line, 128);
for(int j=0; j < 16; j++) {
uint8_t packed = 0;
for(int k=0; k<8; k++) {
packed <<= 1;
if(line[k+j*8] == 0)
packed |= 0x01;
}
line[j] = packed;
}
memcpy(&image[(63-i)*16], line, 16);
}
break;
default:
DebugPort.println("Bad BMP bpp");
bOK = false;
break;
}
if(bOK)
storeSplashScreen(image);
} while(0);
file.close();
SPIFFS.remove("/splash.bmp");
ScreenManager.showSplash();
delay(2000);
}
}
CScreenManager::CScreenManager()
{
_pDisplay = NULL;
_menu = -1;
_subMenu = 0;
_rootMenu = -1;
_bReqUpdate = false;
_DimTime_ms = millis() + 60000;
_MenuTimeout = millis() + 60000;
_pRebootScreen = NULL;
_bDimmed = false;
_bReload = true;
_OTAholdoff = 0;
}
CScreenManager::~CScreenManager()
{
for(int i=0; i < _Screens.size(); i++) {
for(int j=0; j < _Screens[i].size(); j++) {
if(_Screens[i][j]) {
delete _Screens[i][j];
_Screens[i][j] = NULL;
}
}
}
if(_pDisplay) {
delete _pDisplay; _pDisplay = NULL;
}
}
void
CScreenManager::begin()
{
// 128 x 64 OLED support (I2C)
// xxxx_SWITCHCAPVCC = generate display voltage from 3.3V internally
_pDisplay = new C128x64_OLED(OLED_SDA_pin, OLED_SCL_pin);
#if USE_ADAFRUIT_SH1106 == 1
_pDisplay->begin(SH1106_SWITCHCAPVCC);
Wire.begin(OLED_SDA_pin, OLED_SCL_pin, 800000); // speed up I2C from the default crappy 100kHz set via the adafruit begin!
#elif USE_ADAFRUIT_SSD1306 == 1
_pDisplay->begin(SSD1306_SWITCHCAPVCC, 0x3c);
_pDisplay->ssd1306_command(SSD1306_SETPRECHARGE); // 0xd9
_pDisplay->ssd1306_command(0x1F); // correct lame reversal of OLED current phases
#endif
// replace adafruit splash screen
showSplash();
delay(2000);
_loadScreens();
}
void CScreenManager::_unloadScreens()
{
for (auto menuloop : _Screens) {
for(auto menu : menuloop) {
delete menu;
}
}
_Screens.clear();
}
void
CScreenManager::_loadScreens()
{
_unloadScreens();
DebugPort.println("Creating Screens");
std::vector<CScreen*> menuloop;
// create root menu loop
if(NVstore.getUserSettings().menuMode == 0) {
menuloop.push_back(new CDetailedScreen(*_pDisplay, *this)); // detail control
menuloop.push_back(new CBasicScreen(*_pDisplay, *this)); // basic control
menuloop.push_back(new CClockScreen(*_pDisplay, *this)); // clock
menuloop.push_back(new CPrimingScreen(*_pDisplay, *this)); // mode / priming
if(getBoardRevision() != 0 && getBoardRevision() != BRD_V2_NOGPIO) // has GPIO support
menuloop.push_back(new CGPIOInfoScreen(*_pDisplay, *this)); // GPIO info
menuloop.push_back(new CMenuTrunkScreen(*_pDisplay, *this));
}
else if(NVstore.getUserSettings().menuMode == 1) {
menuloop.push_back(new CMenuTrunkScreen(*_pDisplay, *this));
menuloop.push_back(new CBasicScreen(*_pDisplay, *this)); // basic control
menuloop.push_back(new CClockScreen(*_pDisplay, *this)); // clock
}
else if(NVstore.getUserSettings().menuMode == 2) {
menuloop.push_back(new CMenuTrunkScreen(*_pDisplay, *this));
menuloop.push_back(new CBasicScreen(*_pDisplay, *this)); // basic control
menuloop.push_back(new CClockScreen(*_pDisplay, *this)); // clock
if(getBoardRevision() != 0 && getBoardRevision() != BRD_V2_NOGPIO) // has GPIO support
menuloop.push_back(new CGPIOInfoScreen(*_pDisplay, *this)); // GPIO info
}
_Screens.push_back(menuloop);
// create timer screens loop
menuloop.clear();
menuloop.push_back(new CTimerChartScreen(*_pDisplay, *this, 0)); // timer chart
menuloop.push_back(new CSetTimerScreen(*_pDisplay, *this, 0)); // set timer 1
menuloop.push_back(new CSetTimerScreen(*_pDisplay, *this, 1)); // set timer 2
menuloop.push_back(new CSetTimerScreen(*_pDisplay, *this, 2)); // set timer 3
menuloop.push_back(new CSetTimerScreen(*_pDisplay, *this, 3)); // set timer 4
menuloop.push_back(new CSetTimerScreen(*_pDisplay, *this, 4)); // set timer 5
menuloop.push_back(new CSetTimerScreen(*_pDisplay, *this, 5)); // set timer 6
menuloop.push_back(new CSetTimerScreen(*_pDisplay, *this, 6)); // set timer 7
menuloop.push_back(new CSetTimerScreen(*_pDisplay, *this, 7)); // set timer 8
menuloop.push_back(new CSetTimerScreen(*_pDisplay, *this, 8)); // set timer 9
menuloop.push_back(new CSetTimerScreen(*_pDisplay, *this, 9)); // set timer 10
menuloop.push_back(new CSetTimerScreen(*_pDisplay, *this, 10)); // set timer 11
menuloop.push_back(new CSetTimerScreen(*_pDisplay, *this, 11)); // set timer 12
menuloop.push_back(new CSetTimerScreen(*_pDisplay, *this, 12)); // set timer 13
menuloop.push_back(new CSetTimerScreen(*_pDisplay, *this, 13)); // set timer 14
_Screens.push_back(menuloop);
// create User Settings screens loop
menuloop.clear();
if(NVstore.getUserSettings().menuMode == 0) { // standard heater control menu set
menuloop.push_back(new CThermostatModeScreen(*_pDisplay, *this)); // thermostat settings screen
menuloop.push_back(new CFrostScreen(*_pDisplay, *this)); // frost mode screen
if(getTempSensor().getBME280().getCount()) {
menuloop.push_back(new CHumidityScreen(*_pDisplay, *this)); // humidity settings screen
}
menuloop.push_back(new CHomeMenuSelScreen(*_pDisplay, *this)); // Home menu settings screen
menuloop.push_back(new CTimeoutsScreen(*_pDisplay, *this)); // Other options screen
menuloop.push_back(new CMenuSelScreen(*_pDisplay, *this)); // Menu mode screen
if(getBoardRevision() != 0 && getBoardRevision() != BRD_V2_NOGPIO) // has GPIO support ?
menuloop.push_back(new CGPIOSetupScreen(*_pDisplay, *this)); // GPIO settings screen
}
else if(NVstore.getUserSettings().menuMode == 1) { // "no fiddle" menu set
menuloop.push_back(new CMenuSelScreen(*_pDisplay, *this)); // Menu mode screen
}
else if(NVstore.getUserSettings().menuMode == 2) { // no heater menu set
menuloop.push_back(new CNoHeaterHomeMenuSelScreen(*_pDisplay, *this)); // No Heater Home menu settings screen
menuloop.push_back(new CMenuSelScreen(*_pDisplay, *this)); // Menu mode screen
// if(getBoardRevision() != 0 && getBoardRevision() != BRD_V2_NOGPIO) // has GPIO support ?
// menuloop.push_back(new CGPIOSetupScreen(*_pDisplay, *this)); // GPIO settings screen
}
_Screens.push_back(menuloop);
// create System Settings screens loop
if(NVstore.getUserSettings().menuMode == 0 || NVstore.getUserSettings().menuMode == 2) {
menuloop.clear();
menuloop.push_back(new CVersionInfoScreen(*_pDisplay, *this)); // GPIO settings screen
menuloop.push_back(new CWebPageUpdateScreen(*_pDisplay, *this)); // Web Page update screen
if(NVstore.getUserSettings().menuMode == 0) {
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()) {
menuloop.push_back(new CTempSensorScreen(*_pDisplay, *this));
menuloop.push_back(new CBME280Screen(*_pDisplay, *this));
}
else {
menuloop.push_back(new CDS18B20Screen(*_pDisplay, *this));
}
_Screens.push_back(menuloop);
}
// create heater tuning screens loop - password protected
menuloop.clear();
menuloop.push_back(new CFuelMixtureScreen(*_pDisplay, *this)); // mixture tuning
menuloop.push_back(new CHeaterSettingsScreen(*_pDisplay, *this)); // heater system tuning
menuloop.push_back(new CFuelCalScreen(*_pDisplay, *this)); // fuel pump calibration
menuloop.push_back(new CLVCScreen(*_pDisplay, *this)); // low volt cutout calibration
_Screens.push_back(menuloop);
// create branch screens
menuloop.clear();
menuloop.push_back(new CSetClockScreen(*_pDisplay, *this)); // clock set branch screen
menuloop.push_back(new CInheritSettingsScreen(*_pDisplay, *this)); // inherit OEM settings branch screen
menuloop.push_back(new CSettingsScreen(*_pDisplay, *this)); // Tuning info
menuloop.push_back(new CDS18B20Screen(*_pDisplay, *this));
_Screens.push_back(menuloop);
_menu = 0;
#if RTC_USE_DS3231==0 && RTC_USE_DS1307==0 && RTC_USE_PCF8523==0
_rootMenu = 2; // bring up clock set screen first if using millis based RTC!
_subMenu = 2;
#else
_rootMenu = 1; // basic control screen
_subMenu = 1;
#endif
_bReload = false;
reqUpdate();
_enterScreen();
showSplash();
}
bool
CScreenManager::_checkOTAholdoff()
{
if(_OTAholdoff) {
long tDelta = millis() - _OTAholdoff;
if(tDelta < 0)
return false;
_pDisplay->clearDisplay();
_pDisplay->display(); // blank screen
_OTAholdoff = 0;
}
return true;
}
bool
CScreenManager::checkUpdate()
{
if(!_checkOTAholdoff())
return false;
if(_bReload)
_loadScreens();
long dimTimeout = NVstore.getUserSettings().dimTime;
// manage dimming or blanking the display, according to user defined inactivity interval
if(dimTimeout && _DimTime_ms) {
long tDelta = millis() - _DimTime_ms;
if(tDelta > 0) {
// time to dim the display
_dim(true);
_DimTime_ms = 0;
if(dimTimeout < 0) {
_pDisplay->clearDisplay();
_pDisplay->display(); // blank screen
}
}
}
if(NVstore.getUserSettings().menuTimeout && _MenuTimeout) {
long tDelta = millis() - _MenuTimeout;
if(tDelta > 0) {
_MenuTimeout = 0;
// we will be blanking the display, transit through a dim stage first
if(dimTimeout < 0)
_dim(true);
_leaveScreen();
// fall back to main menu
selectMenu(RootMenuLoop);
// upon dim timeout - sticky root menu screens are the first 3 in the list:
// Detailed Control
// Basic Control
// Clock
// return to those upon timeout, otherwise return to Basic Control screen
if((_rootMenu > 2) || ((_rootMenu == 0) && NVstore.getUserSettings().menuMode)) {
selectHomeMenu();
}
_enterScreen();
}
}
static int prevRunState = -1;
int runState = getHeaterInfo().getRunStateEx();
if(runState != prevRunState) {
if(runState > 0 && prevRunState == 0) {
// heater has started
uint8_t userStartMenu = NVstore.getUserSettings().HomeMenu.onStart;
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) {
case 0: DebugPort.println("Detailed control menu"); break;
case 1: DebugPort.println("Basic control menu"); break;
case 2: DebugPort.println("Clock menu"); break;
}
_rootMenu = _subMenu = userStartMenu;
_enterScreen();
}
}
if(runState == 0 && prevRunState > 0) {
// heater has stopped
uint8_t userStopMenu = NVstore.getUserSettings().HomeMenu.onStop;
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) {
case 0: DebugPort.println("Detailed control menu"); break;
case 1: DebugPort.println("Basic control menu"); break;
case 2: DebugPort.println("Clock menu"); break;
}
_rootMenu = _subMenu = userStopMenu;
_enterScreen();
}
}
prevRunState = runState;
}
if(_bReqUpdate) {
if((dimTimeout < 0) && (_DimTime_ms == 0)) {
// no screen updates, we should be blanked!
}
else {
if(_pRebootScreen) {
_pRebootScreen->show();
_bReqUpdate = false;
return true;
}
else {
if(_menu >= 0) {
_Screens[_menu][_subMenu]->show();
_bReqUpdate = false;
return true;
}
}
}
}
return false;
}
void
CScreenManager::reqUpdate()
{
_bReqUpdate = true;
}
bool
CScreenManager::animate()
{
if(!_checkOTAholdoff())
return false;
if(_pRebootScreen)
return false;
if((NVstore.getUserSettings().dimTime < 0) && (_DimTime_ms == 0))
// no screen updates, we should be blanked!
return false;
if(_menu >= 0)
return _Screens[_menu][_subMenu]->animate();
return false;
}
void
CScreenManager::refresh()
{
if(_pDisplay)
_pDisplay->display();
}
void
CScreenManager::_enterScreen()
{
if(_menu >= 0)
_Screens[_menu][_subMenu]->onSelect();
reqUpdate();
}
void
CScreenManager::_leaveScreen()
{
if(_menu >= 0)
_Screens[_menu][_subMenu]->onExit();
_returnMenu = _menu;
_returnSubMenu = _subMenu;
}
void
CScreenManager::returnMenu()
{
_menu = _returnMenu;
_subMenu = _returnSubMenu;
_enterScreen();
}
void
CScreenManager::_changeSubMenu(int dir)
{
_leaveScreen();
_subMenu += dir;
int bounds = _Screens[_menu].size() - 1;
WRAPLIMITS(_subMenu, 0, bounds);
if(_menu == 0)
_rootMenu = _subMenu; // track the root menu for when we branch then return
_enterScreen();
}
void
CScreenManager::nextMenu()
{
if(_menu >= 0 && _menu != BranchMenu) {
_changeSubMenu(+1);
}
}
void
CScreenManager::prevMenu()
{
if(_menu >= 0 && _menu != BranchMenu) {
_changeSubMenu(-1);
}
}
void
CScreenManager::keyHandler(uint8_t event)
{
if(_bDimmed) {
if(event & keyReleased) {
_dim(false);
bumpTimeout();
}
return; // initial press when dimmed is always thrown away
}
bumpTimeout();
// call key handler for active screen
if(_menu >= 0)
_Screens[_menu][_subMenu]->keyHandler(event);
}
void
CScreenManager::bumpTimeout()
{
long dimTime = NVstore.getUserSettings().dimTime;
_DimTime_ms = (millis() + abs(dimTime)) | 1;
_MenuTimeout = (millis() + NVstore.getUserSettings().menuTimeout) | 1;
}
void
CScreenManager::selectMenu(eUIMenuSets menuSet, int specific)
{
_leaveScreen();
if(_menu >= 0) { // only true once we have created the screens
_menu = menuSet;
if(specific >= 0) {
// 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
if(_menu == 0)
_subMenu = _rootMenu; // return to last used root menu
else
_subMenu = 0; // branches always go to first sub menu
}
}
_enterScreen();
}
void
CScreenManager::showRebootMsg(const char* content[2], long delayTime)
{
if(_pRebootScreen == NULL)
_pRebootScreen = new CRebootScreen(*_pDisplay, *this);
_pRebootScreen->setMessage(content, delayTime);
_bReqUpdate = true;
_dim(false);
}
void
CScreenManager::clearDisplay()
{
_pDisplay->clearDisplay();
}
void
CScreenManager::showOTAMessage(int percent, eOTAmodes updateType)
{
static int prevPercent = -1;
if(percent != prevPercent) {
DebugPort.printf("%d%%\r\n", percent);
prevPercent = percent;
_pDisplay->clearDisplay();
if(percent < 0)
return;
_pDisplay->setFontInfo(&arial_8ptBoldFontInfo);
_pDisplay->setCursor(64, -1);
_pDisplay->printCentreJustified("Firmware update");
_pDisplay->setFontInfo(NULL);
_pDisplay->drawFastHLine(0, 10, 128, WHITE);
_pDisplay->setCursor(64,22);
switch(updateType) {
case eOTAnormal: _pDisplay->printCentreJustified("OTA upload"); break;
case eOTAbrowser: _pDisplay->printCentreJustified("Browser upload"); break;
case eOTAWWW: _pDisplay->printCentreJustified("Web download"); break;
}
if(percent) {
_pDisplay->drawRect(14, 32, 100, 8, WHITE);
_pDisplay->fillRect(14, 32, percent, 8, WHITE);
char msg[16];
sprintf(msg, "%d%%", percent);
_pDisplay->setCursor(64,42);
_pDisplay->printCentreJustified(msg);
}
_pDisplay->display();
}
_OTAholdoff = millis() + 1000;
}
void
CScreenManager::_dim(bool state)
{
_bDimmed = state;
_pDisplay->dim(state);
}
void
CScreenManager::showSplash()
{
_pDisplay->clearDisplay();
uint8_t splash[1024];
loadSplashScreen(splash);
_pDisplay->drawBitmap(0, 0, splash, 128, 64, WHITE);
_pDisplay->setTextColor(WHITE);
{
CTransientFont AF(*_pDisplay, &segoeUI_Italic_7ptFontInfo); // temporarily use a midi font
_pDisplay->setCursor(90, 56);
_pDisplay->print(getVersionStr());
}
{
CTransientFont AF(*_pDisplay, &arial_8ptBoldFontInfo);
_pDisplay->setCursor(10, 54);
_pDisplay->print(getVersionStr(true)); // prints "BETA" if minor version defined
}
// Show initial display buffer contents on the screen --
_pDisplay->display();
}
void
CScreenManager::showBootMsg(const char* msg)
{
CTransientFont AF(*_pDisplay, &arialItalic_7ptFontInfo);
_pDisplay->fillRect(0, 50, 128, 14, BLACK);
_pDisplay->setCursor(0, 50);
_pDisplay->print(msg);
_pDisplay->display();
}
void
CScreenManager::showBootWait(int show)
{
static int idx = 0;
// idx++;
BITMAP_INFO bitmap = hourGlassIcon0Info;
switch(idx++ & 0x03) {
case 0: bitmap = hourGlassIcon0Info; break;
case 1: bitmap = hourGlassIcon1Info; break;
case 2: bitmap = hourGlassIcon2Info; break;
case 3: bitmap = hourGlassIcon3Info; break;
}
_pDisplay->fillRect(80, 50, bitmap.width, bitmap.height, BLACK);
if(show)
_pDisplay->drawBitmap(80, 50, bitmap.pBitmap, bitmap.width, bitmap.height, WHITE);
_pDisplay->display();
}
void
CScreenManager::selectHomeMenu()
{
uint8_t userHomeMenu = NVstore.getUserSettings().HomeMenu.onTimeout;
if(userHomeMenu) { // allow user to override defualt screen
userHomeMenu--;
DebugPort.print("Screen Manager: Menu timeout, falling back to user preferred screen: ");
switch(userHomeMenu) {
case 0: DebugPort.println("Detailed control menu"); break;
case 1: DebugPort.println("Basic control menu"); break;
case 2: DebugPort.println("Clock menu"); break;
}
_rootMenu = _subMenu = userHomeMenu;
}
else {
_rootMenu = _subMenu = 1;
DebugPort.println("Screen Manager: Menu timeout, falling back to Basic control screen");
}
}