(le scritte in verde permettono di attivare un link di
contesto se vi si clikka sopra)
22 gennaio 2023
Pagina ecologica no pubblicità no scherzetti no virus no
messaggi subliminali
Da inizio 2023 la storica applicazione di
Blynk è stata chiusa per i privati e la nuova versione non ha previsto la
possibilità di migrare i progetti realizzati in precedenza sulla nuova.
Ho provato a adattare un mio progetto con il nuovo applicativo ma ogni
passaggio mi è stato ostile, la filosofia progettuale è stata stravolta e dopo
diversi giorni di tentativi ho gettato la spugna.
Ho cercato in rete servizi di Cloud che mi permettessero di ospitare i miei
vecchi progetti, anche a pagamento, ma non ho trovato nulla che facesse al mio
scopo, sia per il prezzo che le mie esigenze.
Mi sono concentrato su una vecchia idea che mi avrebbe fatto risparmiare
tanto tempo e nel contempo volevo rendermi autonomo da nuove modifiche sull'uso
di pattaforme Cloud non proprietarie.
In rete ho visto diversi progetti che utilizzavano dei Server proprietari e
l'uso di linguaggi Javascript che ho sempre odiato poi se
si doveva gestire un
Server su un Hosting commerciale, oltre a conoscere bene il linguaggio
Javascript serve spendere per
l'apposito spazio per i DB MySQL.
Quasi tutti sfruttavano una scheda ESP8266 per emulare una eprom o SPIFFS o un
ARRAY o una sceda SD, tutti sistemi che
non garantiscono affidabilità di lettura
e scrittura nel tempo, mentre io cercavo certezze e stabilità.
Io riesco a muovermi bene con i linguaggi di una trentina di anni come HTML, il
DOS, Clipper e il linguaggio C, pertanto ho
elaborato un'alternativa a Blynk
sfruttando queste vecchie, ma sempre utili, conoscenze.
L'idea era di sfruttare comandi FTP per archiviare su ARUBA dei file, prelevarli
all'occorrenza per creare dei grafici e pubblicarli
su una mia apposita pagina
sempre sul mio spazio su ARUBA.
Il primo problema è stato quello di trovare una libreria FTP compatibile coi i
miei progetti.
Il secondo è stato quello di poter usare un vecchio pc XP per elaborare i
grafici.
Il terzo trovare le giuste configurazioni di Gnuplot (il programma che realizza
i grafici) per graficare più dati in contemporanea e
poter aprire e chiudere il
programma a piacere al fine di evitare errori che in passato si generavano dopo
una o due settimane di
ininterrotto lavoro e naturalmente evitare il consumo di
memoria del PC.
Attenzione, i file che si possono
scaricare dal mio Hosting, sono stati compressi, pertanto andranno
dezippati con apposito
programma.
Ecco lo schematico teorico/sperimentale su cui lavorerò per spiegarvi la mia soluzione:
Si legge, ad intervalli regolari il
valore della tensione presente sull'ingresso A0 della schedina Wemos, facendo
variare il valore
dell'ingresso
tramite un trimmer o altro, e si appende il dato dell'orario di acquisizione e il valore
su un file che viene aggiornato
su una directory,
precedentemente creata su ARUBA.
Il programma che utilizzo per la gestione delle mie pagine WEB è "FILEZILLA" è
libero e gratuito e perfetto, potete scaricarlo
dal
sito ufficiale o cliccando sull'immagine
sottostante.
Le credenziali per la connessione FTP ve le
rilascia ARUBA quando acquistate lo spazio HOSTING o potete richiederle in
seguito, comunque per utilizzare l'applicazione che vi descriverò sono
necessarie.
La mia cortella principale su ARUBA si chiama httpdocs pertanto ho creato una
sottodirectory dal nome download dove verrà
aggiornato il nostro file con i
dati.
Ecco il listato che adopereremo puoi scaricarlo da questo link:.
/*
Marchi Gian Domenico 21 gennaio 2023
File test per invio dati su file residente su ARUBA
Listato adattato da originale pubblicato da JOY-IT
inserito RTC per invio orario
FTP passive client for IDE v1.0.1 and w5100/w5200
Posted October 2012 by SurferTim
Modified 6 June 2015 by SurferTim
Web client https://playground.arduino.cc/Code/FTP/
list command FTP testati:
EthernetClient client; / WiFiClient client;
client.connect(server,21) // ftp su porta 21
client.print(F("USER ")); // per inserire user
client.println(user);
client.print(F("PASS ")); // per inserire password
client.println(pasw);
client.println(F("SYST"));
client.println(F("PWD")); // dove siamo
client.println(F("Type I"));
client.println(F("PASV")); // modo passivo
client.println(F("CWD httpdocs")); // cambio directory
dclient.connect(server,hiPort)) // seconda connessione
client.println(F("STOR dati02.txt")); // crea file
client.println(F("APPE PMcasa25.txt")); // appende record
dclient.stop(); // seconda connessione stop
client.stop(); // prima connessione stop
client.println(F("QUIT"));
Serial.println(F("Writing"));
clientBuf[64] // arrey
dclient.write((const uint8_t *) &clientBuf[0], 64); // scrive 65 caratteri
contenuti in array clientBuf
client.available()
client.peek()
client.read()
Serial.write(thisByte)
*/
#include <ESP8266WiFi.h>
#include <Wire.h>
#include <SPI.h>
#include "RTClib.h"
String infodato = ""; // stringa che conterrà il testo da inviare
int tipodatiinvio = 0; // usato per decidere su quale file appendere
float mV = 0; // tensione in millivolt
int valueA0 = 0; // valore letto sulla porta A0
RTC_DS1307 rtc;
/*
esempi di estrazione dati da RTC
DateTime now = rtc.now();
Serial.print(now.year(), DEC);
Serial.print('/');
Serial.print(now.month(), DEC);
Serial.print('/');
Serial.print(now.day(), DEC);
Serial.print(" (");
Serial.print(daysOfTheWeek[now.dayOfTheWeek()]);
Serial.print(") ");
Serial.print(now.hour(), DEC);
Serial.print(':');
Serial.print(now.minute(), DEC);
Serial.print(':');
Serial.print(now.second(), DEC);
Serial.println();
Serial.print(" since midnight 1/1/1970 = ");
Serial.print(now.unixtime());
Serial.print("s = ");
Serial.print(now.unixtime() / 86400L);
Serial.println("d");
// calculate a date which is 7 days, 12 hours, 30 minutes, and 6 seconds into
the future
DateTime future (now + TimeSpan(7,12,30,6));
Serial.print(" now + 7d + 12h + 30m + 6s: ");
Serial.print(future.year(), DEC);
Serial.print('/');
Serial.print(future.month(), DEC);
Serial.print('/');
Serial.print(future.day(), DEC);
Serial.print(' ');
Serial.print(future.hour(), DEC);
Serial.print(':');
Serial.print(future.minute(), DEC);
Serial.print(':');
Serial.print(future.second(), DEC);
Serial.println();
*/
const char* ssid = "xyxyxyxyxyxyx";
const char* password = "xyxyxyxyxyxyx";
boolean debug = false; // true = more messages
//boolean debug = true;
// solo per versione ethernet
//byte mac[] = { 0x4C, 0x75, 0x25, 0x0B, 0x74, 0x6E };
// change to your network settings
//IPAddress ip( 192, 168, 2, 2 );
//IPAddress gateway( 192, 168, 2, 1 );
//IPAddress subnet( 255, 255, 255, 0 );
WiFiClient client; // prima connessione per convenevoli con Server
WiFiClient dclient; // seconda connessione per invio stringa log
char outBuf[128];
char outCount;
String mese = "";
String giorno = "";
String ora = "";
String minuti = "";
String secondi = "";
String invia ="";
const char* server = "miosito.xy"; // inserie dominio senza www e con .xx
char user[] = "yxyxyxyxyxyx"; // username FTP
char pasw[] = "xyxyxyxyxyxy"; // password FTP
// provide text for the WiFi status
const char *str_status[]= {
"WL_IDLE_STATUS",
"WL_NO_SSID_AVAIL",
"WL_SCAN_COMPLETED",
"WL_CONNECTED",
"WL_CONNECT_FAILED",
"WL_CONNECTION_LOST",
"WL_DISCONNECTED"
};
// provide text for the WiFi mode
const char *str_mode[]= { "WIFI_OFF", "WIFI_STA", "WIFI_AP", "WIFI_AP_STA" };
void setup()
{
Serial.begin(9600); // inizializzo seriale
while (!Serial);
Serial.println();
Serial.println ( "Connect to Router requested" );
connectWifi();
if (WiFi.status() == WL_CONNECTED) {
Serial.print("WiFi mode: ");
Serial.println(str_mode[WiFi.getMode()]);
Serial.print ( "Status: " );
Serial.println (str_status[WiFi.status()]);
} else {
Serial.println("");
Serial.println("WiFi connect failed, push RESET button.");
}
// We start by RTC
rtc.begin();
if (! rtc.begin()) {
Serial.println("Couldn't find RTC");
Serial.flush();
while (1) delay(10);
}
if (! rtc.isrunning()) {
// Al primo caricamento imposta orario di sistema( inizio compilazione)
// poi non carica più, se pila ha un basso voltaggio l'orologio si ferma alla
// mancanza alimentazione poi riprende a contare al ritorno.
// Se serve reimpostare orario far leggere la riga sotto.
// Serial.println("RTC is NOT running, let's set the time!");
// When time needs to be set on a new device, or after a power loss, the
// following line sets the RTC to the date & time this sketch was compiled
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
// This line sets the RTC with an explicit date & time, for example to set
// January 21, 2014 at 3am you would call:
// rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
}
// When time needs to be re-set on a previously configured device, the
// following line sets the RTC to the date & time this sketch was compiled
//rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
// This line sets the RTC with an explicit date & time, for example to set
// January 21, 2014 at 3am you would call:
// rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
}
void loop()
{
creaorario(); // crea l'intestazione del record con orario
valueA0 = analogRead(A0);
mV = (valueA0 * 3070 / 1024);
tipodatiinvio = 1; // invio valore A0
doFTP(); // gestione invio su file mVVonair.txt
delay(714933); // aspetta per eseguire un ciclo ogni 15 minuti
}
void creaorario(){
// elaboro l'orario mantenendo sempre lo stesso formato con zero per numeri
inferiori a 10
// provato sprintf per aggiungere gli zero ma con wemos da errori
invia = ""; // serve azzerare si usa più volte
DateTime now = rtc.now();
if(now.month()< 10){
mese = "0" + String(now.month());
}
else
{
mese = String(now.month());
}
if(now.day()< 10){
giorno = "0" + String(now.day());
}
else
{
giorno = String(now.day());
}
if(now.hour()< 10){
ora = "0" + String(now.hour());
}
else
{
ora = String(now.hour());
}
if(now.minute()< 10){
minuti = "0" + String(now.minute());
}
else
{
minuti = String(now.minute());
}
if(now.second()< 10){
secondi = "0" + String(now.second());
}
else
{
secondi = String(now.second());
}
invia = String(now.year()) + "-" + mese + "-" + giorno + " " + ora + ":" +
minuti + ":" + secondi + " , " ;
Serial.println(invia);
/* Serial.print(now.year(), DEC);
Serial.print('/');
Serial.print(now.month(), DEC);
Serial.print('/');
Serial.print(now.day(), DEC);
Serial.print(" (");
Serial.print(daysOfTheWeek[now.dayOfTheWeek()]);
Serial.print(") ");
Serial.print(now.hour(), DEC);
Serial.print(':');
Serial.print(now.minute(), DEC);
Serial.print(':');
Serial.print(now.second(), DEC);
Serial.println();
*/
}
byte doFTP() //routine per iviare stringa sul file di log
{
if (client.connect(server,21)) {
Serial.println(F("Siamo connessi ad ARUBA"));
}
else {
Serial.println(F("Command connection failed"));
return 0;
}
if(!eRcv()) return 0;
Serial.println("Send USER");
client.print(F("USER "));
client.println(user);
if(!eRcv()) return 0;
Serial.println("Send PASSWORD");
client.print(F("PASS "));
client.println(pasw);
if(!eRcv()) return 0;
Serial.println("Send SYST");
client.println(F("SYST"));
if(!eRcv()) return 0;
//Serial.println("Send OPTS UTF8 ON");
//client.println(F("OPTS UTF8 ON"));
//if(!eRcv()) return 0;
Serial.println("Send PWD");
client.println(F("PWD"));
if(!eRcv()) return 0;
Serial.println("Send Type I");
client.println(F("Type I"));
if(!eRcv()) return 0;
Serial.println("Send PASV");
client.println(F("PASV"));
if(!eRcv()) return 0;
char *tStr = strtok(outBuf,"(,");
int array_pasv[6];
for ( int i = 0; i < 6; i++) {
tStr = strtok(NULL,"(,");
array_pasv[i] = atoi(tStr);
if(tStr == NULL)
{
Serial.println(F("Bad PASV Answer"));
}
}
// non funzionano le variabili per indicare directory o file
// pertanto serve darle per esteso
client.println(F("CWD httpdocs")); // sposta su root
//client.println(fold);
if(!eRcv()) return 0;
client.println(F("CWD download")); // sposta su download
//client.println(fold);
if(!eRcv()) return 0;
Serial.println("Send PWD");
client.println(F("PWD"));
if(!eRcv()) return 0;
unsigned int hiPort,loPort;
hiPort = array_pasv[4] << 8;
loPort = array_pasv[5] & 255;
Serial.print(F("Data port: "));
hiPort = hiPort | loPort;
Serial.println(hiPort);
if (dclient.connect(server,hiPort)) { // seconda connessione x invio dati
Serial.println(F("Data connected"));
}
else {
Serial.println(F("Data connection failed"));
client.stop();
return 0;
}
// a seconda del codice scrive il dato sul file
switch(tipodatiinvio){
// case 0: // invio istantaneo solo PM2.5 e PM10
// client.println(F("STOR PMautora.txt")); //appende file PMautora.txt
// invia = String(media25) + ":" + String(media10) + ":" + String(Uma);
// break;
case 1: // invio mV
client.println(F("APPE mVVonair.txt")); //appende file mVVonair se non esite lo
crea
invia = invia + ":" + String(mV);
break;
default:
break;
}
Serial.println("creato file");
if(!eRcv()) {
Serial.print("errore");
dclient.stop();
return 0;
}
Serial.println(F("Writing"));
Serial.println(invia);
dclient.println(invia);
dclient.stop();
Serial.println(F("Data disconnected"));
if(!eRcv()) return 0;
client.println(F("QUIT"));
if(!eRcv()) return 0;
client.stop();
Serial.println(F("Command disconnected"));
return 1;
}
byte eRcv()
{
byte respCode;
byte thisByte;
while(!client.available()) delay(1);
respCode = client.peek();
outCount = 0;
while(client.available())
{
thisByte = client.read();
Serial.write(thisByte);
if(outCount < 127)
{
outBuf[outCount] = thisByte;
outCount++;
outBuf[outCount] = 0;
}
}
if(respCode >= '4')
{
efail();
return 0;
}
return 1;
}
void efail()
{
byte thisByte = 0;
client.println(F("QUIT"));
while(!client.available()) delay(1);
while(client.available())
{
thisByte = client.read();
Serial.write(thisByte);
}
client.stop();
Serial.println(F("Command disconnected"));
}
//----------------------- WiFi handling
void connectWifi() {
Serial.print("Connecting as wifi client to SSID: ");
Serial.println(ssid);
// use in case of mode problem
WiFi.disconnect();
// switch to Station mode
if (WiFi.getMode() != WIFI_STA) {
WiFi.mode(WIFI_STA);
}
WiFi.begin ( ssid, password );
if (debug ) WiFi.printDiag(Serial);
// ... Give ESP 10 seconds to connect to station.
unsigned long startTime = millis();
while (WiFi.status() != WL_CONNECTED && millis() - startTime < 10000) {
delay(500);
Serial.print(".");
}
Serial.println("");
// Check connection
if (WiFi.status() == WL_CONNECTED) {
Serial.print("WiFi connected; IP address: ");
Serial.println(WiFi.localIP());
} else {
Serial.print("WiFi connect failed to ssid: ");
Serial.println(ssid);
Serial.print("WiFi password <");
Serial.print(password);
Serial.println(">");
Serial.println("Check for wrong typing!");
}
} // connectWiFi()
Premessa indispensabile è aver testato e verificato il perfetto funzionamento
della piattaforma del
sistema di sviluppo IDE
con
una scheda Wemos.
Per le librerie da usare, ESP8266WiFi dovrebbe già essere stata installata dopo
il caricamento della scheda Wemos, anche Wire e
SPI dovrebbero essere già presenti, serve solo installare la libreria RTClib-master
puoi scaricarla da questo link:.
Il dato che viene inviato al file di log è composto dalla routine creaorario()
che crea la variabile "invia", con l'orario seguito dal carattere "," per
dividere l'orario dai dati , e il vero e proprio contenuto della variabile "mV".
Impostando una variabile, nel nostro caso "tipodatiinvio", con valori da 0 a X è
possibile far gestire x file diversi con la stessa
routine doFTP() che
differenzia i diversi lavori da fare in base al valore di tipodatiinvio tramite la
funzione switch ... case.
Ho verificato la possibilità di gestire anche l'invio di mail con la stessa
connessione WIFI e in caso di assenza di dati dal WIFI o
di caduta di
connessione del WIFI su cui si appoggia la Wemos tutto riprende automaticamente.
Merita sottolineare che la soluzione da me adottata di inviare il singolo nuovo
dato e non tutto il file, permette di risparmiare giga
nel caso di connessione tramite schedina e non sovraccarica il Server.
Risolto il probema dell'invio dei dati, ora ci concentriamo sulla loro
elaborazione.
Come già accennato, si utilizzerà il programma
Open Source "Gnuplot" per creare i grafici e la relativa schermata .jpg da inviare
alla
pagina di consultazione su Web.
Consiglio di usare l'ultima versione a 32 bit per mantenere la compatibilità con
i pc con vecchi sistemi operativi tipo XP, ciò non
toglie la possibilità, per chi vuole, di usare l'ultima versione a 64 bit avendo
un pc che la supporta.
Questa è la
pagina ufficiale da cui scaricare il software Gnuplot,
per l'ultima versione a 32 bit potete scaricarla da questo link.
Al fine di poter automatizzare tutte le
elaborazioni è importante installare il programma Gnuplot in c:\gnuplot
Ora verifichiamo che venga prodotta l'immagine del grafico.
Creiamo una cartella a piacere come esempio c:\prova\grafica e inseriamoci i seguenti due file G_prova.txt
e mVVonair.txt poi
proviamo il comando che useremo nella fase
successiva quindi digitate in una finestra dei Prompt dei comandi, dalla
cartella
creata, il comando:
C:\gnuplot\bin\wgnuplot.exe -c "G_prova.txt"
Se per 4 secondi vi appare un grafico come quello sottostante allora è tutto a
posto e possiamo procedere con la fase di processo
automatico.
Per il processo in automatico di elaborazione e pubblicazione su WEB utilizzerò
un programma Open Source in grado di creare
dei file .exe eseguibili che contemple le seguenti prestazioni:
- creazione di interfccie video, menù a tendina, imput dati;
- gestione database, archivi, indici, relazioni;
- report, etichette, stampe varie;
- Connessioni dati ADO, SQL e ODBC;
- XML, TCP-IP, FTP;
- uso interfacce GUI;
- uso librerie come le QT;
- lavora su ambiente Windows, Linux, Mac, Arm.
Il programma sorgente è scritto in linguaggio tipo Clipper/DBIII.
Il suo uso è molto vasto e potrà servirvi per molteplici applicazioni sia
grafiche/matematiche, per la gestione dei database che per
l'interfacciamento con internet, a noi ci viene in aiuto per tre funzioni:
- gestire i timer delle azioni da svolgere;
- lanciare programmi dal Prompt dei comandi;
- caricare/scaricare file su Server tramite il comando FTP.
Il programma si chiama "Harbour" e questo è il sito da
dove potete scaricarlo.
Nella dir "harbour\hb32\contrib" sono inseriti moltissimi esempi di sorgenti che
potete studiare per nuove applicazioni.
Scaricato il file serve dezipparlo in c:\harbour
Il programma sorgente, che deve avere l'esensione ".prg", per la nostra prova è scricabile da questo link:.
FUNCTION main()
local cUrl,oUrl
local oClient
cls
? "RICEVI DAL SERVER Il FILE SU valore A0"
? "ELABORA Il GRAFIC0 E LO POSTA SUL SERVER"
? "PER PROVA sostituisci user password e dominio e directory"
? "Testato per ARUBA"
? "@ Gian Domenico Marchi 22/01/2023"
?
// userid password dominio e cartella
cUrl := "ftp://" + "XYXYXYXYXY" + ":" + "XYXYXYXY" + "@" +
"www.XYXYXYXY.it/httpdocs/download/"
oUrl := TUrl():New( cUrl )
oUrl:cUserid := StrTran( oUrl:cUserid, "&at;", "@" )
oClient := TIPClientFTP():new( oUrl )
oClient:nConnTimeout := 60000
do while .t.
//=========================================================================
?
? time() + " SCARICO FILE di prova "
oClient:Open()
? time() + " Open() OK"
//==========================================================================
?
? time() + " Scarico mVVonair.txt..."
oClient:DownloadFile( "mVVonair.txt" )
inkey(1)
//==========================================================================
inkey(1)
? time() + " creo il grafico per G_prova..."
RUN C:\gnuplot\bin\wgnuplot.exe -c "G_prova.txt"
inkey(1)
//==========================================================================
? time() + " Carico grafico prova..."
oClient:UploadFile( "prova.jpg" )
//==========================================================================
?
? time() + " Committing..."
oClient:Commit()
? time() + " Commit() OK"
//==========================================================================
?
? time() + " Closing..."
oClient:Close()
? time() + " Close() OK"
//=========================================================================
? "Aspetta 14 minuti o premi un tasto per nuovo dowload..."
inkey(890)
enddo
RETURN( nil )
Dezippato il file H_prova.prg sulla nostra directory c:\prova\grafica, dopo aver modificato le
nostre credenziali per l'accesso FTP
al nostro Hosting, ora creiamo il nostro
eseguibile lanciando il comando dal Prompt
dei comandi:
c:\harbour\hb32\bin\hbmk2.exe -w3 hbtip.hbc H_prova.prg
Se non ci sono stati errori comparirà la scritta "... Done." e avremo generato
sulla nostra directory il nostro file "H_prova.exe"
Se lo lanciamo ci automatizzerà tutto come desideravamo.
Al momento serve intervenire manualente, ad intervalli settimanali, per
accorciare il file che la scheda Wemos archivia sul nostro
spazio Hosting, scorporando e archiviando i dati più vecchi di 4/5 giorni al
fine di rendere intellegibile il grafico che viene
pubblicato.
Tale lavoro non automatizzato ci permette di non dimenticarci della manutenzione
dei nostri dispositivi, ma volendo si può implementare il sorgente Harbour per
gestire anche questo ultimo lavoro.
Vi saluto sperando che mi leggiate nel mio prossimo lavoro che utilizzerà
questo sistema di automazione, riguarderà il
monitoraggio dell'aria con ben tre postazioni e dove in gioco ci saranno ben 64
file. Intanto vi auguro un buon lavoro.
Per contatti: