Printing incoming I2C Data

Hello guys,

im trying to record data sent from an I2C Master (Projet ECU) to a Slave LCD (2x16 LCD, address 0x38).
i would like tp print this data to serial monitor.

so far i managed to get my Arduino to become a Master on the I2C bus instead of the ECU, and display data on the LCD just fine.

but when i try to record the incoming data from the ECU and print it to serial monitor im getting nothing but gibbrish.

this is the code im using.

#include <Wire.h>
#include <LiquidCrystal.h>

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

void setup()
{
Wire.begin(0x38); // join i2c bus with address 0x38
Wire.onReceive(receiveEvent); // register event
Serial.begin(9600); // start serial for output
}

void loop() {
delay(100);
}

// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void receiveEvent(int howMany) {
while (1 < Wire.available()) { // loop through all but the last
byte c = Wire.read(); // receive byte as a character
Serial.print(c); // print the character
Serial.print("\r");
lcd.print(c);
delay(1000);
// lcd.clear();
}
int x = Wire.read(); // receive byte as an integer
// Serial.println(x); // print the integer
// lcd.print(x);
}

output to serial monitor is:
65
69
65
225
229
128
132
128
0
4
128
132
128
0
4
65
69
65
49
53
128
132
128
0
4
65
69
65
225
229
128
132
128
0
4
128
132
128
0
4
65
69
65
49
53
128
132
128
0
4
65
69
65
225
229
128
132
128
0
4
128
132
128
0
4

can anyone offer assistance please?
thanks.

OP edited and completely changed his post while I was typing this.
Thanks for wasting my time.

Pete

Tried receiving as string without much success too:

====
#include <Wire.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

void setup()
{
Wire.begin(0x38);
Wire.onReceive(receiveEvent);
Serial.begin(9600); // start serial for output
}

String data = "";

void loop()
{
Serial.print(data); // print the character
lcd.print(data);
delay (200);
Serial.print("---"); // print the character
}

void receiveEvent(int howMany)
{
data = "";
while( Wire.available())
{
data += (char)Wire.read();
}

}

=====

output:
------------------------------!%!---!%!---!%!---!%!---!%!---!%!---!%!---!%!---!%!---!%!---!%!---!%!---!%!---!%!---!%!---ÀÄÀ

el_supremo:
OP edited and completely changed his post while I was typing this.
Thanks for wasting my time.

Pete

what are you talking about?

@i3dm, both times you posted, you didn't follow the forum guidelines for posting. Please read these two posts:

How to use this forum - please read.
and
Read this before posting a programming question ...
and use code tags when you are posting code. Sometimes it's good for output too.

Please post the sketch that shows what you are sending.

Wire.onReceive(receiveEvent);
void receiveEvent(int howMany) {
  while (1 < Wire.available()) { // loop through all but the last
    byte c = Wire.read(); // receive byte as a character
    Serial.print(c);         // print the character
    Serial.print("\r");
       lcd.print(c);
       delay(1000);
      // lcd.clear();
  }
  int x = Wire.read();    // receive byte as an integer
//  Serial.println(x);         // print the integer
  //   lcd.print(x);
}

receiveEvent is an ISR (interrupt service routine), interrupts are disabled within it, and you should not be performing serial output which requires interrupts to be enabled.

If you are sending characters for display, the receive event function should read the bytes into a null terminated character array (c string) and set a flag that new data has arrived. Check for the new data flag, and display the new data in loop.

cattledog:
Please post the sketch that shows what you are sending.

Wire.onReceive(receiveEvent);
void receiveEvent(int howMany) {

while (1 < Wire.available()) { // loop through all but the last
    byte c = Wire.read(); // receive byte as a character
    Serial.print(c);        // print the character
    Serial.print("\r");
      lcd.print(c);
      delay(1000);
      // lcd.clear();
  }
  int x = Wire.read();    // receive byte as an integer
//  Serial.println(x);        // print the integer
  //  lcd.print(x);
}




receiveEvent is an ISR (interrupt service routine), interrupts are disabled within it, and you should not be performing serial output which requires interrupts to be enabled.

If you are sending characters for display, the receive event function should read the bytes into a null terminated character array (c string) and set a flag that new data has arrived. Check for the new data flag, and display the new data in loop.

thanks for the help guys, this is all very new to me and im learning as im going.

how do i check the new data flag and siaply the new data?

This is the sending sketch, that displays data on the 2x16 LCD perfectly, just like the original ECU does. im using this sketch to emulate the ECU and what im trying to do with my other arduino is receive this I2C data and print it on serial:

=====

#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>

#define I2C_ADDR 0x38 // <<----- Add your address here. Find it from I2C Scanner
#define BACKLIGHT_PIN 3
#define En_pin 2
#define Rw_pin 1
#define Rs_pin 0
#define D4_pin 4
#define D5_pin 5
#define D6_pin 6
#define D7_pin 7

int n = 1;

LiquidCrystal_I2C lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin);

void setup()
{
lcd.begin (16,2); // <<----- My LCD was 16x2
}

void loop()
{
lcd.clear();
lcd.setCursor (0,0); // go to start of 2nd line
lcd.print("Who is 123456? ");

lcd.setCursor (0,1); // go to start of 2nd line
lcd.print("My Mind is 12345");
delay(1000);

}

Here are two inter-Arduino i2c send and receive sketches written for a 16x2 lcd display. It sends Time and dummy temperature data. The receive portion is adapted from the methods of Robin2's tutorial on serial data. Serial Input Basics - #2 by Robin2 - Programming Questions - Arduino Forum

You can probably adapt parts of it for your purposes. Master sends data, slave controls lcd display.

#include <Wire.h>//must be before RTClib
#include <RTClib.h>
RTC_DS1307 RTC;

int interval = 2000;
unsigned long lastSend;

void setup()
{
  Wire.begin();
  RTC.begin(); 
}

void loop()
{

if (millis() - lastSend  >= interval)
{ 
  DateTime now= RTC.now();
  char timeChars[8];
  sprintf(timeChars,"%02d:%02d:%02d", now.hour(),now.minute(),now.second()); 
  
   static float T = 19.0;//dummy temperature float variable
   char tempChars[4];//will hold xx.x
   dtostrf(T,4,1,tempChars); 
   
  Wire.beginTransmission(1); //to slave device 1
  Wire.write('<'); //startMarker
  Wire.write("Time ");//can put in slave sketch
  Wire.write(timeChars);
  Wire.write("&"); //separator for line1/line2 parse on rx
  Wire.write("Temp C "); //can put in slave sketch        
  Wire.write(tempChars);  
  Wire.write('>'); //endMarker
  Wire.endTransmission(); // send complete data package
  lastSend = millis(); 
 
  T += .1; //change value sent
  if (T >= 25) T = 19;
}

}

Receive

#include<Wire.h>

#include <LiquidCrystal_I2C.h>  // F Malpartida's NewLiquidCrystal library
LiquidCrystal_I2C lcd(0x20, 4, 5, 6, 0, 1, 2, 3, 7, NEGATIVE);

const byte SlaveDeviceId = 1;

const byte numChars = 32; //max 32 for 16x2 lcd display
char receivedChars[numChars];
boolean newData = false;

char* line1; //for 2 line lcd display
char* line2;

void setup() {
  Wire.begin(SlaveDeviceId);

  lcd.begin (16, 2);
  lcd.print(F("XXXXXXXXXXXXXXXX"));//visual cue for setup
  lcd.setCursor(0, 1);
  lcd.print (F("XXXXXXXXXXXXXXXX"));

  delay(2000);
  lcd.clear();
  //could print unchanging parts of line1 and line2 here
  Wire.onReceive(receiveEvent);

}

