Functional Pip-boy 3000 Mk IV from Fallout 4

There have been a few developments I haven't mentioned yet.

The first is I finally found a LCD panel that both should be a perfect fit, and something I can actually drive with an image!

The screen I did want to use only accepts DSI MIPI input, and while in theory the Raspberry Pi can output that signal, the developers an "open source" micro controller, decided to keep the code so that only the two screens they sell can work. All others need to dig into the root coding, and so far after many years I have yet to see a single person implement it successfully.

SPI is a much slower interface, but is also much more compatible, and can be driven by something as simple as an arduino.

The SPI display I found a 4"x4" LCD that is 480x480 pixels, enough to display the text and basic graphics needed for a Pip-Boy.
It should also be much brighter than the VR tweaked screen I was using.

I ordered a couple of them nearly a month ago, but they are on the slow boat from china, so I have no idea when they will arrive.

At the same time I spent over $350 on SparkFun in order to get new development and sensor boards to try to revamp this project. This way I don't have to breadboard every tiny sub-circuit.

I ordered the SparkFun MicroMod with display carrier board. This has a SPI display on board, and I am painfully learning how to get the graphics library to display stuff. So far, I have it displaying just the boot screen text. I am not sure if I want to use the Artemis board or the RP2040 board to drive the thing. The RP2040 is supposed to be much faster, but I am not very familiar with Python, and am much better at coding in C++.

I also ordered the following:
FM Tuner board
Pulse Oximeter and Heart Rate Sensor
Battery charger/ Fuel Gauge
GPIO expander
IR thermal sensor
Motor Driver
9 degree of freedom IMU
A bunch of Qwiic breakouts, splitters and cables

Regarding the question on metal 3D printing: Metal 3D printing is still very expensive. It also still requires lots of supports that have to be manually ground away. It is far from being practical to print out a metal pip-boy body (at least with all the internal structures in place)

However, within the next few months I will have access to a Fuse 1 3D printer. That will make it so that printing out the entire set of parts will drop from $350 to $115. It will at least mean if I need to make any more mechanical changes, they won't be so costly to prototype. The catch is it only makes parts in a dark grey nylon, and some of the parts on the Pip-Boy do need to be white, but there is always paint for that.

All that said, this is still a back burner project for me. I have a few other functional props which may still come first. I need to finish the Dosimeter. I ran into a road block where the MSP430 is very difficult to program without a carrier board, so in-field updates were nearly impossible. I may even report the whole thing over to a more easy to use microcontroller, and simplify some aspects of the design.

I am otherwise spending my free time on Leatherman Tool accessories (and video games). The tool parts sell and help fun the rest of my hobbies. Including ordering all those SparkFun boards. To that end I will soon be revamping my website to be a store, showcase, and blog all in one.
 
There have been a few developments I haven't mentioned yet.

The first is I finally found a LCD panel that both should be a perfect fit, and something I can actually drive with an image!

The screen I did want to use only accepts DSI MIPI input, and while in theory the Raspberry Pi can output that signal, the developers an "open source" micro controller, decided to keep the code so that only the two screens they sell can work. All others need to dig into the root coding, and so far after many years I have yet to see a single person implement it successfully.

SPI is a much slower interface, but is also much more compatible, and can be driven by something as simple as an arduino.

The SPI display I found a 4"x4" LCD that is 480x480 pixels, enough to display the text and basic graphics needed for a Pip-Boy.
It should also be much brighter than the VR tweaked screen I was using.

I ordered a couple of them nearly a month ago, but they are on the slow boat from china, so I have no idea when they will arrive.

At the same time I spent over $350 on SparkFun in order to get new development and sensor boards to try to revamp this project. This way I don't have to breadboard every tiny sub-circuit.

I ordered the SparkFun MicroMod with display carrier board. This has a SPI display on board, and I am painfully learning how to get the graphics library to display stuff. So far, I have it displaying just the boot screen text. I am not sure if I want to use the Artemis board or the RP2040 board to drive the thing. The RP2040 is supposed to be much faster, but I am not very familiar with Python, and am much better at coding in C++.

I also ordered the following:
FM Tuner board
Pulse Oximeter and Heart Rate Sensor
Battery charger/ Fuel Gauge
GPIO expander
IR thermal sensor
Motor Driver
9 degree of freedom IMU
A bunch of Qwiic breakouts, splitters and cables

Regarding the question on metal 3D printing: Metal 3D printing is still very expensive. It also still requires lots of supports that have to be manually ground away. It is far from being practical to print out a metal pip-boy body (at least with all the internal structures in place)

However, within the next few months I will have access to a Fuse 1 3D printer. That will make it so that printing out the entire set of parts will drop from $350 to $115. It will at least mean if I need to make any more mechanical changes, they won't be so costly to prototype. The catch is it only makes parts in a dark grey nylon, and some of the parts on the Pip-Boy do need to be white, but there is always paint for that.

All that said, this is still a back burner project for me. I have a few other functional props which may still come first. I need to finish the Dosimeter. I ran into a road block where the MSP430 is very difficult to program without a carrier board, so in-field updates were nearly impossible. I may even report the whole thing over to a more easy to use microcontroller, and simplify some aspects of the design.

I am otherwise spending my free time on Leatherman Tool accessories (and video games). The tool parts sell and help fun the rest of my hobbies. Including ordering all those SparkFun boards. To that end I will soon be revamping my website to be a store, showcase, and blog all in one.

This is great news! I'm glad you've been able to make progress and I wish you luck with the dosimeter, and looking forward to an update vid on your YT. And personally once I can and it's finished, use a metal printer, as I'd rather'ed keep the components safe for decades/century's then let human error do a dozy to it so it can be a long time pass me down.
 
There have been a few developments I haven't mentioned yet.

The first is I finally found a LCD panel that both should be a perfect fit, and something I can actually drive with an image!

The screen I did want to use only accepts DSI MIPI input, and while in theory the Raspberry Pi can output that signal, the developers an "open source" micro controller, decided to keep the code so that only the two screens they sell can work. All others need to dig into the root coding, and so far after many years I have yet to see a single person implement it successfully.

SPI is a much slower interface, but is also much more compatible, and can be driven by something as simple as an arduino.

The SPI display I found a 4"x4" LCD that is 480x480 pixels, enough to display the text and basic graphics needed for a Pip-Boy.
It should also be much brighter than the VR tweaked screen I was using.

I ordered a couple of them nearly a month ago, but they are on the slow boat from china, so I have no idea when they will arrive.

At the same time I spent over $350 on SparkFun in order to get new development and sensor boards to try to revamp this project. This way I don't have to breadboard every tiny sub-circuit.

I ordered the SparkFun MicroMod with display carrier board. This has a SPI display on board, and I am painfully learning how to get the graphics library to display stuff. So far, I have it displaying just the boot screen text. I am not sure if I want to use the Artemis board or the RP2040 board to drive the thing. The RP2040 is supposed to be much faster, but I am not very familiar with Python, and am much better at coding in C++.

I also ordered the following:
FM Tuner board
Pulse Oximeter and Heart Rate Sensor
Battery charger/ Fuel Gauge
GPIO expander
IR thermal sensor
Motor Driver
9 degree of freedom IMU
A bunch of Qwiic breakouts, splitters and cables

Regarding the question on metal 3D printing: Metal 3D printing is still very expensive. It also still requires lots of supports that have to be manually ground away. It is far from being practical to print out a metal pip-boy body (at least with all the internal structures in place)

However, within the next few months I will have access to a Fuse 1 3D printer. That will make it so that printing out the entire set of parts will drop from $350 to $115. It will at least mean if I need to make any more mechanical changes, they won't be so costly to prototype. The catch is it only makes parts in a dark grey nylon, and some of the parts on the Pip-Boy do need to be white, but there is always paint for that.

All that said, this is still a back burner project for me. I have a few other functional props which may still come first. I need to finish the Dosimeter. I ran into a road block where the MSP430 is very difficult to program without a carrier board, so in-field updates were nearly impossible. I may even report the whole thing over to a more easy to use microcontroller, and simplify some aspects of the design.

I am otherwise spending my free time on Leatherman Tool accessories (and video games). The tool parts sell and help fun the rest of my hobbies. Including ordering all those SparkFun boards. To that end I will soon be revamping my website to be a store, showcase, and blog all in one.
Have you looked at STM chips at all? A few years ago they bought touchgfx and a lot of their higher end chips are supported. Its a WYSIWYG GUI designer that pairs well with their IDE/ecosystem. And lets you edit all the code in C/C++.

I've been meaning to mess around with my Wand Company 2000mkVI for a while now and was going to try out the STM route for electronics.
 
I will take a look. I don't consider myself a great C++ programmer, I can manage with Arduino libraries and code, and write a good state machine, but find I get lost when it gets down to handling some of the lower level stuff. The funny part is the same is true when I try to program in python, I find it to be too unstructured.
 

I did a thing! I managed to learn some Python. (Circuitpython) to be exact.

Over the past few weeks I have spent a little bit of time each day getting the code to the point where I have a workable boot screen. Some aspects of python are easier for me than C++, but some aspects are just as obfuscated and any other programming language. (Especially errors)

First I tried using the SparkFun Artimus board, Arduino code, and their Hyperdisplay library. The Hyperdisplay library is crap. It has one system font, and no support for bitmaps. It was a huge waste of time to even try using it.

