Introduction: Salvaged LED Display Microgame

About: Educational Technologist at The Hewitt School

Good news! I just found a discarded VCR with an LED display. That means it's time to salvage the display and make a project out of it. There is lots of info out there on standard 7-segment displays, but appliance and electronics displays are custom, and this one actually turns out to be 10-segment with non-numerical segments. So it will be an exercise in figuring out how a non-standard display works. In this Instructable I will go over my process for hacking a display like this.

Supplies

discarded non-working appliance, such as microwave, DVD/VCR player with LED display

soldering supplies

breadboard

~10 resistors suitable for LEDs (100-330 ohm)

3V battery pack or power supply

jumper wires

Arduino UNO

Adafruit Metro Mini or Teensy LC

perfboard, 2 3/4" x 3 3/4"

piezo beeper

buttons

3D printer or materials for hand made case.

power bank with micro-USB cable

Step 1: Remove the Display

First you have to get the display off the circuit board. Sometimes the pins are long enough you can just snip them and still have plenty enough to insert to holes in a perf board. I could have snipped these but I went ahead and desoldered them anyway. Desoldering can be tricky because you have to remove enough solder with a desoldering pump so each of the many pins can be wiggled free of its hole. With enough wiggling and desoldering you can get it out intact.

Step 2: Figuring Out the Pins

Insert the pins into a breadboard to figure out what the pins do. This one has 15 pins. Connect a 3V battery pack to the breadboard add a 220ohm resistor to positive or negative, and add 2 jumpers so you can begin connecting them to pairs of pins to see what lights up. LED displays are all variations on the same circuit, with some pins controlling a whole digit, and some pins controlling a certain segment across all digits. So you want to figure out which pins are digits, and which pins are segments. Here below I figured out the far left pin controlled the first digit while the 10 pins from the right side each controlled one segment. To light up segments in the second digit I would move the leftmost jumper one to the right, then move the right jumper along the same 10 pins on the right.

A couple more things about LED display circuits. The digit pins are either positive voltage from the battery or negative. If positive the display is termed a "common anode" type display, and if negative it's a "common cathode" display. The one I have here is common anode. I know that because the jumper on the left side is connected to positive, and stays in place while the jumper connected to negative lights up each segment in one digit as I move it to the right.
Second thing is digits can encompass more than just (alpha)numerical segment sets. On this one it turns out the 5th grid pin controls only the red rectangle at top left and the word TIMER on the right, also red.

Step 3: Make a Diagram

It's very helpful to make a diagram of the pins to write down what each pin connects to. Here I label what the 5 digit sections are, and clarify for myself that each section can have up to 10 different parts lighting up. One digit, the 5th, only has 2 things to light up. Figuring out which cathode pin is for each segment in each digit is not necessary at this time, but it could be helpful in the software stage later depending on what you want to do with the display.

Step 4: Connect All the Pins

Connect all the digit and and segment pins to positive and ground to verify you have it right, and just for fun. It's best for each segment to have its own resistor.

I noticed something interesting with this display, that all was good until I connected the 2 segments for the red LEDs. Once I did that the matching segments in the 4 other digits became very dim. This happened because the same circuit is lighting both red and green LEDs. If you have ever tried putting the legs of multiple LEDs on a coin cell battery and the colors happen to be red or yellow and something else like green, blue, or white, you will see that only the red or yellow will light up. This happens because there is less voltage drop over a red or yellow LED, so any other color on the same circuit won't receive enough current. With all the LEDs drawing current here that is a problem. But this actually won't be a problem when it's connected to an Arduino because in the code we will do multiplexing, or alternating each digit with the segments we want for only a few milliseconds at a time. So red and green LEDs will not be competing for current at any moment.

Step 5: Move the Circuit to an Arduino

Next we will move the circuit from the battery to an Arduino UNO to start developing software for it. It's a lot of wires! But basically all the segments are connecting to pins 2-11, with a 220ohm resistor in series, and the 5 digit pins are connecting to 5 of the analog pins. Why the analog pins? You may not know that you can set analog pins to OUTPUT and use them just as you would a digital pin!

Step 6: Time to Code!

The first thing to do in Arduino code is get all the segments to light up. Once the pin variables are defined I put the pin names in an array. This will make it easier to create patterns through loops in various functions. The first place to use the array in a loop is in setup, to set the pinMode for all the segments and digits. The for loop looks more complicated than it should, but simplest way to get the number of elements in an array in Arduino is to divide the array by the size of one element in the array because of how C works. (See here for more, scroll to bottom)One more thing about this code, since this 7-segment is common anode that means any segment we want to light up has to go LOW while the common pin for its digit goes HIGH. Any segment we want to turn off has to go HIGH so there is no voltage potential between the segment pin and the common pin.

What you get is the same thing as when all pins were connected to the battery, which as you can see, leaves 2 segments very dim because of the red segments. Don't worry, next step is multiplexing.

//common anode 7-segment
const int seg1 = 2; //cathodes
const int seg2 = 3;
const int seg3 = 4;
const int seg4 = 5;
const int seg5 = 6;
const int seg6 = 7;
const int seg7 = 8;
const int seg8 = 9;
const int seg9 = 10;
const int seg10 = 11;

const int d1 = A1;  //anodes
const int d2 = A2;
const int d3 = A3;
const int d4 = A4;
const int d5 = A5;

//put the segments in an array to use more easily later
const int segments[] = {seg1, seg2, seg3, seg4, seg5, seg6, seg7, seg8, seg9, seg10};
const int digits[] = {d1, d2, d3, d4, d5};
void setup() {
  //use the array to set them OUTPUT
  for (int i = 0; i < sizeof(segments) / sizeof(segments[0]); i++) {
    pinMode(segments[i], OUTPUT);
  }
  for (int i = 0; i < sizeof(digits) / sizeof(digits[0]); i++) {
    pinMode(digits[i], OUTPUT);
  }
}

void loop() {
  //use the array to set them all LOW
  //segments go LOW for on because they are cathodes
  for (int i = 0; i < sizeof(segments) / sizeof(segments[0]); i++) {
    digitalWrite(segments[i], LOW);
  }
  //set the digits to HIGH, since common anode
  for (int i = 0; i < sizeof(digits) / sizeof(digits[0]); i++) {
    digitalWrite(digits[i], HIGH);
  }
}

Step 7: Multiplexing

In my experience this doesn't usually happen, but it's not hard to create the multiplexing effect to even out the LEDs. We will add a delay in the digit for loop so they turn on and off really fast.

Here is the updated code in loop():

void loop() {
//use the array to set them all LOW //segments go LOW for on because they are cathodes for (int i = 0; i < sizeof(segments) / sizeof(segments[0]); i++) { digitalWrite(segments[i], LOW); } //set the digits to HIGH, since common anode for (int i = 0; i < sizeof(digits) / sizeof(digits[0]); i++) { digitalWrite(digits[i], HIGH); delay(3); digitalWrite(digits[i], LOW); } }

And here is the better result. Each of the 5 grids is blinking on and off so fast we perceive it as all lit up at once. Cool!

Step 8: Making Useful Functions

Eventually I want to make a game out of this, so it will be helpful to break up these pieces of useful code into custom functions. Let's create functions for on and off so the whole thing can blink.

Here's the code. Notice the on function can't just be called and followed by a delay. Because of the multiplexing, the digits would turn on and off once and then just stay off for the delay. So instead, I have to repeat the multiplexing a bunch of times, for one second about 66 times (66 * 3ms * 5 digits). Now if you haven't made many for loops before, get ready, you will make a lot.

void loop() {
on(); off(); delay(1000); }
void on() {
  for (int j = 0; j < 66; j++) {
    for (int i = 0; i < sizeof(segments) / sizeof(segments[0]); i++) {
      digitalWrite(segments[i], LOW);
    }
    for (int i = 0; i < sizeof(digits) / sizeof(digits[0]); i++) {
      digitalWrite(digits[i], HIGH);
      delay(3);
      digitalWrite(digits[i], LOW);
    }
  }
}
void off() {
  for (int i = 0; i < sizeof(segments) / sizeof(segments[0]); i++) {
    digitalWrite(segments[i], LOW);
  }
  for (int i = 0; i < sizeof(digits) / sizeof(digits[0]); i++) {
    digitalWrite(digits[i], LOW);
  }
}

Step 9: Add Buttons for Interactivity

I have been enjoying these microswitches that come out of a microwave door system. I wired two of them to the Arduino like in the breadboard diagram.

Add the buttons to the code. The first thing I always do when adding any input is use the Serial Monitor to make sure it works. To the existing code I'm adding:

  • a pin declaration for each
  • setting pinMode to INPUT_PULLUP for each (note this pulls each button HIGH by default so when it is pressed it will register as LOW)
  • creating a boolean to monitor when each is pressed and report that to the Serial Monitor.

Here's the code and where to put it:

with the other variables

const int buttonLeft = 13;
const int buttonRight = 12; boolean leftPressed = true; boolean rightPressed = true;

this goes in setup

pinMode(buttonLeft, INPUT_PULLUP);
pinMode(buttonRight, INPUT_PULLUP); Serial.begin(9600);

and here is loop

void loop() {
leftPressed = digitalRead(buttonLeft); rightPressed = digitalRead(buttonRight); Serial.print("left: "); Serial.println(leftPressed); Serial.print("right: "); Serial.println(rightPressed); delay(1000); }

...and check the serial monitor while pressing each one. When not pressed each should report 1, and when pressed it should report 0. That is if the switch is "normally open." If you look at the side contact on the microswitch and the label next to it is not NO but NC, that means the switch is "normally closed" and when you press it it goes open. In that case you will expect it to report 0 when not pressed and 1 when pressed.

Step 10: Make the Buttons Do Something

Here are a couple functions that make the digit move left or right depending on the button pressed. See the comments for explanation.

void loop() {
checkButtons(); showDigit(); }
int whichDigit = 0; //start at digit 0
void checkButtons() {
  leftPressed = digitalRead(buttonLeft);
  rightPressed = digitalRead(buttonRight);
  if(!leftPressed && whichDigit > 0) { //if left button goes LOW and digit is above 0 decrease digit
    whichDigit--;
    delay(500);  //bad debounce (there's a better way that's more complicated)
  }
  if(!rightPressed && whichDigit < 4) { //if right button goes LOW and digit is below 4 increase digit
    whichDigit++;
    delay(500);
  }
}
void showDigit() {
  off();
  digitalWrite(digits[whichDigit], HIGH); //from the digits array turn on the current one
  for (int i = 0; i < sizeof(segments) / sizeof(segments[0]); i++) {
    digitalWrite(segments[i], LOW);
  }
}

Step 11: A Simple Game

A game occurred to me where you could move a player back and forth, represented by the small square at the bottom of each of the 4 digits, while trying to avoid a blip that falls from a random place at the top.

Here is how I coded it, using some of the previous code. Two things about the previous code I had to change were how the off function worked and the button press debounce. The original off didn't work well when I was only displaying some of the segments on a digit. Only setting the digits to LOW allowed some ghosting with the segments left LOW. So to really turn them off I set the digits to LOW and the segments to HIGH. For debouncing, using a simple delay to prevent the button from registering multiple presses ends up blocking any subsequent process you want to move on to, like displaying another element in another function. So I basically set a timer value and compare the current millis() to set when to register a button press again.

//common anode 10-segment
const int seg1 = 2; //cathodes const int seg2 = 3; const int seg3 = 4; const int seg4 = 5; const int seg5 = 6; const int seg6 = 7; const int seg7 = 8; const int seg8 = 9; const int seg9 = 10; const int seg10 = 11;
const int d1 = A1;  //anodes
const int d2 = A2;
const int d3 = A3;
const int d4 = A4;
const int d5 = A5;
const int buttonLeft = 13;
const int buttonRight = 12;
boolean leftPressed = true;
boolean rightPressed = true;
//put the segments in an array to use more easily later
const int segments[] = {seg1, seg2, seg3, seg4, seg5, seg6, seg7, seg8, seg9, seg10};
const int digits[] = {d1, d2, d3, d4, d5};
//for blip position
int xpos = 0;
int ypos = 0;
//for moveBlip
int blipWaitTime = 1000;
double lastBlipMove = 0;
//for checkButtons
int whichDigit = 0;
int buttonDebounce = 500;
double lastPress = 0;
void setup() {
  //use the array to set them OUTPUT
  for (int i = 0; i < sizeof(segments) / sizeof(segments[0]); i++) {
    pinMode(segments[i], OUTPUT);
  }
  for (int i = 0; i < sizeof(digits) / sizeof(digits[0]); i++) {
    pinMode(digits[i], OUTPUT);
  }
  pinMode(buttonLeft, INPUT_PULLUP);
  pinMode(buttonRight, INPUT_PULLUP);
  Serial.begin(9600);
  randomSeed(A0);
}
void loop() {
  checkButtons();
  showPlayer();
  showBlip();
  moveBlip();
  checkHit();
}
void checkHit() {
  if(ypos == 2 && xpos == whichDigit) {
    for(int i = 0; i<4; i++) {
      on();
      off();
      delay(200);
    }
  }
}
void checkButtons() {
  leftPressed = digitalRead(buttonLeft);
  rightPressed = digitalRead(buttonRight);
  //better debounce that only changes the digit if certain amount of time has passed
  if (!leftPressed && whichDigit > 0 && millis() > lastPress + buttonDebounce) {
    whichDigit--;
    lastPress = millis(); //reset the last time pressed
  }
  if (!rightPressed && whichDigit < 3 && millis() > lastPress + buttonDebounce) {
    whichDigit++;
    lastPress = millis();
  }
}
//these are coordinates for x and y positions of the blip so it can fall down one of the 4 digits
//the first of each pair is the digit number, second is the segment number
const int blipCoordinates[4][3][2] = {
  {{4,0},{0,5},{0,4}},
  {{0,8},{1,5},{1,4}},
  {{3,7},{2,5},{2,4}},
  {{4,1},{3,5},{3,4}}
};
void showBlip() {
  off();
  digitalWrite(digits[blipCoordinates[xpos][ypos][0]],HIGH);
  digitalWrite(segments[blipCoordinates[xpos][ypos][1]],LOW);
  delay(3);
}
//moves the blip by changing ypos after time elapsed without blocking other things with a delay
void moveBlip() {
  if(millis() > lastBlipMove + blipWaitTime) {
    ypos++;
    lastBlipMove = millis();
    if(ypos > 2) {
      ypos = 0;
      xpos = random(4);     
    }
  }
}
void showPlayer() {
  const int playerSegments[] = {seg3, seg4, seg5, seg7};
  off();
  digitalWrite(digits[whichDigit], HIGH);
  for (int i = 0; i < sizeof(playerSegments) / sizeof(playerSegments[0]); i++) {
    digitalWrite(playerSegments[i], LOW);
  }
  delay(5);
}
void on() {
  for (int j = 0; j < 20; j++) {
    for (int i = 0; i < sizeof(segments) / sizeof(segments[0]); i++) {
      digitalWrite(segments[i], LOW);
    }
    for (int i = 0; i < sizeof(digits) / sizeof(digits[0]); i++) {
      digitalWrite(digits[i], HIGH);
      delay(3);
      digitalWrite(digits[i], LOW);
    }
  }
}
void off() {
  segmentsHigh();
  digitsLow();
}
void segmentsHigh() {
  for (int i = 0; i < sizeof(segments) / sizeof(segments[0]); i++) {
    digitalWrite(segments[i], HIGH);
  }
}
void digitsLow() {
  for (int i = 0; i < sizeof(digits) / sizeof(digits[0]); i++) {
    digitalWrite(digits[i], LOW);
  }
}

Step 12: Solder It Together

I like using these 2 3/4" x 3 3/4" perf boards for soldering small projects like this together. I also use smaller Arduino compatible boards, such as this Adafruit Metro Mini here, and if I need more pins for more complex displays I use various sizes of Teensy boards. Here are a couple pictures of everything soldered on. I had to change the pin configuration. And I decided to add a salvaged piezo beeper to make little win and lose noises.

Step 13: Make a Project Case

The final step is to protect the bare circuit on the bottom from shorting. I like having the jumpers and components visible from above so for the display games I have been making I keep modifying a 3d printed model that fits right around the 2 3/4" x 3 3/4" perfboard and I add on different attachment structures to the model for each new project. The full code is attached and I changed the mechanic around a bit, like you have to catch the blip instead of escape it, it shows your score every time you catch it, it beeps every 10 points, they fall faster each 10 points, etc. It's actually really fun! It takes some playing around with your hardware once you put it together but with some creativity you can come up with some awesome hacked hardware games.

Pocket-Sized Speed Challenge

Participated in the
Pocket-Sized Speed Challenge