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

Saturday, March 26, 2016

GPS-synchronized Mondrian clock

The challenge was to add GPS to this "basic LED matrix clock".


Since I don't see the point of a GPS clock that does not show seconds, I had to figure out how to fit 6 digits on the 8x16 LED matrix. One way to do it is this:


as used by the "Matrix Clock V1.0". Kind of hard to distinguish between 0 and 8 though.

Another way is based on MixiClock, where 4 digits are crammed in a 8x8 (tri-color) matrix. (This was more than 4 years ago. Incredible how little progress I made since.)

As for the name, I settled for "Mondrian" because Kandinsky was already taken :)



The hours are shown in red, the minutes in green and the seconds in orange. After power up, the seconds blink until the GPS time is received (less than 5 minutes in my house, more than 3 meters away from the nearest window). Only the minutes and the seconds are synchronized (but not the hours).

The Mondrian clock is made of 2 boards: the wsduino with the GPS Bee plugged in (and the antenna affixed on the back with double sided tape, as shown in the next photo), and the LED-mini-display shield.


The GPS Bee module is connected on the hardware serial port (there are only a couple of pins left available, D2 and D17).
The clock has two buttons (on the LED matrix shield) used for incrementing the hours and the minutes respectively. The GPS synchronization occurs every 10 hours. That should be more than enough, considering that the highly accurate DS3231 on the wsduino board is responsible for timekeeping.
The clock, powered by 5V, consumes less than 200mA.
The sketch can be found here (compiles on Arduino IDE 1.0.6 and has dependencies on TinyGPS and DS1307 libraries).


Saturday, March 19, 2016

A recap of Wise Clock 4 features

The list of code changes and new features at the top of the source files (see WiseClock.cpp, for example) grows longer and longer. I thought of getting this list out in the open, for the people who are not particularly interested in the C++ code or just don't want to look at the source code (or release notes file for that matter, not updated in a long while though) to find out what Wise Clock 4 can do.

1. First and foremost, Wise Clock 4 displays quotes retrieved from text file(s) stored on SD card, with both  FAT16 or FAT32 file systems being supported. This piece of functionality is still the core of the software.

2. Secondly, Wise Clock 4 displays the time retrieved from DS3231 extremely accurate real time clock chip. (These first two features inspired its name: "wise" and "clock" :)

3. The display is tri-color (red, green, orange) LED matrix with a minimum resolution of 16x32 and a maximum of 16x128. Currently, the resolution is hard-coded, meaning that the software is compiled and built for a specific display and it cannot be changed dynamically (by the user, from buttons or configuration settings, although this may be a future enhancement);

4. Time can be shown in:
  • various display modes:
    • Pacman;
    • Pong;
    • Words;
    • TIX;
    • Unix;
    • binary;
    • BIG;
    • Quotes (time displayed along with scrolling text);
    • "close enough" (approximate), showing passed minutes pie-chart;
    • dual time-zone analog display, with hour and minute hands "rotating" inside round face;
  • most formats:
    • just hours and minutes or
    • hours, minutes and seconds, or
    • even tenths of a second for chronometer;
    • 12 or 24 (military) hour format;
  • different sizes:
    • medium, on the bottom half of the screen;
    • big, scrolling across or rolling digits or
    • tiny, when more digits need to fit on the screen.
5. Beside clock, Wise Clock 4's other time-related functions:
  • alarm;
  • stop watch (chronometer);
  • count down timer;
  • project time clock;
  • GPS-based time synchronization (requires GPSBee module to be installed);
  • NTP-based time synchronization (requires Esp8266 Wee/WiFly module to be installed);
  • scoreboard (as a separate, independent, standalone, software);
  • time lived since birth;
  • chime every half hour;
  • show night and day on a global map (requires map overlay, see this post);
  • show the current time at up to ten different cities, cycling through each city;
  • show messages predefined for specific days (e.g. birthday/holiday wishes, event reminders etc.) recorded in message.txt file on SD card;
  • automatic, configurable (in SD file) "night" mode for display (saves power and LEDs, produces less light at night);
  • automatic and configurable (SD file) Daylight Savings Time;
