Pi Zero Print Server

After looking at wireless print servers costing £35+ I decided to make my own using my spare Raspberry Pi Zero (original without camera connector).

I needed a wifi dongle and USB connection for the printer, so have bought a Zero4U USB hub HAT and case, in fact I bought two of each as the shipping was fixed, so it came to £23 shipped from the Czech Rebuplic, so shouldn’t take too long to arrive.

I’ve got a spare 16Gb Class4 microSD card – we’ll have to see how that goes, otherwise I have a 32Gb Transcend on order. I’ve got a 5v 2A power supply and DC jack to microUSB adapter, as I don’t fancy the official power supply (its upside-down and looks fragile).

I’ve got Ralink RT5370 and Realtek RTL8188 wifi dongles on order, although I already have a RT5370 to use, but its nice to have spares that cost a Pound or two!

I installed the latest Raspbian Lite and added cups and sane to the installation.

The printer is a Brother DCP-7055 and I know it works with Ubuntu 12.04/14.04 as it used to be connected locally, however that was using x86-64, I’m not sure ARM is supported by Brother’s drivers, it seems printing at least will work with the HL-1250 ppd file or possibly the 7045N Postscript driver or brlaser, but almost definitely the brscan4 driver won’t work as its x86-only.

I don’t think I can connect a laptop to the miniUSB port on the Zero4U whilst its connected to the Zero, so to scan I’ll have to unplug the Zero from the printer unless Brother can be convinced by my constant tweets/emails to make ARM packages!

In other news my acrylic weld glue and needle bottles have arrived, so got to have a go at assembling the ESP8266 clock cases soon – and Dremel that button hole a bit more.

Update: Brother confirmed no current or future plans to support the ARM platform. The DCp-7500 worked out of the box with Raspbian’s built-in brlaser3 installation and DCP-7030 PPD file. No chance in getting the scanner working though, scanimage didn’t even detect it.

The Zero4U is pretty cool, although the acrylic case is a bit weak, took 5-6 days to arrive. The miniUSB port is blocked by the case so you can’t use it at the same time with a Zero and desktop PC anyway.

I assembled the acrylic case for the ESP8266 clock, it was a bit of a nightmare with the weld solvent, but it went together really well. I even managed to add some rails to make the lid removable.

Auto-BST for LED Clock

I’ve finally got around to adding British Summer Time detection to my LED matrix clock, I’ve also reduced flash wear and connection time by using the new persistent() method from the WiFi library. I’m still saving the UTC time to the RTC module, just displaying +/- the hour. The LiFePO4 battery is still putting out over 3.1v after 2 months.

The final code is below:

#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include "LedControl.h"
#include <Wire.h>
#include <RTClib.h>

// ntp server pool
IPAddress timeServerIP;

// ntp time stamp is in the first 48 bytes of the message
const int NTP_PACKET_SIZE = 48;

// buffer to hold incoming and outgoing packets
byte packetBuffer[NTP_PACKET_SIZE];

// create a udp instance
WiFiUDP udp;

// data, clock, cs, numdevices
LedControl lc = LedControl(D7,D5,D6,4);

// ds3231 constuctor
RTC_DS3231 RTC;

const int num[10][8] = {
    {0x00,0x78,0xcc,0xec,0xfc,0xdc,0xcc,0x78}, // zero
    {0x00,0xfc,0x30,0x30,0x30,0x30,0xf0,0x30}, // one
    {0x00,0xfc,0xcc,0x60,0x38,0x0c,0xcc,0x78}, // two
    {0x00,0x78,0xcc,0x0c,0x38,0x0c,0xcc,0x78}, // three
    {0x00,0x0c,0x0c,0xfe,0xcc,0x6c,0x3c,0x1c}, // four
    {0x00,0x78,0xcc,0x0c,0x0c,0xf8,0xc0,0xfc}, // five
    {0x00,0x78,0xcc,0xcc,0xf8,0xc0,0x60,0x38}, // six
    {0x00,0x60,0x60,0x30,0x18,0x0c,0xcc,0xfc}, // seven
    {0x00,0x78,0xcc,0xcc,0x78,0xcc,0xcc,0x78}, // eight
    {0x00,0x70,0x18,0x0c,0x7c,0xcc,0xcc,0x78}  // nine
};

void drawNum(int number, int display)
{
    lc.setColumn(display, 0, num[number][0]);
    lc.setColumn(display, 1, num[number][1]);
    lc.setColumn(display, 2, num[number][2]);
    lc.setColumn(display, 3, num[number][3]);
    lc.setColumn(display, 4, num[number][4]);
    lc.setColumn(display, 5, num[number][5]);
    lc.setColumn(display, 6, num[number][6]);
    lc.setColumn(display, 7, num[number][7]);
}

bool isBST(int year, int month, int day, int hour)
{
    // bst begins at 01:00 gmt on the last sunday of march
    // and ends at 01:00 gmt (02:00 bst) on the last sunday of october

    // january, february, and november are out
    if (month < 3 || month > 10) { return false; }

    // april to september are in
    if (month > 3 && month < 10) { return true; }

    // last sunday of march
    int lastMarSunday =  (31 - (5* year /4 + 4) % 7);

    // last sunday of october
    int lastOctSunday = (31 - (5 * year /4 + 1) % 7);

    // in march we are bst if its past 1am gmt on the last sunday in the month
    if (month == 3)
    {
        if (day > lastMarSunday)
        {
            return true;
        }

        if (day < lastMarSunday)
        {
            return false;
        }

        if (hour < 1)
        {
            return false;
        }

        return true;
    }

    // in october we must be before 1am gmt (2am bst) on the last sunday to be bst
    if (month == 10)
    {
        if (day < lastOctSunday)
        {
            return true;
        }

        if (day > lastOctSunday)
        {
            return false;
        }

        if (hour >= 1)
        {
            return false;
        }

        return true;
    }
}

// send an ntp request to the time server at the given address
unsigned long sendNTPpacket(IPAddress& address)
{
    // set all bytes in the buffer to 0
    memset(packetBuffer, 0, NTP_PACKET_SIZE);

    packetBuffer[0] = 0b11100011;   // li, version, mode
    packetBuffer[1] = 0;            // stratum, or type of clock
    packetBuffer[2] = 6;            // polling interval
    packetBuffer[3] = 0xEC;         // peer clock precision

    // 8 bytes of zero for root delay & root dispersion
    packetBuffer[12] = 49;
    packetBuffer[13] = 0x4E;
    packetBuffer[14] = 49;
    packetBuffer[15] = 52;

    // all ntp fields have been given values, send request
    udp.beginPacket(address, 123);
    udp.write(packetBuffer, NTP_PACKET_SIZE);
    udp.endPacket();
}

void displayDate(unsigned long unixtime)
{
    // power up led matrices
    for (int i=0; i<4; i++)
    {
        lc.shutdown(i,false); // come out of powersaving
        lc.setIntensity(i,5); // set brightness 0-15
        lc.clearDisplay(i);   // clear display
    }

    // handle british summer time
    DateTime nowntp = unixtime;
    int myhour;
    if (isBST(nowntp.year(), nowntp.month(), nowntp.day(), nowntp.hour()))
    {
        myhour = ((unixtime % 86400L) / 3600) + 1; // bst
    }
    else
    {
        myhour = (unixtime % 86400L) / 3600; // utc
    }

    // print hour to led
    if (myhour == 24)
    {
        drawNum(0,0);
        drawNum(0,1);
    }
    else
    {
        drawNum(myhour/10,0);
        drawNum(myhour%10,1);
    }

    // print minute to led
    int myminute = (unixtime % 3600) / 60;
    if (myminute < 10)
    {
        drawNum(0,2);
    }
    else
    {
        drawNum(myminute/10,2);
    }
    drawNum(myminute%10,3);
}

