Close
0%
0%

PathFinder

PathFinder is a wearable, portable device that stores a map of my hometown, which can be viewed and navigated using D-pad buttons.

Similar projects worth following
Greetings Fellow Travellers, and welcome back. This is PathFinder, a DIY digital map device powered by the UNIHIKER M10, designed to help you find your way. I go on rides in mountain areas near my hometown, and there, I frequently get no signal in some areas. Relying completely on Google Maps is a no-no; sometimes, due to data issues, maps are not loaded completely, and this can be a nightmare if you are stranded. This could all be sorted if we had a physical map and some navigation skills. I don’t have a physical map, but I have made a digital map that is loaded on a UNIHIKER M10 board. We can navigate the map using directional buttons. With this, one can help themselves if they are stranded.

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:

  • Crop a 320×240 viewport from the 2000×2000 map
  • Apply an amber tint
  • Draw CRT grid + POI icons
  • Composite scanline overlay
  • Animate crosshair
  • Draw HUD bars
  • Rotate to landscape
  • Save and push
```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 ......
Read more »

PIP MAP v7.step

step - 12.89 MB - 05/07/2026 at 06:48

Download

SW.stl

Standard Tesselated Geometry - 322.35 kB - 05/07/2026 at 06:48

Download

TOP.stl

Standard Tesselated Geometry - 296.96 kB - 05/07/2026 at 06:48

Download

BASE BODY.stl

Standard Tesselated Geometry - 367.95 kB - 05/07/2026 at 06:48

Download

SHADE.stl

Standard Tesselated Geometry - 61.41 kB - 05/07/2026 at 06:48

Download

View all 7 files

  • 1
    BUTTON BOARD ASSEMBLY PROCESS

    Button board assembly was pretty straightforward.

    We placed all four push buttons in their positions, flipped the board over, and then used a soldering iron to solder all the leads of the push buttons.

  • 2
    POWER BOARD ASSEMBLY PROCESS
    • For the assembly process of this PCB, we use a solder paste dispensing syringe. We apply solder paste to each component pad to begin the Main Circuit Assembly process. Here, we are utilizing Sn/Pb 63/37 solder paste, which has a melting temperature of 190 °C.
    • Next, we pick each SMD component and place it in its correct location.
    • All of the components are then permanently bound to their pads when the entire circuit is set on the reflow hotplate, which heats the PCB to the solder paste melting temperature.
    • Next, we positioned every THT component, including the Type C port and the vertical push button, in their proper location. We place the Type C Port on the top side, but flip the position of the push button and place it on the bottom side. Using a soldering iron, both components are soldered in their position.
  • 3
    POWER SOURCE

    For the power source of this project, we are using a lithium-ion cell, the 14500 form factor 3.7V 600mAh Li-ion cell, to be precise, which comes with a pre-installed PCM circuit. This PCM circuit protects the cell from overcharge and over-discharge, and it even provides short-circuit protection.

    • The cell’s B+ and B- are soldered to our power management board’s battery terminals using a soldering iron.
    • To test the setup, I used a multimeter, pressed the vertical button which turns ON the circuit, and we can see the indicator LED light up.
    • Using the multimeter, we can check the output voltage of the circuit, which measured a stable 5V, meaning the setup is working.

View all 8 instructions

Enjoy this project?

Share

Discussions

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates