ShiftOut question for Hannio flipDot Display

Posted on
  • Hello @Gordon

    I've bought a flipdot display controled by a shift register.
    There is an arduino lib to control it...
    i find the code to display one pixel with arduino:

    void FlipDot_5x7::displayPixel(int16_t x, int16_t y, boolean color) {
    		// DR R0 R1 R2 DC C0 C1 C2
    		uint8_t data = color != _invert;
    		data |= y << 1;
    		data |= (color == _invert) << 4;
    		data |=  x << 5;
    		shiftOut(_data, _clock, LSBFIRST, data);

    it needs a LATCH pin more for working, i've added it in the js code
    i've rewrited the code for espruino like this

    var DATA_PIN = A8;
    var CLOCK_PIN = B6;
    var LATCH_PIN = B4;
    var compteur=0;
    var aff= 0;
    var _invert = false;
    function displayPixel(x, y, color) {
    		// DR R0 R1 R2 DC C0 C1 C2
    		var data = color != _invert;
    		data |= y << 1;
    		data |= (color == _invert) << 4;
    		data |=  x << 5;
    		shiftOut(DATA_PIN, {clk:CLOCK_PIN}, data);
            //console.log("data:", data.toString(2));
    function onInit() {
      setInterval(function() {
        displayPixel(aff%5, Math.floor(aff/5)%7, compteur%2);
        compteur ++;
        if (compteur%2 ===0) {aff++;}
      }, 500);

    it does not work...
    if i generate a random number for data

    data = Math.round(Math.random()*255);

    dots flip sometime...
    i suppose it's about format of my byte...

    Luca from Hannio send me the format for the byte

    the data has to be xxxCyyyc where xxx is the binary representation of
    the x coord (and yyy for the y). c is the color, C is the inverted
    value. Thus, for example, x = 2, y = 3, c = 1 would be 010 0 011 1.

    Where i'm wrong?

    best regards


  • In the anonymous function in your onInit() code I would expect the displayPixel() first and then the digitalPulse() for the latching (flank) and (combined?) the output enable for a particular time (apply the power to the coil)... - in other words, flip the lines 28 and 29, and allow a much longer pulse...

    What type of shift register are you using?

    Btw, you can save yourself some work by using MC / Eespruino built-in SPI (serial peripheral interface) appliance: assuming, there are two shif registers, one for x (0..5) and the other one for y (0..6) and the shift registers are in series - first x, then y - then you have an easy game:

    // SPI1 and ports for Espruino Pico / Wifi and 74HC595 shift registers
    SPI1.setup({mosi:B5, sck:B3}); // SPI1, other SPIx can do too
    var dnss = A0; // nss pin used for the latching, 
    var  pls = A1; // for the _OE (neg output enable) 
    var dat = new Uint8Array(2);
    function setPxl(x,y,p) { // polarity 0,1
      x=Math.floor(x); y=Math.floor(y); // optional
      x=(x>4)?4:(x<0)?0:x; y=(y>6)?6:(y<0)?0:y; // optional
      d[0]=Math.pow(2,y); d[1]=Math.pow(2,x); d[p]^=0xFF;
      SPI1.write(d,dnss); digialPulse(pls,1,10);
  • There is 2 ic 74hc238 who are decoding a 3bit adress and convert to 8bits for the shift register 74hc595 (i suppose) for l293d
    I do not have direct access to shift register.
    But perhaps i'm wrong on the way that's works.
    Or in a different way.
    The 595 is a stronger version of standard shift register...

    Here the git page of the project (lib and diagramm)

    In the arduino lib, the latch time is 500 microseconds.
    I've tried with more time but no better results...

    An other question who is alway confuse me on the espruino.
    I want to use the usb for debugging purpose,
    The display is powered in 12v and convert a line to 5v to power the mcu.
    i need to power the espruino from this line.
    On which pin do i need to clip the power (i'm not soldering at the moment)
    On the pin near the gnd one or the next pin (third) or on the ViN pin?
    I'm sad cause i've burned two display with an incorrect power connection when changing clip position from the mcu.

  • If i power the espruino with the 12v, i plug on the 3rd pin (gnd pin side)?
    Apologies for this basic question, but the page is not lighting for my english abilities 😞

  • Hi - which Espruino board are you using? I'm pretty sure the 3rd pin won't be right (that's usually the 3.3v pin) - I think if you do that you could cause some serious damage!

    I'd also make sure that GND of all boards is connected together - it can often be a big cause of problems if that isn't connected.

    In terms of software, what you're doing looks good. The latch is often active low though, so I'd try:

        displayPixel(aff%5, Math.floor(aff/5)%7, compteur%2);

    Or potentially the problem is that digitalPulse is asyncronous? It will be working while you are writing data using displayPixel. I'm not sure if that's your intention? If not:

        setTimeout(function() {
          displayPixel(aff%5, Math.floor(aff/5)%7, compteur%2);
        }, 2);

    Might help?

    Also, I believe there are some issues with shiftOut:

    You could try this?

    shiftOut(DATA_PIN, {clk:CLOCK_PIN, repeat:8}, [E.reverseByte(data)]);

    Or, as @allObjects says, you can use the SPI object. But you'll still need reverseByte

  • @Mrbbp, I see that you use a controller (with 293 type, 4 x half-H bridge)... a lot of hardware, but it makes it possible to drive the coils w/ 12V and the logic with 3.3V (or 5V), and with the tons of diodes it is a pretty cool setup - and so is obviously the driving software.

    From my understanding, the shift register is there to feed the two 238 3-to-8 decoders, and the pulse is only there for the write (latch) into the shift register output buffers. Regarding the pulse: the shift register requires only a raising flank, and we get hat for free by using the built in SPI appliance and the nss (negative signal select) pin in the .write(). The nss pin you connect to the board's write input (which is the output register latch clock of the shift register) - NOTE: Im referring to the Version 2-5 of the driver board which you obviously have...

    To drive the row and column decoders, only 3 bits each are used for each nibble (half a byte, half of the shift register's 8 registers). The fourth bit is used for the data of the selected row and column, respectively. These 4th bits are always low and high or vice-versa for making a change. In other words, the byte you have to send (with SPI) looks like this:

    |MSB|   |   |   |   |   |   |LSB|     <--- Most/Least significant bit
    | H | G | F | E | D | C | B | A |     <--- Shift Register Outputs QX
    |128|64 |32 |16 | 8 | 4 | 2 | 1 |     <--- bit weight in decimal
    | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |     <--- bit pos
    |   |   |   |   |   | 0 | 1 | 2 |     <--- col address bit (3x)
    |   |   |   |   |H/L|   |   |   |     <--- col data bit (1x)
    |   | 0 | 1 | 2 |   |   |   |   |     <--- row address bit (3x)
    |L/H|   |   |   |   |   |   |   |     <--- row data bit (1x)
    | 1 | 0 | 1 | 0 | 0 | 1 | 0 | 0 |64|  dec 164 flips row2/col1 pixel
    |   | row 2 dec |   | col 1 dec |  |   
    | 0 | 0 | 1 | 0 | 1 | 1 | 0 | 0 |44|  dec  44 flips row2/col1 pixel back
    |   | row 2 dec |   | col 1 dec |  |   
    | 0 | x | x | x | 0 | x | x | x | 0|  dec   0 and 255 'de-power' the coils
    +---+---+---+---+---+---+---+---+--+          because BOTH of their ends
    | 1 | x | x | x | 0 | x | x | x | 1|  dec 255 are driven to same voltage,
    +---+---+---+---+---+---+---+---+--+          level either L or H; pref.
                                                  L, therefore preferably 0.
                                                  Altenative: nssPin.reset().
    Unit uses about 50..70/60..90mA idling and 250/350mA on active (9/12V).
    NOTE: Counting of rows and columns starts with 0.

    The idea of the flip dot display though is that it does not use current after setting a(ll) dot(s). Therefore, after updating the whole display, at least these row and column data bits have to be set to low. Depending the implementation, I recommend to do that even between pixel settings... In other words, the data has to be stable (latched) in the shift register output register buffer for a specified time... and you have to figure out how long that time is.

    I find it odd that the addresses are reverse (MSB shifting out first), but it is not that much a complication for the code. Regarding the max. invocation frequency, you may need to look if you can set one pixel after the other, because if you keep the values not stable for required time, the 'derived pulse' - the time between two pixel flips - is too short to actually have the pixel flipped... But if you de-power the coils after a sequence of settings, you may even choose a higher voltage - up to 36 volts according to the L293 spec - to 'pump' enough energy into the coils with a much shorter time of applying stable values (and before de-powering)!

    SPI1.setup({mosi:B5, sck:B3}); // SPI1, other SPIx can do too
    var nssPin = A0; // nss pin used for the latching
    function setPix(x,y,p) { // x(col),y(row),polarity 1/0 or true/false
       SPI1.write( (E.reverseByte(y | (x<<4))>>1) | ((!p) ? 128 : 8), nssPin);

    That's all you need... the nssPin is connected to the write (RCLK) of the 595 shift register. The raising flank of the nssPin does just the right thing...

    Init your 3 pins - data, sclk, rclk - as output and set rclk high and you are just fine from the get go.

    If you loose flips, you may slow down the setPix() invocation frequency... To do that in the Espruino way, you do it event driven and in a producer/consumer pattern. You create a queue that is shared between producer and consumer. The producer - setPix() calculates and stores the values, and kicks the consumer off if it is the first entry in the queue. The consumer does the actual write to the device and works after initial kick off interval driven until everything written, including the de-powering value. The queue or buffer you implement as ring buffer to avoid memory management... (overflow detected, but not handled yet in the code).

    SPI1.setup({mosi:B5, sck:B3}); // SPI1, other SPIx can do too
    var nssPin = A0; // nss pin used for the latching
    var rbuf = new Uint8Array(512); // ring buffer w/ fill counter cnt...
    var cnt = rpt = wpt = 0; // ...and read/write buffer pointers
    function setPix(x,y,p,h) { // x(col),y(row),polarity 1/0 or true/false
      if (++cnt <= 512) {
         rbuf[wpt] = (E.reverseByte(y | (x<<4))>>1) | ((!p) ? 128 : 8); wpt++;
         if (cnt === 1) (setInterval(wriPix,1); // make/invoke it 'async'
      } else {
         // throw error, decide what to do
    function wriPix() {
      var v = rbuf[rpt];
      SPI1.write(v, nssPin);
      if (cnt > 1) { // more to write
        setInterval(wriPix, 2);
        if (++rpt >= 512) rpt = 0; cnt--;
      } else if (v) { // was last one, de-power      
        setInterval(wriPix, 2);
        rbuf[rpt] = 0;   
      } else {
        if (++rpt >= 512) rpt = 0; cnt--;

    If you want to make the code 'safe' to prevent the coordinates not to interfere with the row and column data bits, use this expression which mask everything beyond 3 row and column address bits (works even when coordinates are not integers...):

    (E.reverseByte((y&7) | ((x&7)<<4))>>1) | ((!p) ? 128 : 8)

    Btw, choosing !p makes it 'color'-safe: 0 and just any thing else...

    PS: will test the code when I get a display... ;-)

  • @allObjects: Whao, i'm impressed! Amazing!
    Have to take time to test your code.
    Thanks for the great explanation.
    I've got the version with the silkscreen error on pcb (inverted LAT and 5V pins).
    Sorry i just understand the use of espruino spi.write() - data,clk, latch pins

    @Gordon, sorry, i've forgotted to write: i use a pico board unpinned.

    The arduino lib is great but for english writer, no accents! :/
    Want to combine with the custom font of espruino.

  • @Mrbbp no problem :) You want to connect your input voltage to what's shown as BAT_IN here:

    Ideally just the 5v signal from the board though if you can, rather than 12v.

  • PS: will test the code when I get a display... ;-)

    ...was a hint looking for a sponsor... ;) - and kind of says: sorry, have (had) no chance to test... of course, I could make a dry test: print the bits I'm passing to SPI1.write(...), but that part I actually already use successfully as outlined below.

    I'm tinkering along on a different display 'project' as possible 'sub-project' or 'explorative project' for an other, larger project. Im tinkering with a retro display: National Semiconductor 12 digit 7-segment, red LED Bubble Display NS A5120A (see attached pic) - as found in many pocket calculators in lat 60/early 70 - about $2K a piece was HP's first usable/scientific pocket calculator.

    I use 3 595 shift register in series. The last one sources the 7-segment plus decimal point anodes, and the 12 bit of the 16 bits of the other two drain the common cathodes for each digit... since the shift register drains a measly 6ma on 5V, I'm already in trouble with just 595 and may need to add 12 transistors to reliably drive all segments plus decimal point of a single digit all at once... another 2 chips of ULN2703 or 12 plain transistors w/ 12 Base resistors (the issue is obviously not the driving of the cathodes, - otherwise you could not see the digit lit - but more so the reliable shifting on higher speeds to 'hide' the multiplexing from the slow eye. The heavy drain load makes the latching register not to behave nicely anymore...).

    The setup you see in the picture uses an Espruino-Wifi, which is more or less a Pico w/ onboard ESP8266 (latter here not in use right now even though the blue LED dims...). Also you see that only the drains of tje first 3 digits' are hooked - hidden under the white-blue-striped segment wires (Ran out of common cathode resistors from my reuse-pile... or rusty nails I keep for ever... ;) the dismay of many around me...). I avoid the E.byteReversed() in digit's common anode driving, so think reversed. You see only 1 digit displayed because the digits are time multiplexed with very low frequency.

    The SPI1.write(...) that produces 'this' output is equal to:

    //          Segments   12 Digits, 1st is 'on' (=0=drains)           
    //          for 5 on     -------   ---1 
    //          595 right  middle    left
    //          latches    latches   latches
    //          1st byte   2nd byte  3rd byte
    //          receives   receives  receives
    //          data last  data 2nd  data 1st

    Regarding the SPI write with nss pin passed: SPI slaves requires the slave select line to go and stay low - hence called - nss (negative ss) in order to make a slave look at data on the bus (MOSI - Master Out Slave In wire) and detect when the transmission transaction is completed: ssn goes high. You can control ssn by yourself separately, such as B0.reset() (assuming ssn connected to B0), then do multiple sends with spi.write(aSingleByteOrAnArrayOfBytes); and conclude the 'transaction' with B0.set(). For convenience, @Gordon implemented the spi.write() with nss in addition to accepting an array of bytes and not just a single byte - lest to talk about multiple pins - so that when you have all of your things to send ready-made in one byte array, you just need one call - which makes thing just crazy fast, because it is executed 'down deep in the belly' of Espruino all at once rather than in piece meal, spoon fed...

    1 Attachment

    • NSBDd51R.jpg
  • I'm back on subject with some news and questions.
    I've tested @allObjects's code. I've got strange behavior of the picoboard: pb to upload code, have to unplug+plug to handle the board.
    with a simple loop to flip one pix at a time, i've got the same behavior than with my previous code. Some dot flips in random order...
    I have retest some code with an arduino, and it work very accurately.
    My question is about logic level (3.3v vs 5.5v) with shift register et encoders... is it possible that the strnage behavior is due to this shifting?
    I've bought online this night a shift level breakoutboard.
    I will retried with it at the end of the week.
    so wait and see.

    @allObjects: You use A0 pin for the latch (nssPin) do i need an Analog Pin? On the pico board, the size of this pin is smaller (difficult to clip or solder), therefor i use an other pin. is it a pb?
    You've written

    Init your 3 pins - data, sclk, rclk - as output and set rclk high and you are just fine from the get go.

    Do i need to explicitly use pinMode(xx, 'output')? and an openDrain argument for the nssPin?
    Thanks for your time.


  • I've decided to work by a different way. Too many time lost on that.
    I'm connecting the espruino to an arduino which control the flipdot display trough a serial wire to transfer an arrayBuffer.

  • ...sorry to hear that... but it is for sure a nice experiment to let an Arduino do the bit work and Espruino the byte work. I do not know about the powering... that could be an issue, because the flip dot things are a lot of electromagnetic things (coils) that can 'fire back'

    Regarding the pin for nss, you can pick any that is available and easier in access to you.

    Also note that there was actually something that changed for 1v95 with which I had some glitches when changing pin mode. A good practice is to always intentionally setting the pin mode even though Espruino tries and does it pretty well automatically for you.

    If you take a closer look at my intial and then later code with all this shift out, I had every state in its own timeout to give enough time to let enough energy flow thru. @Gordon then adviced me to just run it all the way thru... to reduce the flicker... This works for electronic devices, such as LEDs, but for sure not for ELECTROMAGNETIC devices, which need a certain pulse width to let enough energy flow. This may be the cause of the irregular flips...

    Sorry not seeing your previous post earlier...

  • Ok, soooo i did it.
    Espruino (Pico) for drawing text + Arduino (Nano) for controlling through a serial com @19200

    Arduino code

     [#include](­rch/?q=%23include) <FlipDot_5x7.h>
     [#define](­ch/?q=%23define) CLOCK 13
     [#define](­ch/?q=%23define) DATA  11
     [#define](­ch/?q=%23define) LATCH 10
    int dataReceived = 0;
      OUT  Not connected to Arduino
      5V   5V
      LAT  LATCH
      GND  GND
      SCL  CLOCK
      SDA  DATA
      GND  GND
      12V  VIN
      12V  VIN
      Change false to true if the color is inverted.
    FlipDot_5x7 flipdot(1, 1, false);
    int liste[35];
    boolean flag = false;
    int nItem = 0;
    int temp;
    void setup() {
      // serial vers sepruino
      // to initiate serial
      flipdot.begin(DATA, CLOCK, LATCH);
      // to begin data sending
    void loop() {
    void ecrit() {
      // draw dot by dot 
      for (int i = 0; i < 35; i++) {
        flipdot.drawPixel(i%5, int(i/5)%7, int(liste[i]));
      // show on display
      flag = false;
      // empty array
    void serialEvent() {
      // for debugging purpose
      temp += Serial.available();
      while (Serial.available() > 0 && !flag) {
        dataReceived = byte(;
        if (dataReceived == 0x3E) { // if endMark
          // for debugging purpose
          /*Serial.print("n bytes recus: ");
          for (int i=0; i<35; i++) {
          nItem = 0;
          temp = 0;
          flag = true;
          // show on display
        } else {
          // invert color
          if (dataReceived==0) {dataReceived=1;} else {dataReceived=0;}
          // fill the array with datas
          liste[nItem] = dataReceived;

    Espruino code

    var compteur = 0;
    var g;
    var longT;
    var texte = "The wind was rising, the wind was falling, lifting up her ugly wool skirt.";
    Graphics.prototype.print = function() {
      for (var y=0;y<this.getHeight();y++)
      	console.log(new Uint8Array(this.buffer,this.getWidth()*y­,this.getWidth()).toString());
    function sendDatas() {
      if (compteur < -longT) {
        compteur = 7;
        console.log("reset compteur: ",compteur);
      g = Graphics.createArrayBuffer(5,7,8);
      var datas = new Uint8Array(g.buffer);
      if (compteur === 0) {
        longT = g.stringWidth(texte);
        console.log(compteur, longT);
    function onInit() {
      Serial1.setup(19200, {tx:B6,rx:B7});
      // wainting for init of serial com from arduino before to send datas
      Serial1.on('data', function(e) {
        console.log("serial In:", e);
        if (e == '@') {
          setInterval(sendDatas, 100);

    a video of the display in action­

    ps: i've fried 5 dots, therefor i've removed the dots on the display (upper line)

  • ERROR fix of core line in code fragments in post #6:

    Polarity ('color') is set by either bit 3 or 7, which have the values 8 and 120, respective (and not 64 and 128, which would be bits 6 and 7), in order meet the spec (paraphrased):

    Data byte has to be xxxCyyyc where xxx is the binary representation of
    the x and y coordiantes). c is the color, w/ C is the inverted of c to drive
    the bridge +to- or -to+; for example, x = 2, y = 3, c = 1 would be 010 0 011 1
    (and reversed when using SPI.write() for the shift-out...).

       SPI1.write( (E.reverseByte(y | (x<<4))>>1) | ((!p) ? 128 : 8), nssPin);

    Recap of wiring:

    Espruino (Pico - 5V tolerant, since flip-dot is 5V driven) to flip-dot-disaplay module (using SPI):

    • GND - GND
    • MOSI - SDA
    • SCK - SCL
    • nssPin - LAT (any pin works)

    FlipDotDisplay (has 5V regulator on board that supplies the logic):

    • GND power source - GND
    • 12V power source - 12V

    *** DO NOT connect any 5V or 3.3V between Espruino and FlipDotDisplay - when Espruino powered (USB or Bat).***

    Flip-Dot-Display module though supports 5V at the 5V pin... which could be used to drive Espruino over its BAT in / USB. Powering Espruino from 5V of Flip-Dot-Display module allows to work with single 12V power supply...

    Code used to create attached clips:

    // flipDot00.js
    var nssPin = A0;   // nss pin                 for LAT (latching)
    var mosi   = B5;   // mosi / serial data pin  for SDA
    var sck    = B3;   // sck  / serial clock pin for SCL
    var spiFdd = SPI1; // SPI# compatible w/ mosi and sck pins for Flip Dot Display
    spiFdd.setup({mosi:mosi, sck:sck}); // SPI1, other SPIx can do too
    function px(x,y,p) { // x(col),y(row),polarity 1/0 or true/false
       spiFdd.write((E.reverseByte(y | (x<<4))>>1) | ((!p) ? 128 : 8),nssPin);
    function fdSetAll(p) {
      for (var c=0;c<5;c++) for (var r=0;r<7;r++) px(c,r,p);
      setTimeout(function(){nssPin.reset();},1­0); // de-power
    n = 0;
    wi = null;
    function f(i) { // flip all every i milliseconds
      var p=false; if (wi) s(); wi = setInterval(function(){fdSetAll(p=!p);},­i);
    function r(i) { // run thru byte w/ inc i // wi = true; in console before r()
      if (wi) s();
      wi = setInterval(function(){spiFdd.write(n=(n­+i)%256,nssPin);},0);
    function s() { // s(); in console stops both, a() and r() and depowers
      if (wi && wi !== true) clearInterval(wi); wi = null; nssPin.reset();

    PS: Great Thanks to @Mrbbp who provided me with two units to validate and have fun...

    4 Attachments

  • Gorgeous!
    Bravo @allObjects.
    Thanks so much for your work.

    Have you try to chain 2 flip dot display?
    How to screen something on one display and follow on the second one?
    In the arduino's lib, it seems possible.
    On the hannio github, there is a schematic to chain two flipdot.

  • ...hold your horses...

    Eis nach em andere, wie z'Paris! (Bernois... as it connects to France)

    Chaining... sure: easy... out of one goes to in of next (daisy-chained), clock and latch (and ground and 12V power) go parallel...

    Nest step is to connect it to a Graphics buffer...

    With a configurable setup any cascading should be possible. Configurability may though have an impact on update speed. Will see.

  • ... oder langsam ächli pressiäre (... or slowly gearing up)

  • ...iuuu - seit dr Matte Giuuu

  • @Spocki, what are your roots?

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview

ShiftOut question for Hannio flipDot Display

Posted by Avatar for Mrbbp @Mrbbp