Skip to content

encukou/t3

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

85 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

T3

The T3 is a minimalistic handheld game console.

Specs:

  • Power: 3 AA batteries
  • Processor: ESP8266
  • Input: 6 buttons (directions, A, B)
  • Display: 3×3 truecolor
  • Sound: 1-bit PWM piezo

Usage

When turned on, the console displays a “T3” welcome animation and enters the game selection menu. Press left/right to select a game, and press A to launch it. Currently these “games” are available:

Dice

Simulates a 6-sided die. Press A to re-roll. When ready, the result is shown colorfully.

Repeat

The game shows a sequnce of directions (left – yellow; up – red; right – green; down – blue); then a single white pixel in the middle. Initially, the sequence just contains one direction.

The player's job is to repeat the sequence. If they're successful, a new direction is added to the sequence, and a new turn begins. If they fail, the sequence is reset, and the game starts over.

Tic Tac

First, set the game up: press left/right to choose Human vs. Computer, or Human vs. Human. You can also press up/down to choose a player's color, and B to select if the human or computer goes first. When ready, press A to start.

Players take turns putting their colors on the board. The first on to get 3 in a row – horizontally, vertically, or diagonally – wins. In case of tie, the game is repeated.

The simulator

A rudimentary simulator is included to aid development and to run games without a physical console. The simulator is not accurate--it runs with the speed and memory of the host computer--but it's useful for validating high-level logic.

To run the simulator, you will need Python 3.5+ and MicroPython, plus the Pyglet and Click libraries. On Fedora, you can install the prerequisites with:

sudo dnf install python3 micropython python3-pyglet python3-click

