Builder in Residence

Project indexBuild 08

OpenMove

An open-source automatic chessboard. Because moving pieces manually is overrated

Status
Active
Weekly log
10 of 10 weeks written up
Last commit
20 Sept 2026
Source
GitHub

Weekly log

Nine weeks, written down as they happened.

Week 00

How OpenMove Started

Date: 12 July

A chessboard was apparently not dramatic enough

OpenMove started with a random YouTube video I cannot find anymore.

Somewhere between that and the magical chessboard from Harry Potter, I got stuck on a question: could I make a real chessboard that understood what was happening on it, thought about the next move, and physically moved a piece back?

Not a finished product. Not even a particularly sensible first project idea. Just a very specific kind of curiosity that refused to leave.

I had been into open source software for a while, so the name OpenMove felt right

Getting into BIR

I was sitting in a regular, boring college lab when I started randomly trying to join different TinkerHub groups.

Somehow, even after registrations had closed, I got into the BIR WhatsApp group. Shann, one of the admins, contacted me, heard the project idea, and let me join BIR properly.

That was how OpenMove accidentally became a Builder-in-Residence project.

At that point, OpenMove was still mostly a concept. There was no assembled board, no tested mechanism, and definitely no tiny wizard living under the chessboard moving pieces around.

But it was enough of an idea to explore properly.

The first technical sketch

  • An 8×8 grid of Hall-effect sensors — one for each square — to detect piece positions.
  • A local Stockfish chess engine to calculate a response.
  • An automated mechanism underneath the board to move chess pieces.

The practical details were not simple at all.

How would the board distinguish pieces reliably? How would a mechanism move across all 64 squares without getting stuck, losing position, or dragging pieces it was not supposed to touch? Could magnets move a piece through the board cleanly? Could the electronics, mechanics, and chess logic be kept separate enough that debugging one did not destroy the others?

At this stage, those were questions—not solved engineering decisions.

Why I wanted to explore it

OpenMove was interesting because it sat right at the intersection of things I wanted to learn: electronics, mechanical design, embedded control, chess logic, and software that has consequences in the physical world.

A chess engine returning a move in software is normal. A board physically carrying that move out is where it gets complicated.

That seemed like a good reason to try it.

What came next

The next step was to turn the vague sketch into smaller experiments: decide how the XY mechanism might work, identify the controller and motion hardware, and validate movement before pretending the board could play chess.

Because a brilliant chess engine is not very useful if the board responds by launching a knight into another dimension.

Experimental corner-origin update — 19 September 2026

The manual XY origin was changed from the centre of a1 to the centre of h1. Firmware 2.1 mirrors the X coordinate while preserving normal chess file names: h1 is (0, 0), local positive X points toward the a-file, and a8 is (306.25, 306.25) mm. The firmware and host prompts were updated and the Uno build succeeds. Firmware 2.0 was uploaded to /dev/ttyUSB0, then HOME was accepted at h1 and status reported homed=1,x=0.00,y=0.00. Physical movement direction validation remains pending. A reversible 1 mm test completed X+, X-, Y+, and Y- jogs without a controller error or reset and returned the reported position to (0.00, 0.00); the observed physical directions still need user confirmation.

The firmware and desktop GUI were then audited together. Firmware 2.1 now reports serial protocol version 2 and its h1 home convention. The GUI waits for startup before requesting status, rejects incompatible firmware/home coordinates, refreshes status after manual hardware commands, and invalidates GUI board synchronization when firmware reports an untrusted board or a physical-state failure. Firmware 2.1 compiles; upload and physical regression testing were next. Firmware 2.1 was uploaded to /dev/ttyUSB0; live status reported protocol 2 and home_square=h1, and SELFTEST passed without motion. The upload correctly cleared home and board confirmation. Physical motion regression testing remains pending.