I also ordered the MicroMod RP2040 board. It took quite a bit of time to get the screen running, due to me learning Python while eventually figuring out that a single pin was mis-mapped in the board setup (not my fault) that was preventing it from working at all. Finally for some reason my display was inverted but I figured out how to update the LCD initialization code to fix that.

The screen shown in the video is 320x240, and the screen I am still waiting to arrive from China will be 480x480 pixels.
The RP2040 MicroMod board I am using just 265kB of ram, so most of the bitmaps have to be written directly to the screen over SPI.

Ani.jpg


It takes quite of bit of work to make the graphics. Using 7zip: I extracted the SWF files from the Pip-Boy companion app. Then using JPEXS Flash Decompiler: I extracted the SVG graphics files from the SWF files. I process the SVG files in Photoshop. Each one has to be down converted to a small 4-bit bitmap or I will run out of memory.

The animations however can't written directly to the display. Instead I have to make and load a single bitmap. It can then be cycled through sections as sprites. This bitmap does have to fit into the system memory.

Also the memory shown during the boot-up is the actual system memory!

Even things like text take up a large amount of memory. The very first boot image with the numbers flying by had to be truncated down in order for it to be rendered, and sent to the screen fast enough. Too long, and the processor would simply run out of memory while converting the text to a bitmap. I also had to learn to make my own bitmap fonts using the fonts actually used in the companion app.

I wanted to also play audio through the speakers. But I ran into an issue where the processor is just spending too much time driving the display. There was no cycles left over to send out WAV files via PWM. It could play a file okay, but then it would be distorted heavily if anything was happening on screen. So for audio I will need an external audio chip, or I will need to upgrade to a full Raspberry Pi (Which I may end up doing anyways) The audio in the video was faked in a video editor.

If your interested in seeing the code, it is below. Please be kind, it is not optimized and is all just serially written out.
You can see where I tried to get the WAV files playing, and all the memory cleanup I have to do.

I probably won't do much more beyond this until my new display arrives, as it will probably be a pain to get up and running.

Python:
# Imports
import gc
import board
import terminalio
import digitalio
import busio
import time
import displayio
from adafruit_bitmap_font import bitmap_font
from adafruit_display_text import bitmap_label, wrap_text_to_pixels
import adafruit_imageload
import ili9341v
from audiocore import WaveFile
from audiopwmio import PWMAudioOut as AudioOut
from vectorio import Rectangle, VectorShape# 2D Shape library
import adafruit_imageload

# print(gc.mem_free(), "Bytes free at the start")

# Watchdog LED stuff
BLINK_ON_DURATION = 0.5  # How long we want the LED to stay on
BLINK_OFF_DURATION = 0.5  # How long we want the LED to stay off
LAST_BLINK_TIME = -1  # When we last changed the LED state
led = digitalio.DigitalInOut(board.LED1)
led.direction = digitalio.Direction.OUTPUT

#Audio Stuff
SPEAKER = board.PWM1
audio = AudioOut(SPEAKER)

# Display Setup
displayio.release_displays()  # Release any display resources
spi = busio.SPI(board.SCK, MOSI=board.COPI, MISO=board.CIPO)
tft_cs = board.D0
tft_dc = board.D1
tft_reset = board.G2
DISPLAY_WIDTH = 320
DISPLAY_HEIGHT = 240
FPS = 79

display_bus = displayio.FourWire(
    spi,
    command=tft_dc,
    chip_select=tft_cs,
    reset=tft_reset,
    baudrate=100000000
    )

display = ili9341v.ILI9341(
    display_bus,
    width=DISPLAY_WIDTH,
    height=DISPLAY_HEIGHT,
    rotation=0,  # The rotation can be adjusted to match your configuration.
    auto_refresh=True,
    native_frames_per_second=79,
    )

# Colors and fonts
TechMono22 = bitmap_font.load_font("fonts/TechMono-22.pcf")
TechMono12 = bitmap_font.load_font("fonts/TechMono-12.pcf")
RobotoB14 = bitmap_font.load_font("fonts/RobotoB-14.pcf")
Roboto12 = bitmap_font.load_font("fonts/Roboto-12.pcf")
Roboto10 = bitmap_font.load_font("fonts/Roboto-10.pcf")

# Colors
Black = 0x000000
Green = 0x00FF00
Mid_Green = 0x007000
Dark_Green = 0x002000

Four_Color_Palette = displayio.Palette(4)
Four_Color_Palette[0] = Black
Four_Color_Palette[1] = Green
Four_Color_Palette[2] = Mid_Green
Four_Color_Palette[3] = Dark_Green
Four_Color_Palette.make_transparent(0)

group_1 = displayio.Group()  # Make a display group
group_2 = displayio.Group()  # Make a second display group
display.show(group_1) #Start with group 1

print(gc.mem_free(), "Bytes free after display setup screen")

# Boot Screen
#wave_file = open("/wav/UI_PipBoy_BootSequence_A.wav", "rb")
#wave = WaveFile(wave_file)
#audio.play(wave)

text = ("*  1 0 0X0000A4 0x00000000000000000 start memory discovery0 0X0000A4 "
        "0x00000000000000000 1 0 0X0000A4 0x00000000000000000 "
        "start memory discovery 0 0X0000A4 0x00000000000000000 1 0 0x000014 "
        "0x00000000000000000 CPUO starting cell relocation0 0X0000A4 "
        "0x00000000000000000 1 0 0x000009 0x00000000000000000 CPUO launch EFI0 "
        "0X0000A4 0x00000000000000000 1 0 0x000009 0X000000000000E003D CPUO "
        "starting EFI0 0X0000A4 0x00000000000000000 1 0 0X0000A4 "
        "0x00000000000000000 start memory discovery0 0X0000A4 0x00000000000000000 "
        "1 0 0X0000A4 0x00000000000000000 start memory discovery 0 0X0000A4 "
        "relocation 0X0000A4 0x00000000000000000 1 0 0x000009 0x00000000000000000 "
        "1 0 0x000014 0x00000000000000000 CPUO starting cell "
        "relocation0 0X0000A4 0x00000000000000000 1 0 0x000009 "
        "0x00000000000000000 CPUO launch EFI0 0X0000A4 0x00000000000000000 1 0 "
        "0x000009 0X000000000000E003D CPUO starting EFI0 0X0000A4 "
        "0x00000000000000000 "
        )

my_text = bitmap_label.Label(
    TechMono12,
    color=Green,
    text="\n".join(wrap_text_to_pixels(text, DISPLAY_WIDTH, TechMono12)),
)
my_text.y = DISPLAY_HEIGHT
group_1.append(my_text)

# Scroll the group upwards
lines = DISPLAY_HEIGHT
while lines > -320:
    group_1.y = group_1.y - 32
    lines = lines - 32
    display.refresh(minimum_frames_per_second=FPS)

#while audio.playing:
        #pass

# Clean Up
group_1.remove(my_text)
while len(group_1):
    group_1.pop()
del my_text
del text
#audio.stop()
#audio.deinit()
#del wave_file
#del wave
gc.collect()
group_1.x = 0
group_1.y = 0

time.sleep(1)

print(gc.mem_free(), "Bytes free after boot screen")

# OS Text
#wave_file = open("/wav/UI_PipBoy_BootSequence_B.wav", "rb")
#wave = WaveFile(wave_file)
#audio.play(wave)

# Blink the cursor at the start
my_text = bitmap_label.Label(TechMono12, color=Green, text=" ")
my_text.x = 10
my_text.y = 10
group_1.append(my_text)
i = 0
while i < 4:
    my_text.text = "▯"
    time.sleep(0.2)
    my_text.text = " "
    time.sleep(0.2)
    i = i + 1
group_1.remove(my_text)
del my_text
gc.collect()

# Display BIOS style screen
text_array = [
            "************** PIP-05 (R) V7 .1.0.8 **************\n",
            " \n",
            " \n",
            " \n",
            "COPYRIGHT 2075 ROBCO(R)\n",
            "LOADER VI.1\n",
            "EXEC VERSION 41.10\n",
            "264k RAM SYSTEM\n",
            f"{gc.mem_free()} BYTES FREE\n",
            "NO HOLOTAPE FOUND\n",
            "LOAD ROM(1): DEITRIX 303",
            ]
line = 0
y = 10
for text in text_array:
    # BIOS screen
    my_text = "my_text" + str(line)
    my_text = bitmap_label.Label(TechMono12, color=Green, text=" "*len(text))  # Reserve the line of text
    my_text.x = 10
    my_text.y = y
    group_1.append(my_text)
    i = 0
    while i < len(text):
        my_text.text = text[0:i] + "▯"  # Create a cursor that runs
        i = i + 1
    my_text.text = text.strip("▯")  # Remove cursor before moving on
    line = line + 1
    y = y + 16

# Blink the cursor at the very end
i = 0
while i < 6:
    my_text.text = text + "▯"
    time.sleep(0.2)
    text = text.strip("▯")
    my_text.text = text
    time.sleep(0.2)
    i = i + 1

# Scroll the group upwards
lines = DISPLAY_HEIGHT
while lines > -240:
    group_1.y = group_2.y - 30
    lines = lines - 30
    display.refresh(minimum_frames_per_second=FPS)
group_1.remove(my_text)
group_1.x = 0
group_1.y = 0

#while audio.playing:
        #pass

# clean Up
#audio.stop()
#audio.deinit()
#del wave_file
#del wave
while len(group_1):
    group_1.pop()
del my_text
del text_array
gc.collect()

print(gc.mem_free(), "Bytes free after BIOS screen")

# Initiating screen
# Import animation bitmap
vault_boy_bit, vault_boy = adafruit_imageload.load("/gfx/vault_boy_boot/ani2.bmp",
                                                    bitmap=displayio.Bitmap,
                                                    palette=displayio.Palette)
vault_boy_grid = displayio.TileGrid(vault_boy_bit, pixel_shader=vault_boy,
                                     width=1, height=1,
                                     tile_height=102, tile_width=72,
                                     default_tile=0,
                                    x = int(DISPLAY_WIDTH / 2) - 51,
                                    y = int(DISPLAY_HEIGHT / 2) - 70
                                    )

print(gc.mem_free(), "Bytes free after making grid")

# Create text label
my_text = bitmap_label.Label(TechMono22, color=Green, text="INITIATING...")
my_text.anchor_point = (0.5, 0.5)
my_text.anchored_position = (DISPLAY_WIDTH / 2, DISPLAY_HEIGHT -40)
group_1.append(my_text)
group_1.append(vault_boy_grid) # Add to group
vault_boy_grid[0] = 0

print(gc.mem_free(), "Bytes free after making text label")

#wave_file = open("/wav/UI_PipBoy_BootSequence_C.wav", "rb")
#wave = WaveFile(wave_file)
#audio.play(wave)

# Animate!
FRAMES = [0,0,0,0,0,0,0,0,0,0,0,0,1,2,0,0,0,0,0,0,0,0,3,4,5,6,7,7,7,7,7]
G = 0
inc = 1
for i in FRAMES:
    vault_boy_grid[0] = i
    my_text.color = (0,G,0)
    if G <= 32:
        inc = 32
    elif G >= 223:
        inc = -32
    G = G + inc
    time.sleep(0.1)

# Import animation bitmap
vault_boy_bit, vault_boy = adafruit_imageload.load("/gfx/walk/ani.bmp",
                                                    bitmap=displayio.Bitmap,
                                                    palette=displayio.Palette)
vault_boy_grid = displayio.TileGrid(vault_boy_bit, pixel_shader=vault_boy,
                                     width=1, height=1,
                                     tile_height=102, tile_width=50,
                                     default_tile=0,
                                    x = int(DISPLAY_WIDTH / 2) -25,
                                    y = int(DISPLAY_HEIGHT / 2) -68
                                    )
group_2.append(vault_boy_grid) # Add to group

# Rectangle encoding [width,height,x,y,color index] (Top left corner anchored)
shapes = [
    [15,6,153,42,1], # Health bar 1
    [15,6,203,84,1], # Health bar 2
    [15,6,203,139,1], # Health bar 3
    [15,6,153,157,1], # Health bar 4
    [15,6,101,139,1], # Health bar 5
    [15,6,101,84,1], # Health bar 6
    [2,3,12,14,1], # Top Line 1
    [38,2,12,12,1], # Top Line 2
    [2,11,48,1,1], # Top Line 3
    [2,2,50,1,1], # Top Line 4
    [2,2,86,1,1], # Top Line 5
    [2,11,88,1,1], # Top Line 6
    [220,2,88,12,1], # Top Line 7
    [2,3,306,14,1], # Top Line 8
    [53,15,13,206,2], # Stimpak background
    [56,15,74,206,2], # Radaway background
    [72,17,12,223,3], # HP background
    [148,17,86,223,3], # Level background
    [103,8,126,228,2], # Level bar surround
    [99,4,128,230,3], # Level bar dark
    [52,4,128,230,1], # Level bar progress
    [72,17,236,223,3], # AP background
    [27,25,95,172,2], # Gun background
    [15,25,124,172,2], # Ammo background
    [27,25,147,172,2], # Helmet background
    [15,25,175,172,2], # Armor background
    [15,25,193,172,2], # Energy background
    [15,25,210,172,2], # Radiation background
    ]

for shape in shapes:
    my_pallet = displayio.Palette(2) # Make a single color palette
    my_pallet[1] =  Four_Color_Palette[shape[4]] # Set color
    my_shape = VectorShape(shape=Rectangle(width=shape[0],height=shape[1]),x=shape[2],y=shape[3],pixel_shader=my_pallet) #Move and color rectangle
    group_2.append(my_shape) #Add rectangle to the group

# Load Small Icons

# Bitmap encloding [x,y,file] (Top left corner anchored)
bitmaps = [
    [98,178,"/gfx/Handgun.bmp"], # Handgun
    [150,178,"/gfx/Helmet.bmp"], #Reticle
    [128,174,"/gfx/Reticle.bmp"], #Helmet
    [181,174,"/gfx/Shield.bmp"], #Shield
    [198,174,"/gfx/Bolt.bmp"], #Bolt
    [213,174,"/gfx/Radiation.bmp"], #Bolt
    ]
    
