Files
Maison/arduinoIDE2platformIO-convertor/testProject/ESP_ticker/ESP_ticker.ino
2026-02-10 13:51:57 +01:00

529 lines
16 KiB
C++

/*
***************************************************************************
** 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.
*
****************************************************************************
*/