diff --git a/.travis.yml b/.travis.yml index 0c2fd5b7..428df884 100644 --- a/.travis.yml +++ b/.travis.yml @@ -53,8 +53,8 @@ install: - wget https://github.com/neu-rah/AnsiStream/archive/master.zip -O /tmp/AnsiStream-master.zip - unzip /tmp/AnsiStream-master.zip -d /tmp/ - - wget https://bitbucket.org/fmalpartida/new-liquidcrystal/downloads/NewliquidCrystal_1.3.4.zip -O /tmp/NewliquidCrystal_1.3.4.zip - - unzip /tmp/NewliquidCrystal_1.3.4.zip -d /tmp/ + # - wget https://bitbucket.org/fmalpartida/new-liquidcrystal/downloads/NewliquidCrystal_1.3.4.zip -O /tmp/NewliquidCrystal_1.3.4.zip + # - unzip /tmp/NewliquidCrystal_1.3.4.zip -d /tmp/ # - wget https://github.com/adafruit/Adafruit-PCD8544-Nokia-5110-LCD-library/archive/master.zip -O /tmp/Adafruit-PCD8544-Nokia-5110-LCD-library-master.zip # - unzip /tmp/Adafruit-PCD8544-Nokia-5110-LCD-library-master.zip -d /tmp/ @@ -69,4 +69,5 @@ script: # - platformio ci --lib="." --lib="/tmp/SD-master" --board=nanoatmega328 --board=teensy31 # - platformio ci --lib="." --lib="/tmp/Streaming-master" --lib="/tmp/Adafruit-GFX-Library-master" --lib="/tmp/PCINT-master" --lib="/tmp/U8glib_Arduino-master" --lib="/tmp/AnsiStream-master" --lib="/tmp/NewliquidCrystal" --lib="/tmp/Adafruit-PCD8544-Nokia-5110-LCD-library-master" --lib="/tmp/Adafruit-ST7735-Library-master" --board=nanoatmega328 --board=teensy31 --board=due # - platformio ci --lib="." --lib="/tmp/Streaming-master" --lib="/tmp/Adafruit-GFX-Library-master" --lib="/tmp/PCINT-master" --lib="/tmp/U8glib_Arduino-master" --lib="/tmp/AnsiStream-master" --lib="/tmp/NewliquidCrystal" --lib="/tmp/Adafruit-ST7735-Library-master" --board=nanoatmega328 --board=teensy31 --board=due - - platformio ci --lib="." --lib="/tmp/Streaming-master" --lib="/tmp/PCINT-master" --lib="/tmp/U8glib_Arduino-master" --lib="/tmp/AnsiStream-master" --lib="/tmp/NewliquidCrystal" --board=nanoatmega328 --board=teensy31 --board=due + # - platformio ci --lib="." --lib="/tmp/Streaming-master" --lib="/tmp/PCINT-master" --lib="/tmp/U8glib_Arduino-master" --lib="/tmp/AnsiStream-master" --lib="/tmp/NewliquidCrystal" --board=nanoatmega328 --board=teensy31 --board=due + - platformio ci --lib="." --lib="/tmp/Streaming-master" --lib="/tmp/PCINT-master" --lib="/tmp/U8glib_Arduino-master" --lib="/tmp/AnsiStream-master" --board=nanoatmega328 --board=teensy31 --board=due diff --git a/README.md b/README.md index 885845b3..64b2742c 100644 --- a/README.md +++ b/README.md @@ -16,11 +16,25 @@ Optionally the system can be operated in semi-automated mode, issuing navigation See the [wiki](https://github.com/neu-rah/ArduinoMenu/wiki) -### COVID-19 note - -**If you need this menu for any device covid-19 related, be my guest, I grant you free usage even if you going to sell the device** - -This menu system has the advantage of being long tested and supporting many interface devices so that makers don't be restricted in hardware. +## What's next — OneMenu / HAPI + +A successor is in active development, built on **HAPI**, a zero-overhead +compile-time composition engine (github.com/InternetOfPins/HAPI). Menu +definitions compile down to code indistinguishable from hand-written C — +see it for yourself: [Compiler Explorer demo](https://godbolt.org/z/6srTzdTY7). + +This isn't a release. The core (**OneMenu**) is solid, and a drop-in +compatibility layer for existing AM4 sketches is now in progress on the +**[AM5 branch](https://github.com/neu-rah/ArduinoMenu/tree/AM5)** of this +repo — real, unmodified AM4 examples (Blink, Button, Confirm so far) already +build against it with just a library-dependency swap, at roughly the same +flash/RAM savings as the Compiler Explorer demo above suggests. Still missing +pieces (masked text-field editing, the idle/suspend API, most device +drivers) before it covers AM4's full example set. If you're interested in +the architecture, want to help with the compatibility layer, or just want to +follow along: github.com/InternetOfPins/HAPI + +No migration is required right now. AM4 keeps working exactly as it does today. ## Simple Example ```c++ @@ -283,6 +297,10 @@ https://github.com/neu-rah/AnsiStream - Unix terminal +**Grove RGB LCD I2C 2x16** + +https://wiki.seeedstudio.com/Grove-LCD_RGB_Backlight/ + **Web browser** - ESP8266 (builtin) diff --git a/examples/ESP32/platformio.ini b/examples/ESP32/platformio.ini index 25c6616a..35cfa66f 100644 --- a/examples/ESP32/platformio.ini +++ b/examples/ESP32/platformio.ini @@ -7,3 +7,4 @@ board = lolin32 framework=arduino build_flags =-Wno-reorder build_unflags=-Werror=reorder + diff --git a/examples/GroveRGB/GroveRGB/GroveRGB.ino b/examples/GroveRGB/GroveRGB/GroveRGB.ino new file mode 100644 index 00000000..33a57a67 --- /dev/null +++ b/examples/GroveRGB/GroveRGB/GroveRGB.ino @@ -0,0 +1,207 @@ +#include + +/******************** +Sept. 2014 Rui Azevedo - ruihfazevedo(@rrob@)gmail.com +Nov. 2020 Graham Whaley - adapted to Grove RGB LCD + +menu output to Grove RGB LCD I2C +output: GROVERGBLCD : https://wiki.seeedstudio.com/Grove-LCD_RGB_Backlight/ +input: click encoder and Serial +board: PJRC Teensy 4.0 : https://www.pjrc.com/store/teensy40.html +***/ + +#include +#include +#include +#include +#include +#include +#include + +using namespace Menu; + +// LCD, standard Grove I2C devices ///////////// +rgb_lcd lcd; + +// Encoder ///////////////////////////////////// +#define encA 3 +#define encB 2 +//this encoder has a button here +#define encBtn 4 + +ClickEncoder clickEncoder(encA,encB,encBtn,2); +ClickEncoderStream encStream(clickEncoder,1); + +//input from the clickencoder + serial +serialIn serial(Serial); +menuIn* inputsList[]={&encStream,&serial}; +chainStream<2> in(inputsList);//3 is the number of inputs + +#define LEDPIN LED_BUILTIN //Teensy 4.0 LED pin + +result doAlert(eventMask e, prompt &item); + +result showEvent(eventMask e,navNode& nav,prompt& item) { + Serial.print("event: "); + Serial.println(e); + return proceed; +} + +int test=55; + +result action1(eventMask e,navNode& nav, prompt &item) { + Serial.print("action1 event: "); + Serial.print(e); + Serial.println(", proceed menu"); + Serial.flush(); + return proceed; +} + +result action2(eventMask e,navNode& nav, prompt &item) { + Serial.print("action2 event: "); + Serial.print(e); + Serial.print(", quiting menu."); + Serial.flush(); + return quit; +} + +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",showEvent,anyEvent,noStyle + ,OP("Sub1",showEvent,anyEvent) + ,OP("Sub2",showEvent,anyEvent) + ,OP("Sub3",showEvent,anyEvent) + ,altOP(altPrompt,"",showEvent,anyEvent) + ,EXIT("//to send javascript thru web socket (live update) #include +#ifdef ESP8266 #include extern "C" { #include "user_interface.h" } +#elif defined(ESP32) +#include + +// Use external analogWrite library for ESP32 (optional) +// for compatibility with older versions of espressif/arduino-esp32 +// https://github.com/Dlloydev/ESP32-ESP32S2-AnalogWrite +#include +#endif using namespace Menu; @@ -105,7 +114,11 @@ const char* serverName="192.168.1.79"; #define HTTP_PORT 80 #define WS_PORT 81 #define USE_SERIAL Serial +#ifdef ESP8266 ESP8266WebServer server(80); +#elif defined(ESP32) +WebServer server(80); +#endif WebSocketsServer webSocket(81); #define MAX_DEPTH 2 @@ -245,60 +258,60 @@ navRoot wsNav(mainMenu, ws_cursors, MAX_DEPTH, none, ws_out); //config myOptions('*','-',defaultNavCodes,false); -void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) { - switch(type) { - case WStype_DISCONNECTED: - //USE_SERIAL.printf("[%u] Disconnected!\n", num); - break; - case WStype_CONNECTED: { - IPAddress ip = webSocket.remoteIP(num); - //USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload); - webSocket.sendTXT(num, "console.log('ArduinoMenu Connected')"); - } - break; - case WStype_TEXT: { - //USE_SERIAL.printf("[%u] get Text: %s\n", num, payload); - // nav.async((const char*)payload);//this is slow!!!!!!!! - __trace(Serial.printf("[%u] get Text: %s\n", num, payload)); - char*s=(char*)payload; - _trace(Serial<<"serve websocket menu"< +#include "..\menuDefs.h" +#include + +namespace Menu +{ + inline menuOut::styles operator | (menuOut::styles lhs, menuOut::styles rhs) + { + using T = std::underlying_type_t ; + return static_cast(static_cast(lhs) | static_cast(rhs)); + } + + inline menuOut::styles& operator |= (menuOut::styles& lhs, menuOut::styles rhs) + { + lhs = lhs | rhs; + return lhs; + } + + + template + class SmartMatrixOut : public gfxOut + { + + using SMBackground=SMLayerBackground; + + RGB currentColour{0,0,0}; + uint16_t cursorX=0, cursorY=0,line=0; + + public: + SMBackground &gfx; + const colorDef (&colors)[nColors]; + SmartMatrixOut(SMBackground &gfx, const colorDef (&c)[nColors], idx_t *t, panelsList &p, fontChoices font=fontChoices::font5x7, menuOut::styles s=minimalRedraw) + : gfxOut(fontLookup(font)->Width, fontLookup(font)->Height, t, p, s), gfx(gfx), colors(c) + { + gfx.setFont(font); + setCursor(0,0); + } + + virtual ~SmartMatrixOut() {} + + size_t write(uint8_t ch) override + { + if(ch=='\n') + { + setCursor(0,line+1); + } + else if(ch!='\r') + { + gfx.drawChar(cursorX,cursorY,currentColour,ch); + cursorX+=resX; + } + + return 1; + } + + inline RGB getColor(colorDefs color = bgColor, bool selected = false, status stat = enabledStatus, bool edit = false) const + { + return memWord(&(stat == enabledStatus ? colors[color].enabled[selected + edit] : colors[color].disabled[selected])); + } + + void setColor(colorDefs c, bool selected = false, status s = enabledStatus, bool e = false) override + { + currentColour=getColor(c,selected,s,e); + } + + void clearLine(idx_t ln, idx_t panelNr = 0, colorDefs color = bgColor, bool selected = false, status stat = enabledStatus, bool edit = false) override + { + const panel p = panels[panelNr]; + gfx.fillRectangle(p.x * resX, (p.y + ln) * resY + fontMarginY, (p.x+p.w) * resX, (p.y + ln + 1)*resY, getColor(color, selected, stat, edit)); + //setCursor(0,ln); + } + void clear() override + { + panels.reset(); + gfx.fillScreen(getColor(bgColor, false, enabledStatus, false)); + setCursor(0, 0); + setColor(fgColor); + } + + void box(idx_t panelNr, idx_t x, idx_t y, idx_t w = 1, idx_t h = 1, colorDefs c = bgColor, bool selected = false, status stat = enabledStatus, bool edit = false) override + { + const panel p = panels[panelNr]; + gfx.drawRectangle((p.x + x) * resX, (p.y + y) * resY + fontMarginY, (p.x + x + w) * resX, (p.y + y + h) * resY, getColor(c, selected, stat, edit)); + } + + void rect(idx_t panelNr, idx_t x, idx_t y, idx_t w = 1, idx_t h = 1, colorDefs c = bgColor, bool selected = false, status stat = enabledStatus, bool edit = false) override + { + const panel p = panels[panelNr]; + gfx.fillRectangle((p.x + x) * resX, (p.y + y) * resY + fontMarginY, (p.x + x + w) * resX, (p.y + y + h) * resY, getColor(c, selected, stat, edit)); + } + + void clear(idx_t panelNr) override + { + const panel p = panels[panelNr]; + gfx.fillRectangle(p.x * resX, p.y * resY, (p.x+p.w) * resX, (p.y+p.h) * resY, getColor(bgColor, false, enabledStatus, false)); + panels.nodes[panelNr] = NULL; + } + + void setCursor(idx_t x, idx_t y, idx_t panelNr = 0) override + { + line=y; + const panel p = panels[panelNr]; + cursorX=(p.x + x) * resX; + cursorY= (p.y + line) * resY + fontMarginY; + } + + void drawCursor(idx_t ln, bool selected, status stat, bool edit = false, idx_t panelNr = 0) override + { + const panel p = panels[panelNr]; + // gfxOut::drawCursor(ln,selected,stat); + gfx.drawRectangle(p.x * resX, (p.y + ln) * resY + fontMarginY, (p.x+p.w) * resX, (p.y + ln +1) * resY, getColor(cursorColor, selected, enabledStatus, false)); + } + }; + +}; //namespace adaGfxOut +#endif diff --git a/src/menuIO/esp8266Out.h b/src/menuIO/esp8266Out.h index 3dbdb810..de6ca37c 100644 --- a/src/menuIO/esp8266Out.h +++ b/src/menuIO/esp8266Out.h @@ -2,14 +2,19 @@ #ifndef RSITE_ARDUINO_MENU_ESP8266OUT #define RSITE_ARDUINO_MENU_ESP8266OUT - #ifdef ESP8266 + #if defined(ESP8266) || defined(ESP32) #include "../menuDefs.h" + #ifdef ESP8266 #include + #include + #elif defined(ESP32) + #include + #include + #endif // based on WebServer: // https://github.com/esp8266/Arduino/tree/master/libraries/ESP8266WebServer // https://github.com/Links2004/arduinoWebSockets #include - #include #include #include "xmlFmt.h" @@ -61,6 +66,7 @@ class esp8266_WebServerOut:public esp8266BufferedOut { public: +#ifdef ESP8266 ESP8266WebServer &server; //using esp8266Out::esp8266Out; esp8266_WebServerOut( @@ -69,18 +75,38 @@ idx_t* t, panelsList& p ):esp8266BufferedOut(t,p),server(srv) {} +#elif defined(ESP32) + WebServer &server; + //using esp8266Out::esp8266Out; + esp8266_WebServerOut( + WebServer &srv, + /*const colorDef (&c)[nColors],*/ + idx_t* t, + panelsList& p + ):esp8266BufferedOut(t,p),server(srv) {} +#endif size_t write(uint8_t ch) override {response<<(char)ch;return 1;} // template inline esp8266_WebServerOut& operator<<(T t) {response< + #include + + namespace Menu { + + class grovergblcdOut:public cursorOut { + public: + rgb_lcd* device; + inline grovergblcdOut(rgb_lcd* o,idx_t *t,panelsList &p,menuOut::styles s=menuOut::minimalRedraw) + :cursorOut(t,p,s),device(o) {} + size_t write(uint8_t ch) override {return device->write(ch);} + void clear() override { + device->clear(); + panels.reset(); + } + void setCursor(idx_t x,idx_t y,idx_t panelNr=0) override { + const panel p=panels[panelNr]; + device->setCursor(p.x+x,p.y+y); + } + idx_t startCursor(navRoot& root,idx_t x,idx_t y,bool charEdit,idx_t panelNr=0) override {return 0;} + idx_t endCursor(navRoot& root,idx_t x,idx_t y,bool charEdit,idx_t panelNr=0) override {return 0;} + idx_t editCursor(navRoot& root,idx_t x,idx_t y,bool editing,bool charEdit,idx_t panelNr=0) override { + trace(MENU_DEBUG_OUT<<"lcdOut::editCursor "<noBlink(); + device->noCursor(); + if (editing) { + device->setCursor(x, y); + if (charEdit) device->cursor(); + else device->blink(); + } + return 0; + } + + }; + + }//namespace Menu + + #endif +#endif diff --git a/src/menuIO/liquidCrystalOutI2C.h b/src/menuIO/liquidCrystalOutI2C.h new file mode 100644 index 00000000..70ad396c --- /dev/null +++ b/src/menuIO/liquidCrystalOutI2C.h @@ -0,0 +1,43 @@ +/* -*- C++ -*- */ + +//for using I2C LiquidCrystal Library provided by +// https://gitlab.com/tandembyte/LCD_I2C + +#ifndef RSITE_ARDUINO_MENU_LIQUIDCRYSTALOUT + #define RSITE_ARDUINO_MENU_LIQUIDCRYSTALOUT + #include "../menuDefs.h" + #include + + namespace Menu { + + class liquidCrystalOut:public cursorOut { + public: + LiquidCrystal_I2C& device; + inline liquidCrystalOut(LiquidCrystal_I2C& o,idx_t *t,panelsList &p,menuOut::styles s=minimalRedraw) + :cursorOut(t,p,s),device(o) {} + size_t write(uint8_t ch) override {return device.write(ch);} + void clear() override { + device.clear(); + panels.reset(); + } + void setCursor(idx_t x,idx_t y,idx_t panelNr=0) override { + const panel p=panels[panelNr]; + device.setCursor(p.x+x,p.y+y); + } + idx_t startCursor(navRoot& root,idx_t x,idx_t y,bool charEdit,idx_t panelNr=0) override {return 0;} + idx_t endCursor(navRoot& root,idx_t x,idx_t y,bool charEdit,idx_t panelNr=0) override {return 0;} + idx_t editCursor(navRoot& root,idx_t x,idx_t y,bool editing,bool charEdit,idx_t panelNr=0) override { + //text editor cursor + device.noBlink(); + device.noCursor(); + if (editing) { + device.setCursor(x, y); + if (charEdit) device.cursor(); + else device.blink(); + } + return 0; + } + }; + + }//namespace Menu +#endif diff --git a/src/menuIO/rotaryEventIn.h b/src/menuIO/rotaryEventIn.h index a2f09653..29aa4195 100644 --- a/src/menuIO/rotaryEventIn.h +++ b/src/menuIO/rotaryEventIn.h @@ -18,13 +18,13 @@ Generic Rotary/Button input // not sure where to put this.. -template inline T operator~ (T a) { return (T)~(int)a; } -template inline T operator| (T a, T b) { return (T)((int)a | (int)b); } -template inline T operator& (T a, T b) { return (T)((int)a & (int)b); } -template inline T operator^ (T a, T b) { return (T)((int)a ^ (int)b); } -template inline T& operator|= (T& a, T b) { return (T&)((int&)a |= (int)b); } -template inline T& operator&= (T& a, T b) { return (T&)((int&)a &= (int)b); } -template inline T& operator^= (T& a, T b) { return (T&)((int&)a ^= (int)b); } +template constexpr inline T operator~ (T a) { return (T)~(int)a; } +template constexpr inline T operator| (T a, T b) { return (T)((int)a | (int)b); } +template constexpr inline T operator& (T a, T b) { return (T)((int)a & (int)b); } +template constexpr inline T operator^ (T a, T b) { return (T)((int)a ^ (int)b); } +template constexpr inline T& operator|= (T& a, T b) { return (T&)((int&)a |= (int)b); } +template constexpr inline T& operator&= (T& a, T b) { return (T&)((int&)a &= (int)b); } +template constexpr inline T& operator^= (T& a, T b) { return (T&)((int&)a ^= (int)b); } #ifndef __rotaryEventIn_h__ @@ -37,60 +37,65 @@ template inline T& operator^= (T& a, T b) { return (T&)((int&)a ^= (int public: - enum EventType { - BUTTON_CLICKED = 1 << 0, - BUTTON_DOUBLE_CLICKED = 1 << 1, - BUTTON_LONG_PRESSED = 1 << 2, - - ROTARY_CW = 1 << 3, - ROTARY_CCW = 1 << 4, - }; - - EventType config; - EventType events; // we could do a fifo if we miss events - - RotaryEventIn(EventType c) - :config(c) { - // config for future use. we could raise if - // we are missing essential stuff - // and we need to absorb an arg anyway... - } - - void registerEvent(EventType e) { - events |= e; // add it to the current events - } - - int peek(void) override { return events; } - int available(void) override {return peek() != 0;} - - int read() override { - // enterCmd - if (events & EventType::BUTTON_CLICKED) { - events &= ~EventType::BUTTON_CLICKED; // remove from events - return options->navCodes[enterCmd].ch; - } - // escCmd - else if (events & (EventType::BUTTON_DOUBLE_CLICKED|EventType::BUTTON_LONG_PRESSED)) { - events &= ~(EventType::BUTTON_DOUBLE_CLICKED|EventType::BUTTON_LONG_PRESSED); // remove - return options->navCodes[escCmd].ch; - } - // downCmd - else if (events & EventType::ROTARY_CW) { - events &= ~EventType::ROTARY_CW; // remove from events - return options->navCodes[upCmd].ch; // down sends up on menu? bug? - } - // upCmd - else if (events & EventType::ROTARY_CCW) { - events &= ~EventType::ROTARY_CCW; // remove from events - return options->navCodes[downCmd].ch; // up sends down on menu? bug? - } - - else - return -1; - } - - void flush() override {} - size_t write(uint8_t v) override {return 0;} + enum EventType { + BUTTON_CLICKED = 1 << 0, + BUTTON_DOUBLE_CLICKED = 1 << 1, + BUTTON_LONG_PRESSED = 1 << 2, + + ROTARY_CW = 1 << 5, // fix for issue #414 + ROTARY_CCW = 1 << 4, + }; +/* +EventType values should be different for codes defined as: +const navCodesDef Menu::defaultNavCodes (menuBase.cpp file) +and special cases in textField::parseInput (items.cpp file) function +*/ + + EventType config; + EventType events; // we could do a fifo if we miss events + + RotaryEventIn(EventType c) + :config(c) { + // config for future use. we could raise if + // we are missing essential stuff + // and we need to absorb an arg anyway... + } + + void registerEvent(EventType e) { + events |= e; // add it to the current events + } + + int peek(void) override { return events; } + int available(void) override {return peek() != 0;} + + int read() override { + // enterCmd + if (events & EventType::BUTTON_CLICKED) { + events &= ~EventType::BUTTON_CLICKED; // remove from events + return options->navCodes[enterCmd].ch; + } + // escCmd + else if (events & (EventType::BUTTON_DOUBLE_CLICKED|EventType::BUTTON_LONG_PRESSED)) { + events &= ~(EventType::BUTTON_DOUBLE_CLICKED|EventType::BUTTON_LONG_PRESSED); // remove + return options->navCodes[escCmd].ch; + } + // downCmd + else if (events & EventType::ROTARY_CW) { + events &= ~EventType::ROTARY_CW; // remove from events + return options->navCodes[upCmd].ch; // down sends up on menu? bug? + } + // upCmd + else if (events & EventType::ROTARY_CCW) { + events &= ~EventType::ROTARY_CCW; // remove from events + return options->navCodes[downCmd].ch; // up sends down on menu? bug? + } + + else + return -1; + } + + void flush() override {} + size_t write(uint8_t v) override {return 0;} }; // class diff --git a/src/menuIo.cpp b/src/menuIo.cpp index 54c1985e..e3ef3ecf 100644 --- a/src/menuIo.cpp +++ b/src/menuIo.cpp @@ -24,7 +24,7 @@ int noInput::peek() {return -1;} // //////////////////////////////////////////////////////////////////////////////// #if defined(MENU_DEBUG) || defined(MENU_ASYNC) - #ifdef ESP8266 + #if defined(ESP8266) || defined(ESP32) template menuOut& menuOut::operator<<(T o) {(*(Stream*)this)<<(o);return *this;} #endif diff --git a/src/menuIo.h b/src/menuIo.h index 6d9a9840..cd2ae280 100644 --- a/src/menuIo.h +++ b/src/menuIo.h @@ -83,7 +83,7 @@ idx_t printText(const char* at,idx_t len); #if defined(MENU_DEBUG) || defined(MENU_ASYNC) virtual menuOut& operator<<(prompt const &p); - #ifdef ESP8266 + #if defined(ESP8266) || defined(ESP32) template menuOut& operator<<(T o); #endif #endif diff --git a/src/nav.cpp b/src/nav.cpp index 743f12f5..b8d840e9 100644 --- a/src/nav.cpp +++ b/src/nav.cpp @@ -20,11 +20,26 @@ idx_t panelsList::maxY() const { // //////////////////////////////////////////////////////////////////////////////// +static uint8_t escaped = 0; + navCmd navNode::navKeys(char ch) { trace(MENU_DEBUG_OUT<<"navNode::navKeys"<useAccel&&strchr(numericChars,ch)) { return navCmd(idxCmd,ch-'1'); } + if ((escaped == 0) && (ch == '\e')) {escaped = 1; return noCmd;} + else if ((escaped == 1) && (ch == '[')) {escaped = 2; return noCmd;} + else if (escaped == 1) {escaped = 0; return noCmd;} + else if (escaped == 2) { + escaped = 0; + switch (ch) { + case 'A': return(downCmd); break; + case 'B': return(upCmd); break; + case 'C': return(enterCmd); break; + case 'D': return(escCmd); break; + default: return(noCmd); break; + } + } for(uint8_t i=0;inavCodes)/sizeof(navCode);i++) if (options->navCodes[i].ch==ch) return options->navCodes[i].cmd; return noCmd;