|
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.cpgz
to a RAM-based file system right after the kernel’s memory./init
script inside the initramfs.mdev
, the Busybox program that creates the /dev/*
files/boot/box
executable (i.e. in the root of the boot volume)The build-initrd.sh script builds the initramfs.cpgz
file from the initrd folder, placing the built Rust binary (from target/arm-linux-unknown-musleabi/release/box
) into the initramfs under /box
’.
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/repo
to 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 ./make-initrd.sh
).
ssh.conf
: when present, enables SSH serverdropbear_ecdsa_host_key
: when present, is used as host key for SSH. Otherwise a host key is generated on each boothostapd.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-Fibox
: 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 /init
script.
Audio is available as follows:
/usr/bin
and /usr/local/share/alsa
respectively in the initrd. Both are available in utils/build and can be cross-compiled using Docker (run build-using-docker.sh).aplay file.wav
from the command line to play some music!To disable, remove the modules or disable the relevant lines in the /init
script.
The device can auto-connect to a Wi-Fi network (on Pi Zero W). To use Wi-Fi, copy wpa_supplicant.conf to wpa_supplicant.conf
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_supplicant
is started using the wpa_supplicant.conf
file on the boot partition, specifying the networks to look forudhcpc
is started and asked to obtain an IP address from the wlan interface
udhcpc
obtains an address it will call /bin/dhcp.sh which will set up the IP adress and routingTo 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:
hostapd
will be started that sets up the WiFi interface to function as an access point192.168.2.1
udhcpd
will be started that will assign each client that connects an IP addressThe 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
command:
# 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[0]=wlan0
bssid[0]=b8:27:eb:74:33:b4
ssid[0]=MY_NETWORK_NAME
num_sta[0]=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=1
in /boot/wpa_supplicant.conf and /boot
is 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 rw
, start 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 /init
script).
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 config.txt
:
start_x=1
gpu_mem=128
Streaming video is possible using ffmpeg
:
/boot/bin/ffmpeg -i /dev/video0 -s 320x240 -r 1 -f mpegts udp://224.1.1.1: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), scp
(enables
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 --target
parameter)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"
The rppal
crate provides access to Raspberry Pi I/O from Rust. The manual can be found here.