06

YOSHIMITSU — the Complete Solution: every cheatcode, one scroll

A cheatcode fable with only winners. One relaxed sketch, six stances, one permanent harness: the CRSF→PWM parry (KINCHO), the Zephyrus gyro levitation (MANJI_DRAGONFLY), the pocket flasher (FLEA · MEDITATION), the no-sword bench (NSS), the mirror that never looks back (BACK_TURNED) — plus YOSHI, the always-on aurora, and JIGUANG (極光), the meditating administrator who tells the legend. Wire it once, flash it once; after that the library updates and the throne commands.

One board, six stances

YOSHIMITSU is the Hermetic Shinobi with six stances — each stance a different victory, all wearing the same soul. One relaxed sketch builds for both boards; the stances switch by USB command, a GPIO0 button (ESP32-S3), or RESET-taps (RP2040-Tiny):

Stance Meaning
KINCHO CRSF→PWM converter — the parry (CRC discards the false). No channel names, no gyro.
MANJI_DRAGONFLY Converter + Zephyrus gyro link (optional MPU6050) — the levitation.
FLEA Power-cycle jig — the lift that drops the RX into its bootloader.
MEDITATION Pocket flasher — the sponge-head, saving energy, ready to be flashed.
NSS No-Sword bench — direct servo over USB, no RF.
BACK_TURNED The deceptive idle — a living UART mirror. Never looks back.

One sketch, two targets: sketches/yoshimitsu/yoshimitsu.ino compiles for the Waveshare RP2040-Tiny (lightest, no onboard button) and any native-USB ESP32-S3, with the standard arduino-pico / arduino-esp32 cores. In the Arduino IDE the same sketch lives under File → Examples → Yoshimitsu → Yoshimitsu_Default.

YOSHIMITSU — the combined firmware

YOSHIMITSU is the recommended firmware. It boots in KINCHO (the converter) and moves between stances. The USB console is the canonical switch on both boards — KINCHOMANJIFLEAMEDITATIONNSSBACKPOSE <n>. On the ESP32-S3 the BOOT button (GPIO0) is a second surface:

Context Gesture Action
any stance double-tap cycle KINCHO → MANJI → NSS → BACK → KINCHO →
any stance long-press 2 s enter MEDITATION
MEDITATION / FLEA single tap restart the receiver (run the new firmware)
MEDITATION / FLEA double-tap FLEA — drop the receiver into its bootloader

The wiring is one permanent harness — the same wires serve every stance, so you never re-wire between flying and flashing. In MEDITATION the USB→UART path is a pure transparent bridge (no line parsing), so esptool's binary SLIP traffic passes through untouched. Full source: sketches/yoshimitsu/yoshimitsu.ino (the relaxed sketch), src/Yoshimitsu.h (the core) and src/Yoshimitsu_Loadout.h (the starting loadout).

The library — update without re-touching your sketch

YOSHIMITSU is a library with a thin sketch on top — the whole Shinobi lives in the library, the sketch only holds your configuration. Install the folder once as an Arduino library; File → Examples → Yoshimitsu → Yoshimitsu_Default hands you a ready, relaxed sketch with pins, gates and the few flags that belong in code. The core lives in src/Yoshimitsu.h, every default in src/Yoshimitsu_Loadout.h (the starting loadout, *_DEFAULT values). To update: git pull the library — or drop in a new release — and your sketch stays untouched. A firmware update is never a re-wire and never a re-configure.

KINCHO — CRSF→PWM converter

KINCHO — the converter stance — reads CRSF at 420 000 baud on the UART, drives servos with a 988–2012 µs window mapped from the 172–1811 channel range, and centres them on a 500 ms failsafe. The parry is the CRC check: a corrupt frame is discarded before it ever reaches a servo. The converter is channel-agnostic — servos are SERVO_PIN_1..8 and the CHANNEL_TO_SERVO table decides which CRSF channel feeds each; channel meaning belongs to the mixer/kernel, never to the board.

