Another I2C LCD library.

I have built an I2C to LCD port expander and now I'm fighting with a modified version of the LiquidCristal library.
Before start adapting LiquidCristal to the I2c bus, I have thoroughly tested the board to be sure that it's working flawlessly.
I'm pretty sure that I will have to use the Wire library with another i2c devices at the same time than the i2c lcd,
I have choosed to not take out only the necessary i2c code from the Wire library that I need to manage the lcd, and to use the Arduino Wire library without change.

I have 2 immediate problems:

  1. When from the modified LiquidCristaI library I call the 3 functions that I need from the Wire, the Arduino get's stuck. In the first invocation of the function that contains it.
    I have used without problems the same 3 functions in sketches used to test the board.
    Wire.beginTransmission(_i2c_port);
    Wire.send(data);
    Wire.endTransmission();
    When are used inside the modified LiquidCristal, Arduino doesn't return from Wire.endTransmission() after the fist call.
    I've checked with bus pirate in I2C sniffer mode and only get one i2c start condition. No more.

  2. I need to add "#include <Wire.h>" to anywhere in the sketch to compile successfully.
    No matter if I place it in top or bottom of the sketch.
    No matter if I already have placed it on the library , I need to put it on the sketch anyway to compile.
    In the pastebin of the sketch I've placed it on bottom.

LcdI2c.cpp http://pastebin.com/download.php?i=wsJ956ft
LcdI2c.h http://pastebin.com/download.php?i=ZCm3ZAtz
Sketch.pde http://pastebin.com/download.php?i=X9QaFvnh

I'm using Duemilanove with atmega168.

The compiled size is:
Binary sketch size: 3348 bytes (of a 14336 byte maximum)

Thank you very much in advance for any contribution, idea or clue.
By the way, please forgive my "English. "

add 1) The Wire.endTransmission() is the workhorse, the other calls just fill a buffer, if you dive into the Wire library code you see this is quite straightforward AND you will find the meaning of the returnvalue(s) of endTransmission().

add 2)
It is a known fact that if you use Wire.h in a library you also must include it in the main sketch (prefered at top, together with defines) otherwise it can't find the instance of Wire object.

Hopes this helps,

Hi Rob, Thanks for take the job of reading this enormous post.
I've decided to go straight and try to find the problem using BusPirate as logic analyzer.

  1. I have loaded the master_writer example for the Wire library.
  2. Setup a trigger to start capture when any I2c pin changes of state.
    3)Captured successfully the i2c traffic.

    Event the light on the LCD goes on and of as the value of x pass 127 (the 8th pin is attached to a mosfet to drive the back-light) and go back when overflow to 0.

Doing the same with my sketch no data is captured.

My sketch and modified library sources compile correctly, call exactly the same Wire functions, no one more or one less.
But does not transmit a single clock or data in the Arduino i2c bus.
Not transmit a single bit and NEVER returns from the first Wire.EndTransmission call.

The offender code:
void LcdI2c::sendData(void){
uint8_t data=0;

for (uint8_t i=0;i<8;i++){
data |=_i2c_data*<<i;*
}
Wire.beginTransmission(_i2c_port); // this is 0x38
Wire.send(data); //first data sent (and last) is 0x0
Wire.endTransmission(); // Never returns from this call.
}
If I comment this 3 Wire lines above , and add some Serial.print everything works fine , except that the i2c bus does not transmit.
The only other Wire function call in all the code is the Wire.begin in the LcdI2c constructor.

The device address is 7 bits so it might need a shift >>1 - its one of the Arduino I2C magic thingies

just give it a try

void LcdI2c::sendData(void){
  uint8_t data=0;

  for (uint8_t i=0;i<8;i++){
   data |=_i2c_data<<i;
 }
 Wire.beginTransmission(_i2c_port >> 1);   // shift one place to mimic 7 bits
 Wire.send(data); 
 Wire.endTransmission(); 
}

