Electronics Modification Programming

Adventures with Flippy the Flip-dot Display

Considering my next project, I wanted to make an electromechanical display using magnets. I turned to the internet for inspiration and quickly came across Flip-dot displays; solenoid driven pixels. A good starting point for what I wanted to do, I looked further.

I found a 900mm, 56×7 display on eBay from a bus salvager (who know such a thing existed!). The displays used to be common on public transport – prior to being replaced my dot matrix LEDs – to display the route number and destination. It cost me Β£170, which may seem expensive to some, but for 392 individually mechanically actuated pixels that are quite a feat of engineering, I thought it cheap.

Manufactured by Hanover Displays and with a basic datasheet to hand, I took the plunge.


Upon arriving and having acquired a USB-RS485 dongle, I tried out a Python module someone had written for their own Hanover display. It demonstrated my display worked but I quickly decided rolling my own driver would be better for my needs – and part of the fun of getting the display in the first place! Most importantly, the module developer had reverse engineered (or somehow knew) the messaging protocol for the displays:

The Hanover Flip-Dot display expects ascii chars representing the hexadecimal bytes; bytes being every 8 rows of dots. For example, an eight row column:

. = 1 => 0xF5 => ['F', '5'] => [0x46, 0x35]
| = 0
. = 1
| = 0
. = 1
. = 1
. = 1
. = 1

Along with a header (containing display resolution and address) and footer (containing CRC). I can only imagine the designers went designers went down this route due to hardware limitations at the time; perhaps they only had access serial controllers that would send ascii characters for use in terminals. Or perhaps there is a reason I am missing?

Message for single dot in second column: B0 ‘2’ fixed, B1 ‘1’ fixed, B2 ‘5’ display addr, B3/4 resolution, B5/6 col 0, B7/8 col 1

Having a logic analyser to scope the hardware output become invaluable when the driver didn’t work at first. Despite apparently unloading the full buffer, the node-serialport was clipping the data. A bit of debugging and a pull-request later and that hitch was solved.

This conversion from hex bytes to ascii characters can quickly become confusing, so I designed my driver to work a 2d matrix of rows and columns, only being encoded to the display format when buffering to the serial port. The matrix means one can intuitively flip a bit at the [x][y] and flip that dot on the display.

Secondly, the Python module used constant pre-defined character arrays encoded for the display. It meant that they didn’t scale well to different size displays. With my matrix implementation, I quickly realised that ascii art would be the perfect font renderer, as one can quickly parse the text strings and set any non-space character as on.Β  Here’s the debug output of my driver sending “hello”:

  ascii  #    # ###### #      #       ####
  ascii  #    # #      #      #      #    #
  ascii  ###### #####  #      #      #    #
  ascii  #    # #      #      #      #    #
  ascii  #    # #      #      #      #    #
  ascii  #    # ###### ###### ######  ####
  ascii                                      +1s
  flipdot Encoded Data: 48,48,55,69,48,56,48,56,48,56,48,56,55,69,48,48,55,69,52,65,52,65,52,65,52,65,52,50,48,48,55,69,52,48,52,48,52,48,52,48,52,48,48,48,55,69,52,48,52,48,52,48,52,48,52,48,48,48,51,67,52,50,52,50,52,50,52,50,51,67,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48 +1ms
  flipdot Writing serial data, size: 120 B : 023135333830303745303830383038303837453030374534413441344134413432303037453430343034303430343030303745343034303430343034303030334334323432343234323343303030303030303030303030303030303030303030303030303030303030303030303030303030303030034241 +0ms

Finally, I added a queue and automatic frame/string scrolling. Once I had all this functionality, it was begging for a GUI!

The GUI was quite quick to develop as the driver was written in node.js – which is primarily a web technology – creating a web app was not a problem. By adding a HTML *canvas* element to emulate the flip-dot display, one can quickly toggle dots on the display to draw any shapes.Β One can send text with any Figlet font, fill the display, enable the clock and show Twitter streams.


I go into the hardware more in my video, but I’ll briefly discuss the electromechanical pixel operation. On my Hanover display, the dots have a magnet inset on one side and an exposed crescent on on the other. Two posts either side are the two poles of a solenoid, hidden below. On my diagram and up close, one can see that the magnet is attracted/repelled from each pole by alternating the current direction in the solenoid coil – this allows control of the exposed dot surface.

Diagram showing how a flip-dot display can toggle the dot with a single solenoid by alternating the current direction.

Residual magnetism in the high remanence core, holds the dot in the last position without the solenoid energised. The un-powered, luminous stable state of the display is what made them great as low-power daylight displays.

View post on

The solenoids are arranged in a multiplexed grid pattern as you’d expect. To raster a frame, the driver appears to enable a row, followed by each column, then the next row… There does not seem to be any frame optimisation – only firing dots that have changed since the last frame for example – causing the display to raster a full frame each time. This fixes the maximum refresh rate to around 2 Hz – slow but enough to display a HH:MM:SS clock!

Considering the electronic area is so large and accessible, one could relatively easily upgrade the driving hardware by hoping directly onto the driver ICs to potentially increase this refresh rate, increase the buffer, better message structure etc. As an electromechanical pixel rather than electrochemical like modern displays, the upper refresh rate would probably be limited by the physical movement of the dot however, along with the solenoid losses.

The display had flying leads for the RS485 and 24 V power that I used for development, but I’ve since been able to install a AC/DC 24 V supply within the enclosure and a Raspberry Pi hosting the node.js web controller. I can now simply plug in an IEC lead, then control the display from any web browser.

Wrap Up

Writing the software and understanding the electromechanical operation of this display has been well worth it. My packages are open-source with links below. They should work for any Hanover Flip-dot display by changing the row and column values at initiation. Even if you don’t have a display, the web controller is setup to emulate by default so you can still have a play!


NPM Package
Package Github
Web controller

38 replies on “Adventures with Flippy the Flip-dot Display”

I was surprised to see an article about flipdot displays on hackaday ( /). After reading your article, I was even more surprised to see that your development was inspired by my dirty python library. Your driver is really well done! Good work.

For my part, after spending nearly two weeks of my free time deboning the protocol, and another week to do something ‘usable’, the fun was gone, and I gave it up.

Nice to see that it was not totally useless πŸ˜€

Thanks! You certainly gave the project a running start and avoided me having to obtain a bus controller with it. I’m curious as to how you reverse engineered it yourself – did you have a controller to sniff?

I got the feeling that might have been the case with the Python driver, but at least you got the hard work done! I was going to start from your base and merge it back in but decided it was going to be easiest to start from a fresh.


I didn’t had a controller neither, but my chance is that someone has sent me a binary dump of transactions sent by a controller. The only thing I knew at that time is that it was a transaction for a system with three displays of different sizes.

I first tried to split the transactions to isolate the displays. Once I was convinced of my splits, I easily understood that the address and the size was present in a kind of header. I also suspected a kind of CRC for the last byte.

It took me roughly two hours from the dump to display the content. (fortunatly, my display had the same size than one of the three display used to dump the communication).
The checksum was the tricky part. First, I thought that it was a CRC. So I made a script to retrieve the polynomial value of the CRC. As you can imagine, the script ran during hours and hours, without success. But in the meantime, I understood how the pixel where coded.

The fact that the pixels are sent as an ASCII representation of their hexadecimal value troubled me. After hours of brainstorming to understand why they coded it that way, I finally understood that the development of these display were made with an old technology, and using a CRC with this technology was then improbable. So, I tried simpler methods, and finally find it. The conclusion is that the checksum couldn’t be simpler.

Once the header, checksum and data format is understood, creating a library to use it is very simple. But this is not my favorite part. That’s why I abandoned very quickly my library, and switch to another project πŸ˜€

Yes I feel like the ASCII byte representation was due to the fact they only had access to serial terminal drivers. I think that part would have had me stumped for a while!

Hi John, I tried your app and it’s not working for some reason. I have a 7×84 Hanover flipdot and am using Windows 7 to drive the display via a USB RS485 adapter via COM12. The display address is 1.

Flipdot-clock -p COM12 -a 1 -r 7 -c 84 produces just :

FlipDot port open on COM12 @: 4800

And nothing else on the display.

The python script from ks156 works on my display works, so for sure the display works.

I have no experience with node.js and am wondering if you can provide some input as to why it is not working for me?


My knowledge in nodejs is almost zero, so I can’t help neither, but …
Looking at these lines , I’m wondering if there’s not a problem with the resolution.

The resolution parameter for a 84*7 is 0x35 0x34 (hanover format), representing 0x54 (84)

The result of the nodejs code is 73, which is not correct.
rows = 84 || 8;
columns = 7 || 56;
data = ((rows * columns) / 8);
col_bytes = rows / 8;
ldata = (columns * col_bytes * 2);
res = (data & 0xFF);

You can try to replace this line by = ((rows * 8) / 8); for debugging.

Hope this help

The comment required approval by the spam filter so didn’t appear at first. Thanks for the reply but I think you’re getting the rows and columns mixed up here. Alan is issuing a command for a display with 7 rows and 84 columns – I think you’ve written something above for 84 rows and 7 columns.

The code rounds the number of rows to the nearest 8 to get the correct number of bytes and expected Hanover resolution format. So an input of -r 7 will round to 8 rows at this line before the following code above.

This ensures that it is always ((columns * [8 multiple of rows]) as in your suggested fix. I’m confident this works as others have used the module with other sizes and my display is in fact a 7 row display too.

I’m not sure what the problem is Alan without looking into it more. Are you sure you have the latest version of the code? There were some constants in some files that others have fixed since I released it.

So I can confirm the following works for me :

flipdot-clock -p COM12 -a 2 -r 8 -c 84
So it looks like the address AND the rows need to be incremented by 1 each for it to work. FYI I also had to increment the display address by 1 in the python script by Ks156 for my display to work.

John, what is the correct command to issue to run term.js? I tried :

flipdot -p COM12 -a 2 -r 8 -c 84
Windows Script Host pops up with an error message

npm run bin/term.js / npm run bin/term.js -p COM12 -a 2 -r 8 -c 84
npm ERR! missing script: bin/term.js

Total newbie in node.js for sure! Any help appreciated. TIA

Are you sure it doesn’t work with (7 rows but with address changed)

flipdot-clock -p COM12 -a 2 -r 7 -c 84

The address is set in with hardware on within the display so that being wrong would make sense for it not working.

The script not working is because the symbolic link hasn’t been created properly. It is Windows problem though. You can run it from the code folder instead here

You are right – it works with -r 7. I was sure I tried that earlier. But the address setting in the display is “1”, so an increment of 1 is still required without changing the code.

term.js is also working now … thanks for all the help and the code. Much appreciated.

I can confirm this script does not calculate resolution correctly for a 16×32 res panel, nor for three linked in succession at 16×96 (original form factor). This is quite possibly due to how Hanover built their displays over the years and the degree of consistency used hin their processes (almost none)

I had hoped that the web interface would help me debug the display logic, however, resolution in web interface is hard-coded and attempting to adjust these values open pandora’s box.

When attempting to debug the flipdot binaries, it becomes apparent that the mapping patterns for these larger boards is far different than for their smaller boards.

If you have any tips, I’m all ears.

Just a suggestion ,,, maybe try the Python script from ks156 to see if it works for your panel(s)?

You’re right, the web interface was rolled together rough and ready for an Art Trail – there are some hard coded elements and it’s a bit of a mess.

I looked at passing the rows and columns to generate the correct size display board but due to expected aspect it was more work than I could afford to do. If you fancy tackling it, feel free to make a pull request on GitHub.

The best way to debug is using DEBUG=* node examples/test.js – it will print various generated data to the terminal

Hi John, I have had success running your driver under Windows and now would like to run in the Raspberry Pi 3 B+ because of the much smaller form factor of the Pi. I see that you also have a Pi connected to your flipdot display.

However, I am having problems getting it to run with the following errors returned :

$ flipdot –help
throw err

Error: Could not locate the bindings file. Tried:
β†’ /usr/lib/node_modules/flipdot-display/node_modules/serialport/build/serialport.node
β†’ /usr/lib/node_modules/flipdot-display/node_modules/serialport/build/Debug/serialport.node
β†’ /usr/lib/node_modules/flipdot-display/node_modules/serialport/build/Release/serialport.node
β†’ /usr/lib/node_modules/flipdot-display/node_modules/serialport/out/Debug/serialport.node
β†’ /usr/lib/node_modules/flipdot-display/node_modules/serialport/Debug/serialport.node
β†’ /usr/lib/node_modules/flipdot-display/node_modules/serialport/out/Release/serialport.node
β†’ /usr/lib/node_modules/flipdot-display/node_modules/serialport/Release/serialport.node
β†’ /usr/lib/node_modules/flipdot-display/node_modules/serialport/build/default/serialport.node
β†’ /usr/lib/node_modules/flipdot-display/node_modules/serialport/compiled/8.11.3/linux/arm/serialport.node
at bindings (/usr/lib/node_modules/flipdot-display/node_modules/bindings/bindings.js:93:9)
at Object. (/usr/lib/node_modules/flipdot-display/node_modules/serialport/lib/bindings/linux.js:2:36)
at Module._compile (module.js:652:30)
at Object.Module._extensions..js (module.js:663:10)
at Module.load (module.js:565:32)
at tryModuleLoad (module.js:505:12)
at Function.Module._load (module.js:497:3)
at Module.require (module.js:596:17)
at require (internal/module.js:11:18)
at Object. (/usr/lib/node_modules/flipdot-display/node_modules/serialport/lib/bindings/auto-detect.js:16:22)

I think it has something to do with the serialport module installation, but am not sure how to resolve it. Appreciate if you can offer some advise on what I can try.

WARNING : Newbie with Pi and Nodejs

Thanks again,


I’m going to be purchasing a Hanover 96×16 display from probably the same seller you purchased yours from. I’m curious what hardware you used specifically for your display. There seems to be an abundance of RS485 dongles out there, so I’m wondering which one you chose and why. Also, which Raspberry Pi model did you use in your project?


Any of the RS485 USB dongles on eBay should work, like this

I used a Raspberry Pi 2 because I had one lying around. I’d get a RPi3 B+ just for the start up time and performance if you’re going to use the web app.

Does anyone have a copy of Flip_Dot_Manual_vB.pdf? Everything from appears to have been removed πŸ™

I have a 96 x 16 Hanover flip-dot display working via a BASIC program I’ve written, but I still feel this document could come in handy

Many thanks.

Hi Ian, do you have the project documented anywhere? Would love to have a look at your BASIC program and your GoL implementation. Thanks!


This is so funny!

I want to let you know your software is working with Hanover LED 80×7 interior next-stop signs as well and I have some addition information about this protocol πŸ™‚

The Hanover LED 80×7 sign:

And of course I tried it with a Hanover flipdot 84×7 which works. However, with both sizes the Web Controller didn’t scale. I have a 23×14, 96×16, 128×16 and 128×19 sign as well, but not tried it yet.

I’m not that smart like you guys, I can’t write programs myself and it took me hours to get this software installed πŸ˜› But I tried it because it looked so funny. Only the fonts are… meh… πŸ˜›

Maybe I have some additional information about the protocol of Hanover RS-485 signs. In addition to the graphical mode you reverse enginered, the protocol also has a text mode. In that mode you send text to the sign and the firmware automatically select a built-in font which fit the text into the sign surface. If the text is too long in the smallest font it will stay blank. Here’s a description of it:

Text-mode support one and two row texts. Two rows is only for signs with 16 or more pixels in the height (like 96×16, 112×16, 128×19). Two row is not described in the document, but so far I remember the two lines are splitted with the “>” character.

This old protocol is supported on the newest Hanover LED signs as well. Some very old flipdot signs only support the text mode.

My hobby is collecting destination signs from different brands and control them using the original control unit and software. For Hanover I do own a control unit and programming software as well as some different signs.

Thanks for sharing this Josefien (and the manual!). Sounds like interesting work! The imgur links don’t work unfortunately however.

It looks like the imgur links are weird: they don’t work when clicked on it, but they do work when copied and pasted into the address bar manually πŸ™‚

And after they are pasted once, they will work when clicked as long as the browser is not restarted πŸ˜›

Great idea! Glad Josefien was able to help you complete this. I learnt about ‘Game of Life’ thanks to this.

Nice to see blog comments of others creating stuff. Were you able to drive the display any faster than ~2 Hz? I’ve been meaning to revisit this and potentially design a custom driver to enable faster refresh rates.

Hi John, sorry for my delayed reply (1 year!), but I’ve only just noticed you message. Alas nope, I was unable to get it to go any faster. I think i takes about .8 seconds to refresh the 196×16 display, which is ‘just’ fast enough for a digital clock :-). Here’s might displaying some retro logos

Thanks for the great info Josefien, I’ve just realised the two lines are actually split with the β€œ<” character.

Ah, you’re right, it was the “<” instead of the “>” πŸ™‚ It’s quite long ago I needed to use these control characters. In some very old programming software for these signs it was needed to manually input these characters to get the text into the right place.

In the editing software the line-number needed the split character as well if the text is two-line like “38%<CENTRAL<STATION”, don’t know if it is used in the protocol as well.

Josefien, you’re a genius! πŸ™‚ Yes 38%<CENTRAL<STATION does indeed work! Do you have any other nuggets of information? πŸ™‚

HI John, great stuff! Just wondering if your library capable of printing a mix of fonts with normal/inverse modes on a line of text? Similar to what the DERIC controllers can do?

Leave a Reply