Follow-up physical GOTO tests exposed that the h1 label was wrong: GOTO g1 reached the black knight on g8, while GOTO h2 reached the black pawn on h7. The file axis was correct and the rank axis was exactly mirrored. The actual manual origin is therefore h8. Firmware 2.2 changes the mapping to h8 = (0, 0), with positive X toward a-file and positive Y toward rank 1. The serial protocol was bumped to version 3 so the GUI cannot connect its motion controls to the incompatible h1 mapping. Firmware 2.2 was uploaded to /dev/ttyUSB0; live status reported protocol 3, home_square=h8, and positive Y toward rank 1, and the no-motion self-test passed. Physical regression testing remains pending.

The h8 conclusion was then falsified by a stronger two-axis test. Under the mirrored build, GOTO e2 reached d7 and GOTO h8 returned to physical a1—an exact 180-degree mapping. The actual machine origin is a1. Firmware 2.3 restores direct coordinates (a1 = (0, 0), positive X toward h-file, positive Y toward rank 8) and bumps the protocol to version 4. Firmware 2.3 was uploaded to /dev/ttyUSB0; live status reported the direct a1 coordinate convention and the no-motion self-test passed. Physical regression testing remains pending.

Later on 19 September, the current working-tree chess-controller sketch was compiled for Arduino Uno and uploaded to /dev/ttyUSB0. The upload completed successfully, and a non-motion serial check showed the startup banner OpenMove chess motion controller 1.2, white-only automation mode, and the manual a1-home prompt. No physical movement was commanded or validated.

The next mapping revision removes the false-origin workaround. Firmware 2.4/protocol 5 explicitly defines real a1 as (0, 0), positive X from a-file to h-file, positive Y from rank 1 to rank 8, and white pieces on ranks 1-2. The GUI checks all five mapping fields and keeps chess movement disabled if the connected firmware disagrees. The Uno build and Python syntax checks passed. Upload was blocked because the running GUI held /dev/ttyUSB0; physical validation from the real a1 is still pending.

The laptop display was measured as 1366x768 at 100% scale under Niri, where the GUI receives an approximately 800x628 tile. Its old fixed 1180x760 startup size was too tall. The layout now uses available screen geometry, tighter controls, a visible scrolling log pane, and a 2000-line log history. Firmware 2.4/protocol 5 was then uploaded successfully. A non-motion status query confirmed home_square=a1, x_axis=a-to-h, y_axis=1-to-8, and white_ranks=1-2. Physical motion from the real a1 remains untested.

The coordinate mapping was then rebuilt from new physical observations. From White's viewpoint, a1 is the bottom-left black square; Y+ moves from a1 toward a2, while the previous X+ moved in the wrong direction from b1 toward a1. The measured adjacent-square centre spacing is 44 mm. With explicit user approval, the experimental H-bot transform and 40 steps/mm calibration were retained. Firmware 2.5/protocol 6 reverses logical X only and uses the 44 mm pitch. It compiled and uploaded successfully, and a non-motion status query confirmed protocol 6, square_mm=44.00, and reverse_logical_x=1. Physical regression testing remained pending. The user subsequently confirmed that the corrected positive-X jog works as intended. Small positive X and Y direction tests now match a1-to-b1 and a1-to-a2; full-square positioning accuracy is still unvalidated.

Experimental PGN replay loop — 20 September 2026

Firmware 2.6/protocol 7 and the desktop controller now support an experimental PGN replay path. The GUI accepts a mainline PGN that starts from the normal chess position, plays non-capturing moves for both White and Black, waits 10 seconds after the last move, then sends reverse RETURN moves to restore the standard position. Its Loop after restore toggle repeats that sequence.

This deliberately rejects captures, en passant, castling, promotions, and PGNs with a custom FEN start. Those actions require physically validated capture storage, replacement, and collision-safe routing; they do not exist yet. The firmware compiled for Arduino Uno using 19,146 bytes of flash and 858 bytes of RAM. It was not uploaded and no physical PGN replay was run, so this remains experimental software only.

The firmware was subsequently uploaded to /dev/ttyUSB0 after the GUI serial connection was closed. A read-only status query confirmed controller version 2.6, protocol 7, the a1 coordinate contract, and both-side automation. The upload reset manual home and board confirmation. No axis movement or physical PGN replay was commanded.

Week 01

First Smoke

Goal this week: Get the first motion electronics powered safely enough to experiment with.

What I did

This week was less “automatic chessboard” and more “can I make these motors do something without turning a driver into a small space heater?”

I started experimenting with the Arduino Uno, CNC Shield V3, stepper drivers, and the stepper-motor power supply.

image image

  • Arduino Uno
  • CNC Shield V3
  • Two DRV8825 stepper drivers
  • Two NEMA 17 stepper motors
  • A 12 V, 3 A adapter for motor power

The CNC Shield gave the Arduino a more practical way to control the stepper drivers, but it also added a few things that needed to be understood before sending motion commands: driver orientation, motor wiring, shared enable behaviour, and especially the DRV8825 current limit.

Figuring out VREF

The DRV8825 drivers use a small VREF adjustment to set their current limit. It is very tempting to treat that tiny potentiometer like a volume knob and turn it until the motor sounds confident. That would be a bad plan.

DriverMeasured VREF
X-axis driver0.65 V
Y-axis driver0.65 V

That did not automatically prove the current limit was correct. The actual current depends on the sense resistors fitted to each driver board, and clone DRV8825 boards are not guaranteed to use the same resistor values.

So the important result from this week was not “the drivers are perfectly configured.” It was that I had a measured starting point instead of guessing.

What I learned

  • The Arduino, CNC Shield, stepper drivers, motors, and power supply need to be treated as one system.
  • A 12 V adapter can still cause trouble if the driver current limit or wiring is wrong.
  • VREF is measured in volts, but it controls motor current indirectly.
  • “The motor is humming” and “the motor is correctly configured” are very different statements.

Problems and blockers

  • The actual current limit could not be confirmed until the sense-resistor markings on both DRV8825 boards were identified.
  • Stepper-driver temperature behaviour still needed to be checked during longer tests.
  • The motors had not yet been validated under the real XY mechanism or a representative load.
  • The chessboard itself was still an idea at this stage; this work only established the first electronics test setup.

Decisions

  • Use the Arduino Uno and CNC Shield V3 as the first low-level motion-control setup.
  • Use the 12 V, 3 A adapter as the bench power source for the stepper side.
  • Keep the DRV8825 VREF values at approximately 0.65 V until the driver hardware and current limits are properly verified.
  • Treat all motion hardware as experimental until it works reliably under a real mechanical load.

Week 02

Getting the Motion Electronics to Agree

Goal this week: Verify that the Arduino, CNC Shield, motors, drivers, and servo could run together before attaching them to an actual mechanism.

The first week established the hardware direction. This week was about making it move.

The setup was an Arduino Uno with a CNC Shield V3, two DRV8825 stepper drivers, two NEMA 17 motors, and a 12 V, 3 A adapter for the motor supply. A servo would eventually control the magnetic pickup mechanism, so it joined the test too.

At this point, there was no chessboard mechanism attached. No belts, rods, carriage, or pieces. That was intentional. If the electronics could not behave on the desk, giving them an XY mechanism would only make the failure more expensive and harder to understand.

image

A small test with a useful job

I uploaded a basic Arduino sketch that pulsed both stepper drivers together, reversed them, and moved the servo between two positions.

The motors ran at around 50 steps per second. Slow enough to watch, slow enough to stop, and fast enough to prove that the Arduino, CNC Shield, and drivers were actually talking to each other.

Both motors moved together and reversed together. The servo also responded to its test positions.

That was the first useful result: the control chain worked end to end.

Arduino Uno → CNC Shield → DRV8825 drivers → NEMA 17 motors
                                      └────→ servo test

It did not prove the final mechanism would work. It only proved that the electronics could produce controlled movement before any real mechanical load entered the conversation.

The VREF problem was not solved by finding a number

Both DRV8825 drivers measured approximately 0.65 V VREF.

That was useful, but not a final answer. VREF controls the driver’s current limit, but the actual limit also depends on the current-sense resistors on the board. Two drivers that look identical can use different resistor values, especially when they are clone boards.

