Nixie Tube Energy Meter
Having recently bought a house, project time has been a bit thin on the ground. As a standard terrace house, the consumer unit and electricity meter were in the entrance hallway, exposed and looking a bit naff. I liked the look of the meter so I quickly created a box that allowed the meter to poke through and leave access to the fuses.



The box covering did the job but felt a bit cumbersome with all that spare space; it needed something else to give it more purpose. An energy meter was the obvious thing but I didn’t want a garish LCD or 7 segment display, it need to match the blown glass electricity meter… …nixie tubes!

Design
I’ve wanted to use nixie tubes in a project for a long time – almost tempted by the bog-standard clock – so once I had the vision on this one I set about it in zero time! I’m afraid I didn’t design the driving circuit myself, instead I went for this very tidy Nixie Module. Working off 5V and SPI it is nice and quick to get a project off the ground. It is expensive but the build quality and time saved designing something else made it a worthy investment. An Arduino would be the microcontroller but I wanted the meter to provide some form of data stream for a web based energy history. To make it an IoT, I a paired ESP8266 with it. I used both together because the Arduino ADC has a better resolution and has been tried and tested.
Display

Considering the expense of the Nixie module and the irony of a high energy impact energy meter not lost on me (the tubes draw around 300mA each), I opted to use only two tubes; I was going to require SI notification anyway for full scale display. Handily, the tubes have colon points on the right-hand side (for clock use), which I designed as indicators for x10 and x1. With both SI units and x10 x1 decimals, one can display from 0 to 100MW – albeit with only 2 significant figures (considering the accuracy of a current transformer system like this, that isn’t a problem).






The Nixie library and arrangement of the SI indicators made displaying the power very simple
void printPwr(uint16_t Pwr) {
tube.printf("%02d",Pwr);
SI_PORT |= (SI_OFF_MASK & SI_WATT_MASK);
if ((Pwr >= 100) && (Pwr < 1000)) { tube.setColon(1,(Colon) Lower); } else if ((Pwr >= 1000) && (Pwr < 10000)) { SI_PORT &= SI_KILO_MASK; tube.setColon(0,(Colon) Upper); } else if (Pwr >= 10000) {
SI_PORT &= SI_MEGA_MASK;
tube.setColon(1,(Colon) Upper);
} else {
tube.setColon(1,(Colon) None);
}
// set kilowatt hours
if (display == (rolling_t) hour)
SI_PORT &= SI_HOUR_MASK;
tube.display();
}
Power Measurement
The power measurement comes by way of SCT-013 clip-on current transformer with a current sensing resistor in parallel, biased by VREF/2, then sampled by the Arduino ADC. Open Energy Monitor have plenty of detail on this, which I followed and a useful library, that I used. In its most basic form, the library samples the ADC reading over a number of samples (in phase with 50Hz mains) and returns the RMS current. With the RMS current and assumed RMS voltage (230V) using \(P = V * I\), the apparent power is calculated.
Apparent power is the assumed power and is what a resistive load will draw. Real power is what we’re billed for and that takes into account non-linear voltage/current relationships and phase lag created by other loads such as motors (which feed power back at certain points in the cycle) and DC converters. The differences are again best summarised by Open Energy Monitor.


Building the system I noticed that the bell (already in the box near the consumer unit) uses a step-down transformer, offering outputs or 3, 5 or 8V. I could adapt the system to sample this AC voltage too, to create a more accurate reading rather than using the assumed 230V RMS. I’m not too bothered though, I mainly want an indicative meter than also looks good! There is already a wide amount of error in the system: transformer tolerance, resistor tolerance, voltage bias drift, ADC resolution etc.
Control
I didn’t want any buttons on the panel and I also wanted to generate reports of power usage. For this, I integrated and EPS8266 using standard AT firmware to act as a TCP packet relay over serial. I found the ESP8266wifi library suited this usage but required a blocking function call to check for incoming connections/messages – not suitable for updating the display in a timely manor, ADC sampling or any task other than a pure TCP client!
I forked the code with functions that poll the ESP8266 serial buffer instead, checking new connections and maintaining a multi-client connection structure, such that when there are connections, messages can be checked or data sent.
When a connection is made and message recieved, a simple command interpreter then acts upon it:
void processCommand(WifiMessage msg) {
// return buffer
char espBuf[MSG_BUFFER_MAX];
// scanf holders
int set;
char str[16];
// Get command and setting
sscanf(msg.message,"%15s %d",str,&set);
/* swSerial.print(str);*/
/* swSerial.println(set);*/
// Stream JSON
if ( !strcmp_P(str,STRM) ) {
snprintf(espBuf,sizeof(espBuf),json,avg[0],avg[1],avg[2],avg[3]);
wifi.send(msg.channel,espBuf);
}
else if ( !strcmp_P(str,ROLL) ) {
snprintf(espBuf,sizeof(espBuf),json,roll[0],roll[1],roll[2],roll[3]);
wifi.send(msg.channel,espBuf,false);
snprintf(espBuf,sizeof(espBuf),json,count[0],count[1],count[2],count[3]);
wifi.send(msg.channel,espBuf,true);
}
// Change colour
else if ( !strcmp_P(str,COLOUR) ) {
tube.setBackgroundColor((Color) set);
tube.display();
wifi.send(msg.channel,msg.message);
}
// Reset rolling counts
else if ( !strcmp_P(str,RESET) ) {
memset(roll, 0, ( (sizeof(roll)/sizeof(roll[0])) * sizeof(roll[0]) ) );
memset(count, 0, ( (sizeof(count)/sizeof(count[0])) * sizeof(count[0]) ) );
wifi.send(msg.channel,"COUNT OK");
}
// Change Nixie display var
else if ( !strcmp_P(str,DISP) ) {
display = (rolling_t) set;
wifi.send(msg.channel,msg.message);
}
// Set ADC calibration
else if ( !strcmp_P(str,CAL) ) {
EEPROM.write(EEPROM_CAL,set);
wifi.send(msg.channel,msg.message);
}
// Return what we are
else if ( !strcmp_P(str,IDN) ) {
wifi.send(msg.channel,"JBR ENERGY MONITOR V0.1");
}
// Reset system by temp enable watchdog
else if ( !strcmp_P(str,RST) ) {
wifi.send(msg.channel,"SYSTEM RESET...");
// soft reset by reseting PC
asm volatile (" jmp 0");
}
// Unknown command
else {
wifi.send(msg.channel,"ERR");
}
}
I have commands to stream JSON formated data, with a plan to have a locally running Raspberry Pi Node.js server, requesting the JSON stream and updating a database/presenting a rolling usage graph. That will be something for another project log. The tubes have RGB backlights, so there are commands to change the colour and also change the display between sec, min, hour or even day rolling average.
Result









If you want to make one yourself or part of the design, the code and OpenSCAD designs are in this Github