Skip to the content
4 layers75 × 63 mmPico RP2350 USB Power, the printed circuit board in 3D

Pico RP2350 USB Power

aaronsilinskas/aura-prototype1

A monorepo to prototype Aura Game Engine core features.

Size
75 × 63 mm
Copper layers
4
Thickness
1.61 mm
Parts
47
Nets
56
Pads
186
Tracks, vias
343, 58
Pours
4
Routed
Yes
KiCad
KiCad 10.0 (file 20260206)
Created
4 May 2026
Updated
7 Nov 2026

Overview

Pico RP2350 USB Power is an open-source printed circuit board by aaronsilinskas, from the aaronsilinskas/aura-prototype repository on GitHub, published under the MIT licence. It is a 4-layer board of 75 × 63 mm with 47 parts on 56 nets, routed with 343 tracks and 58 vias.

It is built around the 74HCT244D, AO3406 and AO3401A, with the AO3400A among its other parts. Its parts include 22 connectors, 17 resistors, 5 transistors, 1 IC and 1 diode. It belongs with the RP2040 and RP2350 boards of this library.

Main components

4

README

README.md

Aura Prototype

The prototype firmware and game engine for Aura, a platform for interactive adventure games played in the real world with handheld devices and props that light up, make sound, and react to how you play. Aura is being built by Mindwidgets; the site explains what Aura is and how it works today.

This repository is where the software is worked out: a game engine that turns buttons, motion, infrared, and radio into games, and an animation engine for the lights, sound, and vibration players get back. It is written in Python and runs on CircuitPython microcontrollers, and most of it also runs on a desktop so it can be tested without hardware.


Status

Aura is in prototype. This is working code under active development, not a kit.

  • You can't buy or build an Aura device yet. There are no build instructions, parts list, or finished circuit board designs here. electronics/ holds only rough sketches so far. The prototype is hand-wired from off-the-shelf breakout boards.
  • You can read the code, run the tests, and watch the effects in a terminal without any hardware. See Try it without hardware.
  • The games are in packs/scenes/, one folder per scene, alongside scenes for testing hardware and browsing effects.

Issues and pull requests are not being sought yet. To hear when there is something to build or play, sign up at mindwidgets.com.


What is in here

text
effects/      Composable animation engine for lights, sound, and vibration
engine/       Event-driven game loop: rules, scenes, state, packs, and I/O abstractions
packs/        Shared effects and rules, and the scenes (games) built from them
magic/        Spell and aura game logic (elements, buffs and debuffs, cast types)
hardware/     CircuitPython drivers and the hardware abstractions above them
app/          Wires the engine to the hardware and runs a scene on a device
examples/     Entry points: a desktop terminal demo and the on-device scene runner
scripts/      Deploy and maintenance scripts that run on the desktop
electronics/  Circuit board designs (rough sketches only, so far)
docs/         Domain map, glossary, and hardware measurements

A few words used throughout the code:

  • A rule is how a game responds to player input, communications, and time.
  • An effect is what players see, hear, and feel.
  • A scene is a game: the rules and effects that combine into it.
  • A pack is a set of rules or effects that more than one scene can use. The elemental magic effects (fire, water, lightning, and the rest) are a pack.

See docs/domain.md for key types and constraints, and GLOSSARY.md for the full glossary.


Try it without hardware

You need Python 3.10 or newer and uv.

sh
uv sync            # install the development tools
uv run pytest      # run the test suite

To watch the effects engine run, start the terminal demo from the repository root (macOS or Linux, in a terminal with 24-bit color):

sh
PYTHONPATH=. uv run python examples/ansi-multi-effect-demo.py

Press a for fire, b for water, c for lightning, d to stop them all, and q to quit.


Hardware the prototype uses

There is no single finished device. The firmware builds whichever of these parts the device's config file declares, and leaves out the rest:

PartWhat the code supports
MicrocontrollerRP2350 with PSRAM, running CircuitPython 10. Pimoroni's Pico 2 boards with PSRAM work well, for example the Pico Plus 2 or Pico LiPo 2. A standard Raspberry Pi Pico 2 has no PSRAM and will not work
LightsIS31FL3741 13×9 RGB LED matrix, NeoPixel strips
SoundI2S audio output
VibrationDRV2605L haptic driver
MotionLIS3DH accelerometer, MMC5603 magnetometer
ButtonsAny GPIO pins
InfraredUp to three emitters (line, cone, and area) and one or more receivers
RadioRFM69HCW at 915 MHz
StorageSD card over SPI

Measurements taken on real hardware are in docs/hardware/recorded-metrics.md. The infrared and radio links were tested with two separate tools, infrared-analyzer and rfm69-analyzer.


Running on a device

These are working notes for a hand-wired prototype, not a build guide.

