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 a System Image
Raspberry Pi OS is the offical operating system of a Raspberry Pi. The Lite version, without a graphical desktop, is recommended for embedded applications unless you decide to create a custom system image. The fewer software modules are running, the lower the power consumption, and the lower the chance that user-space processes will be interrupted.
Installing the System Image on SD Card
This is the most popular method of installing a system image on a single-board computers. SD cards are cheap and reusable, almost all Raspberry Pi models come with an SD card reader. However, SD card memory is not designed to support frequent rewrite operations and may not be as durable as other storage solutions.
The preferred way of putting a system image on an SD card is to use the Raspberry Pi Imager tool that can fetch a Raspberry Pi OS image over the Internet or use a local image file, create a derived image with preconfigured user credentials, WLAN access and more, before writing it 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.
Installing the System Image on eMMC
Some Raspberry Pi models, like the Compute Module 4, come with an embedded MultiMediaCard (eMMC) storage component. A switch or jumper on the carrier board serves to make the Raspberry Pi bootloader boot the compute module into USB mass storage mode. In this mode you can attach the compute module to an external computer via USB. On Ubuntu you can use the
lsusb
command to list all connected USB devices. The Compute Module 4, for example, should show up as "Broadcom Corp. BCM2711 Boot".
To make the operating system recognize the USB device as a mass storage device, we need to build and run Raspberry Pi's rpiboot tool.
sudo apt install libusb-1.0-0-dev git clone --depth=1 https://github.com/raspberrypi/usbboot cd usbboot make sudo ./rpiboot
On Ubuntu, lsusb will now list the Compute Module 4 as "Broadcom Corp. Compute Module". The compute module will also show up in the Disks app as RPi-MSD-0001.
You can now use Raspberry Pi Imager or any flashing utility to flash a system image on the compute module eMMC.
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.
sudo apt install python3-gpiozero pinout
raspi-gpio
The documentation states that "raspi-gpio can get and print the state of a GPIO (or all GPIOs) and can be used to set the function, pulls and value of a GPIO. raspi-gpio must be run as root." "
> sudo apt install raspi-gpio > raspi-gpio funcs 20,21,22 GPIO, DEFAULT PULL, ALT0, ALT1, ALT2, ALT3, ALT4, ALT5 20, DOWN, PCM_DIN, SD12, DPI_D16, SPI6_MOSI, SPI1_MOSI, GPCLK0 21, DOWN, PCM_DOUT, SD13, DPI_D17, SPI6_SCLK, SPI1_SCLK, GPCLK1 22, DOWN, SD0_CLK, SD14, DPI_D18, SD1_CLK, ARM_TRST, SDA6 > raspi-gpio get 20 GPIO 20: level=0 func=INPUT pull=DOWN GPIO 21: level=1 func=INPUT pull=DOWN GPIO 22: level=0 func=INPUT pull=DOWN > raspi-gpio raw 21 00: 00000000 00012000 12000000 3fffffff 10: 24000040 00000924 00000000 6770696f 20: 6770696f 6770696f 6770696f 6770696f 30: 6770696f 102081ff 00003bfb 00000000 40: 00000000 00000000 00000000 00000000 50: 00000000 00000000 00000000 00000000 60: 00000000 00000000 00000000 00000000 70: 00000000 00000000 00000000 00000000 80: 00000000 00000000 00000000 00000000 90: 00000000 00000000 00000000 00000000 e0: 4aa95555 19aaaaaa 50aa5544 f0: 000aaaaa
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
Using SysVInit
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.
Using Systemd
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.
Device Tree Overlays
A device tree overlay is a mechanism in Raspberry Pi computers to dynamically apply changes to existing device trees. This mechanism is mostly used to enable kernel-space support for hardware components that are attached to the Raspberry Pi via its GPIO headers.
Operations with device tree overlays are performed by the Raspberry Pi GPU (VideoCore), without needing to preempt the Linux kernel. The Raspberry Pi primary bootloader, also a VideoCore firmware application, makes use of this device tree parsing and editing capability to apply device tree overlays during the boot process, before passing control to the Linux kernel.
/boot/config.txt
The primary bootloader parses and interprets the /boot/config.txt file. It specifies, among other things, which device tree to select and which overlays to apply.
dtoverlay
dtoverlay is the command-line tool for loading a device tree overlay (or unloading a previously loaded overlay) while the Linux system is running.
VideoCore Device Tree Blob
VideoCore functions both as the GPU of the Raspberry Pi and as a separately programmable unit that connects to a number of I/O devices. The pin assignments for connecting to external devices can be customized through a user-provided device tree blob file, dt-blob.bin. See Raspberry Pi's documentation on the VideoCore device tree blob. This configuration feature is necessary for adapting a compute module to the hardware configuration of a project.
Below is an example configuration for a Compute Module 4, in which the I2C pins for accessing a MIPI CSI-2 camera and a MIPI DSI display are set. Applying this pin configuration is sufficient to make the camera available to user space applications because the device tree, bcm2711-rpi-cm4.dts, already includes a csi entry. The dsi entry for the display, however, needs to be added.
/dts-v1/; // Enabling MIPI DSI display and MIPI CSI-2 camera for // WaveShare CM4-Nano B (https://www.waveshare.com/wiki/CM4-NANO-B). / { videocore { pins_cm4 { // Pi 4 CM4 pin_config { pin@default { polarity = "active_high"; termination = "pull_down"; startup_state = "inactive"; function = "input"; }; // pin pin@p0 { function = "input"; termination = "pull_up"; }; // CAMERA 0 I2C0 SDA pin@p1 { function = "input"; termination = "pull_up"; }; // CAMERA 0 I2C0 SCL pin@p44 { function = "input"; termination = "pull_up"; }; // DISPLAY 1 I2C0 SDA pin@p45 { function = "input"; termination = "pull_up"; }; // DISPLAY 1 I2C0 SCL pin@p128 { function = "output"; termination = "no_pulling"; }; // BT_ON pin@p129 { function = "output"; termination = "no_pulling"; }; // WL_ON pin@p131 { function = "output"; termination = "no_pulling"; }; // ANT1 pin@p133 { function = "output"; termination = "no_pulling"; }; // Camera shutdown }; // pin_config pin_defines { pin_define@HDMI_CONTROL_ATTACHED { type = "external"; number = <0>; }; pin_define@EMMC_ENABLE { type = "external"; number = <1>; }; pin_define@NUM_CAMERAS { type = "internal"; number = <1>; }; pin_define@POWER_LOW { type = "absent"; }; pin_define@LEDS_DISK_ACTIVITY { type = "absent"; }; pin_define@LAN_RUN { type = "absent"; }; pin_define@BT_ON { type = "external"; number = <0>; }; pin_define@WL_ON { type = "external"; number = <1>; }; pin_define@ETH_CLK { type = "absent"; }; pin_define@WL_LPO_CLK { type = "absent"; }; pin_define@USB_LIMIT_1A2 { type = "absent"; }; pin_define@SIO_1V8_SEL { type = "absent"; }; pin_define@SAFE_MODE { type = "absent"; }; // Display pin_define@DISPLAY_I2C_PORT { type = "internal"; number = <0>; }; pin_define@DISPLAY_SDA { type = "internal"; number = <44>; }; pin_define@DISPLAY_SCL { type = "internal"; number = <45>; }; pin_define@DISPLAY_DSI_PORT { type = "internal"; number = <1>; }; // Camera pin_define@CAMERA_0_I2C_PORT { type = "internal"; number = <0>; }; pin_define@CAMERA_0_SDA_PIN { type = "internal"; number = <0>; }; pin_define@CAMERA_0_SCL_PIN { type = "internal"; number = <1>; }; pin_define@CAMERA_0_SHUTDOWN { type = "external"; number = <5>; }; pin_define@CAMERA_0_UNICAM_PORT { type = "internal"; number = <0>; }; pin_define@CAMERA_0_LED { type = "absent"; }; }; // pin_defines }; // pins }; };
Yocto Tips
Creating Kernel Patches for Compute Module 4
A compute module does not know what hardware components its carrier board provides unless the default device tree is modified to include the interfaces to these hardware components. Instead of providing a different complete device tree, you would normally create a patch for the device tree source file provided by a lower Yocto layer.
The modified device tree must be based on the device tree used by the kernel in Yocto. For Compute Module 4, the device tree file to patch is
- ${YOCTO_BUILD_DIR}/tmp/work-shared/raspberrypi4-64/kernel-source/arch/arm/boot/dts/bcm2711-rpi-cm4.dts
Copy this file to your layer's recipe directory and edit the copy to add the peripherals. The Raspberry Pi reference device tree files at the Raspberry Pi Datasheets collection page can be helpful in creating your modified device tree.