SOLVED: bidirectional i2c or other shared data solution?

I have been googling diligently for a couple of days trying to figure this out, and if anyone has already done it and has code I can steal, that would be a big time-saver ... the focus of my project is not i2c development, I just thought I would use i2c as a comm bus between duinos.

I have a need (and googling tells me I am not the first to face this challenge) for a master and slave duino to share some data, with the master writing some elements of a struct and the slave writing others. (It is just fine for the master to control the timing, i.e. write to the slave and request from the slave. There is no need for a "2 master" bus which I gather is complicated and not robust.)

I used the EasyTransfer library initially, when the communication was 1-way, and it works like a charm! Super easy to set up and use. But it seems to be unidirectional, i.e. master sends, slave receives. I have found breadcrumbs here and there indicating that people have tried to modify it to be bidirectional, but never found an actual downloadable file for a 2-way version.

Unf my C programming skills are not quite up to modifying ET myself; I looked at the source code and immediately saw syntax I didn't recognise -- scary! I'd rather not get into a big side project of learning more advanced C and modifying this (excellent!) library, so I was hoping that someone else had already done so & would be willing to share.

What I was hoping for was basically EasyTransfer with one extra command (on the master side): ET.request(SlaveAddress), with a respond-to-request ISR on the slave side that does an ET.send of the shared data.

Alternatively if there is a hardware way to do this -- if there is maybe an IC that can be used as a "drop box" between two duinos so that each can write values to it and read from it, using a shared struct definition, I'd be interested in that option also.

"an IC that can be used as a "drop box" between two duinos so that each can write values to it and read from it,"

That would be a FIFO. Or a shared SRAM. FIFOs seem to become hard to come by. If you have SRAM with I2C interface, and buffer the control lines from each processor, then uC1 could pull an input low to let uC2 know data was being written in. uC2 could then pull the same pin low while reading to let uC1 know not to start writing again. Use an I2C mux chip to provide uC to uC isolation.

Or maybe isolation is not even needed, if each uC is programmed as a I2C master so it doesn't respond as a slave.

SPI would make for faster transfers, but then isolation of SCK, MOSI, and CS would definitely be needed. Simple 74HC125 could provide that - outputs only active when a uC's CS was active.

My problem is solved, thanks to Nick Gammon.

Nick Gammon's I2C page

Nick's excellent write-up plus his library "I2C_Anything" enabled me to do exactly what I want:

In Master's main loop, Master sends a struct of N typed values to Slave. After a decent delay (a couple of millis), Master requests the same struct back from Slave. Slave's receiver ISR pulls the whole struct off the I2C bus and copies all values but one into its own variable space; Slave's request ISR overwrites that one uncopied element of struct and sends the whole packet back; Master writes all elements but one before sending to Slave, and copies the Slave-written element to its own variable space after receiving from Slave.

The timing is entirely driven by Master (no complications of multi-master bus, CSMA hacks, etc). And Nick's handy syntax allows me to deal with my shared data in a rational form (struct of N typed variables) rather than going through a lot of byte conversion hell, sending digits as chars, etc. as I have seen in some other online tutorials about I2C between duinos.

I think Slave could safely re-write elements of struct before handing it back to Master, but I didn't need to do that; my data all fit within the byte limit for a single Wire.write(). I also suspect that the copy to one's own variable space at each end is not necessary; but I'm lazy and it saved me some tedious find/replace.

Bottom line -- it works (so far) flawlessly! Nick's sample code is far more readable than my messy WIP so I refer you to his site for details :-)

I am very pleased to solve this problem, as it allows me to do much-needed load balancing by farming out selected functions to slave duinos, while still being able to get sensor data (easily and simply) back from them to the ringmaster duino running the i2c bus. The slaves can concentrate on rapid and continuous reading of various sensors and switches, while the ringmaster handles all the picayune user interface updating (rafts of buttons and LEDs) & makes decisions based on the received data.

Tazling -

Great to see someone else with a similar Arduino to Arduino issue. I've achieved the same thing using SPI if you're interested in a code snippet or two -SPI example - note I've identified an issue when the interrupt driven 'havedata' is used in a tight loop.

Tazling -

know this is an old post, but im hoping you will still read this...

can you post the code that you write in the end - it sounds like you use a struct to request something from the slave, and the slave sends the additional piece of data back...

this is exactly what i need to do.

can you help ?

I pieced together examples from Nick Gammon's I2C page and Arduino Tutorial for MasterReader to create some example code for a Master to request data from a Slave as well as the Master to send data to a Slave. I hope it helps others.

i2cSimpleTransfer

@getSurreal, thanks, that will help.

In the Master you have:

    Wire.requestFrom(i2c_sensor_slave, sizeof(slave_data));    // request data from the Slave device the size of our struct

    while (Wire.available()) {
        i2cSimpleRead(slave_data);
    }

The 'while' makes no sense. You can do this:

    Wire.requestFrom(i2c_sensor_slave, sizeof(slave_data));    // request data from the Slave device the size of our struct

    if(Wire.available() == sizeof(slave_data)) {
        i2cSimpleRead(slave_data);
    }

In the slave I would use '==' where you have '>='. Which gnome is sitting on the I2C wires and adding bytes ?

 if (payload == sizeof(master_data)){

(check your '(' and ')' in that line please).

@koepel, thanks for the feedback.

That ‘while’ was straight from the Arduino example, but I’ve updated with your suggestions.