Saturday, 27 July 2024

USB HID gadget with Raspberry Pi OS

For some reason the wifi on my PiKVM installation on my Raspberry Pi Zero 2 W described in my previous post has become unstable. Also keeping underlying Arch Linux updated with my ansible update script is a challenge due to PiKVM keeps file system read-only by default. To solve both issues I moved USB HID gadget functionality to Raspberry Pi OS. Started with flashing latest Bookworm 64 bit Lite version to my Raspberry Pi Zero 2 W. I broadly used the configuration in this post.

jordana@pi02w2:~ $ sudo nano /boot/firmware/config.txt
dtoverlay=dwc2,dr_mode=peripheral
jordana@pi02w:~ $ sudo nano /boot/firmware/cmdline.txt
modules-load=dwc2,g_hid rootwait
jordana@pi02w2:~ $ sudo nano /etc/modules
dwc2
libcomposite
jordana@pi02w2:~ $ sudo nano /usr/bin/hid_keyboard_setup.sh
#!/bin/bash
cd /sys/kernel/config/usb_gadget/
mkdir -p g1
cd g1
echo 0x1d6b > idVendor # Linux Foundation
echo 0x0104 > idProduct # Multifunction Composite Gadget
echo 0x0100 > bcdDevice
echo 0x0200 > bcdUSB
mkdir -p strings/0x409
echo "0123456789" > strings/0x409/serialnumber
echo "Raspberry Pi" > strings/0x409/manufacturer
echo "Pi Zero Keyboard" > strings/0x409/product
mkdir -p configs/c.1/strings/0x409
echo "Config 1: HID Keyboard" > configs/c.1/strings/0x409/configuration
echo 120 > configs/c.1/MaxPower
mkdir -p functions/hid.usb0
echo 1 > functions/hid.usb0/protocol
echo 1 > functions/hid.usb0/subclass
echo 8 > functions/hid.usb0/report_length
echo -ne \\x05\\x01\\x09\\x06\\xa1\\x01\\x05\\x07\\x19\\xe0\\x29\\xe7\\x15\\x00\\x25\\x01\\x75\\x01\\x95\\x08\\x81\\x02\\x95\\x01\\x75\\x08\\x81\\x03\\x95\\x05\\x75\\x01\\x05\\x08\\x19\\x01\\x29\\x05\\x91\\x02\\x95\\x01\\x75\\x03\\x91\\x03\\x95\\x06\\x75\\x08\\x15\\x00\\x25\\x65\\x05\\x07\\x19\\x00\\x29\\x65\\x81\\x00\\xc0 > functions/hid.usb0/report_desc
ln -s functions/hid.usb0 configs/c.1/
ls /sys/class/udc > UDC
jordana@pi02w2:~ $ sudo chmod +x /usr/bin/hid_keyboard_setup.sh
jordana@pi02w2:~ $ sudo nano /etc/rc.local
/usr/bin/hid_keyboard_setup.sh

I added the script with support for 0-9 numeric keys, arrow keys and mapped Enter and Esc for remotely driving UI on a STB that support USB HID keyboard to my git repository here.

UPDATE

Configuration above have been updated that supports Raspberry Pi 4 also with its OTG capability on its USB-C port. In order to connect Raspberry Pi 4 to a low power USB 2.0 keyboard port I had to provide power via GPIO. Pimoroni has a nice solution for this here

Monday, 22 July 2024

Low latency WebRTC streaming from HDMI source via zerotier

In order to achieve low latency WebRTC streaming over the Internet I found MediaMTX.

I installed it on my Raspberry Pi 4 on latest 64 bit Bookworm image as follows.

