Maker.io main logo

Pip-Boy 2040 Wrist-Mounted Prop

2022-03-02 | By Adafruit Industries

License: See Original Project 3D Printing Adafruit Feather Raspberry Pi RP2040

Courtesy of Adafruit

Guide by John Park

Overview

Prep your wrist for the apocalypse! Build your own small form-factor, ‎Fallout-inspired Pip-Boy with Feather RP2040 and CircuitPython.‎

pip_1

You can make this prop wrist-computer for cosplay, or for general ‎stylishness all the time. Swap graphic screens on the beautiful round ‎rectangle IPS TFT display with the directional buttons, move the ‎cursor with the joystick, look rad.‎

The demo code is a simple slideshow with navigation controls, but ‎the platform can be used to code your own unique device behavior.‎

Parts

and

  • M4 x 0.7mm thread brass heat-set inserts x8

Build the Pip-Boy 2040‎

Display Circuit

Here's what the circuit looks like when directly wired to the Feather. ‎Don't solder this together just yet, we'll use the Feather Tripler as ‎shown below for the build. ‎

An earlier version of this guide had incorrect wiring; the author ‎humbly apologizes for the errors.‎

circuit_2

This view shows the same wiring (minus the enable switch) but with ‎the screen to the left of the Feather as it will be on the Tripler.‎

view_3

scheme_4

Layout

Here's an initial layout to determine parts placement on the ‎FeatherWing Tripler.‎

layout_5

Tripler

This diagram shows the display wired to the Feather via the Tripler. ‎This illustration has the display off to the side for clarity. On the real ‎board you'll use the header to connect the display to the board with ‎this wiring in place.‎

diagram_6

Here's what it looks like with the display in place but the wiring ‎visible x-ray style.‎

display_7

Non-x-ray version. Note that the display will not overlap the Feather ‎in the final board, thanks to an angled stacking header.‎

display_8

Feather Soldering

A compact way to attach the Feather to the board, while allowing ‎the Joy FeatherWing to mount on top is to solder extra-long pins ‎directly through the Feather to the Tripler as shown here.‎

Then, solder the socket headers to the Joy FeatherWing.‎

soldering_9

soldering_10

soldering_11

Screen Socket Soldering

Use a tall stacking header to create a pluggable socket for the display.‎

This will set the screen at a good height for the build, and you can bend ‎the long headers to use as connection points for the wires on the ‎FeatherWing Tripler.‎

Follow the wiring in the photo as well as the Fritzing diagrams above.‎

Solder in the slide switch as well and wire it to the ground and Enable ‎pins of the Feather.‎

socket_12

socket_13

socket_14

Battery

Use a length of double-stick foam tape to secure the battery to the FeatherWing. This will keep it ‎from rattling around inside your Pip-Boy.‎

Battery_16

Battery_15

Battery_17

Assemble the Pip-Boy Components

Plug in the Joy FeatherWing, and the display.

‎It's now ready for coding and case assembly.‎

assemble_18

assemble_19

assemble_20

Install CircuitPython

CircuitPython is a derivative of MicroPython designed to simplify ‎experimentation and education on low-cost microcontrollers. It ‎makes it easier than ever to get prototyping by requiring no upfront ‎desktop software downloads. Simply copy and edit files on ‎the CIRCUITPY drive to iterate.‎

CircuitPython QuickStart

Follow this step-by-step to quickly get CircuitPython running on your ‎board.‎

Download the latest version of CircuitPython for this board via ‎circuitpython.org

Click the link above to download the latest CircuitPython UF2 file.‎

Save it wherever is convenient for you.‎

file_21

board_22

To enter the bootloader, hold down the BOOT/BOOTSEL ‎button (highlighted in red above), and while continuing to hold it ‎‎(don't let go!), press and release the reset button (highlighted in ‎blue above). Continue to hold the BOOT/BOOTSEL button until the ‎RPI-RP2 drive appears!‎

If the drive does not appear, release all the buttons, and then repeat ‎the process above.‎

You can also start with your board unplugged from USB, press, and ‎hold the BOOTSEL button (highlighted in red above), continue to ‎hold it while plugging it into USB, and wait for the drive to appear ‎before releasing the button.‎

A lot of people end up using charge-only USB cables and it is very ‎frustrating! Make sure you have a USB cable you know is good for ‎data sync.

You will see a new disk drive appear called RPI-RP2.‎‎

Drag the adafruit_circuitpython_etc.uf2 file to RPI-RP2.

drive_23

drive_24

The RPI-RP2 drive will disappear, and a new disk drive called CIRCUITPY will appear.‎

That's it, you're done! :)‎