void gotoSleep()
{
    for (int i=0; i<4; i++)
    {
        lc.clearDisplay(i);
        lc.setIntensity(i,0);
        lc.shutdown(i,true);
    }

    // latch cs pin and pause to work around display0 being stuck high on sleep problem
    digitalWrite(D6, LOW);
    delay(2000);

    // sleep mcu forever
    ESP.deepSleep(0);
}

void setup()
{
    // setup ds3231
    Wire.begin();
    RTC.begin();

    // display rtc time on led matrices
    DateTime now = RTC.now();
    displayDate(now.unixtime());

    // connect to wifi network
    if (WiFi.SSID() != "myssid") {
        WiFi.begin("myssid", "mypassword");
        WiFi.persistent(true);
        WiFi.setAutoConnect(true);
        WiFi.setAutoReconnect(true);
    }

    // static ip, gateway, netmask
    WiFi.config(IPAddress(192, 168, 1, 2), IPAddress(192, 168, 1, 1), IPAddress(255, 255, 255, 0));

    // connect - could drain battery if never connects to wifi
    int wifitries = 0;
    while (WiFi.status() != WL_CONNECTED)
    {
        // give it a moment
        delay(1000);

        // only try 5 times before sleeping
        wifitries++;
        if (wifitries >5)
        {
            gotoSleep();
        }
    }

    udp.begin(2390);

    // get a random server from the pool
    WiFi.hostByName("europe.pool.ntp.org", timeServerIP);

    int cb = 0;
    int ntptries = 0;
    while (!cb)
    {
        // send an ntp packet to a time server
        sendNTPpacket(timeServerIP);

        // wait to see if a reply is available
        delay(3000);
        cb = udp.parsePacket();

        // only try 5 times before sleeping
        ntptries++;
        if (ntptries >5)
        {
            gotoSleep();
        }
    }

    // we've received a packet, read the data into the buffer
    udp.read(packetBuffer, NTP_PACKET_SIZE);

    // the timestamp starts at byte 40 of the received packet and is four bytes,
    // or two words, long. first, extract the two words:
    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);

    // combine the four bytes (two words) into a long integer
    // this is ntp time (seconds since jan 1 1900):
    unsigned long secsSince1900 = highWord << 16 | lowWord;

    // unix time starts on jan 1 1970. in seconds, that's 2208988800:
    const unsigned long seventyYears = 2208988800UL;

    // subtract seventy years:
    unsigned long epoch = secsSince1900 - seventyYears;

    // update ds3231 from ntp unixtime
    RTC.adjust(DateTime(epoch));

    // display ntp time on led matrices
    displayDate(epoch);

    // ntp has a dot rtc does not
    lc.setLed(3, 7, 0, true);

    // wait five seconds before shutting down
    delay(5000);
    gotoSleep();
}

void loop()
{
}

LED Matrix Alarm Clock Update

I’ve been working some more on my 8×8 LED matrix clock.

I’ve added a DS1307 RTC chip, which has to run from the 5v pin on the NodeMCU and use 10k pullup resistors between 3.3v and the SCL/SDA pins on the NodeMCU and DS1307. I’m using the Adafruit fork of RTCLib as it has better ESP8266 support and uses unixtime. I’m using this chip as I’ve got a load of them and already have a veroboard setup with the coincell and crystal.

I’ve also added a button, which when pressed reads the time from the RTC and displays it, then it fetches the NTP time over the internet and updates the display (and RTC). Also a single pixel is lit when the display is showing NTP time, so I can differentiate between RTC and NTP. I’ve done it this way as sometimes it can take up to 10secs to fetch the NTP time, and I really wanted something to display instantly. The battery-backed RTC means it will still show accurate time if there is no internet access. The display sleeps after 5secs.

