Posts with «wise clock 4» label

Sure 32x16 display (HT1632) driven by ESP32

I was recently awoken from the Netflix-induced lethargy by an email from LukeW, who needed help with running the Wise Clock 4 on ESP32. What a great idea that is!

I had an epiphany when I realized what a great development board this is: unbelievable price (about $10 on amazon), great performance (memory, speed, WiFi, BT, BLE etc.), amazing support (Arduino IDE, libraries, examples etc.).

The latest Arduino IDE I had was version 1.6.7 (2017). Trying to install the support package for ESP32, I got a security error along the lines: PKIX path building failed: unable to find valid certification path to requested target.

It works fine when downloading it through the browser (, meaning that the server certificate used for esspressif is not recognized by the JVM run by Arduino (folder "java" in the arduino directory). Identifying and adding the certificate to the JVM's keystore is time consuming, so I decided that it is just easier to upgrade to the latest Arduino IDE (currently 1.8.12), installed this time (a first for me) from Microsoft store. Even though the process is seamless, I have no idea where this software was placed (definitely not where I wanted it). I also learned the new way (where have I been for so long?) used for including and managing libraries ("Add .ZIP library").

Using the ESP32 module is as easy as the first Arduino (Duemilanove). The only thing one needs is a solderless breadboard, which I was lucky to have one around, since I don't remember ever using one before. An interesting fact is that the ESP32 board inserted in the breadboard I have, leaves room for connecting wires only on one side.

Next step was to connect the Sure 32x16 displays (two, daisy chained). I used these pins:

// pins used to connect to ESP32;
#define HT1632_DATA      12 // Data pin (pin 7 of display connector)
#define HT1632_CS            14 // Chip Select (pin 1 of display connector)
#define HT1632_WRCLK  13 // Write clock pin (pin 5 of display connector)
#define HT1632_CLK         27 // clock pin (pin 2 of display connector)

With only the pin changes, the HT1632 files used in Wise Clock 4 software work perfectly fine with ESP32. They can be found here, called from a test sketch that uses 2 displays (#define NUM_DISPLAYS 2 in file MyHT1632.h).

SD card should be next, using the ESP32 support libraries. With ESP32, the sky is the limit: hopefully no more program memory limitations, no need for third party libraries (e.g. Sanguino), better support for sound, easier access to WiFi, support for extra peripherals etc.

From the mailbox

Alex managed to port the Wise Clock 4 code to Arduino Mega2560 (shared here, thanks Alex!). He made this video demonstrating it in action:

Today I have a great day! I did it! I soldered a development board for my Mega2560. A little corrected code and ... voila!  Wiring diagram:
  • rtc sqw (1hz) - pin 2
  • menu key - pin 3
  • set key - pin 4
  • plus key - pin 5
  • speaker - pin 6
  • speaker - pin 7
  • HT1632_WRCLK - pin 10
  • HT1632_CS - pin 11
  • HT1632_DATA - pin 12
  • HT1632_CLK - pin 13
  • rtc sda - pin 20
  • rtc scl - pin 21
(SD While not tested, but I think it works)
  • sd miso - pin 50
  • sd mosi - pin 51
  • sd sck - pin 52
  • sd cs - pin 53

Nelson built his own hand-wired version of WiFiChron and it looks awesome:

MikeM sent in (thanks Mike!) his latest WiFiChron code (available here).
The enclosed zip file compiles under Arduino 1.6.8, though it generates a warning I haven't figured out how to eliminate.
Ray ran into a problem with data overruns. When data in an RSS feed was split between multiple packets, sometimes the last few bytes of a packet were dropped from the RSS buffer. I didn't see that problem with my clock when I was developing the code, nor did I see it on the WiseClock4. I've re-built the RSS state machine to be more CPU efficient, and now the packets are processed without drops. We probably don't need to change the RSS code on the WiseClock4 as it runs at 16 MHz and not 8 MHz like the WiFiChron.
I also changed the PROGMEM statements to fit the 1.6.8 standard.
And finally, I got the PCBs for the 1284-equipped versions of WiFiChron and bGeigie nano.
For both I relied on internal (software-driven) pull-ups (basically I eliminated the pull-up resistors), without checking first if that would work. Unfortunately, the current sanguino library does not implement correctly the function pinMode(x, INPUT_PULLUP). So I had to resort to resistors solder on the back of the board. That, plus missing a (necessary) decoupling capacitor, plus also missing some connections on the bGeigie board, made for a "fun-filled", but in the end successful, testing. More on these in a future post.

Wise Clock 4 software for Arduino 1.6.8

Scott H. put the time and the effort to port the Wise Clock 4 code (also the HDSP and WiFiChron code) to Arduino 1.6.8 (the latest, but maybe not the greatest). This is a big endeavor, which I did not plan to pursue any time soon. Now, thanks to Scott, here we have it. I compiled it and uploaded it myself, on Windows (he did it on Mac).
But before uploading to ATmega1284, this section needs to be inserted in boards.txt (*):

############################################## W/ ATmega1284p 16mhz

Note the 2 new (compared to previous versions of boards.txt) required lines, "upload.tool" and "build.board" (which has a default value though).

Next, as specified in the line "", we need to create the folder "sanguino", containing the core files. Folder structure should look like this:

Note that a few sanguino core files that worked in Arduino 1.0.6 require changes. Like the Wise Clock 4 files, most of these code changes are related to the PROGMEM definition, which now requires every progmem variable to be constant. The modified files are WString.* and Print.*, copies of the arduino core files.

(*) There is a more "user friendly" way to add a new board, that involves downloading packages from a specified URL, but I found the learning curve for this method too steep (or, to say it differently, I was too lazy).

Add a barometer sensor to Wise Clock 4

While cleaning up my desk, I found a little I2C module that I completely forgot about. It is a breakout for BMP180 barometer sensor, which I probably bought on ebay for a couple of dollars (I just checked, it is still under $2). This tiny board can be added literally to any Arduino clock to display atmospheric pressure, with the help of Adafruit_BMP085 library.

Connecting the barometer to Wise Clock 4 is trivial: I soldered wires directly to processor pins (SDA, SCL, VCC and GND), as shown in the photo. There is plenty of clearance between the board and the display.

In the software, as mentioned, I used the Adafruit BMP085 library, which also covers the compatible BMP180. There is no extra setting required from the user: the pressure is displayed together with the temperature, and enabled/disabled from the TEMP+/- menus. Also, there is no extra settings when compiling/building: if the BMP180 module is not installed, no pressure data will be displayed.

Essentially, the new code consists of 2 functions added to the AppTmp class:

boolean CAppTmp::initBarometer()
  bmp = new Adafruit_BMP085_Unified(10085);
  isBaro = bmp->begin();

int CAppTmp::getPressure()
  int pressure = 0;
  if (isBaro)
sensors_event_t event;
    if (event.pressure)
  pressure = event.pressure;
  return pressure;

Another nice thing about BMP180 is that it can also provide the temperature. But, like DS3231, it seems to be a little off compared with a regular thermometer sitting nearby.

Next, I will try to attached the little barometer to wsduino in an aesthetically pleasing manner, probably in the empty space in the top left corner.

July 2014 release of Wise Clock 4 software

This latest code release includes:
  1. introduction of the two-faced (Kandinsky) feature, on 2 or 4 displays;
  2. fixes related to screen centering for 3 or 4 displays (Big, Stopwatch etc);
  3. fix for Pacman app on 3/4 displays;
  4. fix for Words app on 3/4 displays (so the text does not start scrolling in the middle of the screen);
  5. improved the font definition for digits (effectively making them 5x7 pixels; see photo below);
  6. fix for Score app (swapped button functions);

I am sure that there are still a few bugs to fix.
As well, some improvements would be nice, the most notable being the better use of a screen with 3 or 4 displays, especially for the Big mode, where the time is still shown on the (center) 2 displays. (This would call for spreading out of the digits, or even the definition of a wider font.)


A customer recently requested a two-faced Wise Clock, basically two displays back-to-back connected to the same Wise Clock board, and showing the same thing (in the same time) on both sides. This would be another "Kandinsky" clock (see this post for pictures of the original; name coined by Wyolum, I believe).

To include this feature in the current software was not trivial. The main idea was to define a new macro (WANT_TWO_FACE) and play with it around existing calls to HT1632 functions. This was the idea. The actual implementation is quite messy and spreads over 18 or so (mostly the "app") files. In any case, I only beta-tested it with 2 displays and works in all modes except "Big" (I didn't bother to investigate yet).

Here are a few photos.

(The second display has a defect, hence the crooked last 0.)

Next round of testing should cover the 4-display 2-faced clock, that is, 2 displays on each side, like a double dual Wise Clock (I know, it's confusing. I will probably ask permission to rename it "Single Kandinsky Wise Clock" and "Double Kandinsky Wise Clock" since I am running out of attributes. Just kidding.)

On a different front, my friend Nick built a 4-display Wise Clock 4 and sent me this photo (plus a set of laser-cut plates to build one myself too; thanks again Nick). Apparently, the software for the 4-display clock has some bugs, so expect soon a new code release (which will include the "two-faced" feature as well).

And lastly, I found this board on ebay. No, I did not buy it (and I am not going to). Just look carefully at the photo and think how you would encase it, with a knob inserted on the pot's shaft..

Black Friday (and beyond) sale

I was going to have a Black Friday sale anyway, but now I have one more reason for it, and it's an embarrassing one: the latest batch of PCBs have a little flaw, but they can be easily fixed. So, until I run out of them, both the Wise Clock 4 kit and the Complete Wise Clock 4 kit will be $10 less, for $57 and $115 respectively. (As always, I also offer discounts for multiple units, just ask.)

The photos below show a few ways to fix it. Basically, the GND terminal of the USB connector is disconnected from the board's ground. The short wire re-connects them again.

The sleekest way would be on the bottom, using a resistor terminal inserted together with the 2x8-pin female header, as shown in the photo below.

Solder the other end together with the 6-pin FTDI connector.

Here is the story of how this happened. The PCB used to be 101.2 mm in length. That was 1.2 mm longer than the 10 cm limit imposed when using Seeedstudio's PCB prototyping service. I never had a problem before, I always got them manufactured like they were 10 cm in length. Now, Seeedstudio decided to enforce the 10 cm limit (or pay up like they were in the next size bracket). I reacted by shrinking the board, cutting 1.2 mm from the right side. It seems that the 1.2 mm side was very important.

There is more, unfortunately: in the process of generating the Gerber files, I even forgot to select the "Top names" for the silkscreen, so now the resistors are not named at all.

When you install the resistors, keep in mind that 3 of them, with values of 4k7, must be positioned in the correct places, as shown in the assembling instructions. (The other resistors are all 10k, soldered stress-free in the remaining resistor places.)

And that's why the board is now essentially free with the kit. If you don't feel confident that you can do it, please ask me to fix it for you. I will solder the little bridge wire and also solder the three 4k7 resistors correctly. You do the rest.

Note: The photo shows the board bare, but the PCB in the kit comes with the SMD components (SD card socket, the DS3231 and the 3V3 regulator) soldered already.

Play Tetris on Wise Clock 4

No kidding.
I was looking for a suitable application to demo with Wise Clock 4 placed vertically (that is, standing on its shortest side), when I found this hackaday post about Tetris on a LED matrix. The code, already written for Arduino, clean and easy to understand (kudos to Jianan), was a breeze to port. I only had to change a few functions (display, user buttons), comment out a few more (sound, text etc) and downsize from 7 colors to just 3.
Commands come from Bluetooth terminal ("BlueTerm" app on Android), basically replacing the buttons with letters (U, R, L, D; you got the idea).

The Wise Clock 4 vertical stand is possible thanks to the enclosure-mounted power jack, wired to the display's screw terminals, as shown in the photo below (that also captures a part of my messy desk).

(This is a typical example of how one thing leads to another. I don't usually play games, but when I do, I use my implementations. "Time well wasted", as the saying goes.)

New release of Wise Clock 4 software

Some of the latest software features have already been introduced (see this post). Since then, a few bugs have been fixed (again, thanks Mike!), the code was streamlined even more, and some apps (Countdown, Score, Stopwatch, Quote) have been expanded and improved.

Below is a list of the most recent code changes:
  • added PWRON setup option to select app to run when the clock starts up;
  • compilations options (add/remove features) moved to file "UserConf.h". That is:
- Instead of editing WiseClock.cpp to select applications to build, edit "UserConf.h".
- Instead of editing WiseClockVer.h to select WiseClock 3 or 4, edit "UserConf.h".
- Instead of editing HT1632.h to select the number of displays, edit "UserConf.h".
  • on dual display, Countdown includes days as well, in the format DD:HH:MM:SS;
  • Countdown shows the correct amount of time left even if power was down for a while (this is done by saving the end time in eeprom); for this feature, the PWRON must be set to "CNTDWN";
  • similar to Countdown, app Score has been extended to "remember" the numbers even after the power was out for a while; in either case, the respective app must be selected in PWRON;
  • ability to display quotes randomly, if the user selects "RND" in app Quote;
  • on dual display, Stopwatch is now using regular-size font (still using tiny font on single display);
  • Stopwatch has a new mode, "Conventional", in addition to the previous one (called "Rattrapante"); the conventional mode accumulates the time passed between consecutive presses of the start/stop button, as a mechanical chronometer would do; "rattrapante" mode shows the amount of time passed from the moment it was first started, regardless of how many times the chronometer was stopped;
  • on dual display, with "Font+" selected, Score and Stopwatch are using big font and no longer show the current time on the bottom line (they show as before with "Font-" selected); 

One of the challenges in writing complex code for embedded systems is the compromise between following an Object Oriented approach and trying to minimize the amount of RAM at runtime. Let me elaborate a bit. Each C++ object, once created (at runtime), used or not, takes some RAM, storing data members and the vtable (when using virtual functions). Wise Clock software is trying to follow an OO design. Each App class is derived from an abstract base class. The base class defines 2 pure virtual functions (init and run), which must be implemented by every App class.
At runtime, each app is created as a static object. Therefore, if 12 apps are declared (through the use of ifdefs in UserConf.h), 12 objects will be created in RAM, each one storing its own data and its own vtable. That's a pretty big price to pay in the world of embedded systems with little RAM space (just a guess, based on my very limited experience).

A non OO approach would create and use variables only for the app that is in current use, as the code is being executed. Although this solution would use less RAM, the current complexity would make the code unmanageable.

So back to the compromise, how does one solve it? (This is not a rhetorical question.)

"Close enough" Wise Clock display mode

Inspired by this article, MikeM added the "Close enough" mode to the functionality of Wise Clock 4.
This new "face" is accessed by pressing the "Set" (middle) button inside the application "BIG".
The time is shown as an approximation, with the hours as an explicit number, the minutes filling the circle of  a 60-minutes clock and the passing seconds represented by a moving red dot on the bottom line. Thus, one can tell time with a 5-minutes "precision" at a very quick glance.

I will soon publish a new software release that will include this mode. It will also include some other changes I've done recently:
  • moved some "app" functionality out of the main class (WiseClock.cpp) into their own separate classes (AppDemo, AppPacman); will probably need to re-organize WiseClock.cpp, since it almost grew beyond a manageable size (currently at 103KB);
  • in dual-display mode (64x16 resolution), extended AppCountdown to include days in addition to hours: minutes:seconds;

Below is a clip of the countdown in action. The bottom line can show (subject to code change) either the current time (HH:MM::SS) or a static line as in the photo above.

For comparison, this is how the countdown looks on the single-display Wise Clock 4.

  • used (long overdue) proper C++ polymorphism (created CAppBase class with pure virtual functions) to simplify the calls to run() functions of each app class (which are now derived from the base class); essentially, most of the case branches in the switch statement are gone and replaced by a simple pCrtApp->run(), as shown in the code snippet below:

switch (crtApp)
case APP_TMP:
ms = runappTmp();
ms = runappNewSD();
ms = runappStats();
ms = runappSetPowerOn();
default:// for all other apps;
ms = pCrtApp->run();