|Tommy van der Vorst 739001d587||1 month ago|
|src||1 month ago|
|.dockerignore||1 year ago|
|.gitignore||9 months ago|
|README.md||1 month ago|
|make-initrd.sh||6 months ago|
|start-emulator.sh||9 months ago|
|update-firmware.sh||1 month ago|
A Linux distribution for running Rust-based escape box experiences. It is bult to drive drive Raspberry hardware (GPIO, SPI, I2C) while booting as fast as possible.
The application can be run using a very minimal Linux-based image. The Raspberry Pi boots off SD cards. In order to boot, the SD card must contain a first partition that is VFAT formatted. On this partition, the Raspberry bootloader reads the config.txt file for boot options. This file specifies the kernel to load, which initramfs to load, what device tree options to enable, and the command line for the kernel. Our boot image includes a Linux kernel and device tree overlay files borrowed from TinyCore Linux piCore.
Our image boots as follows:
initramfs.cpgzto a RAM-based file system right after the kernel’s memory.
/initscript inside the initramfs.
mdev, the Busybox program that creates the
/boot/boxexecutable (i.e. in the root of the boot volume)
The script places the built
initramfs.cpgz in the
build/boot folder. It will also copy all files from the boot folder there.
In order to create a bootable SD card, simply format it as an MBR-based disk with (at least) a single VFAT (“MS-DOS”) partition (as first partition). Then, copy all contents of the
build/boot directory to the root of the VFAT partition on the SD card.
./update-firmware.sh path/to/firmware/repoto update.
Note that the current kernel and modules were taken from piCore, not the Raspberry Pi repository linked above.
The resulting initramfs image can be tested in a QEMU emulator.
First, install the QEMU ARM system using
apt install qemu-system-arm. Then, use
./start-emulator.sh to start an ARM-based emulated machine based on the initramfs in
./build/boot/initramfs.cpgz (this should be built first using
ssh.conf: when present, enables SSH server
dropbear_ecdsa_host_key: when present, is used as host key for SSH. Otherwise a host key is generated on each boot
hostapd.conf: when present, hostapd is started with the configuration file (Wi-Fi access point functionality)
wpa_supplicant/.conf: when present, the device will try to connect to Wi-Fi
box: will be executed during init; after devices have been initialized but before networking has started.
The SD card’s contents are mounted as
/boot and read-only by default. To mount as read-wite:
mount -o remount,rw /boot
It is strongly advised to keep
/boot mounted read-only. Many SD cards perform processing on write (i.e. to deal with
bad memory blocks) and data can become corrupted if power is interrupted during this.
When used on a Pi Zero, a USB serial interface is provided. Plug in the Pi using the ‘USB’ (not ‘PWR’) port to a computer.
On macOS, look for a “/dev/cu.usbmodemXXXX” device and connect to it using
screen /dev/cu.usbmodemXXXX. By default you
will be provided with a root shell.
To disable (i.e. use the Pi as USB host rather than device), remove the modules or disable the relevant lines in the
Audio is available as follows:
/usr/local/share/alsarespectively in the initrd. Both are available in utils/build and can be cross-compiled using Docker (run build-using-docker.sh).
aplay file.wavfrom the command line to play some music!
To disable, remove the modules or disable the relevant lines in the
The device can auto-connect to a Wi-Fi network (on Pi Zero W). To use Wi-Fi, copy wpa_supplicant.conf to
in the root of the SD card and add your network information. The device will then attempt to connect to a network on the list on boot.
This is implemented as follows:
wpa_supplicantis started using the
wpa_supplicant.conffile on the boot partition, specifying the networks to look for
udhcpcis started and asked to obtain an IP address from the wlan interface
udhcpcobtains an address it will call /bin/dhcp.sh which will set up the IP adress and routing
To make this work on other Raspberry devices than the Zero W, supply the necessary firmware files (can be obtained from e.g. Raspbian) and possibly add other modules that are required.
To disable Wi-Fi, simply ensure there is no
wpa-supplicant.conf in the boot folder. Modules may be deleted in this case
to make the image smaller if so desired.
The device can function as an access point. In order to enable this functionality, place a
hostapd.conf file in the boot
directory (an example is provided here). When this file is present it will cause the following
to happen on boot:
hostapdwill be started that sets up the WiFi interface to function as an access point
udhcpdwill be started that will assign each client that connects an IP address
The access point mode will create and use a virtual interface (
ap0). Simultaneously connecting to Wi-Fi in station mode
is supported and will be attempted when
wpa_supplicant.conf is present as well. Note however that there may be issues
doing this (some people have reported that the AP and station mode must use the same channel, others report that AP will
auto-switch to the station mode channel).
To query hostapd, use the
# hostapd_cli status Selected interface 'wlan0' state=ENABLED phy=phy0 freq=2412 num_sta_non_erp=0 num_sta_no_short_slot_time=0 num_sta_no_short_preamble=0 olbc=0 num_sta_ht_no_gf=0 num_sta_no_ht=0 num_sta_ht_20_mhz=0 num_sta_ht40_intolerant=0 olbc_ht=0 ht_op_mode=0x0 cac_time_seconds=0 cac_time_left_seconds=N/A channel=1 secondary_channel=0 ieee80211n=0 ieee80211ac=0 ieee80211ax=0 beacon_int=100 dtim_period=2 supported_rates=02 04 0b 16 0c 12 18 24 30 48 60 6c max_txpower=20 bss=wlan0 bssid=b8:27:eb:74:33:b4 ssid=MY_NETWORK_NAME num_sta=0
To be able to connect to Wi-Fi without editing the configuration file, WPS (Wireless Protected Setup) can be used. This works as follows:
wpa_cli -iwlan0 -s /var/run/wpa_supplicant/ wps_pbc. This will start scanning and autoconnect to any network in WPS mode. This command could be triggered from your application, i.e. in response to sone user input.
update_config=1in /boot/wpa_supplicant.conf and
/bootis mounted read-write, the configuration will be updated and the network will be remembered.
A typical sequence, upon user requesting WPS pairing, would be to first mount /boot
wps_pbc, then periodically
check if pairing has succeeded (and time-out), then mount
/boot read-only again.
Other useful commands:
# Get the current Wi-Fi status: wpa_cli -iwlan0 -s /var/run/wpa_supplicant/ status # Cancel the WPS pairing operation (does nothing when no pairing is being performed): wpa_cli -iwlan0 -s /var/run/wpa_supplicant/ wps_cancel
As the Raspberry Pi has no (battery-backed) RTC, the system date and time is set to 01-01-2020 00:00:00 on each boot. This ensures that the system date and time is at least reasonable and makes for an easy reference point.
If Wi-Fi is enabled (if
wpa-supplicant.conf is present in
/boot), an attempt will be made to start
ntpd every five seconds. If
ntpd starts it will periodically fetch and update the system date/time from
nl.pool.ntp.org (please edit the NTP server name in the
The system loads V4L modules and supports the Raspberry Pi camera.
Disabling support for the camera is possible by removing the module loading from the
/init script, as well as the
following lines from
Streaming video is possible using
/boot/bin/ffmpeg -i /dev/video0 -s 320x240 -r 1 -f mpegts udp://184.108.40.206:1234?pkt_size=1316
Video encoding with libx264 does not appear to work for some reason (“Illegal instruction”). Additionally this requires
gpu_mem=16 to be set in config.txt.
To set the root password, create a file
root_password.conf in the boot directory with the root password; it will be set
on each boot.
First, set a root password. Then, create an empty file “ssh.conf” in the boot directory. This will cause the SSH server to start on boot (currently, generates host keys on each boot, so you may run into complaints from your SSH client saying that you are connecting to a different host after each reboot).
To start an SSH server from the command line:
passwd # change root user password first mkdir /etc/dropbear mkdir -p /home/root dropbear -R -F -E
Other installed programs are
dbclient (SSH client),
dropbearkey (use to generate keys automatically),
the use of scp, both client and server).
Some functions/programs require randomness (‘entropy’) - e.g. Wi-Fi (wpa_supplicant) will only connect once sufficient randomness. Unfortunately, there are very few sources of randomness available on a Pi running headless (the built in hardware random number generator, while available at
/dev/hwrng, is not used by the kernel!). A tool such as
rngd can use the hardware RNG to seed the kernel entropy pool, but is rather difficult to compile.
The box-linux image includes the
haveged daemon. While it generates weaker entropy,it does allow for very fast connection to Wi-Fi upon boot and prevents slowdowns.
Static NodeJS binaries for armv6l can be downloaded here.
cargo build(optionally select a specific architecture with the
readelf --arch-specific ./target/debug/BINARYNAME
Verification output should look as follows for ARMv6:
Attribute Section: aeabi File Attributes Tag_CPU_name: "ARM v6" Tag_CPU_arch: v6 Tag_ARM_ISA_use: Yes Tag_THUMB_ISA_use: Thumb-1 Tag_ABI_PCS_GOT_use: GOT-indirect Tag_ABI_PCS_wchar_t: 4 Tag_ABI_FP_rounding: Needed Tag_ABI_FP_denormal: Needed Tag_ABI_FP_exceptions: Needed Tag_ABI_FP_number_model: IEEE 754 Tag_ABI_align_needed: 8-byte Tag_ABI_enum_size: int Tag_CPU_unaligned_access: v6 Tag_ABI_FP_16bit_format: IEEE 754
rustup target add arm-unknown-linux-musleabi sudo apt install binutils-arm-linux-gnueabihf
brew install arm-linux-gnueabihf-binutils rustup target add arm-unknown-linux-musleabi
Note, on macOS,
cargo build may complain it cannot find the linker. To fix, invoke as follows:
PATH=$PATH:/usr/local/bin cargo build
This builds for ARMv6 (hard float) which is necessary for the Pi Zero and 1.
In order to build ARMv7 (hard float), use the following:
rustup target add armv7-unknown-linux-musleabihf sudo apt-get install gcc-multilib-arm-linux-gnueabihf
Then, change cargo config accordingly:
[build] target = "arm-unknown-linux-gnueabihf"
rppal crate provides access to Raspberry Pi I/O from Rust. The manual can be found here.