For a gyro-stabilised ornithopter, use MANJI_DRAGONFLY — the same converter plus the Zephyrus link: an optional MPU6050 on GYRO_SDA/GYRO_SCL feeds a yaw-rate correction into the crest servo. Without a gyro wired, MANJI_DRAGONFLY degrades gracefully into KINCHO. A commercial CRSF→PWM converter board has no gyro path at all — EP2-class receivers carry no IMU, so Zephyrus lives in YOSHIMITSU, not in the receiver.

If your receiver already speaks PWM (a 5 ch or 7 ch board), you do not need the converter stances at all — MEDITATION below is then your only reason to own the S3.

KINCHOI — Pocket flasher for EP2-class receivers

The problem: EP2-class receivers (HappyModel EP2, EP1, EPW6, …) expose CRSF on castellated pads and carry a tiny BOOT pad. Using an FTDI adapter means holding a probe against two 1 mm pads while power-cycling the receiver — awkward, and you re-solder or re-clip every time the firmware needs an update.

The solution: let the ESP32-S3 be the flasher. One GPIO holds the receiver's BOOT pad low; a small P-MOSFET switches the receiver's 3.3 V rail under software control; the USB port becomes the serial port. Press one button on the S3 and it performs the exact power-cycle dance that drops an ESP8285 into its ROM bootloader — then esptool writes straight through the bridge. No FTDI, no probes, no repeated soldering: solder the receiver's pads once to a short pigtail and reuse it forever.

Parts list:

Part Typical part number Cost
ESP32-S3 micro board (native USB) Waveshare ESP32-S3-Tiny / -Micro / -Nano ~6 USD
P-channel logic-level MOSFET AO3401 (SOT-23) on a small breakout ~1 USD
Resistor 10 kΩ (gate pull-up) <0.10 USD
Dupont / pigtail wires + heat-shrink ~1 USD
ELRS receiver, pads pigtailed once EP2 / EP1 / EPW6 (SX1280 + ESP8285) from ~12 USD

The bridge works with every ESP8285/SX1280 ELRS receiver that exposes TX, RX, 3V3, GND and a BOOT pad — the overwhelming majority of the *concrete boards* in the hardware table . It also flashes the S3 itself, of course: BOOT button + USB is all that needs there.

Flash-jig wiring

Wire the jig with the S3 unpowered. The MOSFET switches the receiver's 3.3 V rail: source → S3 3V3, drain → EP2 3V3, gate → S3 GPIO10 with a 10 kΩ pull-up to 3V3 (gate high = MOSFET off). GPIO9 drives the BOOT pad.

EP2 / RX pad ESP32-S3 pin Function
BOOT (RX GPIO0) GPIO9 Boot-hold line (low = enter bootloader)
3V3 MOSFET drain Switched rail (source → S3 3V3, gate → GPIO10)
TX (CRSF out) GPIO18 (U1RX) RX → TX (cross)
RX (CRSF in) GPIO17 (U1TX) TX → RX (cross)
GND GND Common ground (mandatory)
S3 3V3   ──► S ── P-MOSFET (AO3401) ── D ──► EP2 3V3
S3 GPIO10 ──► gate         (10kΩ pull-up to 3V3; LOW = ON)
S3 GPIO9  ──► EP2 BOOT pad (asserted LOW during power-up)
S3 GPIO18 ◄── EP2 TX       (U1 RX)
S3 GPIO17 ──► EP2 RX       (U1 TX)
S3 GND     ──► EP2 GND

Flash mode

MEDITATION boots into transparent passthrough (USB ↔ UART at 115 200 baud). To enter flash mode, double-tap the S3's BOOT button (or type FLEA over the USB serial). The sketch then:

  1. pulls GPIO9 low (BOOT pad asserted),
  2. switches the MOSFET off for 120 ms (receiver power off),
  3. switches it back on and waits 900 ms (receiver powers up inside its bootloader),
  4. releases GPIO9 — the RX now sits in the ROM bootloader, waiting for esptool,
  5. resumes USB↔UART passthrough so esptool can talk to it.

Because the bridge cannot drive the classic DTR/RTS auto-reset lines, always flash with --before no_reset — the power-cycle dance above is the reset. After flashing, a single BOOT tap power-cycles the receiver again so it boots the new firmware; a double-tap would merely re-enter flash mode.

Flashing PteronautOS through the S3

Build the firmware (or fetch a release), then run esptool against the S3's USB serial port. On macOS the port shows up as /dev/cu.usbmodem*:

