Vetinari Digital Clock

I wanted to resurrect the attractive digital clock I made in the early 1970s, but it’s been a very long time since it was interesting enough to bother hanging it on a wall. Mutating it into a “Vetinari Clock” has made it, once again, into a talking point.

You can buy modules that convert conventional crystal-controlled analogue clocks into Vetinari clocks, but they have disadvantages. Firstly they require more than minimal conceptual changes to my clock, e.g. timing being derived from a crystal rather than by counting the 50Hz mains cycles. More importantly it simply doesn’t look right: digits ticking over instantaneously have much less visual impact than a second-hand moving slower or faster. Hence I made my own module, and it turned out to be a short and fun hack.

Overview

The digital clock isn’t based on a crystal; it simply counts the 50Hz mains cycles. Therefore using a small MCU to implement a digital Vetinari clock is conceptually simple:

  • the only physical change is to insert a new circuit between the mains input and the clock’s counter input
  • during any 24 hour period, pulse the clock’s counter input 4320000 (60*60*24 * 50) times
  • but during any one second there may be more or less than 50 pulses
  • every second, use a pseudo-random number to determine how long the “second” will be

The MCU implements two algorithms, one to detect 50Hz mains cycles and pass them onto the clock’s counter input, and the other “Vetinari algorithm” that fiddles with the displayed time.

Detecting 50Hz Mains Cycles

The algorithm is insensitive to the amplitude of the input, insensitive to impulsive noise, and insensitive to variation in the MCU’s main loop time.

Measure the mains input voltage using the MCU’s ADC, scale it, and use a standard moving average filter to remove ADC and impulse noise:

long filteredMains;
int filterAF = 2; // averaging factor for the smoothed mains
int FPF = 16; // fixed point scaling factor
long mains = ((long)analogRead(ANALOGUE_IN)) << FPF;
filteredMains = filteredMains - (filteredMains >> filterAF) + (mains >> 2);

Detect the maximum value in a way that is analogous to the way a diode and capacitor detect peak voltages:

 long peakMains;
 int attackAF = 4; // fast averaging factor for "charging the capacitor"
 int decayAF = 12; // slow averaging factor for "discharging the capacitor"
 if ( mains > peakMains ) {
   peakMains = peakMains - (peakMains >> attackAF) + (mains >> attackAF);
 } else {
   peakMains = peakMains - (peakMains >> decayAF) + (mains >> decayAF);
 }

Detecting the minimum value, troughMains, is analogous.

A very simple FSM enforces the hysteresis loop and detects the start of a 50Hz cycle:

bool mainsHigh;
bool lastMainsHigh = mainsHigh;
long thresholdOffset = (peakMains - troughMains) >> 2; // 1/4 the p-p value
if (filteredMains > (peakMains - thresholdOffset)) {
  mainsHigh = true;
}
if (filteredMains < (troughMains + thresholdOffset)) {
  mainsHigh = false;
}
bool start = mainsHigh && !lastMainsHigh;

Vetinari Algorithm

This runs once per second, when start == true.

I had to suck-it-and-see with half a dozen algorithms for causing skipping and stuttering. In the end I settled on using 4 bits from a 15 bit pseudo-random number to choose between:

  • 10 out of 16 seconds, a second really is a second with 50 pulses
  • 4 out of 16 seconds, skipping forward by having 74 pulses
  • 2 out of 16 seconds, stammering by having 2 pulses
unsigned int prbs;
int nextPrbs() {
unsigned int m, n;
m = (prbs >> 14) & 0x1;
n = (prbs >> 13) & 0x1;
prbs <<= 1;
if (m^n) {
  prbs |= 0x01;
}
  return prbs;
}
int nextRandomDelta() {
int delta = 0;
int rnd = nextPrbs();
int bits = rnd & 0x7fff;
if (bits != 0x7fff) {
  switch (bits & 0x000f) {
    case 1:
    case 3:
    case 9:
    case 13:
      delta = 24; // stutter
      break;
    case 2:
    case 5:
      delta = -48; // stammer
      break;
    default:
      delta = 0; // normal
      break;
  }
  return delta;
}

Processor

My original idea was to use a spare atmega328 and voltage regulator, make my own PCB, and program it directly in C using Atmel’s Windows IDE.  Then I realised that, since only two i/o pins are required and I don’t have to worry about minimising power, an Adafruit Arduino Trinket would be smaller, inexpensive and much more convenient. Since the Arduino IDE runs in Linux, I wouldn’t even have to reboot into Windows.

The only problem I encountered with the Trinket was that somehow I managed to get it into a state where its bootloader had been overwritten. Now Trinkets are so inexpensive that it would be sensible to throw it away and use another – but that’s no challenge. Instead I resurrected it by desoldering it, finding a bootloader somewhere on the web, reflashing it using the AVR Dragon I previously used with a naked atmega328, and resoldering it. Problem solved.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s