for image in bitmaps:
    bitmap, palette = adafruit_imageload.load(image[2],bitmap=displayio.Bitmap,palette=displayio.Palette)
    bitmap_tile = displayio.TileGrid(bitmap, pixel_shader=Four_Color_Palette) #Assign bitmap to TileGrid
    bitmap_tile.x = image[0] #Change location of TileGrid
    bitmap_tile.y = image[1]
    group_2.append(bitmap_tile) #Add tile_grid to group
    display.refresh(target_frames_per_second=FPS) # Wait for the image to load

# Load Text

#Label encoding: [Anchor X, Anchor Y, X, Y, Font, Color, Text]
labels = [
    [0.5,0.5,70,4,RobotoB14,Green,"STAT"],
    [0.5,0.5,112,4,RobotoB14,Green,"INV"],
    [0.5,0.5,154,4,RobotoB14,Green,"DATA"],
    [0.5,0.5,198,4,RobotoB14,Green,"MAP"],
    [0.5,0.5,245,4,RobotoB14,Green,"RADIO"],
    [0.5,0.5,68,23,Roboto12,Green,"STATUS"],
    [0.5,0.5,114,23,Roboto12,Mid_Green,"SPECIAL"],
    [0.5,0.5,158,23,Roboto12,Mid_Green,"PERKS"],
    [0.5,0.5,131,191,Roboto10,Green,"18"], # Ammo
    [0.5,0.5,182,191,Roboto10,Green,"10"], # Armor
    [0.5,0.5,200,191,Roboto10,Green,"20"], # Energy
    [0.5,0.5,217,191,Roboto10,Green,"10"], # Rads
    [0.5,0.5,40,214,Roboto10,Dark_Green,"STIMPAK (0)"],
    [0.5,0.5,102,214,Roboto10,Dark_Green,"RADAWAY (0)"],
    [0.5,0.5,48,231,RobotoB14,Green,"HP 115/115"],
    [0.5,0.5,105,231,Roboto10,Green,"LEVEL 50"],
    [0.5,0.5,279,231,RobotoB14,Green,"AP 90/90"],
    ]

for label in labels:
    my_text = bitmap_label.Label(label[4], color=label[5], text=label[6])
    my_text.anchor_point = (label[0],label[1])
    my_text.anchored_position = (label[2],label[3])
    group_2.append(my_text)

# Glitch the GUI at load a bit
with open("/gfx/Static_Screen2.bmp", "rb") as bitmap_file:
    bitmap = displayio.OnDiskBitmap(bitmap_file)
    bitmap_tile = displayio.TileGrid(bitmap, pixel_shader=displayio.ColorConverter())
    bitmap_tile.y = 0
    group_1.append(bitmap_tile)
    # Wait for the image to load.
    display.refresh(target_frames_per_second=FPS)
    time.sleep(0.05)
    bitmap_tile.y = 160
    time.sleep(0.05)
    group_1.remove(bitmap_tile)

display.show(group_2)

# Cleanup group 1
while len(group_1):
    group_1.pop()
gc.collect()


# Animate!
FRAMES = [0,1,2,3,4,5,6,7]
while True:
    for frame in FRAMES:
        vault_boy_grid[0] = frame
        time.sleep(0.15)

time.sleep(60)
group_2.remove(my_shape)
while len(group_2):
    group_2.pop()
gc.collect()
time.sleep(5)

print(gc.mem_free(), "Bytes free in the end")

#Blink LED forever
while True:
    now = time.monotonic()  # Store the current time to refer to later.
    if not led.value:
        if now >= LAST_BLINK_TIME + BLINK_OFF_DURATION:  # Is it time to turn on?
            led.value = True
            LAST_BLINK_TIME = now
    if led.value:
        if now >= LAST_BLINK_TIME + BLINK_ON_DURATION:  # Is it time to turn off?
            led.value = False
            LAST_BLINK_TIME = now
 
Last edited:
No shade thrown here. Programming languages are a closed book to me, beyond the most BASIC. As usual, I am in awe of your will and ability to learn. I'm still banging my head against Blender in a desperate bid to join the 21st century.
 
This is fascinating to read. I worked with teslabe on a Pip-boy retrofit and did this exact same thing. There's a bunch more you can do with some tools to edit the Unity files in the app too. There you can extract all the 70 some-odd audio files.
 
I extracted the audio files from the full game years ago. About 300 files, including all the radio stations. When I did the video I actually had to edit them a bit as the timing wasn't exactly the same of course.
 