disk_25

Safe Mode

You want to edit your code.py or modify the files on ‎your CIRCUITPY drive but find that you can't. Perhaps your board ‎has gotten into a state where CIRCUITPY is read-only. You may have ‎turned off the CIRCUITPY drive altogether. Whatever the reason, safe ‎mode can help.‎

Safe mode in CircuitPython does not run any user code on startup ‎and disables auto-reload. This means a few things. First, safe ‎mode bypasses any code in boot.py (where you can ‎set CIRCUITPY read-only or turn it off completely). Second, it does ‎not run the code in code.py. And finally, it does not automatically ‎soft-reload when data is written to the CIRCUITPY drive.‎

Therefore, whatever you may have done to put your board in a non-‎interactive state, safe mode gives you the opportunity to correct it ‎without losing all of the data on the CIRCUITPY drive.‎

Entering Safe Mode in CircuitPython 6.x

This section explains entering safe mode on CircuitPython 6.x.‎

mode_26

To enter safe mode when using CircuitPython 6.x, plug in your board ‎or hit reset (highlighted in red above). Immediately after the board ‎starts up or resets, it waits 700ms. On some boards, the onboard ‎status LED (highlighted in green above) will turn solid yellow during ‎this time. If you press reset during that 700ms, the board will start up ‎in safe mode. It can be difficult to react to the yellow LED, so you ‎may want to think of it simply as a slow double click of the reset ‎button. (Remember, a fast double click of reset enters the ‎bootloader.)‎

Entering Safe Mode in CircuitPython 7.x

This section explains entering safe mode on CircuitPython 7.x.‎

To enter safe mode when using CircuitPython 7.x, plug in your board ‎or hit reset (highlighted in red above). Immediately after the board ‎starts up or resets, it waits 1000ms. On some boards, the onboard ‎status LED (highlighted in green above) will blink yellow during that ‎time. If you press reset during that 1000ms, the board will start up in ‎safe mode. It can be difficult to react to the yellow LED, so you may ‎want to think of it simply as a slow double click of the reset button. ‎‎(Remember, a fast double click of reset enters the bootloader.)‎

In Safe Mode

Once you've entered safe mode successfully in CircuitPython 6.x, the ‎LED will pulse yellow.‎

If you successfully enter safe mode on CircuitPython 7.x, the LED will ‎intermittently blink yellow three times.‎

If you connect to the serial console, you'll find the following message.‎

Copy Code
Auto-reload is off.
Running in safe mode! Not running saved code.

CircuitPython is in safe mode because you pressed the reset button during boot. Press again to exit safe mode.

Press any key to enter the REPL. Use CTRL-D to reload.

You can now edit the contents of the CIRCUITPY drive. ‎Remember, your code will not run until you press the reset button, or ‎unplug and plug in your board, to get out of safe mode.

Flash Resetting UF2‎

If your board ever gets into a really weird state and doesn't even ‎show up as a disk drive when installing CircuitPython, try loading ‎this 'nuke' UF2 which will do a 'deep clean' on your Flash ‎Memory. You will lose all the files on the board, but at least you'll be ‎able to revive it! After loading this UF2, follow the steps above to re-‎install CircuitPython.‎

Download flash erasing "nuke" UF2

Code the Pip-Boy 2040‎

Text Editor

Adafruit recommends using the Mu editor for editing your ‎CircuitPython code. You can get more info in this guide.‎

Alternatively, you can use any text editor that saves simple text files.‎

Download the Project Bundle

Your project will use a specific set of CircuitPython libraries and ‎the code.py file, along with a folder full of image files. To get ‎everything you need, click on the Download Project Bundle link ‎below, and uncompress the .zip file.‎

Drag the contents of the uncompressed bundle directory onto your ‎Feather board's CIRCUITPY drive, replacing any existing files or ‎directories with the same names, and adding any new ones that are ‎necessary.‎

