qmk_userspace/autoflash_bothsides.sh
2025-10-09 14:45:23 +02:00

197 lines
6.4 KiB
Bash

#!/usr/bin/env bash
# =============================================================================
# QMK Flashing Script for Fingerpunch/Sweeeeep with Liatris (RP2040)
# =============================================================================
# Features:
# - Single prebuilt firmware compilation
# - Automated handedness-aware flashing using uf2-split-left/right targets
# - Robust USB detection across Linux distributions
# - Auto-detection of which side is plugged in based on RP2040 USB serial/Board ID
# - Persistent mapping of USB devices to left/right sides (~/.qmk_rp2040_sides.json)
# - Optional prompting for unknown devices
# - Waits for device mount before flashing
# =============================================================================
set -euo pipefail
# ----------------------
# User-configurable variables
# ----------------------
KEYBOARD="fingerpunch/sweeeeep"
KEYMAP="smathev"
OUTPUT_DIR="$HOME/git_dev/keyboards/latest_firmware"
USB_MOUNT_PATHS=("/media/$USER" "/run/media/$USER" "/mnt")
RP2040_PATTERN="*RP2040*"
USB_WAIT_INTERVAL=0.5
SIDE_MAPPING_FILE="$HOME/.qmk_rp2040_sides.json"
# Ensure mapping file exists
if [[ ! -f "$SIDE_MAPPING_FILE" ]]; then
echo "{}" > "$SIDE_MAPPING_FILE"
fi
# ----------------------
# Function: build_firmware
# Build the firmware once for reuse during flashing
# ----------------------
build_firmware() {
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🛠 Building firmware once for $KEYBOARD"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
qmk compile -kb "$KEYBOARD" -km "$KEYMAP"
}
# ----------------------
# Function: wait_for_rp2040
# Wait until an RP2040 UF2 device is mounted on any of the configured paths
# ----------------------
wait_for_rp2040() {
echo "⏳ Waiting for RP2040 UF2 device..."
local device=""
while true; do
for path in "${USB_MOUNT_PATHS[@]}"; do
device=$(find "$path" -maxdepth 2 -type d -name "$RP2040_PATTERN" 2>/dev/null | head -n1)
if [[ -n "$device" ]]; then
echo "✅ Found RP2040 device at $device"
echo "$device"
return
fi
done
sleep "$USB_WAIT_INTERVAL"
done
}
# ----------------------
# Function: get_rp2040_usb_serial
# Attempt to get the USB serial number of the RP2040 device
# Returns empty string if unavailable
# ----------------------
get_rp2040_usb_serial() {
local mount_point="$1"
local dev
dev=$(findmnt -n -o SOURCE --target "$mount_point" 2>/dev/null)
if [[ -n "$dev" ]]; then
local sys_path
sys_path=$(readlink -f "/sys/class/block/$(basename "$dev")/device")
if [[ -f "$sys_path/serial" ]]; then
cat "$sys_path/serial"
return
fi
fi
echo ""
}
# ----------------------
# Function: get_rp2040_id
# Extract a unique identifier from the mounted RP2040
# Prefers USB serial, falls back to info_uf2.txt Board ID, then mount path
# ----------------------
get_rp2040_id() {
local mount_point="$1"
local usb_serial
usb_serial=$(get_rp2040_usb_serial "$mount_point")
if [[ -n "$usb_serial" ]]; then
echo "$usb_serial"
elif [[ -f "$mount_point/info_uf2.txt" ]]; then
grep "^Board ID" "$mount_point/info_uf2.txt" | awk -F': ' '{print $2}'
else
basename "$mount_point"
fi
}
# ----------------------
# Function: detect_side
# Determine the left/right side of the plugged-in board
# If unknown, prompt the user and update mapping
# ----------------------
detect_side() {
local mount_point="$1"
local rp_id
rp_id=$(get_rp2040_id "$mount_point")
local side
side=$(jq -r --arg id "$rp_id" '.[$id]' "$SIDE_MAPPING_FILE")
if [[ "$side" == "null" ]]; then
read -rp "Unknown device detected. Which side is this half? [left/right]: " side
side=${side,,}
if [[ "$side" != "left" && "$side" != "right" ]]; then
echo "Invalid input. Defaulting to left."
side="left"
fi
# Save mapping
tmpfile=$(mktemp)
jq --arg id "$rp_id" --arg side "$side" '. + {($id): $side}' "$SIDE_MAPPING_FILE" > "$tmpfile"
mv "$tmpfile" "$SIDE_MAPPING_FILE"
fi
echo "$side"
}
# ----------------------
# Function: flash_side
# Flash the prebuilt firmware to the given side (left/right)
# Waits for device and applies UF2 split target
# ----------------------
flash_side() {
local side="$1"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🔌 Flashing $side side..."
# Wait for device
local mount_point
mount_point=$(wait_for_rp2040)
# Auto-detect side if unknown
local detected_side
detected_side=$(detect_side "$mount_point")
if [[ "$detected_side" != "$side" ]]; then
echo "⚠️ Detected side '$detected_side' does not match expected side '$side'. Using detected side."
side="$detected_side"
fi
# Flash using prebuilt UF2 split target
qmk flash -kb "$KEYBOARD" -km "$KEYMAP:uf2-split-$side" -f
echo "$side side flashed successfully."
}
# ----------------------
# Function: main
# Main workflow: build firmware and flash both sides
# ----------------------
main() {
build_firmware
# Ask which side to flash first
read -rp "Which side to flash first? [left/right]: " SIDE1
SIDE1=${SIDE1,,}
if [[ "$SIDE1" != "left" && "$SIDE1" != "right" ]]; then
echo "Invalid input. Must be 'left' or 'right'."
exit 1
fi
# Determine second side
SIDE2=$([[ "$SIDE1" == "left" ]] && echo "right" || echo "left")
read -rp "Will you flash the other side afterward? [y/n]: " DO_SECOND
DO_SECOND=${DO_SECOND,,}
# Flash first side
flash_side "$SIDE1"
# Flash second side if requested
if [[ "$DO_SECOND" == "y" ]]; then
echo "Please reset the $SIDE2 half now, then press Enter to continue..."
read -r
flash_side "$SIDE2"
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🎉 All requested flashing complete!"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
}
# Execute main
main