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
#endif
      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.println();
    Serial.println(buf);
    Serial.print("Timezones from GPS data is ");
    Serial.println(gpstimezone);
#endif

    // 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())
        {
          hour++;
        }
        // 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(hour);
      Serial.print(":");
      Serial.print(minute);
      Serial.print(":");
      Serial.println(second);

      Serial.print(">>>Checking if the time was set correctly... ");
      getTimeFromRTC();
#endif
    }
  }

  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");
#endif
      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");
#endif
      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);
      break;
    }
  }
  
  // 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);
      break;
    }
  }

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


Monday, July 18, 2016

WiFiChron with ATmega1284

The WiFiChron code, with support for ESP8266, nearly reached the program memory limit of ATmega328, yet still missing a few features, the most important being the much needed debugging capability.

Naturally, the next step in WiFiChron's evolution was to upgrade to Atmega644P/1284P. Since there was no room on the board for the 40-pin DIP package, I settled for the SMD version.


The schematic is shown below.


Although functional, the board I designed is far from perfect:
  • requires pull-up resistors for buttons; I relied on software pull-up, but that does not work in the current Sanguino library;
  • requires a couple of more decoupling capacitors;
(I soldered all these extra parts on the bottom side of the PCB, as shown in the next photo. The 595 shift-register is soldered on the bottom by design. The next revision will have the currently missing parts in the SMD package.)
The WiFiChron-1284 board has the same dimensions as the previous revision, and it still fits in the Serpac A20 box.


I burned the bootloader using the on-board ICSP header.

Thanks again to MikeM, who contributed the software (Arduino 1.6.7 - compatible), featuring:
  • proverb display;
  • moon phases;
  • a few new menu options for user settings;
  • improved support for ESP8266;
  • integrated support for GPS module.

More on the code later.

Tuesday, April 26, 2016

From the mailbox

AlexP 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 had 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

NelsonC 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 soldered 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.

Sunday, April 3, 2016

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-Proverb 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).

The Wise Clock 4 files are all in the same "TheClock" folder, directly under "libraries", as shown in the following screenshot.


But before uploading to ATmega1284, this section needs to be inserted in boards.txt (*):

##############################################
atmega1284.name=Sanguino W/ ATmega1284p 16mhz
atmega1284.upload.tool=avrdude
atmega1284.upload.protocol=stk500v1
atmega1284.upload.maximum_size=129024
atmega1284.upload.speed=57600
atmega1284.bootloader.low_fuses=0xFF
atmega1284.bootloader.high_fuses=0x98
atmega1284.bootloader.extended_fuses=0xFD
atmega1284.bootloader.path=atmega
atmega1284.bootloader.file=atmega1284p_16MHz.hex
atmega1284.bootloader.unlock_bits=0x3F
atmega1284.bootloader.lock_bits=0x0F
atmega1284.build.mcu=atmega1284p
atmega1284.build.f_cpu=16000000L
atmega1284.build.core=sanguino
atmega1284.build.board=AVR_ATMEGA1284
##############################################

The two highlighted lines are new for 1.6.8: "upload.tool" and "build.board" (which has a default value though).

Next, as specified in the line "atmega1284.build.core=sanguino", 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 need to be modified to compile on 1.6.8. Like the Wise Clock 4 code itself, most of these 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.

Talking about sanguino core files, which served pretty well so far, it is worth mentioning that the current implementation for pinMode(pin, INPUT_PULLUP) (in file wiring_digital.c) is not working. Until I find a software solution, I will still need to use pull up resistors.


(*) 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).