Return to Robotics Tutorials
Raspberry Pi to ATtiny Communication
When building a robot, it can be extremely useful to combine the application power of a Raspberry Pi with the real-time processing of an Arduino / AVR / ATtiny. This article describes a few methods for sending data reliably between a Raspberry Pi and the ATtiny, which enabled a Raspberry Pi to read an RC receiver's PWM signals.
What is the best processor for my Robot?
Most robots incorporate motor controllers, sensors for guidance, a remote control link and an application controller. In general, microcontrollers such as Arduino / AVR are well suited to the real-time tasks involved in monitoring the sensors or combining them through sensor fusion. On the other hand, a high performance micro-computer such as the Raspberry Pi is ideal for handling high-level operations such as vision processing (eg. OpenCV) and autonomous decision-making algorithms.
As a result, the best "processor" for a complex robot is often a combination! Combining one or more Arduino / AVR microcontrollers with a Raspberry Pi offers the best of both worlds to a robot implementation.
Communicating between Arduino / ATtiny and Raspberry Pi
In the design of C2Bot, I wanted to use a Remote Control (RC) transmitter to drive the robot. This meant reading 6 or more PWM (or PPM) channels from a 9-channel RC receiver (Turnigy 9X). Given the tight timing required, it would be unreasonable to expect that the RPi could sample and read the receiver itself. Instead, I used a $3 ATtiny167 board (Digispark Pro) to measure the RC PWM signals and provide a SPI interface for the Raspberry Pi to read.
There are a number of protocols that can be used to communicate between microcontrollers. Since pins are at a premium and the total throughput demands are often low, serial protocols are quite popular. Common serial protocols include:
- I2C / IIC / TWI - Inter-Integrated Circuit / Two-Wire Interface
Two pins: SDA, SCL
Synchronous protocol. Pin-efficient interface that is suitable for connecting to a large number of slaves. First byte from master provides 7-bit device address and read/write indicator. Serial data (SDA) is a bidirectional signal, changing direction between writes and reads. An acknowledge indicator is sent from the slave to the master after every byte. Pullup resistors are required.
- SPI - Serial Peripheral Interface
Four pins: SCLK, MOSI, MISO, SS#
Synchronous protocol. Requires individual slave select (SS) or chip select (CS / CE) wires from master to each slave. No built-in device addressing as the individual slave select lines are used instead. Master provides the clock towards the slaves. Data transfers are full-duplex, making this a good interface for high-throughput applications.
- UART / RS232 - Universal Asynchronous Receiver Transmitter
Two pins: TX, RX (without flow control)
Four pins: TX, RX, CTS, RTS (with control)
As an asynchronous protocol, there is no clock signal between devices. Instead, both devices must be configured with the same baud rate (eg. 57600bps) and use a start pulse to maintain synchronization.
Bit-banging vs Hardware SPI / I2C
With serial protocols, one or more external device pins must toggle for each bit that needs to be sent. It is possible to write a program that toggles a set of GPIO pins for every bit that is sent -- this is called bit-banging. At relatively low data rates, bit-banging can be feasible. However, at higher speeds it quickly consumes the processor's resources, often making it impossible.
Thankfully, the Raspberry Pi has built-in hardware (in the BRCM chipset) to handle the serial interfaces directly. The controller provides methods that usually support byte-oriented access to the toggling bit-streams. That means that your program only needs to "wake up" and get involved in the data transfer once per byte instead of once per bit. In some cases, an instruction to the controller can include an array of bytes, which relieves the processor and enables it to focus on other high-level tasks.
So, why would we ever bit-bang a serial protocol? Usually this is done because the CPU doesn't provide the interface hardware natively or you want to place the serial interface on alternate pins. However, it is also used to work around bugs that may exist in the hardware design (such as the Raspberry Pi's I2C clock-stretching bug).
Data corruption when reading from Slave
It is very easy to implement an I2C slave or SPI slave on the ATtiny and have the RPi read it. However, I quickly discovered that my PWM signal readings were occasionally marred by disastrous glitches on the read data back from the ATtiny. Since this read data is used to power a set of high-current motor drivers on the robot, the robot would lurch at full throttle once in a while!
How do we prevent serial data errors / corruption between the ATtiny and the Raspberry Pi? One could try averaging the read data or discarding obviously-bad data. You could also layer an additional protocol over top of SPI or I2C that provides a checksum / CRC so that the slave doesn't mis-interpret the master's command and the master doesn't use any read data from the slave that is flagged as errored. One may also adjust the serial data rates until you don't see errors anymore...
However, most of the above approaches don't actually solve the underlying problem!
After significant analysis, I now have error-free transmissions between an ATtiny and the Raspberry Pi. Using a logic analyzer / oscilloscope and careful review of the ISRs in the ATtiny, I was able to identify the source of the occasional glitches and find ways to avoid them. Read the page on Raspberry Pi to SPI slave for details.
Connecting Raspberry Pi to Arduino with SPI
See how to design a reliable SPI slave for communicating with a Raspberry Pi
Connecting Raspberry Pi to Arduino with I2C
Link coming soon...Visits!