Tangara Build

13 January 2026 · Updated 26 January 2026

projecthardwarediymusic-player

Build log for Tangara, an open-source portable music player with an iPod-style form factor. ESP32 brain, 1.8” TFT display, capacitive touchwheel, quality DAC. Fully open hardware, open firmware.

Build Approach

Option 1: Order assembled PCBs - The SMD components are too small for hand soldering without specialized equipment. Order PCBs with assembly service from JLCPCB or PCBWay, then do final assembly.

Components Needed

PCBs (2 boards)

BoardDescription
MainboardESP32, power management, DAC, amp, SD card
FaceplateDisplay, touchwheel sensor, haptic motor

Key Electronic Components

ComponentPartFunction
Main MCUESP32-WROVER-EPrimary processor
Co-processorATSAMD21E18Power management, USB
DACCirrus Logic WM8523Audio conversion
AmplifierTI INA1620Headphone driver
Touch ControllerAT42QT2120Capacitive touchwheel
GPIO ExpanderPCA8575Button/control interface
Haptic DriverDRV2605LDGSVibration feedback
Display1.8” ST7735 TFT160x128, 18-bit color

3D Printed Parts

  • Front case half
  • Back case half
  • Touchwheel cover
  • Switch cover
  • Buttons

Case files are in FreeCAD format in the tangara-hw repository.

Other Parts to Source

  • Battery: 2200mAh LiPo with 3-pin JST connector
  • Ribbon cable (connects faceplate to mainboard)
  • SD card: Standard SDXC (up to 2TB supported)
  • Standoffs and screws

Specifications

Audio

  • 3.5mm headphone output: 200mW @ 32 ohms
  • 16-bit audio at 44.1/48 kHz (DAC supports up to 24-bit/192kHz)
  • Formats: MP3, FLAC, Opus, Vorbis
  • Bluetooth audio (SBC codec)

Power

  • 2200mAh battery
  • 4mA standby current
  • ~120mA active current
  • USB Type-C charging

Display & Input

  • 1.8” TFT, 160x128, 18-bit color
  • 1.6” diameter capacitive touchwheel
  • 2 side buttons, 1 lock switch
  • ERM haptic feedback motor

Build Steps

  1. Download design files from tangara-hw repository
  2. Upload Gerbers + BOM to PCB fab for assembly service
  3. Order PCBs with PCBA service
  4. 3D print case parts
  5. Source battery, SD card, mechanical parts
  6. Final assembly (ribbon cables, battery - no soldering)
  7. Flash firmware
  8. Test and enjoy

Resources

Design Files

Documentation

PCB Fabrication

  • JLCPCB - PCB + assembly service
  • PCBWay - PCB + assembly service

Purchase Pre-built

Estimated Cost

  • DIY kits at events: ~€100
  • Self-ordered PCBs with assembly: $150-250+ (depends on quantities)

Orders

DigiKey Order #96776820 (2026-01-12)

  • ESP32-WROVER-DEVKIT-LIPO - $16.75
  • SparkFun Logic Analyzer 24MHz/8ch - $43.23
  • Chip Quik Flux Pen - $12.57
  • Total: $72.55 AUD

Waveshare Order #260113-024824-E0 (2026-01-13)

  • 1.54inch E-Ink display module (SKU 12955) - $9.99 USD
  • Shipping (UBI) - $5.00 USD
  • Total: $14.99 USD

Notes

  • Most PCBA fabs can handle these boards
  • Major components are standard parts
  • Case designed for both CNC and 3D printing
  • Licensed under CERN Open Hardware License

Custom E-Paper Faceplate Mod

Considering replacing the TFT display with a Waveshare e-paper display for improved battery life and outdoor readability.

Display Comparison

SpecStock TFTWaveshare 1.54”Waveshare 2.13”
Size1.8”1.54”2.13”
Resolution160x128200x200250x122
Colors18-bit (262k)B/W onlyB/W only
Full RefreshInstant2s2s
Partial RefreshN/A0.3s~0.3s
Standby PowerContinuous draw<0.017mW<0.01uA
InterfaceSPI (ST7735)SPISPI
PriceIncluded~$10~$12

Candidate Displays

Waveshare 1.54” e-Paper Module (Best fit)

  • Resolution: 200x200 pixels (higher than stock!)
  • Display area: 27.6mm × 27.6mm
  • Module size: 48mm × 33mm
  • Partial refresh: 0.3s (usable for UI updates)
  • Full refresh: 2s
  • Interface: SPI (VCC, GND, DIN, CLK, CS, DC, RST, BUSY)
  • Voltage: 3.3V/5V compatible
  • Cost: ~$10
  • Product page

Waveshare 2.13” e-Paper HAT V4

  • Resolution: 250x122 pixels
  • Display area: 48.55mm × 23.71mm (wider aspect ratio)
  • Supports fast refresh mode
  • Might require case modifications due to size

Trade-offs

