Za poslední roky se mi nashromáždilo mimo jiné několik Raspberry Pi Zero. Napadlo mě z jednoho takového vyrobit webkameru. Běžné řešení by spočívalo v použití USB Wi-Fi karty pro připojení k lokální síti a samostatného USB kabelu pro přivedení napájecího napětí.
Ovšem je-li jako napájecí zdroj použit USB port nějakého počítače, nebo v tomto případě domácího routeru, mělo by být možné týmž kabelem vést napájení a data a vyhnout se tak zbytečnému použití bezdrátového spoje. Ukázalo se, že to jde celkem jednoduše.
Hostitelé a zařízení
Na začátek si připomeňme několik pojmů z terminologie rozhraní USB. Toto rozhraní propojuje vždy hostitele (host) a zařízení/periferie (device/peripheral). Hostitel obvykle používá zdířku typu A, tedy velký konektor s obdélníkovým průřezem, řídí komunikaci na lince a také poskytuje napájecí napětí zařízením. Ta mohou používat některou ze zdířek typu B, Mini-B, nebo Micro-B. Pro přehlednost vynechávám univerzální konektory typu C.
Toto striktní rozdělení bylo poprvé narušeno rozšiřujícím standardem USB On-The-Go. Jedná se o schopnost USB zařízení za jistých okolností převzít roli hostitele. V praxi tedy například připojit zařízení jako flash disk k mobilnímu telefonu, který v tuto chvíli zaujme roli hostitele, přestože běžně vystupuje také v roli zařízení.
OTG řadič v Raspberry Pi
Jádro každého Raspberry Pi tvoří System-on-Chip od firmy Broadcom, obsahující právě jeden řadič USB rozhraní s podporou OTG. Protože je Raspberry Pi koncipováno jako malý počítač, tedy z pohledu USB hostitel, je tento režim provozován dominantně. Na modelech B Raspberry Pi verze 1, 2 nebo 3 ani jiný než hostitelský režim bez páječky nezprovozníme, protože na USB je napevno připojen čip LAN9512/LAN9514, což je kombinace USB rozbočovače a ethernetového adaptéru. U výrazně méně populárních modelů A tento čip chybí, takže režim zařízení zprovoznit lze, je však třeba vypořádat se s pro tento účel nepatřičným konektorem typu A.
V případě modelů Raspberry Pi Zero a Raspberry Pi 4 je však USB řadič přímo přístupný a to dokonce ve formě příslušného konektoru typu Micro-B, resp. C UFP (Upstream facing port – tedy port zařízení, směřující k hostiteli). Stačí je tedy připojit běžným USB kabelem k USB portu běžného počítače a hardwarově máme vše zařízeno. Zbývá vyřešit softwarovou část.
Úprava Raspberry Pi OS
Aby se Raspberry Pi začalo chovat jako USB zařízení, je třeba ručně přepnout režim USB řadiče. Toho dosáhneme přidáním následujícího řádku do souboru /boot/config.txt
:
dtoverlay=dwc2,dr_mode=peripheral
Tímto se po startu nastaví řadič do správného režimu USB zařízení. Nyní už zbývá správně obsloužit výzvy hostitele v softwaru, tedy vyplnit příslušné deskriptory a k jednotlivým komunikačním kanálům připojit příslušné ovladače. Ty se v Linuxu označují jako USB Gadget, aby se odlišily od ovladačů na straně hostitele, které jsou pro Linux mnohem běžnější.
Virtuální sériová linka
Nejjednodušší je použít nějakou z konfigurací, které jsou předem připravené – to pak stačí jen zavést příslušný modul. Třeba zavedením modulu s názvem g_serial
začneme emulovat zařízení třídy CDC ACM, tedy sériovou linku. Vznikne zařízení /dev/ttyGS0
, kterým můžeme komunikovat s hostitelem na druhém konci USB kabelu. Aby se pomocí této linky mohl hostitel k zařízení přihlásit, musíme na ní spustit getty
, takto:
# systemctl enable serial-getty@ttyGS0.service
# systemctl start serial-getty@ttyGS0.service
Na straně hostitele, pokud jde o Linux, bude sériový port detekován jako zařízení /dev/ttyACM0
a pro přihlášení k našemu zařízení můžeme použít třeba nástroj screen
:
$ screen /dev/ttyACM0 115200
Virtuální ethernet
Pokud místo modulu g_serial
zavedeme modul s názvem g_ether
, vzniknou na obou koncích USB kabelu virtuální ethernetové síťové karty. Budou se pravděpodobně jmenovat usb0
a MAC adresy obou stran budou náhodně vygenerované při každém zavedení modulu. Aby náhodné nebyly, dají se nastavit jako parametry modulu dev_addr
a host_addr
.
Pokud bychom chtěli kombinaci ethernetu a sériové linky v jednom zařízení, zařídí to modul g_cdc
. Existují i další moduly pro jiné funkce USB zařízení, je však také možné vytvořit si vlastní zařízení na míru.
Jde to i bez SD karty
Málo se ví o tom, že Raspberry Pi nižší verze než 4 umí startovat v režimu USB zařízení. K pokusu o start dojde však jen tehdy, když selže start z SD karty. Na straně hostitele musí běžet utilita rpiboot
, která nejprve do zařízení nahraje bootloader ( bootcode.bin
). Ten si pak požádá o firmware grafického subsystému ( start.elf
) a případně i další relevantní soubory, které se běžně nachází v oddílu /boot
na SD kartě.
Ve výchozím stavu utilita do Raspberry Pi nahraje minimalistický firmware, který z něj udělá jen čtečku paměťové karty splňující standard USB Mass Storage. Účelné to je hlavně pro oživování modelů řady Compute Module, které mají eMMC úložiště napevno připájené na základní desce.
Druhý zveřejněný firmware je mnohem zajímavější: umožňuje použít Raspberry Pi Zero jako GPIO expander připojený přes USB k libovolnému počítači. V tomto případě je přes USB nastartováno minimalistické linuxové prostředí, ve kterém je zprovozněna emulace ethernetu přes USB, nastavena místní IPv6 adresa fe80::1
a spuštěn démon pigpiod
. K němu se pak může připojit třeba knihovna gpiozero
.
Bohužel, na mém počítači se Raspberry Pi Zero nastartovat nepodařilo. Nejspíše je na vině chyba popsaná v poznámce návodu flashování Compute module, totiž že v bootloaderu pro BCM2835 je chyba, která způsobuje odeslání poněkud nekorektního USB paketu, který dokáže některé hostitelské řadiče zmást. Při pokusu s Rapsberry Pi 4 v roli hostitele vše fungovalo správně.
Vlastní zařízení na přání
I tak se ale můžeme inspirovat zdrojovými kódy zmíněného linuxového prostředí, zejména tedy spouštěcím skriptem, který nastavuje parametry USB zařízení přímo pomocí rozšíření linuxového jádra zvaného configfs. Díky tomu můžeme nastavit vlastní identifikátory výrobce a produktu (kód výrobce by měl správně být přidělen asociací USB IF), nastavit vlastní třídy zařízení a připojit vlastní kombinaci ovladačů.
Skript jsem upravil tak, aby MAC adresy virtuální síťové karty byly stabilní na obou koncích USB kabelu, odvozené ze sériového čísla desky a připravil unit-file pro systemd, který zajistí spuštění skriptu po startu Raspberry Pi OS. Tím je softwarová část instalace USB zařízení kompletní.
Opatření na straně OpenWRT
Jak jsem psal v úvodu, mým cílem bylo přivést k Raspberry Pi Zero napájení i data jedním USB kabelem z domácího routeru, na kterém běží OpenWRT. Pro podporu Ethernetu přes USB je třeba nainstalovat balíček kmod-usb-net-cdc-ether
. Po připojení zařízení vznikne v systému síťové zařízení s názvem usb0
.
Nejjednodušší zapojení do sítě spočívá ve vytvoření samostatné síťky pro takové zařízení se samostatným rozsahem IP adres, samostatnou konfigurací DHCP serveru a případně i zónou ve firewallu. V takovém případě si při každém připojení kabelu OpenWRT všimne, že zařízení vzniklo a vše nakonfiguruje automaticky.
Pokud bychom ovšem chtěli připojit zařízení do existujícího mostu, je to drobná komplikace, neboť v době, kdy se například br-lan
sestavuje, zařízení usb0
ještě neexistuje. Ne zcela elegantní, ale funkční řešení představuje jednoduchý skript, který reaguje na objevení síťové karty s konkrétní MAC adresou. Rozhraní je přejmenováno na usb-rpi0
a připojeno do správného mostu. Skript je umístěn v /etc/hotplug.d/net/99-rpi0
:
#!/bin/sh
[ "$ACTION" = add ] || exit
MAC="$(cat /sys/class/net/$INTERFACE/address)"
[ "$MAC" = "fa:00:16:ff:c0:ae" ] && {
logger hotplug-usb Renaming $INTERFACE to usb-rpi0
ip link set dev "$INTERFACE" down
ip link set dev "$INTERFACE" name "usb-rpi0"
ip link set dev "usb-rpi0" up master br-lan
}
Webkamera připojená jedním kabelem
Na konci tohoto procesu tedy máme Raspberry Pi Zero připojené jak k napájení, tak i k síti prostřednictvím jediného kabelu. Jeho další využití je již jen na naší fantazii. Pro použití v roli webové kamery můžu doporučit buď webovou aplikaci RPi Cam Web Interface, nebo přímo živý stream na YouTube či obdobnou platformu, který díky hardwarové akceleraci kódování videa není problém ani pro tak výkonově omezený počítač.
Uvedený postup, s výjimkou podpory provozu bez SD karty, funguje naprosto stejně i s Raspberry Pi verze 4, kde je pro hostitelské USB konektory použit externí řadič a původní vestavěný řadič USB s funkcí OTG je připojen k datovým kontaktům napájecího konektoru USB typu C. Jen v tomto případě může být pro hostitelský systém problém zajistit dostatečně tvrdé napájení, pokud bude Raspberry Pi 4 pracovat na plný výkon a případně ještě napájet další periferie.