What works where
Browser Via ESP32 (USB) Bluetooth direct
Chrome, Edge, Opera — Windows, macOS, ChromeOS Yes Yes
Chrome, Edge, Opera — Linux Yes After enabling a flag
Brave (computer) Yes After enabling a flag
Firefox 151 or newer (computer) Yes No
Chrome on Android Not supported here Yes
Safari, any browser on iPhone / iPad No No
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
Copy this address, paste it into the address bar and press Enter (pages can't open it as a link):
chrome://flags/#enable-web-bluetoothCopy
In Edge use edge://flags/#enable-web-bluetooth, in Opera opera://flags/#enable-web-bluetooth.
Set Web Bluetooth to Enabled .
If that entry doesn't exist in your version, enable this one instead:
chrome://flags/#enable-experimental-web-platform-featuresCopy
Click Relaunch at the bottom of the page.
Linux — the ESP32 doesn't show up in the list
Via ESP32 (USB) works as is. Bluetooth direct needs a flag
Copy this address, paste it into the address bar and press Enter:
brave://flags/#brave-web-bluetooth-apiCopy
Set Web Bluetooth API to Enabled .
On Linux , also enable:
brave://flags/#enable-experimental-web-platform-featuresCopy
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
Choose Stats screen at the top.
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.
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.
Example: Hanshow Stellar 2.13″ board. Pad positions vary between models. Photo: atc1441 (ATC_TLSR_Paper, MIT).
Flashing, step by step
Open atc1441's official tool:
ATC_BLE_OEPL_Image_Upload .
Click Advanced View , then the UART Flasher tab.
Plug in the adapter, click Open and pick its port. Then click Load ATC_BLE_OEPL.bin (the latest build).
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.
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.
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:
Tag doesn't show up in “Choose tag”
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.