A device reads two JSON files from the root of its CIRCUITPY drive. Both are required; there are no built-in defaults.

  • aura-device.json declares the hardware: which parts are fitted and which pins they use. Each part is built only if its section is present and enabled. examples/aura-device.pico-2.json is a complete example, tested on Pimoroni Pico 2 boards. examples/aura-device.simulated.json is for the test-only Simulated device, not for flashing to a board.

  • aura-boot.json picks the scene to boot, by its folder name under packs/scenes/:

    json
    {
      "default_scene": "red_light_green_light"
    }

    If the device has an SD card, a scene saved to the card's aura-state.json takes priority over default_scene.

To put the code on a board:

  1. Install CircuitPython 10 on the board, and copy the Adafruit libraries for the parts you declared into CIRCUITPY/lib/.

  2. Download the matching mpy-cross compiler: scripts/setup_mpy_cross.sh

  3. Copy both JSON files to the CIRCUITPY drive.

  4. Deploy the scene runner and all modules:

    sh
    uv run python scripts/deploy.py examples/hardware/scene_demo.py

    The board reboots and starts the scene.

aura-device.json in brief

json
{
  "pixels": [
    {
      "type": "matrix",
      "cols": 13,
      "scope_rows": {
        "global.buff": [0, 1], "global.debuff": [1, 2], "global.main": [2, 5],
        "personal": [5, 7], "directional": [7, 8], "ambient": [8, 9]
      }
    }
  ],
  "buttons": ["GP26", "GP27"],
  "ir": {
    "rx": "GP13",
    "line": "GP10",
    "cone": "GP11",
    "area_of_effect": "GP12"
  },
  "audio": {
    "voices": 2,
    "max_volume": 0.1,
    "i2s_bit_clock": "GP7",
    "i2s_word_select": "GP8",
    "i2s_data": "GP6"
  }
}
  • pixels is a list of light outputs. type is "matrix" (IS31FL3741, at most one) or "neopixel". Each entry maps scopes, the named regions an effect can be shown in, to rows of the matrix (scope_rows) or to pixel ranges of a strip (scope_pixels).
  • buttons is a list of board pin names.
  • ir takes rx (one receiver pin, or a list of them) and at least a line emitter; cone and area_of_effect emitters are optional. Each scene chooses the infrared message format it uses: Aura's own by default, or the tag scene's format, which works with off-the-shelf infrared tag gear.
  • audio takes voices (how many sounds can play at once), max_volume (0.0 to 1.0), and the three I2S pins.

The example file also shows the accelerometer, magnetometer, haptics, radio, sdcard, i2c, spi, high_current_rail, and neopixel_current_budget sections. neopixel_current_budget is optional: when the NeoPixel strips could draw more than current_limit_ma at full white, the device dims them at boot to fit, never below min_brightness.


How the engine works

Event flow

text
(InputEvent | NetworkEvent)
        │
        ▼
   GameEngine             holds rule list; stateless — caller owns GameState
        │  engine.update(state)
        ▼
   GameRule.handle_event(event, state)
        │  rules read/write state.data, queue new events,
        │  call state.effect_controls, call state.scene_controls
        ▼
   EffectManager          routes effects to EffectOutput by scope
        │
        ▼
   EffectOutput           hardware: RGB strip, audio, haptic

Rules are stateless — all mutable game data lives in GameState.data. Rules read and write state.data, fire effects via state.effect_controls, and trigger scene transitions via state.scene_controls.

Packs

A pack is a directory containing a pack manifest and one .py file per item. scan_pack_roots (engine/pack_catalog.py) discovers every pack and scene under the pack roots in one scan; PackRegistry imports items lazily on first use.

  • Effect pack — manifest effects.json; each .py file exports BUILD: a (config) → Effect callable. Effect names use "pack.effect" format (e.g. "elements.fire").
  • Rule pack — manifest rules.json; each .py file exports RULE: a GameRule instance.

The manifest is a JSON object stating the pack's version and, optionally, declaring the pack's Settings:

json
{
  "version": "1.0",
  "settings": {
    "hold_seconds": {"type": "float", "default": 5.0, "description": "Seconds to hold."}
  }
}

A Setting's type is int, float (an int value is accepted), bool, str or list. A list is checked at the top level only; the pack's code checks what is inside it. A Setting with no default is required: every scene declaring the pack must set it. A scene sets a pack's Settings inline, in that pack's reference in scene.json (see Scenes); the scan checks each override and resolves every Setting (the default, then the scene's override, then any setting_overrides passed to the scan), raising SettingOverrideError naming the scene, pack and Setting. Rules read them through state.settings.get(<pack>, <name>) or its typed forms (get_int, get_float, get_bool, get_str), which have no default argument and no write path. Settings never enter GameState's get/set store.

A folder without a manifest is not a pack and is skipped. A manifest that is not a JSON object, or declares a Setting with an unknown type or a default of the wrong type, fails the scan with MalformedManifestError.

A pack's Settings are part of its versioned interface: adding a Setting with a default is a minor bump; adding a required Setting, or removing, renaming or retyping one, is a major bump. The scan does not enforce this.

Version format is MAJOR.MINOR. A Scene declares minimum required versions; the catalog scan checks them at boot (same-major, installed-minor ≥ required-minor).

Scenes

A Scene is a declarative bundle, described by its scene.json:

json
{
  "version": "1.0",
  "effect_packs": [{"name": "elements", "version": "1.0"}],
  "rule_packs": [
    {"name": "return_to_lobby", "version": "1.0", "settings": {"hold_seconds": 2.0}}
  ],
  "settings": {
    "window_seconds": {"type": "float", "default": 3.0, "description": "Seconds per window."}
  }
}
  • version — the scene's own MAJOR.MINOR version
  • effect_packs — ordered {"name": ..., "version": ...} objects (an optional settings object holds that pack's Setting overrides, checked at scan; effects do not receive them yet); version is the minimum compatible version, same major, checked at scan
  • rule_packs — the same object form; all rules loaded and appended in declared order
  • Optional settings — the scene's own Setting declarations, in the manifest's shape; rules read them in the scene namespace, and a Scene Config builds from them with the typed reads (state.settings.get_float("scene", "window_seconds")). A bad declaration fails the scan with MalformedSceneError
  • Optional ir_codec — the IR codec the scene runs (default "aura")

Any other key fails the scan with MalformedSceneError. A scene carries no starting data: rules seed their own runtime state through state.get/state.set.

SceneManager owns a scene stack. load(name) replaces the stack; overlay(name) pushes on top (suspending the current scene); pop() restores the previous scene. Transitions are deferred to end-of-tick — rules call state.scene_controls.load/overlay/pop() during a tick and the transition is applied after engine.update(state) returns. Every scene the manager unloads or suspends has its effects stopped on Scope.ALL automatically.


Licensing

This repo splits licensing by area:

  • Software (effects/, engine/, magic/, packs/, hardware/, app/, scripts/, …) — MIT.
  • Hardware designs (electronics/) — CERN-OHL-P-2.0, governed by electronics/LICENSE.
  • Documentation (docs/) — CC-BY-4.0.

Each area's LICENSE file is authoritative for its scope; pyproject.toml declares the software license for tooling. No per-file license headers are used.

Parts

47
QtyValueFootprintReferences
6100Resistor_SMD:R_0805_2012MetricR7, R8, R12, R13, R14, R15
510KResistor_SMD:R_0805_2012MetricR1, R3, R5, R10, R11
322Resistor_SMD:R_0805_2012MetricR2, R4, R6
3AO3406Package_TO_SOT_SMD:SOT-23Q1, Q2, Q3
2100KResistor_SMD:R_0805_2012MetricR16, R17
133KResistor_SMD:R_0805_2012MetricR9
174HCT244Package_SO:TSSOP-20_4.4x6.5mm_P0.65mmU1
1AO3401APackage_TO_SOT_SMD:SOT-23Q4
1BUTTON AConnector_PinHeader_2.54mm:PinHeader_1x02_P2.54mm_VerticalJ14
1BUTTON BConnector_PinHeader_2.54mm:PinHeader_1x02_P2.54mm_VerticalJ15
1C20917Package_TO_SOT_SMD:SOT-23Q5
1I2C_1Connector_PinHeader_2.54mm:PinHeader_1x04_P2.54mm_VerticalJ3
1I2C_2Connector_PinHeader_2.54mm:PinHeader_1x04_P2.54mm_VerticalJ4
1I2C_3Connector_PinHeader_2.54mm:PinHeader_1x04_P2.54mm_VerticalJ5
1I2S AMPConnector_PinHeader_2.54mm:PinHeader_1x05_P2.54mm_VerticalJ7
1IR RX 1Connector_PinHeader_2.54mm:PinHeader_1x03_P2.54mm_VerticalJ11
1IR RX 2Connector_PinHeader_2.54mm:PinHeader_1x03_P2.54mm_VerticalJ12
1IR RX 3Connector_PinHeader_2.54mm:PinHeader_1x03_P2.54mm_VerticalJ13
1IR TX AOEConnector_PinHeader_2.54mm:PinHeader_1x02_P2.54mm_VerticalJ10
1IR TX CONEConnector_PinHeader_2.54mm:PinHeader_1x02_P2.54mm_VerticalJ9
1IR TX LINEConnector_PinHeader_2.54mm:PinHeader_1x02_P2.54mm_VerticalJ8
1LED STRIP 1Connector_PinHeader_2.54mm:PinHeader_1x03_P2.54mm_VerticalJ16
1LED STRIP 2Connector_PinHeader_2.54mm:PinHeader_1x03_P2.54mm_VerticalJ17
1LED STRIP 3Connector_PinHeader_2.54mm:PinHeader_1x03_P2.54mm_VerticalJ18
1LED STRIP 4Connector_PinHeader_2.54mm:PinHeader_1x03_P2.54mm_VerticalJ19
1LED STRIP 5Connector_PinHeader_2.54mm:PinHeader_1x03_P2.54mm_VerticalJ20
1LED STRIP 6Connector_PinHeader_2.54mm:PinHeader_1x03_P2.54mm_VerticalJ21
1POWERConnector_PinHeader_2.54mm:PinHeader_1x02_P2.54mm_VerticalJ22
1PWRLED_SMD:LED_0805_2012MetricD2
1RFM69HCWConnector_PinHeader_2.54mm:PinHeader_1x07_P2.54mm_VerticalJ1
1RaspberryPi_PicoModule:RaspberryPi_Pico_Common_THTA1
1SD CARDConnector_PinHeader_2.54mm:PinHeader_1x06_P2.54mm_VerticalJ2
1STEMMAConnector_JST:JST_SH_BM04B-SRSS-TB_1x04-1MP_P1.00mm_VerticalJ6

More from aaronsilinskas/aura-prototype

4
  • 4 layers96 × 76 mmPico Plus 2 RP2350 THT, the printed circuit board in 3D

    Pico Plus 2 RP2350 THT

    aaronsilinskas/aura-prototype

    A monorepo to prototype Aura Game Engine core features.

    43 parts1MIT
  • 2 layersPico RP2350 Bare Bones, the printed circuit board in 3D

    Pico RP2350 Bare Bones

    aaronsilinskas/aura-prototype

    A monorepo to prototype Aura Game Engine core features.

    43 parts1MIT
  • 2 layers40 × 60 mmIR Tester, the printed circuit board in 3D

    IR Tester

    aaronsilinskas/aura-prototype

    A monorepo to prototype Aura Game Engine core features.

    35 parts1MIT
  • 2 layers80 × 50 mmLED Indicators, the printed circuit board in 3D

    LED Indicators

    aaronsilinskas/aura-prototype

    A monorepo to prototype Aura Game Engine core features.

    34 parts1MIT

Similar boards

  • 2 layers60.5 × 44 mmFanbee, the printed circuit board in 3D

    Fanbee

    lorenz/fanbee

    A Zigbee-controlled 140mm fan with electronically-controlled valve

    59 parts6MIT
  • 2 layers266.7 × 81.4 mmPCB nRF, the printed circuit board in 3D

    PCB nRF

    Ladniy/Infinity50

    50% wireless keyboard

    123 parts3MIT
  • 2 layers100 × 99.9 mmBattery capacity meter-2024, the printed circuit board in 3D

    Battery capacity meter-2024

    ikatkov/battery-capacity-meter

    Vref pin on Arduino Pro Mini breakout board is not on any pin, solder a wire to capacitor between pins 12 and 13. To the pin13 end.

    77 parts1
  • 2 layers101.5 × 64.4 mmCapibator PCB, the printed circuit board in 3D

    Capibator PCB

    Capibator/hardware

    deals with research material on hardware, schematic, pcb design and bom etc.

    108 parts
  • 2 layers99.3 × 78 mmFlowerManager, the printed circuit board in 3D

    FlowerManager

    HobbyFactory/PCB-Flower-Manager

    115 parts
  • 4 layers27.7 × 17.3 mmLdr switch, the printed circuit board in 3D

    Ldr switch

    ikatkov/ldr-switch

    Based on $0.60 Attiny13 MCU. 1kB flash, 256B ram.

    16 parts

Questions

How big is the Pico RP2350 USB Power PCB?

It measures 75 × 63 mm and has 4 copper layers, 1.61 mm thick.

What parts does the Pico RP2350 USB Power use?

47 parts, built around the 74HCT244D, AO3406, AO3401A and AO3400A. The full list of its parts is on this page, with links to each part's symbol and footprint where PCBKit's library has them.

Where can I download the Pico RP2350 USB Power KiCad files?

From the aaronsilinskas/aura-prototype repository on GitHub, at the commit this page was made from: the board is electronics/scratch/Pico RP2350 USB Power/Pico RP2350 USB Power.kicad_pcb and its schematic electronics/scratch/IR Tester/IR Tester.kicad_sch. PCBKit opens the board in the browser, with nothing to install.

Can I use the Pico RP2350 USB Power design in my own project?

Its repository publishes it under the MIT licence: you may reuse it on that licence's terms (keep the author's notice, and for a copyleft licence share your changes alike).