Research — Manticore XInput (reference repository)
Scope. Manticore XInput is a research project: it is not meant to build one specific fighting board, but to be a reference repository to start other projects from.
A request, not a legal obligation. If this code — or even just its approach and structure — is useful to you, we are glad if you credit the producer: «FULVIO MASSIMO MARIANI & ENTH 2026». It applies in every field, gaming or not: keyboards, pointing devices, industrial controls, teaching.
Why you will not find a complete board here. We have no intention of competing with Brook or GP2040 / GP2040-CE: this code is study and reference material. So we ask you not to republish or sell the project as it is, in its entirety, as a fighting board. Ideas, parts, adaptations and derivatives: welcome, with the credit above.
Software provided as is, without warranty: always test it on your own hardware.
The project
Manticore XInput is a research project on firmware and host tooling for RP2040-class microcontrollers. It studies how much a digital input path can know about itself: end-to-end latency, the elastic settling of mechanical contacts, and how an adaptive debounce policy can learn from that signal instead of picking a fixed window at design time.
- Hot path — exclusive GPIO interrupt in RAM and in-place HID report refresh, gated by a forecast of the next IN transaction: 5 µs minimum latency, 0.51 ms mean, 14 µs mean re-arm.
- Adaptive debounce — per-contact lockout learned from measured bounce energy, with floor, margin, cap, promotion and safe fallbacks, persisted across power cycles.
- Black box — counters, latency histogram, trace and per-contact bounce rings: every opening/closing with a 1 µs timestamp.
- Analysis — bursts segmented by silence, interval growth, instantaneous frequency and STFT of the rebuilt signal. Timing is measured, never amplitude.
Technical paper (full text)
Paper contents
Complete text, nothing cut: from the instrumentation to the measured results, with tables, metric definitions and a reproducibility appendix.
- Abstract
- 1. Introduction
- 2. Related work
- 3. System overview
- 4. Input hot path
- 5. Adaptive debounce
- 6. Instrumentation
- 7. Host tooling
- 8. Verification
- 9. Results
- 10. Discussion
- 11. Limitations and future work
- 12. Conclusion
- References
- Appendix A — Config-mode commands
- Appendix B — Bounce metrics
- Appendix C — Reproducibility
Manticore XInput: a low-latency, self-instrumenting XInput controller for RP2040
Technical report — version 9.9 (8 October 2026)
Author: Fulvio Massimo Mariani, supported by OpenCode + DeepSeek — Manticore XInput project Status: internal technical report / draft for external submission. Figures referenced as F1–F6; F1 is the generated architecture diagram (webapp/architecture.html), F2–F6 are produced from exported capture data (see §9.4).
Abstract
We present Manticore XInput, an open firmware for RP2040-based arcade controllers that targets two properties usually treated separately: end-to-end input latency and observability of the physical contact. The input hot path is an exclusive GPIO interrupt running from RAM that refreshes the USB HID report buffer in place, gated by a forecast of the next interrupt-IN transaction; a learned, per-pin debounce lockout adapts to each contact's measured bounce energy. Around this we built a self-instrumenting black box: microsecond-stamped edge counters, a latency histogram, an event trace, and — since version 9.7 — per-contact bounce rings that record the individual make/break instants of every mechanical bounce. A hosted web application reads the device over USB CDC in config mode, segments the bounce trains by silence, and analyses them in the time, interval and frequency domains (short-time Fourier transform of the reconstructed two-level signal, instantaneous bounce frequency, per-burst settling metrics).
On a single controller we measured a 999 Hz effective poll cadence, 5 µs minimum and 0.51 ms mean edge→report latency, 14 µs mean endpoint re-arm, and 99.95 % accuracy of the interval forecast used by the in-place refresh gate; 0 dropped trace events and 0 failed in-place self-checks over 2.34 M completed IN transfers. The bounce instrumentation shows contact behaviour that coarse counters hide: in one capture the host segmented 112 events, of which 76 were chatter bursts and 36 clean transitions; the bursts' settling windows spanned 50 µs to 480 µs, with growth factors (largest over smallest interval) from 2.1× to 68×, and dominant spectral content from 3.9 kHz to 23.4 kHz. We describe the method, the measured costs (94.5 KB of 264 KB RAM, 53 KB of 2 MB flash), the verification strategy (nine ARM-emulated test suites compiling the real sources against mocked hardware plus a host-side JavaScript test), and the limitations, including the absence of amplitude information — a GPIO can only report when a contact opens and closes, never how hard.
Keywords: USB HID, XInput, RP2040, input latency, contact bounce, adaptive debounce, embedded telemetry, time–frequency analysis.
1. Introduction
Digital inputs built on mechanical contacts are analogue devices wearing a binary mask. When a switch closes, the moving contact does not settle instantaneously: it strikes the counter-contact, rebounds, and oscillates until the elastic energy of the impact is dissipated and the contact force exceeds the restoring force of the spring. An oscilloscope on the electrical node sees a burst of make/break transitions — contact bounce — typically lasting tens to hundreds of microseconds [1], [2]. Firmware hides this with a debounce window: after an edge, further edges are ignored for some fixed time. The window is usually chosen conservatively (2–10 ms), and the choice is invisible to the user — until it costs latency, or fails on a worn contact.
Arcade/fighting-game controllers make the trade-off acute. Players press the same buttons hundreds of times per minute; the input path runs at 125 MHz but the policy is still a constant chosen by a developer. Existing open firmware such as GP2040-CE [7] exposes configuration but measures nothing about the contacts themselves.
This report describes a different approach: make the instrument part of the product. We (i) keep the hot path minimal and RAM-resident, (ii) learn the debounce window per contact from the bounce energies actually observed, and (iii) retain the individual bounce timestamps so that the same signal that drives the debounce can be analysed later — on the host, offline, in the time and frequency domains.
Contributions:
1. An XInput + CDC firmware architecture for RP2040 in which the input ISR refreshes the HID report buffer in place under a forecast of the next IN token (§4). 2. A per-pin adaptive debounce policy that converts measured bounce spans into a lockout, with promotion, caps and fallbacks, persisted across power cycles without ever blocking the input path (§5). 3. A self-instrumenting black box: aggregate counters, a 64 µs-bucket latency histogram, a 512-entry trace ring, and per-pin rings of 128 µs-stamped edges per contact (§6). 4. A host pipeline and web application that turn the edge train into quantitative contact signatures — chatter window, interval growth, instantaneous bounce frequency, STFT of the reconstructed level (§7, §9.4). 5. A verification method: emulated ARM execution of the real sources against mocked hardware, plus UF2 structural verification, in a project without a laboratory (§8).
2. Related work
Contact physics and contact reliability are classical subjects: Holm's treatment of the electrical contact [1] and the modern tribology of connectors [2] explain bounce as the elastodynamic response of the contact system. Signal-processing tools used here are standard [3], [4]. Debouncing practice in digital design is textbook material [5]; the seismological analogy for energy-style scalar summaries of a non-stationary record follows Arias [6]. On the firmware side, GP2040-CE [7] is the closest open project (configuration, SOCD, multiple protocols) but does not measure contact behaviour. TinyUSB [8] provides the USB device stack; the HID class and report semantics follow the USB HID specification [9]. The RP2040's dual-core Cortex-M0+ and its 264 KB SRAM (with the possibility of running code from RAM) are documented in [10].
3. System overview
Target: a 19-contact arcade panel (buttons, stick directions, triggers) wired active-low to GPIOs of an RP2040 (dual Cortex-M0+, 125 MHz, 264 KB SRAM, 2 MB flash). Two USB personalities are used:
- XInput mode — the normal operating mode: a vendor interface
(0xff/0x5d) with VID:PID 045e:028e, a 20-byte report delivered on an interrupt IN endpoint (32-byte packet) at the host's poll interval, plus four vendor control requests used by dashboards: 0x01 GET_CAPABILITIES, 0x02 host action, 0x03 live lock table, 0x04 black-box summary (binary, little-endian, versioned).
- Config mode — re-enumerated as CDC-ACM (
045e:0c00) after a deliberate
gesture (boot combo, 3 s L3+R3+Guide) or a host command. A line protocol over the serial port exposes settings and diagnostics (§6.3). The soft reset into config mode preserves SRAM, so the black box survives it.
Persistent state lives in the last two flash sectors: settings (magic + version + CRC32) and the learned debounce/black-box summary, written only through a parked-core-one flash_safe_execute path (§5.4). The architecture is illustrated in F1 (webapp/architecture.html, generated with the same pipeline as the rest of the documentation).
4. Input hot path
Edge capture. All 19 inputs are configured with both-edge interrupts, grouped in one exclusive IRQ at priority 0 (above USB) whose handler is placed in RAM (__not_in_flash_func). The handler reads a 1 µs timer, classifies the edge against the per-pin lockout learned in §5, and either accepts it (state change) or counts it as a bounce. It performs no division and calls no flash code; the per-contact bounce ring push is a single-producer append (§6.2).
In-place report refresh. Instead of rebuilding the HID report in the main loop, the ISR updates the buffer that the USB controller is already pointed at (i.e. the DPRAM buffer of the endpoint), which removes the "arm after the change" latency entirely for single-word changes. Multi-word changes (e.g. stick + button in the same frame) must not be seen torn by an IN transaction, so the gate estimates the time to the next completion from the observed poll cadence (an EMA and a minimum-interval forecast) and only commits in place when the remaining margin exceeds 80 µs; otherwise the change is deferred to the next re-arm, which is safe by construction. The forecast scored 10 339 hits / 5 misses over the measured session (§9.1).
Re-arm. The IN transfer callback immediately re-arms the endpoint with the freshest state, so a change lands in the next frame rather than waiting for a main-loop iteration; the measured mean completion→re-arm gap is 14 µs.
5. Adaptive debounce
Signal. For every accepted transition the firmware records the span: the time from the accepted edge to the last bounce edge observed inside the window. This is a robust proxy for the bounce energy of that event and does not require storing the whole train (which §6.2 does anyway, for analysis).
Policy. Samples are accumulated in blocks of 3 transitions (MANTICORE_BLOCK_SAMPLES); the block maximum drives the learned value (bounce_max_us). The resulting lockout is
lockout = clamp(bounce_max + margin, floor, cap)
with margin = 100 µs, floor configurable (default 250 µs, 100–4000 µs) and cap = 6000 µs. Three refinements matter in practice:
- Promotion — a pin with a long clean streak (≥24 transitions) may go below
the configured floor, but only down to 150 µs (MANTICORE_PROMOTED_FLOOR_US), and never below a floor the user chose explicitly.
- Unused pins keep a conservative 1000 µs lockout until they are actuated at
least once, so an unmeasured contact cannot produce phantom presses.
- System buttons (START/BACK/GUIDE) use a fixed 5 ms lockout and are excluded
from adaptation: they are not latency-critical and are often wired to multi-purpose gestures.
The adaptation runs on core 1; if its heartbeat stops for 200 ms, core 0 falls back to the last safe value so a stalled core cannot leave the input path unprotected.
Classification, not filtering. A release→re-press pair faster than the chatter window (default 8 ms, SET chattergap) is classified as a self-reopening press (dfire) instead of being filtered out; the counter is exposed per contact and is the operational wear metric used by the host UI. An earlier firmware version filtered these events, which caused visible lag; the measurement showed that the filter, not the path, was the problem.
Persistence. Learned values, streaks and the black-box summary are exported to a versioned record (LEARN_VERSION 2) written only during an idle window (>30 s without an accepted edge, at most one write per minute, forced when entering config mode). The ~46 ms XIP stall of an erase/program therefore never lands between two inputs. Manual per-pin overrides (SET pinlock) are persisted immediately.
6. Instrumentation
6.1 Counters and distributions
The black box (diag.c) keeps cumulative counters (edges, reports, bounces, in-place commits, failed in-place self-checks, dropped queue entries, deferrals) plus distributions: poll-interval min/avg/max, edge→report latency min/avg/max with a 16-bucket histogram of 64 µs bins, phase-in-frame statistics and re-arm gaps. Counters are exported to flash as part of the learned state, so they survive power cycles until an explicit CLR/DIAGCLR.
6.2 Bounce rings
Since 9.8 the black box holds one ring per pin — 128 entries × 32 pins of {uint32_t time_us; uint8_t type}, ~32 KB in the uninitialized-RAM section:
- every accepted edge (
E) is recorded — it marks the start of a burst; - every rejected edge (
B) is recorded with the same 1 µs resolution.
The rings are written only from the input ISR (single producer, no lock, no division — the wrap is a mask), and read in config mode with GPIO interrupts disabled. A per-pin layout was introduced because a single noisy contact could otherwise fill a shared ring and evict every other contact (observed: 1024/1024 edges from three contacts in one session).
6.3 Host protocol
GET | SET | SAVE | STATS | LOCKS | TRACE | BOUNCE | CLR | DIAGCLR | RESET | REBOOT | BOOT, newline-terminated, streamed from a RAM buffer (dumps are much larger than the CDC FIFO). BOUNCE emits
BOUNCE pins=<contacts with data> ev=<emitted event lines> rec=<recorded events>
E <time_us> <pin> accepted edge
B <time_us> <pin> rejected edge (one bounce)
...
OK
grouped per pin, oldest first inside each pin, with a total budget of 1024 event lines shared among the pins that have data — so a chatty contact cannot hide the others. The header distinguishes emitted from recorded events, which is what makes a truncated dump visible to the host.
7. Host tooling
A single-page web application (Chrome/Edge, WebSerial, no build step, no third-party code) connects to config mode and, on connection, loads everything automatically: settings, black box, bounce train, and the firmware manifest. It renders:
- the black box (latency histogram, per-pin counters, degradation table with
dfire ratios);
- the adaptive lockout table and bars;
- the bounce viewer: the newest captures per contact, a step waveform
reconstructed from the edge instants, bars for the intervals between consecutive bounce edges, and a spectrum panel (§9.4);
- history in IndexedDB with export/import;
- the firmware updater:
BOOT→ write the UF2 to theRPI-RP2volume through
the File System Access API, after checking the image against the size and sha256 pinned in manifest.json (§11).
Statistical processing happens on the host because the device has no amplitude to offer: the page rebuilds the two-level level by resampling at 1 µs, splits it into bursts by silence (default gap 5 ms, adjustable), and computes the metrics of Appendix B.
8. Verification
Without a laboratory, correctness is enforced by executing the real firmware sources on an ARM emulator (Unicorn 2.1.4) against mocked hardware: nine suites (firmware_test, dpram_test, settings_test, diag_test, config_test, ui_test, descriptors_test, lcd_test, telemetry_test) covering the input ISR, the in-place gate, persistence, the dump protocol, USB descriptors and the display; the host pipeline has its own dependency-free Node test (segmentation, FFT, STFT, windowing, degenerate inputs). A verify_uf2.py script re-reads the produced image and checks the UF2 structure, the version identity string, the reset vector and the match against the linked BIN, so a released artifact cannot silently differ from the tested tree.
The instrumentation is also what makes the firmware auditable: an external multi-agent review of 9.8 (four independent reviewers over firmware, host code, documentation and the update path) found one blocking defect (an unbounded spectrogram that could freeze the browser tab: 240 frames and 200 k samples caps were added) and several correctness issues (a 32-bit timer wrap that could merge two bursts; a latched busy flag that could silently disable every later command; an update path that would flash an image when the manifest lacked a digest), all fixed in 9.9 with regression tests.
9. Results
9.1 Input path (single controller, one session)
| Quantity | Value |
|---|---|
| Effective poll cadence | 999 Hz (mean interval 1.00 ms) |
| Edge→report latency, min | 5 µs |
| Edge→report latency, mean | 0.51 ms |
| Edge→report latency, max | 45.88 ms (one host stall, see §10) |
| Phase in frame, mean | 29 µs |
| Completion→re-arm, mean | 14 µs |
| Interval forecast | 10 339 hits / 5 misses (99.95 %) |
| Trace entries dropped | 0 |
| Failed in-place self-checks | 0 |
| Accepted edges / completed IN transfers | 21 910 / 2 341 553 |
| Rejected edges (bounces) | 734 968 (33.5 per accepted edge) |
9.2 Cost
| Resource | 9.9 |
|---|---|
| Flash image (BIN) | 53 904 B of 2 MB (2.6 %) |
.text / .rodata (XIP, flash-resident) | 43 248 / 2 652 B |
.data (RAM) | 7 716 B |
.bss (RAM, incl. 20 KB dump buffer) | 37 804 B |
| Uninitialized RAM (black box + bounce rings) | 38 696 B |
| Heap + two 4 KB stacks | 10 240 B |
| Total RAM | 94 456 B ≈ 92 KiB of 264 KiB (35 %) |
The hot path adds no measurable cost: bounce recording is an index increment and two stores per rejected edge in RAM-resident code.
9.3 Bounce case study (single device, one capture)
One BOUNCE dump from a 19-contact panel recorded 1097 edges in the rings, covering the 11 contacts actuated since power-up; the 1024-line budget emitted 843 edge lines, from which the host segmented 76 chatter bursts and 36 clean transitions. Three contacts illustrate the range (P1, K2, K3 are the labels of the panel's face button and trigger positions):
| Contact | Edges | Chatter window | Growth (max/min interval) | Dominant | Energy >20 kHz |
|---|---|---|---|---|---|
| P1 (X) | 3 | 50 µs | ×2.1 | 23.4 kHz | 68 % |
| K2 (B) | 17 | 250 µs | ×3.8 | 3.9 kHz | 45 % |
| K3 (RT) | 18 | 480 µs | ×68 | 3.9 kHz | 31 % |
The corresponding interval sequences (F2–F4) show the expected mechanical signature: intervals grow monotonically as the contact settles (K3: instantaneous bounce frequency falling from 71.4 kHz to 1.0 kHz across 0.48 ms), while P1 ends after three transitions — the same kind of event, two orders of magnitude less energy. Swapping one contact from a clicky to a linear switch changed its signature qualitatively, which is consistent with the click mechanism exciting the contact leaf at closure (§10.3).
9.4 Analysis pipeline parameters
Resampling 1 µs (1 MHz), Hann window, 256-point FFT (0.256 ms window, 64 µs hop), frequency resolution 3.91 kHz; frames capped at 240 and samples at 200 k to bound host work; reported above ~100 kHz as ISR jitter rather than contact content. Metrics per burst are defined in Appendix B; the web application can export the raw edge streams (JSON) and the per-burst metrics (CSV) so any of these numbers can be recomputed.
10. Discussion
10.1 What the adaptive policy buys
The bounce data explains why a fixed window is the wrong instrument: across three contacts of the same controller the settling window differed by an order of magnitude (50 µs vs 480 µs). A 1 ms fixed lockout would protect all three but add ~1 ms of latency to the cleanest contact; a 100 µs window would leak bounces on K3. Learning per pin and clamping to a floor keeps the responsive contacts responsive and the noisy ones safe.
10.2 What latency max means (and does not mean)
latency max = 45.88 ms is a single outlier out of 21 910 samples. The metric is defined as accepted edge → first IN transfer completed afterwards, so it necessarily includes the host's polling behaviour: a 45 ms value means the host stopped servicing the endpoint for ~45 ms (scheduling, port power management, bus contention), not that the firmware took 45 ms. The evidence is in the neighbours of that number: re-arm 14 µs, minimum latency 5 µs, mean 0.51 ms. We therefore treat the histogram as the honest view and the maximum as a host-environment indicator.
10.3 Does the click influence the bounce?
On a clicky switch, the click is produced by a separate leaf that snaps at the actuation point — mechanically coupled to the contact leaf and fired at the very moment the contact closes. The observed change when replacing a clicky switch with a linear one on the same position (different burst shape, different settling window) supports the hypothesis that the click contributes to the bounce train. A clean experiment requires a repeatable actuator (servo/weight), identical switch models in both variants, and comparison of medians over ≥5 actuations per contact; that experiment is future work.
10.4 Distinguishing mechanical bounce from threshold noise
Contacts whose digital edge derives from an analogue signal (comparator or ADC threshold) can produce electrical re-triggering near the threshold. The two phenomena separate by shape: mechanical bounce shows monotonically growing intervals and repeatable form; threshold noise shows irregular intervals without growth and stochastic occurrence. The viewer's five stored bursts per contact make that comparison possible without extra instrumentation.
11. Limitations and future work
- No amplitude. A GPIO reports when the contact opens and closes, never
how hard or how far. Amplitude — contact resistance transients, force, displacement — requires additional hardware (an ADC across a divider on the contact, or an external accelerometer/laser vibrometer). We deliberately report proxies (interval growth, spectral content) as proxies.
- Bandwidth ceiling. Edge timestamps carry ~1 µs quantization and sub-µs
ISR jitter; content above ~100 kHz is instrumentation noise, not contact behaviour. The viewer states this on every spectrum.
- Single-device data. The measured tables come from one controller and a
small number of sessions. The method (and the export path) is designed for replication across devices, switches and actuation styles, but the numbers here are a case study, not a population.
- Actuation variability. Finger-driven actuation introduces variance that
dominates the difference between contacts unless the actuator is fixed; the recommended procedure is ≥5 bursts per contact and comparison of medians.
- Update trust. The updater verifies the image against
sizeandsha256
taken from manifest.json, refuses unverified images, and validates the path, but the digest travels in the same file as the image: HTTPS and the integrity of the hosting are the only root of trust. A signed manifest is future work.
- Planned features. Button remapping with per-game profiles, optional turbo,
and an analogue (Hall/ADC) input variant are on the roadmap; the last one is the natural way to obtain the amplitude this instrument cannot see.
12. Conclusion
A microcontroller that spends its budget on the input hot path can still afford to be an instrument. Manticore XInput keeps the edge→report path at 5 µs minimum and 14 µs re-arm while recording every edge with a microsecond timestamp, learning a lockout per contact from measured bounce energy, and exporting raw bounce trains that the host turns into quantitative settling signatures. The same data that selects the debounce window also answers the engineering questions — how elastic is this contact, how does it decay, is that click audible in the signal — without an oscilloscope attached to the machine.
References
1. R. Holm, Electric Contacts: Theory and Application, 4th ed. Springer, 1967. 2. M. Braunovic, V. V. Konchits, N. K. Myshkin, Electrical Contacts: Fundamentals, Applications and Technology. CRC Press, 2007. 3. F. J. Harris, "On the use of windows for harmonic analysis with the discrete Fourier transform," Proceedings of the IEEE, vol. 66, no. 1, pp. 51–83, 1978. 4. A. V. Oppenheim, R. W. Schafer, Discrete-Time Signal Processing, 3rd ed. Pearson, 2010. 5. P. Horowitz, W. Hill, The Art of Electronics, 3rd ed. Cambridge University Press, 2015 (switch debouncing and Schmitt-trigger practice). 6. C. P. Arias, "A measure of earthquake intensity," in Seismic Design for Nuclear Power Plants. MIT Press, 1970 (intensity summaries of a non-stationary record). 7. GP2040-CE — open-source firmware for RP2040-based game controllers, https://github.com/OpenStickCommunity/GP2040-CE 8. TinyUSB — an open-source cross-platform USB stack for embedded systems, https://github.com/hathach/tinyusb 9. USB Implementers Forum, Device Class Definition for Human Interface Devices (HID), version 1.11, 2001. 10. Raspberry Pi Ltd, RP2040 Datasheet: A microcontroller by Raspberry Pi, 2021.
Citation note: the list is deliberately limited to works the project can name reliably; expand and verify before any external submission.
Appendix A — Config-mode commands
GET settings dump (debounce, socd, forget, chattergap, ...)
SET debounce <us> per-pin floor, 100..4000 (wire unit µs)
SET socd <mode> neutral | last | first | up
SET forget <s> seconds without bounces before forgetting the learned lockout
SET pinlock <gp> <us> manual override (persisted immediately, 0 = automatic)
SET chattergap <ms> malfunction chatter window (1..50)
SAVE persist settings
STATS | LOCKS | TRACE | BOUNCE streamed dumps, terminated by OK
CLR | DIAGCLR reset black box (and adaptive state for CLR)
RESET | REBOOT | BOOT restore defaults / reboot / ROM UF2 bootloader
Appendix B — Bounce metrics
For a burst with accepted edge at t0 and bounce edges t1 … tn (n rejected edges, n+1 edges in total), with intervals dk = tk − t0:
- chatter window
= max(dk)— from the accepted edge to the last bounce; - edges
= n + 1; - interval min / median / max — order statistics of
{dk}; - growth
= max interval / min interval(≥1; large = the contact started
tight and loosened, i.e. a settling oscillation);
- instantaneous frequency
= 1/(2·dk)— one make/break pair per period; - dominant / centroid / high-frequency share — from the Hann-windowed FFT of
the level resampled at 1 µs (share = energy fraction above 20 kHz);
- starts at an accepted edge — whether the burst's first event was an
accepted transition (otherwise it is a tail whose burst fell out of the ring).
Appendix C — Reproducibility
Firmware 9.9 standard UF2 sha256 58f019f7b85d11d70517526b8c110b44c043f6e312f256d38f797787de7f54fa
timing UF2 sha256 2ecc3048231a3589be3077b283b7e36fe7c4f06daecf8b73606cb4be05898969
Build cmake -S . -B build-9.9 -G Ninja -DPICO_SDK_PATH=<pico-sdk-1.5.1> \
-DPICO_BOARD=pico -DCMAKE_BUILD_TYPE=Release && ninja -C build-9.9
Tests python3 tests/run_tests.py <firmware_test|dpram_test|settings_test|diag_test|
config_test|ui_test|descriptors_test|lcd_test|telemetry_test>
node tests/bounce_js_test.mjs
Artifact python3 tests/verify_uf2.py build-9.9/manticore_xinput.uf2 build-9.9/manticore_xinput.bin
Capture play → config mode (L3+R3+Guide 3 s) → web app → Refresh bounce → Export captures
(JSON raw edges + CSV per-burst metrics)
Analysis same parameters as §9.4 (1 µs resampling, Hann, 256-pt FFT, 64 µs hop,
gap 5 ms default, 240-frame / 200 k-sample caps)
Architecture
The architecture diagram is at the bottom of this page, in a zoomable viewer (Ctrl+wheel or pinch, drag to pan, double click for 1:1 / 2x). The vector file is also downloadable: architecture.svg.
Documents and downloads
- Technical paper: italiano · English · 日本語 (full text below as well)
- Source package 9.9: manticore-xinput-9.9-src.zip (1.122931 MB, sha256
b27d9663f76d21b9f6575c4319037e9ddd6eb93c1fe3ac71e676fb1a61d1528d) - Reference web app (WebSerial, Chrome/Edge): inside the source package,
webapp/index.html(open it in Chrome/Edge)
How it is verified
Nine ARM-emulated test suites run the real sources against mocked hardware, a dependency-free Node test covers the host pipeline, and a verifier checks the released UF2 image against the linked binary and the version identity. An external multi-agent review of 9.8 produced one blocking defect and several fixes, all shipped in 9.9 with regression tests.
Schema dell'architettura
Ctrl+rotella o pinch per lo zoom, trascina per spostare, doppio click per 1:1 / 2x. Il pulsante SVG apre il file originale.