firmata / arduino

Firmata firmware for Arduino
GNU Lesser General Public License v2.1
1.54k stars 514 forks source link

Request : Firmata/Arduino as I2C Slave #249

Closed coreyfro closed 8 years ago

coreyfro commented 8 years ago

I would like to request a feature of making an Arduino flashed with Firmata an I2C slave. As embedded Linux systems get cheaper and easier, features such as I2C become more attractive since UARTs on the system or over USB start to become expensive resources in regards to space, power, complexity, and actual cost.

It also seems like I2C would lend itself well to expanding GPIO nearly infinitely, and since I2C on a Rapberry Pi, Beaglebone/board, and other "flavors of the month" systems are available to the user, using an Arduino as a Master in these applications may not make sense, thus freeing up I2C channels on the arduino to make it a slave..

In my searches across the web, I have found that this is an often requested feature, but it seems no one is asking in the right places. To add to confusion, there is a legacy "I2CFirmata" sketch that some people are referring to as a solution to this problem, which is potentially dangerous, since there really isn't any documentation suggesting otherwise:

On the Mega side, one possible solution is to run Firmata which acts like Spark Tinker. There is an I2C version "example" in the Arduino 1.55r2 libraries I looked at. This would allow you to send the types of simple commands you are looking for. What's nice with Firmata is it would make your Arduinos appear "generic" to your Sparks, providing a consistent and simple command structure. Let me know if you need any help.

I am, presently, creating an Opensource IoT class in SF (free, modulo kits and rent) and I am already using Firmata over the Pi's UART to allow students to leverage GPIO on an arduino from the comfort of python. The only trouble is, this UART will be used as a Serial console and I'd like to keep it this way since I want to students to have a fall back from SSH (I anticipate there will me much bricking! ;-)

While I could demonstrate "roll your own" I2C to GPIO 'sketch', I am trying not to dilute my course with things such as the Arduino language. I want to K.I.S.S., down to the holy trinity of "Linux, breadboard, and python."

This seems like a this will be a powerful feature to add to firmata given the changing langscape of DIY embedded electronics.

Thanks!

soundanalogous commented 8 years ago

One limitation here is if an Arduino is used as an i2c slave, you won't be able to attach i2c devices to that Arduino unless it's an Arduino Due or other board with 2 or more I2C ports. I can add this transport at some point regardless but wanted to point that out.

soundanalogous commented 8 years ago

Also which Python Firmata client are you using? It will need to be updated to enable communication over i2c rather than serial. I imagine this will take some coordination.

coreyfro commented 8 years ago

For my personal application, that is fine. I am teaching them how to integrate components to the Raspberry Pi, and I am using the Arduino as an ADC.

In addition, I am allowing the students to opt to use the "ADS1015 12-Bit 4 Channel ADC" (https://www.adafruit.com/products/1083) BUT I am teaching the students Firmata from their Laptops in a Primer Class for the IoT class. And, hats off to you guys, I really like the way Firmata works. So I am erring to the side of Firmata just so I don't have to dilute the lesson further. Firmata is what I am teaching, the Adafruit ADC is optional.

Here's my Pyfirmata:

~$ pip show pyfirmata

Name: pyFirmata Version: 1.0.3 Location: /home/coreyfro/.local/lib/python2.7/site-packages Requires: pyserial

soundanalogous commented 8 years ago

I think the biggest challenge is that the I2C master must request data and I don't think (someone please correct me if I'm wrong) that a Slave can send data without a request from the master. This is a problem in reporting changes in digital inputs and analog inputs. In order for this to work (from what I understand) is the master would have to constantly poll the slave for changes in digital input or analog input. That is a lot of activity on the bus, considering it also includes the master writing digital and analog output. It would probably require buffering digital input changes and possibly even analog input changes depending on a realistic polling interval. This is going to be a big change and one I may not have time to tackle for a while as WiFi and BLE transports are higher priority for me at the moment. However if anyone wants to step in and take the lead on this that would be helpful in getting something started sooner than later.

rwaldron commented 8 years ago

To use the Arduino as an I2C slave, eg. an ADC similar to the ADS1015, would require significant and non-trivial additions to Firmata. I'd actually be willing to write and maintain firmware for this, just to avoid the added complexity in Firmata.

rwaldron commented 8 years ago

Oops, I was reading and writing on my mobile and didn't see your reply until after I posted.

rwaldron commented 8 years ago

W/r to the master making requests from the slave: that's generally how I2C interfaces are designed, because the slave can't know what register the master wants to read from unless the master tells it in a request.

Your concerns are the same I was thinking of when I wrote "significant and non-trivial changes"—I'm mobile, so less inclined to elaborate ;)

rwaldron commented 8 years ago

My offer to write and maintain a firmware still stands—it would be super useful to me as well.

soundanalogous commented 8 years ago

The idea here is to be able to use I2C instead of Serial (UART) as the communication transport between the board and Firmata client application.

coreyfro commented 8 years ago

This is a wealth of information!

Indeed, we would be polling the slave with requests.

My question is, is this process fundamentally incompatible with firmata or just unnecessarily expensive?

If I think of it this way, with either solution, Firmata or ADS1015, the process from the master should be the same, right? Either way, we're polling the slave for data, so to the master, no solution is fundamentally better? Or would there be the issue of initializing firmata with every request?

Is streaming the data fundamental to firmata, or could there be a firmata that is syntactically identical but the workings of the protocol would different, so to the user, apart from expectation of performance, the differences would be invisible?

soundanalogous commented 8 years ago

