Benritrovati,
ogni tanto ho bisogno di voi quando le esigenze si fanno superiori alla mia conoscenza del C. Espongo il mio dilemma:
In un progetto con un sensore BME280 + RTC DS3231 + arduino uno, ho l'esigenza di calcolare le medie temperature sia giornaliere che mensili.
Sto partendo da qui:
#include <Wire.h>
#include "RTClib.h" // https://github.com/adafruit/RTClib - v1.3.1
#include <Adafruit_Sensor.h> // https://github.com/adafruit/Adafruit_Sensor - v1.0.3
#include <Adafruit_BME280.h> // https://github.com/adafruit/Adafruit_BME280_Library - v1.0.10
float t;
unsigned long tempo_s = 0;
byte numero_letture = 0;
float somma = 0.0;
void setup()
{
.....// le solite cose
}
void loop()
{
t = bme.readTemperature();
.....
medie();
}
//================================
// CALCOLO MEDIE GIORNALIERE
//================================
void medie()
{
if ((millis() - tempo_s) > 900000) { // lettura ogni 15 min
somma = somma + t;
numero_letture ++;
tempo_s = millis();
}
if (numero_letture >= 96) { // 96 letture = 24h
media_day = somma / numero_letture; // ottengo la media delle letture
numero_letture = 0; // resetto il contatore e la somma
somma = 0.0F;
}
}
ottenuta media_day come l'azzero per preparala al giorno seguente? e poi come posso predisporre il codice per il relativo calcolo della media mensile. Immagino che mi serviranno 12 variabili globali, una per ogni mese, nelle quali storare il valore ottenuto per poi stampare una tabella.
droidprova:
ottenuta media_day come l'azzero per preparala al giorno seguente?
Non ho capito. Nel codice già azzera dopo 96 letture, quindi devo supporre che legga ogni 15 minuti (24 ore * 4 letture/ora = 96). In alternativa direi che potresti azzerare quando arrivi a mezzanotte leggendo l'ora dall'RTC, memorizzi la media di quel giorno e la riporti a zero.
e poi come posso predisporre il codice per il relativo calcolo della media mensile. Immagino che mi serviranno 12 variabili globali, una per ogni mese, nelle quali storare il valore ottenuto per poi stampare una tabella.
La singola media mensile come vuoi calcolarla? Facendo la media dei valori di temperatura media di ognuno dei suoi giorni (quindi la media della media)?
Una volta calcolata, si, la metti in un array (lascia perdere le sbrodolate di variabili) con 12 elementi.
Ma con questi dati poi che ci devi fare? Arduino non fa da database, tu questi dati non tenerli in memoria (altrimenti se va via la corrente li perdi tutti), quantomeno salvali su una scheda SD locale (estraendo la quale poi ti potrai copiare i dati su un PC), oppure mandarli man mano ad un server tramite rete (ti serve una schield Ethernet o un WiFi con una schedina ESP).
quelle che intendevo è, ma dopo aver usato la routine
/================================
// CALCOLO MEDIE GIORNALIERE
//================================
void medie()
{
if ((millis() - tempo_s) > 900000) { // lettura ogni 15 min
somma = somma + t;
numero_letture ++;
tempo_s = millis();
}
if (numero_letture >= 96) { // 96 letture = 24h
media_day = somma / numero_letture; // ottengo la media delle letture
numero_letture = 0; // resetto il contatore e la somma
somma = 0.0F;
}
}
che mi salva la media del giorno corrente, quest'ultima dove la metto (non voglio usare SD, piuttosto uso delle batterie per non far spegnere arduino) per fare la media mensile?
Non hai spiegato cosa ti serve e cosa dovrai ottenere come risultato finale. Se alla fine devi avere dati su PC, allora la SD sarebbe la soluzione migliore.
Tu dici che piuttosto di usare una SD tieni sempre acceso arduino con pile...
Se proprio non vuoi usare una SD e ti basta avere i dati su arduino, potresti scrivere e leggere i dati sulla eeprom di arduino con i metodi EEPROM.put e EEPROM.get, di cui trovi esempi di utilizzo nel reference.
Salvando la medi di 30 giorni e 12 mesi con scrittura una volta al giorno, non hai problemi di durata della EEPROM o di limite di memoria.
Ho una variabile che mi memorizza il numero giorno e una variabile che memorizza il numero mese
entrambi questi valori li salverò in eprom
2)All'accensione di arduino nella funzione setup() leggo da eerpom il numero giorno e numero mese e
li salvo nelle sue variabili
3)Se è il 1 giorno del mese metto a zero tutti i 30 giorni sulla eeprom
Calcolo la media giornagliera e salvo il valore in eeprom, incremento la variabile giorno e salvo in eeprom
Ultimo giorno del mese, calcolo la media mensile leggendo da eeprom i valori dei 30 giorni, salvo
in eeprom e incremento la variabile mese e salvo in eeprom
Se i mesi superano 12 azzero tutte le variabile e la eeprom
il risultato che vorrei ottenere in seguito al calcolo delle medie è che ogni giorno io possa consultare la media del giorno prima e che il 1° gg del mese consultare la media del mese prima.
Questi dati verrebbero inviati da arduino al mio smartphone tramite connessione bluetooth .
droidprova:
il risultato che vorrei ottenere in seguito al calcolo delle medie è che ogni giorno io possa consultare la media del giorno prima e che il 1° gg del mese consultare la media del mese prima.
Questi dati verrebbero inviati da arduino al mio smartphone tramite connessione bluetooth .
Ora sei stato più preciso, bene. Per dire, in questa descrizione dici che non vuoi avere anche tutte le medie mensili dell'ultimo anno (quindi niente array di 12 mesi), ti basta la media del giorno precedente e quella del mese scorso.
Allora anche se assumi che Arduino non si possa spegnere mai direi che ti basta usare la EEPROM (anche se a me non piace molto come soluzione, ma ok).
Diciamo che se codifichi nella EEPROM la temperatura con 2 byte (es. un intero che indica i decimi di grado, ossia "int(temperatura*10)"), avrai:
A) in memoria un piccolo array di 4 elementi float con le ultime 4 letture (1 ogni 15 minuti)
B) in EEPROM 24x2=48 byte per memorizzare le ultime letture con la media oraria del giorno corrente
C) in EEPROM 2 byte per la media del giorno precedente
D) in EEPROM 31x2=62 byte per memorizzare le letture giornaliere del mese corrente
E) in EEPROM 2 byte per la media del mese precedente.
Ad ogni ciclo quindi esegui questi passi:
aggiungi la lettura all'array A
se è la quarta lettura calcoli la media oraria e la inserisci in B resettando A, quindi:
se è terminato il giorno calcoli da B la media del giorno appena concluso e la metti in C e la inserisci in D, resettando B, quindi:
se è terminato il mese, calcoli da D la media del mese e la metti in E, resettando anche D.
Per cui userai solamente 114 byte dalla EEPROM, e quando interrogherai Arduino via bluetooth, ti farai mandare i contenuti di C ed E.
Se salvare ogni 15 minuti serve per evitare errori in caso mancata alimentazione, allora si dovrà prevedere il salvataggio e la lettura in eeprom ogni quindici minuti del valore dell'indice array A. In pratica se manca l'alimentazione in 15 minuti, devo leggere da eeprom quanti elementi ho già salvato.
#include <Wire.h>
#include "RTClib.h" // https://github.com/adafruit/RTClib - v1.3.10
#include <Adafruit_Sensor.h> // https://github.com/adafruit/Adafruit_Sensor - v1.0.3
#include <Adafruit_BME280.h> // https://github.com/adafruit/Adafruit_BME280_Library - v1.0.10
Adafruit_BME280 bme; // I2C
RTC_DS3231 rtc;
char media_GG[32];
char media_MM[13];
float t;
unsigned long tempo_s = 0;
byte numero_letture = 0;
byte numero_GG;
float media_day = 0.0F;
float media_mese = 0.0F;
float somma_1 = 0.0F;
float somma_2 = 0.0F;
byte salvamese;
byte salvaanno;
//==============================================================
// SETUP
//==============================================================
void setup() {
Serial.begin(9600); // apre la porta seriale e la inizializza a 9600 bps
Wire.begin(); // avvio bus i2c
// avvio sensore BME280
bme.begin();
// avvio RTC
DateTime now = rtc.now();
rtc.begin(); // avvia libreria modulo RTC
if (rtc.lostPower()) {
//rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); // per sincronizzare data e ora togliere il commento a questa riga e aggiornare arduino. Poi ri-commentare e aggiornare di nuovo
// 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));
//rtc.adjust(DateTime(2019, 7, 14, 10, 50, 0));
}
salvamese = now.month();
salvaanno = now.year();
}
//==============================================================
// LOOP
//==============================================================
void loop() {
t = bme.readTemperature();
medie();
}
//==============================================================
// CALCOLO MEDIE GIORNALIERE
//==============================================================
void medie() {
DateTime now = rtc.now();
if ((millis() - tempo_s) > 900000) { // lettura ogni 15 min (max 96 al gg)
somma_1 = somma_1 + t;
numero_letture ++;
tempo_s = millis();
}
if ((now.hour() == 23) && (now.minute() == 59)) { // condizione per salvataggio media GIORNO corrente e reset variabili
media_day = somma_1 / numero_letture; // ottengo la media delle letture
media_GG[now.day()] = media_day; // riempio l'elemento relativo al giorno corrente usanto come indice now.day()
numero_letture = 0; // resetto il contatore e la somma
somma_1 = 0.0F;
numero_GG ++; // conto i giorni del mese in cui sono state effettuate le letture ogni 15 min e incremento
}
if (salvamese != now.month()) // quando cambia il MESE
{
// qui metto il codice per fare la media dei giorni e salvarla nell'elemento array char media_MM[12];
byte i;
for (i = 0; i < 32; i++) { // scansiono l'array per ottenere la somma di tutti i gli elementi riempiti
somma_2 = somma_2 + media_GG[i];
}
media_mese = somma_2 / numero_GG; // ne faccio la media e uso come dividendo il numero dei giorni ottenuto contando i giorni utili
media_MM[now.month()] = media_mese; // riempio l'elemento dell'array media_MM[] relativo al mese corrente usando come indice now.month()
salvamese = now.month(); // prima di uscire resetto variabili
somma_2 = 0.0F;
media_mese = 0.0F;
numero_GG = 0;
}
if (salvaanno != now.year()) // quando cambia l'ANNO resetto l'array con le medie mensili
{
media_MM[13] = 0;
salvaanno = now.year();
}
}
l'IDE lo compila senza errori ma non l'ho ancora provato. Una volta travata la quadra poi inseriro i vari bkp su EEPROM.
if ((now.hour() == 23) && (now.minute() == 59)) { // condizione per salvataggio media GIORNO corrente e reset variabili
media_day = somma_1 / numero_letture; // ottengo la media delle letture
media_GG[now.day()] = media_day; // riempio l'elemento relativo al giorno corrente usanto come indice now.day()
numero_letture = 0; // resetto il contatore e la somma
somma_1 = 0.0F;
numero_GG ++; // conto i giorni del mese in cui sono state effettuate le letture ogni 15 min e incremento
}
se stampo questo:
Serial.println("Medie giorni mese: ");
int i;
for (i = 0; i < 32; i++) { // scansiono l'array per ottenere la somma di tutti i gli elementi riempiti
Serial.print(" ");
Serial.println(media_GG[i], 1);
}
in riferimento al numero della giornata di ieri ottengo "nan"
Scusa ma hai fatto qualche confusione nei tipi di variabili...
Hai "media_day" che è un float, e qui va bene, ma tu quel valore lo assegni a media_GG che è dichiarato "char" che quindi non puoi visualizzare come un numero (con 1 decimale tra l'altro).
Se gli array devono mantenere valori numerici float devi dichiararli float, non char.
finchè now.minute() == 59 il blocco viene eseguito e quindi al primo passaggio le variabili essendo state azzerate poi dal secondo passaggio arduino si trova ad effettuare un calcolo non valido.
Vorrei alzare un flag al primo passaggio ma poi non so dove azzerarlo.
Non ho capito dove ti dà "NaN", se non hai più alcuna scrittura sulla seriale stando al tuo ultimo listato. Se hai aggiunto la visualizzazione su seriale delle medie dei giorni del mese corrente non vedo perché dovrebbe darti NaN, quindi posta l'intero e ultimo listato che hai usato, così vediamo meglio.
A parte questo, secondo me devi prima definirti bene l'algoritmo ossia il flusso con le varie condizioni, e POI scrivere codice.
Intanto secondo quanto hai detto la volta precedente a te non serve mantenere la media mensile di tutti i mesi dell'anno, invece vedo nel listato che hai ancora un "media_MM[13];" e "salvaanno", per cui devi decidere definitivamente (e dirci) cosa vuoi ottenere, e da questo poi potrai derivare la relativa implementazione.
Sarebbe meglio un diagramma di flusso, ma può anche bastare usare i commenti, nidificandoli opportunamente.
Per dire, lasciando stare per ora il discorso EEPROM e concentrandoci su variabili in memoria, puoi ritoccare la mia precedente descrizione dove di fatto ti davo il procedimento, una cosa del genere:
float ultimaOra[4]; // A: le ultime 4 letture (1 ogni 15 minuti)
float ultimoGiorno[24]; // B: le ultime letture con la media oraria del giorno corrente
float mediaIeri; // C: media del giorno precedente
float mediaMese[31]; // D: le letture giornaliere del mese corrente
float mediaMeseScorso; // E: la media del mese precedente.
Ad ogni ciclo quindi esegui questi passi (ti ho messo anche qualche spezzone di possibile codice, il resto devi iniziare a scriverlo tu):
...
byte ptrUltimaOra = 0;
...
void loop()
{
// Se sono passati 15 minuti dall'ultima lettura
if ( )
{
// Leggo il valore corrente dal sensore (inutile leggerla nel loop) e lo aggiungo all'array ultimaOra[]
ultimaOra[ptrUltimaOra++] = bme.readTemperature();
// se è la quarta lettura
if ( ptrUltimaOra == 4 )
{
// calcoli la media oraria
// e la inserisci in ultimoGiorno[] resettando ultimaOra
// se è terminato il giorno
if ( )
{
// calcoli da ultimoGiorno[] la media del giorno appena concluso
// la metti in mediaIeri
// e la inserisci in mediaMese[]
// resetto ultimoGiorno[]
// se è terminato il mese
if ( )
{
// calcoli da mediaMese la media del mese
// la metti in mediaMeseScorso
// resetto anche mediaMese.
}
}
}
}
}
Quando tutto poi ti funziona, allora inizi a pensare alla memorizzazione su EEPROM.