Buonasera a tutti, volevo postare nel gruppo il link al mio repository GitHub per il SW della mia stazioncina barometrica che misura:
(BMP280)
Temperatura
Umidità
Pressione
(PMS5003)
PM1.0
PM2.5
PM10
(Soliti sensori meccanici che ho modificato con interruttori
magnetici NON meccanici tranne la direzione)
Velocità del vento
Direzione del vento
Quantità di pioggia caduta
il tutto alimentato da un pannello solare da 30W batteria 18650 2500mAh
e schedina che la ricarica col pannello solare.
La board è un Arduino MKR-1010 decurtato dei vari led (tranne il LED_BUILTIN) ed alimentato da Vin a 5v.
il link a github è questo:
L'intenzione è di ricevere Consigli/Strigliate/Massacri dai più esperti di me (e ci vuole poco he ;-)))
Specie negli algoritmi di misurazione e tecniche di ottimizzazione , comunicazione dati ecc...
Un grazie a chi vorrà collaborare a migliorarmi
Potevi ottenere tutto con sprintf() e a tal proposito è sempre preferibile usare la versione "n" snprintf() perché in caso di "sforamento" del buffer non va a sporcare i byte seguenti creando possibili casini, cosa che invece fa sprintf().
Quello che invece non riesco proprio a capire e mi sembra tanto strana è la funzione uint16_t readWindDir(void).
Sembrerebbe un classico potenziometro 360°, però la progressione dei valori analogici associata agli angoli risultanti non è lineare, come mai?
ho aggiunto un pdf sotto la directory assets che ti chiarirà le idee,
Do un'occhiata al discorso snprintf (quelli sono spezzoni di codice rimasti da qualche esempio
preso come spunto e mai adattati, grazie dell'info, indago e cerco di aggiustare.
Inoltre la direzione del vento ancora non ho ben chiaro come gestirla a livello statistiche e pure la velocità del vento non sono sicuro sia calcolata correttamente... (mi sembra bassina guardando le statistiche) (dai un'occhiata se vuoi sotto assets/hosted_on_raspberry_pi_server/Grafana/ )
Per adesso grazie 1000 degli spunti!
In pratica sono delle resistenze che vengono combinate con la chiusura di uno/due reed switch...
Io avrei usato una tabella di lookup, che secondo me è più efficiente e manutenibile di quell'albero di if-else-if, ma alla fine è solo una preferenza personale.
Per quanto riguarda la velocità del vento, non mi è immediato il principio di funzionamento.
Come mai usi un array?
Io avrei usato una tabella di lookup, che secondo me è più efficiente e manutenibile di quell'albero > di if-else-if, ma alla fine è solo una preferenza personale.
se per tabella di lookup intendi una specie di "dizionario" con i vari valori che estrai con una chiave, come risolvi il fatto che il valore chiave che ottieni non è mai una costante essendo una conversione A/D ? (correggimi se non ho capito na mazza :-D)
Per quanto riguarda la velocità del vento, non mi è immediato il principio di funzionamento.
Come mai usi un array?
Allora, avevo trovato un articolo che spiegava come determinare la velocità del vento (vedi LINK )
in cui si scriveva che la velocità istantanea si calcolava in una media su 3 secondi e cosi ho impostato un array sufficiente per 10 minuti di archiviazione (poi spiego il perchè) ovvero (10min = 600secondi , ma essendo che calcolo già la media usando un allarme che sveglia la board ogni 3 secondi ed aggiorna l'indice puntato dall'array di storage, mi bastano 200 posizioni ovvero 600/3=200, quindi ogni uint_8 dell'array contiene la media dei precedenti 3 secondi di velocità del vento i quali vengono calcolati sommando i vari 'TICK' ricevuti dall'interrupt generato ad ogni FALLDOWN del segnale dell'interruttore magnetico che le palette del vento generano ad ogni rotazione completa e dividendo per 3(secondi).
Questo buffer di 200 elementi poi è pure duplicato in modo che , una volta riempito e deve essere inviato a destinazione, per l'invio si perderebbero secondi preziosi per le successive misurazioni che sovrascriverebbero l'inizio del buffer stesso, quindi, il buffer riempito viene copiato su un un secondo buffer il quale può venir spedito con calma (abbiamo 10 minuti prima del prossimo svuotamento) mentre il buffer originale può essere prontamente riempito senza problemi.
La necessità del buffer è risultata obbligatoria perchè la stazione è alimentata a batteria+solare e non posso tenere operativo sempre il wifi altrimenti non ce la farebbe specie d'inverno, cosi ho optato x la soluzione dei buffer, ovviamente ogni consiglio migliore è bene accolto!
Oggi non so perchè l'Editor di arduino si è inventato che il mio sorgente NON è nella sua directory e ne deve creare un'altra, non ho idea del perchè... indago.
@L0cutus Incece di usare il '>' per riportare un pezzo di una discussione, evidenzialo nella discussione originale e premi il bottone "Quote" che ti appare ... riporterai sempre il testo, ma ci sarà il riferimento a cosa ti riferisci e l'autore verrà informato
Esattamente!
Ci sono molti modi per farlo, io che sono un'amante delle struct ad esempio avrei pensato una cosa del genere:
Per il discorso delle velocità, delle medie e di tutti gli altri calcoli e statistiche invece, considerato he c'è un webserver, non potevi fare tutto a livello di "front-end"?
Ad esempio, Grafana da dove li prende i dati?
Un database SQL mi pare di ricordare dall'altro post, se non mi sto confondendo...
Ci do un'occhiata, anche a me concettualmente piacciono molto le struct.
Fatto! Spettacolo!
Non sapevo si potessero usare cosi i cicli for su arduino, sembra più una cosa da
linguaggio ad alto livello tipo python
Molto fico l'uso delle struct !
Probabilmente meno immediato per un neofita (tipo me) ma molto elegante/pulito
e meglio gestibile di certo degli if...
PS: Pensa che ho visto giusto oggi il video di un indiano ( tale techiesms ) che parlava del sito Wokwi , non lo avevo mai sentito nominare sto sito, me lo devo guardare per bene.
Se avessi fatto cosi, mi ci sarebbero voluti 4 volte lo spazio per memorizzare il vento (600 secondi = 600 campioni = array da 600 posizioni * 2 (array base + array copia come ti dicevo prima)) per quello almeno per il vento (anche per la pioggia in realtà) ho preferito fare le medie sul posto.
Corretto! Li prende da un database sqlite che viene alimentato da... ok spiego come funzia il tutto:
mkr 1010 prende i dati
ogni 10 minuti li memorizza su un server ftp (raspberry pi) quando ha finito di inviarli tutti e 3
(si, crea 3 files distinti) , memorizza un file speciale che si chiama "Data_Rdy.txt"
ogni 2 minuti node red che si trova sempre sul raspberry pi, cerca il file di cui sopra
e quando lo trova inizia a processare i 3 files memorizzandoli su 3 tabelle in un
database sqlite, infine cancella il file "Data_Rdy.txt"
grafana (installata sempre sullo raspberry pi) ogni 5 minuti prende i dati dal database sqlite
e li mostra.
il gioco reinizia da capo
Si, la cosa è molto macchinosa e mi piacerebbe PARECCHIO semplificarla ma
non so ancora come...
Aldilà del macchinoso, cosa che in effetti è, ci sono alcune "scelte progettuali" che io avrei fatto diversamente.
Il server FTP serve davvero? Non puoi inserire i dati direttamente nel database?
Un database SQLite non è l'ideale per questo tipo di operazioni a mio avviso per tutta una serie di ragioni: è un database embedded e file-based ed in fondo non c'è motivo per questa scelta vista la disponibilità di un server su cui far girare un più classico e flessibile MySQL.
Inoltre è molto molto lento in confronto ad altri DB SQL.
Intendiamoci, io l'ho usato e lo uso sempre volentieri, ma va bene per poche migliaia di record. Se i numeri salgono come nel tuo caso è meglio considerare altro. Mano a mano che crescerà il tuo DB, fare una query sarà un'operazione sempre più lenta!
Ormai il progetto è bello che avviato, ma considera che ci sono database che nascono proprio per memorizzare delle serie numeriche con meccanismi di aggregazione, filtri ed elaborazioni dati automatici.
Ad esempio io ho usato con molta soddisfazione InfluxDB proprio con un Raspberry Pi (primo link che è venuto fuori con Google) e Grafana.
C'è anche la libreria ufficiale per Arduino oppure una comoda e più generica API HTTP (la libreria supporta solo ESP).
allora, sqlite l'ho scelto per la semplicità d'uso/installazione, non sapevo del discorso
velocità ma già me ne sto rendendo conto dalla dashboard di grafana quanto stanno
le query (del vento) a risolversi :-/
MySQL mi sembrava esagerato per i miei usi (non l'ho mai usato ma so che viene
usato nei server web 'seri' )
influxDB l'ho preso in considerazione ma non so perchè l'ho scartato per la gestione del timestamp che mi sembra gestisca lui, ovvero quando gli invii i dati lui gli affibia un
timestamp e le query le fa su quello, sicuramente non ho approfondito (sono meno
di un niubbo nei DB!) che tu sappia, se gli mando in blocco 200 dati del vento, lui non è
che me li considera arrivati tutti in quell'orario? o posso dirgli io dato per dato in che
ora è arrivato? (non so se mi sono spiegato).
InfluxDB è supportato pienamente anche da node-red quindi non ci sarebbero grossi
problemi di implementazione penso.
Purtroppo non lo potrei usare dalla mia board MKR, ma da node-red si.
Non ho scelto le board ESP32 solo perchè intimorito dalla gestione del 'sonno' che fa
rebootare la board invece di interromperla come fanno i vari arduino e mi sembrava
che la cosa mi complicasse parecchio la vita
Il progetto è in piedi ma nulla vieta di fare modifiche a livello SW (come avrai visto ho
la possibilità di fare aggiornamenti OTA), dei dati non mi preoccupo per ora essendo
tutto a livello di 'test' ad oggi.
Grazie ancora per i feedback!
Cavolo, mi sono andato a leggere la libreria di InfluxDB per ESP32 su github ed `e uno spettacolo!
È prevista la spedizione dei dati in "batch" quindi non è necessario essere sempre connessi per
inviarli di continuo, in questo modo bypasserei node-red e invierei i dati dall'ESP32 direttamente
al server raspberry pi con InfluxDB e Grafana.
Porca trota mi tocca spaccarmi il cranio su ESP32 e sul come potrei usarlo per la stazione, mi
rifaccio una basetta con un esp32 , i cavi e collegamenti vari su quella attuale sono tutti con cavi
sganciabili e riposizionabili su una nuova
Il più sarà capire come gestire RTC che prima avevo comodo sul mkr 1010 e il fatto che per
l'appunto, reboota ad ogni risveglio da quello che so.
Certo che puoi!
Ho appena provato ad inserire dei file da un csv (esportato da un'altro bucket) ed ovviamente ciascun punto inserito ha il suo timestamp.
Comunque non serve necessariamente un ESP32
Questa è una richiesta HTTP (in pratica quello che deve "printare" il tuo client" verso il server del DB) per inserire due punti di misura con timestamp aggiornato pochi secondi fa
l'RTC potresti anche evitarlo, perché puoi sincronizzare il tempo con un server NTP (magari che gira pure lui sul Raspberry).
Il fatto che fa il reboot è una scocciatura, ma se i dati da inviare sono sulla SD (o sulla memoria flash) alla fine non lo vedo come un grosso problema.
Avevo settato anche il mkr1010 a prendere l'ora da NTP ogni 2gg se non erro.
Devo iniziare da zero con ESP32, vedo se becco qualche libro che parte dalle basi.
Se non tiene in memoria da qualche parte i dati , per forza di cose dovrò optare per sd esterna,
ma i consumi cosi....
A proposito, hai una qualche board in particolare da consigliare che abbia un po di riguardo
per i consumi?
Il micro ESP32 ha un'area di memoria detta RTC, pensata proprio conservare le variabili durante lo sleep.
Inoltre è possibile usare la stessa flash per salvare dati in modo permanente se necessario (io coniglio la libreria preferences).
Per quanto riguarda la board, ti consiglio una di quelle che è pensata già per essere alimentata da una batteria. Lilygo ad esempio ne ha molte in catalogo.
Io ad esempio ho il T-OI Plus che uso spesso per fare dei test.
Se l'attenzione per i consumi è prioritaria, prendi una scheda che monta una MCU delle più recenti tipo ESP32-S3 (dual core @240MHz, molte periferiche, bassi consumi in deep-sleep[8uA]) oppure un ESP32-C3 (più piccolino, single core @160MHz, bassissimi consumi in deep-sleep [5uA]) contro i 100uA del classico ESP32 (esclusi i led che rimangono sempre accesi ovviamente).
Se vuoi darci un'occhiata ho messo su un gist con uno sketch di test che avevo fatto qualche tempo fa (riadattato un po' secondo le tue esigenze). Anche se è per ESP32, utilizza un generico client e non la libreria specifica quindi se vuoi dovresti riuscire ad adattarlo facilmente anche per il MKR1010.
Scusa il ritardo ma ho fatto qualche test in proposito, più che la memoria non riesco a
capire come 'ragiona' il deepsleep, come immaginerai io ho bisogno di 3 eventi che
risvegliano la board, uno è un timer/allarme che risvegli il MC ogni 3 secondi per aggiornare
il puntatore all'array dei dati del vento (media dei dati del vento ogni 3 secondi) ed altri 2 eventi
che sono legati ad altrettanti 2 pin, uno che misura la quantità di pioggia ed uno per l'anemometro.
Ho provato a creare uno sketch che simula il timer ed i 2 eventi casuali, ho scoperto che quando
si verifica uno dei 2 eventi casuali (vento e pioggia) sui pin, viene 'perso' l'evento timer ovvero la
board non si risveglia più con il timer, sembra venga resettato.
In pratica qualsiasi risveglio della board che non sia di tipo TIMER, resetta il timer stesso
al successivo deeepsleep.
L'unica cosa che mi viene in mente è l'uso di un orologio esterno che non venga intaccato dal deepsleep dell'ESP32 ma la cosa complicherebbe un po il tutto, che dici?
Prova a mettere il codice in questione perché io non ho affatto riscontrato questa cosa...
Hai guardato con attenzione gli esempi inclusi? Soprattutto ai commenti!
Ad esempio, partendo dall'esempio ExternalWakeUp.ino ho rapidamente fatto quello che dici abilitando il wake-up dai pin 32 e 33 e nello stesso momento dal timer.
Lo sketch testato è questo e funziona come ci si aspetta:
se non si fa nulla si risveglia ogni 30 secondi con il timer mentre se metto sul livello HIGH uno qualsiasi dei due pin si risveglia immediatamente (attenzione che i pin in questione vanno tenuti a pull-down).
/*
Deep Sleep with External Wake Up
=====================================
This code displays how to use deep sleep with
an external trigger as a wake up source and how
to store data in RTC memory to use it over reboots
This code is under Public Domain License.
Hardware Connections
======================
Push Button to GPIO 33/32 pulled down with a 10K Ohm
resistor
NOTE:
======
Only RTC IO can be used as a source for external wake
source. They are pins: 0,2,4,12-15,25-27,32-39.
Author:
Pranav Cherukupalli <cherukupallip@gmail.com>
*/
#define uS_TO_S_FACTOR 1000000ULL /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP 30 /* Time ESP32 will go to sleep (in seconds) */
#define BUTTON_PIN_BITMASK 0x300000000 // 2^33 + 2^32 in hex
RTC_DATA_ATTR int bootCount = 0;
/*
Method to print the reason by which ESP32
has been awaken from sleep
*/
void print_wakeup_reason(){
esp_sleep_wakeup_cause_t wakeup_reason;
wakeup_reason = esp_sleep_get_wakeup_cause();
switch(wakeup_reason)
{
case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break;
case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break;
case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break;
default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
}
}
void setup(){
Serial.begin(115200);
delay(1000); //Take some time to open up the Serial Monitor
//Increment boot number and print it every reboot
++bootCount;
Serial.println("Boot number: " + String(bootCount));
//Print the wakeup reason for ESP32
print_wakeup_reason();
/*
First we configure the wake up source
We set our ESP32 to wake up for an external trigger.
There are two types for ESP32, ext0 and ext1 .
ext0 uses RTC_IO to wakeup thus requires RTC peripherals
to be on while ext1 uses RTC Controller so doesnt need
peripherals to be powered on.
Note that using internal pullups/pulldowns also requires
RTC peripherals to be turned on.
*/
// esp_sleep_enable_ext0_wakeup(GPIO_NUM_33,1); //1 = High, 0 = Low
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
Serial.println("Setup ESP32 to sleep for every " + String(TIME_TO_SLEEP) +
" Seconds");
//If you were to use ext1, you would use it like
esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK,ESP_EXT1_WAKEUP_ANY_HIGH);
//Go to sleep now
Serial.println("Going to sleep now");
esp_deep_sleep_start();
Serial.println("This will never be printed");
}
void loop(){
//This is not going to be called
}