Hi

We had a lot of fun yesterday working together on MRLComm and Arduino. Four people ( Grog, kwatters, camality and me ) working on the same sourcecode, discussing different options and ways to make the software better and add some more functionallity.

Sometimes we think in different ways, and sometimes its difficult to explain in a short shoutbox. So creating a post to describe something that is a bit larger is necessary. So thats what I'm doing now to explain the way I think about the i2c interface and the device drivers. Any comments are welcome. 

The nice thing with i2c is that many different devices with very different properties can be used, using the same protocol. Each device has it's own address, so up to 62 different devices can be connected to the same 2 wires. The i2c protocol is a serial protocol and it defines how you address, read and write to a device. Each device defines how the rest of the data should be used. 

Examples of i2c devices in MRL are

Adafruit16CServoDriver https://learn.adafruit.com/16-channel-pwm-servo-driver

AdafruitIna219 https://www.adafruit.com/product/904

Mpu6050 http://playground.arduino.cc/Main/MPU-6050

I2CMux https://learn.adafruit.com/adafruit-tca9548a-1-to-8-i2c-multiplexer-bre…

We have three hardwares that has i2c pins. The Arduino, the Raspberry PI and Esp8266. I will leave Esp8266 out of the discussion for now.  

To make it possible to connect things together in a nice way so that each device can be used on either of the hardwares, a I2CControl interface has been defined.

It's a very small set of methods that corresponds well to file IO. You can think of the methods as open, close, read and write and a combined write/read. ( createDevice, releaseDevice, i2cWrite, i2cRead and i2cWriteRead). All the i2c devices use those methods. The RasPi service implements them and I'm working on implementing them in Arduino.

The structure for the devicedrivers is simple. You connect to a service that implements the I2CControl interface. Then you write or read to the physical i2c device based on the device specifications.

When you write, it's simple. The program continues to execute the next line of code when the data has been written.

For the read it's also easy. You read and get the requested data back so that you can continue to process it. That's how it works now with the RasPi service and that's how I hope that it can be implemented in Arduino. 

But how does that fit with the publish/subscribe pattern ? Imho, each device driver should publish a set of data that is highly device dependant. The Arduino service should not publish "generic" ic2 data. 

The way I think about it now, the Arduino service should read the serial data from MRLComm, and if it's i2c data that gets read, it should handle to "return" that data back to the caller ( device driver ) instead of using callback methods. Then it would work the same way as on the RasPi. 

That pattern has some implications. The device drivers need to execute on the same host as the service that implements the I2CControl interface. I'm not sure if that's a big problem, since all all i2c device driver services still can communicate to remote services. ( I need to test that, but it's the intention ).

The alternative is that each devicedriver has a callback method, and that Arduino / Raspi keeps track off for  each i2c device, to know what service to send the callback to. I think that was the suggestion from yesterday. 

That's a bit more complex, so I want to understand the pros and cons of  the different alternatives better. 

/Mats

GroG

7 years 10 months ago

Mat !  .. heh it was fun .. I've done Agile development before with 2 people at the keyboard, but 4 people modifying code simultaneously was a totally different experience. Was exciting especially when we have very similar end goals but the details we express differently - poetry !

Here are some very general statements, which I think hold true in most situations :

I don't have the answer, and can't say all the pros & cons without learning more about Arduino's Wire I2C methods...

Generally:

  • MRLComm must not block
  • Arduino acts as a relay for I2C writes & reads - knowing nothing about the devices data
  • MRL sends nearly ALL data back with PUBLISH_SENSOR_DATA - its a way to simplify the data pipe from MRLComm to Arduino.  Once it gets back to Arduino.java Arduino.java gives it back to the device which registered for it in the attach - identified by deviceIndex.  Here is where its demuxed.  To say wether its demuxed further in Arduino, or left to the device which registered it is a balance of providing convienence over the device service creator vs maintenance of Arduino. I definately think we should leave the I2C address demuxing with the I2CController Service which registered for it.
  • Arduino.java "can" have methods which "emulate" synchronous reads - while MRLComm is working asynchronously - Arduino can also buffer data
  • I usually prefer to asynchronous reads (addListener / callback) - but potentially both synchronous and asynchronous can be supported at the same time.  Serial does this.  heh I have to check this but, it only calls back if a listener has registered itself, and buffers the data to some constant value for synchronous reads

interresting reading 

http://www.gammon.com.au/forum/?id=10896&reply=4#reply4

http://www.gammon.com.au/forum/?id=10896&reply=10#reply10

 

so Wire.requestFrom() is blocking until the transaction with the device ends. If this is a problem, there is other I2C library that can be implemented

Wire.requestFrom() can return either 0 (transaction failed) or the number of bytes request

Wire.available() always return the number of bytes request, not the number of bytes send by the device. If the device send less bytes than request, it gets padded with 0xFF. If the device send more, the extra bytes are juste discarded