Problem with Grove RGB LCD

Hi, I have a problem with the Grove RGB LCD. On an Arduino all works fine.
But when I paste the same code to the Spark Core Web IDE all compiles fine, but it didn’t run on the core.
Maybe I do something wrong with the i2c address, because only activating the rgb backlight din’t work. I used the default adresses from the library an the grove examples. See links below.
Grove RGB LCD

Grove RGB LCD Library

Hello world example for Grove RGB LCD

Hope someone can help me porting this library or tell me the right i2c address.

On Arduino Mega all fine, but not on the tiny spark core :frowning:

Kind Regards

1 Like

You need to bit shift the I2C address to add the write bit. It’s a bug that I’ve fixed but isn’t live yet.

What address are you using?

Actually Tim, your I2C fix was deployed today!

Hi,

the address for the LCD is

LCD_ADDRESS (0x7c>>1)

and for the background light

RGB_ADDRESS (0xc4>>1)

Will it work this evening? I've tested it last yesterday night.

Yes it should. Give it a try, and let us know how it goes.

Hmm no its not working, maybe address still wrong on my side, or something else went wrong.

Btw Awesome response time in this forum :slight_smile: thats great

1 Like

Actually, disregard what I said @FloX. You do not need to shift the address, since the fix I made was deployed about two hours ago. Let me re-read over your OP and see if we can figure out what’s going on.

On Arduino, I just download the example and the library and it works.
On Spark Core Web IDE, I removed unnecessary includes and copy all inside the IDE:. library header, library source and the example code. But nothing happens, the LCD displays none and the backlight is still off.
Spark Core is Powered by 5V and i2c lines from display are connected to D0 and D1.
Spark Core hardware doc, says its 5v tolerant on the i2c pins.
Maybe a level shifter is the better choice, but it should work anyway.
Thanks for your help guys.

1 Like

Try adding 4.7k pull up resistors on each I2C line… SDA and SCL.

I’m not sure how Arduino does it… maybe they have pullups turned on by default?

Doesn’t look like the Grove LCD has pull ups on it… so you’d need some with Spark.

Yep, Arduino enables pull-ups… @timb were you thinking of turning on weak pull-ups by default in your Spark I2C re-write? Seems like this is going to be a constant “gotcha” for anyone coming from Arduino.

void twi_init(void)
{
  // initialize state
  twi_state = TWI_READY;
  twi_sendStop = true;		// default value
  twi_inRepStart = false;
  
  // activate internal pullups for twi.
  digitalWrite(SDA, 1);
  digitalWrite(SCL, 1);

  // initialize twi prescaler and bit rate
  cbi(TWSR, TWPS0);
  cbi(TWSR, TWPS1);
  TWBR = ((F_CPU / TWI_FREQ) - 16) / 2;

  /* twi bit rate formula from atmega128 manual pg 204
  SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR))
  note: TWBR should be 10 or higher for master mode
  It is 72 for a 16mhz Wiring board with 100kHz TWI */

  // enable twi module, acks, and twi interrupt
  TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA);
}

Hi,
I’m not sure, does the spark core have internal pull ups to activate?
(pinmode and digitalwrite don’t work, or I do something wrong)
Or do I have to add physically resistors to the i2c lines?
(That will be bad, no resistors here :frowning: have to buy it, next week)

No pull-ups you can activate at this time. Most internal pull-ups are really week anyway, so I suggest always throwing a 4.7K on the clock and data lines. Hook that the 3V3 output from the core not the 3V3* output. (Assuming the device is 3.3V. If it’s 5V use that rail instead.)

How can you not have resistors?! Des older them from something! XD

I have resistors, but not here. :wink:

Was just thinking if I was in a pinch how I could make some resistors… one was about 45k, so I started adding them in parallel :wink: A couple alligator clips and Bob’s your uncle. I wonder if these would handle the current… lol Obviously it would be much more practical to not draw a resistor symbol! but I thought it was fun :smile:

http://imgur.com/VKLNSrd

http://imgur.com/Tq29WID

5 Likes

Yeah cool, thats the MacGyver way :wink:

But I thought in something more usual.

Haha, yeah… I was actually thinking WWMD as well :wink: Thanks for the ego boost! :blue_heart: MacGyver.

Hi,
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_CLEARDISPLAY 0x01
#define LCD_RETURNHOME 0x02
#define LCD_ENTRYMODESET 0x04
#define LCD_DISPLAYCONTROL 0x08
#define LCD_CURSORSHIFT 0x10
#define LCD_FUNCTIONSET 0x20
#define LCD_SETCGRAMADDR 0x40
#define LCD_SETDDRAMADDR 0x80

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

// 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 
{

public:
  rgb_lcd();

  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;
  
private:
  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.write(dta[i]);
    }
    Wire.endTransmission();                     // stop transmitting
}

rgb_lcd::rgb_lcd()
{
}

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

    Wire.begin();
    
    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;
    }

    // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION!
    // 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
    delayMicroseconds(50000);


    // 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);
    delayMicroseconds(150);

    // 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
    _displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;
    display();

    // clear it off
    clear();

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

}

/********** 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)
{
    command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT);
}
void rgb_lcd::scrollDisplayRight(void)
{
    command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT);
}

// 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.write(addr);
    Wire.write(dta);
    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!");

    delay(1000);
}

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:
    lcd.print(millis()/1000);

    delay(100);
}

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++)
    {
        Wire.beginTransmission(LCD_ADDRESS);
        Wire.write(dta[i]);
        Wire.endTransmission();
        delayMicroseconds(30);
    }
}

Good idea, but unfortunately this doesn’t work.

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