/**
Sketch: MENU NAVIGATION WITH ENCODER + SERIAL
Adafruit_SSD1306 out
**/
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <menu.h> // https://github.com/neu-rah/ArduinoMenu
#include <menuIO/adafruitGfxOut.h>
#include <menuIO/serialIO.h>
#include <menuIO/chainStream.h>
//include plugins
#include <plugin/cancelField.h>
#include <plugin/barField.h>
#include <AiEsp32RotaryEncoder.h> // https://github.com/igorantolic/ai-esp32-rotary-encoder
#define MAX_DEPTH 2
//#define LARGE_FONT Verdana12
using namespace Menu;
int test=55;
// SSD1306
//#define SSD1306_NO_SPLASH // Disable Adafruit splash screen
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define SCREEN_I2C_ADDRESS 0x3C
#define textScale 1
#define fontX 5
#define fontY 8
// Switch
#define PTT_PIN 36
// Encoder
#define ROTARY_ENCODER_A_PIN 34
#define ROTARY_ENCODER_B_PIN 39
#define ROTARY_ENCODER_BUTTON_PIN 35
#define ROTARY_ENCODER_VCC_PIN -1
#define ROTARY_ENCODER_STEPS 4
#define LED_BUILTIN 2
#define LED_PIN 27
Adafruit_SSD1306 oled(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, false);
AiEsp32RotaryEncoder rotaryEncoder = AiEsp32RotaryEncoder(ROTARY_ENCODER_A_PIN, ROTARY_ENCODER_B_PIN, ROTARY_ENCODER_BUTTON_PIN, ROTARY_ENCODER_VCC_PIN, ROTARY_ENCODER_STEPS);
int ledCtrl = HIGH; //Default LED State of LED at D11 is LOW
result doAlert(eventMask e, prompt &item);
result showEvent(eventMask e, navNode& nav, prompt& item) {
return proceed;
}
result action1(eventMask e) {
Serial.print(e);
Serial.println(" action1 executed, proceed menu");
return proceed;
}
result action2(eventMask e, navNode& nav, prompt &item) {
Serial.print(e);
Serial.println(" action2 executed, quiting menu");
return quit;
}
result ledOn() {
ledCtrl = HIGH;
analogWrite(LED_PIN, 255);
return proceed;
}
result ledOff() {
ledCtrl = LOW;
analogWrite(LED_PIN, 0);
return proceed;
}
result internalLedOn() {
digitalWrite(LED_BUILTIN, HIGH);
return proceed;
}
result internalLedOff() {
digitalWrite(LED_BUILTIN, LOW);
return proceed;
}
int brightnessValue = 50; //Default LED brightness value
result adjustBrightness() {
if (ledCtrl == HIGH) {
int pwm = int(2.55 * float(brightnessValue));
analogWrite(LED_PIN, pwm);
}
return proceed;
}
TOGGLE(ledCtrl, setLed, "Led: ", doNothing, noEvent, noStyle //,doExit,enterEvent,noStyle
, VALUE("On", HIGH, ledOn, enterEvent)//ledOn function is called
, VALUE("Off", LOW, ledOff, enterEvent)//ledOff function is called
);
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", showEvent, anyEvent, noStyle
, OP("Sub1", showEvent, anyEvent)
, OP("Sub2", showEvent, anyEvent)
, OP("Sub3", showEvent, anyEvent)
, altOP(altPrompt, "", showEvent, anyEvent)
, EXIT("<Back")
);
uint16_t year=2017;
uint16_t month=10;
uint16_t day=7;
//define a pad style menu (single line menu)
//here with a set of fields to enter a date in YYYY/MM/DD format
//altMENU(menu,birthDate,"Birth",doNothing,noEvent,noStyle,(systemStyles)(_asPad|Menu::_menuData|Menu::_canNav|_parentDraw)
PADMENU(birthDate,"Birth",doNothing,noEvent,noStyle
,FIELD(year,"","/",1900,3000,20,1,doNothing,noEvent,noStyle)
,FIELD(month,"","/",1,12,1,0,doNothing,noEvent,wrapStyle)
,FIELD(day,"","",1,31,1,0,doNothing,noEvent,wrapStyle)
);
char* constMEM hexDigit MEMMODE="0123456789ABCDEF";
char* constMEM hexNr[] MEMMODE={hexDigit,hexDigit,hexDigit,hexDigit,hexDigit,hexDigit,hexDigit,hexDigit,hexDigit,hexDigit,hexDigit,hexDigit,hexDigit,hexDigit,hexDigit,hexDigit};
char buf1[]="0102030405060708090A0B0C0D0E0F";
const char* const alphaNum[] MEMMODE = {" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.,+-_"};
char name[]="CoPan"; // field will initialize its size by this string length
MENU(mainMenu, "Main menu", doNothing, noEvent, wrapStyle
, BARFIELD(test,"Bar field","%",0,100,10,1,doNothing,noEvent,wrapStyle) //numeric field with a bar
, altFIELD(cancelField,test,"C. Simple","%",0,100,1,0,doNothing,enterEvent,wrapStyle) //cancelable field (1 edit level)
, OP("Op1", action1, anyEvent)
, OP("Op2", action2, enterEvent)
, FIELD(brightnessValue, "Brightness", "%", 0, 100, 5, 5, adjustBrightness, enterEvent, wrapStyle)
, SUBMENU(subMenu)
, SUBMENU(setLed)
, OP("LED On", internalLedOn, enterEvent) // will turn on built-in LED
, OP("LED Off", internalLedOff, enterEvent) // will turn off built-in LED
, SUBMENU(selMenu)
, SUBMENU(chooseMenu)
, OP("Alert test", doAlert, enterEvent)
, EDIT("Key",buf1,hexNr,doNothing,noEvent,wrapStyle)
, EDIT("Name",name,alphaNum,doNothing,noEvent,wrapStyle)
, SUBMENU(birthDate)
, EXIT("<Back")
);
//describing a menu output device without macros
//define at least one panel for menu output
const panel panels[] MEMMODE = {{0, 0, SCREEN_WIDTH, SCREEN_HEIGHT}};
navNode* nodes[sizeof(panels) / sizeof(panel)]; // navNodes to store navigation status
panelsList pList(panels, nodes, 1); // a list of panels and nodes
idx_t tops[MAX_DEPTH] = {0, 0}; // store cursor positions for each level
idx_t serialTops[MAX_DEPTH] = {0};
serialOut outSerial(Serial, serialTops);
const colorDef<uint16_t> colors[6] MEMMODE = {
// {{disabled normal,disabled selected},{enabled normal,enabled selected, enabled editing}}
{{SSD1306_BLACK, SSD1306_WHITE}, {SSD1306_BLACK, SSD1306_WHITE, SSD1306_WHITE}}, //bgColor
{{SSD1306_WHITE, SSD1306_BLACK}, {SSD1306_WHITE, SSD1306_BLACK, SSD1306_BLACK}}, //fgColor
{{SSD1306_WHITE, SSD1306_BLACK}, {SSD1306_WHITE, SSD1306_BLACK, SSD1306_BLACK}}, //valColor
{{SSD1306_WHITE, SSD1306_BLACK}, {SSD1306_WHITE, SSD1306_BLACK, SSD1306_BLACK}}, //unitColor
{{SSD1306_WHITE, SSD1306_BLACK}, {SSD1306_BLACK, SSD1306_BLACK, SSD1306_BLACK}}, //cursorColor
{{SSD1306_WHITE, SSD1306_BLACK}, {SSD1306_BLACK, SSD1306_WHITE, SSD1306_WHITE}}, //titleColor
};
MENU_OUTPUTS(out,MAX_DEPTH
//,SERIAL_OUT(Serial)
,ADAGFX_OUT(oled,colors,fontX,fontY,{0,0,SCREEN_WIDTH/fontX,SCREEN_HEIGHT/fontY})
,NONE // must have 2 items at least
);
serialIn serial(Serial);
// input from the serial
menuIn* inputsList[]={&serial};
chainStream<1> in(inputsList); //2 is the number of inputs
NAVROOT(nav, mainMenu, MAX_DEPTH, in, out);
result alert(menuOut& o, idleEvent e) {
if (e == idling) {
o.setCursor(0, 0);
o.print("alert test");
o.setCursor(0, 1);
o.print("press [select]");
o.setCursor(0, 2);
o.print("to continue...");
}
return proceed;
}
result doAlert(eventMask e, prompt &item) {
nav.idleOn(alert);
return proceed;
}
// when menu is suspended
result idle(menuOut &o, idleEvent e) {
o.clear();
switch (e) {
case idleStart: o.println("suspending menu!"); break;
case idling: o.println("suspended..."); break;
case idleEnd: o.println("resuming menu."); break;
}
return proceed;
}
void rotary_onButtonClick() {
static unsigned long lastTimePressed = 0;
//ignore multiple press in that time milliseconds
if (millis() - lastTimePressed < 200) {
return;
}
lastTimePressed = millis();
nav.doNav(navCmd(enterCmd));
}
int rotary_last_value = 0;
void rotary_loop() {
//dont print anything unless value changed
if (rotaryEncoder.encoderChanged()) {
int v = rotaryEncoder.readEncoder();
//Serial.printf("Last: %d\n",rotary_last_value);
//Serial.printf("Value: %d\n",v);
if ((rotary_last_value == 100) and (v == 0)) {
nav.doNav(navCmd(upCmd));
} else if ((rotary_last_value == 0) and (v == 100)) {
nav.doNav(navCmd(downCmd));
} else if (rotary_last_value < v) {
nav.doNav(navCmd(upCmd));
} else if (rotary_last_value > v) {
nav.doNav(navCmd(downCmd));
}
rotary_last_value = v;
}
if (rotaryEncoder.isEncoderButtonClicked()) {
rotary_onButtonClick();
}
}
void IRAM_ATTR readEncoderISR() {
rotaryEncoder.readEncoder_ISR();
}
void setup() {
Serial.begin(115200);
while (!Serial && (millis() < 1000)) delay(1000);
Serial.println("ArduinoMenu 4.x Adafruit_SSD1306_AiEncoder"); Serial.flush();
Serial.println("Use keys + - * /");
Serial.println("to control the menu navigation");
pinMode(PTT_PIN, INPUT_PULLUP);
pinMode(LED_PIN, OUTPUT);
pinMode(LED_BUILTIN, OUTPUT);
rotaryEncoder.begin();
rotaryEncoder.setup(readEncoderISR);
rotaryEncoder.setBoundaries(0, 100, true);
rotaryEncoder.disableAcceleration();
oled.begin(SSD1306_SWITCHCAPVCC, SCREEN_I2C_ADDRESS, true); //check config
oled.setTextSize(textScale); // Normal 1:1 pixel scale
oled.setTextColor(SSD1306_WHITE); // Draw white text
oled.cp437(true); // Use full 256 char 'Code Page 437' font
oled.clearDisplay();
//oled.println("Panie co pan");
oled.display();
nav.idleTask = idle; // point a function to be used when menu is suspended
//setting some plugins otions
barFieldOptions::fill="\xFF";
//barFieldOptions::empty="░";//if not stick with the defaults
cancelFieldOptions::quitOnEsc=true;
cancelFieldOptions::accelSendEsc=true;//normal is enter
}
void loop() {
// DRAW DISPLAYS
oled.display();
nav.poll();
rotary_loop();
}