Retired 20-Year-Old Flip-Dot Display Rejoins the Workforce

Front of the flip dot display

There comes a time in the life of every electronics nerd when they get into vintage electromechanical displays. Now, I don’t blame you for objecting to my tactless generalization, or to being called a nerd. But I take only mild tautological pleasure in pointing out that you are, well, reading this article.

I myself hopped on this bus a couple years ago, and have been on a wildly passive hunt for old flip-dot displays on eBay since. All this low effort finally paid off last February, when I found a seller flipping dots for profit on kleinanzeigen.de. Through a network of collaborators (one of my oldest friends and the Avis rent-a-car company), one made its way to my desk.

The Single Dot Flip Trick

There is a tiny permanent magnet inside each plastic disc. Pulse current through a coil under it and the generated field repels the magnet and flips the disc.

Coil and iron cores
Magnets, coils & iron cores

Each coil is wrapped around two prongs of a U-shaped iron core. When current flows, one side of the iron core pushes and the other side pulls the magnet inside the disc. Reverse the current pulse and the disc flips back.

I found it surprising that the core remains magnetized enough to keep the disc stable after the power is cut off:

The Matrix Multiplication Miracle

We may be tempted to wire up a bunch of dots in a LED-matrix style:

Schematic of how not to do it
Attempt #1: How not to do it

Try to flip coil A by connecting Col1 to Vcc and Row1 to GND. Sike!! Current also flows through coils C, D and B:

Issue with attempt #1
Issue with Attempt #1: trying to energize coil A also energizes B, C and D

Unlike LEDs, coils allow for bidirectional current. That’s what allows dots to flip both ways. This issue is commonly solved by sprinkling some diodes across the matrix and splitting the rows into RowX+ and RowX-:

Fixed schematic
Attempt #2: a pinch of diodes

In this setup, switching Col1 to Vcc and Row1- to GND only powers coil A. On the downside, RowX+ and RowX- must be coordinated independently, and it’s a bad day at the office when both RowX+ and RowX- are driven at the same time. If you’re hacking on these, remember to power the whole thing with a current-limiting power supply.

The BROSE Module

Front of the module
Front

The module I received is 16x28 dots, made by German company BROSE. I think the “18.KW 02” sticker in the back means it was made in the 18th week of 2002. For niche 20-year-old tech, this is a sort of common piece of hardware. In repos like 545ch4/flippie you even find custom controllers for it.

Back of the module
Back. At least we know what's up with all these diodes

The chunky boy labeled ALCATEL 2840 at the top does most of the heavy lifting. It’s apparently a rebranded FP2800A. It takes a 5-bit address as input (pins A0 through B1) and connects one of its 28 outputs (pins 0A to 3G) to either +24V (if DATA = 1) or GND (if DATA = 0). In each module, these outputs are connected to the 28 columns of the matrix.

FP2800A
FP2800A: The column driver on each module

Each module contains one of these column drivers. The rows are expected to be driven by an external controller, through the 60-pin connectors at the top. This is so that multiple modules can be daisy-chained: each with its own column driver, but sharing a single, external row driver.

60-pin connector
60-pin connector

The 60-pin connector additionally carries the following signals, provided by an external controller:

  • MODx: 8-bit address of a module in the daisy chain
  • COLA0 to COLB1: the 5-bit address of a column within the selected module, from 0 to 27
  • COLDAT: 0 if the column should be connected to GND, 1 if it should be connected to +24V

The last piece of the puzzle is the little red DIP switch on the top left. This is how each module sets its own address, and if this address matches the 8-bit MODx signal from the controller, the FP2800A driver on that particular module will be enabled with the COL_EN signal.

DIP switch
A 74HC688 compares the DIP switch address with the MODx signal
The main driver IC
2840/FP2800A, the main driver IC

The BROSE Controller

Front of the controller
Sections of the controller
Controller sections

Two PCF8574 I²C IO expanders set the module (MODx) and column addresses (COLA0 to COLB1 + COLDAT). These signals are buffered by SN74LS07s into the 60-pin connector and passed along the daisy chain of modules.

A third PCF8574 outputs the signals for the pair of FP2800A row drivers. These drivers then output the RowX+ and RowX- signals into the 60-pin connector.

PCF8574 IO expanders
PCF8574 IO expanders

There’s the secret sauce. The controller receives data through I²C to set the addresses for the drivers via PCF8574 IO expanders.

Let’s now trace back the I²C signals from the IO expanders to the screw-in input terminal. In the middle of the path, a pair of MAX487s snitch under the multimeter probes:

MAX487 RS485 transceivers
MAX487 RS485 transceivers

LeoDJ/Brose_VM-IIC, who did some of the same reverse engineering years ago, aptly describes the setup as “cursed”. The I²C clock and data come directly from the input, except RS485-encoded into the differential pairs SCL_A/B and SDA_A/B. Presumably for better noise immunity over longer wires.

If the goal is to steer this controller with our very own signals, a tempting proposition is to generate these differential I²C SCL_A/B and SDA_A/B ourselves, as BROSE intended. A pair of external TTL-to-RS485 transceivers 1 in front of the input should let us inject I²C data in.

Kind of.

I²C in a Single Breath

When a transmitter sends a single byte to a receiver, a typical I²C transaction looks like this on the wire:

Ack from I²C receiver
ACK from I²C receiver, borrowed from the delightful Philips data handbook, 1989

In a healthy, compliant transmission, the receiver sends back an ACK bit by pulling SDA low on the last clock cycle. Even though the logical data flow is from sender to receiver, the physical medium must support bidirectional communication, lest the ability to ACK is lost.

Fight Back The Lack of ACK

Maybe you already spotted a couple of hints along the way. A controller section annotated with I²C direction switch and a suspicious SDA_OUT signal on the MAX487 transceiver:

The SDA MAX487
SDA_OUT on the MAX487 transceiver

The MAX487 on the controller does, in fact, support bidirectional communication. The DE/nRE pins set the direction with the MAX_SDA_W_nR signal:

MAX_SDA_W_nR Direction Description
0 SDA_A/B -> SDA Controller receives data from the outside world
1 SDA_OUT -> SDA_A/B Controller sends data back to the outside world

So, where does this MAX_SDA_W_nR signal come from? I’m sorry you asked.

Logic circuit to select the direction of the SDA transceiver
MAX487 direction selector for the SDA signal

A pair of SN74HCT00 quad-2-input NAND gates compute the MAX_SDA_W_nR signal from SDA and N_CTRL_SEL_Y.

N_CTRL_SEL_Y is the “controller select” signal. It’s generated by comparing the MODx address (from one of the IO expanders) with the DIP switch address on the controller:

A DIP switch in the controller
Controller select

Things are not looking so hot. If (big, huge “if”) I traced all of this correctly, it seems the controller is capable of sending back an ACK. But only if the MODx from the IO expanders match the controller DIP switch setting. This felt odd to me, because during operation, MODx is supposed to select each module in the daisy chain, not the controller. So most of the time there will be no ACK anyway.

Even if that worked all the time, our hypothetical external RS485 transceiver would need to tap into this direction setting logic to know when to listen for the ACK bit. Madness. I am very curious to learn how the BROSE computer actually handles this, but I don’t have one. I suspect it simply ignores the ACK , and this direction-setting capability is only used to talk to the controller in specific cases. For example, to enumerate all controllers on the bus.

Either way, a reasonable solution is to ignore the ACK ourselves as well. There is just a tiny annoyance: I²C peripherals will wait for the ACK until a timeout error and that gets frustrating fast. LeoDJ/Brose_VM-IIC solved this by bit-banging I²C in software and explicitly ignoring the missing ACK.

A Redemptive Yoink

Luckily, my wife overheard me crying. “Do you really need to do this?”, she asks. Immediately, I knew exactly what she meant.

The problem RS485 solves doesn’t creep up here so much. The ESP32 I’m using to steer the BROSE controller is just a few short centimeters away.

Enlightened in a fit of blind joy, I yanked both MAX487s out of their sockets and tapped wires into the pristine, TTL-level I²C signals, ignoring the input terminal block and all RS485 stuff altogether and speaking directly to the PCF8574 IO expanders.

We are so bACK.

Controller with the MAX487s removed
Controller with the MAX487s removed

The Software

With most of the hardware mapped out, I wrote an ESPHome driver for the BROSE controller. It’s available in rbaron/esphome/components/brose_flipdot.

external_components:
  - source:
      type: git
      url: https://github.com/rbaron/esphome
      ref: brose-flipdot
    components: [brose_flipdot]

display:
  - platform: brose_flipdot
    num_chips: 1
    lambda: |-
        it.printf(0, 1, id(my_font), TextAlign::TOP_LEFT, "Hello");

All the driver does is compute the I²C commands to set the appropriate IO expander pins.

And a VFD on the Side, Please

I wanted to make a beautiful housing for the display. Looking for inspiration, elbows deep in my bucket of maybe-someday parts, I fished out something I’d forgotten about.

In the haze of my recent vacuum fluorescent displays hyperfocus, I had also managed to snag a second, smaller display for around $15 on AliExpress. Validating hoarders everywhere, I thought it would look great as a secondary, tiny display.

VFD display
A small vacuum fluorescent display
VFD display
Back of the display

Conveniently, there’s already a driver for it in trip5/EspHome-VFD-Clock. The end-of-life, built-in ESP12 from the clock ended up being promoted to not only drive the VFD, but the flip-dot display as well. And the LED strip. And the RTC and front panel inputs.

I shamelessly soldered the BROSE I²C lines directly into its pads:

VFD display
The Clock's ESP12 with BROSE I²C lines soldered

Power Supply

Power supply
A 100mA fuse, USB-PD trigger board

After that uptight I²C spec-chasing in previous sections, I concede it is ironic that I should freestyle the USB Power Delivery standard.

Yes, that is essentially a 🚩 USB-C extension cord 🚩 feeding into a USB PD trigger board. Yes, because the USB-C male connector I have only breaks out one CC line, the cable only works in one orientation.

I’m powering the whole thing with 20V over USB-C, cowboy style. The future is already here, just not properly implemented.

Back of the flip dot display
Power measurement
Power measurement with flip-dot display idle, VFD on and dimly-lit LED strip
Open back of the assembly

Front Panel

Anodized aluminum front panel
Anodized aluminum front panel

Armed with some experience from my earlier sheet metal fabrication experiment, I designed this front panel in Fusion. For $27 + shipping, JLCCNC cut, anodized and laser engraved it out of 2mm aluminum.

Laser engraved graphics
Laser engraved graphics

Housing

Exploded view in Fusion
Exploded view in Fusion

I’ve heard that the best way to get into woodworking is to get an IT job, burn out and set up a shop in the garage. I don’t have a garage, but I am feeling the pull toward this shared workshop.

It’s the first time I ever cut a piece of wood. Weeks of planning and overthinking ahead of it. Hours of battle-hardened woodworking YouTube celebrities warning me of all the ways I could hurt myself. How to use a miter saw. How to make bevel cuts. Rip cuts? Table saw, but watch out for kickbacks. Or track saw. Cross cuts? How to cut the lip for the front panel? What the hell, is wood glue really that strong?

The second the saw hit the board, I wondered why I hadn’t done this sooner. What an absolute joy.

Routing the recess for the front panel
Routing the recess for the front panel
Gluing the joints
Gluing the joints
Clamped box
Clamped and left to dry overnight

On the clamped box, you can see how off my 45° joints are. I think I didn’t zero the miter well enough before tilting it. I expected to be upset about it, but it didn’t matter. I leaned into the liberating feeling of sucking at something new.

A trick I learned from MatthiasWandel masks at least part of the mistakes: wood glue and sawdust.

Post sanding
Post sanding

I gave it a coat of 50/50 tung oil and thinner with a rag. It looks great on the white oak and should protect it from the harsh environment of my office shelf.

Tung oil finishing
Tung oil finish

Demo

Calendar, clock, weather and test pattern

Pong on a temporary front panel

Front of the flip dot display
  1. For example on AliExpress