Idea
Here and there on the Internet you may see a geeky device called binary clock. The implementation can be different, but the idea behind is somewhat similar - it is an ordinary digital clock, but showing the time in binary format instead of decimal. After clicking the links you want one for yourself. Here I will tell you what you will need to create one with parts available at almost no cost on eBay.There are a number of places where you can order a simple device which shows time. My idea was a clock which could show time, date and which could read decimal if I ever want to present that to somebody. Such a device wasn't out there, unfortunately, but looking retrospectively I'm positive it all was for the better because that led me to Arduino.
Arduino is an open-source electronics prototyping platform based on flexible, easy-to-use hardware and software. It's intended for artists, designers, hobbyists and anyone interested in creating interactive objects or environments.
Every Arduino device requires two things - the hardware and the software.
Functionality
The BinaryClock can:
- Show time in binary
- Slide time in decimal
- Show date in binary
- Slide date in decimal
- Set up time in binary
- Set up date in binary
- Slide user-defined events from the closest to the furthest
- Slide history information about an event such as when an event was first started and how many years the event is being celebrated
Software
Most of the time to write a program you don't have to have a device which it is intended to be used on. This code is no exception. Just by spending a little bit more time on design step a developer can get rid of a series of headaches later.
The source tree is organized in following way:
arduino/ | Arduino related code |
doc/ | Documentation and schematic / PCB layout |
emulator/ | PC emulator |
include/ | A code which is common for arduino/emulator/lib/test |
lib/ | BinaryClock library - the clock source |
test/ | Tests |
.ycm_extra_conf.py | I use YouCompleteMe which is a very handy plugin for ViM |
LICENSE | The license |
Makefile | The Makefile to build BinaryClock |
project.vim | A ViM project file - read on :) |
The entire source code is published on my GitHub under Apache 2.0 license.
Tools I will need
This is a list of tools you will (or may) need to work with the code. This list will be described in more details further.
- Make (comes with every Unix based OS - Linux/Mac, etc.)
- Arduino software
- Makefile by Sudar / Martin Oldfield
- colorgcc (optional, but is very handy when it comes to compiling code in the console/terminal)
- Exuberant ctags (optional, but is very handy especially when it comes to Emacs or ViM)
For the emulator and tests:
For the emulator:
- ncurses (comes with every Unix based OS)
For the vimproject (optional - read on):
Arduino
Under Arduino folder you will see only two files binary_clock.ino and Makefile.
binary_clock.ino - the code - is less than 200 lines long, half of which is comments. That's because all the functionality is hosted in lib/. This design makes easier testing and reusing the code (see emulator and test below). binary_clock.ino implements the code which is required by Arduino only, such as what pins the display sits on and what pins the buttons are on along with the glue code to bind the BinaryClock to the hardware.
Arduino website popularizes Arduino IDE. While it's ok for playing around, you may find yourself annoyed with the lack of functionality pretty fast. Luckily, there are ways to get around that. I personally like ViM. If you've never used ViM or Emacs - try one of them. Both have a very steep learning curve, but at the end of the day it worth every second. The next thing is Make. It can suit many needs. Just go read about it. To write a good Makefile from scratch is a challenge as the Make system has a very steep learning curve as well. And learning how to do it worth every second as well :). However, in many cases it's just easier to adopt an existing code, than to write your own, especially if it is well supported. And Makefile by Sudar / Martin Oldfield is a very good example of that. I took his Makefile as a basis.
Makefile under arduino folder of the project is definitions for Sudar's Makefile with some extra functionality. It has an error macro in the beginning which checks if Sudar's Makefile is installed. make will throw that error if it is missing.
To compile arduino code, go to arduino folder and run
binary_clock.ino - the code - is less than 200 lines long, half of which is comments. That's because all the functionality is hosted in lib/. This design makes easier testing and reusing the code (see emulator and test below). binary_clock.ino implements the code which is required by Arduino only, such as what pins the display sits on and what pins the buttons are on along with the glue code to bind the BinaryClock to the hardware.
Arduino website popularizes Arduino IDE. While it's ok for playing around, you may find yourself annoyed with the lack of functionality pretty fast. Luckily, there are ways to get around that. I personally like ViM. If you've never used ViM or Emacs - try one of them. Both have a very steep learning curve, but at the end of the day it worth every second. The next thing is Make. It can suit many needs. Just go read about it. To write a good Makefile from scratch is a challenge as the Make system has a very steep learning curve as well. And learning how to do it worth every second as well :). However, in many cases it's just easier to adopt an existing code, than to write your own, especially if it is well supported. And Makefile by Sudar / Martin Oldfield is a very good example of that. I took his Makefile as a basis.
Makefile under arduino folder of the project is definitions for Sudar's Makefile with some extra functionality. It has an error macro in the beginning which checks if Sudar's Makefile is installed. make will throw that error if it is missing.
To compile arduino code, go to arduino folder and run
$ cd BinaryClock/arduino $ makeTo upload the code to a device, run
$ make uploadIf you use arduino as ISP programmer, run
$ make isploadNote: if you don't have colorgcc installed, the Makefile may not work. If it doesn't, just comment two lines of code with "Override Arduino.mk defaults to point to my soft links" above them.
doc
Doc directory contains schematic which comes as a pdf, png and Fritzing project. And as far as the Fritzing project is concerned, there are two parts: FTDI Basic Male Header and Led Matrix LD1088-BS, which you may need to view the project. These parts are not needed for the pdf and the png, though.
emulator
As the name tells, the directory contains emulator related code. Like in the case of Arduino code, emulator is no more than the bindings and the glue code to work with the BinaryClock library (lib/).
Emulator uses ncurses library to emulate the clock in the terminal.
The emulator displays the time the same way it would be displayed on the real piece of hardware. The time follows the standard time representation on any electronic clock - from the left to the right one bar two pixels wide represents hours, minutes and seconds. And the one above reads 12:34:06. BinaryClock uses 24 hour format to eliminate ambiguity. Thus 12 is always 12pm. 12am would have 0 in the first column :)
One more example. On the emulator screen above it reads 23:59:55, which is 11:59pm.
To start the emulator, first you'll have to compile the code. From the project root do
The emulator reads the current time set on your PC when it starts, so you don't have to adjust it. On the real device the time most likely will need to be adjusted.
Emulator uses ncurses library to emulate the clock in the terminal.
To start the emulator, first you'll have to compile the code. From the project root do
$ makeThis will compile everything - the library, Arduino code, the tests and the emulator. If you want to compile emulator only, do this:
$ cd BinaryClock/emulator $ makeHowever it will still compile the library code, because it is the core of the BinaryClock. After the compiler has done its job, run
$ emulator/build/bin/terminal-binary-clockFrom the project root. The emulator will show up. It will display a welcome message and then it will switch to the time screen. On the right you may notice numbers from 1 to 4 and the text further to the right after each of the numbers. The numbers are buttons. By pressing a corresponding button on the keyboard you execute a behavior associated with the button. On the real device, the buttons are simple push buttons. So, if you press 1, the emulator will show the time in text. BinaryClock has only 8x8 LEDs screen and the text is displayed by sliding it from right to left, the same way it slides on TV during the news hour.
include
Include folder contains common functionality which is used from all other places. common.h and logger.h are included almost with every *.c unit. Makefile.include is included in the other Makefiles under lib, emulator, test.
lib
lib is a core of the BinaryClock. It is the code which BinaryClock runs. The modules are:
The library is compiled into libclock.a which later can be referenced during the compilation of the binaries, such as emulator or test.
clock | BinaryClock API |
clock_alphabet | Characters which BinaryClock supports |
clock_button | Functionality to work with the clock buttons |
clock_event | Events specific code |
clock_event_personal | That's where you may set your own events which the clock will know of |
clock_extern | A set of functions which need to be implemented by the user of the library. The pointers need to be assigned with the user-specific implementation |
clock_main | The only functionality to initialize and to update the clock state |
clock_state | BinaryClock state object - the only object to pass around to the clock |
clock_time | A mechanism to update BinaryClock time |
date_time | Internal date-time code |
The library is compiled into libclock.a which later can be referenced during the compilation of the binaries, such as emulator or test.
test
Contains a bunch of unit tests to test every external function from the lib. That proves that the BinaryClock would behave the way the developer wanted it to behave. test_ut is a light-weight test framework written especially for the project. It futures assert-like functionality which you may be familiar with from the modern test frameworks.
Run
Run
$ cd BinaryClock $make checkThe example output will look like
--- main.c:48 (main) - a test suite started --- Starting main.c:main --- [ut_clock] --- ut_clock.c:252 (ut_clock) - a test suite started --- Starting ut_clock.c:ut_clock --- [clock_slidePattern() returns correct result] Starting ut_clock.c:ut_clock --- [clock_drawPattern() correct] Starting ut_clock.c:ut_clock --- [clock_displayBinaryNumber() correct] Starting ut_clock.c:ut_clock --- [clock_slideText() correct] --- ut_clock.c:252 (ut_clock) - a test suite completed --- 4 test(s) ran. 4 succeded, 0 failed Starting main.c:main --- [ut_clock_time] --- ut_clock_time.c:76 (ut_clock_time) - a test suite started --- Starting ut_clock_time.c:ut_clock_time --- [clock_updateUptimeMillis() returns correct delta] Starting ut_clock_time.c:ut_clock_time --- [clock_updateUptimeMillis() handles integers overflows correctly] --- ut_clock_time.c:76 (ut_clock_time) - a test suite completed --- 2 test(s) ran. 2 succeded, 0 failed Starting main.c:main --- [ut_date_time] ... ...truncated... ... Starting main.c:main --- [ut_clock_alphabet] --- ut_clock_alphabet.c:67 (ut_clock_alphabet) - a test suite started --- Starting ut_clock_alphabet.c:ut_clock_alphabet --- [clock_alphabet_getIndexByCharacter() correct] Starting ut_clock_alphabet.c:ut_clock_alphabet --- [clock_alphabet_getIndexByCharacter() returns ERANGE and correct index] --- ut_clock_alphabet.c:67 (ut_clock_alphabet) - a test suite completed --- 2 test(s) ran. 2 succeded, 0 failed Starting main.c:main --- [ut_clock_event] --- ut_clock_event.c:330 (ut_clock_event) - a test suite started --- Starting ut_clock_event.c:ut_clock_event --- [clock_event_initDayOfMonth() is correct] Starting ut_clock_event.c:ut_clock_event --- [clock_event_initDayOfWeek() is correct] Starting ut_clock_event.c:ut_clock_event --- [clock_event_initDayOfYear() is correct] Starting ut_clock_event.c:ut_clock_event --- [clock_event_getEventDetails() is correct] Starting ut_clock_event.c:ut_clock_event --- [clock_event_initList() is correct] Starting ut_clock_event.c:ut_clock_event --- [clock_event_updateList() is correct] Starting ut_clock_event.c:ut_clock_event --- [clock_event_findClosestFromList() is correct] Starting ut_clock_event.c:ut_clock_event --- [clock_event_yearInfoToStr() is correct] --- ut_clock_event.c:330 (ut_clock_event) - a test suite completed --- 8 test(s) ran. 8 succeded, 0 failed --- main.c:48 (main) - a test suite completed --- 6 test(s) ran. 6 succeded, 0 failed
project.vim
This you will only need if you are a ViM geek as I am.
This particular project.vim initializes path variable in ViM essentially telling it where the source code is located. So even if you work with the code under emulator folder and want to do gf (goto file) on a filename which refers to the code in lib, the Vim will know what you are trying to do. It also sets the appropriate ctags for you as you open up a *.c or an *.h file. It rebuilds the appropriate ctags file when you work with the code, so that when you press ctrl+] the tags are always fresh. And finally project.vim saves your current session when you leave Vim and automatically loads the last session when you start Vim. So, you don't have to remember which file was the last one that you worked with, what windows were opened, what line you were at, window size and position, etc.
$ alias gvimproject alias gvimproject='gvim --cmd "source project.vim"'I have the above alias in my environment and whenever I run gvimproject the shell starts an instance of GViM for me and preloads project.vim from the current folder.
This particular project.vim initializes path variable in ViM essentially telling it where the source code is located. So even if you work with the code under emulator folder and want to do gf (goto file) on a filename which refers to the code in lib, the Vim will know what you are trying to do. It also sets the appropriate ctags for you as you open up a *.c or an *.h file. It rebuilds the appropriate ctags file when you work with the code, so that when you press ctrl+] the tags are always fresh. And finally project.vim saves your current session when you leave Vim and automatically loads the last session when you start Vim. So, you don't have to remember which file was the last one that you worked with, what windows were opened, what line you were at, window size and position, etc.
Hello, Sergey. Do you speak russian?
ReplyDeleteHi there! I actually do :)
Delete