#1
  1. <?PHP user_title("gimp"); ?>
    Devshed Supreme Being (6500+ posts)

    Join Date
    Jan 2005
    Location
    Internet
    Posts
    7,652
    Rep Power
    6084

    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
    Code:
    5 * 1 + 5 * 10 + 2 * 100
    Let's think of a common pattern we see. 1, 10, 100... ah!
    Code:
    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
    Code:
    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!
    Code:
    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.)
    Chat Server Project & Tutorial | WiFi-remote-control sailboat (building) | Joke Thread
    “Rational thinkers deplore the excesses of democracy; it abuses the individual and elevates the mob. The death of Socrates was its finest fruit.”
    Use XXX in a comment to flag something that is bogus but works. Use FIXME to flag something that is bogus and broken. Use TODO to leave yourself reminders. Calling a program finished before all these points are checked off is lazy.
    -Partial Credit: Sun

    If I ask you to redescribe your problem, it's because when you describe issues in detail, you often get a *click* and you suddenly know the solutions.
    Ches Koblents
  2. #2
  3. <?PHP user_title("gimp"); ?>
    Devshed Supreme Being (6500+ posts)

    Join Date
    Jan 2005
    Location
    Internet
    Posts
    7,652
    Rep Power
    6084
    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.]
    Last edited by gimp; October 26th, 2009 at 02:49 PM.
    Chat Server Project & Tutorial | WiFi-remote-control sailboat (building) | Joke Thread
    “Rational thinkers deplore the excesses of democracy; it abuses the individual and elevates the mob. The death of Socrates was its finest fruit.”
    Use XXX in a comment to flag something that is bogus but works. Use FIXME to flag something that is bogus and broken. Use TODO to leave yourself reminders. Calling a program finished before all these points are checked off is lazy.
    -Partial Credit: Sun

    If I ask you to redescribe your problem, it's because when you describe issues in detail, you often get a *click* and you suddenly know the solutions.
    Ches Koblents
  4. #3
  5. Contributing User
    Devshed Frequenter (2500 - 2999 posts)

    Join Date
    Sep 2007
    Location
    outside Washington DC
    Posts
    2,642
    Rep Power
    3699
    Originally Posted by gimp
    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.
  6. #4
  7. <?PHP user_title("gimp"); ?>
    Devshed Supreme Being (6500+ posts)

    Join Date
    Jan 2005
    Location
    Internet
    Posts
    7,652
    Rep Power
    6084
    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.


    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.

    Code:
    // 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.
    Chat Server Project & Tutorial | WiFi-remote-control sailboat (building) | Joke Thread
    “Rational thinkers deplore the excesses of democracy; it abuses the individual and elevates the mob. The death of Socrates was its finest fruit.”
    Use XXX in a comment to flag something that is bogus but works. Use FIXME to flag something that is bogus and broken. Use TODO to leave yourself reminders. Calling a program finished before all these points are checked off is lazy.
    -Partial Credit: Sun

    If I ask you to redescribe your problem, it's because when you describe issues in detail, you often get a *click* and you suddenly know the solutions.
    Ches Koblents
  8. #5
  9. <?PHP user_title("gimp"); ?>
    Devshed Supreme Being (6500+ posts)

    Join Date
    Jan 2005
    Location
    Internet
    Posts
    7,652
    Rep Power
    6084
    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:
    Code:
    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
    Code:
    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
    Code:
    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.
    Code:
    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:

    Code:
    | 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.

    Code:
    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

    Code:
    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

    Code:
    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

    Code:
    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.
    Last edited by gimp; October 26th, 2009 at 07:46 PM.
    Chat Server Project & Tutorial | WiFi-remote-control sailboat (building) | Joke Thread
    “Rational thinkers deplore the excesses of democracy; it abuses the individual and elevates the mob. The death of Socrates was its finest fruit.”
    Use XXX in a comment to flag something that is bogus but works. Use FIXME to flag something that is bogus and broken. Use TODO to leave yourself reminders. Calling a program finished before all these points are checked off is lazy.
    -Partial Credit: Sun

    If I ask you to redescribe your problem, it's because when you describe issues in detail, you often get a *click* and you suddenly know the solutions.
    Ches Koblents
  10. #6
  11. <?PHP user_title("gimp"); ?>
    Devshed Supreme Being (6500+ posts)

    Join Date
    Jan 2005
    Location
    Internet
    Posts
    7,652
    Rep Power
    6084
    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.

    Code:
    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
    }

    Code:
    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());
    }
    Last edited by gimp; October 26th, 2009 at 07:56 PM.
    Chat Server Project & Tutorial | WiFi-remote-control sailboat (building) | Joke Thread
    “Rational thinkers deplore the excesses of democracy; it abuses the individual and elevates the mob. The death of Socrates was its finest fruit.”
    Use XXX in a comment to flag something that is bogus but works. Use FIXME to flag something that is bogus and broken. Use TODO to leave yourself reminders. Calling a program finished before all these points are checked off is lazy.
    -Partial Credit: Sun

    If I ask you to redescribe your problem, it's because when you describe issues in detail, you often get a *click* and you suddenly know the solutions.
    Ches Koblents
  12. #7
  13. <?PHP user_title("gimp"); ?>
    Devshed Supreme Being (6500+ posts)

    Join Date
    Jan 2005
    Location
    Internet
    Posts
    7,652
    Rep Power
    6084
    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.)
    Chat Server Project & Tutorial | WiFi-remote-control sailboat (building) | Joke Thread
    “Rational thinkers deplore the excesses of democracy; it abuses the individual and elevates the mob. The death of Socrates was its finest fruit.”
    Use XXX in a comment to flag something that is bogus but works. Use FIXME to flag something that is bogus and broken. Use TODO to leave yourself reminders. Calling a program finished before all these points are checked off is lazy.
    -Partial Credit: Sun

    If I ask you to redescribe your problem, it's because when you describe issues in detail, you often get a *click* and you suddenly know the solutions.
    Ches Koblents

IMN logo majestic logo threadwatch logo seochat tools logo