So 0.65 V was a measurement, not a safety certificate.

The sensible next step was to identify the resistor markings, calculate the current limit properly, and compare the driver temperatures under the same test conditions. Turning a tiny adjustment screw until a motor sounds more confident is not calibration. It is just a very compact way to create new problems.

What did not behave perfectly

Earlier testing had produced a motor hum, and the Y-axis driver appeared hotter than the X-axis driver.

The coordinated low-speed test worked, but it did not explain that difference. It could have been the motor wiring, driver-current configuration, the specific driver board, or something else in the setup. None of those possibilities should be promoted to a conclusion without testing them separately.

The motors also had not been tested with belts, rods, carriage friction, or the weight of a pickup system. A motor moving freely on a desk has not yet earned the right to be called an XY mechanism.

Week 03

Giving the Magnet a Job

Goal this week: Build the first version of the mechanism that lets the magnet pick up and release a chess piece.

Up to this point, the plan had a magnet underneath the board. Cool. Very magnetic. But a magnet sitting at one height cannot really choose whether to hold a piece or let it go.

It needs to move.

That sounds obvious now, but it changed the pickup idea from “put a magnet under the board” into “build a tiny mechanism that can raise and lower a magnet without making the whole board look like it has a second gearbox attached.”

So this week, I worked on the vertical pickup mechanism: a servo-driven, 3D-printed setup that would move the magnet up toward the board to grab a piece, then pull it back down to release it.

image

The goal was simple: when OpenMove wants to move a piece, the magnet goes up, grabs it from below, and the XY system carries it to the next square. When it reaches the destination, the magnet comes down and hopefully lets go instead of deciding that the piece belongs to it now.

Turning servo rotation into useful movement

A servo rotates. The magnet needs to move up and down.

Those are not the same thing, which meant I needed a small mechanism in between. The idea was to use 3D-printed linear-servo parts to convert the servo’s rotary movement into vertical travel for the magnet.

This is one of those parts that looks small in the CAD model and somehow becomes the entire project when you actually start thinking about it.

The magnet has to get close enough to pick up a chess piece through the board, but not stay so close that it drags the piece around when it is supposed to release it. The servo needs enough travel. The printed parts need to move smoothly. The mechanism also has to fit underneath the board with the rest of the XY system.

Simple enough on paper. Paper also does not have friction, tolerances, loose screws, or a servo that has decided today is not its day.

Printing the first parts

image

I 3D printed the parts for the linear-servo mechanism.

This was the first actual piece of the pickup system. Until now, “the magnet will pick up the pieces” was mostly a sentence with a lot of confidence behind it. Now there was a physical assembly meant to hold the magnet and move it vertically underneath the board.

It still needed to be attached to the servo, tested, adjusted, and probably redesigned at least once. But that is normal. A 3D print is not the final answer; it is the first time your assumptions get to meet plastic.

What still needed to survive reality

Printing the parts was progress. It was not proof that the pickup system worked.

  • Is the servo strong enough to move the magnet reliably?
  • How much vertical travel does the magnet actually need?
  • Can the magnet pick up a chess piece through the board?
  • Will it release the piece cleanly?
  • Does the mechanism bind, flex, or get in the way of the XY carriage?

The plan was there. The printed parts were there. The physics was still waiting to give its opinion.

Week 04

Putting Stockfish on a VM Because it sounds cool

image image

Goal this week: Set up Stockfish on an Azure VM so OpenMove could eventually ask a chess engine for moves.

The mechanical side of OpenMove was slowly becoming real: motors, drivers, a servo, and a printed magnet mechanism.

The software side still had one very important missing piece: something had to decide what move the board should make.

That job belongs to Stockfish.

Stockfish, but not directly from the board

The original idea was simple: the board detects a move, Stockfish thinks, and OpenMove physically replies.

In practice, the Arduino should not be running chess logic, validating full game state, talking to a server, and controlling motors at the same time. It already had enough going on.

So I set up Stockfish on an Azure VM instead.

