FluxDrive / Build Log, From a Breadboard to a Wireless Floppy Drive Inside the Amiga 500
FluxDrive is a floppy drive emulator on a single ESP32-S3 that sits inside my Amiga 500 as DF0. It gets its disks over Wi-Fi from the GTi touchscreen and generates the MFM flux stream itself. From a breadboard full of wires to a four-layer board, firmware that reads, writes and keeps your high scores, and a rev B with a front panel on a Raspberry Pi camera cable.
A Gotek costs about twenty euros and works fine. I built my own floppy drive anyway, because I wanted something a Gotek cannot do: pick a game on a touchscreen across the room, and have the Amiga 500 boot it from thin air.
FluxDrive is the result: one ESP32-S3 on a small board that plugs onto the A500's internal floppy connector and is drive DF0. It answers the floppy bus the way a real drive does, keeps the whole disk image in its own memory and generates the MFM flux stream itself. The disks arrive over Wi-Fi from the GTi, the touchscreen disk library originally created by Mez, which Mez and I now develop together. No Gotek, no second microcontroller.
This is the story so far: from a breadboard in August to a four-layer board in the machine, firmware that reads, writes and keeps your high scores, and a second revision with a front panel on a Raspberry Pi camera cable.
1. The idea: the GTi without the Gotek
The GTi is a touchscreen with a library of disk images for a Gotek. To load a disk wirelessly it sends it to a small dongle, Webby, which presents it to the Gotek as a USB stick. Mez wrote down a simpler idea in his OMEGAWARE FluxDrive hardware design document: one ESP32-S3 directly on the Shugart bus, receiving disk images from the GTi over ESP-NOW and generating the flux itself, so the Gotek disappears entirely. My own goal on top of that: the cheapest emulator that still does everything the GTi offers.
2. Where it started: a breadboard full of wires
Before there was a board there was a breadboard: an ESP32-S3 Super Mini and a few 74HCT125 buffers on a ribbon cable to the 34-pin connector. It got the Amiga to poll the drive, step the head and try to read, and it taught me most of what the real board had to get right.
August 2026: the breadboard prototype. Somewhere under the wires sit an ESP32-S3 Super Mini and the 74HCT125 buffers.
- The Amiga pinout is not the PC pinout. On a PC drive disk change is on pin 34. On the Amiga, pin 2 is disk change and pin 34 is /RDY, the ready line that trackloaders like X-Copy check. Out of PC habit I had them the wrong way round.
- The flux has to come from hardware. A double-density track is 100,000 bit cells of 2 µs: 200 ms of gapless output per revolution, about 35,000 flux transitions. I went through three rewrites of the RMT pulse generator under the Arduino core, which can neither mix with the native ESP-IDF driver nor stop a running transmission. Lesson for the board: write the drive against ESP-IDF directly.
- Buffers have to suit the voltage. The 74HCT125 is a 5 V part; the ones on the input side ran at 3.3 V, and that caused the "weak select line" I spent evenings on. The board uses 74LVC logic throughout.
Those findings went back to Mez as review notes on his document, and on 28 September the FluxDrive project started with both documents as its input.
3. From a design document to a board
The v1 hardware spec was written on 28 September and the board was designed in the two days after it. Every stage, from spec to schematic to layout, went through four independent reviews: the Amiga floppy bus, power and EMC, the ESP32-S3, and manufacturing at JLC. Two findings in the first round were blockers:
- The reverse-polarity P-FET in my first power design conducts both ways once it is on. On USB alone it would have fed the 5 V rail of a switched-off A500. It became an eFuse on the Amiga side plus two Schottky diodes, so USB and the Amiga can never feed each other.
- All six output pins I had picked are driven low for about 60 µs when the ESP32-S3 powers up, which would assert every floppy signal at once, also on a RESET press while the Amiga runs. The outputs moved to GPIOs that do not glitch.
The layout review found that on two layers the autorouter had cut the ground plane into 48 pieces under the buffers, so the board went to four layers. JLC's assembly wants every part inside the outline, so the antenna moved onto the board, over copper-free board material. A 1:1 paper template showed it fits in the A500 with room under the shield, and Mez audited rev A before it was ordered: nothing had to change.
Rev A as designed: 60 × 54.5 mm, four layers. The ESP32-S3-WROOM-1 module (16 MB flash, 8 MB PSRAM), two 74LVC14A Schmitt-trigger inverters for the inputs, a 74LVC07A open-drain buffer for the outputs, and the 34-pin header for the Amiga at the front.
4. Rev A arrives
My plan was to skip rev A and go straight to rev B (more below), but I ordered it anyway for bring-up and firmware work. The boards arrived on 22 October. JLC placed the small parts; the ESP32 module is left out of their order and placed by hand on pads without paste, so it sits flat. The photo at the top of this page is that board in my A500.
One thing did not fit: the A500's floppy power plug would not mate with the power header from the top. So I soldered the header on the bottom side of the board, to match the plug. That board has run ever since, and rev B has its power header on the bottom by design.
Bring-up went in the order of Mez's document: power, inputs, outputs. With test firmware in the A500, the inputs counted what Kickstart does at the insert-disk screen (606 selects and 300 steps in two and a half minutes), and switching the outputs by hand showed disk change, track 0 and index on the Amiga's side. The same day a testpiece of code, booted Kickstart 1.2 from an ADF, and taught the first rule of the flux stream: it must be seamless within a track. Sent as one transfer per sector, with about 20 µs of silence in between, Kickstart could not read the disk. Sent as one transfer per revolution, it booted. The flux comes from the SPI peripheral with DMA: at 1 MHz, two SPI bits make one 2 µs bit cell.
5. Firmware v1: behaving like a real drive
Firmware v1 was written from 22 to 24 October on ESP-IDF, with one job: pick an ADF on the GTi, and the Amiga boots it as if a real disk were in the drive. The drive core runs alone on the ESP32's second core. A tight loop watches the select line and releases all six outputs the moment DF0 is not selected; its longest pass measured 366 ns, so the board follows the select line within about 0.4 µs. The disk is kept in flash in two slots with a commit marker, so the last disk boots after a power cycle without the GTi, and a power cut while storing brings back the previous disk instead of a broken one.
The first Workbench boot worked, with intermittent "read/write error" requesters. Diagnostics found two timing problems:
- Lost steps. A one-shot ADC read of the Amiga's supply held a critical section of about 65 µs every 2 ms. Kickstart 1.2 deselects DF0 about 26 µs after each step, so a step that landed in that window looked like it came while the drive was deselected, and was ignored: 4 out of 279 in one test. One lost step leaves the drive a cylinder behind.
- Slow head switches. Kickstart 1.2 raises /SIDE with every select and drops it at every deselect, so nearly every read of head 1 began with a 1.3 ms switch.
The fix: step, select and direction edges are timestamped by hardware (the MCPWM capture unit), so interrupt latency no longer decides whether a step counts; the ADC runs in continuous mode; and both heads stream all the time on two SPI peripherals, so a side change only re-routes the output pin. After that, Workbench 1.3 booted without a single error.
The network side is Webby, Mez's dongle firmware, ported onto the drive. On the GTi, FluxDrive shows up as its own device type with the disk it holds, and firmware updates go over the air with a rollback if a new image does not confirm itself.
The GTi's game list. The bar at the bottom ("SPEELT NU", now playing) shows FluxDrive with Workbench 1.3 in the drive.
6. Sharing the air with a floppy stream
A radio next to a microsecond-accurate flux stream is a risk, so FluxDrive keeps a radio rule: while the drive motor runs, it sends nothing, and a push from the GTi waits until the load is done. Flash gets a rule of its own, because writing flash stalls the chip's cache: every flash write is cut into holds of about 27 ms that only happen between Kickstart's polls of DF0, when the Amiga is not looking.
Then Armour-Geddon broke the radio rule. At its "insert disk 2" prompt it keeps the motor running for minutes with the head parked, so the GTi's push of disk 2 waited until the panel gave up. The rule now holds only while the head moves: motor on but no step for 5 seconds means the Amiga is waiting, and the radio may talk.
7. Let the Amiga see an empty drive
What a swap that is too fast looks like: Workbench still shows the old disk, and the new one is "Disk corrupt - task held".
The first swaps from the GTi went in one go, in 0.28 seconds. Kickstart 1.2 polls DF0 about every 500 ms, so it never saw the drive empty: Workbench kept the old icons and called the new disk corrupt. A real drive is empty while you change disks, so now FluxDrive is too: every swap shows an empty drive for one second, then mounts the new disk with the disk-change line set, cleared by the Amiga's next step as on a real drive.
Speed was the other complaint: a disk took 23 to 30 seconds to arrive. The cause was a TCP receive window of four segments against a round trip of 80 to 160 ms, plus a weak link to the nearest mesh node. A larger window, joining the strongest access point and a bigger send buffer on the panel's side brought it down to about 3.5 to 4 seconds from a PC and 5 to 11 seconds from the panel. The rest is the crowded 2.4 GHz channel.
8. Acceptance
Rev A at work in the open A500, its red LED on, with Amiga Test Kit 1.21 on Kickstart 1.2 on the screen, loaded from FluxDrive.
On my A500 with Kickstart 1.2, Workbench 1.3 boots from an ADF sent by the GTi, X-Copy Pro passes its drive checks (it needs /RDY), AmigaDOS sees a swap from the GTi, Workbench shows DF0 and nothing else, the last disk boots after a power cycle without the GTi, and an over-the-air update at the Workbench and a forced rollback both pass. Loaded so far: James Pond, Xenon, X-It, Dyna Blaster, Armour-Geddon, Utopia, Vroom, Wolfchild, Amiga Test Kit (no disk errors) and my own AmiCrush. Still open: confirming full loads of every game, the push at Armour-Geddon's disk 2 prompt, and other Kickstart versions.
9. v2: writing, and the bug a real capture found
Workbench wants to write its preferences, and games keep high scores on their disks, so v2 started on 24 October with writing:
- FluxDrive captures the Amiga's write data with the RMT peripheral, decodes it into AmigaDOS sectors on the other core and puts them into the disk at once, so the Amiga reads its own write straight back. Three seconds after the last write the changes go into a journal in flash and survive a power cycle.
- The changes go to the GTi the way Mez's dongle already does it: the panel keeps the original ADF untouched and patches the changed sectors into a separate save file next to it. The next time that game goes to FluxDrive, the panel sends the save, high scores included. START FRESH throws the save away, and write protect can be set per disk.
- A track in a game's own format cannot be stored in an ADF, which only holds the eleven standard sectors. Such writes are recognised and counted instead of silently lost.
The first real test failed completely: Workbench wrote eleven times, 121 sectors, and every sector header was rejected. So I recorded one real write of track 80 and took it apart. The timing was clean (pulse spacings of 3.95, 5.92 and 7.89 µs, right on the 4, 6 and 8 µs of MFM), 52,344 pulses, every checksum good. The problem was my decoder. Each AmigaDOS sector header says how many sectors are left until the track gap, and I required that to be 11 minus the sector number. That only holds for a track that starts at sector 0, like the ones FluxDrive generates. The Amiga's trackdisk.device starts writing at whichever sector is under the head, so its sector 7 says "11 to the gap". One condition changed, the recorded write became a test, and the retest wrote 13 tracks: 13 whole, none bad. Since then a new drawer, a file and changed Preferences survive a reset and a power cycle, write protect makes Workbench refuse to write.
START FRESH showed one more gap. The Amiga got the original disk back, but after a power cycle FluxDrive came back with the old one. A new disk is stored in flash in the background, only while the motor is off, which during a game load takes 50 to 80 seconds; a power cut in that window brings back the previous disk, as designed, but the panel did not know. The fix is being built now: the panel shows "storing..." until FluxDrive reports the disk is safe, notices a revert, and START FRESH keeps a backup of the old save until then.
10. Rev B: the real drive back, two virtual drives and a front panel
Rev A replaces the A500's own drive. Rev B keeps it: the original drive plugs into a second 34-pin header on the board, and either drive can be DF0.
- Modes: FluxDrive as DF0; the real drive as DF0 (a stock A500); the real drive as DF0 with FluxDrive as DF1, for copying real floppies with X-Copy; and FluxDrive as DF0 and DF1, two virtual drives, which a spare OR gate on the board made possible.
- The swap happens in logic. When Kickstart reads the drive IDs it selects each drive for about 2 to 3 µs, far too short for software to get out of the way. So FluxDrive's outputs are gated in hardware, and a latch keeps the mode while the ESP32 restarts. Without firmware the board is a stock A500. The steering logic was checked by an exhaustive simulation of 2.16 million input combinations.
- Control from the GTi, the web page or an optional switch in the case, and with an optional wire FluxDrive can reset the Amiga itself after a mode change.
On 25 October the plan grew. I want FluxDrive to work on its own as well, as easily as a Gotek with FlashFloppy: a USB stick of games, a small screen and a knob, no phone, no Wi-Fi, no GTi. Everything that needs in hardware goes into rev B now, so the board only has to be ordered once:
- A front panel on a Raspberry Pi camera cable. A standard 15-pin flat cable: cheap, sold from 15 cm to 1 m, and so thin that it slides through one of the A500's 2.8 mm vent slots with both ends connected.
- The panel has a 1.3 inch 128×64 OLED, a rotary encoder with push, two Cherry MX-compatible key switches with keycaps for EJECT and BACK (they suit the old Amiga look, and I have plenty), a piezo for drive sounds, an activity LED and a USB-A socket for the stick. No reset key: too easy to hit by accident, so reset is a menu item with a confirmation. A colour panel can replace it later on the same cable.
- A USB switch sends the ESP32-S3's USB either to the USB-C socket for a PC or down the cable to the stick, which gets its own current-limited 5 V.
- A 3D-printed wedge case that clamps onto the A500's vent slots, its face tilted for a good view from the chair and doubling as the plate for the key switches.
- A Y cable powers the real drive straight from the A500's drive power cable, so the board's power header only carries FluxDrive and the panel.
Rev B as placed: 60 × 76.1 mm, with the second 34-pin header for the original drive, the mode latch and steering logic, the USB switch and the flat-cable connector on the left edge. Every part is placed and checked; the tracks come next.
11. What I learned
- Read how the real thing works before you debug. The pin 2 and pin 34 swap was invisible from the ESP32's side and plain to see in the A500 schematic.
- Kickstart is faster than you think. It deselects 26 µs after a step and flips /SIDE on every select. Interrupt latency cannot decide what counts; hardware timestamps can.
- Measure, then fix. The lost steps and the slow head switches were found by diagnostics, not by guessing.
- A real capture beats a test that agrees with you. My decoder passed every test on tracks I generated myself, and failed on the first track the Amiga wrote.
- Behave like a drive, including the empty moment. The Amiga has to see one disk go out before the next comes in.
- Hardware defaults must be harmless. Outputs released at power-up, outputs gated in logic, a stock A500 without firmware. And sometimes the fix is not in the firmware at all: the connector goes on the other side of the board.
Status
Rev A is DF0 in my A500, and it reads, writes and keeps saves. Next:
- Finish v2 part 1: the fix above, and the last checks (a game that saves high scores, a power cut during a write, writing with X-Copy).
- Rev B: a few measurements and a fit check in my A500, then routing, and ordering rev B with the front panel board. Then printing the wedge.
- v2 part 2, standalone mode: games from a USB stick, browsed on the OLED with the knob, with drive sounds on the piezo. After that a selector app on the Amiga side, and two virtual drives.
To be continued.
Credits
FluxDrive would not exist without Mez. The GTi was originally created by Mez and is now developed by Mez and me together. GTi
The idea of the board itself comes from his OMEGAWARE FluxDrive hardware design document, the network side of the firmware is his Webby (MIT licence), and he audited rev A before it was ordered. The FluxDrive hardware, the boards and the drive firmware are mine.