(Please contribute instructions for other systems. Note: the emulator won't work on Windows without major changes.)

Run the included t3 script to launch the simulator. Control it with arrow keys, and Z/X for the A/B buttons. To run a particular game directly, pass its name as a command-line argument.

The simulator doesn't currently emit sound.

API

Each T3 cartridge starts with a "magic comment" and selection-screen animation, for example:

# T3 Cartridge
# 1/5  0f0000 0f0000 0f0000  000000 0f0000 000000  000000 000000 000000
# 1/5  000f00 000000 000000  000f00 000f00 000000  000f00 000000 000000
# 1/5  000000 000000 000000  000000 00000f 000000  00000f 00000f 00000f
# 1/5  000000 000000 0f0f00  000000 0f0f00 0f0f00  000000 000000 0f0f00

The line # T3 Cartridge is mandatory. The subsequent lines give the duration (in seconds, with one optional / for entering a fraction), and hex RGB values for all the 9 LEDs. This sequence is repeated on the game selection screen.

The rest of the cartridge file is a Python module, which must define a generator iterator called main, which implements the game. Whenever the function yields a number, it is suspended for that many seconds. Alternately, it can yield t3.wait_for_input() to wait for button events. Display and input updates happen when main is suspended, so it is necessary to yield often.

The yield expression result is a wait info object that exposes how long the wait actually took (which will usually be slightly more than requested), and which buttons were/are pressed:

  • info.elapsed: The elapsed time, in seconds
  • state.pressed[btn]: true for buttons that were pressed during the wait
  • state.released[btn]: true for buttons that were released during the wait
  • state.held[btn]: true for buttons that are currently pressed

The button-related attributes should be indexed by button objects such as t3.left – see below.

The cartridge module may import the t3 module, and use the following functionality from it:

t3.display[i]

The color of the i-th pixel, as a 3-tuple of ints from 0 to 255. May be read or set.

t3.display[x, y]

The color of the pixel in row x and column y, as a 3-tuple of ints from 0 to 255. May be read or set.

t3.display.anim_pixel(pos, r, g, b, steps)

Animates the given LED (which may be given as i or (x, y), see t3.display[...] above) from its current color to the given color, using the given number of steps. This is a coroutine that should be started using t3.start_task.

t3.hls_to_rgb(h, l, s)

Converts a color given by hue, lightness, and saturation to the RGB format needed by t3.display. The arguments should be floats from 0 to 1.

t3.random_uniform(a, b)

Returns a random float from the interval (a, b], using the hardware randomness generator. In the emulator, this chooses from a fixed sequence.

t3.randrange(a, b)

Returns a random integer from range(a, b), using the hardware randomness generator. In the emulator, this chooses from a fixed sequence.

t3.start_task(coro)

Starts a new task, which should be a generator iterator that yields a number whenever it pauses execution – same as main.

t3.wait_for_input()

When yielded from main or a coroutine started with t3.start_task, waits until the next button press or release.

t3.left, t3.right, t3.up, t3.down, t3.a, t3.b

The buttons, as objects of class t3._Button. You should never create new instances of this class.

t3._Button.value

The value of this Button: False for not pressed; True for pressed.

t3.tone(pitch, duty=512)

Starts playing a tone of the given pitch, using a rectangular wave.

The duty is a number from 0 to 1024 that gives the wave's duty cycle; this can be used to emit different "timbres".

Call tone(0) to stop playing a sound.

Building it

Here are notes on building the console.

(Questions and pull requests aimed towards making them more obvious would be more than welcome!)

Bill of Materials

Part Count
NodeMCU board 1
Mini breadboard with mounting holes 1
Tactile switches 6x6x1mm 6
Piezo sounder, 14mm diameter, 7mm height (excl. leads) 1
Sliding switch, 10x2.5x7mm 1
WS2812B LED modules (1cm circular PCB) 9 (in 3x3 square)
Screws, 2x25 (metric) 6
AA Batteries 3
Wires
3D printer filament
Translucent sheet (parchment paper)
Sheet of clear plactic

Tools needed

  • 3D printer
  • Soldering equipment
  • Screwdriver
  • Sharp knife
  • Marker

Instructions

Flash MicroPython onto the NodeMCU board, and upload all files from the sw directory to it.

Print the printed parts from the 3d directory. They'll fit (barely) on a 200×200mm print bed.

Put the buttons in the printed button pads. On the reverse side, solder one wire to all 4 innermost contacts on the pad with 4 buttons. Solder another wire to the 2 innermost contacts on the pad with 2 buttons. Then, solder one wire each (total 6 wires) to all the outermost contacts.

Solder wires to the leads of the speaker.

Connect spacts of the batteries in the battery holder together, and attach power wires, like this:

-------- +  BATTERY  - ---.
                          |
    .---------------------'
    |
    '--- +  BATTERY  - ---.
                          |
    .---------------------'
    |
    '--- +  BATTERY  - ---------------

Solder the WS2812B modules together into a 3x3 array. All + pads should be connected together; all - pads should also be connected, and the individual modules should be chained together using the DI (Data In) and DO (Data Out) pads, like this:

.-- 2 <- 1 <- 0
'------------------.
.-- 5 <- 4 <- 3  <-'
'------------------.
    8 <- 7 <- 6  <-'

The modules should be tightly packed, and should fit tightly into the space on the display on the 3D printed base. Solder wires to the first WS2812B's +, -, and DI pads.

Using two screws, attach the mini breadboard to the 3D printed base.

Snap the speaker into its holder at the top of the console.

Then, attach the button pads to the lower two "columns" in the base.

Connect wires to the breadboard accrording to the diagram below. Always leave the outermost column empty, so you'll be able to insert the NodeMCU later:

                    ---      ooooo  ooooo      ---
                    --- (A0) ooooo  ooooo (D0) ---
      common: 4 buttons  (G) ooooo  ooooo (D1) DI of WS2812B array
     + of WS2812B array (VU) ooooo  ooooo (D2) "left" button
                    --- (S3) ooooo  ooooo (D3) "A" button
                    --- (S2) ooooo  ooooo (D4) "B" button
                    --- (S1) ooooo  ooooo (3V) ---
                    --- (SC) ooooo  ooooo (G)  common: 2 buttons
                    --- (SO) ooooo  ooooo (D5) "down" button
                    --- (SK) ooooo  ooooo (D6) "up" button
                speaker  (G) ooooo  ooooo (D7) "right" button
                    --- (3V) ooooo  ooooo (D8) speaker
                    --- (EN) ooooo  ooooo (RX) ---
                    --- (RS) ooooo  ooooo (TX) ---
      wire (for switch)  (G) ooooo  ooooo (G)  ---
    + of battery holder (VI) ooooo  ooooo (3V) ---
                    ---      ooooo  ooooo      ---

Solder the "wire (for switch)" to the middle terminal of the sliding switch, and the wire from the - terminal to one of the other terminals. Put the sliding switch in place at the top of the console.

Insert the NodeMCU board into the breadboard, as indicated in the diagram above.

Using a hot glue gun, attach the battery holder to the base.

Cut a piece of the translucent sheet in the shape of the top cover. Cut out holes for the buttons (but not the display), the sliding switch, and two screws. Cut out the same shape with the clear plastic.

You can draw markings for the buttons and switch on the translucent cover.

Snap the top cover into the top part of the console. Layer the translucent paper and clear plastic on top, and secure with two screws.

Insert the batteries into the holder.

Attach the bottom cover – insert it into the hooks first, then close it.

Your console is now complete.