I’m not sure if I’m going to replace the NodeMCU with just a bare ESP-12E on some breadboard fed by a LiFePO4 battery, or stick with the whole NodeMCU powered from a USB mains adaptor. The 5v supply is quite handy, and means I don’t need to add a step-up just for the DS1307.

Update: I’ve ordered some DS3231 RTC modules which run from 3.3v, although I must remember to cut the trace or remove the diode/resistor trickle charge circuit as I’ll be using them with non-rechargeable CR2032 batteries. So I can remove the pullup resistors and run the whole clock from 3.3v

I’ve also moved the button to the RST pin and implemented an infinite deepSleep(0); so that the MCU and display sleeps until you reset the MCU. I’ve yet to measure the current consumption.

For the 8×8 LED matrices I’m using my fork of the LedControl library. I’m considering soldering the pin headers a bit differently than they currently are – maybe facing inwards on the underside, rather than sticking out of the top and bottom.

The pinout for the NodeMCU is a bit weird when using Arduino instead of NodeLua, here is the pin mapping, my setup is:

DIN : D7 (GPIO-13)
CLK : D5 (GPIO-14)
CS  : D3 (GPIO-0)
VCC : 3V3
GND : GND

SCL : D1 (GPIO-5), pullup resistor goes to 3.3v
SDA : D2 (GPIO-4), pullup resistor goes to 3.3v
5v  : VIN

Button : RST, other side goes to GND

Here’s a video of it in action before I added the RTC – notice it takes a couple of seconds for the NTP request after the button is pressed:

The code is:

#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include "LedControl.h"
#include <Wire.h>
#include <RTClib.h>

// local port to listen for udp packets
unsigned int localPort = 2390;

// ntp server pool
IPAddress timeServerIP;
const char* ntpServerName = "europe.pool.ntp.org";

// ntp time stamp is in the first 48 bytes of the message
const int NTP_PACKET_SIZE = 48;

// buffer to hold incoming and outgoing packets
byte packetBuffer[NTP_PACKET_SIZE];

// create a udp instance
WiFiUDP udp;

// data, clock, cs, numdevices
LedControl lc = LedControl(D7,D5,D3,4);

// ds1307 constuctor
RTC_DS1307 RTC;

const int num[10][8] = {
    {0x00,0x78,0xcc,0xec,0xfc,0xdc,0xcc,0x78}, // zero
    {0x00,0xfc,0x30,0x30,0x30,0x30,0xf0,0x30}, // one
    {0x00,0xfc,0xcc,0x60,0x38,0x0c,0xcc,0x78}, // two
    {0x00,0x78,0xcc,0x0c,0x38,0x0c,0xcc,0x78}, // three
    {0x00,0x0c,0x0c,0xfe,0xcc,0x6c,0x3c,0x1c}, // four
    {0x00,0x78,0xcc,0x0c,0x0c,0xf8,0xc0,0xfc}, // five
    {0x00,0x78,0xcc,0xcc,0xf8,0xc0,0x60,0x38}, // six
    {0x00,0x60,0x60,0x30,0x18,0x0c,0xcc,0xfc}, // seven
    {0x00,0x78,0xcc,0xcc,0x78,0xcc,0xcc,0x78}, // eight
    {0x00,0x70,0x18,0x0c,0x7c,0xcc,0xcc,0x78}  // nine
};

void drawNum(int number, int display)
{
    lc.setColumn(display, 0, num[number][0]);
    lc.setColumn(display, 1, num[number][1]);
    lc.setColumn(display, 2, num[number][2]);
    lc.setColumn(display, 3, num[number][3]);
    lc.setColumn(display, 4, num[number][4]);
    lc.setColumn(display, 5, num[number][5]);
    lc.setColumn(display, 6, num[number][6]);
    lc.setColumn(display, 7, num[number][7]);
}