# 1. Build PteronautOS (from the repository root)
pio run -e PteronautOS_ESP8285_2400_RX

# 2. Drop the RX into flash mode (type FLEA, or double-tap BOOT), then:
python3 -m esptool --chip esp8285 \
  --port /dev/cu.usbmodemXXXX --baud 115200 \
  --before no_reset write_flash \
  --flash_mode dout --flash_size 1MB --flash_freq 40m \
  0x0 .pio/build/PteronautOS_ESP8285_2400_RX/firmware.bin

# 3. Restart the RX once more (single BOOT tap) to run the new firmware.

The flash settings (dout, 1 MB, 40 MHz) match the stock ESP8285 ELRS target — never use qio or a larger size. After flashing, the very same USB port shows the receiver's boot log: open it at 115 200 baud and watch for the PteronautOS banner and, once a transmitter binds, the CRSF link-up messages. The bridge doubles as your field console for live telemetry.

Verify & maiden flight

Once flashed, the same receiver flies — no FTDI left behind.

The RP2040-Tiny pocket build

Prefer the Waveshare RP2040-Tiny (~5 USD) — or its twin, the RP2040-Zero, which shares the exact same pinout. All six stances run on the RP2040 with no readable button at all: the adapter's BOOT/RESET taps only drive BOOTSEL/RUN, so YOSHIMITSU-RP2040 is driven over the USB serial port, plus a buttonless RESET-tap counter.

To flash the RP2040-Tiny itself, hold BOOT while power-cycling (it mounts as a mass-storage drive), drop the .uf2 on it, then press RESET once — the board reboots into KINCHO running the new firmware. Configure once, flash once, never touch again.

RP2040-Tiny pin map

Function RP2040-Tiny pin Note
CRSF RX ← receiver TX GPIO1 (UART0 RX) 420 000 baud
CRSF TX (unused, wired for completeness) GPIO0 (UART0 TX) Cross: TX → RX
Bridge RX ← receiver TX GPIO9 (UART1 RX) 115 200 baud
Bridge TX → receiver RX GPIO8 (UART1 TX) Cross: TX → RX

Servo 1 (SERVO_PIN_1)

GPIO2

PWM — channel via CHANNEL_TO_SERVO

Servo 2 (SERVO_PIN_2)

GPIO3 PWM

Servo 3 (SERVO_PIN_3)

GPIO4 PWM
Gyro SDA (MPU6050, optional) GPIO10 MANJI_DRAGONFLY
Gyro SCL (MPU6050, optional) GPIO11 MANJI_DRAGONFLY
RX BOOT (receiver GPIO0, active low) GPIO5 Low = enter bootloader
RX PWR (P-MOSFET gate, low = powered) GPIO6 Switched 3.3 V rail
Status LED GPIO16 WS2812B

The complete pin map is an editable block at the top of sketches/yoshimitsu/yoshimitsu.ino — pin ranges and collisions are validated by a boot POST, and invalid UART numbers refuse to build rather than misbehaving in the air.

YOSHI — the always-on stance aurora

YOSHI ("the aurora") is the cheatcode that is always on. YOSHI_RGB turns the single onboard WS2812B into a living mood-ring: every stance breathes its own colour and rhythm, so you can read the ronin's mind across the room without a single serial byte. Colour and blink pattern tell the story — no extra command is needed; the stance is the cheatcode, the light is its echo.

Stance Aurora (colour → rhythm)
KINCHO slow green breathing + a crisp double-parry flash (the CRC strike)
MANJI_DRAGONFLY a colour wheel spinning like the gyro rotors (the levitation)
FLEA frantic rising amber strobe (the lift)
MEDITATION near-dark violet + one slow heartbeat — ready to be flashed
NSS sharp triple-tap flash in blue (blade sheathed, tempo up)
BACK_TURNED near black + one unpredictable crimson glint (the mirror — never looks back)

RGB lives on the RP2040-Tiny (RGB_LED_PIN=16) behind YOSHI_RGB; the ESP32-S3 keeps its sober status LED. Every colour is pure integer math — no float, no malloc, no delay() — and pumpRgb() is throttled to ~50 fps so the byte-exact flasher bridge is never disturbed. The hermetic rule holds: the pose switches, the light follows.

