25 October 2016

100 years of DST dates for Europe: 2016-2116

As described in an earlier post, I want to try implementing automatic DST switch over in my homemade clocks. At least in those that have a firmware inside.

The plan is to hardcode the switch over date, keep track of DST status in non-volatile EEPROM and check whether a change should be made at power up. The array starts on year 2016, so in case the RTC is reset the firmware must ensure it is taken immediately to year 2016.

So, according to the packing format described in the earlier post, this is how 100 years of DST dates for Europe looks like:

int DSTdays[100] = {
0x25, // 2016-03-27 to 2016-10-30
0x14, // 2017-03-26 to 2017-10-29
0x03, // 2018-03-25 to 2018-10-28
0x62, // 2019-03-31 to 2019-10-27
0x40, // 2020-03-29 to 2020-10-25
0x36, // 2021-03-28 to 2021-10-31
0x25, // 2022-03-27 to 2022-10-30
0x14, // 2023-03-26 to 2023-10-29
0x62, // 2024-03-31 to 2024-10-27
0x51, // 2025-03-30 to 2025-10-26
0x40, // 2026-03-29 to 2026-10-25
0x36, // 2027-03-28 to 2027-10-31
0x14, // 2028-03-26 to 2028-10-29
0x03, // 2029-03-25 to 2029-10-28
0x62, // 2030-03-31 to 2030-10-27
0x51, // 2031-03-30 to 2031-10-26
0x36, // 2032-03-28 to 2032-10-31
0x25, // 2033-03-27 to 2033-10-30
0x14, // 2034-03-26 to 2034-10-29
0x03, // 2035-03-25 to 2035-10-28
0x51, // 2036-03-30 to 2036-10-26
0x40, // 2037-03-29 to 2037-10-25
0x36, // 2038-03-28 to 2038-10-31
0x25, // 2039-03-27 to 2039-10-30
0x03, // 2040-03-25 to 2040-10-28
0x62, // 2041-03-31 to 2041-10-27
0x51, // 2042-03-30 to 2042-10-26
0x40, // 2043-03-29 to 2043-10-25
0x25, // 2044-03-27 to 2044-10-30
0x14, // 2045-03-26 to 2045-10-29
0x03, // 2046-03-25 to 2046-10-28
0x62, // 2047-03-31 to 2047-10-27
0x40, // 2048-03-29 to 2048-10-25
0x36, // 2049-03-28 to 2049-10-31
0x25, // 2050-03-27 to 2050-10-30
0x14, // 2051-03-26 to 2051-10-29
0x62, // 2052-03-31 to 2052-10-27
0x51, // 2053-03-30 to 2053-10-26
0x40, // 2054-03-29 to 2054-10-25
0x36, // 2055-03-28 to 2055-10-31
0x14, // 2056-03-26 to 2056-10-29
0x03, // 2057-03-25 to 2057-10-28
0x62, // 2058-03-31 to 2058-10-27
0x51, // 2059-03-30 to 2059-10-26
0x36, // 2060-03-28 to 2060-10-31
0x25, // 2061-03-27 to 2061-10-30
0x14, // 2062-03-26 to 2062-10-29
0x03, // 2063-03-25 to 2063-10-28
0x51, // 2064-03-30 to 2064-10-26
0x40, // 2065-03-29 to 2065-10-25
0x36, // 2066-03-28 to 2066-10-31
0x25, // 2067-03-27 to 2067-10-30
0x03, // 2068-03-25 to 2068-10-28
0x62, // 2069-03-31 to 2069-10-27
0x51, // 2070-03-30 to 2070-10-26
0x40, // 2071-03-29 to 2071-10-25
0x25, // 2072-03-27 to 2072-10-30
0x14, // 2073-03-26 to 2073-10-29
0x03, // 2074-03-25 to 2074-10-28
0x62, // 2075-03-31 to 2075-10-27
0x40, // 2076-03-29 to 2076-10-25
0x36, // 2077-03-28 to 2077-10-31
0x25, // 2078-03-27 to 2078-10-30
0x14, // 2079-03-26 to 2079-10-29
0x62, // 2080-03-31 to 2080-10-27
0x51, // 2081-03-30 to 2081-10-26
0x40, // 2082-03-29 to 2082-10-25
0x36, // 2083-03-28 to 2083-10-31
0x14, // 2084-03-26 to 2084-10-29
0x03, // 2085-03-25 to 2085-10-28
0x62, // 2086-03-31 to 2086-10-27
0x51, // 2087-03-30 to 2087-10-26
0x36, // 2088-03-28 to 2088-10-31
0x25, // 2089-03-27 to 2089-10-30
0x14, // 2090-03-26 to 2090-10-29
0x03, // 2091-03-25 to 2091-10-28
0x51, // 2092-03-30 to 2092-10-26
0x40, // 2093-03-29 to 2093-10-25
0x36, // 2094-03-28 to 2094-10-31
0x25, // 2095-03-27 to 2095-10-30
0x03, // 2096-03-25 to 2096-10-28
0x62, // 2097-03-31 to 2097-10-27
0x51, // 2098-03-30 to 2098-10-26
0x40, // 2099-03-29 to 2099-10-25
0x36, // 2100-03-28 to 2100-10-31
0x25, // 2101-03-27 to 2101-10-30
0x14, // 2102-03-26 to 2102-10-29
0x03, // 2103-03-25 to 2103-10-28
0x51, // 2104-03-30 to 2104-10-26
0x40, // 2105-03-29 to 2105-10-25
0x36, // 2106-03-28 to 2106-10-31
0x25, // 2107-03-27 to 2107-10-30
0x03, // 2108-03-25 to 2108-10-28
0x62, // 2109-03-31 to 2109-10-27
0x51, // 2110-03-30 to 2110-10-26
0x40, // 2111-03-29 to 2111-10-25
0x25, // 2112-03-27 to 2112-10-30
0x14, // 2113-03-26 to 2113-10-29
0x03, // 2114-03-25 to 2114-10-28
0x62, // 2115-03-31 to 2115-10-27
0x40, // 2116-03-29 to 2116-10-25

The row index is computed as (year - 2016). Each row is a BCD representation of intoDST and outFromDST dates, minus 25 (since the last Sunday always falls on 25th or later). So, 0x62 means on 31st (of March) we enter DST and on 27th (of October) we leave DST.

In order to avoid endless loop or complicated date jumps, the firmware will not change the time before 3 AM.

And this is the Perl script that produces the array above. It can be adapted to suit different DST rules, if a reader wants to follow my route.

use strict ;
use warnings ;
use DateTime ;

print "int DSTdays[100] = {\n";
for my $y( 2016..2116 ){

   my $ONdate = DateTime->last_day_of_month( year => $y ,
         month => 3 ) ;
   while ( $ONdate->dow != 7 ) {
      $ONdate = $ONdate->subtract( days => 1 ) ;
   my $OFFdate = DateTime->last_day_of_month( year => $y ,
         month => 10 ) ;
   while ( $OFFdate->dow != 7 ) {
      $OFFdate = $OFFdate->subtract( days => 1 ) ;
   my $ONymd = ($ONdate->day - 25) ;
   my $ONymdx = $ONdate->ymd ;
   #print "$ONymd;" ;
   my $OFFymd = $OFFdate->day - 25;
   my $OFFymdx = $OFFdate->ymd ;
   #print "$OFFymd\n" ;
   print "0x".$ONymd.$OFFymd.", // ".$ONymdx." to ".$OFFymdx."\n";
print "};\n";

Last but not least, this should be implemented in a clock firmware. And DST will be over in 4 days...

20 October 2016

Cheating on DST calculation

My homemade clocks currently lack a commodity feature: automatic switch to daylight saving time and back. Throughout Europe this change occurs in the early hours of the last Sunday of March and October. I do have an RTC that can keep track of day-of-the week, but no clocks o' mine display it, so it is usually left out when setting date and time.
On the other hand I usually have lots of unused code/EEPROM space in my Arduinos, so I am seriously considering to hardcode a static table of the last Sunday of March and October. It is a lazy solution, but a change in the official DST rules would require a firmware update anyway.

Without carefully packing up data, I would need two bytes for each year, one byte per last-Sunday value. Something like:

int DSTdates[3][2] = {
   {27, 30}, // 2016, get it with DSTdates[currentYear-2016][0] and DSTdates[currentYear-2016][1]
   {26, 29}, // 2017
   {25, 28} // 2018
My usual Arduino sketch leaves way more than 1000 bytes free for variables. If I hardcoded DST changeover for 50 years, that would account for 100 bytes. I could even leave it like that, but let's discuss further optimizations.
DST for 2016 will be over in a week, that is more or less the amount of time it will take me to implement this function and update my clock firmwares around the house. Nevertheless, since we still live in 2016, I need to keep that line in the bidimensional array otherwise it would break the lookup algorithm that begins from 2016.
Second optimization. The last Sunday of March and October will always be on day 25 or later, so the array data can be rearranged substracting 25 from every number. That means the day range will be between 0 (= 25 - 25) and 6 (= 31 - 25). If we think of these numbers in BCD, they now fit in one single byte. In the previously allocated 100 bytes I can now store 100 years of DST changeover! This is how it looks now:

int DSTdates[3] = { 25, 14, 03 };  // 2016, 2017, 2018

While the code above is not human-readable, a simple lookup function does the trick. And since I will write a simple program to generate the array, I don't worry about those unreadable values as long as the unpack function works.

Now I need the script to generate the array. Stay tuned!

13 October 2016

Olivetti Logos 262 PD expanson: calcuclock complete!

There you go! My first desk calculator conversion is complete!

I bought an Olivetti Logos 262 PD from 1980's, whose only guilt was to have a VFD display. I reverse-engineered the keyboard connection and designed a circuit to emulate keypresses with a microcontroller. Once the circuit has been built and tested, I wrote an Arduino firmware that reads an RTC (battery-backed clock) and types the numbers into the calculator. Last but not least, the clock circuitry can be disabled and the calculator used as originally intended.

Display modes are bonded to the way a calculator interacts with humans. Mode is changed towards the end of every full minute and includes:
  • HHMM
The latter two modes are interactive in a way that the displayed information changes every second.

 One more thing needs to be implemented, both in hardware and software: a way to set time and date!

If you need inspiration, the firmware is on github. While I have lots of notes on scrap paper, I do not have a complete schematic diagram to share. If I locate another calculator to convert I will draw a diagram so that it can be reproduced.

28 September 2016

Olivetti Logos 262 PD desk calculator - keyboard must be there

While showing date and time is already an achievement, I wanted to make the display of my converted desk calculator look more dynamic so that it would drag the attention of bypassers. Showing hh:mm:ss is as easy as typing in the 6-digit number, press '+' '1' and keep on hitting the '+' key once a second.

While I could make it work interacting with the real keyboard, my first attempt at it with the emulated keypresses failed. The reason is that without a real keyboard connected some "control" settings are missing, especially the "print/don't print" line which defaults to "print every confirmation keypress" (like arithmetical operations, total, ...). This causes the printer go crazy, which delays the sum operation and ruins the effect (besides being annoyingly noisy).

So, next step will be reconnecting the keypad in parallel with my adapter board, adding a power switch to exclude my added circuitry and write a simple firmware to prove +1 can be done every second.

Since the "don't print" setting still activates the printer on fewer conditions (basically when a total is displayed), all uC-controlled interaction will probably have to rely on arithmetical operations: "clear screen" will be "multiply by 0".

Fast forward to Keyboard re-connected. I cannot switch off my added circuitry because the analog multiplex influences key lines and it behaves like a key is always pressed. So, everything must stay powered (at least the adapter board), and the switch must be handled by the Arduino firmware that has to know when it is allowed to take control of the calculator.

Next: I have to write a simple firmware to map the "T" key ("totalizer one"), so that the screen can be wiped without activating the printer (a double press on CE/CA clears "buffers" and prints something).

20 September 2016

Olivetti Logos 262 PD desk calculator - keyboard hookup

The keyboard of the Logos 262 PD is connected to the main board with a flat cable, all soldered. The way it was built is not convenient for attaching lots of wires in parallel and routing them elsewhere. So I opted for a more intrusive approach: unsolder the flat cable on the main PCB and install a row of 1" female headers.

Now I can plug the Arduino adapter board directly on top of the main PCB, or re-seat the original cable into it. Last but not least I left room on the adapter board to solder the original cable, making it in parallel to Arduino signals.
No more keyboard soldered. But wrong headers!

Notice that I used the wrong pin headers: these are optimal as IC sockets, but they do not accept male headers! I had to hack a connector, extract its thin pins and use them on the adapter board. I will keep this in mind for the next conversion!

14 September 2016

RTC drift and smartphone reference time

Once I uploaded the new firmware to my bedside clock, it was easy to figure out if the self-correction routine would work by comparing it with a time reference.

I elected my smartphone as the reference, but in a couple of days I noticed that the homemade clock was gaining time, much more than the expected 2"/day.

Well, it turned out that my smartphone clock was not properly adjusted. The setting "sync time with network" was unchecked. Not a big deal, if the smartphone wasnt't loosing time itself on a daily basis!

So, if you're trying to calibrate a cheap DS1302/DS1307 module, take long time measurements (one/two months) and make sure your reference is reliably in sync with a reliable source. :)

And, probably most important, don't do this serious stuff before going to bed, after a day full of the usual routine, family, work, stress, ...

12 September 2016

Olivetti Logos 262 PD desk calculator - Arduino hookup

Still working on displaying date/time on a desktop calculator (with VFD display). Lacking a keyboard bus, I have to emulate key presses via a microcontroller. How?

The answer came from the Net, of course. Since the original input closes a mechanical contact, I need to reproduce the same function with an electronic control rather than a finger pressure. The simplest solution would be to build a matrix of relays, but it is noisy, large and requires a lot of I/Os. A complex solution puts the [Arduino] in sync with the original microcontroller keyboard scan rate and modifies [Arduino] outputs accordingly. The clever solution instead uses two analog multiplexers connected back to back.

The CD4051/74HC4051 is an 8-to-1 analog multiplexer with output enable. If two of them are connected together through the common I/O, 8+8 lines can be switched (64 combinations) with just 3+3 control lines (2^3 = 8). Plus one Arduino I/O for the Enable control, equivalent to the keypress.

With this setup timing is not critical, and I only need to find out how fast my Arduino can "type in" new numbers, either experimentally or by measuring the keypad scan rate through with the visual help of an oscilloscope. I have already experienced that keeping a key pressed does not lead to multiple readings.

After a quick test on the breadboard I am building the adapter board. Arduino code will follow.

Breadboard test of 74HC4051.