// send an ntp request to the time server at the given address
unsigned long sendNTPpacket(IPAddress& address)
{
    Serial.println("Sending NTP packet...");

    // set all bytes in the buffer to 0
    memset(packetBuffer, 0, NTP_PACKET_SIZE);

    packetBuffer[0] = 0b11100011;   // li, version, mode
    packetBuffer[1] = 0;            // stratum, or type of clock
    packetBuffer[2] = 6;            // polling interval
    packetBuffer[3] = 0xEC;         // peer clock precision

    // 8 bytes of zero for root delay & root dispersion
    packetBuffer[12] = 49;
    packetBuffer[13] = 0x4E;
    packetBuffer[14] = 49;
    packetBuffer[15] = 52;

    // all ntp fields have been given values, send request
    udp.beginPacket(address, 123);
    udp.write(packetBuffer, NTP_PACKET_SIZE);
    udp.endPacket();
}

void displayDate(unsigned long unixtime)
{
    // power up led matrices
    for (int i=0; i<4; i++)
    {
        lc.shutdown(i,false); // come out of powersaving
        lc.setIntensity(i,8); // set brightness 0-15
        lc.clearDisplay(i);   // clear display
    }

    // print hour to led
    int myhour = (unixtime % 86400L) / 3600;
    drawNum(myhour/10,0);
    drawNum(myhour%10,1);

    // print minute to led
    int myminute = (unixtime % 3600) / 60;
    if (myminute < 10)
    {
        drawNum(0,2);
    }
    else
    {
        drawNum(myminute/10,2);
    }
    drawNum(myminute%10,3);
}

void gotoSleep()
{
    for (int i=0; i<4; i++)
    {
        lc.clearDisplay(i);
        lc.setIntensity(i,0);
        lc.shutdown(i,true);
    }

    // latch cs pin and pause to work around display0 being stuck high on sleep problem
    digitalWrite(D3, LOW);
    delay(2000);

    // sleep mcu forever
    ESP.deepSleep(0);
}

void setup()
{
    // setup ds1307
    Wire.begin();
    RTC.begin();

    // display rtc time on led matrices
    DateTime now = RTC.now();
    displayDate(now.unixtime());

    // debug
    Serial.begin(9600);

    // connect to wifi network
    WiFi.begin("ssid", "password");

    // static ip, gateway, netmask
    WiFi.config(IPAddress(192, 168, 1, 2), IPAddress(192, 168, 1, 1), IPAddress(255, 255, 255, 0));

    // connect - could drain battery if never connects to wifi
    int wifitries = 0;
    while (WiFi.status() != WL_CONNECTED)
    {
        // give it a moment
        delay(1000);

        // only try 5 times before sleeping
        wifitries++;
        Serial.print("Wifi connect tries: ");
        Serial.println(wifitries);
        if (wifitries >5)
        {
            Serial.println("Gave up on wifi");
            gotoSleep();
        }
    }

    Serial.println("Starting UDP");
    udp.begin(localPort);
    Serial.print("Local port: ");
    Serial.println(udp.localPort());

    // get a random server from the pool
    WiFi.hostByName(ntpServerName, timeServerIP);

    int cb = 0;
    int ntptries = 0;
    while (!cb)
    {
        // send an ntp packet to a time server
        sendNTPpacket(timeServerIP);

        // wait to see if a reply is available
        delay(3000);
        cb = udp.parsePacket();

        // only try 5 times before sleeping
        ntptries++;
        Serial.print("NTP connect tries: ");
        Serial.println(ntptries);
        if (ntptries >5)
        {
            Serial.println("Gave up on NTP");
            gotoSleep();
        }
    }

    // we've received a packet, read the data from it
    Serial.print("Packet received, length=");
    Serial.println(cb);

    // read the packet into the buffer
    udp.read(packetBuffer, NTP_PACKET_SIZE);

    // the timestamp starts at byte 40 of the received packet and is four bytes,
    // or two words, long. first, esxtract the two words:
    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);

    // combine the four bytes (two words) into a long integer
    // this is ntp time (seconds since jan 1 1900):
    unsigned long secsSince1900 = highWord << 16 | lowWord;
    Serial.print("Seconds since Jan 1 1900 = ");
    Serial.println(secsSince1900);

    // now convert ntp time into everyday time:
    Serial.print("Unix time = ");

    // unix time starts on jan 1 1970. in seconds, that's 2208988800:
    const unsigned long seventyYears = 2208988800UL;

    // subtract seventy years:
    unsigned long epoch = secsSince1900 - seventyYears;

    // print unix time:
    Serial.println(epoch);

    // update ds1307 from ntp unixtime
    RTC.adjust(DateTime(epoch));
    Serial.println("Updated RTC");

    // utc is the time at gmt
    Serial.print("The UTC time is ");

    // print the hour - 86400 equals secs per day
    Serial.print((epoch % 86400L) / 3600);
    Serial.print(':');

    // in the first 10 minutes of each hour, we'll want a leading '0'
    if (((epoch % 3600) / 60) < 10)
    {
        Serial.print('0');
    }

    // print the minute (3600 equals secs per minute)
    Serial.print((epoch % 3600) / 60);
    Serial.print(':');

    // in the first 10 seconds of each minute, we'll want a leading '0'
    if ((epoch % 60) < 10)
    {
        Serial.print('0');
    }

    // print the second
    Serial.println(epoch % 60);
    Serial.println("");

    // display ntp time on led matrices
    displayDate(epoch);

    // ntp has a dot rtc does not
    lc.setLed(3, 7, 0, true);

    // wait five seconds before shutting down
    delay(5000);
    gotoSleep();
}

void loop()
{
}

Edit: finished video.

Multiple LED Matrices

I’ve been playing with the LedControl library again, this time preparing my 8×8 matrices to be used as a clock, although I’m waiting for two more to arrive from AliExpress.

Anyway, its pretty simple to wire them up in series, you basically use the same pinout for connecting to an Arduino as you do for connecting the matrices together – VCC, GND, DIN, CS, CLK.

For my sketch, I’d prefer them to be rotated 90 degrees, so that the input/chip was on the bottom and output wires at the top (rather than left to right), so I had to edit the hex a bit.

At the moment I’m just using an Arduino Nano3 clone as it has a handy 5v output, eventually I’ll convert it to using a NodeMCU (or bare ESP-14) or a Raspberry Pi Zero, as I want wifi support for fetching time via NTP, however LedControl seems to be AVR-specific as it uses PROGMEM etc; I had some success with this library on my NodeMCU, scrolling wasn’t great though.

The sketch basically counts from 01-12, the same as the hours on a clock, I’ve just got to add the minutes, but that will be simple enough.

#include "LedControl.h"

// data-in, clock, cs, numdevices
LedControl lc = LedControl(12,11,10,2);

const int num[10][8] = {
    {0x00,0x78,0xcc,0xec,0xfc,0xdc,0xcc,0x78}, // zero
    {0x00,0xfc,0x30,0x30,0x30,0x30,0xf0,0x30}, // one
    {0x00,0xfc,0xcc,0x60,0x38,0x0c,0xcc,0x78}, // two
    {0x00,0x78,0xcc,0x0c,0x38,0x0c,0xcc,0x78}, // three
    {0x00,0x0c,0x0c,0xfe,0xcc,0x6c,0x3c,0x1c}, // four
    {0x00,0x78,0xcc,0x0c,0x0c,0xf8,0xc0,0xfc}, // five
    {0x00,0x78,0xcc,0xcc,0xf8,0xc0,0x60,0x38}, // six
    {0x00,0x60,0x60,0x30,0x18,0x0c,0xcc,0xfc}, // seven
    {0x00,0x78,0xcc,0xcc,0x78,0xcc,0xcc,0x78}, // eight
    {0x00,0x70,0x18,0x0c,0x7c,0xcc,0xcc,0x78}  // nine
};

/* non-rotated version
int num[10][8] = {
    {0x7c,0xfe,0x9a,0xb2,0xfe,0x7c,0x00,0x00}, // zero
    {0x42,0x42,0xfe,0xfe,0x02,0x02,0x00,0x00}, // one
    {0x46,0xce,0x9a,0x92,0xf6,0x66,0x00,0x00}, // two
    {0x44,0xc6,0x92,0x92,0xfe,0x6c,0x00,0x00}, // three
    {0x18,0x38,0x68,0xc8,0xfe,0xfe,0x08,0x00}, // four
    {0xe4,0xe6,0xa2,0xa2,0xbe,0x9c,0x00,0x00}, // five
    {0x3c,0x7e,0xd2,0x92,0x9e,0x0c,0x00,0x00}, // six
    {0xc0,0xc6,0x8e,0x98,0xf0,0xe0,0x00,0x00}, // seven
    {0x6c,0xfe,0x92,0x92,0xfe,0x6c,0x00,0x00}, // eight
    {0x60,0xf2,0x92,0x96,0xfc,0x78,0x00,0x00}  // nine
};
*/

void drawNum(int number, int display)
{
    lc.setColumn(display, 0, num[number][0]);
    lc.setColumn(display, 1, num[number][1]);
    lc.setColumn(display, 2, num[number][2]);
    lc.setColumn(display, 3, num[number][3]);
    lc.setColumn(display, 4, num[number][4]);
    lc.setColumn(display, 5, num[number][5]);
    lc.setColumn(display, 6, num[number][6]);
    lc.setColumn(display, 7, num[number][7]);
}

void setup(void)
{
    // switch from powersaving into normal mode
    lc.shutdown(0,false);
    lc.shutdown(1,false);

    // set the brightness 0..15
    lc.setIntensity(0,8);
    lc.setIntensity(1,8);
}

void loop()
{
    // 0
    lc.clearDisplay(1);
    drawNum(0,0);

    // 1-9
    for (int i=1; i<=9; i++)
    {
        lc.clearDisplay(1);
        drawNum(i,1);
        delay(1000);
    }

    // 10
    lc.clearDisplay(0);
    drawNum(1,0);
    lc.clearDisplay(1);
    drawNum(0,1);
    delay(1000);

    // 11
    lc.clearDisplay(1);
    drawNum(1,1);
    delay(1000);

    // 12
    lc.clearDisplay(1);
    drawNum(2,1);
    delay(1000);
}

And the Makefile:

BOARD_TAG    = nano328
MONITOR_PORT = /dev/ttyUSB0
ARDUINO_LIBS = LedControl

include /usr/share/arduino/Arduino.mk

Alarm Clock Design

I’m thinking of building an alarm clock next, I need something that has bright lights that can be turned on and off, as my wall clock I can’t read at night. I’m probably going to use four MAX7219 controlled 8×8 LED matrices just to display the four digits. I was thinking of a 7-segment display, but that’s a bit small.

Also I’d like it to be NTP synced, so needs wifi. I’ll possibly use a Raspberry Pi Zero with wifi dongle, if I can get my hands on one, otherwise it’ll be an ESP-12; probably powered by a USB mains adaptor as the LED’s need 5v even though the zero/esp8266 are 3.3v

Not sure if its going to be just a clock or an alarm, I don’t think a piezo buzzer will be very good, so maybe a speaker.

I’ve already got a bunch of DS1307 RTC chips and 32.768KHz 12.5pF crystals, just have to power at 5v and use pullup resistors on SDA/SCL to 3.3v

I made a test sketch on the 4tronix ESP-12e breakout, which just prints the NTP time to the serial monitor, its based on the demo from the ESP8266WiFi library:

#include <ESP8266WiFi.h>
#include <WiFiUdp.h>

// local port to listen for udp packets
unsigned int localPort = 2390;

// ntp server pool
IPAddress timeServerIP;
const char* ntpServerName = "europe.pool.ntp.org";

