Custom firmware for the Ulanzi TC001 pixel display clock that enables portable API monitoring for markets, trade shows and events.
- Overview
- Hardware Specifications
- Features
- Requirements
- Installation
- Configuration
- Usage
- Backup & Restore
- Battery Monitoring
- API Requirements
- Button Controls
- Development
- Future Enhancements
- Troubleshooting
- License
This firmware transforms your Ulanzi TC001 into a self-contained API monitoring device. Perfect for scenarios where you need portable, at-a-glance monitoring without relying on external infrastructure.
⚠️ Important: This firware displays a single metric on the screen and cannot scroll through various apis etc. This is something I'd like to do in the future so please use the Discussions area to suggest new features and enhancements.
Key Use Cases:
- Markets: Social media followers
- Trade show booth metrics (visitor counts, lead generation)
- Support ticket monitoring on the go
- Real-time sales dashboards
- IoT sensor data display
- Any REST API with JSON responses
Why This Over AWTRIX? AWTRIX requires an external server to POST data to the device. This firmware polls APIs directly from the device, making it ideal for portable use on public WiFi networks where you can't run external servers.
- Backup & Restore - Save and restore your device configuration via the web interface
- Web Installer - Implemented easy web installer to load firmware onto devices
- Password Protection - All configuration pages are now protected by password authentication. Default password is
ulanzitc001, configurable in General Settings - Session Management - 30-minute session timeout with automatic refresh on activity
- Secure Logout - Logout button added to main page for secure session termination
- Manual API Refresh - Press the middle button (Button 2) for 1 second to force an immediate API refresh
- Improved Button Handling - Completely redesigned button state machine for cleaner, more scalable button combinations
- Automatic URL Encoding - API endpoints are now automatically URL-encoded, so you can paste URLs directly from documentation without manual encoding
- HTML Form Security - Fixed HTML escaping to properly handle special characters in configuration fields
- Test API Timeout - Added 10-second timeout to Test API function to prevent web UI lockups
- MAC Address Fix - Was not properly retrieving MAC address to generate unique device name
- Interactive Icon Editor - Added color picker for selecting paint colors, Made icon preview grid clickable to paint individual pixels, Added "Clear All" button to reset icon, Implemented two-way sync between JSON array and visual editor.
- API Retry Logic - Added 10 second timeout to API requests, Implemented automatic retry (up to 2 attempts) for network/timeout errors.
- General Config - Move display brightness to a general config page.
- Reboot - Able to reboot device from config page.
- Tidy Up - Tidied up the config screens, removed all the dedicated battery information as it was to much info.
- Real-time battery monitoring - Voltage and percentage tracking
- Web UI display - Live battery status
- Button combination - Hold Button 2 + Button 3 to show battery on display
- Low battery warnings - Audio alerts at 20% (low) and 10% (critical)
- Smart voltage curve - Accurate LiPo battery percentage calculation
Device: Ulanzi TC001 Pixel Display Clock Display: 32x8 WS2812 LED Matrix Microcontroller: ESP32 Battery: Built-in LiPo battery (monitored via GPIO34)
| GPIO | Function |
|---|---|
| GPIO14 | Button 3 |
| GPIO15 | Buzzer |
| GPIO21 | I2C SDA |
| GPIO22 | I2C SCL |
| GPIO26 | Button 1 |
| GPIO27 | Button 2 |
| GPIO32 | WS2812 LED Matrix |
| GPIO34 | Battery ADC (voltage monitoring) |
| GPIO35 | Light Sensor |
- 🔌 Access Point mode for initial setup
- 🌐 Web-based WiFi configuration
- 💾 Persistent credential storage
- 🔄 Easy reconfiguration via buttons
- 🔗 Direct API polling of a SINGLE api endpoint (no external server needed)
- 🔐 Custom header authentication support
- ⏱️ Configurable polling intervals (5-3600 seconds)
- 🔄 Manual refresh via button hold (Button 2 for 1 second)
- 🧭 Flexible JSON path navigation with array filtering
- 📊 Support for nested objects and arrays
- 🔍 JSON Array filtering by field values (e.g.,
users[name=John].age) - ✅ HTTPS support
- 📜 Scrolling or static value display modes
- 🎨 Color-coded status (green=ok, red=error, yellow=warning)
- 🖼️ 8x8 icon support (scrolls with text)
- 🏷️ Optional display prefix and suffix
- 🎯 Automatic centering in static mode
- ⚡ Immediate API polling on startup (no waiting for first interval)
- 🌞 Auto brightness using onboard light sensor
- 🎚️ Manual brightness config slider (1-255)
- 🌙 Adaptive dimming for dark environments (as low as brightness 1)
- ☀️ Automatic brightening in daylight (up to brightness 255)
- 🎯 Non-linear curve optimized for indoor use
- 💾 Brightness settings persist across reboots
- 🔄 Real-time brightness updates every 100ms in auto mode
- 🎨 8x8 pixel RGB icon
- 🔄 Icons scroll with text
- 📋 Simple JSON array format:
[[r,g,b],[r,g,b],...] - 👁️ Live design and preview in configuration interface
- 💾 Stored locally (no external dependencies)
- ⚙️ Full configuration page
- 🧪 API connection testing
- 📊 Status monitoring with battery info
- 🔄 Factory reset option
- 📊 Real-time voltage and percentage monitoring
- 🔊 Low battery audio alerts (20% and 10%)
- 🎮 Button combination (Button 2 + 3) to show battery
- 📈 Non-linear voltage curve for accurate LiPo readings
- 🔒 Secure NVS storage for API key
- 🎭 Masked API key display
- 🔒 Password restricted config area
- 💾 Persistent settings across reboots
- 🆔 Unique device identification (MAC-based)
- 📡 Unique AP names per device
- 🔴 Hardware and web-based factory reset
- Ulanzi TC001 Pixel Display Clock
- USB-C cable for programming
- Computer with Arduino IDE
- Arduino IDE 1.8.x or 2.x
- Python (for firmware backup via esptool)
Install via Arduino Library Manager:
- WiFiManager by tzapu
- Adafruit GFX Library
- Adafruit NeoMatrix
- Adafruit NeoPixel
- ArduinoJson by Benoit Blanchon (v6.x or v7.x)
Built-in ESP32 libraries (no installation needed):
- WiFi
- WebServer
- HTTPClient
- Preferences
The easiest way to install or update the firmware is using our web-based installer:
Requirements:
- Google Chrome or Microsoft Edge browser
- USB cable connected to your TC001 device
Steps:
- Visit the web installer link above
- Click "Install Firmware" button
- Select your TC001's serial port when prompted
- Wait for installation to complete (about 30 seconds)
- Device will restart automatically
Benefits:
- ✅ No software installation required
- ✅ Works on Windows, Mac, and Linux
- ✅ Preserves your WiFi and API settings during updates
- ✅ One-click installation process
If you want to modify the code or compile from source, use the Arduino IDE method below.
Before flashing custom firmware, backup the original:
# Install esptool
pip install esptool
# Backup firmware (replace COM3 with your port)
esptool.py --port COM3 --baud 115200 read_flash 0x0 0x400000 tc001_backup.binTo restore stock firmware later:
esptool.py --port COM3 --baud 115200 write_flash 0x0 tc001_backup.bin-
Download and install Arduino IDE
-
Add ESP32 board support:
- Go to File → Preferences
- Add to "Additional Board Manager URLs":
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json - Go to Tools → Board → Boards Manager
- Search for "ESP32" and install
-
Install required libraries:
- Sketch → Include Library → Manage Libraries
- Search and install each library from the Requirements section
- Open the
Ulanzi-TC001-API-Monitor.inosketch in Arduino IDE - Configure board settings:
- Tools → Board → ESP32 Arduino → ESP32 Dev Module
- Tools → Port → Select your TC001's COM port
- Click Upload button
- Wait for compilation and upload to complete
- Device will create WiFi AP:
TC001-XXXXXX - Connect to this AP with your phone/computer
- Captive portal should open automatically (or navigate to
192.168.4.1) - Configure your WiFi credentials
- Device will connect and display its IP address
- Browse to the displayed IP address to continue configuration
The web interface is protected by password authentication to secure your configuration.
Default Password: ulanzitc001
When you first access the web interface at your device's IP address, you'll be presented with a login screen. Enter the default password to access the configuration pages.
Changing the Password:
- Log in to the web interface
- Navigate to General Settings (Config button)
- Scroll to the "Admin Password" section
- Enter your new password
- Click "Save Settings"
Session Management:
- Sessions remain active for 30 minutes with automatic refresh on activity
- Click the "Logout" button on the main page to end your session
- If you forget your password, perform a factory reset (hold all 3 buttons for 3 seconds)
Navigate to your TC001's IP address (displayed on the LED matrix) to access the configuration interface.
| Setting | Description | Example |
|---|---|---|
| API Endpoint URL | Full URL to your API (query parameters automatically encoded) | https://api.example.com/data?filter=Name eq 'Value' |
| API Header Name | Authentication header name | APIKey, Authorization, X-API-Key |
| API Key | Your API authentication key | your-secret-key-here |
| JSON Path | Path to value in JSON response | OverdueWorkflows[Username=John].Overdue |
| Display Prefix | Text before the value (optional) | Tickets: |
| Display Suffix | Text after the value (optional) | open |
| Icon Data | 8x8 RGB icon as JSON array (optional) | [[255,0,0],[0,255,0],...] |
| Enable Scrolling | Checkbox for scroll vs static mode | Checked = scrolling (default) |
| Polling Interval | Seconds between API calls | 60 (range: 5-3600) |
| Auto Brightness | Checkbox for automatic brightness control | Checked = use light sensor, Unchecked = manual |
| Manual Brightness | Slider for brightness level (when auto disabled) | 40 (range: 1-255) |
Note about URL encoding: You can paste API URLs directly from your API documentation. Special characters in query parameters (like spaces, quotes, etc.) are automatically URL-encoded when the request is sent. For example, filter=Name eq 'John' is automatically encoded to filter=Name%20eq%20%27John%27.
The firmware supports flexible JSON path navigation with array filtering:
Simple root level:
{ "count": 42 }JSON Path: count
Nested object:
{ "data": { "unassigned": 12 } }JSON Path: data.unassigned
Array access by index:
{ "results": [{ "value": 99 }] }JSON Path: results[0].value
Array filtering by field value:
{
"OverdueWorkflows": [
{ "Username": "John", "Overdue": 5 },
{ "Username": "Jane", "Overdue": 12 }
]
}JSON Path: OverdueWorkflows[Username=Jane].Overdue
Complex nested:
{
"TC001MatrixDisplay": [
{ "OpenRequests": 12 }
]
}JSON Path: TC001MatrixDisplay[0].OpenRequests
Use the format arrayName[fieldName=value] to filter arrays:
users[name=John].age- Find user with name "John" and get their ageproducts[id=12345].price- Find product with id "12345" and get pricetickets[status=open].count- Find ticket with status "open" and get count
This searches through the array and returns the first object where the field matches the value.
Icons are 8x8 pixels in RGB format. The JSON array should contain exactly 64 pixel values:
[[255,0,0],[255,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[255,0,0],[255,0,0],
[255,0,0],[255,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[255,0,0],[255,0,0],
...64 total pixels...]Each pixel is [red, green, blue] with values 0-255.
The web interface provides a live preview of your icon before saving. Icons scroll alongside the text when scrolling is enabled, or display static with centered text when scrolling is disabled.
Scrolling Mode (default):
- Icon and text scroll continuously from right to left
- Ideal for longer text or when you want continuous motion
- Full content visibility over time
Static Mode:
- Content is centered on the display
- Perfect for short displays (e.g., icon + number)
- If content is wider than 32 pixels, it left-aligns
- Ideal for at-a-glance monitoring
The TC001 offers two brightness control modes to suit different environments and preferences.
Auto Brightness Mode (Recommended):
- Uses the onboard light sensor (GPIO35) to automatically adjust brightness
- Adapts in real-time to ambient lighting conditions
- Brightness ranges:
- Very dark (sensor 0-100): Brightness 1-5 (very dim for nighttime)
- Dim room (sensor 100-500): Brightness 5-30 (low for dark rooms)
- Normal indoor (sensor 500-2000): Brightness 30-120 (comfortable for typical indoor lighting)
- Bright light (sensor 2000-4095): Brightness 120-255 (high for daylight/bright rooms)
- Updates every 100ms for smooth transitions
- Perfect for displays in rooms with varying light throughout the day
Manual Brightness Mode:
- Fixed brightness level set via slider (1-255)
- Brightness 1 = barely visible (ideal for very dark rooms)
- Brightness 40-80 = comfortable for normal indoor use
- Brightness 150-255 = very bright (for well-lit areas or outdoor use)
- Useful when consistent brightness is preferred
Tip: Start with auto brightness mode and observe the display throughout the day. If you find it too bright or dim at certain times, you can switch to manual mode and set your preferred level.
You must save API configuration before using the Test Connection button to verify:
- API endpoint is reachable
- Authentication is working
- JSON path correctly extracts the value
- Response format is as expected
The TC001 has a built-in LiPo battery that is continuously monitored. The firmware provides comprehensive battery information:
Battery Voltage Ranges
- 4.2V = 100% (fully charged)
- 3.9V = ~75% (good)
- 3.7V = ~40% (moderate)
- 3.5V = ~15% (low)
- 3.0V = 0% (empty)
- Single beep = Low battery warning (20%) - plays once per boot
- Double beep = Critical battery (10%) - repeats periodically
Method 1: Web Interface
- Navigate to your TC001's IP address in a browser
- Battery status appears at the top with:
- Percentage and voltage display
Method 2: Button Combination
- Hold Button 2 + Button 3 together for 0.5 seconds
- Battery information displays on the LED matrix for 3 seconds
Battery monitoring runs automatically with these settings:
- Update interval: 10 seconds
- Samples per reading: 10 (averaged for stability)
- Low battery threshold: 20%
- Critical threshold: 10%
Once configured, the device will:
- Auto-connect to saved WiFi on startup
- Begin polling the API at configured intervals
- Display the current value (scrolling or static based on settings)
- Update automatically when new data arrives
- Show error messages if API fails
| Color | Meaning |
|---|---|
| 🟢 Green | Normal operation, displaying API value |
| 🔴 Red | Error state (connection failed, parsing error) |
| 🟡 Yellow | Not configured or warning |
API Response:
{
"StatusCode": 200,
"Summary": "OK",
"RecordCount": 1,
"TC001MatrixDisplay": [
{ "OpenRequests": 12 }
]
}Configuration:
- API Endpoint:
https://support.yourcompany.com/api/tickets - Header Name:
APIKey - API Key:
Bi...5# - JSON Path:
TC001MatrixDisplay[0].OpenRequests - Display Prefix:
Tickets: - Display Suffix:
open - Icon Data:
[[...]](ticket icon) - Enable Scrolling: ✅ Checked
- Polling Interval:
60 - Auto Brightness: ✅ Checked
- Manual Brightness: N/A (auto mode)
Result: Icon and "Tickets: 12 open" scroll continuously, updates every 60 seconds, brightness adjusts automatically to room lighting.
API Response:
{
"OverdueWorkflows": [
{ "Username": "Sue Walker", "Overdue": 1, "Today": 0 },
{ "Username": "John Smith", "Overdue": 5, "Today": 2 }
]
}Configuration:
- API Endpoint:
https://workflow.company.com/api/stats - JSON Path:
OverdueWorkflows[Username=Sue Walker].Overdue - Display Prefix: `` (empty)
- Display Suffix: `` (empty)
- Icon Data:
[[...]](person icon) - Enable Scrolling: ⬜ Unchecked (static)
- Polling Interval:
300 - Auto Brightness: ⬜ Unchecked
- Manual Brightness:
80
Result: Icon and number "1" centered on display, updates every 5 minutes, fixed brightness at 80 for consistent visibility.
API Response:
{
"symbol": "BTCUSDT",
"price": "95574.27000000"
}Configuration:
- API Endpoint:
https://api.binance.com/api/v3/ticker/price?symbol=BTCUSDT - API Key: (leave blank - no auth needed)
- JSON Path:
price - Display Prefix:
BTC: $ - Display Suffix: `` (empty)
- Enable Scrolling: ✅ Checked
- Polling Interval:
120 - Auto Brightness: ✅ Checked
- Manual Brightness: N/A (auto mode)
Result: "BTC: $43250.5432" scrolls continuously, updates every 2 minutes, brightness adapts to lighting conditions.
The firmware includes a backup and restore feature that allows you to save your device configuration and restore it later. This is useful for:
- Firmware updates - Save settings before updating, restore after
- Device migration - Transfer settings to a new TC001 device
- Configuration backup - Keep a safe copy of your working setup
- Log in to the web interface at your device's IP address
- Click the Backup / Restore button on the home page
- Navigate to the Backup & Restore page
- Click Download Backup
- A JSON file will be downloaded to your computer with the naming format:
tc001-backup-YYYY-MM-DD.json
What's Included in Backups:
- API endpoint URL and header configuration
- JSON path settings
- Display prefix and suffix
- Polling interval
- Scroll mode settings
- Icon data (8x8 pixel design)
- Brightness preferences (auto/manual mode and level)
What's NOT Included (for security):
- WiFi credentials (SSID and password)
- Admin password
- API keys/authentication tokens
- Navigate to the Backup & Restore page
- Click Choose File and select your backup JSON file
- Click Upload & Restore
- If there's a version mismatch between the backup and current firmware, you'll be prompted to confirm
- The device will automatically restart to apply the restored settings
Important Notes:
- After restoring, you'll need to re-enter your API key (not stored in backups for security)
- WiFi settings are preserved separately and won't be affected by restore
- The device restarts automatically after a successful restore
Backup files are stored in JSON format and can be viewed/edited in any text editor:
{
"version": "v1.0.8",
"device_id": "XXXXXX",
"backup_date": "2024-01-15T10:30:00.000Z",
"api_endpoint": "https://api.example.com/data",
"api_header_name": "APIKey",
"json_path": "data.count",
"display_prefix": "Count: ",
"display_suffix": "",
"polling_interval": 60,
"scroll_enabled": true,
"icon_data": "[[255,0,0],...]",
"auto_brightness": true,
"manual_brightness": 40
}When restoring a backup from a different firmware version:
- A warning dialog will appear showing both versions
- You can choose to proceed or cancel
- Most settings are forward/backward compatible
- New features in newer firmware will use default values if not present in the backup
Your API must meet these requirements:
- ✅ HTTP/HTTPS GET endpoint
- ✅ JSON response format
- ✅ Stable endpoint URL
- ⚙️ Header-based authentication (APIKey, Authorization, etc.)
The API can return any valid JSON structure. Use the JSON path configuration to navigate to your desired value. Values can be:
- Numbers:
42,3.14 - Strings:
"active","12 items" - Booleans:
true,false
| Action | Result |
|---|---|
| Hold Button 1 during startup | Enter WiFi configuration mode |
| Hold Button 2 for 1 second | Force immediate API refresh (manual update) |
| Hold Button 2 + Button 3 for 0.5s | Show battery status on display (3 seconds) |
| Hold all 3 buttons for 3 seconds | Factory reset (WiFi + API settings) |
| Hold Left (Button 1) + Right (Button 3) buttons | Power on/off (hardware feature) |
Two ways to factory reset:
- Hardware: Hold all 3 buttons for 3 seconds (device beeps twice)
- Web Interface: Click "Factory Reset" button on home page
Factory reset clears:
- ✅ WiFi credentials
- ✅ API configuration
- ✅ Icon data
- ✅ All stored settings
Device will restart in AP mode for reconfiguration.
Currently single-file Arduino sketch:
Ulanzi-TC001-API-Monitor/
├── Ulanzi-TC001-API-Monitor.ino # Main firmware file
├── README.md # Documentation
├── Ulanzi-TC001-API-Monitor.bin # Pre-compiled firmware binary
├── docs/ # GitHub Pages web installer
│ ├── index.html # Web installer interface
│ ├── manifest.json # ESP Web Tools manifest
│ └── firmware/ # Firmware binaries folder
│ ├── bootloader.bin # ESP32 bootloader
│ ├── partitions.bin # Partition table
│ ├── boot_app0.bin # Boot app
│ └── customfirmware.bin # Application binary
└── images/ # Documentation images
Main components:
- WiFi Management - WiFiManager integration, AP mode
- Web Server - Configuration interface, status pages
- API Client - HTTP requests, JSON parsing with array filtering
- Display Manager - LED matrix control, scrolling and static modes
- Icon Handler - JSON icon parsing, RGB565 conversion, rendering
- Battery Monitor - Voltage reading, percentage calculation, alerts
- Storage - NVS preferences for configuration
- Button Handler - Physical button controls
If you're a developer creating a new release, follow these steps to generate all required binary files for the web installer:
- Open
Ulanzi-TC001-API-Monitor.inoin Arduino IDE - Configure board settings:
- Tools → Board → ESP32 Arduino → ESP32 Dev Module
- Tools → Partition Scheme → Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS)
- Go to Sketch → Export Compiled Binary (or press Ctrl+Alt+S)
- Wait for compilation to complete
- Arduino IDE will create several files in your sketch folder:
Ulanzi-TC001-API-Monitor.ino.bin- Main applicationUlanzi-TC001-API-Monitor.ino.bootloader.bin- BootloaderUlanzi-TC001-API-Monitor.ino.partitions.bin- Partition table
The boot_app0.bin file is not exported by Arduino IDE but is required for ESP32 flashing. You can find it in your Arduino ESP32 package:
Windows:
C:\Users\<YourUsername>\AppData\Local\Arduino15\packages\esp32\hardware\esp32\<version>\tools\partitions\boot_app0.bin
Mac:
~/Library/Arduino15/packages/esp32/hardware/esp32/<version>/tools/partitions/boot_app0.bin
Linux:
~/.arduino15/packages/esp32/hardware/esp32/<version>/tools/partitions/boot_app0.bin
Copy and rename the exported files to docs/firmware/:
# Copy main application (rename to customfirmware.bin)
cp Ulanzi-TC001-API-Monitor.ino.bin docs/firmware/customfirmware.bin
# Copy bootloader
cp Ulanzi-TC001-API-Monitor.ino.bootloader.bin docs/firmware/bootloader.bin
# Copy partition table
cp Ulanzi-TC001-API-Monitor.ino.partitions.bin docs/firmware/partitions.bin
# Copy boot_app0.bin from Arduino ESP32 package
cp <path-to-arduino15>/packages/esp32/hardware/esp32/<version>/tools/partitions/boot_app0.bin docs/firmware/boot_app0.binUpdate version numbers in these files:
Ulanzi-TC001-API-Monitor.ino- Line 12:String buildNumber = "vX.X.X";docs/manifest.json- Line 3:"version": "X.X.X",docs/index.html- Line 7 (title) and line 22 (version display)README.md- "What's New" section at the top
Before pushing to GitHub, test the web installer locally:
-
Start a local web server in the
docsfolder:cd docs python -m http.server 8000 -
Open Chrome/Edge and navigate to
http://localhost:8000 -
Test the installation process with your TC001 device
-
Verify settings are preserved during update
git add .
git commit -m "Release vX.X.X - Description of changes"
git push origin mainIf not already enabled:
- Go to repository Settings → Pages
- Source: Deploy from a branch
- Branch: main → /docs folder
- Click Save
GitHub Pages will be available at: https://tommysharpnz.github.io/Ulanzi-TC001-API-Monitor/
For manual flashing or troubleshooting:
0x1000 (4096) - bootloader.bin
0x8000 (32768) - partitions.bin
0xE000 (57344) - boot_app0.bin
0x10000 (65536) - customfirmware.bin (main application)
The web installer automatically flashes all files at the correct addresses.
Planned features for future releases:
- Concept of "screens" allowing for multiple API endpoint support with button navigation or auto rotation
- Multiple API endpoints with button navigation
- Threshold-based color coding (e.g., red if value > 10)
- Time display option
- Temperature/weather display
- OTA (Over-The-Air) firmware updates
- Show recent serial log entries in web config UI
Symptoms: Device stuck in AP mode or shows "WIFI FAIL"
Solutions:
- Hold Button 1 during startup to manually enter config mode
- Verify WiFi credentials are correct
- Check WiFi signal strength
- Ensure 2.4GHz network (ESP32 doesn't support 5GHz)
Symptoms: Display shows old value or error message
Solutions:
- Use "Test Connection" button in web interface
- Check API endpoint is accessible from device's network
- Verify API key is correct and not expired
- Confirm JSON path matches actual response structure
- Check Serial Monitor for detailed error messages
Symptoms: Extracted value doesn't match expected data
Solutions:
- Verify JSON path syntax in configuration
- Use "Test Connection" to see raw API response
- Check for array indices starting at 0
- Ensure nested object paths use correct notation
- For array filtering, verify field names and values match exactly
Symptoms: Icon doesn't appear or shows incorrectly
Solutions:
- Verify JSON array has exactly 64 pixels
- Check preview in web interface before saving
- Ensure each pixel is
[r,g,b]format with values 0-255 - Clear icon data field and re-paste if corrupted
- Check serial monitor for icon parsing errors
Symptoms: Can't browse to device's IP address
Solutions:
- Verify device is on same network as your computer/phone
- Check IP address shown on LED display
- Try pinging the device:
ping 192.168.x.x - Reboot the device (power cycle)
- Factory reset and reconfigure if needed
Connect USB and open Serial Monitor (115200 baud) to see:
- Startup messages
- WiFi connection status
- Battery voltage and percentage
- API request/response details
- JSON parsing results
- Icon parsing status
- Error messages
- WiFi credentials stored in ESP32 WiFi NVS
- API settings stored in custom NVS namespace:
tc001 - Settings persist across firmware updates
- Factory reset clears both WiFi and custom NVS
- Icon data stored as JSON string (max ~512 bytes)
- ADC: 12-bit resolution (0-4095) on GPIO34
- Reference voltage: 3.3V
- Voltage divider: Typically 2:1 ratio
- Update frequency: Every 10 seconds
- Samples per reading: 10 (averaged)
- Non-linear voltage curve for LiPo batteries
- API keys stored in plaintext in NVS
- Keys only masked in web UI display
- Use HTTPS endpoints when possible
- Requires 2.4GHz WiFi (ESP32 limitation)
- Outbound HTTP/HTTPS connectivity needed
- No incoming connections required
- May not work on networks with captive portals
- Typical API response time: 200-500ms
- Display update rate: 50ms per scroll step (scrolling mode)
- Static mode: Updates every 1 second
- Auto brightness: Updates every 100ms
- Battery monitoring: Updates every 10 seconds
- Memory usage: ~150KB RAM with icon loaded
- Light sensor: 12-bit ADC (0-4095 range) on GPIO35
- Auto mode brightness mapping:
- Very dark (0-100): Brightness 1-5
- Dim room (100-500): Brightness 5-30
- Normal indoor (500-2000): Brightness 30-120
- Bright light (2000-4095): Brightness 120-255
- Manual mode: Direct brightness control (1-255)
- Brightness settings stored in NVS, persist across reboots
- Updates apply immediately upon saving configuration
Contributions welcome! Areas for improvement:
- Documentation improvements
- Bug fixes and testing
- Battery monitoring enhancements
- New features
- Icon library
- Example configurations
This project is open source and available under the MIT License.
- Ulanzi for the TC001 hardware platform
- tzapu for the excellent WiFiManager library
- Adafruit for the LED matrix libraries
- Benoit Blanchon for ArduinoJson
- The ESP32 Arduino community
- Claude AI for helping me with coding and documentation where I needed it
For issues, questions, or feature requests:
- Open an issue on GitHub
- Check existing issues for solutions
- Consult the troubleshooting section above
Device Identification:
- Device Name Format:
TC001-Display-XXXXXX - AP Name Format:
TC001-XXXXXX - Where XXXXXX = Last 6 characters of MAC address
