Sono alcuni giorni che non riesco a far funzionare questo codice o meglio funzionare funziona ma mi arrivano dei risultati completamente sballati e impossibili:
void BATTERY() {
const int maxRetries = 5; // Massimo numero di tentativi per evitare loop infiniti
int retryCount = 0; // Contatore dei tentativi
do {
analogSetPinAttenuation(5, ADC_11db); // Imposta l'attenuazione per una gamma di tensione più alta
const int numSamples = 100; // Aumenta il numero di campioni per una lettura più stabile
float minVoltage = 2.80; // Valore minimo per 0%
float maxVoltage = 4.20; // Valore massimo per 100%
// Chiamata a readVoltage() per ottenere una lettura media dei campioni
float PIN5 = readVoltage(5, numSamples);
// Calcola la tensione media (supponendo che PIN5 sia già la media dei campioni)
voltageLevel = 3.3f * PIN5 * 2 / 4096.0f;
batteryFraction = (voltageLevel - minVoltage) / (maxVoltage - minVoltage);
if (batteryFraction < 0) batteryFraction = 0; // Assicura che la percentuale non sia negativa
batteryFraction = constrain(batteryFraction, 0, 1); // Limita la percentuale tra 0 e 1 (0-100%)
Serial.println((String) "Raw:" + PIN5 + " Voltage:" + voltageLevel + "V Percent: " + (batteryFraction * 100) + "%");
delay(100); // Ridotto il delay per velocizzare i tentativi in caso di errore
retryCount++; // Incrementa il contatore dei tentativi
} while (voltageLevel > 4.5 && retryCount < maxRetries);
// Se dopo i tentativi la tensione è ancora alta, potresti voler gestire questo caso
if (voltageLevel > 4.5) {
Serial.println("ATTENZIONE: Tensione ancora troppo alta dopo " + String(maxRetries) + " tentativi. Controllare il sensore!");
logString += "ATTENZIONE: Tensione ancora troppo alta dopo " + String(maxRetries) + " tentativi. Controllare il sensore!";
}
}
Ora preciso che ovviamente il divisore è 2, testato anche con multimetro.
La batteria è una batteria al litio con un range da 2.80 a 4.20v (il BMS stacca se scende sotto 2.70 quindi andrebbe a 0v ma per i miei scopi mi va bene considerare lo 0% leggermente più alto).
La scheda è una ESP32C6.
Nella stampa escono valori apparentemente randomici e completamente impossibili ad esempio a volte mi dice che la batteria raggiunge 4.7v ma è impossibile perché il BMS staccherebbe prima e andrebbe in protezione e comunque dubito che una batteria possa raggiungere 4.7v.
Altre volte mi dice che il voltaggio scende 3.1v che è un valore possibile ma mi sembra altamente improbabile specialmente perché avviene nell'arco di pochissimo tempo (20 minuti).
La batteria è stata testata a parte, scollegandola da tutto, e non ha presentato anomalie.
questa è l'attivazione di ieri sera alle 20 ma è letteralmente impossibile che nel momento in cui l'ESP32 si è acceso il voltaggio fosse 2.37v e dopo che aveva alimentato la pompa di irrigazione quella stessa batteria che nel frattempo non ha alcun modo di ricaricarsi sia salita di voltaggio a 3.10v.
Non ho mai usato il C6, ma l'ADC degli ESP32 è particolarmente rognoso e molto soggetto a disturbi e va configurato a dovere.
Le API Arduino spesso non sono sufficienti per ottenere un buon risultato ed è meglio ricorrere alle istruzioni native ESP-IDF.
Se mi ricordo, domani ti posso girare lo sketch con cui avevo ottenuto risultati migliori, nel frattempo dai un'occhiata qui.
PIN5 (l'ho chiamato così per semplicità e sì confermo è anche il pin collegato).
Da come l'ho capita io poi correggetemi se sbaglio:
PIN5 effettua 100 letture e ne fa la media. Però quando effettua la lettura essa non assume il valore del voltaggio direttamente ma viene letta come un numero tra 0 e 4095.
Quindi PIN5/4096.0f = il valore del voltaggio rispetto al riferimento.
Essendo il riferimento la tensione a 3.3v per ottenere il voltaggio reale devi poi moltiplicare per la tensione di riferimento e nel mio caso dovendo abbassare la tensione per non sovraccaricare la scheda moltiplicare per 2.
Quindi riassumento PIN5/4096f = valore di riferimento.
Valore di riferimento * 3.3f = Voltaggio reale dopo il divisore.
Voltaggio dopo divisore * 2 = voltaggio batteria
Almeno teoricamente dovrebbe essere così.
Oggi ho modificato il circuito perché mi è venuto il dubbio che possa essere la terra a non essere stabile in quanto non aveva un collegamento diretto e unico. Ho ipotizzato ci potessero essere dei disturbi. Vediamo se cambia qualcosa.
I 3,3v ho provato a fare varie simulazioni ma anche quando si accende la pompa che comunque è comandata da un relé optoisolato non ha variazioni significative (la massima varianza che sono riuscito a simulare è di 0,03v) e comunque la lettura della batteria avviene prima che si accenda la pompa e dopo che si è già spenta.
Mi sono effettivamente accorto che c'è un errore in readVoltage anche se non dovrebbe aver inciso così tanto...
Allego la versione attuale di readVoltage:
float readVoltage(int pin, int numSamples) {
float sum = 0;
int validSamples = 0;
const float referenceVoltage = 3.3; // Tensione di riferimento (es. 3.3V)
const float adcResolution = 4095.0; // Per ADC a 12 bit (usa .0 per calcoli in virgola mobile)
for (int i = 0; i < numSamples; i++) {
int rawSample = analogRead(pin);
// Converte il valore grezzo in tensione PRIMA del confronto
float voltageSample = (rawSample / adcResolution) * referenceVoltage;
if (voltageSample > 1.30) { // Ora il confronto è corretto (Volt vs Volt)
sum += voltageSample;
validSamples++;
}
delay(10);
}
// La media sarà già un valore di tensione
return validSamples > 0 ? sum / validSamples : 0.0;
}
e di conseguenza modifico battery:
void BATTERY() {
const int maxRetries = 5; // Massimo numero di tentativi per evitare loop infiniti
int retryCount = 0; // Contatore dei tentativi
float PIN5;
do {
// Reinizializza la tensione filtrata ad ogni tentativo di ripetizione
// In questo modo, il filtro parte da zero con le nuove letture.
// filteredVoltage = 0.0; // Potresti volerlo azzerare, ma il filtro esponenziale si adatterà comunque.
// Lo mantengo così per un adattamento più rapido in caso di spike temporanei.
analogSetPinAttenuation(5, ADC_11db); // Imposta l'attenuazione per una gamma di tensione più alta
const int numSamples = 100; // Aumenta il numero di campioni per una lettura più stabile
float minVoltage = 2.80; // Valore minimo per 0%
float maxVoltage = 4.20; // Valore massimo per 100%
// Chiamata a readVoltage() per ottenere una lettura media dei campioni
PIN5 = readVoltage(5, numSamples);
// Calcola il livello di batteria usando la tensione filtrata
batteryFraction = (PIN5 - minVoltage) / (maxVoltage - minVoltage);
if (batteryFraction < 0) batteryFraction = 0; // Assicura che la percentuale non sia negativa
batteryFraction = constrain(batteryFraction, 0, 1); // Limita la percentuale tra 0 e 1 (0-100%)
Serial.println((String) "Raw:" + PIN5 + " Voltage:" + PIN5 + "V Percent: " + (batteryFraction * 100) + "%");
delay(100); // Ridotto il delay per velocizzare i tentativi in caso di errore
retryCount++; // Incrementa il contatore dei tentativi
// Condizione per ripetere il ciclo: se filteredVoltage è maggiore di 4.5V E non abbiamo esaurito i tentativi
} while (PIN5 > 4.5 && retryCount < maxRetries);
// Se dopo i tentativi la tensione è ancora alta, potresti voler gestire questo caso
if (PIN5 > 4.5) {
Serial.println("ATTENZIONE: Tensione ancora troppo alta dopo " + String(maxRetries) + " tentativi. Controllare il sensore!");
logString += "ATTENZIONE: Tensione ancora troppo alta dopo " + String(maxRetries) + " tentativi. Controllare il sensore!";
}
}
Dovrai aspettare stasera dopo le 21 perché con le modifiche effettuate mi ha dato risultato 0v a tutto quindi o ieri rimontando ho collegato qualcosa male o per qualche motivo persiste un problema T_T
Potrebbe essere che abbia collegato qualcosa male... ieri c'era la bambina che mentre lo rimontavo non dava pace. Però prometto di aggiornarvi appena possibile.
AGGIORNAMENTO:
Allora tornato a casa ho fatto varie prove al banco usando un alimentatore quindi con un voltaggio estremamente controllato e verificabile:
Prima di tutto parliamo delle cose incomprensibili:
Mi domanderete perché c'è quel 1.25 a moltiplicare? Ebbene non riesco a capirlo nemmeno io ma chiedendo a diversi amici tutti mi dicono che va inserito per forza anche se nessuno sa dirmi il perché ed effettivamente al banco con quel moltiplicatore ottengo risultati con un errore medio di circa 0,01v. Rimango estremamente confuso sul perché sia necessaria quella sorta di costante ma a quanto pare è necessaria.
Il secondo aspetto che ho notato è che è importantissimo che il GND sia identico sia per alimentazione che test. Basta che il GND abbia anche solo una minima ma veramente minima differenza che escono numeri a caso ma veramente completamente sballati.
Per darvi un'idea se uno il GND dell'alimentazione (sempre usando al banco un alimentatore controllato) senza collegare il GND dell'alimentatore da banco che invece simula la batteria appaiono risultati senza senso tipo:
0.00
3.60
0.10
2.70
e così via valori che almeno all'apparenza sembrerebbero randomici o quasi.
Quindi adesso andrò a riprovare usando la batteria e stasera alle 20.30 circa vi aggiorno se modificando la formula e cercando di usare il GND comune si riesce a ottenere un risultato possibile.
No, come ho scritto sopra prima ho provato usando un alimentatore da banco quindi sono sicurissimo della tensione che arrivava al pin 5 e tra l'altro l'ho verificata anche con un multimetro a parte.
A quanto sono riuscito a trovare ma senza riscontri ufficiali quel 1.25 deriva da:
il profilo a 11db prevede un voltaggio in ingresso da 0 a 3.9v, essendo Vref interno al microcontrollore 3.3 ottieni che:
3.9/3.3=1,18
mettici poi che Vref non è così preciso a 3.3 e ottieni che 1.25 è una sorta di calibrazione.
Però ripeto non ho trovato documentazione che asserisse ufficialmente una cosa del genere ma solo altri utenti che ipotizzavano questa cosa.
Non mi sono spiegato bene, intendevo dire che se hai da una parte per esempio 5v (al pin adatto) e GND e poi dall'altra (ad esempio all'altro pin GND accanto al pin 3.3v) un GND che non è esattamente lo stesso ma un GND che viene da una qualsiasi altra fonte succede che produce risultati strani.
Per spiegarmi meglio data questa scheda se colleghi 5v in basso a destra e GND, e poi in alto a sinistra colleghi un GND che però non è lo stesso collegato in basso a destra la scheda funziona lo stesso perché sono comunque due GND ma quando deve misurare il voltaggio produce valori apparentemente casuali.
11 dB significa un rapporto tra tensioni pari a 3.548:
11 = 20 log10 (V1/V2)
V1/V2 = 10(11/20) = 3.548
Il fondoscala 3.9 è concorde con un riferimento di 1.1 V, infatti:
1.1 x 10 (11/20) = 3.9
Se moltiplichiamo il riferimento per 3 (quindi lo portiamo a 3.3 V) e contemporaneamente dividiamo per 3 il rapporto tra tensioni (portandolo da 3.548 a 1.1826), a parità di tensione di ingresso leggiamo lo stesso valore.
Vmis = ADC x (Vrif/Risol) x (1/Attenuaz)
Vmis = ADC x (1.1/4096) x 3.548
Vmis = ADC x (3.3/4096) x 1.826
Considerando una tolleranza superiore al 5% della tensione di riferimento, il valore 1.25 appare perfettamente plausibile.
Di fatto tutta la parte destra dell'equazione si può trasformare in un valore di calibrazione ottenuto da una lettura precisa della tensione in ingresso e dal valore fornito dall' ADC:
cal = Vin / ADC
Questo valore compensa tutte le tolleranze (comprese quelle di eventuali resistenze se si usasse un partitore esterno invece dell'attenuazione interna).
A prescindere dai conti fatti per ottenere un livello in tensione "sensato" a partire dal valore adimensionale dell'ADC, il problema principale di @saviolo_t mi pare di capire che sia principalmente l'estrema variabilità delle misure.
Come c'è scritto chiaramente nel link che avevo messo qualche post fa:
The ESP32-C6 ADC can be sensitive to noise leading to large discrepancies in ADC readings. Depending on the usage scenario, you may need to connect a bypass capacitor (e.g. a 100 nF ceramic capacitor) to the ADC input pad in use, to minimize noise. Besides, multisampling may also be used to further mitigate the effects of noise.
Io ho avuto gli stessi problemi di instabilità nelle letture con l'ESP32-C3 (l'ADC integrato dovrebbe essere sostanzialmente lo stesso) ed ho fatto molta fatica ad ottenere risultati accettabili con le API Arduino (la classica analogRead() per intenderci).
Non so se nel frattempo hanno migliorato i sorgenti del core, ma l'esempio incluso nell'ambiente proprietario Espressif funziona molto bene e non ci vuole molto a convertirlo in uno sketch compilabile con Arduino IDE.