// ntp time stamp is in the first 48 bytes of the message
const int NTP_PACKET_SIZE = 48;

// buffer to hold incoming and outgoing packets
byte packetBuffer[NTP_PACKET_SIZE];

// create a udp instance
WiFiUDP udp;

// send an ntp request to the time server at the given address
unsigned long sendNTPpacket(IPAddress& address)
{
    Serial.println("Sending NTP packet...");

    // set all bytes in the buffer to 0
    memset(packetBuffer, 0, NTP_PACKET_SIZE);

    packetBuffer[0] = 0b11100011;   // li, version, mode
    packetBuffer[1] = 0;            // stratum, or type of clock
    packetBuffer[2] = 6;            // polling interval
    packetBuffer[3] = 0xEC;         // peer clock precision

    // 8 bytes of zero for root delay & root dispersion
    packetBuffer[12] = 49;
    packetBuffer[13] = 0x4E;
    packetBuffer[14] = 49;
    packetBuffer[15] = 52;

    // all ntp fields have been given values, send request
    udp.beginPacket(address, 123);
    udp.write(packetBuffer, NTP_PACKET_SIZE);
    udp.endPacket();
}

void setup()
{
    // debug
    Serial.begin(9600);

    // connect to wifi network
    WiFi.begin("SSID", "passphrase");

    // static ip, gateway, netmask
    WiFi.config(IPAddress(192, 168, 1, 2), IPAddress(192, 168, 1, 1), IPAddress(255, 255, 255, 0));

    while (WiFi.status() != WL_CONNECTED)
    {
        delay(200);
    }

    Serial.println("Starting UDP");
    udp.begin(localPort);
    Serial.print("Local port: ");
    Serial.println(udp.localPort());
}

void loop()
{
    // get a random server from the pool
    WiFi.hostByName(ntpServerName, timeServerIP);

    // send an ntp packet to a time server
    sendNTPpacket(timeServerIP);

    // wait to see if a reply is available
    delay(1000);

    int cb = udp.parsePacket();
    if (!cb)
    {
        Serial.println("No packet yet");
    }
    else
    {
        // we've received a packet, read the data from it
        Serial.print("Packet received, length=");
        Serial.println(cb);

        // read the packet into the buffer
        udp.read(packetBuffer, NTP_PACKET_SIZE);

        // the timestamp starts at byte 40 of the received packet and is four bytes,
        // or two words, long. first, esxtract the two words:
        unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
        unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);

        // combine the four bytes (two words) into a long integer
        // this is ntp time (seconds since jan 1 1900):
        unsigned long secsSince1900 = highWord << 16 | lowWord;
        Serial.print("Seconds since Jan 1 1900 = ");
        Serial.println(secsSince1900);

        // now convert ntp time into everyday time:
        Serial.print("Unix time = ");

        // unix time starts on jan 1 1970. in seconds, that's 2208988800:
        const unsigned long seventyYears = 2208988800UL;

        // subtract seventy years:
        unsigned long epoch = secsSince1900 - seventyYears;

        // print unix time:
        Serial.println(epoch);

        // utc is the time at gmt
        Serial.print("The UTC time is ");

        // print the hour - 86400 equals secs per day
        Serial.print((epoch % 86400L) / 3600);
        Serial.print(':');

        // in the first 10 minutes of each hour, we'll want a leading '0'
        if (((epoch % 3600) / 60) < 10)
        {
            Serial.print('0');
        }

        // print the minute (3600 equals secs per minute)
        Serial.print((epoch % 3600) / 60);
        Serial.print(':');

        // in the first 10 seconds of each minute, we'll want a leading '0'
        if ((epoch % 60) < 10)
        {
            Serial.print('0');
        }

        // print the second
        Serial.println(epoch % 60);
        Serial.println("");
    }

    // wait ten seconds before asking for the time again
    delay(10000);
}