Download Project Bundle

Copy Code
# SPDX-FileCopyrightText: 2021 john park for Adafruit Industries
# SPDX-License-Identifier: MIT
import time
import board
from adafruit_simplemath import map_range
import displayio
from adafruit_seesaw.seesaw import Seesaw
import adafruit_imageload
from adafruit_st7789 import ST7789

displayio.release_displays()

i2c_bus = board.I2C()
ss = Seesaw(i2c_bus)

spi = board.SPI()  # setup for display over SPI
tft_cs = board.D5
tft_dc = board.D6
display_bus = displayio.FourWire(
    spi, command=tft_dc, chip_select=tft_cs, reset=board.D9
)

display = ST7789(display_bus, width=280, height=240, rowstart=20, rotation=270)

screen = displayio.Group()  # Create a Group to hold content
display.show(screen)  # Add it to the Display

# display image
image = displayio.OnDiskBitmap("/img/bootpip0.bmp")
palette = image.pixel_shader
background = displayio.TileGrid(image, pixel_shader=palette)
screen.append(background)

# load cursor on top
cursor_on = True
if cursor_on:
    image, palette = adafruit_imageload.load("/img/cursor_green.bmp")
    palette.make_transparent(0)
    cursor = displayio.TileGrid(image, pixel_shader=palette)
    screen.append(cursor)

    cursor.x = 0  # hide cursor during bootup
    cursor.y = 0

display.show(screen)

boot_file_names = [
    "/img/bootpip0.bmp",
    "/img/bootpip1.bmp",
    "/img/bootpip2.bmp",
    "/img/bootpip3.bmp",
    "/img/bootpip4.bmp",
    "/img/bootpip5.bmp",
    "/img/statpip0.bmp",
]

screenmap = {
    (0): (
        "/img/statpip0.bmp",
        "/img/statpip1.bmp",
        "/img/statpip2.bmp",
        "/img/statpip3.bmp",
        "/img/statpip4.bmp",
        "/img/statpip2.bmp",
        "/img/statpip6.bmp",
        "/img/statpip7.bmp",
        "/img/statpip8.bmp",
    ),
    (1): ("/img/invpip0.bmp", "/img/invpip1.bmp"),
    (2): ("/img/datapip0.bmp", "/img/datapip1.bmp", "/img/datapip2.bmp"),
    (3): ("/img/mappip0.bmp", "/img/mappip1.bmp", "/img/mappip2.bmp"),
    (4): ("/img/radiopip0.bmp", "/img/radiopip1.bmp"),
    (5): ("/img/holopip0.bmp", "/img/holopip1.bmp"),
}

BUTTON_UP = 6  # A is UP
BUTTON_RIGHT = 7  # B is RIGHT
BUTTON_DOWN = 9  # Y is DOWN
BUTTON_LEFT = 10  # X is LEFT
BUTTON_SEL = 14  # SEL button is unused
button_mask = (
    (1 << BUTTON_UP)
    | (1 << BUTTON_RIGHT)
    | (1 << BUTTON_DOWN)
    | (1 << BUTTON_LEFT)
    | (1 << BUTTON_SEL)
)

ss.pin_mode_bulk(button_mask, ss.INPUT_PULLUP)

tab_number = 0
sub_number = 0

def image_switch(direction):  # advance or go back through image list
    # pylint: disable=global-statement
    global tab_number
    # pylint: disable=global-statement
    global sub_number
    # pylint: disable=global-statement
    global image
    # pylint: disable=global-statement
    global palette
    if direction == 0:  # right
        tab_number = (tab_number + 1) % len(screenmap)
    if direction == 1:  # left
        tab_number = (tab_number - 1) % len(screenmap)
    if direction == 2:  # down
        sub_number = (sub_number + 1) % len((screenmap[tab_number]))
    if direction == 3:  # up
        sub_number = (sub_number - 1) % len((screenmap[tab_number]))

    image = displayio.OnDiskBitmap(screenmap[tab_number][sub_number])
    palette = image.pixel_shader
    screen[0] = displayio.TileGrid(image, pixel_shader=palette)


last_joy_x = 0
last_joy_y = 0

