ESP-NTP-MQTT2.ino

edited version for 8 max7219 modules, and added date.
mail@pastecode.io avatar
unknown
plain_text
a year ago
12 kB
4
Indexable
/*
 * Based on code from https://github.com/G6EJD/ESP8266-MAX7219-LED-4x8x8-Matrix-Clock
  >> ESP-NTP-MQTT V0.1 > This is designed to connect to WiFi and collect NTP time. 27-MAR-2020. AKA COVID-19 year.
  >> ESP-NTP-MQTT V0.2 > MQTT Callback added. Send a message to the address below.
  >> ESP-NTP-MQTT V0.3 > Start to add Led Matrix code for MAX7219.
  >> ESP-NTP-MQTT V0.4 > Base ESP8266 code has NTP functionality in it now, using that as it only queries NTP once an hour
  >>                   > Also added MQTT brightness topic to alter brightness.
  >> ESP-NTP-MQTT V0.5 > Add 'shutdown' MQTT to switch off MAX displays
  >>                   > Add defaultBrightness
  >>                   > Add debugMode
  >> ESP-NTP-MQTT V0.6 > Fixed string handling code
  >>				           > Removed 'shutdown' MQTT option, now just set brightness to '0' to turn off
  >>				           > Fixed a memory leak issue that was causing reboots						
  >> ESP-NTP-MQTT V0.7 > Added LWT (Last Will and Testament) code to track online status 
  >> 

  ******* Edited version based on https://github.com/JXGA/ESP8266-NTP-Clock for eight modules (8x8x8) the original one only worked with 4, now showing date too. *******
*/

////////////////////////////////////////////////////////
// Includes:

#include <PubSubClient.h>   // MQTT Client
#include <SPI.h>            // SPI for MAX display
#include <Adafruit_GFX.h>   // Graphics Generator for MAX display
#include <Max72xxPanel.h>   // Panel Module for MAX display
#include <ESP8266WiFi.h>    // WiFi
#include <coredecls.h>      // settimeofday_cb()
#include <PolledTimeout.h>  // PolledTimeout
#include <time.h>           // time() ctime()
#include <sys/time.h>       // struct timeval
#include <TZ.h>             // TimeZone Database (includes DST etc.)

////////////////////////////////////////////////////////

#define STASSID "yourwifi"        // Enter your WiFi SSID here
#define STAPSK  "wifipassword"    // Enter your Wifi password here

#define MYTZ TZ_Europe_Copenhagen        // Set your timezone here

#define timeServer "uk.pool.ntp.org"  // Set your timeserver here

const char* mqtt_server = "mqttserver";  // Set your MQTT server address here
const char* mqttUser = "mqttuser";                       // Set your MQTT username here (if used)
const char* mqttPassword = "mqttpassword";                   // Set your MQTT password here (if used)

const char* willTopic = "nodeNTP_2/status/LWT";  // Modify to set LWT topic or message
const int willQoS = 0;
const bool willRetain = true;
const char* willMessage = "disconnected";		     // Messages
const char* willMessageConnect = "connected";											   

int pinCS = D6;                             // Attach CS to this pin, DIN to MOSI and CLK to SCK (cf http://arduino.cc/en/Reference/SPI )
int numberOfHorizontalDisplays = 8;         // Display number (horiz)
int numberOfVerticalDisplays   = 1;         // Display number (vert)
int wait = 70;                              // In milliseconds, at the end of the scrolling MQTT message before returning to time
int spacer = 1;                             // Font Spacer
int width  = 5 + spacer;                    // The font width is 5 pixels
int defaultBrightness = 1;                  // Default brightness (1-15) or (0 - Off)
int debugMode = 0;                          // Enable (1) or Disable (0) serial outputs of NTP time

////////////////////////////////////////////////////////

// for testing purpose:
extern "C" int clock_gettime(clockid_t unused, struct timespec *tp);

char time_value[20];      // This var will contain the digits for the time
Max72xxPanel matrix = Max72xxPanel(pinCS, numberOfHorizontalDisplays, numberOfVerticalDisplays);
static time_t now;  // An object which can store a time
byte actualSecond;

