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

236 lines
8.3 KiB
Bash

#!/usr/bin/env bash
# =============================================================================
# QMK Auto-Flashing Script for Fingerpunch/Sweeeeep with Liatris (RP2040)
# =============================================================================
# OPTIMIZED VERSION - Features:
# - Single firmware compilation
# - TRUE auto-detection: plug in any side, script detects which it is
# - Automated handedness-aware flashing using uf2-split-left/right bootloader targets
# - Robust USB detection across Linux distributions
# - Persistent mapping of USB devices to left/right sides (~/.qmk_rp2040_sides.json)
# - No need to specify which side first - script figures it out!
# - Waits for device mount before flashing
# =============================================================================
set -euo pipefail
# ----------------------
# User-configurable variables
# ----------------------
KEYBOARD="fingerpunch/sweeeeep"
KEYMAP="smathev"
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
# Ensure jq is installed
if ! command -v jq &> /dev/null; then
echo "❌ Error: 'jq' is required but not installed."
echo " Install it with: sudo apt-get install jq"
exit 1
fi
# ----------------------
# Function: build_firmware
# Build the firmware once for reuse during flashing
# ----------------------
build_firmware() {
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🛠 Building firmware for $KEYBOARD"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
qmk compile -kb "$KEYBOARD" -km "$KEYMAP"
echo "✅ Firmware compiled successfully"
echo ""
}
# ----------------------
# 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 || echo "")
if [[ -n "$dev" ]]; then
local sys_path
sys_path=$(readlink -f "/sys/class/block/$(basename "$dev")/device" 2>/dev/null || echo "")
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 -i "^Board-ID" "$mount_point/INFO_UF2.TXT" | awk -F': ' '{print $2}' | tr -d '\r\n '
elif [[ -f "$mount_point/info_uf2.txt" ]]; then
grep -i "^Board-ID" "$mount_point/info_uf2.txt" | awk -F': ' '{print $2}' | tr -d '\r\n '
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")
echo " Device ID: $rp_id"
local side
side=$(jq -r --arg id "$rp_id" '.[$id] // "null"' "$SIDE_MAPPING_FILE")
if [[ "$side" == "null" || -z "$side" ]]; then
echo ""
echo "⚠️ Unknown device detected!"
read -rp " Which side is this keyboard 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"
echo " ✅ Saved mapping: $rp_id$side"
fi
echo "$side"
}
# ----------------------
# Function: flash_side_auto
# Automatically detect and flash whichever keyboard half is plugged in
# ----------------------
flash_side_auto() {
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🔌 Waiting for keyboard half in bootloader mode..."
echo " (Double-tap RESET button on Liatris controller)"
echo ""
# Wait for device
local mount_point
mount_point=$(wait_for_rp2040)
# Auto-detect which side
local detected_side
detected_side=$(detect_side "$mount_point")
echo ""
echo "🎯 Detected: $detected_side side"
echo "📤 Flashing as $detected_side..."
echo ""
# Flash using the detected side's bootloader target
qmk flash -kb "$KEYBOARD" -km "$KEYMAP" -bl "uf2-split-$detected_side"
echo ""
echo "$detected_side side flashed successfully!"
echo ""
}
# ----------------------
# Function: main
# Main workflow: build firmware and flash both sides automatically
# ----------------------
main() {
echo ""
echo "╔═══════════════════════════════════════════════════════════╗"
echo "║ QMK Auto-Flash: Fingerpunch Sweeeeep + Liatris (RP2040) ║"
echo "╚═══════════════════════════════════════════════════════════╝"
echo ""
# Build firmware once
build_firmware
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🚀 Ready to flash!"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "Instructions:"
echo " 1. Enter bootloader on FIRST keyboard half (either side)"
echo " 2. Script will auto-detect which side it is"
echo " 3. After first side completes, do the same for the OTHER half"
echo ""
read -rp "Press Enter when ready to start..."
echo ""
# Flash first side (whichever is plugged in)
flash_side_auto
# Ask if user wants to flash the second side
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
read -rp "Flash the other keyboard half now? [y/n]: " DO_SECOND
DO_SECOND=${DO_SECOND,,}
echo ""
if [[ "$DO_SECOND" == "y" ]]; then
echo "Please:"
echo " 1. Unplug the keyboard half you just flashed"
echo " 2. Plug in the OTHER half"
echo " 3. Enter bootloader mode (double-tap RESET)"
echo ""
read -rp "Press Enter when ready..."
echo ""
# Flash second side (auto-detected)
flash_side_auto
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🎉 Flashing complete!"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "✓ Handedness has been set in EEPROM"
echo "✓ Future firmware updates can be flashed to both sides"
echo "✓ Handedness will persist across updates"
echo ""
}
# Execute main
main