#  bootup images
for i in range(len(boot_file_names)):
    image = displayio.OnDiskBitmap(boot_file_names[i])
    palette = image.pixel_shader
    screen[0] = displayio.TileGrid(image, pixel_shader=palette)
    time.sleep(0.1)

while True:
    time.sleep(0.01)
    joy_x = ss.analog_read(2)
    joy_y = ss.analog_read(3)
    if (abs(joy_x - last_joy_x) > 3) or (abs(joy_y - last_joy_y) > 3):
        if cursor_on:
            cursor.x = int(map_range(joy_x, 10, 1023, 0, 264))
            cursor.y = int(map_range(joy_y, 10, 1023, 224, 0))
        last_joy_x = joy_x
        last_joy_y = joy_y

    buttons = ss.digital_read_bulk(button_mask)

    if not buttons & (1 << BUTTON_UP):
        image_switch(3)
        time.sleep(0.15)

    if not buttons & (1 << BUTTON_RIGHT):
        sub_number = 0  # go back to top level screen of tab grouping
        image_switch(0)
        time.sleep(0.15)

    if not buttons & (1 << BUTTON_DOWN):
        image_switch(2)
        time.sleep(0.15)

    if not buttons & (1 << BUTTON_LEFT):
        sub_number = 0
        image_switch(1)
        time.sleep(0.15)

    if not buttons & (1 << BUTTON_SEL):
        print("unused select button")

View on GitHub

Use the Pip-Boy

While this platform can be used to code your own unique behavior, ‎the project example is a simple slideshow with input controls.‎

When the Pip-Boy 2040 boots up it will run through a series of ‎images, then pause. You can use the left and right buttons to get ‎between menus, and the up and down buttons to show the sub-‎menu screens.‎

The joystick will move the cursor around the screen.‎

3D Printed Pip-Boy 2040 Case

case_27

You can build a 3D printed, wrist-mounted case for your Pip-Boy ‎‎2040. A nylon watch strap will allow you to adjust it for a perfect fit -- ‎or you can use Velcro strips to secure it to a costume jacket or rad ‎cyber armor.‎

Model files

Print the Case

Print the three model files (linked above) at 0.2mm layer height and ‎‎18% infill.‎

print_28

Threaded Inserts

For case section assembly, use M4 x 30mm socket-head screws, and M4 x ‎‎0.7mm thread brass heat-set inserts.‎

For more info on using heat-set threaded inserts, see this guide.‎

thread_28

Using your soldering iron, carefully heat and sink the four inserts into the ‎model as shown.‎

iron_29

iron_30

iron_31

iron_32

gaming_33

Case Top

Secure the Joy FeatherWing and the Display to the case top using ‎eight M2.5 x 10mm screws and nuts.‎

top_34

top_35

top_36

top_37

top_38

Middle

Align the boards and press the header pins into the sockets.‎

Then, fit the board into the middle section, making sure to line up ‎the USB-C port.‎

middle_39

middle_40

middle_41

Base

Flip the base so the curved face is down (this is contoured for your ‎arm), with the flat side facing the board.‎

Use the M4 screws to assemble the three sections, screwing them ‎into the threaded inserts.‎

base_42

base_43

base_44

Extra Inserts

You can add four inserts on top of the excess screw length for that ‎post-apocalyptic atom-punk look!‎

inserts_45

Strap

Add your watch strap and you're ready to wear the Pip-Boy 2040.‎

strap_46

Flip the slide switch to power it on.

flip_47

flip_48

flip_49

flip_50

制造商零件编号 5206
GRAPHIC DISPLAY TFT - 1.7"
Adafruit Industries LLC
制造商零件编号 3632
JOY FEATHERWING FOR ALL FEATHERS
Adafruit Industries LLC
制造商零件编号 4884
ADAFRUIT FEATHER RP2040
Adafruit Industries LLC
制造商零件编号 3417
FEATHERWING TRIPLER PROTO BOARD
Adafruit Industries LLC
制造商零件编号 3002
SHORT FEATHER MALE HEADERS - 12-
Adafruit Industries LLC
制造商零件编号 2940
SHORT FEATHER HEADERS KIT - 12-P
Adafruit Industries LLC
制造商零件编号 3299
BLACK NYLON SCREW AND STAND-OFF
Adafruit Industries LLC
Add all DigiKey Parts to Cart
TechForum

Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.

Visit TechForum