The parts games are made of

A screen, something to hold, and the four circuits in the gallery that put them together.

Games are the part of Mokxi people keep coming back to, so four parts are worth knowing well: the small OLED, the thumb stick, the rotary encoder and the addressable LED ring. This page is what each one does, what the firmware has to do to drive it, and which example to open to see it working.

The SSD1306 OLED, 128x64

The little blue-and-white screen every hobby kit has. Two wires and power: it is an I2C slave at address 0x3c, and it acknowledges 0x3d as well if you set the property that way.

The part models the command set the common libraries actually send (display on and off, the addressing mode, the column and page windows, contrast, segment remap, COM scan direction and the charge pump) and both page and horizontal addressing, so a driver written against a real panel works here unchanged. What you see on the canvas is the panel's own graphics RAM, drawn pixel for pixel.

There is no I2C peripheral on either the Uno or the ESP32-C3 in Mokxi, so the bus is bit-banged: mokxi_i2c.h pulls the two lines down and lets them go, and the module's own 4k7 pull-ups bring them back up. Any two pins will do, which is why the examples use the two everybody expects.

A 128x64 monochrome frame is 1024 bytes. On an ESP32-C3 that is nothing; on an Uno it is half the chip's memory, and sending all of it over a software bus takes a third of a second. That is why the driver sends only the eight-column blocks of each page that changed, and why a game on this panel rubs out the ball where it was and draws it where it is rather than repainting.

See it: OLED dice (ESP32-C3), OLED pong (Uno) and OLED pong on an ESP32-C3.

The thumb stick

Two 10 k potentiometers and a push switch: the KY-023 module and its clones. Drag the black cap on the canvas and both axes move; let go and it springs back to the middle, the way the real one does. Click or hold the cap to push the stick straight down, which shorts SW to ground.

Each axis is a real divider across whatever is on VCC and GND, with the track's own impedance behind the wiper, so an ADC input with a load on it sees what it would on the bench.

A thumb stick needs an ADC, and both boards now have one. The Uno's is the ATmega328P's ten-bit converter on A0 to A5; the ESP32-C3's is ADC1, twelve bits, on GPIO 0 to GPIO 4. So a stick works on either, an axis reads 0 to 1023 on one and 0 to 4095 on the other, and mokxi_joystick.h in the firmware library hides the difference.

See it: OLED pong (Uno), OLED pong on an ESP32-C3, ESP32-C3 thumb stick and Snake on two matrices.

The rotary encoder (KY-040)

A shaft that counts clicks either way around, with a switch under it. Three contacts to ground: CLK and DT are the two halves of a quadrature encoder and SW is the push. The module carries 10 k pull-ups on the quadrature pair; SW has none, which is why every sketch reads that one with INPUT_PULLUP.

Three ways to turn it on the canvas:

  • drag around the knob: the pointer's bearing about the shaft is the shaft's angle;
  • the scroll wheel over the knob: one notch is one detent, away from you clockwise;
  • drag up and down: for a touch screen, and for a drag that starts on the shaft itself where a bearing means nothing.

Turning it does not jump the pins from one state to the next. One click is four quarter steps, played out eight milliseconds apart, so firmware that polls in a loop sees the sequence the way it would on a bench. And the contacts chatter, once per quarter step, because that is the whole lesson: a sketch that watches for a falling edge on CLK counts three clicks for one. Turn the chatter off in the properties when the encoder is not the point.

mokxi_rotary.h does it the right way, with the sixteen-entry state table: a transition scores +1, -1, or nothing when the pair jumped two states at once, which is what a bounce looks like. A click is reported when four quarter steps have added up and the shaft is back on a detent.

See it: Ring Simon.

The WS2812 ring and strip

Addressable RGB LEDs on one wire, with no clock: a bit is a high pulse whose length is the bit, 0.4 microseconds for a nought and 0.8 for a one, twenty- four bits an LED, green first. The part decodes those pulses off the pin's own edges with the timing tolerance a real strip has, and fifty microseconds of quiet latches the frame.

layout picks the picture. strip is a row, matrix is sixteen wide, and ring bends the row into a circle, LED 0 at twelve o'clock and the rest clockwise, which is how the rings people buy are numbered. A ring is a strip electrically, so nothing about the wiring or the firmware changes; only the drawing does.

The driver, mokxi_ws2812.h, makes the pulses with a counted loop rather than a timer, because the ESP32-C3's SYSTIMER ticks at 16 MHz and reading it takes longer than the pulse being measured. It is an ESP32-C3 header: at 16 MHz an Uno has twenty cycles for a whole bit and six and a half for the first pulse, which digitalWrite cannot do without a page of hand-written assembly.

See it: Ring Simon and Rainbow strip.

The five games

Open any of them from the Examples menu, or from the templates on the site.

Game Board Screen Input
OLED pong Uno SSD1306 thumb stick
OLED pong on an ESP32-C3 ESP32-C3 SSD1306 thumb stick
Snake on two matrices Uno two MAX7219 thumb stick
Ring Simon ESP32-C3 WS2812 ring rotary encoder
OLED dice ESP32-C3 SSD1306 arcade button

The two pongs are the same circuit on the two boards, which is the shortest way to see what a board changes and what it does not.

Each is real firmware compiled for the board it names, running on the same core everything else here runs on. Press Run, then drive the input with the pointer: drag the stick's cap, turn the encoder's knob, hold the button down.

There is a sixth, Stacker, on two MAX7219 matrices with one button, and it plays itself until you press something.