But doesn’t work on ESP8266…
After some testing I noticed I was using eightbitcolor: True, which is RGB332. Changing to False, 16-bpp RGB565, gives a better image but also needs more memory… Well, actually more fragments. fragmentation: 6 was unstable, with crashes every few minutes. fragmentation: 8 is stable, has been running for over 8h.
Noticed very slow performance when drawing images, a full-screen draw took ~750ms. Checked the code, figured Image::draw(...) tries to draw the whole image regardless of the display clipping state. This is bad because fragmentation does 8 calls internally, each clipped to only 1/8th of the display. Updated the code to draw only the visible part, and now it draws the whole screen in ~230ms.
Submitted the patches to github, st7789v as PR#8613, and Image as PR#8630. These can be used as external_components while they are not accepted (see config in the spoiler tag).
Click the image below for a video of it running, so you can get an idea of the refresh speed. The striped page serves to emphasize the side-effects of fractional buffering, i.e., lambda is called once per fragment, therefore fill(rand()) uses a different colors each time and we get stripes.

YAML configuration
esphome:
name: geekmagic-smalltv-01
friendly_name: geekmagic-smalltv-01
platformio_options:
board_build.f_cpu: 160000000L
on_boot:
- priority: 600
then:
- delay: 1s
- output.turn_on: pwm_output
- output.set_level:
id: pwm_output
level: 50%
esp8266:
board: esp12e
external_components:
- source:
type: git
url: https://github.com/lhartmann/esphome.git
ref: dev
refresh: 0s
components: [st7789v]
- source:
type: git
url: https://github.com/lhartmann/esphome.git
ref: image_clipping
refresh: 0s
components: [image]
# Enable logging
logger:
# Enable Home Assistant API
api:
encryption:
key: !secret api_key
ota:
- platform: esphome
password: !secret ota_password
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
# ap:
# ssid: "Geekmagic-Smalltv-01"
# password: !secret fallback_password
captive_portal:
time:
- id: ha_time
platform: homeassistant
timezone: America/Recife
font:
- id: roboto
file: "gfonts://Roboto"
size: 20
- id: terminus12n
file: terminus-font-4.49.1/ter-u12n.bdf
- id: terminus32b
file: terminus-font-4.49.1/ter-u32b.bdf
glyphs: "0123456789:"
- id: spleen64
file:
type: web
url: https://raw.githubusercontent.com/fcambus/spleen/refs/heads/master/spleen-32x64.bdf
glyphs: "0123456789:"
image:
- id: bg_image
file: https://lcsvh.com/ironman.png
type: rgb565
resize: 240x240
spi:
clk_pin: GPIO14
mosi_pin: GPIO13
interface: hardware
id: spihwd
output:
- id: pwm_output
platform: esp8266_pwm
pin: GPIO05
inverted: true
frequency: 1000 Hz
light:
- platform: monochromatic
output: pwm_output
name: "Backlight"
color:
- id: color_green
red: 0%
green: 100%
blue: 0%
- id: color_ironman_red
hex: eb1c24
- id: color_ironman_yellow
hex: fcb712
- id: color_ironman_blueglow
hex: ade0fb
- id: color_white
hex: FFFFFF
interval:
- interval: 5s
then:
- display.page.show_next: my_display
- component.update: my_display
display:
- id: my_display
platform: st7789v
model: custom
spi_id: spihwd
height: 240
width: 240
offset_height: 0
offset_width: 0
fragmentation: 8
dc_pin: GPIO00
reset_pin: GPIO02
eightbitcolor: False
update_interval: never
spi_mode: mode3
data_rate: 40000000
auto_clear_enabled: False
pages:
- id: page_clock
lambda: |-
// Calculate the center positions
int center_x = it.get_width() / 2;
int center_y = it.get_height() / 2;
// Background image (also replaces autoclear)
it.image(0, 0, id(bg_image), ImageAlign::TOP_LEFT);
it.printf(0, 240, id(terminus12n), id(color_ironman_red), TextAlign::BOTTOM_LEFT, "Hello World");
// This is a bad-example, to demonstrate side-effect multiplication.
// With even fragmentation this is cut in half, with k on top and k+1 below.
static int framecounter = 0;
it.printf(240, 240, id(terminus12n), id(color_ironman_yellow), TextAlign::BOTTOM_RIGHT, "%d", framecounter++);
auto now = id(ha_time).now();
if (true || now.is_valid()) {
it.printf(12, 12, id(spleen64), id(color_ironman_red), TextAlign::TOP_LEFT, "%02d", now.hour);
it.printf(240-12, 12, id(spleen64), id(color_ironman_yellow), TextAlign::TOP_RIGHT, "%02d", now.minute);
}
- id: page_blankish
lambda: |-
it.fill(Color(rand()));
This is really cool @lhartmann , thanks for sharing!
Previously, updating every 60s:
With your component (fragmentation: 8, haven’t played around with this setting yet), updating every 5s:
And more importantly, it doesn’t do a massive flicker on every update.
Bought myself one of those monitors Smart Weather Clock ESP8266 I’ve seen in commercials, people set up currency exchange rates for themselves there. But in the settings there are no settings for exchange rates, and I can not find a module or firmware for it with exchange rates! Maybe someone saw something about exchange rates for this monitor?
The ESP8266 based models (SmallTV and SmallTV Ultra) only include the clock/weather screen. Only the ESP32 version (a.k.a. SmallTV Pro) has the additional screens.
If, however, you can get exchange rates via home assistant, then you can create a custom screen starting from the settings I posted above.
Updates on the pull-requests:
PR#8630 for clipping-aware image redraw was merged. It is now accessible by pointing external_components directly from esphome’s dev branch. In a future update this will also be unnecessary.
external_components:
- source:
type: git
url: https://github.com/esphome/esphome.git
ref: dev
refresh: 0s
components: [image]
PR#8613 for the fractional framebuffer was rejected, mostly because the st7789v driver is deprecated in favor of ili9xxx, which is also about to be deprecated in favor of a newer driver. In response to this, I moved my modified version to a separate repository, where it will stay archived.
external_components:
- source:
type: git
url: https://github.com/lhartmann/esphome-st7789v-fractional-framebuffer
ref: main
refresh: 0s
components: [st7789v]
There are plans to bring fraction framebuffer to ALL buffered displays but, while they don’t materialize, the configuration above is expected to remain valid.
And, just for fun, one last picture of it working (draws in 170ms).
Thanks for the updates on your work. I was just playing with your code and it stopped compiling. Luckly I saw the update just as you posted it. Your example is a starting point for me but it’s a great one. Thanks again.
I had a cool idea that I thought I’d share, a decora light switch face plate with this small tv integrated would be a really inexpensive way to get a display in each room, and if you had a pir sensor, it could turn on when it sees motion, so it’s not wasting power. Could be cool, and a LOT cheaper than other options. Also, it appears like when you take it apart, you can make it really really thin.
This is my configuration, maybe someone will find it useful.
esphome:
name: smalltv
friendly_name: SmallTV
esp8266:
board: esp12e
framework:
version: recommended
external_components:
- source:
type: git
url: https://github.com/rletendu/esphome.git
ref: st7789_nobuffer_202312
# refresh: 0s
components: [st7789v]
# Enable logging
logger:
ota:
platform: esphome
# Enable Home Assistant API
api:
encryption:
key: "pihTkDrpuE3BSqFHagILJHX+1ZBQxh97jMuZq3JO6gw="
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
fast_connect: true
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Cash From Cahos"
password: "CFC_Rules_Since_69"
# Ici, on active le serveur WEB du ESP8266, en allant sur l'IP du ESP8266, on tombe sur une interface pour piloter les relais
captive_portal:
spi:
clk_pin: GPIO14
mosi_pin: GPIO13
interface: hardware
id: spihwd
# status_led:
# pin: GPIO2
output:
- platform: esp8266_pwm
pin: GPIO05
frequency: 10 Hz
id: pwm_output
light:
- platform: monochromatic
output: pwm_output
name: "Backlight"
font:
- file: 'Roboto-Italic.ttf'
id: font1
size: 38
- file: 'Ubuntu-Regular.ttf'
id: font2
size: 30
- file: 'Ubuntu-Regular.ttf'
id: font3
size: 20
- file: 'led.ttf'
id: font4
size: 85
time:
- platform: homeassistant
id: esptime
sensor:
- platform: homeassistant
entity_id: sensor.moc_l1_l2_l3
id: Mocl13
internal: true
accuracy_decimals: 1
unit_of_measurement: "W"
device_class: "power"
state_class: "measurement"
- platform: homeassistant
entity_id: sensor.sonoffbasic_ds_multi_out
id: tempzew
internal: true
accuracy_decimals: 2
unit_of_measurement: "°C"
device_class: "temperature"
state_class: "measurement"
- platform: homeassistant
entity_id: sensor.temp_dom
id: poktv
internal: true
accuracy_decimals: 2
unit_of_measurement: "°C"
device_class: "temperature"
state_class: "measurement"
- platform: homeassistant
entity_id: sensor.temperature_max
id: maxout
internal: true
accuracy_decimals: 2
unit_of_measurement: "°C"
device_class: "temperature"
state_class: "measurement"
text_sensor:
- platform: homeassistant
name: "Energy 0/1"
entity_id: input_select.energy_0_1
id: energy
color:
- id: color_w
red: 90%
green: 90%
blue: 90%
- id: color_green
red: 10%
green: 60%
blue: 40%
- id: color_red
red: 100%
green: 0%
blue: 0%
- id: color_blue
red: 10%
green: 40%
blue: 60%
- id: color_blue2
red: 70%
green: 70%
blue: 100%
display:
- platform: st7789v
model: "Custom"
spi_id: spihwd
height: 240
width: 240
offset_height: 0
offset_width: 0
# dc_pin: GPIO02
# reset_pin: GPIO04
dc_pin: GPIO00
reset_pin: GPIO02
#backlight_pin: GPIO25
eightbitcolor: True
#update_interval: never
update_interval: 30s
id: disp
spi_mode: mode3
lambda: |-
const auto RED = Color(255, 0, 0, 0);
// Calculate the center positions
int center_x = it.get_width() / 2;
int center_y = it.get_height() / 2;
// Print "Hello World" centered
// it.printf(center_x, center_y - 20, id(font2), TextAlign::CENTER, "Hello World");
// Display the date in abbreviated weekday, MM/DD/YY format
it.strftime(center_x, 130, id(font1), id(color_blue), TextAlign::CENTER, "%d:%b:%Y", id(esptime).now());
it.strftime(center_x, 190, id(font4), id(color_blue2), TextAlign::CENTER, "%H:%M", id(esptime).now());
// it.print(0, 0, id(font1), RED, "Hello World in RED!");
// it.printf(0, 200, id(font1),"Test1");
// it.print(130, 5, id(font3), id(color_w), "Grid Power:");
it.printf(240,40, id(font2), id(color_w), TextAlign::RIGHT, "%.1fW",id(Mocl13).state);
it.printf(center_x, 90, id(font3), id(color_green), TextAlign::CENTER, "%s", to_string(id(energy).state).c_str());
it.printf(4, 4, id(font1),TextAlign::LEFT, "%.1f°C", id(tempzew).state);
Or any esphome+screen combination.
So i wasn’t able to flash this with ESPhome(i’m a beginner at this) so i just came up with a different solution. I changed the settings to show the image gallery. Deleted all images. And in Home Assistant, i generate an image on sensor change(to generate an image you can do lots of different solutions, i did it with PHP) and just simply upload the file to the device with this api call:
curl --request POST \
--url 'http://192.168.0.36/doUpload?dir=%2Fimage%2F' \
--header 'Content-Type: multipart/form-data' \
--form [email protected]
I setup a shell script to handle the image generation and uploading, triggered by an automation on sensor status change.
And thats it, if you upload a file with the same name, it will just replace it automatically, works almost instantly. This is my setup, just to have a quick look at my door statuses:
Possibilities are endless, because you can upload multiple images and if you setup a slideshow, it will switch between each screen automatically(or generate a GIF that switches between screens).
kmplngj’s Git from here:
It’s a great code for the ultra version. I modified it with monospace font and removed formatting, columns, etc… so there is only small and medium font rendering with colors, and alignment to the left only. I also disabled webserver, so there is muuch more ram for fonts to use and the thing works stable.
does anyone have any advice on how to recover these devices once esphome is installed? mine is now bootlooping, wont connect to wifi, wont setup an ap, and cannot be see by any of my computers! =(
Anyone knows which ESP this cute version has?
https://de.aliexpress.com/item/1005008045089831.html
I have a smalltv-Ultra search for a working yaml config.
Hello! ESP12f or ESP32-wroom-32 version is easier to install ESP-HOME?
There is absolutely no difference in ‘ease of installing’ between esp8266 or esp32, but the later is more modern, faster, usually comes with more flash and can do WPA3. So if you get to choose, go with the esp32 (or even esp32-s3 by now). Yes, all of these need their own device specific YAML files, but at 10’000 above sea level the differences are minor and ‘cosmetic’ only.







