The Arduino was by far the easiest part of the system to setup. Hardware-wise, the Tx
line is connected to the Pi’s RxD
GPIO line. I didn’t have any AND gates to protect the serial line, so I built one using a two NAND gates from a spare CD4011be chip I had:
I used the Arduino libraries for this prototype, so the code is more or less as simple as it can get:
:::c
// The pin used as the Com. Authorisation line.
#define PIN_AUTH 13
void setup() {
Serial.begin(9600);
pinMode(PIN_AUTH, INPUT);
}
void loop() {
// Every second, check if the Auth line is high,
// and if it is, send the message
if(digitalRead(PIN_AUTH) == HIGH) {
Serial.write("Hello, world!");
Serial.write('\n');
}
delay(1000);
}
The setup on the Raspberry Pi was a bit more involved, because I have a Pi 3. On that hardware generation, the main serial interface (uart0
) is used by the bluetooth chip, leaving only the sub-par miniUART
interface for GPIO serial.
After a bit of fiddling, it turns out the best solution for my case is to simply disable the Bluetooth chip and re-map uart0
to the Tx/Rx pins, as explained in this StackOverflow thread. Once this is done, the TTY console must be disabled like on every Pi: otherwise, the boot process outputs debug info on the lines.
Once this is done, any program can access the GPIO Tx/Rx lines by opening the /dev/ttyAMA0
file. The test program loops until some data is available, and displays any received string on the output.
:::c
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <stdint.h>
#include <stddef.h>
typedef uint8_t byte_t;
int main(int argc, const char** argv) {
int stream = -1;
// Open a file descriptor to the serial port
stream = open("/dev/ttyAMA0", O_RDWR | O_NOCTTY);
if(stream < 0) {
fprintf(stderr, "unable to open serial connection");
}
// configure the serial reader
struct termios options;
tcgetattr(stream, &options);
options.c_cflag = B9600 | CS8 | CLOCAL | CREAD;
options.c_iflag = IGNPAR;
options.c_oflag = 0;
options.c_lflag = 0;
tcflush(stream, TCIFLUSH);
tcsetattr(stream, TCSANOW, &options);
// Loop forever and display data whenever it's available
// on the serial line.
byte_t rxBuffer[512];
ssize_t rxLength = 0;
while(1) {
rxLength = read(stream, (void*)rxBuffer, 512);
if(rxLength == -1) { continue; }
rxBuffer[rxLength] = '\0';
printf("%s", rxBuffer);
}
close(stream);
return 0;
}
The serial reader is initialised with the Arduino’s standard parameters: 8-bit words, 1 stop bit, no parity bits, at 9600 bauds. Here, read operations are blocking (the read()
call doesn’t return until data is available) because it was simpler to write. Future prototypes will use non-blocking calls by calling open()
with the O_NDELAY
flag. (documentation)
Overall, the system seems to work, at least with a single payload. This week’s goal will be to try and add multiple payloads to check whether the bus can handle scaling.
After a bit of testing, I found one problem. Since I was controlling the CAU
line manually by connecting as disconnecting the line, a low state meant the pin wasn’t connected to anything. The problem is, any background electrical noise will be enough to change the state of the pin randomly. Two solutions exist:
CS
(chip select, or chip enable), they use a /CS
(not chip select) pin.The next step will be adding more Arduinos to that setup, and possibly trying to convert it to SPI (which already implements a Chip Select system as part of the protocol).