QRPickle is a simple, lightweight dashboard designed for portable, QRP, and field-day amateur radio operations. Built to run on a standard ESP32 Cheap Yellow Display (CYD) with no external PSRAM, it consolidates real-time tracking data and tools into a clean, easy-to-use touchscreen interface.
This project is inspired by cburns42/HamClockCYD and SP3KON/ESP32-HAM-CLOCK, which resulted in a group build at BARC with VU3GWN forking it to(coffeedev/BARC-HAM-CLOCK).
This codebase, firmware architecture, network parsing engine, memory management schema, and layout optimization layers were designed, audited, and developed using a Large Language Model (LLM).
| Boot & Splash | Main Dashboard | App Menu |
|---|---|---|
![]() |
![]() |
![]() |
| HamAlert & DX Cluster | POTA / SOTA Spots | Solar Propagation |
![]() |
![]() |
![]() |
| APRS Radar | APRS Messaging | APRS Beaconing |
![]() |
![]() |
|
| Weather Forecast | On-Device Settings | Smart Sleep Mode |
![]() |
![]() |
![]() |
When on the same network, navigate to the device's IP address to access the integrated Web UI. The UI interacts with the C++ backend via asynchronous REST API endpoints.
| Basic Network & Station Config | Advanced Settings |
|---|---|
![]() |
![]() |
| Staged Profile Management | Live Hardware Telemetry |
![]() |
![]() |
| Over-The-Air (OTA) Firmware Flashing | |
![]() |
- Stream-Buffered HamAlert & DX Cluster: Maintains a persistent, asynchronous TCP Telnet socket to background-listen for targeted DX spots. Uses a custom "Smart Parser" to read arbitrary-length streams without overflowing the FreeRTOS stack.
- Heap-Protected POTA & SOTA Fetching: Standard HTTPS requests require ~40KB of contiguous RAM for RSA encryption (
mbedTLS). QRPickle uses element-by-element JSON stream deserialization and task-staggering to preventBIGNUMOut-Of-Memory kernel panics. - APRS-IS Integration: Connects to the global APRS-IS network. Features a tactical Radar view, live spotter tracking, custom macros, and direct bidirectional messaging.
- Solar & Weather Telemetry: Pulls active K-Index, A-Index, and SFI from solar endpoints, and maps granular, bitmask-driven 24-hour weather forecasts via OpenWeatherMap API.
- Adaptive Power & Wi-Fi Management: Enforces hardware-level radio power caps (
esp_wifi_set_max_tx_power(52)) to prevent LDO voltage brownouts on the CYD board. Falls back to an integrated Setup AP (192.168.4.1) if routing fails. - Dynamic Staged Profiles: Save and hot-swap complete configuration layouts (Callsign, Grid, Network, Theme, Settings) via LittleFS for quick transitions between Home, Mobile, and Field ops.
- Monochrome & Tactical Themes: Driven by LVGL 9, featuring high-contrast modes including Tactical Field Red, GitHub Slate Dark, Terminal Phosphor Green, and pure-binary E-Ink simulations.
- OTA: Manual firmware update through the web dashboard and ability to pull from Github.
- Adaptive brightness: Option to enable adaptive brightness or set it manually.
- LED Status: A ver elementary notificaiton system is implemented through the LED lights on the backside of the ESP32-CYD. Details are documented in the LED Colours file in the docs folder.
Because this firmware packs LVGL graphics, web assets, and an OTA system into a standard 4MB flash ESP32 without external PSRAM, we utilize a heavily customized partition table (partitions.csv):
nvs(Non-Volatile Storage): 20KB. Stores atomic active configuration variables (Wi-Fi credentials, active callsign, volume/brightness state).app0(Factory / OTA 1): ~1.5MB. The primary execution partition for the compiled C++ binary.app1(OTA 2): ~1.5MB. The secondary fallback partition used during wireless Over-The-Air updates.spiffs / littlefs(Data Drive): ~800KB. Formatted as LittleFS. Stores the Web UI (index.html,app.js,style.css), stored JSON profiles, and system log trace dumps.
- LVGL (Light and Versatile Graphics Library) v9.x: Core C-based UI framework handling all screens, layouts, animations, and touch interactions.
- TFT_eSPI: Highly optimized hardware-specific SPI driver for the ILI9341 display and XPT2046 resistive touch controller.
- ArduinoJson v7: Zero-allocation stream parsing for OpenWeatherMap, POTA, and SOTA HTTPS payloads.
- ESP-IDF Native APIs: Utilized directly alongside the Arduino Core wrapper for strict Wi-Fi TX power limits, FreeRTOS task handling, and lwIP socket management.
The project is built entirely within PlatformIO (VS Code).
Ensure your platformio.ini is configured for your specific CYD hardware pins. No external hardware modifications are required.
Before the firmware will function fully (especially the Web UI), you must write the data/ folder to the LittleFS partition.
pio run --target uploadfsCompile the C++ source and burn the binary to the ESP32 via USB.
pio run --target uploadOnce deployed in the field, further updates can be pushed wirelessly. Compile your firmware in PlatformIO to generate firmware.bin or littlefs.bin, open the System Info tab on the Web UI, and push the payload directly over your local network.
Designed for operational reliability out in the field. Keep the signal clean, the power consumption managed, and the heap layout stable. 73!
This project utilizes a heavily customized partition table (partitions.csv) to fit a complex FreeRTOS application, LVGL graphics, a Web UI, and an Over-The-Air (OTA) update system into a standard 4MB ESP32 flash chip.
Here is the exact memory map breakdown:
| Partition Name | Address Offset | Hexadecimal Size | Human-Readable Size | Operational Assignment |
|---|---|---|---|---|
nvs |
0x009000 |
0x005000 |
20 KB | Core hardware flags, automated network authorization pairs, calibration registers, and ambient settings profiles. |
otadata |
0x00E000 |
0x002000 |
8 KB | Real-time execution targeting registers managed by the ESP32 bootloader tracking loops to determine current stable application selection flags (app0 vs app1). |
app0 |
0x010000 |
0x1C0000 |
1.75 MB (1,792 KB) | Primary system firmware image block storage. Expanded to resolve memory boundary overhead limitations from growing graphics frameworks. |
app1 |
0x1D0000 |
0x1C0000 |
1.75 MB (1,792 KB) | Mirror staging architecture slot dedicated to downloading incoming firmware packages directly from the GitHub API CDN pipelines without colliding with current live executions. |
spiffs |
0x390000 |
0x070000 |
448 KB | Dedicated LittleFS structural loop containing web console visual layouts (index.html, style.css, app.js), local operator descriptions, and raw static binary user definitions. |
You do not need to install VS Code or PlatformIO to use QRPickle. Pre-compiled binaries for the CYD are available in the repository releases.
Download Latest Release Binaries Here
The release folder contains the following files:
firmware.bin(The core C++ application)littlefs.bin(The Web UI and filesystem data)bootloader.bin&partitions.bin(Low-level ESP32 boot structures)
You can flash QRPickle directly from your Chrome or Edge web browser using the Espressif Web Flasher.
- Connect your CYD to your computer via USB.
- Open the Espressif Web Flasher Tool.
- Set your baud rate to
921600and click Connect. - Add the release files to the flash tool using their exact partition offsets:
0x1000➔bootloader.bin0x8000➔partitions.bin0x10000➔firmware.bin0x350000➔littlefs.bin
- Click Program and wait for the process to finish. The device will automatically reboot into QRPickle!
- And the device should boot up, and create a WiFi access-point called
QRPickle-Setupwhich does not require authentication.















