7

I have found a few good tutorials on how to send and receive data via I2C in connected arduinos. My limitation is now how to transmit different types of data like for instance, long, float, etc. Right now the communication via I2C using the wire library only allows transmission of numbers between 0 and 255. How can one transmit different types of numbers?.

In my code a master receives three types of data from the slave, and it send data of a fourth variable to the slave.

here is the code for the master

#include <Wire.h>

const int SLAVE_ADDRESS = 8; //Slave arduino ID
int table[]={0,0,0}; //the data will be transmited via table as to allow different data to be transfer.
void setup ()
{
Wire.begin ();   
Serial.begin (9600);  // start serial for output
}  // end of setup
void loop()
{
Wire.requestFrom(SLAVE_ADDRESS, 3);// request 3 bytes from slave device #8

for(int i=0;i<3;i++)//organizes the data from the slave in the table
{
int c = Wire.read(); // receive a byte as character
table[i]=c;
}
//displays the data
 Serial.print('\n');
   Serial.print(table[0]);
    Serial.print('\t');
   Serial.print(table[1]);
    Serial.print('\t');
   Serial.print(table[2]);
   Serial.print('\n');

delay (500);   

// transmit just this variable to the slave
int Work=1;
Wire.beginTransmission (8);
Wire.write (Work);
Wire.endTransmission ();
}

here is the code for the slave

#include <Wire.h>
int table[]={0,0,0};
int Work=0;

void setup() {
Wire.begin(8);                // join i2c bus with address #8
Wire.onRequest(requestEvent); // register event
Wire.onReceive(receiveEvent); // register event
}

void loop() {
int x=120;  
int y=1200;// gets converted to an strange # because is larger than 255
int z=3;
  table[0]=x;
  table[1]=y;
  table[2]=z;
}


void requestEvent()
{
  uint8_t Buffer[3];
  Buffer[0]=table[0];
  Buffer[1]=table[1];
  Buffer[2]=table[2];
  Wire.write(Buffer,3);
}

void receiveEvent(int howMany)
{Work = Wire.read();}

3 Answers 3

3

I2C is truly a powerful option of Arduino, for too many reasons; yet the amount of tutorials available are not that many and unfortunately are too complicated for the average person.

After working on this for 2 days, I think I have a way to transfer pretty much anything between master and slaves and viceversa. Note that I2C does not transfer floats or even integers larger than 255, there are several ways to go about this and here is a good tutorial http://www.gammon.com.au/i2c and even a library: http://forum.arduino.cc/index.php?topic=171682.0

The solution I found was simpler. Basically we convert any value, string, number, text, float, you name it, and turn into a variable char, which can be transferred via I2C. Once transferred, you can convert back to a number although in my case below, I just wanted to display the data from the slave.

Here is the code. I provide comments on different parts for clarity. I hope this helps. it worked for me.

//master
#include <Wire.h>

char t[10]={};//empty array where to put the numbers comming from the slave
volatile int Val; // varaible used by the master to sent data to the slave

void setup() {
  Wire.begin();        // join i2c bus (address optional for master)
  Serial.begin(9600);  // start serial for output
}

void loop() {
  Wire.requestFrom(8, 3);    // request 3 bytes from slave device #8

//gathers data comming from slave
int i=0; //counter for each bite as it arrives
  while (Wire.available()) { 
    t[i] = Wire.read(); // every character that arrives it put in order in the empty array "t"
    i=i+1;
  }

Serial.println(t);   //shows the data in the array t
delay(500); //give some time to relax

// send data to slave. here I am just sending the number 2
  Val=2;
  Wire.beginTransmission (8);
  Wire.write (Val);
  Wire.endTransmission ();
}

here the other part //slave

#include <Wire.h>

char t[10]; //empty array where to put the numbers going to the master
volatile int Val; // variable used by the master to sent data to the slave

void setup() {
  Wire.begin(8);                // Slave id #8
  Wire.onRequest(requestEvent); // fucntion to run when asking for data
  Wire.onReceive(receiveEvent); // what to do when receiving data
  Serial.begin(9600);  // serial for displaying data on your screen
}

