mirror of
https://github.com/qmk/qmk_userspace.git
synced 2025-11-03 10:20:07 -05:00
Add files via upload
This commit is contained in:
parent
ffdd049589
commit
0bd86793ac
18 changed files with 3628 additions and 1 deletions
197
autoflash_bothsides.sh
Normal file
197
autoflash_bothsides.sh
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
#!/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
|
||||
341
autoflash_bothsides_corrected.sh
Normal file
341
autoflash_bothsides_corrected.sh
Normal file
|
|
@ -0,0 +1,341 @@
|
|||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# QMK Auto-Flashing Script for Fingerpunch/Sweeeeep with Liatris (RP2040)
|
||||
# =============================================================================
|
||||
# CORRECTED VERSION - Uses HOST USB information for device identification
|
||||
#
|
||||
# Key Insight:
|
||||
# - Liatris overwrites EEPROM on flash, so on-board info is unreliable
|
||||
# - Board-ID in INFO_UF2.TXT is the SAME for all controllers of the same type
|
||||
# - ONLY the host's USB serial/path is reliable for distinguishing sides
|
||||
#
|
||||
# Features:
|
||||
# - Single firmware compilation
|
||||
# - TRUE auto-detection using USB serial from host system
|
||||
# - First-run learning: asks user to identify which side is which
|
||||
# - Persistent mapping stored in ~/.qmk_rp2040_sides.json
|
||||
# - Automated flashing using uf2-split-left/right bootloader targets
|
||||
# - Robust USB detection across Linux distributions
|
||||
# =============================================================================
|
||||
|
||||
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
|
||||
# Returns the mount point
|
||||
# ----------------------
|
||||
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_usb_serial_from_host
|
||||
# Get the USB serial number from the HOST system (not from the device itself)
|
||||
# This is the ONLY reliable way to identify devices when EEPROM is wiped
|
||||
# ----------------------
|
||||
get_usb_serial_from_host() {
|
||||
local mount_point="$1"
|
||||
|
||||
# Method 1: Get the block device, then trace to USB serial
|
||||
local dev
|
||||
dev=$(findmnt -n -o SOURCE --target "$mount_point" 2>/dev/null || echo "")
|
||||
|
||||
if [[ -n "$dev" ]]; then
|
||||
local block_dev=$(basename "$dev")
|
||||
|
||||
# Try to find USB serial through sysfs
|
||||
local sys_path="/sys/class/block/$block_dev"
|
||||
|
||||
# Walk up the device tree to find the USB device
|
||||
local current_path=$(readlink -f "$sys_path/device" 2>/dev/null || echo "")
|
||||
|
||||
while [[ -n "$current_path" && "$current_path" != "/sys" ]]; do
|
||||
# Check if this directory has a serial file
|
||||
if [[ -f "$current_path/serial" ]]; then
|
||||
cat "$current_path/serial"
|
||||
return
|
||||
fi
|
||||
# Also check for idVendor/idProduct to confirm it's a USB device
|
||||
if [[ -f "$current_path/idVendor" ]]; then
|
||||
# Found USB device level, check for serial
|
||||
if [[ -f "$current_path/serial" ]]; then
|
||||
cat "$current_path/serial"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
# Move up one level
|
||||
current_path=$(dirname "$current_path")
|
||||
done
|
||||
fi
|
||||
|
||||
# Method 2: Use udevadm to get USB info
|
||||
if [[ -n "$dev" ]]; then
|
||||
local serial
|
||||
serial=$(udevadm info --query=property --name="$dev" 2>/dev/null | grep "ID_SERIAL_SHORT=" | cut -d'=' -f2)
|
||||
if [[ -n "$serial" ]]; then
|
||||
echo "$serial"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
# Method 3: Fallback - use the mount point path as identifier
|
||||
# This is less reliable but better than nothing
|
||||
echo "mount_path_$(basename "$mount_point")"
|
||||
}
|
||||
|
||||
# ----------------------
|
||||
# Function: get_usb_device_path
|
||||
# Get a unique identifier based on USB physical port location
|
||||
# This persists even when serial is not available
|
||||
# ----------------------
|
||||
get_usb_device_path() {
|
||||
local mount_point="$1"
|
||||
|
||||
local dev
|
||||
dev=$(findmnt -n -o SOURCE --target "$mount_point" 2>/dev/null || echo "")
|
||||
|
||||
if [[ -n "$dev" ]]; then
|
||||
# Get the physical USB path (bus and port numbers)
|
||||
local devpath
|
||||
devpath=$(udevadm info --query=property --name="$dev" 2>/dev/null | grep "DEVPATH=" | cut -d'=' -f2)
|
||||
if [[ -n "$devpath" ]]; then
|
||||
# Extract the USB bus and port info (e.g., /devices/pci0000:00/0000:00:14.0/usb1/1-3/1-3:1.0)
|
||||
echo "$devpath" | grep -oP 'usb\d+/\d+-[\d.]+'
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# ----------------------
|
||||
# Function: get_device_identifier
|
||||
# Get the best available identifier for the USB device
|
||||
# Prefers USB serial, falls back to USB port location
|
||||
# ----------------------
|
||||
get_device_identifier() {
|
||||
local mount_point="$1"
|
||||
|
||||
# Try to get USB serial from host
|
||||
local usb_serial
|
||||
usb_serial=$(get_usb_serial_from_host "$mount_point")
|
||||
|
||||
# If serial doesn't start with "mount_path_", it's a real serial
|
||||
if [[ -n "$usb_serial" && "$usb_serial" != mount_path_* ]]; then
|
||||
echo "serial:$usb_serial"
|
||||
return
|
||||
fi
|
||||
|
||||
# Try USB device path
|
||||
local usb_path
|
||||
usb_path=$(get_usb_device_path "$mount_point")
|
||||
if [[ -n "$usb_path" ]]; then
|
||||
echo "usbpath:$usb_path"
|
||||
return
|
||||
fi
|
||||
|
||||
# Final fallback: use mount point basename
|
||||
echo "mount:$(basename "$mount_point")"
|
||||
}
|
||||
|
||||
# ----------------------
|
||||
# Function: detect_side
|
||||
# Determine the left/right side of the plugged-in board
|
||||
# On first encounter, ask user to identify the side
|
||||
# ----------------------
|
||||
detect_side() {
|
||||
local mount_point="$1"
|
||||
local device_id
|
||||
device_id=$(get_device_identifier "$mount_point")
|
||||
|
||||
echo " Device Identifier: $device_id"
|
||||
|
||||
local side
|
||||
side=$(jq -r --arg id "$device_id" '.[$id] // "null"' "$SIDE_MAPPING_FILE")
|
||||
|
||||
if [[ "$side" == "null" || -z "$side" ]]; then
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "⚠️ UNKNOWN DEVICE - First Time Setup"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
echo "The script has detected a keyboard half that it hasn't"
|
||||
echo "seen before. This is expected on first run."
|
||||
echo ""
|
||||
echo "Please tell me which side this is so I can remember it"
|
||||
echo "for future flashing sessions."
|
||||
echo ""
|
||||
read -rp "Which side is currently plugged in? [left/right]: " side
|
||||
side=${side,,}
|
||||
|
||||
if [[ "$side" != "left" && "$side" != "right" ]]; then
|
||||
echo "❌ Invalid input. Must be 'left' or 'right'."
|
||||
echo " Exiting to avoid incorrect flashing."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Save mapping
|
||||
tmpfile=$(mktemp)
|
||||
jq --arg id "$device_id" --arg side "$side" '. + {($id): $side}' "$SIDE_MAPPING_FILE" > "$tmpfile"
|
||||
mv "$tmpfile" "$SIDE_MAPPING_FILE"
|
||||
|
||||
echo ""
|
||||
echo "✅ Saved mapping: $side side"
|
||||
echo " Next time this device is detected, it will be"
|
||||
echo " automatically identified as the $side side."
|
||||
echo ""
|
||||
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 based on HOST USB information
|
||||
local detected_side
|
||||
detected_side=$(detect_side "$mount_point")
|
||||
|
||||
echo ""
|
||||
echo "🎯 Detected: $detected_side side"
|
||||
echo "📤 Flashing with handedness: $detected_side"
|
||||
echo ""
|
||||
|
||||
# Flash using the detected side's bootloader target
|
||||
# Using -bl (bootloader) parameter with uf2-split-left or uf2-split-right
|
||||
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 ""
|
||||
echo "This script will:"
|
||||
echo " • Build firmware once"
|
||||
echo " • Auto-detect which keyboard half you plug in"
|
||||
echo " • Flash the correct handedness (left/right)"
|
||||
echo " • Remember your devices for future flashing"
|
||||
echo ""
|
||||
echo "Note: On first run, you'll be asked to identify each side."
|
||||
echo " After that, detection is fully automatic!"
|
||||
echo ""
|
||||
|
||||
# Build firmware once
|
||||
build_firmware
|
||||
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "🚀 Ready to flash!"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
read -rp "Press Enter to start flashing the first side..."
|
||||
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 first keyboard half"
|
||||
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 "✓ Device mappings saved to: $SIDE_MAPPING_FILE"
|
||||
echo "✓ Future runs will automatically detect sides"
|
||||
echo "✓ Future firmware updates will preserve handedness"
|
||||
echo ""
|
||||
|
||||
# Show the saved mappings
|
||||
echo "Saved device mappings:"
|
||||
jq '.' "$SIDE_MAPPING_FILE"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Execute main
|
||||
main
|
||||
236
autoflash_bothsides_optimized.sh
Normal file
236
autoflash_bothsides_optimized.sh
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
#!/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
|
||||
200
qmk_flash_tools/MAPPING_LOGIC.md
Normal file
200
qmk_flash_tools/MAPPING_LOGIC.md
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
# Device Mapping Logic - Simple Explanation
|
||||
|
||||
## The Three States
|
||||
|
||||
```
|
||||
State 1: EMPTY → Learn both devices
|
||||
State 2: PARTIAL → Complete the mapping
|
||||
State 3: COMPLETE → Only allow known devices
|
||||
```
|
||||
|
||||
## State Transitions
|
||||
|
||||
```
|
||||
┌─────────────┐
|
||||
│ EMPTY │ No devices mapped
|
||||
│ (0/2) │
|
||||
└──────┬──────┘
|
||||
│ Flash first device
|
||||
▼
|
||||
┌─────────────┐
|
||||
│ PARTIAL │ One device mapped
|
||||
│ (1/2) │
|
||||
└──────┬──────┘
|
||||
│ Flash second device
|
||||
▼
|
||||
┌─────────────┐
|
||||
│ COMPLETE │ Both devices mapped
|
||||
│ (2/2) │ ← Stays here forever
|
||||
└─────────────┘
|
||||
```
|
||||
|
||||
## Behavior by State
|
||||
|
||||
### State 1: EMPTY (0/2 devices)
|
||||
```
|
||||
Action: LEARN
|
||||
Rule: Save devices as user specifies
|
||||
Result: Build initial mapping
|
||||
```
|
||||
|
||||
### State 2: PARTIAL (1/2 devices)
|
||||
```
|
||||
Action: COMPLETE
|
||||
Rule: Known device must match, unknown must be the missing side
|
||||
Result: Complete mapping OR error if ambiguous
|
||||
```
|
||||
|
||||
### State 3: COMPLETE (2/2 devices)
|
||||
```
|
||||
Action: VERIFY
|
||||
Rule: Only known devices allowed, must match expected side
|
||||
Result: Continue OR error immediately
|
||||
```
|
||||
|
||||
## Decision Tree
|
||||
|
||||
```
|
||||
Device Detected
|
||||
│
|
||||
▼
|
||||
┌────────────────┐
|
||||
│ Mapping State? │
|
||||
└───┬────┬───┬───┘
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
Empty Part Comp
|
||||
│ │ │
|
||||
│ │ ▼
|
||||
│ │ ┌──────────────┐
|
||||
│ │ │ Device Known?│
|
||||
│ │ └──┬───────┬───┘
|
||||
│ │ │ │
|
||||
│ │ NO YES
|
||||
│ │ │ │
|
||||
│ │ ▼ ▼
|
||||
│ │ REJECT Match?
|
||||
│ │ │
|
||||
│ │ ┌─────┴─────┐
|
||||
│ │ YES NO
|
||||
│ │ │ │
|
||||
│ │ ▼ ▼
|
||||
│ │ CONTINUE MISMATCH
|
||||
│ │ OPTIONS
|
||||
│ │
|
||||
│ ▼
|
||||
│ ┌──────────────┐
|
||||
│ │ Device Known?│
|
||||
│ └──┬───────┬───┘
|
||||
│ │ │
|
||||
│ NO YES
|
||||
│ │ │
|
||||
│ ▼ ▼
|
||||
│ Expected Match?
|
||||
│ unmapped │
|
||||
│ side? ┌───┴────┐
|
||||
│ │ YES NO
|
||||
│ ▼ │ │
|
||||
│ ┌───┐ ▼ ▼
|
||||
│ │YES│CONTINUE ERROR
|
||||
│ └─┬─┘
|
||||
│ │NO
|
||||
│ ▼
|
||||
│ ERROR
|
||||
│
|
||||
▼
|
||||
SAVE AS
|
||||
EXPECTED
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: First Time (Empty → Partial → Complete)
|
||||
```
|
||||
$ ./autoflash_modular.sh
|
||||
|
||||
State: EMPTY (0/2)
|
||||
You: "left"
|
||||
Device: ABC123
|
||||
Action: Save ABC123 → left
|
||||
New State: PARTIAL (1/2)
|
||||
|
||||
You: "Flash right? yes"
|
||||
Device: XYZ789
|
||||
Action: Save XYZ789 → right
|
||||
New State: COMPLETE (2/2)
|
||||
|
||||
Mapping: {"ABC123": "left", "XYZ789": "right"}
|
||||
```
|
||||
|
||||
### Example 2: Normal Use (Complete)
|
||||
```
|
||||
$ ./autoflash_modular.sh
|
||||
|
||||
State: COMPLETE (2/2)
|
||||
You: "left"
|
||||
Device: ABC123
|
||||
Check: ABC123 is mapped to "left" ✅
|
||||
Action: Continue
|
||||
|
||||
You: "right"
|
||||
Device: XYZ789
|
||||
Check: XYZ789 is mapped to "right" ✅
|
||||
Action: Continue
|
||||
```
|
||||
|
||||
### Example 3: Unknown Device (Complete → Rejected)
|
||||
```
|
||||
$ ./autoflash_modular.sh
|
||||
|
||||
State: COMPLETE (2/2)
|
||||
You: "left"
|
||||
Device: UNKNOWN
|
||||
Check: Not in mapping ❌
|
||||
Action: REJECT and EXIT
|
||||
|
||||
Error: "Unknown device! Expected ABC123 or XYZ789"
|
||||
```
|
||||
|
||||
### Example 4: Partial Mapping - Good
|
||||
```
|
||||
State: PARTIAL (1/2) - Only "left" mapped
|
||||
|
||||
Scenario A: Known device
|
||||
You: "left"
|
||||
Device: ABC123 (known as left)
|
||||
Action: Continue ✅
|
||||
|
||||
Scenario B: Unknown device for unmapped side
|
||||
You: "right" (unmapped)
|
||||
Device: UNKNOWN
|
||||
Action: Save as right ✅
|
||||
New State: COMPLETE
|
||||
```
|
||||
|
||||
### Example 5: Partial Mapping - Bad
|
||||
```
|
||||
State: PARTIAL (1/2) - Only "left" mapped
|
||||
|
||||
You: "left" (already mapped)
|
||||
Device: UNKNOWN
|
||||
Question: Is this a replacement for left? Or the unmapped right?
|
||||
Action: ERROR - ambiguous ❌
|
||||
Message: "Cannot determine! Clear mappings."
|
||||
```
|
||||
|
||||
## Key Principle
|
||||
|
||||
**Once both sides are mapped (COMPLETE state), the script becomes protective:**
|
||||
- ✅ Only the two known devices can be flashed
|
||||
- ❌ Any unknown device is rejected immediately
|
||||
- 🛡️ This prevents accidentally flashing the wrong keyboard
|
||||
|
||||
**To reset:** `cd qmk_flash_tools && rm device_mappings.json`
|
||||
|
||||
## Why This Works
|
||||
|
||||
1. **Learning Phase** (Empty/Partial) - Flexible, builds mapping
|
||||
2. **Protection Phase** (Complete) - Strict, prevents mistakes
|
||||
3. **Clear Errors** - Always know why something failed
|
||||
4. **Easy Recovery** - Delete mapping file to restart
|
||||
223
qmk_flash_tools/QUICK_REFERENCE.md
Normal file
223
qmk_flash_tools/QUICK_REFERENCE.md
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
# QMK Flash Tools - Quick Reference
|
||||
|
||||
## 🚀 Common Commands
|
||||
|
||||
### Flash Both Keyboard Sides
|
||||
```bash
|
||||
cd qmk_flash_tools
|
||||
./autoflash_modular.sh
|
||||
```
|
||||
|
||||
### Test Individual Components
|
||||
```bash
|
||||
# Test side mapping (no hardware needed)
|
||||
./test/test_side_mapping.sh
|
||||
|
||||
# Test device detection (needs keyboard in bootloader)
|
||||
./test/test_device_detection.sh
|
||||
|
||||
# Test QMK functions
|
||||
./test/test_qmk_helpers.sh
|
||||
|
||||
# Run all tests
|
||||
./test/run_all_tests.sh
|
||||
```
|
||||
|
||||
### View Saved Mappings
|
||||
```bash
|
||||
cd qmk_flash_tools
|
||||
cat device_mappings.json
|
||||
# or
|
||||
jq '.' device_mappings.json
|
||||
```
|
||||
|
||||
### Reset Mappings
|
||||
```bash
|
||||
# Reset all
|
||||
cd qmk_flash_tools
|
||||
rm device_mappings.json
|
||||
|
||||
# Or use the library
|
||||
source lib/side_mapping.sh
|
||||
clear_all_mappings
|
||||
```
|
||||
|
||||
## 🔍 Debug Commands
|
||||
|
||||
### Check Device Info
|
||||
```bash
|
||||
source lib/device_detection.sh
|
||||
mount_point="/media/$USER/RPI-RP2" # Adjust path
|
||||
print_device_info "$mount_point"
|
||||
```
|
||||
|
||||
### Manually Test Detection
|
||||
```bash
|
||||
source lib/device_detection.sh
|
||||
source lib/side_mapping.sh
|
||||
|
||||
# Wait for device
|
||||
mount_point=$(wait_for_rp2040)
|
||||
|
||||
# Get identifier
|
||||
device_id=$(get_device_identifier "$mount_point")
|
||||
echo "Device ID: $device_id"
|
||||
|
||||
# Detect side
|
||||
side=$(detect_side "$device_id")
|
||||
echo "Side: $side"
|
||||
```
|
||||
|
||||
### Test QMK Commands
|
||||
```bash
|
||||
# Check QMK version
|
||||
qmk --version
|
||||
|
||||
# List keymaps
|
||||
qmk list-keymaps -kb fingerpunch/sweeeeep
|
||||
|
||||
# Compile only (no flash)
|
||||
qmk compile -kb fingerpunch/sweeeeep -km smathev
|
||||
|
||||
# Flash with specific bootloader
|
||||
qmk flash -kb fingerpunch/sweeeeep -km smathev -bl uf2-split-left
|
||||
```
|
||||
|
||||
## 🛠️ Manual Side Mapping
|
||||
|
||||
### Add Mapping Manually
|
||||
```bash
|
||||
source lib/side_mapping.sh
|
||||
save_side_mapping "serial:ABC123XYZ" "left"
|
||||
save_side_mapping "serial:DEF456RST" "right"
|
||||
```
|
||||
|
||||
### Check Specific Device
|
||||
```bash
|
||||
source lib/side_mapping.sh
|
||||
side=$(get_saved_side "serial:ABC123XYZ")
|
||||
echo "Device is: $side"
|
||||
```
|
||||
|
||||
### List All Mappings
|
||||
```bash
|
||||
source lib/side_mapping.sh
|
||||
list_all_mappings
|
||||
```
|
||||
|
||||
## 📝 Environment Variables
|
||||
|
||||
```bash
|
||||
# Keyboard configuration
|
||||
export KEYBOARD="fingerpunch/sweeeeep"
|
||||
export KEYMAP="smathev"
|
||||
|
||||
# Device detection
|
||||
export USB_MOUNT_PATHS=("/media/$USER" "/run/media/$USER" "/mnt")
|
||||
export RP2040_PATTERN="*RP2040*"
|
||||
export USB_WAIT_INTERVAL=0.5
|
||||
|
||||
# Side mapping file (relative to qmk_flash_tools/)
|
||||
export SIDE_MAPPING_FILE="./device_mappings.json"
|
||||
```
|
||||
|
||||
## 🐛 Troubleshooting One-Liners
|
||||
|
||||
```bash
|
||||
# Check if RP2040 is mounted
|
||||
ls /media/$USER/ | grep -i rp2040
|
||||
|
||||
# Find all USB devices
|
||||
lsusb
|
||||
|
||||
# Check USB device info
|
||||
udevadm info --query=property /dev/sdb1 # Adjust device
|
||||
|
||||
# Monitor USB events (run in separate terminal)
|
||||
udevadm monitor
|
||||
|
||||
# Check QMK firmware location
|
||||
qmk config user.qmk_home
|
||||
|
||||
# Force clean and rebuild
|
||||
qmk clean && qmk compile -kb fingerpunch/sweeeeep -km smathev
|
||||
|
||||
# Check if jq is installed
|
||||
jq --version
|
||||
```
|
||||
|
||||
## 📂 File Locations
|
||||
|
||||
```
|
||||
Main script: qmk_flash_tools/autoflash_modular.sh
|
||||
Libraries: qmk_flash_tools/lib/*.sh
|
||||
Tests: qmk_flash_tools/test/*.sh
|
||||
Mapping file: qmk_flash_tools/device_mappings.json
|
||||
QMK firmware: ~/qmk_firmware/ (or user.qmk_home)
|
||||
Build output: ~/qmk_firmware/.build/
|
||||
```
|
||||
|
||||
## 🔧 Common Fixes
|
||||
|
||||
### Device not appearing
|
||||
```bash
|
||||
# Check dmesg for USB events
|
||||
dmesg | tail -n 20
|
||||
|
||||
# Try different USB port
|
||||
# Try different USB cable (must be data cable, not charge-only)
|
||||
```
|
||||
|
||||
### Wrong side detected
|
||||
```bash
|
||||
# Clear and re-learn
|
||||
source lib/side_mapping.sh
|
||||
clear_mapping "serial:YOUR_DEVICE_ID"
|
||||
# Then run autoflash again
|
||||
```
|
||||
|
||||
### Build fails
|
||||
```bash
|
||||
# Update QMK
|
||||
python3 -m pip install --upgrade qmk
|
||||
|
||||
# Pull latest QMK firmware
|
||||
cd ~/qmk_firmware
|
||||
git pull
|
||||
|
||||
# Clean and try again
|
||||
qmk clean
|
||||
```
|
||||
|
||||
### Permission denied
|
||||
```bash
|
||||
# Make scripts executable
|
||||
chmod +x qmk_flash_tools/*.sh
|
||||
chmod +x qmk_flash_tools/lib/*.sh
|
||||
chmod +x qmk_flash_tools/test/*.sh
|
||||
```
|
||||
|
||||
## 📚 Function Quick Reference
|
||||
|
||||
### device_detection.sh
|
||||
- `wait_for_rp2040()` - Wait for device
|
||||
- `get_usb_serial_from_host(mount)` - Get serial
|
||||
- `get_usb_device_path(mount)` - Get USB path
|
||||
- `get_device_identifier(mount)` - Get ID
|
||||
- `print_device_info(mount)` - Debug info
|
||||
|
||||
### side_mapping.sh
|
||||
- `init_mapping_file()` - Create file
|
||||
- `save_side_mapping(id, side)` - Save
|
||||
- `get_saved_side(id)` - Retrieve
|
||||
- `detect_side(id)` - Auto-detect/prompt
|
||||
- `list_all_mappings()` - Show all
|
||||
- `clear_mapping(id)` - Remove one
|
||||
- `clear_all_mappings()` - Reset
|
||||
|
||||
### qmk_helpers.sh
|
||||
- `build_firmware(kb, km)` - Compile
|
||||
- `flash_side(kb, km, side)` - Flash with handedness
|
||||
- `check_qmk_installed()` - Verify QMK
|
||||
- `verify_keyboard_exists(kb)` - Check KB
|
||||
- `clean_build()` - Clean
|
||||
338
qmk_flash_tools/README.md
Normal file
338
qmk_flash_tools/README.md
Normal file
|
|
@ -0,0 +1,338 @@
|
|||
# QMK Flash Tools - Modular Edition
|
||||
|
||||
Automated flashing tools for QMK split keyboards with RP2040 controllers (like Liatris).
|
||||
|
||||
## 📁 Structure
|
||||
|
||||
```
|
||||
qmk_flash_tools/
|
||||
├── autoflash_modular.sh # Main flashing script
|
||||
├── lib/ # Reusable library modules
|
||||
│ ├── device_detection.sh # USB device detection functions
|
||||
│ ├── side_mapping.sh # Device-to-side mapping storage
|
||||
│ └── qmk_helpers.sh # QMK build/flash wrappers
|
||||
├── test/ # Standalone test scripts
|
||||
│ ├── test_device_detection.sh
|
||||
│ ├── test_side_mapping.sh
|
||||
│ └── test_qmk_helpers.sh
|
||||
└── README.md # This file
|
||||
```
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
### 1. Flash Both Sides
|
||||
|
||||
```bash
|
||||
cd qmk_flash_tools
|
||||
chmod +x autoflash_modular.sh
|
||||
./autoflash_modular.sh
|
||||
```
|
||||
|
||||
The script will:
|
||||
1. Build firmware once
|
||||
2. **First time only:** Ask which side you'll flash (left/right)
|
||||
3. **Subsequent runs:** Auto-detect which side is plugged in
|
||||
4. Wait for you to enter bootloader
|
||||
5. Verify and flash with correct handedness
|
||||
6. Repeat for the other side
|
||||
|
||||
### 2. Test Individual Components
|
||||
|
||||
```bash
|
||||
# Test device detection
|
||||
cd test
|
||||
chmod +x test_device_detection.sh
|
||||
./test_device_detection.sh
|
||||
|
||||
# Test side mapping
|
||||
chmod +x test_side_mapping.sh
|
||||
./test_side_mapping.sh
|
||||
|
||||
# Test QMK helpers
|
||||
chmod +x test_qmk_helpers.sh
|
||||
./test_qmk_helpers.sh
|
||||
```
|
||||
|
||||
## 🔧 Configuration
|
||||
|
||||
Edit the top of `autoflash_modular.sh`:
|
||||
|
||||
```bash
|
||||
KEYBOARD="fingerpunch/sweeeeep" # Your keyboard
|
||||
KEYMAP="smathev" # Your keymap
|
||||
USB_MOUNT_PATHS=(...) # Where USB drives mount
|
||||
SIDE_MAPPING_FILE="..." # Device mappings (defaults to ./device_mappings.json)
|
||||
```
|
||||
|
||||
## 📚 Library Documentation
|
||||
|
||||
### device_detection.sh
|
||||
|
||||
Functions for detecting RP2040 devices via host USB information.
|
||||
|
||||
**Key Functions:**
|
||||
- `wait_for_rp2040()` - Wait for device to enter bootloader
|
||||
- `get_usb_serial_from_host(mount_point)` - Get USB serial from host
|
||||
- `get_usb_device_path(mount_point)` - Get USB port location
|
||||
- `get_device_identifier(mount_point)` - Get best available ID
|
||||
- `print_device_info(mount_point)` - Debug info
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
source lib/device_detection.sh
|
||||
mount_point=$(wait_for_rp2040)
|
||||
device_id=$(get_device_identifier "$mount_point")
|
||||
echo "Device: $device_id"
|
||||
```
|
||||
|
||||
### side_mapping.sh
|
||||
|
||||
Functions for storing and retrieving which device is left/right.
|
||||
|
||||
**Key Functions:**
|
||||
- `init_mapping_file()` - Create mapping file if needed
|
||||
- `save_side_mapping(device_id, side)` - Save mapping
|
||||
- `get_saved_side(device_id)` - Retrieve saved side
|
||||
- `detect_side(device_id)` - Get side (prompts if unknown)
|
||||
- `list_all_mappings()` - Show all saved mappings
|
||||
- `clear_mapping(device_id)` - Remove a mapping
|
||||
- `clear_all_mappings()` - Reset all mappings
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
source lib/side_mapping.sh
|
||||
export SIDE_MAPPING_FILE="./device_mappings.json"
|
||||
|
||||
save_side_mapping "serial:ABC123" "left"
|
||||
side=$(get_saved_side "serial:ABC123")
|
||||
echo "Side: $side"
|
||||
```
|
||||
|
||||
### qmk_helpers.sh
|
||||
|
||||
Wrapper functions for QMK CLI commands.
|
||||
|
||||
**Key Functions:**
|
||||
- `check_qmk_installed()` - Verify QMK is available
|
||||
- `build_firmware(keyboard, keymap)` - Compile firmware
|
||||
- `flash_side(keyboard, keymap, side)` - Flash with handedness
|
||||
- `flash_with_bootloader(keyboard, keymap, bootloader)` - Flash with specific bootloader
|
||||
- `verify_keyboard_exists(keyboard)` - Check keyboard definition
|
||||
- `clean_build()` - Clean build artifacts
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
source lib/qmk_helpers.sh
|
||||
|
||||
check_qmk_installed || exit 1
|
||||
build_firmware "fingerpunch/sweeeeep" "smathev"
|
||||
flash_side "fingerpunch/sweeeeep" "smathev" "left"
|
||||
```
|
||||
|
||||
## 🧪 Testing Workflow
|
||||
|
||||
### Test Device Detection
|
||||
|
||||
1. **Without device:**
|
||||
```bash
|
||||
./test/test_device_detection.sh
|
||||
# Shows "no device found", good for baseline
|
||||
```
|
||||
|
||||
2. **With device:**
|
||||
```bash
|
||||
# Enter bootloader mode on keyboard
|
||||
./test/test_device_detection.sh
|
||||
# Shows USB serial, path, and identifier
|
||||
```
|
||||
|
||||
### Test Side Mapping
|
||||
|
||||
```bash
|
||||
./test/test_side_mapping.sh
|
||||
# Runs comprehensive tests:
|
||||
# - Create mapping file
|
||||
# - Save/retrieve mappings
|
||||
# - Clear mappings
|
||||
# - Interactive prompt (optional)
|
||||
```
|
||||
|
||||
### Test QMK Helpers
|
||||
|
||||
```bash
|
||||
./test/test_qmk_helpers.sh
|
||||
# Tests:
|
||||
# - QMK installation check
|
||||
# - Keyboard verification
|
||||
# - Build (optional)
|
||||
# - Function signatures
|
||||
```
|
||||
|
||||
## 🐛 Troubleshooting
|
||||
|
||||
### Device not detected
|
||||
|
||||
```bash
|
||||
# Check if device appears
|
||||
ls /media/$USER/
|
||||
# Should see RPI-RP2 or similar
|
||||
|
||||
# Run device detection test
|
||||
./test/test_device_detection.sh
|
||||
```
|
||||
|
||||
### Can't identify device
|
||||
|
||||
The script uses these methods in order:
|
||||
1. USB serial number (most reliable)
|
||||
2. USB physical port path
|
||||
3. Mount point name (fallback)
|
||||
|
||||
Check which method worked:
|
||||
```bash
|
||||
source lib/device_detection.sh
|
||||
mount_point="/media/$USER/RPI-RP2"
|
||||
print_device_info "$mount_point"
|
||||
```
|
||||
|
||||
### Side mismatch detected
|
||||
|
||||
If you see a mismatch warning:
|
||||
|
||||
**Option 1: Exit and plug in correct side (safest)**
|
||||
- Choose `[e]` to exit
|
||||
- Unplug the keyboard
|
||||
- Plug in the correct side
|
||||
- Run the script again
|
||||
|
||||
**Option 2: Update the mapping**
|
||||
- Choose `[c]` to clear old mapping and save new one
|
||||
- Use this if you know the old mapping was wrong
|
||||
|
||||
**Option 3: Force flash (dangerous!)**
|
||||
- Choose `[f]` to flash anyway
|
||||
- Only use if you're absolutely certain
|
||||
- May result in swapped left/right behavior
|
||||
|
||||
Or manually reset mappings:
|
||||
```bash
|
||||
source lib/side_mapping.sh
|
||||
clear_mapping "serial:ABC123" # Use your device ID
|
||||
```
|
||||
|
||||
Or reset all mappings:
|
||||
```bash
|
||||
cd qmk_flash_tools
|
||||
rm device_mappings.json
|
||||
```
|
||||
|
||||
### Build fails
|
||||
|
||||
```bash
|
||||
# Test QMK directly
|
||||
qmk compile -kb fingerpunch/sweeeeep -km smathev
|
||||
|
||||
# Check keyboard exists
|
||||
qmk list-keymaps -kb fingerpunch/sweeeeep
|
||||
```
|
||||
|
||||
## 💡 Advanced Usage
|
||||
|
||||
### Use in other scripts
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
source /path/to/qmk_flash_tools/lib/device_detection.sh
|
||||
source /path/to/qmk_flash_tools/lib/side_mapping.sh
|
||||
|
||||
# Your custom logic here
|
||||
mount_point=$(wait_for_rp2040)
|
||||
device_id=$(get_device_identifier "$mount_point")
|
||||
side=$(detect_side "$device_id")
|
||||
echo "Detected $side side"
|
||||
```
|
||||
|
||||
### Custom keyboard configuration
|
||||
|
||||
```bash
|
||||
# Set environment variables before running
|
||||
export KEYBOARD="your/keyboard"
|
||||
export KEYMAP="your_keymap"
|
||||
./autoflash_modular.sh
|
||||
```
|
||||
|
||||
### Different mapping file
|
||||
|
||||
```bash
|
||||
export SIDE_MAPPING_FILE="/tmp/my_test_mappings.json"
|
||||
./autoflash_modular.sh
|
||||
```
|
||||
|
||||
## 📋 Requirements
|
||||
|
||||
- **bash** - Shell interpreter
|
||||
- **qmk** - QMK CLI (`python3 -m pip install qmk`)
|
||||
- **jq** - JSON processor (`sudo apt-get install jq`)
|
||||
- **findmnt** - Usually included with util-linux
|
||||
- **udevadm** - Usually included with systemd
|
||||
|
||||
## 🔒 File Permissions
|
||||
|
||||
Make scripts executable:
|
||||
```bash
|
||||
chmod +x autoflash_modular.sh
|
||||
chmod +x test/*.sh
|
||||
chmod +x lib/*.sh
|
||||
```
|
||||
|
||||
## 📝 How Device Mapping Works
|
||||
|
||||
The script intelligently handles three states:
|
||||
|
||||
### 🟢 Empty Mapping (First Time)
|
||||
- **No devices mapped yet**
|
||||
- **Asks:** "Which side will you flash first?"
|
||||
- Script learns both sides as you flash them
|
||||
- First device = saved as what you specify (left/right)
|
||||
- Second device = saved as the other side
|
||||
- Result: Complete mapping of both sides
|
||||
|
||||
### 🟡 Partial Mapping (One Side Known)
|
||||
- **One device mapped, one unknown**
|
||||
- **Auto-detects:** No asking needed!
|
||||
- If you plug in the known device → "Detected: left side"
|
||||
- If you plug in unknown device → "Detected: right side (inferred)"
|
||||
- Result: Completes mapping automatically
|
||||
|
||||
### 🔴 Complete Mapping (Both Sides Known)
|
||||
- **Both devices are mapped**
|
||||
- **Auto-detects:** Fully automatic!
|
||||
- Plug in any side → "Detected: left side" or "Detected: right side"
|
||||
- Only the two known devices are allowed
|
||||
- Unknown device → **Rejected immediately** (safety feature)
|
||||
- Result: Fast, automatic flashing with full protection
|
||||
|
||||
### Why This Matters
|
||||
- **First run**: Asks which side (one-time setup) ✅
|
||||
- **Normal use**: Fully automatic - just plug and flash! ✅
|
||||
- **Protection**: Unknown devices rejected - can't flash wrong keyboard ✅
|
||||
- **Smart**: Knows when to ask vs. when to auto-detect 🧠
|
||||
|
||||
## 📝 Additional Notes
|
||||
|
||||
- **Input timing**: You're asked which side BEFORE entering bootloader (so you can still type!)
|
||||
- **EEPROM wipe**: Liatris overwrites EEPROM on flash, so we use HOST USB info
|
||||
- **Board-ID**: INFO_UF2.TXT is NOT unique per device, don't rely on it
|
||||
- **USB serial**: Burned into RP2040 chip, persists even when EEPROM wiped
|
||||
|
||||
## 🆘 Support
|
||||
|
||||
Issues? Check:
|
||||
1. Run test scripts to isolate the problem
|
||||
2. Check device detection with `print_device_info()`
|
||||
3. Verify mappings with `list_all_mappings()`
|
||||
4. Test QMK commands directly: `qmk compile -kb ... -km ...`
|
||||
|
||||
## 📄 License
|
||||
|
||||
Same as your QMK userspace configuration.
|
||||
164
qmk_flash_tools/SOLUTION_SUMMARY.md
Normal file
164
qmk_flash_tools/SOLUTION_SUMMARY.md
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
# Solution Summary: Ask BEFORE Bootloader
|
||||
|
||||
## The Problem You Identified 🎯
|
||||
|
||||
> "Once I enter the bootloader I can't use the device - so I need to input the device-side beforehand."
|
||||
|
||||
**Absolutely correct!** This is a critical UX issue that the original design missed.
|
||||
|
||||
## The Solution ✅
|
||||
|
||||
### New Workflow Order:
|
||||
|
||||
1. **ASK which side** (while keyboard still works!)
|
||||
2. **WAIT for bootloader** (user enters bootloader mode)
|
||||
3. **DETECT device** (get USB serial from host)
|
||||
4. **VERIFY match** (does device match expected side?)
|
||||
5. **FLASH** (with correct handedness)
|
||||
|
||||
### Code Changes Made:
|
||||
|
||||
#### 1. New Function: `detect_side_with_expected()`
|
||||
**Location:** `lib/side_mapping.sh`
|
||||
|
||||
```bash
|
||||
detect_side_with_expected(device_id, expected_side)
|
||||
```
|
||||
|
||||
This function:
|
||||
- Takes the **expected** side (what user said)
|
||||
- Checks if device is in mappings
|
||||
- **First time:** Saves device as expected side ✅
|
||||
- **Matches:** Continues automatically ✅
|
||||
- **Mismatch:** Gives user options ⚠️
|
||||
|
||||
#### 2. Mismatch Handling
|
||||
|
||||
When device doesn't match expected:
|
||||
```
|
||||
⚠️ WARNING: SIDE MISMATCH DETECTED
|
||||
|
||||
Expected: left side
|
||||
Saved mapping says: right side
|
||||
|
||||
What would you like to do?
|
||||
[e] Exit safely (recommended)
|
||||
[c] Clear this mapping and save as left
|
||||
[f] Force flash as left anyway (DANGEROUS)
|
||||
```
|
||||
|
||||
#### 3. Updated Main Script
|
||||
**Location:** `autoflash_modular.sh`
|
||||
|
||||
New function:
|
||||
```bash
|
||||
flash_side_with_verification(expected_side)
|
||||
```
|
||||
|
||||
Changes to workflow:
|
||||
```bash
|
||||
# OLD: Asked AFTER device detected
|
||||
flash_side_auto() # User can't type in bootloader!
|
||||
|
||||
# NEW: Ask BEFORE device connected
|
||||
read -p "Which side will you flash first? [left/right]:"
|
||||
flash_side_with_verification("left") # ✅ Can type!
|
||||
```
|
||||
|
||||
## Complete Flow Example 📋
|
||||
|
||||
### First Run:
|
||||
```bash
|
||||
$ ./autoflash_modular.sh
|
||||
|
||||
Which side will you flash first? [left/right]: left ← Can type!
|
||||
|
||||
Please enter bootloader on the left half:
|
||||
Press Enter when ready...
|
||||
[User enters bootloader, presses Enter]
|
||||
|
||||
Waiting for device...
|
||||
Found device: serial:ABC123
|
||||
New device detected (first time setup)
|
||||
✅ Saving as left side
|
||||
|
||||
Flashing: left side
|
||||
✅ left side flashed successfully!
|
||||
|
||||
Flash the right side now? [y/n]: y
|
||||
|
||||
Please enter bootloader on the right half:
|
||||
Press Enter when ready...
|
||||
[User enters bootloader, presses Enter]
|
||||
|
||||
Waiting for device...
|
||||
Found device: serial:XYZ789
|
||||
New device detected (first time setup)
|
||||
✅ Saving as right side
|
||||
|
||||
Flashing: right side
|
||||
✅ right side flashed successfully!
|
||||
|
||||
🎉 Flashing complete!
|
||||
```
|
||||
|
||||
### Subsequent Runs (Auto-verified):
|
||||
```bash
|
||||
$ ./autoflash_modular.sh
|
||||
|
||||
Which side will you flash first? [left/right]: left
|
||||
|
||||
Please enter bootloader on the left half:
|
||||
[User enters bootloader]
|
||||
|
||||
Found device: serial:ABC123
|
||||
✅ Confirmed: This is the left side (matches saved mapping)
|
||||
|
||||
Flashing: left side
|
||||
✅ Done!
|
||||
```
|
||||
|
||||
### Mismatch Scenario:
|
||||
```bash
|
||||
Which side will you flash first? [left/right]: left
|
||||
|
||||
[User accidentally plugs in RIGHT keyboard]
|
||||
|
||||
Found device: serial:XYZ789
|
||||
⚠️ WARNING: SIDE MISMATCH DETECTED
|
||||
|
||||
Expected: left side
|
||||
Saved mapping says: right side
|
||||
|
||||
Your choice [e/c/f]: e
|
||||
|
||||
❌ Exiting safely. Please plug in the correct keyboard half.
|
||||
```
|
||||
|
||||
## Why This Works 🎯
|
||||
|
||||
✅ **User can type** - Asked before bootloader
|
||||
✅ **Safety first** - Verifies device matches
|
||||
✅ **Clear errors** - Obvious when wrong side plugged in
|
||||
✅ **Flexible** - Can update mappings if needed
|
||||
✅ **Persistent** - Remembers devices forever
|
||||
|
||||
## Files Modified
|
||||
|
||||
- `lib/side_mapping.sh` - Added `detect_side_with_expected()`
|
||||
- `autoflash_modular.sh` - Changed to ask first, then verify
|
||||
- `README.md` - Updated workflow documentation
|
||||
- `WORKFLOW.md` - New visual flow diagrams
|
||||
|
||||
## Testing
|
||||
|
||||
Test the new flow:
|
||||
```bash
|
||||
# Test mismatch handling
|
||||
./test/test_side_mapping.sh
|
||||
|
||||
# Test full workflow (needs hardware)
|
||||
./autoflash_modular.sh
|
||||
```
|
||||
|
||||
Your insight was **absolutely correct** - asking before bootloader is the only way to make this work! 🎉
|
||||
190
qmk_flash_tools/UX_IMPROVEMENT.md
Normal file
190
qmk_flash_tools/UX_IMPROVEMENT.md
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
# User Experience Comparison
|
||||
|
||||
## Old Behavior (Always Ask)
|
||||
```
|
||||
Run 1 (First time):
|
||||
───────────────────
|
||||
Script: "Which side first? [left/right]:"
|
||||
You: "left"
|
||||
[Flash left]
|
||||
Script: "Flash right? [y/n]:"
|
||||
You: "y"
|
||||
[Flash right]
|
||||
|
||||
Run 2 (Second time):
|
||||
────────────────────
|
||||
Script: "Which side first? [left/right]:" ← Asked again!
|
||||
You: "left"
|
||||
[Flash left]
|
||||
Script: "Flash right? [y/n]:"
|
||||
You: "y"
|
||||
[Flash right]
|
||||
|
||||
Run 3, 4, 5...
|
||||
──────────────
|
||||
Same thing every time - always asking! 😕
|
||||
```
|
||||
|
||||
## New Behavior (Smart Auto-Detect)
|
||||
```
|
||||
Run 1 (First time - Empty mapping):
|
||||
────────────────────────────────────
|
||||
Script: "Which side first? [left/right]:" ← Only asked first time
|
||||
You: "left"
|
||||
[Flash left]
|
||||
Script: "Flash other side? [y/n]:"
|
||||
You: "y"
|
||||
[Flash right]
|
||||
|
||||
Run 2 (Subsequent - Complete mapping):
|
||||
───────────────────────────────────────
|
||||
Script: "Plug in a keyboard half and enter bootloader..."
|
||||
You: [Plugs in left half]
|
||||
Script: "🎯 Auto-detected: left side" ← No asking!
|
||||
[Flash left]
|
||||
Script: "Flash other side? [y/n]:"
|
||||
You: "y"
|
||||
You: [Plugs in right half]
|
||||
Script: "🎯 Auto-detected: right side" ← No asking!
|
||||
[Flash right]
|
||||
|
||||
Run 3, 4, 5...
|
||||
──────────────
|
||||
Always auto-detects - never asks again! 🎉
|
||||
```
|
||||
|
||||
## Workflow Comparison
|
||||
|
||||
### First Time Setup (Empty → Complete)
|
||||
|
||||
**Old:**
|
||||
```
|
||||
Ask → Flash left → Ask → Flash right
|
||||
```
|
||||
|
||||
**New:**
|
||||
```
|
||||
Ask once → Flash left → Flash right (inferred)
|
||||
```
|
||||
|
||||
### Normal Use (Complete mapping)
|
||||
|
||||
**Old:**
|
||||
```
|
||||
Ask every time → Flash
|
||||
```
|
||||
|
||||
**New:**
|
||||
```
|
||||
Auto-detect → Flash ← No asking!
|
||||
```
|
||||
|
||||
## Decision Logic
|
||||
|
||||
```
|
||||
┌─────────────────────┐
|
||||
│ Check mapping state │
|
||||
└──────────┬──────────┘
|
||||
│
|
||||
┌─────┴──────┐
|
||||
│ │
|
||||
▼ ▼
|
||||
Empty Partial/Complete
|
||||
│ │
|
||||
▼ ▼
|
||||
📝 ASK 🎯 AUTO-DETECT
|
||||
```
|
||||
|
||||
## User Benefits
|
||||
|
||||
### Empty Mapping (First Time)
|
||||
✅ **Asks** - Needs to learn your devices
|
||||
✅ **Simple** - Just answer "left" or "right"
|
||||
✅ **One-time** - Only happens once
|
||||
|
||||
### Partial Mapping (Learning Second Device)
|
||||
✅ **Smart** - Knows which device is which
|
||||
✅ **Infers** - Unknown must be the unmapped side
|
||||
✅ **Automatic** - No asking needed
|
||||
|
||||
### Complete Mapping (Normal Use)
|
||||
✅ **Instant** - Recognizes device immediately
|
||||
✅ **No questions** - Fully automatic
|
||||
✅ **Protected** - Rejects unknown devices
|
||||
|
||||
## Key Insight
|
||||
|
||||
**User knows their hardware!**
|
||||
- If they replaced a controller → They know to clear mappings
|
||||
- If mapping exists → Trust it and auto-detect
|
||||
- Only ask when necessary (empty mapping)
|
||||
|
||||
## Example Sessions
|
||||
|
||||
### Session 1: Brand New Setup
|
||||
```
|
||||
$ ./autoflash_modular.sh
|
||||
|
||||
📝 First time setup - learning your keyboard halves
|
||||
|
||||
Which side will you flash first? [left/right]: left
|
||||
|
||||
Plug in left half and enter bootloader...
|
||||
✅ Saved as left side
|
||||
✅ left side flashed!
|
||||
|
||||
Flash the right side now? [y/n]: y
|
||||
|
||||
Plug in right half and enter bootloader...
|
||||
✅ Saved as right side (completes mapping)
|
||||
✅ right side flashed!
|
||||
|
||||
🎉 Complete! Mapping saved.
|
||||
```
|
||||
|
||||
### Session 2: Normal Reflashing
|
||||
```
|
||||
$ ./autoflash_modular.sh
|
||||
|
||||
✅ Device mapping exists - auto-detection enabled
|
||||
|
||||
Plug in a keyboard half and enter bootloader...
|
||||
🎯 Auto-detected: left side (known device)
|
||||
✅ left side flashed!
|
||||
|
||||
Flash the other keyboard half now? [y/n]: y
|
||||
|
||||
Plug in other half and enter bootloader...
|
||||
🎯 Auto-detected: right side (known device)
|
||||
✅ right side flashed!
|
||||
|
||||
🎉 Done!
|
||||
```
|
||||
|
||||
### Session 3: One Side Only
|
||||
```
|
||||
$ ./autoflash_modular.sh
|
||||
|
||||
✅ Device mapping exists - auto-detection enabled
|
||||
|
||||
Plug in a keyboard half and enter bootloader...
|
||||
🎯 Auto-detected: right side (known device)
|
||||
✅ right side flashed!
|
||||
|
||||
Flash the other keyboard half now? [y/n]: n
|
||||
|
||||
✅ Done!
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
| Scenario | Old Behavior | New Behavior |
|
||||
|----------|-------------|--------------|
|
||||
| First time | Ask | Ask (once) |
|
||||
| Second time | Ask | Auto-detect |
|
||||
| Every time after | Ask | Auto-detect |
|
||||
| User input needed | Always | Only first run |
|
||||
| Speed | Slow | Fast |
|
||||
| Convenience | Low | High |
|
||||
|
||||
**Result:** Ask once, auto-detect forever! 🚀
|
||||
200
qmk_flash_tools/WORKFLOW.md
Normal file
200
qmk_flash_tools/WORKFLOW.md
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
# Workflow Diagrams
|
||||
|
||||
## Standard Flashing Flow
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────────────┐
|
||||
│ START: Run ./autoflash_modular.sh │
|
||||
└───────────────────┬────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌────────────────────────────────────────────────────────────┐
|
||||
│ Build firmware once │
|
||||
└───────────────────┬────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌────────────────────────────────────────────────────────────┐
|
||||
│ PROMPT: "Which side will you flash first? [left/right]:" │
|
||||
│ USER INPUT: "left" ◄── Can type! Not in bootloader yet │
|
||||
└───────────────────┬────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌────────────────────────────────────────────────────────────┐
|
||||
│ Script: "Enter bootloader mode on the LEFT half..." │
|
||||
│ Script: "Press Enter when ready..." │
|
||||
│ USER: Double-taps RESET on LEFT keyboard │
|
||||
│ USER: Presses Enter │
|
||||
└───────────────────┬────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌────────────────────────────────────────────────────────────┐
|
||||
│ Script: Waiting for RP2040 device... │
|
||||
│ Script: Found device at /media/user/RPI-RP2 │
|
||||
└───────────────────┬────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌────────────────────────────────────────────────────────────┐
|
||||
│ Script: Get device identifier (e.g., serial:ABC123) │
|
||||
└───────────────────┬────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────┴───────────┐
|
||||
│ │
|
||||
▼ ▼
|
||||
┌─────────┐ ┌──────────────┐
|
||||
│ First │ │ Subsequent │
|
||||
│ Time │ │ Run │
|
||||
└────┬────┘ └──────┬───────┘
|
||||
│ │
|
||||
│ ▼
|
||||
│ ┌──────────────────────────────┐
|
||||
│ │ Device in mappings? │
|
||||
│ └────┬─────────────────┬───────┘
|
||||
│ │ │
|
||||
│ ▼ ▼
|
||||
│ ┌────────────┐ ┌────────────┐
|
||||
│ │ Matches? │ │ Mismatch! │
|
||||
│ │ ✅ Yes │ │ ⚠️ No │
|
||||
│ └─────┬──────┘ └─────┬──────┘
|
||||
│ │ │
|
||||
▼ │ │
|
||||
┌────────────────────┐ │ │
|
||||
│ Save as LEFT │ │ │
|
||||
└────────┬───────────┘ │ │
|
||||
│ │ │
|
||||
│ ▼ ▼
|
||||
│ ┌────────────┐ ┌─────────────────────┐
|
||||
│ │ Continue │ │ PROMPT USER: │
|
||||
│ │ flashing │ │ [e] Exit │
|
||||
│ └─────┬──────┘ │ [c] Clear & update │
|
||||
│ │ │ [f] Force flash │
|
||||
│ │ └──────┬──────────────┘
|
||||
│ │ │
|
||||
└─────────────────┴──────────────────┘
|
||||
│
|
||||
▼
|
||||
┌────────────────────────────────────────────────────────────┐
|
||||
│ Flash LEFT side with uf2-split-left │
|
||||
└───────────────────┬────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌────────────────────────────────────────────────────────────┐
|
||||
│ ✅ LEFT side flashed successfully! │
|
||||
└───────────────────┬────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌────────────────────────────────────────────────────────────┐
|
||||
│ PROMPT: "Flash the RIGHT side now? [y/n]:" │
|
||||
└───────────────────┬────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────┴───────────┐
|
||||
│ │
|
||||
▼ ▼
|
||||
┌─────────┐ ┌──────────┐
|
||||
│ Yes │ │ No │
|
||||
└────┬────┘ └────┬─────┘
|
||||
│ │
|
||||
│ ▼
|
||||
│ ┌────────────┐
|
||||
│ │ DONE │
|
||||
│ └────────────┘
|
||||
│
|
||||
▼
|
||||
(Repeat process for RIGHT side)
|
||||
```
|
||||
|
||||
## Mismatch Handling Detail
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────────────┐
|
||||
│ USER said: "left" │
|
||||
│ Device detected: serial:ABC123 │
|
||||
│ Mapping says: "right" │
|
||||
└───────────────────┬────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌────────────────────────────────────────────────────────────┐
|
||||
│ ⚠️ WARNING: SIDE MISMATCH DETECTED │
|
||||
│ │
|
||||
│ Expected: left side │
|
||||
│ Saved mapping says: right side │
|
||||
│ │
|
||||
│ This means either: │
|
||||
│ 1. You plugged in the WRONG keyboard half │
|
||||
│ 2. The saved mapping is incorrect │
|
||||
│ │
|
||||
│ What would you like to do? │
|
||||
│ [e] Exit safely (recommended) │
|
||||
│ [c] Clear this mapping and save as left │
|
||||
│ [f] Force flash as left anyway (DANGEROUS) │
|
||||
└───────────────────┬────────────────────────────────────────┘
|
||||
│
|
||||
┌──────────┼──────────┐
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌────────┐ ┌────────┐ ┌──────────────┐
|
||||
│ [e] │ │ [c] │ │ [f] │
|
||||
│ Exit │ │ Clear │ │ Force │
|
||||
└───┬────┘ └───┬────┘ └──────┬───────┘
|
||||
│ │ │
|
||||
│ │ ▼
|
||||
│ │ ┌──────────────────┐
|
||||
│ │ │ "Are you sure?" │
|
||||
│ │ │ [yes/no] │
|
||||
│ │ └────┬─────────────┘
|
||||
│ │ │
|
||||
│ │ ┌──────┴──────┐
|
||||
│ │ │ │
|
||||
│ │ ▼ ▼
|
||||
│ │ ┌────┐ ┌────────┐
|
||||
│ │ │yes │ │no │
|
||||
│ │ └─┬──┘ └───┬────┘
|
||||
│ │ │ │
|
||||
│ ▼ ▼ │
|
||||
│ ┌───────────┐ │
|
||||
│ │ Update │ │
|
||||
│ │ mapping & │ │
|
||||
│ │ continue │ │
|
||||
│ └─────┬─────┘ │
|
||||
│ │ │
|
||||
└────────────┴────────────────┘
|
||||
│
|
||||
▼
|
||||
┌────────────┐
|
||||
│ Exit 1 │
|
||||
└────────────┘
|
||||
```
|
||||
|
||||
## First Time Setup
|
||||
|
||||
```
|
||||
Run 1: First Device
|
||||
───────────────────
|
||||
User: "left"
|
||||
Device: serial:ABC123
|
||||
Mapping: {} (empty)
|
||||
Result: ✅ Save serial:ABC123 → left
|
||||
|
||||
Run 1: Second Device
|
||||
────────────────────
|
||||
User: "right"
|
||||
Device: serial:XYZ789
|
||||
Mapping: {"serial:ABC123": "left"}
|
||||
Result: ✅ Save serial:XYZ789 → right
|
||||
|
||||
Run 2+: Known Devices
|
||||
─────────────────────
|
||||
User: "left"
|
||||
Device: serial:ABC123
|
||||
Mapping: {"serial:ABC123": "left", "serial:XYZ789": "right"}
|
||||
Result: ✅ Match! Continue automatically
|
||||
```
|
||||
|
||||
## Why This Flow Works
|
||||
|
||||
1. **User can type** - Asked BEFORE entering bootloader
|
||||
2. **Safety first** - Verifies device matches expectation
|
||||
3. **Clear on mismatch** - User knows exactly what's wrong
|
||||
4. **Flexible** - Can update mappings or exit safely
|
||||
5. **Persistent** - Only asks once, remembers forever
|
||||
319
qmk_flash_tools/autoflash_modular.sh
Normal file
319
qmk_flash_tools/autoflash_modular.sh
Normal file
|
|
@ -0,0 +1,319 @@
|
|||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# QMK Auto-Flashing Script (Modular Version)
|
||||
# =============================================================================
|
||||
# For: Fingerpunch/Sweeeeep with Liatris (RP2040)
|
||||
#
|
||||
# Features:
|
||||
# - Intelligent device detection with three-state verification
|
||||
# - Learns devices on first run, verifies on subsequent runs
|
||||
# - Rejects unknown devices once mapping is complete (safety)
|
||||
# - Uses HOST USB information (reliable even when EEPROM is wiped)
|
||||
# - Modular design with separate testable libraries
|
||||
# =============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Get the directory where this script is located
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
LIB_DIR="$SCRIPT_DIR/lib"
|
||||
|
||||
# Configuration
|
||||
KEYBOARD="fingerpunch/sweeeeep"
|
||||
KEYMAP="smathev"
|
||||
export USB_MOUNT_PATHS=("/media/$USER" "/run/media/$USER" "/mnt")
|
||||
export RP2040_PATTERN="*RP2040*"
|
||||
export USB_WAIT_INTERVAL=0.5
|
||||
export SIDE_MAPPING_FILE="$SCRIPT_DIR/device_mappings.json"
|
||||
|
||||
# Source library modules
|
||||
source "$LIB_DIR/device_detection.sh"
|
||||
source "$LIB_DIR/side_mapping.sh"
|
||||
source "$LIB_DIR/qmk_helpers.sh"
|
||||
|
||||
# ----------------------
|
||||
# Function: flash_side_with_verification
|
||||
# Ask user which side they're flashing, then verify device matches
|
||||
# ----------------------
|
||||
flash_side_with_verification() {
|
||||
local expected_side="$1"
|
||||
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "<EFBFBD> Preparing to flash: $expected_side side"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
echo "Please enter bootloader mode on the $expected_side half:"
|
||||
echo " • Double-tap RESET button on Liatris controller"
|
||||
echo " • Wait for RPI-RP2 drive to appear"
|
||||
echo ""
|
||||
read -rp "Press Enter when you've entered bootloader mode..."
|
||||
echo ""
|
||||
echo "⏳ Waiting for device..."
|
||||
|
||||
# Wait for device to be mounted
|
||||
local mount_point
|
||||
mount_point=$(wait_for_rp2040)
|
||||
|
||||
echo ""
|
||||
|
||||
# Get unique device identifier from HOST
|
||||
local device_id
|
||||
device_id=$(get_device_identifier "$mount_point")
|
||||
|
||||
# Verify device matches expected side
|
||||
local verified_side
|
||||
verified_side=$(detect_side_with_expected "$device_id" "$expected_side")
|
||||
|
||||
echo ""
|
||||
echo "🎯 Flashing: $verified_side side"
|
||||
echo ""
|
||||
|
||||
# Flash using the verified side
|
||||
if flash_side "$KEYBOARD" "$KEYMAP" "$verified_side"; then
|
||||
echo ""
|
||||
echo "✅ $verified_side side flashed successfully!"
|
||||
echo ""
|
||||
return 0
|
||||
else
|
||||
echo ""
|
||||
echo "❌ Failed to flash $verified_side side"
|
||||
echo ""
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ----------------------
|
||||
# Function: flash_side_auto_detect
|
||||
# Auto-detect which side is plugged in (no asking)
|
||||
# Used when mapping exists (partial or complete)
|
||||
# ----------------------
|
||||
flash_side_auto_detect() {
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "🔌 Plug in a keyboard half and enter bootloader mode"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
echo "Instructions:"
|
||||
echo " • Plug in the keyboard half you want to flash"
|
||||
echo " • Double-tap RESET button on Liatris controller"
|
||||
echo " • Wait for RPI-RP2 drive to appear"
|
||||
echo ""
|
||||
read -rp "Press Enter when you've entered bootloader mode..."
|
||||
echo ""
|
||||
echo "⏳ Waiting for device..."
|
||||
|
||||
# Wait for device to be mounted
|
||||
local mount_point
|
||||
mount_point=$(wait_for_rp2040)
|
||||
|
||||
echo ""
|
||||
|
||||
# Get unique device identifier from HOST
|
||||
local device_id
|
||||
device_id=$(get_device_identifier "$mount_point")
|
||||
|
||||
# Get saved side (if exists)
|
||||
local saved_side
|
||||
saved_side=$(get_saved_side "$device_id" 2>/dev/null)
|
||||
|
||||
local detected_side
|
||||
|
||||
if [[ -n "$saved_side" ]]; then
|
||||
# Device is known - use saved mapping
|
||||
echo " 🎯 Auto-detected: $saved_side side (known device)" >&2
|
||||
detected_side="$saved_side"
|
||||
else
|
||||
# Device is unknown - infer from mapping state
|
||||
local mapping_state
|
||||
mapping_state=$(get_mapping_state)
|
||||
|
||||
if [[ "$mapping_state" == "partial" ]]; then
|
||||
# Partial mapping - this must be the unmapped side
|
||||
local unmapped_side
|
||||
unmapped_side=$(get_unmapped_side)
|
||||
echo " 🎯 Auto-detected: $unmapped_side side (inferred from partial mapping)" >&2
|
||||
detected_side="$unmapped_side"
|
||||
|
||||
# Save this device
|
||||
echo " 📝 Saving device mapping..." >&2
|
||||
save_side_mapping "$device_id" "$detected_side"
|
||||
else
|
||||
# Complete mapping but unknown device - this should error
|
||||
echo "" >&2
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" >&2
|
||||
echo "❌ ERROR: UNKNOWN DEVICE (COMPLETE MAPPING)" >&2
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" >&2
|
||||
echo "" >&2
|
||||
echo "Detected device: $device_id" >&2
|
||||
echo "Expected one of the known devices:" >&2
|
||||
get_mapped_devices | tr ' ' '\n' | while read dev; do
|
||||
local side=$(get_saved_side "$dev")
|
||||
echo " - $dev ($side side)" >&2
|
||||
done
|
||||
echo "" >&2
|
||||
echo "Both sides are already mapped!" >&2
|
||||
echo "If you replaced a controller, clear mappings:" >&2
|
||||
echo " rm $SIDE_MAPPING_FILE" >&2
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "🎯 Flashing: $detected_side side"
|
||||
echo ""
|
||||
|
||||
# Flash using the detected side
|
||||
if flash_side "$KEYBOARD" "$KEYMAP" "$detected_side"; then
|
||||
echo ""
|
||||
echo "✅ $detected_side side flashed successfully!"
|
||||
echo ""
|
||||
return 0
|
||||
else
|
||||
echo ""
|
||||
echo "❌ Failed to flash $detected_side side"
|
||||
echo ""
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ----------------------
|
||||
# Function: main
|
||||
# Main workflow
|
||||
# ----------------------
|
||||
main() {
|
||||
echo ""
|
||||
echo "╔═══════════════════════════════════════════════════════════╗"
|
||||
echo "║ QMK Auto-Flash: Fingerpunch Sweeeeep + Liatris (RP2040) ║"
|
||||
echo "╚═══════════════════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
echo "This script will:"
|
||||
echo " • Build firmware once"
|
||||
echo " • Auto-detect which keyboard half you plug in"
|
||||
echo " • Flash the correct handedness (left/right)"
|
||||
echo " • Remember your devices for future flashing"
|
||||
echo ""
|
||||
echo "Note: On first run, you'll be asked to identify each side."
|
||||
echo " After that, detection is fully automatic!"
|
||||
echo ""
|
||||
|
||||
# Check dependencies
|
||||
if ! check_qmk_installed; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! check_jq_installed; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Initialize mapping file
|
||||
init_mapping_file
|
||||
|
||||
# Build firmware once
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
if ! build_firmware "$KEYBOARD" "$KEYMAP"; then
|
||||
echo "❌ Build failed. Exiting."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "🚀 Ready to flash!"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
|
||||
# Check mapping state to determine if we need to ask
|
||||
local mapping_state
|
||||
mapping_state=$(get_mapping_state)
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# EMPTY MAPPING: Must ask user which side (learning mode)
|
||||
# -----------------------------------------------------------------
|
||||
if [[ "$mapping_state" == "empty" ]]; then
|
||||
echo "📝 First time setup - learning your keyboard halves"
|
||||
echo ""
|
||||
|
||||
# Ask which side to flash first (BEFORE entering bootloader)
|
||||
local first_side
|
||||
while true; do
|
||||
read -rp "Which side will you flash first? [left/right]: " first_side
|
||||
first_side=${first_side,,}
|
||||
if [[ "$first_side" == "left" || "$first_side" == "right" ]]; then
|
||||
break
|
||||
else
|
||||
echo "⚠️ Please enter 'left' or 'right'"
|
||||
fi
|
||||
done
|
||||
echo ""
|
||||
|
||||
# Flash first side with verification
|
||||
if ! flash_side_with_verification "$first_side"; then
|
||||
echo "❌ Flashing failed. Exiting."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Determine the other side
|
||||
local second_side
|
||||
if [[ "$first_side" == "left" ]]; then
|
||||
second_side="right"
|
||||
else
|
||||
second_side="left"
|
||||
fi
|
||||
|
||||
# Ask if user wants to flash the second side
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
read -rp "Flash the $second_side side now? [y/n]: " DO_SECOND
|
||||
DO_SECOND=${DO_SECOND,,}
|
||||
echo ""
|
||||
|
||||
if [[ "$DO_SECOND" == "y" ]]; then
|
||||
# Flash second side with verification
|
||||
if ! flash_side_with_verification "$second_side"; then
|
||||
echo "❌ Flashing failed. Exiting."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# PARTIAL or COMPLETE MAPPING: Auto-detect (no asking needed)
|
||||
# -----------------------------------------------------------------
|
||||
else
|
||||
echo "✅ Device mapping exists - auto-detection enabled"
|
||||
echo ""
|
||||
|
||||
# Flash first side with auto-detection
|
||||
if ! flash_side_auto_detect; then
|
||||
echo "❌ Flashing failed. Exiting."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 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
|
||||
# Flash second side with auto-detection
|
||||
if ! flash_side_auto_detect; then
|
||||
echo "❌ Flashing failed. Exiting."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "🎉 Flashing complete!"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
echo "✓ Handedness has been set in EEPROM"
|
||||
echo "✓ Device mappings saved to: $SIDE_MAPPING_FILE"
|
||||
echo "✓ Future runs will automatically detect sides"
|
||||
echo "✓ Future firmware updates will preserve handedness"
|
||||
echo ""
|
||||
|
||||
# Show the saved mappings
|
||||
echo "Current device mappings:"
|
||||
list_all_mappings
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Execute main
|
||||
main "$@"
|
||||
187
qmk_flash_tools/lib/device_detection.sh
Normal file
187
qmk_flash_tools/lib/device_detection.sh
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# Device Detection Library
|
||||
# =============================================================================
|
||||
# Functions for detecting and identifying RP2040 devices in bootloader mode
|
||||
# Uses HOST-side USB information (serial, port location)
|
||||
# =============================================================================
|
||||
|
||||
# ----------------------
|
||||
# Function: wait_for_rp2040
|
||||
# Wait until an RP2040 UF2 device is mounted on any of the configured paths
|
||||
# Returns the mount point
|
||||
# Usage: mount_point=$(wait_for_rp2040)
|
||||
# ----------------------
|
||||
wait_for_rp2040() {
|
||||
local usb_mount_paths=("${USB_MOUNT_PATHS[@]:-/media/$USER /run/media/$USER /mnt}")
|
||||
local rp2040_pattern="${RP2040_PATTERN:-*RP2040*}"
|
||||
local wait_interval="${USB_WAIT_INTERVAL:-0.5}"
|
||||
|
||||
echo "⏳ Waiting for RP2040 UF2 device..." >&2
|
||||
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" >&2
|
||||
echo "$device"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
sleep "$wait_interval"
|
||||
done
|
||||
}
|
||||
|
||||
# ----------------------
|
||||
# Function: get_usb_serial_from_host
|
||||
# Get the USB serial number from the HOST system (not from the device itself)
|
||||
# This is the ONLY reliable way to identify devices when EEPROM is wiped
|
||||
# Usage: serial=$(get_usb_serial_from_host "/media/user/RPI-RP2")
|
||||
# ----------------------
|
||||
get_usb_serial_from_host() {
|
||||
local mount_point="$1"
|
||||
|
||||
if [[ -z "$mount_point" ]]; then
|
||||
echo "" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Method 1: Get the block device, then trace to USB serial
|
||||
local dev
|
||||
dev=$(findmnt -n -o SOURCE --target "$mount_point" 2>/dev/null || echo "")
|
||||
|
||||
if [[ -n "$dev" ]]; then
|
||||
local block_dev=$(basename "$dev")
|
||||
|
||||
# Try to find USB serial through sysfs
|
||||
local sys_path="/sys/class/block/$block_dev"
|
||||
|
||||
# Walk up the device tree to find the USB device
|
||||
local current_path=$(readlink -f "$sys_path/device" 2>/dev/null || echo "")
|
||||
|
||||
while [[ -n "$current_path" && "$current_path" != "/sys" ]]; do
|
||||
# Check if this directory has a serial file
|
||||
if [[ -f "$current_path/serial" ]]; then
|
||||
cat "$current_path/serial"
|
||||
return 0
|
||||
fi
|
||||
# Also check for idVendor/idProduct to confirm it's a USB device
|
||||
if [[ -f "$current_path/idVendor" ]]; then
|
||||
# Found USB device level, check for serial
|
||||
if [[ -f "$current_path/serial" ]]; then
|
||||
cat "$current_path/serial"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
# Move up one level
|
||||
current_path=$(dirname "$current_path")
|
||||
done
|
||||
fi
|
||||
|
||||
# Method 2: Use udevadm to get USB info
|
||||
if [[ -n "$dev" ]]; then
|
||||
local serial
|
||||
serial=$(udevadm info --query=property --name="$dev" 2>/dev/null | grep "ID_SERIAL_SHORT=" | cut -d'=' -f2)
|
||||
if [[ -n "$serial" ]]; then
|
||||
echo "$serial"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# No serial found
|
||||
echo ""
|
||||
return 1
|
||||
}
|
||||
|
||||
# ----------------------
|
||||
# Function: get_usb_device_path
|
||||
# Get a unique identifier based on USB physical port location
|
||||
# This persists even when serial is not available
|
||||
# Usage: usbpath=$(get_usb_device_path "/media/user/RPI-RP2")
|
||||
# ----------------------
|
||||
get_usb_device_path() {
|
||||
local mount_point="$1"
|
||||
|
||||
if [[ -z "$mount_point" ]]; then
|
||||
echo "" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local dev
|
||||
dev=$(findmnt -n -o SOURCE --target "$mount_point" 2>/dev/null || echo "")
|
||||
|
||||
if [[ -n "$dev" ]]; then
|
||||
# Get the physical USB path (bus and port numbers)
|
||||
local devpath
|
||||
devpath=$(udevadm info --query=property --name="$dev" 2>/dev/null | grep "DEVPATH=" | cut -d'=' -f2)
|
||||
if [[ -n "$devpath" ]]; then
|
||||
# Extract the USB bus and port info (e.g., /devices/pci0000:00/0000:00:14.0/usb1/1-3/1-3:1.0)
|
||||
local usbpath=$(echo "$devpath" | grep -oP 'usb\d+/\d+-[\d.]+')
|
||||
if [[ -n "$usbpath" ]]; then
|
||||
echo "$usbpath"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
return 1
|
||||
}
|
||||
|
||||
# ----------------------
|
||||
# Function: get_device_identifier
|
||||
# Get the best available identifier for the USB device
|
||||
# Prefers USB serial, falls back to USB port location, then mount path
|
||||
# Usage: device_id=$(get_device_identifier "/media/user/RPI-RP2")
|
||||
# Returns: "serial:ABC123" or "usbpath:usb1/1-3" or "mount:RPI-RP2"
|
||||
# ----------------------
|
||||
get_device_identifier() {
|
||||
local mount_point="$1"
|
||||
|
||||
if [[ -z "$mount_point" ]]; then
|
||||
echo "Error: mount_point required" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Try to get USB serial from host (PREFERRED)
|
||||
local usb_serial
|
||||
usb_serial=$(get_usb_serial_from_host "$mount_point")
|
||||
|
||||
if [[ -n "$usb_serial" ]]; then
|
||||
echo "serial:$usb_serial"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Try USB device path (FALLBACK 1)
|
||||
local usb_path
|
||||
usb_path=$(get_usb_device_path "$mount_point")
|
||||
if [[ -n "$usb_path" ]]; then
|
||||
echo "usbpath:$usb_path"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Final fallback: use mount point basename (FALLBACK 2)
|
||||
echo "mount:$(basename "$mount_point")"
|
||||
return 0
|
||||
}
|
||||
|
||||
# ----------------------
|
||||
# Function: print_device_info
|
||||
# Print detailed information about a detected device (for debugging)
|
||||
# Usage: print_device_info "/media/user/RPI-RP2"
|
||||
# ----------------------
|
||||
print_device_info() {
|
||||
local mount_point="$1"
|
||||
|
||||
echo "Device Information:"
|
||||
echo " Mount Point: $mount_point"
|
||||
|
||||
local serial=$(get_usb_serial_from_host "$mount_point")
|
||||
echo " USB Serial: ${serial:-[not available]}"
|
||||
|
||||
local usbpath=$(get_usb_device_path "$mount_point")
|
||||
echo " USB Path: ${usbpath:-[not available]}"
|
||||
|
||||
local device_id=$(get_device_identifier "$mount_point")
|
||||
echo " Identifier: $device_id"
|
||||
}
|
||||
143
qmk_flash_tools/lib/qmk_helpers.sh
Normal file
143
qmk_flash_tools/lib/qmk_helpers.sh
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# QMK Helper Functions Library
|
||||
# =============================================================================
|
||||
# Functions for building and flashing QMK firmware
|
||||
# =============================================================================
|
||||
|
||||
# ----------------------
|
||||
# Function: build_firmware
|
||||
# Build QMK firmware for the specified keyboard and keymap
|
||||
# Usage: build_firmware "fingerpunch/sweeeeep" "smathev"
|
||||
# ----------------------
|
||||
build_firmware() {
|
||||
local keyboard="$1"
|
||||
local keymap="$2"
|
||||
|
||||
if [[ -z "$keyboard" || -z "$keymap" ]]; then
|
||||
echo "❌ Error: keyboard and keymap required" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" >&2
|
||||
echo "🛠 Building firmware for $keyboard" >&2
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" >&2
|
||||
|
||||
qmk compile -kb "$keyboard" -km "$keymap"
|
||||
|
||||
if [[ $? -eq 0 ]]; then
|
||||
echo "✅ Firmware compiled successfully" >&2
|
||||
echo "" >&2
|
||||
return 0
|
||||
else
|
||||
echo "❌ Firmware compilation failed" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ----------------------
|
||||
# Function: flash_with_bootloader
|
||||
# Flash firmware using a specific bootloader target
|
||||
# Usage: flash_with_bootloader "fingerpunch/sweeeeep" "smathev" "uf2-split-left"
|
||||
# ----------------------
|
||||
flash_with_bootloader() {
|
||||
local keyboard="$1"
|
||||
local keymap="$2"
|
||||
local bootloader="$3"
|
||||
|
||||
if [[ -z "$keyboard" || -z "$keymap" || -z "$bootloader" ]]; then
|
||||
echo "❌ Error: keyboard, keymap, and bootloader required" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "📤 Flashing firmware with bootloader: $bootloader" >&2
|
||||
echo "" >&2
|
||||
|
||||
qmk flash -kb "$keyboard" -km "$keymap" -bl "$bootloader"
|
||||
|
||||
if [[ $? -eq 0 ]]; then
|
||||
echo "" >&2
|
||||
echo "✅ Firmware flashed successfully!" >&2
|
||||
return 0
|
||||
else
|
||||
echo "" >&2
|
||||
echo "❌ Firmware flashing failed" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ----------------------
|
||||
# Function: flash_side
|
||||
# Flash firmware for a specific keyboard side (left/right)
|
||||
# Usage: flash_side "fingerpunch/sweeeeep" "smathev" "left"
|
||||
# ----------------------
|
||||
flash_side() {
|
||||
local keyboard="$1"
|
||||
local keymap="$2"
|
||||
local side="$3"
|
||||
|
||||
if [[ -z "$keyboard" || -z "$keymap" || -z "$side" ]]; then
|
||||
echo "❌ Error: keyboard, keymap, and side required" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ "$side" != "left" && "$side" != "right" ]]; then
|
||||
echo "❌ Error: side must be 'left' or 'right'" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local bootloader="uf2-split-$side"
|
||||
|
||||
echo "🎯 Flashing $side side with handedness" >&2
|
||||
|
||||
flash_with_bootloader "$keyboard" "$keymap" "$bootloader"
|
||||
}
|
||||
|
||||
# ----------------------
|
||||
# Function: check_qmk_installed
|
||||
# Verify QMK CLI is installed and available
|
||||
# Usage: check_qmk_installed || exit 1
|
||||
# ----------------------
|
||||
check_qmk_installed() {
|
||||
if ! command -v qmk &> /dev/null; then
|
||||
echo "❌ Error: 'qmk' CLI is not installed or not in PATH" >&2
|
||||
echo " Install it with: python3 -m pip install qmk" >&2
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# ----------------------
|
||||
# Function: verify_keyboard_exists
|
||||
# Check if a keyboard definition exists in QMK
|
||||
# Usage: verify_keyboard_exists "fingerpunch/sweeeeep"
|
||||
# ----------------------
|
||||
verify_keyboard_exists() {
|
||||
local keyboard="$1"
|
||||
|
||||
if [[ -z "$keyboard" ]]; then
|
||||
echo "❌ Error: keyboard required" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Try to list keymaps for this keyboard
|
||||
qmk list-keymaps -kb "$keyboard" &>/dev/null
|
||||
|
||||
if [[ $? -eq 0 ]]; then
|
||||
return 0
|
||||
else
|
||||
echo "❌ Error: Keyboard '$keyboard' not found in QMK" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ----------------------
|
||||
# Function: clean_build
|
||||
# Clean previous build artifacts
|
||||
# Usage: clean_build
|
||||
# ----------------------
|
||||
clean_build() {
|
||||
echo "🧹 Cleaning previous build..." >&2
|
||||
qmk clean &>/dev/null
|
||||
echo "✅ Build directory cleaned" >&2
|
||||
}
|
||||
481
qmk_flash_tools/lib/side_mapping.sh
Normal file
481
qmk_flash_tools/lib/side_mapping.sh
Normal file
|
|
@ -0,0 +1,481 @@
|
|||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# Side Mapping Library
|
||||
# =============================================================================
|
||||
# Functions for storing and retrieving keyboard side mappings
|
||||
# Maps USB device identifiers to left/right sides
|
||||
# =============================================================================
|
||||
|
||||
# Default mapping file location (will be set by calling script)
|
||||
# Falls back to current directory if not set
|
||||
SIDE_MAPPING_FILE="${SIDE_MAPPING_FILE:-./device_mappings.json}"
|
||||
|
||||
# ----------------------
|
||||
# Function: init_mapping_file
|
||||
# Ensure the mapping file exists
|
||||
# Usage: init_mapping_file
|
||||
# ----------------------
|
||||
init_mapping_file() {
|
||||
if [[ ! -f "$SIDE_MAPPING_FILE" ]]; then
|
||||
echo "{}" > "$SIDE_MAPPING_FILE"
|
||||
echo "Created new mapping file: $SIDE_MAPPING_FILE" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
# ----------------------
|
||||
# Function: check_jq_installed
|
||||
# Verify jq is available for JSON operations
|
||||
# Usage: check_jq_installed || exit 1
|
||||
# ----------------------
|
||||
check_jq_installed() {
|
||||
if ! command -v jq &> /dev/null; then
|
||||
echo "❌ Error: 'jq' is required but not installed." >&2
|
||||
echo " Install it with: sudo apt-get install jq" >&2
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# ----------------------
|
||||
# Function: get_saved_side
|
||||
# Retrieve the saved side mapping for a device identifier
|
||||
# Usage: side=$(get_saved_side "serial:ABC123")
|
||||
# Returns: "left", "right", or empty string if not found
|
||||
# ----------------------
|
||||
get_saved_side() {
|
||||
local device_id="$1"
|
||||
|
||||
if [[ -z "$device_id" ]]; then
|
||||
echo "" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
check_jq_installed || return 1
|
||||
init_mapping_file
|
||||
|
||||
local side
|
||||
side=$(jq -r --arg id "$device_id" '.[$id] // ""' "$SIDE_MAPPING_FILE" 2>/dev/null)
|
||||
|
||||
# Handle null or empty
|
||||
if [[ "$side" == "null" || -z "$side" ]]; then
|
||||
echo ""
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "$side"
|
||||
return 0
|
||||
}
|
||||
|
||||
# ----------------------
|
||||
# Function: save_side_mapping
|
||||
# Save a device identifier to side mapping
|
||||
# Usage: save_side_mapping "serial:ABC123" "left"
|
||||
# ----------------------
|
||||
save_side_mapping() {
|
||||
local device_id="$1"
|
||||
local side="$2"
|
||||
|
||||
if [[ -z "$device_id" || -z "$side" ]]; then
|
||||
echo "Error: device_id and side required" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ "$side" != "left" && "$side" != "right" ]]; then
|
||||
echo "Error: side must be 'left' or 'right'" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
check_jq_installed || return 1
|
||||
init_mapping_file
|
||||
|
||||
# Save mapping
|
||||
local tmpfile=$(mktemp)
|
||||
jq --arg id "$device_id" --arg side "$side" '. + {($id): $side}' "$SIDE_MAPPING_FILE" > "$tmpfile"
|
||||
|
||||
if [[ $? -eq 0 ]]; then
|
||||
mv "$tmpfile" "$SIDE_MAPPING_FILE"
|
||||
echo "✅ Saved mapping: $device_id → $side" >&2
|
||||
return 0
|
||||
else
|
||||
rm -f "$tmpfile"
|
||||
echo "❌ Failed to save mapping" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ----------------------
|
||||
# Function: prompt_for_side
|
||||
# Ask user to identify which side a device is
|
||||
# Usage: side=$(prompt_for_side "serial:ABC123")
|
||||
# Returns: "left" or "right"
|
||||
# ----------------------
|
||||
prompt_for_side() {
|
||||
local device_id="$1"
|
||||
|
||||
echo "" >&2
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" >&2
|
||||
echo "⚠️ UNKNOWN DEVICE - First Time Setup" >&2
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" >&2
|
||||
echo "" >&2
|
||||
echo "The script has detected a keyboard half that it hasn't" >&2
|
||||
echo "seen before. This is expected on first run." >&2
|
||||
echo "" >&2
|
||||
echo "Device ID: $device_id" >&2
|
||||
echo "" >&2
|
||||
echo "Please tell me which side this is so I can remember it" >&2
|
||||
echo "for future flashing sessions." >&2
|
||||
echo "" >&2
|
||||
|
||||
local side
|
||||
while true; do
|
||||
read -rp "Which side is currently plugged in? [left/right]: " side
|
||||
side=${side,,}
|
||||
|
||||
if [[ "$side" == "left" || "$side" == "right" ]]; then
|
||||
echo "$side"
|
||||
return 0
|
||||
else
|
||||
echo "❌ Invalid input. Please type 'left' or 'right'." >&2
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# ----------------------
|
||||
# Function: detect_side
|
||||
# Determine the left/right side of a device
|
||||
# Checks saved mapping first, prompts user if unknown, then saves
|
||||
# Usage: side=$(detect_side "serial:ABC123")
|
||||
# Returns: "left" or "right"
|
||||
# ----------------------
|
||||
detect_side() {
|
||||
local device_id="$1"
|
||||
|
||||
if [[ -z "$device_id" ]]; then
|
||||
echo "Error: device_id required" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo " Device Identifier: $device_id" >&2
|
||||
|
||||
# Try to get saved side
|
||||
local side
|
||||
side=$(get_saved_side "$device_id" 2>/dev/null)
|
||||
|
||||
if [[ -n "$side" ]]; then
|
||||
# Found in mapping
|
||||
echo " ✅ Recognized: $side side (from saved mapping)" >&2
|
||||
echo "$side"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Not found - prompt user
|
||||
side=$(prompt_for_side "$device_id")
|
||||
|
||||
# Save the mapping
|
||||
save_side_mapping "$device_id" "$side"
|
||||
|
||||
echo "" >&2
|
||||
echo " Next time this device is detected, it will be" >&2
|
||||
echo " automatically identified as the $side side." >&2
|
||||
echo "" >&2
|
||||
|
||||
echo "$side"
|
||||
return 0
|
||||
}
|
||||
|
||||
# ----------------------
|
||||
# Function: get_mapping_state
|
||||
# Determine the current state of the mapping file
|
||||
# Returns: "empty", "partial", or "complete"
|
||||
# ----------------------
|
||||
get_mapping_state() {
|
||||
check_jq_installed || return 1
|
||||
init_mapping_file
|
||||
|
||||
local count
|
||||
count=$(jq 'length' "$SIDE_MAPPING_FILE")
|
||||
|
||||
case $count in
|
||||
0) echo "empty" ;;
|
||||
1) echo "partial" ;;
|
||||
2) echo "complete" ;;
|
||||
*) echo "unknown" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# ----------------------
|
||||
# Function: get_mapped_devices
|
||||
# Get list of all mapped device IDs
|
||||
# Returns: space-separated list of device IDs
|
||||
# ----------------------
|
||||
get_mapped_devices() {
|
||||
check_jq_installed || return 1
|
||||
init_mapping_file
|
||||
|
||||
jq -r 'keys[]' "$SIDE_MAPPING_FILE" | tr '\n' ' '
|
||||
}
|
||||
|
||||
# ----------------------
|
||||
# Function: get_unmapped_side
|
||||
# If mapping is partial, determine which side is NOT mapped yet
|
||||
# Returns: "left", "right", or empty string if both/neither mapped
|
||||
# ----------------------
|
||||
get_unmapped_side() {
|
||||
check_jq_installed || return 1
|
||||
init_mapping_file
|
||||
|
||||
local has_left has_right
|
||||
has_left=$(jq -r 'to_entries | map(select(.value == "left")) | length' "$SIDE_MAPPING_FILE")
|
||||
has_right=$(jq -r 'to_entries | map(select(.value == "right")) | length' "$SIDE_MAPPING_FILE")
|
||||
|
||||
if [[ "$has_left" -eq 0 ]]; then
|
||||
echo "left"
|
||||
elif [[ "$has_right" -eq 0 ]]; then
|
||||
echo "right"
|
||||
else
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
# ----------------------
|
||||
# Function: detect_side_with_expected
|
||||
# Detect device side and verify it matches expected side
|
||||
# Uses intelligent verification based on mapping state
|
||||
# Usage: side=$(detect_side_with_expected "serial:ABC123" "left")
|
||||
# Returns: "left" or "right", or exits on error
|
||||
# ----------------------
|
||||
detect_side_with_expected() {
|
||||
local device_id="$1"
|
||||
local expected_side="$2"
|
||||
|
||||
if [[ -z "$device_id" || -z "$expected_side" ]]; then
|
||||
echo "Error: device_id and expected_side required" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo " Device Identifier: $device_id" >&2
|
||||
echo " Expected Side: $expected_side" >&2
|
||||
|
||||
# Get current mapping state
|
||||
local mapping_state
|
||||
mapping_state=$(get_mapping_state)
|
||||
echo " Mapping State: $mapping_state" >&2
|
||||
|
||||
# Try to get saved side for this device
|
||||
local saved_side
|
||||
saved_side=$(get_saved_side "$device_id" 2>/dev/null)
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# STATE 1: EMPTY MAPPING (Learning Mode)
|
||||
# -----------------------------------------------------------------
|
||||
if [[ "$mapping_state" == "empty" ]]; then
|
||||
echo "" >&2
|
||||
echo " 📝 Learning mode (first time setup)" >&2
|
||||
echo " ✅ Saving device as $expected_side side" >&2
|
||||
save_side_mapping "$device_id" "$expected_side"
|
||||
echo "$expected_side"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# STATE 2: PARTIAL MAPPING (One Side Known)
|
||||
# -----------------------------------------------------------------
|
||||
if [[ "$mapping_state" == "partial" ]]; then
|
||||
local unmapped_side
|
||||
unmapped_side=$(get_unmapped_side)
|
||||
|
||||
if [[ -n "$saved_side" ]]; then
|
||||
# Device is known - verify it matches
|
||||
if [[ "$saved_side" == "$expected_side" ]]; then
|
||||
echo " ✅ Confirmed: $expected_side side (matches saved mapping)" >&2
|
||||
echo "$expected_side"
|
||||
return 0
|
||||
else
|
||||
# MISMATCH in partial state
|
||||
echo "" >&2
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" >&2
|
||||
echo "❌ ERROR: MISMATCH IN PARTIAL MAPPING" >&2
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" >&2
|
||||
echo "" >&2
|
||||
echo "Expected: $expected_side side" >&2
|
||||
echo "Device is saved as: $saved_side side" >&2
|
||||
echo "" >&2
|
||||
echo "This device is known but on the wrong side!" >&2
|
||||
echo "Clear mappings and re-learn: rm $SIDE_MAPPING_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
# Device is unknown - check if user expects the unmapped side
|
||||
if [[ "$expected_side" == "$unmapped_side" ]]; then
|
||||
echo "" >&2
|
||||
echo " ℹ️ Unknown device, expecting unmapped side" >&2
|
||||
echo " ✅ Saving as $expected_side side (completes mapping)" >&2
|
||||
save_side_mapping "$device_id" "$expected_side"
|
||||
echo "$expected_side"
|
||||
return 0
|
||||
else
|
||||
# User expects mapped side but got unknown device
|
||||
echo "" >&2
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" >&2
|
||||
echo "❌ ERROR: AMBIGUOUS DEVICE IN PARTIAL MAPPING" >&2
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" >&2
|
||||
echo "" >&2
|
||||
echo "Expected: $expected_side side (which is already mapped)" >&2
|
||||
echo "But got: Unknown device $device_id" >&2
|
||||
echo "" >&2
|
||||
echo "This could be:" >&2
|
||||
echo " 1. A replacement controller for $expected_side side" >&2
|
||||
echo " 2. The wrong side plugged in" >&2
|
||||
echo "" >&2
|
||||
echo "Cannot determine safely!" >&2
|
||||
echo "Clear mappings and re-learn: rm $SIDE_MAPPING_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# STATE 3: COMPLETE MAPPING (Both Sides Known)
|
||||
# -----------------------------------------------------------------
|
||||
if [[ "$mapping_state" == "complete" ]]; then
|
||||
if [[ -z "$saved_side" ]]; then
|
||||
# Unknown device with complete mapping = ERROR
|
||||
echo "" >&2
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" >&2
|
||||
echo "❌ ERROR: UNKNOWN DEVICE (COMPLETE MAPPING)" >&2
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" >&2
|
||||
echo "" >&2
|
||||
echo "Detected device: $device_id" >&2
|
||||
echo "Expected one of the known devices:" >&2
|
||||
get_mapped_devices | tr ' ' '\n' | while read dev; do
|
||||
local side=$(get_saved_side "$dev")
|
||||
echo " - $dev ($side side)" >&2
|
||||
done
|
||||
echo "" >&2
|
||||
echo "Mapping is complete (both sides known)." >&2
|
||||
echo "Unknown devices are not allowed!" >&2
|
||||
echo "" >&2
|
||||
echo "If you replaced a controller, clear mappings:" >&2
|
||||
echo " rm $SIDE_MAPPING_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Device is known - verify it matches
|
||||
if [[ "$saved_side" == "$expected_side" ]]; then
|
||||
echo " ✅ Confirmed: $expected_side side (matches saved mapping)" >&2
|
||||
echo "$expected_side"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# MISMATCH! Device is known but wrong side
|
||||
echo "" >&2
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" >&2
|
||||
echo "⚠️ WARNING: SIDE MISMATCH DETECTED" >&2
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" >&2
|
||||
echo "" >&2
|
||||
echo "Expected: $expected_side side" >&2
|
||||
echo "Saved mapping says: $saved_side side" >&2
|
||||
echo "" >&2
|
||||
echo "This means either:" >&2
|
||||
echo " 1. You plugged in the WRONG keyboard half" >&2
|
||||
echo " 2. The saved mapping is incorrect" >&2
|
||||
echo "" >&2
|
||||
echo "What would you like to do?" >&2
|
||||
echo " [e] Exit safely (recommended)" >&2
|
||||
echo " [c] Clear this mapping and save as $expected_side" >&2
|
||||
echo " [f] Force flash as $expected_side anyway (DANGEROUS)" >&2
|
||||
echo "" >&2
|
||||
|
||||
local choice
|
||||
while true; do
|
||||
read -rp "Your choice [e/c/f]: " choice
|
||||
choice=${choice,,}
|
||||
|
||||
case "$choice" in
|
||||
e)
|
||||
echo "" >&2
|
||||
echo "❌ Exiting safely. Please plug in the correct keyboard half." >&2
|
||||
exit 1
|
||||
;;
|
||||
c)
|
||||
echo "" >&2
|
||||
echo "🔄 Clearing old mapping and saving as $expected_side side..." >&2
|
||||
save_side_mapping "$device_id" "$expected_side"
|
||||
echo "✅ Mapping updated" >&2
|
||||
echo "$expected_side"
|
||||
return 0
|
||||
;;
|
||||
f)
|
||||
echo "" >&2
|
||||
echo "⚠️ WARNING: Flashing device as $expected_side despite mismatch!" >&2
|
||||
echo " This could result in incorrect keyboard behavior." >&2
|
||||
read -rp " Are you absolutely sure? [yes/no]: " confirm
|
||||
if [[ "$confirm" == "yes" ]]; then
|
||||
# Don't update mapping, just return expected side
|
||||
echo " Proceeding with $expected_side..." >&2
|
||||
echo "$expected_side"
|
||||
return 0
|
||||
else
|
||||
echo " Cancelled. Exiting." >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "Invalid choice. Please enter 'e', 'c', or 'f'." >&2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
# ----------------------
|
||||
# Function: list_all_mappings
|
||||
# Display all saved device mappings
|
||||
# Usage: list_all_mappings
|
||||
# ----------------------
|
||||
list_all_mappings() {
|
||||
check_jq_installed || return 1
|
||||
init_mapping_file
|
||||
|
||||
echo "Saved Device Mappings:"
|
||||
jq '.' "$SIDE_MAPPING_FILE"
|
||||
}
|
||||
|
||||
# ----------------------
|
||||
# Function: clear_mapping
|
||||
# Remove a specific device mapping
|
||||
# Usage: clear_mapping "serial:ABC123"
|
||||
# ----------------------
|
||||
clear_mapping() {
|
||||
local device_id="$1"
|
||||
|
||||
if [[ -z "$device_id" ]]; then
|
||||
echo "Error: device_id required" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
check_jq_installed || return 1
|
||||
init_mapping_file
|
||||
|
||||
local tmpfile=$(mktemp)
|
||||
jq --arg id "$device_id" 'del(.[$id])' "$SIDE_MAPPING_FILE" > "$tmpfile"
|
||||
|
||||
if [[ $? -eq 0 ]]; then
|
||||
mv "$tmpfile" "$SIDE_MAPPING_FILE"
|
||||
echo "✅ Removed mapping for: $device_id" >&2
|
||||
return 0
|
||||
else
|
||||
rm -f "$tmpfile"
|
||||
echo "❌ Failed to remove mapping" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ----------------------
|
||||
# Function: clear_all_mappings
|
||||
# Remove all device mappings (reset to empty)
|
||||
# Usage: clear_all_mappings
|
||||
# ----------------------
|
||||
clear_all_mappings() {
|
||||
echo "{}" > "$SIDE_MAPPING_FILE"
|
||||
echo "✅ Cleared all mappings" >&2
|
||||
}
|
||||
41
qmk_flash_tools/test/run_all_tests.sh
Normal file
41
qmk_flash_tools/test/run_all_tests.sh
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# Quick Testing Script - Run all tests in sequence
|
||||
# =============================================================================
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
echo "╔═══════════════════════════════════════════════════════════╗"
|
||||
echo "║ QMK Flash Tools - Quick Test Suite ║"
|
||||
echo "╚═══════════════════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
|
||||
# Test 1: Side Mapping (no hardware needed)
|
||||
echo "Running side mapping tests..."
|
||||
echo ""
|
||||
bash "$SCRIPT_DIR/test_side_mapping.sh"
|
||||
echo ""
|
||||
|
||||
# Test 2: QMK Helpers (partial, no hardware needed)
|
||||
echo "Running QMK helper tests..."
|
||||
echo ""
|
||||
bash "$SCRIPT_DIR/test_qmk_helpers.sh"
|
||||
echo ""
|
||||
|
||||
# Test 3: Device Detection (requires hardware)
|
||||
echo "Running device detection tests..."
|
||||
echo ""
|
||||
read -rp "Do you have a keyboard in bootloader mode? [y/n]: " has_device
|
||||
|
||||
if [[ "$has_device" == "y" ]]; then
|
||||
bash "$SCRIPT_DIR/test_device_detection.sh"
|
||||
else
|
||||
echo "⏭️ Skipping device detection test"
|
||||
echo " Run manually when you have hardware ready:"
|
||||
echo " ./test/test_device_detection.sh"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "╔═══════════════════════════════════════════════════════════╗"
|
||||
echo "║ ✅ All tests complete! ║"
|
||||
echo "╚═══════════════════════════════════════════════════════════╝"
|
||||
111
qmk_flash_tools/test/test_device_detection.sh
Normal file
111
qmk_flash_tools/test/test_device_detection.sh
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# Test Device Detection Functions
|
||||
# =============================================================================
|
||||
# Standalone test script for device_detection.sh library
|
||||
# =============================================================================
|
||||
|
||||
# Get the directory where this script is located
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
LIB_DIR="$SCRIPT_DIR/../lib"
|
||||
|
||||
# Source the device detection library
|
||||
source "$LIB_DIR/device_detection.sh"
|
||||
|
||||
# Set configuration for testing
|
||||
export USB_MOUNT_PATHS=("/media/$USER" "/run/media/$USER" "/mnt")
|
||||
export RP2040_PATTERN="*RP2040*"
|
||||
export USB_WAIT_INTERVAL=0.5
|
||||
|
||||
echo "╔═══════════════════════════════════════════════════════╗"
|
||||
echo "║ Device Detection Library Test ║"
|
||||
echo "╚═══════════════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
|
||||
# Test 1: Check if device is currently connected
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Test 1: Looking for currently connected RP2040..."
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
# Quick check without waiting
|
||||
device_found=""
|
||||
for path in "${USB_MOUNT_PATHS[@]}"; do
|
||||
device_found=$(find "$path" -maxdepth 2 -type d -name "$RP2040_PATTERN" 2>/dev/null | head -n1)
|
||||
if [[ -n "$device_found" ]]; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ -n "$device_found" ]]; then
|
||||
echo "✅ RP2040 device found at: $device_found"
|
||||
echo ""
|
||||
|
||||
# Test 2: Get USB serial
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Test 2: Getting USB serial from host..."
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
usb_serial=$(get_usb_serial_from_host "$device_found")
|
||||
if [[ -n "$usb_serial" ]]; then
|
||||
echo "✅ USB Serial: $usb_serial"
|
||||
else
|
||||
echo "⚠️ USB Serial not available"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test 3: Get USB device path
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Test 3: Getting USB device path..."
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
usb_path=$(get_usb_device_path "$device_found")
|
||||
if [[ -n "$usb_path" ]]; then
|
||||
echo "✅ USB Path: $usb_path"
|
||||
else
|
||||
echo "⚠️ USB Path not available"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test 4: Get device identifier
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Test 4: Getting device identifier..."
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
device_id=$(get_device_identifier "$device_found")
|
||||
echo "✅ Device Identifier: $device_id"
|
||||
echo ""
|
||||
|
||||
# Test 5: Print full device info
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Test 5: Full device information..."
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
print_device_info "$device_found"
|
||||
echo ""
|
||||
|
||||
else
|
||||
echo "⚠️ No RP2040 device currently connected"
|
||||
echo ""
|
||||
echo "To test device detection:"
|
||||
echo " 1. Enter bootloader mode on your keyboard (double-tap RESET)"
|
||||
echo " 2. Run this test script again"
|
||||
echo ""
|
||||
echo "Or test the wait function (will wait for device):"
|
||||
read -rp "Wait for device? [y/n]: " wait_test
|
||||
|
||||
if [[ "$wait_test" == "y" ]]; then
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Test: Waiting for RP2040 device..."
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Enter bootloader mode on your keyboard now..."
|
||||
echo ""
|
||||
|
||||
mount_point=$(wait_for_rp2040)
|
||||
echo ""
|
||||
echo "✅ Device detected!"
|
||||
echo ""
|
||||
print_device_info "$mount_point"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "✅ Device detection tests complete"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
108
qmk_flash_tools/test/test_qmk_helpers.sh
Normal file
108
qmk_flash_tools/test/test_qmk_helpers.sh
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# Test QMK Helper Functions
|
||||
# =============================================================================
|
||||
# Standalone test script for qmk_helpers.sh library
|
||||
# =============================================================================
|
||||
|
||||
# Get the directory where this script is located
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
LIB_DIR="$SCRIPT_DIR/../lib"
|
||||
|
||||
# Source the QMK helpers library
|
||||
source "$LIB_DIR/qmk_helpers.sh"
|
||||
|
||||
echo "╔═══════════════════════════════════════════════════════╗"
|
||||
echo "║ QMK Helpers Library Test ║"
|
||||
echo "╚═══════════════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
|
||||
# Test configuration
|
||||
TEST_KEYBOARD="fingerpunch/sweeeeep"
|
||||
TEST_KEYMAP="smathev"
|
||||
|
||||
# Test 1: Check QMK installation
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Test 1: Check QMK CLI installation..."
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
if check_qmk_installed; then
|
||||
echo "✅ QMK CLI is installed"
|
||||
qmk --version
|
||||
else
|
||||
echo "❌ QMK CLI is not installed"
|
||||
exit 1
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test 2: Verify keyboard exists
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Test 2: Verify keyboard exists..."
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Checking for: $TEST_KEYBOARD"
|
||||
if verify_keyboard_exists "$TEST_KEYBOARD"; then
|
||||
echo "✅ Keyboard exists in QMK"
|
||||
else
|
||||
echo "⚠️ Keyboard not found (may need to adjust TEST_KEYBOARD variable)"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test 3: Test build (optional)
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Test 3: Build firmware (optional)..."
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "This will compile the firmware for $TEST_KEYBOARD:$TEST_KEYMAP"
|
||||
read -rp "Proceed with build test? [y/n]: " do_build
|
||||
|
||||
if [[ "$do_build" == "y" ]]; then
|
||||
echo ""
|
||||
if build_firmware "$TEST_KEYBOARD" "$TEST_KEYMAP"; then
|
||||
echo "✅ Build test passed"
|
||||
else
|
||||
echo "⚠️ Build test failed (check keyboard/keymap configuration)"
|
||||
fi
|
||||
else
|
||||
echo "⏭️ Skipped build test"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test 4: Test clean build
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Test 4: Clean build directory..."
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
read -rp "Proceed with clean test? [y/n]: " do_clean
|
||||
|
||||
if [[ "$do_clean" == "y" ]]; then
|
||||
clean_build
|
||||
echo "✅ Clean test passed"
|
||||
else
|
||||
echo "⏭️ Skipped clean test"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test 5: Function signature tests (no actual flashing)
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Test 5: Test function signatures (dry run)..."
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
echo "Testing flash_side with missing arguments:"
|
||||
flash_side "" "" "" 2>&1 | head -n1
|
||||
echo ""
|
||||
|
||||
echo "Testing flash_side with invalid side:"
|
||||
flash_side "$TEST_KEYBOARD" "$TEST_KEYMAP" "middle" 2>&1 | head -n1
|
||||
echo ""
|
||||
|
||||
echo "Testing flash_with_bootloader with missing arguments:"
|
||||
flash_with_bootloader "" "" "" 2>&1 | head -n1
|
||||
echo ""
|
||||
|
||||
echo "✅ Function signature tests passed"
|
||||
echo ""
|
||||
|
||||
# Summary
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "✅ QMK helpers tests complete"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
echo "Note: Actual flashing tests require hardware and should be"
|
||||
echo " done through the main autoflash script."
|
||||
148
qmk_flash_tools/test/test_side_mapping.sh
Normal file
148
qmk_flash_tools/test/test_side_mapping.sh
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# Test Side Mapping Functions
|
||||
# =============================================================================
|
||||
# Standalone test script for side_mapping.sh library
|
||||
# =============================================================================
|
||||
|
||||
# Get the directory where this script is located
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
LIB_DIR="$SCRIPT_DIR/../lib"
|
||||
|
||||
# Use a test mapping file
|
||||
export SIDE_MAPPING_FILE="/tmp/qmk_test_mappings.json"
|
||||
|
||||
# Source the side mapping library
|
||||
source "$LIB_DIR/side_mapping.sh"
|
||||
|
||||
echo "╔═══════════════════════════════════════════════════════╗"
|
||||
echo "║ Side Mapping Library Test ║"
|
||||
echo "╚═══════════════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
echo "Test mapping file: $SIDE_MAPPING_FILE"
|
||||
echo ""
|
||||
|
||||
# Clean up test file if it exists
|
||||
rm -f "$SIDE_MAPPING_FILE"
|
||||
|
||||
# Test 1: Initialize mapping file
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Test 1: Initialize mapping file..."
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
init_mapping_file
|
||||
if [[ -f "$SIDE_MAPPING_FILE" ]]; then
|
||||
echo "✅ Mapping file created"
|
||||
cat "$SIDE_MAPPING_FILE"
|
||||
else
|
||||
echo "❌ Failed to create mapping file"
|
||||
exit 1
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test 2: Check jq installation
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Test 2: Check jq installation..."
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
if check_jq_installed; then
|
||||
echo "✅ jq is installed"
|
||||
else
|
||||
echo "❌ jq is not installed"
|
||||
exit 1
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test 3: Save mappings
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Test 3: Save test mappings..."
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
save_side_mapping "serial:ABC123" "left"
|
||||
save_side_mapping "serial:XYZ789" "right"
|
||||
save_side_mapping "usbpath:usb1/1-3" "left"
|
||||
|
||||
echo ""
|
||||
echo "Current mappings:"
|
||||
cat "$SIDE_MAPPING_FILE"
|
||||
echo ""
|
||||
|
||||
# Test 4: Retrieve saved sides
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Test 4: Retrieve saved sides..."
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
side1=$(get_saved_side "serial:ABC123")
|
||||
echo "Device serial:ABC123 → $side1"
|
||||
if [[ "$side1" == "left" ]]; then
|
||||
echo "✅ Correct"
|
||||
else
|
||||
echo "❌ Expected 'left', got '$side1'"
|
||||
fi
|
||||
|
||||
side2=$(get_saved_side "serial:XYZ789")
|
||||
echo "Device serial:XYZ789 → $side2"
|
||||
if [[ "$side2" == "right" ]]; then
|
||||
echo "✅ Correct"
|
||||
else
|
||||
echo "❌ Expected 'right', got '$side2'"
|
||||
fi
|
||||
|
||||
side3=$(get_saved_side "serial:UNKNOWN")
|
||||
echo "Device serial:UNKNOWN → ${side3:-[not found]}"
|
||||
if [[ -z "$side3" ]]; then
|
||||
echo "✅ Correct (not found)"
|
||||
else
|
||||
echo "❌ Expected empty, got '$side3'"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test 5: List all mappings
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Test 5: List all mappings..."
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
list_all_mappings
|
||||
echo ""
|
||||
|
||||
# Test 6: Clear specific mapping
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Test 6: Clear specific mapping..."
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
clear_mapping "usbpath:usb1/1-3"
|
||||
echo ""
|
||||
echo "Mappings after clearing 'usbpath:usb1/1-3':"
|
||||
cat "$SIDE_MAPPING_FILE"
|
||||
echo ""
|
||||
|
||||
# Test 7: Interactive test (optional)
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Test 7: Interactive prompt test (optional)..."
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
read -rp "Test the interactive prompt? [y/n]: " test_prompt
|
||||
|
||||
if [[ "$test_prompt" == "y" ]]; then
|
||||
echo ""
|
||||
test_device_id="serial:TEST_DEVICE"
|
||||
detected_side=$(detect_side "$test_device_id")
|
||||
echo ""
|
||||
echo "You identified the device as: $detected_side"
|
||||
echo ""
|
||||
echo "Updated mappings:"
|
||||
cat "$SIDE_MAPPING_FILE"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test 8: Clear all mappings
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Test 8: Clear all mappings..."
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
clear_all_mappings
|
||||
echo ""
|
||||
echo "Mappings after clearing all:"
|
||||
cat "$SIDE_MAPPING_FILE"
|
||||
echo ""
|
||||
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "✅ Side mapping tests complete"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
echo "Cleaning up test file: $SIDE_MAPPING_FILE"
|
||||
rm -f "$SIDE_MAPPING_FILE"
|
||||
|
|
@ -25,7 +25,7 @@
|
|||
// Tapping and timing configuration
|
||||
#define TAPPING_TERM 200
|
||||
#define FLOW_TAP 130
|
||||
//#define PERMISSIVE_HOLD // Activate mod immediately when another key pressed _REDUNDANT due to SpeculativeHold
|
||||
#define PERMISSIVE_HOLD // Activate mod immediately when another key pressed
|
||||
#define AUTO_SHIFT_TIMEOUT 140 // at what point are you holding the key to send a SHIFTED value
|
||||
#define RETRO_SHIFT // Enable retroactive shift
|
||||
#define RETRO_TAPPING // Enable retroactive tapping
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue