Notes About Working with Various Arduino & Netduino Microcontroller Boards

Wednesday, March 13, 2013

Sending Data from an Arduino to a Raspberry Pi Using I2C

Here is an example of how to send digital information from an Arduino (Leonardo) to a Raspberry Pi. The Leonardo gathers data from an array of DS18B20 1-wire digital thermometers and sends the readings to the Raspberry Pi over an I2C connection.  The Raspberry Pi is the I2C master and the Leonardo acts as slave.  The Arduino polls the sensors in a loop, and the Raspberry Pi requests the data every 5 seconds. The Arduino code is written in C. I have have used Python 2 on the RPi.

Note: the order that the sensors are arranged in does not affect the order in which they are read, but they will always be read in the same order, so you can work out which reading in the sequence applies to which DS18B20 thermometer by warming one with a finger and seeing which reading increases. With the I2C data block limited to 32 bytes, sending the 8 byte device ID of each sensor takes up too much space.

Connections:


The 1-wire DS18B20s are connected in parasitic power mode. Looking at the flat side the sensors -

  • The left pin of the 1st sensor is connected to GND on the breadboard/Arduino.
  • The center pin on the 1st pin is connected to the Arduino digital pin 10 (with a 4k7 pull-up to 5V).
  •  Connect the left pin of each sensor to the right pin of the same sensor.
  • Connect the center pin of each sensor to the center pin of the next sensor. 
  • Connect the right pin of each sensor to the left pin of the next sensor.

Connect the SDA and SCL lines between the Arduino and the Raspberry Pi (SDA to SDA & SCL to SCL). Be sure to connect the Arduino's GND to GND on the RPi. Note that I am not using a level shifter to convert between 3V3 and 5V. After reading a number of posts online, I used a multimeter to check the voltage on the SDA and SCL lines coming from the Arduino Leonardo. The voltage was consistently 3.4 - 3.5 volts. 

The RPi has built-in pull-up resistors on SDA and SCL, so no external resistors are needed.  

Code:


Here is the Arduino C code. The Python code to be run on the RPi is posted on my Raspberry Pi blog. The portion of the code below that reads the temperature from the DS18B20s is based sample code that comes with the Arduino OneWire library.

I2C on the Raspberry Pi seems to be limited to 32 byte blocks of data. This example sends 4 temperature readings. Each reading is a 4-byte float, so the data fits in one block. I'm sure it's possible to send larger amounts of data, but I don't know how to do so (yet).

#include <Wire.h>

#include <OneWire.h>

const int DEV_CNT = 4, I2C_ADDR = 0x03;

OneWire ds(10);  // 1wire bus on pin 10
int j = 0;
float fahrenheit[DEV_CNT];
byte dev[DEV_CNT][8];

void setup(void) {
  Wire.begin(I2C_ADDR);
  Wire.onRequest(requestEvent); //Register i2c request handler 
}

void loop(void) {
  byte i, present = 0, type_s = 0, data[12], addr[8];
  if(!ds.search(addr)) {
    ds.reset_search();
    delay(250);
    return;
  } 
  // Read 8-byte device ID
  for( i = 0; i < 8; i++) { dev[j][i] = addr[i]; }
  ds.reset();
  ds.select(addr);
  ds.write(0x44,1);   // start conversion, parasite power on   
  delay(750);      
  present = ds.reset();
  ds.select(addr);    
  ds.write(0xBE);     // Read Scratchpad
  for ( i = 0; i < 9; i++) {  data[i] = ds.read(); }
  // convert the data to actual temperature
  unsigned int raw = (data[1] << 8) | data[0];
  fahrenheit[j] = (float) raw / 16.0 * 1.8 + 32.0;
  ++j %= DEV_CNT;  // Increment j but keep within range of device count
}

void requestEvent() // Handle i2c requests
{
  // Buffer to hold temp data, 6 bytes for each device
  byte buffer[DEV_CNT*4];
  // Populate buffer with temp. data
  for(int a = 0; a < DEV_CNT; a++) {
    byte* b = (byte*) &fahrenheit[a];
    // 4 bytes for each float
    for(int i = 0; i < 4; i++) {
      buffer[a*DEV_CNT+i] = b[i];
    }
  }
  // Send data over i2c connection
  Wire.write(buffer, DEV_CNT*4);
}




3 comments:

  1. Hi Brad,

    I wanted to do the same thing and was having hard time with sending more than one byte. Your example was perfect for me. Many thanks.

    P.S. I found a little error in the loop which load the data to the buffer. This error is only visible if the number of device is not 4! Here is how the code should read if I am not mistaken.

    // 4 bytes for each float
    for(int i = 0; i < 4; i++) {
    buffer[a*4+i] = b[i];
    }

    ReplyDelete
    Replies
    1. Hi André -

      Thanks for the note. I'm glad this helped you. I'll look into the code improvement that you suggest.

      Delete
  2. How do I send an addtional float value ?

    ReplyDelete