Tuesday, April 17, 2018

Upgrade your HDSP clock to an NTP-synchronized WiFiChron

So you have the simple HDSP clock but you wish you had the NTP-based time synchronization and the weather feature of the WiFiChron (not to mention the alarm). The "solution" is to buy the WiFiChron kit :)

The alternative "solution" is to hack your HDSP clock, by adding a proto-shield with an ESP8266 module, a 3V3 regulator (e.g. MCP1700) and a buzzer, the components that make the difference between the two clocks.

Before building the ESP8266 proto-shield, perform the following 4 modifications (highlighted in the next photo) on the HDSP board:
  1. isolate the FTDI's CTS pin (second from the top) by cutting its traces to the ground;
  2. wire the newly isolated FTDI pin 2 to pin 16 of the processor (D10, used for the buzzer);
  3. cut the trace connecting pin 1 of the LED display (RST) to pin 3 of the processor;
  4. solder the right angle FTDI male header on the back of the board, towards the USB plug.

The small proto-shield, that looks like in the photos below, will plug in the FTDI header. Naturally, the same FTDI header is used for either the FDTI breakout (to upload sketches) or the ESP8266 proto-shield.

Besides the hardware, one little software change (of the original WiFiChron sketch, available here) is required as well, due to the second button connected to A3 (instead of A1 as in WiFiChron):

    #define PIN_BTN_UP 17  // A3 in hacked HDSP
// #define PIN_BTN_UP 15  // A1 in WiFiChron

Since the 3rd button ("Down") is missing on the HDSP board, only the "Up" button function will be available (the left button).

Follow this post to set up the network connection.
And remember, the ESP8266 baud rate MUST be lower than 115200 (set it to 38400), since the ATmega328 with internal clock cannot handle this speed reliably (according to the datasheet, the drift is about 8%).

Thursday, October 19, 2017

WiFiChron "user manual"

With the daylight saving time ("DST") fall back fast approaching (Nov 5, 2017 for North America), a few explanations and details are required to prepare the WiFiChron clock to automatically set itself.

As much as I would like the menu items to be intuitive, I have to acknowledge that some of them look a little cryptic, and the reason is the 8-character space constraint of the display. Below is the list of all menu items, in the order they appear when the "Set" (left-most) button is pressed.
  1. Alrm on/off - alarm on or off by pressing either the "Up" or "Down" button;
  2. Al 00.00 - alarm time; first set the hours (flashing) by pressing either the "Up" or "Down" button, then move to the minutes by pressing "Set" (minutes will flash); set minutes with "Up"/"Down" buttons;
  3. HH.MM.SS - current time; move from hours to minutes, to seconds by pressing the "Set" button; set the flashing value by pressing "Up" or "Down";
  4. 12/24 Hour - display time in either military (24 hour) mode or am/pm (12 hour) mode;
  5. Scroll 4 - text scrolling speed;
  6. Chimes Y/N - chime (or not) every half hour (top and bottom of the hour);
  7. Moon   Y/N - display (or not) moon phases, in scrolling words (calculated based on the date);
  8. Brite  27 - display brightness;
  9. Yr    2017 - current year;
  10. Month 1 - current month;
  11. Date  1 - current day of the month;
  12. TzHr -1 - time zone hours offset;
  13. TzMn -15 - time zone minutes offset (some places, e.g. Newfoundland and Labrador, use half-hour offsets);
  14. WiFi  Off/On/Ini - connect (or not) to WiFi network (see this post for details);
  15. DST En Y/N - enable or disable automatic time self-adjust when DST changes;
  16. DSTsm 3 - stands for "DST start month"; month for DST spring forward, by default set to 3 (March);
  17. DSTsw 2 - stands for "DST start week"; week of the month for DST spring forward, by default set to 2 (second Sunday);
  18. DSTem 11 - stands for "DST end month"; month for DST fall back, by default set to 11 (November);
  19. DSTew 1 - stands for "DST end week"; week of the month for DST fall back, by default set to 1 (first Sunday);

Here are some photos, courtesy of Tom, of his custom-enclosed, WiFi-enabled, WiFiChron.

Thursday, September 14, 2017

Remixed WiFiChron board

Thanks to all buyers who waited patiently for the new batch of re-designed WiFiChron PCBs to arrive.
The new board eliminates the need for the 2 extra wires on the back (that connected Rx and Tx to ESP8266). The XBee socket, intended for the $45 GPSBee, was also dropped, and replaced with a 3-pin header, used for connecting the $5 GPS modules available on ebay.

Here is the list of notable changes:
  • the only RTC now supported is DS3231;
  • DS3231 is now powered from the regulated 3V3;
  • GPS module is now connected to D17 (already supported in software);
  • as mentioned above, Rx and Tx already connected to ESP8266 module (no need to solder the 2 wires on the back); as before, remember to unplug the ESP8266 module from its socket when uploading a new sketch;
This is how the new board looks like:

and assembled:

Below is the new schematic (compatible with the old one, just re-mixed):

The latest release of the WiFiChron software should still work unchanged with the new board.

On the assembly front, youtuber 12voldvids put together this nice video:

Here is another video on WiFiChron, courtesy of RayS:

If anyone else would like to make and publish videos on any of my kits, I will gladly support it, with explanations/clarifications and, obviously, discounts :)

Monday, July 24, 2017

IV-3 VFD confusion

So you want to build Axiris's IV-3 shield for Arduino, sourcing the IV-3 VFD tubes yourself, from any of the numerous ebay sellers, as I did.

Firstly, it is important to note that, although the assembly manual for IV-3 shield refers to it as "IV-3/IV-3a/IV-6 VFD shield for Arduino", which would make you think one could install either IV-3, IV-3A or IV-6 tubes, this is not quite the case. The reason is the difference in pin configuration:
  • IV-3: 9-segment + dot, 14 pins (1 not connected)

(pin configuration is bottom view)
  • IV-3A and IV-6: 7 segment + dot, 12 pins (1 not connected)
(pin configuration is bottom view)
  • and then, there is IV-3 v-82, which I bought on ebay: 7 segment + dot, 14 pins (3 not connected)
(pin configuration is top view)

IV-3 v-82 (as I named it, based on the printing on the back of the tube), is an amalgamation between IV-3 and IV-3A:
- can be found in either 7 segment or 9 segment (+ dot), although only 7 segments are connected;
- has 14 pins (as to support a 9 segment digit + dot), with 3 not connected;
- pin sequence differs from both IV-3 and IV-3A;
- the "key" (the trimmed unconnected pin) is on the opposite side compared to IV-3/IV-3A.
Below are some photos, with the "axiris" IV-3A on the left.

(Also notice the color of the ceramic insulator, white for the "axiris" IV-3A, pink for the IV-3 v-82.)

To adapt the IV-3 v-82 tube to the Axiris board, the pins need to be scrambled like this:

which leads to this ugly assemblage:

It would probably work with proper heat-shrink tubing around the tube terminals, but I preferred to order the correct IV-3A for which the board was designed.

Conclusion: Pay attention when (and if) you order the tubes for "Axiris IV-3 VFD shield" separately. The sure bet is to order IV-3A, with the white ceramic insulator, and the trimmed pin on the right side (when looking at the digit).

