Embedded Networking and Communications
Individual Assignment
- design, build, and connect wired or wireless node(s) with network or bus addresses ✅
An address-able network communication protocol
This week I worked with Dhanu to design, build and program boards that could communicate with each other over I²C.
We did the protocol exploration and circuitry design together, and the programming on our own.
The design allows for any number of boards to be connected to a shared medium (2 cables + ground), but for this proof of concept, we used only 2 boards: a primary board that controls the communication, and a peripheral board, that responds to all requests addressed to it.
The I²C Protocol (I-Squared-C)
The protocol of choice was I²C since it supports addressable devices and requires minimal wiring and setup.
Proof of concept
The first step this week was to try to validate our assumptions about this communication protocol before designing the board.
In order to achieve this, we used a couple of Raspberry Pis and two breadboards to set up the pull-up resistors needed on SDA and SCL channels.
During the initial iteration we saw no communication going through (not even garbage or malformed data) until we saw that we forgot the shared GND across both boards.
This was something we knew was needed, but we just forgot during the wiring. The interesting thing we got out of this was that this protocol seems to have some type of error detection built in: If the signal is not correct/clean/within range, the signal is not interpreted, probably treated as noise or discarded.
Design Considerations
There were various design choices we had to make:
- I²C requires pull-up resistors for both communication lines.
- This meant adjusting some previous design to have those.
- I²C requires specific wiring (shared SCL, SDA, GND lines)
- This required investigating the datasheet for both the D11C chip from atmel. We also found very useful the summary and documentation by rodrigo-shiordia it really helped get the certainty we needed, to ensure we picked the correct pins for I²C on this specific microcontroller.
On the SAMD11C the pins for SDA and SCL are pins PA14 and PA15.
This was the gold nugget needed, in order to send the board to the milling machine.
Board Design
Since we could fit both circuits into a single copper board, we milled them both at the same time.
Programming the boards
Iteration 1
The first dipped toe into I²C comms came from this sample code from the official Arduino sample code for the protocol.
This code shows two devices that communicate via I²C. The peripheral board takes up address 8, and the primary coordinator board tries to communicate with that address (hard coded).
This works as a starting point, although the first time we tried to get them to interact, we forgot to join both grounds.
Below you can see the exact moment where communication started (right after connecting the second GND pin)
Iteration 2
Here is where we began programming a simple hide and seek game.
Iteration 2 takes over from the previous one, but I wanted the primary board to be able to search multiple addresses in a short period of time. The equivalent of a network scanner/sniffer, but for I²C instead of Ethernet/TCP/UCP/IP.
The purpose of this feature is that my board will have to find/detect the address where Dhanu’s board is hiding behind.
It features a small random address selector
int pickARandomAddress() {
int busMinAddress = 1;
int busMaxAddress = 20;
return random(busMinAddress, busMaxAddress);
}
and a buffer flusher
void flushReadBuffer(){
while (Wire.available()) {
char c = Wire.read();
}
}
For the pickARandomAddress()
function, the address range (1 to 20) had to be pre-agreed with Dhanu so that both boards
would be playing in the same address space.
The purpose of the flushReadBuffer()
is to consume any leftover data that might be left in the inbound buffer to avoid
false positives in subsequent iterations/scans. Since the board scans various addresses in each sweep, we have to flush
this after every successful detection.
See the full code for iteration 2: seeker.ino and hider.ino
Iteration 3
Most of the improvements in code were on the Hider side. Read Dhanu’s explanation for more information.
Iteration 3 provided small changes to the seeker:
- a larger address space,
- faster scans,
- and shorter delays.
I was pleased to see that 10ms was more than enough to wait for responses.
The I²C protocol supports speeds of up to 100 kbit/s in standard mode and the 400 kbit/s fast mode. Plenty of bandwidth left for our needs, who transmit 6 bytes at a time. The protocol latency was minimal and did not prove to be a cause for concern.
The working game
This is the setup of the boards, so they can talk over a shared medium.
And this is the recording of various rounds of the game. As you can see, some rounds, the seeker detects the target board multiple times, because the address selection algorithm is pseudo-random and simple.
Closing thoughts
Despite this being only a simple example, where we utilise a hide-and-seek game as a proof of concept (addressing various devices through their address on a shared bus, instead of through the physical connection), the general aim for electronics communications is to make our devices discoverable by the other peers, not hard to find.
This exercise in futility is further aggravated by the fact that the target devices require physical cabling (and a short one, at that) to enable communication between devices.
Readers of this documentation and sample code provided are strongly advised against using the concepts we explored here for legit corporate espionage or honest international cybercrime. Your target will find your device, guaranteed! Watch out!
Assets
- Board Design
- Iteration 1 - Simple Comms / Sample code from Arduino
- controller.ino - This controller connects to a known target and requests data at regular intervals
- peripheral.ino - This peripheral awaits requests from the controller and responds, when interrogated
- Iteration 2 - Hide and Seek with Smart seeker and dumb Hider
- seeker.ino - This seeker can randomly look for a target device at random addresses, and report successful findings
- hider.ino - This hider is a still target that never changes address. It is the same code as iteration 1
- Iteration 3 - Hide and seek game (with smart hider and smart seeker)
- seeker.ino - This controller connects to a known target and requests data at regular intervals. It’s the same as the previous one, but it performs faster searches with shorter delays, across a larger search space (50 addresses instead of 20)
- hider.ino - This hider is smarter than the previous iteration. It hides itself randomly into a new address every 2 seconds, making detection a bit more difficult and giving meaning to the seeker’s job and existence.
Future iterations: We could easily make the hider change places the first time it is detected, this would avoid repeated detection during the same scan/run.