(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: