I2C: Send mulitple values from a device

I setup my Raspberry Pi as the master and 9 Ardiuno devices as the slave. I2C is working. I can get a value from the Arduino to send to the Pi Master and display on an I2C LCD screen. I have been looking for how to send more than one value from a slave to a master? I did setup an array to store the values

deviceID, communication_value = 0 or 1, photo resistor value (0 to 255)

I have the 9 Ardiuno device 14 - 1c. LCD screens are 24 and 27.

I can program in Python and C++ and have written data to external files and databases. What is the trick to send more than 1 value on I2C.

Please, post you codes for both Master and Slave where you have sent 1-byte data from Slave to Master instead of telling the Master to collect 1-byte data from Slave.

I2C Pi Master and Ardiuno Slave

Ardiuno Slave 14.

#include <Wire.h>

const int ledPin = 13; // onboard LED
int ValueA3 = 0;
static_assert(LOW == 0, "Expecting LOW to be 0");
int Checking_In = 1;


void setup() {
  Serial.begin(9600);
  Wire.begin(0x14);                // join i2c bus with address #4
  
  Wire.onReceive(receiveEvent); // register event
  Wire.onRequest(requestEvent);  // send date to Pi
  Wire.onRequest(ard_checkingIn);  // send date to Pi
  
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW); // turn it off
}

void loop() {
  delay(100);
}

// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void receiveEvent() {
  while (Wire.available()) { // loop through all but the last
    char c = Wire.read(); // receive byte as a character
    digitalWrite(ledPin, c);
  }
}

void requestEvent()
{
  
}

void ard_checkingIn()
{
Wire.write(Checking_In);
}

Pi Master

#! /usr/bin/env python3
#include <iostream>
#include <wiringPiI2C.h>
import time

from smbus import SMBus
from array import *
import I2C_LCD_driver_24
import I2C_LCD_driver_27

ard_addr = bytearray([0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c])
dev_name = ['14', '15', '16', '17', '18', '19', '1a', '1b', '1c']
    
number = bytearray([0, 0, 0, 0, 0, 0, 0, 0, 0])
check_in = [0, 0, 0, 0, 0, 0, 0, 0, 0]
lcd_24_screen_format = [ [1,0,1,2,1,3], [2,0,2,2,2,3], [3,0,3,2,3,3], [4,0,4,2,4,3], [1,5,1,7,1,8], [2,5,2,7,2,8],
                         [3,5,3,7,3,8], [4,5,4,7,4,8], [1,10,1,12,1,13] ]


bus = SMBus(1) # indicates /dev/ic2-1
lcd_24 = I2C_LCD_driver_24.lcd()
lcd_27 = I2C_LCD_driver_27.lcd()

cnt = 0
check_in_answer = ""

while True:
    lcd_24.lcd_clear()
    while cnt < 9:
        
        check_in[cnt] = bus.read_byte(ard_addr[cnt])
        print("Arduino", dev_name[cnt], "reporting in", check_in[cnt])
        
        
        bus.write_byte(ard_addr[cnt], 0x1) # switch it on
        time.sleep(2)
        bus.write_byte(ard_addr[cnt], 0x0) # switch it on
        
        
        str_addr = "{}".format(dev_name[cnt])
        str_check_in = "{}".format(check_in[cnt])
        lcd_24.lcd_display_string(str_addr, lcd_24_screen_format[cnt][0], lcd_24_screen_format[cnt][1])
        lcd_24.lcd_display_string(": ", lcd_24_screen_format[cnt][2], lcd_24_screen_format[cnt][3])
        if (str_check_in == "1"):
            check_in_answer = "Y"
        elif (str_check_in == "0"):
            check_in_answer = "N"
        lcd_24.lcd_display_string(check_in_answer, lcd_24_screen_format[cnt][4], lcd_24_screen_format[cnt][5])
        cnt +=1
        
        
    time.sleep(3)    
    cnt = 0
    print(" ")






Why two callbacks for one event?

Thank you very much for the code.

I can follow your Slave code and not the Master Code. Moreover, I don't own a RPi.

1. Normally, Master requests the Slave to send 1-byte (or more) data by executing the following code:

Wire.requestFrom(slaveAddress, n);  //n = number of data bytes requested

2. In response, Slave goes to the following ISR and stores the data in the buffer for onward transmission to the Master. The codes are:

Wire.onRequest(sendEvent);    //ISR declaration in the setup()
void sendEvent() //ISR definition
{
     Wire.write (highByte(y));  // let us say int y = 0x1234;
     Wire.write(lowByte(y));
}

3. Master collects the arrived data bytes from its buffer, reconstruct the int-type data item and shows on SM.

byte y1 = Wire.read();  //MSByte first
int y = y1<<8|Wire.read();
Serial.println(y, HEX);

why not
Wire.write(y,2);
?

As I understand from the following reference, y should be an 8-bit value or a string or an array. My y in the given example of post #5 is not an array.
WireWrite

yes, it was my mistake, the correct syntax is
Wire.write((uint8_t)&y,2)

Your code is not compiled? It does not fit with syntax given in post #7.

yes
I'm too inattentive...

Wire.write((uint8_t *) &y, 2);

I write a lot of C++ code and usually separate code into void subroutines to organize the code and call them. In Python, I put code in separate py files and create a library to call in functions. It is just habit I guess.

GolamMostafa,

The Raspberry Pi 4B 8GB 64-bit has more memory and processing power than the Arduino devices. Your method looks intriguing. I could setup one of my two Arduino Mega devices as a master. I setup on another sketch an array(3,9) on the Arduino devices. I can write the data to the array on each Arduino and serial.print it out. I just couldn't figure out how to write the array data to the Pi to receive. The Pi was getting 0's . I was going to reset it with a Mega a Master to receive the data.

1 Like

Still, it is same. Please, try to understand the syntaxes of post #7.

Wire.write((uint8_t *) &y, 2);

I have never seen it done like this. Unsigned 8 bit integer and write two bits.

The following cdoes will work.

byte y[] = {0x12, 0x34};
Wire.write(y, 2);

what this syntaxes

my code matches the third line

uint16_t may be casted as array of two uint8_t - is it new for you?

The 3rd line says:
data : an array of data which means:

byte y[] = {0x12, 0x34};
Wire.write(y, 2);    //Wire.write(y, sizeof y);

@GolamMostafa

byte y[] = {0x12, 0x34};

and

uint16_t y = 0x1234;

has the same memory organisation and may be easily conversed each other

it is not the " unsigned 8 bit integer", it is the pointer to it.

You can do the following as I2C is byte-oriented network.

void sendEvent()
{
  byte *ptr;
  ptr = (byte*)&y;
  byte m1 = *ptr;
  Wire.write(m1);
  ptr++;
  byte m2 = *ptr;
  Wire.write(m2);
}

@GolamMostafa
your code do the same as mine , but my one is shorter