Here you go, guys.
I converted my Peter Jensen IN-14 clock (
tubeclock.com) from a PIC to an
Electric Imp.
The clock (scroll down):
http://store.tubeclock.com/index.php/nixie-tube-clocks/clocks
The Imp:
http://electricimp.com/
I built a small daughtercard that plugs into the PIC socket and itself
has a socket for an April, which is the minimum breakout board for the
Imp. It's really just a power supply and the socket for the imp.
April:
http://www.adafruit.com/products/1130
The schematic of the daughtercard is extraordinary simple:
April Pin PIC PIN
1 -------- Button 1 --- 4
2 -------- Button 2 --- 12
5 -------- SCLK ------- 8
7 -------- DATA ------- 6
8 -------- LATCH ------ 7
9 -------- BLANK ------ 5
Vin ------ 12V -------------------- To fuse
GND ------ GND -------- 14
The Imp has pretty high instantaneous current requirements. The average
power consumption is quite low, but the worst-case WiFi transmit event
can require 400mA. The tubeclock 5V supply is just a 5.1V Zener with a
1k resistor, so it's not up to the task. Fortunately the April power
supply can take up to 17V input, so I just ran a wire to the load side
of the fuse. I added a 100uF cap across the 12V input to help soak up
those transients.
The SD form factor Imp gives you six pins to play with, and they can be
configured in many ways. It's easy to add an i2c I/O expander if you
need one, but we don't in this case.
Pins 1 and 2 are configured as digital inputs connected to the buttons.
I'm not using them for anything yet, right now they just log events to
the server.
Pins 5 and 7 are configured as SPI, and are used to write to the HV5622
shift registers. Pin 8 is a simple digital out connected to the Latch
Enable on the HV5622s. This gives you direct control of the display. The
code below has a function that takes a 6-digit string and two booleans
for the colon bulbs, and creates the bitmapped blob to send to the HV5622s.
Pin 9 is configured as a PWM output, and is connected to the blanking
input on the HV5622s. This gives a brightness control.
That's all there is to the hardware.
I've included the code for a very simple clock, it's just pulling time
from the built-in RTC (which is synched at connect), has a hardcoded
timezone offset, and crossfades the digits.
My next step is to use the Imp agent to create a web control panel for
timezones, and update the clock more frequently (it drifts by a couple
of seconds a day, but corrects if you reboot it)
Here's the device code so far:
// Button Class from
github.com/electricimp
//
----------------------------------------------------------------------------
// Name: Button
// Purpose: Show the right way to debounce a button press
//
----------------------------------------------------------------------------
class button{
static NORMALLY_HIGH = 1;
static NORMALLY_LOW = 0;
_pin = null;
_pull = null;
_polarity = null;
_pressCallback = null;
_releaseCallback = null;
constructor(pin, pull, polarity, pressCallback, releaseCallback){
_pin = pin; //Unconfigured IO pin, eg hardware.pin2
_pull = pull; //DIGITAL_IN_PULLDOWN, DIGITAL_IN or
DIGITAL_IN_PULLUP
_polarity = polarity; //Normal button state, ie 1 if button is
pulled up and the button shorts to GND
_pressCallback = pressCallback; //Function to call on a button
press (may be null)
_releaseCallback = releaseCallback; //Function to call on a
button release (may be null)
_pin.configure(_pull, debounce.bindenv(this));
}
function debounce(){
_pin.configure(_pull);
imp.wakeup(0.020, getState.bindenv(this)); //Based on googling,
bounce times are usually limited to 10ms
}
function getState(){
if( _polarity == _pin.read() ){
if(_releaseCallback != null){
_releaseCallback();
}
}else{
if(_pressCallback != null){
_pressCallback();
}
}
_pin.configure(_pull, debounce.bindenv(this));
}
}
//Example Instantiation
minuteButton <- button(hardware.pin1, DIGITAL_IN, button.NORMALLY_HIGH,
function(){server.log("Minutes Button Pressed")},
function(){server.log("Minutes Button Released")}
);
hourButton <- button(hardware.pin2, DIGITAL_IN, button.NORMALLY_HIGH,
function(){server.log("Hours Button Pressed")},
function(){server.log("Hours Button Released")}
);
/* Hardware Configuration
This code is designed to support a
tubeclock.com IN-14 clock.
There are two SuperTex HV5622 driver chips, nominally SPI with
latch (!LE) and blanking (!BL) lines. There is a polarity line,
tied high on the
tubeclock.com board.
There are also two buttons on the board, currently NC because
they are connected to the 5V supply on the clock.
*/
// SPI to the HV drivers
display <- hardware.spi257;
display.configure(SIMPLEX_TX | MSB_FIRST | CLOCK_2ND_EDGE , 5000);
displaywriter <- display.write.bindenv(display);
// Latch Enable
latch <- hardware.pin8;
latch.configure(DIGITAL_OUT);
latchwriter <- latch.write.bindenv(latch);
latchwriter(1);
// Display Blanking (PWM for brightness control)
blank <- hardware.pin9;
blank.configure(PWM_OUT,(1.0/120.0),0.9);
function displayBuffer (buffer) {
latchwriter(0);
displaywriter(buffer);
latchwriter(1);
}
function renderBuffer(string, colon_left, colon_right) {
local offbits = [0,2,5,0,3,5];
local offbytes = [0,1,2,4,5,6]
local buffer = blob(8);
for (local index=0;index<6;index++) {
local digit = string.slice(index,index+1).tointeger();
local byte0 = (0x80>>(digit+(offbits[index])));
local byte1 = (0x80>>((digit-8)+(offbits[index])));
buffer[(offbytes[index])] = buffer[(offbytes[index])] | byte0;
buffer[(offbytes[index])+1] = buffer[(offbytes[index])+1] | byte1;
}
if (colon_left)
buffer[2] = buffer[2] | 0x08;
if (colon_right)
buffer[5] = buffer[5] | 0x20;
return buffer;
}
imp.configure("Nixie Clock", [], []);
server.log(format("impeeID %s", hardware.getimpeeid()));
server.log(format("Voltage %f", hardware.voltage()));
server.log(format("MAC address %s", imp.getmacaddress()));
server.log(format("WiFi %d dBm", imp.rssi()));
server.log(format("Software Version %s", imp.getsoftwareversion()));
function crossfade (old_buffer, new_buffer, steps) {
for (local i=0;i<steps;i++) {
displayBuffer(old_buffer);
imp.sleep((steps - i)*0.0001);
displayBuffer(new_buffer);
imp.sleep(i * 0.0001);
}
}
function nixieClock() {
local tzoffset = (-8*3600);
local t;
local prev_t=time();
while ((t = time()) == prev_t); // Synchronize seconds
local prev_d = date(((prev_t) + tzoffset),'u');
local prev_d_buffer = renderBuffer(format("%02d%02d%02d",
prev_d.hour, prev_d.min, prev_d.sec) , true, true);
local d = date((t + tzoffset),'u');
local d_buffer = renderBuffer(format("%02d%02d%02d", d.hour, d.min,
d.sec) , true, true);
crossfade(prev_d_buffer,d_buffer,50);
// displayBuffer( renderBuffer(format("%02d%02d%02d", d.hour, d.min,
d.sec) , true, true));
imp.wakeup(0.70, nixieClock);
}
nixieClock();