The VM runs Stockfish inside a container and exposes a small API that accepts a chess position in FEN format, runs a limited search, and returns a move in UCI notation.

For example, the engine can receive a board position and return a move like e2e4.

That is a much cleaner boundary than letting an Arduino somehow become a chess computer, web client, and motion controller at the same time. It is also a good way to make debugging slightly less cursed later.

Keeping the engine in its lane

The API was deliberately small.

It validates the position, runs Stockfish with a bounded search time, and sends back the engine move. The Azure VM service is not directly connected to the board, and it is not publicly exposed as a “free chess engine endpoint for the internet.”

Board sensors
     ↓
Host controller checks the game state
     ↓
Stockfish API returns a move
     ↓
Host controller validates it again
     ↓
Arduino moves the mechanism

That extra validation matters. A chess engine move is only useful if it still matches the physical board. If someone moves a piece while the engine is thinking, blindly sending the old response to the motors would be a very efficient way to make the board play a move from an alternate universe.

What worked

  • Stockfish was set up on an Azure VM.
  • The service could receive a FEN position and return a UCI move.
  • The engine ran inside a container.
  • The service was kept separate from the Arduino and physical hardware.
  • Basic API behaviour was tested before treating it as part of the project.

Week 05

Teaching the Board to Say “No”

Goal this week: Give the motion prototype a small serial controller without pretending that it was ready to play chess.

The motors had already passed a very small bench test. That was useful, but “both motors moved” is not a coordinate system, a safety boundary, or a way to recover when the carriage gets lost. This week was about putting a boring but necessary layer between a terminal command and the hardware.

The controller got a vocabulary

The experimental Arduino sketch in code/openmove_chess/ gained commands for manual home, status, jogging, square moves, actuator endpoints, board reset, and an emergency stop. It also keeps a small internal chess position so it can reject obviously illegal requests instead of letting every typo become motor movement.

That does not make it a chessboard yet. It is a motion-controller prototype wearing a chess notation trench coat.

The important boundary stayed the same: the Uno owns pins, timing, and the actuator. A host computer will eventually own game rules, engine calls, and anything involving the internet.

A terminal UI, because serial monitors get old fast

code/openmove_tui.py was added as a thin manual control interface. It can send commands and show the controller response; it does not bypass the firmware's checks.

The controller starts untrusted after upload. Someone has to set the physical home position and explicitly reset/confirm the board before chess-style motion is allowed. That is deliberately annoying. An automatic board should be a little suspicious of itself before moving pieces around.

What this did not prove

No physical chess move, square accuracy, clearance, homing repeatability, or pickup behaviour was validated here. The coordinate calibration and routing in the firmware remained experimental. Software restrictions are not a substitute for a carriage that has actually moved under load.

Next

Use small manual jogs to establish a real origin and axis directions, then measure one square before allowing the firmware to speak confidently about a1, h8, or anything in between.

Week 06

A Coordinate System Is Not a Vibe

Goal this week: Make the firmware's idea of a chess square explicit enough to test it against the actual machine.

Writing goto e4 looks cute until the carriage heads toward a different corner than the one in your head. The early controller revisions made that risk very obvious: motor direction, H-bot transforms, manual home, and chess notation were all tightly coupled.

The useful part: fewer hidden assumptions

The motion firmware was tightened around a manual-home workflow and explicit status reporting. The controller can report its coordinate contract, and the host tools can refuse to issue chess motion when the connected firmware does not match the expected mapping.

That is not glamorous work. It is also the difference between a mapping mistake being a visible error and it becoming a very confident rook relocation.

Still experimental

At this point, the origin label, positive-axis directions, steps-per-millimetre calibration, and square pitch were not final. The firmware could compile and be uploaded, but compilation only proves that C++ survived the compiler. It says nothing about where a magnet ends up in real space.

The 4 mm rods, belt geometry, H-bot transform, and pickup mechanism also remained prototype choices. Nothing here promoted them to final hardware.

Next

Run the smallest physical tests first: set the real a1, jog positive X and Y, and compare a commanded square with the board. One measured fact beats three pages of coordinate folklore.

Week 07