I imagine a near complete rewrite at the communication layer would be required for any Firmata client libraries to adapt an i2c transport. You would have to know how many bytes to request in each case. That is currently unnecessary when using UART since START_SYSEX and END_SYSEX mark the beginning and end of the data so the client never needs to care about number of bytes. With I2C however you would so that would be a major change.

A good test would be take StandardFirmata and strip out all of the existing I2C code (it could be added back later via compiler conditional to include if a board has a second I2C port - use Wire1). Then add Wire.onReceive and Wire.onRequest callbacks in setup(). Move these lines to the onReceive callback. For onRequest you'd need 2 or more "registers" since you'd probably want to poll for changes in digital input and analog input at different rates (digital input much more frequently). In the onRequest callback you'd move checkDigitalInputs() to be called for one register and you'd move this block to be called when the other 'register' is requested. At that point the loop() function should be empty and the Wire callbacks will do all of the work. You'd remove all of the currentMillis / previousMillis code since the client application would control the polling rate.

You'd initiate the connection with something like this (around line 744):

Wire.begin(ADDRESS);
Firmata.begin(Wire);

and remove these lines.

I think that's enough to get something to work on the Firmata side. The 'register' values for digital input vs analog input polling would need to be formalized.

Then the majority of the work is in on the client side to switch from UART to I2C based communication. On the client side you may need to know the number of bytes to request. You'd have to count the number of bytes returned and in many cases this will vary by board capabilities.

soundanalogous commented 8 years ago

Actually it would require additional 'registers' you'd probably want to poll the value of each analog pin separately, or poll all analog pin that have reporting set to "enabled" and expect the 3 bytes returned for each pin (byte 0: pin number, byte 1 & 2: value) This would require a new sub protocol for Firmata to document these data layer changes).

soundanalogous commented 8 years ago

Ignore last comment. It's incorrect. Number of bytes returned each time analog is polled would be: number of analog pins enabled * number of bytes returned by Firmata.sendAnalog. That value could be known on the client side if you are keeping track of how many analog pins are enabled. For reading digital input it may be more challenging since currently only changes are reported. So if a single pin in an 8 pin port was changed, that entire port will be returned (1 byte per port). However you won't know how many (if any) bytes will be returned. I guess you could expect the maximum (number of ports board has * number of bytes returned for each port) and if you get less then you get less but at least you requested the max number of bytes.

soundanalogous commented 8 years ago

Also if you're only looking to use Firmata as a way to add an ADC to a RPi then this route is overkill.

rwaldron commented 8 years ago

@soundanalogous

to be able to use I2C instead of Serial (UART) as the communication transport between the board and Firmata client application.

Sure, but like you said in your last comment, that's overkill for using Arduino as an ADC IO expander, where an ADS1015 could be used as an alternative—which is the firmware I'm willing to write and maintain (because it's relevant to my needs as well).

soundanalogous commented 8 years ago

@rwaldron are you talking about writing firmware to use an Arduino board as an I2C ADC expander (so as an I2C ADC backpack essentially) in which case Firmata is not needed at all, but rather the work is in defining the I2C interface so it can be used in place of a dedicated controller (like the ADS1015)?

fivdi commented 8 years ago

I think the biggest challenge is that the I2C master must request data and I don't think (someone please correct me if I'm wrong) that a Slave can send data without a request from the master. This is a problem in reporting changes in digital inputs and analog inputs. In order for this to work (from what I understand) is the master would have to constantly poll the slave for changes in digital input or analog input. That is a lot of activity on the bus, considering it also includes the master writing digital and analog output. It would probably require buffering digital input changes and possibly even analog input changes depending on a realistic polling interval.

To the best of my knowledge the assumption that the master must request data and that a slave can't send data without a request from the master is correct. However, many I2C devices can also use interrupt lines to inform the master that they need attention. For example, the ADXL345 accelerometer has two interrupt lines (INT1 and INT2.) A similar technique could be used here in order to avoid unnecessary polling. Each slave connected to the I2C bus could also have a dedicated interrupt line. These could be connected to digital inputs on the Raspberry Pi. In reality most people will only want to connect a small number of slaves to the Raspberry Pi, BeagleBone Black, or whatever, so only a small number of digital inputs will be needed. Whenever the slave wants to send information to the master, it would interrupt the master to tell it that it needs attention.

It's possible to detect hardware interrupts on Linux boards using the Linux epoll API. Despite being called epoll, polling isn't involved :). There's also a Node.js addon called epoll which can be used to detect hardware interrupts in JavaScript. It can detect several thousand interrupts per second on a Raspberry Pi. I don't know if there's something similar in Python.

What will be challenging is implementing code that can communicate with a Raspberry Pi at high baud rates. There's a hardware bug in the BCM2835 and BCM2836 processors used by the Raspberry Pi 1 & 2 that prevent them from supporting I2C clock stretching.

Earlier on this year I experimented a bit with Linux boards, AVRs, and I2C. The results can be seen here.

rwaldron commented 8 years ago

@fivdi this is really neat :)

@soundanalogous

are you talking about writing firmware to use an Arduino board as an I2C ADC expander (so as an I2C ADC backpack essentially) in which case Firmata is not needed at all, but rather the work is in defining the I2C interface so it can be used in place of a dedicated controller (like the ADS1015)?

Yes sir. I wanted to see what I could come up with, because I think this will be useful to Johnny-Five, regardless of whether or not it's accepted as an alternative solution here. https://github.com/rwaldron/arduino-adc-io-expander

soundanalogous commented 8 years ago

Nice! That is definitely a better approach than trying use Firmata. I like @fivdi's approach as well.

soundanalogous commented 8 years ago

closing this for now as I don't think there is a strong use case for Firmata over i2c yet given all of the other available transports (Serial, BLE, TCP/IP)

rwaldron commented 8 years ago

👍