Male zmeny v index.html
- prerobenie settings handleru - presunutie do ~/data folderu - odstranenie starych suborov - pridanie parsovania settings.json - obsluha SPFFS
This commit is contained in:
@@ -81,7 +81,9 @@
|
||||
|
||||
$(function () {
|
||||
|
||||
settings = [ { "kedy": 0 , "cas": "6:38", "datum": null, "edit": true}, { "kedy": 0 , "cas": "17:00", "datum": null, "edit":false} ];
|
||||
settings = %%SETTINGS%%;
|
||||
settings = settings["data"];
|
||||
|
||||
when = { 0 : "Pracovne dni",
|
||||
1 : "Vikendy",
|
||||
2 : "Zajtra",
|
||||
@@ -112,8 +114,16 @@
|
||||
},
|
||||
postChanges: function () {
|
||||
console.log("SAVE CHANGES TO ESP");
|
||||
axios.get('https://api.coindesk.com/v1/bpi/currentprice.json')
|
||||
.then(response => (console.log(response)));
|
||||
|
||||
axios.post('/settings', {
|
||||
data: this.settings
|
||||
})
|
||||
.then(function (response) {
|
||||
console.log(response);
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
1
data/settings.json
Normal file
1
data/settings.json
Normal file
@@ -0,0 +1 @@
|
||||
{ "data": [ { "kedy": 0, "cas": "", "datum":"", "edit": true} ] }
|
||||
@@ -1,166 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
|
||||
|
||||
|
||||
<!-- <link rel="stylesheet" type="text/css" href="css/bootstrap-datetimepicker.css"> -->
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.15.1/moment.min.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.43/css/bootstrap-datetimepicker.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.43/css/bootstrap-datetimepicker-standalone.css">
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.43/js/bootstrap-datetimepicker.min.js"></script>
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/3.1.0/mustache.min.js"></script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<!-- Optional JavaScript -->
|
||||
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/3.1.4/js/bootstrap-datetimepicker.min.js" integrity="sha256-sU6nRhzXDAC31Wdrirz7X2A2rSRWj10WnP9CA3vpYKw=" crossorigin="anonymous"></script>
|
||||
|
||||
<div class="container">
|
||||
<h1 align="center">Budík</h1>
|
||||
<div class="row">
|
||||
<div data-form-number=0 class='form-row'>
|
||||
<div class="form-group col-sm-3">
|
||||
<select data-form="type" class="form-control mr-sm-2" id="inlineFormCustomSelect">
|
||||
<option value="0" selected>Pracovne dni</option>
|
||||
<option value="1">Vikendy</option>
|
||||
<option value="2">Zajtra</option>
|
||||
<option value="3">Iba raz</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-sm-3">
|
||||
<div class='input-group time' id='datetimepicker2'>
|
||||
<input data-form="time" type='text' class="form-control" />
|
||||
<span class="input-group-addon">
|
||||
<span class="glyphicon glyphicon-time"></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-sm-3">
|
||||
<div class='input-group time' id='datetimepicker3'>
|
||||
<input data-form="date" type='text' class="form-control" />
|
||||
<span class="input-group-addon">
|
||||
<span class="glyphicon glyphicon-time"></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="btn-toolbar col-sm-3" role="toolbar" aria-label="Toolbar with button groups">
|
||||
<div class="btn-group mr-2" role="group" aria-label="First group">
|
||||
<button data-button=1 type="button" class="btn btn-secondary glyphicon glyphicon-plus"></button>
|
||||
<button data-button=2 type="button" class="btn btn-secondary glyphicon glyphicon-minus"></button>
|
||||
<button data-button=3 type="button" class="btn btn-secondary glyphicon glyphicon-edit"></button>
|
||||
<button data-button=4 type="button" class="btn btn-secondary glyphicon glyphicon-save"></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
var nf = 0;
|
||||
|
||||
var when = { 0 : "Pracovne dni",
|
||||
1 : "Vikendy",
|
||||
2 : "Zajtra",
|
||||
3 : "Iba raz"
|
||||
};
|
||||
|
||||
var when_rev = {};
|
||||
|
||||
function add_to_end(s) {
|
||||
|
||||
h = $('.btn-toolbar')[0].outerHTML;
|
||||
$('.container')
|
||||
.append('<div class="row"><div class="col-sm-3">'+ when[s.kedy] +'</div><div class="col-sm-3">'+ s.cas +'</div><div class="col-sm-3">'+ s.datum +'</div>' + h + '</div>');
|
||||
|
||||
}
|
||||
|
||||
function set_datepicker() {
|
||||
$('#datetimepicker3').datetimepicker({
|
||||
pickTime: false,
|
||||
format: "D.M.YYYY"
|
||||
});
|
||||
$('#datetimepicker2').datetimepicker({
|
||||
format : 'HH:mm',
|
||||
pickDate: false
|
||||
});
|
||||
}
|
||||
|
||||
function set_click_handler() {
|
||||
$('.glyphicon-plus').click(function() {
|
||||
console.log('PLUS');
|
||||
nf++;
|
||||
form = $('*[data-form-number=0]').html();
|
||||
html1 = "<div class='row'><div data-form-number=" + nf + " class='form-row'>" + form + "</div></div>";
|
||||
$('.container').append(html1);
|
||||
set_datepicker();
|
||||
});
|
||||
$('.glyphicon-minus').click(function(el) {
|
||||
console.log('MINUS');
|
||||
$(this.parentNode.parentNode.parentNode).remove();
|
||||
});
|
||||
$('.glyphicon-edit').click(function() {
|
||||
console.log('EDIT');
|
||||
data = $(this.parentNode.parentNode.parentNode);
|
||||
|
||||
type = data.children().html();
|
||||
time = data.children().next().html();
|
||||
date = data.children().next().next().html();
|
||||
|
||||
if (date == "null") date = "";
|
||||
nf++;
|
||||
form = $('*[data-form-number=0]').html();
|
||||
html1 = "<div data-form-number=" + nf + " class='form-row'>" + form + "</div>";
|
||||
$(this.parentNode.parentNode.parentNode).html(html1);
|
||||
$('*[data-form-number='+ nf +']').find('*[data-form="type"]').val(when_rev[type]);
|
||||
$('*[data-form-number='+ nf +']').find('*[data-form="time"]').val(time);
|
||||
$('*[data-form-number='+ nf +']').find('*[data-form="date"]').val(date);
|
||||
set_datepicker();
|
||||
});
|
||||
$('.glyphicon-save').click(function() {
|
||||
console.log('SAVE');
|
||||
|
||||
forms = $(this.parentNode.parentNode.parentNode);
|
||||
|
||||
type = forms.find('*[data-form="type"]').val();
|
||||
time = forms.find('*[data-form="time"]').val();
|
||||
date = forms.find('*[data-form="date"]').val();
|
||||
|
||||
h = $('.btn-toolbar')[0].outerHTML;
|
||||
|
||||
forms.replaceWith('<div class="col-sm-3">'+ when[type] +'</div><div class="col-sm-3">'+ time +'</div><div class="col-sm-3">'+ date+'</div>' + h + '</div>');
|
||||
});
|
||||
}
|
||||
|
||||
for (var key in when) {
|
||||
when_rev[when[key]] = key;
|
||||
};
|
||||
|
||||
$(function () {
|
||||
|
||||
settings = [ { "kedy": 0 , "cas": "6:38", "datum": null}, { "kedy": 0 , "cas": "17:00", "datum": null} ];
|
||||
settings.forEach(s => add_to_end(s));
|
||||
|
||||
|
||||
|
||||
set_datepicker();
|
||||
set_click_handler();
|
||||
|
||||
$('.container').append('<button type="button" class="btn btn-dark">Nastav</button>');
|
||||
$('.container > button').click(function() {
|
||||
console.log('SET SETTINGS');
|
||||
})
|
||||
});
|
||||
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
291
src/main.cpp
291
src/main.cpp
@@ -1,4 +1,3 @@
|
||||
#include <NTPClient.h>
|
||||
// change next line to use with another board/shield
|
||||
#include <WiFi.h>
|
||||
#include <SPI.h>
|
||||
@@ -12,9 +11,11 @@
|
||||
#include <ArduinoJson.h>
|
||||
#include <DateTime.h>
|
||||
#include <IotWebConf.h>
|
||||
#include <NTP.h>
|
||||
#include <SPIFFS.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "calendar.h"
|
||||
|
||||
#ifndef PSTR
|
||||
#define PSTR // Make Arduino Due happy
|
||||
#endif
|
||||
@@ -37,8 +38,14 @@ DNSServer dnsServer;
|
||||
WebServer server(80);
|
||||
IotWebConf iotWebConf(thingName, &dnsServer, &server, wifiInitialApPassword);
|
||||
|
||||
|
||||
enum Lines_names { TIME_LINE, DATE_LINE, WEATHER_LINE, NAMES_LINE, LAST_LINE };
|
||||
enum Lines_names
|
||||
{
|
||||
TIME_LINE,
|
||||
DATE_LINE,
|
||||
WEATHER_LINE,
|
||||
NAMES_LINE,
|
||||
LAST_LINE
|
||||
};
|
||||
String Lines[LAST_LINE];
|
||||
|
||||
Adafruit_NeoMatrix *matrix;
|
||||
@@ -50,12 +57,48 @@ WiFiUDP ntpUDP;
|
||||
// You can specify the time server pool and the offset (in seconds, can be
|
||||
// changed later with setTimeOffset() ). Additionaly you can specify the
|
||||
// update interval (in milliseconds, can be changed using setUpdateInterval() ).
|
||||
NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", 3600, 60000);
|
||||
bool initialized = false;
|
||||
NTP ntp(ntpUDP);
|
||||
HTTPClient client;
|
||||
DynamicJsonDocument doc(1024);
|
||||
String settings;
|
||||
|
||||
String get_name_of_day(int m,int d) {
|
||||
for (int i=0;i<sizeof(cal_names)/sizeof(cal_names[0]); i++) {
|
||||
enum when_names {
|
||||
WORK_DAYS,
|
||||
WEEKENDS,
|
||||
TOMOROW,
|
||||
ONCE
|
||||
};
|
||||
|
||||
struct timer_def {
|
||||
int when; int h; int m; int d; int mh; int y;
|
||||
};
|
||||
|
||||
int ntimers;
|
||||
#define MAX_TIMERS 10
|
||||
timer_def Timers[MAX_TIMERS];
|
||||
|
||||
|
||||
String getValue(String data, char separator, int index)
|
||||
{
|
||||
int found = 0;
|
||||
int strIndex[] = { 0, -1 };
|
||||
int maxIndex = data.length() - 1;
|
||||
|
||||
for (int i = 0; i <= maxIndex && found <= index; i++) {
|
||||
if (data.charAt(i) == separator || i == maxIndex) {
|
||||
found++;
|
||||
strIndex[0] = strIndex[1] + 1;
|
||||
strIndex[1] = (i == maxIndex) ? i+1 : i;
|
||||
}
|
||||
}
|
||||
return found > index ? data.substring(strIndex[0], strIndex[1]) : "";
|
||||
}
|
||||
|
||||
String get_name_of_day(int m, int d)
|
||||
{
|
||||
for (int i = 0; i < sizeof(cal_names) / sizeof(cal_names[0]); i++)
|
||||
{
|
||||
if (cal_names[i].m == m && cal_names[i].d == d)
|
||||
return String(cal_names[i].name);
|
||||
}
|
||||
@@ -66,9 +109,11 @@ String get_weather_line()
|
||||
{
|
||||
client.begin("http://api.openweathermap.org/data/2.5/weather?q=Trnava,sk&lang=sk&APPID=d15c61931a053026e98769dd9fc46ba3");
|
||||
int code = client.GET();
|
||||
if (code > 0) {
|
||||
if (code > 0)
|
||||
{
|
||||
|
||||
if(code == HTTP_CODE_OK) {
|
||||
if (code == HTTP_CODE_OK)
|
||||
{
|
||||
float temp;
|
||||
String w;
|
||||
String desc;
|
||||
@@ -87,21 +132,26 @@ String get_weather_line()
|
||||
return "Error";
|
||||
}
|
||||
|
||||
String get_time_line(){
|
||||
return String(DateTime.format("%H:%M"));
|
||||
String get_time_line()
|
||||
{
|
||||
return String(ntp.formattedTime("%H:%M"));
|
||||
}
|
||||
|
||||
String get_date_line(){
|
||||
return String(DateTime.format("%d.%m"));
|
||||
String get_date_line()
|
||||
{
|
||||
return String(ntp.formattedTime("%d.%m"));
|
||||
}
|
||||
|
||||
String get_names_line() {
|
||||
String get_names_line()
|
||||
{
|
||||
int d = DateTime.getParts().getMonthDay();
|
||||
int m = DateTime.getParts().getMonth();m++;
|
||||
int m = DateTime.getParts().getMonth();
|
||||
m++;
|
||||
return get_name_of_day(m, d);
|
||||
}
|
||||
|
||||
void setup_matrix() {
|
||||
void setup_matrix()
|
||||
{
|
||||
matrix = new Adafruit_NeoMatrix(32, 8, PIN,
|
||||
NEO_MATRIX_BOTTOM + NEO_MATRIX_RIGHT +
|
||||
NEO_MATRIX_COLUMNS + NEO_MATRIX_ZIGZAG,
|
||||
@@ -113,38 +163,181 @@ void setup_matrix() {
|
||||
matrix->setTextColor(colors[1]);
|
||||
}
|
||||
|
||||
void setup(){
|
||||
void returnOK() {
|
||||
server.send(200, "text/plain", "");
|
||||
}
|
||||
|
||||
void handleSave() {
|
||||
String data;
|
||||
|
||||
Serial.println(server.uri());
|
||||
Serial.println(server.method());
|
||||
Serial.println(server.argName(0));
|
||||
Serial.println(server.arg(0));
|
||||
if (server.uri() != "/settings" && server.method() != HTTP_POST) {
|
||||
return;
|
||||
}
|
||||
|
||||
data = server.arg(0);
|
||||
|
||||
Serial.println("Data:" + data);
|
||||
|
||||
File file = SPIFFS.open("/settings.json", "w");
|
||||
if (!file)
|
||||
{
|
||||
Serial.println("Failed to open file for writing");
|
||||
return;
|
||||
}
|
||||
|
||||
settings = data;
|
||||
file.print(data.c_str());
|
||||
file.close();
|
||||
|
||||
server.send(200, "text/plain", "");
|
||||
|
||||
}
|
||||
|
||||
void read_config()
|
||||
{
|
||||
File file = SPIFFS.open("/settings.json", "r");
|
||||
if (!file)
|
||||
{
|
||||
Serial.println("Failed to open settings file for reading");
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.println("====File Content SETTINGS====");
|
||||
String s;
|
||||
while (file.available())
|
||||
{
|
||||
s += (char)file.read();
|
||||
}
|
||||
Serial.print(s);
|
||||
|
||||
file.close();
|
||||
|
||||
ntimers = 0;
|
||||
settings = s;
|
||||
Serial.println("SETTINGS: " + settings);
|
||||
|
||||
deserializeJson(doc, settings);
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
for (int i=0;i < obj["data"].size(); i++) {
|
||||
int t = obj["data"][i]["kedy"].as<int>();
|
||||
String c = obj["data"][i]["cas"].as<String>();
|
||||
String datum = obj["data"][i]["datum"].as<String>();
|
||||
|
||||
int h = getValue(c,':',0).toInt();
|
||||
int m = getValue(c,':',1).toInt();
|
||||
int d = 0;
|
||||
int mh = 0;
|
||||
int y = 0;
|
||||
|
||||
Serial.println(t);
|
||||
Serial.println(c);
|
||||
Serial.println(datum);
|
||||
|
||||
if (datum != NULL) {
|
||||
d = getValue(datum,'.',0).toInt();
|
||||
mh = getValue(datum,'.',1).toInt();
|
||||
y = getValue(datum,'.',2).toInt();
|
||||
}
|
||||
|
||||
Timers[ntimers].when = t;
|
||||
Timers[ntimers].h = h;
|
||||
Timers[ntimers].m = m;
|
||||
Timers[ntimers].d = d;
|
||||
Timers[ntimers].mh = mh;
|
||||
Timers[ntimers].y = y;
|
||||
|
||||
ntimers++;
|
||||
}
|
||||
Serial.println("N timers:" + ntimers);
|
||||
}
|
||||
|
||||
void calculate_timers()
|
||||
{
|
||||
timer_def *t;
|
||||
struct tm tm;
|
||||
for (int i=0; i<ntimers ; i++)
|
||||
{
|
||||
t = &Timers[i];
|
||||
|
||||
tm.tm_sec = 0;
|
||||
tm.tm_min = t->m;
|
||||
tm.tm_hour = t->h;
|
||||
|
||||
if (t->d !=0) {
|
||||
tm.tm_mday = t->d;
|
||||
tm.tm_mon = t->mh;
|
||||
tm.tm_year = t->y;
|
||||
} else {
|
||||
tm.tm_mday = 0;
|
||||
tm.tm_mon = 0;
|
||||
tm.tm_year = 0;
|
||||
}
|
||||
|
||||
if (t->d == 0) {
|
||||
if (t->when == WORK_DAYS) {
|
||||
|
||||
} else if (t->when == WEEKENDS) {
|
||||
|
||||
} else if (t->when == TOMOROW ) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
|
||||
//iotWebConf.setStatusPin(PIN);
|
||||
iotWebConf.init();
|
||||
|
||||
if (!SPIFFS.begin(true))
|
||||
{
|
||||
Serial.println("An Error has occurred while mounting SPIFFS");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// -- Set up required URL handlers on the web server.
|
||||
server.on("/", handleRoot);
|
||||
server.on("/config", [] { iotWebConf.handleConfig(); });
|
||||
server.on("/settings", HTTP_POST, handleSave);
|
||||
server.onNotFound([]() { iotWebConf.handleNotFound(); });
|
||||
|
||||
setup_matrix();
|
||||
|
||||
|
||||
read_config();
|
||||
}
|
||||
|
||||
void scroll_line(String line) {
|
||||
void scroll_line(String line)
|
||||
{
|
||||
int x = 0, mx = 32;
|
||||
int y = 0;
|
||||
|
||||
if (line.length() * 6 > mx) {
|
||||
for (x = 0; x < line.length()*6 - mx; x++) {
|
||||
if (line.length() * 6 > mx)
|
||||
{
|
||||
for (x = 0; x < line.length() * 6 - mx; x++)
|
||||
{
|
||||
matrix->fillScreen(0);
|
||||
matrix->setCursor(-x, y);
|
||||
matrix->setTextColor(colors[1]);
|
||||
matrix->print(line);
|
||||
matrix->show();
|
||||
if (x == 0) delay(300);
|
||||
else delay(100);
|
||||
if (x == 0)
|
||||
delay(300);
|
||||
else
|
||||
delay(100);
|
||||
}
|
||||
delay(1000);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
matrix->fillScreen(0);
|
||||
matrix->setCursor(x, y);
|
||||
matrix->setTextColor(colors[1]);
|
||||
@@ -154,6 +347,8 @@ void scroll_line(String line) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void handleRoot()
|
||||
{
|
||||
// -- Let IotWebConf test and handle captive portal requests.
|
||||
@@ -162,36 +357,60 @@ void handleRoot()
|
||||
// -- Captive portal request were already served.
|
||||
return;
|
||||
}
|
||||
String s = "<!DOCTYPE html><html lang=\"en\"><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1, user-scalable=no\"/>";
|
||||
s += "<title>IotWebConf 02 Status and Reset</title></head><body>Hello world!";
|
||||
s += "Go to <a href='config'>configure page</a> to change settings.";
|
||||
s += "</body></html>\n";
|
||||
|
||||
File file = SPIFFS.open("/index.html", "r");
|
||||
if (!file)
|
||||
{
|
||||
Serial.println("Failed to open file for reading");
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.println("====File Content====");
|
||||
String s;
|
||||
while (file.available())
|
||||
{
|
||||
s += (char)file.read();
|
||||
}
|
||||
s.replace("%%SETTINGS%%",settings);
|
||||
Serial.print(s);
|
||||
|
||||
file.close();
|
||||
|
||||
server.send(200, "text/html", s);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
void loop()
|
||||
{
|
||||
unsigned long sec;
|
||||
unsigned long n = 0;
|
||||
|
||||
// -- doLoop should be called as frequently as possible.
|
||||
iotWebConf.doLoop();
|
||||
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
timeClient.begin();
|
||||
timeClient.update();
|
||||
sec = timeClient.getEpochTime();
|
||||
if (WiFi.status() == WL_CONNECTED)
|
||||
{
|
||||
if (initialized == false)
|
||||
{
|
||||
ntp.ruleDST("CEST", Last, Sun, Mar, 2, 120); // last sunday in march 2:00, timetone +120min (+1 GMT + 1h summertime offset)
|
||||
ntp.ruleSTD("CET", Last, Sun, Oct, 3, 60); // last sunday in october 3:00, timezone +60min (+1 GMT)
|
||||
ntp.begin();
|
||||
initialized = true;
|
||||
}
|
||||
sec = ntp.epoch();
|
||||
DateTime.setTime(sec);
|
||||
|
||||
if (n++ % 25 == 0) Lines[WEATHER_LINE] = get_weather_line();
|
||||
if (n++ % 25 == 0)
|
||||
Lines[WEATHER_LINE] = get_weather_line();
|
||||
|
||||
Lines[TIME_LINE] = get_time_line();
|
||||
Lines[DATE_LINE] = get_date_line();
|
||||
Lines[NAMES_LINE] = get_names_line();
|
||||
|
||||
for (int i=0;i<LAST_LINE;i++) {
|
||||
for (int i = 0; i < LAST_LINE; i++)
|
||||
{
|
||||
Serial.println(Lines[i]);
|
||||
scroll_line(Lines[i]);
|
||||
iotWebConf.doLoop();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user