Building a binary LED clock based on Raspberry Pi

As a small side project, I recently built a binary clock as a gift for a friend. Nothing says “I care” more than a homemade LED binary clock. This tutorial will walk you through the motions of setting up both the hardware and software you’ll need to duplicate it, as well as provide you with the tools you need to extend the code I show to display anything you want on the LED display.

What you’ll need

In order to do this, you’ll need the following parts:

  1. A Raspberry Pi. Get it anywhere Pi’s are sold!
  2. A hardware clock to keep track of time across reboots without internet access. I got mine from AB Electronics, and the tutorial is tailored to that specific clock module.
  3. An LED array to use as a display. I ordered mine from Adafruit and have also tailored the tutorial to match that.
  4. A 5V power supply. You can get this in a hardware store for around 15€.
  5. A micro-USB power supply for the Raspberry Pi, of course.
  6. A soldering iron to wire up your fancy new device.
  7. A CR2032 button battery to power the hardware clock.

Additionally, the tutorial works with the software in my BinaryClock repository. The code is easy to extend to do things above and beyond what I’ve implemented here, if you’re interested.

Setting up the hardware clock

The Raspberry Pi doesn’t use a hardware clock, so every time you power it down the time is reset. Normally this isn’t a problem because if you have an internet connection, the Pi will update the clock using an NTP server. If this isn’t the case, the time will be incorrect. I wanted the clock to be able to report time accurately without internet access, for example in an office with no WiFi, so an extra hardware clock was necessary.

Connecting the clock module I recommended to the Raspberry Pi isn’t hard – you just slide it down over the GPIO pins so that the clock is hanging over the body of the Pi. The main reason I chose this clock is because it extends the GPIO pins rather than blocking them. If you’re working with another hardware clock, you’ll need to ensure that you can still access the GPIO pins after attaching it. Otherwise you won’t be able to control your LED matrix.

The RTC Pi from AB Electronics

The RTC Pi from AB Electronics

The folks at AB Electronics have a nice tutorial on setting up their hardware clock with a Raspberry Pi. If you’re feeling lazy, you don’t have to follow it yourself – the Makefile in BinaryClock will take care of it for you when you install the clock. If I had written it really elegantly, configuring the clock would a separate target that wouldn’t be executed automatically on install, but as it is, it’s a dependency of the install target, so if you install my clock program you’ll have the hardware clock set up automatically.

For those of you, however, who prefer to do it themselves, here’s the executive summary:

# Instructions for Raspberry Pi v2

# Install i2c-tools
sudo apt-get install i2c-tools

# Comment out the following lines from /etc/modprobe.d/raspi-blacklist.conf:
# blacklist spi-bcm2708
# blacklist i2c-bcm2708

# Add the following line to /etc/modules:
# i2c-dev

# Add your user of choice (in this case we'll use the pi user) to the i2c group
sudo adduser pi i2c

# Get your Pi up to date
sudo apt-get update
sudo apt-get upgrade
sudo apt-get dist-upgrade

# Reboot!
sudo reboot

# Comment out the following line from /etc/modprobe.d/raspi-blacklist.conf:
# blacklist i2c-bcm2708

# Now switch to root and register the device with the kernel
sudo su
echo ds1307 0x68 > /sys/class/i2c-adapter/i2c-1/new_device
exit

# Now you can check the time on the hardware clock like this:
sudo hwclock -r
# If the clock isn't set, you can set it from the system time like this:
sudo hwclock -w
# If needed, set system time first with the UNIX tool date

# Add the following line to /etc/modules:
# rtc-ds1307

# Now instruct the Pi to set the time from the hardware clock at boot
# This is done by adding these lines to /etc/rc.local:
# echo ds1307 0x68 > /sys/class/i2c-adapter/i2c-1/new_device
# hwclock -s

# Now reboot and you're done!
sudo reboot

For further details, see the tutorial linked above.

Wiring up the LED array

Adafruit has a great tutorial on wiring up the display. If you’re interested in the specifics, see that page. Basically, you need to wire it with the following connections:

