├── .gitignore ├── ESP-NTP-MQTT.ino ├── LICENSE ├── MQTT-Demo.gif ├── Readme.md └── old_ver └── V0.3.ino /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/c,osx,c++,java,linux,python 3 | # Edit at https://www.gitignore.io/?templates=c,osx,c++,java,linux,python 4 | 5 | ### C ### 6 | # Prerequisites 7 | *.d 8 | 9 | # Object files 10 | *.o 11 | *.ko 12 | *.obj 13 | *.elf 14 | 15 | # Linker output 16 | *.ilk 17 | *.map 18 | *.exp 19 | 20 | # Precompiled Headers 21 | *.gch 22 | *.pch 23 | 24 | # Libraries 25 | *.lib 26 | *.a 27 | *.la 28 | *.lo 29 | 30 | # Shared objects (inc. Windows DLLs) 31 | *.dll 32 | *.so 33 | *.so.* 34 | *.dylib 35 | 36 | # Executables 37 | *.exe 38 | *.out 39 | *.app 40 | *.i*86 41 | *.x86_64 42 | *.hex 43 | 44 | # Debug files 45 | *.dSYM/ 46 | *.su 47 | *.idb 48 | *.pdb 49 | 50 | # Kernel Module Compile Results 51 | *.mod* 52 | *.cmd 53 | .tmp_versions/ 54 | modules.order 55 | Module.symvers 56 | Mkfile.old 57 | dkms.conf 58 | 59 | ### C++ ### 60 | # Prerequisites 61 | 62 | # Compiled Object files 63 | *.slo 64 | 65 | # Precompiled Headers 66 | 67 | # Compiled Dynamic libraries 68 | 69 | # Fortran module files 70 | *.mod 71 | *.smod 72 | 73 | # Compiled Static libraries 74 | *.lai 75 | 76 | # Executables 77 | 78 | ### Java ### 79 | # Compiled class file 80 | *.class 81 | 82 | # Log file 83 | *.log 84 | 85 | # BlueJ files 86 | *.ctxt 87 | 88 | # Mobile Tools for Java (J2ME) 89 | .mtj.tmp/ 90 | 91 | # Package Files # 92 | *.jar 93 | *.war 94 | *.nar 95 | *.ear 96 | *.zip 97 | *.tar.gz 98 | *.rar 99 | 100 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 101 | hs_err_pid* 102 | 103 | ### Linux ### 104 | *~ 105 | 106 | # temporary files which can be created if a process still has a handle open of a deleted file 107 | .fuse_hidden* 108 | 109 | # KDE directory preferences 110 | .directory 111 | 112 | # Linux trash folder which might appear on any partition or disk 113 | .Trash-* 114 | 115 | # .nfs files are created when an open file is removed but is still being accessed 116 | .nfs* 117 | 118 | ### OSX ### 119 | # General 120 | .DS_Store 121 | .AppleDouble 122 | .LSOverride 123 | 124 | # Icon must end with two \r 125 | Icon 126 | 127 | # Thumbnails 128 | ._* 129 | 130 | # Files that might appear in the root of a volume 131 | .DocumentRevisions-V100 132 | .fseventsd 133 | .Spotlight-V100 134 | .TemporaryItems 135 | .Trashes 136 | .VolumeIcon.icns 137 | .com.apple.timemachine.donotpresent 138 | 139 | # Directories potentially created on remote AFP share 140 | .AppleDB 141 | .AppleDesktop 142 | Network Trash Folder 143 | Temporary Items 144 | .apdisk 145 | 146 | ### Python ### 147 | # Byte-compiled / optimized / DLL files 148 | __pycache__/ 149 | *.py[cod] 150 | *$py.class 151 | 152 | # C extensions 153 | 154 | # Distribution / packaging 155 | .Python 156 | build/ 157 | develop-eggs/ 158 | dist/ 159 | downloads/ 160 | eggs/ 161 | .eggs/ 162 | lib/ 163 | lib64/ 164 | parts/ 165 | sdist/ 166 | var/ 167 | wheels/ 168 | pip-wheel-metadata/ 169 | share/python-wheels/ 170 | *.egg-info/ 171 | .installed.cfg 172 | *.egg 173 | MANIFEST 174 | 175 | # PyInstaller 176 | # Usually these files are written by a python script from a template 177 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 178 | *.manifest 179 | *.spec 180 | 181 | # Installer logs 182 | pip-log.txt 183 | pip-delete-this-directory.txt 184 | 185 | # Unit test / coverage reports 186 | htmlcov/ 187 | .tox/ 188 | .nox/ 189 | .coverage 190 | .coverage.* 191 | .cache 192 | nosetests.xml 193 | coverage.xml 194 | *.cover 195 | .hypothesis/ 196 | .pytest_cache/ 197 | 198 | # Translations 199 | *.mo 200 | *.pot 201 | 202 | # Scrapy stuff: 203 | .scrapy 204 | 205 | # Sphinx documentation 206 | docs/_build/ 207 | 208 | # PyBuilder 209 | target/ 210 | 211 | # pyenv 212 | .python-version 213 | 214 | # pipenv 215 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 216 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 217 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 218 | # install all needed dependencies. 219 | #Pipfile.lock 220 | 221 | # celery beat schedule file 222 | celerybeat-schedule 223 | 224 | # SageMath parsed files 225 | *.sage.py 226 | 227 | # Spyder project settings 228 | .spyderproject 229 | .spyproject 230 | 231 | # Rope project settings 232 | .ropeproject 233 | 234 | # Mr Developer 235 | .mr.developer.cfg 236 | .project 237 | .pydevproject 238 | 239 | # mkdocs documentation 240 | /site 241 | 242 | # mypy 243 | .mypy_cache/ 244 | .dmypy.json 245 | dmypy.json 246 | 247 | # Pyre type checker 248 | .pyre/ 249 | 250 | # End of https://www.gitignore.io/api/c,osx,c++,java,linux,python 251 | 252 | -------------------------------------------------------------------------------- /ESP-NTP-MQTT.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Based on code from https://github.com/G6EJD/ESP8266-MAX7219-LED-4x8x8-Matrix-Clock 3 | >> ESP-NTP-MQTT V0.1 > This is designed to connect to WiFi and collect NTP time. 27-MAR-2020. AKA COVID-19 year. 4 | >> ESP-NTP-MQTT V0.2 > MQTT Callback added. Send a message to the address below. 5 | >> ESP-NTP-MQTT V0.3 > Start to add Led Matrix code for MAX7219. 6 | >> ESP-NTP-MQTT V0.4 > Base ESP8266 code has NTP functionality in it now, using that as it only queries NTP once an hour 7 | >> > Also added MQTT brightness topic to alter brightness. 8 | >> ESP-NTP-MQTT V0.5 > Add 'shutdown' MQTT to switch off MAX displays 9 | >> > Add defaultBrightness 10 | >> > Add debugMode 11 | >> ESP-NTP-MQTT V0.6 > Fixed string handling code 12 | >> > Removed 'shutdown' MQTT option, now just set brightness to '0' to turn off 13 | >> > Fixed a memory leak issue that was causing reboots 14 | >> ESP-NTP-MQTT V0.7 > Added LWT (Last Will and Testament) code to track online status 15 | >> 16 | */ 17 | 18 | //////////////////////////////////////////////////////// 19 | // Includes: 20 | 21 | #include // MQTT Client 22 | #include // SPI for MAX display 23 | #include // Graphics Generator for MAX display 24 | #include // Panel Module for MAX display 25 | #include // WiFi 26 | #include // settimeofday_cb() 27 | #include // PolledTimeout 28 | #include // time() ctime() 29 | #include // struct timeval 30 | #include // TimeZone Database (includes DST etc.) 31 | 32 | //////////////////////////////////////////////////////// 33 | 34 | #define STASSID "" // Enter your WiFi SSID here 35 | #define STAPSK "" // Enter your Wifi password here 36 | 37 | #define MYTZ TZ_Europe_London // Set your timezone here 38 | 39 | #define timeServer "uk.pool.ntp.org" // Set your timeserver here 40 | 41 | const char* mqtt_server = ""; // Set your MQTT server address here 42 | const char* mqttUser = ""; // Set your MQTT username here (if used) 43 | const char* mqttPassword = ""; // Set your MQTT password here (if used) 44 | 45 | const char* willTopic = "nodeNTP_1/status/LWT"; // Modify to set LWT topic or message 46 | const int willQoS = 0; 47 | const bool willRetain = true; 48 | const char* willMessage = "disconnected"; // Messages 49 | const char* willMessageConnect = "connected"; 50 | 51 | int pinCS = D2; // Attach CS to this pin, DIN to MOSI and CLK to SCK (cf http://arduino.cc/en/Reference/SPI ) 52 | int numberOfHorizontalDisplays = 4; // Display number (horiz) 53 | int numberOfVerticalDisplays = 1; // Display number (vert) 54 | int wait = 70; // In milliseconds, at the end of the scrolling MQTT message before returning to time 55 | int spacer = 1; // Font Spacer 56 | int width = 5 + spacer; // The font width is 5 pixels 57 | int defaultBrightness = 5; // Default brightness (1-15) or (0 - Off) 58 | int debugMode = 0; // Enable (1) or Disable (0) serial outputs of NTP time 59 | 60 | //////////////////////////////////////////////////////// 61 | 62 | // for testing purpose: 63 | extern "C" int clock_gettime(clockid_t unused, struct timespec *tp); 64 | 65 | char time_value[20]; // This var will contain the digits for the time 66 | Max72xxPanel matrix = Max72xxPanel(pinCS, numberOfHorizontalDisplays, numberOfVerticalDisplays); 67 | static time_t now; // An object which can store a time 68 | byte actualSecond; 69 | 70 | static esp8266::polledTimeout::periodicMs showTimeNow(1000); // this uses the PolledTimeout library to allow an action to be performed every 1000 milli seconds 71 | 72 | WiFiClient espClient; 73 | PubSubClient client(espClient); 74 | int wifiAttempt = 0; 75 | 76 | //////////////////////////////////////////////////////// 77 | 78 | // This is a shortcut to print a bunch of stuff to the serial port. It's very confusing, but shows what values are available 79 | #define PTM(w) \ 80 | Serial.print(" " #w "="); \ 81 | Serial.print(tm->tm_##w); 82 | 83 | void printTm(const char* what, const tm* tm) { 84 | Serial.print(what); 85 | PTM(isdst); PTM(yday); PTM(wday); 86 | PTM(year); PTM(mon); PTM(mday); 87 | PTM(hour); PTM(min); PTM(sec); 88 | } 89 | // 90 | 91 | void reconnect() { 92 | // Loop until we're reconnected 93 | while (!client.connected()) { 94 | Serial.print("Attempting MQTT connection..."); 95 | // Create a random client ID 96 | String clientId = "NTPNode-"; 97 | clientId += String(random(0xffff), HEX); 98 | // Attempt to connect 99 | if (client.connect(clientId.c_str(), mqttUser, mqttPassword, willTopic, willQoS, willRetain, willMessage)) { 100 | Serial.print("Connected: "); 101 | Serial.println(clientId); 102 | // Once connected, publish an announcement... 103 | client.publish(willTopic, willMessageConnect,1); 104 | client.subscribe("nodeNTP_1/inmsg"); // Subscribe to mqtt messages in this topic (eg nodeNTP_1/inmsg "Hello World" - will display text message) 105 | client.subscribe("nodeNTP_1/bright"); // Subscribe to messages in this topic (eg nodeNTP_1/bright "5" - sets the display brightness 1-15 or 0 to switch off) 106 | } else { 107 | Serial.print("failed, rc="); 108 | Serial.print(client.state()); 109 | Serial.println(" try again in 5 seconds"); 110 | // Wait 5 seconds before retrying 111 | delay(5000); 112 | } 113 | } 114 | } 115 | 116 | void callback(char* topic, byte* payload, unsigned int length) { 117 | // Handles receiving mqtt messages 118 | 119 | //char* payloadData; // we start, assuming open 120 | 121 | //Terminate the payload with 'nul' \0 122 | payload[length] = '\0'; 123 | 124 | Serial.print("Message arrived in topic: "); 125 | Serial.println(topic); 126 | 127 | if (strcmp(topic,"nodeNTP_1/inmsg")==0) { 128 | Serial.print("Message:"); 129 | 130 | char *payloadData = (char *) payload; 131 | Serial.print(payloadData); 132 | 133 | matrix.fillScreen(LOW); 134 | display_message(payloadData); 135 | 136 | Serial.println(); 137 | Serial.println("-----------------------"); 138 | 139 | delay(1000); 140 | } 141 | 142 | if (strcmp(topic,"nodeNTP_1/bright")==0) { 143 | int aNumber = atoi((char *)payload); 144 | Serial.print("Brightness set to: "); 145 | Serial.println(aNumber); 146 | if (aNumber >= 0 || aNumber <= 15) { 147 | matrix.shutdown(false); 148 | matrix.setIntensity(aNumber); 149 | } 150 | if (aNumber == 0) { 151 | matrix.shutdown(true); 152 | } 153 | } 154 | } 155 | 156 | 157 | void display_message(String message) { 158 | // Displays a message on the matrix 159 | for ( int i = 0 ; i < width * message.length() + matrix.width() - spacer; i++ ) { 160 | int letter = i / width; 161 | int x = (matrix.width() - 1) - i % width; 162 | int y = (matrix.height() - 8) / 2; // center the text vertically 163 | while ( x + width - spacer >= 0 && letter >= 0 ) { 164 | if ( letter < message.length() ) { 165 | matrix.drawChar(x, y, message[letter], HIGH, LOW, 1); // HIGH LOW means foreground ON, background off, reverse to invert the image 166 | } 167 | letter--; 168 | x -= width; 169 | } 170 | matrix.write(); // Send bitmap to display 171 | delay(wait / 2); 172 | } 173 | } 174 | 175 | void showTime() { // This function gets the current time 176 | now = time(nullptr); // Updates the 'now' variable to the current time value 177 | 178 | byte actualHour = localtime(&now)->tm_hour; 179 | byte actualMinute = localtime(&now)->tm_min; 180 | actualSecond = localtime(&now)->tm_sec; 181 | 182 | sprintf(time_value, "%02d:%02d:%02d",actualHour,actualMinute,actualSecond); 183 | 184 | } 185 | 186 | void time_is_set_scheduled() { // This function is set as the callback when time data is retrieved 187 | // In this case we will print the new time to serial port, so the user can see it change (from 1970) 188 | showTime(); 189 | } 190 | 191 | void setup() { 192 | Serial.begin(115200); 193 | delay(1000); 194 | Serial.println(); 195 | Serial.println("Booted"); 196 | Serial.println("Init Matrix"); 197 | 198 | matrix.fillScreen(0); 199 | matrix.setIntensity(defaultBrightness); // Use a value between 0 and 15 for brightness 200 | matrix.setRotation(0, 1); // The first display is position upside down 201 | matrix.setRotation(1, 1); // The first display is position upside down 202 | matrix.setRotation(2, 1); // The first display is position upside down 203 | matrix.setRotation(3, 1); // The first display is position upside down 204 | display_message("Ready"); 205 | 206 | delay(1000); 207 | 208 | Serial.println("Connecting to Wi-Fi"); 209 | 210 | // start network 211 | WiFi.persistent(false); 212 | WiFi.mode(WIFI_STA); 213 | WiFi.begin(STASSID, STAPSK); 214 | 215 | wifiAttempt = 0; 216 | 217 | while (WiFi.status() != WL_CONNECTED) { 218 | delay(500); 219 | wifiAttempt = wifiAttempt + 1; 220 | if (wifiAttempt > 40) { 221 | ESP.restart(); 222 | } 223 | Serial.print("."); 224 | } 225 | 226 | String ip = WiFi.localIP().toString().c_str(); 227 | Serial.println("WiFi connected:" + ip); 228 | display_message("Connected - " + ip); 229 | 230 | client.setServer(mqtt_server, 1883); 231 | client.setCallback(callback); 232 | 233 | // install callback - called when settimeofday is called (by SNTP or us) 234 | // once enabled (by DHCP), SNTP is updated every hour 235 | settimeofday_cb(time_is_set_scheduled); 236 | 237 | // This is where your time zone is set 238 | configTime(MYTZ, timeServer); 239 | 240 | // On boot up the time value will be 0 UTC, which is 1970. 241 | // This is just showing you that, so you can see it change when the current data is received 242 | Serial.printf("Time is currently set by a constant:\n"); 243 | showTime(); 244 | } 245 | 246 | void loop() { 247 | 248 | //matrix.fillScreen(LOW); 249 | 250 | //Ensures MQTT client is connected 251 | { 252 | if (!client.connected()) { 253 | reconnect(); 254 | } 255 | client.loop(); 256 | } 257 | 258 | if (showTimeNow) { 259 | showTime(); 260 | 261 | if (debugMode) { 262 | Serial.print("time_value var is: "); 263 | Serial.println(time_value); 264 | } 265 | 266 | matrix.drawChar(1, 0, time_value[0], HIGH, LOW, 1); // H 267 | matrix.drawChar(9, 0, time_value[1], HIGH, LOW, 1); // HH 268 | //matrix.drawChar(14,0, time_value[2], HIGH,LOW,1); // HH: // Static ':' Symbol 269 | if (actualSecond % 2 == 0) { 270 | matrix.drawChar(14, 0, ':', HIGH, LOW, 1); 271 | } else { 272 | matrix.drawChar(14, 0, ' ', HIGH, LOW, 1); 273 | } 274 | matrix.drawChar(19, 0, time_value[3], HIGH, LOW, 1); // HH:M 275 | matrix.drawChar(26, 0, time_value[4], HIGH, LOW, 1); // HH:MM 276 | matrix.write(); // Send bitmap to display 277 | 278 | if (debugMode) { 279 | Serial.println(); 280 | // human readable serial debug. Switch on debugMode at the vars to enable. 281 | Serial.print("ctime: "); 282 | Serial.print(ctime(&now)); 283 | } 284 | 285 | } 286 | //delay(50); 287 | 288 | } 289 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Johnny Moore 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MQTT-Demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JXGA/ESP8266-NTP-Clock/37ce82dd1676eda370a71be1e54f93aafdb10944/MQTT-Demo.gif -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # ESP-NTP-MQTT 2 | 3 | This project uses an ESP8266 to connect to our local NTP server to display the time on a MAX7219 8x8x4 LED Matrix. 4 | 5 | It is possible to also a public NTP pool/server following an overhaul of the code! 6 | 7 | In addition, the ESP8266 subscribes to an MQTT topic and scrolls messages. I schedule messages from Home Assistant - they run on pre-defined times and give info like weather & 3D printer status. Demo gif is below. 8 | 9 | You will need to insert an NTP server, SSID/Password, MQTT Server, User & Password, plus check the number of MAX7219 displays is correct. 10 | 11 | ## A note on NTP: 12 | 13 | NTP servers are located all over the world, so it's best to use an NTP server that is physically close to you for best performance. 14 | 15 | NTP pools are are clusters of public time servers and are grouped into localities. 16 | 17 | If you can spare the resources and would like another project, try setting up a Chrony server (can run in a Docker container!). You can then serve multiple clocks and your whole network from a local server, reducing the load on the NTP pool. 18 | 19 | ## Libraries: 20 | 21 | A number of other libraries are required. These are below! 22 | 23 | 1. ESP8266 libs: https://github.com/esp8266/Arduino 24 | 2. PubSubClient: https://github.com/knolleary/pubsubclient 25 | 3. Adafruit-GFX: https://github.com/adafruit/Adafruit-GFX-Library 26 | 4. Max72xxPanel: https://github.com/markruys/arduino-Max72xxPanel 27 | 28 | ## Demo: 29 | ![MQTT-Gif](MQTT-Demo.gif) 30 | 31 | ## Display rotation: 32 | 33 | If your displays are upside down, see issue (#1). 34 | 35 | This code may help you: 36 | 37 | ``` 38 | matrix.setPosition(0, 3, 0); // (Display-Nr ab Einspeisung, X, Y) 39 | matrix.setPosition(1, 2, 0); 40 | matrix.setPosition(2, 1, 0); 41 | matrix.setPosition(3, 0, 0); 42 | 43 | matrix.fillScreen(0); 44 | 45 | matrix.setIntensity(0); // Use a value between 0 and 15 for brightness 46 | matrix.setRotation(0, 3); // The first display is position upside down 47 | matrix.setRotation(1, 3); // The first display is position upside down 48 | matrix.setRotation(2, 3); // The first display is position upside down 49 | matrix.setRotation(3, 3); // The first display is position upside down 50 | display_message("Ready"); 51 | ``` 52 | -------------------------------------------------------------------------------- /old_ver/V0.3.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Based on code from https://github.com/G6EJD/ESP8266-MAX7219-LED-4x8x8-Matrix-Clock 3 | >> ESP-NTP-MQTT V0.1 > This is designed to connect to WiFi and collect NTP time. 27-MAR-2020. AKA COVID-19 year. 4 | >> ESP-NTP-MQTT V0.2 > MQTT Callback added. Send a message to the address below. 5 | >> ESP-NTP-MQTT V0.3 > Start to add Led Matrix code for MAX7219. 6 | */ 7 | 8 | #include // NTP Client 9 | #include // MQTT Client 10 | #include // SPI for MAX display 11 | #include // Graphics Generator for MAX display 12 | #include // Panel Module for MAX display 13 | 14 | #define DEBUG_ON 15 | 16 | // Consts for NTP 17 | NTPtime NTPch("ie.pool.ntp.org"); // Choose server pool as required 18 | strDateTime dateTime; 19 | 20 | // Consts for WiFi 21 | char *ssid = "SSID_GOES_HERE"; // Set you WiFi SSID 22 | char *password = "PASSWD_GOES_HERE"; // Set you WiFi password 23 | int wifiAttempt = 0; 24 | WiFiClient espClient; 25 | 26 | // Consts for MQTT Rec. 27 | const char* mqtt_server = "MQTT_SERVER_GOES_HERE"; 28 | const char* mqttUser = "MQTT_USER_GOES_HERE"; 29 | const char* mqttPassword = "MQTT_PASSWD_GOES_HERE"; 30 | PubSubClient client(espClient); 31 | 32 | // Consts for Loop 33 | int lastMinute = 0; 34 | int lastSecond = 0; 35 | 36 | // Consts for MAX 37 | int pinCS = D2; // Attach CS to this pin, DIN to MOSI and CLK to SCK (cf http://arduino.cc/en/Reference/SPI ) 38 | int numberOfHorizontalDisplays = 4; 39 | int numberOfVerticalDisplays = 1; 40 | char time_value[20]; // This var will contain the digits for the time 41 | Max72xxPanel matrix = Max72xxPanel(pinCS, numberOfHorizontalDisplays, numberOfVerticalDisplays); 42 | int wait = 70; // In milliseconds, at the end of the scrolling MQTT message before returning to time 43 | 44 | int spacer = 1; 45 | int width = 5 + spacer; // The font width is 5 pixels 46 | 47 | void reconnect() { 48 | // Loop until we're reconnected 49 | while (!client.connected()) { 50 | Serial.print("Attempting MQTT connection..."); 51 | // Create a random client ID 52 | String clientId = "NTPNode-"; 53 | clientId += String(random(0xffff), HEX); 54 | // Attempt to connect 55 | if (client.connect(clientId.c_str(), mqttUser, mqttPassword)) { 56 | Serial.println("connected"); 57 | // Once connected, publish an announcement... 58 | client.publish("nodeNTP/avail", "online"); 59 | client.subscribe("nodeNTP/msg"); 60 | } else { 61 | Serial.print("failed, rc="); 62 | Serial.print(client.state()); 63 | Serial.println(" try again in 5 seconds"); 64 | // Wait 5 seconds before retrying 65 | delay(5000); 66 | } 67 | } 68 | } 69 | 70 | void callback(char* topic, byte* payload, unsigned int length) { 71 | 72 | String payloadData; // we start, assuming open 73 | 74 | Serial.print("Message arrived in topic: "); 75 | Serial.println(topic); 76 | 77 | Serial.print("Message:"); 78 | payload[length] = '\0'; 79 | payloadData = String((char*)payload); 80 | Serial.print(payloadData); 81 | 82 | display_message(payloadData); 83 | 84 | Serial.println(); 85 | Serial.println("-----------------------"); 86 | 87 | delay(100); 88 | } 89 | 90 | void setup() { 91 | Serial.begin(115200); 92 | Serial.println(); 93 | Serial.println("Booted"); 94 | 95 | delay(1000); 96 | 97 | Serial.println("Init Matrix"); 98 | 99 | matrix.fillScreen(0); 100 | matrix.setIntensity(0); // Use a value between 0 and 15 for brightness 101 | matrix.setRotation(0, 1); // The first display is position upside down 102 | matrix.setRotation(1, 1); // The first display is position upside down 103 | matrix.setRotation(2, 1); // The first display is position upside down 104 | matrix.setRotation(3, 1); // The first display is position upside down 105 | display_message("Ready"); 106 | 107 | delay(1000); 108 | 109 | Serial.println("Connecting to Wi-Fi"); 110 | 111 | WiFi.mode(WIFI_STA); 112 | WiFi.begin (ssid, password); 113 | 114 | wifiAttempt = 0; 115 | while (WiFi.status() != WL_CONNECTED) { 116 | delay(500); 117 | wifiAttempt = wifiAttempt + 1; 118 | if (wifiAttempt > 40) { 119 | ESP.restart(); 120 | } 121 | Serial.print("."); 122 | } 123 | 124 | Serial.println("WiFi connected"); 125 | display_message("Connected"); 126 | 127 | client.setServer(mqtt_server, 1883); 128 | client.setCallback(callback); 129 | 130 | } 131 | 132 | void display_message(String message) { 133 | for ( int i = 0 ; i < width * message.length() + matrix.width() - spacer; i++ ) { 134 | //matrix.fillScreen(LOW); 135 | int letter = i / width; 136 | int x = (matrix.width() - 1) - i % width; 137 | int y = (matrix.height() - 8) / 2; // center the text vertically 138 | while ( x + width - spacer >= 0 && letter >= 0 ) { 139 | if ( letter < message.length() ) { 140 | matrix.drawChar(x, y, message[letter], HIGH, LOW, 1); // HIGH LOW means foreground ON, background off, reverse to invert the image 141 | } 142 | letter--; 143 | x -= width; 144 | } 145 | matrix.write(); // Send bitmap to display 146 | delay(wait / 2); 147 | } 148 | } 149 | 150 | void loop() { 151 | 152 | matrix.fillScreen(LOW); 153 | 154 | //Ensures MQTT client is connected 155 | { 156 | if (!client.connected()) { 157 | reconnect(); 158 | } 159 | client.loop(); 160 | } 161 | 162 | 163 | String myTime; 164 | 165 | 166 | // first parameter: Time zone in floating point (0 for UTC?); second parameter: 1 for European summer time; 2 for US daylight saving time; 0 for no DST adjustment; (contributed by viewwer, not tested by me) 167 | 168 | dateTime = NTPch.getNTPtime(0.0, 1); 169 | 170 | 171 | // check dateTime.valid before using the returned time 172 | // Use "setSendInterval" or "setRecvTimeout" if required 173 | 174 | if (dateTime.valid) { 175 | 176 | // NTPch.printDateTime(dateTime); 177 | 178 | byte actualHour = dateTime.hour; 179 | byte actualMinute = dateTime.minute; 180 | byte actualSecond = dateTime.second; 181 | int actualyear = dateTime.year; 182 | byte actualMonth = dateTime.month; 183 | byte actualday = dateTime.day; 184 | byte actualdayofWeek = dateTime.dayofWeek; 185 | 186 | if (lastSecond != actualSecond) { 187 | 188 | 189 | // Pretty display for future use: 190 | if (actualHour < 10) { 191 | myTime = myTime + "0" + actualHour; 192 | } 193 | else { 194 | myTime = actualHour; 195 | } 196 | 197 | if (actualMinute < 10) { 198 | myTime = myTime + ":0" + actualMinute; 199 | } 200 | else { 201 | myTime = myTime + ":" + actualMinute; 202 | } 203 | 204 | if (actualSecond < 10) { 205 | myTime = myTime + ":0" + actualSecond; 206 | } 207 | else { 208 | myTime = myTime + ":" + actualSecond; 209 | } 210 | 211 | myTime.toCharArray(time_value, 10); 212 | matrix.drawChar(1, 0, time_value[0], HIGH, LOW, 1); // H 213 | matrix.drawChar(9, 0, time_value[1], HIGH, LOW, 1); // HH 214 | // matrix.drawChar(14,0, time_value[2], HIGH,LOW,1); // HH: // Static ':' Symbol 215 | if (actualSecond % 2 == 0) { 216 | matrix.drawChar(14, 0, ':', HIGH, LOW, 1); 217 | } else { 218 | matrix.drawChar(14, 0, ' ', HIGH, LOW, 1); 219 | } 220 | matrix.drawChar(19, 0, time_value[3], HIGH, LOW, 1); // HH:M 221 | matrix.drawChar(26, 0, time_value[4], HIGH, LOW, 1); // HH:MM 222 | matrix.write(); // Send bitmap to display 223 | 224 | Serial.println(myTime); // Display on Serial 225 | 226 | lastSecond = actualSecond; // Loop for MQTT Update (Below) 227 | 228 | } 229 | 230 | delay(50); 231 | 232 | if (actualSecond == 1) { 233 | if (lastMinute != actualMinute) { 234 | Serial.println(); 235 | Serial.println("60 Second MQTT Update running"); 236 | client.publish("nodeNTP/avail", "online"); 237 | Serial.println("...complete"); 238 | lastMinute = actualMinute; 239 | } 240 | } 241 | 242 | } 243 | 244 | } 245 | --------------------------------------------------------------------------------