These are some of the flip-disc displays we got from a bus recycling company.
There is a microcontroller unit inside each screen which is actually driving the matrix and includes bunch of different bitmap fonts.
The microcontroller uses RS485 (differential signaling serial) at 4800 baud rate for external communication. For interfacing use either:
We have three different sizes:
All display sizes confirmed to be working with 24V 2.5A 60W LED strip power supply and bench with bench power supplies we have. If you experience quirks make sure power is wired up reliably.
There are two wire bundles going to the screen
Requires 24V, the PCB includes header for connecting toggle pin
If planning to take any of the "flippers" somewhere, check the offsite kit page.
Following snippets use display.py
from https://github.com/ChalmersRobotics/mqtt-flipdot-driver/blob/master/display.py
Some snippets are available here https://git.k-space.ee/artur/flip-disc
Reverse engineering (extracting fonts): https://git.k-space.ee/k-space/mobitec-tools
Note that brltty
seems to claim /dev/ttyUSB0
on Ubuntu, to prevent that apt-get purge brltty
.
For flipping pixels on the small display:
from display import DisplayBuffer, Font
from time import sleep
import serial
port = serial.serial_for_url("/dev/ttyUSB0", baudrate=4800)
disp = DisplayBuffer(0x0a, 28, 13) # The smallest we have
while True:
# Each byte corresponds to vertical 5 pixels, that's decimal values 32-63
# Presumably the least significant 5 bits map to individual pixels
disp.put_text(b'????????????????????????????',0, 0, Font.BITMAP)
disp.put_text(b'????????????????????????????',0, 12, Font.BITMAP)
buf = disp.finalize_buffer()
port.write(buf)
sleep(1)
buf = disp.finalize_buffer()
port.write(buf)
sleep(1)
port.close()
Another example using built-in fonts for medium/large displays:
from datetime import datetime
from display import DisplayBuffer, Font
from time import sleep
import serial
port = serial.serial_for_url("/dev/ttyUSB0", baudrate=4800)
disp = DisplayBuffer(0x06, 128, 16)
while True:
i = datetime.strftime(datetime.now(), " %H:%M:%S")
j = datetime.strftime(datetime.now(), " %Y-%m-%d %A")
disp.put_text(i.encode("ascii"),0, 0, Font.F70)
disp.put_text(j.encode("ascii"),0, 15, Font.F70)
buf = disp.finalize_buffer()
port.write(buf)
sleep(1)
port.close()
cat flipdisc/display.py
import enum
class Font(enum.Enum):
F61 = (0x61, 5)
F62 = (0x62, 5)
F63 = (0x63, 5)
F64 = (0x64, 5)
F65 = (0x65, 5)
F66 = (0x66, 5)
F67 = (0x67, 5)
F68 = (0x68, 5)
F69 = (0x69, 5)
F70 = (0x70, 5)
F71 = (0x71, 5)
F72 = (0x72, 5)
F73 = (0x73, 5)
F74 = (0x74, 5)
F75 = (0x75, 5)
F76 = (0x76, 5)
SMALL_F = (0x64, 5)
BITMAP = (0x77, 5)
# F<HEIGHT>_<Fat>/<Thin>
F13_F = (0x61, 13) # on 0x70 too
F9_F = (0x62, 9)
F19_F = (0x63, 19)
F7_F = (0x64, 7)
F7 = (0x65, 7)
F6 = (0x66, 7)
SYMBOL = (0x67, 16)
F16_T = (0x68, 16)
F13_T = (0x69, 13)
#F13_F = (0x70, 13)
F15_T = (0x71, 15)
F5 =(0x72, 5)
F13_TT = (0x73, 13)
# 74: Only one character... and only A seems to work
F13 = (0x75, 13) # same as F13_T but wider...
def __init__(self, cmd, height):
self.cmd = cmd
self.height = height
class DisplayBuffer:
""" Acts as a buffer for creating commands to send to the display """
def __init__(self, address: int, width: int, height:int):
self.address = address
self.width = width
self.height = height
# create initial frame buffer
self.reset_buffer()
def reset_buffer(self):
self.buffer = bytearray()
# add display header: 0xff (start), address, 0xA2 (always 0xA2)
self.buffer.extend([0xff, self.address, 0xA2])
# set display size
self.buffer.extend([0xd0, self.width]) # width
self.buffer.extend([0xd1, self.height]) # height
def put_text(self, text:str, x: int = 0, y: int = 0, font: Font = Font.SMALL_F):
# set position (TODO: make the position aware of the font size, y-coordinate seems a bit fishy xD )
self.buffer.extend([0xd2, x]) # x coordinate
self.buffer.extend([0xd3, y]) # y coordinate
# select font
self.buffer.extend([0xd4, font.cmd])
# write characters
# self.buffer.extend([ord(c) for c in text])
self.buffer.extend([c for c in text])
def put_bitmap(self, bitmap: bytearray, x: int = 0, y: int = 0):
# set position
self.buffer.extend([0xd2, x]) # x coordinate
self.buffer.extend([0xd3, y]) # y coordinate
# select bitmap font
self.buffer.extend([0xd4, Font.BITMAP.cmd])
# TODO: write real bitmap data here (somehow xD)
self.buffer.extend(bitmap)
def finalize_buffer(self) -> bytearray:
""" completes the this frame and returns the bytes to be written to the display """
# add checksum
self._add_checksum(self.buffer)
# add stop command
self.buffer.append(0xff)
# reset the buffer and return the old one
buffer = self.buffer
self.reset_buffer()
return buffer
@staticmethod
def _add_checksum(data: bytearray):
""" appends a checksum to the specified data """
# sum all bytes except the first and take the lower 8 bits
csum = sum(data[1:]) & 0xff
# append checksum to data
if csum == 0xfe:
data.extend([0xfe, 0x00])
elif csum == 0xff:
data.extend([0xfe, 0x01])
else:
data.append(csum)
All displays use 26 pin bus to connect an 8051 microcontroller board to the rest.
Full display update seems to run in 7 pixel columns batches.
Every 7 pixel column group shares 74HC4514 (4bit input 1-to-16 decoder) whose pins are connected to TD6278 8channel high voltage source drivers and ULN2003AG darlignton arrawys, presumably one is used to drive a pixel pin to GND and other to 24V.
Every 7 pixel column also shared 74HC4514 whose pins are connected to the same driver chips.
Column select signals go to 74HC139D which is shared between 28 pixels (4 groups)
Sigrok Pulseview dump for following script can be downloaded
flipdisc.srzip
#!/usr/bin/env python3
from display import DisplayBuffer, Font
from time import sleep
import serial
port = serial.serial_for_url("/dev/ttyUSB0", baudrate=4800)
disp = DisplayBuffer(0x06, 128, 16)
# Clear screen
buf = disp.finalize_buffer()
port.write(buf)
sleep(5)
# Display "Hello world"
disp.put_text(b"Hello world", 0, 4, Font.F6)
buf = disp.finalize_buffer()
port.write(buf)
sleep(10)
# Clear screen
buf = disp.finalize_buffer()
port.write(buf)
sleep(10)
# Fill with turned on pixels
for q in range(0,32,5):
disp.put_text(b'?' * 128, 0, q, Font.BITMAP)
buf = disp.finalize_buffer()
port.write(buf)
port.close()
Send text to scroll on flip disc via telnet and microcontroller or whatnot.