2
\$\begingroup\$

I've been given 12 arduinos (Mega) and 12 breakout boards (custom built. they pump up the output of the digital pins to 12v @120mA) to control around 300 LEDs. I'm using a pseudo PWM library to fade the LEDs.

This could have been achieved with a much, much cheaper setup. Open to suggestions, but time is of the essence.

My quandary is getting all these micro controllers synced. I've never been involved in a project where timing was so significant; I would usually use the TX/RX serial pins to just relay commands out, but I'd sleep better at night if there was some kind of heartbeat from a master to 11 slaves. Is I2C the way to go in this case, or do I look at alternatives?

tl;dr Whats the best way to keep 12 arduinos in sync?

\$\endgroup\$
5
  • 3
    \$\begingroup\$ The question is how tight is timing. What are the LEDs supposed to do? What kind of timing error can you tolerate? Can you elaborate more on the hierarchy of the system? One arduino could control all 12 boards on the same I2C bus, but I2C is rather slow. A single arduino acting as a master generating a pulse to synchronize the rest sounds about right (latching a timer for example). You can even make each arduino find the offset of itself with the master when this occurs and so "calibrate" itself. \$\endgroup\$ Mar 4, 2013 at 19:30
  • 2
    \$\begingroup\$ Do you just need to sync them or send information from master to slave? If you need to sync them, use interrupt pin on slaves and master to trigger that interrupt. \$\endgroup\$
    – Gossamer
    Mar 4, 2013 at 19:43
  • \$\begingroup\$ The timing isn't that tight, I can probably afford a 50ms gap between controllers without noticing a break. There are 3 objects holding an array of RGB LED strips, 4 arduinos to an object. The objects need to play the same program at the same time. The programs are simple cascading effects where the LEDs fade up and down the objects. What I don't want is there to be a time difference when crossing to the next arduino. More a question of reliability than speed. \$\endgroup\$
    – rom
    Mar 4, 2013 at 19:49
  • \$\begingroup\$ So you just want to trigger the same routine on each Arduino at the same time? What is the distance between each Arduino? Will they be arranged in a line or a star type configuration? \$\endgroup\$
    – Oli Glaser
    Mar 5, 2013 at 2:29
  • 1
    \$\begingroup\$ To follow this up- I ended up making a serial hierarchy. The master would interrupt all the controllers at once (I suppose this was the 'heartbeat' I was looking for), then pass a command to 3 leaders, who would forward these on to 2 other slaves. The speed was fast enough not to be noticeable. Ridiculous to use so many micro controllers, I'd have used LED drivers on an I2C line given more time. \$\endgroup\$
    – rom
    Oct 18, 2013 at 21:23

2 Answers 2

4
\$\begingroup\$

Use two pins per chip an input and an output and chain them together serially. Have the "master" chip drive the toggle the pin each 'frame' and all the slaves interrupt on pin change, register the heartbeat event and 'sync', then propagate the pin state to the next chip, and so on... if you want to get fancy, wrap the output of the last chip in the chain back to the master so that the master can determine whether the message was received and what the latency of that response was.

This is a nice, simple approach that doesn't require a lot of software sophistication or much worry about electrical characteristics and signal integrity. There are, obviously, other more complex and feature rich approaches to solving the problem you pose.

\$\endgroup\$
1
\$\begingroup\$

The AVR TWI can be configured to accept a "general call" transfer, which is essentially an I²C write to address 0. All slaves can have an individual slave address and also respond to address 0 (which can be simply ACK'ing SLA+W, for example).

This can be used to sync the slaves. Send data to all slaves, then send SLA+W to the general call address. All slaves will receive this "command" and can start processing data at more or less the same time.

The AVR TWI will generate an interrupt when a stop or repeated start has been generated on the bus. This can only happen when all slaves have released SCL so you can be sure that all slaves that ACKed SLA+W have also processed the command and released SCL.

With this "protocol" you can not be sure that all 11 slaves ACKed the command.

The timing depends on the other code running on your slaves. If no other interrupt is active, it boils down to interrupt latency, which is a few clock cycles (I'm too lazy to look that up now).

I'm not sure if the following works with the hardware TWI: It might be possible to turn the general call into a read transfer. All slaves return 2 bytes and each slave outputs a zero at an individual position. The wired AND functionality of the I²C bus will result in a combined answer from all slaves. The result could look like 1111 1000 0000 0000. The master can check for all the zeros it expected and act accordingly. It might get complicated from here if you want to add an "abort" function that kicks in if not all slaves did their job. I've only tried this once with bit-banged I²C.

One more thing to try instead of a general call read: Mess with the address mask register. The TWI can be configured to accept a single address, but it can also apply an address mask when checking the address. This can be used to turn a non-zero address into a custom general call address that can be used to read data from all slaves at once.

Working this out sounds like a cool project by itself...


And another edit: I've had a look at the datasheet and I've come to the conclusion that the hardware TWI can't be fooled to accept addresses that way. You can try my first suggestion (just send a general call) or write a bit-banged I²C driver that will do whatever you want. Or try something totally different, like vicatcu's suggestion.

\$\endgroup\$
2
  • \$\begingroup\$ General call definitely works on AVR... I'm pretty sure ThingM/BlinkM use it. I think it can only be used to write data to slaves, not read data from slaves. It's viable, but I think overall bus length would be the biggest problem with it. \$\endgroup\$
    – vicatcu
    Mar 5, 2013 at 4:01
  • \$\begingroup\$ Let's see if rom answers the questions in Oli Glaser's comment... \$\endgroup\$
    – Christoph
    Mar 5, 2013 at 8:26

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.