Skip to content

Arduino Raspberry Pi Serial Communication Protocol via USB and C C

NicoHood edited this page May 5, 2016 · 1 revision

Extracted from the old Wordpress blog.

Please have a look at the repository for the most up to date information https://github.com/NicoHood/NicoHoodProtocol https://github.com/NicoHood/SerialProtocol

Update 1.5 in work please be patient. Some Syntax may be broken at the moment, sorry. But great stuff is in work!

Update 1.1 released (check different syntax, new Command function)!

Hi there! The last days i've been working on an easy to use Protocol for Arduino and Raspberry Pi Serial USB connection. The main goal was to get a stable, easy to use communication with a lot of possibilities. And not to use Python. I dont like Python, i never learned that and every Arduino User knows C/C++. I will explain you some general infos, the basic connection and how to use the Protocol. It can also be used for connecting more than one Arduino/Raspberry together.

This Article should tell you every information you need. I know i am lazy at reading too, but you might need all that stuff. So read carefully before asking questions ;) If you really do not want to know how the basic Serial Communication for Arduino/Raspberry works you can skip the part "Time to code".

Some general things about Arduino - Raspberry communication:

You have three options to connect them: I2C, SPI and Serial. Problem with I2C and SPI is, that you need a logic level converter. The Pi uses 3.3V and the Arduino 5V. Connecting 5V to the Pi would destroy it immideatly. One blog said, that you dont need that for I2C but i wouldnt do that without. So the easiest way is Serial via Usb (GPIO also possible but with converter). Just plug in your Arduino, programm it and let the communication begin!

Setting up Arduino + Raspberry Pi:

There are three ways to program your Arduino: With the Raspberry and the Arduino IDE (see instructions below), with an ICSP or FTDI programmer or with your normal PC (see Picture below).

You can install the Arduino IDE and update with the following code. Start the Arduino IDE on your desktop to use it. Do not copy the span tag which appears in some browsers!

[code language="bash"] sudo apt-get install arduino mkdir downloads cd downloads wget http://arduino.googlecode.com/files/arduino-1.0.5-linux32.tgz tar zxvf arduino-1.0.5-linux32.tgz cd arduino-1.0.5 rm -rf hardware/tools sudo cp -ru lib /usr/share/arduino sudo cp -ru libraries /usr/share/arduino sudo cp -ru tools /usr/share/arduino sudo cp -ru hardware /usr/share/arduino sudo cp -ru examples /usr/share/doc/arduino-core sudo cp -ru reference /usr/share/doc/arduino-core [/code]

With the Leonardo/Micro/Mega2560 you can use the USB port to program with your normal PC and the other hardware serials for communicating with the Raspberry. An easy way to do this is to use a FTDI programmer or another Arduino Uno as serial to USB slave. In my case i used a FTDI programmer breakout and an Arduino Pro Micro. The advantage of this is that you can use the USB Serial for debug output (and the Media Keys library as well), program it with your pc and only let the raspberry communicate via the Serial1. Then you can control everything via ssh. Connection below, similar with other Arduinos. A FTDI breakout is easier to use on a breadboard.

Connect TX with RX and RX with TX. Connect the grounds together, do NOT connect VCC. The FTDI is connected to the Raspberry and the Pro Micro to the PC. But as i said you can use a normal Uno as well without that stuff.

/connection_steckplatine.png

You do not need to install the Python-Serial. But you need to install Wiring Pi.

[code language="bash"] sudo apt-get install git-core sudo apt-get update sudo apt-get upgrade cd /tmp && git clone git://git.drogon.net/wiringPi cd wiringPi && ./build gpio -v gpio readall [/code]

And now? I am a lazy guy and i used my PC to program the Arduino as described above. What i wanted to do was to have my program at one place together to edit. Optinally you can add a share to your Arduino folder on your local PC. Rightclick on your windows folder and click share.

[code language="bash"] cd Desktop mkdir Arduino sudo nano /etc/fstab //PCNAME/Arduino /home/pi/Desktop/Arduino cifs username=Nico,password=Hood 0 0 [/code]

Now we installed the IDE, connected our Arduino to the Raspberry, installed WiringPi and set our share up.

Time to code:

Two devices - two programs. We will use C/C++ in this case because most Arduino users should know about that and its easy to use. I will first explain the basic stuff and then how to use the library later. Maybe you want to create something on your own. The code can be found in the library master as well.

The Arduino part should be easy. Send an ASCII text to the serial and read the serial. Keep in mind that i used two different Serials for this with my Pro Micro. You can remove the debug Serial for Uno and change everything to "Serial". Then you can just see the led light up.

[code language="cpp"] /* Arduino_Serial_Test.ino - SerialProtocol library - demo Copyright (c) 2014 NicoHood. All right reserved. Program to test serial communication*/

// indicator led and button int ledPin = 9; int buttonPin=8; unsigned long lastButton=0;

void setup() { // Pin setup pinMode(ledPin, OUTPUT); pinMode(buttonPin, INPUT); digitalWrite(buttonPin, HIGH);

// for Leonardo you can wait for the debug Serial //delay(1000); //while(!Serial); Serial1.begin(9600); Serial1.println("Hello World!"); Serial1.println("My name is Arduino Micro. Via Serial 1.");

Serial.begin(9600); Serial.println("Hello World!"); Serial.println("My name is Arduino Micro. Via Usb."); }

void loop() { // send a ping with a button press if(digitalRead(buttonPin)==LOW && millis()-lastButton>=200){ Serial1.println("Ping!"); // debounce lastButton=millis(); }

// print received signal to debug output if(Serial1.available()){ digitalWrite(ledPin, HIGH); char newChar=Serial1.read(); // print to debug Serial Serial.write(newChar); } digitalWrite(ledPin, LOW); } [/code]

The Raspberry Pi code is not more complicated. You just dont have that easy Arduino environment. No setup and no loop. But i will do something similar for you. This code only sends a ping every 3 seconds because it would be more complicated to add a button to the pin (we dont want to add hardware, thats the Arduino part) or programm a textinput (needs multitasking, didnt looked that up yet). I just left that out here. We will use the Serial library from Wiring Pi. The code has to be compiled with gcc and with the special WiringPi link and the Raspberry definition.

Go to the saved folder and compile with (abort with CTRL+C):

[code language="bash"] cd /home/pi/Desktop/Arduino/Arduino_Serial_Test sudo gcc -o Pi_Serial_Test.o Pi_Serial_Test.cpp -lwiringPi -DRaspberryPi -pedantic -Wall sudo ./Pi_Serial_Test.o [/code]

Save as .cpp

[code language="cpp"] /* Pi_Serial_test.cpp - SerialProtocol library - demo Copyright (c) 2014 NicoHood. All right reserved. Program to test serial communication

Compile with: sudo gcc -o Pi_Serial_Test.o Pi_Serial_Test.cpp -lwiringPi -DRaspberryPi -pedantic -Wall sudo ./Pi_Serial_Test.o */

// just that the Arduino IDE doesnt compile these files. #ifdef RaspberryPi

//include system librarys #include <stdio.h> //for printf #include <stdint.h> //uint8_t definitions #include <stdlib.h> //for exit(int); #include <string.h> //for errno #include <errno.h> //error output

//wiring Pi #include <wiringPi.h> #include <wiringSerial.h>

// Find Serial device on Raspberry with ~ls /dev/tty* // ARDUINO_UNO "/dev/ttyACM0" // FTDI_PROGRAMMER "/dev/ttyUSB0" // HARDWARE_UART "/dev/ttyAMA0" char device[]= "/dev/ttyUSB0"; // filedescriptor int fd; unsigned long baud = 9600; unsigned long time=0;

//prototypes int main(void); void loop(void); void setup(void);

void setup(){

printf("%s \n", "Raspberry Startup!"); fflush(stdout);

//get filedescriptor if ((fd = serialOpen (device, baud)) < 0){ fprintf (stderr, "Unable to open serial device: %s\n", strerror (errno)) ; exit(1); //error }

//setup GPIO in wiringPi mode if (wiringPiSetup () == -1){ fprintf (stdout, "Unable to start wiringPi: %s\n", strerror (errno)) ; exit(1); //error }

}

void loop(){ // Pong every 3 seconds if(millis()-time>=3000){ serialPuts (fd, "Pong!\n"); // you can also write data from 0-255 // 65 is in ASCII 'A' serialPutchar (fd, 65); time=millis(); }

// read signal if(serialDataAvail (fd)){ char newChar = serialGetchar (fd); printf("%c", newChar); fflush(stdout); }

}

// main function for normal c++ programs on Raspberry int main(){ setup(); while(1) loop(); return 0; }

#endif //#ifdef RaspberryPi

[/code]

How to use the library (Syntax):

The library includes the same syntax for each device. You just have to compile it a bit different and setup the Serial yourself. Just have a look at the example, its easy! I will only explain the provided functions and not how to set up everything again because its written above and in the example.

[code language="cpp"] // first you need to setup your Serial for each device. Serial1.begin(9600); int fd =serialOpen ("/dev/ttyUSB0", 9600);

// then you need to pass the Serial/Filedescriptor to the Protocol // you can change this while reading to another Serial to connect // different Arduinos to your Raspberry or connect different devices // to you Arduino Mega for example. The protocol can be used for // Arduino - Arduino communication as well Protocol.setSerial(Serial1); Protocol.setSerial(fd);

//how to read data void loop(){ // if you want to read every input in the buffer use while // this is for important stuff. If the buffer is full // data will get lost. Be aware of that //while(Protocol.read()){ // read exactly one input if available if(Protocol.read()){ // either getAddress or getCommand is >0. //you can use an if here and then a switch for example Protocol.getAddress(); Protocol.getData(); Protocol.getCommand(); } }

// how to send data // do not send 0 as Address/Command this will result in 64/16. // zero is used for the indication of Address or Command // Address from 1-64 uint8_t address = 42; // Data up to 32bit (long) uint32_t data = 0xABCDEF01 Protocol.sendAddress(address, data); // Command from 1-16 uint8_t command = 13; Protocol.sendCommand(command);

// Some things to mention here: // i might change some stuff. Have a look at the github readme // or the example sketches. I try to update the blog, because its // the only information yet.

[/code]

How to use the library(praxis):

How would you use that stuff now? Well you have 64 adresses to send. Feel free to use them! For example you could send a led state via address 1, an analog read via address 2, text chars for debug output via address 3 and other results like recieved ir signal via address 4. So much variation. You have up to 4byte (long) data to send, but it will automaticly only send the needed information (see how it works below). You could also use one address to send different values (like an ir remote output) to the pi and then you start predefined System commands like a shutdown. Just use a switch for the different addresses and implement the stuff you need. Thats your part, have fun. For questions you can ask, but please post the code to a pastebin.

You can also use the direct 16 bit command. Then you can use these command for predefined System commands(like shutdown/reboot), a Ping or other stuff. The 16 bit command only takes one byte to send! But there is no data with this command.

The library does not provide any direct errorcorrection only about the protocol itself. If the buffer is full or data gets lost you will lose this information. The Protocol should be basic. You can do that stuff yourself. A confirming command with timeout shouldnt be too hard to implement.

By default there is no Error output, because Arduino Uno users dont have a 2nd Serial Port. By default its set to the command line/Serial for Arduino Mega/Leonardo users. Activate error output at the top of the .h file. Infos arent really needed. Error output only shows corrupted blocks.

Ideas what you can do with that:

  • control leds/motors
  • read analog values
  • control raspberry with shutdown/start program functions
  • write text to a display connected to the arduino
  • read data from the Raspberry's SD card
  • create a php website with the raspberry and but this information there (i have no idea about that yet, thats your part. Someone could possibly port this library to php too. Let me know if there is any progress)
  • Use an Arduino Mega as master device to control other Pis/Arduinos. For example the Leonardo for an USB Media Keyboard (see other blogpost)
  • Send the time from a realtime clock to the raspberry
  • Wrap a Data check around the Protocol with a timeout to ensure all data reads.

How to compile the library:

Compiling is a bit different here. Its because we need a makefile to compile differen cpp files. Normally the Arduino IDE does this for you but on the Raspberry we need to create a makefile. If you dont want to create own Raspberry librarys just use the file over and over and do not rename the files. Simply use $ sudo make for compiling the example on Pi and CRTL+U in the IDE to upload the sketch to your Arduino.

The makefile with the update 1.1 is made a bit different. Add your paths for every file to Sources and compile with the commands in the makefile. It is important that you also link the library in your main .cpp right. Have a look at the example: #include "../../SerialProtocol.h". Now you should be able to lazy edit the files in the Arduino IDE all together but compile it on both devices. Make sure to add a #ifdef RaspberryPi to the Raspberry sketch files.

[code language="bash"] #how to compile: #cd /home/pi/Desktop/Arduino/libraries/SerialProtocol/examples/Arduino_Serial #sudo make && sudo ./Pi_Serial

CC=gcc CFLAGS=-lwiringPi -DRaspberryPi -c -pedantic -Wall LDFLAGS=-lwiringPi -DRaspberryPi -pedantic -Wall SOURCES=Pi_Serial.cpp ../../SerialProtocol.cpp OBJECTS=$(SOURCES:.cpp=.o) EXECUTABLE=Pi_Serial

all: $(SOURCES) $(EXECUTABLE)

$(EXECUTABLE): $(OBJECTS) $(CC) $(LDFLAGS) $(OBJECTS) -o $@ rm *.o

.cpp.o: $(CC) $(CFLAGS) $&lt; -o $@

clean: rm *.o $(EXECUTABLE)

rebuild: clean all [/code]

Additional - How the Protocol works and the advantages:

Okay now for the people who want to understand whats going on here. That's my concept if it helps anyone. The image was just for me, it maybe doesnt make any sense to you. Have a look at the .h file, there is another visualation.

concept

 

The idea was:

  • Send data between Arduino an Raspberry in general
  • Write the library as compact as possible
  • Do not send more bytes than needed
  • The Protocol should know at anytime what byte is read (Lead, Data, End)
  • Use it for instable wireless serial communication
  • Send up to 32bit (long) with up to 64 different addresses
How it works:

The picture shows some examples of the protocol: 32bit, 16 bit, 10bit and 8 bit sending. Serial communication always uses a whole byte which is send. As you can see the first bit (from the left indicates data (0) or lead/end(1). A lead starts with 11 and the end with 10. The lead contains information about the length of the signal (2-6 bytes). I had to do a little trick here: If you send 32bit of information the first bit is stored in the length, so length would be 7 if the first bit is 1. That was nesssecary to still be able to use 64 addresses (6bit). The rest of the lead is filled with data. Datablocks will follow and the end contains the address which can be used for different functions. Whats cool about the protocol is that a 1 bit information (on/off for led) only takes 2 byte which will be send (because a 6bit address is included), a 10 bit information (0-1023 analog read) exactly takes 3 bytes, an int 4 bytes and a long 6 bytes. This should be okay with the errorcorrection and the 6 bit address.

Limits:

The Arduino has a 64 byte buffer, the Raspberry ive no idea. If its full, its full. Use a while for the reading to ensure quick reading of all bytes if you have more stuff in your main loop. As you can see in the example a very fast PWM signal works just fine without errors (if you turned them on). You can also try to change the baud to a faster, higher one if you need to send more data. Try to use the command function for simple instructions.

Everything else is just limited by the Serial itself. If you still have problems with the limitation try to use I2C maybe. Its possibly faster. You can also try some of the hardware bridges, linked below. Alamode looks very nice, because it has an sd adapter + real time clock + other stuff.

If you want to know how its implemented have a look at the files themselves.

 

Downloads:

Serial Communication Library

Usefull links/reading stuff:

Installing the Arduino IDE on a Raspberry Pi Download and Install WiringPi WiringPi Serial Library Raspberry PI – accessing your Windows shares How to create a makefile (german) How to create a makefile (english) Connect Raspberry Pi and Arduino with Serial USB Cable Raspberry Pi and Arduino Connected Over Serial GPIO Raspberry Pi and Arduino Connected Using I2C

Hardware:

Arduino - Raspberry Bridges i found: Alamode (looked very nice) Cooking Hacks Bridge Watterott RPI-Shield Bridge

Buy FTDI programmer on Ebay (might be outdated)

Copyright and special thanks

Copyright 2014 NicoHood Thanks to Daniel Garcia from the FastLed community who helped me with this code.

Comment for feedback, help or other ideas.

Its nice to hear if people use this, so let me know what you think about it. Correcting writing mistakes is appreciated!
Clone this wiki locally