This repo documents the LCD interface inside the HelloCubic Lite cube (ESP8266) from GeekMagicClock
It also comptabile with the Smalltv-Ultra from GeekMagicClock
- Important information
- Teardown
- Screen hardware configuration
- How the screen works
- What's next ?
- The firmware
- Install guide
- License
- Support
Warning: I am not responsible for bricking your devices. Flash at your own risk
I recommend making a complete backup of your flash before doing anything I've upload my factory backup (version 7.0.17 for cube and 9.0.40 for small tv); it might be useful. Backup tested and approved, it works
Version i've bought :
- MCU: ESP8266
- LCD controller: ST7789 (RGB565)
- Case: 3d printed
- Controller: ST7789
- Resolution: 240x240 pixels
- Color Format: RGB565 (16-bit color)
- Interface: SPI (Serial Peripheral Interface)
- SPI Speed: 40 MHz (80 MHz is possible, but unstable and outside datasheet spec)
- Rotation: Upside-down for cube display, normal for the small tv
The wiring is the same form small TV and cube
The display is connected to the ESP8266 using the following GPIO pins:
| Function | GPIO Pin | Description |
|---|---|---|
| MOSI | GPIO 13 | SPI Master Out Slave In (data from ESP8266 to screen) |
| SCK | GPIO 14 | SPI Clock |
| CS | GND | Chip Select, tied permanently to GND |
| DC | GPIO 0 | Data/Command select (LOW=command, HIGH=data) |
| RST | GPIO 2 | Reset pin |
| Backlight | GPIO 5 | Backlight control (Active LOW) |
Chip select (CS) polarity: This board ties CS of the display permanently to GND.
SPI mode: SPI Mode 3 (CPOL=1, CPHA=1)
Data/command pin: LOW for commands, HIGH for data
Backlight: Active-low control - set GPIO 5 LOW to turn the backlight on, HIGH to turn it off
The firmware initializes the display through the lcdEnsureInit() function which performs the following steps:
-
Backlight activation: GPIO 5 is configured as output and driven based on the
LCD_BACKLIGHT_ACTIVE_LOWconfiguration (typically driven LOW to turn on the backlight) -
SPI bus initialization: Hardware SPI is initialized with:
- Clock speed: Defined by
LCD_SPI_HZ(typically 40 MHz) - Mode: Defined by
LCD_SPI_MODE(Mode 3 required for this display)
- Clock speed: Defined by
-
Hardware reset sequence: The RST pin (GPIO 2) is toggled with timing:
- Set HIGH → wait 120ms → Set LOW → wait 120ms → Set HIGH → wait 120ms
-
Display controller initialization: A vendor-specific initialization sequence is executed via
lcdRunVendorInit()which includes:- Sleep out (0x11) with 120ms delay
- Porch settings (0xB2) with parameters: HS=0x1F, VS=0x1F, Dummy=0x00, HBP=0x33, VBP=0x33
- Tearing effect (0x35) set to OFF (0x00)
- Memory access control/MADCTL (0x36) set to default (0x00)
- Color mode (0x3A) set to 16-bit RGB565 (0x05)
- Power control settings:
- Power B7 (0xB7) = 0x00
- Power BB (0xBB) = 0x36
- Power C0 (0xC0) = 0x2C
- Power C2 (0xC2) = 0x01
- Power C3 (0xC3) = 0x13
- Power C4 (0xC4) = 0x20
- Power C6 (0xC6) = 0x13
- Power D0 (0xD0) = 0xA4, 0xA1
- Power D6 (0xD6) = 0xA1
- Gamma correction (0xE0, 0xE1) with predefined curves (14 bytes each)
- Gamma control (0xE4) = 0x1D, 0x00, 0x00
- Display inversion (0x21)
- Display ON (0x29)
- Column address setup (0x2A): 0x00 to 0xEF
- Row address setup (0x2B): 0x00 to 0xEF
- RAM write command (0x2C)
-
Post-initialization:
- 10ms delay for display stabilization
- Display rotation is applied (from configuration via
getLCDRotationSafe()) - Screen is filled with black and text color is set to white
The ST7789 communicates via SPI with the following signal handling:
- Clock: SCK (GPIO 14) - drives the SPI clock at the configured frequency (40 MHz)
- Data: MOSI (GPIO 13) - carries command bytes or pixel data from ESP8266 to display
- Chip Select: CS is permanently tied to GND (always active)
- Data/Command mode: DC pin (GPIO 0) indicates the type of data:
- DC = LOW: Command byte follows
- DC = HIGH: Pixel/parameter data follows
The display requires SPI Mode 3 (CPOL=1, CPHA=1) which is explicitly configured in the initialization sequence.
The firmware uses the Arduino_GFX library with a custom ST7789 display driver. Drawing operations are managed through the global g_lcd instance:
-
Text rendering:
- Implemented via
lcdDrawTextWrapped()which provides word-wrapping support - Text wrapping algorithm handles spaces, tabs, and newlines
- Text size is scaled by integer multipliers (6×8 pixels per character at size 1)
- Supports automatic line wrapping with configurable character and line limits
- Implemented via
-
Graphics primitives:
- Direct access to Arduino_GFX API via
DisplayManager::getGfx() - Rectangle filling:
fillRect(x, y, width, height, color) - Full screen fills:
fillScreen(color) - Direct SPI writes are batched between
beginWrite()andendWrite()calls
- Direct access to Arduino_GFX API via
-
GIF playback:
- Managed via the
Gifclass instances_gif - Supports full-screen GIF playback with optional duration limits
- Can be stopped at any time via
DisplayManager::stopGif()
- Managed via the
-
Performance optimizations:
- Hardware SPI: Uses ESP8266's hardware SPI peripheral (40 MHz) for efficient transfers
- Batch writes: Commands and data are batched between
beginWrite()/endWrite()calls - Yield calls:
yield()is called during long operations to prevent watchdog timeout - Direct streaming: GIF frames are streamed directly without intermediate buffering
The display uses RGB565 (16-bit) color encoding:
- Red channel: 5 bits (bits 15-11)
- Green channel: 6 bits (bits 10-5)
- Blue channel: 5 bits (bits 4-0)
This format provides 65,536 distinct colors and is the standard for ST7789 displays.
Common color constants (defined in DisplayManager.h):
-
Black:
0x0000 -
White:
0xFFFF -
Red:
0xF800 -
Green:
0x07E0 -
Blue:
0x001F -
Cyan:
0x07FF -
Magenta:
0xF81F -
Yellow:
0xFFE0etc...
Okay, now we have a minimal firmware that works.
I really like ESP devices and I really enjoy working with ESP-IDF, so I’m planning if possible (I haven’t checked compatibility yet, I’m still new to this world) to create a firmware close to the original one in terms of features, but fully open source ofc \o/
Since ESP IDF is not compatible with esp8266, i'm going to build the firmware on top of plateformIO
That’s the project, at least
This is the "real" firmware I want to improve, with clean and reliable code
| Component | Technology / Library | Main Role |
|---|---|---|
| Microcontroller | ESP8266 (esp12e) | Main hardware platform |
| Build environment | PlatformIO | Project management, build, upload |
| Framework | Arduino Framework | Software base for ESP8266 |
| Filesystem | LittleFS | Local storage LittleFS |
| Graphics display | Arduino_GFX Library | ST7789 display management (SPI, RGB565) |
| Web UI (frontend) | Pico.css, Alpine.js | Minimalist web user interface |
To use the open-source firmware on your compatible GeekMagic device, follow these steps:
git clone https://github.com/Times-Z/GeekMagic-Open-Firmware.gitcp data/config-{hellocubic|smalltv}.example data/config.jsonYou can edit this JSON file to configure your firmware, for example by modifying wifi_ssid and wifi_password so that your device connects to your network
Note: The Wi-Fi credentials and API token in config.json are migrated to EEPROM "secure storage" on first boot. After that, these credentials are erased from config.json
Configuration options:
wifi_ssid: Your WiFi network namewifi_password: Your WiFi passwordapi_token: Bearer token for API authenticationlcd_rotation: Display rotation settingntp_server: NTP server for time synchronization
Security of stored secrets:
- All sensitive values (API keys, wifi credentials, tokens, etc.) are stored in EEPROM using a device-unique obfuscation scheme
- The obfuscation key is derived from the ESP8266's MAC address, chip ID, and a salt (configurable here in code) using SHA-256
- The JSON payload is XORed with this derived key before being written to EEPROM, and de-obfuscated on read
- This makes it much harder to recover secrets from a raw flash dump on another device, or with only partial knowledge of the hardware
Limitations:
- This is not true encryption and does not provide hardware-backed security or protection against a determined attacker with full device access
- The public salt is not secret; if an attacker knows the salt, MAC, and chip ID, they can reconstruct the key
- There is no secure element or tamper resistance
Do not assume confidentiality against a determined attacker
Warning: If the salt, the chip ID or the mac address change, the eeprom secure storage is clear and reset to ensure no data leak is possible
To build the firmware you can use decontainer, docker or build it by yourself using PlateformIO
pio run && pio run --target buildfs
# or using devcontainer aliases
build && buildfs
# or docker
./scripts/build-with-docker.shThe generated files will be located in:
.pio/build/esp12e/
There are two possible flashing methods:
- OTA (Over-The-Air) – no prerequisites
- USB – requires a USB-to-TTL converter to connect to the ESP
Flashing is done in two steps
Go to:
http://{your_geekmagic_ip}/update
This is the update endpoint of the original firmware. Upload the firmware.bin file
Once this step is complete, the device will reboot. Depending on the model, the screen orientation may be correct or not — this is normal
At this point, only the firmware has been flashed. The filesystem (configuration, web app) still needs to be flashed
The firmware will create a Wi‑Fi access point with the following credentials:
- SSID:
GeekMagic - Password:
$str0ngPa$$w0rd
Connect to this access point and go to:
http://192.168.4.1/legacyupdate
From there, select and flash the filesystem using the littlefs.bin file
Once the device reboots, the setup is complete!
This project is licensed under the GPLv3 License - see the LICENSE file for details
- Found a bug or question ? Open an issue
Made with ❤️
Star us on GitHub if you find this project useful !
This project took me a lot of time !




