The other day, I touched the Raspberry Pi with Playing with Ubuntu Desktop on Raspberry Pi 4, but this time I tried to move this Raspberry Pi using the Yocto Project. I tried using Yocto for the first time, but I think it was a good introduction, so I will summarize the contents.
Mainly, there are mechanisms and tools for creating Linux distributions for embedded and IoT. You can flexibly customize and build embedded Linux images. It seems to be adopted by many embedded Linux providers. Yocto Project - Wikipedia
I started the Raspberry Pi 4 that I had at hand with the image built with Yocto.
Yocto was built on Ubuntu 20.04 LTS with WSL2. The target is Raspberry Pi 4 as mentioned above.
First, install WSL2. The distribution is using Ubuntu 20.04 LTS this time. Please refer to here for the procedure, for example. https://docs.microsoft.com/ja-jp/windows/wsl/install-win10
When the environment starts up, update the package for the time being.
$ sudo apt update && sudo apt upgrade -y
Next, install the necessary packages by referring to Yocto Project Quick Start.
$ sudo apt install -y gawk wget git-core diffstat unzip texinfo gcc-multilib build-essential chrpath socat cpio python python3 python3-pip python3-pexpect xz-utils debianutils iputils-ping libsdl1.2-dev xterm
First, clone Poky (reference distribution) with Git.
The version specifies the latest Gatesgarth (Yocto 3.2)
at this point.
Releases - Yocto Project
$ mkdir ~/yocto && cd ~/yocto
$ git clone git://git.yoctoproject.org/poky
$ cd poky
$ git checkout -b gatesgarth-24.0.1 refs/tags/gatesgarth-24.0.1
Next, add a BSP layer for Raspberry Pi.
There is meta-raspberrypi
in OpenEmbedded Layer Index, so clone it. The branch specifies gatesgarth
according to Poky.
$ cd ~/yocto/poky
$ git clone git://git.yoctoproject.org/meta-raspberrypi
$ cd meta-raspberrypi
$ git checkout -b gatesgarth origin/gatesgarth
Refer to the procedure described in the README of meta-raspberrypi
.
Dependencies
This layer depends on:
* URI: git://git.yoctoproject.org/poky
- branch: master
- revision: HEAD
* URI: git://git.openembedded.org/meta-openembedded
- layers: meta-oe, meta-multimedia, meta-networking, meta-python
- branch: master
- revision: HEAD
Quick Start
1. source poky/oe-init-build-env rpi-build
2. Add this layer to bblayers.conf and the dependencies above
3. Set MACHINE in local.conf to one of the supported boards
4. bitbake core-image-base
5. Use bmaptool to copy the generated .wic.bz2 file to the SD card
6. Boot your RPI
First, load oe-init-build-env
and set up the environment. When executed, the rpi-build
directory is created and moved. Build is done in this directory.
$ cd ~/yocto/poky
$ source oe-init-build-env rpi-build
Next, make additional settings for this layer. add-layer
will add meta-raspberrypi
to conf/bblayers.conf
.
$ bitbake-layers add-layer ../meta-raspberrypi/
$ cat conf/bblayers.conf
:
BBLAYERS ?= " \
/home/user/yocto/poky/meta \
/home/user/yocto/poky/meta-poky \
/home/user/yocto/poky/meta-yocto-bsp \
/home/user/yocto/poky/meta-raspberrypi \
"
Next, add the dependent layer described in the README in the same way. Again, specify gatesgarth
for the branch. Also, an error will occur depending on the order in which add-layer
is executed, so add in the following order.
$ cd ~/yocto/poky
$ git clone git://git.openembedded.org/meta-openembedded
$ cd meta-openembedded
$ git checkout -b gatesgarth origin/gatesgarth
$ cd ~/yocto/poky/rpi-build
$ bitbake-layers add-layer ../meta-openembedded/meta-oe/
$ bitbake-layers add-layer ../meta-openembedded/meta-python/
$ bitbake-layers add-layer ../meta-openembedded/meta-multimedia/
$ bitbake-layers add-layer ../meta-openembedded/meta-networking/
$ cat conf/bblayers.conf
:
BBLAYERS ?= " \
/home/user/yocto/poky/meta \
/home/user/yocto/poky/meta-poky \
/home/user/yocto/poky/meta-yocto-bsp \
/home/user/yocto/poky/meta-raspberrypi \
/home/user/yocto/poky/meta-openembedded/meta-oe \
/home/user/yocto/poky/meta-openembedded/meta-python \
/home/user/yocto/poky/meta-openembedded/meta-multimedia \
/home/user/yocto/poky/meta-openembedded/meta-networking \
"
Next, set MACHINE
in local.conf
. For the machine name to be specified, refer to Machines in here. This time, the settings are as follows.
~/yocto/poky/rpi-build/conf/local.conf
MACHINE ?= "raspberrypi4-64"
When I used WSL2 at the end, the following error appeared at build time. (This error did not occur on Ubuntu 18.04 installed on VMware.) It was solved by the countermeasure in Stack Overflow.
ERROR: Task (/home/user/yocto/poky/meta/recipes-kernel/linux-libc-headers/linux-libc-headers_5.8.bb:do_install) failed with exit code '134'
~/yocto/poky/rpi-build/conf/local.conf
PSEUDO_IGNORE_PATHS_append = ",/run/"
The setting is completed up to this point. Execute the build with the bitbake
command. It takes a lot of time to build, and the PC runs at full capacity. Let's wait patiently.
For reference, it took about 3-4 hours on my notebook PC (CPU: Core i7-8550U, memory: 16.0 GB). Also, SSD uses about 50GB.
$ cd ~/yocto/poky/rpi-build
$ bitbake core-image-base
If you are using WSL2, the following warning will be displayed. Please refer to "6. Optimize your WSLv2 storage often" in here for the countermeasures.
WARNING: You are running bitbake under WSLv2, this works properly but you should optimize your VHDX file eventually to avoid running out of storage space
When the build is complete, the OS image will be created in rpi-build/tmp/deploy/images/raspberrypi4-64
.
Write core-image-base-raspberrypi4-64.wic.bz2
to the SD card using the bmaptool
command. However, in the WSL2 environment I am using this time, I did not know how to specify the device name of the SD card.
$ cd ~/yocto/poky/rpi-build/tmp/deploy/images/raspberrypi4-64
$ sudo apt install -y bmap-tools
$ sudo bmaptool copy core-image-base-raspberrypi4-64.wic.bz2 /dev/sdX #I don't know how to specify the device name of the SD card
So this time, after decompressing wic.bz2, I wrote it to the SD card using Raspberry Pi Imager on Windows.
$ cd ~/yocto/poky/rpi-build/tmp/deploy/images/raspberrypi4-64
$ cp core-image-base-raspberrypi4-64.wic.bz2 /tmp/. #Since it is a symbolic link, copy it to a suitable place
$ bunzip2 /tmp/core-image-base-raspberrypi4-64.wic.bz2
$ mv /tmp/core-image-base-raspberrypi4-64.wic /mnt/c/Users/user/Downloads/. #Move to a suitable directory on the Windows side
Select core-image-base-raspberrypi4-64.wic
from Use custom in the Operating System and write to the SD card.
I think that there is no problem even if you write with Etcher or DD for Windows.
Insert the created SD card into the Raspberry Pi and turn on the power to start the OS.
When the output to the console stops and you press the Enter key, log in:
is displayed, so log in as root
. There is no password.
If successful, the environment is minimal, but you can operate it firmly in the terminal.
raspberrypi4-64 log in: root
root@raspberrypi4-64:~# uname -mnr
raspberrypi4-64 5.4.72-v8 aarch64
root@raspberrypi4-64:~# cat /etc/issue
Poky (Yocto Project Reference Distro) 3.2.1 \n \l
Next, I will write my own simple source code and summarize the procedure to install the compiled executable binary. The following will be helpful. Creating a General Layer Using the bitbake-layers Script
First, create a layer. Execute the following command to create the meta-hello
directory and a template in it.
$ cd ~/yocto/poky
$ bitbake-layers create-layer meta-hello
$ ls meta-hello/
COPYING.MIT README conf recipes-example
Next, make additional settings for the created layer.
$ cd ~/yocto/poky/rpi-build
$ bitbake-layers add-layer ../meta-hello/
$ cat conf/bblayers.conf
:
BBLAYERS ?= " \
/home/user/yocto/poky/meta \
/home/user/yocto/poky/meta-poky \
/home/user/yocto/poky/meta-yocto-bsp \
/home/user/yocto/poky/meta-raspberrypi \
/home/user/yocto/poky/meta-openembedded/meta-oe \
/home/user/yocto/poky/meta-openembedded/meta-python \
/home/user/yocto/poky/meta-openembedded/meta-multimedia \
/home/user/yocto/poky/meta-openembedded/meta-networking \
/home/user/yocto/poky/meta-hello \
Next, prepare the source code. Create a files
directory and place the source code as shown below.
$ cd ~/yocto/poky/meta-hello
$ mkdir -p recipes-hello/hello && cd recipes-hello/hello
$ mkdir files && cd files
$ vim hello.c
I prepared Hello world in C language.
hello.c
#include <stdio.h>
int main(int argc, char const *argv[])
{
printf("Hello, world!\n");
return 0;
}
Next, create a recipe (metadata used to build the package).
$ cd ~/yocto/poky/meta-hello/recipes-hello/hello
$ vim hello_1.0.bb
Here is a reference for how to write a recipe file.
Single .c File Package (Hello World!)
However, if it is left as it is, the build will fail, so add TARGET_CC_ARCH + =" $ {LDFLAGS} "
.
Default Linker Hash Style Changed
If you don't know md5
of LIC_FILES_CHKSUM
, leave it blank and the correct value will be displayed as an error at build time.
text:hello_1.0.bb
SUMMARY = "Simple helloworld application"
SECTION = "hello"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
TARGET_CC_ARCH += "${LDFLAGS}"
SRC_URI = "file://hello.c"
S = "${WORKDIR}"
do_compile() {
${CC} hello.c -o hello
}
do_install() {
install -d ${D}${bindir}
install -m 0755 hello ${D}${bindir}
}
The final directory / file structure is as follows.
meta-hello/
├── COPYING.MIT
├── README
├── conf
│ └── layer.conf
├── recipes-example
│ └── example
│ └── example_0.1.bb
└── recipes-hello
└── hello
├── files
│ └── hello.c
└── hello_1.0.bb
Finally, specify the addition of the image with IMAGE_INSTALL_append
in local.conf
.
~/yocto/poky/rpi-build/conf/local.conf
IMAGE_INSTALL_append = " hello" #Note that a leading whitespace character is required
When you are ready so far, build it.
$ cd ~/yocto/poky/rpi-build/
$ bitbake core-image-base
Write the completed OS image to the SD card and start it with Raspberry Pi. Log in and check that you can execute the hello
command. If all goes well, you will see Hello, world!
.
raspberrypi4-64 log in: root
root@raspberrypi4-64:~# hello
Hello, world!
Next, set up to enable SSH connection via Wi-Fi.
In the environment so far, it seems that wlan0
is recognized when you check with the ip
command.
root@raspberrypi4-64:~# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc mq qlen 1000
link/ether dc:a6:32:b8:2b:4a brd ff:ff:ff:ff:ff:ff
3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP8000> mtu 1500 qdisc pfifo_fast qlen 1000
link/ether dc:a6:32:b8:2b:4b brd ff:ff:ff:ff:ff:ff
It seems easy to use ConnMan to make a Wi-Fi connection. Make additional settings for the image in local.conf.
Also, add OpenSSH to make an SSH connection. It seems good to specify it as IMAGE_FEATURES
by referring to here.
~/yocto/poky/rpi-build/conf/local.conf
IMAGE_INSTALL_append = " connman connman-client"
IMAGE_FEATURES_append = " ssh-server-openssh"
Up to this point, build the OS image with bitbake core-image-base
and start Raspberry Pi from the SD card.
Use the connmanctl
command to set up the Wi-Fi connection according to the following procedure.
root@raspberrypi4-64:~# connmanctl
connmanctl> enable wifi
connmanctl> scan wifi
connmanctl> services
Buffalo-A-0F20 wifi_dca632b82b4b_42756666616c6f2d412d30463230_managed_psk
Buffalo-G-0F20 wifi_dca632b82b4b_42756666616c6f2d472d30463230_managed_psk
WARPSTAR-D56FD6 wifi_dca632b82b4b_57415250535441522d443536464436_managed_psk
connmanctl> agent on
connmanctl> connect wifi_dca632b82b4b_42756666616c6f2d412d30463230_managed_psk
Passphrase?
connmanctl> exit
When you have finished entering the passphrase, exit connmanctl
and check again with the ip
command. The IP address is assigned to wlan0
.
If you try an SSH connection to this IP address, you should be able to log in as root.
$ ssh [email protected] #IP address assigned to wlan0 of Raspberry Pi
Last login: Sun Jan 10 02:06:14 2021 from 192.168.1.7
root@raspberrypi4-64:~#
It is not directly related to SSH connection, but I added it because it was convenient to use SFTP in my development environment.
In addition, set the root password and create a new pi user. Also add the sudo
command.
Also, when I was investigating with the addition of OpenSSH, I often saw a setting to enable systemd, so I will include this as well.
~/yocto/poky/rpi-build/conf/local.conf
IMAGE_INSTALL_append = " openssh-sftp-server sudo"
IMAGE_FEATURES_append = " ssh-server-openssh"
# user settings
INHERIT_append = " extrausers"
EXTRA_USERS_PARAMS = "useradd -P raspberry pi;usermod -P raspberry root;"
# systemd settings
DISTRO_FEATURES_append = " systemd"
VIRTUAL-RUNTIME_init_manager = "systemd"
DISTRO_FEATURES_BACKFILL_CONSIDERED = "sysvinit"
VIRTUAL-RUNTIME_initscripts = ""
IMX_DEFAULT_DISTRO_FEATURES_append = " systemd"
Build up to this point, start with Raspberry Pi and check the SSH connection. (This build will take some time again.)
In addition to root, pi users can also log in with SSH. Use the newly set password. (In this example, raspberry
)
To enable sudo as a pi user, set it from root with the visudo
command.
/etc/sudoers
root ALL=(ALL) ALL
pi ALL=(ALL) ALL #Add here
Add Python as a development environment. Since I am using Raspberry Pi, I will add rpi-gpio
to easily control GPIO. Also, since I didn't have an LED at hand, I used a command called raspi-gpio
to control GPIO to check it.
~/yocto/poky/rpi-build/conf/local.conf
IMAGE_INSTALL_append = " python3 python3-pip rpi-gpio raspi-gpio"
Build the OS image and start it with Raspberry Pi. I am checking with the root user.
Let's check if Python and pip can be used. You can also see that the output is as shown below and that rpi-gpio
is added.
# python3 --version
Python 3.8.5
# python3 -m pip list
Package Version
---------- -------
pip 20.0.2
RPi.GPIO 0.7.0
setuptools 49.6.0
Also make sure that you can use the raspi-gpio
command. You can check how to use it easily with help.
GPIO pin 1 seems to be pulled up by input by default.
# raspi-gpio help
# raspi-gpio get 1
GPIO 1: level=1 fsel=0 func=INPUT pull=UP
Write a simple sample code to control GPIO in Python. Set GPIO pin 1 as the output setting and switch ON/OFF every second.
gpio.py
import time
import RPi.GPIO as GPIO
def main():
pin_num = 1
GPIO.setmode(GPIO.BCM)
GPIO.setup(pin_num, GPIO.OUT)
for i in range(10):
GPIO.output(pin_num, i % 2)
print(f'GPIO {"ON" if i % 2 else "OFF"}')
time.sleep(1)
GPIO.cleanup(pin_num)
if __name__ == '__main__':
main()
If you repeatedly execute raspi-gpio
while executing the above Python code and check the GPIO pin 1, you can see that func is switched to output and level is switched to 0/1.
# python3 gpio.py #Check the following in another session during execution
# raspi-gpio get 1
GPIO 1: level=0 fsel=1 func=OUTPUT pull=NONE
# raspi-gpio get 1
GPIO 1: level=1 fsel=1 func=OUTPUT pull=NONE
# raspi-gpio get 1
GPIO 1: level=0 fsel=1 func=OUTPUT pull=NONE
# raspi-gpio get 1
GPIO 1: level=1 fsel=1 func=OUTPUT pull=NONE
I will also briefly summarize how to control GPIO from a device file without using raspi-gpio. If you check by the following procedure, you can see that GPIO pin 1 is the input by default. With this method as well, you can see how the Python sample code is executed and switched to output to turn it on and off.
# cd /sys/class/gpio
# ls
export gpiochip0 gpiochip504 unexport
# echo 1 > export
# ls
export gpio1 gpiochip0 gpiochip504 unexport
# cd gpio1
# ls
active_low device direction edge power subsystem uevent value
# cat active_low direction value
0
in
1
Finally, build the toolchain and build a cross-compilation environment on your local PC.
This time I built and ran the C ++ code, but when I ran it I got an error saying that libstdc ++. So.6
could not be found. It seems that the C ++ standard library is not included by default, so make additional settings.
~/yocto/poky/rpi-build/conf/local.conf
IMAGE_INSTALL_append = " libstdc++"
TOOLCHAIN_TARGET_TASK_append = " libstdc++-staticdev"
Summarizing the contents so far, the final contents of local.conf are as follows.
~/yocto/poky/rpi-build/conf/local.conf
MACHINE ?= "raspberrypi4-64"
# for WSL2
PSEUDO_IGNORE_PATHS_append = ",/run/"
# image
IMAGE_INSTALL_append = " hello connman connman-client openssh-sftp-server sudo python3 python3-pip rpi-gpio raspi-gpio libstdc++"
IMAGE_FEATURES_append = " ssh-server-openssh"
# user settings
INHERIT_append = " extrausers"
EXTRA_USERS_PARAMS = "useradd -P raspberry pi;usermod -P raspberry root;"
# systemd settings
DISTRO_FEATURES_append = " systemd"
VIRTUAL-RUNTIME_init_manager = "systemd"
DISTRO_FEATURES_BACKFILL_CONSIDERED = "sysvinit"
VIRTUAL-RUNTIME_initscripts = ""
IMX_DEFAULT_DISTRO_FEATURES_append = " systemd"
# for c++
TOOLCHAIN_TARGET_TASK_append = " libstdc++-staticdev"
After writing local.conf, build the OS image and toolchain. It will take some time to build the toolchain, so please wait slowly. Also, please note that SSD will use about 80GB in the previous procedure.
$ cd ~/yocto/poky/rpi-build
$ bitbake core-image-base
$ bitbake core-image-base -c populate_sdk #Toolchain build
When the build is complete, you will have a toolchain in rpi-build/tmp/deploy/sdk
. You can install it by executing the .sh file in it. The installation destination is / opt/poky/3.2.1
by default.
You can use the cross-compilation environment by loading environment-setup-cortexa72-poky-linux
.
$ cd ~/yocto/poky/rpi-build/tmp/deploy/sdk
$ sudo ./poky-glibc-x86_64-core-image-base-cortexa72-raspberrypi4-64-toolchain-3.2.1.sh
$ source /opt/poky/3.2.1/environment-setup-cortexa72-poky-linux
This time, C ++ Hello world! Build with CMake and run it with Raspberry Pi.
$ mkdir ~/hello && cd ~/hello
$ vim hello.cpp
$ vim CMakeLists.txt
$ mkdir build && cd build
$ cmake ..
$ make
The hello.cpp
and CMakeLists.txt
used this time have the following contents.
hello.cpp
#include <iostream>
int main(int argc, char const *argv[])
{
std::cout << "Hello, world!" << std::endl;
return 0;
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.0.0)
project(hello VERSION 0.1.0)
set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "limited configs" FORCE)
add_executable(hello
hello.cpp
)
target_compile_features(hello
PRIVATE cxx_std_17
)
Use the readelf
command to check the executed binary that has been built. You can see that the Machine is AArch64
and cross-compilation is possible. (The PC that was built this time is x86_64
)
$ readelf -h hello
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: DYN (Shared object file)
Machine: AArch64
Version: 0x1
:
$ uname -m
x86_64
If you copy the execution binary to Raspberry Pi with the scp
command and move it,Hello, world!
Is displayed.
Local PC
$ scp ./hello [email protected]:/home/root/.
RaspberryPi
# ~/hello
Hello, world!
This time, I targeted Raspberry Pi 4 and built Linux using Yocto Project and actually ran it. I think I was able to confirm the basic procedure to some extent. Yocto doesn't have much information in Japanese, and it takes a long time to build, so I felt that it was difficult for beginners to get along with. I think this article is a good tutorial for those who are new to Yocto. I would be grateful if you could use it as a reference for people who are new to Yocto.
This is the last introduction of the books I referred to. For the beginning, I referred to Introduction to Yocto Project. It was very helpful because I touched Yocto for the first time. The contents summarized this time, especially in the first half, were moved according to my development environment by referring to the contents of this book, and I summarized it while checking it additionally.
Recommended Posts