Overview
The purpose of this project is to use a Raspberry Pi as a SIP phone or an intercom system entry panel (doorphone) for testing purposes.
With SIP-VoIP functionality, when used as a SIP phone, it can serve as an indoor monitor or a communication tool for family members. When used as a doorphone, it allows remote visitor verification, conversation with visitors, and door lock control.
Using Yocto, which enables the creation of embedded Linux distributions, I will build a custom image tailored for the Raspberry Pi 2. The mobile application used will be Linhome.
Components
-
Computer board: Raspberry Pi 2 (with camera, button, door switch, microphone, and speaker)
-
Mobile app: Android Linhome (for door control, visitor verification via video, and communication)
For microphone and speaker connections, the I²S (Inter-IC Sound) interface will be used. The googlevoicehat-soundcard has been selected as the sound device because it supports I²S audio input and output. ( hifiberry-dac was not selected as it does not support I²S audio input.)
-
MIC: INMP441 Datasheet
-
DAC: MAX98357 I2S Class-D Mono Amp
-
Sound Device:googlevoicehat-soundcard
-
picamera v1.3: Hardware Specification
Note : The button and door switch will not be physically connected. Instead, GPIO pin status checks and operations will be performed remotely via SSH connection.
Yocto Project
Use Version 5.0.4 Scarthgap
The Yocto Project ® 5.0.4 documentation
10 Images — The Yocto Project ® 5.0.4 documentation
11 Features — The Yocto Project ® 5.0.4 documentation
For the build process, a Docker container will be used, incorporating the build tool Poky and the necessary layers (meta-xxx) for the build.
In Yocto, a Recipe is a set of files that define build conditions and procedures, categorized by functionality or target device. A collection of these recipes forms a Layer.
Creating the Docker Image
A Dockerfile will be created to bundle the necessary recipes and build environment for compiling Linphone into a single container.
All work will be done within the custom directory scarthgap-raspberrypi2/.
scarthgap-raspberrypi2/dockerfile
FROM debian:11
ARG YOCTO_VERSION=scarthgap
ARG BITBAKE_TARGET=core-image-sato
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get -y upgrade
# Required Packages for the Host Development System
# https://docs.yoctoproject.org/ref-manual/system-requirements.html#required-packages-for-the-build-host
RUN apt-get update && \
apt-get install -y gawk wget git diffstat unzip texinfo gcc build-essential chrpath socat \
cpio python3 python3-pip python3-pexpect xz-utils debianutils iputils-ping python3-git \
python3-jinja2 libegl1-mesa libsdl1.2-dev pylint3 xterm python3-subunit mesa-common-dev \
zstd liblz4-tool screen nano && \
apt-get clean
# Additional host packages needed for build
RUN apt-get update && \
apt-get install -y file && \
apt-get clean
# Additional host packages needed for qemu
RUN apt-get update && \
apt-get install -y iproute2 && \
apt-get clean
# Additional host packages required by poky/scripts/wic
RUN apt-get update && \
apt-get install -y curl dosfstools mtools parted syslinux tree zip && \
apt-get clean
# Create a non-root user that will perform the actual build
RUN id build 2>/dev/null || useradd --uid 30000 --create-home build
RUN apt-get install -y sudo
RUN echo "build ALL=(ALL) NOPASSWD: ALL" | tee -a /etc/sudoers
# Fix error "Please use a locale setting which supports utf-8."
# See https://wiki.yoctoproject.org/wiki/TipsAndTricks/ResolvingLocaleIssues
RUN apt-get install -y locales
RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && \
echo 'LANG="en_US.UTF-8"'>/etc/default/locale && \
dpkg-reconfigure --frontend=noninteractive locales && \
update-locale LANG=en_US.UTF-8
ENV LC_ALL=en_US.UTF-8
ENV LANG=en_US.UTF-8
ENV LANGUAGE=en_US.UTF-8
USER build
WORKDIR /home/build
# Clone yocto
RUN git clone -b "${YOCTO_VERSION}" git://git.yoctoproject.org/poky
WORKDIR poky
RUN git clone -b "${YOCTO_VERSION}" git://git.openembedded.org/meta-openembedded
RUN git clone -b "${YOCTO_VERSION}" git://git.yoctoproject.org/meta-raspberrypi
WORKDIR /home/build/poky
#RUN bash -c 'source oe-init-build-env && bitbake ${BITBAKE_TARGET}'
CMD ["bash"]
Create the image using the Dockerfile:
$ docker build scarthgap-raspberrypi2 -t raspberrypi2
Then, start a container named raspi2 from the above image, specifying build as the shared folder between the host and the container, and modify the access permissions of the build directory:
$ docker run --name raspi2 -v ./build:/home/build/poky/build -ti raspberrypi2 bash
# sudo chown -R build:build build
Download the meta-bc Yocto branch for linphone-sdk into /home/build/poky:
# git clone https://gitlab.linphone.org/BC/public/meta-bc.git -b feature/yocto-kirkstone
Then, modify meta-bc/conf/layer.conf and set:
meta-bc/conf/layer.conf
LAYERSERIES_COMPAT_bc = "scarthgap"
Execute the initial command for build environment:
# source oe-init-build-env
You had no conf/local.conf file. This configuration file has therefore been
created for you from /home/build/poky/meta-poky/conf/templates/default/local.conf.sample
You may wish to edit it to, for example, select a different MACHINE (target
hardware).
You had no conf/bblayers.conf file. This configuration file has therefore been
created for you from /home/build/poky/meta-poky/conf/templates/default/bblayers.conf.sample
To add additional metadata layers into your configuration please add entries
to conf/bblayers.conf.
The Yocto Project has extensive documentation about OE including a reference
manual which can be found at:
https://docs.yoctoproject.org
For more information about OpenEmbedded see the website:
https://www.openembedded.org/
This is the default build configuration for the Poky reference distribution.
### Shell environment set up for builds. ###
You can now run 'bitbake <target>'
Common targets are:
core-image-minimal
core-image-full-cmdline
core-image-sato
core-image-weston
meta-toolchain
meta-ide-support
You can also run generated qemu images with a command like 'runqemu qemux86-64'.
Other commonly useful commands are:
- 'devtool' and 'recipetool' handle common recipe tasks
- 'bitbake-layers' handles common layer tasks
- 'oe-pkgdata-util' handles common target package tasks
Note : Running # source oe-init-build-env will automatically change the working directory to /home/build/poky/build and generate configuration files in the build/conf directory.
Editing Build Configuration Files
Layer List(Scarthgap Ver5.0)
Layer “meta-raspberrypi” Recipe (Machine) List(Scarthgap Ver5.0)
Edit the bblayers.conf, local.conf files in the build/conf directory shared with the host.
bblayers.conf : Specify the necessary meta-layers for the image build.
Add the following entries to include the required layers:
build/conf/bblayers.conf
# POKY_BBLAYERS_CONF_VERSION is increased each time build/conf/bblayers.conf
# changes incompatibly
POKY_BBLAYERS_CONF_VERSION = "2"
BSPDIR := "${@os.path.abspath(os.path.dirname(d.getVar('FILE', True)) + '/../..')}"
BBPATH = "${TOPDIR}"
BBFILES ?= ""
BBLAYERS ?= " \
${BSPDIR}/meta \
${BSPDIR}/meta-poky \
${BSPDIR}/meta-openembedded/meta-oe \
${BSPDIR}/meta-openembedded/meta-networking \
${BSPDIR}/meta-openembedded/meta-python \
${BSPDIR}/meta-openembedded/meta-multimedia \
${BSPDIR}/meta-yocto-bsp \
${BSPDIR}/meta-raspberrypi \
${BSPDIR}/meta-bc \
"
local.conf : Configure additional recipes, target machine, and build conditions for the image.
Additional Recipes:
IMAGE_INSTALL:append = …
Here, options to build the linphone-sdk from meta-bc will also be added. See below.
Target machine
MACHINE = “raspberrypi2”
Video extension feature
VIDEO_CAMERA = “1”
If other features are required, please refer to the meta-raspberrypi documentation and enable them.
build/conf/local.conf
MACHINE = "raspberrypi2"
VIDEO_CAMERA = "1"
DISTRO ?= "poky"
PACKAGE_CLASSES ?= "package_ipk"
EXTRA_IMAGE_FEATURES ?= "debug-tweaks ssh-server-openssh"
USER_CLASSES ?= "buildstats"
PATCHRESOLVE = "noop"
BB_DISKMON_DIRS ??= "\
STOPTASKS,${TMPDIR},1G,100K \
STOPTASKS,${DL_DIR},1G,100K \
STOPTASKS,${SSTATE_DIR},1G,100K \
STOPTASKS,/tmp,100M,100K \
HALT,${TMPDIR},100M,1K \
HALT,${DL_DIR},100M,1K \
HALT,${SSTATE_DIR},100M,1K \
HALT,/tmp,10M,1K"
CONF_VERSION = "2"
# Additional configuration, added recipes of Branch: kirkstone https://layers.openembedded.org/layerindex/branch/kirkstone/recipes
IMAGE_INSTALL:append = " alsa-plugins alsa-utils alsa-lib boost gettext glew libopus libsrtp libvpx pulseaudio v4l-utils raspi-gpio linphone-sdk openssl mpg123"
LICENSE_FLAGS_ACCEPTED = "commercial"
PACKAGE_CLASSES ?= "package_ipk"
PACKAGECONFIG:append:pn-linphone-sdk = " h264 mdns"
PACKAGECONFIG:append:pn-avahi = " libdns_sd"
BB_NUMBER_THREADS ?= "8"
CORE_IMAGE_EXTRA_INSTALL += "dhcpcd"
CORE_IMAGE_EXTRA_INSTALL += "init-ifupdown"
INHERIT += "rm_work"
Image Build: Bitbake
Run the bitbake command to build linphone (-sdk) (this may take several hours depending on the host machine’s specifications). Even if this standalone build is skipped, linphone-sdk will be included during the final image build, as it is specified in IMAGE_INSTALL:append in local.conf. Performing this standalone build in advance can minimize recovery costs in case of errors.
BitBake User Manual
# bitbake linphone-sdk
Build the final image by specifying core-image-sato:
# bitbake core-image-sato
The build image will be output to the following folder. Extract the compressed file below.
build/tmp/deploy/images/raspberrypi2/core-image-sato-raspberrypi2-2024xxxxxxxxxx.rootfs.wic.bz2
$ bzip2 -d core-image-sato-raspberrypi2-2024xxxxxxxxxx.rootfs.wic.bz2
Write image to SD
$ sudo dd if=core-image-sato-raspberrypi2-2024xxxxxxxxxx.rootfs.wic of=/dev/sda bs=1024
Since the image file size is between 1-2GB, to make efficient use of the SD card’s capacity, please expand the root directory to the maximum capacity of the SD card using a disk utility.
Creating the googlevoicehat Overlay
The googlevoicehat-soundcard.dtbo is a device tree overlay used to enable the microphone and DAC via the GPIO I2S interface. However, it is not compiled by default, so it must be manually compiled from the device tree source file.
Manually compile the overlay file from the googlevoicehat dts file (run this inside the raspi2 container).
# find . -name "*.dts" | grep googlevoicehat
./build/tmp/work-shared/raspberrypi2/kernel-source/arch/arm/boot/dts/overlays/googlevoicehat-soundcard-overlay.dts
# sudo apt install device-tree-compiler
# dtc -@ -I dts -O dtb -o googlevoicehat-soundcard.dtbo build/tmp/work-shared/raspberrypi2/kernel-source/arch/arm/boot/dts/overlays/googlevoicehat-soundcard-overlay.dts
Note : dtc (Device Tree Compiler) is a utility available on many Linux systems that compiles device tree source (DTS) files into binary device tree blob (DTB/DTBO) files.
After compiling, copy the generated device tree overlay googlevoicehat-soundcard-overlay.dtbo to the SD card’s /boot/overlays directory.
Boot Conditions via config.txt
Additional boot conditions can be set in /boot/config.txt.
https://www.raspberrypi.com/documentation/computers/config_txt.html
Specify Video Memory:
gpu_mem=128
Add Overlays:
dtoverlay=googlevoicehat-soundcard
Boot and Functionality Check (Video and Sound Output)
Insert the SD card with the image written to it into the Raspberry Pi and boot it up.
Video and Camera Functionality
Verify the camera connection:
# v4l2-ctl --all
Driver Info:
Driver name : bcm2835 mmal
Card type : mmal service 16.1
Bus info : platform:bcm2835_v4l2-0
Driver version : 6.6.22
Capabilities : 0x85200005
Video Capture
Video Overlay
Read/Write
Streaming
Extended Pix Format
Device Capabilities
.......
.......
Test One Shot:
# v4l2-ctl --stream-mmap --stream-count=1 --stream-to=capture.jpeg
Confirm Video Format:
# v4l2-ctl --list-formats
ioctl: VIDIOC_ENUM_FMT
Type: Video Capture
[0]: 'YU12' (Planar YUV 4:2:0)
[1]: 'YUYV' (YUYV 4:2:2)
[2]: 'RGB3' (24-bit RGB 8-8-8)
[3]: 'JPEG' (JFIF JPEG, compressed)
[4]: 'H264' (H.264, compressed)
[5]: 'MJPG' (Motion-JPEG, compressed)
[6]: 'YVYU' (YVYU 4:2:2)
[7]: 'VYUY' (VYUY 4:2:2)
[8]: 'UYVY' (UYVY 4:2:2)
[9]: 'NV12' (Y/UV 4:2:0)
[10]: 'BGR3' (24-bit BGR 8-8-8)
[11]: 'YV12' (Planar YVU 4:2:0)
[12]: 'NV21' (Y/VU 4:2:0)
[13]: 'RX24' (32-bit XBGR 8-8-8-8)
Streaming Test:
# v4l2-ctl --stream-mmap --stream-count=10
<<<<<<< 5.55 fps
<<<
Sound-related
Check the Playback and Capture devices:
# aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: vc4hdmi [vc4-hdmi], device 0: MAI PCM i2s-hifi-0 [MAI PCM i2s-hifi-0]
Subdevices: 1/1
Subdevice #0: subdevice #0
card 1: sndrpigooglevoi [snd_rpi_googlevoicehat_soundcar], device 0: Google voiceHAT SoundCard HiFi voicehat-hifi-0 [Google voiceHAT SoundCard HiFi voicehat-hifi-0]
Subdevices: 1/1
Subdevice #0: subdevice #0
# arecord -l
**** List of CAPTURE Hardware Devices ****
card 1: sndrpigooglevoi [snd_rpi_googlevoicehat_soundcar], device 0: Google voiceHAT SoundCard HiFi voicehat-hifi-0 [Google voiceHAT SoundCard HiFi voicehat-hifi-0]
Subdevices: 1/1
Subdevice #0: subdevice #0
Based on the above output, create the ALSA configuration file (.asoundrc) with dmixer support here.
.asoundrc
pcm.!default {
type asym
playback.pcm "plug:softvol"
capture.pcm "plug:capture_level"
}
ctl.!default {
type hw
card 1
}
pcm.voicehat_playback {
type hw
card 1
device 0
format S32_LE
}
pcm.voicehat_capture {
type hw
card 1
device 0
format S32_LE
}
pcm.softvol {
type softvol
slave.pcm "dmixed_playback"
control.name "Softvol Playback Volume"
control.card 1
min_dB -3.0
max_dB 30.0
}
pcm.dmixed_playback {
type dmix
ipc_key 1024
slave {
pcm "voicehat_playback"
period_time 0
period_size 1024
buffer_size 8192
rate 48000
format S32_LE
}
bindings {
0 0
1 1
}
}
pcm.capture_level {
type softvol
slave.pcm "voicehat_capture"
control.name "Level Capture"
control.card 1
min_dB -3.0
max_dB 30.0
}
Test Speaker:
# speaker-test -D plughw:1,0 -c2
# speaker-test -D plughw:1,0 -c2 --test=wav -w /usr/share/sounds/alsa/Front_Center.wav
Note : plughw:1,0 means card 1, device 0. In ~/.asound.conf : specify card 1
Test Record and Play
# arecord -d 5 -f cd -V mono test.wav
# aplay test.wav
Adjust volume and mic sensitive by alsamixer:
# alsamixer
Starting the Linphone Daemon
Make sure to create the necessary database files (directories) beforehand during the initial startup.
# cd /opt/belledonne-communications/bin/
# mkdir -p /home/root/.local/share/linphone
# touch /home/root/.local/share/linphone/x3dh.c25519.sqlite3
# touch /home/root/.local/share/linphone/linphone.db
Run Command:
# /opt/belledonne-communications/bin/linphone-daemon
daemon-linphone>
Command Options:
# /opt/belledonne-communications/bin/linphone-daemon --help
daemon-linphone [<options>]
Licence: Commercial
where options are :
--help Print this notice.
--dump-commands-help Dump the help of every available commands.
--dump-commands-html-help Dump the help of every available commands.
--pipe <pipepath> Create an unix server socket in the specified path to receive commands from. For Windows just use a name instead of a path.
--log <path> Supply a file where the log will be saved.
--factory-config <path> Supply a readonly linphonerc style config file to start with.
--config <path> Supply a linphonerc style config file to start with.
--disable-stats-events Do not automatically raise RTP statistics events.
--enable-lsd Use the linphone sound daemon.
-C Enable video capture.
-D Enable video display.
--auto-answer Automatically answer incoming calls.
Start the Linphone daemon with video capture, auto-answer enabled, and logging options activated.
# linphone-daemon --config linphonerc -C --auto-answer --log ./linphone_log
Linhone Daemon Help:
daemon-linphone>help
Status: Ok
adaptive-jitter-compensation [audio|video] [enable|disable]
answer <call_id>
audio-codec-disable <payload_type_number>|<mime_type>|ALL
audio-codec-enable <payload_type_number>|<mime_type>|ALL
audio-codec-get <payload_type_number>|<mime_type>
audio-codec-move <payload_type_number>|<mime_type> <index>
......
......
User Registration and Confirmation:
daemon-linphone>register sip:[email protected] sip.linphone.org password
Status: Ok
Id: 1
daemon-linphone>register-status ALL
Status: Ok
Id: 1
State: LinphoneRegistrationOk
Call:
daemon-linphone>call [email protected]
If the caller is within the same domain, the part after the “@” can be omitted.
daemon-linphone>call capitalfuse
Verify the send and receive functionality with the Linhome client.
The settings of Linhome are fixed through provisioning to the Linhome Free SIP Service . To modify the settings, you need to prepare a custom account manager, generate a QR code containing a custom URL, and scan it.