Files
2026-07-15 17:32:04 -04:00

6.1 KiB

Raspberry Pi Provisioning — Zero-Touch Player Setup

Goal

Customer unboxes Pi → plugs in power → content plays. That's it. No keyboard, no monitor, no SSH.

Provisioning Flow

Step 1: Image the SD Card (We Do This)

Using Raspberry Pi Imager (CLI version for batch flashing):

# One-time image prep
cat << 'EOF' > /tmp/signage-firstboot.sh
#!/bin/bash
# Runs on first boot only — sets up the player

# 1. Set hostname
hostnamectl set-hostname signage-player

# 2. Enable auto-login to console (required for kiosk mode)
raspi-config nonint do_boot_behaviour B2

# 3. Install dependencies
apt-get update && apt-get install -y chromium-browser unclutter x11-xserver-utils

# 4. Create player service
cat > /etc/systemd/system/signage-player.service << 'SRVEOF'
[Unit]
Description=Digital Signage Player
After=network-online.target
Wants=network-online.target

[Service]
User=pi
Environment=DISPLAY=:0
ExecStartPre=/usr/bin/sleep 5
ExecStart=/usr/bin/bash /home/pi/player.sh
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
SRVEOF

# 5. Create player script
cat > /home/pi/player.sh << 'PLAYEOF'
#!/bin/bash
# Kiosk player — registers if new, plays if registered

DEVICE_ID_FILE="/home/pi/.device-id"
TOKEN_FILE="/home/pi/.device-token"
API="https://signage.itpropartner.com/api"
REG_CODE="${1:-}"

# Hide mouse cursor
unclutter -idle 0.1 &

# Turn off screen blanking
xset s off
xset s noblank
xset -dpms

# Register if not yet registered
if [ ! -f "$DEVICE_ID_FILE" ] && [ -n "$REG_CODE" ]; then
  SERIAL=$(cat /proc/cpuinfo | grep Serial | head -1 | awk '{print $3}')
  RESPONSE=$(curl -s -X POST "$API/devices/register" \
    -H "Content-Type: application/json" \
    -d "{\"code\":\"$REG_CODE\",\"serial\":\"$SERIAL\"}")
  echo "$RESPONSE" | python3 -c "import sys,json;d=json.load(sys.stdin);open('$DEVICE_ID_FILE','w').write(str(d['id']));open('$TOKEN_FILE','w').write(d['token'])" 2>/dev/null
fi

# Read device ID
DEVICE_ID=$(cat "$DEVICE_ID_FILE" 2>/dev/null || echo "")

# Launch kiosk browser
if [ -n "$DEVICE_ID" ]; then
  chromium-browser --kiosk \
    --no-sandbox \
    --disable-infobars \
    --disable-session-crashed-bubble \
    --disable-features=Translate \
    --noerrdialogs \
    "$API/player/$DEVICE_ID"
else
  # Show registration screen with QR code
  chromium-browser --kiosk \
    --no-sandbox \
    "$API/player/register?code=$REG_CODE"
fi
PLAYEOF

chmod +x /home/pi/player.sh
chown pi:pi /home/pi/player.sh

# 6. Enable service
systemctl enable signage-player.service

# 7. Clean up (remove self)
rm /boot/signage-firstboot.sh
EOF

Step 2: Flash + Inject Registration (We Do)

# Using Raspberry Pi Imager CLI on your machine
rpi-imager-cli --write raspios-lite.img --device /dev/sdX

# Mount boot partition and inject first-boot script
mount /dev/sdX1 /mnt
cp /tmp/signage-firstboot.sh /mnt/
umount /mnt

Step 3: Ship to Customer

Pi arrives at customer location. They plug in:

  1. Power (USB-C)
  2. HDMI to TV
  3. Ethernet (or WiFi pre-configured)

That's it. No keyboard needed. No monitor for setup.

What the Customer Sees

First Boot (Unregistered)

┌──────────────────────────────────────┐
│                                      │
│         DIGITAL SIGNAGE              │
│                                      │
│   ┌────────────────────────────┐     │
│   │  This screen needs to be   │     │
│   │  activated.                │     │
│   │                            │     │
│   │  Registration Code:       │     │
│   │    SIGN-4K7P-M9X2         │     │
│   │                            │     │
│   │  [████████████░░░░░░]      │     │
│   │  Waiting for activation...│     │
│   └────────────────────────────┘     │
│                                      │
└──────────────────────────────────────┘

After Registration (Normal Operation)

┌──────────────────────────────────────┐
│                                      │
│         <content plays fullscreen>   │
│                                      │
│                                      │
│                                      │
│                                      │
│                                      │
│                                      │
└──────────────────────────────────────┘

Recovery / Re-provisioning

If Pi needs to be re-registered (different customer):

# On Pi via SSH or USB keyboard:
rm /home/pi/.device-id /home/pi/.device-token
sudo reboot

Then register via the CMS with a new code.

If customer wants to move Pi to a different TV:

Just plug HDMI into the new TV. Pi auto-detects resolution via EDID. No config needed.

If SD card dies:

Flash a new one (same image) → insert → Pi boots → waiting for registration → activate from CMS. 5 minutes total.

Startup Behavior Summary

Event What Happens
Power on Boot → auto-login → start player service
Network connected Register or poll for playlist
Content fetched Fullscreen Chromium kiosk
Signal loss Content cached locally, keeps playing
Power loss Auto-restart on power return
Reboot Same as power on
SD card failure Replace card, re-register
TV resolution change Auto-detected via HDMI EDID

Provisioning Checklist (For Us)

  • Prepare base Raspberry Pi OS lite image
  • Add first-boot script (auto-login, dependencies, service)
  • Create player kiosk launch script
  • Configure Chromium kiosk flags
  • Test: flash → boot → play → power loss → reboot → play again
  • Package for batch flashing (10+ Pis at once)