void loop() {
  int aRead = analogRead(A0); //plug a potentiometer or a resistor to pin A0, so you can see data being transfer
  float x = aRead/1024.0*5.0; //generate a float number, with this method you can use any time of data pretty much 

  dtostrf(x, 3, 2, t); //convers the float or integer to a string. (floatVar, minStringWidthIncDecimalPoint, numVarsAfterDecimal, empty array);
  Serial.println(Val);         // print the character
 delay(500);
}

// function: what to do when asked for data
void requestEvent() {
Wire.write(t); 
}

// what to do when receiving data from master
void receiveEvent(int howMany)
{Val = Wire.read();}
4
  • How you request the same size (3) of string as the one you creates? where is the NULL terminator ?
    – Curnelious
    Commented Sep 17, 2016 at 9:33
  • Moreover your dtostrf is very wrong, how the total size of the string is 3 and you have 2 numbers after the point ? 1.2 is 3 chars, how can it have 2 chars after the point ?
    – Curnelious
    Commented Sep 17, 2016 at 9:46
  • 2
    This is not a good way, split the value into bytes, make it a byte array and cast it back to, for example, a float. float myFloat = *(float *)&byteArray; What it does, takes the address of the byteArray and cast the byte array (for example uint8_t byteArray[4];) to a float pointer and access it contents. You really doesn't need such expensive translation to a string.
    – Codebeat
    Commented Jan 9, 2018 at 0:28
  • I am 90% sure that the base library does have support for sending more datatypes than just byte. There is also be a method which takes arbitrary data and a "size" which is the number of bytes needed to send that data. See arduino.cc/en/Reference/WireWrite
    – user47164
    Commented Dec 1, 2021 at 18:18
0

I believe that for your scenario, i.e. sending different types of variables, the EasyTransfer library (by Bill Porter) would be an elegant solution. The library can be found here: https://github.com/madsci1016/Arduino-EasyTransfer. There are implementations for serial, I2C, and VirtualWire and the examples provided are pretty self-explanatory.

The handling is very easy. After including the library, you create an EasyTransfer object, define a data structure that basically represents a "container" of your data and this can consist of different variables.

struct RECEIVE_DATA_STRUCTURE{
  //put your variable definitions here for the data you want to receive
  //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO
  int blinks;
  int pause;
};

Once the "container" is filled, i.e. the variables are set on the sender side, they are sent with a simple command

  ET.sendData(I2C_SLAVE_ADDRESS);

On the receiver side, you define an identiacl data structureand receive the data by

ET.receiveData();

Hope this helps.

7
  • Thanks, that library is promising. I looked it over but could not find a tutorial to communicate via I2C. The tutorial I found was using tx/rx. is there any example you are aware of?.
    – Camilo
    Commented Sep 24, 2015 at 18:21
  • I found that easy transfer does not allow slave to master transfer. the slave can receive data from master, but the master can not from the slave. the library will require some changes, that have not been outdated: forum.arduino.cc/index.php?topic=171682.0
    – Camilo
    Commented Sep 24, 2015 at 19:43
  • The github page there is a folder EasyTransferI2C which is also included when you download the ZIP file. In that folder there is an Examples folder, in which you find the sketches for the sender (which is called EasyTransfer_TX_Example) and the other one for the receiver (EasyTransfer_RX_Example)
    – hobie
    Commented Sep 24, 2015 at 20:12
  • I tried the exampled but it only send data from the master to the slave, it does not offer the opportunity to send from slave to master. In the arduino forum above, a person said he improved the library to have that function but it does not appear in github
    – Camilo
    Commented Sep 24, 2015 at 20:43
  • Sorry, I cannot help you further on this.
    – hobie
    Commented Sep 26, 2015 at 11:07
0

Use the 'I2C_Anything' library, available here. I use it in all my I2C projects. It is extremely simple and works great. Here's the entire .h file

// Written by Nick Gammon
// May 2012

#include <Arduino.h>
#include <Wire.h>

template <typename T> unsigned int I2C_writeAnything (const T& value)
  {
  Wire.write((byte *) &value, sizeof (value));
  return sizeof (value);
  }  // end of I2C_writeAnything

template <typename T> unsigned int I2C_readAnything(T& value)
  {
    byte * p = (byte*) &value;
    unsigned int i;
    for (i = 0; i < sizeof value; i++)
          *p++ = Wire.read();
    return i;
  }  // end of I2C_readAnything

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.