Project Details
Design Goals:
AimTracer was designed around a few practical requirements:
- Small and light enough to mount on a (match air-) pistol
- No permanent modification of the pistol
- Useful for both live-fire and dry-fire training
- Automatic shot detection without a trigger-mounted switch
- Immediate feedback after every shot
- Native support for iPhone and Android
- Fully local data storage without accounts or cloud services
- Open raw data instead of an inaccessible proprietary score
- Reproducible sessions that can be compared with electronic target reports
The device deliberately evaluates the shooting process rather than predicting the hit. Target alignment, sight picture and the exact aiming point remain outside the scope of an IMU-only system.
Hardware
The complete sensor unit is built around a single Seeed Studio XIAO nRF52840 Sense:
- Nordic nRF52840 microcontroller
- 64 MHz ARM Cortex-M4F processor
- 256 KB RAM
- 1 MB internal flash and 2 MB onboard flash
- Integrated Bluetooth Low Energy radio and antenna
- Integrated LSM6DS3TR-C six-axis IMU
- Three-axis gyroscope
- Three-axis accelerometer
- Integrated digital PDM microphone
- USB-C connection
- BQ25101 LiPo charger
- Board dimensions of approximately 21 × 17.8 mm
Additionally:
- Internal 3.7 V, 150 mAh LiPo battery
- Physical power switch
- Custom 3D-printed enclosure and pistol mount for
- 10mm mounting for weight bars (as seen on my Walther LP 300 XT)
- 30mm, 30.5mm and 33mm compressed air cylinder mounting for different brands like Walther, Steyr or Feinwerkbau.
- Picatinny mount, for mounting on real guns (or also replicas or co2 weapons)
No external sensor board, microphone module or additional signal wiring is required.
The current mounting profile places the XIAO with its component and sensor side facing down, PCB underside facing up and USB-C connector pointing toward the shooter. The mount must be rigid: movement between the sensor and pistol would appear as movement in the measurement.
Sensor Configuration
The firmware uses the following acquisition settings:
- IMU acquisition rate: 416 Hz
- Gyroscope range: ±500°/s
- Gyroscope scale: 0.0175°/s per LSB
- Accelerometer range: ±8 g
- Accelerometer scale: 0.000244 g per LSB
- IMU interface: 400 kHz I²C
- PDM microphone rate: 16 kHz
- Microphone mode: mono
- Microphone gain: 20
- Default live-data rate: 25 Hz
- Configurable live-data range: 5–50 Hz
At startup, the device collects 256 stationary gyroscope samples and calculates the zero-rate bias for all three axes. A new calibration can also be requested from the app. The device must remain motionless during this process.
Shot Detection
AimTracer combines acoustic and motion information instead of relying on one sensor alone.
The firmware continuously monitors:
- PDM microphone peak amplitude
- Change in accelerometer values
- Maximum angular velocity reported by the gyroscope
Three detection modes are available:
- Audio only
- Motion only
- Audio and motion within a shared time window
The combined mode is the default. It helps reject conversations and shots from neighbouring lanes while also avoiding triggers caused by ordinary pistol movement.
Current default values are:
- Microphone threshold: 7,000 raw PCM units
- Acceleration-delta threshold: 1,500 raw LSB
- Gyroscope threshold: 2,000 raw LSB
- Audio/motion coincidence window: 40 ms
- Trigger refractory period: 1,200 ms
These values are starting points. They can be changed from either app and sent to the running firmware over Bluetooth. Live fire and dry fire may require different threshold settings.
A manual test trigger is included for calibration, debugging and checking the complete capture pipeline without relying on automatic detection.
Ring Buffer and Shot Capture
Sensor acquisition runs continuously while the device is armed.
A circular buffer always retains the latest movement data. When a trigger is detected, AimTracer preserves the movement before the trigger and continues recording afterward.
Default capture window:
- 500 ms before the trigger
- Trigger sample at time zero
- 250 ms after the trigger
- 750 ms total captured time
Detection remains active at 416 Hz. For transfer and storage, every fourth IMU sample is retained, producing a 104 Hz shot trace. A standard capture therefore contains approximately 78 samples instead of roughly 312.
Every stored sample contains:
- Gyroscope X, Y and Z
- Accelerometer X, Y and Z
- Microphone peak
- Sample index
- Trigger marker
Two independent shot slots allow one shot to be transferred while the next capture is prepared. Additional triggers are counted as dropped if both slots are occupied.
Bluetooth Protocol
AimTracer uses a custom, versioned Bluetooth Low Energy GATT protocol.
Current versions:
- Firmware: 0.5
- Mobile apps: 0.5.0
- BLE protocol: 1
The custom service contains separate characteristics for:
- Device status
- Control commands
- Live sensor values
- Complete shot transfers
- Runtime configuration
- Power information
A standard BLE Battery Service is advertised alongside the custom service.
Every custom protocol packet is exactly 20 bytes long, allowing operation with the minimum commonly available ATT payload. All multibyte values use little-endian encoding.
Live sensor values use BLE notifications. Complete shot data uses confirmed ATT indications so that the firmware does not send the next packet until the phone has acknowledged the current one.
A shot transfer consists of:
- Metadata packet
- One packet for every sample
- End packet
Transfers include:
- Monotonically increasing shot ID
- Trigger timestamp
- Sample rate
- Sample count
- Trigger index
- Recorded sensor peaks
- Sequential sample indices
- CRC-32 checksum
The apps reject transfers with missing samples, incorrect IDs, unexpected packet counts or invalid checksums instead of silently storing incomplete shots.
Device Control
The apps can send commands to:
- Start a session
- Stop a session
- Arm the detector
- Disarm the detector
- Create a manual test shot
- Restart gyroscope calibration
- Read the current configuration
- Update trigger and capture settings
The device also publishes diagnostic information such as:
- Sensor-ready state
- Calibration state
- Session and armed state
- Capture state
- Transfer progress
- Firmware version
- Number of captured shots
- Number of dropped triggers
- Latest microphone peak
- Last firmware error
Battery and Charging
The attached single-cell LiPo is charged through the XIAO’s USB-C connector and onboard BQ25101 charger.
The firmware selects the conservative 50 mA charging mode. A 500 mAh battery therefore needs roughly ten hours plus the slower final charging phase when charged from empty.
Once per second, AimTracer reports:
- Estimated battery percentage
- Measured battery voltage
- USB power presence
- Active charging state
- Selected charging current
- Low-battery warning below 20>#/p###
- Critical-battery warning at or below 5>#/p###
Charging is only shown when both USB voltage and the charger’s active-low status output confirm an active charging cycle. If USB is connected but no active charge cycle is detected, the app displays “USB connected” instead.
The percentage is estimated from LiPo voltage and is intentionally treated as approximate. A firmware calibration factor is provided for comparison with a multimeter.
iPhone App
The iPhone app is written natively in Swift and SwiftUI and uses Core Bluetooth.
Requirements and features:
- iOS 17 or later
- Bluetooth device discovery
- Automatic service and characteristic setup
- Sensor-ready and calibration status
- Live movement trace
- Capture and transfer progress
- Battery percentage, voltage and charging state
- Session creation and management
- Shot-by-shot visualization
- Local history
- Session comparison
- PDF, CSV and JSON sharing
Background BLE operation is intentionally disabled in the MVP. The app should remain in the foreground during an active session.
Android App
The Android version is written in Kotlin with Jetpack Compose and the native Android BLE API.
Requirements and features:
- Android 8.0 or later
- Minimum API level 26
- Native BLE scanning and connection
- Feature parity with the iPhone version
- Live trace and device status
- Shot transfer validation
- Session history and comparison
- Battery and charging information
- PDF, CSV and raw JSON export through the system document picker
Both apps decode the same protocol and apply equivalent motion-analysis calculations.
Motion Trace
The visible path is calculated by integrating gyroscope angular velocity over the short shot window.
For the fixed enclosure orientation:
- Horizontal movement to the right:
-gz - Vertical movement upward:
gx - Rotation around the pistol’s longitudinal axis:
gy
The trace is re-centred so that the trigger sample is located at (0, 0).
This means:
- The blue pre-trigger line approaches the centre
- The centre represents the moment of trigger detection
- The orange post-trigger line shows the initial follow-through
- The graph represents relative angular movement
- It does not represent an absolute point on the target
Large movement on the graph does not automatically mean a poor hit, and a small trace does not guarantee a high score. Sight alignment and the location of the complete trace relative to the target remain unknown.
Motion Metrics
AimTracer currently calculates three values from the three-dimensional gyroscope magnitude:
RMS = sqrt(mean(gx² + gy² + gz²))
The result is expressed as angular velocity in degrees per second. Lower values indicate less angular movement within the evaluated window.
Hold Stability
The hold window begins with the captured pre-trigger data and ends approximately 167 ms before trigger detection.
It measures general pistol stability before the immediate release phase.
Trigger Behaviour
The trigger window covers approximately:
- 150 ms before the trigger
- The trigger itself
- 80 ms after the trigger
This window is intended to respond to movement associated with releasing the shot.
Follow-Through
The follow-through window covers the first 250 ms after the trigger.
It indicates whether the pistol remains stable or is moved immediately after the shot.
These metrics are engineering measurements, not validated sports-science scores. Their main purpose is to reveal patterns and compare repeated executions by the same shooter.
Ranking System
Shots are ranked separately for hold, trigger behaviour and follow-through.
For each metric:
- Shots are sorted by ascending RMS
- The lowest movement receives rank 1
- Equal measurements receive the same rank
- The rank is converted into a relative value from 0 to 100
The overall comparison index is currently calculated with equal weighting:
(hold score + trigger score + follow-through score) / 3
This index is relative to the shots inside the current session. A score of 90 in one session is not automatically equivalent to 90 in another session.
False detections can be deleted from the session so they do not distort averages and rankings.
Session Analysis
Supported training programs:
- LP20
- LP40
- LP60
- Dry-fire training
- Free training
A completed session provides:
- Mean value for every metric
- Best value for every metric
- Rank of every shot
- Overall comparison index
- Progress over the complete session
- Comparison between the first and final groups of up to ten shots
- Comparison with up
Moritz Wenzel