jordana@pi4g:~ $ mkdir mediamtx && cd mediamtx
jordana@pi4g:~/mediamtx $ wget https://github.com/bluenviron/mediamtx/releases/download/v1.8.4/mediamtx_v1.8.4_linux_arm64v8.tar.gz
jordana@pi4g:~/mediamtx $ tar -xvzf mediamtx_v1.8.4_linux_arm64v8.tar.gz 
jordana@pi4g:~/mediamtx $ nano mediamtx.yml
webrtcIPsFromInterfaces: no
webrtcAdditionalHosts: [zerotier_IP_of_Raspberry]
paths:
  cam:
    runOnInit: ffmpeg -f v4l2 -pix_fmt yuv420p -s:v 1280x720 -i /dev/video0 -f alsa -i hw:3,0 -filter:v fps=50 -c:v libx264 -c:a libopus -async 50 -preset ultrafast -tune zerolatency -f rtsp rtsp://localhost:$RTSP_PORT/$MTX_PATH
    runOnInitRestart: yes
jordana@pi4g:~/mediamtx $ ./mediamtx 

In order to find out the audio configuration hw:[card],[device] of the USB capture device I used arecord tool.

jordana@pi4g:~ $ arecord -l
**** List of CAPTURE Hardware Devices ****
card 3: MS2109 [MS2109], device 0: USB Audio [USB Audio]
  Subdevices: 0/1
  Subdevice #0: subdevice #0

To stream WebRTC from the other side of a zerotier connection I simply had to use a browser to load this URL.

http://zerotier_IP_of_Raspberry:8889/cam/

I had to use Chromium from Crostini to have a stream within a minute but once it is up&running the latency and quality is fantastic!

UPDATE

After a few days of working stable the need arose to make a service out of it as described in the MediaMTX GitHub.

jordana@pi4g:~/mediamtx $ sudo mv mediamtx /usr/local/bin/
jordana@pi4g:~/mediamtx $ sudo mv mediamtx.yml /usr/local/etc/
jordana@pi4g:~ $ sudo tee /etc/systemd/system/mediamtx.service >/dev/null << EOF
[Unit]
Wants=network.target
[Service]
ExecStart=/usr/local/bin/mediamtx /usr/local/etc/mediamtx.yml
[Install]
WantedBy=multi-user.target
EOF
jordana@pi4g:~ $ sudo systemctl daemon-reload
jordana@pi4g:~ $ sudo systemctl enable mediamtx
jordana@pi4g:~ $ sudo systemctl start mediamtx

Sunday, 14 July 2024

Finally playing with OBS an Pi KVM

I have heard about OBS several times before and I always wanted to try it. Recently I got asked by a developer to use OBS to provide remote access to an HDMI signal. I am a happy user of a cheap HDMI to USB capture card for a long time but so far I have only used it with Camera application running directly on Android or ChromeOS host where it was connected to. Fortunately OBS is supported by Raspberry Pi OS:

jordana@pi4g:~ $ apt search obs-studio
Sorting... Done
Full Text Search... Done
obs-studio/stable,now 29.0.2+dfsg-1+b1 arm64
  recorder and streamer for live video content

Here came the first issue by launching OBS I bumped into this error message:

It did not take too long to find that we need an environmental variable to set on Raspberry Pi 4 and Raspberry Pi 5 so from command line it looks like this:

jordana@pi4g:~ $ LIBGL_ALWAYS_SOFTWARE=1 obs
jordana@pi5g:~ $ MESA_GL_VERSION_OVERRIDE=3.3 obs

I have bought a new HDMI capture card for this excersize from Amazon. To make it work, I only had to add it as a v4l2 video capture device as source and restart OBS. As during first time install OBS asked for a stream key I created an account on Twitch to get a stream key. Streaming via Twitch was also working more or less - althought I had to reduce framerate and resolution and it remained barely watchable. I guess the Raspberry Pi 4 with software only encoding is struggling with Motion JPEG source from the HDMI capture card. The delay was also excessive for the use case I had hence ultimately I will not use OBS let alone streaming but it was definitely a nice encounter with OBS.

Another ask from the developer was how to extend IR remote control over the network. While I am still playing with the idea there was a nice workaround possible to extend USB keyboard over the network. After some research, it turned out that OTG functionality in Linux have a usb gadget mode that does exactly what I need here. After a couple of unsuccessful tries I found this DIY PiKVM v2 in this github repository. As I have a couple of spare pi02w that supports OTG functionality and I only needed keyboard functionality I flashed the corresponding image on an SD card and added wifi password to pikvm.txt in PIBOOT partition. Please note that I did not have to escape \ in my Wifi password as in the guide for whatever the reason is. The nice feature of a pi02w is that its power consumption during light use largely remains below 5V/500mA standard USB 2 specification hence it can be a direct substitution of a USB keyboard where pi02w is only powered via the USB port itself. I have also added remote Tailscale access for this Pi KVM project that is beautifully integrated on my Chromebook with an Android app as such can nicely co-exist with Zerotier installation in the Linux subsystem Crostini.

UPDATE

After a while I started to have instable wifi connection to my Pi Zero 2 W. I added the following one line pikvm.txt to PIBOOT:

ENABLE_OTG_SERIAL=1

This way I could connect from a Pi 4 as follows:

jordana@pi4g:~ $ minicom --device /dev/ttyACM0 --baudrate 115200

Adding one line to this config file resolved my connection issue:

[root@pikvm ~]# vi /etc/wpa_supplicant/wpa_supplicant-wlan0.conf
network={
    ssid="broadcom"
    psk=*************
    key_mgmt=WPA-PSK-SHA256
}








Monday, 25 December 2023

Raspberry Pi OS moves to Network Manager

There has been a major change on network configuration coming on Raspberry Pi OS for quite some time now. Along with the introduction of Debian Bookworm, the old dhcpcd has been replaced with Network Manager. This changes the way how I connect back to my parents that requires new configuration. Some of the configuration is similar to that of Inovato's Quadra that I used during the Raspberry Pi shortage.

To demonstrate the configuration here I used my 4GB Raspberry Pi 4 for which Imager 1.8.3 recommends Raspberry Pi OS 64-bit based on Debian Bookworm now. While this image includes desktop, I still prefer to use text based user interface to configure Network Manager. Please note the elegant way 5GHz is used hence higher speed and less interference.

jordana@pi4g:~ $ sudo nmtui
Edit a connection
<Add>
Device wlan0
SSID broadcom
Mode <Access Point>
Channel <A (5 GHz)>
Security 
<WPA & WPA2 Personal>
Password ********************
IPv4 CONFIGURATION <Shared>
<Show>
Addresses 192.168.3.1/24
<OK>
<Back>
Quit

This configuration automatically sets up DHCP on the access point with dnsmasq built into Network Manager hence separate dnsmasq should not be installed from apt package repository. However we will need to use Google DNS server as DHCP option hence we need to add a configuration file for that.

jordana@pi4g:~ $ sudo nano /etc/NetworkManager/dnsmasq-shared.d/server.conf
dhcp-option=6,8.8.8.8

Text based user interface does not allow setting the necessary IPv4 routing rules so for that I need to use command line interface of Network Manager. First we need to check UUID for the connection created above on wlan0 device.

jordana@pi4g:~ $ nmcli connection
NAME                UUID           TYPE      DEVICE     
Wi-Fi connection 1  6a55d164-xxxx  wifi      wlan0      
jordana@pi4g:~ $ sudo nmcli connection modify 6a55d164-xxxx ipv4.routing-rules "priority 1 from 192.168.3.0/24 table 1"

Next we need to install zerotier and set up its IP address and authorization on Zerotier Central web console to get the interface and IP address we want.

jordana@pi4g:~ $ ip a
4: zt5u4vfg76: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 2800 qdisc pfifo_fast state UNKNOWN group default qlen 1000
    inet 192.168.193.3/24 brd 192.168.193.255 scope global zt5u4vfg76

Finally, after setting up Managed Routes 192.168.3.0/24 via 192.168.193.3 on Zerotier Central web console we need to dispatch default route towards my parents Raspberry Pi.

jordana@pi4g:~ $ sudo nano /etc/NetworkManager/dispatcher.d/10-zt5u4vfg76-up
#!/bin/bash
if [ "$1" = "zt5u4vfg76" ] && [ "$2" = "up" ]; then
  sudo /sbin/ip route add default via 192.168.193.4 table 1
fi
jordana@pi4g:~ $ sudo chmod +x /etc/NetworkManager/dispatcher.d/10-zt5u4vfg76-up

My friend struggled with the automatic choice of TKIP by Network Manager and needed the following configuration to make it work with iPad:

sudo nmcli con modify "Wi-Fi Hot" 802-11-wireless-security.proto rsn
sudo nmcli con modify "Wi-Fi Hot" 802-11-wireless-security.pairwise ccmp

UPDATE

Even though Network Manager takes care about MASQUERADE I realised that I still need this iptables rule to ensure zerotier-cli can access zerotier service locally.

quadra@inovato:~$ sudo iptables -t nat -I POSTROUTING -o lo -j ACCEPT
quadra@inovato:~$ sudo iptables-save -f /etc/iptables.rules
quadra@inovato:~$ sudo nano /etc/rc.local
iptables-restore /etc/iptables.rules

Sunday, 21 May 2023

Enabling TRIM with USB SSD

From time to time I had hiccups with my USB 3 SSD when run Raspberry Pi diagnostics (agnostics). Occasionally it did not reach the 160MB/s advertised write speed. They were a bargain on amazon so I thought I have to live with this. 64GB SSD drive was 10 GBP while the USB 3 enclosure was 20 GBP.

64 GB SSD drive

USB 3 enclosure

This article from Jeff Geerling explains how to configure TRIM on SSD:

Enabling TRIM on an external SSD on a Raspberry Pi

Initially I observed that TRIM was not configured on my boot drive:

jordana@pi4g:~ $ sudo fstrim -v /
fstrim: /: the discard operation is not supported
jordana@pi4g:~ $ lsblk -D
NAME   DISC-ALN DISC-GRAN DISC-MAX DISC-ZERO
sda           0      0B       0B         0
├─sda1        0      0B       0B         0
└─sda2        0      0B       0B         0

I installed the SCSI tools based on the article and confirmed that my drive supports TRIM:

jordana@pi4g:~ $ sudo apt-get install -y sg3-utils lsscsi
jordana@pi4g:~ $ sudo sg_vpd -p bl /dev/sda
Block limits VPD page (SBC):
  Maximum unmap LBA count: 4194240
  Maximum unmap block descriptor count: 8
jordana@pi4g:~ $ sudo sg_vpd -p lbpv /dev/sda
Logical block provisioning VPD page (SBC):
  Unmap command supported (LBPU): 1

I checked that the provisioning mode is full and changed it to unmap:

jordana@pi4g:~ $ sudo find /sys/ -name provisioning_mode -exec grep -H . {} + | sort
/sys/devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb2/2-1/2-1:1.0/host0/target0:0:0/0:0:0:0/scsi_disk/0:0:0:0/provisioning_mode:full
jordana@pi4g:~ $ sudo su
root@pi4g:/home/jordana# echo unmap > /sys/devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb2/2-1/2-1:1.0/host0/target0:0:0/0:0:0:0/scsi_disk/0:0:0:0/provisioning_mode

This enabled TRIM and made my write speed consistent:

jordana@pi4g:~ $ sudo find /sys/ -name provisioning_mode -exec grep -H . {} + | sort
/sys/devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb2/2-1/2-1:1.0/host0/target0:0:0/0:0:0:0/scsi_disk/0:0:0:0/provisioning_mode:unmap
jordana@pi4g:~ $ lsblk -D
NAME   DISC-ALN DISC-GRAN DISC-MAX DISC-ZERO
sda           0      512B       4G         0
├─sda1        0      512B       4G         0
└─sda2        0      512B       4G         0
jordana@pi4g:~ $ sudo fstrim -v /
/: 42.3 GiB (45465530368 bytes) trimmed
jordana@pi4g:~ $ sh /usr/share/agnostics/sdtest.sh
Run 1
prepare-file;0;0;164663;321
seq-write;0;0;163840;320
rand-4k-write;0;0;60179;15044
rand-4k-read;53108;13277;0;0
Sequential write speed 163840 KB/sec (target 10000) - PASS
Random write speed 15044 IOPS (target 500) - PASS
Random read speed 13277 IOPS (target 1500) - PASS
jordana@pi4g:~ $ systemd-analyze
Startup finished in 2.275s (kernel) + 14.610s (userspace) = 16.885s 
graphical.target reached after 10.126s in userspace

