Skip to content

t-ogura/zmk-config-prospector

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

436 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Prospector Scanner - ZMK Status Display Device

Version 2.2.1 ZMK Compatible MIT License

Latest release: v2.2.1 Release Notes | All releases

What's New in v2.2.1 (patch from v2.2.0)

Fix: Cold-boot peripheral connection failures on multi-peripheral split keyboards. Status advertisement no longer steals radio time from ZMK split scan/connect during peripheral discovery (see issue #20).

Multi-peripheral builds (e.g. central + right half + trackball): set CONFIG_PROSPECTOR_EXPECTED_PERIPHERAL_COUNT=2 (or higher) in your keyboard config so the burst/silent cycle stays engaged until all peripherals are up. Default is 1 (typical 2-half split). Standalone keyboards are unaffected.

What's New in v2.2.0 (from v2.1.0)

New Features:

  • 3 new display layouts: Operator, Radii, Field (inspired by carrefinho)
  • NVS settings persistence (channel, brightness, layout survive reboot)
  • Version protocol: keyboard firmware version displayed on scanner Quick Actions screen
  • Boot burst for immediate scanner detection on keyboard startup
  • High-priority change detection: layer/modifier/profile changes update instantly, WPM/battery at 1Hz
  • CONFIG_PROSPECTOR_DEFAULT_LAYOUT for non-touch startup layout selection
  • 3-battery bar display (Operator layout auto-switches from arc when 3+ peripherals)

Stability & BLE:

  • Scanner freeze fix: mutex + work handler architecture with lock-free BT RX ring buffer
  • 3-mode Hybrid ADV: Piggyback / Non-connectable / Connectable Proxy
  • Burst on layer, modifier, and profile changes (5×15ms) for reliable delivery
  • Activity-based intervals: 1Hz when typing, configurable idle interval

Compatibility:

  • Keyboard firmware: ZMK main (Zephyr 4.x) and ZMK 0.3 (Zephyr 3.5) both supported
  • Scanner firmware: ZMK main required
  • GitHub Actions: updated for ZMK board variant (xiao_ble/nrf52840/zmk)

Keyboard-side west.yml:

- name: prospector-zmk-module
  remote: prospector
  revision: v2.2.1
  path: modules/prospector-zmk-module

📋 Table of Contents

  1. What is Prospector Scanner?
  2. Key Features
  3. Touch Mode vs Non-Touch Mode
  4. Quick Start
  5. Hardware & Wiring
  6. Configuration Guide
  7. Architecture Overview
  8. Protocol Specification
  9. Display Features
  10. Keyboard Integration
  11. Documentation

What is Prospector Scanner?

Prospector Scanner in action

Prospector Scanner is an independent BLE status display device for ZMK keyboards. It monitors your keyboard's status (battery, layer, modifiers, WPM, etc.) in real-time without consuming a BLE connection slot.

About Prospector

Prospector is a community hardware platform originally created by carrefinho as a universal ZMK keyboard dongle (keyboard → dongle → PC connection). This project takes the same hardware platform but uses it in a completely different way:

Original Prospector (Dongle Mode):

  • Keyboard connects to Prospector via BLE
  • Prospector connects to PC via USB or BLE
  • ⚠️ Limitation: Keyboard can only connect to dongle (loses multi-device capability)
  • ⚠️ Limitation: Requires keyboard-specific dongle shield configuration
  • ⚠️ Limitation: PC can't connect to keyboard directly

This Project (Scanner Mode):

  • Keyboard broadcasts status via BLE Advertisement (observer mode)
  • Prospector only listens - does NOT connect to keyboard
  • Advantage: Keyboard maintains full 5-device connectivity
  • Advantage: Works with ANY ZMK keyboard (no shield needed)
  • Advantage: Keyboard can connect directly to PC/tablet/phone

Why Scanner Mode?

Dongle Mode vs Scanner Mode

Key Benefit: Your keyboard stays fully functional with all devices while Scanner provides visual monitoring.

Hardware Platform

Both modes use the same hardware:

  • MCU: Seeeduino XIAO BLE (nRF52840)
  • Display: Waveshare 1.69" Round LCD with touch panel (ST7789V + CST816S)
  • 3D Case: Open-source design from original Prospector

Important: Even "non-touch mode" uses the touch-enabled LCD - we simply don't wire the 4 touch pins. Original Prospector uses the same display but doesn't utilize touch functionality.

Where to Buy

Ready-made Prospector kits are available at beekeeb.

Kit specifications:

  • Touch panel: ✅ Supported (CST816S wired)
  • Ambient light sensor: ❌ Not included
  • Battery: ❌ Not included (USB powered only)

See Touch Mode Guide for touch configuration.

Why Use It?

  • See Everything: Battery, layer, modifiers, WPM, signal strength
  • Multi-Keyboard: Monitor multiple keyboards simultaneously
  • Zero Connection Cost: Uses BLE advertisements (observer mode)
  • Professional UI: YADS-style widget layout with NerdFont icons
  • Split Keyboard Support: Shows both left/right side information

🎯 Key Features

📱 YADS-Style Professional UI

  • Multi-Widget Display: Connection status, layer indicators, modifier keys, WPM tracking, battery visualization, and signal strength
  • Split Keyboard Support: Unified display showing both left and right side information with intelligent layout
  • Color-Coded Status: 5-level battery indicators (Green/Light Green/Yellow/Orange/Red), unique pastel layer colors
  • Real-time Updates: Instant response to keyboard state changes with sub-second latency
  • WPM Tracking: Real-time Words Per Minute calculation with intelligent decay during idle periods
  • NerdFont Icons: Professional modifier key indicators (󰘴 Ctrl, 󰘵 Shift, 󰘶 Alt, GUI)

🔋 Smart Power Management (v2.0 Enhanced)

  • Activity-Based Intervals: 1Hz (1000ms) when typing, low frequency when idle for maximum battery efficiency
  • Automatic Transitions: Seamless switching between active/idle states with configurable timeouts
  • WPM-Aware Updates: Higher frequency during active typing sessions with decay algorithm
  • Scanner Battery Support: Real-time battery monitoring for scanner device with charging indicator
  • Timeout Dimming: Automatic brightness reduction to 5% after configurable inactivity (v2.0)
  • USB/Battery Profiles: Different brightness and power settings for USB vs battery operation

🎮 Universal Compatibility

  • Any ZMK Keyboard: Works with split, unibody, or any ZMK-compatible device
  • Non-Intrusive: Keyboards maintain full 5-device connectivity
  • Multi-Keyboard: Monitor multiple keyboards simultaneously with channel isolation
  • No Pairing Required: Uses BLE advertisements (observer mode) - no connection slot consumed

🎯 Touch Panel Support (v2.0 New)

  • Optional CST816S Touch: Enable with CONFIG_PROSPECTOR_TOUCH_ENABLED=y
  • 4-Direction Swipe Gestures: Navigate settings screens intuitively
  • On-Device Settings: Adjust max layers, channel filter, and more via touch
  • Thread-Safe LVGL: Freeze prevention with mutex + work handler architecture

👉 Touch Mode Guide → for complete setup and screen descriptions

🌞 Adaptive Brightness (v1.1+)

  • APDS9960 Integration: Ambient light sensor with 4-pin mode (no interrupt required)
  • Smooth Transitions: Configurable fade duration and steps (default: 800ms, 12 steps)
  • Non-linear Response: Intelligent mapping for comfortable viewing in all lighting conditions
  • Auto/Manual Toggle: Switch between sensor control and manual adjustment (touch mode)
  • Activity-Based Dimming: Automatic reduction to 5% when keyboards go idle (v2.0)

Touch Mode vs Non-Touch Mode

v2.0 supports two build configurations: Touch Mode and Non-Touch Mode.

Quick Comparison

Feature Non-Touch Mode Touch Mode
Display Waveshare 1.69" Touch LCD Same display
Wiring 6 display pins + power (touch pins not connected) +4 touch pins (TP_SDA/SCL/INT/RST)
Settings Kconfig only (rebuild to change) Interactive on-device adjustment
Gestures Not supported 4-direction swipe gestures
Firmware Size ~900KB ~920KB (+20KB)
Configuration Edit prospector_scanner.conf Edit prospector_scanner.conf + enable touch

Note: Both modes use the same Waveshare 1.69" Touch LCD hardware. Non-touch mode simply leaves the 4 touch pins unconnected (same as original Prospector).

Which Mode Should I Choose?

Choose Non-Touch Mode if:

  • ✅ You want simpler wiring (6 display pins only, no touch pins)
  • ✅ You don't need on-device settings adjustment
  • ✅ You want maximum firmware simplicity
  • ✅ You're following original Prospector hardware setup

Choose Touch Mode if:

  • ✅ You want to wire the 4 touch pins for interactive control
  • ✅ You want to adjust settings without rebuilding firmware
  • ✅ You want swipe gestures for future features
  • ✅ You're comfortable with +4 pin wiring

Touch Mode Details

👉 Complete Touch Mode Guide →

Touch mode requires:

  • Same Waveshare 1.69" Round LCD (with CST816S touch controller)
  • 4 additional connections: TP_SDA, TP_SCL, TP_INT, TP_RST
  • Configuration change: Set CONFIG_PROSPECTOR_TOUCH_ENABLED=y in prospector_scanner.conf

This guide focuses on Non-Touch Mode (standard Prospector wiring). For touch-specific setup, see the touch mode guide.


Quick Start

Prerequisites

  • Hardware: Seeeduino XIAO BLE + Waveshare 1.69" Round LCD
  • Keyboard: ZMK keyboard with status advertisement enabled (see Keyboard Integration)

Step 1: Get Firmware

Option A: GitHub Actions (Recommended)

  1. Fork this repository

  2. Enable GitHub Actions

    • In your fork, go to "Actions" tab
    • Click "I understand my workflows, enable them"
  3. Trigger Build

    • Go to "Actions" tab
    • Select "Build" workflow
    • Click "Run workflow" → "Run workflow"
    • Wait ~5-10 minutes
  4. Download Firmware

    • Click completed workflow run
    • Scroll to "Artifacts" section
    • Download:
      • prospector_scanner - Non-touch mode (this guide)
      • prospector_scanner_touch - Touch mode (see Touch Mode Guide)
    • Extract and flash the .uf2 file

Option B: Local Build

# Clone and setup
git clone https://github.com/YOUR_USERNAME/zmk-config-prospector.git
cd zmk-config-prospector
python3 -m venv .venv
source .venv/bin/activate
pip install west

# Initialize workspace
west init -l config
west update

# Build (non-touch mode)
west build -b xiao_ble/nrf52840 -s zmk/app -- \
  -DSHIELD=prospector_scanner \
  -DZMK_CONFIG="$(pwd)/config"

# Build (touch mode) - see Touch Mode Guide for details
west build -b xiao_ble/nrf52840 -s zmk/app -- \
  -DSHIELD=prospector_scanner \
  -DZMK_CONFIG="$(pwd)/config" \
  -DEXTRA_CONF_FILE="$(pwd)/config/prospector_scanner_touch.conf"

# Output: build/zephyr/zmk.uf2

Step 2: Wire Hardware

Purchased from beekeeb? Skip this step - your kit is pre-wired!

See Hardware & Wiring for detailed pinout.

Minimum connections (6 display pins + power):

LCD_DIN  → Pin 10 (MOSI)
LCD_CLK  → Pin 8  (SCK)
LCD_CS   → Pin 9  (CS)
LCD_DC   → Pin 7  (Data/Command)
LCD_RST  → Pin 3  (Reset)
LCD_BL   → Pin 6  (Backlight PWM)
VCC      → 3.3V
GND      → GND

Optional: APDS9960 sensor (4-pin, no interrupt needed)

SDA → D4 (P0.04)
SCL → D5 (P0.05)
VCC → 3.3V
GND → GND

Step 3: Flash Firmware

  1. Enter bootloader:

    • Connect XIAO BLE via USB
    • Press RESET button twice quickly (within 0.5 seconds)
    • XIAO-SENSE drive appears
  2. Flash firmware:

    • Drag .uf2 file to XIAO-SENSE drive
    • Drive disconnects automatically
    • Scanner reboots

Step 4: Configure Keyboard

4a. Add module to your keyboard's config/west.yml:

manifest:
  remotes:
    - name: zmkfirmware
      url-base: https://github.com/zmkfirmware
    - name: prospector
      url-base: https://github.com/t-ogura

  projects:
    - name: zmk
      remote: zmkfirmware
      revision: main
      import: app/west.yml

    # Add this:
    - name: prospector-zmk-module
      remote: prospector
      revision: v2.2.1
      path: modules/prospector-zmk-module

4b. Add to your keyboard's .conf file:

# Enable status advertisement
CONFIG_ZMK_STATUS_ADVERTISEMENT=y
CONFIG_ZMK_STATUS_ADV_KEYBOARD_NAME="MyKeyboard"

# Split keyboard only: enable peripheral battery fetching
CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING=y

4c. Rebuild and flash:

west update
west build -b your_board -- -DSHIELD=your_shield
# Copy .uf2 to bootloader drive

Step 5: Test

  1. Power on scanner (should show "Waiting for keyboards...")
  2. Power on keyboard
  3. Scanner should detect keyboard within a few seconds
  4. Check display shows: device name, layer, battery, etc.

Success! Your scanner is working.


Hardware & Wiring

Required Components

Component Specification Link
MCU Seeeduino XIAO BLE (nRF52840) Seeed Studio
Display Waveshare 1.69" Round LCD (ST7789V) Waveshare

Optional Components

Component Purpose Link
APDS9960 Ambient light sensor (auto-brightness) Adafruit
LiPo Battery Portable operation (400-600mAh) Generic 3.7V LiPo

Pin Mapping

Display Connections (Required)

Display Pin XIAO BLE Pin nRF52840 GPIO Function
LCD_DIN Pin 10 P1.15 SPI MOSI (data out)
LCD_CLK Pin 8 P1.13 SPI clock
LCD_CS Pin 9 P1.14 Chip select
LCD_DC Pin 7 P1.12 Data/Command select
LCD_RST Pin 3 P0.03 Display reset
LCD_BL Pin 6 P1.11 Backlight PWM
VCC 3.3V - Power (3.3V)
GND GND - Ground

APDS9960 Sensor (Optional)

Sensor Pin XIAO BLE Pin nRF52840 GPIO Function
VCC 3.3V - Power (3.3V)
GND GND - Ground
SDA D4 P0.04 I2C data
SCL D5 P0.05 I2C clock

Note: v2.0 supports 4-pin connection - no interrupt pin needed (polling mode).

Battery (Optional)

Battery Wire XIAO BLE Pad Description
+ (Red) BAT+ Positive terminal
- (Black) BAT- Ground

Wiring Diagram

Waveshare 1.69" LCD             Seeeduino XIAO BLE
┌─────────────────┐             ┌──────────────┐
│                 │             │              │
│  Display Pins   │             │  3.3V ───────┼─── VCC
│  ├─ LCD_DIN ────┼─────────────┤  Pin 10      │
│  ├─ LCD_CLK ────┼─────────────┤  Pin 8       │
│  ├─ LCD_CS ─────┼─────────────┤  Pin 9       │
│  ├─ LCD_DC ─────┼─────────────┤  Pin 7       │
│  ├─ LCD_RST ────┼─────────────┤  Pin 3       │
│  ├─ LCD_BL ─────┼─────────────┤  Pin 6       │
│  ├─ VCC ────────┼─────────────┤  3.3V        │
│  └─ GND ────────┼─────────────┤  GND         │
│                 │             │              │
└─────────────────┘             └──────────────┘

Optional: APDS9960 Sensor
┌─────────────┐
│  APDS9960   │
│  VCC ───────┼─────────────────┤  3.3V        │
│  GND ───────┼─────────────────┤  GND         │
│  SDA ───────┼─────────────────┤  D4          │
│  SCL ───────┼─────────────────┤  D5          │
└─────────────┘                 └──────────────┘

Optional: LiPo Battery
             ┌─ BAT+ (Red wire)
     Battery ┤
             └─ BAT- (Black wire)

Wiring Tips

  1. Test Display First: Wire display pins and verify basic operation before adding sensor
  2. Keep Wires Short: I2C works best with wires < 10cm
  3. Built-in Pull-ups: XIAO BLE has I2C pull-ups on D4/D5 - no external resistors needed
  4. Polarity Check: Double-check VCC/GND before powering on
  5. Clean Solder: Poor solder joints cause intermittent display issues

Configuration Guide

Configuration file: config/prospector_scanner.conf

Essential Settings

Scanner Mode (Required)

# Enable scanner mode
CONFIG_PROSPECTOR_MODE_SCANNER=y

# Multi-keyboard support (enabled by default)
CONFIG_PROSPECTOR_MULTI_KEYBOARD=y

Display & LVGL

# Display subsystem
CONFIG_ZMK_DISPLAY=y
CONFIG_DISPLAY=y
CONFIG_LVGL=y

# Required LVGL widgets
CONFIG_LV_USE_BTN=y
CONFIG_LV_USE_SLIDER=y
CONFIG_LV_USE_SWITCH=y

# Fonts (already configured in default prospector_scanner.conf)
# CONFIG_LV_FONT_MONTSERRAT_12=y
# CONFIG_LV_FONT_MONTSERRAT_16=y
# ... etc

Note: Font settings are pre-configured in prospector_scanner.conf. No manual font configuration needed.

Layer Display Configuration

# Number of layer indicators shown on screen
CONFIG_PROSPECTOR_MAX_LAYERS=7        # Range: 4-10

# Visual effect:
# - 4 layers: Wide spacing, large indicators
# - 7 layers: Medium spacing (default)
# - 10 layers: Tight spacing, maximum capacity

Match your keyboard: Set this to match your keyboard's layer count for best appearance. If your keyboard has 5 layers, set to 5 for optimal spacing.

Layer Slide Mode (v2.1 New)

# Enable dial-style animated layer display
CONFIG_PROSPECTOR_LAYER_SLIDE_DEFAULT=y

What it does: Active layer slides to center position with smooth animation. Layer changes animate based on direction (increase = slide from right, decrease = slide from left).

Over-max behavior: When layer exceeds MAX_LAYERS, displays a single large number instead of the layer list.

Touch mode: Can be toggled on-device. See Touch Mode Guide for details.

Channel Feature (v1.1.2+)

Channels allow filtering specific keyboards in multi-keyboard environments.

Keyboard Side (your keyboard's .conf)

# Channel broadcasting (add to your keyboard config)
CONFIG_PROSPECTOR_CHANNEL=0    # 0 = broadcast to all scanners (default)
                                # 1-9 = specific channel (recommended)
                                # 10-255 = also supported but not selectable in touch UI

Scanner Side (config/prospector_scanner.conf)

Touch mode: Channel can be changed dynamically on-device. See Touch Mode Guide.

Non-touch mode: Set channel via Kconfig:

# Channel filter for scanner (non-touch mode)
CONFIG_PROSPECTOR_SCANNER_CHANNEL=0   # 0 = receive all (default)
                                       # 1-9 = specific channel (recommended)

Recommended range: Use channels 0-9 for compatibility with touch mode UI.

Use case examples:

  • Home/Office separation: Home keyboards on channel 1, office on channel 2
  • Multi-user: Each user's keyboards on different channels
  • Testing: Isolate test keyboards from production display

Compatibility: Keyboard side works with v2.0.0+. Scanner side channel filter requires v2.1.0+ for touch mode dynamic switching.

Peripheral Battery Slot Mapping (v2.1 New)

For split keyboards with multiple peripherals (e.g., keyboard half + trackball), you can remap which peripheral appears in which display slot.

# Add to your keyboard's .conf file
CONFIG_ZMK_STATUS_ADV_HALF_PERIPHERAL=0   # Keyboard half slot (default: 0)
CONFIG_ZMK_STATUS_ADV_AUX1_PERIPHERAL=1   # Aux1 slot, e.g., trackball (default: 1)
CONFIG_ZMK_STATUS_ADV_AUX2_PERIPHERAL=2   # Aux2 slot, e.g., numpad (default: 2)

When to use: After settings_reset, peripherals may reconnect in different order than expected. Use these settings to fix the display order without re-pairing.

Example: If your trackball (should be Aux1) connected before keyboard half:

CONFIG_ZMK_STATUS_ADV_HALF_PERIPHERAL=1   # Keyboard half is now index 1
CONFIG_ZMK_STATUS_ADV_AUX1_PERIPHERAL=0   # Trackball is now index 0

Brightness Control

Fixed Brightness (Simple)

# Disable ambient light sensor
CONFIG_PROSPECTOR_USE_AMBIENT_LIGHT_SENSOR=n

# Set fixed brightness (0-100%)
CONFIG_PROSPECTOR_FIXED_BRIGHTNESS=85

Best for: Users without APDS9960 sensor, or who prefer manual control.

Automatic Brightness (Advanced)

# Enable ambient light sensor (requires APDS9960 hardware)
CONFIG_PROSPECTOR_USE_AMBIENT_LIGHT_SENSOR=y

# Brightness range
CONFIG_PROSPECTOR_ALS_MIN_BRIGHTNESS=20       # Minimum (dark rooms)
CONFIG_PROSPECTOR_ALS_MAX_BRIGHTNESS_USB=100  # Maximum (USB power)

# Smooth fade transitions
CONFIG_PROSPECTOR_BRIGHTNESS_FADE_DURATION_MS=800  # 800ms fade
CONFIG_PROSPECTOR_BRIGHTNESS_FADE_STEPS=12         # 12 steps

What happens: Scanner reads ambient light every few seconds and smoothly adjusts brightness (20-100%) with 800ms fade. No jarring changes.

Hardware requirement: APDS9960 sensor wired to D4/D5 (4-pin connection, no interrupt pin needed).

Timeout Brightness (v2.0 New)

# Auto-dim when no keyboard activity
CONFIG_PROSPECTOR_SCANNER_TIMEOUT_MS=480000      # 8 minutes (0=disabled)
CONFIG_PROSPECTOR_SCANNER_TIMEOUT_BRIGHTNESS=5   # Dim to 5%

What it does:

  1. If no keyboard data received for 8 minutes → dim to 5%
  2. When keyboard sends data again → restore previous brightness
  3. Works with both USB and battery power

Use case: Battery-powered scanner - extends battery life when keyboards are turned off.

Disable: Set CONFIG_PROSPECTOR_SCANNER_TIMEOUT_MS=0 to disable timeout.

Scanner Battery Support

# Enable scanner's own battery monitoring
CONFIG_PROSPECTOR_BATTERY_SUPPORT=y   # Requires LiPo connected to XIAO BLE
CONFIG_ZMK_BATTERY_REPORTING=y        # ZMK battery subsystem

What you see: Battery icon (🔋) in top-right corner with percentage and charging indicator.

Hardware requirement: LiPo battery connected to XIAO BLE's BAT+/BAT- pads.

No battery? Set CONFIG_PROSPECTOR_BATTERY_SUPPORT=n - no battery widget shown.

USB Logging (Development)

# Enable USB serial logging
CONFIG_LOG=y
CONFIG_ZMK_LOG_LEVEL_DBG=y

# Reduce BT noise
CONFIG_BT_LOG_LEVEL_WRN=y
CONFIG_LOG_DEFAULT_LEVEL=4

How to use:

  1. Enable these settings
  2. Rebuild firmware
  3. Connect via USB
  4. Open serial monitor (e.g., screen /dev/ttyACM0 115200)
  5. See debug logs for troubleshooting

Production: Set CONFIG_LOG=n to disable logging and reduce firmware size.

Complete Configuration Example

# ===== SCANNER MODE =====
CONFIG_PROSPECTOR_MODE_SCANNER=y
CONFIG_PROSPECTOR_MULTI_KEYBOARD=y

# ===== DISPLAY =====
CONFIG_ZMK_DISPLAY=y
CONFIG_DISPLAY=y
CONFIG_LVGL=y
CONFIG_PROSPECTOR_MAX_LAYERS=7

# ===== LAYER DISPLAY (v2.1) =====
# CONFIG_PROSPECTOR_LAYER_SLIDE_DEFAULT=y  # Enable slide animation mode

# ===== CHANNEL FILTER (non-touch mode only) =====
# CONFIG_PROSPECTOR_SCANNER_CHANNEL=0      # 0=all, 1-255=specific channel

# ===== BRIGHTNESS =====
# Option 1: Fixed brightness (simple)
CONFIG_PROSPECTOR_USE_AMBIENT_LIGHT_SENSOR=n
CONFIG_PROSPECTOR_FIXED_BRIGHTNESS=85

# Option 2: Auto-brightness (requires APDS9960)
# CONFIG_PROSPECTOR_USE_AMBIENT_LIGHT_SENSOR=y
# CONFIG_PROSPECTOR_ALS_MIN_BRIGHTNESS=20
# CONFIG_PROSPECTOR_ALS_MAX_BRIGHTNESS_USB=100
# CONFIG_PROSPECTOR_BRIGHTNESS_FADE_DURATION_MS=800

# ===== TIMEOUT =====
CONFIG_PROSPECTOR_SCANNER_TIMEOUT_MS=480000  # 8 min (0=disabled)
CONFIG_PROSPECTOR_SCANNER_TIMEOUT_BRIGHTNESS=5

# ===== BATTERY =====
CONFIG_PROSPECTOR_BATTERY_SUPPORT=n  # Enable if LiPo connected

# ===== LOGGING (optional) =====
# CONFIG_LOG=y
# CONFIG_ZMK_LOG_LEVEL_DBG=y

Copy this template and customize for your needs.

For keyboard-side v2.1 features (channel, peripheral mapping), see Keyboard Integration section.


🏗️ Architecture Overview

System Design

Scanner Mode Design (Independent Monitoring):

  • Keyboard → Multiple Devices (up to 5 via normal BLE connections)
  • Keyboard → Scanner (BLE Advertisement broadcast only - no connection)
  • Scanner operates independently without consuming connection slots

System Components

┌─────────────┐    BLE Adv     ┌──────────────┐
│   Keyboard  │ ──────────────→│   Scanner    │
│             │    26-byte     │   Display    │
│ (1Hz/idle)  │    Protocol    │  (USB/Battery)│
└─────────────┘                └──────────────┘
       │
       ├── Device 1 (PC)
       ├── Device 2 (Tablet)
       ├── Device 3 (Phone)
       ├── Device 4 (...)
       └── Device 5 (...)

Key Points:

  • Keyboard broadcasts status via BLE Advertisement (observer mode)
  • Scanner only listens - does NOT connect to keyboard
  • Keyboard maintains full 5-device connectivity
  • Scanner can be powered via USB or battery (optional)

Communication Flow

Keyboard                           Scanner
────────                          ────────
[Keypress detected]
    │
    ├─→ Update internal state
    │   (layer, modifiers, WPM)
    │
    ├─→ Package into 26-byte
    │   advertisement payload
    │
    └─→ Broadcast BLE Adv ─────→  [BLE Observer Mode]
        (1Hz when typing,              │
         30s when idle,                ├─→ Parse advertisement
         burst on changes)
                                       │   (battery, layer, etc.)
                                       │
                                       ├─→ Update LVGL widgets
                                       │   (battery bars, layer
                                       │    indicators, WPM, etc.)
                                       │
                                       └─→ Display to screen
                                           (YADS-style UI)

📊 Protocol Specification

BLE Advertisement Format (26 bytes)

The keyboard broadcasts its status using a custom BLE Advertisement payload. Scanner receives this in observer mode (no connection needed).

Offset Field Size Description Example
0-1 Manufacturer ID 2 bytes 0xFF 0xFF (Custom/Local use) FF FF
2-3 Service UUID 2 bytes 0xAB 0xCD (Prospector Protocol ID) AB CD
4 Version 1 byte [7:4]=module major, [3:0]=module minor 22 (v2.2)
5 Battery Level 1 byte Main battery 0-100% (Central for split) 5A (90%)
6 Active Layer 1 byte Current layer 0-15 02 (Layer 2)
7 Profile Slot 1 byte [6]=dev flag, [5:3]=patch, [2:0]=profile 0-4 01 (Profile 1, v*.*.0 release)
8 Connection Count 1 byte Number of connected BLE devices 0-5 03 (3 devices)
9 Status Flags 1 byte Caps/Charging/USB/BLE status bits 18 (USB+BLE)
10 Device Role 1 byte 0=Standalone, 1=Central, 2=Peripheral 01 (Central)
11 Device Index 1 byte Split keyboard index (0=left, 1=right) 00
12-14 Peripheral Batteries 3 bytes Left/Right/Aux battery levels 0-100% 52 00 00 (82%, none, none)
15-18 Layer Name 4 bytes ASCII layer identifier (optional) 4C30... ("L0")
19-22 Keyboard ID 4 bytes Hardware-unique ID (HWINFO) 12345678
23 Modifier Flags 1 byte L/R Ctrl,Shift,Alt,GUI states 05 (LCtrl+LAlt)
24 WPM Value 1 byte Words per minute 0-255 3C (60 WPM)
25 Channel 1 byte Channel number 0-255 (0=broadcast to all) 06 (Channel 6)

Version Encoding (Offset 4, v2.2.0+)

Version byte:       [7:4] = module major (0-15)
                    [3:0] = module minor (0-15)

Profile Slot byte:  [6]   = dev flag (0=release, 1=dev)
                    [5:3] = patch version (0-7)
                    [2:0] = BLE profile slot (0-4)

Example: v2.2.0 release, profile 1
  version = 0x22, profile_slot = 0x01

Backward compatible: v2.1.0 and earlier send version=0x01 (protocol v1).
Scanner detects major=0 and displays "< v2.2".

Status Flags (Offset 9)

Bit 7 6 5 4 3 2 1 0
    │ │ │ │ │ │ │ └─ Caps Word (1=active, 0=inactive)
    │ │ │ │ │ │ └─── Charging (1=yes, 0=no)
    │ │ │ │ │ └───── USB Connected (1=yes, 0=no)
    │ │ │ │ └─────── USB HID Ready (1=yes, 0=no)
    │ │ │ └───────── BLE Connected (1=yes, 0=no)
    │ │ └─────────── BLE Bonded (1=yes, 0=no)
    │ └───────────── Reserved (protocol version, future)
    └─────────────── Reserved (protocol version, future)

Modifier Flags (Offset 23)

Bit 7 6 5 4 3 2 1 0
    │ │ │ │ │ │ │ └─ Left Ctrl
    │ │ │ │ │ │ └─── Left Shift
    │ │ │ │ │ └───── Left Alt
    │ │ │ │ └─────── Left GUI (Win/Cmd)
    │ │ │ └───────── Right Ctrl
    │ │ └─────────── Right Shift
    │ └───────────── Right Alt
    └─────────────── Right GUI

Broadcast Intervals

The keyboard adjusts broadcast frequency based on activity to save battery:

  • Active Mode (typing detected): 1000ms interval (1 Hz, configurable)

    • Triggered by any keypress
    • Provides periodic WPM and battery updates
    • Layer/modifier/profile changes trigger immediate burst (5×15ms)
  • Idle Mode (no typing): 30000ms interval (configurable)

    • Activated after 5 seconds of no keypresses (configurable)
    • Battery-friendly for long idle periods
    • Layer changes still trigger burst for immediate response

🎨 Display Features

Main Screen

Main Screen Display

No. Element Description
Keyboard Name Keyboard name configured via CONFIG_ZMK_STATUS_ADV_KEYBOARD_NAME
WPM Words Per Minute - typing speed (5 characters = 1 word)
Connection Profile BLE connection status. Colors: White = Waiting, Blue = Communicating, Green = Connected. Number (0-4): Active BLE profile
Layer Current active layer index with pastel color coding
Modifier Keys Active modifier keys displayed when held (Ctrl, Shift, Alt, Cmd/Win)
Battery Level Battery bars with percentage. Split keyboards: L = Peripheral, R = Central
Communication Status RX signal strength (dBm) and reception frequency (Hz)

Note: Scanner's own battery (if LiPo connected) appears at top-right corner.

Display Brightness

Auto-Brightness (APDS9960 Sensor)

  • Hardware: APDS9960 ambient light sensor (4-pin mode, no interrupt needed)
  • Behavior: Smooth fade transitions (800ms, 12 steps)
  • Range: 20-100% based on ambient light

Timeout Dimming (v2.0)

  • Automatic: Dims to 5% when no keyboard activity detected
  • Configurable: CONFIG_PROSPECTOR_SCANNER_TIMEOUT_MS (default: 8 minutes, 0=disabled)
  • Wake on Activity: Returns to normal brightness when keyboard sends data

Keyboard Integration

Your ZMK keyboard needs to broadcast status via BLE Advertisement.

Step 1: Add Module to Keyboard

Edit your keyboard's config/west.yml:

manifest:
  remotes:
    - name: zmkfirmware
      url-base: https://github.com/zmkfirmware
    - name: prospector
      url-base: https://github.com/t-ogura

  projects:
    - name: zmk
      remote: zmkfirmware
      revision: main
      import: app/west.yml

    # Add this:
    - name: prospector-zmk-module
      remote: prospector
      revision: v2.2.1
      path: modules/prospector-zmk-module

ZMK version compatibility:

  • v2.2.0: Keyboard-side supports both Zephyr 4.x and 3.x. Scanner requires Zephyr 4.x.
  • v2.0.0 / v2.1.0: Zephyr 3.x only

Step 2: Enable Status Advertisement

Minimal Configuration (Quick Start)

Add to your keyboard's .conf file:

# Required: Enable status advertisement
CONFIG_ZMK_STATUS_ADVERTISEMENT=y
CONFIG_ZMK_STATUS_ADV_KEYBOARD_NAME="MyKeyboard"

That's it! Your keyboard will broadcast status at default intervals.

Full Configuration (All Options)

# ===== REQUIRED =====
CONFIG_ZMK_STATUS_ADVERTISEMENT=y
CONFIG_ZMK_STATUS_ADV_KEYBOARD_NAME="MyKeyboard"  # Shown on scanner (max 8 chars)

# ===== POWER OPTIMIZATION (all have sensible defaults) =====
# CONFIG_ZMK_STATUS_ADV_ACTIVITY_BASED=y          # Default: y (enabled)
# CONFIG_ZMK_STATUS_ADV_ACTIVE_INTERVAL_MS=1000   # Default: 200 (recommended: 1000 = 1Hz)
# CONFIG_ZMK_STATUS_ADV_IDLE_INTERVAL_MS=30000    # Default: 30000 (30s when idle)
# CONFIG_ZMK_STATUS_ADV_ACTIVITY_TIMEOUT_MS=5000  # Default: 5000 (5s before idle)

# ===== SPLIT KEYBOARD =====
CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING=y  # Fetch peripheral battery
CONFIG_ZMK_STATUS_ADV_CENTRAL_SIDE="RIGHT"        # Which side is central (LEFT/RIGHT)

# ===== PERIPHERAL BATTERY MAPPING (v2.1, for multi-peripheral setups) =====
# Use when connection order doesn't match physical layout (e.g., after settings_reset)
# CONFIG_ZMK_STATUS_ADV_HALF_PERIPHERAL=0         # Keyboard half slot (default: 0)
# CONFIG_ZMK_STATUS_ADV_AUX1_PERIPHERAL=1         # Aux1 slot - e.g., trackball (default: 1)
# CONFIG_ZMK_STATUS_ADV_AUX2_PERIPHERAL=2         # Aux2 slot - e.g., numpad (default: 2)

# ===== WPM TRACKING =====
CONFIG_ZMK_STATUS_ADV_WPM_WINDOW_SECONDS=30       # WPM calculation window (5-120s)

# ===== CHANNEL (v1.1.2+, optional) =====
# For multi-keyboard environments - filter by channel on scanner side
# CONFIG_PROSPECTOR_CHANNEL=0                     # 0=broadcast to all (default)
# CONFIG_PROSPECTOR_CHANNEL=1                     # 1-9=recommended (touch UI compatible)

Step 3: Rebuild Keyboard Firmware

# In your keyboard config directory
west update
west build -b your_board -- -DSHIELD=your_shield

# Flash to keyboard
# (Copy .uf2 to bootloader drive)

Step 4: Verify

  1. Power on keyboard
  2. Power on scanner
  3. Scanner should detect keyboard within 1-2 seconds
  4. Device name should match CONFIG_ZMK_STATUS_ADV_KEYBOARD_NAME

Documentation

Guides

  • Touch Mode Guide - Touch panel setup, screen navigation, UI element descriptions

Release History

  • v2.2.0 (2026-04): New layouts, BLE ADV rebuild, version protocol, stability improvements
  • v2.1.0 (2026-01): Zephyr 4.x, improved responsiveness, layer slide mode
  • v2.0.0 (2025-11): Touch support, USB display fix, thread safety
  • v1.1.x (2025-08): Ambient light sensor, power optimization
  • v1.0.0 (2025-08): Initial release with YADS-style UI

GitHub Resources

  • Issues - Bug reports and feature requests
  • Actions - Automated firmware builds
  • Releases - Pre-built firmware downloads

Community & Related Projects


Troubleshooting

Scanner Shows "Waiting for keyboards..."

Problem: Scanner not detecting keyboard.

Solutions:

  1. Check keyboard has CONFIG_ZMK_STATUS_ADVERTISEMENT=y enabled
  2. Verify keyboard firmware rebuilt and flashed after adding module
  3. Check keyboard is powered on and BLE is active
  4. Try power cycling both devices

Build Error: 'lv_font_montserrat_XX' undeclared

Problem: LVGL font not enabled.

Solution: Use the default prospector_scanner.conf which includes all required font settings. If you're using a custom config, copy the font settings from the default file.

Display Shows Nothing / Blank Screen

Problem: Display not initializing.

Solutions:

  1. Check wiring - especially VCC/GND polarity
  2. Verify backlight pin (LCD_BL → Pin 6)
  3. Test with settings reset firmware
  4. Check XIAO BLE has power (LED indicator)
  5. Verify display module (try with non-touch firmware if you have touch display)

Connection Shows "BLE" When USB Connected

Problem: USB connection not detected (should show "> USB").

This is a known issue in v1.x - Fixed in v2.0.

Solution: Upgrade to v2.0 firmware (both scanner and keyboard).

APDS9960 Sensor Not Working

Problem: Brightness doesn't change with room lighting.

Solutions:

  1. Check 4-pin wiring: VCC/GND/SDA (D4)/SCL (D5)
  2. Verify CONFIG_PROSPECTOR_USE_AMBIENT_LIGHT_SENSOR=y enabled
  3. Check sensor has clear view (not blocked by case)
  4. Try increasing CONFIG_PROSPECTOR_ALS_MIN_BRIGHTNESS (sensor might be too dim)

Note: v2.0 does NOT require interrupt pin - 4-pin connection works.

Scanner Freezes During Use

Problem: Display stops updating, becomes unresponsive.

Solution: Upgrade to v2.2.0 or later firmware. This version uses mutex + work handler architecture with data processing separated from display rendering, preventing LVGL thread-safety issues.

Battery Widget Not Showing

Problem: No battery icon in top-right corner.

Solutions:

  1. Check LiPo battery connected to BAT+/BAT- pads
  2. Verify CONFIG_PROSPECTOR_BATTERY_SUPPORT=y enabled
  3. Rebuild firmware after enabling battery support

No battery? Set CONFIG_PROSPECTOR_BATTERY_SUPPORT=n - this is expected behavior.


Advanced Topics

Building Custom Keyboard List

Scanner can monitor multiple keyboards simultaneously. Display automatically shows keyboards that broadcast status. No pairing needed.

WPM Calculation Windows

Adjust WPM responsiveness:

# Ultra-responsive (10s window, 6x multiplier)
# CONFIG_ZMK_STATUS_ADV_WPM_WINDOW_SECONDS=10

# Balanced (30s window, 2x multiplier) - DEFAULT
# CONFIG_ZMK_STATUS_ADV_WPM_WINDOW_SECONDS=30

# Stable (60s window, 1x multiplier)
# CONFIG_ZMK_STATUS_ADV_WPM_WINDOW_SECONDS=60

Shorter window = more responsive but jumpier. Longer window = more stable but slower to reflect changes.

Split Keyboard Central Side

For split keyboards, specify which side is central (has BLE profiles):

# In keyboard's .conf file
CONFIG_ZMK_STATUS_ADV_CENTRAL_SIDE="LEFT"  # or "RIGHT" (default)

This tells scanner which side to treat as "main" for connection status display.

Debug Widget (Development)

Enable debug overlay for development:

CONFIG_PROSPECTOR_DEBUG_WIDGET=y

Shows technical information overlaid on screen. Disable for production (=n).


Roadmap

Future development may include Periodic Advertising (v2 protocol) for improved power efficiency. See CLAUDE.md for technical design notes.


License & Attribution

License

This project is licensed under the MIT License. See LICENSE file for details.

Third-Party Components

ZMK Firmware

YADS (Yet Another Dongle Screen)

NerdFonts

Original Prospector

This project builds upon the Prospector hardware platform created by carrefinho:

  • Original Project: prospector by carrefinho
  • Original Firmware: prospector-zmk-module
  • Hardware Design: Seeeduino XIAO BLE + Waveshare 1.69" Round LCD with touch panel
  • 3D Case Design: Open-source STL files for 3D printing
  • License: MIT License

Difference: Original Prospector uses the hardware as a dongle (keyboard connects to it), while this project uses it as an independent status monitor (keyboard stays independent). Both are valid uses of the same excellent hardware platform.


Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Make your changes
  4. Test thoroughly
  5. Commit (git commit -m 'Add amazing feature')
  6. Push to branch (git push origin feature/amazing-feature)
  7. Open a Pull Request

For major changes, please open an issue first to discuss what you would like to change.


Questions? Open an issue or join ZMK Discord.

Prospector Scanner v2.2.1 - ZMK Status Display Device

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors