2

I am trying to format data from several Arduino-based systems so my Rpi3 can display it as a web page. But the Rpi3 seems to only work as an I2C master, and the Arduinos have little memory in which to store data.

The sensors include weather and a solar panel sun tracker. Arduino has no problem sending out the data to other Arduinos. The only device on my I2C bus with the memory to do the data logging is the Rpi3.

It looks like the alternative is to do some kind of polling but then I am faced with the question of how often to poll each device, etc. And the Arduinos then would have to spend much more time looking for the requests. My Arduino programs utilize specific delay() times (like when they are moving the solar panels) so interrupts would be a problem in maintaining proper timing.

If there is a solution whereby I can have the Rpi3 waiting for data to come in, then I can handle the programming to do the rest.

So my question is: Is there a good way to set up the Rpi3 to wait for the data and process it as it comes in?

  • I am currently adding I2C slave support to my pigpio library. How much data per message and how often between Arduino and Pi? The input (slave I2C) FIFOs on the Pi are limited to 16 bytes. – joan Oct 27 '16 at 19:18
  • Interesting. I think 16 bytes should be sufficient if I format each message to a single-byte code (wind speed, temperature, panel tilt angle, in-box temperature) then just the data value in BCD. Once I have it in the Rpi3 I can flesh it out for presentation. – SDsolar Oct 27 '16 at 20:31
  • When I first read this I was going to comment that "I presume the UART is occupied with something else" but based on some of your recent answers I am now wondering if it is because you are worried about making it impossible to re-flash the board. If so I think you may want to give that another chance, the UART is probably the simplest means of doing this kind of thing and I think the problem you are worried about impossible to create in a serious way. – goldilocks Oct 28 '16 at 6:45
  • Acknowledged. However, I am heavily invested in an I2C system using long runs of cable and the I2C&Power units, linking all the sensors and Arduinos. Everything is working fine, but now I want to add data logging and display via web browser, and the Rpi3 seems the best way to do it. But it insists on being a master on the bus. So polling could work, but I would rather have the Arduinos doing their work (some of which is timed) and sending data when it is available rather than have them interrupted at random times. The pigpio library might be just what the doctor ordered. Thanks, @joan. – SDsolar Oct 28 '16 at 8:53
  • I ran into the TX/RX problem again yesterday. Just having wires on them, even unplugged from the breadboard, caused problems with updating the program. But I found a great solution in SoftwareSerial which allowed me to move the communications to 10 and 11. Still, I do plan to use I2C to connect to multiple Nanos and Rpi3s. @Joan has a great solution for the multiple Pi problem now. Thanks for your comment. – SDsolar Dec 7 '16 at 18:35
5

pigpio (from V57) now supports the Raspberry Pi acting as a I2C slave device.

The core function is the C bsc_xfer.

The Python wrapper bsc_i2c provides an I2C slave interface.

As a test between a Pi3 and an Arduino Pro Mini I used the following code.

On the Pi3.

#!/usr/bin/env python

import time
import pigpio

I2C_ADDR=9

def i2c(id, tick):
   global pi

   s, b, d = pi.bsc_i2c(I2C_ADDR)

   if b:

      print(d[:-1])

pi = pigpio.pi()

if not pi.connected:
    exit()

# Respond to BSC slave activity

e = pi.event_callback(pigpio.EVENT_BSC, i2c)

pi.bsc_i2c(I2C_ADDR) # Configure BSC as I2C slave

time.sleep(600)

e.cancel()

pi.bsc_i2c(0) # Disable BSC peripheral

pi.stop()

On the Arduino Pro Mini.

// This example code is in the public domain.


#include <Wire.h>

void setup()
{
   Wire.begin(); // join i2c bus as master
}

char str[17];

int x = 0;

void loop()
{
   sprintf(str, "Message %7d\n", x);
   if (++x > 9999999) x=0;

   Wire.beginTransmission(9); // transmit to device #9
   Wire.write(str);           // sends 16 bytes
   Wire.endTransmission();    // stop transmitting

   delay(50);
}

So the Arduino is sending 20 16-byte messages per second (16 is the slave receive FIFO size so it is a sensible maximum message size).

The Python was executing on a Linux laptop networked with the Pi.

Over a 600 second period the Arduino sent 11526 messages. The Python saw 11018 (95.6%).

The missing messages could be detected by the gap between successive message numbers which should always be 1. The actual gap counts were

  count gap
  10855 1
      9 2
     20 3
     95 4
     32 5
      1 6
      3 7
      1 8
      2 9

The performance seems perfectly reasonable to me for a userland solution. Occasionally there will be inconvenient reschedules. I'd expect better figures if the Python was running on the local Pi.

I repeated the test on a local Pi and all the sent messages were received error free (100%).

  • That is excellent. I will definitely use it for the I2C portion of the system. Thank you for the examples, @Joan. – SDsolar Dec 7 '16 at 18:32
2

Is there a good way to set up the Rpi3 to wait for the data and process it as it comes in?

Probably not via I2C [normatively; see joan's answer], although you could try and see what happens if you request information from a slave allowing an indeterminant amount of time for the request to complete, then just implement things on the Arduino such that you assume such a request is always pending (which it could be if this works, and you do the pi side implementation appropriately). It may be that such requests will fail quickly, but that could be leveraged by making requests at intervals and only processing the ones that don't throw an error.

Otherwise you'd have to poll (sucessfully) for information at intervals, but that would lead to this:

the Arduinos then would have to spend much more time looking for the requests. My Arduino programs utilize specific delay() times (like when they are moving the solar panels) so interrupts would be a problem in maintaining proper timing.

Regarding which I'd say if your ISR just sets a volatile boolean indicating a request is ready, then you handle that outside the ISR in your main loop, the ISR is unlikely to take more than a few microseconds to execute, which I doubt is going to create any timing problems for you moving solar panels.

However, doing things the other way around should not be hard if you are determined to avoid any possible interference. When new data is ready, send an interrupt to the Pi using a non I2C pin; the Arduino could then wait for the request, which should be very quick, reply, and continue on.

  • 1
    As I commented to the OP I am adding slave I2C support to my pigpio library, – joan Oct 27 '16 at 19:19
  • Any idea when it will be available? Also, would it be run on an interrupt basis like on the Arduino, or would I need a separate process looping to monitor the inputs continuously? – SDsolar Oct 27 '16 at 20:33
  • The problem with libraries isn't the code, it is in documenting the code, which is what I am currently doing. To cater for the slave I've added a new feature, an event. Code can request to be notified of events. In this case BSC (the I2C slave) activity triggers an event. So the Pi code waits for an event, reads the BSC data, and repeats. As I say the code is done. I just now need to update the pigpio C, Python, pigs, and pigpiod_if2 documentation. How long depends on how much effort/time I feel like spending on it. I'll probably do a new release (V57) before Monday 31st. – joan Oct 27 '16 at 22:09
  • Ah the joys of maintaining public software! Kudos to you though. – goldilocks Oct 27 '16 at 22:20
  • 1
    @Archway V57 is out. The Python bsc_i2c has an example. – joan Oct 30 '16 at 16:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.