Skip to main content

Logo

Pixel Display Library

Control all your 16x16 or 32x32 or 64x64 iDotMatrix Pixel Displays

Report Bug or Request Feature

Contributors License

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

  1. Clone the repo
git clone https://github.com/markusressel/python3-idotmatrix-library.git
  1. 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 font
  • 8x8 font, 5x7 font, dot matrix font
  • LED font / LCD font
  • retro 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)
  • Create Images or GIFs
    • pixilart
      • Create a new drawing and select a preset for the size of your display (e.g. 64x64)

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.

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

  1. Fork the Project
  2. Create your Feature Branch (git checkout -b feature/AmazingFeature)
  3. Commit your Changes (git commit -m 'Add some AmazingFeature')
  4. Push to the Branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

License

Distributed under the GNU GENERAL PUBLIC License. See LICENSE for more information.

Acknowledgements

Original project by derkalle4 and jmgraeffe.

Metadata

Release files for idotmatrix-api-client 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for idotmatrix-api-client 0.2.0
File Size Uploaded Provenance
idotmatrix_api_client-0.2.0.tar.gz 420.1 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for idotmatrix-api-client 0.2.0
File Interpreter ABI Platform
idotmatrix_api_client-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 871.0 kB

Release files / idotmatrix_api_client-0.2.0.tar.gz

Download URL idotmatrix_api_client-0.2.0.tar.gz
Size 420.1 kB
Tags Source
SHA-256 checksum
How to use checksums
ed491a3c3837afbbc2642c9d7ae474631ead745d42e6cfc4b610744d63df0416
BLAKE2b-256 checksum
How to use checksums
e4f964e90292deec9a69cc349fdc35b69d06753febef755cb663f2fc3dda8f70
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 log

Release files / idotmatrix_api_client-0.2.0-py3-none-any.whl

Download URL idotmatrix_api_client-0.2.0-py3-none-any.whl
Size 450.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
512421d3671ac0fedc0bed3d36af27e24b5f65acc57c13a3a6946cae5d3b5094
BLAKE2b-256 checksum
How to use checksums
ea2015c59a782bcd2aa1a0a968904f28a8db9587a6c8333dea686b5f3321abce
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 log

Security for idotmatrix-api-client 0.2.0

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.

Select from 2 files

Attestation for idotmatrix_api_client-0.2.0.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 log

A 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.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page