To make this setting persistent I had to find out my idVendor:idProduct with lsusb and configure udev rule as follows:

jordana@pi4g:~ $ lsusb -tv
/:  Bus 02.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/4p, 5000M
    ID 1d6b:0003 Linux Foundation 3.0 root hub
    |__ Port 1: Dev 2, If 0, Class=Mass Storage, Driver=uas, 5000M
        ID 2109:0715 VIA Labs, Inc. VL817 SATA Adaptor
jordana@pi4g:~ $ sudo nano /etc/udev/rules.d/10-trim.rules
ACTION=="add|change", ATTRS{idVendor}=="2109", ATTRS{idProduct}=="0715", SUBSYSTEM=="scsi_disk", ATTR{provisioning_mode}="unmap"

Saturday, 13 May 2023

Install zerotier and use it with zeronsd on WSL

This is a guide how to install zerotier and use it with zeronsd on Windows Subsystem for Linux (WSL).

To install WSL run Windows Powershell as Administrator. As zerotier requires systemd I installed Debian:

PS C:\Windows\system32> wsl --install -d Debian
PS C:\Windows\system32> wsl --set-version Debian 2
PS C:\Windows\system32> wsl -l -v
  NAME      STATE           VERSION
* Debian    Stopped         2

Once installed I started Debian, updated its packages and added configuration to enable systemd:

jordana@dad-pc:~$ sudo apt update
jordana@dad-pc:~$ sudo apt upgrade

jordana@dad-pc:~$ sudo nano /etc/wsl.conf
[boot]
systemd=true

After reboot of Debian I had to install openssh server and reboot again to see systemd starting properly. 

jordana@dad-pc:~$ sudo apt install openssh-server

After this I have seen systemd starting properly so I could install zerotier with curl:

jordana@dad-pc:~$ systemctl list-unit-files --type=service
jordana@dad-pc:~$ sudo apt install curl

jordana@dad-pc:~$ curl -s https://install.zerotier.com | sudo bash
jordana@dad-pc:~$ sudo zerotier-cli join xxxxxxxxxxxxxxx
200 join OK

In order to configure local domain name resolution is routed to zeronsd, I had to disable that WSL generates /etc/resolv.conf in /etc/wsl.conf and add localhost to /etc/resolv.conf and reboot again.

jordana@dad-pc:~$ sudo nano /etc/wsl.conf
[network]
generateResolvConf = false
jordana@dad-pc:~$ sudo nano /etc/resolv.conf
nameserver localhost

Finally I could install dnsmasq and configure zeronsd local domain routed towards zeronsd server:

jordana@dad-pc:~$ sudo apt install dnsmasq
jordana@dad-pc:~$ sudo nano /etc/dnsmasq.conf
server=/zeronsd/192.168.193.1
server=8.8.8.8
jordana@dad-pc:~$ sudo systemctl restart dnsmasq

In order to let ansible to keep all apt packages latest I had to install python3 and configure sudo without password:

jordana@dad-pc:~$ sudo apt install python3
jordana@dad-pc:~$ sudo nano /etc/sudoers.d/nopasswd
jordana ALL=(ALL) NOPASSWD: ALL

Tuesday, 25 April 2023

Another workaround for ssh login issue over zerotier

 From a hotel room during a business trip I have experienced again that ssh over zerotier stuck at some initialization phase:

jordana@penguin:~$ ssh -v ubuntu@oracle.zeronsd
OpenSSH_8.4p1 Debian-5+deb11u1, OpenSSL 1.1.1n  15 Mar 2022
debug1: Reading configuration data /etc/ssh/ssh_config
...
debug1: expecting SSH2_MSG_KEX_ECDH_REPLY

I suspect this has something to do with dropped packets again due to MTU limitations over zerotier tunnel as ssh into public IP directly works OK. To workaround this time I changed this line in ssh config:

jordana@penguin:~$ sudo nano /etc/ssh/ssh_config
   MACs hmac-sha2-256

While this made ssh work from terminal directly but ansible still could not connect.