ESL Uploader

Pictures, text and self-updating stats screens for electronic shelf labels (ESL) running the ATC_BLE_OEPL firmware ·

1 Picture

Drop a picture here or click to choose
You can also paste one (Ctrl+V)

1 Text

Drag a line up or down on the preview to place it. Lines stack from top to bottom, centred on the screen; a line that's too wide shrinks to fit.

1 Stats screen

Shows the tag's Bluetooth name (so you can find it), screen and board temperature, battery and the time of the last update. Values are read from the tag at upload.

Tag
—
Battery
—
Screen
—
Board
—
Updated
—
Automatic refresh

2 Tag & layout

Orientation
Fit

Drag the preview to move the picture.

3 Colours

Dithering

4 Send

Not connected

Can't connect?

Log
Load a picture to start

Setup & help

This page talks to the hardware straight from your browser: Web Serial for an ESP32 board over USB, Web Bluetooth for sending directly to a tag. Not every browser has both turned on.

What works where

BrowserVia ESP32 (USB)Bluetooth direct
Chrome, Edge, Opera — Windows, macOS, ChromeOSYesYes
Chrome, Edge, Opera — LinuxYesAfter enabling a flag
Brave (computer)YesAfter enabling a flag
Firefox 151 or newer (computer)YesNo
Chrome on AndroidNot supported hereYes
Safari, any browser on iPhone / iPadNoNo

Your browser: —. Web Serial —, Web Bluetooth —.

Easiest setup: Chrome or Edge on Windows or macOS. Both methods work with no changes.

Two ways to send

  • Bluetooth direct: your computer talks to the tag. Nothing extra to buy, but you upload by hand each time.
  • Via ESP32: an inexpensive ESP32 board on USB (ESP32, -S3, -C3, -C6, e.g. a Seeed XIAO ESP32-C3) does the Bluetooth part for you, and can refresh stats screens by itself every 1–60 minutes while it stays plugged in, even with this page closed.

Automatic refresh of stats screens

The ESLs can't redraw a screen by themselves: something has to send the new one. Two options (details in the ESP32 & auto refresh tab):

  • An ESP32 board refreshes the tags on its list on its own while it stays plugged into the computer, even with the page closed.
  • Bluetooth direct: this page refreshes the chosen tag, but only while it stays open.

Before you start

  • The page must be opened over https:// (or from localhost when testing). Browsers block USB and Bluetooth on plain http.
  • Bluetooth direct: Bluetooth must be switched on in your computer's settings and the tag must be in range (a few metres).
  • Via ESP32: plug in the ESP32 board with a data USB cable (some cables only charge) and close any other program using it, such as the Arduino serial monitor. Only one program can use the port at a time.

Windows, macOS, ChromeOS

Nothing to set up. Both Via ESP32 and Bluetooth direct work out of the box.