6. Wise Clock 4 functions not related to time:
7. Other Wise Clock 4 features:
  • manual (press "Plus" button) or auto dimming (with LDR) of the display brightness;
  • manual (press "Set" button) adjustment of the speed for text-scrolling;
  • remote control using 4-button keyfob;
  • uses different colors to indicate how close the alarm is to go off (red two hours before, orange before that);
  • define and display quotes, partially or entirely, in different colors;
  • select a mode ("app") to start at power on;
  • select one of the many different fonts;
  • post sensor data to monitoring web site;
  • display info retrieved from the web (through WiFi), e.g. stock quotes, weather data etc.
  • can be used as electronic commercial signage for advertisement, news, announcements etc. due to its high visibility/readability and ease to set up and configure;
  • can be used in competitions or sporting events, to display timing and/or info, visible from both sides of the finish line (see Kandisky clock).
8. A vast tree of menu items, allowing the user to access its many functions easily, to choose the time-display mode, to set values (e.g. time) and/or parameters (e.g. amount of time for a countdown) and to save settings (e.g. the app running at power on).

The menu is split into two loops: one for settings, the other one for apps.
  • Settings menu items are: APPS, ALARM, AL+/AL-, DATE+/DATE-, REMI+/REMI-, TEMP+/TEMP-, MESG+/MESG-, CHME+/CHME-, FONT+/FONT-, CELS/FAHR, 24H+/24H-, DST+/DST-, TIME, Y M D, DAY, NGHT+/-, LOG+/-, ANIM+/-, PWRON.
  • Apps menu items are: SETUP, QUOTE, BIG, TIX, CNTDN, STOPW, WORDS, SCORE, PONG, PACMN, CHRON, RACER, UNIX, LIVED, TCLOK, UTC, LIFE, DEMO, STATS, NEWSD.
Selecting the "SETUP" menu entry will take you to the Settings menu loop, selecting the "APPS" menu entry will take you to the Apps menu loop.

Each app can use the buttons "Plus" and "Set", with button "Menu" always being used to trigger the display of the menu.
The buttons "Set" and "Plus" have different functions for different apps. For example, in SCORE, one of the simplest apps, each button increments the score for one side/player; in CHRON, "Set" moves from one function to the other, with "Plus" triggering that function (e.g. increment the number of hours).
Menu navigation is intuitive and easy to understand after playing with the buttons a little bit.

9. From the source code perspective, the Wise Clock 4 software is modular, flexible and extensible (see this mod, for example), based on a framework (implemented in WiseClock.cpp) and multiple "applications" (files named App*.cpp) that can be enabled/disabled (included in/excluded from the software) at build-time by macro definitions (e.g. #define) and activated/executed at run time.



Add light sensor to Wise Clock 4

This is long overdue.
As we know, the display brightness on Wise Clock 4 can be changed between 5 levels, by pressing the "Plus" button. To adjust the brightness automatically, based on the light conditions, we need to add a light-sensitive sensor of some sort, and the most common is the LDR (light-dependent resistor).
Any of the countless articles and tutorials on Arduino + LDR will teach how to connect the LDR to an analog pin, using a voltage divider. For Wise Clock 4, the LDR is connected between A0 (pin 40 of the processor) and ground, with a 10k resistor between A0 and Vcc, as shown below:

  GND|----[ LDR ]---A0---[10k resistor]----+Vcc

The automatic brightness adjustment is enabled in software with this macro (in file UserConf.h):

// use an LDR (connected to A0) to automatically adjust
// (every 5 seconds) screen brightness to ambient light;
#define AUTO_BRIGHTNESS

and implemented in this new function:

void WiseClock::checkBrightness()
{
#ifdef AUTO_BRIGHTNESS
  // adjust brightness every 5 seconds;
  if (millis() - lastCheck > 5000)
  {
    int ldrValue = analogRead(0); // A0
    byte brightLevel = map(ldrValue, 0, 1023, MAX_BRIGHTNESS, 0);
    if (nBrightness != brightLevel)
    {
      nBrightness = brightLevel;
      setBrightness(brightLevel);
    }
    lastCheck = millis();
  }
#endif
}

called from the main loop (TheClock.ino):

void loop()
{
unsigned long ms = millis();

checkSound(ms);
wiseClock.checkSerial();
wiseClock.checkScroll(ms);

if ((long)(ms - wiseClock.nextRun) >= 0)
{
alarmClock.getTimeFromRTC();
alarmClock.checkAlarm();
wiseClock.checkMenuActive();
wiseClock.runCrtApp();
}

wiseClock.checkBrightness();
}

It really doesn't get much simpler than this.

I also considered other hardware solutions (and actually bought the parts):
- I2C light sensor TSL2561
but I did not follow through after I saw how effective (and so much cheaper) the LDR is.

Monday, February 29, 2016

Hockey scoreboard

What's missing from this picture (of a backyard hockey rink)?


You got it (probably the post title helped too): a scoreboard.
For those not familiar, a traditional one looks like in the photo below (click on it to see it bigger).


It may look simple, but it provides a lot of functionality:
  • display 2-digit home score;
  • display 2-digit visitor score;
  • countdown timer, with the starting time configurable/settable; timer displays mostly minutes and seconds, but under 1 minute, it displays seconds and tenths of a second;
  • stop/resume the countdown;
  • display round/period; number of periods is configurable/user-settable;
  • buzzer sounding at the end of the round, game or on demand;
  • penalty timers for up to four players (that also stop/resume with the main timer);
  • functions as a clock when not used in a game.
The control for the hockey scoreboard is pretty complex too. The control console is usually wired to the scoreboard. Sometimes the console is detachable (brought in to the booth by the score keeper), connected to a cable that plugs in.

An easy and quick way to build a home-made hockey scoreboard is to re-program a dual-display (16 x 64 pixels) Wise Clock 4. The scoreboard should be controlled wirelessly, from a distance of at least 10 meters (which disqualifies infrared remotes and also the most common Bluetooth, "class 2"). Potential solutions for the scoreboard remote control are:
  1. XBee
    • good distance range;
    • not cheap;
    • reliable communication;
    • requires building and programming a remote control device;
    • already hardware-supported by Wise Clock 4 board;
  2. RFM-12
    • good distance range;
    • cheap;
    • reliability probably ok for short messages;
    • requires building and programming a remote control device;
    • uses SPI interface, requiring some hardware hacking;
  3. RF 4-button key fob
    • cheap
    • easy to integrate
    • does not require any software support;
    • reliability at distance greater than 10m (?);
    • no need to build the remote control;
    • unfortunately limited to only 4 buttons;
  4. WiFi
    • cheap (ESP8266)
    • works as access point; user connects from smart phone or tablet, to access HTML form pages to type/input commands;
    • requires extra coding;
    • no need for extra remote control device (since using phone or tablet);
For the user, the easiest way to control the scoreboard would be by pressing physical buttons (the other, less-friendly, way would be by entering/selecting messages, e.g. "STOP", or "resume").

If using buttons, at least 4 are required to cover the minimum functionality:
  • reset scoreboard - D0;
  • start/stop/resume countdown timer - D1;
  • increment home score - D3;
  • increment visitor score - D2 (available) or D22 (re-routed)
which makes the 3rd solution above (RF 4-button key fob) an easy pick.

This is how the prototype scoreboard works on my desk:



The software is a branch-out of the Wise Clock 4 main software (using the same libraries, classes and functions, but in a separate .ino file). I created a new font (file fontFull.h) for the score digits, defined in a 8x16 matrix.
To add extra features ("clock mode", "chrono mode", message scrolling etc), more buttons/commands would be required on the remote control. Which means that the RF 4-button key fob is barely enough for now, and another solution must be implemented. My next favourite is the RFM-12, a topic for a future post.

Saturday, February 20, 2016

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;
bmp->getEvent(&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 attache the little barometer to wsduino in an aesthetically pleasing manner, probably in the empty space in the top left corner.

Sunday, February 14, 2016

New Wise Clock 4 software release

The latest release of the Wise Clock 4 software can be found here.

It includes a few new features:
The WiFi settings are configured in the SD card file message.txt, and they look like this:

#### ESP8266 configuration
#
# Esp8266.ssid Wireless SSID to connect to
# Esp8266.phrase pass phrase to use (WPA authentication)
# Esp8266.sntp optional SNTP server IP address (not hostname) for time synchronization
# Esp8266.rssurl optional URL of an RSS feed, like http://w1.weather.gov/xml/current_obs/KMYF.rss

Esp8266.ssid   BELL475
Esp8266.phrase 7xyz1E9F6
Esp8266.sntp   
Esp8266.rssurl 

This feature is enabled in the code by the following line in file UserConf.h:
#define WANT_ESP8266
  • support for GPSBee (from seeedstudio): only the minutes and seconds are set from the GPS data, the hours remain as set from the buttons; synchronization is performed once a day and/or when the clock is reset (powered off-on);
To enable this feature, make sure the following line in UserConf.h is not commented out:
#define WANT_GPS
  • "Night" mode: turns the display off at night, between the hours set in message.txt file, as shown:
#### turn display off at night (in conjunction with menu "NIGHT");
#### formatted as HHMM (military time);
Night.start 1030
Night.end   600

This feature is enabled/disabled from the NIGHT+/- option in the SETUP menu. In Night mode, the display shows just a random dot. The implementation is in AppNight.class, and can be changed to display any desired effect (e.g. Bezier splines, Lissajous curve etc).
  • "Unix time" mode: displays advancing Unix time (passing seconds from Jan 1, 1970); accessed through the "UNIX" menu option.
Also, debugging (sending messages to Serial port) is now controlled by a single line in UserConf.h:
#define _DEBUG_

Before compiling and uploading the software, make sure the debug macro above is commented out (since it is not in the code you just downloaded). With the debug disabled, the code runs faster and takes less memory space.


Saturday, January 23, 2016

Break

I was going to say that this was the typical case of work/life taking precedence over hobbies. Not a good excuse though, since one can always find bits and pieces of time, like coins between couch cushions :). The best explanation for the long break is probably laziness. But I didn't just loiter around. With my desk full of just-started or half-finished projects screaming for time and attention, how could I? Here are some of the things I re-visited and tried to complete.

1. The Axiris IV-3 clock
As designed by Axiris team, the enclosure for the IV-3 shield can only accommodate one Arduino. The only way to make a real (with RTC) clock is to replace the Arduino with a wdsuino. Then, there is the issue of buttons (or any other way to set the time without using a PC, through USB). I managed to add an extra board that holds two buttons, accessible through the holes on the bottom side of the enclosure.
Below are some pictures.






The sketch I used is included below (based on the sample code from Axiris).

#include "Wire.h"
#include "DS1307.h"

#define PIN_BTN_SETHOUR 13
#define PIN_BTN_SETMIN  12

// globals; their values are set in getTimeFromRTC();
int hour;
int minute;
int second = 0;
int year, month, day;

// receive commands from serial port in this buffer;
char cmdBuffer[30] = {0};
byte nCrtBufIndex = 0;

// read time from DS1307 at intervals; for 5000, that's about 6 times a second;
#define MAX_TIME_READING_COUNTER  5000
long timeReadingCounter = MAX_TIME_READING_COUNTER;

/*
       pin 2
        ---
 pin 7 |   | pin 3
       |   |
 pin 8  ---
       |   | pin 4
 pin 6 |   |
        --- . pin 9
       pin 5
*/

static  byte  digit_seg_data[12*7] PROGMEM =
{
/* pin    2     3     4     5     6     7     8  */
        HIGH, HIGH, HIGH, HIGH, HIGH, HIGH,  LOW,    // Digit 0
         LOW, HIGH, HIGH,  LOW,  LOW,  LOW,  LOW,    // Digit 1
        HIGH, HIGH,  LOW, HIGH, HIGH,  LOW, HIGH,    // Digit 2
        HIGH, HIGH, HIGH, HIGH,  LOW,  LOW, HIGH,    // Digit 3
         LOW, HIGH, HIGH,  LOW,  LOW, HIGH, HIGH,    // Digit 4
        HIGH,  LOW, HIGH, HIGH,  LOW, HIGH, HIGH,    // Digit 5
        HIGH,  LOW, HIGH, HIGH, HIGH, HIGH, HIGH,    // Digit 6
        HIGH, HIGH, HIGH,  LOW,  LOW,  LOW,  LOW,    // Digit 7
        HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH,    // Digit 8
        HIGH, HIGH, HIGH, HIGH,  LOW, HIGH, HIGH,    // Digit 9
         LOW,  LOW,  LOW,  LOW,  LOW,  LOW, HIGH,     // Hyphen
         LOW,  LOW,  LOW,  LOW,  LOW,  LOW, LOW      // empty/not lit at all
};

typedef  struct  _TUBE
{
  byte    digit;      // 0..9
  byte    dot;        // HIGH or LOW
}
TUBE;

// Display state of the tubes (read-write): {digit, dot}
static  TUBE  tube_list[4] =
{
  { 10,  LOW },
  { 10,  LOW },
  { 10,  LOW },
  { 10,  LOW }
};

// Variables accessed at interrupt level
static  byte    cur_tube = 3;        // 0..3

ISR(TIMER1_COMPA_vect)
{
  const  byte  *digit_seg_p;
  byte          digit;
  byte          pin;

  // Clear pins as fast as possible
  PORTC &= ~0b00001111;  // Clear pin A[0..3]
  PORTD &= ~0b11111100;  // Clear pin 2..7
  PORTB &= ~0b00000011;  // Clear pin 8..9

  __builtin_avr_delay_cycles(8*40);  // 40 us (at 16 MHz)

  // Select the next tube
  cur_tube++;
  cur_tube %= 4;

  digit = tube_list[cur_tube].digit;
  digit_seg_p = digit_seg_data + 7*digit;
  for (pin = 2; pin < 9; pin++, digit_seg_p++)         digitalWrite(pin,pgm_read_byte(digit_seg_p));
  digitalWrite(pin,tube_list[cur_tube].dot);

  // Enable the current tube
  digitalWrite(A0+cur_tube,HIGH);
}

void  setup ()
{
  Serial.begin(9600);
  pinMode(A0,OUTPUT);
  pinMode(A1,OUTPUT);
  pinMode(A2,OUTPUT);
  pinMode(A3,OUTPUT);
  pinMode(2,OUTPUT);
  pinMode(3,OUTPUT);
  pinMode(4,OUTPUT);
  pinMode(5,OUTPUT);
  pinMode(6,OUTPUT);
  pinMode(7,OUTPUT);
  pinMode(8,OUTPUT);
  pinMode(9,OUTPUT);
  pinMode(10,OUTPUT);

  // Turn on the LEDs
  digitalWrite(10,HIGH);

// (fc) filaments, for my special case; normally this is not needed;
  pinMode(11,OUTPUT);
// power the filaments;
  analogWrite(11, 200);

  // set-time buttons;
  pinMode(12, INPUT_PULLUP);
  pinMode(13, INPUT_PULLUP);

  cli();

  // We've observed on Arduino IDE 1.5.8 that TCCR1A is non-zero at this point. 
  // So let's play safe and write all relevant timer registers.
  TCCR1A = 0b00000000;

// (fc) half the number for 8MHz Arduino
//  OCR1A  = 250-1;       // 250 Hz (62500/250)
  OCR1A  = 125-1;       // 250 Hz (62500/250)

  TCNT1  = 0;
  TIMSK1 = 0b00000010;
  TIFR1  = 0b00000000;
  TCCR1B = 0b00001100;  // Enable timer

  sei();
}

void loop()
{
  checkButtons();
  
  timeReadingCounter++;
  if (timeReadingCounter > MAX_TIME_READING_COUNTER)
  {
    getTimeFromRTC();
    display_time();

    timeReadingCounter = 0;
  }
}

static void display_time()
{
  unsigned long   ms = millis() % 1000;
  boolean        dot = (ms < 500) ? HIGH : LOW;
  byte            u;

  cli();
  u = hour;
  tube_list[1].digit = (u % 10);
  tube_list[1].dot = dot;

  tube_list[0].digit = ((u/10) ? u/10 : 11);
  tube_list[0].dot = LOW;

  u = minute;

  tube_list[3].digit = (u % 10);
  tube_list[3].dot = LOW;

  tube_list[2].digit = (u / 10);
  tube_list[2].dot = LOW;

  sei();
}

void getTimeFromRTC()
{
  int rtc[7];
  RTC_DS1307.get(rtc, true);

  // check to avoid glitches;
  if (rtc[DS1307_MIN] < 60 && rtc[DS1307_HR] < 24 && rtc[DS1307_SEC] < 60)
  {
    second = rtc[DS1307_SEC];
    minute = rtc[DS1307_MIN];
    hour   = rtc[DS1307_HR];
  }

  // check to avoid glitches;
  if (rtc[DS1307_YR] <= 2050 && rtc[DS1307_MTH] <= 12  && rtc[DS1307_DATE] <= 31)
  {
    day    = rtc[DS1307_DATE];
    month  = rtc[DS1307_MTH];
    year   = rtc[DS1307_YR];
  }
}

void setTime(int hh, int mm, int ss)
{
  getTimeFromRTC();

  // NOTE: when setting, year is 2 digits; when reading, year is 4 digits;
  RTC_DS1307.stop();
  RTC_DS1307.set(DS1307_SEC,  ss);
  RTC_DS1307.set(DS1307_MIN,  mm);
  RTC_DS1307.set(DS1307_HR,   hh);
  RTC_DS1307.set(DS1307_DOW,  1);
  RTC_DS1307.set(DS1307_DATE, day);
  RTC_DS1307.set(DS1307_MTH,  month);
  RTC_DS1307.set(DS1307_YR, year > 2000? year-2000 : year);
  RTC_DS1307.start();
}

void setDate(int newyear, int newmonth, int newday)
{
  getTimeFromRTC();

  // NOTE: when setting, year is 2 digits; when reading, year is 4 digits;
  RTC_DS1307.stop();
  RTC_DS1307.set(DS1307_SEC,  second);
  RTC_DS1307.set(DS1307_MIN,  minute);
  RTC_DS1307.set(DS1307_HR,   hour);
  RTC_DS1307.set(DS1307_DATE, newday);
  RTC_DS1307.set(DS1307_MTH,  newmonth);
  RTC_DS1307.set(DS1307_YR,   newyear);
  RTC_DS1307.set(DS1307_DOW,  1);
  RTC_DS1307.start();
}

void checkButtons()
{
  // increment hours and minutes;
  if (LOW == digitalRead(PIN_BTN_SETHOUR))
  {
    hour++;
    if (hour>23) hour = 0;
    setTime(hour, minute, 0);
    delay(200);
  }
  if (LOW == digitalRead(PIN_BTN_SETMIN))
  {
    minute++;
    if (minute > 59) minute = 0;
    setTime(hour, minute, 0);
    delay(200);
  }
}

2. WiFiChron1284 edition, featuring ATmega1284 SMD
This should be the "ultimate" upgrade for the WiFiChron board. The ATmega1284 processor will provide extra room for the ESP8266 WiFi code (debugging, parsing RSS feeds, data buffering etc).
The PCB design of the new board is shown below. (The boards are ordered and being manufactured).



3. Adapter for dual HDSP display
This adapter, suggested by Ray S, is the new addition to the collection of adapters developed for HDSP clock or WiFiChron.
Combined with the above mentioned WiFiChron1284, it would make a miniature Wise Clock 4 (without the SD card though). The two HDSP-2534 displays share all signals except for the CE (chip enable). The second display uses pin 10 for CE. Writing to each display is done by enabling the right CE line. This allows independent control of each display (dimming, text scrolling etc.).

Photos of the prototype are shown below.