236 lines
		
	
	
	
		
			8.3 KiB
		
	
	
	
		
			Bash
		
	
	
	
	
	
			
		
		
	
	
			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
 |