#include <SPI.h>
#include <Wire.h>
#include <RotaryEncoder.h>
// #include <pcint.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <menu.h>
#include <menuIO/adafruitGfxOut.h>
// #include <TimerOne.h>
// #include <ClickEncoder.h>
// #include <menuIO/clickEncoderIn.h>
#include <menuIO/keyIn.h>
#include <menuIO/chainStream.h>
#include <menuIO/serialOut.h>
#include <menuIO/serialIn.h>
using namespace Menu;
// TFT LCD Display
#define PIN_TFT_CS 53
#define PIN_TFT_DC 47
#define PIN_TFT_RST 45
Adafruit_ILI9341 gfx = Adafruit_ILI9341(PIN_TFT_CS, PIN_TFT_DC, PIN_TFT_RST);
// Rotary Encoder
#define PIN_ROT_ENC_SW 23
#define PIN_ROT_ENC_DT 3
#define PIN_ROT_ENC_CLK 2
RotaryEncoder* encoder;
#define LEDPIN LED_BUILTIN
/*
result doAlert(eventMask e, prompt &item);
int test = 55;
int ledCtrl = LOW;
result myLedOn() {
ledCtrl = HIGH;
return proceed;
}
result myLedOff() {
ledCtrl = LOW;
return proceed;
}
TOGGLE(ledCtrl, setLed, "Led: ", doNothing, noEvent, noStyle //,doExit,enterEvent,noStyle
, VALUE("On", HIGH, doNothing, noEvent)
, VALUE("Off", LOW, doNothing, noEvent)
);
int selTest = 0;
SELECT(selTest, selMenu, "Select", doNothing, noEvent, noStyle
, VALUE("Zero", 0, doNothing, noEvent)
, VALUE("One", 1, doNothing, noEvent)
, VALUE("Two", 2, doNothing, noEvent)
);
int chooseTest = -1;
CHOOSE(chooseTest, chooseMenu, "Choose", doNothing, noEvent, noStyle
, VALUE("First", 1, doNothing, noEvent)
, VALUE("Second", 2, doNothing, noEvent)
, VALUE("Third", 3, doNothing, noEvent)
, VALUE("Last", -1, doNothing, noEvent)
);
//customizing a prompt look!
//by extending the prompt class
class altPrompt: public prompt {
public:
altPrompt(constMEM promptShadow& p): prompt(p) {}
Used printTo(navRoot &root, bool sel, menuOut& out, idx_t idx, idx_t len, idx_t) override {
return out.printRaw(F("special prompt!"), len);;
}
};
MENU(subMenu, "Sub-Menu", doNothing, noEvent, noStyle
, altOP(altPrompt, "", doNothing, noEvent)
, OP("Op", doNothing, noEvent)
, EXIT("<Back")
);
const char* constMEM hexDigit MEMMODE = "0123456789ABCDEF";
const char* constMEM hexNr[] MEMMODE = {"0", "x", hexDigit, hexDigit};
char buf1[] = "0x11";
MENU(mainMenu, "Main menu", doNothing, noEvent, wrapStyle
, OP("Op1", doNothing, noEvent)
, OP("Op2", doNothing, noEvent)
, OP("Op3", doNothing, noEvent)
, OP("Op4", doNothing, noEvent)
// ,FIELD(test,"Test","%",0,100,10,1,doNothing,noEvent,wrapStyle)
, SUBMENU(subMenu)
, SUBMENU(setLed)
, OP("LED On", myLedOn, enterEvent)
, OP("LED Off", myLedOff, enterEvent)
, SUBMENU(selMenu)
, SUBMENU(chooseMenu)
//,OP("Alert test",doAlert,enterEvent)
, EDIT("Hex", buf1, hexNr, doNothing, noEvent, noStyle)
, EXIT("<Back")
);
#define ILI9341_GRAY RGB565(128,128,128)
const colorDef<uint16_t> colors[6] MEMMODE = {
{{(uint16_t)ILI9341_BLACK, (uint16_t)ILI9341_BLACK}, {(uint16_t)ILI9341_BLACK, (uint16_t)ILI9341_BLUE, (uint16_t)ILI9341_BLUE}}, //bgColor
{{(uint16_t)ILI9341_GRAY, (uint16_t)ILI9341_GRAY}, {(uint16_t)ILI9341_WHITE, (uint16_t)ILI9341_WHITE, (uint16_t)ILI9341_WHITE}},//fgColor
{{(uint16_t)ILI9341_WHITE, (uint16_t)ILI9341_BLACK}, {(uint16_t)ILI9341_YELLOW, (uint16_t)ILI9341_YELLOW, (uint16_t)ILI9341_RED}}, //valColor
{{(uint16_t)ILI9341_WHITE, (uint16_t)ILI9341_BLACK}, {(uint16_t)ILI9341_WHITE, (uint16_t)ILI9341_YELLOW, (uint16_t)ILI9341_YELLOW}}, //unitColor
{{(uint16_t)ILI9341_WHITE, (uint16_t)ILI9341_GRAY}, {(uint16_t)ILI9341_BLACK, (uint16_t)ILI9341_BLUE, (uint16_t)ILI9341_WHITE}}, //cursorColor
{{(uint16_t)ILI9341_WHITE, (uint16_t)ILI9341_YELLOW}, {(uint16_t)ILI9341_BLUE, (uint16_t)ILI9341_RED, (uint16_t)ILI9341_RED}}, //titleColor
};
*/
result handleEventFanSpeedSet(eventMask e, prompt& item);
result handleEventWifiNetworkSelected(eventMask e, prompt& item);
result handleEventMQTTSet(eventMask e, prompt& item);
result handleEventSaveSettings(eventMask e, prompt& item);
int wifiNetworkSelect = 0;
SELECT(wifiNetworkSelect, wifiNetworkMenu, " Network", handleEventWifiNetworkSelected, exitEvent, noStyle,
VALUE("NONE", 0, doNothing, noEvent),
VALUE("PROD", 1, doNothing, noEvent),
VALUE("LAB", 2, doNothing, noEvent),
VALUE("MOBILE", 3, doNothing, noEvent));
int connectionStatus = 0;
SELECT(connectionStatus, connectionStatusMenu, " Comm", doNothing, noEvent, noStyle,
VALUE("OFFLINE", 0, doNothing, noEvent),
VALUE("ONLINE", 1, doNothing, noEvent),
VALUE("BROKERED", 2, doNothing, noEvent));
int roasterName = 99;
SELECT(roasterName, roasterNameMenu, " Roaster", doNothing, noEvent, wrapStyle,
VALUE("1L", 0, doNothing, noEvent),
VALUE("1R", 1, doNothing, noEvent),
VALUE("2L", 2, doNothing, noEvent),
VALUE("2R", 3, doNothing, noEvent),
VALUE("3L", 4, doNothing, noEvent),
VALUE("3R", 5, doNothing, noEvent),
VALUE("4L", 6, doNothing, noEvent),
VALUE("4R", 7, doNothing, noEvent),
VALUE("DEV", 99, doNothing, noEvent));
SELECT(roasterName, roasterNameROMenu, " Roaster", doNothing, noEvent, wrapStyle,
VALUE("1L", 0, doNothing, noEvent),
VALUE("1R", 1, doNothing, noEvent),
VALUE("2L", 2, doNothing, noEvent),
VALUE("2R", 3, doNothing, noEvent),
VALUE("3L", 4, doNothing, noEvent),
VALUE("3R", 5, doNothing, noEvent),
VALUE("4L", 6, doNothing, noEvent),
VALUE("4R", 7, doNothing, noEvent),
VALUE("DEV", 99, doNothing, noEvent));
int ktc1_offset = 0;
int ktc2_offset = 0;
int ktc3_offset = 0;
int ktc4_offset = 0;
int ktc5_offset = 0;
int ktc1_temp = 0;
int ktc2_temp = 0;
int ktc3_temp = 0;
int ktc4_temp = 0;
int ktc5_temp = 0;
int fan_speed = 0;
int sd_free_space = 0;
const char* constMEM digit MEMMODE = "0123456789";
const char* constMEM mqttBrokerAddrNr[] MEMMODE = {"1", "9", "2", ".", "1", "6", "8", ".", digit, digit, ".", digit, digit, digit};
char mqttBrokerAddr[] = "192.168.01.100";
MENU(settingsMenu, " Settings", doNothing, noEvent, wrapStyle,
OP("Device", doNothing, noEvent),
SUBMENU(roasterNameMenu),
FIELD(ktc1_offset, " P1 Offset", " F", 0, 100, 1, 0, doNothing, noEvent, noStyle),
FIELD(ktc2_offset, " P2 Offset", " F", 0, 100, 1, 0, doNothing, noEvent, noStyle),
FIELD(ktc3_offset, " P3 Offset", " F", 0, 100, 1, 0, doNothing, noEvent, noStyle),
FIELD(ktc4_offset, " P4 Offset", " F", 0, 100, 1, 0, doNothing, noEvent, noStyle),
FIELD(ktc5_offset, " P5 Offset", " F", 0, 100, 1, 0, doNothing, noEvent, noStyle),
OP("Connectivity", doNothing, noEvent),
SUBMENU(wifiNetworkMenu),
EDIT(" MQTT", mqttBrokerAddr, mqttBrokerAddrNr, handleEventMQTTSet, exitEvent, noStyle),
OP("Storage", doNothing, noEvent),
FIELD(sd_free_space, " SD", " Gb Free", 0, 32, 0, 0, doNothing, noEvent, noStyle),
OP("", doNothing, noEvent),
OP("<Save>", handleEventSaveSettings, enterEvent),
EXIT("<Back>")
);
// ,
// , OP("Op", doNothing, noEvent)
MENU(mainMenu,
" Dashboard",
doNothing,
noEvent,
wrapStyle,
OP("", doNothing, noEvent),
SUBMENU(roasterNameROMenu),
OP("", doNothing, noEvent),
OP(" Fan Control", doNothing, noEvent),
FIELD(fan_speed, " Speed", " %", 0, 100, 5, 0, handleEventFanSpeedSet, exitEvent, noStyle),
OP("", doNothing, noEvent),
OP(" KTC Reading", doNothing, noEvent),
FIELD(ktc1_temp, " Probe 1:", " F", 0, 1200, 0, 0, doNothing, noEvent, noStyle),
FIELD(ktc2_temp, " Probe 2:", " F", 0, 1200, 0, 0, doNothing, noEvent, noStyle),
FIELD(ktc3_temp, " Probe 3:", " F", 0, 1200, 0, 0, doNothing, noEvent, noStyle),
FIELD(ktc4_temp, " Probe 4:", " F", 0, 1200, 0, 0, doNothing, noEvent, noStyle),
FIELD(ktc5_temp, " Probe 5:", " F", 0, 1200, 0, 0, doNothing, noEvent, noStyle),
OP("", doNothing, noEvent),
OP(" Device: ", doNothing, noEvent),
SUBMENU(connectionStatusMenu),
SUBMENU(settingsMenu));
// EDIT(" Roaster:", deviceName, deviceNameNr, doNothing, noEvent, noStyle),
//monochromatic color table
#define Black RGB565(0, 0, 0)
#define Red RGB565(255, 0, 0)
#define Green RGB565(0, 255, 0)
#define Blue RGB565(0, 0, 255)
#define Gray RGB565(128, 128, 128)
#define LighterRed RGB565(255, 150, 150)
#define LighterGreen RGB565(150, 255, 150)
#define LighterBlue RGB565(150, 150, 255)
#define DarkerRed RGB565(150, 0, 0)
#define DarkerGreen RGB565(0, 150, 0)
#define DarkerBlue RGB565(0, 0, 150)
#define Cyan RGB565(0, 255, 255)
#define Magenta RGB565(255, 0, 255)
#define Yellow RGB565(255, 255, 0)
#define White RGB565(255, 255, 255)
// Theme
#define COLOR_SM_DARK_BROWN 0x5185 // #4C3128
#define COLOR_SM_LIGHT_BROWN 0xBCAF // #B0967C
#define COLOR_SM_LIGHT_RED 0xD0C4 // #CF161F
#define COLOR_SM_DARK_RED 0x9185 // #4C3128
// Default
#define COLOR_BACKGROUND ILI9341_BLACK
#define COLOR_TEXT COLOR_SM_DARK_BROWN
// Highlight
#define COLOR_BACKGROUND_HIGHLIGHT COLOR_SM_LIGHT_BROWN
#define COLOR_TEXT_HIGHTLIGHT ILI9341_WHITE
// NavBar
#define COLOR_BACKGROUND_NAVBAR COLOR_SM_DARK_RED
#define COLOR_TEXT_NAVBAR ILI9341_WHITE
// define menu colors --------------------------------------------------------
// {{disabled normal,disabled selected},{enabled normal,enabled selected, enabled editing}}
const colorDef<uint16_t> colors[6] MEMMODE = {
{ { (uint16_t)Black, COLOR_SM_DARK_BROWN }, { (uint16_t)Black, COLOR_SM_DARK_BROWN, COLOR_BACKGROUND_HIGHLIGHT } }, //bgColor
{ { (uint16_t)Gray, (uint16_t)Gray }, { (uint16_t)White, (uint16_t)White, (uint16_t)White } }, //fgColor
{ { (uint16_t)Yellow, (uint16_t)Yellow }, { (uint16_t)Yellow, (uint16_t)Yellow, (uint16_t)Red } }, //valColor
{ { (uint16_t)Gray, (uint16_t)Gray }, { (uint16_t)White, (uint16_t)Yellow, (uint16_t)Yellow } }, //unitColor
{ { (uint16_t)White, (uint16_t)White }, { (uint16_t)Black, COLOR_SM_DARK_BROWN, (uint16_t)White } }, //cursorColor, i.e. the box outline(?)
{ { (uint16_t)White, (uint16_t)Yellow }, { COLOR_BACKGROUND_NAVBAR, COLOR_TEXT_NAVBAR, (uint16_t)Red } }, //titleColor
};
serialIn serial(Serial);
// ClickEncoder clickEncoder(PIN_ROT_ENC_CLK, PIN_ROT_ENC_DT, PIN_ROT_ENC_SW);
// ClickEncoderStream encStream(clickEncoder, 1);
// MENU_INPUTS(in, &encStream, &serial);
MENU_INPUTS(in, &serial);
// void timerIsr() {
// clickEncoder.service();
// }
#define MAX_DEPTH 4
#define textScale 2
MENU_OUTPUTS(out, MAX_DEPTH
, ADAGFX_OUT(gfx, colors, 6 * textScale, 9 * textScale, {0, 0, 21, 18})
, SERIAL_OUT(Serial)
);
NAVROOT(nav, mainMenu, MAX_DEPTH, in, out);
//when menu is suspended
result idle(menuOut& o, idleEvent e) {
if (e == idling) {
o.println(F("suspended..."));
o.println(F("press [select]"));
o.println(F("to continue"));
}
return proceed;
}
result handleEventFanSpeedSet(eventMask e, prompt& item) {
Serial.println("Fan speed changed");
return proceed;
}
result handleEventMQTTSet(eventMask e, prompt& item) {
Serial.println("MQTT server changed");
return proceed;
}
result handleEventSaveSettings(eventMask e, prompt& item) {
Serial.println("Save settings");
return proceed;
}
result handleEventWifiNetworkSelected(eventMask e, prompt& item) {
static int lastWifiNetworkSelect = 0;
if (lastWifiNetworkSelect != wifiNetworkSelect) {
Serial.print("Changed wifi network: " + String(wifiNetworkSelect));
// connectToWifiNetwork();
lastWifiNetworkSelect = wifiNetworkSelect;
}
return proceed;
}
// IRQ routine to handle rotary encoder ticks.
void handleRotEncChange() {
encoder->tick();
}
void inputDeviceReadCallback() {
// Rotary Encoder read.
static bool lastClick = false;
bool click = digitalRead(PIN_ROT_ENC_SW);
if (click != lastClick) {
// Serial.println("Rot Enc changed: " + String(click));
lastClick = click;
if (click) {
// On Release.
// Serial.println("Rot Enc click: " + String(click));
nav.doNav(enterCmd);
}
} else {
encoder->tick(); // Check the state.
int8_t rot = (int8_t)encoder->getDirection();
if (rot) {
if (rot == -1) {
// Serial.println("Rot Enc Left");
nav.doNav(upCmd);
} else if (rot == 1) {
// Serial.println("Right");
nav.doNav(downCmd);
}
}
}
}
void setup() {
//options=&myOptions;//can customize options
pinMode(LEDPIN, OUTPUT);
Serial.begin(115200);
while (!Serial);
// Encoder setup
pinMode(PIN_ROT_ENC_SW, INPUT_PULLUP);
Serial.println(__FILE__);
Serial.flush();
nav.idleTask = idle; //point a function to be used when menu is suspended
// Disable read-only values.
mainMenu[1].disable(); // Roaster
mainMenu[7].disable(); // Probe 1
mainMenu[8].disable(); // Probe 2
mainMenu[9].disable(); // Probe 3
mainMenu[10].disable(); // Probe 4
mainMenu[11].disable(); // Probe 5
mainMenu[14].disable(); // Network
// Disable
settingsMenu[11].disable(); // SD
// settingsMenu[0].disable(); // Connectivity
//outGfx.usePreview=true;//reserve one panel for preview?
//nav.showTitle=false;//show menu title?
pinMode(PIN_ROT_ENC_SW, INPUT_PULLUP);
// Timer1.initialize(1000);
// Timer1.attachInterrupt(timerIsr);
encoder = new RotaryEncoder(PIN_ROT_ENC_CLK, PIN_ROT_ENC_DT, RotaryEncoder::LatchMode::TWO03);
// Register interrupt routine.
// Note: without attaching the hardware, the interrupt will trigger continuously.
attachInterrupt(digitalPinToInterrupt(PIN_ROT_ENC_CLK), handleRotEncChange, CHANGE);
attachInterrupt(digitalPinToInterrupt(PIN_ROT_ENC_DT), handleRotEncChange, CHANGE);
SPI.begin();
gfx.begin();
gfx.setRotation(0);
gfx.setTextSize(2);//test scalling
gfx.setTextWrap(false);
gfx.fillScreen(ILI9341_BLACK);
gfx.setTextColor(ILI9341_RED, ILI9341_BLACK);
gfx.println("Splash Screen");
// TODO: Print SM logo.
delay(1000);
// Navigate to settings.
nav.doNav(downCmd);
nav.doNav(enterCmd);
}
void loop() {
static long lastRun = 0;
if(millis() > lastRun + 100) {
inputDeviceReadCallback();
nav.poll();
lastRun = millis();
}
}
/*
// Basic hardware hookup test.
void setup() {
Serial.begin(9600);
while (!Serial);
pinMode(PIN_ROT_ENC_SW, INPUT_PULLUP);
// Initialize TFT display
Serial.println("Initialize TFT.");
tft.begin();
tft.setRotation(0);
tft.setCursor(0, 0);
tft.println("Hello world!");
}
void loop() {
static long lastRun = 0;
if(millis() > lastRun + 1000) {
Serial.println("RotEnc: " + String(digitalRead(PIN_ROT_ENC_SW)));
}
}
*/