Advantages

  • Massively better battery life - Display draws almost nothing when static
  • Sunlight readable - No backlight needed, works great outdoors
  • Always-on display - Shows info even when “off”
  • Retro aesthetic - Unique look, like a Kindle music player

Challenges

  • No color - Album art would be B/W or dithered
  • Slow updates - Progress bars would need rethinking
  • Refresh limitations - Can’t do partial refresh indefinitely (need periodic full refresh)
  • Ghosting - Previous images can leave artifacts
  • Firmware changes required - Display driver needs to be rewritten

UI Considerations for E-Paper

The 0.3s partial refresh is usable but requires UI rethinking:

ElementStock TFTE-Paper Approach
Progress barSmooth animationUpdate every 5-10 seconds, or use text time display
Track changeInstant0.3s partial update (acceptable)
Scrolling textSmoothPaginated or static
Album artFull colorDithered B/W, or skip entirely
Volume indicatorInstantFlash briefly, return to main view
VisualizerReal-timeNot feasible - skip

Hardware Modifications Required

Custom Faceplate PCB

The stock faceplate connects to mainboard via ribbon cable. Would need to design a new faceplate PCB with:

  • E-paper display connector (different pinout from ST7735)
  • Same ribbon cable interface to mainboard
  • Touchwheel and haptic motor connections (can reuse)
  • Possibly different mounting holes for display

Pin Mapping (ESP32 to E-Paper)

Stock ST7735 uses these pins (from tangara docs):

  • Data/Command: GPIO33
  • PWM Brightness: GPIO32 (not needed for e-paper)
  • SPI bus shared

E-paper needs:

  • DIN (MOSI) - can share SPI bus
  • CLK (SCK) - can share SPI bus
  • CS - needs dedicated GPIO
  • DC - can reuse GPIO33
  • RST - needs GPIO
  • BUSY - needs GPIO (for checking refresh status)

Firmware Changes

Would need to:

  1. Replace ST7735 driver with e-paper driver (GxEPD2 library or similar)
  2. Implement partial refresh strategy
  3. Redesign UI for slow refresh constraints
  4. Add periodic full-refresh logic

Implementation Approach

  1. Order Waveshare 1.54” module for prototyping
  2. Test with ESP32 dev board first (before committing to custom PCB)
  3. Port display driver to Tangara firmware
  4. Design minimal UI that works with 0.3s refresh
  5. Design custom faceplate PCB in KiCad
  6. 3D print modified case front (adjust for display dimensions)
  7. Order custom faceplate PCB
  8. Integrate and test

Resources

E-Paper

Libraries

Important Notes

  • After several partial refreshes, must do full refresh to prevent permanent ghosting
  • Recommended: Full refresh every 30 partial updates or every 24 hours
  • Do not leave display powered on without sleep mode - can damage panel

Firmware Analysis

Analysis of tangara-fw repository to understand display driver and UI architecture.

Architecture Overview

tangara-fw/
├── src/drivers/
│   ├── display.cpp      # Display driver, LVGL integration
│   └── display_init.cpp # ST7735 initialization sequence
├── lib/
│   ├── lvgl/            # LVGL graphics library
│   └── luavgl/          # Lua bindings for LVGL
└── lua/                 # UI code (Lua scripts)
    ├── playing.lua      # Now-playing screen
    ├── widgets.lua      # Reusable UI components
    ├── main_menu.lua    # Main menu
    ├── theme_dark.lua   # Dark theme
    ├── theme_light.lua  # Light theme
    └── ...

Display Driver (src/drivers/)

Current controller: ST7735 (not ST7789 as some docs say)

  • Resolution: 160×128
  • Color: 16-bit RGB565
  • Interface: SPI @ 40MHz

Key GPIOs: | Function | GPIO | |----------|------| | Data/Command | 33 | | Backlight PWM | 32 | | Chip Select | 22 |

LVGL Buffer: 2,048 bytes (1/10th screen)

Key functions in display.cpp:

  • FlushDataCallback() - Called by LVGL to push pixels
  • Handles byte-swapping for RGB565
  • Manages SD card mux (shared SPI bus)
  • PWM backlight control with power curve

UI Layer (Lua + LVGL)

The UI is written in Lua using LVGL via luavgl bindings. This is actually good news for the e-paper mod - Lua is easier to modify than C++.

Key files to modify:

FilePurposeE-Paper Impact
playing.luaNow-playing screenProgress bar updates, album art
widgets.luaStatusBar, listsBattery animation, scroll behavior
theme_*.luaVisual themesColors → B/W conversion

Now-Playing Screen (playing.lua)

Uses reactive bindings that auto-update on state change:

-- Track info binding
playback.track:bind(function(track)
  title:set { text = track.title }
  album:set { text = track.album }
  artist:set { text = track.artist or "Unknown Artist" }
end)

-- Progress binding (updates slider)
playback.position:bind(function(pos)
  scrubber:set { value = pos / track.duration * 100 }
  cur_time:set { text = format_time(pos) }
end)

Problem for e-paper: Position binding fires frequently → too many refreshes

Widgets with Animation Issues

StatusBar battery indicator:

  • Uses overlay animation for charging state
  • Dynamically updates percentage text
  • Need to replace with static charging icon

InfiniteList (scrolling):

  • Lazy-loads items during scroll
  • Would cause excessive partial refreshes
  • Need debouncing or pagination approach

Required Firmware Changes

1. New Display Driver

Create display_epaper.cpp:

  • Replace ST7735 init with Waveshare e-paper commands
  • Implement partial refresh regions
  • Add BUSY pin handling (wait for refresh complete)
  • Remove backlight PWM (not needed)
  • Track refresh count for periodic full refresh

2. LVGL Configuration

  • Reduce color depth to 1-bit (monochrome)
  • Increase flush threshold (don’t flush every frame)
  • Add dirty-region tracking for partial updates

3. UI Modifications (Lua)

playing.lua changes:

-- Throttle position updates
local last_update = 0
playback.position:bind(function(pos)
  local now = time.now()
  if now - last_update > 5 then  -- Update every 5 seconds
    cur_time:set { text = format_time(pos) }
    last_update = now
  end
end)

widgets.lua changes:

  • Remove battery charging animation
  • Use static icons
  • Debounce list scrolling

New theme (theme_epaper.lua):

  • All colors → black or white
  • High contrast borders
  • Larger touch targets (no precision scrolling)

Complexity Assessment

ComponentDifficultyNotes
Display driverMediumWell-isolated, clear interface
LVGL configEasyJust config changes
Lua UI modsEasyScripted, no recompile for iteration
New e-paper themeEasyCopy existing theme, adjust colors
Faceplate PCBMediumNew design needed

Overall: Feasible project. The Lua-based UI is a big advantage - can iterate quickly without reflashing.

Prototyping Strategy

  1. Phase 1: Driver only - Get e-paper displaying anything via ESP32 dev board
  2. Phase 2: LVGL integration - Port driver to work with LVGL flush callback
  3. Phase 3: UI iteration - Modify Lua scripts for e-paper constraints (can test on TFT first with artificial delays)
  4. Phase 4: Hardware - Design faceplate PCB once software is proven

Firmware Resources


E-Paper Development Progress (2026-01-26)

Hardware Testing Complete

Successfully tested the Waveshare 1.54” e-paper display with ESP32-WROVER dev board:

Working Pin Configuration: | E-Paper Pin | ESP32 GPIO | |-------------|------------| | DIN (MOSI) | GPIO 14 | | CLK (SCLK) | GPIO 13 | | CS | GPIO 15 | | DC | GPIO 27 | | RST | GPIO 26 | | BUSY | GPIO 25 |

Confirmed Working:

  • Full refresh (clears ghosting)
  • Partial refresh (~0.3s updates)
  • LVGL integration with 1-bit color depth
  • Progress bar and time display updates
  • Correct color polarity (white background, black text)

Arduino Prototype

Working LVGL + GxEPD2 prototype at:

~/Documents/Arduino/lvgl_epaper_tangara/lvgl_epaper_tangara.ino

Key implementation details:

  • Uses GxEPD2_154_D67 driver for SSD1681 controller
  • Custom SPI pins via SPI.begin(CLK, -1, MOSI, CS)
  • LVGL flush callback converts pixels and triggers refresh
  • Partial refresh every update, full refresh every 10 updates
  • Color inversion: bool pixel = color_p->full ? false : true;

Tangara E-Paper Firmware Fork

Forked and modified tangara-fw for e-paper support:

~/dev/tangara-fw-epaper/

Files Added:

FileDescription
src/drivers/display_epaper.cppSSD1681 display driver
src/drivers/display_epaper.hppDriver header
src/drivers/display_config.hppKconfig integration
src/drivers/KconfigDisplay selection menu
lua/theme_epaper.luaBlack/white theme
lua/playing_epaper.luaThrottled Now Playing UI
lua/widgets_epaper.luaStatic widgets

CMakeLists.txt Modified: Conditional compilation based on CONFIG_DISPLAY_TYPE_EPAPER

Kconfig Options

Build-time configuration via idf.py menuconfig:

Display Configuration
├── Display Type
│   ├── ( ) TFT LCD (ST7735/ST7789)
│   └── (●) E-Paper (Waveshare 1.54" SSD1681)
└── E-Paper Display Settings
    ├── Pin Configuration
    ├── Refresh Settings (interval, partial enable)
    └── Power Management (deep sleep)

Build Instructions

cd ~/dev/tangara-fw-epaper
idf.py set-target esp32
idf.py menuconfig
# Navigate to: Display Configuration → E-Paper
idf.py build
idf.py flash

Remaining TODO

  • Test build with ESP-IDF
  • Integrate touchwheel with e-paper UI
  • Create additional Lua screens (menu, browser, settings)
  • Design custom faceplate PCB
  • Test battery life improvement
  • Outdoor readability testing