BerryJam – Simple I2C Protocol for Advanced Communication Between Arduinos

download BerryJam – Simple I2C Protocol for Advanced Communication Between Arduinos

of 12

description

BerryJam – Simple I2C Protocol for Advanced Communication Between Arduinos

Transcript of BerryJam – Simple I2C Protocol for Advanced Communication Between Arduinos

  • Main task advanced communication between multiple Arduinos using I2C bus.

    Main problem most online tutorials covers just one blinking LED with almost no practical use. Slave justexecutes ONE and SAME function every time Master asks about it. Id like to outsource slave Arduinos forzillions of tasks.

    Proposed solution simple protocol which enables to send any number of commands to Slave, opposingsingle return from simple

    Strong communication skills are always a key to success:

    Say we have one Arduino(Slave) dedicated for position sensors and motor drives. Other slave for handling userinterface tasks (displays, input controls, wifi communication). And a Master Arduino for controlling them alltogether. Master can ask to do any number of tasks(not just one from one Arduino).

    RECENT POSTS

    Spining BLDC(Gimbal) motors atsuper slooooooow speeds withArduino and L6234April 18, 2015

    Driving 47 LED with 4 resistorsonly (kinda Charlieplexing)August 8, 2014

    Simple I2C protocol for advancedcommunication between ArduinosJuly 17, 2014

    YEAH BITCH! LAZERS!June 29, 2014

    Hello world!April 20, 2014

    Wire.onRequest();

    Simple I2C protocol for advanced communication between Arduinos

    BLOG CONTACT

    1 http://www.berryjam.eu/2014/07/advanced-arduino-i2c-communication/

  • Official Wire (I2C library for Arduino ) reference doesnt add a lot of clarity for novices.Good starting point is this tutorial: http://arduino.cc/en/Tutorial/MasterWriter. It just lacks of two pull-upresistors on SDA and SCL lines. Do not forget them. * Also some people report problems when multipleArduinos are connected to one computer at the same time.

    As you can see from above tutorial, communication is performed by transferring chars or bytes (max value255). Thats not much use if you want to transfer real-life values like integers from or controlsome Digital PMW Pin.

    So, second highly recommended tutorial can be found here: http://jamesreubenknowles.com/arduino-i2c-1680. It shows how to easily transfer and receive integer values by splitting them into two bytes (warning:some bitshifting involved).

    Main problem for me was that I had multiple sensors on one sensor-dedicated slave. And on masters request Slave could only return one value. So I made simple protocol for choosing desired

    sensor. The same method can be used for performing different tasks on slave, not just returning differentvalues. You can make slave perform any function from its arsenal.

    Basic algorithm:

    Master opens connection and sends integer value (command number) to selected Slave.1. Slave receives code number and makes function selection.2. Master requests selected(in 2. step) function to be executed on Slave by 3. Slave executes specified command and returns result to Master.4. Master receives result.5. Master closes connection.6. Slave resets command number and waits for next command from Master.7.

    First, lets look at slave code. There are two functions for reading one or other sensor:

    When slave receives command number, it executes function:

    Now when we have command (LastMasterCommand), we will wait for execution request from Master:

    analogRead();

    Wire.onRequest();

    Wire.onReceive();Wire.onRequest();

    receiveCommand();

    1234567891011121314

    #define XSensorPin A1#define YSensorPin A2

    ...

    int GetXSensorValue(){int val = analogRead(XSensorPin);return val;

    }

    int GetYSensorValue(){int val = analogRead(YSensorPin);return val;

    }

    1234567891011

    byte LastMasterCommand = 0;

    void setup(){Wire.onReceive(receiveCommand); // register talk to slave event

    }

    ...

    void receiveCommand(int howMany){LastMasterCommand = Wire.read(); // 1 byte (maximum 256 commands), it can be easily changed to inte

    }

    1234

    void setup(){Wire.onRequest(slavesRespond); // Perform on master's request

    }

    Sensors reading functions

    Command decryption

    Request execution

    2 http://www.berryjam.eu/2014/07/advanced-arduino-i2c-communication/

  • Function on Masters side for getting X Sensor value from Slave Arduino:

    Thats all, functions can do any tasks, not only read sensors. For example they can control a motor or LEDsand return no useful data (just report successful execution).Using same technique You can expand this simple protocol, i.e. pass a command number AND then someparameters to Slave, and then execute it. I showed basic principle.

    Complete Master side program: (click to expand)

    56789101112131415161718192021222324252627282930313233343536

    ...

    void slavesRespond(){

    int returnValue = 0;

    switch(LastMasterCommand){case 0: // No new command was received

    Wire.write("NA");break;

    case 1: // Return X sensor valuereturnValue = GetXSensorValue();

    break;

    case 2: // Return Y sensor valuereturnValue = GetYSensorValue();

    break;

    case 3:// Another useful function if received command = 3, and so on...

    break;

    }

    uint8_t buffer[2]; // split integer return value into two bytes bufferbuffer[0] = returnValue >> 8;buffer[1] = returnValue & 0xff;Wire.write(buffer, 2); // return slave's response to last commandLastMasterCommand = 0; // null last Master's command and wait for next

    }

    1234567891011121314151617181920212223

    int GetXSensorValue(byte SlaveDeviceId){

    // SEND COMMAND Wire.beginTransmission(SlaveDeviceId);Wire.write(1); // Transfer command ("1") to get X sensor value;delay(10);

    // GET RESPONSEint receivedValue;int available = Wire.requestFrom(SlaveDeviceId, (byte)2);if(available == 2){

    receivedValue = Wire.read()

  • Complete Slave side program: (click to expand)

    1011121314151617181920212223242526272829303132333435363738394041424344

    Serial.begin(9600); // start serial for output}

    void loop(){

    int x = GetXSensorValue(1); // 1 - slave's addressSerial.print("X Sensor value: ");Serial.println(x);

    delay (100);}

    int GetXSensorValue(byte SlaveDeviceId){

    // SEND COMMAND Wire.beginTransmission(SlaveDeviceId);Wire.write(1); // Transfer command ("1") to get X sensor value;delay(10);

    // GET RESPONSEint receivedValue;int available = Wire.requestFrom(SlaveDeviceId, (byte)2);if(available == 2){

    receivedValue = Wire.read()

  • UPADATEUPADATE

    Update: I wrote new example for sending command AND passing data for Slave functions.

    Now Master sends not only command number (one byte), but 11 bytes data packet consisting of:

    Command number (one byte)1-st argument value (int) (or two bytes)2-nd argument value (int) (or two bytes)3-rd argument value (int) (or two bytes)4-th argument value (int) (or two bytes)5-th argument value (int) (or two bytes)

    I plan to use functions with maximum 5 integer arguments. If You need more/less or wish to change input/outputvariables type it can be easily done, by examining these examples. They are completely based on previousones. Maximum size of standard buffer in Wire library is 32 bytes.

    In order to work correctly, data packet has always be the same size (even if Your function doesnt need anyarguments You have to pass zeroes or any values). Also if You are not interested in return value of any Slavesfunction, than You can execute commands in function. There is no need to ask for return

    if You are interested in just sending data from Master to Slave. But if You have just onefunction that returns something, use everywhere. Wire likes consistency. A lot.

    In this example Master creates a data packet (command number, and five integer variables), sends it to Slave,Slave performs sum of all 5 variables and returns result to Master. Master prints result in serial window. Slaveacts as a math-coprocessor for the Master

    Tip if You dont see correct answer in serial window press reset button on Master, while serial window isopen. This will solve early power-on miscommunication issues. Later both sides will work correctly.

    Complete Master side program v0.2: (click to expand)

    Wire.onReceive();Wire.onRequest();

    Wire.onRequest();

    4546474849505152535455565758596061626364

    returnValue = GetYSensorValue();break;

    }

    byte buffer[2]; // split int value into two bytes bufferbuffer[0] = returnValue >> 8;buffer[1] = returnValue & 255;Wire.write(buffer, 2); // return response to last commandLastMasterCommand = 0; // null last Master's command

    }

    int GetXSensorValue(){int val = analogRead(XSensorPin);return val;

    }

    int GetYSensorValue(){int val = analogRead(YSensorPin);return val;

    }

    123456

    // Simple I2C protocol v0.2 for Arduino// Master side program // (c) 2014 Ignas Gramba//#include

    5 http://www.berryjam.eu/2014/07/advanced-arduino-i2c-communication/

  • Complete Slave side program v0.2: (click to expand)

    789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172

    byte DataPacket[11];byte command_nr;int a, b, c, d, e; // argumentsbyte SlaveDeviceId = 1;

    void setup(){

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

    command_nr = 2; // initialise test valuesa = 105;b = 2350;c = 4587;d = 12587;e = 12;

    }

    void loop(){

    makeDataPacket(command_nr, a, b, c, d, e);

    sendDataPacket();

    int response = receiveResponse();Serial.print("Slave response: ");Serial.println(response);

    while(1); // Stop}

    void makeDataPacket(byte command, int aa, int bb, int cc, int dd, int ee){DataPacket[0] = command;DataPacket[1] = aa >> 8;DataPacket[2] = aa & 255;DataPacket[3] = bb >> 8;DataPacket[4] = bb & 255;DataPacket[5] = cc >> 8;DataPacket[6] = cc & 255;DataPacket[7] = dd >> 8;DataPacket[8] = dd & 255;DataPacket[9] = ee >> 8;DataPacket[10]= ee & 255;

    }

    void sendDataPacket(){Wire.beginTransmission(SlaveDeviceId);Wire.write(DataPacket, 11);delay(10);

    }

    int receiveResponse(){int receivedValue;int available = Wire.requestFrom(SlaveDeviceId, (byte)2);if(available == 2){

    receivedValue = Wire.read()

  • Top

    7 CommentsJuly 17, 2014 by Ignas Gramba in Blog

    13141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061

    Wire.onReceive(receiveDataPacket); // register talk eventWire.onRequest(slavesRespond); // register callback event

    }

    void loop(){}

    void receiveDataPacket(int howMany){// if (howMany != 11) return; // ErrorLastMasterCommand = Wire.read();a = Wire.read()

  • Tagged with: arduino, communication, i2c, iic, master, multiple, multitasking, protocol, sensors, slave, tasks

    7 COMMENTS

    8 http://www.berryjam.eu/2014/07/advanced-arduino-i2c-communication/

  • 9 http://www.berryjam.eu/2014/07/advanced-arduino-i2c-communication/

  • 10 http://www.berryjam.eu/2014/07/advanced-arduino-i2c-communication/

  • Name *

    Email *

    Website

    Comment

    LEAVE A COMMENT

    11 http://www.berryjam.eu/2014/07/advanced-arduino-i2c-communication/

  • 2014 All Rights Reserved.

    12 http://www.berryjam.eu/2014/07/advanced-arduino-i2c-communication/