Thursday, January 2, 2014

Cheap 4-digit displays from SparkFun

Datasheet? We don't need no steeking Datasheet.

Erm. It would have saved a bunch of hassle.... but no problem! Today's most excellent delivery included a whole stack of 4-digit LED displays, in white, for a mere tuppence ha'penny each. That's about 60p allowing for inflation. No, really. 60 New Pence. 6/10ths of a pound. See Sparkfun's product page. Of course, this cheepness comes with a downside: Namely, no datasheet anywhere on t'interwebs. All links to the part number lead to dead Alibaba pages, so not that helpful really. So, some breadboard, a power supply and a tedious amount of plug & pray later, we discover that these odd little chaps have four un-connected decimal points (boo!), a working "clock" style separator, and the following pinout:

  1. digit 1 anode
  2. digit 2 anode
  3. digit 3 anode
  4. digit 4 anode
  5. segment a cathode
  6. segment b cathode
  7. segment c cathode
  8. segment d cathode
  9. segment e cathode
  10. segment f cathode
  11. segment g cathode
  12. lower DP
  13. upper DP

Lower DP & upper DP may or may not be the wrong way around, I forget. I ain't using them anyway, so I've ignore them for the time being. YMMV. If you want to use them, note that lower DP belongs to digit 2, and upper DP belongs to digit 3 - erm, ahem, or vice versa. Between them you get your clock colon thing.


So... let's make these babies do something. As I have lots, but only a small amount of interconnectors, I decided on 2 displays which would scroll a message. Why walk, when one can run?

Obviously, we can't hook this straight into the Arduino, lacking as I do the 24 digital pins necessary. I guess you could do it on a Mega - but why would you? So many pins.... instead, why not use a couple of shift registers? The 74HC595 seems like a perfect fit, and at a mere 30p per unit from Farnell, it's a steal. You could use just 2 of them (1 per active digit), but you'd need another 8 pins from the Arduino then to run the common anodes. Or.... heck, throw another '595 on the barbie why not. We shall use 3 then. Here's how it works:

U1 (74HC595) is connected to the 7 segments + decimal point cathodes of the left-hand display. Q0=a, Q1=b, ..., Q6=g, Q7=dp1 & dp2

U2 (74HC595) is connected to the 7 segments + decimal point cathodes of the right-hand display, same as above.

U3 (74HC595) is connected to the anodes of both displays, so Q0=LH Digit 1, Q1=LH Digit 2, ..., Q4=RH Digit 1, Q5=RH Digit 2, etc. Connection to each pin is via a current limiting resistor of 270 ohms. Note that these resistors would be better if they were connected to each segment on the cathode; connecting to the anode means a minus sign is bright, and an "8" is dim, as the same current has to drive all of the segments. But I ran out of interconnects, so it wasn't happening.

Making it work - software...

Each chip is daisy-chained such that U3 feeds into U2 which feeds into U1. Data coming out of U1 is discarded. Therefore, to load all the shift registers, one transmits the LH byte, then the RH byte, then the "digit select" byte to the shift registers. The old data is pushed out of the way, the new data is then latched in. In common with all(?) of these multi-digit displays, one has to rapidly cycle between each digit individually firing the appropriate LEDs, making it look like all 4 digits are lit at once. Hence, to light all 8 digits of the display, you send the following in a tight loop:

  • 1st char, 5th char, bit pattern 10001000
  • 2nd char, 6th char, bit pattern 01001100
  • 3rd char, 7th char, bit pattern 00100010
  • 4th char, 8th char, bit pattern 00010001

The first job was to borrow the 7-segment tutorial from LucidTronix (, thanks chaps!

Then, just play around adapting things until a test message worked; then put the main message loop code in place, and voila! A perfect scrolling display!

It works best in a darkened room, as the unlit segments are still clearly visible on the naked displays. Fortunately, having access to a complete workshop means I could sandblast a little perspex, which works almost as well :)


 -- 7 Segment Dual Scrolling Display  
 -- by Ade Vickers  
 -- Adapted from LucidTronix 7 Segment LED and 74HC595 shift   
 -- register tutorial at:  
 -- Circuit requirements:  
 --  3x 74XX595 shift registers  
 --  2x 4-character LED displays (common anode or cathode)  
 --  Current limiting resistors - 1 per segment. Use resistor arrays, much neater  
 --  Decoupling caps to taste  
 --  An Arduino.  
 -- Version On     By Comment  
 -- -------- ----------- --- ----------------------------------------------------  
 -- 1.00   02-Jan-2014 JAV Created  
 // SPI interface.  
 int dataPin = 2;  
 int latchPin = 3;  
 int clockPin = 4;  
   0 1 2 3 4 5 6 7 8 9  
   a b c d e f g h i j  
   k l m n o p q r s t  
   u v w z y z -  _ ^  
 Invert all 0/1s if you are using a common cathode display  
 byte dec_digits[] = { 0b00000011,0b10011111,0b00100101,0b00001101,0b10011001,0b01001001,0b01000001,0b00011111,0b00000001,0b00001001,  
            0b00010001, 0b11000001, 0b01100011, 0b10000101, 0b01100001, 0b01110001, 0b01000011, 0b11010001, 0b10011111, 0b10001111,   
            0b10010001, 0b11100011, 0b01010111, 0b11010101, 0b00000011, 0b00110001, 0b00011001, 0b11110101, 0b01001001, 0b11100001,   
            0b10000011, 0b10100011, 0b10101011, 0b11111111, 0b10011001, 0b11111111, 0b11111101, 0b11111111, 0b01111111, 0b11101111 };  
 // Position codes  
 byte dec_display[]= { 0b00010001, 0b00100010 ,0b01000100, 0b10001000 };   
 // The message to display. Terminates with char 255 (because it turns out zero is a valid character, doh!).  
 // TODO: Read from serial port?  
 byte message[] = {38, 36, 39, 36, 38, 37, 7, 36, 28, 14, 16, 22, 14, 23, 29, 37, 13, 18, 28, 25, 21, 10, 34, 37, 12, 24, 30, 27, 29, 14, 28, 14,   
          34, 37, 24, 15, 37, 28, 25, 10, 27, 20, 15, 30, 23, 37, 37, 37, 29, 17, 10, 23, 20, 28, 37, 16, 30, 34, 28, 37, 37, 39, 36, 38,   
          36, 39, 37, 37, 37, 255};  
 int msgLength;  
 // Loop variables  
 int iPos = 0;  // Position in message to start sending digits this time (assuming we're scrolling the message)  
 int iCount = 0; // Counter to provide a delay  
 void setup() {  
  //set pins to output so you can control the shift register  
  pinMode(latchPin, OUTPUT);  
  pinMode(clockPin, OUTPUT);  
  pinMode(dataPin, OUTPUT);  
  // Work out how long the message is  
  // Max 1000 chars.  
  // Use a Geordie loop for this  
  for (int yi = 0; yi <= 999; yi++) {  
   if (message[yi]==255) {  
     msgLength = yi;  
     yi=1000; // Quit the loop now  
 void loop() {  
  int _d0, _d1; // The 2 digits we will activate  
  // There are 3 shift registers chained together, in left-to-right order DIG1, DIG2, DIGSELECT  
  // DIG1 can display any one of the 4 digits at a time. The 4 MSBits of DIGSELECT choose which one is ON.  
  // DIG2 is identical. The least significant 4 bits of DIGSELECT choose which one.  
  // e.g. to show the number 1 at position 1 (counting L->R), we load 3 bytes: 1001 1111, 1111 1111, 1000 0000  
  // So DIG1 gets 1001 1111 (= segs b & c switched on - remember, active LOW on the cathode),   
  // DIG2 gets 1111 1111 = all off  
  // DIGSELECT gets 1000 0000 = display digit 1 in DIG1  
  // e.g. to show 1234 5678, we would:  
  // - Send 1 (1001 1111), 5 (0100 1000) and 1000 1000, pause a ms or 2, then  
  // - send 2 (0010 0101), 6 (0100 0001) and 0100 0100, pause a ms or 2.... then the next 2 digits.  
  // Then repeat.  
  for (int digitOffset = 0; digitOffset < 4; digitOffset++) {  
   // Set the appropriate digits.  
   _d0 = iPos + 4 + digitOffset;  
   _d1 = iPos + digitOffset;  
   // These 2 lines cause the message to wrap around when we run out of characters.  
   if (_d0 >= msgLength) _d0 = _d0 - msgLength;   
   if (_d1 >= msgLength) _d1 = _d1 - msgLength;  
   // Write the appropriate digits  
   digitalWrite(latchPin, LOW);  
   shiftOut(dataPin, clockPin, LSBFIRST, dec_digits[message[_d0]]);   
   shiftOut(dataPin, clockPin, LSBFIRST, dec_digits[message[_d1]]);   
   shiftOut(dataPin, clockPin, LSBFIRST, dec_display[3 - digitOffset]);   
   digitalWrite(latchPin, HIGH);  
   // Wait 5ms to allow POV, then move to the next digit  
   // Turns out we don't need this. Run the bugger as fast as possible, otherwise  
   // it just flickers.  
  // Cheesy delay loop. After 200 counters, move to the next digit in the message.  
  // Comment out to prevent scrolling. Adjust value to adjust scrolling speed.  
  if (iCount > 200) {  
   //Shift to the next digit  
   iCount = 0;  
   if (iPos >= msgLength) iPos = 0;  

A short video:

Saturday, May 22, 2010

Electronics, or, why didn't I use a GREEN light?

Back in Ye Olde Days (okay, 1984), before computers existed (according to some kids) and before the Internet existed to distract us from work, those of us of a slightly nerdy disposition would do Electronic Stuff.

Whilst perusing an elderly Everyday Electronics magazine, I spotted a project which - gasp - I could make use of today! How cool is that? After a quick blast around the virtual aisles of Farnell, and an impatient 24 hour wait, I got my sweaty mitts on a shiny new µA747CN dual op-amp, the heart of the circuit.

Above is the article, all 2-and-a-bit pages of it, and the rather optimistic £5 estimate. Below, is the schematic:

Sharp-eyed viewers will note the use of a 7-segment LED display to craftily give the user a "middle" reading, whilst only using a "low" and "high" sensor. Clever. No good for me, though, 'cos I had no 7-seg display; and besides, I wanted 3 LEDs, as I'll make a nicer panel than the one pictured in the book.

Poking around in the scrap pile revealed an elderly 74LS02 chip, quad 2-input NOR. Perfect! When both main LEDs are extinguished (logic zero), the NOR will be on. So I just found my "middle" lamp. And, after faffing around with some pull-down resistors and other bits, I ended up with this schematic:

Breadboarding the circuit took no time at all (ahem, who am I kidding, it took ages...) - actually, I made the circuit and then the schematic. You could never tell I was a computer programmer, right? Here it is assembled & working with green LEDs:

Above, there are no probes in the water, so the LOW lamp is lit. When probes A and B (the two orange jumpers) are popped into the water, the MID lamp lights (on the original circuit, LOW and HIGH would both be out, leaving just the two elements lit to give an "I" character):

And, finally, with probe "C" (blue) in the water as well, the HIGH lamp comes on:

Perfect, right? Except I'd like the LOW level light to be RED. After all, it's supposed to be a warning. But substituting a single red LED didn't have quite the effect I wanted. In the following pics, all three LEDs are red, but the outcome is the same:

Here the LOW level light is on. But also, there's a faint glow from the HIGH level. Why? Do I need to up the 3M3Ω resistors to 4M7Ω, as per the original schematic? Does this also explain why the current draw rises to a bit over 9mA (from 7.5mA with the green LEDs)?

With the two probes in the water, the only lamp on is the logic-driven middle lamp:

Except, that is, for the faint glow from BOTH low & high lamps...

Finally, the high level:

The LOW level is glowing slightly as well...

Any suggestions gratefully received.

PS: I would like to make it clear that I don't own a caravan.... but I do own a racecar transporter with a water tank... which runs out at the least convenient moment...