Wiring plan connecting the Raspberry Pi pins to the LED display. The GPIO pins on the Raspberry Pi are shown on the left in the positions they’d have if you were holding the Pi with the pins on the top right. They are connected to the LED array’s input pins. Source: Adafruit.

You’ll need to connect the LED array’s pins to the pins sticking up from the real time clock and not to those of the Raspberry Pi, if you’re wanting to use the real time clock to track time.

I ended up cutting up one of the connectors provided with the LED array, stripping the wires, and soldering them to a connector piece that I placed on top of the real time clock’s pins. This limited the number of useless dangling wires.

The Raspberry Pi connected to the LED array.

The Raspberry Pi connected to the LED array.

Once that’s done, you need to provide the LED array with power. Adafruit suggests using a screw jack adapter.

The power supply, connected to the LED array via a screw jack adapter.

The power supply, connected to the LED array via a screw jack adapter.

After doing this, I found the whole assembly was a bit wobbly, so I ended up splicing the power adapter and the connecter to the LED array apart and soldering them together for a more solid hold. Be careful if you do this – I had a friend who is also an engineer help me and so I knew I wasn’t building a machine designed to catch fire at some point. If you don’t know for sure you’re not making something designed to catch fire, stick with the screw jack adapter 🙂

After you’ve completed these two steps, your hardware is ready so you can get to work on the software!

Installing the clock software

This last bit is easy. If you’ve only soldered up to this point, great! Attach the hardware clock to the Raspberry Pi, connect the LED array to the GPIO pins, give both devices power and you’re ready to rumble. You can install the clock like this:

# Clone the repository and its submodule, rpi-rgb-led-matrix
git clone --recursive https://github.com/erget/BinaryClock.git
# Install
cd BinaryClock
sudo make install

Now, when you restart your Pi or run the following:

sudo piclock

You’ll see the clock interpreting the time in binary.

The finished device showing the current time in binary

The finished device showing the current time in binary

Writing your own display

If you’re happy with your binary clock, you can stop reading. If you’re interested in understanding how it’s implemented or how to do it yourself, read on!

The rpi-rgb-led-matrix submodule

In the BinaryClock repository you’ll find a subfolder named “external”, which contains another subfolder, “rpi-rgb-led-matrix”. This folder is a submodule containing code by Henner Zeller. The Makefile in BinaryClock compiles it by using the Makefile in this submodule, and aside from compiling the individual objects, this also compiles an example program called “led-matrix”. You can run it to see different examples of what you can do with the LED matrix, and you can also use it to gather examples of how to write your own classes for interacting with the display. Because it requires accessing the GPIO pins, it must be run with sudo rights.

The library offers a few utilities: The RGBMatrix class, which basically symbolizes the board itself and allows you to set the colors of individual pixels. It also contains the classes RGBMatrixManipulator and DisplayUpdater, which make sure that the display can be updated continuously in a separate thread, allowing the display to be animated without halting the program that controls it. All of these classes make use of the Thread and GPIO classes. The most important one is GPIO, because you have to initialize it manually in order to use the GPIO pins to interact with your LEDs.

In the example, working with these classes goes like this (this is an executive summary, shortened out of main.cc for clarity):

// Initialize GPIO pins, exiting with error if unsuccessful
GPIO io;
if (!io.Init())
  return 1;
// Instantiate helper classes
RGBMatrix m(&io);
RGBMatrixManipulator image_gen(&m);
DisplayUpdater updater(&m);
// Start the updater with a high priority
updater.Start(10);
// Start generating the image for the LED array
image_gen.Start();
// Wait for user input and exit
std::cout << "Press <RETURN> to exit and reset LEDs." << std::endl;
getchar();

This is a lot of bookkeeping and I would have liked to move all of that into a class that abstracts away these details, but in the end I was only able to do that with partial success because it got a bit complicated with dangling pointers. Also, note that this is a reduced example from main.cc – starting an RGBMatrixManipulator all by itself doesn’t do much. In the actual example subclasses implement the Start method, but the base classes don’t. I didn’t include such an example for the sake of brevity.

The BinaryDate class

Now to the binary clock itself. The binary clock uses a class that looks like this:

/**
 * A clock that reports time in binary.
 */
class BinaryDate {
public:
  BinaryDate();
  std::string get_year();
  std::string get_month();
  std::string get_day();
  std::string get_hour();
  std::string get_minute();
  std::string get_second();
  std::string get_day_of_week();
private:
  int year, month, day, hour, minute, second, day_of_week;
  void update_time();
};

A glance in clock.cpp will show you the implementation. At its heart it contains an update_time method which creates a time_t object which is passed to a tm object using the localtime function in time.h in order to get the current local time. Then the tm object is queried in order to set the year, month, day, hour, minute, second, and day_of_week properties. Each time one of the getters is called, it updates the object’s time, creates a std::bitset object which is the binary representation of the integer for the property in question, and converts the bitset into a string. The string is returned to the caller.

The BinaryClock class

The BinaryClock class looks like this:

/**
 * A binary clock that reports the time tracked by a BinaryDate.
 */
class BinaryClock {
public:
  BinaryClock();
  std::string report_time();
private:
  BinaryDate bindate;
};

All it does is report the time contained in its bindate property as a formated string. The string looks like this:


* *                  # #
***                 # # #
***     01100       # # #  0111
* *                 # # #

 * *                ###
* * *               #  #
* * *  001100       #  #  01101
* * *               ###

****                # # #
**                  # # #
  **   011010       # # #   000
****                 # #

The string is formated to match the dimensions of the LED array and is used by the following classes to control what’s displayed there.

The StringReporter class

I like producing strings and parsing them to control the LEDs rather than controlling the LEDs directly – this gives me more control and also opens up the possibility of displaying the content in other media. For example, having the BinaryClock class return a string rather than accessing the LEDs directly had the advantage that I could play around with the class on the command line on my laptop before pushing the code to the Raspberry Pi to test it live with the LED array.

To implement the interface between strings and the LED array, I used an abstract class called StringReporter. The idea is that the StringReporter takes a string and interprets it so that the LED array displays it appropriately. It looks like this:

/*
 * An abstract reporter that parses a string and passes it to an LED array.
 *
 * Virtual functions:
 *   get_string - Produce the string to report
 *   set_color - Set color of LED pixel based on char found with get_string
 *
 * Run() opens a separate thread from the calling stack. In this thread,
 * get_string is called continually. For each char found in get_string, a color
 * is set in the LED array using set_color.
 *
 * For some reason, the call to UpdateScreen in the destructor calls a pure
 * virtual function somewhere along the call chain. I'm not sure where, this
 * happens, but I'm leaving it there so that I always clear the screen on exit.
 */
class StringReporter : public RGBMatrixManipulator {
public:
  StringReporter(RGBMatrix *m) : RGBMatrixManipulator(m) {}
  virtual ~StringReporter();
  void Run();
protected:
  int r, g, b, x, y;
  virtual std::string get_string() = 0;
  virtual void set_color(char symbol) = 0;
};

It is up to the client code to implement the virtual functions – get_string needs to return the string that the StringReporter reports, and set_color needs to interpret the symbols in that string appropriately. Run is implemented as follows:

void StringReporter::Run() {
  while (running_) {
    auto report_string = get_string();
    x = y = 0;
    for (unsigned int i = 0; i < report_string.length(); ++i) {
      auto symbol = report_string[i];
      set_color(symbol);
    }
    usleep(5000);
  }
}

Pretty simple. All it does is check that the StringReporter is still running. If it is, it calls the object’s get_string method, sets the x and y coordinates referring to individual LEDs in the array to 0, and then iterates over the chars in the string. Each char is passed to the object’s set_color method, which has the job of reacting to it appropriately.

I have proposed moving the StringReporter into the rpi-rgb-led-matrix library, since it implements a rather abstract functionality that other users might be interested in. We’ll see what Henner thinks about the idea.

The BinaryClockReporter class

The abstract StringReporter class is implemented in the BinaryClockReporter class. Here I mixed in the functionality of the BinaryClock class using multiple inheritance. Most of the time I’m against multiple inheritance if it’s used only as a way of recycling code, but in this case it made sense because the BinaryClockReporter has an “is-a” relationship to both StringReporter and BinaryClock. It looks like this:

/*
 * A concrete StringReporter that reports the time as formated by BinaryClock.
 */
