Dovrei fare diverse letture della porta analogica A3 di Arduino Uno, durante i 5 secondi che ho impostato a Millis().
Ho notato con l'applicativo Wokwi che invece ne fa solo una.
Dove sbaglio?
Il link di wokwi è il seguente:
EzioGi
Dovrei fare diverse letture della porta analogica A3 di Arduino Uno, durante i 5 secondi che ho impostato a Millis().
Ho notato con l'applicativo Wokwi che invece ne fa solo una.
Dove sbaglio?
Il link di wokwi è il seguente:
EzioGi
Quante? Non è né descritto, né desumibile dal codice.
Distanziate tra loro di quanto? Idem come sopra. Non è prevista nessuna elaborazione ciclica che ogni tot effettui qualche operazione. C'è solo un delay(5000) finale, che per quel tempo blocca tutto.
La lettura dell'analogico è condizionato fortemente da questa doppia condizione:
if(dato == 1 && cellaRam[1] == 0){
soprattutto quel cellaRam.
Io ti consiglio di metterti li, con excel o carta e penna e simularti i valori dato, cellaRam[1] val, etc. provando a fare tre o 4 loop, vedrai che troverai l'inghippo.
Vedi bene dove imponi a 1 cellaRam.
In tutti i casi comunque, devi anche dire (come chiesto da Claudi_FF) quanti ne vuoi leggere.
Perchè se le letture sono 4 da mettere nelle celle di cellaRam, non si capisce perchè lavori solo con cellaRam[1] (dato da 1 a 4 ok, perchè poi solo se 1 legge ??)
Aggiungo: la condizione perchè avvenga "qualcosa" è questa:
if(dato == 1 && cellaRam[1] == 0)
Se è vera più avanti:
cellaRam[1] = 1; // scrive nella seconda cella il valore 1
quindi se non cambi il valore di cellaRam[1] la condizione non sara mai più vera.
Ciao, Ale.
edit: Nid è stato più veloce di me!
Cerco di spiegare meglio quello che vorrei fare.
Brevemente, il programma dovrebbe presentare casualmente su LCD un numero per volta dei tre numeri (1,2,3) e rimanere presente per 10 secondi.
Contemporaneamente nei 10 secondi dovrei acquisire e leggere un ingresso ADC e salvare il valore sulla eeprom dalla cella 5 in poi, per poi leggere i dati con comodo.
Quindi dopo aver visualizzato i primi tre numeri, la visualizzazione, la lettura e il salvataggio andrebbero ripetuti per un'altra decina di volte, on totale 30 volte..
Nei dieci secondi che evidenzio un numero vorrei leggere e salvare più dati possibili.
Per adesso non so ancora quanti dati posso acquisire nei 10 secondi in cui millis lavora, perché non riesco a capire come usare millis in quel tempo.
Più dati acquisisco e salvo, meglio è.
Adesso nello sketch manca la visualizzazione del secondo numero, poi del terzo.
Se non risolvo millis(10") non vado avanti nello sviluppo del programma.
Spero di essere stato abbastanza chiaro nell'esposizione.
EzioGi
Io non ho ancora ben chiaro cosa vuoi fare, sopratutto nei 10 secondi di "lettura e annotazione" devi fare anche altro? Altrimenti fai un loop bloccante, tipo:
uint32_t stopTime = millis() + 10000UL;
uint16_t value = 0;
uint32_t cnt = 0;
while(millis() < stopTime){
value = analogRead(A3);
delay(50) // valore da adattare al tempo di scrittura
cnt++;
}
Serial.print("Effettuate ");
Serial.print(cnt);
Serial.println(" letture/salvataggi");
Con questo codice intanto ti fai un idea di quanti dati puoi riuscire a trattare (dubito che la eeprom sia sufficiente).
Ciao, Ale.
Da quanto leggo la EEPROM interna in scrittura accetta fino a 300 byte al secondo. Però appunto è piccola, quindi i dieci secondi (che al primo post erano cinque
) vanno suddivisi per lo spazio disponibile. E probabilmente usare la EEPROM interna in questo modo come datalogger non è neanche la soluzione "giusta".
Un appunto sul tuo esempio: dal momento che per ora OP è in profonda crisi con millis, secondo me meglio non mostrare esempi che soffrono del ritorno a zero ogni 1192 ore. Perché uno potrebbe dire "ah finalmente ho capito" e trovarsi con condizioni che imprevedibilmente ogni tanto sbarellano.
uint32_t startTime = millis();
uint16_t value = 0;
uint32_t cnt = 0;
while(millis()-startTime < 10000UL){
value = analogRead(A3);
delay(50) // valore da adattare al tempo di scrittura
cnt++;
}
Serial.print("Effettuate ");
Serial.print(cnt);
Serial.println(" letture/salvataggi");
Ho l'impressione che attribuisci a millis un funzionamento automagico che in realtà non ha. millis ti da l'ora di sistema in millisecondi dall'accensione, nient'altro (e tra l'altro dopo circa 1192 ore di funzionamento continuo, 232 ms, si azzera e riparte).
Quello che "lavora" deve essere la logica che scrivi, usando l'ora di sistema (o altro metodo più o meno preciso) per calcolare il tempo trascorso da un momento "start" che decidi tu.
In passato c'è stato un utente a cui millis era altrettanto indigesto (qui il post). È bastato solo "nascondere" millis dentro due funzioni chiamate start_tempo e trascorso che improvvisamente è diventato tutto chiaro.
Ho cercato di capire la descrizione e sinceramente mi spiace ma devo dire che non mi è ben chiaro né il contesto (di quale applicazione pratica parliamo?) né il comportamento desiderato.
A parte il discorso dell'EEPROM che come detto in precedenza non è utilizzabile all'infinito, per prima cosa leggendo il più velocemente possibile il dato analogico ("Più dati acquisisco e salvo, meglio è"), i byte richiesti saranno veramente tanti (se abbiamo un "int" tra 0 e 1023 sono 2 byte per campione, e campionando alla massima velocità anche di un Arduino UNO saranno sicuramente migliaia al secondo, e si può saturare facilmente una EEPROM, anche esterna, nel giro di pochi minuti...) oltre a dover considerare la velocità di scrittura della EEPROM che sarà probabilmente il maggiore collo di bottiglia al throughput generale. Non sapendo quale sia il contesto e quali i requisiti di questo progetto, non è facile quantificare.
Diciamo che per ora non posso farcela ma proverò stasera a farti su Wokwi (e condividerti) uno sketch, derivato dal tuo, che ogni 10 secondi cambia il "numero casuale" mostrato, e nel frattempo effettua in continuo le letture e le salva (in questa fase) in un array con controllo ciclico (ossia quando viene completato, ricomincia a scrivere al primo elemento), e vediamo come si comporta, ma magari prova a spiegare meglio il contesto ed i requisiti...
Ok, per iniziare un poco con cose concrete (spero), vedi questo sketch che ti ho scritto "al volo" (quindi probabilmente non è esattamente quello che cerchi, ma è per iniziare a parlarne):
A parte quindi come ti ho strutturato lo sketch (spero sia abbastanza chiaro e semplice), ovviamente nell'emulatore credo che la velocità di elaborazione non sia proprio quella di un Arduino "fisico", ma dovresti già capire quanti dati produce una cosa del genere.
Parto dai due concetti che non mi erano chiari: Millis() e velocità di lettura dell'ingresso ADC, adesso va meglio dopo i vostri chiarimenti.
Inoltre cerco di spiegare meglio quello che vorrei fare.
Partendo da un tempo base di 10 secondi e da tre numeri casuali che per semplicità ho scelto 1, 2 e 3.
Il programma ogni 10 secondi dovrebbe visualizzare random un primo dei tre numeri e poi random uno degli altri due rimasti ed infine l'ultimo rimanente.
La cosa si dovrebbe ripetere per una decina di volte.
Nei 10 secondi di tempo base, dopo la visualizzazione del numero estratto su LCD dovrebbe conteraneamente leggere l'ingresso ADC e salvare i valori letti.
L'idea di salvare su EEprom mi è venuta perché l'ho già sperimentata e conosco come fare, ma potrei anche usare una SD che però non conosco.
Io non avevo idea di quante letture l'ADC potesse fare in 10 secondi e visto che come mi avete detto sono tante, dovrò trovare un escamotage per ridurle a una decina o poco più.
L'idea di usare un Array[4] mi era sembrato di semplificare lo sketch.
Vedrò di sperimentare gli esempi che avete postato.
Grazie.
EzioGi
Vabbè questo è relativamente semplice, nel mio esempio generavo un numero casuale e basta, invece deve partire da uno a caso dei tre, poi uno degli altri due e poi l'ultimo (e poi ricominciare?). Per ora tralascerei, non è essenziale almeno per ora.
Anche questo non è difficile, basta inizializzare una variabile che conti quante volte sono stati estratti tutti e tre i numeri, e poi smettere di elaborare (è così?).
I problemi dell'EEPROM sono quelli che abbiamo già citato (non solo io) legati al numero di scritture "garantite" (più o meno). Se hai già lavorato con le EEPROM va bene ma hai mai scritto su EEPROM grandi quantità di dati, in continua scrittura?
Per la quantità di memoria necessaria, se occupi 2 byte ad acquisizione (immagino, ma anche qui dipende da cosa stiamo acquisendo e come...), se diciamo fai 100 letture al secondo, per 10 secondi sono 2000 byte, per 3 valori, siamo a 6000 byte, e se questo ciclo si ripete per 30 volte, siamo sui 180k byte. Sono tutti calcoli che devi fare (e devi farli tu visto che ancora non mi è chiaro l'ambito di tutto questo discorso).
In alternativa c'è la scheda SD, e qui scrivere un file in append non credo sia un grosso problema, sicuramente non soffre del problema delle EEPROM.
Ulteriore alternativa sarebbe mandare i dati (es. via UDP o TCP) ad un qualche server, che si possa occupare di acquisire e memorizzare i dati (ed a quel punto fare qualsiasi altra operazione che un PC può fare più agilmente di un Arduino).
Ma, ripeto ancora, tutto dipende dai requisiti, dall'ambito in cui stiamo lavorando, e dalle attese e possibilità a disposizione, tutte cose che ancora non ci hai voluto dire...
Detta così continua ad essere troppo vaga... Cosa è collegato all'ingresso analogico che devi leggere di continuo? Che range ti aspetti in ingresso? Con che velocità può variare? Devi salvare sempre e comunque la lettura o solo quando varia? A seconda delle risposte si possono adottare diverse strategie, sopratutto per il salvataggio dei dati.
Ciao, Ale.
È l'ennesimo segreto militare che, nonostante gli sforzi, non troverà soluzione finché non sarà svelato e la soluzione sarà ben diversa da tutte quelle a cui potevano portare le specifiche fornite... ![]()
Già, ennesimo caso di richiesta di consigli o aiuto ma in mancanza del contesto e dei requisiti (chissà per quale segreto) non possiamo dire alla fine molto di concreto. O ci dice tutto o quasi, oppure io più di quanto ho fatto non posso fare.
Ma non lo fa perché altrimenti poi dovrebbe ucciderci... ![]()
Il semplice contesto è che da qualche anno sto emulando gli esperimenti che Cleve Backster aveva fatto con le piante di Phiilodendro, anni addietro.
Ultimamente mi era venuta l’idea di fare una nuova versione meno complessa e sostituire la funzione Delay() con la funzione Millis().
Non c’è nessun “segreto militare” e comunque la battuta non mi fa ridere.
Lo sketch della prima versione è il seguente, se può interessare a qualcuno:
/********************************************************************************
Cleve Backster versione 1
Pianta utilizzata Philodendron
sketch: Piante v5.7
12 dicembre 2020
Arduino Uno, LCD 16x2 (I2C)
SDA A4, SCL A5, Sensore A0(ADS1115), Batteria A1(ADS1115), 18B20 pin digitale 2
Con temperatura alta avvia ventilatore
Memorizza i valori se diversi dalle soglie in 2 byte consecutivi
Inserito volt batteria su LCD
Sensore A1(ADS1115) per controllo batteria bassa
Inserito funzione elapsedmillis() per i 5 secondi dei tasti
Inserito il simbolo batteria, termometro, RAM e ADs
*********************************************************************************/
#include <Wire.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <PCF8574_HD44780_I2C.h>
#include <Adafruit_ADS1015.h>
#include <avr/sleep.h>
#include <elapsedMillis.h>
#define led13 13 // led giallo di Arduino Uno
#define switchMem 11 // pulsante abilitazione salvataggio dati
#define ledGiallo 10
#define ledVerde 9
#define ledRosso 8
#define SU 6 // pulsante su
#define GIU 7 // pulsante giù
#define ventilatore 5
#define cicalino 4
#define ONE_WIRE_BUS 2 // porta digitale 2 di Arduino
//************** Funzioni ************
void temperatura();
void memorizzaDato();
void Batteria();
void letturaADS();
void visualizzaSoglie();
void allarme();
void elapsedmillisloop();
// ******* Variabili globali *********
int16_t adc0 = 0; // variabile per ingresso ADs
int sogliaINF = 0;
int sogliaSUP = 0;
float minimoBatt = 9.22; // 6volt x lipo 2S, 9,22volt x lipo 3S
int memON = LOW;
int indirizzo = 0;
byte byteUno = 0;
byte byteDue = 0;
float Volt = 0.0;
int statoSU = 0;
int statoGIU = 0;
PCF8574_HD44780_I2C lcd(0x27,16,2);
Adafruit_ADS1115 ads(0x48);
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
byte b1[8]= // batteria scarica = 9,22v
{
B01110,
B10001,
B10001,
B10001,
B10001,
B11111,
B11111,
};
byte b2[8]= // 9,69v
{
B01110,
B10001,
B10001,
B10001,
B11111,
B11111,
B11111,
};
byte b3[8]={ // 10,16v
B01110,
B10001,
B10001,
B11111,
B11111,
B11111,
B11111,
};
byte b4[8]={ // 10,63v
B01110,
B10001,
B11111,
B11111,
B11111,
B11111,
B11111,
};
byte b5[8]={ // batetria carics = 11,1v
B01110,
B11111,
B11111,
B11111,
B11111,
B11111,
B11111,
};
byte t[8]={ // termometro
0b00100,
0b00100,
0b00100,
0b00100,
0b01110,
0b11111,
0b11111,
0b01110
};
byte i[8] = { // integrato
0b01110,
0b11111,
0b01110,
0b11111,
0b01110,
0b11111,
0b01110,
0b11111
};
byte ADs[8] = { // ADs 1115
0b00000,
0b01000,
0b01100,
0b11110,
0b01111,
0b11110,
0b01100,
0b01000
};
void setup(){
lcd.init();
lcd.setBacklight(0);
Serial.begin(9600);
Wire.begin();
ads.begin();
sensors.begin();
delay(1000);
lcd.home();
lcd.setCursor(2, 0); // colonna 2, riga 0
lcd.print("Piante v5.7");
lcd.setCursor(0, 1); // colonna 0, riga 1
lcd.print("12 dicembre 2020");
lcd.setBacklight(1);
delay(3000);
lcd.clear();
// ****** simboli batteria *********
lcd.createChar(1, b1);
lcd.createChar(2, b2);
lcd.createChar(3, b3);
lcd.createChar(4, b4);
lcd.createChar(5, b5);
lcd.createChar(6, t);
lcd.createChar(7, i);
lcd.createChar(8, ADs);
// ******* Impostazioni pin *********
pinMode(led13, OUTPUT);
pinMode(ledRosso, OUTPUT);
pinMode(ledVerde, OUTPUT);
pinMode(ledGiallo, OUTPUT);
pinMode(ventilatore, OUTPUT);
pinMode(cicalino, OUTPUT);
pinMode(switchMem, INPUT_PULLUP);
pinMode(SU, INPUT_PULLUP);
pinMode(GIU, INPUT_PULLUP);
digitalWrite(memON, HIGH);
digitalWrite(led13, LOW);
digitalWrite(ledRosso, LOW);
digitalWrite(ledVerde, LOW);
digitalWrite(ledGiallo, LOW);
digitalWrite(ventilatore, LOW);
digitalWrite(cicalino, LOW);
// *********** regolazione soglie **********
adc0 = ads.readADC_SingleEnded(0);
delay(20);
if(adc0 <= 0){
adc0 = 0;
}
sogliaSUP = adc0;
sogliaINF = adc0;
visualizzaSoglie();
}
elapsedMillis elapsedTime;
unsigned int intervallo = 5000;
void loop(){
Batteria();
temperatura();
letturaADS();
memorizzaDato();
elapsedmillisloop();
//delay(100);
}
/***********************************************************************
* Funzioni *
************************************************************************/
void elapsedmillisloop() {
if (elapsedTime > intervallo) {
if (statoSU = digitalRead(SU) == LOW){
sogliaSUP = sogliaSUP + 10;
delay(500);
visualizzaSoglie();
}
else if(statoGIU = digitalRead(GIU) == LOW){
sogliaINF = sogliaINF - 10;
delay(500);
visualizzaSoglie();
}
elapsedTime = 0;
}
}
void letturaADS(){
//int16_t adc0;
adc0 = ads.readADC_SingleEnded(0);
delay(20);
if(adc0 < 0){
adc0 = 0;
}
lcd.setCursor(0, 0); // colonna 0, riga 0
lcd.write(8);
lcd.setCursor(2, 0); // colonna 4, riga 0
lcd.print(adc0);
//Serial.println(adc0);
}
void visualizzaSoglie(){
lcd.clear();
lcd.setCursor(0, 0); // colonna 0, riga 0
lcd.print("Soglia SUP ");
lcd.print(sogliaSUP);
lcd.setCursor(0, 1); // colonna 0, riga 1
lcd.print("Soglia INF ");
lcd.print(sogliaINF);
delay(5000);
lcd.clear();
}
void Batteria(){
int16_t adc1;
float oldVolt;
adc1 = ads.readADC_SingleEnded(1);
delay(20);
Volt = ((adc1 * 0.1875)/1000) * 3;
if(Volt != oldVolt){
lcd.setCursor(9, 1); // colonna 9, riga 1
//lcd.print("B:");
/* map(value, fromLow, fromHigh, toLow, toHigh)
value: il numero da mappare.
fromLow: il limite inferiore dell'intervallo corrente del valore.
fromHigh: il limite superiore dell'intervallo corrente del valore.
toLow: il limite inferiore dell'intervallo target del valore.
toHigh: il limite superiore dell'intervallo target del valore.
*/
int x = map(Volt, 9.22, 11.1, 1, 5);
switch (x){
case 1:
lcd.write(1);
break;
case 2:
lcd.write(2);
break;
case 3:
lcd.write(3);
break;
case 4:
lcd.write(4);
break;
case 5:
lcd.write(5);
break;
//default:
// lcd.print(Volt);
//break;
}
lcd.setCursor(11, 1); // colonna 11, riga 1
lcd.print(Volt);
digitalWrite(ledRosso, LOW);
//Serial.println(Volt);
//Serial.println(x);
//Serial.println(oldVolt);
//Serial.println(minimoBatt);
}
if(Volt <= minimoBatt){
lcd.clear();
lcd.setCursor(9, 1); // colonna 9, riga 1
lcd.write(1);
lcd.setCursor(11, 1); // colonna 11, riga 1
lcd.print(Volt);
allarme();
}
oldVolt = Volt;
}
void temperatura(){ // se temperatura alta avvia ventilatore
int Celsius;
int oldCelsius = 0;
sensors.requestTemperatures();
Celsius = sensors.getTempCByIndex(0);
if(oldCelsius != Celsius){
lcd.setCursor(0, 1); // colonna 0, riga 1
lcd.write(6);
lcd.setCursor(2, 1); // colonna 2, riga 1
lcd.print(Celsius);
//Serial.println(Celsius);
lcd.print((char)223);
lcd.print("C");
}
if (Celsius >= 25) { // temperatura superiore di 25 gradi
//digitalWrite(ledRosso, HIGH);
digitalWrite(ventilatore, HIGH);
}
else{
//digitalWrite(ledRosso, LOW);
digitalWrite(ventilatore, LOW);
}
}
void memorizzaDato(){ // salva dato in due byte consecutivi nella 24LC256
memON = digitalRead(switchMem); // con 0 abilito salvataggio in Mem
if(memON == 1 && (adc0 <= sogliaINF || adc0 >= sogliaSUP)){
digitalWrite(ledVerde, HIGH);
lcd.setCursor(9, 0); // colonna 9, riga 0
lcd.write(7);
lcd.setCursor(11, 0); // colonna 9, riga 0
lcd.print(indirizzo);
byteUno = ((adc0 >> 8) & 0xFF);
byteDue = (adc0 & 0xFF);
Wire.beginTransmission(0x50);
Wire.write(highByte(indirizzo));
Wire.write(lowByte(indirizzo));
Wire.write(byte(byteUno));
Wire.endTransmission();
indirizzo++;
delay(5);
Wire.beginTransmission(0x50);
Wire.write(highByte(indirizzo));
Wire.write(lowByte(indirizzo));
Wire.write(byte(byteDue));
Wire.endTransmission();
indirizzo++;
delay(5);
digitalWrite(ledVerde, LOW);
if(indirizzo >= 32000){
allarme();
}
}
else{
lcd.setCursor(9, 0); // colonna 9, riga 0
lcd.write(7);
lcd.setCursor(11, 0); // colonna 9, riga 0
lcd.print(indirizzo);
digitalWrite(ledVerde, LOW);
}
}
void allarme(){
for(int i = 0; i<10; i++){
digitalWrite(ledRosso, HIGH);
tone(cicalino, 350);
delay(200);
digitalWrite(ledRosso, LOW);
noTone(cicalino);
delay(200);
}
lcd.setBacklight(0);
sleep_enable();
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_cpu();
}
Un ringraziamento a chi ha voluto chiarirmi le idee con esempi e documenti oltre alle considerazioni scritte.
Per me l’argomento è chiuso cosi come questo post.
EzioGi
Mamma mia che grande bruttezza. Rendere di lettura difficoltosa una semplice digitalRead, ed utilizzare inutilmente una variabile statoSu che poi non viene usata. Io semplificherei e cancellerei la variabile statoSU (anche statoGIU)
if (digitalRead(SU) == LOW){
OK, chiaro, quel codice salva al massimo 100 campioni (da 16 bit) al secondo... registrandoli su una EEPROM I2C esterna da 32 kB (totale 163 secondi di registrazione possibile).
Quella EEPROM ha un milione di riscritture garantite, ma valgono per ogni pagina da 64 byte completa. Scrivere un byte alla volta (come viene fatto) riscrive ogni volta l'intera pagina, riducendo quindi di 64 volte il numero di registrazioni possibili, che diventano sole quindicimila. Almeno scrivendo ogni campione da 16 bit in un colpo solo, si salirebbe subito a trentamila.
Usando invece la EEPROM interna di Arduino Uno/Mega, i tempi di registrazione si ridurrebbero a 5 secondi per una Uno, e 20 secondi per una Mega.
Beh, io continuo a pensare che non sia una grande idea usare una EEPROM, e in fondo neanche una SD, ma sia meglio mandare i dati o direttamente su seriale (cosa che permetterebbe da PC di visualizzarli graficamente e magari anche di memorizzarli su un DB locale) oppure via UDP o TCP ad un qualche server (anche qeusto potrebbe mostrare i dati e/o registrarli su un file), il tutto per una ben più agevole elaborazione.
Ma tant'è....
This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.