Added (highly unstable and experimental) NTP time support

Added timezones library (CET for now, you can easily adapt it to your timezone however)
Added overlays to support both digital and analog clockfaces, basic countdown
Improved serial debug
Included license notes in settings file
Added a bit of guides to readme file

Warning! Using NTP usually results in a complete system crash after 1-48 hours.
Please only enable it if you are willing to experiment with it.
To get a proper WLED experience, make sure the checkbox for NTP is disabled in settings!
This commit is contained in:
cschwinne 2017-01-15 00:24:28 +01:00
parent 7cd7108aa7
commit e9ae7c34c7
9 changed files with 188 additions and 56 deletions

View File

@ -16,3 +16,67 @@ Additions for V0.3 (nearly complete!)
- Support for power pushbutton
- Full OTA software update capability
- Password protected OTA page for added security (OTA lock)
Compile settings:
Board: WeMos D1 mini
CPU frequency: 80 MHz
Flash size : 4MB (1MB settings)
Upload speed: 115200
Quick start guide:
1. Make sure your ESP module has a min. 4MB SPI flash module. (currently working on supporting 1MB modules)
Connect a WS2812B RGB led strip to GPIO2. Optionally connect a NO-pushbutton to GPIO0 (internal pull-up) and ground.
2. Follow a guide to setup your Arduino client (I am using version 1.6.9) with the ESP8266 libraries.
For current compiles I use an old version from 15th August 2016.
3. You will also need the ESP8266 SPIFFS sketch data uploader. (currently working on making this step unnecessary)
4. In file "wled00.ino", change the LED count to the amount you connected. Proceed to flash the sketch and the SPIFFS data.
You should also change the access point and OTA update passphrases for added security (you can change them later, this is just the "factory default").
5. Connect to automatically started WiFi access point "WLED-AP" using default passwort "wled1234". Go to the IP "192.168.4.1".
6. Click on the wrench icon to edit settings like connecting the module to your home WiFi.
7. Have fun with the software!
Advanced module control via HTTP requests:
Base URL scheme: "<moduleip>/ajax_in". This will return a XML file with some current values.
Add one or multiple of the following parameters after the base url to change values:
"&A=<0-255>" set LED brightness (yellow slider)
"&R=<0-255>" set LED red value (red slider)
"&G=<0-255>" set LED green value (green slider)
"&B=<0-255>" set LED blue value (blue slider)
"&FX=<0-47>" set LED effect (refer to WS2812FX library)
"&SX=<0-255>" set LED effect speed (refer to WS2812FX library)
"&NR=<0 or 1>" receive notifications on or off
"&NS=<0 or 1>" send (direct) notifications on or off
"&NL=<0 or 1>" turns nightlight function on or off
("&OL=<0, 1, 3 or 5>" experimental clock overlays)
("&I=<0-255>" experimental individual LED control)
("&I=<0-255>&I2=<0-255>" experimental individual LED range control)
Software update procedure:
Method 1: Reflash the new update source via USB.
Method 2: The software has an integrated OTA software update capability.
First you have to enable it by typing in the correct OTA passphrase (default: "wledota") in the settings menu.
Remove the tick in the checkbox "OTA locked". Then save settings and reboot the ESP.
Now you can go to "<moduleip>/update" to update binary firmware.
To edit flash content (images and HTML), go to "<moduleip>/edit".
After you are done, it is recommended to lock the OTA function again.
To do so, tick the checkbox again (you can change the passphrase by typing in a new one now). Reboot.
If you try to access the update page now, you should see the message "OTA lock active".

View File

@ -139,6 +139,8 @@
Send notifications on button press: <input type="checkbox" name="NSBTN" value="0"> <br>
Send nightlight notifications: <input type="checkbox" name="NSFWD" value="0"> <br>
<h3>Time</h3>
<b>Warning! Using NTP usually results in a complete system crash after 1-48 hours. <br>
Please only enable it if you are willing to experiment with it. </b> <br>
Get time from NTP server: <input type="checkbox" name="NTPON" value="0"> <br>
Current local time is <span class="times">unknown</span> <br>
<h3>Security</h3>
@ -156,7 +158,12 @@
HTTP traffic is not encrypted. An attacker in the same network could intercept form data!<br>
<h3>About</h3>
WLED version 0.3pd <br>
(c) 2016 Christian Schwinne <br>
(c) 2016-2017 Christian Schwinne <br>
<i>Licensed under the MIT license</i> <br>
<i>Uses libraries:</i> <br>
<i>ESP8266 Arduino Core</i> <br>
<i>WS2812FX by kitesurfer1404 (Aircoookie fork)</i> <br>
<i>Timezone library by JChristensen</i> <br>
Server message: <span class="msg"> XML response error! </span>
<br><br>
<input type="submit" name="SUBM" value="Save">

View File

@ -16,15 +16,32 @@
#include <TimeLib.h>
#include <Timezone.h>
//to toggle usb serial debug (un)comment following line
#define DEBUG
#ifdef DEBUG
#define DEBUG_PRINT(x) Serial.print (x)
#define DEBUG_PRINTLN(x) Serial.println (x)
#define DEBUG_PRINTF(x) Serial.printf (x)
#else
#define DEBUG_PRINT(x)
#define DEBUG_PRINTLN(x)
#define DEBUG_PRINTF(x)
#endif
/*
* @title WLED project sketch
* @version 0.3pd
* @author Christian Schwinne
*/
//Hardware-settings (only changeble via code)
uint8_t led_amount = 10;
uint8_t led_amount = 84;
uint8_t buttonPin = 0; //needs pull-up
//AP and OTA default passwords (change them!)
String appass = "wled1234";
String otapass = "wledota";
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 TZ(CEST, CET);
@ -37,7 +54,6 @@ String clientssid = "Your_Network_Here";
String clientpass = "Dummy_Pass";
String cmdns = "led";
String apssid = "WLED-AP";
String appass = "wled1234";
uint8_t apchannel = 1;
uint8_t aphide = 0;
boolean useap = true;
@ -51,7 +67,6 @@ boolean fadeTransition = true;
boolean seqTransition = false;
uint16_t transitionDelay = 1500;
boolean ota_lock = true;
String otapass = "wledota";
boolean only_ap = false;
boolean buttonEnabled = true;
boolean notifyDirect = true, notifyButton = true, notifyNightlight = false, notifyMaster = true;
@ -62,15 +77,15 @@ boolean nightlightFade = true;
uint16_t udpPort = 21324;
uint8_t effectDefault = 0;
uint8_t effectSpeedDefault = 75;
boolean ntpEnabled = true;
boolean ntpEnabled = false;
const char* ntpServerName = "time.nist.gov";
long ntpRetryMs = 12000;
long ntpResyncMs = 72000000;
long ntpRetryMs = 9600;
long ntpResyncMs = 72000000L;
int overlayMin = 0, overlayMax = 9;
int analogClock12pixel = 25;
boolean analogClockSecondsTrail = false;
boolean analogClock5MinuteMarks = true;
boolean nixieClockDisplaySeconds = true;
boolean nixieClockDisplaySeconds = false;
boolean nixieClock12HourFormat = false;
boolean overlayReverse = true;
uint8_t overlaySpeed = 200;
@ -107,7 +122,7 @@ boolean ntpConnected = false;
boolean ntpSyncNeeded = true;
boolean ntpPacketSent = false;
long ntpPacketSentTime, ntpSyncTime;
uint8_t overlayCurrent = 5;
uint8_t overlayCurrent = 0;
long overlayRefreshMs = 200;
long overlayRefreshedTime;
int overlayArr[6];
@ -126,11 +141,17 @@ WS2812FX strip = WS2812FX(led_amount, 2, NEO_GRB + NEO_KHZ800);
File fsUploadFile;
#ifdef DEBUG
int debugIndex = 0;
int lastWifiState = 3;
long wifiStateChangedTime = 0;
#endif
void down()
{
bri_t = 0;
setAllLeds();
Serial.println("MODULE TERMINATED");
DEBUG_PRINTLN("MODULE TERMINATED");
while (1) {delay(1000);}
}
@ -138,7 +159,7 @@ void reset()
{
bri_t = 0;
setAllLeds();
Serial.println("MODULE RESET");
DEBUG_PRINTLN("MODULE RESET");
ESP.reset();
}
@ -161,6 +182,27 @@ void loop() {
handleNetworkTime();
handleOverlays();
strip.service();
//DEBUG
#ifdef DEBUG
debugIndex ++;
if (debugIndex > 99999)
{
debugIndex = 0;
DEBUG_PRINTLN("---MODULE DEBUG INFO---");
DEBUG_PRINT("Runtime: "); DEBUG_PRINTLN(millis());
DEBUG_PRINT("Unix time: "); DEBUG_PRINTLN(now());
DEBUG_PRINT("Wifi state: "); DEBUG_PRINTLN(WiFi.status());
if (WiFi.status() != lastWifiState)
{
wifiStateChangedTime = millis();
}
lastWifiState = WiFi.status();
DEBUG_PRINT("Wifi state: "); DEBUG_PRINTLN(wifiStateChangedTime);
DEBUG_PRINT("NTP sync needed: "); DEBUG_PRINTLN(ntpSyncNeeded);
DEBUG_PRINT("Client IP: "); DEBUG_PRINTLN(WiFi.localIP());
}
#endif
}

View File

@ -48,7 +48,7 @@ void XML_response()
void XML_response_settings()
{
Serial.println("XML settings response");
DEBUG_PRINTLN("XML settings response");
String resp;
resp = resp + "<?xml version = \"1.0\" ?>";
resp = resp + "<vs>";
@ -148,11 +148,11 @@ void XML_response_settings()
resp = resp + "<ntpon>";
resp = resp + bool2int(ntpEnabled);
resp = resp + "</ntpon>";
Serial.println("pretime");
DEBUG_PRINTLN("pretime");
resp = resp + "<times>";
resp = resp + getTimeString();
resp = resp + "</times>";
Serial.println("posttime");
DEBUG_PRINTLN("posttime");
resp = resp + "<noota>";
resp = resp + bool2int(ota_lock);
resp = resp +"</noota>";
@ -188,6 +188,6 @@ void XML_response_settings()
resp = resp + "</sip>";
resp = resp + "<msg>WLED 0.3pd OK</msg>";
resp = resp + "</vs>";
Serial.println(resp);
DEBUG_PRINTLN(resp);
server.send(200, "text/xml", resp);
}

View File

@ -9,7 +9,7 @@ void handleSettingsSet()
{
if (!server.arg("CPASS").indexOf('*') == 0)
{
Serial.println("Setting pass");
DEBUG_PRINTLN("Setting pass");
clientpass = server.arg("CPASS");
}
}

View File

@ -32,7 +32,7 @@ String getContentType(String filename){
}
bool handleFileRead(String path){
Serial.println("handleFileRead: " + path);
DEBUG_PRINTLN("handleFileRead: " + path);
if(path.endsWith("/")) path += "index.htm";
String contentType = getContentType(path);
String pathWithGz = path + ".gz";
@ -53,24 +53,24 @@ void handleFileUpload(){
if(upload.status == UPLOAD_FILE_START){
String filename = upload.filename;
if(!filename.startsWith("/")) filename = "/"+filename;
Serial.print("handleFileUpload Name: "); Serial.println(filename);
DEBUG_PRINT("handleFileUpload Name: "); DEBUG_PRINTLN(filename);
fsUploadFile = SPIFFS.open(filename, "w");
filename = String();
} else if(upload.status == UPLOAD_FILE_WRITE){
//Serial.print("handleFileUpload Data: "); Serial.println(upload.currentSize);
//DEBUG_PRINT("handleFileUpload Data: "); DEBUG_PRINTLN(upload.currentSize);
if(fsUploadFile)
fsUploadFile.write(upload.buf, upload.currentSize);
} else if(upload.status == UPLOAD_FILE_END){
if(fsUploadFile)
fsUploadFile.close();
Serial.print("handleFileUpload Size: "); Serial.println(upload.totalSize);
DEBUG_PRINT("handleFileUpload Size: "); DEBUG_PRINTLN(upload.totalSize);
}
}
void handleFileDelete(){
if(server.args() == 0) return server.send(500, "text/plain", "BAD ARGS");
String path = server.arg(0);
Serial.println("handleFileDelete: " + path);
DEBUG_PRINTLN("handleFileDelete: " + path);
if(path == "/")
return server.send(500, "text/plain", "BAD PATH");
if(!SPIFFS.exists(path))
@ -84,7 +84,7 @@ void handleFileList() {
if(!server.hasArg("dir")) {server.send(500, "text/plain", "BAD ARGS"); return;}
String path = server.arg("dir");
Serial.println("handleFileList: " + path);
DEBUG_PRINTLN("handleFileList: " + path);
Dir dir = SPIFFS.openDir(path);
path = String();
@ -109,7 +109,7 @@ void handleFileCreate(){
if(server.args() == 0)
return server.send(500, "text/plain", "BAD ARGS");
String path = server.arg(0);
Serial.println("handleFileCreate: " + path);
DEBUG_PRINTLN("handleFileCreate: " + path);
if(path == "/")
return server.send(500, "text/plain", "BAD PATH");
if(SPIFFS.exists(path))

View File

@ -12,15 +12,17 @@ void wledInit()
while (dir.next()) {
String fileName = dir.fileName();
size_t fileSize = dir.fileSize();
#ifdef DEBUG
Serial.printf("FS File: %s, size: %s\n", fileName.c_str(), formatBytes(fileSize).c_str());
#endif
}
Serial.printf("\n");
DEBUG_PRINTF("\n");
}
Serial.println("Init EEPROM");
DEBUG_PRINTLN("Init EEPROM");
EEPROM.begin(1024);
loadSettingsFromEEPROM();
Serial.print("CC: SSID: ");
Serial.print(clientssid);
DEBUG_PRINT("CC: SSID: ");
DEBUG_PRINT(clientssid);
WiFi.disconnect(); //close old connections
@ -34,27 +36,27 @@ void wledInit()
if (apssid.length()>0)
{
Serial.print("USING AP");
Serial.println(apssid.length());
DEBUG_PRINT("USING AP");
DEBUG_PRINTLN(apssid.length());
initAP();
} else
{
Serial.println("NO AP");
DEBUG_PRINTLN("NO AP");
WiFi.softAPdisconnect(true);
}
initCon();
Serial.println("");
Serial.print("Connected! IP address: ");
Serial.println(WiFi.localIP());
DEBUG_PRINTLN("");
DEBUG_PRINT("Connected! IP address: ");
DEBUG_PRINTLN(WiFi.localIP());
// Set up mDNS responder:
if (cmdns != NULL && !only_ap && !MDNS.begin(cmdns.c_str())) {
Serial.println("Error setting up MDNS responder!");
DEBUG_PRINTLN("Error setting up MDNS responder!");
down();
}
Serial.println("mDNS responder started");
DEBUG_PRINTLN("mDNS responder started");
if (udpPort > 0 && udpPort != 123)
{
@ -128,7 +130,7 @@ void wledInit()
});
server.begin();
Serial.println("HTTP server started");
DEBUG_PRINTLN("HTTP server started");
// Add service to MDNS
MDNS.addService("http", "tcp", 80);
// Initialize NeoPixel Strip
@ -153,12 +155,12 @@ void initCon()
WiFi.begin(clientssid.c_str(), clientpass.c_str());
while(WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.println("C_NC");
DEBUG_PRINTLN("C_NC");
fail_count++;
if (fail_count > 32)
{
WiFi.disconnect();
Serial.println("Can't connect to network. Opening AP...");
DEBUG_PRINTLN("Can't connect to network. Opening AP...");
String save = apssid;
only_ap = true;
if (apssid.length() <1) apssid = "WLED-AP";

View File

@ -15,8 +15,8 @@ void handleNetworkTime()
ntpSyncNeeded = false;
ntpPacketSent = false;
ntpSyncTime = millis();
Serial.print("Time: ");
Serial.println(now());
DEBUG_PRINT("Time: ");
DEBUG_PRINTLN(now());
} else
{
if (millis() - ntpPacketSentTime > ntpRetryMs)
@ -29,7 +29,7 @@ void handleNetworkTime()
WiFi.hostByName(ntpServerName, ntpIp);
if (ntpIp[0] == 0)
{
Serial.println("DNS f!");
DEBUG_PRINTLN("DNS f!");
ntpIp = ntpBackupIp;
}
sendNTPpacket();
@ -45,9 +45,25 @@ void handleNetworkTime()
bool getNtpTime()
{
int size = ntpUdp.parsePacket();
if (size >= 48) {
if (ntpUdp.parsePacket()) {
ntpUdp.read(ntpBuffer, 48); // read packet into the buffer
#ifdef DEBUG
int i= 0;
while (i < 48)
{
Serial.print(ntpBuffer[i], HEX);
Serial.print(".");
i++;
if ((i % 4) ==0) Serial.println();
}
#endif
if (ntpBuffer[40] == 0 && ntpBuffer[41] == 0 && ntpBuffer[42] == 0 && ntpBuffer[43] == 0)
{
DEBUG_PRINTLN("Bad NTP response!");
return false;
}
unsigned long secsSince1900;
// convert four bytes starting at location 40 to a long integer
secsSince1900 = (unsigned long)ntpBuffer[40] << 24;
@ -63,7 +79,8 @@ bool getNtpTime()
void sendNTPpacket()
{
while (ntpUdp.parsePacket()>0);
Serial.println("Sending NTP packet");
ntpUdp.flush(); //discard old packets
DEBUG_PRINTLN("Sending NTP packet");
memset(ntpBuffer, 0, 48);
ntpBuffer[0] = 0b11100011; // LI, Version, Mode
ntpBuffer[1] = 0; // Stratum, or type of clock

View File

@ -32,7 +32,7 @@ void nixieNumber(int number, int dur)
{
if (nixieClockI < 0)
{
Serial.print(number);
DEBUG_PRINT(number);
int digitCnt = -1;
int digits[4];
digits[3] = number/1000;
@ -51,15 +51,15 @@ void nixieNumber(int number, int dur)
} else { //single digit
digitCnt = 1;
}
Serial.print(" ");
DEBUG_PRINT(" ");
for (int i = 0; i < digitCnt; i++)
{
Serial.print(digits[i]);
DEBUG_PRINT(digits[i]);
overlayArr[digitCnt-1-i] = digits[i];
overlayDur[digitCnt-1-i] = ((dur/4)*3)/digitCnt;
overlayPauseDur[digitCnt-1-i] = 0;
}
Serial.println(" ");
DEBUG_PRINTLN(" ");
for (int i = 1; i < digitCnt; i++)
{
if (overlayArr[i] == overlayArr[i-1])
@ -86,14 +86,14 @@ void nixieNumber(int number, int dur)
}
for (int i = 0; i <6; i++)
{
Serial.print(overlayArr[i]);
Serial.print(" ");
Serial.print(overlayDur[i]);
Serial.print(" ");
Serial.print(overlayPauseDur[i]);
Serial.print(" ");
DEBUG_PRINT(overlayArr[i]);
DEBUG_PRINT(" ");
DEBUG_PRINT(overlayDur[i]);
DEBUG_PRINT(" ");
DEBUG_PRINT(overlayPauseDur[i]);
DEBUG_PRINT(" ");
}
Serial.println(" ");
DEBUG_PRINTLN(" ");
nixieClockI = 0;
} else {
nixieDisplay(overlayArr, overlayDur, overlayPauseDur, 6);
@ -237,7 +237,7 @@ void handleOverlays()
{
nixieDisplay(overlayArr, overlayDur, overlayPauseDur, 6);
}
}
} break;
case 5: {//countdown
if (now() >= countdownTime)
{