static esp8266::polledTimeout::periodicMs showTimeNow(1000); // this uses the PolledTimeout library to allow an action to be performed every 1000 milli seconds

WiFiClient espClient;
PubSubClient client(espClient);
int wifiAttempt = 0;

////////////////////////////////////////////////////////

// This is a shortcut to print a bunch of stuff to the serial port.  It's very confusing, but shows what values are available
#define PTM(w) \
  Serial.print(" " #w "="); \
  Serial.print(tm->tm_##w);

void printTm(const char* what, const tm* tm) {
  Serial.print(what);
  PTM(isdst); PTM(yday); PTM(wday);
  PTM(year);  PTM(mon);  PTM(mday);
  PTM(hour);  PTM(min);  PTM(sec);
}
//

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Create a random client ID
    String clientId = "NTPNode-";
    clientId += String(random(0xffff), HEX);
    // Attempt to connect
    if (client.connect(clientId.c_str(), mqttUser, mqttPassword, willTopic, willQoS, willRetain, willMessage)) {
      Serial.print("Connected: ");
      Serial.println(clientId);
      // Once connected, publish an announcement...
      client.publish(willTopic, willMessageConnect,1);
      client.subscribe("nodeNTP_2/inmsg");             // Subscribe to mqtt messages in this topic (eg nodeNTP_2/inmsg "Hello World" - will display text message)
      client.subscribe("nodeNTP_2/bright");            // Subscribe to messages in this topic (eg nodeNTP_1/bright "5" - sets the display brightness 1-15 or 0 to switch off)
      } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void callback(char* topic, byte* payload, unsigned int length) {
  // Handles receiving mqtt messages
  
  //char* payloadData;             // we start, assuming open
  
  //Terminate the payload with 'nul' \0
  payload[length] = '\0';
  
  Serial.print("Message arrived in topic: ");
  Serial.println(topic);
  
  if (strcmp(topic,"nodeNTP_2/inmsg")==0) {
    Serial.print("Message:");

    char *payloadData = (char *) payload;
    Serial.print(payloadData);

    matrix.fillScreen(LOW);
    display_message(payloadData);
  
    Serial.println();
    Serial.println("-----------------------");

    delay(1000);
  }

  if (strcmp(topic,"nodeNTP_2/bright")==0) {
    int aNumber = atoi((char *)payload);
    Serial.print("Brightness set to: ");
    Serial.println(aNumber);
    if (aNumber >= 0 || aNumber <= 15) {
        matrix.shutdown(false);
        matrix.setIntensity(aNumber);
    } 
    if (aNumber == 0) {
        matrix.shutdown(true);
    } 
   }
}


void display_message(String message) {
    // Displays a message on the matrix
  for ( int i = 0 ; i < width * message.length() + matrix.width() - spacer; i++ ) {
    int letter = i / width;
    int x = (matrix.width() - 1) - i % width;
    int y = (matrix.height() - 8) / 2; // center the text vertically
    while ( x + width - spacer >= 0 && letter >= 0 ) {
      if ( letter < message.length() ) {
        matrix.drawChar(x, y, message[letter], HIGH, LOW, 1); // HIGH LOW means foreground ON, background off, reverse to invert the image
      }
      letter--;
      x -= width;
    }
    matrix.write(); // Send bitmap to display
    delay(wait / 2);
  }
}

void showTime() {           // This function gets the current time
  now = time(nullptr);      // Updates the 'now' variable to the current time value

  byte actualHour = localtime(&now)->tm_hour;
  byte actualMinute = localtime(&now)->tm_min;
  actualSecond = localtime(&now)->tm_sec;

  sprintf(time_value, "%02d:%02d:%02d",actualHour,actualMinute,actualSecond);
  
}

void time_is_set_scheduled() {    // This function is set as the callback when time data is retrieved
  // In this case we will print the new time to serial port, so the user can see it change (from 1970)
  showTime();
}

void setup() {
  Serial.begin(115200);
  delay(1000);
  Serial.println();
  Serial.println("Booted");
  Serial.println("Init Matrix");

  matrix.fillScreen(0);
  matrix.setIntensity(defaultBrightness); // Use a value between 0 and 15 for brightness
  matrix.setRotation(0, 1);    // The first display is position upside down
  matrix.setRotation(1, 1);    // The first display is position upside down
  matrix.setRotation(2, 1);    // The first display is position upside down
  matrix.setRotation(3, 1);    // The first display is position upside down
  matrix.setRotation(4, 1);    // The first display is position upside down
  matrix.setRotation(5, 1);    // The first display is position upside down
  matrix.setRotation(6, 1);    // The first display is position upside down
  matrix.setRotation(7, 1);    // The first display is position upside down
  display_message("Ready");

  delay(1000);

  Serial.println("Connecting to Wi-Fi");

  // start network
  WiFi.persistent(false);
  WiFi.mode(WIFI_STA);
  WiFi.begin(STASSID, STAPSK);

  wifiAttempt = 0;

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    wifiAttempt = wifiAttempt + 1;
    if (wifiAttempt > 40) {
      ESP.restart();
    }
    Serial.print(".");
  }

  String ip = WiFi.localIP().toString().c_str();
  Serial.println("WiFi connected:" + ip);
  display_message("Connected - " + ip);

  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
  
  // install callback - called when settimeofday is called (by SNTP or us)
  // once enabled (by DHCP), SNTP is updated every hour
  settimeofday_cb(time_is_set_scheduled);

  // This is where your time zone is set
  configTime(MYTZ, timeServer);
  
  // On boot up the time value will be 0 UTC, which is 1970.  
  // This is just showing you that, so you can see it change when the current data is received
  Serial.printf("Time is currently set by a constant:\n");
  showTime();
}

void loop() {

 //matrix.fillScreen(LOW);

  //Ensures MQTT client is connected
  {
    if (!client.connected()) {
      reconnect();
    }
    client.loop();
  }

if (showTimeNow) {
    showTime();

      if (debugMode) {
         Serial.print("time_value var is: ");
         Serial.println(time_value);
      }

      //matrix.drawChar(11,0, time_value[2], HIGH,LOW,1); // HH: // Static ':' Symbol
      if (actualSecond % 2 == 0) {
        matrix.drawChar(11, 0, ':', HIGH, LOW, 1);
      } else {
       matrix.drawChar(11, 0, ' ', HIGH, LOW, 1);
      }
      matrix.drawChar(1, 0, time_value[0], HIGH, LOW, 1); // H
      matrix.drawChar(7, 0, time_value[1], HIGH, LOW, 1); // HH
      matrix.drawChar(15, 0, time_value[3], HIGH, LOW, 1); // HH:M
      matrix.drawChar(21, 0, time_value[4], HIGH, LOW, 1); // HH:MM

    // Display date (ddmmm)
    int day = localtime(&now)->tm_mday;
    int month = localtime(&now)->tm_mon + 1; // Months are 0-11, so add 1
    
    matrix.drawChar(32, 0, '0' + day / 10, HIGH, LOW, 1);
    matrix.drawChar(38, 0, '0' + day % 10, HIGH, LOW, 1);
    
    // Display abbreviated month name
    const char* monthNames[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
    const char* abbreviatedMonth = monthNames[month - 1];
    matrix.drawChar(46, 0, abbreviatedMonth[0], HIGH, LOW, 1);
    matrix.drawChar(52, 0, abbreviatedMonth[1], HIGH, LOW, 1);
    matrix.drawChar(58, 0, abbreviatedMonth[2], HIGH, LOW, 1);

    matrix.write(); // Send bitmap to display

      if (debugMode) {
        Serial.println();
        // human readable serial debug. Switch on debugMode at the vars to enable.
        Serial.print("ctime:     ");
        Serial.print(ctime(&now));
      }
      
     }
    //delay(50);
      
}