/* * This file is part of the "bluetoothheater" distribution * (https://gitlab.com/mrjones.id.au/bluetoothheater) * * Copyright (C) 2018 Ray Jones * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "ScreenManager.h" #include "DetailedScreen.h" #include "BasicScreen.h" #include "PrimingScreen.h" #include "WiFiScreen.h" #include "FuelMixtureScreen.h" #include "SetClockScreen.h" #include "SetTimerScreen.h" #include "ClockScreen.h" #include "RebootScreen.h" #include "HeaterSettingsScreen.h" #include "SettingsScreen.h" #include "ThermostatModeScreen.h" #include "FontDumpScreen.h" #include "TimerChartScreen.h" #include "InheritSettingsScreen.h" #include "GPIOScreen.h" #include "VersionInfoScreen.h" #include "OtherOptionsScreen.h" #include #include "../cfg/pins.h" #include "../cfg/BTCConfig.h" #include "../protocol/helpers.h" #include "keypad.h" //////////////////////////////////////////////////////////////////////////////////////////////// // 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 unsigned char DieselSplash [] PROGMEM = { // 'Splash2, 128x64px 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x02, 0x3e, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x80, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf0, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x80, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0f, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x21, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x01, 0xf0, 0x88, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x20, 0x80, 0x00, 0x40, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x84, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x20, 0x40, 0x00, 0x20, 0x00, 0x00, 0x07, 0xc0, 0x00, 0x84, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x40, 0x40, 0x00, 0x10, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x40, 0x20, 0x00, 0x08, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x40, 0x10, 0x00, 0x04, 0x03, 0xe0, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x80, 0x10, 0x00, 0x02, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x80, 0x08, 0x00, 0x0f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x80, 0x08, 0x01, 0xf1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x18, 0x00, 0x01, 0x00, 0x04, 0x3e, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x07, 0xc0, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x1c, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x40, 0x64, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3c, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x40, 0x84, 0x00, 0x00, 0x01, 0x80, 0x00, 0x01, 0xc0, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x23, 0x04, 0x00, 0x00, 0x00, 0x60, 0x00, 0x1e, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x1c, 0x02, 0x00, 0x00, 0x00, 0x1c, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0xf8, 0x02, 0x00, 0x00, 0x00, 0x03, 0xfe, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x88, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x88, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x88, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x88, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x88, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x88, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x88, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x88, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x88, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xf8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x02, 0x00, 0x00, 0x00, 0x01, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x07, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0xf8, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x1f, 0x20, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x03, 0xe8, 0x20, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x7c, 0x08, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x08, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x21, 0x10, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x08, 0x00, 0x00, 0x00, 0x21, 0x10, 0x01, 0x00, 0x00, 0x84, 0x00, 0x40, 0x01, 0x40, 0x00, 0x11, 0x08, 0x00, 0x00, 0x00, 0x21, 0x10, 0x01, 0x00, 0x00, 0x80, 0x00, 0x40, 0x01, 0x40, 0x00, 0x11, 0x3a, 0x4e, 0x73, 0x18, 0xe1, 0x13, 0x1d, 0xcc, 0xb0, 0x80, 0xc7, 0x75, 0x99, 0x4c, 0xb0, 0x1f, 0x4a, 0x52, 0x94, 0xa5, 0x21, 0xf4, 0xa5, 0x12, 0xc0, 0x81, 0x29, 0x46, 0x25, 0x52, 0xc0, 0x11, 0x4a, 0x52, 0x94, 0x3d, 0x21, 0x17, 0xa5, 0x1e, 0x80, 0x81, 0x29, 0x44, 0x25, 0x5e, 0x80, 0x11, 0x49, 0x92, 0x94, 0xa1, 0x21, 0x14, 0x25, 0x10, 0x80, 0x85, 0x29, 0x44, 0x25, 0x50, 0x80, 0x11, 0x39, 0x8e, 0x93, 0x18, 0xe1, 0x13, 0x1c, 0xcc, 0x80, 0x78, 0xc9, 0x34, 0x19, 0x4c, 0x80 }; CScreenManager::CScreenManager() { _pDisplay = NULL; _menu = -1; _subMenu = 0; _rootMenu = -1; _bReqUpdate = false; _DimTime = millis() + 60000; _pRebootScreen = NULL; } 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(bool bNoClock) { // 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 _pDisplay->clearDisplay(); _pDisplay->drawBitmap(0, 0, DieselSplash, 128, 64, WHITE); // Show initial display buffer contents on the screen -- _pDisplay->display(); DebugPort.println("Creating Screens"); std::vector menuloop; // create root menu loop menuloop.push_back(new CDetailedScreen(*_pDisplay, *this)); // detail control menuloop.push_back(new CBasicScreen(*_pDisplay, *this)); // basic control if(!bNoClock) menuloop.push_back(new CClockScreen(*_pDisplay, *this)); // clock menuloop.push_back(new CPrimingScreen(*_pDisplay, *this)); // mode / priming menuloop.push_back(new CWiFiScreen(*_pDisplay, *this)); // comms info menuloop.push_back(new CGPIOInfoScreen(*_pDisplay, *this)); // GPIO info menuloop.push_back(new CSettingsScreen(*_pDisplay, *this)); // Tuning 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 heater tuning screens loop - password protected menuloop.clear(); menuloop.push_back(new CFuelMixtureScreen(*_pDisplay, *this)); // tuning menuloop.push_back(new CHeaterSettingsScreen(*_pDisplay, *this)); // tuning _Screens.push_back(menuloop); // create User Settings screens loop menuloop.clear(); menuloop.push_back(new CThermostatModeScreen(*_pDisplay, *this)); // experimental settings screen menuloop.push_back(new CGPIOScreen(*_pDisplay, *this)); // GPIO settings screen menuloop.push_back(new CVersionInfoScreen(*_pDisplay, *this)); // GPIO settings screen menuloop.push_back(new COtherOptionsScreen(*_pDisplay, *this)); // Other options screen _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 CFontDumpScreen(*_pDisplay, *this)); // font dump branch screen _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 _enterScreen(); } bool CScreenManager::checkUpdate() { if(_DimTime) { long tDelta = millis() - _DimTime; if(tDelta > 0) { // time to dim the display // if(NVstore.getDimTime()) _pDisplay->dim(true); _DimTime = 0; _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 = _subMenu = 1; if(NVstore.getHomeMenu()) { // allow user to override defualt screen DebugPort.print("Falling back to user home screen #"); DebugPort.println(NVstore.getHomeMenu()-1); _rootMenu = _subMenu = NVstore.getHomeMenu()-1; } } _enterScreen(); } } if(_bReqUpdate) { 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(_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(); } void CScreenManager::_changeSubMenu(int dir) { _leaveScreen(); _subMenu += dir; int bounds = _Screens[_menu].size() - 1; ROLLUPPERLIMIT(_subMenu, bounds, 0); ROLLLOWERLIMIT(_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(_DimTime == 0) { if(event & keyReleased) { _pDisplay->dim(false); _DimTime = (millis() + NVstore.getDimTime()) | 1; } return; // initial press when dimmed is thrown away } _DimTime = (millis() + NVstore.getDimTime()) | 1; // call key handler for active screen if(_menu >= 0) _Screens[_menu][_subMenu]->keyHandler(event); } 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! } 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; _pDisplay->dim(false); }