Skip to content

Telemetry service (Pi → ESP32-S3)

The ESP32-S3 status screen shows the compute module model, CPU and NVMe temperature, fan speed, throttling flags, clock and load. The Pi sends them: a small shell script writes one line every 2 seconds to the ESP32-S3’s USB serial port.

EGGFLY_PI_INFO model=CM5 rev=1.0 cpu=54.8 nvme=41 fan=6521 fanpwm=51 fansrc=pwmfan thr=0x0 freq=2400 load=0.52 up=1234
Field Source
model, rev /proc/device-tree/model (CM3 / CM4 / CM4S / CM5 …)
cpu /sys/class/thermal/thermal_zone0/temp, °C
nvme hwmon named nvme, °C
fan, fanpwm, fansrc the fan hwmon with the highest tacho reading (else the first with a non-zero duty, else the first fan found): RPM, duty %, driver name
thr vcgencmd get_throttled raw bits - the ESP32-S3 decodes them into words
freq cpufreq scaling_cur_freq, MHz
load, up /proc/loadavg, /proc/uptime

Fields that cannot be read are simply left out. The ESP32-S3 firmware parses the line in its serial-command handler.

The three files are in the image sources, typixdeck-stage/09-pi-info/files/:

Terminal window
[ -d ~/typixdeck-pi-gen ] || git clone --depth 1 -b typixdeck https://github.com/TypixNode/pi-gen.git ~/typixdeck-pi-gen
F=~/typixdeck-pi-gen/typixdeck-stage/09-pi-info/files
sudo install -m 755 $F/typixdeck-pi-info /usr/local/bin/
sudo install -m 644 $F/typixdeck-pi-info.service /etc/systemd/system/
sudo install -m 644 $F/99-typixdeck-esp.rules /etc/udev/rules.d/
sudo systemctl daemon-reload
sudo udevadm control --reload
sudo udevadm trigger --subsystem-match=tty --action=add
systemctl status typixdeck-pi-info

Do not systemctl enable it - udev starts it (see below).

How it is built - and how to write your own

Section titled “How it is built - and how to write your own”

1. udev starts it when the ESP32-S3 appears

Section titled “1. udev starts it when the ESP32-S3 appears”
/etc/udev/rules.d/99-typixdeck-esp.rules
SUBSYSTEM=="tty", ATTRS{idVendor}=="303a", ATTRS{idProduct}=="80c3", \
SYMLINK+="typixdeck-esp", TAG+="systemd", ENV{SYSTEMD_WANTS}+="typixdeck-pi-info.service"
  • Matches only the ESP32-S3 application firmware (303a:80c3) and gives it a stable name, /dev/typixdeck-esp - no guessing between ttyACM0 and ttyACM1.
  • SYSTEMD_WANTS starts the service the moment the port appears. The ROM download modes (303a:0009, 303a:1001) deliberately do not match.
/etc/systemd/system/typixdeck-pi-info.service
[Unit]
Description=TypixDeck: push Pi telemetry to the ESP32-S3 dashboard
BindsTo=dev-typixdeck\x2desp.device
After=dev-typixdeck\x2desp.device
[Service]
Type=simple
ExecStart=/usr/local/bin/typixdeck-pi-info /dev/typixdeck-esp
Restart=on-failure
RestartSec=3
# root is needed for /dev/vcio (vcgencmd) and the tty; lock everything else down
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
RestrictAddressFamilies=AF_UNIX
DeviceAllow=/dev/typixdeck-esp rw
DeviceAllow=/dev/vcio rw
DeviceAllow=/dev/vchiq rw
DevicePolicy=closed
  • System unit, not a user unit: it works on Lite images and before anyone logs in.
  • BindsTo= the device: when the ESP32-S3 is being reflashed it leaves the application mode, the device disappears and the service stops by itself, so it never fights the flasher for the serial port. When the firmware comes back, udev starts it again.
  • No [Install] section: nothing but udev should start it.
  • It needs root only for vcgencmd (/dev/vcio) and the tty; the sandboxing lines take everything else away.

typixdeck-pi-info is plain bash. The parts that matter if you write your own:

Terminal window
# raw tty: no echo (the ESP's own output would come back as "commands"),
# no CR/LF translation, don't wait for carrier
stty -F "$DEV" 115200 raw -echo -echoe -echok -echoctl -echoke -onlcr -icrnl clocal || exit 1
while :; do
line="model=$MODEL"
v=$(cpu_temp); [ -n "$v" ] && line="$line cpu=$v"
# ... one field per reader, skipped when empty ...
# open/close per write: the port stays free for the flasher between lines
printf 'EGGFLY_PI_INFO %s\n' "$line" > "$DEV" || exit 1
sleep "$INTERVAL"
done
  • Put the port in raw mode without echo first, or the ESP32-S3’s own output is echoed back to it.
  • Open the port for each write instead of holding it: between lines it is free for esptool or other tools.
  • Exit on a write error; systemd (Restart=on-failure) or udev brings it back.
  • Look sensors up by name (/sys/class/hwmon/*/name), never by hwmonN - the numbers change between boots. On boards that expose several fans (a CM5 has both the board’s EMC2301 and its own 4-pin pwmfan), report the one that is actually spinning.