Open-source control for Grandwell LED signs — a modern replacement for MIM!Plus (2003).
Thousands of Grandwell LED message boards from the early 2000s are still perfectly good hardware: bright, reliable, built to run 24/7. What killed them is software. The original control program, MIM!Plus, is a 16-bit Windows application from 2003 — it simply cannot run on any modern 64-bit Windows, and Grandwell's era of RS-232 signs is long discontinued. Working signs get unplugged and scrapped because nothing can talk to them anymore.
This project fixes that. The sign's serial protocol (internally tagged
KSU) was fully reverse-engineered, byte by byte, and reimplemented as
a small pure-Python library that runs on Windows 11, Linux, or anything
with a USB-serial adapter.
It is not a toy: this code drives the patient-queue display of a busy public health clinic in Mexico, every working day, on a ~20-year-old Grandwell Flash FL-TLR-2420.
- Text messages — up to 3 simultaneous lines, positioned freely (X, Y)
- Rotating pages — several screens that cycle on the sign after a single write (the sign animates by itself; you can unplug the PC)
- Colors — red / green / yellow / off, on tricolor models
- Entry effects — instant, curtain open, random pixels, flash
- Scrolling marquee — any length of text, rendered from a real font
- Big text — full-height proportional text, auto-fitted to the panel
- Self-updating clock — date/time blocks the sign keeps ticking on its
own, plus a
SetClockcommand to sync the sign's internal clock (something MIM!Plus itself failed to transmit on our hardware) - Page duration (
Delay) and per-page effects
| Part | Notes |
|---|---|
A Grandwell sign with an RS232 IN (4P4C) jack |
Verified: Flash FL-TLR-2420. Other MIM!Plus-era models very likely speak the same protocol — please report! |
| DB9-female → 4P4C cable | Sold cheaply online (often labeled "4nb"). See HARDWARE.md |
| USB → DB9 serial adapter | Verified with Prolific PL2303; FTDI should work too |
Full wiring details, gotchas and calibration: HARDWARE.md
pip install pyserial pillow # pillow only needed for marquee/big text
git clone https://github.com/JoshuaEngine7/grandwell-ksu
cd grandwell-ksu
# Every example is a dry run until you give it a port —
# it prints the exact frame bytes instead of sending:
python examples/send_text.py "HELLO WORLD"
python examples/send_text.py "HELLO WORLD" COM3 # now it sends
python examples/send_text.py "HELLO" /dev/ttyUSB0 # Linux works tooMore examples in examples/: three_lines.py,
rotating_pages.py, clock.py, set_clock.py, marquee.py,
big_text.py.
As a library:
from grandwell import pages_frame, send_frame
frame = pages_frame([
{"text": "WELCOME", "effect": "open_horizontal", "delay_ms": 3000},
{"lines": ["MON-FRI", "8:00-16:00"], "delay_ms": 4000},
{"text": "OUT OF SERVICE", "color": "red", "effect": "flash", "delay_ms": 5000},
])
ok, msg = send_frame(frame, port="COM3") # one write; the sign rotates alone
print(msg)Offline test suite (no hardware needed, validates against the sizes and checksums of the original MIM!Plus captures):
python tests/test_frames.pyLearned the hard way — see REVERSE_ENGINEERING.md:
- The sign's flash memory has limited write cycles. One write per user action. Never loop writes: the sign rotates pages, animates scrolls and updates the clock by itself after a single frame.
- Normal text frames: ≤255 bytes of body (the length field is one byte). The marquee and big-text formats use 16-bit lengths and have no such limit.
- Marquee at 8 px height only. A 24 px scroll hung our sign in "WAITING..." until power-cycled.
- The sign is write-only. It never answers on the wire; the only ACK is watching the LEDs.
- PROTOCOL.md — the complete decoded wire format: frame structure, every confirmed command, checksums, bitmap formats
- HARDWARE.md — cables, adapters, serial settings, calibration for other panel sizes
- REVERSE_ENGINEERING.md — how the protocol was decoded with zero documentation: differential captures, an emulator for the 16-bit original, and a lot of byte-diffing
Decoded and hardware-verified: TEXT, ClearScr, ChgColor, Position, Display Modes (5 of 22), Delay, YScroll, Time/Date/Day, SetClock, multi-page, marquee ("Long"), bitmap images (0x1B), both checksum variants.
Not yet mapped (captures welcome!): the remaining display modes, the speed
byte for non-bitmap effects, Show (.bmp playback), Schedule,
per-sign addressing (multi-drop RS-485), and the Bar/Box drawing
commands.
The most valuable contribution is hardware reports: if you have any
Grandwell (or rebadged) sign, try examples/send_text.py and open an
issue with your model and what happened — worst case the sign ignores the
frame. MIM!Plus Capture files (.TXD) of unmapped commands are gold.
This is an independent interoperability project, developed by black-box reverse engineering for the purpose of keeping owned hardware usable. It contains no code, fonts, manuals or assets from MIM!Plus or Grandwell. "Grandwell" and "MIM!Plus" are trademarks of their respective owners, who are not affiliated with this project.
License: MIT