P.S. Also, make sure the tubes are "mirrory" black at the top. If the top is white, then the tube is damaged for sure, air got in the tube (basically the tube's glass is cracked), like in the photo below (right tube is damaged).

Sunday, August 21, 2016

On the GPS features of WiFiChron

The WiFiChron GPS-synchronized clock was begging for some documentation on the menu options and some details on its features. Here they are.

The GPS-WiFiChron uses TinyGPS library to read the time from NMEA sentences (specifically GPRMC and GPGGA strings) from the uBlox Neo GPS module. The clock starts reading the data from the GPS module (using SoftwareSerial library, receiving on pin D7) 2 minutes after power up.

Once a valid GPRMC or GPGGA sentence is received, the minutes and seconds (but not the hours) are automatically re-set from the satellite UTC time. The successful synchronization is indicated by the "up arrow" at the end of the scrolling date (e.g. Sunday August 21, 2016 ^).

In order to automatically set the hours as well, I added a new menu option, GPS? (Yes/No). When this is selected (set to "Yes"), the local hour is calculated based on the longitude, by estimating the timezone using a simple formula:

// estimate time zone from longitude;
int8_t gpstimezone = round(floatLongitude / 15);
hour = gpshour + gpstimezone;

This formula will not work everywhere in the world, since not all time offsets are 15 degrees apart (take for example Newfoundland). So, if you live in one of these places, the GPS auto-sync will not work for you. In this case, make sure that the GPS option is set to "No".

The second GPS-specific menu item is DST? (Yes/No). Its purpose is to adjust the hour according to the North American DST rules (that is, moving one hour ahead in the spring and one hour back in the fall, at specified dates and times). When DST is set to "Yes" (meaning Daylight Saving Time is in effect), the hour is incremented by 1 if the date is between the second Sunday in March and first Sunday in November. The DST hour adjustment works based on the date set up by the user (from the buttons, menu option "Date").
If DST is not observed in the place you live (most Equatorial countries, but also some Canadian provinces like Saskatchewan), then select "No" for DST.

To recap:
  • regardless of the GPS and DST menu settings, the minutes and the seconds are always synchronized with the satellite time;
  • if you want the hours synchronized as well, then set GPS menu option to "Yes";
  • if you want the clock to adjust itself with DST changes (in spring and fall), then set the DST to "Yes";
  • if you want the clock to set its complete time (hours, minutes, seconds) automatically from the satellite, then set the GPS menu option to "Yes", then restart it (power on) and wait a few minutes until a valid sentence is received from the satellite;
  • the date must be set correctly for the DST to adjust the hours (if the DST menu option is set to "Yes").

As an interesting side note, I should mention that AdamM, a customer who bought a WiFiChron GPS-synchronized clock, pointed out that the time is off by tens of seconds compared to any other GPS clock he had (this one, for example). After investigation, I found a few problems in the code (which are now fixed), the main one being that DS1307 library I was using reset the seconds in the start() function (!).

For those interested, below is the relevant (GPS-specific) code, which could be adapted for any other GPS clock.

boolean checkGPS()
  boolean newData = false;

  // for one second, parse GPS data for relevant time values;
  for (unsigned long start = millis(); millis() - start < 1000;)
    while (ss.available())
      char c = ss.read();
#ifdef _DEBUG_
      Serial.write(c); // uncomment this line if you want to see the GPS data flowing
      if (gps.encode(c)) // Did a new valid sentence come in?
        newData = true;

  if (newData)
    float flat, flon;
    unsigned long age, fix_age;
    int gpsyear;
    byte gpsmonth, gpsday, gpshour, gpsminute, gpssecond, hundredths;

    gps.f_get_position(&flat, &flon, &age);
    gps.crack_datetime(&gpsyear, &gpsmonth, &gpsday, &gpshour, &gpsminute, &gpssecond, &hundredths, &fix_age);

    // estimate time zone from longitude;
    int8_t gpstimezone = round(flon/15);

#ifdef _DEBUG_
    char buf[50] = {0};
    sprintf(buf, "GPS TIME=%02d:%02d:%02d, GPS DATE=%4d/%02d/%02d", gpshour, gpsminute, gpssecond+1, gpsyear, gpsmonth, gpsday);
    Serial.print("Timezones from GPS data is ");

    // determine if the time is off (and therefore must be set);
    if (gpsminute != minute || gpssecond != second)
      minute = gpsminute;
      second = gpssecond;

      if (gpsAutoHour)
        // automatically set the hour based on timezone/longitude;
        hour = gpshour + gpstimezone;

        // set to summer time if DST is selected;
        if (doDST && isSummerTime())
        // make sure hours stays between 0 and 23, otherwise setTime() will fail;
        if (hour < 0)
          hour = hour + 24;
        else if (hour > 24)
          hour = hour - 24;

      setTime(hour, minute, second);

#ifdef _DEBUG_
      Serial.print("RTC synced from GPS to ");

      Serial.print(">>>Checking if the time was set correctly... ");

  return newData;

// determines if it is the moment to change the hour;
// also set the dstAdjust to either 1 or -1;
boolean isDstMoment()
  if (minute != 0 || second !=0)
    return false;
  // in the second Sunday in March, 2 -> 3;
  if (month == 3)
    // check for second Sunday;
    if (dow == 1 && day >=8 && day <=14)
#ifdef _DEBUG_
      Serial.println("switching to summer time; add one hour");
      dstAdjust = 1;
      return true;
  // in the first Sunday in November, 2 -> 1;
  else if (month == 11)
    // check for first Sunday;
    if (dow == 1 && day >=1 && day <=7)
#ifdef _DEBUG_
      Serial.println("switching to winter time; subtract one hour");
      dstAdjust = -1;
      return true;

  dstAdjust = 0;
  return false;

// determines if it is the moment to change the hour;
// also set the dstAdjust to either 1 or -1;
boolean isSummerTime()
  unsigned long summerStartTime = 0;
  unsigned long winterStartTime;

  if (month < 3 || month > 11)
    return false;

  // find the second Sunday in March; start with March 8th;
  for (int iDay=8; iDay<15 font="" iday="">
    if (zellersDow(year, 3, iDay) == 1)  // is it Sunday?
      summerStartTime = makeUnixTime(year, 3, iDay, 2, 0, 0);
  // find the first Sunday in November;
  for (int iDay=1; iDay<8 font="" iday="">
    if (zellersDow(year, 11, iDay) == 1)  // is it Sunday?
      winterStartTime = makeUnixTime(year, 11, iDay, 2, 0, 0);

  time_t timeNow = makeUnixTime(year, month, day, hour, minute, second);
  if (timeNow > summerStartTime && timeNow < winterStartTime)
    return true;
    return false;