Resistenza interna batteria polimeri di litio (lipo)

Stavo silenzioso perche' volevo vedere a che punto arrivavi, ma visto che ti hanno gia' ragguagliato ti dico qualcosa pure io

Innanzi tutto cosa vuoi fare uno strumento di misura o un gadget ?
perche' come detto all'inizio, la scelta di Arduino con un ADC a 10 bit non e' il massimo, la cosa migliore sarebbe quella di usare un ADC esterno.
Comunque andiamo avanti
Prima di tutto, per avere il minore errore possibile vanno messe resistenze di precisione e non le normali al 5% di tolleranza
E sopratutto serve una Vref stabile su Arduino. Non puoi usare la tensione fornita dall'USB o dal regolatore della scheda.
Poi, come gia' detto da @icio , un partitore porta a una riduzione di risoluzione della misura.
Se hai un partitore che divide per 4 ( 4° cella ) non avrai piu' 5/1023 = circa 5mV, ma 5/1023*4 = circa 20mV.
Cioe' se la tensione della batteria varia di 20mV ( es. 4,01 - 4.03 ) tu non te ne accorgi
Se poi ci aggiungiamo il fatto che l'ADC ha un'accuratezza di +/- 2 LSB, questo errore aumenta e non di poco
Fai tu il conto dell'errore che viene all'8° cella

Poi c'e' quello che dice @cyberhs, ovvero che i calcoli fatti con il partitore sono teoricamente corretti se tutte le celle sono nelle stesse condizioni.
Mi ero preparato una tabella, dove ti volevo far notare cosa misuravi se c'era una cella difettosa ( in questo caso la terza ).
Se guardi la colonna G, vedi la tensione che misuri sulle varie celle. Dalla difettosa in poi sono tutte sballate.
Pero' a questo errore si puo' rimediare, Se tu il valore letto lo moltiplichi per il valore di divisione del partitore ( sempre che siano reistenze di precisione ) e da questo sottrai il valore totale delle celle precedenti, riesci comunque ad avere il valore corretto ( vedi altra figura )

Per spiegartelo meglio ti faccio un esempio del codice

// Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
  float voltage1 = Cella1 * (5.0 / 1023.0);
  float volt_tot = voltage1 ;
  float voltage2 = ( Cella2 * (5.0 / 1023.0)) * 2 - volt_tot ;
  volt_tot = volt_tot + voltage2 ;
  float voltage3 = ( Cella3 * (5.0 / 1023.0)) * 3 - volt_tot ;
  volt_tot = volt_tot + voltage3 ;
  float voltage4 = ( Cella4 * (5.0 / 1023.0)) * 4 - volt_tot ;
  volt_tot = volt_tot + voltage4 ;

all'uscita del ciclo volt_tot sara' gia' la tensione totale del pacco batteria, non e' che devi risommare niente

Vi ringrazio molto dell'aiuto che mi state dando ma soprattutto dell'avermi messo sotto gli occhi l'errore indotto dal partitore resistivo.
Innanzitutto non vorrei farne un gadget ma uno strumento, conscio della sua imprecisione ma il discorso è che se io uso sempre lo steso strumento per controllare tutte le mie lipo, ho una misura che in valore assoluto non sarà il massimo ma sarà sempre presa dallo stesso strumento e quindi mi terrà aggiornato sull'andamento vitale delle varie lipo.
Ad oggi infatti, un caricabatterie ti dice un risultato e un altro te ne dice un altro ancora (parlo di marche molto bone nel settore : Robbe, Hyperion e iCharger) per non parlare dei vari "gadget" esterni.

Seconda cosa : le resistenze che ho usato fino ad ora sono del 5% ma non mi interessava ora come ora la precisione, ma il progetto in quanto tale. Ho ordinato (sono in viaggio) delle resitenze con lo 0,5% di tolleranza che già inducono errori ma "accettabili".
Questa mattina ho letto il post di cyberhs e stavo proprio pensando ad una soluzione sw come quella che hai scritto tu :slight_smile:

Vorrei continuare sulla strada dei partitori per il semplice fatto che se no dovrei trovare un'alimentazione di 4,2*8=33,6V per alimentare gli operazionali.
Potrei anche ridurre il progetto a 6 celle scendendo a 25,2V ma rimane sempre il problema di trovare quella alimentazione.
Io contavo di metterci un 7805 per poter usare una fonte quasi qualunque (tipicamente una lipo 3 celle che è il minimo che uso) ed avere quindi una tensione di riferimento che ritengo precisa. Oltremodo, ridurre a 6 celle in quetso momento non sarebbe un problema, ma mi castrerebbe in un futuro dovessi passare anche solo all'uso delle lipo da 7 celle che stanno facendo capolino sempre più insistentemente nel mondo elicotteristico.

Per quanto riguarda lo scrivere il codice per scrivere sul display invece, non riesco a tirar fuori nulla di buono :frowning:

Ragazzi, devo ringraziarvi infinitamente perchè il progetto sta prendendo sempre più forma.
FINALMENTE sono riuscito a scrivere sul display i valori delle varie celle.Ora devo vedere se per caso riesco a far stare in linea 8 celle anzichè solo 6. Curioserò se esiste la possibilità di ridimensionare i caratteri al ribasso solo che già così è proprio piccola la scritta.
Non ci ho pensato subito....semmai cercherò un altro display più grande.

Però sto notando delle cose che, avendo già letto qua e la, mi aspettavo. Se in un dato ingresso analogico non c'è tensione (caso classico quando uso lipo 3 celle e quindi rimanogno senza tensione le altre 3) , in realtà la scheda legge dei valori "a babbo".
Escludendo l'ipotesi di andare sugli op-amp (che penso mi darebbero uno 0 esatto e quindi non avrei il problema), c'è possibilità secondo voi di poter ovviare al problema in qualche modo?
Al momento mi è venuta una sola pensata ovvero quella di aggiungere qualche riga di codice per ogni cella tale per cui se leggo un valore inferiore ai 2,5V (fino a tale tensione è arrivata la lettura in assenza di celle collegate), considero 0 quella cella.
Però la soluzione non mi garba molto perchè nel caso in cui una cella sia difettosa ( per esempio in caso di crash dell'elicottero capita non raramente che si sbelinino le lipo), rischio di non accorgermi del problema.

Altra soluzione potrebbe essere quella di dichiarare all'inizio dello sketch il numero di celle della lipo e quindi non leggere quelle eccedenti, ma volevo fare una cosa il più automatica possibile : accendo, collego, misuro, leggo e spengo.

Un'altra cosa che non so se può influenzare è quella di impostare una V di riferimento con l'apposita funzione, per la quale ho visto che spesso ci si basa sul diodo LM4040

Risolto questo problema, mi ingenio per collegare il mosfet alla scheda per attaccare le resistenze di carico, fare la 2° misurazione e tirar giù finalmente questa benedetta Resistenza Interna

Grazie

potresti mettere una pulldown di alto valore su ogni ADC

per il ref ci sarebbe da implementare un algoritmo che lo gestisce per correggere le letture, e comunque quello da te scelto non vale la pena visto che ha una tolleranza troppo alta per questo progetto

vorrei ricordare che c'e' un partitore in ingresso, quindi c'e' gia' una resistenza.
Quando fai le letture da ADC devi scartare sempre la prima lettura

partit.png

Testato:
potresti mettere una pulldown di alto valore su ogni ADC

per il ref ci sarebbe da implementare un algoritmo che lo gestisce per correggere le letture, e comunque quello da te scelto non vale la pena visto che ha una tolleranza troppo alta per questo progetto

Io pensavo di mettere una Vref esatta da 5V con un 7805 ovviamente alimentando sempre con una tensione un pò superiore.
Però in effetti mi sono sempre chiesto se era necessario riparametrare le letture se gli do una Vref esterna.
Devo però dire che le letture fatte fino ad ora rispecchiano la realtà confrontate con il tester quindi il risulttao lo reputo decisamente già molto buono.
Per l'esattezza, in una lipo 6S su 5 celle ho uno scarto di circa 3 centesimi di Volt tra tester e Arduino, mentre sulla 5° cella ho uno scarto di 10 centesimi.
Considero lo scarto di 3 o 4 centesimi quasi irrisorio considerando che ho le resistenze con tolleranza al 5% e sto aspettando quelle allo 0,5%.
Mentre la 5° cella non so....proverò a cambiare ingresso fisico

Come si fa a riparametrare quindi?

Brunello:
vorrei ricordare che c'e' un partitore in ingresso, quindi c'e' gia' una resistenza.
Quando fai le letture da ADC devi scartare sempre la prima lettura

Inteso nel senso che faccio una lettura, la cancello e la rifaccio e la 2° è valida?

Per come è implementata la analogread non serve scartare nessuna misura

Ora mi sorgono 2 problemi :
il primo è che quando attacco il connettore di bilanciamento della lipo, sulla scheda Leonardo (anche se non alimentata) si accende il led "on" e lampeggia lentamente il led "L". Ovviamente la massa delle lipo è collegata alla massa di alimentazione ma mi fa strano giro di corrente....

2° problema : lo sketch legge le 6 tensioni (per ora non fa altro), le scrive sull'lcd insieme alle immagini delle batterie e ricomincia dopo un ritardo impostato.
Ebbene, per 3 cicli tutto è perfetto ma dal 4° in avanti inizia a scrivere che ci sono problemi a caricare le immagini e non visualizza più le stesse dandomi l'errore.
Vi allego il pezzo di sketch per comodità se qualcuno riuscisse ad aiutarmi a capire il perchè :frowning:

void loop() {
  // Legge le tensioni sui singoli PIN
  int Cella1 = analogRead(A0);
  int Cella2 = analogRead(A1);
  int Cella3 = analogRead(A2);
  int Cella4 = analogRead(A3);
  int Cella5 = analogRead(A4);
  int Cella6 = analogRead(A5);
  //int Cella7 = analogRead(A6);
  //int Cella8 = analogRead(A7);
  
    
  // Converte la CellaX in Volt moltiplicando per il valore di divisione del partitore resistivo, poi sottrae la tensione delle celle precedenti
  float voltage1 = Cella1 * (5.0 / 1023.0);
  float Volt_T = voltage1;
  float voltage2 = (Cella2 * (5.0 / 1023.0))*2 - voltage1;
  Volt_T = Volt_T + voltage2;
  float voltage3 = (Cella3 * (5.0 / 1023.0))*3 - Volt_T;
  Volt_T = Volt_T + voltage3;
  float voltage4 = (Cella4 * (5.0 / 1023.0))*4 - Volt_T;
  Volt_T = Volt_T + voltage4;
  float voltage5 = (Cella5 * (5.0 / 1023.0))*5 - Volt_T;
  Volt_T = Volt_T + voltage5;
  float voltage6 = (Cella6 * (5.0 / 1023.0))*6 - Volt_T;
  Volt_T = Volt_T + voltage6;
  //float voltage7 = (Cella7 * (5.0 / 1023.0))*7 - Volt_T;
  //Volt_T = Volt_T + voltage7;
  //float voltage8 = (Cella8 * (5.0 / 1023.0))*8 - Volt_T;
  //Volt_T = Volt_T + voltage8;
   
  
   
  PImage logo1;
  PImage logo2;
  PImage logo3;

  // carica le 3 immagini .bmp delle 3 batterie
  
  logo1 = TFTscreen.loadImage("volt1.bmp");
  if (!logo1.isValid()) {TFTscreen.println("errore caricando volt1.bmp");}
  
  logo2 = TFTscreen.loadImage("volt2.bmp");
  if (!logo2.isValid()) {TFTscreen.println("errore caricando volt2.bmp");}
    
  logo3 = TFTscreen.loadImage("volt3.bmp");
  if (!logo3.isValid()) {TFTscreen.println("errore caricando volt3.bmp");}
 
  
 //definisce colore di scrittura 
  TFTscreen.stroke (255,0,0);
  
  //per ogni cella disegna una batteria "logoX" e sopra di essa il corrispondente valore in Volt con 2 decimali
  
  TFTscreen.image (logo1,1,80);
  TFTscreen.setCursor (1,60);
  TFTscreen.print (voltage1,2); 
  delay (200);
  
  TFTscreen.image (logo2,28,80);
  TFTscreen.setCursor (28,60);
  TFTscreen.print (voltage2,2);
  delay (200);
  
  TFTscreen.image (logo3,55,80);
  TFTscreen.setCursor (55,60);
  TFTscreen.print (voltage3,2);
  delay (200);
  
  TFTscreen.image (logo1,82,80);
  TFTscreen.setCursor (82,60);
  TFTscreen.print (voltage4,2);
  delay (200);
  
  TFTscreen.image (logo2,109,80);
  TFTscreen.setCursor (109,60);
  TFTscreen.print (voltage5,2);
  delay (200);
  
  TFTscreen.image (logo3,136,80);
  TFTscreen.setCursor (136,60);
  TFTscreen.print (voltage6,2);
  delay (200);
  
  //scrive il valore totale sopra a tutto
  
  TFTscreen.setCursor (1,45);
  TFTscreen.print (Volt_T,2);
  delay (200);
  
  delay (5000);
  TFTscreen.background(255,255,255);
  
  
  
}

Hai usato resistori da 1k e la corrente che fluisce in essi in parte alimenta Leonardo.
Porta i resistori a 10k.
Per il secondo problema, mi sa che è un problema di RAM: comincia da usare la funzione F() nei print e vedi se migliora.

Ho provato questa funzione F() ma non ci riesco :fearful:
Seguo le indicazioni qui Serial.print() - Arduino Reference ma il compilatore mi da sempre errore... :blush:

Dunque, i vari "print" con la F() funzionano ma quando vado a compilare la riga di comando per disegnare le immagini, mi da sempre l'errore che riporto :
Questa è la riga di comando che scrivo
TFTscreen.print(F (voltage1,2));
e questo è l'errore che mi dichiara :
'F' was not declared in this scope

Ho fatto diversi tentativi di sintassi ma non ci riesco proprio. Puoi aiutarmi?
Grazie :slight_smile:

La funzione F() serve unicamente per risparmiare lo spazio delle costanti stringa.

TFTscreen.println(F("errore caricando volt1.bmp"));

cyberhs:
La funzione F() serve unicamente per risparmiare lo spazio delle costanti stringa.

TFTscreen.println(F("errore caricando volt1.bmp"));

Ah, ecco. Allora tutte le altre stringhe le ho trasformate in print F() ma il difetto persiste : fa 3 o 4 cicli e poi dà errore in caricamento immagine da sd.
Ti vine in mente qualcos'altro per caso?

prova ad aggiungere la stampa della memoria libera.
Nel loop aggiungi
TFTscreen.print(freeram() );

cosi' vediamo quanta RAM c'e' a disposizione

int freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
  }

Hai allocato spazio di memoria per 3 loghi, probabilmente la stessa immagine ma traslata di posizione.

L'allocazione dovresti farla a livello globale, mentre ora ad ogni loop la ricrei.

Probabilmente lo spazio in memoria è insufficiente: prova ad usarne una sola caricando in essa di volta in volta le varie immagini.

Ciao Brunello, ho fatto il copia/incolla del tuo pezzo di codice ma mi da errore "a function-definition in not allowed here before '{' token" ......

cyberhs:
Hai allocato spazio di memoria per 3 loghi, probabilmente la stessa immagine ma traslata di posizione.

L'allocazione dovresti farla a livello globale, mentre ora ad ogni loop la ricrei.

Probabilmente lo spazio in memoria è insufficiente: prova ad usarne una sola caricando in essa di volta in volta le varie immagini.

Ho pensato ad un qualcosa del genere ed ho già provato a tirar fuori dal loop il pezzo di codice che legge la SD e carica i 3 loghi ma non ci sono riuscito.... :blush:
Porca miseria : non avevo mai programmato in C e sono cmq almeno 25 anni che non scrivevo una riga di codice :fearful:

avevo messo freeRam tutto minuscolo

TFTscreen.print(freeRam() );

Brunello:
avevo messo freeRam tutto minuscolo

TFTscreen.print(freeRam() );

Scusa ma io ho inserito queste righe :

int freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
  }
  
  
  TFTscreen.print(freeRam());

ma non funzionano ... dove sto sbagliando?