Open Sourcing a Clock

6 Jul

In my ongoing quest to open source more of my work, I’ve open sourced my binary clock app 0’Clock today.

BinaryClockTicking

Admittedly there isn’t much to it. It is just a clock after all. So instead of posting the original Objective-C source, I’ve rewritten it in Swift. If you know someone just getting started with Swift, it might be an easy introduction project. Plus, who doesn’t like a fine binary clock?

One interesting change I decided to make while rewriting it is how to handle setting the LEDs On or Off for a given column. Before I was just using a switch statement, which was easy enough. It went something like this…

func setValue(value: Int, forColumn col: Int) {
    // Turn LEDs for given column OFF
    for row in 0...rows {
        // set LED for row OFF
        setLEDon(false, forColumn: col, row: row)
    }

    // Now turn the desired LEDs ON
    switch value {
        case 1:
        setLEDon(true, forColumn: col, row: 0)
        case 2:
        setLEDon(true, forColumn: col, row: 1)
        case 3:
        setLEDon(true, forColumn: col, row: 0)
        setLEDon(true, forColumn: col, row: 1)
        ...
        case 9:
        setLEDon(true, forColumn: col, row: 0)
        setLEDon(true, forColumn: col, row: 3)
    }
}

While it worked it wasn’t exactly elegant. The logic was easy enough to follow, mainly because there are a small amount of cases and only one kind of action to be taken in each case.

But here’s another way to accomplish the same goal…

func setValue(value: Int, forColumn col: Int) {
    let onesState   = (value & 0b0001) == 0b0001
    let twosState   = (value & 0b0010) == 0b0010
    let foursState  = (value & 0b0100) == 0b0100
    let eightsState = (value & 0b1000) == 0b1000
    
    setLEDon(onesState,   forColumn: col, row: 0)
    setLEDon(twosState,   forColumn: col, row: 1)
    setLEDon(foursState,  forColumn: col, row: 2)
    setLEDon(eightsState, forColumn: col, row: 3)
}

The 0b prefix tells the compiler that the number is binary, instead of a real number like normal.

While we can debate a little over which one is clearer, the second one is more efficient. But let’s be honest, we’re not worried about efficiency in our little clock app. Clarity is much more valuable in this case. But I would argue that once you grasp the basic bitwise operation that is going on here this version is actually clearer. Plus it’s an opportunity to learn a little about bitwise operations, which I just love.

So for those unfamiliar with bitwise operations let’s dive in a see what exactly is going on here. If you already know all about bitwise operations feel free to skip to the bottom for the repo link.

In order to show a number as a binary representation using rows of LEDs we need someway to decide which rows should be lit and which ones should be turned off. In the old way we turned off all the LEDs for a given column and then checked for each possible number 0-9 turning on the corresponding rows as needed. In the new method we’re checking each binary bit of a given number to see if that power of 2 is 1 (On) or 0 (Off) and then turning the LED for that row On or Off to match.

First, a short math refresher is in order…When we say 11 we all know what that means; we learn at a very early age that 11 really means 1 x 10 + 1, or more simply 10 + 1. Let’s take a look at a few more examples…

33
100s 10s 1s
0 3 3
0 x 100 + 3 x 10 + 3 x 1

 

512
100s 10s 1s
5 1 2
5 x 100 + 1 x 10 + 2 x 1

 

We can think about binary numbers in the same basic way; just instead of dealing with powers of 10, we’re dealing with powers of 2. And we indicate that a power of 2 is “On” by showing a 1 in its place, or “Off” by showing a 0 in its place. This is where we get the 1’s and 0’s for binary from.

2
4s 2s 1s
0 1 0
0 x 4  + 1 x 2 + 0 x 1

 

5
4s 2s 1s
1 0 1
1 x 4  + 0 x 2 + 1 x 1

 

Ok, now for a brief discussion about bitwise operations. A bitwise operation is just a way of saying we’re going to manipulate a set of bits in some way. In math we normally talk about operations like addition, subtraction, multiplication, and division. In binary there is a different set of operations we can perform and we call them bitwise operations. These include AND, OR, XOR, NOT, and a few others. But for today we’ll just be discussing AND.

Just like in math were we represent addition with a + symbol we represent the AND operation with the & symbol.

The AND operation takes two bits and if they are both 1 the result will be 1, otherwise the result will be 0. Here’s an example where value is the input number and mask is the binary number we AND it with. This style of using AND is called masking, because we “mask” out the bits we don’t care about, leaving only the bit (or bits) we do care about in the result.

5 & 1 5 & 2 5 & 4 5 & 8
Value 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
Mask 0 0 0 1 0 0 1 0 0 1 0 0 1 0 0 0
Result 0 0 0 1 0 0 0 0  0 1 0 0 0 0 0 0

 

So in that scenario we’re left with 1 is On, 2 is Off, 4 is On, and 8 is Off. Or put more simply 1 + 4, which of course equals 5, the number we gave it.

Ok, with that out of the way, let’s take a look at the code again.

Here’s how the code evaluates out if the value was 5 (or 0101 in binary)

let onesState = (value  & 0b0001) == 0b0001 // step 1
let onesState = (0b0101 & 0b0001) == 0b0001 // step 2
let onesState = 0b0001 == 0b0001            // step 3
let onesState = true                        // result

let twosState = (value  & 0b0010) == 0b0010
let twosState = (0b0101 & 0b0010) == 0b0010
let twosState = 0b0000 == 0b0010
let twosState = false

let foursState = (value  & 0b0100) == 0b0100
let foursState = (0b0101 & 0b0100) == 0b0100
let foursState = 0b0100 == 0b0100
let foursState = true

let eightsState = (value  & 0b1000) == 0b1000
let eightsState = (0b0101 & 0b1000) == 0b1000
let eightsState = 0b0000 == 0b1000
let eightsState = false

So with that we have the following results

setLEDon(onesState,   forColumn: col, row: 0)
setLEDon(twosState,   forColumn: col, row: 1)
setLEDon(foursState,  forColumn: col, row: 2)
setLEDon(eightsState, forColumn: col, row: 3)

Which becomes…

setLEDon(true,  forColumn: col, row: 0)
setLEDon(false, forColumn: col, row: 1)
setLEDon(true,  forColumn: col, row: 2)
setLEDon(false, forColumn: col, row: 3)

This allows us to set the LEDs precisely to the desired state given any number.

Now I’m not arguing that bitwise operations are the solution to all your problems, and it can be easy to create some obscure and complicated code using them. But they are a very valuable tool to have in your toolbox, and, I think, too often over looked.

Here’s the repo on GitHub.