Skip to content

Commit

Permalink
groveRGBLCD: Add support for I2C Grove RGB LCD
Browse files Browse the repository at this point in the history
Add support for the Grove RGB LCD:
https://wiki.seeedstudio.com/Grove-LCD_RGB_Backlight/
along with example file.

This was as simple as replacing a standard 'lcd' element with the
correct header and an 'rgb_lcd' object.

Tested on a PJRC Teensy 4.0 board with a KY-040 click shaft
encoder.

Signed-off-by: Graham Whaley <[email protected]>
  • Loading branch information
grahamwhaley committed Nov 20, 2020
1 parent 6d7dd1c commit e09f2ff
Show file tree
Hide file tree
Showing 4 changed files with 270 additions and 0 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,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)
Expand Down
207 changes: 207 additions & 0 deletions examples/GroveRGB/GroveRGB/GroveRGB.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
#include <Arduino.h>

/********************
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 <Wire.h>
#include <menu.h>
#include <menuIO/groveRGBLCDOut.h>
#include <menuIO/serialOut.h>
#include <menuIO/serialIn.h>
#include <menuIO/clickEncoderIn.h>
#include <menuIO/chainStream.h>

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("<Back")
);

/*extern menu mainMenu;
TOGGLE((mainMenu[1].enabled),togOp,"Op 2:",doNothing,noEvent,noStyle
,VALUE("Enabled",enabledStatus,doNothing,noEvent)
,VALUE("disabled",disabledStatus,doNothing,noEvent)
);*/

// char* constMEM hexDigit MEMMODE="0123456789ABCDEF";
// char* constMEM hexNr[] MEMMODE={"0","x",hexDigit,hexDigit};
// char buf1[]="0x11";

MENU(mainMenu,"Main menu",doNothing,noEvent,wrapStyle
,OP("Op1",action1,anyEvent)
,OP("Op2",action2,enterEvent)
//,SUBMENU(togOp)
,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")
);

//const panel panels[] MEMMODE={{0,0,16,2}};
//navNode* nodes[sizeof(panels)/sizeof(panel)];
//panelsList pList(panels,nodes,1);

#define MAX_DEPTH 2
/*idx_t tops[MAX_DEPTH];
liquidCrystalOut outLCD(lcd,tops,pList);//output device for LCD
menuOut* constMEM outputs[] MEMMODE={&outLCD};//list of output devices
outputsList out(outputs,1);//outputs list with 2 outputs*/

MENU_OUTPUTS(out, MAX_DEPTH
,GROVERGBLCD_OUT(lcd, {0, 0, 16, 2})
,NONE
);
NAVROOT(nav,mainMenu,MAX_DEPTH,in,out);//the navigation root object

result alert(menuOut& o,idleEvent e) {
if (e==idling) {
o.setCursor(0,0);
o.print("alert test");
o.setCursor(0,1);
o.print("[select] to continue...");
}
return proceed;
}

result doAlert(eventMask e, prompt &item) {
nav.idleOn(alert);
return proceed;
}

result idle(menuOut& o,idleEvent e) {
switch(e) {
case idleStart:o.print("suspending menu!");break;
case idling:o.print("suspended...");break;
case idleEnd:o.print("resuming menu.");break;
}
return proceed;
}

void setup() {
pinMode(encBtn,INPUT_PULLUP);
pinMode(LEDPIN,OUTPUT);
Serial.begin(115200);
while(!Serial);
Serial.println("Arduino Menu Library");Serial.flush();

lcd.begin(16,2);
lcd.setRGB(32, 32, 32);
nav.idleTask=idle;//point a function to be used when menu is suspended
mainMenu[1].enabled=disabledStatus;
nav.showTitle=false;
lcd.setCursor(0, 0);
lcd.print("Menu 4.x LCD");
lcd.setCursor(0, 1);
lcd.print("r-site.net");
delay(2000);
}

void loop() {
clickEncoder.service();
nav.poll();
digitalWrite(LEDPIN, ledCtrl);
delay(100);//simulate a delay as if other tasks are running
}
7 changes: 7 additions & 0 deletions src/macros.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
#define ANSISERIAL_OUT(...) ON(ANSISERIAL_OUT,__COUNTER__,__VA_ARGS__)
#define LIQUIDCRYSTAL_OUT(...) ON(LIQUIDCRYSTAL_OUT,__COUNTER__,__VA_ARGS__)
#define LCD_OUT(...) ON(LCD_OUT,__COUNTER__,__VA_ARGS__)
#define GROVERGBLCD_OUT(...) ON(GROVERGBLCD_OUT,__COUNTER__,__VA_ARGS__)
#define ADAGFX_OUT(...) ON(ADAGFX_OUT,__COUNTER__,__VA_ARGS__)
#define TFT_eSPI_OUT(...) ON(TFT_eSPI_OUT,__COUNTER__,__VA_ARGS__)
#define TFT_OUT(...) ON(TFT_OUT,__COUNTER__,__VA_ARGS__)
Expand Down Expand Up @@ -123,6 +124,11 @@ Menu::idx_t id##Tops##n[md];\
PANELS(id##Panels##n,__VA_ARGS__);\
Menu::lcdOut id##n(&device,id##Tops##n,id##Panels##n);

#define VAR_GROVERGBLCD_OUT(id,md,n,device,...)\
Menu::idx_t id##Tops##n[md];\
PANELS(id##Panels##n,__VA_ARGS__);\
Menu::grovergblcdOut id##n(&device,id##Tops##n,id##Panels##n);

#define VAR_ADAGFX_OUT(id,md,n,gfx,color,fontW,fontH,...)\
Menu::idx_t id##Tops##n[md];\
PANELS(id##Panels##n,__VA_ARGS__);\
Expand Down Expand Up @@ -173,6 +179,7 @@ Menu::utftOut id##n(gfx,color,id##Tops##n,id##Panels##n,fontW,fontH);
#define REF_ANSISERIAL_OUT(id,md,n,...) &id##n,
#define REF_LIQUIDCRYSTAL_OUT(id,md,n,...) &id##n,
#define REF_LCD_OUT(id,md,n,...) &id##n,
#define REF_GROVERGBLCD_OUT(id,md,n,...) &id##n,
#define REF_ADAGFX_OUT(id,md,n,...) &id##n,
#define REF_TFT_eSPI_OUT(id,md,n,...) &id##n,
#define REF_U8GLIB_OUT(id,md,n,...) &id##n,
Expand Down
52 changes: 52 additions & 0 deletions src/menuIO/groveRGBLCDOut.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/* -*- C++ -*- */

//for using the Grove 2x16 RGB LCD i2c
// https://wiki.seeedstudio.com/Grove-LCD_RGB_Backlight/
// https://github.com/Seeed-Studio/Grove_LCD_RGB_Backlight
// Uses default I2C device addresses on default I2C bus

#ifndef RSITE_ARDUINO_MENU_GROVERGBLCDOUT
#define RSITE_ARDUINO_MENU_GROVERGBLCDOUT

#ifndef ARDUINO_SAM_DUE
#include "../menuDefs.h"
#include <Wire.h>
#include <rgb_lcd.h>

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 "<<x<<","<<y<<endl);
//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
#endif

0 comments on commit e09f2ff

Please sign in to comment.