TAFY is an open-source, modular, extensible firmware for electric foam dart blasters, written in MicroPython for the Raspberry Pi Pico 2. The goal is universal firmware — one codebase that can run on flywheel pistols, AEB rifles, and everything in between, with smart accessories and deep customizability from boot sounds to pin assignments.
- Features
- Hardware Requirements
- Installation
- Configuration
- Writing a Driver
- SmartBus
- Roadmap
- Contributing
- Intended Use
- License
Modular Fire Mechanism Support
TAFY auto-discovers fire mechanism drivers at boot. Drop a new *_fire.py into the fire_mech/ folder and it becomes available immediately — no registry to update, no imports to add. Currently included drivers:
- Flywheel + Solenoid — Electric flywheels with a solenoid pusher. The primary development target.
- Flywheel + Mechanical — Electric flywheels with a mechanical pusher (e.g. a slam-fire setup).
Modular Display Support
Same auto-discovery pattern as fire mechanisms. Drop a new *_display.py into display/ and it's available. Currently included drivers:
- LCD1602 over I2C — 16x2 character LCD, shows fire mode and ammo count.
- SSD1309 OLED over I2C — 128x64 OLED display (driver in progress).
- 7-Segment over I2C — Numeric display array (driver in progress).
- Dummy — No-display fallback. Automatically used if the configured display fails to initialize.
Fire Modes
- Single shot
- Burst fire (configurable shot count)
- Full auto
- Safety
Mode is determined by dedicated selector pins with a safe hardware default — if pin state is ambiguous, TAFY falls back to SAFE automatically.
Startup Sound & Status Tones
Plays configurable tunes over a piezo buzzer or low-power speaker via PWM. Sounds include startup, safety on/off, mode change, error, and update complete. Fully customizable via config/tunes.json.
Ammo Counter Tracks remaining dart capacity and displays it in real time. Capacity is initialized from config and updated on each shot.
Safety Switch Dedicated safety pin, defaulting low (safe) on disconnect. Hardware-safe by design.
Thread-Safe Config System
All configuration lives in JSON files under config/. The Config class provides a thread-safe get/set/dump API with per-file locking. Pin assignments, fire mode behavior, display type, volume, and more are all configurable without touching the code.
SmartBus Scaffolding The I2C-based SmartBus accessory system is initialized and the manifest format is defined. Full device detection and communication are actively in development. See SmartBus below.
Onboard LED Indicator Uses the Pico 2's built-in LED to signal status — solid on when running, slow blink on fatal config error, fast blink on driver error.
OTA Update Support
update() function provides a framework for over-the-air updates over I2C, with visual and audio feedback during and after the update process.
| Component | Requirement |
|---|---|
| Microcontroller | Raspberry Pi Pico 2 (RP2350) |
| Firmware | MicroPython |
| Fire Mechanism | Flywheel + solenoid or flywheel + mechanical pusher |
| Display | LCD1602 (I2C), SSD1309 OLED (I2C), or none |
| Buzzer | Piezo buzzer or low-power speaker on a PWM-capable pin |
| Power | 3S or 4S LiPo recommended for flywheel builds |
Default pin assignments are defined in config/pin_out.json and are fully remappable.
Note: Motors must not be driven directly from Pico GPIO. A MOSFET-based motor driver board is required. Driving motors directly will destroy the Pico.
Requirements: Python 3, mpremote (pip install mpremote)
-
Flash MicroPython onto your Pico 2. Official instructions here.
-
Clone this repo:
git clone https://github.com/Batcastle/TAFY.git cd TAFY -
Edit
config/main.jsonto match your hardware (blaster type, display type, pin assignments, etc.). -
Deploy to your Pico:
python deploy.py
-
Your Pico will reboot and TAFY will start automatically.
All configuration lives in config/. Each .json file is loaded at boot and is accessible through the thread-safe Config API.
| File | Purpose |
|---|---|
main.json |
Core settings: blaster type, display type, fire mode, volume, SmartBus, etc. |
pin_out.json |
GPIO pin assignments and I2C bus map |
tunes.json |
Startup and status sound definitions |
7_seg.json |
7-segment display settings |
lcd1602_i2c.json |
LCD1602 display settings |
ssd1309_i2c.json |
SSD1309 OLED settings |
SmartBus_Manifest.json |
Known SmartBus device definitions |
Key settings in main.json:
{
"blaster_type": "flywheel_solenoid",
"display_type": "lcd1602_i2c",
"burst_mode_shots": 3,
"volume": 0.25,
"startup_sound": "lcars_boot_sequence",
"mode": "release"
}Set "mode": "debug" to enable verbose serial output and memory reporting.
TAFY is designed to be extended. Adding support for a new blaster type or display is straightforward.
- Create
fire_mech/yourname_fire.py - Subclass
fire_mech.base.FireMechanism - Set
self.FIRE_TYPEandself.HARDWARE_CONFIGto describe your hardware - Implement
fire_trigger_pulled(), and optionallyrev_trigger_pulled(),spin_up(),spin_down(),fire(), andtrigger_solenoid() - Set
"blaster_type": "yourname"inmain.json
HARDWARE_CONFIG tells the main loop what your mechanism supports:
self.HARDWARE_CONFIG = {
"rev_switch": True, # Has a separate flywheel rev trigger
"motor": True, # Has a motor
"solenoid": True, # Has a solenoid
"fire_switch": True # Has a fire trigger
}- Create
display/yourname_display.py - Define
DISPLAY_TYPE,STATE, andDISPLAY_MODEat module level - Implement
init(config, i2c_obj, locks, silent=False, split_thread=True)— return thedisplay_mainfunction ifsplit_thread=True - Implement
display_main(config, locks)— this runs in the background thread and pushes updates to the display - Set
"display_type": "yourname"inmain.json
See display/dummy_display.py for a minimal template and display/lcd1602_i2c_display.py for a full implementation.
SmartBus is TAFY's modular I2C accessory system. It allows smart devices to be hot-swapped onto the blaster and communicate with the firmware in real time.
Physical Interface (5 pins):
- 3.3V power (1A)
- Ground
- I2C SDA
- I2C SCL
- ID/Sense (analog — device type identified by resistor value)
Devices identify themselves by placing a specific resistor value on the ID/Sense line, which forms a voltage divider with an internal resistor on the mainboard. The firmware reads the resulting voltage, calculates the resistance, and looks up the device type in SmartBus_Manifest.json.
Currently Defined Devices:
| Resistor | Device | Role |
|---|---|---|
| 47kΩ | SmartSpine v1 | Clip-on smart mag adapter (Hall effect follower tracking) |
| 22kΩ | SmartMag v1 | Fully integrated smart magazine |
| 33kΩ | Barrel Chronometer v1 | Muzzle velocity measurement |
| 4.7kΩ | Power Only | Dumb powered accessories (lights, lasers, etc.) |
SmartMag / SmartSpine Smart magazines report real-time ammo level and magazine ID over I2C using Hall effect sensors to track the dart follower position. SmartSpines are clip-on adapters that bring the same capability to standard Talon-compatible magazines, requiring only a small magnet pressed into the follower.
Barrel Chronometer Measures muzzle velocity using IR break-beam sensors at a fixed known distance. Reports FPS to the display in real time. Future versions may include a dedicated onboard processor for higher precision and shot logging.
SmartBus device detection and communication are actively in development. The manifest format and physical protocol are defined; firmware support for reading and routing device data is coming in an upcoming release.
- SmartBus device detection — Resistance reading, manifest lookup, and device initialization
- SSD1309 OLED display driver — Full display driver for 128x64 I2C OLED
- Fire system reliability — Debounce tuning, trigger logic validation, and real-hardware testing on custom PCB
- AEB rifle support — Motor-driven pusher fire mechanism driver
- Flywheel pistol support — Compact flywheel configuration support
- SmartMag integration — Live ammo count from Hall effect sensor data over SmartBus
- Custom PCB hardware — Dedicated mainboard, motor driver board, power board, and control board replacing the current breadboard prototype
- USB-C charging — Onboard charging via BMS integration
- Barrel Chronometer — Hardware design and firmware integration for muzzle velocity measurement
- SmartSpine hardware — Physical design and production of the clip-on mag adapter
- Gatling blaster support — Rotating barrel coordination and high-capacity drum feeding
- Shotgun blaster support — Multi-dart simultaneous fire, shell-based capacity tracking
- CO2/high-pressure sniper support — Solenoid valve control for ultra-high-velocity builds
- Companion app — Wireless shot logging, performance trend tracking, maintenance alerts, and dart profiling
- High-precision chronometer — Sub-microsecond timing for velocities well beyond 300fps
Contributions are very welcome. TAFY is designed from the ground up to be extended by the community, and the driver system is specifically built to make adding new hardware support approachable even for contributors who are new to embedded development.
Good places to start:
- Check the Issues page for open bugs and feature requests tagged
good first issue - Write a fire mechanism or display driver for hardware you own
- Improve documentation or add example configurations
Guidelines:
- Follow the existing driver ABI when adding new fire mechanism or display drivers — see Writing a Driver above
- Keep hardware-specific logic inside drivers, not in
main.py - Configuration values belong in
config/*.json, not hardcoded in source - Test on real hardware if possible before submitting a PR
Questions, ideas, or just want to show off your TAFY build? Open an issue or start a discussion — all are welcome.
TAFY is designed for recreational use — Nerf battles, backyard skirmishes, office wars, community events. The kind of place where kids and adults alike can have a blast and go home in one piece.
That said, we're not naive about what this hardware can do. A high-performance blaster at 180fps will sting, and a dart to an unprotected eye is a serious injury. High-performance builds are explicitly part of TAFY's vision, and with that comes a real expectation of responsibility — eye protection, muzzle discipline, and knowing your audience. TAFY is also modular and flexible enough that someone could adapt it for use in a real firearm without much difficulty, and we're not going to pretend otherwise.
The line we draw is intent. Plinking, sport shooting, competitive Nerf, even recreational use in a real firearm — that's your call to make responsibly. Using TAFY to deliberately harm or kill another person without just cause is antithetical to everything this project stands for. At its core, TAFY is about making Nerf more fun and bringing people together. That's it.
TAFY is also, by design, modular and powerful. A motivated person could adapt it for use in a real firearm without much difficulty. We're not naive about that. But we want to be unambiguous about where we stand:
Using TAFY — or any derivative of it — to harm another person is antithetical to everything this project is about.
TAFY exists to make Nerf more fun. To give your blaster personality. To bring people together over a shared hobby that's as accessible to a ten year old as it is to a grown adult who just really loves foam darts. That's it. That's the whole mission.
We can't stop anyone from doing anything with open-source software, and we're not going to pretend otherwise. But if you're building something with TAFY, we ask that you carry that spirit forward. Build something fun. Build something people can enjoy safely. Build something that brings people together rather than putting them at risk.
At its core, TAFY is about making Nerf more fun and bringing people together. Period.
TAFY is free software, released under the GNU General Public License v3.0.
Copyright 2026 Thomas Castleman
You are free to use, modify, and distribute this firmware under the terms of the GPL. Any modifications or derivative works must also be released under the GPL. Hardware designs included in this repository are released under the same terms.