Problem with Grove RGB LCD


pull-ups work like a charm, but only for the rgb backlight.
The LCD is still empty.


Maybe someone will have a look at this. I dont know whats wrong, maybe a timing issue.
I2C addresses work on Arduino.
On Spark Core only the rgb backlight works as mentioned before.
Here is the code:

#include <inttypes.h>
#include <stdio.h>
#include <string.h>

// Device I2C Arress
#define LCD_ADDRESS     (0x7c>>1)
#define RGB_ADDRESS     (0xc4>>1)

// color define 
#define WHITE           0
#define RED             1
#define GREEN           2
#define BLUE            3

#define REG_RED         0x04        // pwm2
#define REG_GREEN       0x03        // pwm1
#define REG_BLUE        0x02        // pwm0

#define REG_MODE1       0x00
#define REG_MODE2       0x01
#define REG_OUTPUT      0x08

// commands
#define LCD_RETURNHOME 0x02
#define LCD_CURSORSHIFT 0x10
#define LCD_FUNCTIONSET 0x20

// flags for display entry mode
#define LCD_ENTRYRIGHT 0x00
#define LCD_ENTRYLEFT 0x02

// flags for display on/off control
#define LCD_DISPLAYON 0x04
#define LCD_DISPLAYOFF 0x00
#define LCD_CURSORON 0x02
#define LCD_CURSOROFF 0x00
#define LCD_BLINKON 0x01
#define LCD_BLINKOFF 0x00

// flags for display/cursor shift
#define LCD_DISPLAYMOVE 0x08
#define LCD_CURSORMOVE 0x00
#define LCD_MOVERIGHT 0x04
#define LCD_MOVELEFT 0x00

// flags for function set
#define LCD_8BITMODE 0x10
#define LCD_4BITMODE 0x00
#define LCD_2LINE 0x08
#define LCD_1LINE 0x00
#define LCD_5x10DOTS 0x04
#define LCD_5x8DOTS 0x00

class rgb_lcd : public Print 


  void begin(uint8_t cols, uint8_t rows, uint8_t charsize = LCD_5x8DOTS);

  void clear();
  void home();

  void noDisplay();
  void display();
  void noBlink();
  void blink();
  void noCursor();
  void cursor();
  void scrollDisplayLeft();
  void scrollDisplayRight();
  void leftToRight();
  void rightToLeft();
  void autoscroll();
  void noAutoscroll();

  void createChar(uint8_t, uint8_t[]);
  void setCursor(uint8_t, uint8_t); 
  virtual size_t write(uint8_t);
  void command(uint8_t);
  // color control
  void setRGB(unsigned char r, unsigned char g, unsigned char b);               // set rgb
  void setPWM(unsigned char color, unsigned char pwm){setReg(color, pwm);}      // set pwm
  void setColor(unsigned char color);
  void setColorAll(){setRGB(0, 0, 0);}
  void setColorWhite(){setRGB(255, 255, 255);}
  //using spark_wiring_print::write;
  void send(uint8_t, uint8_t);
  void setReg(unsigned char addr, unsigned char dta);

  uint8_t _displayfunction;
  uint8_t _displaycontrol;
  uint8_t _displaymode;

  uint8_t _initialized;

  uint8_t _numlines,_currline;

// CPP

void i2c_send_byte(unsigned char dta)
    Wire.beginTransmission(LCD_ADDRESS);        // transmit to device #4
    Wire.write(dta);                            // sends five bytes
    Wire.endTransmission();                     // stop transmitting

void i2c_send_byteS(unsigned char *dta, unsigned char len)
    Wire.beginTransmission(LCD_ADDRESS);        // transmit to device #4
    for(int i=0; i<len; i++)
    Wire.endTransmission();                     // stop transmitting