Linux — enable Bluetooth direct

  1. Copy this address, paste it into the address bar and press Enter (pages can't open it as a link):
    chrome://flags/#enable-web-bluetooth
    In Edge use edge://flags/#enable-web-bluetooth, in Opera opera://flags/#enable-web-bluetooth.
  2. Set Web Bluetooth to Enabled.
  3. If that entry doesn't exist in your version, enable this one instead:
    chrome://flags/#enable-experimental-web-platform-features
  4. Click Relaunch at the bottom of the page.

Linux — the ESP32 doesn't show up in the list

  • Your user needs access to serial ports. Run this once, then log out and back in:
    sudo usermod -aG dialout $USER
  • Chromium installed as a snap (Ubuntu's default) also needs USB access:
    sudo snap connect chromium:raw-usb

Via ESP32 (USB) works as is. Bluetooth direct needs a flag

  1. Copy this address, paste it into the address bar and press Enter:
    brave://flags/#brave-web-bluetooth-api
    Set Web Bluetooth API to Enabled.
  2. On Linux, also enable:
    brave://flags/#enable-experimental-web-platform-features
  3. Click Relaunch.
Flag names change between Brave versions. If a link shows nothing, type bluetooth into the search box at the top of brave://flags.

Brave on Linux also needs the dialout group step from the Chrome tab if the ESP32 doesn't appear.

Use “Via ESP32 (USB)”

Firefox has Web Serial from version 151 on computers, so the ESP32 method works. The first time, Firefox asks you to allow serial access for this site, then shows the port list.

  • Older Firefox: update it via Menu → Help → About Firefox.
  • Linux: add yourself to the dialout group (see the Chrome tab) if the ESP32 doesn't appear.

Bluetooth direct isn't available

Firefox doesn't support Web Bluetooth, and no setting turns it on. Use the ESP32, or open this page in Chrome or Edge.

Safari (Mac) and every browser on iPhone / iPad

Apple's WebKit engine supports neither Web Serial nor Web Bluetooth, and every iPhone and iPad browser uses WebKit, Chrome included. No setting changes that.

  • Mac: install Chrome or Edge. Both methods then work with no setup.
  • iPhone / iPad: use a computer or an Android phone with Chrome. Third-party Web Bluetooth apps (for example Bluefy) may work for Bluetooth direct, but we haven't tested them.

What the ESP32 does

Any ESP32 board with Bluetooth (ESP32, ESP32-S3, ESP32-C3, ESP32-C6; not the ESP32-S2, which has no Bluetooth) running our free ESP32_ATC_OEPL_Bridge sketch acts as a Bluetooth sender. A Seeed XIAO ESP32-C3 is a tiny, cheap choice (attach the small antenna that comes with it: the board has none). Plugged into this computer it carries your uploads (Via ESP32) and keeps the stats screens on its list up to date by itself, even with the page closed: every few minutes it connects to each tag, reads its battery and temperatures, draws the screen and uploads it.

Turning on automatic refresh

  1. Choose Stats screen at the top.
  2. With the ESP32 (works with the page closed): Via ESP32 (USB) → Connect → Scan, pick the tag, tick Keep this tag's stats screen up to date and set the interval (1–60 minutes). The list and the interval are saved on the ESP32 and survive a power cut, and you can close the page: the ESP32 keeps refreshing while it stays plugged into the computer. The interval is shared by every tag on the list.
  3. With Bluetooth direct (only while the page is open): Bluetooth direct → Choose tag, tick the box and set the interval. Closing the page, or the computer going to sleep, stops it.
Every refresh redraws the whole screen, which takes ~15 s on black/white/red tags and is what uses the battery. 10 minutes or more is a good balance; 1 minute is for testing.

The clock on the stats screen

The ESP32 has no battery-backed clock. It gets the time from this page every time you connect, and keeps it while it stays powered. After a power cut, the screens show Updated --:-- until the page (or any computer sending it time) connects again. It never uses WiFi: it only bridges USB to Bluetooth.

The time zone is taken from your browser when the page connects (or set it with tz, e.g. tz EET-2EEST,M3.5.0/3,M10.5.0/4).

Installing the sketch

The sketch is free and open source: ESP32_ATC_OEPL_Bridge on GitHub (download, full instructions and all its serial commands).

  • Arduino IDE with the esp32 boards package; pick your board (e.g. XIAO_ESP32C3, ESP32 Dev Module, ESP32C3 Dev Module).
  • USB CDC On Boot: Enabled if the board's USB socket is wired straight to the ESP32 chip (XIAO, "SuperMini" boards, the USB port of an S3/C3 DevKit); Disabled if it goes through a USB-serial chip (CP2102, CH340: most classic ESP32 boards). With the wrong setting the board stays silent.
  • Libraries: NimBLE-Arduino (2.x) and Adafruit GFX.
  • Open ESP32_ATC_OEPL_Bridge.ino (it needs render.h and fonts.h next to it) and upload.

Which firmware the tag needs

This page talks to tags running ATC_BLE_OEPL, a free custom firmware by atc1441. ESLs from a store come with the shop's own firmware and won't work until you replace it once, over a wire. After that, updates go over Bluetooth.

  • Covered here: Hanshow electronic shelf labels (Stellar series) with a Telink TLSR825x chip (e.g. TLSR8359). Open the case and read the chip marking first. Identical-looking tags with other chips need a different firmware.
  • Flashed tags show up in Bluetooth as ATC_xxxxxx.
  • You flash at your own risk. The original firmware is erased and can't be restored unless you backed it up yourself.

What you need

  • A 3.3 V USB-to-serial adapter (CP2102, CH340, FT232…). 5 V can destroy the chip.
  • A 1 kΩ resistor, thin wires (or pogo pins), and a soldering iron or a steady hand.
  • Chrome, Edge or Brave on a computer. The flasher also uses Web Serial.

Wiring

  • Adapter GND → tag GND
  • Adapter 3V3 → tag VCC. Take the batteries out while the adapter powers the tag.
  • Adapter TX → 1 kΩ → tag SWS (labelled SWS_RXD on many boards)
  • Optional: adapter RTS → tag NRST. This lets the flasher reset the chip by itself, but it doesn't work on every board.
Hanshow ESL circuit board with wires from a USB-to-serial adapter: 3V3 to VCC, TXD to SWS_RXD, RTS to NRST, GND to GND
Example: Hanshow Stellar 2.13″ board. Pad positions vary between models. Photo: atc1441 (ATC_TLSR_Paper, MIT).

Flashing, step by step

  1. Open atc1441's official tool: ATC_BLE_OEPL_Image_Upload. Click Advanced View, then the UART Flasher tab.
  2. Plug in the adapter, click Open and pick its port. Then click Load ATC_BLE_OEPL.bin (the latest build).
  3. Click Unlock Flash. If it doesn't take, set Activation to 2–3 s, try again and power-cycle the tag (unplug 3V3 for a second) while the flasher is trying to catch the chip.
  4. Pick your type in Set Device Type and click Write Firmware & Type. Common Hanshow types:
    • 19 / 47: 2.13″ BWR (UC / V2)
    • 16 / 45: 2.13″ BWR (SSD / V2)
    • 10 / 25: 2.13″ BW (UC / SSD)
    • 18: 1.54″ BWR
    • 24: 2.9″ BWR
    • 20 / 21: 4.2″ BWR (SSD / UC)
    BWR = black/white/red, BW = black/white. UC/SSD is the panel's controller chip. If unsure, try one: a wrong type gives a blank or garbled picture but does no harm, and you can change it later.
  5. Remove the wires, put the batteries back. The tag now advertises as ATC_xxxxxx. Come back to this page and use Bluetooth direct or the ESP32.

Afterwards (no wires needed)

  • Wrong picture / wrong size: in the official tool (Advanced View → BLE Connection), connect to the tag, choose Set Device Type and click Set Type.
  • Firmware updates: same place, click Flash latest ATC_BLE_OEPL.bin. It installs over Bluetooth.
  • Supported by this page right now: 2.13″ (250×122) and 4.2″ (400×300) tags. The page reads the size from the tag.

“The device has been lost” / the ESP32 connects but scan or upload fails (Linux)

Some Chromium-based browsers (Brave and older Chrome builds) choke on a serial port that the Arduino serial monitor or a Python script used last. Fix it with either of these, then press Connect again:

  • Unplug the ESP32 and plug it back in, or
  • run
    stty -F /dev/ttyACM0 min 1
    (boards with a CP2102/CH340 chip appear as /dev/ttyUSB0 instead)

Tag doesn't show up in “Choose tag”

  • Check that Bluetooth is on. On Linux:
    rfkill unblock bluetooth; bluetoothctl power on
  • Move closer. Tags advertise only every few seconds, so give the list ~10 s.
  • Only tags running the ATC_BLE_OEPL firmware appear (names start with ATC_).
  • A tag accepts one connection at a time. Disconnect other phones, apps or the ESP32 first.

Scan via ESP32 finds nothing / “Tag not found”

  • The ESP32 board must run the bridge sketch (see the ESP32 & auto refresh tab). Open the Log under the Upload button: it should show “Scanning 6 s…”.
  • Keep the tag within a few metres of the ESP32 board. Small boards have short-range antennas; a XIAO ESP32-C3 needs its antenna attached.

Stats screen shows “Updated --:--” or doesn't refresh

  • --:--: the ESP32 lost its clock (power cut). Connect this page once.
  • ESP32 refresh: in Stats screen → Via ESP32 → Connect, check that the tag is on the list and look at Last refresh. A tag that's out of range or busy redrawing is retried once after 20 s.
  • Bluetooth direct refresh stops when the page is closed or the computer sleeps.
  • A tag that's busy redrawing (~15 s) doesn't show up in a scan. Scan again.

Upload works but the picture looks wrong

  • Pick the right tag size and orientation in step 2. The page reads the size from the tag and warns about a mismatch.
  • Black/white/red tags take ~15 s to refresh and flash several times. That's normal.