My current project is one of these WordClocks built with WS2812B LEDs and an ESP8266 and it works great so far. The only problem I have is with the NTP Client library (https://github.com/arduino-libraries/NTPClient). My sketch syncs the RTC time every 30min with the NTP server but sometimes it throws an exception 9 during timeClient.update(). This is the decoded exception:
PC: 0x402059f9: NTPClient::sendNTPPacket() at /Users/janik/Documents/Arduino/libraries/NTPClient/NTPClient.cpp line 172
EXCVADDR: 0x3ffee9ff
Decoding stack results
0x4010095a: millis() at /Users/janik/Library/Arduino15/packages/esp8266/hardware/esp8266/2.7.4/cores/esp8266/core_esp8266_wiring.cpp line 188
0x40205a45: NTPClient::forceUpdate() at /Users/janik/Documents/Arduino/libraries/NTPClient/NTPClient.cpp line 76
0x40205ae7: NTPClient::update() at /Users/janik/Documents/Arduino/libraries/NTPClient/NTPClient.cpp line 99
0x4020113e: updatetime() at /Users/janik/Documents/Arduino/WordClock/WordClock.ino line 44
0x4010095a: millis() at /Users/janik/Library/Arduino15/packages/esp8266/hardware/esp8266/2.7.4/cores/esp8266/core_esp8266_wiring.cpp line 188
0x40201f12: loop() at /Users/janik/Documents/Arduino/WordClock/WordClock.ino line 474
0x401005e0: ets_post(uint8, ETSSignal, ETSParam) at /Users/janik/Library/Arduino15/packages/esp8266/hardware/esp8266/2.7.4/cores/esp8266/core_esp8266_main.cpp line 177
0x4020d384: loop_wrapper() at /Users/janik/Library/Arduino15/packages/esp8266/hardware/esp8266/2.7.4/cores/esp8266/core_esp8266_main.cpp line 197
And this the encoded from the ESPSaveCrash library:
Crash # 1 at 1814320 ms
Restart reason: 2
Exception cause: 9
epc1=0x402059f9 epc2=0x00000000 epc3=0x00000000 excvaddr=0x3ffee9ff depc=0x00000000
>>>stack>>>
3fffff00: 4023bb8a 3ffee95c 4010095a 4a7ef9db
3fffff10: 3fffdad0 00000000 3ffee8f4 40205a45
3fffff20: 3fffdad0 3ffee8f4 3ffee8f4 40205ae7
3fffff30: 00000000 4bc6a7f0 3ffeec08 4020113e
3fffff40: 00000000 00000000 4bc6a7f0 00000000
3fffff50: 001bab7b 00000001 4010095a 389374bc
3fffff60: 00000000 00000000 3ffee760 3ffeed50
3fffff70: 3fffdad0 00000000 3ffee95c 40201f12
3fffff80: 00000000 00000000 00000001 401005e0
3fffff90: 3fffdad0 00000000 3ffeed10 3ffeed50
3fffffa0: 3fffdad0 00000000 3ffeed10 4020d384
3fffffb0: feefeffe feefeffe 3ffe8510 401013e5
<<<stack<<<
And this is my code (I know kinda messy but I've tried to comment as much as possible) / you can ignore the whole setting Leds part, which also has some german comments but that function only sets the leds, so only the letters light up which are needed to display the current time:
#include <FastLED.h>
#include <RTClib.h>
#include <ESP8266WiFi.h>
#include <ArduinoOTA.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
#include <ButtonKing.h>
#include <EspSaveCrash.h>
#define NUM_LEDS 110
#define DATA_PIN 5
EspSaveCrash SaveCrash;
char *_debugOutputBuffer;
#define DEBUG_OUTPUT_SIZE 2048
const char* ssid = "myssid";
const char* password = "mypassword";
long utcOffsetInSeconds = 7200;
int refreshtime;
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, utcOffsetInSeconds);
CRGB leds[NUM_LEDS];
CHSV color = CHSV(0, 255, 150);
long lastcolor = 0;
long currcolor;
RTC_DS3231 rtc;
ButtonKing button(13, false);
int lastminute;
boolean forcetimeupdate;
int timeout, hour1, minute1, second1;
float brightness, brg, brg1;
boolean nexthour;
boolean einstrue;
int hourled;
boolean active = true;
boolean diffcolor = false;
//update time function
void updatetime() {
if ((WiFi.status() == WL_CONNECTED)) {
timeClient.update(); //CRASHES HERE FOR SOME REASON WITH EXCEPTION 9
delay(100);
Serial.println("update");
//check if time from server is legit, otherwise get time again from NTP
while (timeClient.getEpochTime() < 1601573968) {
timeClient.update();
ArduinoOTA.handle();
leds[10] = CRGB::Orange;
leds[0] = CRGB::Orange;
leds[109] = CRGB::Orange;
leds[99] = CRGB::Orange;
FastLED.show();
delay(500);
fill_solid(leds, NUM_LEDS, CRGB::Black);
FastLED.show();
delay(100);
}
rtc.adjust(DateTime(2019, 1, 21, timeClient.getHours(), timeClient.getMinutes(), timeClient.getSeconds()));
refreshtime = millis();
forcetimeupdate = false;
}
else {
//no connection to wifi, trying to reconnect to wifi
WiFi.disconnect();
Serial.println("trying to reconnect to WiFi");
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
int timeout1 = 0;
while (WiFi.status() != WL_CONNECTED && timeout1 < 25) {
timeout1 = timeout1 + 1;
delay(1000);
Serial.print(".");
}
if (!(timeout1 < 25)) {
Serial.println("connection could not be established: Timeout");
//setting forcetimeupdate to true, so it will be called every minute till success
forcetimeupdate = true;
refreshtime = millis();
}
else {
Serial.println("connected");
forcetimeupdate = false;
updatetime();
}
}
}
//timeClient.begin() always call early in setup() otherwise exception
void setup() {
_debugOutputBuffer = (char *) calloc(DEBUG_OUTPUT_SIZE, sizeof(char));
button.setClick(onClick);
button.setLongClickStop(onLongPress);
button.setDoubleClick(onDoubleClick);
Serial.begin(9600);
while (!Serial) {
delay(100);
}
SaveCrash.print();
pinMode(13, INPUT);
pinMode(A0, INPUT);
delay(100);
Serial.println("starting");
FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);
//FastLED.setMaxPowerInVoltsAndMilliamps(5,500);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
timeout = 0;
while (WiFi.status() != WL_CONNECTED && timeout < 25) {
timeout = timeout + 1;
delay(1000);
Serial.print(".");
}
if (!(timeout < 25)) {
Serial.println("connection could not be established: Timeout");
forcetimeupdate = true;
refreshtime = millis();
}
else {
Serial.println("connected");
forcetimeupdate = false;
}
if (! rtc.begin()) {
Serial.println("Couldn't find RTC");
while (1);
}
ArduinoOTA.setHostname("WordClock");
ArduinoOTA.begin();
delay(2000);
timeClient.begin();
updatetime();
//rtc.adjust(DateTime(2019, 1, 21, 12, 35, 40));
//setting inital brightness of the leds
brightness = analogRead(A0);
brg1 = map(brightness, 0, 1024, 0, 255);
//configuring the inital time
lastminute = -3;
DateTime now = rtc.now();
hour1 = now.hour();
minute1 = now.minute();
second1 = now.second();
//startup animation
for (int i = 0; i < 11; i++) { //direction down
for (int j = 0; j < 11; j++) { //fill line
leds[j + 10 * i] = CRGB::White;
}
if (!((i - 2) < 0)) {
for (int k = 0; k < 11; k++) {
leds[k + 10 * (i - 2)] = CRGB::Black;
}
}
FastLED.show();
delay(70);
Serial.println("-");
}
}
//basic leds needed for every displayed time
void setbasic() {
//es ist / it is
for (int i = 0; i < 6; i++) {
if (!(i == 2)) {
leds[i] = color;
}
}
FastLED.show();
}
//setting leds according to current time
void setleds(int hour2, int minute2) {
fill_solid(leds, NUM_LEDS, CRGB::Black);
//12 hour format
if (hour2 > 12) {
hour2 = hour2 - 12;
}
else if (hour2 == 0) {
hour2 = 12;
}
einstrue = false;
if (minute2 < 4) {
nexthour = false;
einstrue = true;
//uhr
for (int i = 99; i < 102; i++) {
leds[i] = color;
}
}
else if (minute2 < 8 && minute2 > 3) {
//fünf
leds[7] = color;
leds[8] = color;
leds[9] = color;
leds[10] = color;
//nach
leds[38] = color;
leds[39] = color;
leds[40] = color;
leds[41] = color;
nexthour = false;
}
else if (minute2 < 13 && minute2 > 7) {
//zehn
leds[18] = color;
leds[19] = color;
leds[20] = color;
leds[21] = color;
//nach
leds[38] = color;
leds[39] = color;
leds[40] = color;
leds[41] = color;
nexthour = false;
}
else if (minute2 < 18 && minute2 > 12) {
//viertel
leds[26] = color;
leds[27] = color;
leds[28] = color;
leds[29] = color;
leds[30] = color;
leds[31] = color;
leds[32] = color;
//nach
leds[38] = color;
leds[39] = color;
leds[40] = color;
leds[41] = color;
nexthour = false;
}
else if (minute2 < 23 && minute2 > 17) {
//zwanzig
leds[11] = color;
leds[12] = color;
leds[13] = color;
leds[14] = color;
leds[15] = color;
leds[16] = color;
leds[17] = color;
//nach
leds[38] = color;
leds[39] = color;
leds[40] = color;
leds[41] = color;
nexthour = false;
}
else if (minute2 < 28 && minute2 > 22) {
//fünf
leds[7] = color;
leds[8] = color;
leds[9] = color;
leds[10] = color;
//vor
leds[35] = color;
leds[36] = color;
leds[37] = color;
//halb
leds[44] = color;
leds[45] = color;
leds[46] = color;
leds[47] = color;
nexthour = true;
}
else if (minute2 < 33 && minute2 > 27) {
//halb
leds[44] = color;
leds[45] = color;
leds[46] = color;
leds[47] = color;
nexthour = true;
}
else if (minute2 < 38 && minute2 > 32) {
//fünf
leds[7] = color;
leds[8] = color;
leds[9] = color;
leds[10] = color;
//nach
leds[38] = color;
leds[39] = color;
leds[40] = color;
leds[41] = color;
//halb
leds[44] = color;
leds[45] = color;
leds[46] = color;
leds[47] = color;
nexthour = true;
}
else if (minute2 < 43 && minute2 > 37) {
//zwanzig
leds[11] = color;
leds[12] = color;
leds[13] = color;
leds[14] = color;
leds[15] = color;
leds[16] = color;
leds[17] = color;
//vor
leds[35] = color;
leds[36] = color;
leds[37] = color;
nexthour = true;
}
else if (minute2 < 48 && minute2 > 42) {
//viertel
leds[26] = color;
leds[27] = color;
leds[28] = color;
leds[29] = color;
leds[30] = color;
leds[31] = color;
leds[32] = color;
//vor
leds[35] = color;
leds[36] = color;
leds[37] = color;
nexthour = true;
}
else if (minute2 < 53 && minute2 > 47) {
//zehn
leds[18] = color;
leds[19] = color;
leds[20] = color;
leds[21] = color;
//vor
leds[35] = color;
leds[36] = color;
leds[37] = color;
nexthour = true;
}
else if (minute2 < 58 && minute2 > 52) {
//fünf
leds[7] = color;
leds[8] = color;
leds[9] = color;
leds[10] = color;
//vor
leds[35] = color;
leds[36] = color;
leds[37] = color;
nexthour = true;
}
else if (minute2 > 57) {
nexthour = true;
einstrue = true;
//uhr
for (int i = 99; i < 102; i++) {
leds[i] = color;
}
}
if (nexthour == true) {
hourled = hour2 + 1;
if (hourled == 13) {
hourled = 1;
}
} else {
hourled = hour2;
}
switch (hourled) {
case 1:
if (!einstrue) {
leds[60] = color;
}
leds[61] = color;
leds[62] = color;
leds[63] = color;
break;
case 2:
leds[62] = color;
leds[63] = color;
leds[64] = color;
leds[65] = color;
break;
case 3:
leds[67] = color;
leds[68] = color;
leds[69] = color;
leds[70] = color;
break;
case 4:
leds[77] = color;
leds[78] = color;
leds[79] = color;
leds[80] = color;
break;
case 5:
leds[73] = color;
leds[74] = color;
leds[75] = color;
leds[76] = color;
break;
case 6:
leds[104] = color;
leds[105] = color;
leds[106] = color;
leds[107] = color;
leds[108] = color;
break;
case 7:
leds[55] = color;
leds[56] = color;
leds[57] = color;
leds[58] = color;
leds[59] = color;
leds[60] = color;
break;
case 8:
leds[89] = color;
leds[90] = color;
leds[91] = color;
leds[92] = color;
break;
case 9:
leds[81] = color;
leds[82] = color;
leds[83] = color;
leds[84] = color;
break;
case 10:
leds[93] = color;
leds[94] = color;
leds[95] = color;
leds[96] = color;
break;
case 11:
leds[85] = color;
leds[86] = color;
leds[87] = color;
break;
case 12:
leds[49] = color;
leds[50] = color;
leds[51] = color;
leds[52] = color;
leds[53] = color;
break;
}
setbasic();
}
void loop() {
button.isClick();
ArduinoOTA.handle();
//forcetimeupdate is true when last updatetime() wasnt successful because of no internet connection
if (forcetimeupdate) {
//update from NTP every minute
if ((millis() - refreshtime) > 60000) {
updatetime();
}
//normally sync time from NTP every 30min
} else {
//update from NTP every 30min
if ((millis() - refreshtime) > 1800000) {
updatetime();
}
}
if (active) {
//get current time
DateTime now = rtc.now();
hour1 = now.hour();
minute1 = now.minute();
second1 = now.second();
/*Serial.print(hour1);
Serial.print(":");
Serial.print(minute1);
Serial.print(":");
Serial.println(second1);*/
//make colors a little bit brighter than normal white
if (diffcolor) {
if ((brg1 + 30) >= 255) {
color = CHSV(minute1 * 4.25, 255, 255);
}
else {
color = CHSV(minute1 * 4.25, 255, brg1 + 30);
}
}
else {
color = CHSV(0, 0, brg1);
}
//fill_solid(leds,NUM_LEDS,CRGB::Black);
//only update when time has changed
if (!(lastminute == minute1)) {
setleds(hour1, minute1);
}
lastminute = minute1;
//update brightness to match surrounding, only update brightness if brightness changed 20 to last change
brightness = analogRead(A0);
brg = map(brightness, 0, 1024, 30, 255); //30 , 1024, 0, 255
if ((brg1 - brg) > 20 || (brg - brg1) > 20) {
if (brg <= 30) {
// FastLED.setBrightness(10);
brg1 = 30;
}
else if (brg >= 255) {
//FastLED.setBrightness(255);
brg1 = 255;
}
else {
// FastLED.setBrightness(brg);
brg1 = brg;
}
//again make colors a little bit brighter than normal white
if (diffcolor) {
if ((brg1 + 30) >= 255) {
color = CHSV(minute1 * 4.25, 255, 255);
}
else {
color = CHSV(minute1 * 4.25, 255, brg1 + 30);
}
}
else {
color = CHSV(0, 0, brg1);
}
setleds(hour1, minute1);
brg1 = brg;
Serial.print("brightness: ");
Serial.print(brg1);
Serial.print(" absolut brg: ");
Serial.println(brightness);
}
}
ArduinoOTA.handle();
delay(50);
}
//Buttonking functions for changing UTC offset
void onLongPress() {
if (utcOffsetInSeconds == 7200) {
Serial.println("Changing UTC Offset to 3600");
utcOffsetInSeconds = 3600;
}
else if (utcOffsetInSeconds = 3600) {
Serial.println("Changing UTC Offset to 7200");
utcOffsetInSeconds = 7200;
}
timeClient.setTimeOffset(utcOffsetInSeconds);
updatetime();
if (active) {
lastminute = 300;
setleds(hour1, minute1);
}
}
//Buttonking functions for checking and displaying wifi connectivity and change between white and dynamic color (color changing to current time, mapped HSV to minutes)
void onDoubleClick() {
if ((WiFi.status() == WL_CONNECTED)) {
Serial.println("Wifi connected");
for (int i = 0; i < 11; i++) { //direction down
for (int j = 0; j < 11; j++) { //fill line
leds[j + 10 * i] = CRGB::Green;
}
if (!((i - 2) < 0)) {
for (int k = 0; k < 11; k++) {
leds[k + 10 * (i - 2)] = CRGB::Black;
}
}
FastLED.show();
delay(50);
}
}
else if (WiFi.status() != WL_CONNECTED) {
Serial.println("Wifi not connected");
for (int i = 0; i < 11; i++) { //direction down
for (int j = 0; j < 11; j++) { //fill line
leds[j + 10 * i] = CRGB::Red;
}
if (!((i - 2) < 0)) {
for (int k = 0; k < 11; k++) {
leds[k + 10 * (i - 2)] = CRGB::Black;
}
}
FastLED.show();
delay(70);
}
}
fill_solid(leds, NUM_LEDS, CRGB::Black);
FastLED.show();
if (diffcolor) {
diffcolor = false;
} else {
diffcolor = true;
}
if (active) {
lastminute = 300;
setleds(hour1, minute1);
}
}
//Buttonking functions for changing active status: toggle LEDs on or off
void onClick() {
Serial.println("Clicked");
if (active) {
active = false;
Serial.println("turning LEDs off");
fill_solid(leds, NUM_LEDS, CRGB::Black);
FastLED.show();
}
else {
active = true;
lastminute = 300;
Serial.println("turning LEDs on");
}
}
I'm not sure if the error is on my end or if it is an error in the NTP library. From the error message from the ESPSaveCrash library I know that it's always happening at about 30min, so it's the first time sync. If anyone knows a better more reliable library for an ESP8266 please let me know. I've got the exception on an NodeMCU V3 and a Wemos D1 Mini.
User contributions licensed under CC BY-SA 3.0