197 lines
		
	
	
	
		
			6.4 KiB
		
	
	
	
		
			Bash
		
	
	
	
	
	
			
		
		
	
	
			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
 |