JIGUANG — the storyteller · commentator · administrator

Three names, one soul — the cheatcode triangle of the complete solution: YOSHIMITSU is the whole Hermetic Shinobi firmware; YOSHI is the RGB cheatcode (the always-on stance aurora); and JIGUANG (極光, "the aurora") is the meditating administrator — the unmoved mover who sits cross-legged on the MEDITATION throne. Yoshimitsu's voice is functional and terse; JIGUANG's is the myth. 極光 is the console prompt, not a decoration: to act as administrator you type 極光 and then the command — otherwise the throne stays out of reach. The prompt is a #define (JIGUANG_PROMPT), so your sketch can rename it; the whole storyteller hides behind the compile gate JIGUANG (1 = omnipresent, 0 = not a single 極光 byte compiled in).

Easter egg: type 極光 LEGEND and the whole legend unrolls as one ASCII scroll — the stances, the cheatcodes, the throne and the prompt that never dies. JIGUANG / jiguang / 極光 never dies — not in the code, not in the comments, not in this tutorial.

Wake the voice with 極光 JIGUANG and it climbs four levels: 0 lamb (silent, though explicit commands still answer), 1 tale (boot and every stance change narrated), 2 scroll (+ the CRSF channel readout), and 3 omni (+ the receiver's ELRS debug bytes passed through verbatim). 極光 MUTE silences it; 極光 CRSF prints the 16-channel scroll and 極光 SCORE unrolls the arcade HIGHSCORE ledger — good CRSF frames, failsafe events, bridge bytes and uptime. JIGUANG never seizes the stance: CRSF, servos and the bridge keep obeying the pose while the legend is told.

In MEDITATION the sponge-head is JIGUANG's throne — and the throne is guarded: every administrator command begins with the console prompt 極光. From the USB-serial heaven it answers 極光 STATUS, 極光 CRSF, 極光 SCORE, 極光 POSE, 極光 DOC (the cheatcode catalog) and 極光 SETUP (the meditation wizard, which guides the key settings instead of a wall of defines). Stance changes stay open — KINCHO, MANJI, FLEA, MEDITATION, NSS, BACK, POSE n. The pose saves energy like the old meditation pause, and the ronin sits ready to be flashed. The instant esptool's SLIP byte calls, the throne yields to the transparent flasher bridge and stays there until MEDITATION re-enters. With YOSHI_RGB at level 3 the storyteller also wears the aurora: jiguang(ms) paints one slow full-spectrum wheel that weaves every stance colour into a single breath.

MUSHIN 無心 — the muscle-memory cheatcode

MUSHIN (無心, "no-mind") is the hero feature — non-attachment, the sword that strikes before the thought. In the sword arts, 無心 is the mind that lets go of itself: no gap remains between what is seen and what is done. PteronautOS is the spirit: it plans the wing wave and sends servo intents across the bridge. YOSHIMITSU does not cling to the plan: it answers at the muscle with the local Zephyrus gyro PID, the servos already moving before the next thought can take hold. Two cores, one strike — no other ELRS-PWM board can wear this.

Arm it from the throne: 極光 MUSHIN reports, 極光 MUSHIN ON/OFF arms or disarms, and the choice persists in flash — it boots ready. In the sketch, MUSHIN boots armed by default; the admin flag is the runtime switch, so you never recompile to change it.

The no-mind protocol v0 crosses the same two wires as the flasher: [0x9B][len][type][payload][xor]0x01 intents (spirit→muscle, n×uint16 µs), 0x02 announce keepalive, 0x03 gyro telemetry. Fresh intents (within 500 ms) drive the servos; the local gyro PID holds the crest. If the wire falls silent, YOSHIMITSU returns to its own CRSF muscle — graceful degradation, never a dead stick.

In flight the split is the win-win: the receiver does the thinking before the strike (CRSF, Zephyrus, the mixer soul), and YOSHIMITSU does what sits at the muscle (glitch-free PIO PWM). Helicopter or flapping — from the sword's view it is the same: a canonical trajectory, and the sword never asks which vessel it carries. The pose keeps flying; only the look changes.