White-side Motion Firmware

Date: 18 September 2026

The chess-controller firmware now automates white pieces only. After a human physically moves a black piece, entering that coordinate move validates it and updates the firmware's internal board without moving the carriage. White moves continue to use the existing piece-aware validation and mechanical routing.

That split was intentional. It let the controller exercise move validation without pretending the board could safely route every piece for both sides. Less impressive on a demo video, much less likely to create an expensive new problem.

Automated knight moves are rejected for now. The square-boundary lane route cannot be called collision-free until it is measured against the largest piece base and tested on the physical board.

Firmware version 0.9 compiled for Arduino Uno using 12,880 bytes of flash and 527 bytes of SRAM, then uploaded successfully to /dev/ttyUSB0. It accepts legal human-performed black captures as state-only updates, rejects unvalidated promotion for both colours, rejects king capture, invalidates state after a rejected black entry, and guards step timing against unsigned underflow.

No HOME or motion command was issued during the upload. Physical movement, clearance, and accuracy are still unvalidated for this build. Captures, castling, en passant, and physical promotion stay blocked until their mechanisms are proven.

Week 08

The Board Finally Argued Back

Date: 19 September 2026

This week was a coordinate-system reality check. The first manual-origin label was h1. Physical GOTO tests disagreed: GOTO g1 reached g8, and GOTO h2 reached h7. That ruled out the label. A later two-axis test ruled out the next theory too: the apparent h8 mapping was an exact 180-degree transform, which established the actual machine origin as a1.

That is not a cute correction. A wrong mapping can move a real carriage to the wrong square while software insists it is being helpful. The firmware protocol was bumped as incompatible coordinate contracts were corrected so the GUI would not quietly drive a newer board with an older assumption.

The current contract

a1 = (0, 0)
+X = a-file → h-file
+Y = rank 1 → rank 8
white pieces = ranks 1–2
square pitch = 44 mm

The 44 mm centre-to-centre pitch came from a physical measurement. The existing H-bot transform and 40 steps/mm calibration were retained with explicit approval; they are still experimental, not magically certified by being in a constant.

Firmware 2.5/protocol 6 compiled and uploaded. Small positive X and Y jogs were physically confirmed to match a1 → b1 and a1 → a2. That is a real win, but keep the champagne corked: full-square positioning accuracy, repeatability, belt tension, rod rigidity, and motion under pickup load are still unmeasured.

Next

Measure full-square travel repeatedly from the real a1. Only then does it make sense to ask the mechanism to carry a piece instead of merely pointing at one.

Week 09

Replaying a Game Without Lying About the Hard Parts

Date: 20 September 2026

The desktop controller and firmware gained an experimental PGN replay path. Given a mainline PGN from the standard starting position, the GUI can send non-capturing moves for both colours, wait 10 seconds after the last move, and send reverse RETURN moves to restore the standard position. The loop toggle can repeat that sequence.

This is a controlled replay experiment, not autonomous chess. The parser rejects captures, castling, en passant, promotions, and custom-FEN games. Those need pickup, release, removal storage, collision clearance, and recovery paths that have not been physically validated. Calling that “feature incomplete” is too polite; it would be physically dishonest.

Trust is part of motion

The follow-up firmware 2.7 keeps protocol 7 and improves the failure boundary. If an emergency stop, failed move, failed return, or DISABLE command loses home or board trust, the controller reports that change. The GUI refreshes its state before replay and after failure instead of continuing from a stale cache.

That matters because a failed replay does not restore the physical board. The operator must set home and reset the board before new movement. There is no clever software shortcut around a carriage that no longer knows where it is.

Firmware 2.7 compiled for an Arduino Uno using 19,226 bytes of flash and 858 bytes of RAM, and was uploaded to /dev/ttyUSB0. The GUI replay changes were checked headlessly against a scripted fake controller. No post-upload axis motion, physical replay, pickup test, or full game has been run.

Next

repeatable full-square travel, homing, and a magnet moving one piece cleanly. The software is allowed to wait. Gravity and friction have been waiting longer.