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:


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.


Debug data: