For testing this out, I went to a remote area in the mountains near my hometown and used this map to navigate a road I had never been to before. I followed the map and was able to find my way without any issues.

This article covers the complete build process of this project, from the construction process of PCBs to code and device assembly, so let’s get started with the build.

HARDWARE-UNIHIKER M1

The star of our project is the UNIHIKER M10, which is a single-board computer that is quite unique. It comes with an onboard 2.8-inch touch display and has Wi-Fi and Bluetooth built in.

The thing that makes this device truly unique is the processor and co-processor. It is powered by an RK3308 ARM 64-bit processor, which has 4 cores and is clocked at 1.2GHz. It has 512MB DDR3 RAM and comes with an onboard 16GB eMMC chip.

For controlling GPIOs, it has a co-processor, the GD32VF103C8T6. The name might be long, but it’s a RISC-V processor clocked at 108MHz, with 64KB flash and 32KB SRAM.

The CPU cannot directly control GPIOs, so a co-processor has been added, which we can control using Python. The system controls the co-processor using the PinPong library.

The board is made by DFRobot, and you can check out more details about this board through their well-documented wiki page.

https://www.unihiker.com/products/m10

MAP UI BUILD

The UNIHIKER M10 runs full Debian Linux. Everything on it, including the screen, GPIO pins, and sensors, is controlled via Python, so there’s no Arduino IDE support and, sadly, no C.

For the map, I took inspiration from the Fallout: New Vegas Pip-Boy 3000 interface, which features an amber tint. Normal maps are boring, and as a Fallout fan, it was my duty to create a map inspired by the Pip-Boy.

Two libraries are used. The first is the UNIHIKER driver, which controls the built-in 240×320 display. Then there’s the PinPong library, which is used to read and write GPIO pins.

Displaying Frames

We first create the image widget once and just update its file on every frame.

gui = GUI()# Create oncedisplay_img = gui.draw_image(x=0, y=0, image="/tmp/frame.png")# Every frame: save new image, then update the same widgetframe.save("/tmp/frame.png")display_img.config(image="/tmp/frame.png")

Rendering with Pillow

All graphics are composed in memory using Pillow — no framebuffer, no pygame. Each frame goes through:

```pythonfrom PIL import Image, ImageDrawviewport = self._map.crop((x0, y0, x0 + VIEWPORT_W, y0 + VIEWPORT_H))frame    = viewport.convert("RGBA")draw     = ImageDraw.Draw(frame)```

Amber Tint

The map loads as normal RGB. To get the Pip-Boy phosphor look, the Red channel is used as the brightness source for all three channels:

r, g, b = img.split()img = Image.merge("RGB", (    r,    r.point(lambda v: int(v * 0.71)),   # amber green    r.point(lambda v: int(v * 0.26)),   # amber blue))Ratios `(1.0, 0.71, 0.26)` match the target amber `(255, 182, 66)`.

Physical Buttons

Buttons are wired to GPIO pins with internal pull-up resistors. When pressed, they pull the pin LOW, so a reading of `0` means "pressed".

from pinpong.board import Board, PinBoard("unihiker").begin()btn_up = Pin(Pin.P3, Pin.IN, Pin.PULLUP)if btn_up.read_digital() == 0:    move_y = -PAN_SPEEDif not hasattr(Pin, "PULLUP") and hasattr(Pin, "PULL_UP"):    Pin.PULLUP = Pin.PULL_UP

Landscape on a Portrait Screen

The physical display is 240×320 (portrait). I draw into a 320×240 canvas (landscape) and rotate every frame before saving:

frame = frame.rotate(-90, expand=True)# result: 240×320 — fills the screen when held sideways

The Render Loop

Standard fixed-timestep loop — sleep for whatever time is left in the frame budget.

target_fps = 15frame_time = 1.0 / target_fpswhile True: t0 = time.time() # ... read buttons, render, push frame ... sleep_t = frame_time - (time.time() - t0) if sleep_t > 0: time.sleep(sleep_t)

Libraries

| Library | Install | Purpose ||---|---|---|| `pillow` | `pip install pillow` | All image rendering || `unihiker` | pre-installed | Push frames to the screen || `pinpong` | pre-installed | GPIO buttons and buzzer || `math`, `time`, `os` | standard library | Animation, timing, file checks |

UNIHIKER POWER INPUT

Next, after configuring the map setup, we tackled the UNIHIKER power input problem. The UNIHIKER M10 uses a USB Type-C port as a power input, which provides a stable 5V for the setup to work. The issue with this board is that there is no onboard battery connector.

Near the Type-C port, we find two terminals. The first terminal is connected to the USB Type-C port’s VCC, and the other terminal is connected to GND.

By providing 5V to these two terminals, we can power this board, but for that, we need a stable power source.

3D DESIGN

For this project, my idea was to make a device that has a screen with four buttons next to it, inspired by the cyberpunk genre, where there is a strong emphasis on displays, and the form is boxy and symmetrical.

To enhance the design, I added a screen shade part, which also serves a practical purpose. When the device is used outdoors, it provides shade to the display, making it easier to see.

I also added a large knob, which isn’t a practical or functional part and was included purely for aesthetic purposes. Additionally, I added one more part on the front side for aesthetics.

By adding different parts, my goal was to use three colored filaments, red, white, and black, to 3D print the components, creating a great color combination.

Inside the model, there is a proper circuit layout, including the button board. Each circuit has its own mounting screw bosses, where we place the boards and secure them using M2 screws.

We also added openings on one side of the device to access the USB port of the UNIHIKER M10 if we want to reprogram the device. Openings were also added for accessing the USB port of the power module and the on/off push button.

For attaching an ID card strap, we added a hook-like part on one end, where the strap can be connected.

3D PRINTED PARTS

Once the model was finalized, we 3D printed all the parts using Hyper PLA with the same settings, which included a 0.2mm layer height, 25% infill, and tree supports with a 0.3mm Z distance.

The front body was printed with white Hyper PLA. The back body, the shade part, and the aesthetic part were all printed in black Hyper PLA.

Red Hyper PLA was used to print the four switches and the knob part.

PCB DESIGN- POWER BOARD

Here, we are reusing the circuit used in my portable display project.

In this circuit, the IP5306 Power Management IC draws a consistent 5V/2A from a 3.7V Li-ion battery. Its high-cut and low-cut characteristics prevent overcharging and overdischarging of the cell.

The charging port we're using is a Type C through-hole port that connects to the IC charging port. Along with the charging port and GND, we included a 10uF filter capacitor and a 10uF capacitor combination with a 2Ohm resistor.

Additionally, four LEDs, which will serve as battery-full indication LEDs, are added to the IC's LED port.

A push button has been added to turn this device on and off.

Additionally, we added two extra filter capacitors to the IC and GND outputs.

PCB DESIGN- BUTTON BOARD

Here’s the second PCB we used in our project, the Button Board.

Here, we have added four 4×4mm push buttons on a 20×20mm board. The placement of each button and the mounting holes are all positioned according to the 3D model.

A CON5 connector is also added. The first pin is connected to GND, and the remaining four terminals are connected to each button pin. Through this connector, we will later pair the UNIHIKER M10 with our button board.

BTW, we are reusing this PCB from a previous project, which you can check out from the link below.

https://www.hackster.io/Arnov_Sharma_makes/hitpad-6182e1

PCBWAY SERVICE

After finalizing the design, I generated the PCB Gerber files and sent them to PCBWay for fabrication

Two Orders were placed, one for the button board, which we selected to be made in Purple Solder mask with white silkscreen, and a Power Board order was placed for Blue soldermask with white silkscreen.

After placing the order, PCBs arrived within a week, which was super fast, and the quality of the boards was also high.

Over the past ten years, PCBWay has built a strong reputation for providing reliable PCB manufacturing and assembly services, becoming a go-to choice for engineers and makers worldwide.

Honestly, if you’re making custom PCBs and not checking out PCBWay, you’re just making things harder for yourself. They also offer CNC machining and 3D printing services