class BinaryClockReporter : public StringReporter, public BinaryClock {
public:
  BinaryClockReporter(RGBMatrix *m) : StringReporter(m), BinaryClock() {}
private:
  void set_color(char symbol);
  std::string get_string();
};

That means that it inherits all of the properties and methods of both its parent classes and their respective parent classes. The abstract methods it implements look like this:

std::string BinaryClockReporter::get_string() {
  return report_time();
}

void BinaryClockReporter::set_color(char symbol) {
  switch (symbol) {
    case '*': r = b = 0; g = 255; break;
    case '#': g = b = 0; r = 255; break;
    case '0': r = g = 0; b = 255; break;
    case '1': r = g = 255; b = 0; break;
    case '\n': x = -1; y++; break;
    default: r = g = b = 0; break;
  }
  matrix_->SetPixel(x, y, r, g, b);
  x++;
}

No surprises here. get_string only calls the BinaryClock’s report_time method, while set_color examines each char it receives from Run and reacts appropriately. First, it examines the char and generates a color from it. If it doesn’t know the char it finds, it sets the color to black. If it finds a carriage return, it does a carriage return along the LED array coordinates. Finally, it sets the color of the given pixel at its current coordinates before incrementing the X-coordinate.

Putting it all together

I don’t like code that contains unnecessary bookkeeping, because in my opinion it distracts from the actual logic that you’re describing when you write your code. For this reason, I abstracted some of the details of keeping track of the matrix and GPIO pins into a helper class: The LedClock. It’s the last class in BinaryClock and it looks like this:

/*
 * An interface between a BinaryClockReporter and the LED array.
 *
 * The LedClock takes care of managing proper threading for the
 * BinaryClockReporter and the DisplayUpdater.
 */
class LedClock {
public:
  LedClock(RGBMatrix m) : reporter(&m), updater(&m) {}
  void run();
private:
  BinaryClockReporter reporter;
  DisplayUpdater updater;
};

This encapsulates the logic of setting up the DisplayUpdater and the RGBMatrixManipulator (in this case as inherited by BinaryClockReporter). When the user calls the run method, this is what happens:

void LedClock::run() {
  updater.Start(10);
  reporter.Start();
}

The DisplayUpdater and BinaryClockReporter are started, creating a separate thread that updates the clock continually.

The main function, then, is simple – all it has to do is setup the GPIO and RGBMatrix, instantiate the LedClock, start the display and wait for the user to press Enter to exit:

/*
 * Initialize GPIO pins and RGB matrix, then create and run LedClock.
 */
int main(int argc, char *argv[]) {
  GPIO io;
  if (!io.Init()) {
    std::cerr << "Couldn't access GPIO pins." << std::endl;
    return 1;
  }
  RGBMatrix matrix(&io);
  LedClock clock(matrix);
  clock.run();
  getchar();
  return 0;
}

Extending the library on your own

If you want to extend the library on your own, all you need to do is implement a StringReporter that produces an appropriate string and interprets the symbols found in it while iterating correctly through the X and Y coordinates.

Another thing that might be useful would be to change the LedClock class to not be so clock-specific. It would need a different name, like LedDisplay, and would have to use a StringReporter pointer rather than a BinaryClockReporter for its reporter property. Otherwise it could stay the same. This would allow other users to reuse this logical encapsulation, so that their code would be as simple as in the main function. In this case, however, it would also be a good idea to remove the call to getchar from the run method, so that control of the program would remain in the hands of the developer who implements the main function.

If you come up with a cool way of using this code – whether for your own clock or for a display showing different things, I’d love to hear about it! And, as always, the code is open source so you’re free to do whatever you want with it. Send me a pull request if you do anything cool 😉

Advertisements
About

My name’s Daniel Lee. I’m an enthusiast for open source and sharing. I grew up in the United States and did my doctorate in Germany. I've founded a company for planning solar power. I've worked on analog space suit interfaces, drones and a bunch of other things in my free time. I'm also involved in standards work for meteorological data. Now I work for the German Weather Service on improving forecasts for weather and renewable power production.

Tagged with: , , ,
Posted in Uncategorized

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

From the archive