Ajout Converter
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
***************************************************************************
|
||||
** Program : Debug.h, part of ESP_tickerExtend
|
||||
**
|
||||
** Copyright (c) 2021 Willem Aandewiel
|
||||
** Met dank aan Erik
|
||||
**
|
||||
** TERMS OF USE: MIT License. See bottom of file.
|
||||
***************************************************************************
|
||||
*/
|
||||
|
||||
/*---- start macro's ------------------------------------------------------------------*/
|
||||
|
||||
#define Debug(...) ({ Serial.print(__VA_ARGS__); \
|
||||
})
|
||||
#define Debugln(...) ({ Serial.println(__VA_ARGS__); \
|
||||
})
|
||||
#define Debugf(...) ({ Serial.printf(__VA_ARGS__); \
|
||||
})
|
||||
|
||||
#define DebugFlush() ({ Serial.flush(); \
|
||||
})
|
||||
|
||||
|
||||
#define DebugT(...) ({ Debug(__VA_ARGS__); \
|
||||
})
|
||||
#define DebugTln(...) ({ Debugln(__VA_ARGS__); \
|
||||
})
|
||||
#define DebugTf(...) ({ Debugf(__VA_ARGS__); \
|
||||
})
|
||||
|
||||
/*---- einde macro's ------------------------------------------------------------------*/
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
* persons to whom the Software is furnished to do so, subject to the
|
||||
* following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
|
||||
* OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
|
||||
* THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
****************************************************************************
|
||||
*/
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
***************************************************************************
|
||||
** Program : ESP_ticker.h, part of ESP_ticker
|
||||
**
|
||||
** Copyright (c) 2021 Willem Aandewiel
|
||||
**
|
||||
** TERMS OF USE: MIT License. See bottom of file.
|
||||
***************************************************************************
|
||||
*/
|
||||
|
||||
#include <FS.h>
|
||||
#include <LittleFS.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266WebServer.h>
|
||||
|
||||
#include "TimeSyncClass.h"
|
||||
|
||||
#include "Debug.h"
|
||||
//aaw#include "networkStuff.h"
|
||||
|
||||
#include <MD_Parola.h> // @ 3.7.3 (was 3.5.5)
|
||||
#include <MD_MAX72xx.h>
|
||||
#include "parola_Fonts_data.h"
|
||||
#include <SPI.h>
|
||||
|
||||
// Define the number of devices we have in the chain and the hardware interface
|
||||
// NOTE: These pin numbers are for ESP8266 hardware SPI and will probably not
|
||||
// work with your hardware and may need to be adapted
|
||||
//#define HARDWARE_TYPE MD_MAX72XX::PAROLA_HW
|
||||
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
|
||||
//#define HARDWARE_TYPE MD_MAX72XX::GENERIC_HW
|
||||
#define MAX_DEVICES 8
|
||||
#define MAX_SPEED 50
|
||||
|
||||
//#define CLK_PIN 14 // or SCK
|
||||
//#define DATA_PIN 13 // or MOSI
|
||||
#define CS_PIN 15 // or SS
|
||||
|
||||
#define SETTINGS_FILE "/settings.ini"
|
||||
#define LOCAL_SIZE 255
|
||||
#define NEWS_SIZE 512
|
||||
#define JSON_BUFF_MAX 255
|
||||
#define MAX_NO_NO_WORDS 20
|
||||
|
||||
|
||||
// HARDWARE SPI
|
||||
MD_Parola P = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
|
||||
|
||||
// WiFi Server object and parameters
|
||||
ESP8266WebServer httpServer(80);
|
||||
|
||||
#include <ESP8266mDNS.h> // part of ESP8266 Core https://github.com/esp8266/Arduino
|
||||
|
||||
// Scrolling parameters
|
||||
|
||||
uint8_t inFX, outFX;
|
||||
textEffect_t effect[] =
|
||||
{
|
||||
PA_PRINT,
|
||||
//PA_SCAN_HORIZ,
|
||||
PA_SCROLL_LEFT,
|
||||
PA_WIPE,
|
||||
PA_SCROLL_UP_LEFT,
|
||||
PA_SCROLL_UP,
|
||||
PA_OPENING_CURSOR,
|
||||
PA_GROW_UP,
|
||||
PA_MESH,
|
||||
PA_SCROLL_UP_RIGHT,
|
||||
//PA_BLINDS,
|
||||
PA_CLOSING,
|
||||
PA_RANDOM,
|
||||
PA_GROW_DOWN,
|
||||
PA_SCAN_VERT,
|
||||
PA_SCROLL_DOWN_LEFT,
|
||||
PA_WIPE_CURSOR,
|
||||
//PA_DISSOLVE,
|
||||
PA_OPENING,
|
||||
PA_CLOSING_CURSOR,
|
||||
PA_SCROLL_DOWN_RIGHT,
|
||||
PA_SCROLL_RIGHT,
|
||||
//PA_SLICE,
|
||||
PA_SCROLL_DOWN,
|
||||
};
|
||||
|
||||
|
||||
bool Verbose = false;
|
||||
char cDate[15], cTime[10];
|
||||
uint32_t nrReboots;
|
||||
// Global message buffers shared by Wifi and Scrolling functions
|
||||
char cMsg[NEWS_SIZE];
|
||||
char tempMessage[LOCAL_SIZE] = "";
|
||||
uint8_t msgType;
|
||||
char actMessage[NEWS_SIZE], timeMsg[20];
|
||||
char onTickerMessage[LOCAL_SIZE] = {};
|
||||
char fileMessage[LOCAL_SIZE];
|
||||
uint8_t newsMsgID = 0;
|
||||
uint8_t localMsgID = 0;
|
||||
int16_t valueLDR, valueIntensity;
|
||||
char fChar[10];
|
||||
String lastReset = "";
|
||||
uint32_t timeTimer = 0;
|
||||
uint32_t ntpTimer = millis() + 30000;
|
||||
uint32_t weerTimer = 0;
|
||||
uint32_t newsapiTimer = 0;
|
||||
uint32_t revisionTimer = 0;
|
||||
String noWords[MAX_NO_NO_WORDS+1];
|
||||
char settingHostname[41];
|
||||
char settingNewsNoWords[LOCAL_SIZE];
|
||||
uint8_t settingLocalMaxMsg, settingTextSpeed, settingMaxIntensity;
|
||||
uint16_t settingLDRlowOffset, settingLDRhighOffset;
|
||||
char settingWeerLiveAUTH[51], settingWeerLiveLocation[51];
|
||||
uint8_t settingWeerLiveInterval;
|
||||
char settingNewsAUTH[51];
|
||||
uint8_t settingNewsInterval, settingNewsMaxMsg;
|
||||
bool LittleFSmounted = false;
|
||||
FSInfo LittleFSinfo;
|
||||
time_t now;
|
||||
struct tm timeinfo;
|
||||
bool timeSynced = false;
|
||||
|
||||
|
||||
TimeSync timeSync;
|
||||
|
||||
const char *weekDayName[] { "Unknown", "Zondag", "Maandag", "Dinsdag", "Woensdag"
|
||||
, "Donderdag", "Vrijdag", "Zaterdag", "Unknown" };
|
||||
const char *flashMode[] { "QIO", "QOUT", "DIO", "DOUT", "Unknown" };
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
* persons to whom the Software is furnished to do so, subject to the
|
||||
* following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
|
||||
* OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
|
||||
* THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
****************************************************************************
|
||||
*/
|
||||
@@ -0,0 +1,528 @@
|
||||
/*
|
||||
***************************************************************************
|
||||
** Program : ESP_ticker (lichtkrant)
|
||||
*/
|
||||
const char* FWversion = "v1.7.3 (04-05-2023)";
|
||||
/*
|
||||
** Copyright (c) 2021 .. 2023 Willem Aandewiel
|
||||
**
|
||||
** TERMS OF USE: MIT License. See bottom of file.
|
||||
***************************************************************************
|
||||
|
||||
Arduino-IDE settings for ESP-12E:
|
||||
|
||||
- Board: "Generic ESP8266 Module" (ALLWAYS!!!!!)
|
||||
- Buildin Led: "2"
|
||||
- Upload Speed: "115200"
|
||||
- CPU Frequency: "80 MHz" (or if you need the speed: 160MHz)
|
||||
- Flash size: "4MB (FS:2M OTA~1019KB)"
|
||||
- Flash mode: "DIO" / "DOUT"
|
||||
- Flash Frequency: "40MHz"
|
||||
- Reset Method: "nodemcu" or something else
|
||||
- Debug port: "Disabled"
|
||||
- Debug Level: "None"
|
||||
- IwIP Variant: "v2 Lower Memory"
|
||||
- VTables: "Flash"
|
||||
- Exceptions: "Legacy (new can return nullptr)"
|
||||
- Erase Flash: "Only Sketch"
|
||||
- Espressif FW: "nonos-sdk 2.2.1+100 (190703)"
|
||||
- SSL Support: "All SSL ciphers (most compatible)"
|
||||
- Port: "ESPticker at <-- IP address -->"
|
||||
|
||||
Arduino ESP8266 core v2.7.+
|
||||
*/
|
||||
|
||||
|
||||
// Use the Parola library to scroll text on the display
|
||||
// IP address for the ESP8266 is displayed on the scrolling display
|
||||
// after startup initialisation and connected to the WiFi network.
|
||||
//
|
||||
// Connections for ESP8266 hardware SPI are:
|
||||
// Vcc 3v3 LED matrices seem to work at 3.3V
|
||||
// GND GND GND
|
||||
// DIN D7 HSPID or HMOSI
|
||||
// CS or LD D8 HSPICS or HCS
|
||||
// CLK D5 CLK or HCLK
|
||||
//
|
||||
// MD_MAX72XX library can be found at https://github.com/MajicDesigns/MD_MAX72XX
|
||||
//
|
||||
|
||||
#define USE_UPDATE_SERVER
|
||||
|
||||
#define _HOSTNAME "ESPticker"
|
||||
#include "ESP_ticker.h"
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
int16_t calculateIntensity()
|
||||
{
|
||||
int a0In = 0;
|
||||
for (int l=0; l<2; l++)
|
||||
{
|
||||
//--- read analog A0
|
||||
a0In+= analogRead(A0);
|
||||
delay(200);
|
||||
}
|
||||
a0In = a0In / 2; //-- smooth things up a bit
|
||||
|
||||
DebugTf("analogRead[%d], ", a0In);
|
||||
//---test if (a0In < settingLDRlowOffset) a0In = settingLDRlowOffset;
|
||||
Debugf(" LDRlowOffset[%d] LDRhighOffset[%d] ", settingLDRlowOffset, settingLDRhighOffset);
|
||||
valueLDR = (valueLDR + a0In) / 2;
|
||||
if (valueLDR < settingLDRlowOffset) valueLDR = settingLDRlowOffset;
|
||||
if (valueLDR > settingLDRhighOffset) valueLDR = settingLDRhighOffset;
|
||||
Debugf(" ==> valueLDR[%d]\r\n", valueLDR);
|
||||
|
||||
//--- map LDR to offset..1024 -> 0..settingMax
|
||||
int intensity = map(valueLDR, settingLDRlowOffset, settingLDRhighOffset, 0, settingMaxIntensity);
|
||||
//DebugTf("map(%d, %d, %d, 0, %d) => [%d]\r\n", valueLDR, settingLDRlowOffset, settingLDRhighOffset
|
||||
// , 0 , settingMaxIntensity);
|
||||
|
||||
return intensity;
|
||||
|
||||
} // calculateIntensity()
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
char *updateTime()
|
||||
{
|
||||
time(&now);
|
||||
snprintf(timeMsg, 20, "%02d : %02d", localtime(&now)->tm_hour, localtime(&now)->tm_min);
|
||||
return timeMsg;
|
||||
|
||||
} // updateTime()
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
bool getTheLocalTime(struct tm *info, uint32_t ms)
|
||||
{
|
||||
//-- getLocalTime() is not implemented in the ArduinoIDE
|
||||
//-- so this is a 'work around' function
|
||||
uint32_t start = millis();
|
||||
time_t now;
|
||||
while((millis()-start) <= ms)
|
||||
{
|
||||
time(&now);
|
||||
localtime_r(&now, info);
|
||||
if(info->tm_year > (2016 - 1900))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
delay(10);
|
||||
}
|
||||
return false;
|
||||
|
||||
} // getTheLocalTime()
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
void splitNewsNoWords(const char *noNo)
|
||||
{
|
||||
DebugTln(noNo);
|
||||
int8_t wc = splitString(String(noNo), ' ', noWords, MAX_NO_NO_WORDS);
|
||||
for(int8_t i=0; i<wc; i++)
|
||||
{
|
||||
noWords[i].trim();
|
||||
if (noWords[i].length() > 1)
|
||||
{
|
||||
noWords[i].toLowerCase();
|
||||
DebugTf("NoNoWord[%d] [%s]\r\n", i, noWords[i].c_str());
|
||||
}
|
||||
}
|
||||
|
||||
} // splitNewsNoWords()
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
bool hasNoNoWord(const char *cIn)
|
||||
{
|
||||
for(int8_t i=0; i<MAX_NO_NO_WORDS; i++)
|
||||
{
|
||||
String sIn = String(cIn);
|
||||
sIn.toLowerCase();
|
||||
int idx = sIn.indexOf(noWords[i]);
|
||||
if ((idx > -1) && (noWords[i].length() > 1)) // yes! it's in there somewhere
|
||||
{
|
||||
DebugTf("found [%s]\r\n", noWords[i].c_str());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
//DebugTln("no NoNo words found!");
|
||||
return false;
|
||||
|
||||
} // hasNoNoWord()
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
void nextNieuwsBericht()
|
||||
{
|
||||
bool breakOut = false;
|
||||
newsMsgID++;
|
||||
if (newsMsgID >= settingNewsMaxMsg) newsMsgID = 0;
|
||||
while (!readFileById("NWS", newsMsgID))
|
||||
{
|
||||
DebugTln("File not found!");
|
||||
newsMsgID++;
|
||||
if (newsMsgID > settingNewsMaxMsg)
|
||||
{
|
||||
newsMsgID = 0;
|
||||
breakOut = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!breakOut)
|
||||
{
|
||||
snprintf(actMessage, NEWS_SIZE, "** %s **", fileMessage);
|
||||
//DebugTf("newsMsgID[%d] %s\r\n", newsMsgID, actMessage);
|
||||
utf8Ascii(actMessage);
|
||||
P.displayScroll(actMessage, PA_LEFT, PA_SCROLL_LEFT, (MAX_SPEED - settingTextSpeed));
|
||||
}
|
||||
|
||||
} // nextNieuwsBericht()
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
void nextLocalBericht()
|
||||
{
|
||||
bool nothingThere = false;
|
||||
|
||||
localMsgID++;
|
||||
if (localMsgID > settingLocalMaxMsg)
|
||||
{
|
||||
localMsgID = 0;
|
||||
nothingThere = true;
|
||||
}
|
||||
while (!readFileById("LCL", localMsgID))
|
||||
{
|
||||
DebugTf("File [/newsFiles/LCL-%03d] not found!\r\n", localMsgID);
|
||||
localMsgID++;
|
||||
if (localMsgID > settingLocalMaxMsg)
|
||||
{
|
||||
DebugTln("Back to LCL-000, exit while-loop");
|
||||
localMsgID = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (nothingThere && (localMsgID == 0))
|
||||
{
|
||||
nothingThere = true;
|
||||
getRevisionData();
|
||||
}
|
||||
else nothingThere = false;
|
||||
|
||||
snprintf(actMessage, LOCAL_SIZE, "** %s **", fileMessage);
|
||||
//DebugTf("localMsgID[%d] %s\r\n", localMsgID, actMessage);
|
||||
utf8Ascii(actMessage);
|
||||
P.displayScroll(actMessage, PA_LEFT, PA_SCROLL_LEFT, (MAX_SPEED - settingTextSpeed));
|
||||
|
||||
if ((millis() - revisionTimer) > 900000)
|
||||
{
|
||||
revisionTimer = millis();
|
||||
getRevisionData();
|
||||
}
|
||||
|
||||
} // nextLocalBericht()
|
||||
|
||||
|
||||
//=====================================================================
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
while(!Serial) { /* wait a bit */ }
|
||||
|
||||
lastReset = ESP.getResetReason();
|
||||
|
||||
DebugTln("\r\n[MD_Parola WiFi Message Display]\r\n");
|
||||
DebugTf("Booting....[%s]\r\n\r\n", String(FWversion).c_str());
|
||||
|
||||
P.begin();
|
||||
P.displayClear();
|
||||
P.displaySuspend(false);
|
||||
P.setIntensity(2);
|
||||
P.displayScroll(actMessage, PA_LEFT, PA_NO_EFFECT, 20);
|
||||
P.setTextEffect(PA_SCROLL_LEFT, PA_NO_EFFECT);
|
||||
do
|
||||
{
|
||||
yield();
|
||||
} while( !P.displayAnimate() );
|
||||
|
||||
actMessage[0] = '\0';
|
||||
|
||||
//================ LittleFS ===========================================
|
||||
if (LittleFS.begin())
|
||||
{
|
||||
DebugTln(F("LittleFS Mount succesfull\r"));
|
||||
LittleFSmounted = true;
|
||||
|
||||
readSettings(true);
|
||||
splitNewsNoWords(settingNewsNoWords);
|
||||
|
||||
if (settingNewsInterval == 0)
|
||||
{
|
||||
removeNewsData();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!LittleFS.exists("/newsFiles/LCL-000"))
|
||||
{
|
||||
char LCL000[100];
|
||||
sprintf(LCL000, "ESP_ticker %s by Willem Aandewiel", String(FWversion).c_str());
|
||||
writeFileById("LCL", 0, LCL000);
|
||||
}
|
||||
if (!LittleFS.exists("/newsFiles/LCL-001"))
|
||||
{
|
||||
char LCL001[100];
|
||||
sprintf(LCL001, "ESP_ticker %s by Willem Aandewiel", String(FWversion).c_str());
|
||||
writeFileById("LCL", 1, LCL001);
|
||||
}
|
||||
writeFileById("NWS", 1, "(c) 2021 Willem Aandewiel");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugTln(F("LittleFS Mount failed\r")); // Serious problem with LittleFS
|
||||
LittleFSmounted = false;
|
||||
}
|
||||
//==========================================================//
|
||||
// writeLastStatus(); // only for firsttime initialization //
|
||||
//==========================================================//
|
||||
readLastStatus(); // place it in actTimestamp
|
||||
|
||||
// attempt to connect to Wifi network:
|
||||
int t = 0;
|
||||
while ((WiFi.status() != WL_CONNECTED) && (t < 25))
|
||||
{
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
t++;
|
||||
}
|
||||
if ( WiFi.status() != WL_CONNECTED) {
|
||||
DebugTln("Attempting to connect to WiFi network\r");
|
||||
sprintf(actMessage, "Connect to AP '%s' and configure WiFi on 192.168.4.1 ", _HOSTNAME);
|
||||
P.setTextEffect(PA_SCROLL_LEFT, PA_NO_EFFECT);
|
||||
do { yield(); } while( !P.displayAnimate() );
|
||||
//P.print(" 192.168.4.1");
|
||||
}
|
||||
// Connect to and initialise WiFi network
|
||||
digitalWrite(LED_BUILTIN, HIGH);
|
||||
startWiFi(_HOSTNAME, 240); // timeout 4 minuten
|
||||
digitalWrite(LED_BUILTIN, LOW);
|
||||
|
||||
startMDNS(settingHostname);
|
||||
|
||||
DebugTln("Get time from NTP");
|
||||
timeSync.setup();
|
||||
timeSync.sync(300);
|
||||
time(&now);
|
||||
if (localtime(&now)->tm_year > 120)
|
||||
{
|
||||
timeSynced = true;
|
||||
Serial.println("Time synchronized with NTP Service");
|
||||
}
|
||||
else
|
||||
{
|
||||
timeSynced = false;
|
||||
Serial.println("Could not synchronize time with NTP Service");
|
||||
}
|
||||
|
||||
time(&now);
|
||||
Serial.println("-------------------------------------------------------------------------------");
|
||||
if (!getTheLocalTime(&timeinfo, 10000))
|
||||
{
|
||||
Debugln("Time : Failed to obtain time!");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debugf( "Time : %04d-%02d-%02d %02d:%02d:%02d\r\n", localtime(&now)->tm_year+1900
|
||||
, localtime(&now)->tm_mon+1
|
||||
, localtime(&now)->tm_mday
|
||||
, localtime(&now)->tm_hour
|
||||
, localtime(&now)->tm_min
|
||||
, localtime(&now)->tm_sec);
|
||||
}
|
||||
|
||||
nrReboots++;
|
||||
writeLastStatus();
|
||||
//writeToLog("=========REBOOT==========================");
|
||||
|
||||
snprintf(cMsg, sizeof(cMsg), "Last reset reason: [%s]", ESP.getResetReason().c_str());
|
||||
DebugTln(cMsg);
|
||||
//writeToLog(cMsg);
|
||||
|
||||
Serial.print("\nGebruik 'telnet ");
|
||||
Serial.print (WiFi.localIP());
|
||||
Serial.println("' voor verdere debugging\r\n");
|
||||
|
||||
//================ Start HTTP Server ================================
|
||||
setupFSexplorer();
|
||||
httpServer.serveStatic("/FSexplorer.png", LittleFS, "/FSexplorer.png");
|
||||
httpServer.on("/", sendIndexPage);
|
||||
httpServer.on("/index", sendIndexPage);
|
||||
httpServer.on("/index.html",sendIndexPage);
|
||||
httpServer.serveStatic("/index.css", LittleFS, "/index.css");
|
||||
httpServer.serveStatic("/index.js", LittleFS, "/index.js");
|
||||
// all other api calls are catched in FSexplorer onNotFounD!
|
||||
httpServer.on("/api", HTTP_GET, processAPI);
|
||||
|
||||
|
||||
httpServer.begin();
|
||||
DebugTln("\nServer started\r");
|
||||
|
||||
// Set up first message as the IP address
|
||||
sprintf(actMessage, "%03d.%03d.%d.%d", WiFi.localIP()[0], WiFi.localIP()[1], WiFi.localIP()[2], WiFi.localIP()[3]);
|
||||
DebugTf("\nAssigned IP[%s]\r\n", actMessage);
|
||||
P.displayScroll(actMessage, PA_LEFT, PA_NO_EFFECT, (MAX_SPEED - settingTextSpeed));
|
||||
P.setTextEffect(PA_SCROLL_LEFT, PA_NO_EFFECT);
|
||||
|
||||
valueIntensity = calculateIntensity(); // read analog input pin 0
|
||||
|
||||
P.setIntensity(valueIntensity);
|
||||
newsMsgID = 0;
|
||||
do { yield(); } while( !P.displayAnimate() );
|
||||
|
||||
P.setFont(ExtASCII);
|
||||
|
||||
inFX = 0;
|
||||
outFX= 0;
|
||||
|
||||
for (int i=0; i<=settingNewsMaxMsg; i++)
|
||||
{
|
||||
writeFileById("NWS", i, "");
|
||||
//DebugTf("readFileById(NWS, %d)\r\n", i);
|
||||
//readFileById("NWS", i);
|
||||
}
|
||||
|
||||
} // setup()
|
||||
|
||||
|
||||
//=====================================================================
|
||||
void loop()
|
||||
{
|
||||
httpServer.handleClient();
|
||||
MDNS.update();
|
||||
yield();
|
||||
|
||||
if ((millis() > weerTimer) && (strlen(settingWeerLiveAUTH) > 5))
|
||||
{
|
||||
weerTimer = millis() + (settingWeerLiveInterval * (300 * 1000)); // Interval in Minutes!
|
||||
if (settingWeerLiveInterval > 0) getWeerLiveData();
|
||||
}
|
||||
|
||||
if ((millis() > newsapiTimer) && (strlen(settingNewsAUTH) > 5))
|
||||
{
|
||||
newsapiTimer = millis() + (settingNewsInterval * (300 * 1000)); // Interval in Minutes!
|
||||
if (settingNewsInterval > 0)
|
||||
{
|
||||
if (!getNewsapiData()) //-- first try ...
|
||||
{
|
||||
delay(100);
|
||||
if (!getNewsapiData()) //-- second try ...
|
||||
{
|
||||
//-- try again in two(2) minutes ...
|
||||
newsapiTimer = millis() + (2 * (60 * 1000)); // Interval in Minutes!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (P.displayAnimate()) // done with animation, ready for next message
|
||||
{
|
||||
yield();
|
||||
msgType++;
|
||||
DebugTf("msgType[%d]\r\n", msgType);
|
||||
time(&now);
|
||||
if (localtime(&now)->tm_year > 120) timeSynced = true;
|
||||
|
||||
|
||||
switch(msgType)
|
||||
{
|
||||
case 1: if (!(millis() > timeTimer)) { DebugTln("Not yet time to display weekday"); return; }
|
||||
if (!timeSynced) { DebugTf("Time not (yet) synced!!\n"); return; }
|
||||
inFX = random(0, ARRAY_SIZE(effect));
|
||||
outFX = random(0, ARRAY_SIZE(effect));
|
||||
snprintf(actMessage, LOCAL_SIZE, weekDayName[localtime(&now)->tm_wday+1]);
|
||||
snprintf(onTickerMessage, 120, "%s", actMessage);
|
||||
DebugT(" ["); Debug(onTickerMessage); Debugln("]");
|
||||
P.displayClear();
|
||||
P.displayText(actMessage, PA_CENTER, (MAX_SPEED - settingTextSpeed), 1000, effect[inFX], effect[outFX]);
|
||||
DebugTf("Animate IN[%d], OUT[%d] %s\r\n", inFX, outFX, actMessage);
|
||||
break;
|
||||
case 2: if (!(millis() > timeTimer)) { DebugTln("Not yet time to display the time"); return; }
|
||||
if (!timeSynced) { DebugTf("Time not (yet) synced!!\n"); return; }
|
||||
timeTimer = millis() + 60000;
|
||||
inFX = random(0, ARRAY_SIZE(effect));
|
||||
outFX = random(0, ARRAY_SIZE(effect));
|
||||
sprintf(actMessage, "%s", updateTime());
|
||||
snprintf(onTickerMessage, 120, "%s", actMessage);
|
||||
DebugT(" ["); Debug(onTickerMessage); Debugln("]");
|
||||
P.displayText(actMessage, PA_CENTER, (MAX_SPEED - settingTextSpeed), 2000, effect[inFX], effect[outFX]);
|
||||
DebugTf("Animate IN[%d], OUT[%d] %s\r\n", inFX, outFX, actMessage);
|
||||
break;
|
||||
case 3: nextLocalBericht();
|
||||
P.setTextEffect(PA_SCROLL_LEFT, PA_NO_EFFECT);
|
||||
break;
|
||||
case 6: nextLocalBericht();
|
||||
P.setTextEffect(PA_SCROLL_LEFT, PA_NO_EFFECT);
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
case 7:
|
||||
case 8: if (settingNewsInterval > 0)
|
||||
nextNieuwsBericht();
|
||||
else nextLocalBericht();
|
||||
P.setTextEffect(PA_SCROLL_LEFT, PA_NO_EFFECT);
|
||||
break;
|
||||
case 9: if (settingWeerLiveInterval > 0)
|
||||
{
|
||||
snprintf(actMessage, LOCAL_SIZE, "** %s **", tempMessage);
|
||||
Debugf("\t[%s]\r\n", actMessage);
|
||||
utf8Ascii(actMessage);
|
||||
}
|
||||
else nextLocalBericht();
|
||||
P.setTextEffect(PA_SCROLL_LEFT, PA_NO_EFFECT);
|
||||
break;
|
||||
case 10: if (settingNewsInterval > 0)
|
||||
nextNieuwsBericht();
|
||||
else nextLocalBericht();
|
||||
break;
|
||||
default: msgType = 0;
|
||||
return;
|
||||
|
||||
} // switch()
|
||||
|
||||
//DebugTln(actMessage);
|
||||
valueIntensity = calculateIntensity(); // read analog input pin 0
|
||||
DebugTf("Intensity set to [%d]\r\n", valueIntensity);
|
||||
P.setIntensity(valueIntensity);
|
||||
// Tell Parola we have a new animation
|
||||
P.displayReset();
|
||||
DebugTln("End of displayAnimate()..");
|
||||
|
||||
} // dislayAnimate()
|
||||
|
||||
|
||||
} // loop()
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
* persons to whom the Software is furnished to do so, subject to the
|
||||
* following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
|
||||
* OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
|
||||
* THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
****************************************************************************
|
||||
*/
|
||||
@@ -0,0 +1,331 @@
|
||||
/*
|
||||
***************************************************************************
|
||||
** Program : FSexplorer
|
||||
** Version : 2.0 10-05-202
|
||||
**
|
||||
** Mostly stolen from https://www.arduinoforum.de/User-Fips
|
||||
** For more information visit: https://fipsok.de
|
||||
** See also https://www.arduinoforum.de/arduino-Thread-LittleFS-DOWNLOAD-UPLOAD-DELETE-Esp8266-NodeMCU
|
||||
**
|
||||
***************************************************************************
|
||||
Copyright (c) 2018 Jens Fleischer. All rights reserved.
|
||||
|
||||
This file is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
This file is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
*******************************************************************
|
||||
** Usage:
|
||||
**
|
||||
** setup()
|
||||
** {
|
||||
** setupFSexplorer();
|
||||
** httpServer.serveStatic("/FSexplorer.png", LittleFS, "/FSexplorer.png");
|
||||
** httpServer.on("/", sendIndexPage);
|
||||
** httpServer.on("/index", sendIndexPage);
|
||||
** httpServer.on("/index.html",sendIndexPage);
|
||||
** httpServer.begin();
|
||||
** }
|
||||
**
|
||||
** loop()
|
||||
** {
|
||||
** httpServer.handleClient();
|
||||
** .
|
||||
** .
|
||||
** }
|
||||
*/
|
||||
|
||||
#define MAX_FILES_IN_LIST 25
|
||||
|
||||
const char Helper[] = R"(
|
||||
<br>You first need to upload these two files:
|
||||
<ul>
|
||||
<li>FSexplorer.html</li>
|
||||
<li>FSexplorer.css</li>
|
||||
</ul>
|
||||
<form method="POST" action="/upload" enctype="multipart/form-data">
|
||||
<input type="file" name="upload">
|
||||
<input type="submit" value="Upload">
|
||||
</form>
|
||||
<br/><b>or</b> you can use the <i>Flash Utility</i> to flash firmware or LittleFS!
|
||||
<form action='/update' method='GET'>
|
||||
<input type='submit' name='SUBMIT' value='Flash Utility'/>
|
||||
</form>
|
||||
)";
|
||||
const char Header[] = "HTTP/1.1 303 OK\r\nLocation:FSexplorer.html\r\nCache-Control: no-cache\r\n";
|
||||
|
||||
//=====================================================================================
|
||||
void setupFSexplorer() // Funktionsaufruf "spiffs();" muss im Setup eingebunden werden
|
||||
{
|
||||
LittleFS.begin();
|
||||
|
||||
if (LittleFS.exists("/FSexplorer.html"))
|
||||
{
|
||||
httpServer.serveStatic("/FSexplorer.html", LittleFS, "/FSexplorer.html");
|
||||
httpServer.serveStatic("/FSexplorer", LittleFS, "/FSexplorer.html");
|
||||
}
|
||||
else
|
||||
{
|
||||
httpServer.send(200, "text/html", Helper); //Upload the FSexplorer.html
|
||||
}
|
||||
httpServer.on("/api/listfiles", APIlistFiles);
|
||||
httpServer.on("/LittleFSformat", formatLittleFS);
|
||||
httpServer.on("/upload", HTTP_POST, []() {}, handleFileUpload);
|
||||
httpServer.on("/ReBoot", reBootESP);
|
||||
httpServer.on("/update", updateFirmware);
|
||||
httpServer.onNotFound([]()
|
||||
{
|
||||
if (Verbose) DebugTf("in 'onNotFound()'!! [%s] => \r\n", String(httpServer.uri()).c_str());
|
||||
if (httpServer.uri().indexOf("/api/") == 0)
|
||||
{
|
||||
if (Verbose) DebugTf("next: processAPI(%s)\r\n", String(httpServer.uri()).c_str());
|
||||
processAPI();
|
||||
}
|
||||
else if (httpServer.uri() == "/")
|
||||
{
|
||||
DebugTln("index requested..");
|
||||
sendIndexPage();
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugTf("next: handleFile(%s)\r\n"
|
||||
, String(httpServer.urlDecode(httpServer.uri())).c_str());
|
||||
if (!handleFile(httpServer.urlDecode(httpServer.uri())))
|
||||
{
|
||||
httpServer.send(404, "text/plain", "FileNotFound\r\n");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
} // setupFSexplorer()
|
||||
|
||||
|
||||
//=====================================================================================
|
||||
void APIlistFiles() // Senden aller Daten an den Client
|
||||
{
|
||||
FSInfo LittleFSinfo;
|
||||
|
||||
typedef struct _fileMeta {
|
||||
char Name[30];
|
||||
int32_t Size;
|
||||
} fileMeta;
|
||||
|
||||
_fileMeta dirMap[MAX_FILES_IN_LIST+1];
|
||||
int fileNr = 0;
|
||||
|
||||
Dir dir = LittleFS.openDir("/"); // List files on LittleFS
|
||||
while (dir.next() && (fileNr < MAX_FILES_IN_LIST))
|
||||
{
|
||||
dirMap[fileNr].Name[0] = '\0';
|
||||
//strncat(dirMap[fileNr].Name, dir.fileName().substring(1).c_str(), 29); // remove leading '/'
|
||||
strncat(dirMap[fileNr].Name, dir.fileName().c_str(), 29);
|
||||
dirMap[fileNr].Size = dir.fileSize();
|
||||
fileNr++;
|
||||
}
|
||||
DebugTf("fileNr[%d], Max[%d]\r\n", fileNr, MAX_FILES_IN_LIST);
|
||||
|
||||
// -- bubble sort dirMap op .Name--
|
||||
for (int8_t y = 0; y < fileNr; y++) {
|
||||
yield();
|
||||
for (int8_t x = y + 1; x < fileNr; x++) {
|
||||
//DebugTf("y[%d], x[%d] => seq[y][%s] / seq[x][%s] ", y, x, dirMap[y].Name, dirMap[x].Name);
|
||||
if (compare(String(dirMap[x].Name), String(dirMap[y].Name)))
|
||||
{
|
||||
//Debug(" !switch!");
|
||||
fileMeta temp = dirMap[y];
|
||||
dirMap[y] = dirMap[x];
|
||||
dirMap[x] = temp;
|
||||
} /* end if */
|
||||
//Debugln();
|
||||
} /* end for */
|
||||
} /* end for */
|
||||
|
||||
if (fileNr >= MAX_FILES_IN_LIST)
|
||||
{
|
||||
fileNr = MAX_FILES_IN_LIST;
|
||||
dirMap[fileNr].Name[0] = '\0';
|
||||
//--- if you change this message you also have to
|
||||
//--- change FSexplorer.html
|
||||
strncat(dirMap[fileNr].Name, "More files not listed ..", 29);
|
||||
dirMap[fileNr].Size = 0;
|
||||
fileNr++;
|
||||
}
|
||||
|
||||
String temp = "[";
|
||||
for (int f=0; f < fileNr; f++)
|
||||
{
|
||||
DebugTf("[%3d] >> [%s]\r\n", f, dirMap[f].Name);
|
||||
if (temp != "[") temp += ",";
|
||||
temp += R"({"name":")" + String(dirMap[f].Name) + R"(","size":")" + formatBytes(dirMap[f].Size) + R"("})";
|
||||
}
|
||||
|
||||
LittleFS.info(LittleFSinfo);
|
||||
temp += R"(,{"usedBytes":")" + formatBytes(LittleFSinfo.usedBytes * 1.05) + R"(",)" + // Berechnet den verwendeten Speicherplatz + 5% Sicherheitsaufschlag
|
||||
R"("totalBytes":")" + formatBytes(LittleFSinfo.totalBytes) + R"(","freeBytes":")" + // Zeigt die Größe des Speichers
|
||||
(LittleFSinfo.totalBytes - (LittleFSinfo.usedBytes * 1.05)) + R"("}])"; // Berechnet den freien Speicherplatz + 5% Sicherheitsaufschlag
|
||||
|
||||
httpServer.send(200, "application/json", temp);
|
||||
|
||||
} // APIlistFiles()
|
||||
|
||||
|
||||
//=====================================================================================
|
||||
bool handleFile(String&& path)
|
||||
{
|
||||
if (httpServer.hasArg("delete"))
|
||||
{
|
||||
DebugTf("Delete -> [%s]\n\r", httpServer.arg("delete").c_str());
|
||||
LittleFS.remove(httpServer.arg("delete")); // Datei löschen
|
||||
httpServer.sendContent(Header);
|
||||
return true;
|
||||
}
|
||||
if (!LittleFS.exists("/FSexplorer.html")) httpServer.send(200, "text/html", Helper); //Upload the FSexplorer.html
|
||||
if (path.endsWith("/")) path += "index.html";
|
||||
return LittleFS.exists(path) ? ({File f = LittleFS.open(path, "r"); httpServer.streamFile(f, contentType(path)); f.close(); true;}) : false;
|
||||
|
||||
} // handleFile()
|
||||
|
||||
|
||||
//=====================================================================================
|
||||
void handleFileUpload()
|
||||
{
|
||||
static File fsUploadFile;
|
||||
HTTPUpload& upload = httpServer.upload();
|
||||
if (upload.status == UPLOAD_FILE_START)
|
||||
{
|
||||
if (upload.filename.length() > 30)
|
||||
{
|
||||
upload.filename = upload.filename.substring(upload.filename.length() - 30, upload.filename.length()); // Dateinamen auf 30 Zeichen kürzen
|
||||
}
|
||||
Debugln("FileUpload Name: " + upload.filename);
|
||||
fsUploadFile = LittleFS.open("/" + httpServer.urlDecode(upload.filename), "w");
|
||||
}
|
||||
else if (upload.status == UPLOAD_FILE_WRITE)
|
||||
{
|
||||
Debugln("FileUpload Data: " + (String)upload.currentSize);
|
||||
if (fsUploadFile)
|
||||
fsUploadFile.write(upload.buf, upload.currentSize);
|
||||
}
|
||||
else if (upload.status == UPLOAD_FILE_END)
|
||||
{
|
||||
if (fsUploadFile)
|
||||
fsUploadFile.close();
|
||||
Debugln("FileUpload Size: " + (String)upload.totalSize);
|
||||
httpServer.sendContent(Header);
|
||||
}
|
||||
|
||||
} // handleFileUpload()
|
||||
|
||||
|
||||
//=====================================================================================
|
||||
void formatLittleFS()
|
||||
{ //Formatiert den Speicher
|
||||
if (!LittleFS.exists("/!format")) return;
|
||||
DebugTln(F("Format LittleFS"));
|
||||
LittleFS.format();
|
||||
httpServer.sendContent(Header);
|
||||
|
||||
} // formatLittleFS()
|
||||
|
||||
//=====================================================================================
|
||||
const String formatBytes(size_t const& bytes)
|
||||
{
|
||||
return (bytes < 1024) ? String(bytes) + " Byte" : (bytes < (1024 * 1024)) ? String(bytes / 1024.0) + " KB" : String(bytes / 1024.0 / 1024.0) + " MB";
|
||||
|
||||
} //formatBytes()
|
||||
|
||||
//=====================================================================================
|
||||
const String &contentType(String& filename)
|
||||
{
|
||||
if (filename.endsWith(".htm") || filename.endsWith(".html")) filename = "text/html";
|
||||
else if (filename.endsWith(".css")) filename = "text/css";
|
||||
else if (filename.endsWith(".js")) filename = "application/javascript";
|
||||
else if (filename.endsWith(".json")) filename = "application/json";
|
||||
else if (filename.endsWith(".png")) filename = "image/png";
|
||||
else if (filename.endsWith(".gif")) filename = "image/gif";
|
||||
else if (filename.endsWith(".jpg")) filename = "image/jpeg";
|
||||
else if (filename.endsWith(".ico")) filename = "image/x-icon";
|
||||
else if (filename.endsWith(".xml")) filename = "text/xml";
|
||||
else if (filename.endsWith(".pdf")) filename = "application/x-pdf";
|
||||
else if (filename.endsWith(".zip")) filename = "application/x-zip";
|
||||
else if (filename.endsWith(".gz")) filename = "application/x-gzip";
|
||||
else filename = "text/plain";
|
||||
return filename;
|
||||
|
||||
} // &contentType()
|
||||
|
||||
//=====================================================================================
|
||||
bool freeSpace(uint16_t const& printsize)
|
||||
{
|
||||
FSInfo LittleFSinfo;
|
||||
LittleFS.info(LittleFSinfo);
|
||||
Debugln(formatBytes(LittleFSinfo.totalBytes - (LittleFSinfo.usedBytes * 1.05)) + " im Spiffs frei");
|
||||
return (LittleFSinfo.totalBytes - (LittleFSinfo.usedBytes * 1.05) > printsize) ? true : false;
|
||||
|
||||
} // freeSpace()
|
||||
|
||||
|
||||
//=====================================================================================
|
||||
void updateFirmware()
|
||||
{
|
||||
DebugTln(F("Redirect to updateIndex .."));
|
||||
doRedirect("wait ... ", 1, "/updateIndex", false);
|
||||
|
||||
} // updateFirmware()
|
||||
|
||||
//=====================================================================================
|
||||
void reBootESP()
|
||||
{
|
||||
DebugTln(F("Redirect and ReBoot .."));
|
||||
doRedirect("Reboot ESP - lichtKrant ..", 60, "/", true);
|
||||
|
||||
} // reBootESP()
|
||||
|
||||
//=====================================================================================
|
||||
void doRedirect(String msg, int wait, const char* URL, bool reboot)
|
||||
{
|
||||
String redirectHTML =
|
||||
"<!DOCTYPE HTML><html lang='en-US'>"
|
||||
"<head>"
|
||||
"<meta charset='UTF-8'>"
|
||||
"<style type='text/css'>"
|
||||
"body {background-color: lightblue;}"
|
||||
"</style>"
|
||||
"<title>Redirect to Main Program</title>"
|
||||
"</head>"
|
||||
"<body><h1>FSexplorer</h1>"
|
||||
"<h3>"+msg+"</h3>"
|
||||
"<br><div style='width: 500px; position: relative; font-size: 25px;'>"
|
||||
" <div style='float: left;'>Redirect over </div>"
|
||||
" <div style='float: left;' id='counter'>"+String(wait)+"</div>"
|
||||
" <div style='float: left;'> seconden ...</div>"
|
||||
" <div style='float: right;'> </div>"
|
||||
"</div>"
|
||||
"<!-- Note: don't tell people to `click` the link, just tell them that it is a link. -->"
|
||||
"<br><br><hr>If you are not redirected automatically, click this <a href='/'>Main Program</a>."
|
||||
" <script>"
|
||||
" setInterval(function() {"
|
||||
" var div = document.querySelector('#counter');"
|
||||
" var count = div.textContent * 1 - 1;"
|
||||
" div.textContent = count;"
|
||||
" if (count <= 0) {"
|
||||
" window.location.replace('"+String(URL)+"'); "
|
||||
" } "
|
||||
" }, 1000); "
|
||||
" </script> "
|
||||
"</body></html>\r\n";
|
||||
|
||||
DebugTln(msg);
|
||||
httpServer.send(200, "text/html", redirectHTML);
|
||||
if (reboot)
|
||||
{
|
||||
delay(5000);
|
||||
ESP.restart();
|
||||
delay(5000);
|
||||
}
|
||||
|
||||
} // doRedirect()
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
** With thanks to Michael
|
||||
*/
|
||||
|
||||
#include "TimeSyncClass.h"
|
||||
|
||||
TimeSync::TimeSync() {}
|
||||
|
||||
/**
|
||||
* Sets up the time synchronization with the specified timezone.
|
||||
*
|
||||
* @param timeZone The timezone to set.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws None
|
||||
*/
|
||||
void TimeSync::setup(const char *timeZone)
|
||||
{
|
||||
configTime(0, 0, "pool.ntp.org", "time.nist.gov", "time.google.com");
|
||||
setenv("TZ", timeZone, 1);
|
||||
tzset();
|
||||
|
||||
Serial.printf("Timezone set to: %s\r\n", timeZone);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the system time is synchronized.
|
||||
*
|
||||
* @return True if the system time is synchronized, false otherwise.
|
||||
*
|
||||
* @throws None
|
||||
*/
|
||||
bool TimeSync::isSynced()
|
||||
{
|
||||
time_t now;
|
||||
time(&now);
|
||||
|
||||
return (bool)(localtime(&now)->tm_year > 120);
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronizes the system time with the NTP service.
|
||||
*
|
||||
* @param maxTry The maximum number of attempts to synchronize the time.
|
||||
*
|
||||
* @return True if the system time is synchronized with the NTP service,
|
||||
* False otherwise.
|
||||
*
|
||||
* @throws None
|
||||
*/
|
||||
bool TimeSync::sync(uint16_t maxTry)
|
||||
{
|
||||
time_t now;
|
||||
time(&now);
|
||||
uint8_t tries = 0;
|
||||
|
||||
Serial.print("Get time from NTP service .");
|
||||
do
|
||||
{
|
||||
time(&now);
|
||||
delay(20);
|
||||
if ((tries%10)==0)
|
||||
{
|
||||
Serial.print(". ");
|
||||
}
|
||||
tries++;
|
||||
} while ((localtime(&now)->tm_year <= 120) && (tries < maxTry));
|
||||
|
||||
if (localtime(&now)->tm_year > 120)
|
||||
Serial.println(" Synchronized with NTP Service!");
|
||||
else Serial.println(" NOT (YET) SYNCED!");
|
||||
|
||||
return localtime(&now)->tm_year > 120;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the current time.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws None
|
||||
*/
|
||||
void TimeSync::logTime()
|
||||
{
|
||||
time_t now;
|
||||
time(&now);
|
||||
struct tm timeinfo;
|
||||
|
||||
if (localtime(&now)->tm_year <= 120)
|
||||
{
|
||||
Serial.println("Failed to obtain time");
|
||||
return;
|
||||
}
|
||||
Serial.printf("%s", asctime(&timeinfo));
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
** See MIT license at the end of this file
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
class TimeSync
|
||||
{
|
||||
public:
|
||||
TimeSync();
|
||||
void setup(const char *timeZone = "CET-1CEST,M3.5.0,M10.5.0/3");
|
||||
bool sync(uint16_t maxTry);
|
||||
bool isSynced();
|
||||
void logTime();
|
||||
|
||||
};
|
||||
|
||||
/**********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Willem Aandewiel (mrWheel)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
**********************************************************************************
|
||||
*/
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
***************************************************************************
|
||||
** Program : FSexplorer.html, part of ESP_ticker
|
||||
** Version : v1.3.1
|
||||
** For more information visit: https://fipsok.de
|
||||
***************************************************************************
|
||||
*/
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
//background-color: #a9a9a9;
|
||||
background-color: lightblue;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
align-items: left;
|
||||
}
|
||||
h1,h2 {
|
||||
color: #e1e1e1;
|
||||
text-shadow: 2px 2px 2px black;
|
||||
}
|
||||
a:link {
|
||||
text-decoration: none;
|
||||
}
|
||||
input {
|
||||
height: 35px;
|
||||
font-size: 13px;
|
||||
}
|
||||
h1+main {
|
||||
display: flex;
|
||||
}
|
||||
section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0.2em;
|
||||
}
|
||||
hr {
|
||||
border: 0;
|
||||
clear:both;
|
||||
display:block;
|
||||
width: 99%;
|
||||
background-color:darkblue;
|
||||
height: 5px;
|
||||
}
|
||||
|
||||
#left {
|
||||
align-items: flex-end;
|
||||
text-shadow: 1px 1px 2px #757474;
|
||||
}
|
||||
.note {
|
||||
background-color: salmon;
|
||||
padding: 0.5em;
|
||||
margin-top: 1em;
|
||||
text-align: center;
|
||||
max-width: 320px;
|
||||
border-radius: 0.5em;
|
||||
}
|
||||
.button {
|
||||
width: 150px;
|
||||
height: 40px;
|
||||
font-size: 14px;
|
||||
margin-top: 1em;
|
||||
cursor: pointer;
|
||||
background-color: lightgray;
|
||||
box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
[type=submit] {
|
||||
height: 40px;
|
||||
min-width: 70px;
|
||||
}
|
||||
[value^=Format] {
|
||||
background-color: #ddd;
|
||||
}
|
||||
[title] {
|
||||
background-color: silver;
|
||||
font-size: 16px;
|
||||
width: 125px;
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
<!--
|
||||
***************************************************************************
|
||||
** Program : FSexplorer.html, part of ESP_ticker
|
||||
** Version : v1.4.0 (02-01-2021)
|
||||
**
|
||||
** Copyright (c) 2021 Willem Aandewiel
|
||||
**
|
||||
** TERMS OF USE: MIT License. See bottom of file.
|
||||
***************************************************************************
|
||||
-->
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
|
||||
<link rel="stylesheet" href="FSexplorer.css">
|
||||
<title>FSexplorer ESP</title>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
let span = document.querySelector('span');
|
||||
let main = document.querySelector('main');
|
||||
let fileSize = document.querySelector('fileSize');
|
||||
let elem = document.querySelectorAll('input');
|
||||
//fetch('json').then(function (response) {
|
||||
fetch('api/listfiles').then(function (response) {
|
||||
return response.json();
|
||||
}).then(function (json) {
|
||||
let dir = '<table width=90%>';
|
||||
for (var i = 0; i < json.length - 1; i++) {
|
||||
dir += "<tr>";
|
||||
if (json[i].name.indexOf("More files not") > -1)
|
||||
{
|
||||
dir += `<td width=20% nowrap>${json[i].name}</td>`;
|
||||
dir += `<td width=10% nowrap></td>`;
|
||||
dir += `<td width=10% nowrap></td>`;
|
||||
}
|
||||
else
|
||||
{
|
||||
dir += `<td width=20% nowrap><a href ="${json[i].name}" target="_blank">${json[i].name}</a></td>`;
|
||||
dir += `<td width=10% nowrap><small>${json[i].size}</small></td>`;
|
||||
dir += `<td width=10% nowrap><a href ="${json[i].name}"download="${json[i].name}"> Download </a></td>`;
|
||||
if ( json[i].name != 'FSexplorer.html' && json[i].name != 'FSexplorer.css' && json[i].name != 'FSexplorer.png'
|
||||
&& json[i].name != 'index.html' && json[i].name != 'index.js' && json[i].name != 'index.css'
|
||||
&& json[i].name != 'settings.png'
|
||||
)
|
||||
{
|
||||
dir += `<td width=10% nowrap><a href ="${json[i].name}?delete=/${json[i].name}"> Delete </a></td>`;
|
||||
if (json[i].name == '!format')
|
||||
{
|
||||
document.getElementById('FormatLittleFS').disabled = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dir += `<td width=10% nowrap> </td>`;
|
||||
}
|
||||
}
|
||||
dir += "<td width=50%> </td></tr>";
|
||||
} // for ..
|
||||
main.insertAdjacentHTML('beforeend', dir);
|
||||
document.querySelectorAll('[href*=delete]').forEach((node) => {
|
||||
node.addEventListener('click', () => {
|
||||
if (!confirm('Weet je zeker dat je dit bestand wilt verwijderen?!')) event.preventDefault();
|
||||
});
|
||||
});
|
||||
main.insertAdjacentHTML('beforeend', '</table>');
|
||||
main.insertAdjacentHTML('beforeend', `<p><b>LittleFS</b> gebruikt ${json[i].usedBytes} van ${json[i].totalBytes}`);
|
||||
free = json[i].freeBytes;
|
||||
fileSize.innerHTML = "<b> </b><p>"; // spacer
|
||||
|
||||
}); // function(json)
|
||||
elem[0].addEventListener('change', () => {
|
||||
let nBytes = elem[0].files[0].size, output = `${nBytes} Byte`;
|
||||
for (var aMultiples = [
|
||||
' KB',
|
||||
' MB'
|
||||
], i = 0, nApprox = nBytes / 1024; nApprox > 1; nApprox /= 1024, i++) {
|
||||
output = nApprox.toFixed(2) + aMultiples[i];
|
||||
}
|
||||
if (nBytes > free) {
|
||||
fileSize.innerHTML = `<p><small> Bestand Grootte: ${output}</small><strong style="color: red;"> niet genoeg ruimte! </strong><p>`;
|
||||
elem[1].setAttribute('disabled', 'disabled');
|
||||
}
|
||||
else {
|
||||
fileSize.innerHTML = `<b>Bestand grootte:</b> ${output}<p>`;
|
||||
elem[1].removeAttribute('disabled');
|
||||
}
|
||||
});
|
||||
//elem[6].addEventListener('click', () => {
|
||||
document.getElementById('FormatLittleFS').addEventListener('click', () => {
|
||||
if (!confirm(`Weet je zeker dat je deze functie wilt uitvoeren?\nAls je doorgaat moet je zelf FSexplorer.html en álle andere systeem bestanden weer uploaden!!.`)) event.preventDefault();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h2>FSexplorer ESP</h2>
|
||||
<div>
|
||||
<main></main>
|
||||
</div>
|
||||
<form action="/upload" method="POST" enctype="multipart/form-data"><input type="file" name="upload">
|
||||
<input type="submit" value="Upload" disabled>
|
||||
</form>
|
||||
<span></span>
|
||||
<fileSize></fileSize>
|
||||
<hr>
|
||||
<div style='width: 40%'>
|
||||
<span>
|
||||
<form style='float: left;' action='/update' method='GET'>
|
||||
<input type='submit' class='button' name='SUBMIT' value='Update Firmware' ENABLED/>
|
||||
</form>
|
||||
<form style='float: right;' action='/'>
|
||||
<input type='submit' class='button' name='SUBMIT' value='Exit FSexplorer'>
|
||||
</form>
|
||||
</span>
|
||||
|
||||
</div>
|
||||
<div style='width: 40%'>
|
||||
<span>
|
||||
<form style='float: left;' action='/ReBoot'>
|
||||
<input type='submit' class='button' name='SUBMIT' value='ReBoot'>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
<div style='width: 40%'>
|
||||
<span>
|
||||
<form style='float right;' action="/LittleFSformat" method="POST">
|
||||
<input id='FormatLittleFS' type='submit' class='button' name='SUBMIT' value='Format LittleFS' DISABLED/>
|
||||
</form>
|
||||
</span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
***************************************************************************
|
||||
** Program : index.css, part of ESP_ticker
|
||||
** Version : v1.3.1
|
||||
**
|
||||
** Copyright (c) 2020 Willem Aandewiel
|
||||
**
|
||||
** TERMS OF USE: MIT License. See bottom of file.
|
||||
***************************************************************************
|
||||
*/
|
||||
* {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
html {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
font-family: 'Dosis', sans-serif;
|
||||
line-height: 1.6;
|
||||
color: black;
|
||||
background: #e6ffff;
|
||||
}
|
||||
|
||||
table {
|
||||
color: black;
|
||||
border-collapse: collapse;
|
||||
width: 90%;
|
||||
background: lightblue;
|
||||
}
|
||||
|
||||
th {
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
white-space: nowrap;
|
||||
border-right: 1px solid #ddd;
|
||||
border-left: 1px solid #ddd;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
td {
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
white-space: nowrap;
|
||||
border-bottom: 1px solid #ddd;
|
||||
border-right: 1px solid #ddd;
|
||||
border-left: 1px solid #ddd;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
tfoot {
|
||||
font-size: 12px;
|
||||
color: darkblue;
|
||||
}
|
||||
|
||||
input {
|
||||
padding-top: 3px;
|
||||
padding-bottom: 2px;
|
||||
font-size: 12pt;
|
||||
}
|
||||
|
||||
.settingDiv {
|
||||
background: lightblue;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.outer-div {
|
||||
/*width: 90%; */
|
||||
text-align: center;
|
||||
background-color: #e6ffff
|
||||
}
|
||||
.inner-div {
|
||||
display: inline-block;
|
||||
/*
|
||||
margin: 0 auto;
|
||||
padding: 3px;
|
||||
background-color: lightblue
|
||||
*/
|
||||
}
|
||||
|
||||
.container-card {
|
||||
padding: 50;
|
||||
margin: 50;
|
||||
list-style: none;
|
||||
flex-direction: row; /*row | row-reverse | column | column-reverse*/
|
||||
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -ms-flexbox;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
|
||||
-webkit-flex-flow: row wrap;
|
||||
justify-content: space-around; /* flex-start | flex-end | center | space-between | space-around | space-evenly */
|
||||
|
||||
}
|
||||
|
||||
.container-box {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
flex-direction: column; /*row | row-reverse | column | column-reverse*/
|
||||
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -ms-flexbox;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
|
||||
-webkit-flex-flow: column wrap;
|
||||
justify-content: flex-start;
|
||||
margin-top: 10px;
|
||||
|
||||
}
|
||||
|
||||
/*--------------------- N A V - B A R -----------------*/
|
||||
.nav-container {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
|
||||
background: #00bffe;
|
||||
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -ms-flexbox;
|
||||
display: -webkit-flex;
|
||||
display: flex-start;
|
||||
-webkit-flex-flow: row wrap;
|
||||
min-height: 24px;
|
||||
|
||||
}
|
||||
|
||||
.nav-img {
|
||||
top: 1px;
|
||||
display: inline-block;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
font-size: 16px;
|
||||
padding: 10px 0;
|
||||
height: 20px;
|
||||
width: 110px;
|
||||
border: none;
|
||||
color: white;
|
||||
|
||||
}
|
||||
|
||||
.nav-left {
|
||||
flex: 2 1 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.nav-right {
|
||||
flex: 1 1 0;
|
||||
text-align: right;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.nav-container a:hover {
|
||||
color: black;
|
||||
background: skyblue; /* only for FSexplorer - Rest does not work */
|
||||
}
|
||||
|
||||
.nav-clock {
|
||||
//top: 1px;
|
||||
color: black;
|
||||
float: right;
|
||||
font-size: small;
|
||||
font-weight:bold;
|
||||
text-align: right;
|
||||
background: white;
|
||||
width: 200px;
|
||||
padding-right: 10px;
|
||||
background: #e6ffff;
|
||||
}
|
||||
|
||||
/*-------------------------*/
|
||||
/***
|
||||
@media all and (max-width: 600px) {
|
||||
.nav-container {
|
||||
justify-content: space-around;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (max-width: 400px) {
|
||||
.nav-container {
|
||||
-webkit-flex-flow: column wrap;
|
||||
flex-flow: column wrap;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
***/
|
||||
|
||||
.tabButton {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.tabButton a:hover {
|
||||
background-color: gray;
|
||||
}
|
||||
|
||||
|
||||
.header h1 span {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
left: 10px;
|
||||
}
|
||||
|
||||
.bottom {
|
||||
position: fixed;
|
||||
font-size: small;
|
||||
color: gray;
|
||||
bottom:0;
|
||||
}
|
||||
.right-0 {right: 0; padding-right: 10px; }
|
||||
.left-0 {left: 0; padding-left: 10px; }
|
||||
|
||||
//--- next 4 are for the API doc page --------
|
||||
.div1 {
|
||||
float: left;
|
||||
margin-left: 20px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.div1 a:link, a:visited {
|
||||
background-color: deepSkyBlue;
|
||||
color: white;
|
||||
padding: 5px 15px;
|
||||
width: 210px;
|
||||
text-align: right;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
}
|
||||
.div1 a:hover, a:active {
|
||||
background-color: deepSkyBlue;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.div2 {
|
||||
margin-left: 280px;
|
||||
margin-top: -52px;
|
||||
}
|
||||
|
||||
|
||||
/* define tag selectors () -------------------------------------------------- */
|
||||
|
||||
button { width: 100px; }
|
||||
|
||||
/*
|
||||
***************************************************************************
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
* persons to whom the Software is furnished to do so, subject to the
|
||||
* following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
|
||||
* OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
|
||||
* THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
***************************************************************************
|
||||
*/
|
||||
@@ -0,0 +1,533 @@
|
||||
/*
|
||||
***************************************************************************
|
||||
** Program : index.js, part of ESP_ticker
|
||||
** Version : v1.6.0 (02-01-2021)
|
||||
**
|
||||
** Copyright (c) 2021 Willem Aandewiel
|
||||
**
|
||||
** TERMS OF USE: MIT License. See bottom of file.
|
||||
***************************************************************************
|
||||
*/
|
||||
const APIGW='http://'+window.location.host+'/api/';
|
||||
|
||||
"use strict";
|
||||
|
||||
let needReload = true;
|
||||
|
||||
window.onload=bootsTrapMain;
|
||||
window.onfocus = function() {
|
||||
if (needReload) {
|
||||
window.location.reload(true);
|
||||
}
|
||||
};
|
||||
|
||||
//============================================================================
|
||||
function bootsTrapMain() {
|
||||
console.log("bootsTrapMain()");
|
||||
document.getElementById('saveMsg').addEventListener('click',function()
|
||||
{saveMessages();});
|
||||
document.getElementById('M_FSexplorer').addEventListener('click',function()
|
||||
{ console.log("newTab: goFSexplorer");
|
||||
location.href = "/FSexplorer";
|
||||
});
|
||||
document.getElementById('S_FSexplorer').addEventListener('click',function()
|
||||
{ console.log("newTab: goFSexplorer");
|
||||
location.href = "/FSexplorer";
|
||||
});
|
||||
document.getElementById('back').addEventListener('click',function()
|
||||
{ console.log("newTab: goBack");
|
||||
location.href = "/";
|
||||
});
|
||||
document.getElementById('Settings').addEventListener('click',function()
|
||||
{settingsPage();});
|
||||
document.getElementById('saveSettings').addEventListener('click',function()
|
||||
{saveSettings();});
|
||||
needReload = false;
|
||||
refreshDevTime();
|
||||
refreshDevInfo();
|
||||
refreshMessages();
|
||||
|
||||
document.getElementById("displayMainPage").style.display = "block";
|
||||
document.getElementById("displaySettingsPage").style.display = "none";
|
||||
|
||||
} // bootsTrapMain()
|
||||
|
||||
function settingsPage()
|
||||
{
|
||||
document.getElementById("displayMainPage").style.display = "none";
|
||||
|
||||
var settingsPage = document.getElementById("settingsPage");
|
||||
refreshSettings();
|
||||
document.getElementById("displaySettingsPage").style.display = "block";
|
||||
|
||||
} // settingsPage()
|
||||
|
||||
|
||||
//============================================================================
|
||||
function refreshDevTime()
|
||||
{
|
||||
//console.log("Refresh api/v0/devtime ..");
|
||||
fetch(APIGW+"v0/devtime")
|
||||
.then(response => response.json())
|
||||
.then(json => {
|
||||
console.log("parsed .., data is ["+ JSON.stringify(json)+"]");
|
||||
for( let i in json.devtime ){
|
||||
if (json.devtime[i].name == "dateTime")
|
||||
{
|
||||
//console.log("Got new time ["+json.devtime[i].value+"]");
|
||||
document.getElementById('theTime').innerHTML = json.devtime[i].value;
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(function(error) {
|
||||
var p = document.createElement('p');
|
||||
p.appendChild(
|
||||
document.createTextNode('Error: ' + error.message)
|
||||
);
|
||||
});
|
||||
|
||||
} // refreshDevTime()
|
||||
|
||||
|
||||
//============================================================================
|
||||
function refreshDevInfo()
|
||||
{
|
||||
document.getElementById('devName').innerHTML = "";
|
||||
fetch(APIGW+"v0/devinfo")
|
||||
.then(response => response.json())
|
||||
.then(json => {
|
||||
//console.log("parsed .., data is ["+ JSON.stringify(json)+"]");
|
||||
data = json.devinfo;
|
||||
for( let i in data )
|
||||
{
|
||||
if (data[i].name == "fwversion")
|
||||
{
|
||||
document.getElementById('devVersion').innerHTML = json.devinfo[i].value;
|
||||
|
||||
} else if (data[i].name == 'hostname')
|
||||
{
|
||||
document.getElementById('devName').innerHTML += data[i].value+" ";
|
||||
|
||||
} else if (data[i].name == 'ipaddress')
|
||||
{
|
||||
document.getElementById('devName').innerHTML += " ("+data[i].value+") ";
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(function(error) {
|
||||
var p = document.createElement('p');
|
||||
p.appendChild(
|
||||
document.createTextNode('Error: ' + error.message)
|
||||
);
|
||||
});
|
||||
} // refreshDevInfo()
|
||||
|
||||
//============================================================================
|
||||
function refreshMessages()
|
||||
{
|
||||
console.log("refreshMessages() ..");
|
||||
data = {};
|
||||
fetch(APIGW+"v0/messages")
|
||||
.then(response => response.json())
|
||||
.then(json => {
|
||||
console.log("then(json => ..)");
|
||||
msg = json.messages;
|
||||
for( let i in msg )
|
||||
{
|
||||
msg[i].value = msg[i].value.replaceAll("@1@", ":");
|
||||
msg[i].value = msg[i].value.replaceAll("@2@", "{");
|
||||
msg[i].value = msg[i].value.replaceAll("@3@", "}");
|
||||
msg[i].value = msg[i].value.replaceAll("@4@", ",");
|
||||
msg[i].value = msg[i].value.replaceAll("@5@", "\\");
|
||||
msg[i].value = msg[i].value.replaceAll("@6@", "\%");
|
||||
|
||||
console.log("["+msg[i].name+"]=>["+msg[i].value+"]");
|
||||
var messages = document.getElementById('mainPage');
|
||||
if( ( document.getElementById("msgR_"+msg[i].name)) == null )
|
||||
{
|
||||
var rowDiv = document.createElement("div");
|
||||
rowDiv.setAttribute("class", "msgDiv");
|
||||
rowDiv.setAttribute("id", "msgR_"+msg[i].name);
|
||||
rowDiv.setAttribute("style", "text-align: right;");
|
||||
rowDiv.style.marginLeft = "10px";
|
||||
rowDiv.style.marginRight = "10px";
|
||||
rowDiv.style.width = "850px";
|
||||
rowDiv.style.border = "thick solid lightblue";
|
||||
rowDiv.style.background = "lightblue";
|
||||
//--- field Name ---
|
||||
var fldDiv = document.createElement("div");
|
||||
fldDiv.setAttribute("style", "margin-right: 10px;");
|
||||
fldDiv.style.width = "30px";
|
||||
fldDiv.style.float = 'left';
|
||||
fldDiv.textContent = msg[i].name;
|
||||
rowDiv.appendChild(fldDiv);
|
||||
//--- input ---
|
||||
var inputDiv = document.createElement("div");
|
||||
inputDiv.setAttribute("style", "text-align: left;");
|
||||
|
||||
var sInput = document.createElement("INPUT");
|
||||
sInput.setAttribute("id", "M_"+msg[i].name);
|
||||
sInput.setAttribute("style", "background: white");
|
||||
|
||||
if (msg[i].type == "s")
|
||||
{
|
||||
sInput.setAttribute("type", "text");
|
||||
sInput.setAttribute("maxlength", msg[i].maxlen);
|
||||
sInput.setAttribute("size", 100);
|
||||
|
||||
sInput.setAttribute("value", msg[i].value);
|
||||
//console.log("addEventListener(M_"+msg[i].name+")");
|
||||
sInput.addEventListener('change',
|
||||
function() { setBackGround("M_"+msg[i].name, "lightgray"); },
|
||||
false
|
||||
);
|
||||
}
|
||||
inputDiv.appendChild(sInput);
|
||||
|
||||
rowDiv.appendChild(inputDiv);
|
||||
messages.appendChild(rowDiv);
|
||||
}
|
||||
else
|
||||
{
|
||||
document.getElementById("M_"+msg[i].name).style.background = "white";
|
||||
document.getElementById("M_"+msg[i].name).value = data[i].value;
|
||||
}
|
||||
}
|
||||
document.getElementById("waiting").innerHTML = "";
|
||||
})
|
||||
.catch(function(error) {
|
||||
var p = document.createElement('p');
|
||||
p.appendChild(
|
||||
document.createTextNode('Error: ' + error.message)
|
||||
);
|
||||
});
|
||||
|
||||
} // refreshMessages()
|
||||
|
||||
|
||||
//============================================================================
|
||||
function refreshSettings()
|
||||
{
|
||||
console.log("refreshSettings() ..");
|
||||
data = {};
|
||||
fetch(APIGW+"v0/settings")
|
||||
.then(response => response.json())
|
||||
.then(json => {
|
||||
console.log("then(json => ..)");
|
||||
data = json.settings;
|
||||
for( let i in data )
|
||||
{
|
||||
console.log("["+data[i].name+"]=>["+data[i].value+"]");
|
||||
var settings = document.getElementById('settingsPage');
|
||||
if( ( document.getElementById("D_"+data[i].name)) == null )
|
||||
{
|
||||
var rowDiv = document.createElement("div");
|
||||
rowDiv.setAttribute("class", "settingDiv");
|
||||
//----rowDiv.setAttribute("id", "settingR_"+data[i].name);
|
||||
rowDiv.setAttribute("id", "D_"+data[i].name);
|
||||
rowDiv.setAttribute("style", "text-align: right;");
|
||||
rowDiv.style.marginLeft = "10px";
|
||||
rowDiv.style.marginRight = "10px";
|
||||
rowDiv.style.width = "800px";
|
||||
rowDiv.style.border = "thick solid lightblue";
|
||||
rowDiv.style.background = "lightblue";
|
||||
//--- field Name ---
|
||||
var fldDiv = document.createElement("div");
|
||||
fldDiv.setAttribute("style", "margin-right: 10px;");
|
||||
fldDiv.style.width = "270px";
|
||||
fldDiv.style.float = 'left';
|
||||
fldDiv.textContent = translateToHuman(data[i].name);
|
||||
rowDiv.appendChild(fldDiv);
|
||||
//--- input ---
|
||||
var inputDiv = document.createElement("div");
|
||||
inputDiv.setAttribute("style", "text-align: left;");
|
||||
|
||||
var sInput = document.createElement("INPUT");
|
||||
//----sInput.setAttribute("id", "setFld_"+data[i].name);
|
||||
sInput.setAttribute("id", data[i].name);
|
||||
|
||||
if (data[i].type == "s")
|
||||
{
|
||||
sInput.setAttribute("type", "text");
|
||||
sInput.setAttribute("maxlength", data[i].maxlen);
|
||||
sInput.setAttribute("size", 50);
|
||||
}
|
||||
else if (data[i].type == "f")
|
||||
{
|
||||
sInput.setAttribute("type", "number");
|
||||
sInput.max = data[i].max;
|
||||
sInput.min = data[i].min;
|
||||
sInput.step = (data[i].min + data[i].max) / 1000;
|
||||
}
|
||||
else if (data[i].type == "i")
|
||||
{
|
||||
sInput.setAttribute("type", "number");
|
||||
sInput.setAttribute("size", 10);
|
||||
sInput.max = data[i].max;
|
||||
sInput.min = data[i].min;
|
||||
//sInput.step = (data[i].min + data[i].max) / 1000;
|
||||
sInput.step = 1;
|
||||
}
|
||||
sInput.setAttribute("value", data[i].value);
|
||||
sInput.addEventListener('change',
|
||||
function() { setBackGround(data[i].name, "lightgray"); },
|
||||
false
|
||||
);
|
||||
inputDiv.appendChild(sInput);
|
||||
|
||||
rowDiv.appendChild(inputDiv);
|
||||
settings.appendChild(rowDiv);
|
||||
}
|
||||
else
|
||||
{
|
||||
//----document.getElementById("setFld_"+data[i].name).style.background = "white";
|
||||
document.getElementById(data[i].name).style.background = "white";
|
||||
//----document.getElementById("setFld_"+data[i].name).value = data[i].value;
|
||||
document.getElementById(data[i].name).value = data[i].value;
|
||||
}
|
||||
}
|
||||
//console.log("-->done..");
|
||||
})
|
||||
.catch(function(error) {
|
||||
var p = document.createElement('p');
|
||||
p.appendChild(
|
||||
document.createTextNode('Error: ' + error.message)
|
||||
);
|
||||
});
|
||||
|
||||
} // refreshSettings()
|
||||
|
||||
|
||||
//============================================================================
|
||||
function saveMessages()
|
||||
{
|
||||
console.log("Saving messages ..");
|
||||
let changes = false;
|
||||
|
||||
//--- has anything changed?
|
||||
var page = document.getElementById("mainPage");
|
||||
var mRow = page.getElementsByTagName("input");
|
||||
//var mRow = document.getElementById("mainPage").getElementsByTagName('div');
|
||||
for(var i = 0; i < mRow.length; i++)
|
||||
{
|
||||
//do something to each div like
|
||||
var msgId = mRow[i].getAttribute("id");
|
||||
var field = msgId.substr(2);
|
||||
//console.log("msgId["+msgId+", msgNr["+field+"]");
|
||||
value = document.getElementById(msgId).value;
|
||||
//console.log("==> name["+field+"], value["+value+"]");
|
||||
|
||||
changes = false;
|
||||
|
||||
if (getBackGround("M_"+field) == "lightgray")
|
||||
{
|
||||
setBackGround("M_"+field, "white");
|
||||
changes = true;
|
||||
}
|
||||
if (changes) {
|
||||
console.log("Changes where made in ["+field+"]["+value+"]");
|
||||
//processWithTimeout([(data.length -1), 0], 2, data, sendPostReading);
|
||||
sendPostMessages(field, value);
|
||||
}
|
||||
}
|
||||
|
||||
} // saveMessages()
|
||||
|
||||
|
||||
//============================================================================
|
||||
function saveSettings()
|
||||
{
|
||||
console.log("saveSettings() ...");
|
||||
let changes = false;
|
||||
|
||||
//--- has anything changed?
|
||||
var page = document.getElementById("settingsPage");
|
||||
var mRow = page.getElementsByTagName("input");
|
||||
//var mRow = document.getElementById("mainPage").getElementsByTagName('div');
|
||||
for(var i = 0; i < mRow.length; i++)
|
||||
{
|
||||
//do something to each div like
|
||||
var msgId = mRow[i].getAttribute("id");
|
||||
var field = msgId;
|
||||
//console.log("msgId["+msgId+", msgNr["+field+"]");
|
||||
value = document.getElementById(msgId).value;
|
||||
//console.log("==> name["+field+"], value["+value+"]");
|
||||
|
||||
changes = false;
|
||||
|
||||
if (getBackGround(field) == "lightgray")
|
||||
{
|
||||
setBackGround(field, "white");
|
||||
changes = true;
|
||||
}
|
||||
if (changes) {
|
||||
console.log("Changes where made in ["+field+"]["+value+"]");
|
||||
var value = value.replace(/,/g, " ");
|
||||
value = value.replace(/ /g, " ");
|
||||
value = value.replace(/ /g, " ");
|
||||
value = value.replace(/ /g, " ");
|
||||
//processWithTimeout([(data.length -1), 0], 2, data, sendPostReading);
|
||||
sendPostSetting(field, value);
|
||||
}
|
||||
}
|
||||
|
||||
} // saveSettings()
|
||||
|
||||
|
||||
//============================================================================
|
||||
function sendPostMessages(field, value)
|
||||
{
|
||||
//console.log("sendPostMessages(value): "+value+"]");
|
||||
value = value.replaceAll("\"", "'");
|
||||
value = value.replaceAll(":", "@1@");
|
||||
value = value.replaceAll("{", "@2@");
|
||||
value = value.replaceAll("}", "@3@");
|
||||
value = value.replaceAll(",", "@4@");
|
||||
value = value.replaceAll("\\", "@5@");
|
||||
value = value.replaceAll("\%", "@6@");
|
||||
console.log("sendPostMessages(value): "+value+"]");
|
||||
|
||||
const jsonString = {"name" : field, "value" : value};
|
||||
console.log("sendPostMessages(): "+JSON.stringify(jsonString));
|
||||
const other_params = {
|
||||
headers : { "content-type" : "application/json; charset=UTF-8"},
|
||||
body : JSON.stringify(jsonString),
|
||||
method : "POST",
|
||||
mode : "cors"
|
||||
};
|
||||
|
||||
fetch(APIGW+"v0/messages", other_params)
|
||||
.then(function(response) {
|
||||
}, function(error) {
|
||||
console.log("Error["+error.message+"]"); //=> String
|
||||
});
|
||||
|
||||
} // sendPostMessages()
|
||||
|
||||
|
||||
//============================================================================
|
||||
function sendPostSetting(field, value)
|
||||
{
|
||||
const jsonString = {"name" : field, "value" : value};
|
||||
console.log("sending: "+JSON.stringify(jsonString));
|
||||
const other_params = {
|
||||
headers : { "content-type" : "application/json; charset=UTF-8"},
|
||||
body : JSON.stringify(jsonString),
|
||||
method : "POST",
|
||||
mode : "cors"
|
||||
};
|
||||
|
||||
fetch(APIGW+"v0/settings", other_params)
|
||||
.then(function(response) {
|
||||
//console.log(response.status ); //=> number 100–599
|
||||
//console.log(response.statusText); //=> String
|
||||
//console.log(response.headers); //=> Headers
|
||||
//console.log(response.url); //=> String
|
||||
//console.log(response.text());
|
||||
//return response.text()
|
||||
}, function(error) {
|
||||
console.log("Error["+error.message+"]"); //=> String
|
||||
});
|
||||
|
||||
} // sendPostSetting()
|
||||
|
||||
|
||||
//============================================================================
|
||||
function translateToHuman(longName) {
|
||||
//for(var index = 0; index < (translateFields.length -1); index++)
|
||||
for(var index = 0; index < translateFields.length; index++)
|
||||
{
|
||||
if (translateFields[index][0] == longName)
|
||||
{
|
||||
return translateFields[index][1];
|
||||
}
|
||||
};
|
||||
return longName;
|
||||
|
||||
} // translateToHuman()
|
||||
|
||||
|
||||
|
||||
//============================================================================
|
||||
function setBackGround(field, newColor) {
|
||||
//console.log("setBackground("+field+", "+newColor+")");
|
||||
document.getElementById(field).style.background = newColor;
|
||||
|
||||
} // setBackGround()
|
||||
|
||||
|
||||
//============================================================================
|
||||
function getBackGround(field) {
|
||||
//console.log("getBackground("+field+")");
|
||||
return document.getElementById(field).style.background;
|
||||
|
||||
} // getBackGround()
|
||||
|
||||
|
||||
//============================================================================
|
||||
function round(value, precision) {
|
||||
var multiplier = Math.pow(10, precision || 0);
|
||||
return Math.round(value * multiplier) / multiplier;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
function printAllVals(obj) {
|
||||
for (let k in obj) {
|
||||
if (typeof obj[k] === "object") {
|
||||
printAllVals(obj[k])
|
||||
} else {
|
||||
// base case, stop recurring
|
||||
console.log(obj[k]);
|
||||
}
|
||||
}
|
||||
} // printAllVals()
|
||||
|
||||
|
||||
var translateFields = [
|
||||
[ "author", "Auteur" ]
|
||||
,[ "localMaxMsg", "max. aantal boodschappen" ]
|
||||
,[ "textSpeed", "scroll snelheid" ]
|
||||
,[ "maxIntensity", "max. Intensiteit leds" ]
|
||||
,[ "LDRlowOffset", "LDR min. waarde" ]
|
||||
,[ "LDRhighOffset", "LDR max. waarde" ]
|
||||
,[ "weerliveAUTH", "weerlive.nl auth. token" ]
|
||||
,[ "weerliveLocation", "weerlive.nl locatie" ]
|
||||
,[ "weerliveInterval", "weerlive.nl refresh interval in minuten" ]
|
||||
,[ "newsapiAUTH", "newsapi.org auth. token" ]
|
||||
,[ "newsapiMaxMsg", "newsapi.nl max. items" ]
|
||||
,[ "newsapiInterval", "newapi.nl refresh interval in minuten" ]
|
||||
,[ "newsNoWords", "skip items met deze woorden" ]
|
||||
];
|
||||
|
||||
//============================================================================
|
||||
String.prototype.replaceAll = function(str1, str2, ignore)
|
||||
{
|
||||
return this.replace(new RegExp(str1.replace(/([\/\,\!\\\^\$\{\}\[\]\(\)\.\*\+\?\|\<\>\-\&])/g,"\\$&"),(ignore?"gi":"g")),(typeof(str2)=="string")?str2.replace(/\$/g,"$$$$"):str2);
|
||||
//return this.replace(new RegExp(str1.replace(/([\/\,\!\\\^\$\[\]\(\)\.\*\+\?\|\<\>\-\&])/g,"\\$&"),(ignore?"gi":"g")),(typeof(str2)=="string")?str2.replace(/\$/g,"$$$$"):str2);
|
||||
}
|
||||
|
||||
/*
|
||||
***************************************************************************
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
* persons to whom the Software is furnished to do so, subject to the
|
||||
* following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
|
||||
* OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
|
||||
* THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
***************************************************************************
|
||||
*/
|
||||
@@ -0,0 +1 @@
|
||||
Dit is een dummy local news file!
|
||||
@@ -0,0 +1 @@
|
||||
Dit is een dummy news file!
|
||||
@@ -0,0 +1,13 @@
|
||||
Hostname = ESPticker
|
||||
localMaxMsg = 5
|
||||
textSpeed = 25
|
||||
maxIntensity = 6
|
||||
LDRlowOffset = 70
|
||||
LDRhighOffset = 700
|
||||
weerLiveAUTH =
|
||||
weerLiveLocatie = Amsterdam
|
||||
weerLiveInterval = 15
|
||||
newsAUTH =
|
||||
newsNoWords = Voetbal show UEFA KNVB vannederland boulevard voetbalzone
|
||||
newsMaxMsg = 4
|
||||
newsInterval = 15
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,537 @@
|
||||
/*
|
||||
***************************************************************************
|
||||
** Program : helperStuff, part of ESP_ticker
|
||||
**
|
||||
** Copyright (c) 2021 Willem Aandewiel
|
||||
**
|
||||
** TERMS OF USE: MIT License. See bottom of file.
|
||||
***************************************************************************
|
||||
*/
|
||||
|
||||
#include <IPAddress.h>
|
||||
|
||||
//===========================================================================================
|
||||
bool compare(String x, String y)
|
||||
{
|
||||
for (int i = 0; i < min(x.length(), y.length()); i++) {
|
||||
if (x[i] != y[i])
|
||||
{
|
||||
return (bool)(x[i] < y[i]);
|
||||
}
|
||||
}
|
||||
return x.length() < y.length();
|
||||
|
||||
} // compare()
|
||||
|
||||
|
||||
//===========================================================================================
|
||||
boolean isValidIP(IPAddress ip)
|
||||
{
|
||||
/* Works as follows:
|
||||
* example:
|
||||
* 127.0.0.1
|
||||
* 1 => 127||0||0||1 = 128>0 = true
|
||||
* 2 => !(false || false) = true
|
||||
* 3 => !(false || false || false || false ) = true
|
||||
* 4 => !(true && true && true && true) = false
|
||||
* 5 => !(false) = true
|
||||
* true && true & true && false && true = false ==> correct, this is an invalid addres
|
||||
*
|
||||
* 0.0.0.0
|
||||
* 1 => 0||0||0||0 = 0>0 = false
|
||||
* 2 => !(true || true) = false
|
||||
* 3 => !(false || false || false || false) = true
|
||||
* 4 => !(true && true && true && tfalse) = true
|
||||
* 5 => !(false) = true
|
||||
* false && false && true && true && true = false ==> correct, this is an invalid addres
|
||||
*
|
||||
* 192.168.0.1
|
||||
* 1 => 192||168||0||1 =233>0 = true
|
||||
* 2 => !(false || false) = true
|
||||
* 3 +> !(false || false || false || false) = true
|
||||
* 4 => !(false && false && true && true) = true
|
||||
* 5 => !(false) = true
|
||||
* true & true & true && true && true = true ==> correct, this is a valid address
|
||||
*
|
||||
* 255.255.255.255
|
||||
* 1 => 255||255||255||255 =255>0 = true
|
||||
* 2 => !(false || false ) = true
|
||||
* 3 +> !(true || true || true || true) = false
|
||||
* 4 => !(false && false && false && false) = true
|
||||
* 5 => !(true) = false
|
||||
* true && true && false && true && false = false ==> correct, this is an invalid address
|
||||
*
|
||||
* 0.123.12.1 => true && false && true && true && true = false ==> correct, this is an invalid address
|
||||
* 10.0.0.0 => true && false && true && true && true = false ==> correct, this is an invalid address
|
||||
* 10.255.0.1 => true && true && false && true && true = false ==> correct, this is an invalid address
|
||||
* 150.150.255.150 => true && true && false && true && true = false ==> correct, this is an invalid address
|
||||
*
|
||||
* 123.21.1.99 => true && true && true && true && true = true ==> correct, this is annvalid address
|
||||
* 1.1.1.1 => true && true && true && true && true = true ==> correct, this is annvalid address
|
||||
*
|
||||
* Some references on valid ip addresses:
|
||||
* - https://www.quora.com/How-do-you-identify-an-invalid-IP-address
|
||||
*
|
||||
*/
|
||||
boolean _isValidIP = false;
|
||||
_isValidIP = ((ip[0] || ip[1] || ip[2] || ip[3])>0); // if any bits are set, then it is not 0.0.0.0
|
||||
_isValidIP &= !((ip[0]==0) || (ip[3]==0)); // if either the first or last is a 0, then it is invalid
|
||||
_isValidIP &= !((ip[0]==255) || (ip[1]==255) || (ip[2]==255) || (ip[3]==255)) ; // if any of the octets is 255, then it is invalid
|
||||
_isValidIP &= !(ip[0]==127 && ip[1]==0 && ip[2]==0 && ip[3]==1); // if not 127.0.0.0 then it might be valid
|
||||
_isValidIP &= !(ip[0]>=224); // if ip[0] >=224 then reserved space
|
||||
|
||||
DebugTf( "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
|
||||
if (_isValidIP)
|
||||
Debugln(F(" = Valid IP"));
|
||||
else
|
||||
Debugln(F(" = Invalid IP!"));
|
||||
|
||||
return _isValidIP;
|
||||
|
||||
} // isValidIP()
|
||||
|
||||
|
||||
//===========================================================================================
|
||||
bool isNumericp(const char *timeStamp, int8_t len)
|
||||
{
|
||||
for (int i=0; (i<len && i<12);i++)
|
||||
{
|
||||
if (timeStamp[i] < '0' || timeStamp[i] > '9')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
} // isNumericp()
|
||||
|
||||
|
||||
//===========================================================================================
|
||||
int8_t splitString(String inStrng, char delimiter, String wOut[], uint8_t maxWords)
|
||||
{
|
||||
int16_t inxS = 0, inxE = 0, wordCount = 0;
|
||||
|
||||
inStrng.trim();
|
||||
while(inxE < inStrng.length() && wordCount < maxWords)
|
||||
{
|
||||
inxE = inStrng.indexOf(delimiter, inxS); //finds location of first ,
|
||||
wOut[wordCount] = inStrng.substring(inxS, inxE); //captures first data String
|
||||
wOut[wordCount].trim();
|
||||
//DebugTf("[%d] => [%c] @[%d] found[%s]\r\n", wordCount, delimiter, inxE, wOut[wordCount].c_str());
|
||||
inxS = inxE;
|
||||
inxS++;
|
||||
wordCount++;
|
||||
}
|
||||
// zero rest of the words
|
||||
for(int i=wordCount; i< maxWords; i++)
|
||||
{
|
||||
wOut[wordCount][0] = 0;
|
||||
}
|
||||
// if not whole string processed place rest in last word
|
||||
if (inxS < inStrng.length())
|
||||
{
|
||||
wOut[maxWords-1] = inStrng.substring(inxS, inStrng.length()); // store rest of String
|
||||
}
|
||||
|
||||
return wordCount;
|
||||
|
||||
} // splitString()
|
||||
|
||||
|
||||
|
||||
//===========================================================================================
|
||||
void strConcat(char *dest, int maxLen, const char *src)
|
||||
{
|
||||
if (strlen(dest) + strlen(src) < maxLen)
|
||||
{
|
||||
strcat(dest, src);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugTf("Combined string > %d chars\r\n", maxLen);
|
||||
}
|
||||
|
||||
} // strConcat()
|
||||
|
||||
|
||||
//===========================================================================================
|
||||
void strConcat(char *dest, int maxLen, float v, int dec)
|
||||
{
|
||||
static char buff[25];
|
||||
if (dec == 0) sprintf(buff,"%.0f", v);
|
||||
else if (dec == 1) sprintf(buff,"%.1f", v);
|
||||
else if (dec == 2) sprintf(buff,"%.2f", v);
|
||||
else if (dec == 3) sprintf(buff,"%.3f", v);
|
||||
else if (dec == 4) sprintf(buff,"%.4f", v);
|
||||
else if (dec == 5) sprintf(buff,"%.5f", v);
|
||||
else sprintf(buff,"%f", v);
|
||||
|
||||
if (strlen(dest) + strlen(buff) < maxLen)
|
||||
{
|
||||
strcat(dest, buff);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugTf("Combined string > %d chars\r\n", maxLen);
|
||||
}
|
||||
|
||||
} // strConcat()
|
||||
|
||||
|
||||
//===========================================================================================
|
||||
void strConcat(char *dest, int maxLen, int v)
|
||||
{
|
||||
static char buff[25];
|
||||
sprintf(buff,"%d", v);
|
||||
|
||||
if (strlen(dest) + strlen(buff) < maxLen)
|
||||
{
|
||||
strcat(dest, buff);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugTf("Combined string > %d chars\r\n", maxLen);
|
||||
}
|
||||
|
||||
} // strConcat()
|
||||
|
||||
|
||||
//===========================================================================================
|
||||
void strToLower(char *src)
|
||||
{
|
||||
for (int i = 0; i < strlen(src); i++)
|
||||
{
|
||||
if (src[i] == '\0') return;
|
||||
if (src[i] >= 'A' && src[i] <= 'Z')
|
||||
src[i] += 32;
|
||||
}
|
||||
} // strToLower()
|
||||
|
||||
//===========================================================================================
|
||||
// a 'save' string copy
|
||||
void strCopy(char *dest, int maxLen, const char *src, int frm, int to)
|
||||
{
|
||||
int d=0;
|
||||
//DebugTf("dest[%s], src[%s] max[%d], frm[%d], to[%d] =>\r\n", dest, src, maxLen, frm, to);
|
||||
dest[0] = '\0';
|
||||
for (int i=0; i<=frm; i++)
|
||||
{
|
||||
if (src[i] == 0) return;
|
||||
}
|
||||
for (int i=frm; (src[i] != 0 && i<=to && d<maxLen); i++)
|
||||
{
|
||||
dest[d++] = src[i];
|
||||
}
|
||||
dest[d] = '\0';
|
||||
|
||||
} // strCopy()
|
||||
|
||||
//===========================================================================================
|
||||
// a 'save' version of strncpy() that does not put a '\0' at
|
||||
// the end of dest if src >= maxLen!
|
||||
void strCopy(char *dest, int maxLen, const char *src)
|
||||
{
|
||||
dest[0] = '\0';
|
||||
strcat(dest, src);
|
||||
|
||||
} // strCopy()
|
||||
|
||||
//===========================================================================================
|
||||
// 'tttABCtDEtFGHttt' => 'ABCtDEtFGHttt'
|
||||
void strLTrim(char *dest, int maxLen, const char tChar )
|
||||
{
|
||||
char tmp[maxLen];
|
||||
int tPos = 0;
|
||||
bool done = false;
|
||||
|
||||
tmp[0] = '\0';
|
||||
|
||||
for (int dPos=0; (dPos<maxLen && dest[dPos] != '\0'); dPos++)
|
||||
{
|
||||
if (dest[dPos] != tChar || done)
|
||||
{
|
||||
tmp[tPos++] = dest[dPos];
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
tmp[tPos] = '\0';
|
||||
strCopy(dest, maxLen, tmp);
|
||||
|
||||
} // strLTrim()
|
||||
|
||||
//===========================================================================================
|
||||
// 'tttABCtDEtFGHttt' => 'tttABCtDEtFGH'
|
||||
void strRTrim(char *dest, int maxLen, const char tChar )
|
||||
{
|
||||
char tmp[maxLen];
|
||||
int dPos, tPos, dMax;
|
||||
bool done = false;
|
||||
|
||||
for(dMax=0; dMax<maxLen; dMax++) { tmp[dMax] = '\0'; }
|
||||
|
||||
dMax = strlen(dest)-1;
|
||||
for (dPos=dMax; (dPos>=0 && !done); dPos--)
|
||||
{
|
||||
if (dest[dPos] == tChar)
|
||||
{
|
||||
tPos = dPos;
|
||||
tmp[tPos] = '\0';
|
||||
}
|
||||
else done = true;
|
||||
}
|
||||
dPos++;
|
||||
for(dMax = 0; dMax <= dPos; dMax++)
|
||||
{
|
||||
tmp[dMax] = dest[dMax];
|
||||
}
|
||||
tmp[dMax+1] = '\0';
|
||||
strCopy(dest, maxLen, tmp);
|
||||
|
||||
} // strRTrim()
|
||||
|
||||
//===========================================================================================
|
||||
// 'tttABCtDEtFGHttt' => 'ABCtDEtFGH'
|
||||
void strTrim(char *dest, int maxLen, const char tChar )
|
||||
{
|
||||
char sTmp[maxLen];
|
||||
|
||||
strCopy(sTmp, maxLen, dest);
|
||||
strLTrim(sTmp, maxLen, tChar);
|
||||
strRTrim(sTmp, maxLen, tChar);
|
||||
strCopy(dest, maxLen, sTmp);
|
||||
|
||||
} // strTrim()
|
||||
|
||||
//===========================================================================================
|
||||
void strRemoveAll(char *dest, int maxLen, const char tChar)
|
||||
{
|
||||
char tmp[maxLen];
|
||||
int tPos = 0;
|
||||
tmp[0] = '\0';
|
||||
for (int dPos=0; (dPos<maxLen && dest[dPos] != '\0'); dPos++)
|
||||
{
|
||||
if (dest[dPos] != tChar)
|
||||
{
|
||||
tmp[tPos++] = dest[dPos];
|
||||
}
|
||||
}
|
||||
tmp[tPos] = '\0';
|
||||
strCopy(dest, maxLen, tmp);
|
||||
|
||||
} // strRemoveAll()
|
||||
|
||||
|
||||
//===========================================================================================
|
||||
void strTrimCntr(char *dest, int maxLen)
|
||||
{
|
||||
char tmp[maxLen];
|
||||
int tPos = 0;
|
||||
tmp[0] = '\0';
|
||||
for (int dPos=0; (dPos<maxLen && dest[dPos] != '\0'); dPos++)
|
||||
{
|
||||
if (dest[dPos] >= ' ' && dest[dPos] <= '~') // space = 32, '~' = 127
|
||||
{
|
||||
tmp[tPos++] = dest[dPos];
|
||||
}
|
||||
}
|
||||
tmp[tPos] = '\0';
|
||||
strCopy(dest, maxLen, tmp);
|
||||
|
||||
} // strTrimCntr()
|
||||
|
||||
//===========================================================================================
|
||||
int strIndex(const char *haystack, const char *needle, int start)
|
||||
{
|
||||
// strindex(hay, needle) ????
|
||||
char *p = strstr(haystack+start, needle);
|
||||
if (p) {
|
||||
//DebugTf("found [%s] at position [%d]\r\n", needle, (p - haystack));
|
||||
return (p - haystack);
|
||||
}
|
||||
return -1;
|
||||
|
||||
} // strIndex()
|
||||
|
||||
//===========================================================================================
|
||||
int strIndex(const char *haystack, const char *needle)
|
||||
{
|
||||
return strIndex(haystack, needle, 0);
|
||||
|
||||
} // strIndex()
|
||||
|
||||
//===========================================================================================
|
||||
int stricmp(const char *a, const char *b)
|
||||
{
|
||||
for (;; a++, b++) {
|
||||
int d = tolower((unsigned char)*a) - tolower((unsigned char)*b);
|
||||
if (d != 0 || !*a)
|
||||
return d;
|
||||
}
|
||||
|
||||
} // stricmp()
|
||||
|
||||
//===========================================================================================
|
||||
char *intToStr(int32_t v)
|
||||
{
|
||||
static char buff[25];
|
||||
sprintf(buff,"%d", v);
|
||||
return buff;
|
||||
|
||||
} // intToStr()
|
||||
|
||||
//===========================================================================================
|
||||
char *floatToStr(float v, int dec)
|
||||
{
|
||||
static char buff[25];
|
||||
if (dec == 0) sprintf(buff,"%.0f", v);
|
||||
else if (dec == 1) sprintf(buff,"%.1f", v);
|
||||
else if (dec == 2) sprintf(buff,"%.2f", v);
|
||||
else if (dec == 3) sprintf(buff,"%.3f", v);
|
||||
else if (dec == 4) sprintf(buff,"%.4f", v);
|
||||
else if (dec == 5) sprintf(buff,"%.5f", v);
|
||||
else sprintf(buff,"%f", v);
|
||||
return buff;
|
||||
|
||||
} // floattToStr()
|
||||
|
||||
//===========================================================================================
|
||||
float formatFloat(float v, int dec)
|
||||
{
|
||||
return (String(v, dec).toFloat());
|
||||
|
||||
} // formatFloat()
|
||||
|
||||
//===========================================================================================
|
||||
float strToFloat(const char *s, int dec)
|
||||
{
|
||||
float r = 0.0;
|
||||
int p = 0;
|
||||
int d = -1;
|
||||
|
||||
r = strtof(s, NULL);
|
||||
p = (int)(r*pow(10, dec));
|
||||
r = p / pow(10, dec);
|
||||
//DebugTf("[%s][%d] => p[%d] -> r[%f]\r\n", s, dec, p, r);
|
||||
return r;
|
||||
|
||||
} // strToFloat()
|
||||
|
||||
//===========================================================================================
|
||||
void parseJsonKey(const char *sIn, const char *key, char *val, int valLen)
|
||||
{
|
||||
// json key-value pair looks like:
|
||||
// "samenv": "Zwaar bewolkt",
|
||||
// or "samenv": "Zwaar bewolkt"}
|
||||
int keyStart = strIndex(sIn, key);
|
||||
int sepStart = strIndex(sIn, ",", keyStart);
|
||||
if (sepStart == -1)
|
||||
{
|
||||
sepStart = strIndex(sIn, "}", keyStart);
|
||||
}
|
||||
strCopy(val, valLen, sIn, keyStart+strlen(key), sepStart);
|
||||
strRemoveAll(val, valLen, ':');
|
||||
strRemoveAll(val, valLen, ',');
|
||||
strRemoveAll(val, valLen, '}');
|
||||
strRemoveAll(val, valLen, '"');
|
||||
strTrim(val, valLen, ' ');
|
||||
|
||||
} // parseJsonKey()
|
||||
|
||||
//===========================================================================================
|
||||
uint8_t utf8Ascii(uint8_t ascii)
|
||||
// Convert a single Character from UTF8 to Extended ASCII according to ISO 8859-1,
|
||||
// also called ISO Latin-1. Codes 128-159 contain the Microsoft Windows Latin-1
|
||||
// extended characters:
|
||||
// - codes 0..127 are identical in ASCII and UTF-8
|
||||
// - codes 160..191 in ISO-8859-1 and Windows-1252 are two-byte characters in UTF-8
|
||||
// + 0xC2 then second byte identical to the extended ASCII code.
|
||||
// - codes 192..255 in ISO-8859-1 and Windows-1252 are two-byte characters in UTF-8
|
||||
// + 0xC3 then second byte differs only in the first two bits to extended ASCII code.
|
||||
// - codes 128..159 in Windows-1252 are different, but usually only the €-symbol will be needed from this range.
|
||||
// + The euro symbol is 0x80 in Windows-1252, 0xa4 in ISO-8859-15, and 0xe2 0x82 0xac in UTF-8.
|
||||
//
|
||||
// Modified from original code at http://playground.arduino.cc/Main/Utf8ascii
|
||||
// Extended ASCII encoding should match the characters at http://www.ascii-code.com/
|
||||
//
|
||||
// Return "0" if a byte has to be ignored.
|
||||
{
|
||||
static uint8_t cPrev;
|
||||
uint8_t c = '\0';
|
||||
|
||||
if (ascii < 0x7f) // Standard ASCII-set 0..0x7F, no conversion
|
||||
{
|
||||
cPrev = '\0';
|
||||
c = ascii;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (cPrev) // Conversion depending on preceding UTF8-character
|
||||
{
|
||||
case 0xC2: c = ascii; break;
|
||||
case 0xC3: c = ascii | 0xC0; break;
|
||||
case 0x82: if (ascii==0xAC) c = 0x80; // Euro symbol special case
|
||||
}
|
||||
cPrev = ascii; // save last char
|
||||
}
|
||||
//Debugf("\nConverted 0x%02x", ascii);
|
||||
//Debugf(" to 0x%02x", c);
|
||||
|
||||
return(c);
|
||||
|
||||
} // utf8Ascii(uint8_t)
|
||||
|
||||
//===========================================================================================
|
||||
void utf8Ascii(char* s)
|
||||
// In place conversion UTF-8 string to Extended ASCII
|
||||
// The extended ASCII string is always shorter.
|
||||
{
|
||||
uint8_t c;
|
||||
char *cp = s;
|
||||
|
||||
//DebugTf("\nConverting: %c", s);
|
||||
|
||||
while (*s != '\0')
|
||||
{
|
||||
c = utf8Ascii(*s++);
|
||||
if (c != '\0')
|
||||
*cp++ = c;
|
||||
}
|
||||
*cp = '\0'; // terminate the new string
|
||||
|
||||
} // utf8Ascii(char)
|
||||
|
||||
|
||||
void getRevisionData()
|
||||
{
|
||||
if (!LittleFS.exists("/newsFiles/LCL-000"))
|
||||
{
|
||||
char LCL000[100];
|
||||
sprintf(LCL000, "ESP_ticker %s (c) by Willem Aandewiel", String(FWversion).c_str());
|
||||
writeFileById("LCL", 0, LCL000);
|
||||
}
|
||||
|
||||
} // getRevisionData()
|
||||
|
||||
/***************************************************************************
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
* persons to whom the Software is furnished to do so, subject to the
|
||||
* following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
|
||||
* OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
|
||||
* THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
****************************************************************************
|
||||
*/
|
||||
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
***************************************************************************
|
||||
** Program : jsonStuff, part of ESP_ticker
|
||||
**
|
||||
** Copyright (c) 2021 Willem Aandewiel
|
||||
**
|
||||
** TERMS OF USE: MIT License. See bottom of file.
|
||||
***************************************************************************
|
||||
*/
|
||||
|
||||
//aaw//WiFiServer httpServer(80);
|
||||
static char objSprtr[10] = "";
|
||||
|
||||
//=======================================================================
|
||||
void sendStartJsonObj(const char *objName)
|
||||
{
|
||||
char sBuff[50] = "";
|
||||
objSprtr[0] = '\0';
|
||||
|
||||
snprintf(sBuff, sizeof(sBuff), "{\"%s\":[\r\n", objName);
|
||||
httpServer.sendHeader("Access-Control-Allow-Origin", "*");
|
||||
httpServer.setContentLength(CONTENT_LENGTH_UNKNOWN);
|
||||
httpServer.send(200, "application/json", sBuff);
|
||||
|
||||
} // sendStartJsonObj()
|
||||
|
||||
|
||||
//=======================================================================
|
||||
void sendEndJsonObj()
|
||||
{
|
||||
httpServer.sendContent("\r\n]}\r\n");
|
||||
|
||||
//httpServer.sendHeader( "Content-Length", "0");
|
||||
//httpServer.send ( 200, "application/json", "");
|
||||
|
||||
} // sendEndJsonObj()
|
||||
|
||||
|
||||
//=======================================================================
|
||||
void sendNestedJsonObj(const char *cName, const char *cValue)
|
||||
{
|
||||
char jsonBuff[JSON_BUFF_MAX] = "";
|
||||
|
||||
snprintf(jsonBuff, sizeof(jsonBuff), "%s{\"name\": \"%s\", \"value\": \"%s\"}"
|
||||
, objSprtr, cName, cValue);
|
||||
|
||||
httpServer.sendContent(jsonBuff);
|
||||
sprintf(objSprtr, ",\r\n");
|
||||
|
||||
} // sendNestedJsonObj(*char, *char)
|
||||
|
||||
|
||||
//=======================================================================
|
||||
void sendNestedJsonObj(const char *cName, String sValue)
|
||||
{
|
||||
char jsonBuff[JSON_BUFF_MAX] = "";
|
||||
|
||||
if (sValue.length() > (JSON_BUFF_MAX - 65) )
|
||||
{
|
||||
DebugTf("[2] sValue.length() [%d]\r\n", sValue.length());
|
||||
}
|
||||
|
||||
snprintf(jsonBuff, sizeof(jsonBuff), "%s{\"name\": \"%s\", \"value\": \"%s\"}"
|
||||
, objSprtr, cName, sValue.c_str());
|
||||
|
||||
httpServer.sendContent(jsonBuff);
|
||||
sprintf(objSprtr, ",\r\n");
|
||||
|
||||
} // sendNestedJsonObj(*char, String)
|
||||
|
||||
|
||||
//=======================================================================
|
||||
void sendNestedJsonObj(const char *cName, int32_t iValue)
|
||||
{
|
||||
char jsonBuff[200] = "";
|
||||
|
||||
snprintf(jsonBuff, sizeof(jsonBuff), "%s{\"name\": \"%s\", \"value\": %d}"
|
||||
, objSprtr, cName, iValue);
|
||||
|
||||
httpServer.sendContent(jsonBuff);
|
||||
sprintf(objSprtr, ",\r\n");
|
||||
|
||||
} // sendNestedJsonObj(*char, int)
|
||||
|
||||
//=======================================================================
|
||||
void sendNestedJsonObj(const char *cName, uint32_t uValue)
|
||||
{
|
||||
char jsonBuff[200] = "";
|
||||
|
||||
snprintf(jsonBuff, sizeof(jsonBuff), "%s{\"name\": \"%s\", \"value\": %u }"
|
||||
, objSprtr, cName, uValue);
|
||||
|
||||
httpServer.sendContent(jsonBuff);
|
||||
sprintf(objSprtr, ",\r\n");
|
||||
|
||||
} // sendNestedJsonObj(*char, uint)
|
||||
|
||||
|
||||
//=======================================================================
|
||||
void sendNestedJsonObj(const char *cName, float fValue)
|
||||
{
|
||||
char jsonBuff[200] = "";
|
||||
|
||||
snprintf(jsonBuff, sizeof(jsonBuff), "%s{\"name\": \"%s\", \"value\": %.3f }"
|
||||
, objSprtr, cName, fValue);
|
||||
|
||||
httpServer.sendContent(jsonBuff);
|
||||
sprintf(objSprtr, ",\r\n");
|
||||
|
||||
} // sendNestedJsonObj(*char, float)
|
||||
|
||||
|
||||
//=======================================================================
|
||||
// ************ function to build Json Settings string ******************
|
||||
//=======================================================================
|
||||
void sendJsonSettingObj(const char *cName, float fValue, const char *fType, int minValue, int maxValue)
|
||||
{
|
||||
char jsonBuff[200] = "";
|
||||
|
||||
snprintf(jsonBuff, sizeof(jsonBuff), "%s{\"name\": \"%s\", \"value\": %.3f, \"type\": \"%s\", \"min\": %d, \"max\": %d}"
|
||||
, objSprtr, cName, fValue, fType, minValue, maxValue);
|
||||
|
||||
httpServer.sendContent(jsonBuff);
|
||||
sprintf(objSprtr, ",\r\n");
|
||||
|
||||
} // sendJsonSettingObj(*char, float, *char, int, int)
|
||||
|
||||
|
||||
//=======================================================================
|
||||
void sendJsonSettingObj(const char *cName, float fValue, const char *fType, int minValue, int maxValue, int decPlaces)
|
||||
{
|
||||
char jsonBuff[200] = "";
|
||||
|
||||
switch(decPlaces) {
|
||||
case 0:
|
||||
snprintf(jsonBuff, sizeof(jsonBuff), "%s{\"name\": \"%s\", \"value\": %.0f, \"type\": \"%s\", \"min\": %d, \"max\": %d}"
|
||||
, objSprtr, cName, fValue, fType, minValue, maxValue);
|
||||
break;
|
||||
case 2:
|
||||
snprintf(jsonBuff, sizeof(jsonBuff), "%s{\"name\": \"%s\", \"value\": %.2f, \"type\": \"%s\", \"min\": %d, \"max\": %d}"
|
||||
, objSprtr, cName, fValue, fType, minValue, maxValue);
|
||||
break;
|
||||
case 5:
|
||||
snprintf(jsonBuff, sizeof(jsonBuff), "%s{\"name\": \"%s\", \"value\": %.5f, \"type\": \"%s\", \"min\": %d, \"max\": %d}"
|
||||
, objSprtr, cName, fValue, fType, minValue, maxValue);
|
||||
break;
|
||||
default:
|
||||
snprintf(jsonBuff, sizeof(jsonBuff), "%s{\"name\": \"%s\", \"value\": %f, \"type\": \"%s\", \"min\": %d, \"max\": %d}"
|
||||
, objSprtr, cName, fValue, fType, minValue, maxValue);
|
||||
|
||||
}
|
||||
|
||||
httpServer.sendContent(jsonBuff);
|
||||
sprintf(objSprtr, ",\r\n");
|
||||
|
||||
} // sendJsonSettingObj(*char, float, *char, int, int, int)
|
||||
|
||||
|
||||
//=======================================================================
|
||||
void sendJsonSettingObj(const char *cName, int iValue, const char *iType, int minValue, int maxValue)
|
||||
{
|
||||
char jsonBuff[200] = "";
|
||||
|
||||
snprintf(jsonBuff, sizeof(jsonBuff), "%s{\"name\": \"%s\", \"value\": %d, \"type\": \"%s\", \"min\": %d, \"max\": %d}"
|
||||
, objSprtr, cName, iValue, iType, minValue, maxValue);
|
||||
|
||||
httpServer.sendContent(jsonBuff);
|
||||
sprintf(objSprtr, ",\r\n");
|
||||
|
||||
} // sendJsonSettingObj(*char, int, *char, int, int)
|
||||
|
||||
|
||||
//=======================================================================
|
||||
void sendJsonSettingObj(const char *cName, const char *cValue, const char *sType, int maxLen)
|
||||
{
|
||||
char jsonBuff[200] = "";
|
||||
|
||||
snprintf(jsonBuff, sizeof(jsonBuff), "%s{\"name\": \"%s\", \"value\":\"%s\", \"type\": \"%s\", \"maxlen\": %d}"
|
||||
, objSprtr, cName, cValue, sType, maxLen);
|
||||
|
||||
httpServer.sendContent(jsonBuff);
|
||||
sprintf(objSprtr, ",\r\n");
|
||||
|
||||
} // sendJsonSettingObj(*char, *char, *char, int, int)
|
||||
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
* persons to whom the Software is furnished to do so, subject to the
|
||||
* following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
|
||||
* OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
|
||||
* THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
****************************************************************************
|
||||
*/
|
||||
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
***************************************************************************
|
||||
** Program : littlefsStuff, part of ESP_ticker
|
||||
**
|
||||
** Copyright (c) 2021 Willem Aandewiel
|
||||
**
|
||||
** TERMS OF USE: MIT License. See bottom of file.
|
||||
***************************************************************************
|
||||
*/
|
||||
|
||||
//====================================================================
|
||||
void readLastStatus()
|
||||
{
|
||||
char buffer[50] = "";
|
||||
char dummy[50] = "";
|
||||
|
||||
File _file = LittleFS.open("/sysStatus.csv", "r");
|
||||
if (!_file)
|
||||
{
|
||||
DebugTln("read(): No /sysStatus.csv found ..");
|
||||
}
|
||||
if(_file.available()) {
|
||||
int l = _file.readBytesUntil('\n', buffer, sizeof(buffer));
|
||||
buffer[l] = 0;
|
||||
DebugTf("read lastUpdate[%s]\r\n", buffer);
|
||||
sscanf(buffer, "%[^;]; %[^;]; %u; %[^;]", cDate, cTime, &nrReboots, dummy);
|
||||
DebugTf("values timestamp[%s %s], nrReboots[%u], dummy[%s]\r\n"
|
||||
, cDate
|
||||
, cTime
|
||||
, nrReboots
|
||||
, dummy);
|
||||
yield();
|
||||
}
|
||||
_file.close();
|
||||
|
||||
} // readLastStatus()
|
||||
|
||||
|
||||
//====================================================================
|
||||
void writeLastStatus()
|
||||
{
|
||||
if (ESP.getFreeHeap() < 8500) // to prevent firmware from crashing!
|
||||
{
|
||||
DebugTf("Bailout due to low heap (%d bytes)\r\n", ESP.getFreeHeap());
|
||||
return;
|
||||
}
|
||||
char buffer[50] = "";
|
||||
snprintf(buffer, sizeof(buffer), "%04d-%02d-%02d; %02d:%02d:%02d; %010u; %s;\n"
|
||||
, localtime(&now)->tm_year+1900, localtime(&now)->tm_mon+1, localtime(&now)->tm_mday
|
||||
, localtime(&now)->tm_hour, localtime(&now)->tm_min, localtime(&now)->tm_sec
|
||||
, nrReboots
|
||||
, "meta data");
|
||||
DebugTf("writeLastStatus() => %s\r\n", buffer);
|
||||
|
||||
File _file = LittleFS.open("/sysStatus.csv", "w");
|
||||
if (!_file)
|
||||
{
|
||||
DebugTln("write(): No /sysStatus.csv found ..");
|
||||
}
|
||||
_file.print(buffer);
|
||||
_file.flush();
|
||||
_file.close();
|
||||
|
||||
} // writeLastStatus()
|
||||
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool readFileById(const char* fType, uint8_t mId)
|
||||
{
|
||||
String percChar = "%%";
|
||||
String backSlash = "\\";
|
||||
String rTmp;
|
||||
char fName[50] = "";
|
||||
|
||||
sprintf(fName, "/newsFiles/%s-%03d", fType, mId);
|
||||
|
||||
DebugTf("read [%s] ", fName);
|
||||
|
||||
if (!LittleFS.exists(fName))
|
||||
{
|
||||
Debugln("Does not exist!");
|
||||
return false;
|
||||
}
|
||||
|
||||
File f = LittleFS.open(fName, "r");
|
||||
|
||||
while(f.available())
|
||||
{
|
||||
rTmp = f.readStringUntil('\n');
|
||||
//Debugf("rTmp(in) [%s]\r\n", rTmp.c_str());
|
||||
rTmp.replace("\r", "");
|
||||
}
|
||||
f.close();
|
||||
|
||||
rTmp.replace("@1@", ":");
|
||||
rTmp.replace("@2@", "{");
|
||||
rTmp.replace("@3@", "}");
|
||||
rTmp.replace("@4@", ",");
|
||||
rTmp.replace("@5@", backSlash);
|
||||
rTmp.replace("@6@", percChar);
|
||||
//DebugTf("rTmp(out) [%s]\r\n", rTmp.c_str());
|
||||
|
||||
snprintf(fileMessage, LOCAL_SIZE, rTmp.c_str());
|
||||
if (strlen(fileMessage) == 0)
|
||||
{
|
||||
Debugln("file is zero bytes long");
|
||||
return false;
|
||||
}
|
||||
Debugf("OK! \r\n\t[%s]\r\n", fileMessage);
|
||||
|
||||
if (mId == 0)
|
||||
{
|
||||
LittleFS.remove("/newsFiles/LCL-000");
|
||||
DebugTln("Remove LCL-000 ..");
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
} // readFileById()
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool writeFileById(const char* fType, uint8_t mId, const char *msg)
|
||||
{
|
||||
String rTmp;
|
||||
char fName[50] = "";
|
||||
sprintf(fName, "/newsFiles/%s-%03d", fType, mId);
|
||||
|
||||
DebugTf("write [%s]-> [%s]\r\n", fName, msg);
|
||||
|
||||
if (strlen(msg) < 3)
|
||||
{
|
||||
LittleFS.remove(fName);
|
||||
Debugln("Empty message, file removed!");
|
||||
return true;
|
||||
}
|
||||
|
||||
DebugTln("LittleFS.open()...");
|
||||
File file = LittleFS.open(fName, "w");
|
||||
if (!file)
|
||||
{
|
||||
Debugf("open(%s, 'w') FAILED!!! --> Bailout\r\n", fName);
|
||||
return false;
|
||||
}
|
||||
yield();
|
||||
|
||||
Debugln(F("Start writing data .. \r"));
|
||||
DebugFlush();
|
||||
Debugln(msg);
|
||||
file.println(msg);
|
||||
file.close();
|
||||
|
||||
DebugTln("Exit writeFileById()!");
|
||||
return true;
|
||||
|
||||
} // writeFileById()
|
||||
|
||||
|
||||
//=======================================================================
|
||||
void updateMessage(const char *field, const char *newValue)
|
||||
{
|
||||
int8_t msgId = String(field).toInt();
|
||||
|
||||
DebugTf("-> field[%s], newValue[%s]\r\n", field, newValue);
|
||||
|
||||
if (msgId < 0 || msgId > settingLocalMaxMsg)
|
||||
{
|
||||
DebugTf("msgId[%d] is out of scope! Bailing out!\r\n", msgId);
|
||||
return;
|
||||
}
|
||||
|
||||
writeFileById("LCL", msgId, newValue);
|
||||
|
||||
} // updateMessage()
|
||||
|
||||
|
||||
//====================================================================
|
||||
void writeToLog(const char *logLine)
|
||||
{
|
||||
if (ESP.getFreeHeap() < 8500) // to prevent firmware from crashing!
|
||||
{
|
||||
DebugTf("Bailout due to low heap (%d bytes)\r\n", ESP.getFreeHeap());
|
||||
return;
|
||||
}
|
||||
char buffer[150] = "";
|
||||
snprintf(buffer, sizeof(buffer), "%04d-%02d-%02d; %02d:%02d:%02d; %s;\n"
|
||||
, localtime(&now)->tm_year+1900, localtime(&now)->tm_mon+1, localtime(&now)->tm_mday
|
||||
, localtime(&now)->tm_hour, localtime(&now)->tm_min, localtime(&now)->tm_sec
|
||||
, logLine);
|
||||
DebugTf("writeToLogs() => %s\r\n", buffer);
|
||||
File _file = LittleFS.open("/sysLog.csv", "a");
|
||||
if (!_file)
|
||||
{
|
||||
DebugTln("write(): No /sysLog.csv found ..");
|
||||
}
|
||||
_file.print(buffer);
|
||||
_file.flush();
|
||||
_file.close();
|
||||
|
||||
} // writeLastStatus()
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
* persons to whom the Software is furnished to do so, subject to the
|
||||
* following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
|
||||
* OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
|
||||
* THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
****************************************************************************
|
||||
*/
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
***************************************************************************
|
||||
** Program : networkStuff.ino
|
||||
**
|
||||
** Copyright (c) 2021 Willem Aandewiel
|
||||
**
|
||||
** TERMS OF USE: MIT License. See bottom of file.
|
||||
***************************************************************************
|
||||
** Usage:
|
||||
**
|
||||
** #define HOSTNAME thisProject
|
||||
**
|
||||
** setup()
|
||||
** {
|
||||
** startWiFi(_HOSTNAME, 240); // timeout 4 minuten
|
||||
** startMDNS(_HOSTNAME);
|
||||
** httpServer.on("/index", <sendIndexPage>);
|
||||
** httpServer.on("/index.html",<sendIndexPage>);
|
||||
** httpServer.begin();
|
||||
** }
|
||||
**
|
||||
** loop()
|
||||
** {
|
||||
** handleWiFi();
|
||||
** MDNS.update();
|
||||
** httpServer.handleClient();
|
||||
** .
|
||||
** .
|
||||
** }
|
||||
*/
|
||||
|
||||
|
||||
#include <ESP8266WiFi.h> //ESP8266 Core WiFi Library
|
||||
#include <ESP8266WebServer.h> // Version 1.0.0 - part of ESP8266 Core https://github.com/esp8266/Arduino
|
||||
#include <ESP8266mDNS.h> // part of ESP8266 Core https://github.com/esp8266/Arduino
|
||||
|
||||
#include <WiFiUdp.h> // part of ESP8266 Core https://github.com/esp8266/Arduino
|
||||
#include <ModUpdateServer.h> // https://github.com/mrWheel/ModUpdateServer
|
||||
#include "updateServerHtml.h"
|
||||
#include <WiFiManager.h> // part of ESP8266 Core
|
||||
|
||||
ESP8266HTTPUpdateServer httpUpdater(true);
|
||||
|
||||
bool isConnected = false;
|
||||
|
||||
/**
|
||||
void configModeCallback (WiFiManager *myWiFiManager)
|
||||
{
|
||||
Debugln(F("Entered config mode\r"));
|
||||
Debugln(WiFi.softAPIP().toString());
|
||||
Debugln(" ----> brows to 192.168.4.1");
|
||||
Debugln(myWiFiManager->getConfigPortalSSID());
|
||||
|
||||
} // configModeCallback()
|
||||
**/
|
||||
|
||||
//===========================================================================================
|
||||
void startWiFi(const char* hostname, int timeOut)
|
||||
{
|
||||
WiFiManager manageWiFi;
|
||||
uint32_t lTime = millis();
|
||||
String thisAP = String(hostname) + "-" + WiFi.macAddress();
|
||||
|
||||
DebugT("start WiFi ...");
|
||||
|
||||
manageWiFi.setDebugOutput(true);
|
||||
|
||||
//-- reset settings - wipe stored credentials for testing
|
||||
//-- these are stored by the esp library
|
||||
//manageWiFi.resetSettings();
|
||||
|
||||
|
||||
//-- set callback that gets called when connecting to previous
|
||||
//-- WiFi fails, and enters Access Point mode
|
||||
//manageWiFi.setAPCallback(configModeCallback);
|
||||
|
||||
//-- sets timeout until configuration portal gets turned off
|
||||
//-- useful to make it all retry or go to sleep in seconds
|
||||
manageWiFi.setTimeout(timeOut); // in seconden ...
|
||||
|
||||
//-- fetches ssid and pass and tries to connect
|
||||
//-- if it does not connect it starts an access point with the specified name
|
||||
//-- here "lichtKrant-<MAC>"
|
||||
//-- and goes into a blocking loop awaiting configuration
|
||||
if (!manageWiFi.autoConnect(thisAP.c_str()))
|
||||
{
|
||||
DebugTln(F("failed to connect and hit timeout"));
|
||||
|
||||
//reset and try again, or maybe put it to deep sleep
|
||||
//delay(3000);
|
||||
//ESP.reset();
|
||||
//delay(2000);
|
||||
DebugTf(" took [%d] seconds ==> ERROR!\r\n", (millis() - lTime) / 1000);
|
||||
return;
|
||||
}
|
||||
|
||||
Debugln();
|
||||
DebugT(F("Connected to " )); Debugln (WiFi.SSID());
|
||||
DebugT(F("IP address: " )); Debugln (WiFi.localIP());
|
||||
DebugT(F("IP gateway: " )); Debugln (WiFi.gatewayIP());
|
||||
Debugln();
|
||||
|
||||
httpUpdater.setup(&httpServer);
|
||||
httpUpdater.setIndexPage(UpdateServerIndex);
|
||||
httpUpdater.setSuccessPage(UpdateServerSuccess);
|
||||
DebugTf(" took [%d] seconds => OK!\r\n", (millis() - lTime) / 1000);
|
||||
|
||||
} // startWiFi()
|
||||
|
||||
|
||||
//=======================================================================
|
||||
void startMDNS(const char *Hostname)
|
||||
{
|
||||
DebugTf("[1] mDNS setup as [%s.local]\r\n", Hostname);
|
||||
if (MDNS.begin(Hostname)) // Start the mDNS responder for Hostname.local
|
||||
{
|
||||
DebugTf("[2] mDNS responder started as [%s.local]\r\n", Hostname);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugTln(F("[3] Error setting up MDNS responder!\r\n"));
|
||||
}
|
||||
MDNS.addService("http", "tcp", 80);
|
||||
|
||||
} // startMDNS()
|
||||
|
||||
/***************************************************************************
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
* persons to whom the Software is furnished to do so, subject to the
|
||||
* following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
|
||||
* OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
|
||||
* THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
****************************************************************************
|
||||
*/
|
||||
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
***************************************************************************
|
||||
** Program : newsapi_org
|
||||
**
|
||||
** Copyright (c) 2021 .. 2023 Willem Aandewiel
|
||||
**
|
||||
** TERMS OF USE: MIT License. See bottom of file.
|
||||
***************************************************************************
|
||||
*/
|
||||
|
||||
//-- http://newsapi.org/v2/top-headlines?country=nl&apiKey=API_KEY
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
bool getNewsapiData()
|
||||
{
|
||||
const char* newsapiHost = "newsapi.org";
|
||||
const int httpPort = 80;
|
||||
int newsapiStatus = 0;
|
||||
char newsMessage[NEWS_SIZE] = {};
|
||||
int startPos, endPos;
|
||||
int32_t maxWait;
|
||||
char jsonResponse[1024];
|
||||
char val[51] = "";
|
||||
|
||||
WiFiClient newsapiClient;
|
||||
|
||||
Debugln();
|
||||
DebugTf("getNewsapiData(%s)\r\n", newsapiHost);
|
||||
|
||||
// We now create a URI for the request
|
||||
String url = "/v2/top-headlines?country=nl&apiKey=";
|
||||
url += settingNewsAUTH;
|
||||
|
||||
DebugTf("Requesting URL: %s/v2/top-headlines?country=nl&apiKey=secret\r\n", newsapiHost);
|
||||
DebugFlush();
|
||||
//Debugln("\r\n=======================================");
|
||||
//DebugFlush();
|
||||
//Debug(newsapiHost);
|
||||
//Debugln(url);
|
||||
//Debugln("=======================================");
|
||||
|
||||
if (!newsapiClient.connect(newsapiHost, httpPort))
|
||||
{
|
||||
DebugTln("connection failed");
|
||||
sprintf(tempMessage, "connection to %s failed", newsapiHost);
|
||||
//-- empty newsMessage store --
|
||||
for(int i=0; i<=settingNewsMaxMsg; i++)
|
||||
{
|
||||
//sprintf(newsMessage, "");
|
||||
if (i==1) writeFileById("NWS", i, "There is No News ....");
|
||||
else writeFileById("NWS", i, "");
|
||||
}
|
||||
|
||||
newsapiClient.flush();
|
||||
newsapiClient.stop();
|
||||
return false;
|
||||
}
|
||||
|
||||
// This will send the request to the server
|
||||
newsapiClient.print(String("GET ") + url + " HTTP/1.1\r\n" +
|
||||
"Host: " + newsapiHost + "\r\n" +
|
||||
"User-Agent: ESP-ticker\r\n" +
|
||||
"Connection: close\r\n\r\n");
|
||||
delay(10);
|
||||
|
||||
newsapiClient.setTimeout(5000);
|
||||
while (newsapiClient.connected() || newsapiClient.available())
|
||||
{
|
||||
yield();
|
||||
while(newsapiClient.available())
|
||||
{
|
||||
Debugln();
|
||||
//--- skip to find HTTP/1.1
|
||||
//--- then parse response code
|
||||
if (newsapiClient.find("HTTP/1.1"))
|
||||
{
|
||||
newsapiStatus = newsapiClient.parseInt(); // parse status code
|
||||
DebugTf("Statuscode: [%d] ", newsapiStatus);
|
||||
if (newsapiStatus != 200)
|
||||
{
|
||||
Debugln(" ERROR!");
|
||||
while(newsapiClient.available())
|
||||
{
|
||||
char nC = newsapiClient.read();
|
||||
Debug(nC);
|
||||
}
|
||||
Debugln();
|
||||
newsapiClient.flush();
|
||||
newsapiClient.stop();
|
||||
for(int i=0; i<=settingNewsMaxMsg; i++)
|
||||
{
|
||||
//sprintf(newsMessage, "");
|
||||
if (i==1) writeFileById("NWS", i, "There is No News ....");
|
||||
else writeFileById("NWS", i, "");
|
||||
}
|
||||
newsapiClient.flush();
|
||||
newsapiClient.stop();
|
||||
return false;
|
||||
}
|
||||
Debugln(" OK!");
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugTln("Error reading newsapi.org.. -> bailout!");
|
||||
for(int i=0; i<=settingNewsMaxMsg; i++)
|
||||
{
|
||||
//sprintf(newsMessage, "");
|
||||
if (i==1) writeFileById("NWS", i, "There is No News ....");
|
||||
else writeFileById("NWS", i, "");
|
||||
}
|
||||
newsapiClient.flush();
|
||||
newsapiClient.stop();
|
||||
return false;
|
||||
}
|
||||
//--- skip headers
|
||||
uint16_t msgIdx = 0;
|
||||
int msgNr = 0;
|
||||
while (newsapiClient.find("\"title\":\""))
|
||||
{
|
||||
for (int i=0; i<NEWS_SIZE; i++) newsMessage[i] = 0;
|
||||
msgIdx = 0;
|
||||
while( (strIndex(newsMessage, "\"description\":") == -1) && (msgIdx < (NEWS_SIZE - 2)) )
|
||||
{
|
||||
newsMessage[msgIdx++] = (char)newsapiClient.read();
|
||||
}
|
||||
newsMessage[msgIdx] = '\0';
|
||||
if (msgIdx > 30) newsMessage[msgIdx - 16] = 0;
|
||||
Debugf("\t[%2d] %s\r\n", msgNr, newsMessage);
|
||||
if (!hasNoNoWord(newsMessage) && strlen(newsMessage) > 15)
|
||||
{
|
||||
if (msgNr <= settingNewsMaxMsg)
|
||||
{
|
||||
writeFileById("NWS", msgNr, newsMessage);
|
||||
}
|
||||
msgNr++;
|
||||
}
|
||||
} // while find(title)
|
||||
} // while available ..
|
||||
|
||||
} // connected ..
|
||||
|
||||
newsapiClient.flush();
|
||||
newsapiClient.stop();
|
||||
updateMessage("0", "News brought to you by 'newsapi.org'");
|
||||
|
||||
return true;
|
||||
|
||||
} // getNewsapiData()
|
||||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
void removeNewsData()
|
||||
{
|
||||
char nwsName[15];
|
||||
|
||||
for(int n=0; n<=settingNewsMaxMsg; n++)
|
||||
{
|
||||
sprintf(nwsName, "/newsFiles/NWS-%03d", n);
|
||||
DebugTf("Remove [%s] from LittleFS ..\r\n", nwsName);
|
||||
LittleFS.remove(nwsName);
|
||||
}
|
||||
|
||||
} // removeNewsData()
|
||||
|
||||
/***************************************************************************
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
* persons to whom the Software is furnished to do so, subject to the
|
||||
* following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
|
||||
* OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
|
||||
* THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
****************************************************************************
|
||||
*/
|
||||
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
***************************************************************************
|
||||
** Program : newsapi_org
|
||||
**
|
||||
** Copyright (c) 2021 .. 2023 Willem Aandewiel
|
||||
**
|
||||
** TERMS OF USE: MIT License. See bottom of file.
|
||||
***************************************************************************
|
||||
*/
|
||||
|
||||
//-- http://newsapi.org/v2/top-headlines?country=nl&apiKey=API_KEY
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
bool getNewsapiData()
|
||||
{
|
||||
const char* newsapiHost = "newsapi.org";
|
||||
const int httpPort = 80;
|
||||
int newsapiStatus = 0;
|
||||
char newsMessage[NEWS_SIZE] = {};
|
||||
int startPos, endPos;
|
||||
int32_t maxWait;
|
||||
char jsonResponse[1024];
|
||||
char val[51] = "";
|
||||
|
||||
WiFiClient newsapiClient;
|
||||
|
||||
Debugln();
|
||||
DebugTf("getNewsapiData(%s)\r\n", newsapiHost);
|
||||
|
||||
// We now create a URI for the request
|
||||
String url = "/v2/top-headlines?country=nl&apiKey=";
|
||||
url += settingNewsAUTH;
|
||||
|
||||
DebugTf("Requesting URL: %s/v2/top-headlines?country=nl&apiKey=secret\r\n", newsapiHost);
|
||||
DebugFlush();
|
||||
//Debugln("\r\n=======================================");
|
||||
//DebugFlush();
|
||||
//Debug(newsapiHost);
|
||||
//Debugln(url);
|
||||
//Debugln("=======================================");
|
||||
|
||||
if (!newsapiClient.connect(newsapiHost, httpPort))
|
||||
{
|
||||
DebugTln("connection failed");
|
||||
sprintf(tempMessage, "connection to %s failed", newsapiHost);
|
||||
//-- empty newsMessage store --
|
||||
for(int i=0; i<=settingNewsMaxMsg; i++)
|
||||
{
|
||||
//sprintf(newsMessage, "");
|
||||
if (i==1) writeFileById("NWS", i, "There is No News ....");
|
||||
else writeFileById("NWS", i, "");
|
||||
}
|
||||
|
||||
newsapiClient.flush();
|
||||
newsapiClient.stop();
|
||||
return false;
|
||||
}
|
||||
|
||||
// This will send the request to the server
|
||||
newsapiClient.print(String("GET ") + url + " HTTP/1.1\r\n" +
|
||||
"Host: " + newsapiHost + "\r\n" +
|
||||
"User-Agent: ESP-ticker\r\n" +
|
||||
"Connection: close\r\n\r\n");
|
||||
delay(10);
|
||||
|
||||
newsapiClient.setTimeout(5000);
|
||||
while (newsapiClient.connected() || newsapiClient.available())
|
||||
{
|
||||
yield();
|
||||
while(newsapiClient.available())
|
||||
{
|
||||
Debugln();
|
||||
//--- skip to find HTTP/1.1
|
||||
//--- then parse response code
|
||||
if (newsapiClient.find("HTTP/1.1"))
|
||||
{
|
||||
newsapiStatus = newsapiClient.parseInt(); // parse status code
|
||||
DebugTf("Statuscode: [%d] ", newsapiStatus);
|
||||
if (newsapiStatus != 200)
|
||||
{
|
||||
Debugln(" ERROR!");
|
||||
while(newsapiClient.available())
|
||||
{
|
||||
char nC = newsapiClient.read();
|
||||
Debug(nC);
|
||||
}
|
||||
Debugln();
|
||||
newsapiClient.flush();
|
||||
newsapiClient.stop();
|
||||
for(int i=0; i<=settingNewsMaxMsg; i++)
|
||||
{
|
||||
//sprintf(newsMessage, "");
|
||||
if (i==1) writeFileById("NWS", i, "There is No News ....");
|
||||
else writeFileById("NWS", i, "");
|
||||
}
|
||||
newsapiClient.flush();
|
||||
newsapiClient.stop();
|
||||
return false;
|
||||
}
|
||||
Debugln(" OK!");
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugTln("Error reading newsapi.org.. -> bailout!");
|
||||
for(int i=0; i<=settingNewsMaxMsg; i++)
|
||||
{
|
||||
//sprintf(newsMessage, "");
|
||||
if (i==1) writeFileById("NWS", i, "There is No News ....");
|
||||
else writeFileById("NWS", i, "");
|
||||
}
|
||||
newsapiClient.flush();
|
||||
newsapiClient.stop();
|
||||
return false;
|
||||
}
|
||||
//--- skip headers
|
||||
uint16_t msgIdx = 0;
|
||||
int msgNr = 0;
|
||||
while (newsapiClient.find("\"title\":\""))
|
||||
{
|
||||
for (int i=0; i<NEWS_SIZE; i++) newsMessage[i] = 0;
|
||||
msgIdx = 0;
|
||||
while( (strIndex(newsMessage, "\"description\":") == -1) && (msgIdx < (NEWS_SIZE - 2)) )
|
||||
{
|
||||
newsMessage[msgIdx++] = (char)newsapiClient.read();
|
||||
}
|
||||
newsMessage[msgIdx] = '\0';
|
||||
if (msgIdx > 30) newsMessage[msgIdx - 16] = 0;
|
||||
Debugf("\t[%2d] %s\r\n", msgNr, newsMessage);
|
||||
if (!hasNoNoWord(newsMessage) && strlen(newsMessage) > 15)
|
||||
{
|
||||
if (msgNr <= settingNewsMaxMsg)
|
||||
{
|
||||
writeFileById("NWS", msgNr, newsMessage);
|
||||
}
|
||||
msgNr++;
|
||||
}
|
||||
} // while find(title)
|
||||
} // while available ..
|
||||
|
||||
} // connected ..
|
||||
|
||||
newsapiClient.flush();
|
||||
newsapiClient.stop();
|
||||
updateMessage("0", "News brought to you by 'newsapi.org'");
|
||||
|
||||
return true;
|
||||
|
||||
} // getNewsapiData()
|
||||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
void removeNewsData()
|
||||
{
|
||||
char nwsName[15];
|
||||
|
||||
for(int n=0; n<=settingNewsMaxMsg; n++)
|
||||
{
|
||||
sprintf(nwsName, "/newsFiles/NWS-%03d", n);
|
||||
DebugTf("Remove [%s] from LittleFS ..\r\n", nwsName);
|
||||
LittleFS.remove(nwsName);
|
||||
}
|
||||
|
||||
} // removeNewsData()
|
||||
|
||||
/***************************************************************************
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
* persons to whom the Software is furnished to do so, subject to the
|
||||
* following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
|
||||
* OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
|
||||
* THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
****************************************************************************
|
||||
*/
|
||||
@@ -0,0 +1,262 @@
|
||||
// Data file for UTF-8 example user defined fonts
|
||||
#pragma once
|
||||
|
||||
MD_MAX72XX::fontType_t ExtASCII[] PROGMEM =
|
||||
{
|
||||
0, // 0 - 'Unused'
|
||||
0, // 1 - 'Unused'
|
||||
0, // 2 - 'Unused'
|
||||
0, // 3 - 'Unused'
|
||||
0, // 4 - 'Unused'
|
||||
0, // 5 - 'Unused'
|
||||
0, // 6 - 'Unused'
|
||||
0, // 7 - 'Unused'
|
||||
0, // 8 - 'Unused'
|
||||
0, // 9 - 'Unused'
|
||||
0, // 10 - 'Unused'
|
||||
0, // 11 - 'Unused'
|
||||
0, // 12 - 'Unused'
|
||||
0, // 13 - 'Unused'
|
||||
0, // 14 - 'Unused'
|
||||
0, // 15 - 'Unused'
|
||||
0, // 16 - 'Unused'
|
||||
0, // 17 - 'Unused'
|
||||
0, // 18 - 'Unused'
|
||||
0, // 19 - 'Unused'
|
||||
0, // 20 - 'Unused'
|
||||
0, // 21 - 'Unused'
|
||||
0, // 22 - 'Unused'
|
||||
0, // 23 - 'Unused'
|
||||
0, // 24 - 'Unused'
|
||||
0, // 25 - 'Unused'
|
||||
0, // 26 - 'Unused'
|
||||
0, // 27 - 'Unused'
|
||||
0, // 28 - 'Unused'
|
||||
0, // 29 - 'Unused'
|
||||
0, // 30 - 'Unused'
|
||||
0, // 31 - 'Unused'
|
||||
2, 0, 0, // 32 - 'Space'
|
||||
1, 95, // 33 - '!'
|
||||
3, 7, 0, 7, // 34 - '"'
|
||||
5, 20, 127, 20, 127, 20, // 35 - '#'
|
||||
5, 36, 42, 127, 42, 18, // 36 - '$'
|
||||
5, 35, 19, 8, 100, 98, // 37 - '%'
|
||||
5, 54, 73, 86, 32, 80, // 38 - '&'
|
||||
2, 4, 3, // 39
|
||||
3, 28, 34, 65, // 40 - '('
|
||||
3, 65, 34, 28, // 41 - ')'
|
||||
5, 42, 28, 127, 28, 42, // 42 - '*'
|
||||
5, 8, 8, 62, 8, 8, // 43 - '+'
|
||||
2, 128, 96, // 44 - ','
|
||||
5, 8, 8, 8, 8, 8, // 45 - '-'
|
||||
2, 96, 96, // 46 - '.'
|
||||
5, 32, 16, 8, 4, 2, // 47 - '/'
|
||||
5, 62, 81, 73, 69, 62, // 48 - '0'
|
||||
3, 66, 127, 64, // 49 - '1'
|
||||
5, 114, 73, 73, 73, 70, // 50 - '2'
|
||||
5, 33, 65, 73, 77, 51, // 51 - '3'
|
||||
5, 24, 20, 18, 127, 16, // 52 - '4'
|
||||
5, 39, 69, 69, 69, 57, // 53 - '5'
|
||||
5, 60, 74, 73, 73, 49, // 54 - '6'
|
||||
5, 65, 33, 17, 9, 7, // 55 - '7'
|
||||
5, 54, 73, 73, 73, 54, // 56 - '8'
|
||||
5, 70, 73, 73, 41, 30, // 57 - '9'
|
||||
1, 20, // 58 - ':'
|
||||
2, 128, 104, // 59 - ';'
|
||||
4, 8, 20, 34, 65, // 60 - '<'
|
||||
5, 20, 20, 20, 20, 20, // 61 - '='
|
||||
4, 65, 34, 20, 8, // 62 - '>'
|
||||
5, 2, 1, 89, 9, 6, // 63 - '?'
|
||||
5, 62, 65, 93, 89, 78, // 64 - '@'
|
||||
5, 124, 18, 17, 18, 124, // 65 - 'A'
|
||||
5, 127, 73, 73, 73, 54, // 66 - 'B'
|
||||
5, 62, 65, 65, 65, 34, // 67 - 'C'
|
||||
5, 127, 65, 65, 65, 62, // 68 - 'D'
|
||||
5, 127, 73, 73, 73, 65, // 69 - 'E'
|
||||
5, 127, 9, 9, 9, 1, // 70 - 'F'
|
||||
5, 62, 65, 65, 81, 115, // 71 - 'G'
|
||||
5, 127, 8, 8, 8, 127, // 72 - 'H'
|
||||
3, 65, 127, 65, // 73 - 'I'
|
||||
5, 32, 64, 65, 63, 1, // 74 - 'J'
|
||||
5, 127, 8, 20, 34, 65, // 75 - 'K'
|
||||
5, 127, 64, 64, 64, 64, // 76 - 'L'
|
||||
5, 127, 2, 28, 2, 127, // 77 - 'M'
|
||||
5, 127, 4, 8, 16, 127, // 78 - 'N'
|
||||
5, 62, 65, 65, 65, 62, // 79 - 'O'
|
||||
5, 127, 9, 9, 9, 6, // 80 - 'P'
|
||||
5, 62, 65, 81, 33, 94, // 81 - 'Q'
|
||||
5, 127, 9, 25, 41, 70, // 82 - 'R'
|
||||
5, 38, 73, 73, 73, 50, // 83 - 'S'
|
||||
5, 3, 1, 127, 1, 3, // 84 - 'T'
|
||||
5, 63, 64, 64, 64, 63, // 85 - 'U'
|
||||
5, 31, 32, 64, 32, 31, // 86 - 'V'
|
||||
5, 63, 64, 56, 64, 63, // 87 - 'W'
|
||||
5, 99, 20, 8, 20, 99, // 88 - 'X'
|
||||
5, 3, 4, 120, 4, 3, // 89 - 'Y'
|
||||
5, 97, 89, 73, 77, 67, // 90 - 'Z'
|
||||
3, 127, 65, 65, // 91 - '['
|
||||
5, 2, 4, 8, 16, 32, // 92 - '\'
|
||||
3, 65, 65, 127, // 93 - ']'
|
||||
5, 4, 2, 1, 2, 4, // 94 - '^'
|
||||
5, 64, 64, 64, 64, 64, // 95 - '_'
|
||||
2, 3, 4, // 96 - '`'
|
||||
5, 32, 84, 84, 120, 64, // 97 - 'a'
|
||||
5, 127, 40, 68, 68, 56, // 98 - 'b'
|
||||
5, 56, 68, 68, 68, 40, // 99 - 'c'
|
||||
5, 56, 68, 68, 40, 127, // 100 - 'd'
|
||||
5, 56, 84, 84, 84, 24, // 101 - 'e'
|
||||
4, 8, 126, 9, 2, // 102 - 'f'
|
||||
5, 24, 164, 164, 156, 120, // 103 - 'g'
|
||||
5, 127, 8, 4, 4, 120, // 104 - 'h'
|
||||
3, 68, 125, 64, // 105 - 'i'
|
||||
4, 64, 128, 128, 122, // 106 - 'j'
|
||||
4, 127, 16, 40, 68, // 107 - 'k'
|
||||
3, 65, 127, 64, // 108 - 'l'
|
||||
5, 124, 4, 120, 4, 120, // 109 - 'm'
|
||||
5, 124, 8, 4, 4, 120, // 110 - 'n'
|
||||
5, 56, 68, 68, 68, 56, // 111 - 'o'
|
||||
5, 252, 24, 36, 36, 24, // 112 - 'p'
|
||||
5, 24, 36, 36, 24, 252, // 113 - 'q'
|
||||
5, 124, 8, 4, 4, 8, // 114 - 'r'
|
||||
5, 72, 84, 84, 84, 36, // 115 - 's'
|
||||
4, 4, 63, 68, 36, // 116 - 't'
|
||||
5, 60, 64, 64, 32, 124, // 117 - 'u'
|
||||
5, 28, 32, 64, 32, 28, // 118 - 'v'
|
||||
5, 60, 64, 48, 64, 60, // 119 - 'w'
|
||||
5, 68, 40, 16, 40, 68, // 120 - 'x'
|
||||
5, 76, 144, 144, 144, 124, // 121 - 'y'
|
||||
5, 68, 100, 84, 76, 68, // 122 - 'z'
|
||||
3, 8, 54, 65, // 123 - '{'
|
||||
1, 119, // 124 - '|'
|
||||
3, 65, 54, 8, // 125 - '}'
|
||||
5, 2, 1, 2, 4, 2, // 126 - '~'
|
||||
0, // 127 - 'Unused'
|
||||
6, 20, 62, 85, 85, 65, 34, // 128 - 'Euro sign'
|
||||
0, // 129 - 'Not used'
|
||||
2, 128, 96, // 130 - 'Single low 9 quotation mark'
|
||||
5, 192, 136, 126, 9, 3, // 131 - 'f with hook'
|
||||
4, 128, 96, 128, 96, // 132 - 'Single low 9 quotation mark'
|
||||
8, 96, 96, 0, 96, 96, 0, 96, 96, // 133 - 'Horizontal ellipsis'
|
||||
3, 4, 126, 4, // 134 - 'Dagger'
|
||||
3, 20, 126, 20, // 135 - 'Double dagger'
|
||||
4, 2, 1, 1, 2, // 136 - 'Modifier circumflex'
|
||||
7, 35, 19, 104, 100, 2, 97, 96, // 137 - 'Per mille sign'
|
||||
5, 72, 85, 86, 85, 36, // 138 - 'S with caron'
|
||||
3, 8, 20, 34, // 139 - '< quotation'
|
||||
6, 62, 65, 65, 127, 73, 73, // 140 - 'OE'
|
||||
0, // 141 - 'Not used'
|
||||
5, 68, 101, 86, 77, 68, // 142 - 'z with caron'
|
||||
0, // 143 - 'Not used'
|
||||
0, // 144 - 'Not used'
|
||||
2, 3, 4, // 145 - 'Left single quote mark'
|
||||
2, 4, 3, // 146 - 'Right single quote mark'
|
||||
4, 3, 4, 3, 4, // 147 - 'Left double quote marks'
|
||||
4, 4, 3, 4, 3, // 148 - 'Right double quote marks'
|
||||
4, 0, 24, 60, 24, // 149 - 'Bullet Point'
|
||||
3, 8, 8, 8, // 150 - 'En dash'
|
||||
5, 8, 8, 8, 8, 8, // 151 - 'Em dash'
|
||||
4, 2, 1, 2, 1, // 152 - 'Small ~'
|
||||
7, 1, 15, 1, 0, 15, 2, 15, // 153 - 'TM'
|
||||
5, 72, 85, 86, 85, 36, // 154 - 's with caron'
|
||||
3, 34, 20, 8, // 155 - '> quotation'
|
||||
7, 56, 68, 68, 124, 84, 84, 8, // 156 - 'oe'
|
||||
0, // 157 - 'Not used'
|
||||
5, 68, 101, 86, 77, 68, // 158 - 'z with caron'
|
||||
5, 12, 17, 96, 17, 12, // 159 - 'Y diaresis'
|
||||
2, 0, 0, // 160 - 'Non-breaking space'
|
||||
1, 125, // 161 - 'Inverted !'
|
||||
5, 60, 36, 126, 36, 36, // 162 - 'Cent sign'
|
||||
5, 72, 126, 73, 65, 102, // 163 - 'Pound sign'
|
||||
5, 34, 28, 20, 28, 34, // 164 - 'Currency sign'
|
||||
5, 43, 47, 252, 47, 43, // 165 - 'Yen'
|
||||
1, 119, // 166 - '|'
|
||||
4, 102, 137, 149, 106, // 167 - 'Section sign'
|
||||
3, 1, 0, 1, // 168 - 'Spacing diaresis'
|
||||
7, 62, 65, 93, 85, 85, 65, 62, // 169 - 'Copyright'
|
||||
3, 13, 13, 15, // 170 - 'Feminine Ordinal Ind.'
|
||||
5, 8, 20, 42, 20, 34, // 171 - '<<'
|
||||
5, 8, 8, 8, 8, 56, // 172 - 'Not sign'
|
||||
0, // 173 - 'Soft Hyphen'
|
||||
7, 62, 65, 127, 75, 117, 65, 62, // 174 - 'Registered Trademark'
|
||||
5, 1, 1, 1, 1, 1, // 175 - 'Spacing Macron Overline'
|
||||
3, 2, 5, 2, // 176 - 'Degree'
|
||||
5, 68, 68, 95, 68, 68, // 177 - '+/-'
|
||||
3, 25, 21, 19, // 178 - 'Superscript 2'
|
||||
3, 17, 21, 31, // 179 - 'Superscript 3'
|
||||
2, 2, 1, // 180 - 'Acute accent'
|
||||
4, 252, 64, 64, 60, // 181 - 'micro (mu)'
|
||||
5, 6, 9, 127, 1, 127, // 182 - 'Paragraph Mark'
|
||||
2, 24, 24, // 183 - 'Middle Dot'
|
||||
3, 128, 128, 96, // 184 - 'Spacing sedilla'
|
||||
2, 2, 31, // 185 - 'Superscript 1'
|
||||
4, 6, 9, 9, 6, // 186 - 'Masculine Ordinal Ind.'
|
||||
5, 34, 20, 42, 20, 8, // 187 - '>>'
|
||||
6, 64, 47, 16, 40, 52, 250, // 188 - '1/4'
|
||||
6, 64, 47, 16, 200, 172, 186, // 189 - '1/2'
|
||||
6, 85, 53, 31, 40, 52, 250, // 190 - '3/4'
|
||||
5, 48, 72, 77, 64, 32, // 191 - 'Inverted ?'
|
||||
5, 120, 20, 21, 22, 120, // 192 - 'A grave'
|
||||
5, 120, 22, 21, 20, 120, // 193 - 'A acute'
|
||||
5, 122, 21, 21, 21, 122, // 194 - 'A circumflex'
|
||||
5, 120, 22, 21, 22, 121, // 195 - 'A tilde'
|
||||
5, 120, 21, 20, 21, 120, // 196 - 'A diaresis'
|
||||
5, 120, 20, 21, 20, 120, // 197 - 'A ring above'
|
||||
6, 124, 10, 9, 127, 73, 73, // 198 - 'AE'
|
||||
5, 30, 161, 161, 97, 18, // 199 - 'C sedilla'
|
||||
4, 124, 85, 86, 68, // 200 - 'E grave'
|
||||
4, 124, 86, 85, 68, // 201 - 'E acute'
|
||||
4, 126, 85, 85, 70, // 202 - 'E circumflex'
|
||||
4, 124, 85, 84, 69, // 203 - 'E diaresis'
|
||||
3, 68, 125, 70, // 204 - 'I grave'
|
||||
3, 68, 126, 69, // 205 - 'I acute'
|
||||
3, 70, 125, 70, // 206 - 'I circumplex'
|
||||
3, 69, 124, 69, // 207 - 'I diaresis'
|
||||
6, 4, 127, 69, 65, 65, 62, // 208 - 'Capital Eth'
|
||||
5, 124, 10, 17, 34, 125, // 209 - 'N tilde'
|
||||
5, 56, 68, 69, 70, 56, // 210 - 'O grave'
|
||||
5, 56, 70, 69, 68, 56, // 211 - 'O acute'
|
||||
5, 58, 69, 69, 69, 58, // 212 - 'O circumflex'
|
||||
5, 56, 70, 69, 70, 57, // 213 - 'O tilde'
|
||||
5, 56, 69, 68, 69, 56, // 214 - 'O diaresis'
|
||||
5, 34, 20, 8, 20, 34, // 215 - 'Multiplication sign'
|
||||
7, 64, 62, 81, 73, 69, 62, 1, // 216 - 'O slashed'
|
||||
5, 60, 65, 66, 64, 60, // 217 - 'U grave'
|
||||
5, 60, 64, 66, 65, 60, // 218 - 'U acute'
|
||||
5, 58, 65, 65, 65, 58, // 219 - 'U circumflex'
|
||||
5, 60, 65, 64, 65, 60, // 220 - 'U diaresis'
|
||||
5, 12, 16, 98, 17, 12, // 221 - 'Y acute'
|
||||
4, 127, 18, 18, 12, // 222 - 'Capital thorn'
|
||||
4, 254, 37, 37, 26, // 223 - 'Small letter sharp S'
|
||||
5, 32, 84, 85, 122, 64, // 224 - 'a grave'
|
||||
5, 32, 84, 86, 121, 64, // 225 - 'a acute'
|
||||
5, 34, 85, 85, 121, 66, // 226 - 'a circumflex'
|
||||
5, 32, 86, 85, 122, 65, // 227 - 'a tilde'
|
||||
5, 32, 85, 84, 121, 64, // 228 - 'a diaresis'
|
||||
5, 32, 84, 85, 120, 64, // 229 - 'a ring above'
|
||||
7, 32, 84, 84, 124, 84, 84, 8, // 230 - 'ae'
|
||||
5, 24, 36, 164, 228, 40, // 231 - 'c sedilla'
|
||||
5, 56, 84, 85, 86, 88, // 232 - 'e grave'
|
||||
5, 56, 84, 86, 85, 88, // 233 - 'e acute'
|
||||
5, 58, 85, 85, 85, 90, // 234 - 'e circumflex'
|
||||
5, 56, 85, 84, 85, 88, // 235 - 'e diaresis'
|
||||
3, 68, 125, 66, // 236 - 'i grave'
|
||||
3, 68, 126, 65, // 237 - 'i acute'
|
||||
3, 70, 125, 66, // 238 - 'i circumflex'
|
||||
3, 69, 124, 65, // 239 - 'i diaresis'
|
||||
4, 48, 75, 74, 61, // 240 - 'Small eth'
|
||||
4, 122, 9, 10, 113, // 241 - 'n tilde'
|
||||
5, 56, 68, 69, 70, 56, // 242 - 'o grave'
|
||||
5, 56, 70, 69, 68, 56, // 243 - 'o acute'
|
||||
5, 58, 69, 69, 69, 58, // 244 - 'o circumflex'
|
||||
5, 56, 70, 69, 70, 57, // 245 - 'o tilde'
|
||||
5, 56, 69, 68, 69, 56, // 246 - 'o diaresis'
|
||||
5, 8, 8, 42, 8, 8, // 247 - 'Division sign'
|
||||
6, 64, 56, 84, 76, 68, 58, // 248 - 'o slashed'
|
||||
5, 60, 65, 66, 32, 124, // 249 - 'u grave'
|
||||
5, 60, 64, 66, 33, 124, // 250 - 'u acute'
|
||||
5, 58, 65, 65, 33, 122, // 251 - 'u circumflex'
|
||||
5, 60, 65, 64, 33, 124, // 252 - 'u diaresis'
|
||||
4, 156, 162, 161, 124, // 253 - 'y acute'
|
||||
4, 252, 72, 72, 48, // 254 - 'small thorn'
|
||||
4, 157, 160, 160, 125, // 255 - 'y diaresis'
|
||||
};
|
||||
@@ -0,0 +1,57 @@
|
||||
; PlatformIO Project Configuration File
|
||||
;
|
||||
; Build options: build flags, source filter
|
||||
; Upload options: custom upload port, speed and extra flags
|
||||
; Library options: dependencies, extra library storages
|
||||
; Advanced options: extra scripting
|
||||
;
|
||||
; Please visit documentation for the other options and examples
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[platformio]
|
||||
workspace_dir = .pio.nosync
|
||||
default_envs = esp12eDev
|
||||
|
||||
[env:esp12eDev]
|
||||
platform = espressif8266
|
||||
board = esp12e
|
||||
framework = arduino
|
||||
board_build.filesystem = littlefs
|
||||
monitor_speed = 115200
|
||||
upload_speed = 115200
|
||||
#--- upload_port only needed for FileSys upload
|
||||
upload_port = /dev/cu.usbserial-3224144
|
||||
build_flags = -DDEBUG
|
||||
lib_ldf_mode = deep+
|
||||
lib_deps =
|
||||
bblanchon/ArduinoJson@6.19.4
|
||||
https://github.com/PaulStoffregen/Time
|
||||
jandrassy/TelnetStream@^1.3.0
|
||||
majicdesigns/MD_Parola@^3.7.3
|
||||
tzapu/WiFiManager@^0.16.0
|
||||
https://github.com/mrWheel/ModUpdateServer
|
||||
|
||||
monitor_filters =
|
||||
esp8266_exception_decoder
|
||||
|
||||
[env:esp12eProd]
|
||||
platform = espressif8266
|
||||
board = esp12e
|
||||
framework = arduino
|
||||
board_build.filesystem = littlefs
|
||||
monitor_speed = 115200
|
||||
upload_speed = 115200
|
||||
#--- upload_port only needed for FileSys upload
|
||||
upload_port = /dev/cu.usbserial-3224144
|
||||
lib_ldf_mode = deep+
|
||||
lib_deps =
|
||||
bblanchon/ArduinoJson@6.19.4
|
||||
bblanchon/StreamUtils@^1.9.0
|
||||
https://github.com/PaulStoffregen/Time
|
||||
jandrassy/TelnetStream@0.1.0
|
||||
majicdesigns/MD_Parola@^3.7.3
|
||||
tzapu/WiFiManager@^0.16.0
|
||||
https://github.com/mrWheel/ModUpdateServer
|
||||
|
||||
monitor_filters =
|
||||
esp8266_exception_decoder
|
||||
@@ -0,0 +1,375 @@
|
||||
/*
|
||||
***************************************************************************
|
||||
** Program : restAPI, part of ESP_ticker
|
||||
**
|
||||
** Copyright (c) 2021 Willem Aandewiel
|
||||
**
|
||||
** TERMS OF USE: MIT License. See bottom of file.
|
||||
***************************************************************************
|
||||
*/
|
||||
|
||||
#include <FS.h>
|
||||
#include <LittleFS.h>
|
||||
|
||||
//=======================================================================
|
||||
void processAPI()
|
||||
{
|
||||
char fName[40] = "";
|
||||
char URI[50] = "";
|
||||
String words[10];
|
||||
|
||||
strncpy( URI, httpServer.uri().c_str(), sizeof(URI) );
|
||||
|
||||
if (httpServer.method() == HTTP_GET)
|
||||
DebugTf("from[%s] URI[%s] method[GET] \r\n"
|
||||
, httpServer.client().remoteIP().toString().c_str()
|
||||
, URI);
|
||||
else DebugTf("from[%s] URI[%s] method[PUT] \r\n"
|
||||
, httpServer.client().remoteIP().toString().c_str()
|
||||
, URI);
|
||||
|
||||
if (ESP.getFreeHeap() < 8500) // to prevent firmware from crashing!
|
||||
{
|
||||
DebugTf("==> Bailout due to low heap (%d bytes))\r\n", ESP.getFreeHeap() );
|
||||
httpServer.send(500, "text/plain", "500: internal server error (low heap)\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
int8_t wc = splitString(URI, '/', words, 10);
|
||||
|
||||
if (Verbose)
|
||||
{
|
||||
DebugT(">>");
|
||||
for (int w=0; w<wc; w++)
|
||||
{
|
||||
Debugf("word[%d] => [%s], ", w, words[w].c_str());
|
||||
}
|
||||
Debugln(" ");
|
||||
}
|
||||
|
||||
if (words[1] != "api")
|
||||
{
|
||||
sendApiNotFound(URI);
|
||||
return;
|
||||
}
|
||||
|
||||
if (words[2] != "v0")
|
||||
{
|
||||
sendApiNotFound(URI);
|
||||
return;
|
||||
}
|
||||
|
||||
if (words[3] == "devinfo")
|
||||
{
|
||||
sendDeviceInfo();
|
||||
}
|
||||
else if (words[3] == "devtime")
|
||||
{
|
||||
sendDeviceTime();
|
||||
}
|
||||
else if (words[3] == "settings")
|
||||
{
|
||||
if (httpServer.method() == HTTP_PUT || httpServer.method() == HTTP_POST)
|
||||
{
|
||||
postSettings();
|
||||
}
|
||||
else
|
||||
{
|
||||
sendDeviceSettings();
|
||||
}
|
||||
}
|
||||
else if (words[3] == "messages")
|
||||
{
|
||||
if (httpServer.method() == HTTP_PUT || httpServer.method() == HTTP_POST)
|
||||
{
|
||||
postMessages();
|
||||
}
|
||||
else
|
||||
{
|
||||
sendLocalMessages();
|
||||
}
|
||||
}
|
||||
else if (words[3] == "news")
|
||||
{
|
||||
sendNewsMessages();
|
||||
}
|
||||
else sendApiNotFound(URI);
|
||||
|
||||
} // processAPI()
|
||||
|
||||
|
||||
//=======================================================================
|
||||
void sendDeviceInfo()
|
||||
{
|
||||
FSInfo LittleFSinfo;
|
||||
|
||||
sendStartJsonObj("devinfo");
|
||||
|
||||
sendNestedJsonObj("author", "Willem Aandewiel (www.aandewiel.nl)");
|
||||
sendNestedJsonObj("fwversion", FWversion);
|
||||
|
||||
snprintf(cMsg, sizeof(cMsg), "%s %s", __DATE__, __TIME__);
|
||||
sendNestedJsonObj("compiled", cMsg);
|
||||
sendNestedJsonObj("hostname", settingHostname);
|
||||
sendNestedJsonObj("ipaddress", WiFi.localIP().toString().c_str());
|
||||
sendNestedJsonObj("macaddress", WiFi.macAddress().c_str());
|
||||
sendNestedJsonObj("freeheap", ESP.getFreeHeap());
|
||||
sendNestedJsonObj("maxfreeblock", ESP.getMaxFreeBlockSize());
|
||||
sendNestedJsonObj("chipid", String( ESP.getChipId(), HEX ).c_str());
|
||||
sendNestedJsonObj("coreversion", String( ESP.getCoreVersion() ).c_str() );
|
||||
sendNestedJsonObj("sdkversion", String( ESP.getSdkVersion() ).c_str());
|
||||
sendNestedJsonObj("cpufreq", ESP.getCpuFreqMHz());
|
||||
sendNestedJsonObj("sketchsize", formatFloat( (ESP.getSketchSize() / 1024.0), 3));
|
||||
sendNestedJsonObj("freesketchspace", formatFloat( (ESP.getFreeSketchSpace() / 1024.0), 3));
|
||||
|
||||
snprintf(cMsg, sizeof(cMsg), "%08X", ESP.getFlashChipId());
|
||||
sendNestedJsonObj("flashchipid", cMsg); // flashChipId
|
||||
sendNestedJsonObj("flashchipsize", formatFloat((ESP.getFlashChipSize() / 1024.0 / 1024.0), 3));
|
||||
sendNestedJsonObj("flashchiprealsize", formatFloat((ESP.getFlashChipRealSize() / 1024.0 / 1024.0), 3));
|
||||
|
||||
LittleFS.info(LittleFSinfo);
|
||||
sendNestedJsonObj("spiffssize", formatFloat( (LittleFSinfo.totalBytes / (1024.0 * 1024.0)), 0));
|
||||
|
||||
sendNestedJsonObj("flashchipspeed", formatFloat((ESP.getFlashChipSpeed() / 1000.0 / 1000.0), 0));
|
||||
|
||||
FlashMode_t ideMode = ESP.getFlashChipMode();
|
||||
sendNestedJsonObj("flashchipmode", flashMode[ideMode]);
|
||||
sendNestedJsonObj("boardtype",
|
||||
#ifdef ARDUINO_ESP8266_NODEMCU
|
||||
"ESP8266_NODEMCU"
|
||||
#endif
|
||||
#ifdef ARDUINO_ESP8266_GENERIC
|
||||
"ESP8266_GENERIC"
|
||||
#endif
|
||||
#ifdef ESP8266_ESP01
|
||||
"ESP8266_ESP01"
|
||||
#endif
|
||||
#ifdef ESP8266_ESP12
|
||||
"ESP8266_ESP12"
|
||||
#endif
|
||||
);
|
||||
sendNestedJsonObj("ssid", WiFi.SSID().c_str());
|
||||
sendNestedJsonObj("wifirssi", WiFi.RSSI());
|
||||
|
||||
sendNestedJsonObj("lastreset", lastReset);
|
||||
|
||||
httpServer.sendContent("\r\n]}\r\n");
|
||||
|
||||
} // sendDeviceInfo()
|
||||
|
||||
|
||||
//=======================================================================
|
||||
void sendDeviceTime()
|
||||
{
|
||||
struct tm* timeinfo;
|
||||
char actTime[50];
|
||||
|
||||
sendStartJsonObj("devtime");
|
||||
// Get current time
|
||||
time(&now);
|
||||
timeinfo = localtime(&now); // This is now correct
|
||||
|
||||
snprintf(actTime, 49, "%02d-%02d-%04d %02d:%02d"
|
||||
, timeinfo->tm_mday, timeinfo->tm_mon+1, timeinfo->tm_year+1900
|
||||
, timeinfo->tm_hour, timeinfo->tm_min);
|
||||
sendNestedJsonObj("dateTime", actTime);
|
||||
sendNestedJsonObj("epoch", (uint32_t)now);
|
||||
|
||||
sendEndJsonObj();
|
||||
|
||||
} // sendDeviceTime()
|
||||
|
||||
|
||||
//=======================================================================
|
||||
void sendDeviceSettings()
|
||||
{
|
||||
DebugTln("sending device settings ...\r");
|
||||
|
||||
sendStartJsonObj("settings");
|
||||
|
||||
sendJsonSettingObj("hostname", settingHostname, "s", sizeof(settingHostname) -1);
|
||||
sendJsonSettingObj("localMaxMsg", settingLocalMaxMsg, "i", 1, 20);
|
||||
sendJsonSettingObj("textSpeed", settingTextSpeed, "i", 10, MAX_SPEED);
|
||||
sendJsonSettingObj("LDRlowOffset", settingLDRlowOffset, "i", 0, 500);
|
||||
sendJsonSettingObj("LDRhighOffset", settingLDRhighOffset, "i", 500, 1024);
|
||||
sendJsonSettingObj("maxIntensity", settingMaxIntensity, "i", 0, 15);
|
||||
sendJsonSettingObj("weerliveAUTH", settingWeerLiveAUTH, "s", sizeof(settingWeerLiveAUTH) -1);
|
||||
sendJsonSettingObj("weerliveLocation", settingWeerLiveLocation, "s", sizeof(settingWeerLiveLocation) -1);
|
||||
sendJsonSettingObj("weerliveInterval", settingWeerLiveInterval, "i", 15, 120);
|
||||
sendJsonSettingObj("newsapiAUTH", settingNewsAUTH, "s", sizeof(settingNewsAUTH) -1);
|
||||
sendJsonSettingObj("newsapiMaxMsg", settingNewsMaxMsg, "i", 1, 20);
|
||||
sendJsonSettingObj("newsapiInterval", settingNewsInterval, "i", 15, 120);
|
||||
sendJsonSettingObj("newsNoWords", settingNewsNoWords, "s", sizeof(settingNewsNoWords) -1);
|
||||
|
||||
sendEndJsonObj();
|
||||
|
||||
} // sendDeviceSettings()
|
||||
|
||||
|
||||
//=======================================================================
|
||||
void sendLocalMessages()
|
||||
{
|
||||
int mID;
|
||||
|
||||
DebugTln("sending local Messages ...\r");
|
||||
|
||||
sendStartJsonObj("messages");
|
||||
|
||||
for (mID=1; mID <= settingLocalMaxMsg; mID++)
|
||||
{
|
||||
if (readFileById("LCL", mID))
|
||||
{
|
||||
//--- next 5 lines are realy dirty ...
|
||||
char newMsg[LOCAL_SIZE] = "";
|
||||
String tmp = String(fileMessage);
|
||||
tmp.replace("\\", "\\\\");
|
||||
sprintf(newMsg, "%s", tmp.c_str());
|
||||
//sendJsonSettingObj(intToStr(mID), fileMessage, "s", sizeof(fileMessage) -1);
|
||||
sendJsonSettingObj(intToStr(mID), newMsg, "s", sizeof(newMsg) -1);
|
||||
}
|
||||
else
|
||||
{
|
||||
sendJsonSettingObj(intToStr(mID), "", "s", sizeof(fileMessage) -1);
|
||||
}
|
||||
}
|
||||
|
||||
sendEndJsonObj();
|
||||
|
||||
} // sendlocalMessages()
|
||||
|
||||
|
||||
//=======================================================================
|
||||
void sendNewsMessages()
|
||||
{
|
||||
int nID;
|
||||
|
||||
DebugTln("sending news Messages ...\r");
|
||||
|
||||
sendStartJsonObj("newsapi");
|
||||
|
||||
for (nID=0; nID <= settingNewsMaxMsg; nID++)
|
||||
{
|
||||
if (readFileById("NWS", nID))
|
||||
{
|
||||
sendJsonSettingObj(intToStr(nID), fileMessage, "s", sizeof(fileMessage) -1);
|
||||
}
|
||||
}
|
||||
|
||||
sendEndJsonObj();
|
||||
|
||||
} // sendNewsMessages()
|
||||
|
||||
|
||||
//=======================================================================
|
||||
void postMessages()
|
||||
{
|
||||
//------------------------------------------------------------
|
||||
// json string: {"name":"4","value":"Bericht tekst"}
|
||||
//------------------------------------------------------------
|
||||
// so, why not use ArduinoJSON library?
|
||||
// I say: try it yourself ;-) It won't be easy
|
||||
String wOut[5];
|
||||
String wPair[5];
|
||||
String jsonIn = httpServer.arg(0).c_str();
|
||||
char field[25] = "";
|
||||
char newValue[101]="";
|
||||
jsonIn.replace("{", "");
|
||||
jsonIn.replace("}", "");
|
||||
jsonIn.replace("\"", "");
|
||||
int8_t wp = splitString(jsonIn.c_str(), ',', wPair, 5) ;
|
||||
for (int i=0; i<wp; i++)
|
||||
{
|
||||
//DebugTf("[%d] -> pair[%s]\r\n", i, wPair[i].c_str());
|
||||
int8_t wc = splitString(wPair[i].c_str(), ':', wOut, 5) ;
|
||||
//DebugTf("==> [%s] -> field[%s]->val[%s]\r\n", wPair[i].c_str(), wOut[0].c_str(), wOut[1].c_str());
|
||||
if (wOut[0].equalsIgnoreCase("name")) strCopy(field, sizeof(field), wOut[1].c_str());
|
||||
if (wOut[0].equalsIgnoreCase("value")) strCopy(newValue, sizeof(newValue), wOut[1].c_str());
|
||||
}
|
||||
DebugTf("--> field[%s] => newValue[%s]\r\n", field, newValue);
|
||||
updateMessage(field, newValue);
|
||||
httpServer.send(200, "application/json", httpServer.arg(0));
|
||||
|
||||
} // postMessages()
|
||||
|
||||
|
||||
//=======================================================================
|
||||
void postSettings()
|
||||
{
|
||||
//------------------------------------------------------------
|
||||
// json string: {"name":"settingInterval","value":9}
|
||||
// json string: {"name":"settingHostname","value":"abc"}
|
||||
//------------------------------------------------------------
|
||||
// so, why not use ArduinoJSON library?
|
||||
// I say: try it yourself ;-) It won't be easy
|
||||
String wOut[5];
|
||||
String wPair[5];
|
||||
String jsonIn = httpServer.arg(0).c_str();
|
||||
char field[25] = "";
|
||||
char newValue[101]="";
|
||||
jsonIn.replace("{", "");
|
||||
jsonIn.replace("}", "");
|
||||
jsonIn.replace("\"", "");
|
||||
int8_t wp = splitString(jsonIn.c_str(), ',', wPair, 5) ;
|
||||
for (int i=0; i<wp; i++)
|
||||
{
|
||||
//DebugTf("[%d] -> pair[%s]\r\n", i, wPair[i].c_str());
|
||||
int8_t wc = splitString(wPair[i].c_str(), ':', wOut, 5) ;
|
||||
//DebugTf("==> [%s] -> field[%s]->val[%s]\r\n", wPair[i].c_str(), wOut[0].c_str(), wOut[1].c_str());
|
||||
if (wOut[0].equalsIgnoreCase("name")) strCopy(field, sizeof(field), wOut[1].c_str());
|
||||
if (wOut[0].equalsIgnoreCase("value")) strCopy(newValue, sizeof(newValue), wOut[1].c_str());
|
||||
}
|
||||
DebugTf("--> field[%s] => newValue[%s]\r\n", field, newValue);
|
||||
updateSetting(field, newValue);
|
||||
httpServer.send(200, "application/json", httpServer.arg(0));
|
||||
|
||||
} // postSettings()
|
||||
|
||||
|
||||
//====================================================
|
||||
void sendApiNotFound(const char *URI)
|
||||
{
|
||||
httpServer.sendHeader("Access-Control-Allow-Origin", "*");
|
||||
httpServer.setContentLength(CONTENT_LENGTH_UNKNOWN);
|
||||
httpServer.send ( 404, "text/html", "<!DOCTYPE HTML><html><head>");
|
||||
|
||||
strCopy(cMsg, sizeof(cMsg), "<style>body { background-color: lightgray; font-size: 15pt;}");
|
||||
strConcat(cMsg, sizeof(cMsg), "</style></head><body>");
|
||||
httpServer.sendContent(cMsg);
|
||||
|
||||
strCopy(cMsg, sizeof(cMsg), "<h1>ESP - lichtKrant</h1><b1>");
|
||||
httpServer.sendContent(cMsg);
|
||||
|
||||
strCopy(cMsg, sizeof(cMsg), "<br>[<b>");
|
||||
strConcat(cMsg, sizeof(cMsg), URI);
|
||||
strConcat(cMsg, sizeof(cMsg), "</b>] is not a valid ");
|
||||
httpServer.sendContent(cMsg);
|
||||
|
||||
strCopy(cMsg, sizeof(cMsg), "</body></html>\r\n");
|
||||
httpServer.sendContent(cMsg);
|
||||
|
||||
} // sendApiNotFound()
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
* persons to whom the Software is furnished to do so, subject to the
|
||||
* following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
|
||||
* OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
|
||||
* THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
****************************************************************************
|
||||
*/
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
***************************************************************************
|
||||
** Program : sendIndexPage
|
||||
**
|
||||
** Copyright (c) 2021 Willem Aandewiel
|
||||
**
|
||||
** TERMS OF USE: MIT License. See bottom of file.
|
||||
***************************************************************************
|
||||
*/
|
||||
|
||||
static const char indexPage[] =
|
||||
R"(
|
||||
<!DOCTYPE html>
|
||||
<html charset="UTF-8">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" type="text/css" href="/index.css">
|
||||
<script src="/index.js"></script>
|
||||
<title>ESP Lichtkrant</title>
|
||||
</head>
|
||||
<body>
|
||||
<font face="Arial">
|
||||
<!-- <div class="dev-header"> -->
|
||||
<div class="header">
|
||||
<h1>
|
||||
<span id="sysName">ESP - lichtKrant</span>
|
||||
<span id="devName" style='font-size: small;'>-</span>
|
||||
<span id="devVersion" style='font-size: small;'>[version]</span>
|
||||
<span id='theTime' class='nav-item nav-clock'>00:00</span>
|
||||
</h1>
|
||||
</div>
|
||||
</font>
|
||||
<div id="displayMainPage" style="display:none">
|
||||
<div class="nav-container">
|
||||
<div class='nav-left'>
|
||||
<a id='saveMsg' class='nav-item tabButton' style="background: lightblue;">opslaan</a>
|
||||
<a id='M_FSexplorer' class='nav-img'>
|
||||
<img src='/FSexplorer.png' alt='FSexplorer'></a>
|
||||
<a id='Settings' class='nav-img'>
|
||||
<img src='/settings.png' alt='Settings'></a>
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
<div id="mainPage">
|
||||
<div id="waiting">Wait! retrieving local messages .....</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="displaySettingsPage" style="display:none">
|
||||
<div class="nav-container">
|
||||
<div class='nav-left'>
|
||||
<a id='back' class='nav-item tabButton' style="background: lightblue;">Terug</a>
|
||||
<a id='saveSettings' class='nav-item tabButton' style="background: lightblue;">opslaan</a>
|
||||
<a id='S_FSexplorer' class='nav-img'>
|
||||
<img src='/FSexplorer.png' alt='FSexplorer'></a>
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
<div id="settingsPage"></div>
|
||||
</div>
|
||||
|
||||
<!-- KEEP THIS --->
|
||||
|
||||
<!-- Pin to bottom right corner -->
|
||||
<div class="bottom right-0">2021 © Willem Aandewiel</div>
|
||||
|
||||
<!-- Pin to bottom left corner -->
|
||||
<div id="message" class="bottom left-0">-</div>
|
||||
|
||||
<script>
|
||||
window.onload=bootsTrapMain;
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
)";
|
||||
|
||||
void sendIndexPage()
|
||||
{
|
||||
httpServer.send(200, "text/html", indexPage);
|
||||
|
||||
} // sendIndexPage()
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
* persons to whom the Software is furnished to do so, subject to the
|
||||
* following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
|
||||
* OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
|
||||
* THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
****************************************************************************
|
||||
*/
|
||||
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
***************************************************************************
|
||||
** Program : settingsStuff, part of ESP_ticker
|
||||
**
|
||||
** Copyright (c) 2021 Willem Aandewiel
|
||||
**
|
||||
** TERMS OF USE: MIT License. See bottom of file.
|
||||
***************************************************************************
|
||||
*/
|
||||
|
||||
//=======================================================================
|
||||
void writeSettings(bool show)
|
||||
{
|
||||
DebugTf("Writing to [%s] ..\r\n", SETTINGS_FILE);
|
||||
File file = LittleFS.open(SETTINGS_FILE, "w"); // open for reading and writing
|
||||
if (!file)
|
||||
{
|
||||
DebugTf("open(%s, 'w') FAILED!!! --> Bailout\r\n", SETTINGS_FILE);
|
||||
return;
|
||||
}
|
||||
yield();
|
||||
|
||||
DebugT(F("Start writing setting data "));
|
||||
|
||||
file.print("Hostname = "); file.println(settingHostname); Debug(F("."));
|
||||
file.print("localMaxMsg = "); file.println(settingLocalMaxMsg); Debug(F("."));
|
||||
file.print("textSpeed = "); file.println(settingTextSpeed); Debug(F("."));
|
||||
file.print("maxIntensity = "); file.println(settingMaxIntensity); Debug(F("."));
|
||||
file.print("LDRlowOffset = "); file.println(settingLDRlowOffset); Debug(F("."));
|
||||
file.print("LDRhighOffset = "); file.println(settingLDRhighOffset); Debug(F("."));
|
||||
file.print("weerLiveAUTH = "); file.println(settingWeerLiveAUTH); Debug(F("."));
|
||||
file.print("weerLiveLocatie = "); file.println(settingWeerLiveLocation); Debug(F("."));
|
||||
file.print("weerLiveInterval = "); file.println(settingWeerLiveInterval); Debug(F("."));
|
||||
file.print("newsAUTH = "); file.println(settingNewsAUTH); Debug(F("."));
|
||||
file.print("newsNoWords = "); file.println(settingNewsNoWords); Debug(F("."));
|
||||
file.print("newsMaxMsg = "); file.println(settingNewsMaxMsg); Debug(F("."));
|
||||
file.print("newsInterval = "); file.println(settingNewsInterval); Debug(F("."));
|
||||
|
||||
file.close();
|
||||
|
||||
Debugln(F(" done"));
|
||||
|
||||
if (show)
|
||||
{
|
||||
DebugTln(F("Wrote this:"));
|
||||
DebugT(F(" Hostname = ")); Debugln(settingHostname);
|
||||
DebugT(F(" newsNoWords = ")); Debugln(settingNewsNoWords);
|
||||
DebugT(F(" localMaxMsg = ")); Debugln(settingLocalMaxMsg);
|
||||
DebugT(F(" textSpeed = ")); Debugln(settingTextSpeed);
|
||||
DebugT(F(" LDRlowOffset = ")); Debugln(settingLDRlowOffset);
|
||||
DebugT(F(" LDRhighOffset = ")); Debugln(settingLDRhighOffset);
|
||||
DebugT(F(" maxIntensity = ")); Debugln(settingMaxIntensity);
|
||||
DebugT(F(" weerLiveAUTH = ")); Debugln(settingWeerLiveAUTH);
|
||||
DebugT(F(" weerLiveLocatie = ")); Debugln(settingWeerLiveLocation);
|
||||
DebugT(F("weerLiveInterval = ")); Debugln(settingWeerLiveInterval);
|
||||
DebugT(F(" newsAUTH = ")); Debugln(settingNewsAUTH);
|
||||
DebugT(F(" newsMaxMsg = ")); Debugln(settingNewsMaxMsg);
|
||||
DebugT(F(" newsInterval = ")); Debugln(settingNewsInterval);
|
||||
|
||||
} // Verbose
|
||||
|
||||
} // writeSettings()
|
||||
|
||||
|
||||
//=======================================================================
|
||||
void readSettings(bool show)
|
||||
{
|
||||
String sTmp;
|
||||
String words[10];
|
||||
char cTmp[LOCAL_SIZE], cVal[101], cKey[101];
|
||||
|
||||
File file;
|
||||
|
||||
DebugTf(" %s ..\r\n", SETTINGS_FILE);
|
||||
|
||||
snprintf(settingHostname, sizeof(settingHostname), "%s", _HOSTNAME);
|
||||
snprintf(settingNewsNoWords, sizeof(settingNewsNoWords),"Voetbal, show, UEFA, KNVB");
|
||||
settingLocalMaxMsg = 5;
|
||||
settingTextSpeed = 25;
|
||||
settingLDRlowOffset = 70;
|
||||
settingLDRhighOffset = 700;
|
||||
settingMaxIntensity = 6;
|
||||
snprintf(settingWeerLiveAUTH, 50, "");
|
||||
snprintf(settingWeerLiveLocation, 50, "");
|
||||
settingWeerLiveInterval = 0;
|
||||
snprintf(settingNewsAUTH, 50, "");
|
||||
settingNewsMaxMsg = 4;
|
||||
settingNewsInterval = 0;
|
||||
|
||||
if (!LittleFS.exists(SETTINGS_FILE))
|
||||
{
|
||||
DebugTln(F(" .. file not found! --> created file!"));
|
||||
writeSettings(show);
|
||||
}
|
||||
|
||||
for (int T = 0; T < 2; T++)
|
||||
{
|
||||
file = LittleFS.open(SETTINGS_FILE, "r");
|
||||
if (!file)
|
||||
{
|
||||
if (T == 0) DebugTf(" .. something went wrong opening [%s]\r\n", SETTINGS_FILE);
|
||||
else DebugT(T);
|
||||
delay(100);
|
||||
}
|
||||
} // try T times ..
|
||||
|
||||
DebugTln(F("Reading settings:\r"));
|
||||
while(file.available())
|
||||
{
|
||||
sTmp = file.readStringUntil('\n');
|
||||
snprintf(cTmp, sizeof(cTmp), "%s", sTmp.c_str());
|
||||
//strTrim(cTmp, sizeof(cTmp), '\r');
|
||||
strTrimCntr(cTmp, sizeof(cTmp));
|
||||
//DebugTf("cTmp[%s] (%d)\r\n", cTmp, strlen(cTmp));
|
||||
int sEq = strIndex(cTmp, "=");
|
||||
strCopy(cKey, 100, cTmp, 0, sEq -1);
|
||||
strCopy(cVal, 100, cTmp, sEq +1, strlen(cTmp));
|
||||
//DebugTf("cKey[%s], cVal[%s]\r\n", cKey, cVal);
|
||||
strTrim(cKey, sizeof(cKey), ' ');
|
||||
strTrim(cVal, sizeof(cVal), ' ');
|
||||
//DebugTf("cKey[%s], cVal[%s]\r\n", cKey, cVal);
|
||||
|
||||
//strToLower(cKey);
|
||||
if (stricmp(cKey, "hostname") == 0) strCopy(settingHostname, sizeof(settingHostname), cVal);
|
||||
if (stricmp(cKey, "localMaxMsg") == 0) settingLocalMaxMsg = atoi(cVal);
|
||||
if (stricmp(cKey, "textSpeed") == 0) settingTextSpeed = atoi(cVal);
|
||||
if (stricmp(cKey, "LDRlowOffset") == 0) settingLDRlowOffset = atoi(cVal);
|
||||
if (stricmp(cKey, "LDRhighOffset") == 0) settingLDRhighOffset = atoi(cVal);
|
||||
if (stricmp(cKey, "maxIntensity") == 0) settingMaxIntensity = atoi(cVal);
|
||||
if (stricmp(cKey, "weerLiveAUTH") == 0) strCopy(settingWeerLiveAUTH, sizeof(settingWeerLiveAUTH), cVal);
|
||||
if (stricmp(cKey, "weerlivelocatie") == 0) strCopy(settingWeerLiveLocation, sizeof(settingWeerLiveLocation), cVal);
|
||||
if (stricmp(cKey, "weerLiveInterval") == 0) settingWeerLiveInterval = atoi(cVal);
|
||||
if (stricmp(cKey, "newsAUTH") == 0) strCopy(settingNewsAUTH, sizeof(settingNewsAUTH), cVal);
|
||||
if (stricmp(cKey, "newsNoWords") == 0) strCopy(settingNewsNoWords, sizeof(settingNewsNoWords), cVal);
|
||||
if (stricmp(cKey, "newsMaxMsg") == 0) settingNewsMaxMsg = atoi(cVal);
|
||||
if (stricmp(cKey, "newsInterval") == 0) settingNewsInterval = atoi(cVal);
|
||||
|
||||
} // while available()
|
||||
|
||||
file.close();
|
||||
|
||||
//--- this will take some time to settle in
|
||||
//--- probably need a reboot before that to happen :-(
|
||||
MDNS.setHostname(settingHostname); // start advertising with new(?) settingHostname
|
||||
if (settingLocalMaxMsg > 20) settingLocalMaxMsg = 20;
|
||||
if (settingLocalMaxMsg < 1) settingLocalMaxMsg = 1;
|
||||
if (settingTextSpeed > MAX_SPEED) settingTextSpeed = MAX_SPEED;
|
||||
if (settingTextSpeed < 10) settingTextSpeed = 10;
|
||||
if (settingLDRlowOffset > 500) settingLDRlowOffset = 500;
|
||||
if (settingLDRlowOffset < 1) settingLDRlowOffset = 0;
|
||||
if (settingLDRhighOffset < 500) settingLDRhighOffset = 500;
|
||||
if (settingLDRhighOffset > 1024) settingLDRhighOffset = 1024;
|
||||
if (settingMaxIntensity > 15) settingMaxIntensity = 15;
|
||||
if (settingMaxIntensity < 1) settingMaxIntensity = 1;
|
||||
if (strlen(settingWeerLiveLocation) < 1) sprintf(settingWeerLiveLocation, "Amsterdam");
|
||||
if (settingWeerLiveInterval == 0)
|
||||
{
|
||||
settingWeerLiveInterval = 0; // geen weerberichten
|
||||
}
|
||||
else
|
||||
{
|
||||
if (settingWeerLiveInterval > 120) settingWeerLiveInterval = 120; // minuten!
|
||||
if (settingWeerLiveInterval < 15) settingWeerLiveInterval = 15;
|
||||
}
|
||||
if (settingNewsMaxMsg > 20) settingNewsMaxMsg = 20;
|
||||
if (settingNewsMaxMsg < 1) settingNewsMaxMsg = 1;
|
||||
if (settingNewsInterval > 120) settingNewsInterval = 120;
|
||||
if (settingNewsInterval == 0)
|
||||
{
|
||||
settingNewsInterval = 0; // geen nieuwsberichten
|
||||
}
|
||||
else
|
||||
{
|
||||
if (settingNewsInterval < 15) settingNewsInterval = 15;
|
||||
}
|
||||
|
||||
DebugTln(F(" .. done\r"));
|
||||
|
||||
if (!show) return;
|
||||
|
||||
Debugln(F("\r\n==== read Settings ===================================================\r"));
|
||||
Debugf(" Hostname : %s\r\n", settingHostname);
|
||||
Debugf(" local Max. Msg : %d\r\n", settingLocalMaxMsg);
|
||||
Debugf(" text Speed : %d\r\n", settingTextSpeed);
|
||||
Debugf(" LDR low offset : %d\r\n", settingLDRlowOffset);
|
||||
Debugf(" LDR high offset : %d\r\n", settingLDRhighOffset);
|
||||
Debugf(" max Intensity : %d\r\n", settingMaxIntensity);
|
||||
Debugf(" WeerLive.nl AUTH : %s\r\n", settingWeerLiveAUTH);
|
||||
Debugf(" WeerLive.nl Locatie : %s\r\n", settingWeerLiveLocation);
|
||||
Debugf(" WeerLive.nl Interval : %d\r\n", settingWeerLiveInterval);
|
||||
Debugf(" newsapi.org AUTH : %s\r\n", settingNewsAUTH);
|
||||
Debugf(" newsapi.org NoWords : %s\r\n", settingNewsNoWords);
|
||||
Debugf(" newsapi.org Max. Msg : %d\r\n", settingNewsMaxMsg);
|
||||
Debugf(" newsapi.org Interval : %d\r\n", settingNewsInterval);
|
||||
|
||||
Debugln(F("-\r"));
|
||||
|
||||
} // readSettings()
|
||||
|
||||
|
||||
//=======================================================================
|
||||
void updateSetting(const char *field, const char *newValue)
|
||||
{
|
||||
DebugTf("-> field[%s], newValue[%s]\r\n", field, newValue);
|
||||
|
||||
if (!stricmp(field, "Hostname")) {
|
||||
strCopy(settingHostname, sizeof(settingHostname), newValue);
|
||||
if (strlen(settingHostname) < 1) strCopy(settingHostname, sizeof(settingHostname), _HOSTNAME);
|
||||
char *dotPntr = strchr(settingHostname, '.') ;
|
||||
if (dotPntr != NULL)
|
||||
{
|
||||
byte dotPos = (dotPntr-settingHostname);
|
||||
if (dotPos > 0) settingHostname[dotPos] = '\0';
|
||||
}
|
||||
Debugln();
|
||||
DebugTf("Need reboot before new %s.local will be available!\r\n\n", settingHostname);
|
||||
}
|
||||
if (!stricmp(field, "localMaxMsg")) settingLocalMaxMsg = String(newValue).toInt();
|
||||
if (!stricmp(field, "textSpeed")) settingTextSpeed = String(newValue).toInt();
|
||||
if (!stricmp(field, "LDRlowOffset")) settingLDRlowOffset = String(newValue).toInt();
|
||||
if (!stricmp(field, "LDRhighOffset")) settingLDRhighOffset = String(newValue).toInt();
|
||||
if (!stricmp(field, "maxIntensity")) settingMaxIntensity = String(newValue).toInt();
|
||||
|
||||
if (!stricmp(field, "weerLiveAUTH")) strCopy(settingWeerLiveAUTH, sizeof(settingWeerLiveAUTH), newValue);
|
||||
if (!stricmp(field, "weerLiveLocation")) strCopy(settingWeerLiveLocation, sizeof(settingWeerLiveLocation), newValue);
|
||||
if (!stricmp(field, "weerLiveInterval")) settingWeerLiveInterval = String(newValue).toInt();
|
||||
|
||||
if (!stricmp(field, "newsapiAUTH")) strCopy(settingNewsAUTH, sizeof(settingNewsAUTH), newValue);
|
||||
if (!stricmp(field, "newsNoWords")) strCopy(settingNewsNoWords, sizeof(settingNewsNoWords), newValue);
|
||||
if (!stricmp(field, "newsapiMaxMsg")) settingNewsMaxMsg = String(newValue).toInt();
|
||||
if (!stricmp(field, "newsapiInterval")) settingNewsInterval = String(newValue).toInt();
|
||||
|
||||
writeSettings(false);
|
||||
|
||||
if (settingWeerLiveInterval == 0) memset(tempMessage, 0, sizeof(tempMessage));
|
||||
else if (settingWeerLiveInterval < 15) settingWeerLiveInterval = 15;
|
||||
if (settingNewsInterval == 0) removeNewsData();
|
||||
else if (settingNewsInterval < 15) settingNewsInterval = 15;
|
||||
//--- rebuild noWords array --
|
||||
splitNewsNoWords(settingNewsNoWords);
|
||||
|
||||
} // updateSetting()
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
* persons to whom the Software is furnished to do so, subject to the
|
||||
* following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
|
||||
* OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
|
||||
* THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
****************************************************************************
|
||||
*/
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
***************************************************************************
|
||||
** Program : updateServerHtml.h
|
||||
**
|
||||
** Copyright (c) 2021 Willem Aandewiel
|
||||
**
|
||||
** TERMS OF USE: MIT License. See bottom of file.
|
||||
***************************************************************************
|
||||
*/
|
||||
|
||||
static const char UpdateServerIndex[] PROGMEM =
|
||||
R"(<html charset="UTF-8">
|
||||
<style type='text/css'>
|
||||
body {background-color: lightblue;}
|
||||
</style>
|
||||
<body>
|
||||
<h1>ESP - lichtKrant Flash utility</h1>
|
||||
<form method='POST' action='?cmd=0' enctype='multipart/form-data'>
|
||||
Selecteer een "<b>.ino.bin</b>" bestand<br/>
|
||||
<input type='file' accept='ino.bin' name='firmware'>
|
||||
<input type='submit' value='Flash Firmware'>
|
||||
</form>
|
||||
<form method='POST' action='?cmd=100' enctype='multipart/form-data'>
|
||||
Selecteer een "<b>.mklittlefs.bin</b>" bestand<br/>
|
||||
<input type='file' accept='mklittlefs.bin' name='filesystem'>
|
||||
<input type='submit' value='Flash LittleFS'>
|
||||
</form>
|
||||
<hr>
|
||||
<br/><font color='red'>Let op!!!</font>
|
||||
<br/>Bij het flashen van LittleFS raakt u alle bestanden op LittleFS kwijt.
|
||||
<br/>Maak daarom eerst een kopie van deze bestanden (met de FSexplorer)
|
||||
en zet deze na het flashen van LittleFS weer terug.
|
||||
<hr>
|
||||
<br/>
|
||||
Klik <span style='font-size:1.3em;'><b><a href="/">hier</a></b></span>
|
||||
om terug te keren naar het hoofdscherm!
|
||||
</body>
|
||||
</html>)";
|
||||
|
||||
static const char UpdateServerSuccess[] PROGMEM =
|
||||
R"(<html charset="UTF-8">
|
||||
<style type='text/css'>
|
||||
body {background-color: lightgray;}
|
||||
</style>
|
||||
<body>
|
||||
<h1>lichtKrant Flash utility</h1>
|
||||
<br/>
|
||||
<h2>Update successful!</h2>
|
||||
<br/>
|
||||
<br/>Wait for the lichtKrant to reboot and start the HTTP server
|
||||
<br/>
|
||||
<br>
|
||||
<br/>Wacht nog <span style='font-size: 1.3em;' id="waitSeconds">30</span> seconden ..
|
||||
<br/>Als het lijkt of er niets gebeurd, wacht dan tot de teller
|
||||
op 'nul' staat en klik daarna <span style='font-size:1.3em;'><b><a href="/">hier</a></b></span>!
|
||||
</body>
|
||||
<script>
|
||||
var seconds = document.getElementById("waitSeconds").textContent;
|
||||
var countdown = setInterval(function() {
|
||||
seconds--;
|
||||
document.getElementById('waitSeconds').textContent = seconds;
|
||||
if (seconds <= 0) {
|
||||
clearInterval(countdown);
|
||||
window.location.assign("/")
|
||||
}
|
||||
}, 1000);
|
||||
</script>
|
||||
</html>)";
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
* persons to whom the Software is furnished to do so, subject to the
|
||||
* following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
|
||||
* OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
|
||||
* THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
****************************************************************************
|
||||
*/
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
***************************************************************************
|
||||
** Program : weerlive_nl
|
||||
**
|
||||
** Copyright (c) 2021 .. 2023 Willem Aandewiel
|
||||
**
|
||||
** TERMS OF USE: MIT License. See bottom of file.
|
||||
***************************************************************************
|
||||
*/
|
||||
|
||||
void getWeerLiveData()
|
||||
{
|
||||
const char* weerliveHost = "weerlive.nl";
|
||||
const int httpPort = 80;
|
||||
int weerliveStatus = 0;
|
||||
String tempString;
|
||||
int startPos, endPos;
|
||||
int32_t maxWait;
|
||||
char jsonResponse[1536];
|
||||
char val[51] = "";
|
||||
bool gotData = false;
|
||||
|
||||
WiFiClient weerliveClient;
|
||||
|
||||
DebugTf("getWeerLiveData(%s)\r\n", weerliveHost);
|
||||
|
||||
// We now create a URI for the request
|
||||
String url = "/api/json-data-10min.php?key=";
|
||||
url += settingWeerLiveAUTH;
|
||||
url += "&locatie=";
|
||||
url += settingWeerLiveLocation;
|
||||
|
||||
DebugTf("Requesting URL: %s/api/json-data-10min.php?key=secret&locatie=%s\r\n", weerliveHost, settingWeerLiveLocation);
|
||||
Debugln(url);
|
||||
if (!weerliveClient.connect(weerliveHost, httpPort))
|
||||
{
|
||||
DebugTln("connection failed");
|
||||
sprintf(tempMessage, "connection to %s failed", weerliveHost);
|
||||
weerliveClient.flush();
|
||||
weerliveClient.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
// This will send the request to the server
|
||||
weerliveClient.print(String("GET ") + url + " HTTP/1.1\r\n" +
|
||||
"Host: " + weerliveHost + "\r\n" +
|
||||
"Connection: close\r\n\r\n");
|
||||
delay(10);
|
||||
|
||||
weerliveClient.setTimeout(5000);
|
||||
|
||||
while ((weerliveClient.connected() || weerliveClient.available()) && !gotData)
|
||||
{
|
||||
yield();
|
||||
while(weerliveClient.available() && !gotData)
|
||||
{
|
||||
//--- skip to find HTTP/1.1
|
||||
//--- then parse response code
|
||||
if (weerliveClient.find("HTTP/1.1"))
|
||||
{
|
||||
weerliveStatus = weerliveClient.parseInt(); // parse status code
|
||||
DebugTf("Statuscode: [%d] ", weerliveStatus);
|
||||
if (weerliveStatus != 200)
|
||||
{
|
||||
Debugln(" ERROR!");
|
||||
weerliveClient.flush();
|
||||
weerliveClient.stop();
|
||||
return;
|
||||
}
|
||||
Debugln(" OK!");
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugTln("Error reading weerLive.. -> bailout!");
|
||||
weerliveClient.flush();
|
||||
weerliveClient.stop();
|
||||
return;
|
||||
}
|
||||
//--- skip headers
|
||||
if (weerliveClient.find("\r\n\r\n"))
|
||||
{
|
||||
int charsRead = 0;
|
||||
charsRead = weerliveClient.readBytesUntil('\0', jsonResponse, sizeof(jsonResponse));
|
||||
jsonResponse[(charsRead -1)] = '\0';
|
||||
gotData = true;
|
||||
DebugTln("Got weer data!");
|
||||
}
|
||||
} // while available ..
|
||||
|
||||
} // connected ..
|
||||
|
||||
weerliveClient.flush();
|
||||
weerliveClient.stop();
|
||||
//-- jsonResponse looks like:
|
||||
//-- { "liveweer": [{"plaats": "Baarn", "timestamp": "1683105785", "time": "03-05-2023 11:23", "temp": "10.4", "gtemp": "8.8", "samenv": "Licht bewolkt", "lv": "56", "windr": "NO", "windrgr": "44", "windms": "3", "winds": "2", "windk": "5.8", "windkmh": "10.8", "luchtd": "1029.4", "ldmmhg": "772", "dauwp": "2", "zicht": "35", "verw": "Zonnig en droog, donderdag warmer", "sup": "06:03", "sunder": "21:08", "image": "lichtbewolkt", "d0weer": "halfbewolkt", "d0tmax": "15", "d0tmin": "3", "d0windk": "2", "d0windknp": "6", "d0windms": "3", "d0windkmh": "11", "d0windr": "NO", "d0windrgr": "44", "d0neerslag": "0", "d0zon": "35", "d1weer": "halfbewolkt", "d1tmax": "20", "d1tmin": "5", "d1windk": "2", "d1windknp": "6", "d1windms": "3", "d1windkmh": "11", "d1windr": "O", "d1windrgr": "90", "d1neerslag": "20", "d1zon": "60", "d2weer": "regen", "d2tmax": "19", "d2tmin": "12", "d2windk": "2", "d2windknp": "6", "d2windms": "3", "d2windkmh": "11", "d2windr": "ZW", "d2windrgr": "225", "d2neerslag": "80", "d2zon": "30", "alarm": "0", "alarmtxt": ""}]}
|
||||
|
||||
int prevLength = strlen(jsonResponse);
|
||||
strTrimCntr(jsonResponse, 1534);
|
||||
DebugTf("jsonResponse now [%d]chars (before trim [%d]chars)\r\n", strlen(jsonResponse), prevLength);
|
||||
DebugTf("jsonResponse is [%s]\r\n\n", jsonResponse);
|
||||
|
||||
parseJsonKey(jsonResponse, "plaats", val, 50);
|
||||
snprintf(tempMessage, LOCAL_SIZE, val);
|
||||
parseJsonKey(jsonResponse, "samenv", val, 50);
|
||||
snprintf(cMsg, LOCAL_SIZE, " %s %s ", tempMessage, val);
|
||||
parseJsonKey(jsonResponse, "d0tmin", val, 50);
|
||||
snprintf(tempMessage, LOCAL_SIZE, "%s min %s°C ", cMsg, val);
|
||||
parseJsonKey(jsonResponse, "d0tmax", val, 50);
|
||||
snprintf(cMsg, LOCAL_SIZE, "%s max %s°C", tempMessage, val);
|
||||
parseJsonKey(jsonResponse, "luchtd", val, 50);
|
||||
snprintf(tempMessage, LOCAL_SIZE, "%s - luchtdruk %s hPa ", cMsg, val);
|
||||
|
||||
parseJsonKey(jsonResponse, "d1weer", val, 50);
|
||||
snprintf(cMsg, LOCAL_SIZE, "%s - morgen %s ", tempMessage, val);
|
||||
parseJsonKey(jsonResponse, "d1tmin", val, 50);
|
||||
snprintf(tempMessage, LOCAL_SIZE, "%s min %s°C ", cMsg, val);
|
||||
parseJsonKey(jsonResponse, "d1tmax", val, 50);
|
||||
snprintf(cMsg, LOCAL_SIZE, "%s max %s°C", tempMessage, val);
|
||||
|
||||
snprintf(tempMessage, LOCAL_SIZE, "%s", cMsg);
|
||||
Debugln("\r\n");
|
||||
Debugf("\tWeer[%s]\r\n", tempMessage);
|
||||
|
||||
} // getWeerLiveData()
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
* persons to whom the Software is furnished to do so, subject to the
|
||||
* following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
|
||||
* OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
|
||||
* THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
****************************************************************************
|
||||
*/
|
||||
Reference in New Issue
Block a user