Arduino Project Hub

Arduino I2C Multi-Master Approach - Why and How © GPL3+

The Arduino is simple, cheap and power efficient but has limitations. Sometimes, you may want to have more than one access your I2C bus.

  • 3,976 views
  • 4 comments
  • 5 respects

Components and supplies

About this project

Micro-Controller Limitations

I have been building "connected sensors" for a couple years but focused initially on the "sensor" part and only recently moved to "connected".   Each time I explore a new technology for connectivity - WiFi, Cellular, Bluetooth, LoRa, etc. - I run into some of the limitations of Arduino particularly its limited working memory. These issues stem from two sources when I start to connect my sensors: 1) Working with long variables or keys and 2) String manipulation such as parsing a 64-bit GSMloc string to extract time or location from a nearby cell tower.

Before arriving at this multi-master solution, I had tried all the usual suspects: using non-Atmel varieties such as the Teensy, moving static variables into program memory and using the F( ) argument in print statements. I have also started putting TI's FRAM chips on my boards as I don't like the limited life of Arduino's EEPROM (100,000 writes).

This article describes another approach, using more than one microcontroller to manage the functions of my board. These microcontrollers would share access to and communicate over a common I2C bus. I wanted to use the standard Arduino Wire Library, and I was encouraged to see this library claimed multi-master support. However, there is a big catch as described by wayneft in this thread on Arduino.cc:  Arduino's multi-master support can arbitrate between masters vying to take initial control or the bus, but it can mistakenly release the bus before a master is done with it. This can lead to the bus and your programs locking up.

Avoiding Collisions and Race Conditions

I developed a simple solution to this problem that, along with the multi-master support that Wire does provide, allows for robust shared control of the I2C bus. I added lines to connect two sets of I/O pins on my microcontrollers: Busy and Clock. The Clock line is the 32kbs square wave that is output by my real time clock. Both the Busy and Clock lines are pulled high using 4.7k ohm pull-up resistors. This allows us to arbitrate control of the bus as each microcontroller needs to "claim" the bus using the Busy line and the Clock line ensures that they don't both claim the bus at the same moment.

Here is how it works with sample code snippets provided below:

  • If the Arduino wants to read or write to the I2C bus it first needs to check if the Busy line is High (this indicates that the bus is currently free). Then, it needs to check the Clock line and can only pull the Busy line Low when the Clock line is Low
  • If the Simblee wants to read or write to the I2C bus it first needs to check if the Busy line is High (this indicates that the bus is currently free). Then, it needs to check the Clock line and can only pull the Busy line Low when the Clock line is High.
  • If the Simblee or Arduino want access to the bus and finds it Low, they need to wait.
  • I implemented this in code by creating two functions: TakeTheBus(); and GiveUpTheBus();.  Each returns a boolean so you can write your code to wait for a successful attempt before proceeding.

This type of communications, where a pull-up resistor holds the line high and the only time a microcontroller "writes" to the bus is to pull it low, should prevent situations which would damage either of the processors.

Compared to SPI

You may be saying - "I2C is also known as the Two Wire Interface. This approach uses four, that is no better than SPI!".  True, but consider these points:

  • I2C sensors are generally more common and cheaper
  • the four wire requirement is only between the two masters
  • I2C sensors can communicate over a longer distance from your board

With this approach, you can effectively split the work between the two microcontrollers.  I had the Arduino focused on servicing interrupts from the sensors and logging data to the FRAM. The Simblee was able to read the data from that FRAM and serve it to the application on the phone using Bluetooth LE.  Each sketch became shorter and easier to debug and the pressures on the Arduino's memory were reduced.  I plan to document the board design in a subsequent post.

I hope you found this article helpful.

Schematics

High-Level Schematic
system_diagram_8uX3BtLLci.graffle
System Diagram

Code

Sample Code for Master-Master ArbitrationC/C++
These functions make sure that one or the other microcontroller but not both can control the i2c bus.
// Example of how to use the functions
unsigned long FRAMread32(unsigned long address)
{  
  long four;
  long three;
  long two;
  long one;
  if(TakeTheBus()) {  // Request exclusive access to the bus 
    //Read the 4 bytes from memory.
    four = fram.read8(address);
    three = fram.read8(address + 1);
    two = fram.read8(address + 2);
    one = fram.read8(address + 3);
  }
  GiveUpTheBus();// Release exclusive access to the bus
  //Return the recomposed long by using bitshift.
  return ((four << 0) & 0xFF) + ((three << 8) & 0xFFFF) + ((two << 16) & 0xFFFFFF) + ((one << 24) & 0xFFFFFFFF);
}



// Function that takes control of the i2c bus
boolean TakeTheBus() 
{
  int timeout = 10000;  // We will wait ten seconds then give up
  unsigned long startListening = millis();
  //Serial.println("Simblee has the Bus");
  while(digitalRead(SQWPin)) {} // The Simblee will wait until the SQW pin goes low
  while (!digitalRead(TalkPin))  { // Only proceed once the TalkPin is high or we timeout
    if (millis() >= timeout + startListening) return 0;  // timed out
  }
  pinMode(TalkPin,OUTPUT);  // Change to output
  digitalWrite(TalkPin,LOW);  // Claim the bus
  
  return 1;           // We have it
}

// Function that gives up control of the i2c bus
boolean GiveUpTheBus() 
{
  
  pinMode(TalkPin,INPUT);  // Start listening again
  //Serial.println("Simblee gave up the Bus");
  
  return 1;
}

Comments

Similar projects you might like

3D-Printed Prank Vibrating Cup

Project tutorial by Alex Wulff

  • 1,291 views
  • 2 comments
  • 14 respects

The Magic Display With Arduino - The Mind Reader

Project tutorial by LAGSILVA

  • 1,513 views
  • 2 comments
  • 6 respects

Very First Hands On Arduino - Voice Activated LED

Project tutorial by Liren Yeo

  • 771 views
  • 1 comment
  • 9 respects

Get Connected! WiFi Registration Using AP Mode!

Project tutorial by Brian Carbonette

  • 1,447 views
  • 0 comments
  • 16 respects

The Soldering Iron Controller for Hakko T12 Tips

Project tutorial by Alexander

  • 8,672 views
  • 4 comments
  • 26 respects

Voltmeter Using Arduino

by Team Next Tech Lab

  • 3,175 views
  • 8 comments
  • 44 respects
Add projectSign up / Login