24 December 2016

My first Nixie based instrument at work

Since the Philips PM2422A instrument works, it has been put into use. Here it is measuring a 1% (vintage) 10.7 Mohm resistor.

The reading has been jumping between 10.71 and 10.70, and finally settled on the latter value. I wonder if there is a warm up time for these instruments?!

The picture also shows the emulated "1" leftmost symbol.



22 December 2016

My first Nixie-based instrument

At the ARI Torino HAM rally few Sunday ago (Nov, 27th 2016) during my third or fourth tour of exhibitors I spotted what looked like a box with Nixies. The box read "Philips PM2422". A quick picture search with the smartphone confirmed my suspicions: a 3 and half digit multimeter, with Nixies. I was even able to power it up right there, and the tubes happily glew.
 
I paid 30€ and I think it was the right price. The seller then realized he could have asked more, I nodded and I replied that I did not negotiate the price on purpose.
 
Once home I opened it, and everything looked fine. No fat electrolytics, no sign of burns. But a surprise: there are only three tubes inside, not 4 or 4+1 as in the PM2421. The most significant digit is a long neon lamp, so there's implicit leading zero blanking. And the minus sign is another small neon lamp. That's it. Cheaper than full feature tubes but effective. I think more recent versions of the same instrument carry four tubes. I got a PM2422/A2, while they went up to /A5.
 
Finally I own a bench multimeter!
 
Oh, by the way, can it be a clock? Well, it can count from 0 to 1999, so as a 24h clock it would work up to 19:59. It is suitable as an office desktop clock since most people leave way before 8 pm. Or simply make it a 12h clock. I want to explore if a simple DAC produces a stable enough reading, soon.

12 December 2016

Nixie Tube Ciapapuer - 2 - video

If you are curious how the Nixie Tube Ciapapuer looks, here's a short video:


In the end I dropped the fading effect descried in the previous post and opted to include an "off" step in the sequence.

The object is now sitting on my office desk and pretends to be reading visitors' minds. J One funny moment was when a colleague picked up the box and the "off" moment kicked in, that caused a bit of surprise and worry. I should include an accelerometer and intentionally switch off the tube when the box is moved.

06 December 2016

Nixie Tube Ciapapuer - 1


Or, in plain English, "Nixie tube home decor".

Literally: "Nixie tube dust collector."

Why, why would I ever want to build a device with just one Nixie? First of all, to use symbol Nixies like ZM1001 (side view) and B5094 (top view). Then because: housing is simpler than using several tubes, firmware is pretty simple (no RTC or strict timings), circuit is simple, it will result in a compact object.

My first attempt is with a ZM1001 that has 6 symbols, so there are 6 cathodes to switch. The typical 74141 driver has 10 outputs, nevertheless since I am lazy I choose it for this project, thus reducing the connection to uC to 3 data lines.

In order to differentiate from my previous projects and learn something new, this ornament firmware will also:
  • randomize transitions and next symbol
  • fade-in/fade-out
Fading requires one more control line. One way is to PWM the anode voltage through a PMOS or PNP: this requires a component with high Vds/Vce (that is missing in my junk box). Another way that is doable in this project is to switch the BCD combination to unused 74141 outputs. Either way uses a 4th output from the microcontroller, but the latter is "low voltage" and implemented in the basic circuit "by design."
So, going for fading through switching to unused outputs, a look at the 74141 truth table indicates that I can use the MSB (Most Significant Bit) to switch over to unused outputs (8, 9). Indicentally I had not planned to use those outputs, so now it is a matter of writing the proper software routine.

03 November 2016

How fast can Arduino toggle a pin?


I set up this experiment because I wanted to know how fast a standard 16 MHz Arduino board can toggle a pin. Why? Well, say you have a display that can only count up or down: you need to throw in a fast "clock" to reach the desired value unless you are starting from 0.

The following code shows two approaches. A "pure" Arduino code and a lower level version that fiddles with ATmega internals.





void setup() {
  pinMode(5, OUTPUT);
}

void loop() {

 for (long j=0; j<200000; j++) {
   digitalWrite(5, HIGH);
   digitalWrite(5, LOW);
  }

  for (long i=0; i>2000000; i++) {
   PORTD = PORTD & B00000111;
   PORTD = PORTD | B00100000;
  }

}


If you connect a frequency counter to pin 5 of the Arduino board you will read two frequencies:

  1. 114 kHz (122)
  2. 1445 kHz (1700)

The slowest is generated with digitalWrite, while the second comes from the PORTD code. That's 12 times faster! Values in parenthesis can be achieved if you run just the toggling commands alone in the main loop(), so they are a sort of "absolute maximum achievable."

Edit: I forgot how to read my frequency counter and later realized that the value represents kHz and not Hz. The frequencies have now been verified with an oscilloscope too. Note that the faster mode generates a "low" impulse as short as 120 ns, but the loop instructions cause the "high" level to last for about 550 ns.

28 October 2016

Easy socket for ZM1001 (and ZM1000 if necessary)

At HAM Radio Messe 2016 I bought a board from Philips PM2421 meter. It mounts 4x ZM1000 numeric nixies and 1x ZM1001 symbol nixie (+, -, ~, X, Y, Z). I tried to reuse the existing circuitry, especially to take advantage of the sockets on board, but there are limitations too: the thousands nixie has been wired only to show 0 or 1 (good for a 12-hour clock) and interfacing to the existing digital logic is not straightforward. On the other hand I could cut traces and feed data to the buffer or to the driver, but then I would have a large board behind the display ... for nothing!

Further down the conversion road I decided to unplug the symbol nixie, that would not be used in a clock anyway. Plan was to try to unsolder its socket, but the dense pinout looks familiar. A quick check at the datasheet reveals that those pins are 2.54 mm apart (or 0.1" if you prefer). Moreover the ZM1001 has only 6 symbols + the anode, so a "modern" socket can be built out of few female headers on a breadboard or custom PCB.

Here's a possible layout (bottom view):


There are no overlapping (red) lines and, if the homebrew socket does not bring unused pins to the board, a simple veroboard can be used:

Veroboard view of the homemade partial socket.
Mind that ZM1001 has two Anode pins, but just one grid, so using just one is fine. At least it looks so, and the datasheet does not suggest against it.

The tube sits well in the socket and has headroom so that pins are not stressed.
So, socket completed. The trick is that unused pins stay floating in the air and are not connected to anything, so routing without a PCB is simplified. The circuit above made with veroboard has copper strips running along its longest side and required very few cuts and jumpers to make it work with the 7441. Also, thanks to the fact that only 6 symbols are used, I can use 7441 outputs all on the same side.




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.

#!/usr/bin/perl
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!