void loop() {

  if (newData == true) {
    line1 = strtok(receivedChars, "&"); //parse line 1
    line2 = strtok(NULL, "\0"); //parse line2
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print(line1);
    lcd.setCursor (0, 1);
    lcd.print(line2);
    newData = false;
  }
}
//Serial receive from Robin2 forum post http://forum.arduino.cc/index.php?topic=288234.msg2016582#msg2016582
void receiveEvent(int bytesReceived)
{
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char rc;
  while (Wire.available() > 0 && newData == false) { //get all bytes from buffer
    rc = Wire.read();

    if (recvInProgress == true) {
      if (rc != endMarker) {
        receivedChars[ndx] = rc;
        ndx++;
        if (ndx >= numChars) {
          ndx = numChars - 1;
        }
      }
      else {
        receivedChars[ndx] = '\0'; // terminate the string
        recvInProgress = false;
        ndx = 0;
        newData = true;
      }
    }

    else if (rc == startMarker) {
      recvInProgress = true;
    }
  }
}

This is the sending sketch ...

If you don't follow the suggestions in reply #4 I'm going to lock this thread as time-wasting.

Use code tags!

Don't just ignore us.

got it, apologies.
first timer here :slight_smile:

cattledog:
Here are two inter-Arduino i2c send and receive sketches written for a 16x2 lcd display. It sends Time and dummy temperature data. The receive portion is adapted from the methods of Robin2's tutorial on serial data. Serial Input Basics - #2 by Robin2 - Programming Questions - Arduino Forum

You can probably adapt parts of it for your purposes. Master sends data, slave controls lcd display.

#include <Wire.h>//must be before RTClib

#include <RTClib.h>
RTC_DS1307 RTC;

int interval = 2000;
unsigned long lastSend;

void setup()
{
  Wire.begin();
  RTC.begin();
}

void loop()
{

if (millis() - lastSend  >= interval)
{
  DateTime now= RTC.now();
  char timeChars[8];
  sprintf(timeChars,"%02d:%02d:%02d", now.hour(),now.minute(),now.second());
 
  static float T = 19.0;//dummy temperature float variable
  char tempChars[4];//will hold xx.x
  dtostrf(T,4,1,tempChars);
 
  Wire.beginTransmission(1); //to slave device 1
  Wire.write('<'); //startMarker
  Wire.write("Time ");//can put in slave sketch
  Wire.write(timeChars);
  Wire.write("&"); //separator for line1/line2 parse on rx
  Wire.write("Temp C "); //can put in slave sketch       
  Wire.write(tempChars); 
  Wire.write('>'); //endMarker
  Wire.endTransmission(); // send complete data package
  lastSend = millis();

T += .1; //change value sent
  if (T >= 25) T = 19;
}

}




Receive



#include<Wire.h>

#include <LiquidCrystal_I2C.h>  // F Malpartida's NewLiquidCrystal library
LiquidCrystal_I2C lcd(0x20, 4, 5, 6, 0, 1, 2, 3, 7, NEGATIVE);

const byte SlaveDeviceId = 1;

const byte numChars = 32; //max 32 for 16x2 lcd display
char receivedChars[numChars];
boolean newData = false;

char* line1; //for 2 line lcd display
char* line2;

void setup() {
  Wire.begin(SlaveDeviceId);

lcd.begin (16, 2);
  lcd.print(F("XXXXXXXXXXXXXXXX"));//visual cue for setup
  lcd.setCursor(0, 1);
  lcd.print (F("XXXXXXXXXXXXXXXX"));

delay(2000);
  lcd.clear();
  //could print unchanging parts of line1 and line2 here
  Wire.onReceive(receiveEvent);

}

void loop() {

if (newData == true) {
    line1 = strtok(receivedChars, "&"); //parse line 1
    line2 = strtok(NULL, "\0"); //parse line2
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print(line1);
    lcd.setCursor (0, 1);
    lcd.print(line2);
    newData = false;
  }
}
//Serial receive from Robin2 forum post Serial Input Basics - #2 by Robin2 - Programming Questions - Arduino Forum
void receiveEvent(int bytesReceived)
{
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char rc;
  while (Wire.available() > 0 && newData == false) { //get all bytes from buffer
    rc = Wire.read();

if (recvInProgress == true) {
      if (rc != endMarker) {
        receivedChars[ndx] = rc;
        ndx++;
        if (ndx >= numChars) {
          ndx = numChars - 1;
        }
      }
      else {
        receivedChars[ndx] = '\0'; // terminate the string
        recvInProgress = false;
        ndx = 0;
        newData = true;
      }
    }

else if (rc == startMarker) {
      recvInProgress = true;
    }
  }
}

Thank you, i was able to load that with some mods and print I2C data on the receiver.
the problem is that I2C data being sent to the LCD is probably a different format in some way, and that is what i need to print.

i do NOT need to print I2C data being sent from Arduino 1 to Arduino 2.
i DO need to serial print the data sent from Arduino 2 to the LCD, via serial monitor as string or char.
When i configured the codes to Tx and Rx on address 0x38, boh Arduinos communicate fine and data flows and prints.
When i connected the Arduino 2 (receiver) to my ECU (which is sending Data to LCD address 0x38 constantly) it never goes into the "if data == true" loop. dontknow why.

any idea how to do that? im still looking :slight_smile:
appriciate the help very much, thank you guys.

Set the receiver to the same slave address as the LCD. It should then receive data intended for the LCD. If it interfered with the operation of the LCD then put a diode in the SDA line going to the spy, so that the spy can't pull the main SDA line low.

So i did make "some" progress.
spliced the I2C harness between the ECU and original LCD, and ran the I2C sniffer.
the sniffer does see the SCL and SDA digital lines changing, but none of the sketches i have prints the data.

i did notice that when i try to join as a receiver on the bus with 0x38 (same address as original LCD), the LCD display gets corrupted. i guess you cant use tho identical addres receivers.

still looking for a way to decode the LCD data sent from ECU to LCD and print it.
im basically trying to do a reverse function of i2c "lcd.print" - lcd.print takes chars and sends them to LCD via I2C, i would like to get that same I2C data and decode it back to chars.

thanks.

MorganS:
Set the receiver to the same slave address as the LCD. It should then receive data intended for the LCD. If it interfered with the operation of the LCD then put a diode in the SDA line going to the spy, so that the spy can't pull the main SDA line low.

no matter what i do, i cant receive data in my Rx eben when its on the right address as the lcD (0x38)

Having an Arduino with the same address will always cause troubles. The Arduino acknowledges its address and might stretch the clock pulse. If a diode is used to prevent the Arduino messing up the I2C bus, then the Arduino might not receive the same data, because it really needs that clock pulse stretching.

This is a I2C sniffer with an Arduino Uno : http://www.johngineer.com/blog/?p=455

This is a logic analyzer with an Arduino Uno or Mega : GitHub - gillham/logic_analyzer: Implementation of a SUMP compatible logic analyzer for the Arduino
I tried this one, but the buffer is so small, it is hard to capture the right data.

On Ebay and AliExpress are 8 channel 24MHz logic analyzers with USB, for 5 dollars.

Why do you need to use the Rx arduino as a "spy" as the data is passed from the ECU to the lcd? The lcd is currently at address 0x38. Can it be changed? The ECU currently sends to 0x38. Can the ECU be programmed to send to a different address?

I would think you could change the architecture so that the Arduino is the primary target for the ECU, and then the Arduino both serial prints the data and prints to the lcd. The Arduino will become a relay station, receiving data from the ecu and then sending it to both the lcd and the serial monitor.

why do i need a logic analyzer?

record data from i2c bus = get a logic analyzer

Well, logic analyzer acting as a protocol analyzer.

Or use an Arduino with two I2C interfaces, such as a Due or Teensy. Have it act as a slave on address 0x38 on one of those and put the LCD on the other.

A logic analyzer sounds like a good suggestion. You can never have enough tools. It will be useful for all sorts of other stuff.