Pixel Display Library
Control all your 16x16 or 32x32 or 64x64 iDotMatrix Pixel Displays
Report Bug
or
Request Feature
Table Of Contents
About The Project
This repository aims to reverse engineer the iDotMatrix Android App for pixel screen displays like this one on Aliexpress. The goal is to provide a library which can be used to connect and control these displays without the need for the official app.
Getting Started
Prerequisites
Please install the following for your distribution (Windows may work but it is untested):
- latest Python3
- Python3 Virtual Env
Installation
use latest github source code
- Clone the repo
git clone https://github.com/markusressel/python3-idotmatrix-library.git
- Install the latest version locally
cd python3-idotmatrix-library/
python3 -m venv venv
source venv/bin/activate # on Windows use `venv\Scripts\activate`
pip install --upgrade pip poetry
poetry install
Usage
Device API Client
import asyncio
from idotmatrix import IDotMatrixClient, ScreenSize
async def main():
# create a new IDotMatrixClient instance
client = IDotMatrixClient(
# (optional) the screen size of your device, ScreenSize.SIZE_64x64, SIZE_32x32 or SIZE_16x16.
# If it is left out, the client asks the device when connecting (so call `connect` before using images or GIFs).
screen_size=ScreenSize.SIZE_64x64,
# mac_address="00:11:22:33:44:55", # (optional) specify your device's Bluetooth address
)
# (optional) connect to first found iDotMatrix Pixel Display
# If the device is not connected when issuing commands, an automatic connection attempt will be made.
await client.connect()
# do something with the device by using one of the toplevel functions or modules e.g. chronograph, clock, countdown, etc.
await client.set_brightness(50) # set brightness to 50%
await client.text.show_text("Hello World!") # display text on the screen
if __name__ == "__main__":
try:
asyncio.run(main())
except KeyboardInterrupt:
quit()
For more examples please check the example.py.
Images and GIFs
from idotmatrix.util.image_utils import ResizeMode
# show a photo or any other image, scaled to the screen
await client.image.show_image_file("photo.jpg", resize_mode=ResizeMode.FILL)
# show an animated GIF
await client.gif.upload_gif_file("animation.gif")
Both can be mixed freely, no mode switching is needed in between. They work like the official app, which waits
for the device to acknowledge each chunk of data. An UploadError is raised if the device rejects an upload or does
not answer.
client.image.upload_image_file uses the DIY mode instead (see client.image.set_mode), which is meant for drawing
pixel by pixel and has to be left again before a GIF can be shown.
Home Assistant and other Bluetooth stacks
A Home Assistant integration that is built on this library is in development: ha-idotmatrix.
By default the library connects with bleak itself. If something else owns the Bluetooth stack, like the scanner and
the Bluetooth proxies of Home Assistant, hand over what it found or how to connect:
# a BLEDevice that a scanner found, instead of a MAC address
client = IDotMatrixClient(screen_size=ScreenSize.SIZE_64x64, ble_device=ble_device)
# or provide the whole connection, by implementing `idotmatrix.transport.Transport`
client = IDotMatrixClient(
screen_size=ScreenSize.SIZE_64x64,
mac_address="00:11:22:33:44:55",
transport_factory=lambda address_or_device, on_disconnected: MyTransport(address_or_device, on_disconnected),
)
Turn off install_signal_handlers if your application handles SIGINT and SIGTERM itself, otherwise the client
replaces the handlers of the event loop to close the connection on exit. To connect with bleak-retry-connector
(Bluetooth proxies), create the transport yourself: BleakTransport(address_or_device, on_disconnected, connector=...)
where the connector is an async function that returns a connected BleakClient, like establish_connection.
ConnectionManager.set_device(ble_device) hands over a newer BLEDevice later. An established connection stays, the
new one is used for the next connection. A Transport has to write to the device, deliver its notifications and call
on_disconnected when the link is lost, see idotmatrix/transport.py and the FakeTransport in tests/ for an example.
Digital Picture Frame
Besides the IDotMatrixClient, this repository also contains a DigitalPictureFrame class which can be used
to display images and GIFs in a slideshow fashion.
See example_digital_picture_frame.py
for more details on how to use it.
The DigitalPictureFrame can
- display a static list of images and/or GIFs
- watch one or more directories for images and GIFs
- automatically reconnect to the device in case of an error or connection loss
Fonts
text.display_text() (and TextModule.show_text()) rasterize each character directly to on/off
pixels (1-bit, no anti-aliasing) and send it to the device using one of two fixed per-character
cell sizes:
- a compact 8x16 cell, used automatically whenever every character of the string fits within 8px wide / 16px tall
- the full 16x32 cell, used as a fallback for wider/taller glyphs (to avoid clipping)
Because there's no anti-aliasing step, regular vector fonts (the kind designed for print/screen text with serifs, thin strokes, or fine curves) often render with broken strokes, gaps, or lopsided shapes at these tiny sizes - especially once a font's fine details get thresholded straight to black/white. This shows up as visual "artifacts": jagged edges, disconnected letterforms, or an uneven look between different characters of the same font.
Pixel/bitmap-style fonts avoid this problem entirely, since they are designed on a fixed pixel grid to begin with (no fine detail to lose) and render cleanly at any size that's a clean multiple of their native grid.
font_size doesn't grow the per-character cell
The 8x16/16x32 cell picked above is fixed in pixel width (8px or 16px) regardless of the
font_size you pass in - font_size only changes how big the glyph is drawn within that fixed
cell, it never widens the cell itself. Each character's cell bitmap is then sent back-to-back with
no extra spacing added between characters.
This means a font that isn't specifically designed to stay narrow at the font_size you're using
will render a glyph that fills (or even overflows) the entire 8px/16px cell width, leaving no blank
margin to act as letter-spacing - so consecutive characters end up touching or overlapping. This is
why the same font can look perfectly fine at one font_size and become illegible at a larger one:
a font's glyph width generally scales with font_size, but the cell width doesn't.
The bundled default (Rain-DRM3.otf) works around this by being intentionally narrow relative to
its point size, so it still leaves a gap even at larger sizes. Most other fonts need a specific,
manually-verified font_size to look right - see the naming convention below.
Bundled fonts
The idotmatrix/fonts/ directory is part of the installed package and ships a selection of pixel/bitmap fonts (with their respective
license files) that have been verified to render cleanly. Each filename (other than the default) is
prefixed with the font_size value(s) it has been manually tested and confirmed to render with
legible spacing between characters - always pass a matching font_size, since (per above) using a
larger one than the font was designed for will make characters touch/overlap:
| Font file(s) | Recommended font_size |
Notes |
|---|---|---|
16-PressStart2P-Regular.ttf |
16 | Classic 8x8 arcade-style pixel font. Most reliable default at size 16. |
16-dogica.ttf, 16-dogicabold.ttf, 16-dogicapixel.ttf, 16-dogicapixelbold.ttf |
16 | Small pixel-grid font family, regular/bold/"pixel" variants. |
16-PixelOperator8.ttf, 16-PixelOperator8-Bold.ttf |
16 | 8px-grid pixel font family. |
16-MinecraftStandard.otf, 16-MinecraftStandardBoldOblique.otf |
16 | Blocky pixel-grid font, good legibility at small sizes. |
16-ErbosDraco1StOpenNbpRegular-l5wX.ttf |
16 | Narrow pixel-style font (fits the compact 8x16 cell at size 16). |
16-PixelifySans-Regular.ttf |
16 | Rounded pixel-grid font. |
16_32-HomeVideo-BLG6G.ttf, 16_32-HomeVideoBold-R90Dv.ttf |
16 or 32 | Wider retro/VHS-style font, verified to keep readable spacing at both sizes. |
16_32-MayfairNbpBold-gAA4.ttf |
16 or 32 | Bold pixel-style display font, verified to keep readable spacing at both sizes. |
32-Jersey10-Regular.ttf |
32 | Retro digital/LCD-style font, verified at size 32. |
Rain-DRM3.otf |
any | Default font, used when no font_path is given. Narrow enough to stay legible across sizes. |
Pick whichever fits your desired look, but stick to the recommended font_size(s) listed above (or
your own tested size) for that font - otherwise characters may run into each other. Narrower fonts
(that fit within 8px wide per character at the size used) will also get tighter, more compact text
since they qualify for the 8x16 cell instead of the 16x32 one.
Font licenses
Each font keeps its own license, shipped next to it in idotmatrix/fonts/. They do not change the
license of this library's code.
| Font(s) | License |
|---|---|
| Rain-DRM3, Dogica, Press Start 2P, Pixelify Sans, Jersey 10 | SIL Open Font License 1.1 |
| Pixel Operator | CC0 1.0 |
| Home Video, Minecraft Standard | Public Domain |
| Erbos Draco 1st Open NBP (source) | CC BY-SA |
| Mayfair NBP Bold (source) | CC BY-SA |
The two CC BY-SA fonts are redistributed unmodified. Copies of derived fonts must be shared under the same license, with attribution to the original authors (see the links above).
Finding more fonts
When looking for additional fonts, search for:
pixel font/bitmap font8x8 font,5x7 font,dot matrix fontLED font/LCD fontretro pixel font/monospace pixel font
Good sources: dafont.com/bitmap.php, fontspace.com's pixel-fonts tag
Always test a new font at the font size you intend to use before committing to it - some
pixel-style fonts are only pixel-perfect at their native design size and look uneven at others.
Check each font's license before bundling/redistributing it (see the existing *-License.txt
files next to each font in idotmatrix/fonts/ for examples).
Roadmap
If you want to contribute please focus on the reverse-engineering part because my personal skills are not that good. Many thanks for all contributions! If you want to dive deep into other issues please check for "#TODO" comments in the source code as well.
- Reverse Engineering
- Chronograph
- Clock
- Countdown
- Graffiti Board
- DIY-Mode
- Animated Images
- Display Text
- Alarm & Buzzer (available according to issue #18)
- Cloud-API to download images
- Cloud-API to upload images to device
- Cloud-Firmware Update possible?
- Eco-Mode
- Fullscreen Color
- MusicSync (with the microphone of the device)
- Scoreboard
- bluetooth password protection
- understand the returned byte arrays of the device for better error logs (done for the upload acknowledgements, see the protocol notes in AGENTS.md)
Helpful Links
- Create Images or GIFs
- pixilart
- Create a new drawing and select a preset for the size of your display (e.g. 64x64)
- pixilart
Other Hardware
This software is specifically built for devices which are supported by the iDotMatrix app. There are other devices being sold which have a similar form factor and claim to be compatible, but which are not. Below is a list of known non-iDotMatrix-compatible hardware and associated software.
- For hardware supported by the "LOY SPACE" app: https://github.com/eskibars/popledder/
Contributing
Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are greatly appreciated.
- If you have suggestions for adding or removing projects, feel free to open an issue to discuss it, or directly create a pull request after you edit the README.md file with necessary changes.
- Please make sure you check your spelling and grammar.
- Create individual PR for each suggestion.
- Please also read through the Code Of Conduct before posting your first idea as well.
Creating A Pull Request
- Fork the Project
- Create your Feature Branch (
git checkout -b feature/AmazingFeature) - Commit your Changes (
git commit -m 'Add some AmazingFeature') - Push to the Branch (
git push origin feature/AmazingFeature) - Open a Pull Request
License
Distributed under the GNU GENERAL PUBLIC License. See LICENSE for more information.
Acknowledgements
Original project by derkalle4 and jmgraeffe.
- Othneil Drew - README Template
- LordRippon - Reverse Engineering for the Displays
- 8none1 - Reverse Engineering for the Displays
- schorsch3000 - smaller fixes
- tekka007 - code refactoring and reverse engineering
- inselberg - Reverse Engineering for the Displays
Metadata
Release files for idotmatrix-api-client 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| idotmatrix_api_client-0.2.1.tar.gz | 420.2 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| idotmatrix_api_client-0.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 871.2 kB
Release files / idotmatrix_api_client-0.2.1.tar.gz
| Download URL | idotmatrix_api_client-0.2.1.tar.gz |
|---|---|
| Size | 420.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7acc4495c652112794d68659132ef1286c423ca3685be704541810a2baf4242a
|
|
BLAKE2b-256 checksum How to use checksums |
d0e6408eacbb15a241d5e39dc4b0fb37e74c8a86076dab104d2ace083b8a10a0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logRelease files / idotmatrix_api_client-0.2.1-py3-none-any.whl
| Download URL | idotmatrix_api_client-0.2.1-py3-none-any.whl |
|---|---|
| Size | 450.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
48f662f1d9a2e4cb026993b822b50edb45fea9fb0ff09ec4cf166512438d1547
|
|
BLAKE2b-256 checksum How to use checksums |
714a3c140bc170a9a14437f103013eede160bd575089493a50568cb87da46bcd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logSecurity for idotmatrix-api-client 0.2.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for idotmatrix_api_client-0.2.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.