Tangara Build
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)
| Board | Description |
|---|---|
| Mainboard | ESP32, power management, DAC, amp, SD card |
| Faceplate | Display, touchwheel sensor, haptic motor |
Key Electronic Components
| Component | Part | Function |
|---|---|---|
| Main MCU | ESP32-WROVER-E | Primary processor |
| Co-processor | ATSAMD21E18 | Power management, USB |
| DAC | Cirrus Logic WM8523 | Audio conversion |
| Amplifier | TI INA1620 | Headphone driver |
| Touch Controller | AT42QT2120 | Capacitive touchwheel |
| GPIO Expander | PCA8575 | Button/control interface |
| Haptic Driver | DRV2605LDGS | Vibration feedback |
| Display | 1.8” ST7735 TFT | 160x128, 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
- Download design files from tangara-hw repository
- Upload Gerbers + BOM to PCB fab for assembly service
- Order PCBs with PCBA service
- 3D print case parts
- Source battery, SD card, mechanical parts
- Final assembly (ribbon cables, battery - no soldering)
- Flash firmware
- Test and enjoy
Resources
Design Files
- Codeberg - tangara-hw - PCB and case designs
- SourceHut Project - All repositories
Documentation
- Cool Tech Zone - Tangara - Official site
- Assembly Guide
- Electronic Design
PCB Fabrication
Purchase Pre-built
- Crowd Supply - Official crowdfunding page
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
| Spec | Stock TFT | Waveshare 1.54” | Waveshare 2.13” |
|---|---|---|---|
| Size | 1.8” | 1.54” | 2.13” |
| Resolution | 160x128 | 200x200 | 250x122 |
| Colors | 18-bit (262k) | B/W only | B/W only |
| Full Refresh | Instant | 2s | 2s |
| Partial Refresh | N/A | 0.3s | ~0.3s |
| Standby Power | Continuous draw | <0.017mW | <0.01uA |
| Interface | SPI (ST7735) | SPI | SPI |
| Price | Included | ~$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:
| Element | Stock TFT | E-Paper Approach |
|---|---|---|
| Progress bar | Smooth animation | Update every 5-10 seconds, or use text time display |
| Track change | Instant | 0.3s partial update (acceptable) |
| Scrolling text | Smooth | Paginated or static |
| Album art | Full color | Dithered B/W, or skip entirely |
| Volume indicator | Instant | Flash briefly, return to main view |
| Visualizer | Real-time | Not 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:
- Replace ST7735 driver with e-paper driver (GxEPD2 library or similar)
- Implement partial refresh strategy
- Redesign UI for slow refresh constraints
- Add periodic full-refresh logic
Implementation Approach
- Order Waveshare 1.54” module for prototyping
- Test with ESP32 dev board first (before committing to custom PCB)
- Port display driver to Tangara firmware
- Design minimal UI that works with 0.3s refresh
- Design custom faceplate PCB in KiCad
- 3D print modified case front (adjust for display dimensions)
- Order custom faceplate PCB
- Integrate and test
Resources
E-Paper
Libraries
- GxEPD2 - Arduino e-paper library
- Moddable e-paper tips
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:
| File | Purpose | E-Paper Impact |
|---|---|---|
playing.lua | Now-playing screen | Progress bar updates, album art |
widgets.lua | StatusBar, lists | Battery animation, scroll behavior |
theme_*.lua | Visual themes | Colors → 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
| Component | Difficulty | Notes |
|---|---|---|
| Display driver | Medium | Well-isolated, clear interface |
| LVGL config | Easy | Just config changes |
| Lua UI mods | Easy | Scripted, no recompile for iteration |
| New e-paper theme | Easy | Copy existing theme, adjust colors |
| Faceplate PCB | Medium | New design needed |
Overall: Feasible project. The Lua-based UI is a big advantage - can iterate quickly without reflashing.
Prototyping Strategy
- Phase 1: Driver only - Get e-paper displaying anything via ESP32 dev board
- Phase 2: LVGL integration - Port driver to work with LVGL flush callback
- Phase 3: UI iteration - Modify Lua scripts for e-paper constraints (can test on TFT first with artificial delays)
- 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_D67driver 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:
| File | Description |
|---|---|
src/drivers/display_epaper.cpp | SSD1681 display driver |
src/drivers/display_epaper.hpp | Driver header |
src/drivers/display_config.hpp | Kconfig integration |
src/drivers/Kconfig | Display selection menu |
lua/theme_epaper.lua | Black/white theme |
lua/playing_epaper.lua | Throttled Now Playing UI |
lua/widgets_epaper.lua | Static 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