I have updated the python code in the post above. Before I was faking much of the final screen with a bitmap, now it is all generated with text and rectangles. That way with more code the values or graphics could be updated if needed.
 
*&^$%!!!!! These displays are ticking me off!

So the display I ordered almost two months ago finally arrived. I also ordered a breakout board and 60 pin 0.5mm pitched connector.

I spent about an hour just soldering the tiny pins. I had to flood solder, and then wick as I don't have a micro-soldering station and scope at home.
After verifying each of the 60 pins was both connected, and not shorted, I proceeded to wire up the display.

When I went to try powering the backlight, way too much current was being drawn... the problem: The breakout board is numbered backwards!
Hopefully I didn't just fry one of the two displays.

I finally get it all wired up to a Raspberry Pi on the SPI lines. That is when I finally figure out, the display is not SPI exclusive. It is SPI+RGB. I didn't read the datasheet carefully enough. It uses SPI for control, but DSI RGB for the actual video data. You can wire a DSI display to a Pi, if you want to use up every single pin on the Pi. I may try seeing if I can wire it for 8-bit mode, which should require fewer lines, maybe the display will even work with just green connected. After all, I only need like 4 to 6 shades of green for the Pip-Boy.

The only driver I have found for this chipset was written in Rust, a programming language I never even heard of. And even then, it would have to be tweaked for 8-bits.

If only the damn Pi foundation would document the MIPI display interface, it would make wiring modern high res displays much easier.

All that, and I also found out that there was a 4" 720x720 ready to use display on the market last year, the HyperDisplay 4.0 Square. It uses most the pins, but keeps i2c open, so it may be the next one I try out (if it ever comes back onto the market)
 
51171386536_3eaa4cec63_h.jpg


How about a new 4K rendering to get everyone back into the mood to read long overly detailed design posts!

--------------------

51171621633_1a87a880fe_h.jpg


So I spent a few hours adding the HyperPixel 4.0 Square to the CAD design. That led me to make quite a few other changes.

--------------------

51171621623_81da2877fa_h.jpg


The issue is thickness. Once you stack the screen driver board, my custom PCB, and the Raspberry pi, I start running out of space fast.
Even with the headers soldered on backwards to save a precious 2.54mm each. So what I will do is offset the Raspberry pi and re-route the HyperPixel header.

--------------------

51172512045_7a95c33f3d_h.jpg


The place I am out of space the most is near the top of the screen, where all Holotape eject mechanisms are located.
So currently I have the screen rotated 90 degrees. Not an issue for the GUI as the image will be square, however it changed a key aspect of the Pip-Boy, the direction of the sub-pixels.

You see the horizontal lines you see in the Pip-Boy in-game are simulating the scan lines that you see on a CRT screen. However when you render just one color on a RGB display you get a similar effect due to the Red and Blue pixels being off, leaving a 2x gap between green pixels.

Take a look at my GUI test video. You will see those horizontal lines in the video there are an artifact of the display and which way the pixels are oriented. They look almost exactly how the screen looks in-game!

The zoomed in image above is a rendering showing what the display would look like with 720x720 pixels. I first blew up the image by 3x and then overlayed the sub-pixel pattern in photoshop.

Here you can see now that the sub-pixels run vertical. A subtle change, but one that still alters the final look. Since I am now re-routing the Pi Zero location, I may go back and see if I can reorient the display back to vertical.

--------------------

51170719232_98e6f55c1c_h.jpg


While I am updating the design, one thing that bothered me was the Holotape release mechanism. It was too complicated, and finicky to put together.

--------------------

51171387201_d990f5f02d_h.jpg


So I completely redesigned it. (About four times before settling on this design)

I made it so that the eject button is directly connected to the metal rod that runs sideways. I then added hooks to the caddy that grab the rod. There are of course still springs that return the rod and eject back to the start. I also added a second speed limiter for that buttery smooth opening effect.

--------------------

51172180414_e9d112da01_h.jpg


Another thing that has been bothering me.... the Holotapes. The whole faking it thing where the tape didn't actually carry any data. So lets fix that too.

--------------------

51172489565_a475a52563_h.jpg


After playing with the Trinket M0 for another project I was impressed by the tiny size compared to the processing power.

The catch was powering the thing. I found a 105mAh rechargeable battery on Adafruit, and a tiny charging PCB to go with it.

Getting the trinket to sip power will be a bit of a challenge, but not impossible, and I could always switch to Arduino coding for even lower power. To save on power, I added a hard switch connected to the Holotape spindle.

--------------------

51170719812_2833d3bb1e_h.jpg

The Holotapes will now be able to both send and receive data over 38KHz Infrared. With the goal of being able to save a short audio clip or log file to the tape and receive it upon insertion into the Pip-Boy. The Trinket M0 has 256KB of flash, enough for plenty of text a short clip.
--------------------
51171387176_63fb931514_h.jpg
The front label will not he a thin piece of spring steel. This will cover the USB jack on the Trinket. This will be used to re-charge the battery inside the holotape, and re-program the Trinket M0 inside.