void rgb_lcd::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) 

    if (lines > 1) {
        _displayfunction |= LCD_2LINE;
    _numlines = lines;
    _currline = 0;

    // for some 1 line displays you can select a 10 pixel high font
    if ((dotsize != 0) && (lines == 1)) {
        _displayfunction |= LCD_5x10DOTS;

    // according to datasheet, we need at least 40ms after power rises above 2.7V
    // before sending commands. Arduino can turn on way befer 4.5V so we'll wait 50

    // this is according to the hitachi HD44780 datasheet
    // page 45 figure 23

    // Send function set command sequence
    command(LCD_FUNCTIONSET | _displayfunction);
    delayMicroseconds(4500);  // wait more than 4.1ms

    // second try
    command(LCD_FUNCTIONSET | _displayfunction);

    // third go
    command(LCD_FUNCTIONSET | _displayfunction);

    // finally, set # lines, font size, etc.
    command(LCD_FUNCTIONSET | _displayfunction);

    // turn the display on with no cursor or blinking default

    // clear it off

    // Initialize to default text direction (for romance languages)
    // set the entry mode
    command(LCD_ENTRYMODESET | _displaymode);
    // backlight init
    setReg(0, 0);
    setReg(1, 0);
    setReg(0x08, 0xAA);     // all led control by pwm


/********** high level commands, for the user! */
void rgb_lcd::clear()
    command(LCD_CLEARDISPLAY);        // clear display, set cursor position to zero
    delayMicroseconds(2000);          // this command takes a long time!

void rgb_lcd::home()
    command(LCD_RETURNHOME);        // set cursor position to zero
    delayMicroseconds(2000);        // this command takes a long time!

void rgb_lcd::setCursor(uint8_t col, uint8_t row)

    col = (row == 0 ? col|0x80 : col|0xc0);
    unsigned char dta[2] = {0x80, col};

    i2c_send_byteS(dta, 2);


// Turn the display on/off (quickly)
void rgb_lcd::noDisplay()
    _displaycontrol &= ~LCD_DISPLAYON;
    command(LCD_DISPLAYCONTROL | _displaycontrol);

void rgb_lcd::display() {
    _displaycontrol |= LCD_DISPLAYON;
    command(LCD_DISPLAYCONTROL | _displaycontrol);

// Turns the underline cursor on/off
void rgb_lcd::noCursor()
    _displaycontrol &= ~LCD_CURSORON;
    command(LCD_DISPLAYCONTROL | _displaycontrol);

void rgb_lcd::cursor() {
    _displaycontrol |= LCD_CURSORON;
    command(LCD_DISPLAYCONTROL | _displaycontrol);

// Turn on and off the blinking cursor
void rgb_lcd::noBlink()
    _displaycontrol &= ~LCD_BLINKON;
    command(LCD_DISPLAYCONTROL | _displaycontrol);
void rgb_lcd::blink()
    _displaycontrol |= LCD_BLINKON;
    command(LCD_DISPLAYCONTROL | _displaycontrol);

// These commands scroll the display without changing the RAM
void rgb_lcd::scrollDisplayLeft(void)
void rgb_lcd::scrollDisplayRight(void)

// This is for text that flows Left to Right
void rgb_lcd::leftToRight(void)
    _displaymode |= LCD_ENTRYLEFT;
    command(LCD_ENTRYMODESET | _displaymode);

// This is for text that flows Right to Left
void rgb_lcd::rightToLeft(void)
    _displaymode &= ~LCD_ENTRYLEFT;
    command(LCD_ENTRYMODESET | _displaymode);

// This will 'right justify' text from the cursor
void rgb_lcd::autoscroll(void)
    _displaymode |= LCD_ENTRYSHIFTINCREMENT;
    command(LCD_ENTRYMODESET | _displaymode);

// This will 'left justify' text from the cursor
void rgb_lcd::noAutoscroll(void)
    _displaymode &= ~LCD_ENTRYSHIFTINCREMENT;
    command(LCD_ENTRYMODESET | _displaymode);

// Allows us to fill the first 8 CGRAM locations
// with custom characters
void rgb_lcd::createChar(uint8_t location, uint8_t charmap[])

    location &= 0x7; // we only have 8 locations 0-7
    command(LCD_SETCGRAMADDR | (location << 3));
    unsigned char dta[9];
    dta[0] = 0x40;
    for(int i=0; i<8; i++)
        dta[i+1] = charmap[i];
    i2c_send_byteS(dta, 9);

/*********** mid level commands, for sending data/cmds */

// send command
inline void rgb_lcd::command(uint8_t value)
    unsigned char dta[2] = {0x80, value};
    i2c_send_byteS(dta, 2);

// send data
inline size_t rgb_lcd::write(uint8_t value)

    unsigned char dta[2] = {0x40, value};
    i2c_send_byteS(dta, 2);
    return 1; // assume sucess

void rgb_lcd::setReg(unsigned char addr, unsigned char dta)
    Wire.beginTransmission(RGB_ADDRESS); // transmit to device #4
    Wire.endTransmission();    // stop transmitting

void rgb_lcd::setRGB(unsigned char r, unsigned char g, unsigned char b)
    setReg(REG_RED, r);
    setReg(REG_GREEN, g);
    setReg(REG_BLUE, b);

const unsigned char color_define[4][3] = 
    {255, 255, 255},            // white
    {255, 0, 0},                // red
    {0, 255, 0},                // green
    {0, 0, 255},                // blue

void rgb_lcd::setColor(unsigned char color)
    if(color > 3)return ;
    setRGB(color_define[color][0], color_define[color][1], color_define[color][2]);

// APP

rgb_lcd lcd;

const int colorR = 128;
const int colorG = 0;
const int colorB = 128;

void setup() 
    // set up the LCD's number of columns and rows:
    lcd.begin(16, 2);
    lcd.setRGB(colorR, colorG, colorB);
    lcd.setCursor(0, 0);
    // Print a message to the LCD.
    lcd.print("hello, world!");


void loop() 
    // set the cursor to column 0, line 1
    // (note: line 1 is the second row, since counting begins with 0):
    lcd.setCursor(0, 1);
    // print the number of seconds since reset:



The only difference I see between RGB and LCD, is that the RGB is just single writes, whereas the LCD is all multiple writes. I know the Spark Core is much more sequential in sending I2C data than the Arduino, so maybe the Grove LCD doesn’t have a good way to buffer the data? Let’s try rewriting this a bit… I’m sure I’ve broken something in the LCD by doing this, but maybe not… can’t hurt to take a stab in the dark.

void i2c_send_byteS(unsigned char *dta, unsigned char len)
    for(int i=0; i<len; i++)


Good idea, but unfortunately this doesn’t work.

Is it a big difference between Arduino I2C and Spark Core I2C?


Rats… well not that big of a deal… maybe 10 - 30 microseconds between multiple sent bytes where the bus returns to an idle state, vs. the core where the bytes are all sent out with no delay in between. It’s not bad typically for a hardware I2C device. I just have no idea what is on the Grove LCD for I2C decoding… can you read me off a part number? Their spec is not very good at describing the details.


Hmm, PCA9633 for I2C RGB backlight.
But for the LCD: I can’t find something useful.
JHD1313M1 / P131021 is the only number mentioned on the back.
The datasheet mentioned this JHD1214 Y/YG 1.0

Controller: AIP31068L or equal
SCL cycle time max 400KHz
SCL pulse width: min 1.3 us


Ok, I did a bunch of looking and I think all our previous efforts are a waste of time… except for the Pullup resistors.

I just decided to start from scratch and port the code how I normally would… (which seems to be working well)

Try this (builds locally and on web IDE):


Thanks for your help.
But still not working.
Only RGB Backlight works fine.
Pullups are 4.7K against 5V rail. Is this fine or should I try other resistors, too.


That should be good for pull ups… rats. This one is tough… need to see the I2C bus on Arduino vs. Spark Core while it’s talking to this LCD.


Hey, I wanted to bump this post, since it seems like a lot of work has been done on I2C since the last bits of the conversation. I have one of these grove LCD’s and it was very simple to get working on the Arduino Uno, and was hoping I could similar functionality on the Spark Core. If nobody’s gotten this working, does anybody have a recommendation for an LCD with a RGB backlight that I can work with using an existing library? I’m not wedded to the I2C route…but as a micro controller noob, this grove piece with the arduino is literally my only experience with running an LCD…this seems like a good way of doing it without using a lot of pins.


@stevepvc, I can port the library for you later today. To use I2C on the Spark you will need two pull-up resistors (4.7K ohm is good). You will need to connect the LCD as follows, assuming you are powering the Spark via the USB connector:

Grove      Spark
GND        GND
Vcc        Vin
SCA        D0 (SCA)
SCL        D1 (SCL)

You will need to connect a resistor from each I2C line (D0, D1) to Vin (5V). Note that the Spark pins normally work at 3.3V as opposed to 5V for the Arduino but they will tolerate the higher voltage. I will let you know when the library is ready. :slight_smile:


Thanks, @peekay123! You’ve been extremely helpful to people in this area, and I appreciate your help specifically here.

Here is my interpretation of your wiring instructions. Can you confirm whether or not I’m following your instructions correctly? (Note: the fritzing diagram shows a Grove OLED rather than a LCD because that’s the diagram module available…the input pins on the LCD are the same, though.


@stevepvc, the wiring looks fine except for the two power wires which should connect to the breadboard, not directly to the Core ( :stuck_out_tongue: )

I published the adapted library in the IDE (GROVE_LCD_RGB_BACKLIGHT) so you can use any of the examples included. Let me know how it goes :smile:


Ha! Yeah, I guess it would be a little easier to connect the wires to the breadboard. I updated the pic for any other lost souls that find their way here.

Thanks for the work on this, @peekay123! I’ll chime in with how it works later.


I have troubles to make any I2C driven display (okok… I just tried two) to work with my spark. I failed convincing a OLED SSD1306 and gave up since Im not realy shure if its maybe a SH1106 (its not written anywhere on the pcb) and I thought for a first step the Groove LCD should work fine. I just tell you this because it maybe helps to localize the problem.

I have the same Issue as FloX with the Groove LCD! I used your library @peekay123.
The background lightning is working as it should, but no text is written to the LCD. I even tried with a sencond LCD to rule out hardware problems. I2C is basicaly working, cause I can receive data from a MPU9150 and a BMP180 and a pull-up is attached to both I2C lines.

Someone has an idea?


I found the issue :open_mouth: ! The backlight works fine if you just connect the display to the 3.3V line. Using the 5V instead solved my problem - display is working now.


So I’ve run into this same issue. I came at it from using a ST Micro Nucleo board which has a very similar processor to the Particle. My first thought was “ah hah, it is a 5v vs 3.3v problem” but even after setting up my chip with open drain outputs, and 10K pullups, the LCD doesn’t acknowledge that it is there. The RGB backlight does. Here is a picture of the LCD ignoring me : scope capture of i2c bus


@ChuckM, even though the Nucleo has a similar processor to the Particle, it doesn’t have the same firmware. As such, we can’t really support it since this is a Particle forum. However, if I understand the image you posted, the display is not sending an ACK or anything else. Make sure the display address matches your expectations. I would HIGHLY suggest that you get the display working with an arduino with a known good library and go from there.


@peekay123 no worries. And yes I had run it using the Grove library on an Arduino first and was looking at differences in the outputs. The “big” difference is that the AVR implementation of i2c in the Wiring library versus the bake it your self driver I had written. The pull ups comments here were a huge help in terms of getting me closer. The actual problem ended up being two of course, one that my SDA/SCL lines were swapped (no ack no nothing) but also the way the state machine implements i2c I was sending stop at the end of my write but it turned out that with a 100Khz bus I could end up sending stop before the last byte went out. That cut off the transaction. Now I busy wait on the BTF & TXE flags in the i2c unit before I send the stop and its working as expected.

Next comes the interesting part of removing my various patches to figure out the problem and get to the core driver that will run it.

BTW I expect to use this on a Particle board when the cellular Particle ships later this year :smile:


@ChuckM, super glad I helped a little! You’ll be happy to know that when you move over to the Electron, it will be a LOT easier and faster to get things done than on your existing platform! :stuck_out_tongue: