Introduction
The Raspberry Pi Ltd. is a company producing small and affordable single-board, bare-bones computer systems, microcontrollers and accessories, marketed under the Raspberry Pi brand. All Raspberry Pi computing products use Arm-based processors. I use Raspberry Pi computers as task controller, and the microcontrollers as state agent in my robots.
Processors and Architectures
- Arm Cortex-A7 with 32-bit Armv7 programming model
- Raspberry Pi 2 B (Broadcom BCM2836 processor @ 900 MHz)
- Arm Cortex-A53 with 64-bit Armv8-A programming model.
- Raspberry Pi 2 B rev 1.2 (Broadcom BCM2837 processor @ 900 MHz)
- Raspberry Pi 3 B (Broadcom BCM2837 processor @ 1.2 GHz)
- Raspberry Pi 3 B+ (Broadcom BCM2837B0 processor @ 1.4 GHz)
- Raspberry Pi Zero 2 W (@ 1 GHz)
- Arm Cortex-A72 with 64-bit Armv8-A programming model
- Raspberry Pi 4 (Broadcom BCM2711 processor @ 1.5 GHz)
- Compute Module 4 (Broadcom BCM2711 @ 1.5 GHz)
- Arm Cortex-M0+ with Armv6-M programming model
- Raspberry Pi Pico (up to 133 MHz)
Installing Raspberry Pi OS on Micro SD Card
Raspberry Pi OS is the offical operating system of a Raspberry Pi. The Lite version, without a graphical desktop, is recommended for embedded applications. The fewer software modules are running, the lower the power consumption, and the lower the chance that user-space processes will be interrupted.
Preparing the SD Card
The most convenient method to preparing the SD card is to use the Raspberry Pi Imager, which can fetch images over the Internet or use local image files. Imager can even pre-configure the image with user credentials, WLAN access etc. before writing to the SD card.
Using built-in tools on Linux
The SD card can also be prepared by using standard Linux tools instead of Raspberry Pi Imager. First, we need to find out which device to copy the install image to. Type
df -h
or
sudo fdisk --list
in the command terminal to look up the mount point for the SD card. Let us assume the card was mounted as /dev/sdb2. The card needs to be unmounted before we can copy the downloaded Raspberry Pi OS install image:
umount /dev/sdb2
Copy the image to the card with the dd command:
sudo dd bs=4M if=2021-01-11-raspios-buster-armhf-lite.img of=/dev/sdb
where we use the path to the device file /dev/sdb instead of the partition /dev/sdb2, which was unmounted. The option bs is used for adjusting the buffer size for the writing operation.
Using built-in tools on macOS
On Apple macOS, you can use the built-in 'diskutil' tool as an alternative to the Raspberry Pi Imager. First, attach the SD card to your Mac and determine the assigned disk number disk# (e.g., disk2) under which the card has been mounted to the filesystem by opening Terminal and typing
diskutil list
then unmount the disk
diskutil unmountDisk disk2
and copy the data with the dd tool
sudo dd bs=1m if=~/Downloads/raspios.img of=/dev/rdisk2
where I assume that the downloaded and unzipped Raspberry Pi OS image file is located at ~/Downloads/raspios.img and the SD card was mounted as disk2.
Upgrading a Raspberry Pi OS Installation
You don't want to erase your existing Raspberry Pi OS environment and set up all the software from start each time a new version of Raspberry Pi OS is released. Fortunately, Raspberry Pi OS can upgrade itself to the latest release with the following commands.
sudo apt-get update sudo apt-get dist-upgrade
Assigning a Static Ethernet IP Address
Edit the file /etc/network/interfaces and make sure that the entries for the interface eth0 are as follows:
auto eth0 iface eth0 inet static address 192.168.0.20 netmask 255.255.255.0 broadcast 192.168.0.255
where the IP address 192.168.0.20 is just an example.
Make sure you don't break the WiFi connectivity because of editing /etc/network/interfaces. If there is no empty describing wlan0 we will need to create one. For dynamically assigned IPs via DHCP you will need these lines:
auto wlan0 iface wlan0 inet dhcp wpa-ssid <your WiFi router name (SSID)> wpa-psk <your WiFi password>
The new network setting becomes effective on the next system boot or when you type
sudo ifdown eth0 sudo ifup eth0
Setting Up SSH
- As a security measure, SSH is not enabled by default on headless (= Raspbian Lite, without GUI) installations. Place a file named "ssh" into the root folder of the boot partition (= /boot) to enable SSH.
- In the web browser go to the configuration page of your home Internet router (e.g., http://192.168.1.1).
- Open the page that lists all currently connected LAN devices.
- Connect the Raspberry Pi via Ethernet cable to the LAN router.
- Look for a new entry in the list of LAN devices. That should be your Raspberry Pi. Note the IP address. Let's assume it is 192.168.1.10.
- Optional: You should be able to read the MAC address of the Raspberry Pi from the table. For future reference, write down the MAC address somewhere.
- Open Terminal and type ssh pi@192.168.1.10 to log in via SSH. On a fresh Raspbian installation the username is pi and the password is raspberry.
- After logging in type sudo raspi-config to start the Raspbian configuration tool, where you can change the password or expand the filesystem to take up the whole SD card, among other things.
Setting Up WiFi
Raspberry Pi 2
The Raspberry Pi 2 does not have built-in WiFi. I assume that you are using a USB WiFi adapter. Before connecting the adapter you need to edit the network interfaces on the Raspberry Pi. Log in to the Raspberry Pi over SSH and edit /etc/network/interfaces as follows:
auto lo iface lo inet loopback iface eth0 inet dhcp allow-hotplug wlan0 auto wlan0 iface wlan0 inet dhcp wpa-ssid "ssid" wpa-psk “password"
where ssid is the SSID (a.k.a. name) of your WLAN router, and password is supplied in encoded form. Do not type the quotation marks. The password is encoded by running
wpa_passphrase <ssid> <password>
If your WLAN router uses a hidden SSID use this configuration:
auto lo iface lo inet loopback iface eth0 inet dhcp auto wlan0 allow-hotplug wlan0 iface wlan0 inet dhcp wpa-scan-ssid 1 wpa-ap-scan 1 wpa-key-mgmt WPA-PSK wpa-proto RSN WPA wpa-pairwise CCMP TKIP wpa-group CCMP TKIP wpa-ssid "My Secret SSID" wpa-psk "My SSID PSK” iface default inet dhcp
Instead of putting the WPA configuration directly into interfaces you can put it into the file /etc/wpa_supplicant/wpa_supplicant.conf
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev update_config=1 network={ ssid="<your ssid>" psk=<your encoded password given by running wpa_passphrase> scan_ssid=1 proto=WPA RSN key_mgmt=WPA-PSK pairwise=CCMP TKIP }
and refer to it from interfaces like so
auto lo iface lo inet loopback iface eth0 inet dhcp allow-hotplug wlan0 iface wlan0 inet dhcp wpa-roam /etc/wpa_supplicant/wpa_supplicant.conf iface default inet dhcp
Raspberry Pi 3 or newer
The Raspberry Pi 3 has a WLAN transmitter built in and it is turned on by default. You should see the device wlan0 along with eth0 and lo when typing
ifconfig -a
Make sure the Raspberry Pi can log in to your WLAN router. If a MAC filter is active, add the Raspberry Pi WiFi MAC address to the list of allowed devices. The MAC address of the Pi is displayed in the output of ifconfig -a as HWaddr. Open a text editor to add the network configuration details, such as your login credentials, to the file /etc/wpa_supplicant/wpa_supplicant.conf.
network={ ssid="MySSID" psk=MyCodedPassword scan_ssid=1 proto=WPA RSN key_mgmt=WPA-PSK pairwise=CCMP TKIP }
where MySSID is the SSID (a.k.a. name) of the router, and MyCodedPassword is the encoded password obtained via
wpa_passphrase MySSID MyPassword
and where MyPassword is the actual password.
Power Saving
If the WiFi connection is unstable you can turn off the WiFi power management feature, which may cause connection dropouts, by issuing the command
sudo iw dev wlan0 set power_save off
or by manually editing /etc/network/interfaces, where you add the line
wireless-power off
right after the line
wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf
within the wlan0 block. You can check whether power management is currently turned on or off, among other parameters, with the command
iwconfig
For Raspberry Pi 2 and WiFi dongles with 8192CU or 8188CUS chip (for example, Edimax EW-7811un dongles)you can prevent connections from being dropped by creating the file /etc/modprobe.d/8192cu.conf
sudo nano /etc/modprobe.d/8192cu.conf
and adding the line
options 8192cu rtw_power_mgnt=0 rtw_enusbss=0
Camera
There is a camera module for Raspberry Pi, which I use in my robots. In fact, the camera module is part of the Core Module.
Refer to the official camera setup instructions. Be careful with the camera module, handle the camera board and the connector strip delicately.
To be able to access the camera through the Python programming environment you need to install the python-picamera package
The Raspberry Pi project does not provide a C/C++ library for accessing the camera. Instead it provides utility applications raspistill and raspivid which record still images and videos, respectively. A number of third-party C++ libraries have been created based on the open source code of these utility applications. The RaspiCam C++ library developed at the University of Cordoba is one such library (source code available from SourceForge).
Video Streaming
To stream video from the Raspberry Pi camera to the network, open a terminal on the RPi and type
raspivid -t 0 -w 1280 -h 720 -hf -ih -fps 30 -o - | nc -k -l 8100
To view the stream on a client computer with Mplayer installed, open a terminal there and type
mplayer -fps 300 -demuxer h264es ffmpeg://tcp://192.168.0.20:8100
Alternatively, you can use GStreamer to transport the video frames. Make sure that the command line interface to GStreamer, gst-launch-1.0, is installed.
sudo apt install gstreamer1.0-tools
For streaming from the RPi you can type
raspivid -t 0 -w 640 -h 480 -fps 30 -hf -b 2000000 -o - | gst-launch-1.0 -q fdsrc ! h264parse ! rtph264pay config-interval=1 pt=96 ! gdppay ! tcpserversink host=0.0.0.0 port=8100
For viewing on the client, you can type
gst-launch-1.0 -v tcpclientsrc host=192.168.0.20 port=8100 ! gdpdepay ! rtph264depay ! avdec_h264 ! videoconvert ! videoflip method=vertical-flip ! autovideosink sync=false
where 192.168.0.20 is assumed to be the IP address of the server (Raspberry Pi).
- On the Raspberry Pi side you can elevate the priority of the process by beginning the command with "nice -20".
- For verbose output from gst-launch-1.0, use the option -v instead of -q.
- Note that we used the videoflip filter to correct the rotation of the decoded image.
Disabling the LED
To disable the red indicator light of the camera, add the following line to /boot/config.txt and reboot:
disable_camera_led=1
pinout
A cool command-line tool provided by Raspberry Pi, and also available when running Ubuntu instead of Raspberry Pi OS, is pinout. It displays a layout of the Raspberry Pi board using ASCII art, a list of significant hardware features of the board, and the GPIO pin layout in tabular form.
GPIO Programming in C
Raspberry Pi 2, 3 & 4 come with 40 General Purpose Input Output (GPIO) pins (layout) that can be used to interface with the other electronic components of your project.
Using libgpiod
The standard C++ programming interface for GPIO on Linux is libgpiod, as described on the Jetson page.
Using WiringPi
Using the WiringPi library is a more popular method for programming the GPIO pins on a Raspberry Pi with C/C++. WiringPi is installed from source like this
sudo apt-get install git-core git clone git://git.drogon.net/wiringPi cd wiringPi ./build
Read the INSTALL file for what to do with the created shared library. Basically you need to copy wiringPi/libwiringPi.so.x.xx to /usr/local/lib and add the line
include /usr/local/lib
to the file /etc/ld.so.conf so that the library loader also looks in /usr/local/lib for dynamically loadable libraries. It is also possible to create a static library.
After installation, create a test program called blink.cpp
#include <stdio.h> #include <iostream> #include <wiringPi.h> static int LED_PORT = 7; // The physical pin number. Corresponds to BCM GPIO pin 4. void blink() { digitalWrite(4, HIGH); delay(1000); digitalWrite(4, LOW); delay(1000); }; int main(int numargs, char** args) { wiringPiSetup(); pinMode(LED_PORT, OUTPUT); for (int n = 0; n < 10; n++) { blink(); } std::cout << "finished blinking" << std::endl; return 0; }
and build like this
g++ blink.cpp -o blink -lstdc++ -lwiringPi -IwiringPi
where I assumed that the wiringPi root folder is located in the same folder as the blink.cpp source file.
Which pins can be used for what purpose is explained on the wiringPi pins page.
PWM
There are some Python libraries that communicate with the GPIO daemon to provide hardware PWM on selected pins. GPIO Zero (tutorial) is one such library and is included in Raspbian. With GPIO Zero you can implement dimming LEDs or control a brushed DC motor behind an H-Bridge circuit.
Running A Program On Startup
We want the Raspberry Pi to start the robot control program as soon as it's done with booting. In order to make sure that the system has completely initialized itself we will not use /etc/rc.local for starting our program. Instead, we will create a cron task with the crontab command
sudo crontab -e
which opens the editor of choice (most likely nano) where you can enter the program launch command. In our case this will be
@reboot python /home/pi/Core.py
where the leading @reboot indicates to cron that this command shall be executed only once, right after the booting process has completed. Notice that it is not necessary to start the command with sudo, which is necessary when running program from the command line.
Communicating With Other Devices
There are three popular communication protocols that the Raspberry Pi supports and that are often used in embedded systems: I2C, SPI, and UART.
I2C
Set the I2C clock speed by editing /boot/config.txt
dtparam=i2c1=on dtparam=i2c_arm_baudrate=xxx
where xxx is the desired frequency. Keep in mind that ATmega microcontrollers used on Arduino boards support only up to 400 kHz while Raspberry Pi can support higher speeds.
Wiring To I2C Devices With 5 V Logic Level
Normally you can not connect a GPIO pin to a device with 5 V logic (most of the Arduino models, for example). The GPIO data pins of the Raspberry Pi use 3.3 V as the HIGH voltage level and can be damaged when 5 V is applied. However, because I2C pins have open-collector outputs they can be wired directly to I2C pins of devices with incompatible voltage levels. As long as the pull-up line voltage, which corresponds to the lowest of the device HIGH voltage levels, exceeds the minimum voltage for which HIGH is defined on each device, the devices will be able to sense the correct data bit.
Links:
- Tutorial by SparkFun on I2C
- Article by Adafruit on how to set up I2C and SPI on a Raspberry Pi
- Tutorial for setting up an I2C bus between a Raspberry Pi and an Arduino.
SPI
First of all, you need to use the raspi-config command-line configuration utility to enable the SPI pins.
sudo raspi-config
You can use WiringPi SPI to add SPI capability to your C/C++ program.
UART
Here is a tutorial that shows how to communicate with an Arduino using the UART interface.
CAN
There are various CAN adapter boards available for Raspberry Pi. For example PiCAN2.
Development Tools
Raspbian Buster from 2019 comes with GCC version 8.3.0, which fully implements the C++14 standard.
If you are connected to the Raspberry Pi via a terminal session you can use VIM for editing code files.