The cool part is I can prototype the Trinket PCB with off the shelf parts and mabye get at least one circuit on this project totally complete.
I may also look at using the Trinket inside the Vault-Tec Dosimeter, and Radiation King Radio. I was going to use a MSP430 for those projects, but have run into issues with their ability to be reprogrammed in-circuit.
--------------------
Also, this update doesn't guarantee more progress to follow right away, so just hit the watch button as I work on person projects sporadically.
 
Last edited:
Progress is progress. Had you given any thought to how to reword the game holotapes? There's no write-on-label inset in those shells, as well as the consideration of a wraparound label on the case. For those, d'you still want to have something like an RFID chip that tells the Pip-Boy which game it should play from internal memory? 'Cause I also think the Pip-Boy games are a bit more than the onboard capacity of your new recordable holotapes.

That aside, this new iteration is wonderful for being able to record personal journal entries or make notes on interesting things we encountered in our travels of the Wasteland...

You have dumped so much time and energy into the screen issue, it absolutely astounds me. I, too, keep sporadically looking around to see what new and interesting componentry might have come along that would suit, but you've been finding better things recently than I have.

You have been a massive inspiration, though. I've been painfully teaching myself Blender via YouTube videos. One of the things I hope to do with it is see if I can get anything into a New Vegas-style Pip-Boy. I have doubts. *lol* I still prefer this one, but I like wardrobe options. :) As always, standing by and looking forward to whatever you do whenever you do it. You never disappoint. It is always, at the very least, interesting and educational.
 
I have thought about trying to wire up one of the steel pins, and the screws accessible on the outisde to the 5V lines. That way you could charge it by putting it on a dock with some pogo pins. Otherwise, I have no plans to integrate any of the min-games into the GUI. I am now studying the PiBoy code found on GitHub. It was originally made as a demo for the Adafruit Pip-Boy project, and looks to be a good start.
 
Have you considered the Pi Compute Module? The Pi 4 version switched from a SODIMM slot to low-profile mezzanine connectors. They're more difficult to solder than the header pins, but it would take up less vertical space, and you might have other screen options with the additional pins that are exposed.
 
Earlier in the design I actually had the compute module (The SODIMM version), as it had the DSI video lanes on the connector instead of a secondary ribbon cable. I find it funny that the Pi Foundation retired their "long term embedded" product so soon, replacing it with the newer version. The reality is until they document the DSI interface so anyone can write a driver (That isn't a display engineer) then there is no need to go to the compute module. I wouldn't try to solder those connectors (the LCD one I just did was pain enough), I would have to get a board with the SMT parts pre-populated to use it.

-------------------
Wavy Gear updates!

j1fQ9w2.gif


In my effort to make the geared down radio knob have as high of a turn ratio as possible I ended with eight gears in a nested form. This seemed clever and compact however, it also added something I didn't account for: Backlash. You see normal gears have a bit of slack between the gears. This isn't much of an issue if you never change the direction of the gears. And by having four separate gearing interfaces I ended up with a lot of backlash. The result is that there is a lag between when you move the knob and when the needle starts moving. It is just no good at precisely tuning a radio.

------

g9BZIPw.gif


The solution: Wavy gears (and less gears). Helical cut gears are often use to combat backlash. Because I am 3D printing these gears I can go a step further. I am not sure what to even call these. Symmetrical-half-helical-wavy-gears or something like that.

Now there are only two gear interfaces, and regardless of which direction they turn they should stay in perfect sync. And I still managed to keep a gear ratio of 1:4 between the knob and the needle.

-----

To make the profile for these I went back to the website I used many years ago: GearGenerator.com. They now charge a small fee, but the website makes it easy to play with all sorts of gearing combinations.

At first I was going to do a planetary gear, as those have less backlash and have huge ratios. But there were two issues: 1) The gears would be too small to 3D print even on a SLS machine. 2) Planetary gears invert the direction of the output.

I probably tried a dozen combinations of gears before settling on this 7-13-6-14 combination.
 
Wow! A bunch of cool updates! You mentioned you've pulled a lot of images and audio files from the app. Would you be willing to post that somewhere? I've been planning at taking a crack at making a GUI for the Wand Company pipboy for a while now.

Also I feel your pain on searching for a display. I've been trying to find the right one for that Wand Company pipboy for a couple years now. those 720x720 displays look interesting. Not big enough in active area but close enough, and high res.
 

Your message may be considered spam for the following reasons:

If you wish to reply despite these issues, check the box below before replying.
Be aware that malicious compliance may result in more severe penalties.
Back
Top