From 19d8bbc0c3cb3d3a6dc23f5ea35ea6e7a870aa02 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Thu, 29 Dec 2016 00:03:58 +0100 Subject: [PATCH] NTP added added more settings updated readme improved boot time --- TODO.txt | 5 +-- readme.md | 14 ++++++- wled00/WS2812FX.cpp | 2 +- wled00/data/settings.htm | 64 ++++++++++++++++++----------- wled00/wled00.ino | 36 +++++++++++++--- wled00/wled01_eeprom.ino | 25 +++++++---- wled00/wled02_xml.ino | 31 ++++++++++++-- wled00/wled03_set.ino | 41 ++++++++++++++++++ wled00/wled05_init.ino | 15 +++---- wled00/wled10_ntp.ino | 89 ++++++++++++++++++++++++++++++++++++++++ 10 files changed, 267 insertions(+), 55 deletions(-) create mode 100644 wled00/wled10_ntp.ino diff --git a/TODO.txt b/TODO.txt index c2419f6..6103b95 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,6 +1,3 @@ -color cycle -other vfx (fire...) -sequence simple slide transition additional color picker field implement HSB slider option @@ -14,6 +11,8 @@ use iframe for all adv. features? /dumpeeprom and /pusheeprom (ota lock!) aux trigger pin randomizer +ir, touch, pin input +Automations clock functions: analog clock on range (dots) diff --git a/readme.md b/readme.md index 8599469..2186d95 100644 --- a/readme.md +++ b/readme.md @@ -1,8 +1,18 @@ WLED is a basic, fast and (relatively) (ok, VERY relatively) secure implementation of a ESP8266 webserver to control Neopixel (WS2812B) leds -Features: +Uses ESP8266 Arduino libraries from 15th August 2016! Untested with newer version! +Contents in the /data directory need to be uploaded to SPIFFS. + +Features: (V0.2) - RGB and brightness sliders - Settings page - configuration over network - Access Point and station mode - automatic failsafe AP -- Edit page. Change html and other files via OTA. Support for ArduinoOTA. +- Edit page. Change html and other files via OTA. +Additions for V0.3 (nearly complete!) +- WS2812FX library integrated for nearly 50 special effects! +- Nightlight function (gradually dims down) +- Notifier function (multiple ESPs sync color via UDP broadcast) +- Support for power pushbutton +- Full OTA software update capability +- Password protected OTA page for added security (OTA lock) diff --git a/wled00/WS2812FX.cpp b/wled00/WS2812FX.cpp index dbb8cd1..583c702 100644 --- a/wled00/WS2812FX.cpp +++ b/wled00/WS2812FX.cpp @@ -1171,7 +1171,7 @@ void WS2812FX::mode_fire_flicker_soft(void) { void WS2812FX::mode_fire_flicker_int(int rev_intensity) { - byte p_r = (_color & 0x00FF0000) >> 16; + byte p_r = (_color & 0x00FF0000) >> 16; byte p_g = (_color & 0x0000FF00) >> 8; byte p_b = (_color & 0x000000FF) >> 0; byte flicker_val = max(p_r,max(p_g, p_b))/rev_intensity; diff --git a/wled00/data/settings.htm b/wled00/data/settings.htm index 29a219f..6e12086 100644 --- a/wled00/data/settings.htm +++ b/wled00/data/settings.htm @@ -32,6 +32,12 @@ document.S_form.APPASS.value = this.responseXML.getElementsByTagName('appass')[0].innerHTML; //fake pass like ****** document.S_form.APCHAN.value = this.responseXML.getElementsByTagName('apchan')[0].innerHTML; document.S_form.DESC.value = this.responseXML.getElementsByTagName('desc')[0].innerHTML; + document.S_form.CLDFR.value = this.responseXML.getElementsByTagName('cldef')[0].innerHTML; + document.S_form.CLDFG.value = this.responseXML.getElementsByTagName('cldef')[1].innerHTML; + document.S_form.CLDFB.value = this.responseXML.getElementsByTagName('cldef')[2].innerHTML; + document.S_form.CLDFA.value = this.responseXML.getElementsByTagName('cldfa')[0].innerHTML; + document.S_form.FXDEF.value = this.responseXML.getElementsByTagName('fxdef')[0].innerHTML; + document.S_form.SXDEF.value = this.responseXML.getElementsByTagName('sxdef')[0].innerHTML; document.S_form.BTNON.checked = (this.responseXML.getElementsByTagName('btnon')[0].innerHTML)!=0?true:false; document.S_form.TFADE.checked = (this.responseXML.getElementsByTagName('tfade')[0].innerHTML)!=0?true:false; document.S_form.TDLAY.value = this.responseXML.getElementsByTagName('tdlay')[0].innerHTML; @@ -44,6 +50,8 @@ document.S_form.NSDIR.checked = (this.responseXML.getElementsByTagName('nsdir')[0].innerHTML)!=0?true:false; document.S_form.NSBTN.checked = (this.responseXML.getElementsByTagName('nsbtn')[0].innerHTML)!=0?true:false; document.S_form.NSFWD.checked = (this.responseXML.getElementsByTagName('nsfwd')[0].innerHTML)!=0?true:false; + document.S_form.NTPON.checked = (this.responseXML.getElementsByTagName('ntpon')[0].innerHTML)!=0?true:false; + document.getElementsByClassName("times")[0].innerHTML = this.responseXML.getElementsByTagName('times')[0].innerHTML; document.S_form.NOOTA.checked = (this.responseXML.getElementsByTagName('noota')[0].innerHTML)!=0?true:false; document.S_form.NORAP.checked = (this.responseXML.getElementsByTagName('norap')[0].innerHTML)!=0?true:false; document.getElementsByClassName("sip")[0].innerHTML = this.responseXML.getElementsByTagName('sip')[0].innerHTML; @@ -75,54 +83,64 @@

WiFi setup

Connect to existing network

- Network SSID (leave empty to not connect):

+ Network SSID (leave empty to not connect):

Network password:

Static IP (leave at 0.0.0.0 for DHCP):
- . - . - . -
+ . + . + . +
Static gateway:
- . - . - . -
+ . + . + . +
Static subnet mask:
- . - . - . -
+ . + . + . +
mDNS address (leave empty for no mDNS):
- http:// .local
+ http:// .local
Client IP: Not connected

Configure Access Point

- AP SSID (leave empty for no AP):

+ AP SSID (leave empty for no AP):

Hide AP SSID:
AP password (leave empty for open):

- AP channel:
+ AP channel:
AP IP: Not active

Application setup

Web setup

- Server description:
+ Server description:

LED setup

- The default boot LED color is the current color when settings are saved.
- Brightness factor: %
+ Default RGB color: + + +
+ Default brightness: (0-255)
+ Default effect ID:
+ Default effect speed:
+ Ignore and use current color, brightness and effects:
+ Brightness factor: %

Button setup

On/Off button enabled:

Transitions

Fade:
- Transition Delay: ms
+ Transition Delay: ms

Timed light

- Target brightness: (0-255)
- Change after: min
+ Target brightness: (0-255)
+ Change after: min
Fade:

Daisy chain

- UDP Port:
+ UDP Port:
Receive notifications:
Send notifications on direct change:
Send notifications on button press:
Send nightlight notifications:
+

Time

+ Get time from NTP server:
+ Current local time is unknown

Security

OTA locked:
Passphrase:
diff --git a/wled00/wled00.ino b/wled00/wled00.ino index b845a07..35701b5 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -8,6 +8,9 @@ #include "WS2812FX.h" #include #include +#include +#include +#include /* * @title WLED project sketch @@ -15,8 +18,15 @@ * @author Christian Schwinne */ //Hardware-settings (only changeble via code) -uint8_t led_amount = 84; +uint8_t led_amount = 9; uint8_t buttonPin = 0; //needs pull-up + +TimeChangeRule CEST = {"CEST", Last, Sun, Mar, 2, 120}; //Central European Summer Time +TimeChangeRule CET = {"CET ", Last, Sun, Oct, 3, 60}; //Central European Standard Time +Timezone CE(CEST, CET); +TimeChangeRule *tcr; //pointer to the time change rule, use to get the TZ abbrev +time_t local; + //Default CONFIG String serverDescription = "WLED 0.3pd"; String clientssid = "Your_Network_Here"; @@ -30,7 +40,8 @@ boolean useap = true; IPAddress staticip(0, 0, 0, 0); IPAddress staticgateway(0, 0, 0, 0); IPAddress staticsubnet(255, 255, 255, 0); -byte col[]{255, 127, 0}; +byte col_s[]{255, 127, 0}; +byte bri_s = 127; uint8_t bri_nl = 0; boolean fadeTransition = true; boolean seqTransition = false; @@ -45,19 +56,24 @@ uint8_t bri_n = 100; uint8_t nightlightDelayMins = 60; boolean nightlightFade = true; uint16_t udpPort = 21324; -uint8_t effectCurrent = 0; -uint8_t effectSpeed = 75; +uint8_t effectDefault = 0; +uint8_t effectSpeedDefault = 75; +boolean ntpEnabled = true; +const char* ntpServerName = "time.nist.gov"; +long ntpRetryMs = 20000; +long ntpResyncMs = 72000000; double transitionResolution = 0.011; //Internal vars +byte col[]{0, 0, 0}; byte col_old[]{0, 0, 0}; byte col_t[]{0, 0, 0}; byte col_it[]{0, 0, 0}; long transitionStartTime; long nightlightStartTime; float tper_last = 0; -byte bri = 127; +byte bri = 0; byte bri_old = 0; byte bri_t = 0; byte bri_it = 0; @@ -68,12 +84,21 @@ boolean nightlightActive = false; boolean nightlightActive_old = false; int transitionDelay_old; int nightlightDelayMs; +uint8_t effectCurrent = 0; +uint8_t effectSpeed = 75; boolean udpConnected = false; byte udpIn[16]; +IPAddress ntpIp; +byte ntpBuffer[48]; +boolean ntpConnected = false; +boolean ntpSyncNeeded = true; +boolean ntpPacketSent = false; +long ntpPacketSentTime, ntpSyncTime; ESP8266WebServer server(80); ESP8266HTTPUpdateServer httpUpdater; WiFiUDP notifierUdp; +WiFiUDP ntpUdp; WS2812FX strip = WS2812FX(led_amount, 2, NEO_GRB + NEO_KHZ800); @@ -111,6 +136,7 @@ void loop() { handleTransitions(); handleNightlight(); handleButton(); + handleNetworkTime(); strip.service(); } diff --git a/wled00/wled01_eeprom.ino b/wled00/wled01_eeprom.ino index fbbf3a2..b0764f1 100644 --- a/wled00/wled01_eeprom.ino +++ b/wled00/wled01_eeprom.ino @@ -56,10 +56,10 @@ void saveSettingsToEEPROM() EEPROM.write(243, staticsubnet[1]); EEPROM.write(244, staticsubnet[2]); EEPROM.write(245, staticsubnet[3]); - EEPROM.write(246, col[0]); - EEPROM.write(247, col[1]); - EEPROM.write(248, col[2]); - EEPROM.write(249, bri); + EEPROM.write(246, col_s[0]); + EEPROM.write(247, col_s[1]); + EEPROM.write(248, col_s[2]); + EEPROM.write(249, bri_s); EEPROM.write(250, receiveNotificationsDefault); EEPROM.write(251, fadeTransition); EEPROM.write(253, (transitionDelay >> 0) & 0xFF); @@ -78,6 +78,12 @@ void saveSettingsToEEPROM() { EEPROM.write(i, serverDescription.charAt(i-292)); } + EEPROM.write(324, effectDefault); + EEPROM.write(325, effectSpeedDefault); + //326 reserved for effectIntensity + EEPROM.write(327, ntpEnabled); + //328 reserved for timezone setting + //329 reserved for dst setting EEPROM.commit(); } @@ -141,10 +147,10 @@ void loadSettingsFromEEPROM() staticsubnet[1] = EEPROM.read(243); staticsubnet[2] = EEPROM.read(244); staticsubnet[3] = EEPROM.read(245); - col[0] = EEPROM.read(246); - col[1] = EEPROM.read(247); - col[2] = EEPROM.read(248); - bri = EEPROM.read(249); + col_s[0] = EEPROM.read(246); col[0] = col_s[0]; + col_s[1] = EEPROM.read(247); col[1] = col_s[1]; + col_s[2] = EEPROM.read(248); col[2] = col_s[2]; + bri_s = EEPROM.read(249); bri = bri_s; receiveNotifications = EEPROM.read(250); receiveNotificationsDefault = receiveNotifications; fadeTransition = EEPROM.read(251); @@ -166,4 +172,7 @@ void loadSettingsFromEEPROM() if (EEPROM.read(i) == 0) break; serverDescription += char(EEPROM.read(i)); } + effectDefault = EEPROM.read(324); effectCurrent = effectDefault; + effectSpeedDefault = EEPROM.read(325); effectSpeed = effectSpeedDefault; + ntpEnabled = EEPROM.read(327); } diff --git a/wled00/wled02_xml.ino b/wled00/wled02_xml.ino index d829cdb..640503d 100644 --- a/wled00/wled02_xml.ino +++ b/wled00/wled02_xml.ino @@ -31,9 +31,9 @@ void XML_response() resp = resp + ""; resp = resp + effectCurrent; resp = resp + ""; - resp = resp + ""; + resp = resp + ""; resp = resp + effectSpeed; - resp = resp + ""; + resp = resp + ""; resp = resp + ""; resp = resp + serverDescription; resp = resp + ""; @@ -94,8 +94,23 @@ void XML_response_settings() resp = resp + apchannel; resp = resp + ""; resp = resp + ""; - resp = resp + serverDescription; - resp = resp + ""; + resp = resp + serverDescription; + resp = resp + ""; + for (int i = 0; i < 3; i++) + { + resp = resp + ""; + resp = resp + col_s[i]; + resp = resp + ""; + } + resp = resp + ""; + resp = resp + bri_s; + resp = resp + ""; + resp = resp + ""; + resp = resp + effectDefault; + resp = resp + ""; + resp = resp + ""; + resp = resp + effectSpeedDefault; + resp = resp + ""; resp = resp + ""; resp = resp + bool2int(buttonEnabled); resp = resp + ""; @@ -126,6 +141,14 @@ void XML_response_settings() resp = resp + ""; resp = resp + bool2int(notifyNightlight); resp = resp + ""; + resp = resp + ""; + resp = resp + bool2int(ntpEnabled); + resp = resp + ""; + Serial.println("pretime"); + resp = resp + ""; + resp = resp + getTimeString(); + resp = resp + ""; + Serial.println("posttime"); resp = resp + ""; resp = resp + bool2int(ota_lock); resp = resp +""; diff --git a/wled00/wled03_set.ino b/wled00/wled03_set.ino index f118ccb..6638fae 100644 --- a/wled00/wled03_set.ino +++ b/wled00/wled03_set.ino @@ -94,6 +94,46 @@ void handleSettingsSet() if (i >= 0 && i <= 255) staticsubnet[3] = i; } if (server.hasArg("DESC")) serverDescription = server.arg("DESC"); + if (server.hasArg("CBEOR")) + { + col_s[0] = col[0]; + col_s[1] = col[1]; + col_s[2] = col[2]; + bri_s = bri; + effectDefault = effectCurrent; + effectSpeedDefault = effectSpeed; + } else { + if (server.hasArg("CLDFR")) + { + int i = server.arg("CLDFR").toInt(); + if (i >= 0 && i <= 255) col_s[0] = i; + } + if (server.hasArg("CLDFG")) + { + int i = server.arg("CLDFG").toInt(); + if (i >= 0 && i <= 255) col_s[1] = i; + } + if (server.hasArg("CLDFB")) + { + int i = server.arg("CLDFB").toInt(); + if (i >= 0 && i <= 255) col_s[2] = i; + } + if (server.hasArg("CLDFA")) + { + int i = server.arg("CLDFA").toInt(); + if (i >= 0 && i <= 255) bri_s = i; + } + if (server.hasArg("FXDEF")) + { + int i = server.arg("FXDEF").toInt(); + if (i >= 0 && i <= 255) effectDefault = i; + } + if (server.hasArg("SXDEF")) + { + int i = server.arg("SXDEF").toInt(); + if (i >= 0 && i <= 255) effectSpeedDefault = i; + } + } buttonEnabled = server.hasArg("BTNON"); fadeTransition = server.hasArg("TFADE"); if (server.hasArg("TDLAY")) @@ -128,6 +168,7 @@ void handleSettingsSet() notifyDirect = server.hasArg("NSDIR"); notifyButton = server.hasArg("NSBTN"); notifyNightlight = server.hasArg("NSFWD"); + ntpEnabled = server.hasArg("NTPON"); if (server.hasArg("OPASS")) { if (!ota_lock) diff --git a/wled00/wled05_init.ino b/wled00/wled05_init.ino index 08fa123..aac65a9 100644 --- a/wled00/wled05_init.ino +++ b/wled00/wled05_init.ino @@ -1,13 +1,6 @@ void wledInit() { Serial.begin(115200); - Serial.println(); - - for(uint8_t t = 4; t > 0; t--) { - Serial.printf("[SETUP] BOOT WAIT %d...\n", t); - Serial.flush(); - delay(1000); - } SPIFFS.begin(); { @@ -59,10 +52,14 @@ void wledInit() } Serial.println("mDNS responder started"); - if (udpPort > 0) + if (udpPort > 0 && udpPort != 123) { udpConnected = notifierUdp.begin(udpPort); } + if (ntpEnabled && !only_ap) + { + ntpConnected = ntpUdp.begin(123); + } //SERVER INIT //settings page @@ -132,7 +129,7 @@ void wledInit() MDNS.addService("http", "tcp", 80); // Initialize NeoPixel Strip strip.init(); - strip.setMode(0); + strip.setMode(effectCurrent); strip.setColor(0); strip.setSpeed(effectSpeed); strip.setBrightness(255); diff --git a/wled00/wled10_ntp.ino b/wled00/wled10_ntp.ino new file mode 100644 index 0000000..a7de08c --- /dev/null +++ b/wled00/wled10_ntp.ino @@ -0,0 +1,89 @@ +void handleNetworkTime() +{ + if (ntpEnabled && ntpConnected) + { + if (ntpSyncNeeded) + { + if (ntpPacketSent) + { + if (getNtpTime()) + { + ntpSyncNeeded = false; + ntpPacketSent = false; + ntpSyncTime = millis(); + Serial.print("Time: "); + Serial.println(now()); + } else + { + if (millis() - ntpPacketSentTime > ntpRetryMs) + { + ntpPacketSent = false; //try new packet + } + } + } else + { + WiFi.hostByName(ntpServerName, ntpIp); + sendNTPpacket(); + ntpPacketSent = true; + ntpPacketSentTime = millis(); + } + } else if (millis() - ntpSyncTime > ntpResyncMs) + { + ntpSyncNeeded = true; + } + } +} + +bool getNtpTime() +{ + int size = ntpUdp.parsePacket(); + if (size >= 48) { + ntpUdp.read(ntpBuffer, 48); // read packet into the buffer + unsigned long secsSince1900; + // convert four bytes starting at location 40 to a long integer + secsSince1900 = (unsigned long)ntpBuffer[40] << 24; + secsSince1900 |= (unsigned long)ntpBuffer[41] << 16; + secsSince1900 |= (unsigned long)ntpBuffer[42] << 8; + secsSince1900 |= (unsigned long)ntpBuffer[43]; + setTime(secsSince1900 - 2208988800UL + (millis() - ntpPacketSentTime)/2000); //naive approach to improve accuracy, utc + return true; + } + return false; //unable to get the time +} + +void sendNTPpacket() +{ + Serial.println("Sending NTP packet"); + memset(ntpBuffer, 0, 48); + ntpBuffer[0] = 0b11100011; // LI, Version, Mode + ntpBuffer[1] = 0; // Stratum, or type of clock + ntpBuffer[2] = 6; // Polling Interval + ntpBuffer[3] = 0xEC; // Peer Clock Precision + ntpBuffer[12] = 49; + ntpBuffer[13] = 0x4E; + ntpBuffer[14] = 49; + ntpBuffer[15] = 52; + ntpUdp.beginPacket(ntpIp, 123); //NTP requests are to port 123 + ntpUdp.write(ntpBuffer, 48); + ntpUdp.endPacket(); +} + +String getTimeString() +{ + local = CE.toLocal(now(), &tcr); + String ret = monthStr(month(local)); + ret = ret + " "; + ret = ret + day(local); + ret = ret + " "; + ret = ret + year(local); + ret = ret + ", "; + ret = ret + hour(local); + ret = ret + ":"; + if (minute(local) < 10) ret = ret + "0"; + ret = ret + minute(local); + ret = ret + ":"; + if (second(local) < 10) ret = ret + "0"; + ret = ret + second(local); + return ret; +} +