Connessione e scambio di dati tra ESP32 e Android via BLE

Metto quà all'inizio il file zippato contenente lo sketh per ESP32, il file .aia per Mit inventor i files LTSpice (se volete realizzare il circuito LC) e l'app .apk compilata
ZipFiles.zip (5.5 MB)
ZipFiles2_temperature.zip (6.9 MB)

Ciao a tutti
Ho deciso di sviluppare questo software per evitare di utiizzare un touch screen dedicato per ogni progetto utilizzato su ESP32 o Arduino
Soprattutto su Arduino l'utilizzo di un touch screen và ad occupare un numero notevole di pin che sono già meno rispetto a un ESP32 e ne rimangono liberi ben pochi.
Il programma l'ho testato sia con ESP32 Wroom che con ESP32-D Wroom, le diffrenze che ho riscontrato tra i due sono che ESP32 deve essere bindato al telefono, mentre su ESP32-D non è necessaria la bindatura, inoltre, quando si carica un nuovo sketch su ESP32-D, non serve premere il tasto di reset ogni volta (veramente noioso), e il monitor seriale parte direttamente.

Cosa serve:

  1. ESP32 o Arduino con BLE
  2. Telefono con BLE
  3. Arduino Ide + MIT App Inventor 2 per Android ( Windows per il login MIT App Inventor) come browser consigliano di utilizzare Chrome o Firefox, io, che ho usato la connessione USB mi son trovato meglio con Chrome, anche se ogni tanto perdono la connessione e bisogna ristabilirla (consiglio di chiudere l'app sul telefono, poi dare il comando di reset connessione nel menu di Mit e poi ristabilire una nuova connessione).
    Se volete utilizzare il progetto così com'è, avrete bisogno di un circuito CL tank collegato a ESP32 (pin 16 e 17) e il generatore di forme d'onda AD9833 che utilizza i pin SCLK = 18, MISO = 19, MOSI = 23, SELECT = 5 e un pin a scelta (io ho utilizzato il pin 4) per l'uscita di LedC che può variare il Duty cycle e quindi cambiare l'illuminazione di un led o la velocità di un motore.
    Il circuito CL permette di misurare l'induttanza di un induttore collegato, oltre alla frequenza di risonanza (utile ad esempio per un metal detector per ottenere il massimo dal campo magnetico generato) e la lunghezza del ciclo (utile per determinare lo spazio minimo tra un segnale di ON e il successivo).
    Purtroppo AD9833, che è ottimo per generare una sinusoidale pulita sino a 12500 Hz, non ha a disposizione comandi per variare il Duty cycle, per cui per il tipo di onda quadra con Duty variabile sono ricorso alla funzione LedC di ESP32 high speed (sino a 19000 Hz con risoluzione 12 bit) che viene utilizzata nel mio sketch se selezionate 3 come tipo di onda.

Perchè Mit inventor e non Android studio o altro?
Perchè tutto fila via meglio e spedito ed è + alla portata di tutti..l'unica cosa che gli rimprovero è il salvataggio in automatico che non si può disabilitare, e questo costringe a salvare spesso il lavoro, così se si sbaglia qualcosa si carica la versione precedente.
Android Studio è sempre in evoluzione e quando carichi un programma fatto un anno prima (sempre con Android Studio) ti genera una marea di errori e ci devi perdere delle ore per risolverli, se poi cerchi di importare pezzi di codice trovati online la cosa si complica maggiormente anche perchè magari quell' istruzione non è + supportata dalle nuove versioni Android, oppure perchè quelle impostazioni sulla privacy (ridicole) continuano a cambiare....rendendo la programmazione su quel tool veramente uno stress e una perdita di tempo enorme.

Chiusa questa parentesi passiamo ad esaminare prima il lato Android con questa immagine che visualizza la prima pagina delle 3 che ho impostato nel programma (selezionabili col tasto B1-B2-B3 sul telefono), ogni pagina ha 6 slot disponibili (potete aumentare sia i bottoni che gli slot e quindi riesaminare tutto il codice sia lato Android che ESP32 per adattarlo alle vostre esigenze) e qua sotto includo uno screenshot della prima pagina

Potete utilizzare queste pagine sia per ricevere dati da Arduino (ad esempio la pagina B1 nel mio esempio riceve i dati dal circuito CL collegato ad ESP32, mentre la pagina B2 l'ho utilizzata per inviare dati ad ESP32, per cui posso selezionare il tipo di onda di AD9833, frequenza ecc. (oppure se seleziono tipo di onda 3 ESP32 eseguirà la routine software high speed che si basa sull'istruzione ledc di Espressif, che genera un'onda quadra con duty cycle modificabile), e il Duty selezionabile nell'indice 10 funzionerà solo sul tipo di onda 3 perchè AD9833 non permette di impostare questo campo.
Quà sotto metto due immagini, la prima visualizza la pagina 2 (B2) e la seconda è presa dall'oscilloscopio ( e poi visualizzate in un mio programma sviluppato in .net) che mostra le 2 forme d'onda attivate dal programma, nel canale 1 una sinusoidale generata da AD9833 e nel canale 2 l'onda quadra generata da ESP32 con duty cycle 25%


I files che impostano i 3 campi di ogni riga, sono letti all'avvio dell' app (caricati dalla sezione MEDIA di MIT) e sono List1Field.csv List2Field.csv e List3Field.csv.
In MIT, se cliccate con il mouse sopra ad esempio in MEDIA List1Field.csv potete scaricare sul PC questo file e modificarlo con un editor di testo e poi ricaricarlo dopo le modifiche in MIT con lo stesso nome e si sostituirà al precedete.
Il primo file (List1Field.csv) è un elenco indice (prima colonna), il secondo file è il nome che apparirà sul bottone (seconda colonna) e il terzo campo è il dato modificable per l'invio verso ESP32 (terza colonna con sfondo bianco) oppure per visualizzare i dati in arrivo da ESP32.
Il terzo campo può essere utile per impostare una variabile di partenza di quel dato campo(se è un campo di spedizione dati e non di ricezione), ma facciamo un esempio: se ad esempio, nella pagina 2 (B2) nella prima riga (scelta della forma d'onda) c'è impostato 1, cliccando sul bottone WaveForm 1-2-3-4, nel campo testo da inviare apparirà automaticamente 1 per cui basta premere su SEND che il comando di cambio di forma d'onda invierà 1 ad ESP32, ovvero selezione onda sinusoidale; se invece nel campo testo inserite 2 verrà salvato sino al riavvio dell'app.
Per attivare l'uscita della forma d'onda bisogna premere sulla scelta della forma d'onda, altrimenti resterà in modo OFF, e poi potrete scegliere la frequenza ecc..
Purtroppo negli ASSETS possiamo solo leggere i files e non salvarli da programma (ad esempio all'uscita dell'app, per cui ce li saremmo trovati uguali al prossimo utilizzo dell'APP) e andare a salvarli in altri FileScope (App,Cache,Legacy,Private e Shared) ci si và a scontrare con le varie versioni Android e con le impostazioni di Privacy, per cui ho preferito utilizzare Assets e non altre directory sparse su SD, memoria interna ecc. almeno tutto rimane interno all'app per una più semplice portabilità su altri telefoni.

Sotto alle 6 righe c'è un tasto di start/stop, questo serve per comunicare a ESP32 di cominciare ad inviare i dati tramite la routine1 che si trova nel loop principale, questa routine funziona solo se stiamo visualizzando la pagina B1 (la posizione della pagina viene controllata da ESP32 che utilizza la routine1 solo se sul telefono viene visualizzata la pagina B1), ma volendo si può anche disabilitare questo controllo, tanto, tramite l'indice del messaggio inviato da ESP32, i dati arriveranno solo alla riga del relativo indice.

Sotto a questo tasto di start/stop c'è una label che visualizza l'indice a cui invieremo i dati nella casella di testo a fianco (cliccando su questa casella di testo viene visualizzata una tastiera con soli dati numerici che possiamo inviare a ESP32 premendo sul tasto SEND ).
Anche in questo caso c'è un controllo se effettivamente siamo nella pagina giusta, ovvero, potremo inviare dati a AD9833 solo se ci troviamo nella pagina 2 (B2).
Il campo testo può inviare un massimo di 10 cifre non superiore a 2147483647.

Lo sketch per ESP32 l'ho già documentato abbastanza nei rem, diciamo che nelle routines iniziali troviamo quella che interpreta la stringa di dati in arrivo dal telefono che sono praticamente 2, ovvero un INDICE (i primi 2 caratteri) e un Valore che sono i numeri provenienti dalla terza colonna del telefono (text box).
Se il secondo valore è nullo, è una stringa di comando, ad esempio per comunicare ad ESP32 che ho cambiato pagina sul telefono (B1-B2-B3).
Se invece è presente il secondo valore, allora viene processato con switch (Identity) {
case...

class MyCallbacks : public BLECharacteristicCallbacks {
  void onWrite(BLECharacteristic* pCharacteristic) {
    String stringReceived = pCharacteristic->getValue();
    if (stringReceived.length() > 0) {
      incomingNumbers = "";
      for (int i = 0; i < stringReceived.length(); i++) {
        incomingNumbers += stringReceived[i];
      }
      String StringIncoming = incomingNumbers.substring(0, 2);  // the first 2 characters are the recipient's identifier (1-18)
      int Identity = StringIncoming.toInt();
      StringIncoming = incomingNumbers.substring(2);  // and then the second value in the string from 2 to the end of the string MAX 10 chars
      Valore = StringIncoming.toFloat();
      Serial.println((String)Valore);
      if (Valore == 0) {       // if not are numbers in second field of incoming string then....
                               // if want reset calibration of metal detection or if you change inductor, just press off sending messages on phone
        if (Identity == 30) {  // 30 is the identifier of the start/stop send button

Altre subroutines sono per la connessione/disconnessione BLE e dopo il SETUP arriviamo al LOOP dove viene gestito l'invio di dati da ESP32 al telefono, che nel mio progetto interessano solo la prima pagina del programma di cui riporto uno screenshot quà sotto:

void loop() {
  if (deviceConnected) {
    static uint32_t updateTime;
    if (millis() - updateTime >= 1000 && start1 == 1) {  // update time is set any 1 second
      updateTime = millis();
      switch (routine) {  // we update only data choose by B1-B2-B3 button on phone
        case 1:
          routine1();  // is a OUTPUT routine for CL tank circuit (for measure freq,period and inductance of inserted inductor)
          break;
        case 2:
          //is a INPUT routine already done in class MyCallbacks
          break;
        case 3:
          // routine3(); free to use INPUT (in class MyCallbacks) OR OUTPUT same of routine 1
          break;

Il Loop guarda innanzitutto se siamo connessi al BLE e poi ogni secondo, a secondo della variabile ROUTINE salta ad una delle 3 possibili routine.
Nel mio esempio è solo la routine 1 che invia dati verso la prima pagina del telefono e riporto qua il codice:

void routine1() {  // routine 1 that send 3 parameters to phone, so Freq/Period/inductance of CL circuit
  numPulses = 0;
  digitalWrite(16, HIGH);
  Delay(10);  // carico il condensatore
  digitalWrite(16, LOW);
  delayMicroseconds(30);  // <<<<<<< lasso di tempo tra fine segnale ESP32 e inizio oscillazione circuito CL tank
  readTime();
  inductance = (float)(pow(1.0 / (6.283185307 * freq), 2)) / capacitance;
  inductance *= 100000L;  // uF
  if (numPulses > 0) {
    stringVal = String(freq);
    T_buff[(stringVal.length() + 1)];  // string + null terminated char
    dtostrf(freq, 1, 2, T_buff);       // 1 is mininum width, 2 is number ofdecimals
    stringVal = String(T_buff);
    stringSend = "01" + stringVal;
    // Serial.print(freq);
    // Serial.print("   >>>  ");
    // Serial.println(stringSend);  //display send string
    pCharacteristic->setValue(stringSend);
    pCharacteristic->notify();
    Delay(10);
    stringVal = String(Periodo);
    T_buff[(stringVal.length() + 1)];
    dtostrf(Periodo, 1, 0, T_buff);  //1 is mininum width, 2 is precision
    stringVal = String(T_buff);
    stringSend = "02" + stringVal;
    // Serial.print(Periodo);
    // Serial.print("   >>>  ");
    // Serial.println(stringSend);  //display send string
    pCharacteristic->setValue(stringSend);
    pCharacteristic->notify();
    Delay(10);

    stringVal = String(inductance);
    T_buff[(stringVal.length() + 1)];
    dtostrf(inductance, 1, 2, T_buff);  //1 is mininum width, 2 is precision
    stringVal = String(T_buff);
    stringSend = "03" + stringVal;
    // Serial.print(inductance);
    // Serial.print("   >>>  ");
    // Serial.println(stringSend);  //display send string
    pCharacteristic->setValue(stringSend);
    pCharacteristic->notify();
    Delay(10);
    if (counterPeriod < 10) {
      counterPeriod++;
      calibratedPeriod += Periodo;
      if (counterPeriod == 10) {
        calibratedPeriod /= 10;
        stringVal = String(calibratedPeriod);
        T_buff[(stringVal.length() + 1)];
        dtostrf(calibratedPeriod, 1, 0, T_buff);  //1 is mininum width, 2 is precision
        stringVal = String(T_buff);
        stringSend = "06" + stringVal;  // send calibratedPeriod to index 06
        pCharacteristic->setValue(stringSend);
        pCharacteristic->notify();
        Delay(10);
      }
    }
    // this condition work as metal detector, if the Periodo variable change it is because a proximity of metals is revealed
    // if period is decreased is because are a non ferrous material near so alluminium, ore ecc..if increase are a ferrous material near
    // but distance of detection is very low (5 cm aprox in air) because 3.3 volt of ESP32 generate a very low magnetic field
    if (counterPeriod == 10) {  // when calibration is done can see if are variation from calibrated period and new period
      if (Periodo > calibratedPeriod + 1 || Periodo < calibratedPeriod - 1) {
        tempVariation = Periodo - calibratedPeriod;
      } else {
        tempVariation = 0;
      }
      stringVal = String(tempVariation);
      T_buff[(stringVal.length() + 1)];
      dtostrf(tempVariation, 1, 0, T_buff);  //1 is mininum width, 2 is precision
      stringVal = String(T_buff);
      stringSend = "04" + stringVal;  // send calibratedPeriod to index 06
      pCharacteristic->setValue(stringSend);
      pCharacteristic->notify();
      Delay(10);
    }
  } else {
    Serial.println("insert an inductor");
  }
}

La prima parte invia un segnale di start al circuito CL ( digitalWrite(16, HIGH) ) e poi interroga la routine readTime()

void IRAM_ATTR isr() {
  unsigned long now = micros();
  if (numPulses == 0) {
    Time1 = now;
  } else {
    Time2 = now;
    detachInterrupt(17);  // disable interrupt after second signal from CL wave
  }
  ++numPulses;
}

void readTime() {
  // Opzioni: DISABLED RISING FALLING CHANGE ONLOW ONHIGH ONLOW_WE ONHIGH_WE (wake up the ESP32 from light sleep)
  attachInterrupt(17, isr, CHANGE);
  Delay(5);
  //formula frequenza = 1 / Periodo
  Periodo = (float)(Time2 - Time1) * 2.0;
  freq = 1.0 / Periodo;
  freq *= 1000000.0;  // Hz
}

che attiva un interrupt ed esamina i cambiamenti (CHANGE) sul pin 17, e al secondo segnale si interrompe l'interrupt e si valuta la lunghezza e quindi la frequenza e l'induzione.
E in seguito si inviano questi dati, una stringa alla volta perchè il BLE di default (ma si possono aumentare) ha un massimo di 20 bytes di spedizione (3bytes di intestazione e 20 di dati) e, importante, ci vuole un ritardo (20 millisecondi) tra un invio e il successivo per non sovraccaricare il BLE che non è molto veloce nell'invio.
Nel mio esempio, ogni 10 letture, calcolo anche la durata media del Periodo che mi serve per determinare la Calibrazione, perchè il circuito funziona anche come metal detector (5-6 cm di distanza in quanto il segnale di start proveniente da ESP32 è di soli 3.3 Volt e quindi il campo magnetico generato dall'induttore è veramente basso) e nel campo Metal Detection sul telefono mi appariranno numeri negativi se avvicino metalli non ferrosi all'induttore e numeri positivi se avvicino metalli ferrosi.

Penso di aver elencato le cose essenziali di questo progetto, poi stà a voi adattare la parte MIT e la parte ESP32 al vostro progetto, ma di base avrete una connessione BLE funzionante utilizzando 2 soli UUID, e un display touch di tutto rispetto anche utilizzando un vecchio telefono e un notevole risparmio di energia su ESP32 senza display, e tanti GPIO liberi.
Con calma stò eseguendo un video e prossimamente metterò il link qua sotto.
Ciaooo a tutti e buon divertimento

4 Likes

Ho aggiunto il sensore di temperatura DS18B20 e utilizzato la prima pagina dove c'era lo slot 05 libero per visualizzarla.
metto anche quà i nuovi files:

ZipFiles2_temperature.zip (6.9 MB)

e uno screenshot:

Stò lavorando su una board + complessa e non vorrei che il post venga chiuso.
Ho avuto seri problemi nel misurare i condensatori con ESP32.
Ora stà provando utilizzando un 555 e si comiciano a vedere dei risultati decenti nel rilevare la capacità del condensatore rispetto alla frequenza emessa dal 555.
Terminato questo pubblicherò anche il PCB e tutto il progetto.
Ciao

> Blockquote

questo è il circuito che stò utilizzando con ICM7555 per calcolare il condensatore, basato su questo articolo:
https://schematicsforfree.com/files/Oscillators%20and%20Generators/Astables/555%20Duty%20Cycle%20Adjustment%20with%20Fixed%20Frequency.pdf

Anche io non amo Android Studio. Mit App inventor è senza dubbio una soluzione "facile" però hai anche tanti tanti limiti, primo fra tutti il fatto che puoi realizzare solo app Android.

Non entro nel merito del firmware che non ho guardato, ma volevo suggerirti di valutare anche Qt, ovviamente per il prossimo progetto visto che questo ormai mi pare di capire che sia già quasi completo.

Il vantaggio di Qt rispetto ad altri sistemi di sviluppo, oltre a quello di programmare nativamente in C++ (che per alcuni è un ostacolo, ma per me invece è un valore aggiunto), è che hai un unico ambiente multipiattaforma all'interno dello stesso progetto.
In questo momento ad esempio sto sviluppando un'app BLE che funziona sia con sistemi Desktop Windows/Linux e sia in ambiente mobile Android/iOS.
Ho penato giusto un po' per configurare il build-kit Android su Windows mentre su Linux ha funzionato al primo colpo.

In passato ho usato con molta soddisfazione anche Flutter come strumento di sviluppo App che probabilmente è una soluzione ancora più flessibile e moderna rispetto a Qt.

Sviluppo a parte, volevo suggerirti di curare di più l'ergonomia cognitiva delle interfacce grafiche perché secondo me quella che hai realizzato è un po' confusa.
Io non amo molto immagini di background, gradienti e cose simili. Preferisco interfacce lineari e graficamente "pulite" con palette dei colori omogenee, ma aldilà delle preferenze personali devi sempre metterti nei panni dell'eventuale utilizzatore anche se alla fine sarai solo tu stesso.

Giusto per fare un esempio, quei tre pulsanti per navigare le pagine a me sembravano dei led di stato. Potevi usare un classico "navigator" a tab o swipe tipico degli ambienti mobile e liberarti anche spazio inutilmente occupato sul layout grafico. Altro esempio: i pulsanti per la gestione della connessione BLE andrebbero visualizzati solo quando è necessario, una volta connesso al dispositivo puoi nasconderli e mostrare solo lo stato della connessione stessa.

Sono piccole accortezze che secondo me fanno la differenza, soprattutto quando hai giustamente piacere nel condividere il tuo lavoro a beneficio della community.

1 Like

Grazie dei consigli, purtroppo per esperienza con Android studio ho avuto dei pessimi risultati anche per aggiornare app che avevo sviluppato per versioni precedenti di Android e che con le recenti avevano problemi vari soprattutto riguardo alla ridicola PRIVACY che sinceramente è solo un'ostacolo per l'utilizzatore dell'app, perchè coi sistemi di controllo che hanno sui nostri telefoni sanno anche quanta carta igienica consumiamo settimanalmente :rofl:
Trovo anche assurdo che la CE abbia scelto il telefono come strumento per accedere a dati sensibili (banca,spid, pagamenti) dal momento che il nostro cell siamo obbligati ad averlo sempre dietro e quidi soggetto a crash, furti e smarrimenti, mentre con la chiavetta USB o un tastierino numerico potevamo accedere tranquillamente da PC anche portatile.
I blocchi con riconoscimento facciale o impronta sono altrettanto ridicoli e bypassabili da hacker neanche tanto esperto e sono solo una rottura di scatole per l'utente finale.
Cmq, tornando al nostro post, con Mit app Inventor ho potuto riscrivere e veder funzionare su telefoni nuovi alcune app che avevo sviluppato con Android Studio e che non funzionavano più con le version android recenti (anche se ricompilate ovviamente).
Certo, hai delle limitazioni rispetto a una programmazione diretta, ma come ben sappiamo possiamo superarle e aggirarle con una programmazione esperta.
QT l'avevo provato ma non mi ha entusiasmato.
Il mercato iOS ha una base utenti molto grande(purtroppo si vedono le file di gente per accaparrarsi l'ultimo cell venduto a peso d'oro) e a quella si deve rinunciare.
Riguardo all'interfaccia grafica sì, si possono evitare i 3 tasti alla base perchè l'informazione può essere inclusa nella stringa inviata ad ESP32 e viceversa.
Riguardo al collegamento BT invece, far scomparire i tasti di connessione/disconnessione/controllo connessine non sono d'accordo, occupano poco spazio e ci vorrebbe cmq almeno un'icona per farla apparire/scomparire.
Cmq per ora (dopo una lunga battaglia) son riuscito a superare il problema della sezione che misura la capacità dei condensatori, utilizzando interrupt sui dati in uscita da ICM7555 che è praticamente identico al 555 ma consuma meno.
Ciaoooo e buona programmazione :waving_hand: :waving_hand: :waving_hand:

Aggiungo a questo post una considerazione riguardo ai valori delle resistori utilizzati in rapporto alla temperatura ambiente, che possono cambiare i risultati nelle misurazioni effettuate.

Ho preso come esempi 3 resistori attorno ai 5.6K come valore base e riporto quà le misurazioni a 28.8 gradi Celsius e a 8.8 gradi Celsius, per cui una differenza di 20 gradi.

Resistore strato metallico 1/4 W 1% precisione

5.636K a 8.8C 5.590K a 28.8C = 0.046/20=2.3Ohm ogni grado

Resistore strato metallico 1/2W 1% precisione

5.666K a 8.8C 5.636K a 28.8C = 0.03/20=1.5Ohm ogni grado

Resistore multigiro trimmer Cermet 3W

5.2725K a 8.8C 5.2741 a 28.8C = 0.0016/20=0.00008Ohm ogni grado

Notiamo subito che i resistori da 1/4 di Watt subiscono maggiormente delle differenze di temperatura ambiente(avendo meno strato), mentre la resistenza multigiro è quella che risente meno della differenza di temperatura.

Se il nostro progetto dispone anche di un misuratore di temperatura ambiente ed è dedicato alla misurazione di resistenze/condensatori/induttanze non possiamo fare a meno di applicare queste differenze al nostro calcolo se utilizziamo resistori di valore fisso di 1/4 di Watt o di 1/2 Watt.

Se invece utilizziamoi resistori multigiro trimmer in materiale Cermet possiamo anche trascurare queste differenze di temperatura.

Ho fatto questo approfondimento perchè notavo differenze nelle misurazioni sia a causa della temperatura ambiente, sia a causa del passaggio di tensione attraverso i componenti elettrici(resistenze di contatto e di alimentazione)(effetto Joule e Volta).

Queste misurazioni sono state eseguite sul singolo resistore non collegato al circuito finale, perchè effettuarle quando il circuito stà lavorando non è cosa semplice, e variano anche a seconda della qualità delle saldature ma soprattutto rispetto alla corrente di lavoro…accontentiamoci della variazione di temperatura ambiente.

Non ho considerato i resistori surface mounted anche perchè a livello amatoriale diventa un problema la saldatura, e, se non avete problemi di spazio, direi che la scelta migliore cade sui resistori multigiro trimmer ( se non vogliamo svenarci coi resistori a film spesso che godono di una minore differenza a seconda delle variazioni di temperatura ambiente)

Questi resistori sono anche molto comodi per tarare ad esempio un circuito oscillatore nella misurazione di condensatori (su cui sto lavorando per questo progetto)

E rieccoci per una nuova puntata con varie migliorie anche nello sketch.

Allora….FONDAMENTALE è un’alimentazione stabile… e non basta collegare una Lipo ma, dopo aver testato vari buck, finalmente con un LT3045 a bassissimo rumore e 500mA, ho avuto dei risultati stabili nella misurazione dei condensatori, nella configurazione con 555 in modalità astabile per generare una forma d’onda quadra che varia in ampiezza a seconda del condensatore testato.

E tra i vari 555 testati (NA555, ICM7555, TS555) ho ottenuto con quello della Texas(TS555) dei risultati migliori, sia nella configurazione classica con 2 resistenze, sia con una singola resistenza che è anche più semplice da realizzare sia come componenti che come formula utilizzata nello sketch.

In questo link trovate un confronto tra questi tre tipi di 555 :

E quà trovate il link al circuito in test:

https://www.falstad.com/circuit/circuitjs.html?ctz=CQAg7AnCCsCmC0BGRIDMAOATAOjANk2i0QBZUIIAGPEkPdEKCkS7S99xAKET2hEwlKIXsMJ4ReYaRZsOlMOmjLEqRFmiZBJTFwDuAoQPQMIEzCZZcA8iBJnDDaPkcsrANxDwSDdNIl+bsJG-MJh2ND6Xj5o6LQOGLSUUaioEoloaWiYYSmUtIgWaPkggckAHl6IRFVGpMJIqGB2hiAAKgDKytAAwgByADoAzgCCQwAuAIYARgA2sMPQlACkwwAmAK7jAJ5clfBF0BIo0FC0gjAAalxCqDAQmCIuZsHsbpgA+oiUgh+UH+VvtMgR9UNhBOofMgSGQKEtoH85JREB8kF8uABjRjmSwJOItRAIZACbAkVToPCFdAQU4QMCkZqsThWAwOcTY4wMZIGQRiI4ifHs7kiB4Cfls-kVKooVAkCRgWiys6tNoAUQ6bWGPT2Ih0AlQj14-EwqH4BQYACVuPtqgx4BA7mo7vbgq0MZMAA6TDEASymADsMQshgMAI7DVh4B0M4YAKmGVrjwwACrAAE4+gD2awAFBjthj5gBKKK8UrCVAlQKoLhpgXnfmQkWPCsKLTCpuIUWdwoiUv45CPDKDlmxAq9jJaAql0XsptCnVNMT2OzIsWPAob3Doa4GVLpfFNA9JFL0ERFSubrk3Sh3GKqY5nzBl6QQL4-Eh-AHfNTIxEcFEmU4VEUU+LtQUxYpFRyKCxwJBAGDBVJkOQzB6VNfEgJQYU8UVEoMmFS84P3ODCLPLsh3IopCJcDIyEeNCWx4BU7ANAQwA3QIpxgGAOFkGglHkITmQtWAhh9CZJkDWBTwYbjJwuQjBQueiBEUqJ7HMGIfGXa9KiWOSoxgch2I3c80HBShrhyKAyypaIGHspIkTXEhsAoDzPLMJY1yw2QUG4Awy0COzqKibxfAae8woMCLzztGICK4IA

Spiego un attimo le scelte fatte in questo circuito:

Alimentazione del chip TS555 5 Volt da LT3045 perchè con 3.3 di ESP32 non era così preciso.

Resistenza variabile multigiro (che come spiegavo nel post precedente risulta più stabile al variare della temperatura ambiente) tarata sui 300Kohm.

Si potrebbe arrivare anche a 1Mohm di resistenza per diminuire il consumo della batteria, ci sarebbero misure più precise nei condensatori piccolissimi(ciclo più lungo), però avremmo un tempo decisamente troppo lungo nella misura attorno ai 10uF o oltre.

In uscita potete vedere un partitore di tensione per abbassarla sino a 3.3 massimi che si possono applicare agli ingressi digitali di ESP32.

Come calibrare il circuito:

  1. Direi indispensabile un’oscilloscopio o un tester molto veloce nelle letture (non collegare l’uscita ad ESP32 prima di aver testato l’uscita del partitore).

  2. I pin digitali di ESP32 considerano un’uscita alta quando la tensione supera i 2.5 V, per cui dovrete regolare il partitore in modo che i picchi di tensione siano compresi tra 2.8V e 3.2 per sicurezza.

  3. il simulatore di cui vi ho messo il link sopra è abbastanza preciso, ma prima di collegare l’uscita a ESP32 è sempre meglio verificarla.

Quà sotto metto un’immagine di un condensatore da 10pF:

Come potete vedere i bordi dell’onda non sono ben squadrati (siamo a 95KHz) ma diciamo da 20-30pF in sù avremo un’onda perfettamente quadra.

E ora passiamo alla lettura dei dati su ESP32

La formula base in questo tipo di circuito con 1 resistenza è Condensatore = ln(2) * R * ciclo

che nel mio sketch si traduce in capacitance = 0.693147 * 307.22 * ((float)(Periodo - 5.0) / 100000.0);

Ma perchè quel Periodo - 5.0 ?

Perchè se voi scollegate il condensatore da testare, avrete comunque 5 uS di default come risposta dal circuito e per misurare condensatori tipo quello visualizzato sopra che ha un ciclo di 10.5 uS, se non togliete questi 5 uS avrete una lettura completamente errata, mentre non ci sono problemi con condensatori il cui ciclo è molto maggiore.

E questa correzione sinceramente non l’ho trovata in nessun esempio online e se non l’applicate avrete misure errate in condensatori di piccola capacità.

Lo chiameremo tempo morto, e se cambiate il valore della resistenza R1 dovrete considerare questo tempo nella formula applicata e misurarlo senza condensatore.

Potete usare ESP32 per misurare questo tempo morto, basta che ad esempio cambiate la linea if (Periodo > 6) { in if (Periodo > 1) { e togliere il condensatore da testare.

Ora metto lo sketch per misurare solo il condensatore, stò cambiando anche il metodo di misurazione delle induzioni (utilizzando oscillatore Colpitt LC ) e alla fine penso di fare un PCB che unisca entrambi e mandi i dati al telefono via BT.

// Connettere l'uscita Pin3 del TS555 al pin 4 di ESP32 con partitore di tensione per avere <3.3V
#include "esp_intr_alloc.h"
#define ESP_INTR_FLAG_LEVEL3 (1 << 3)  ///< Accept a Level 3 interrupt vector
volatile unsigned long Time[3];
unsigned long now;
long Periodo, TotPeri;
int numPulses, Letture, Divisore;
float freq, capacitance, TotCapacitance, MediaCapacitance, FreqMedia, TotFreq, MediaPeriodo;

void setup() {
  Serial.begin(115200);
  Delay(1000);
  pinMode(4, INPUT);
  Serial.println("OK");
  gpio_dump_io_configuration(stdout, SOC_GPIO_VALID_GPIO_MASK);
  Delay(200);
}
void IRAM_ATTR isr() {
  numPulses++;
  now = micros();
  Time[numPulses] = now;
  if (numPulses == 2) {
    detachInterrupt(digitalPinToInterrupt(4));  // disattivo interrupt dopo il secondo segnale
  }
}

void loop() {
  numPulses = 0;
  Time[0] = micros();
  // Opzioni: DISABLED RISING FALLING CHANGE ONLOW ONHIGH ONLOW_WE ONHIGH_WE (wake up the ESP32 from light sleep)
  attachInterrupt(digitalPinToInterrupt(4), isr, RISING);
  while (numPulses < 2) {
    delayMicroseconds(1);
  }
  Periodo = (Time[2] - Time[1]);
  if (Periodo > 6) {
    Letture++;
    freq = 1.00 / Periodo;
    TotPeri += Periodo;
    TotFreq += freq;
    // Logaritmo Naturale di 2 = 0.693147
    // R1=307.22 singola resistenza
    capacitance = 0.693147 * 307.22 * ((float)(Periodo - 5.0) / 100000.0);  // 95.4546KHz Periodo 10.50 Capaci 11.712pF  con codensatore 10pF
    TotCapacitance += capacitance;
    MediaCapacitance = ((float)TotCapacitance / Letture);
    MediaPeriodo = (float)TotPeri / Letture;
    FreqMedia = (float)TotFreq / Letture;

    if (Letture > 5) {
      FreqMedia *= 1000000.0;
      Serial.println("");
      Serial.print("*********  ");
      if (FreqMedia > 1000) {
        Serial.print((FreqMedia /= 1000.0), 4);
        Serial.print("KHz   Periodo ");
      } else {
        Serial.print((FreqMedia), 4);
        Serial.print("Hz   Periodo ");
      }
      Serial.print(MediaPeriodo, 2);
      Serial.print("   Capaci ");
      if (MediaCapacitance > 1000) {
        Serial.print((MediaCapacitance /= 1000.0), 4);
        Serial.print("uF");
      } else {
        if (MediaCapacitance > 1) {
          Serial.print((MediaCapacitance), 4);
          Serial.print("nF");
        } else {
          Serial.print((MediaCapacitance *= 1000.0), 4);
          Serial.print("pF");
        }
      }
      Serial.println("  *********");
      TotPeri = 0;
      Letture = 0;
      MediaPeriodo = 0;
      MediaCapacitance = 0;
      TotCapacitance = 0;
      FreqMedia = 0;
      TotFreq = 0;
      Delay(2000);
    } else {
      freq *= 1000000.0;
      if (freq > 1000) {
        Serial.print((freq /= 1000.0), 4);
        Serial.print(" KHz ");
      } else {
        Serial.print((freq), 4);
        Serial.print(" Hz ");
      }
      Serial.print("  Periodo ");
      Serial.print(Periodo);
      Serial.print("uS");
      Serial.print("  Media ");
      Serial.print(MediaPeriodo, 2);
      Serial.print("  Capaci ");
      if (capacitance > 1000) {
        Serial.print((capacitance /= 1000.0), 4);
        Serial.println("uF");
      } else {
        if (capacitance > 1) {
          Serial.print((capacitance), 4);
          Serial.println("nF");
        } else {
          Serial.print((capacitance *= 1000.0), 4);
          Serial.println("pF");
        }
      }
      Delay(100);
      Periodo = 0;
    }
  } else {
    Serial.print(".");
    Periodo = 0;
    TotPeri = 0;
    Letture = 0;
    MediaPeriodo = 0;
    MediaCapacitance = 0;
    TotCapacitance = 0;
    FreqMedia = 0;
    TotFreq = 0;
  }
  Delay(100);
}
void Delay(unsigned long timeToWait) {
  unsigned long adesso = millis();
  while ((millis() - adesso) < timeToWait)
    ;
}


Come potete vedere utilizzo il GPIO D4 come input di lettura, ho notato che questo pin ha una risposta più precisa negli interrupt, forse perchè è settato già con una priorità alta rispetto ad altri pin, in quanto questo pin è possibile utilizzarlo come touch (ma anche D2 ad esempio).

Lo sketch attende che gli siano arrivati 2 segnali alti e stabilisce così il periodo corrispondente in uS e la conseguente frequenza risultante.

Vengono visualizzati i singoli risultati del campionamento oppure ogni 5 letture viene visualizzata la media dei risultati.

Come potete confrontare, rispetto all’immagine dell’oscilloscopio, i dati corrispondono, ovvero:

95.4546KHz Periodo 10.50 uS Capacità 11.712pF con codensatore 10pF ceramico

Se volete aumentare la precisione per i piccoli condensatori dovrete aumentare la resistenza.

Alla prossima…e buon divertimento

2 Likes

Ora passiamo alla parte che concerne la lettura degli induttori perchè alla fine presenterò un PCB che racchiuderà entrambi i circuiti selezionabili tramite swich, ovvero lettura condensatori+induttori e visualizzazione risultati su app del telefono via BT.
La maggioranza dei tester di fascia media-economica non hanno una lettura degli induttori e siccome alcuni non hanno neanche quella dei condensatori, ho riunito entrambe le funzioni.
Ho provato vari esempi presenti sul web con risultati deludenti in termini di precisione.
Ho avuto modo di testare sia circuiti LC che Colpitt e alla fine ho utilizzato il Colpitt per generare una forma d'onda sinusoidale che cambia a seconda dell'induttore e dei condensatori collegati nel tank loop.
Il risultato è stato subito decente sin dall'inizio, ma ho dovuto testare varie configurazioni sia con trasistor che con integrati per trovare quella che si adattava meglio nella lettura sia di piccoli induttori che di grandi.
Nella foto quà sotto metto il risultato con il test di un induttore da 47.000 mF che ho anche utilizzato come base per la taratura delle 2 resistenze principali nel circuito (R1 e R2).

Come potete notare, i risultati su Esp32 sono identici a quelli visualizzati sull'oscilloscopio.
Per avere un duty cycle abbastanza lungo per ridurre errori di lettura anche con induttori relativamente piccoli, per essere letto in modo più preciso da ESP32 utilizzando gli interrupt, bisogna disporre di un condensatore NON ELETTROLITICO di circa 32-33 uF o maggiore (maggiore è il duty cycle e minore è la possibilità di errore).
Purtroppo queste misure non sono reperibili tra quelli in polietilene a strato metallico se non rare eccezzioni a prezzi esorbitanti, per cui ho creato 2 banchi con 4 condensatori da 8uF collegati in parallelo, facendo in modo che il totale misurato sia simile tra di loro.
Non servono condensatori con alte tensioni, perchè il circuito Colpitt a differenza di quello LC, non genera sbalzi di tensione/corrente da un input di 5V 0.5A se non al primo input sin che il circuito non si stabilizza.

In questa foto potete vedere 3 degli induttori esaminati, in particolare il primo (che era marcato 47mH+47mH) l'ho utilizzato per calibrare il circuito con le 2 resistenze R1-R2, e una volta calibrato, anche l'induttore che ho fatto per un metal detector (numero 3 nella foto) corrispondeva come valori a quelli riportati da un mio programma con cui l'ho costruito e che si basa su formule matematiche.
Il numero 2 nella foto è il più piccolo dei 3, non è marcato come valori, e col mio circuito mi dice che è 10.3406 uH
In basso potete vedere il banco condensatori di 32+32 uF che ho utilizzato nel circuito.
Come alimentazione ho utilizzato una batteria Lipo 12V 1A e il circuito con LT3045 di cui metto la foto quà sotto(potete effettuare una ricerca tramite foto su internet e decidere dove acquistarlo):

Il consumo è abbastanza elevato con questa modalità Colpitt, e quando la batteria scende sotto 11.5V(da 12.6 a piena carica) ho visto che le letture cambiano leggermente in basso, per cui questo circuito con LT3045 non sembra adattarsi al cambio di tensione, ma quando l'alimentazione è sufficiente, l'uscita è veramente priva di rumori, tanto che si potrebbe pensare di alimentarlo con un alimentatore 220V>12V se lo si utilizza in banco e verificare che l'uscita sia altrettanto pulita.
Passiamo ora ad esaminare lo sketch per ESP32, che deriva da quello utilizzato per la misura dei condensatori, ovvero la routine che utilizza gli interrupt (ho usato sempre il pin D4 che si dimostra + preciso rispetto ad altri pin non dedicati al touch) legge il tempo in microsecondi che passano tra la prima lettura del fronte di salita (che ricordo è tra 2.5V e 3.3V) e la seconda e quindi stabilisce il duty cycle e la conseguente frequenza del segnale di input con la formula Frequenza=1/duty Cycle e poi viene calcolata l'induttanza con la formula:
inductance = ((float)(pow(1.0 / (6.283185307 * freq), 2)) / capacitance) / 1000.0; // in milli Henry
di cui 6.283185307 è 2 volte pigreco, freq è la frequenza e capacitance è calcolato nella dichiarazione delle variabili a inizio sketch e deriva dal calcolo >>> (condensatore 1 * condensatore 2) / (condensatore 1 + condensatore 2)
I due banchi di condensatori vanno misurati scollegati dal circuito ed è fondamentale che la lettura della loro capacità in Farad sia il più precisa possibile
Siccome ho utilizzato dei condensatori pagati qulache euro per 50 pezzi, dopo averli sottoposti a carico nel circuito, li ho tolti e misurati una seconda volta, per questo è comodo averli inseriti in una base per integrati e non saldati al circuito (come vedete nella foto precedente).

La Calibrazione
I valori delle due resistenze (R1-R2) che ho riportato nel circuito LTSpice (140 Ohm e 57.24 Ohm) sono indicativi, perchè poi il tutto dipende dalle saldature, cavi e dal transistor utilizzato, per cui bisogna utilizzare delle resistenze multigiro da 1K per la prima(R1) e una multigiro da 100Ohm per la seconda(R2) e lavorando su di esse, il nostro ESP32 deve restituire un duty cycle simile a quello derivante dall'operazione matematica (6.283185307 * Sqrt(Henry * capacitance)) * 1000000.0
nella quale 6.283185307 è 2 volte il pigreco
moltiplicato la radice quadrata degli Henry del nostro induttore moltiplicato per capacitance che è stata calcolata a inizio sketch come ho detto prima sul valore dei 2 banchi di condensatori.
Nel file zippato ho incluso un programmino per calcolare in automatico il duty cycle, inserendo nei campi gialli(modificabili) i vostri valori dei banchi condensatori e il vostro induttore di cui conoscete il valore in micro henry, l'ho compilato in modalità .NET Framework,Version=v4.8.1 per cui funziona da Windows Vista in avanti.
Per cui, guardando il valore nel serial monitor di ESP32(la durata del ciclo) oppure con un oscilloscopio, potrete regolare R1 e R2 in modo che siano simili come micro secondi a quelli derivanti dall'operazione matematica.
La resistenza R3 è una multigiro da 10K, dovrebbe andare bene con valore attorno a 2K, ma se nel serial monitor vedrete un valore errato nel calcolo dell'induttanza, allora provate ad aumentarla o diminuirla, questo succede perchè magari l'uscita 1 del comparatore non è sufficiente per pilotare l'ingresso di ESP32.
Ricapitolando: per verificare la lunghezza del duty cycle è meglio utilizzare un'oscilloscopio collegato al pin3 del transistor (non collegare D4 dell'Esp32 a questo pin perchè la tensione è ben superiore ai 3.3V sopportati da ESP32), mentre, per verificare l'uscita del comparatore, potete verificare il risultato anche con Esp32, perchè a seconda come regolate R3, la corrente sarà sufficente per pilotare l'ingresso di Esp32 oppure no.
Lo sketch esegue 5 misurazioni e poi fà una media dei risultati, e come potete vedere dalla prima immagine, su 5 misurazioni i risultati sono identici per cui direi che è un buon risultato, mentre alcune piccole variazioni si possono osservare quando la tensione della batteria cala sotto 11.7V.

Prossimamente sviluppo il PCB che contiene sia il misuratore dei condensatori, sia questo per l'induttanza, selezionabili con un selettore, in modo che le sonde siano uniche per i 2 circuiti, e l'alimentazione andrà ad alimentare solo 1 dei due circuiti per risparmiare la batteria.

Nel file zippato troverete il file .ino per Esp32, il file per i calcoli, il file per LTSpice e le immagini.

Ciao

Articolo Induttore.zip (802.9 KB)

2 Likes

Ho pensato di inserire nel progetto anche la lettura delle resistenze utilizzando sia il ponte di Wheatstone sia un potenziometro digitale.Il ponte di Wheatstone è quello che utilizza 3 resistenze note per conoscere il valore della quarta che è incognita, ne trovate varie spiegazioni del suo funzionamento e formule su internet, ad esempio quà:

https://resources.altium.com/it/p/wheatstone-bridges

E quà sotto inserisco un'immagine del ponte di Wheatstone:

Nelle mie prove ho utilizzato dapprima 3 resistenze fisse misurate da 10K ( stò utilizzando solo resistenze multigiro che come già spiegato precedentemente sono meno sensibili alle variazioni della temperatura ambiente e della temperatura d'esercizio ), i risultati della misurazione della quarta resistenza incognita erano già buoni nella gamma intermedia di valori, però non erano precise nella gamma inferiore e superiore ( per fare un paragone possiamo classificarle come il range di valori restituiti dai multitester che vanno dai 40€ ai 60€ ).Utilizzando in R2 un potenziometro digitale ( X9C104 da 100K ) mi permette di misurare decentemente resistenze che partono da 30 Ohm sino a 5M Ohm, mentre fuori da questo campo avremo valori meno attendibili.Nel mio caso, ho settato il potenziometro digitale per prendere come resistenza di max range 103K con l'istruzione X9C10X pot(103260); nella libreria di Rob Tillaart GitHub - RobTillaart/X9C10X: Arduino Library for X9C10X series digital potentiometer. ma i valori della resistenza selezionata non corrispondono a quelli effettivamente misurati nel circuito in funzione, quindi ho utilizzato un array di valori effettivamente misurati in 8 posizioni ( l'integrato può restituirci 100 valori di resistenza tra 0 e 99 )

//POSIZIONE ARRAY    0     1         2             3            4              5               6              7             8

//POSIZIONE X9C10X         0         1             5            10            20             40            70          99

static float resistenzaTester[] = { 0, 40.34, 1084.56, 5252.60, 10479.44, 20890.62, 41756.22, 72900.22, 102968.22 };  // with X9C10X pot(103260);

Per misurare questi valori serve un discreto multitester utilizzando il programma di test che includo nel file .zip

Se volete utilizzare un array di dimensioni maggiori potrete avere una maggiore precisione nei valori intermedi di misurazione, ma io l'ho lasciato con soli 8 settaggi perchè, non conoscendo la resistenza incognita, faccio un rapido scan e poi stabilito il miglior valore che si avvicina di più allo ZERO, eseguo un'altro scan più preciso.Tenete presente che comunque, anche settato a 0, il chip X9C104 ci restituisce 33/40 Ohm come minimo, per cui sotto questo valore i risultati sono meno attendibili nella resistenza incognita.
X9C104 in formato DIP-8 l'ho trovato quà:
https://it.aliexpress.com/item/1005009336948864.html

Il circuito è alimentato a 5V ( mi raccomando un'alimentazione stabile e filtrata ) e vengono lette le tensioni su V0 e V1 non con ESP32(10bit) ma con l'integrato ADS1115 (16bit) e 4 ingressi.Un ingresso viene utilizzato per leggere la tensione in arrivo dall'alimentazione ( che concorre nella formula finale di calcolo ) e altri 2 ingressi per leggere i valori di V0 e V1.
Come suggerito nel datasheet TI
10.2 Power-Supply Decoupling
Good power-supply decoupling is important to achieve optimum performance. VDD must be decoupled with at least a 0.1µF capacitor
Ho provato ad aggiungerlo (e c'è nello schema) ma non ho visto cambiamenti di stabilità nelle letture ( utilizzando il modulo LT3045 sull'alimentazione )

Da notare che X9C104 viene utilizzato come resistenza variabile e non come potenziometro, ovvero con il pin RW collegato a massa.

Prossimamente pubbliccherò il PCB che raggruppa misura Condensatori, Resistenze e Induttori
In allegato i 2 sketch per ESP32, quello di test di R2 e quello finale

01Tester.zip (167.4 KB)