Helpful Information
 
 
Category: Embedded Programming
Beginner Arduino Project - Binary LED clock (seconds)

This little project will teach you how to create basic programs with your arduino. (More importantly, it'll give you something that blinks and glows and has LEDs.)

Okay, so we're creating a binary clock that counts seconds. (If you want to add minutes and hours later, it's incredibly intuitive.) First thing first: Binary!

Okay, so we have 60 seconds in a minute. How many LEDs should we use to fully describe all 60 possible seconds? (0-59). What you want to do when posed with a problem like this is quickly figure out which power of two is immediately above your target range (less one); in this case, 64 is the next power above 59.

So how does the binary clock work? Well, here's a quick intro to binary.

In our decimal system, we have a ones place, a tens place, a hundreds place, and so forth. So the first digit to the left of the decimal point is the ones, the second is the tens, the third is the hundreds.

Therefore a number like 255 is

5 * 1 + 5 * 10 + 2 * 100
Let's think of a common pattern we see. 1, 10, 100... ah!

5 * 10^0 + 5 * 10^1 + 2 * 10^2
So we have the ones place is 10^0, the tens place is 10^1, the hundreds is 10^2 and so forth.

Now let's think of this in terms of a list: If we go left from the decimal, we have the 0th element, 1st element, 2nd element, and so forth. 0th element corresponds to 10^0, 1st to 10^1, 2nd to 10^2, and so forth.

So now let's consider only having 2 possibilities per digit instead of 10: we have 0-1 instead of 0-9.

Therefore a number xyz is

z * 1 + y * 2 + x * 4 =
z * 2^0 + y * 2^1 + x * 2^2

So let's look at some basic examples - let's count to 7!

000 = 0 * 2^2 + 0 * 2^1 + 0 * 2^0 = 0
001 = 0 * 2^2 + 0 * 2^1 + 1 * 2^0 = 1
010 = 0 * 2^2 + 1 * 2^1 + 0 * 2^0 = 2
011 = 0 * 2^2 + 1 * 2^1 + 1 * 2^0 = 3
100 = 1 * 2^2 + 0 * 2^1 + 0 * 2^0 = 4
101 = 1 * 2^2 + 0 * 2^1 + 1 * 2^0 = 5
110 = 1 * 2^2 + 1 * 2^1 + 0 * 2^0 = 6
111 = 1 * 2^2 + 1 * 2^1 + 1 * 2^0 = 7

You get the point.

So now you know how to represent 0 - 59 with how many LEDs? Well, 64 is 2^6 so we want 6 LEDs.

What numbers will the LEDs correspond to? 32, 16, 8, 4, 2, and 1. (I ordered them in descending order because you're going to probably want to order LEDs in the same way that you'd order binary.)

Okay, so now you know what the output should look like.

First thing obviously first: Let's wire up 6 LEDs and test each of them. This will enable you to find defects in the LEDs and not have to worry about any of the physical components later.

So, let's have all the LEDs share a common ground. If you have a breadboard, the easiest thing to do is to connect your arduino's ground to the breadboard's negative line along the side. (There should be three grounds: one near pin 13, and two near 5V and VIN near the bottom of the arduino. If you have a mega, you also have two grounds below pins 52 and 53.) If you have one of those tiny breadboards for protoshields just wire up ground to two of the lines. Then, stick in your LEDs so they don't have any connectivity between each other nor their anode (+) and cathode (-) pins. Anode is longer. Attach a wire to each cathode pin of the LEDs and the other end to the negative line(s) on your breadboard. Attach a wire to each anode pin of the LEDs and attach the other end to an individual pin (let's go 11, 10, 9, 6, 5, 3, with 11 being the 32-value LED and 3 being the 1-value LED.) I chose those pins because they're PWM.

[I'll upload a picture later.]

it'll give you something that blinks and glows and has LEDs.)

Blinking lights are cool. All the mainframes in the 60s had them. I missed them when later models came out.

The best beginner projects are visual, and what's more visual than blinking lights?

To continue:

I modified my last post slightly. You should use pins 11, 10, 9, 6, 5, and 3. These are PWM pins. Let me explain PWM as I understand it.

PWM stands for pulse width modulation.

Consider a square wave. You send a HIGH pulse followed by a LOW pulse. To put it more simply, you turn a pin on, then you turn it off.

http://lh6.ggpht.com/_3ON5J19SiH8/SuX-y-QpcdI/AAAAAAAAAYE/sl7AisOkvkk/square_wave.jpg
Credit http://enginova.com/RMS_fig_2.jpg

Now, let's say that we keep sending this over and over. Let's say that we have 500 HIGH and 500 LOW signals per second. What happens?

Well, the Arduino uses 5V, so to put it really simply, since we have 5V half the time, it's the same as 2.5V all the time for many practical uses.*

Now, with PWM you can specify basically how much voltage you want to send on a 5V line. The values range from 0 to 255 (8-bit resolution), with 255 being 5V (HIGH) and 0 being 0V (LOW or off).

So why are we hooking the LEDs up to PWM outputs? Well, most LEDs really only want something like 2-3V. Now, I've run LEDs in the past off of 5V continuously for hours and hours and nothing ever happened to them, but I've also burned one or two. Better off not burning them if you don't have to, right?


So let's play with PWM a bit. Let's just test all the LEDs.

Make sure they're wired up correctly, and write yourself a program.


// the arduino program tabs two spaces, so I'll be following that standard

// list of pins
int pins[] = {3, 5, 6, 9, 10, 11};

void setup() {
// this is like the constructor of the program
for (int i = 0; i < 6; i++) {
pinMode(pins[i], OUTPUT);
}
}

void loop() {
// this function is basically in an infinite recursive loop.

// let's make the LEDs brighter
for (int brightness = 0; brightness < 255; brightness += 5) {
for (int i = 0; i < 6; i++) {
analogWrite(pins[i], brightness);
}
delay(100);
}
}

This should result in your LEDs getting brighter. At some point they stop getting any brighter. You should take note of what PWM output is the brightness you want. For example, if you have green LEDs, you might find that they don't get any brighter after an output of 50 or 100 or 150, etc. We'll use this later. (An easy way to count off the value is to just set the delay to be longer - 1 second instead of 100 milliseconds - and just count up by 5.)




*Note: To convert this into 2.5V analog, you just put an RC filter. Wiki it. This is really simple; just one resistor and one capacitor will transform it into a real analog signal.

Okay, so all six leds are wired up. They all work. Let's say you like 100 the most as a PWM output (approximately 2V).

Let's consider the process.

First, we need to get the time from the arduino. The time can be obtained in milliseconds or microseconds* from the time the arduino was last powered up / reset. In fact we only need seconds, so we're going to want the measurement that's equally or slightly more precise; clearly milliseconds will be easier to deal with.

Ok, 1000 ms per sec. Here's how we get elapsed seconds:

seconds = millis() / 1000;
We see that millis() returns elapsed milliseconds.

Now, we have seconds, but they go from 0 to very large. We'll want to limit them to be from 0 to 59. For this, we want the modulus operator. (Basically the modulus takes the remainder of a division: Let's say you divide 7 by 3, you get 2 remainder 1, therefore 7 % 3 == 1. Also, a very quick way to tell if something is even is to do number % 2 == 0; if it's even this will return true.) Obviously we want to do modulus 60

seconds = (millis() / 1000) % 60;

So now we have seconds, 0-59 inclusive. We can store this as a byte. Now, you remember how we think of this as bit representations, right? We're going to have two leading zeros and six bits, like so

00000000 to 00111011 = 0 to 59

And now we have to extract the six bits individually to determine if the corresponding light should be powered.

Let's think about this using an example.

00101010 is 42
0th bit off
1st bit on
2nd bit off
3rd bit on
4th bit off
5th bit on
ignore 6th and 7th bits

Here's where we have to dig into bitwise operators. You really, really want to google bitwise operators to get a better understanding than I can impart.

These are the important ones:


| is OR, this returns true if the first bit is 1 or the second bit is 1.
EXAMPLE
11111111 |
00000000 =
11111111

10101010 |
11001010 =
11101010


& is AND, this returns true if the first bit is 1 and the second bit is 1.
EXAMPLE
11111111 &
00000000 =
00000000

10100101 &
01010110 =
00000100


^ is XOR or exclusive OR, this returns true if the first bit is not equal to the second bit.
EXAMPLE
01011001 ^
10010101 =
11001100

10101010 ^
01010101 =
11111111


~ is NOT, this is applied to only one operand and just switches the bits.
EXAMPLE
~11111111 = 00000000
~00000000 = 11111111
~01100101 = 10011010
~~1111111 = 11111111 (two NOTs cancel.)


Confused yet? Well, here's two more. These are bit shifting functions.


We have two bit-shifting functions: >> and <<. Great.
Consider the number 42: 00101010.

What happens if we just shift all the bits over one spot to the right?
00101010 >> 1 becomes 00010101 = 21.

What happens if we just shift all the bits over two spots to the right?
00101010 >> 2 becomes 00001010 = 10.

And three spots to the right?
00101010 >> 3 becomes 00000101 = 5.

Now let's shift it the other way.
00101010 << 1 becomes 01010100 = 84
00101010 << 2 becomes 10101000 = 168

See a pattern here? Yeah, basically bit shifting is multiplying by 2 raised to a certain power. Shift right x and it's 2^-x, shift left x and it's 2^x. (This is ^ as raising to the power, mister nitpicker.)
http://arduino.cc/en/Reference/Bitshift for more info on how ints will change and how leading 1s replicate when shifted right and how to change that.




Oh boy! Now we need to know how to get the 0-5th bits from the end of our seconds, which is currently a byte with values 0-59.

Consider again our number 42, or 00101010.
There are a few ways to get the correct bits out.

One way is to actually shift the bits an appropriate amount and then see if we're left with an even or odd number. For example


byte sec = 42; // 00101010
(sec >> 0) % 2 == 0
(sec >> 1) % 2 == 1
(sec >> 2) % 2 == 0
(sec >> 3) % 2 == 1
(sec >> 4) % 2 == 0
(sec >> 5) % 2 == 1

Or we can do an AND comparison, like so


byte sec = 42; // 00101010
sec & 1 == 0 // 00101010 & 00000001 == 00000000
sec & 2 == 2 // 00101010 & 00000010 == 00000010
sec & 4 == 0 // 00101010 & 00000100 == 00000000
sec & 8 == 8 // 00101010 & 00001000 == 00001000
sec & 16 == 0 // 00101010 & 00010000 == 00000000
sec & 32 == 32 // 00101010 & 00100000 == 00100000

Or we could do an OR comparison


byte sec = 42; // 00101010
sec | 1 == sec + 1 == 43 // 00101010 | 00000001 == 00101011
sec | 2 == sec == 42 // 00101010 | 00000010 == 00101010
sec | 4 == sec + 4 == 46 // 00101010 | 00000100 == 00101110
sec | 8 == sec == 42 // 00101010 | 00001000 == 00101010
sec | 16 == sec + 16 == 58 // 00101010 | 00010000 == 00111010
sec | 32 == sec == 42 // 00101010 | 00100000 == 00101010

So now you know how to read time, extract seconds from time, extract seconds between 0-59 from seconds, and extract individual bits to power the LEDs!

In case you're confused, the three patterns I listed are
1) Power LED x if (sec >> x) % 2 == 1
2) Power LED x if sec & 2^x == 2^x
3) Power LED if sec | 2^x == sec
(^ here is 2 to the power of x, not 2 XOR x. You know that, mister wiseguy.)

A pro can weigh in on which method, if any, is superior in terms of runtime. (Chances are that you'll never see any difference in runtime but if the optimization is switching one line for another, it can't hurt!)

*I'll cover a bug with microseconds in a separate thread.

We've wired the LEDs. We've tested them to make sure they work. They operate best at a PWM value of optimumPWM. We know how to get the time, convert to seconds, and figure out which LEDs to light. Let's put it together.

I strongly encourage you to fill in all the blanks yourself.


int pins[] = {3, 5, 6, 9, 10, 11};
byte optimumPWM = 100;

void setup() {
declare each pin in pins[] an output
}

void loop() {
save the time in millis; // you'll need another variable
convert the time to seconds 0-59; // you'll need another variable

for each pin in pins[]
analogWrite(pin, optimumPWM * (boolean to test if that LED should be powered);
// a boolean is really just a bit, 0 or 1, so we can multiply a number by a boolean

pause 1000 milliseconds minus the time it took to execute this iteration of the loop() function
// that means use millis() and the time we saved in the beginning of the function
}



byte pins[] = {3, 5, 6, 9, 10, 11};
byte optimumPWM = 100;
long time;
byte secs; // haha "sex"... sorry

void setup() {
for (int i = 0; i < 6; i++) { // or i < sizeof(pins) instead of i < 6
pinMode(pins[i], OUTPUT);
}
}

void loop() {
time = millis();
secs = (time / 1000) % 60;

for (int i = 0; i < 6; i++) {
analogWrite(pins[i], optimumPWM * ((secs >> i) % 2 == 1));
// a boolean is really just a bit, 0 or 1, so we can multiply a number by a boolean
}

delay(1000 + time - millis());
}

If there's any interest I can also write up a follow-up guide on how to set the time (vie button input or via serial input - I did serial for my clock.)










privacy (GDPR)