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&#039;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&#039;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()
{
}

Fitting an LDO to an ESP12F

I had a cunning plan to use the bottom half of my WIFI Witty board as a programmer for bare ESP-12F boards with those white breakouts, but then found that they rely on the regulator being on the ESP12, the whole of the Witty is 5v.

So I figured out that the solder pads on the underside of the breakouts are for a SOT-89 regulator, albeit with a stupid GND-VIN-VOUT pinout, so an LM1117T or suchlike wouldn’t do:

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.

Wifi Witty ESP-12F Board

I’ve just received one of these Wifi Witty AKA Gizwits ESP-12F boards.

The board is in two parts – one part has reset/flash buttons, a CH341 UART and female headers; the other half has USB for power only, a button (which appears to be an input, not reset or flash) and male headers which take up an entire breadboard.

Despite having flash/reset buttons I can only seem to program it from the Arduino IDE with the NodeMCU v1.0 profile which does the DTR reset thing, pressing/holding the flash button seems to do nothing if I try the generic board profile, although some have said that its starts in flash mode as soon as you plug it in.

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.