Ajout Converter
This commit is contained in:
14
arduinoIDE2platformIO-convertor/.gitignore
vendored
Normal file
14
arduinoIDE2platformIO-convertor/.gitignore
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
.pio
|
||||
.pio.nosync
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
.vscode/ipch
|
||||
.vscode/**/*
|
||||
testProject/*/PlatformIO/*
|
||||
claude-dev
|
||||
changes
|
||||
backups
|
||||
changes/*
|
||||
next.py
|
||||
saveNext.py
|
||||
21
arduinoIDE2platformIO-convertor/LICENSE
Normal file
21
arduinoIDE2platformIO-convertor/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 Willem Aandewiel
|
||||
|
||||
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.
|
||||
23
arduinoIDE2platformIO-convertor/README.md
Normal file
23
arduinoIDE2platformIO-convertor/README.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# How to convert a ArduinoIDE project to PlatformIO
|
||||
|
||||
1) You have to clone the repo to your computer.
|
||||
2) In a terminal window you `cd` to the folder where you cloned the repo.
|
||||
3) Then you type `python3 arduinoIDE2platformIO.py --project_dir <pathToArduinoProject>`
|
||||
(note, there are two `--` before `project_dir`)
|
||||
|
||||
The converted project is located in `<pathToArduinoProject>/platformIO/`
|
||||
|
||||
All you have to do is edit the `platformio.ini` file to your needs.
|
||||
|
||||
Mind you: the convertor will not always do everything for you. Sometimes you have to iterate [compile] -> solve compile errors -> [compile] -> solve compile errors ...
|
||||
|
||||
If it compiles without errors test it. If it works as expected you can cleanup the ‘arduinoGlue.h’ file with the ‘crossReference.py’ file.
|
||||
|
||||
Next step can be to look for prototypes that are not used in any other file then where the function is defined. You can then move that prototype definition to the '.h' file of the corresponding '.cpp' file.
|
||||
It is not necessary but it makes better C(++) code.
|
||||
|
||||
|
||||
## structure ArduinoIDE project
|
||||
|
||||
## structure PlatformIO project
|
||||
|
||||
54
arduinoIDE2platformIO-convertor/analizeNext.py
Normal file
54
arduinoIDE2platformIO-convertor/analizeNext.py
Normal file
@@ -0,0 +1,54 @@
|
||||
import ast
|
||||
|
||||
# Read the content of the uploaded file
|
||||
file_path = '../../next.py'
|
||||
with open(file_path, 'r') as file:
|
||||
file_content = file.read()
|
||||
|
||||
# Parse the content of the file
|
||||
parsed_ast = ast.parse(file_content)
|
||||
|
||||
# Initialize a dictionary to store function calls
|
||||
function_calls = {}
|
||||
|
||||
class FunctionVisitor(ast.NodeVisitor):
|
||||
def __init__(self):
|
||||
self.current_function = None
|
||||
|
||||
def visit_FunctionDef(self, node):
|
||||
self.current_function = node.name
|
||||
function_calls[self.current_function] = []
|
||||
self.generic_visit(node)
|
||||
|
||||
def visit_Call(self, node):
|
||||
if isinstance(node.func, ast.Name) and self.current_function:
|
||||
function_calls[self.current_function].append(node.func.id)
|
||||
self.generic_visit(node)
|
||||
|
||||
# Visit all nodes in the AST
|
||||
visitor = FunctionVisitor()
|
||||
visitor.visit(parsed_ast)
|
||||
|
||||
# Filter only the functions defined in the file
|
||||
defined_functions = set(function_calls.keys())
|
||||
|
||||
# Sort functions based on the order of calling
|
||||
sorted_function_calls = sorted(
|
||||
[(func, [call for call in calls if call in defined_functions])
|
||||
for func, calls in function_calls.items()],
|
||||
key=lambda item: item[0]
|
||||
)
|
||||
|
||||
# Print the sorted function calls
|
||||
for function, calls in sorted_function_calls:
|
||||
if function != "main":
|
||||
#print(f"Function '{function}' calls: {', '.join(calls) if calls else 'No calls'}")
|
||||
print(f"Function '{function}' calls:")
|
||||
for func in calls:
|
||||
print(f"\t{func}")
|
||||
for function, calls in sorted_function_calls:
|
||||
if function == "main":
|
||||
#print(f"Function '{function}' calls: {', '.join(calls) if calls else 'No calls'}")
|
||||
print(f"Function '{function}' calls:")
|
||||
for func in calls:
|
||||
print(f"\t{func}")
|
||||
2928
arduinoIDE2platformIO-convertor/arduinoIDE2platformIO.py
Normal file
2928
arduinoIDE2platformIO-convertor/arduinoIDE2platformIO.py
Normal file
File diff suppressed because it is too large
Load Diff
200
arduinoIDE2platformIO-convertor/crossReference.py
Normal file
200
arduinoIDE2platformIO-convertor/crossReference.py
Normal file
@@ -0,0 +1,200 @@
|
||||
#------------------------------------------------------------
|
||||
#
|
||||
# X-Reference a arduinoGlue.h file
|
||||
#
|
||||
# file name : crossReference.py
|
||||
#
|
||||
# by : Willem Aandewiel
|
||||
#
|
||||
# Version : v0.81 (04-10-2024)
|
||||
#
|
||||
# Usage: python3 crossReference.py <path to PlatformIO project
|
||||
#
|
||||
# input file : arduinoGlue.h
|
||||
# output file: modifiedGlue.h (modified)
|
||||
#
|
||||
# This program tries to determine what extern declared variables
|
||||
# are used in the source files. If not used in any other file
|
||||
# than the one where the variable is declared it will be commented out.
|
||||
#
|
||||
# It does more-or-less the same for prototype functions
|
||||
#
|
||||
# It is to the user to remove "not used" variables or prototypes.
|
||||
# For prototypes it can me nessesary to move the prototypes to the
|
||||
# header file of the .cpp file.
|
||||
#
|
||||
# license : MIT (see at the bottom of this file)
|
||||
#------------------------------------------------------------
|
||||
|
||||
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import logging
|
||||
|
||||
logging.basicConfig(level=logging.INFO, format='- %(levelname)s - %(message)s')
|
||||
|
||||
def process_extern_variables(arduino_glue_content, src_folder):
|
||||
extern_pattern = re.compile(r'extern\s+(\w+)\s+(\w+)')
|
||||
extern_vars = extern_pattern.findall(arduino_glue_content)
|
||||
|
||||
var_usage = {var: set() for _, var in extern_vars}
|
||||
var_source = {var: "" for _, var in extern_vars}
|
||||
|
||||
for root, _, files in os.walk(src_folder):
|
||||
for file in files:
|
||||
if file.endswith('.cpp') or file.endswith('.h'):
|
||||
file_path = os.path.join(root, file)
|
||||
try:
|
||||
with open(file_path, 'r') as f:
|
||||
content = f.read()
|
||||
for _, var in extern_vars:
|
||||
if re.search(r'\b' + var + r'\b', content):
|
||||
if not var_source[var]:
|
||||
var_source[var] = os.path.splitext(file)[0]
|
||||
else:
|
||||
var_usage[var].add(file)
|
||||
except IOError as e:
|
||||
logging.error(f"Error reading {file_path}: {e}")
|
||||
|
||||
new_content = []
|
||||
is_macro = False
|
||||
for line in arduino_glue_content.split('\n'):
|
||||
if line.strip().startswith('#define'):
|
||||
is_macro = True
|
||||
|
||||
if is_macro:
|
||||
new_content.append(line)
|
||||
is_macro = line.strip().endswith('\\')
|
||||
else:
|
||||
extern_match = extern_pattern.search(line)
|
||||
if extern_match:
|
||||
var_type, var_name = extern_match.groups()
|
||||
usage = var_usage[var_name]
|
||||
source = var_source[var_name]
|
||||
if not usage:
|
||||
new_content.append(f"//-- not used {line}")
|
||||
else:
|
||||
usage_str = ", ".join(usage)
|
||||
new_content.append(f"//-- used in {usage_str}")
|
||||
if "//--" not in line:
|
||||
new_content.append(f"{line:<70} //-- from {source}")
|
||||
else:
|
||||
new_content.append(line)
|
||||
else:
|
||||
new_content.append(line)
|
||||
|
||||
return "\n".join(new_content), extern_vars, var_usage
|
||||
|
||||
def process_prototypes(arduino_glue_content, src_folder):
|
||||
prototype_pattern = re.compile(r'(\w+\s+\w+\s*\([^)]*\)\s*;)')
|
||||
prototypes = prototype_pattern.findall(arduino_glue_content)
|
||||
|
||||
prototype_usage = {p: set() for p in prototypes}
|
||||
|
||||
for root, _, files in os.walk(src_folder):
|
||||
for file in files:
|
||||
if file.endswith('.cpp'):
|
||||
file_path = os.path.join(root, file)
|
||||
try:
|
||||
with open(file_path, 'r') as f:
|
||||
content = f.read()
|
||||
for prototype in prototypes:
|
||||
func_name = re.search(r'(\w+)\s*\(', prototype).group(1)
|
||||
if re.search(r'\b' + func_name + r'\b', content):
|
||||
if os.path.basename(file_path) != 'arduinoGlue.cpp':
|
||||
prototype_usage[prototype].add(os.path.basename(file_path))
|
||||
except IOError as e:
|
||||
logging.error(f"Error reading {file_path}: {e}")
|
||||
|
||||
new_content = []
|
||||
for line in arduino_glue_content.split('\n'):
|
||||
prototype_match = prototype_pattern.search(line)
|
||||
if prototype_match:
|
||||
prototype = prototype_match.group(1)
|
||||
usage = prototype_usage[prototype]
|
||||
if not usage:
|
||||
new_content.append("//-- not used anywhere")
|
||||
else:
|
||||
usage_str = ", ".join(usage)
|
||||
new_content.append(f"//-- Used in: {usage_str}")
|
||||
new_content.append(line)
|
||||
else:
|
||||
new_content.append(line)
|
||||
|
||||
return "\n".join(new_content)
|
||||
|
||||
def process_arduino_project(project_path):
|
||||
try:
|
||||
arduino_glue_path = os.path.join(project_path, 'include', 'arduinoGlue.h')
|
||||
temp_path = os.path.join(project_path, 'include', 'modifiedGlue.h')
|
||||
src_folder = os.path.join(project_path, 'src')
|
||||
|
||||
logging.info(f"arduinoGlue.h: {arduino_glue_path}")
|
||||
|
||||
# Read arduinoGlue.h
|
||||
try:
|
||||
with open(arduino_glue_path, 'r') as f:
|
||||
arduino_glue_content = f.read()
|
||||
except IOError as e:
|
||||
logging.error(f"Error reading arduinoGlue.h: {e}")
|
||||
return
|
||||
|
||||
# Process extern variables
|
||||
arduino_glue_content, extern_vars, var_usage = process_extern_variables(arduino_glue_content, src_folder)
|
||||
|
||||
# Process prototypes
|
||||
arduino_glue_content = process_prototypes(arduino_glue_content, src_folder)
|
||||
|
||||
# Write updated arduinoGlue.h
|
||||
try:
|
||||
logging.info(f"write to {temp_path}")
|
||||
with open(temp_path, 'w') as f:
|
||||
f.write(arduino_glue_content)
|
||||
except IOError as e:
|
||||
logging.error(f"Error writing to arduinoGlue.h: {e}")
|
||||
return
|
||||
|
||||
# Output results for extern variables
|
||||
for var_type, var_name in extern_vars:
|
||||
usage = var_usage[var_name]
|
||||
if len(usage) > 1:
|
||||
usage_str = ", ".join(usage)
|
||||
print(f"{var_type} {var_name} {usage_str}")
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"An unexpected error occurred: {e}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) != 2:
|
||||
print("Usage: python crossReference.py <path_to_platformio_project>")
|
||||
sys.exit(1)
|
||||
|
||||
project_path = sys.argv[1]
|
||||
process_arduino_project(project_path)
|
||||
|
||||
|
||||
#*******************************************************************************************
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2024 Willem Aandewiel
|
||||
#
|
||||
# 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.
|
||||
#************************************************************************************************
|
||||
|
||||
16
arduinoIDE2platformIO-convertor/saveNext.sh
Executable file
16
arduinoIDE2platformIO-convertor/saveNext.sh
Executable file
@@ -0,0 +1,16 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
NOW=$(date '+%Y-%m-%d_%H.%M')
|
||||
echo ${NOW}
|
||||
SOURCE="${HOME}/tmp/arduinoIDE2platformIO-convertor/next.py"
|
||||
#DEST="${HOME}/Documents/claudeProgress/next_${NOW}.py"
|
||||
DEST="${HOME}/Documents/claudeProgress/"
|
||||
echo "Copy ${SOURCE} to ${DEST}/next_${NOW}.py"
|
||||
#
|
||||
cp -v ${SOURCE} ${DEST}/next_${NOW}.py
|
||||
cp -v ${SOURCE} ../../changes/next_${NOW}.py
|
||||
|
||||
echo "Done!"
|
||||
|
||||
ls -l ${DEST}
|
||||
|
||||
@@ -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