Thanks again robtillaart.
The port value that I give to the library it's already in 7 bit format.
I have done a new test:
Copy the Arduino Wire library to sketchbook/libraries/LiquidCrystal2/
Replace all "LiquidCristal" references in the headers and source to "LiquidCristal2".
Add "#include <Wire.h>" at the top of LiquidCristal2.cpp file.
Add the next 3 Wire functions to the top of void LiquidCrystal2::send(uint8_t value, uint8_t mode) { 

Wire.beginTransmission(0x38); 
Wire.send(255);
Wire.endTransmission();

Just to light up the LCD, no lcd commands involved here.

Load into arduino:

#include <Wire.h>
#include <LiquidCrystal2.h>

#define PORT 0x38

LiquidCrystal2 lcd(13,12,11,10,9,8,7); 

void setup (){
  Wire.begin();
  lcd.begin(16, 2);
  lcd.print("hello, world!");
}

void loop (){
   lcd.setCursor(0, 1);
  lcd.print(millis()/1000);
}

There is no movement on I2c bus.
If I take the 3 Wire functions out of the lib, and put it in the sketch works and do what it have to do.

Thanks for the patience.

The LiquidCrystal2 lcd(13,12,11,10,9,8,7); line sounds very much like a standard parallel display, nothing like an I2C display

I2C is on analog pin 4 and analog pin 5!

have you tried - I2CScanner: Arduino as I2C bus scanner – todbot blog to find the I2C display?

I call the constructor of the LiquidCristal2 and give that pins only to fake initialize the library.
I'm not trying to use the lcd in paralell, only give to the lib some pins not used for the i2c bus.
I know where are the i2c pins and it's the Wire library who cares about that.
I've checked the i2c port value with that pde it's the one I'm using.
The same port works perfectly if you take the wire part out off the LiquidCristal2 and put in the sketch.
Thanks again for checking.
There must be something wrong with the compiler or the arduino libraries (not 100% sure of course), because I never seen before that a "non declared function in this scope" error can be removed including the required header after the function it's called.
Maybe the libraries not where designed that you can use one inside another, and there's no other way than doing all from scratch if you don't want duplicate code everywhere.

I call the constructor of the LiquidCristal2 and give that pins only to fake initialize the library.

Why not create an appropiate constructor, with the device-id as param? (it makes it difficult for us to debug you code in this way and I dont want to download every BETA library)

You need to strip the whole library to just get this to work. Find below how it would look, should get you started. code is not tested if it will compile but I guess it is easier to debug than the 200+ lines of the modded lcd lib.

#include <Wire.h>
#include <I2C_LCD.h>

#define DEVICE 0x38

I2C_LCD lcd(DEVICE); 

void setup ()
{
  lcd.begin(16, 2);
  lcd.print("hello, world!");
}

void loop ()
{
}

That means that the first version of the library should only have these three functions

  • lcd(deviceid)
  • begin(rows, columns)
  • print(char *)

I2C_LCD.h file

#ifndef LcdI2c_h
#define LcdI2c_h

#include Wire.h"

class I2C_LCD 
{
public:
  I2C_LCD(uint8_t device);

  void begin(uint8_t cols, uint8_t rows);
  void print(char * str);

private:
  int send(uint8_t data);
  uint8_t _device;
  uint8_t _cols;
  uint8_t _rows;
};

#endif
// END OF FILE

I2C_LCD.cpp

#include "I2C_LCD.h "
#include <Wire.h>

#include "WProgram.h"

I2C_LCD::I2C_LCD(uint8_t device)
{
  _device = device;
}

void I2C_LCD::begin(uint8_t cols, uint8_t rows)
{
  // might add some constrain e.g. cols = constrain(cols, 1, 4);
  _cols = cols;
  _rows = rows;
}

void  I2C_LCD::print(char * str)
{
  int i = 0;
  while(str[i] != '\0')
  {
    send(str[i]);
    i++;
  }
};

int I2C_LCD::send(uint8_t data)
{
  Wire.beginTransmission(_device);
  Wire.send(data);
  return Wire.endTransmission();
}