Magnetometro (gaussmetro) AC/DC

Ciao a tutti
Ciao, @Etemenanki

Vorrei realizzare un magnetometro (strumento di misura) per flussi variabili. Ho trovato questo:

The key feature of the code is that the magnetic field is measured 2000 times in a row. This takes about 0.2-0.3 seconds. By keeping track of the sum and of the squared sum of the measurements, it is possible to calculate both the mean and the standard deviation, which are reported as DC and AC. By averaging a large number of measurements, the precision increases, theoretically by sqrt(2000)~45. So with a 10-bit ADC, we can reach the precision of a 15-bit ADC! It makes a big difference: 1 ADC count is 5mV, which is ~0.3mT. Thanks to the averaging, we improve the precision of from 0.3mT to 0.01mT.

As a bonus, we also get the standard deviation, so fluctuating fields are identified as such. A field fluctuating at 50Hz does ~10 full cycles during the measurement time, so its AC value can be well measured.

// Sketch for Arduino magnetometer
// Measures strength of magnetic field with SS49E hall sensor on A0
// Displays values on I2C SSD1306 128x64 0.96" monochrome OLED display
// Connect SCK to pin A5 and SDA to pin A4
// Connect null-button between GND and A1 for setting output to zero 

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

Adafruit_SSD1306 display(128,64,&Wire,-1);

#define HALL_PIN A0
#define NULL_PIN A1

#define nmeas 2000      // stay below 2048 to avoid overflow  
#define cal 0.349*0.582 // calibration constant in mT per ADC count. nominal (5.0/1024)/0.014=0.349:               

float offset=511.0; // nominal 511: halfway 0 and 1024

void setup() {
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.setTextColor(WHITE);
  display.clearDisplay();
  pinMode(HALL_PIN,INPUT); //pullup increases current and reduces noise
  pinMode(NULL_PIN,INPUT_PULLUP); //avoids need for external pullup resistor
}

void loop() {

  //perform the measurement. AnalogRead() takes ~100mus.
  long unsigned int sum=0;
  long unsigned int sumsq=0;
  for (int imeas=0; imeas<nmeas; imeas++){
    long int val= analogRead(A0);
    sum=sum+val;
    sumsq=sumsq+val*val;
  }

  //recalculate offset if null-button is pressed
  if(digitalRead(NULL_PIN)==0)offset=float(sum)/nmeas;
  
  //calculate DC and AC field strength. AC corresponds to RMS of the fluctuations
  float B_DC=(float(sum)/nmeas-offset)*cal;
  float B_ACsq=(float(sumsq)/nmeas-pow(float(sum)/nmeas,2))*pow(cal,2);
  float B_AC=0.0;
  if (B_ACsq>pow(0.005,2)) B_AC=pow(B_ACsq,0.5);
  pow((float(sumsq)/nmeas-pow(float(sum)/nmeas,2)),0.5)*cal;
  
  char txt[10]; //text buffer needed to print formatted float  

  // print DC value
  display.clearDisplay();
  display.setTextSize(1);
  display.setCursor(0,2);
  display.print("DC");
  display.setCursor(0,18);
  display.print("mT");
  display.setTextSize(3);
  display.setCursor(0,2);
  dtostrf(B_DC,7,2,txt);
  display.print(txt);

  // print AC value
  display.setCursor(0,34);
  display.setTextSize(1);
  display.print("AC"); 
  display.setCursor(0,50);
  display.print("mT");
  display.setTextSize(3);
  display.setCursor(0,34);
  dtostrf(B_AC,7,2,txt);
  display.print(txt);

  display.display();

}

Usa il classico SS49E/SS495. Che ne dite? C'è di meglio a buon prezzo?
Ricordo che tanti anni fa ne provai uno di Nuova Elettronica (l'elettronica ancora si acquistava nei negozi e io solitamente andavo da un suo rivenditore), probabilmente un UGN3503, e notai che era molto sensibile alle sollecitazioni meccaniche, alla flessione dei terminali...
Vedo che ci sono anche gli A1301/A1302...
Meglio ancora sarebbe un I2C, purché sia abbastanza veloce per campionare i 50Hz.

Grazie
Gianluca

tipo TMAG5273 ad esempio ?
O forse A1454 ... non so pero' la velocita' se ti va bene, credo dipenda anche dalla risoluzione e dal campo che vuoi misurarci.

Per quei sensori l'uscita dipende anche dall'allineamento dell'asse del chip con il campo, e' possibile che se non e' allineato correttamente dia risultati sballati, forse servirebbe un 3 assi e poi integrare le letture, come ad esempio ALS31313 o roba simile ... pero' un po tutti quei sensori non sono molto sensibili ai campi a bassa intensita', mi sa.

Se devi misurare campi magnetici molto bassi, la soluzione migliore potrebbe essere un sensore fluxgate, si possono anche autocostruire con un po di pazienza, ma poi tararli per avere una scala di lettura coerente diventa un problema, altrimenti ci sono anche commerciali.

EDIT: a 3 assi c'e' anche questo che sembra piu programmabile https://www.mouser.it/datasheet/2/734/MLX90393_Datasheet_Melexis-953267.pdf ... rimane comunque il fatto che se sono sensibili, sono anche influenzati in parte dal campo magnetico terrestre, e le misure dovrebbero essere fatte in ambienti schermati in quei casi.

Grazie, Etemenanki.

Soffro da oltre tre anni di borsite retrocalcaneare al piede sinistro e, non avendo tratto un significativo giovamento da infiltrazioni e cure con Medrol e Arcoxia e pagando oltre 100€ ogni visita, ho voluto provare qualcosa di diverso: sto provando la magnetoterapia con un elettromagnete su un nucleo a "E", senza la "I" di chiusura magnetica, recuperato anni fa da una pompa dell'aria per acquari. Vorrei, quindi, misurare il campo magnetico alternato prodotto che, secondo misure e calcoli fatti con la collaborazione di una persona di un gruppo di misure elettroniche su Facebook, dovrebbe essere dell'ordine di 50mT, cioè 500G. Questo è l'ordine di grandezza che al momento vorrei misurare (in AC!); se, poi, è abbastanza sensibile anche ai bassi livelli, potrebbe essere utile in futuro per altre cose, ma al momento non è indispensabile.

Forse l'I2C complica parecchio, perché a me basterebbe qualcosa come quello che si vede nel sito all'inizio di questa discussione, cioè un solo asse con la lettura della componente alternata e, per altri usi possibili, anche della componente continua. Anche il funzionamento a 5V del sensore mi dà meno pensieri per qualcosa su cui non voglio perdere tempo a sperimentare tanto per il gusto di farlo. Sì, il sensore I2C non ha bisogno di taratura, se ci si accontenta dell'accuratezza dichiarata.

Se proprio mi interessassero livelli più bassi di quelli che il convertitore a 10 bit riesce a rilevare, sensore permettendo potrei anche aggiungere un operazionale che amplifica il segnale e lo manda a un altro ingresso analogico per una portata più sensibile.

Come lo vedi, quindi, un sensore analogico? Quale mi consigli?

Spero che questo ti aiuti!

Grazie, Victor. Ho dato un'occhiata. Purtroppo Google non riesce a tradurre correttamente tutta la pagina in italiano, però credo che non mi farebbe qualcosa di più di ciò che sto tentando adesso, che sembra funzionare... I dolori, però, non sono costanti, ma molto variabili, perciò un miglioramento della durata di qualche giorno può non essere significativo.

Gianluca! Quello che stai facendo ora è stato fatto 30 anni fa, il dispositivo è stato prodotto dall'industria e fornito alle sale di fisioterapia.
Le misurazioni oggettive hanno mostrato un miglioramento del + 5%, e questo è a livello di errore. La frequenza era di 5000Hz.
La tecnica di Mishin si basa sulla ricerca di Nikola Tesla. La bobina è un dipolo a spirale.

PS Prendi nota però, forse hai qualcuno che ha espresso questo argomento

Misurare il campo effettivo su un "coso" del genere non e' facilissimo, perche' tende comunque a richiudersi sul nucleo, per cui in punti diversi avrai intensita' che cambiano, anche parecchio (per ora tralasciamo le frequenze migliori ed altre cose simili), anche se il nucleo ad E tendera' probabilmente a concentrare il flusso in corrispondenza del polo centrale (ma comunque le linee curveranno parecchio verso i due laterali, sarebbe probabilmente andato meglio un nucleo centrale dritto, senza la E)

Il problema e' che curvando le linee cosi tanto, sara' difficile stabilire quanta intensita' hai effettivamente all'esterno del nucleo (se appoggi il sensore di piatto sulla faccia centrale avrai probabilmente la massima lettura, che pero' dovrebbe decrescere velocemente con la distanza, ed anche spostandoti rispetto all'asse centrale della faccia o inclinando il sensore avrai parecchia deviazione)

L'apparecchietto che hai postato potrebbe essere un buon punto di inizio, con qualche modifica ed un paio di "trucchi" potrebbe diventare anche migliore (a quanto ho capito, a te' non serve rilevare la polarita' del campo, ma solo la sua intensita', che sia AC o DC), per cui a livello di test iniziali potresti provare un solo sensore tipo i tuoi, ma passando prima la sua uscita attraverso un raddrizzatore di precisione a doppia semionda (un LM358 o un TL072 e pochi altri componenti), in modo da avere una sola polarita' in uscita indifferentemente dal senso del campo ... in seguito si potrebbe creare una sonda a 3 assi e compensazione termica semplicemente incollando insieme 3 sensori dei tuoi ognuno a 90 gradi rispetto agli altri, il piu vicini possibile, (e se si volesse pure la compensazione termica basterebbe incollarci in mezzo una NTC, ma quello si potra' prevedere se necessario piu avanti), ogni sensore avra' il suo raddrizzatore e poi la MCU potra' fare la somma dei 3 vettori e stabilire l'intensita' del campo in modo indipendente da come e' ruotata rispetto all'avvolgimento (ma appunto, piu avanti, se vorrai, per i primi test e per vedere come si comporta dovrebbe bastare un sensore)

EDIT: no, aspetta, lo zero in quei sensori corrisponde alla meta' dell'alimentazione, quindi non servono neppure i raddrizzatori di precisione (ma bisogna fare tutto il calcolo via software)

Infatti in quel programma fa il calcolo!

Purtroppo il datasheet che ho trovato del 49E e' piuttosto "scarno" e non fornisce curve di variazione in funzione dell'inclinazione dell'asse rispetto a quella del campo magnetico ... se la variazione fosse lineare, basterebbe semplicemente usare tre sensori a 90 gradi e sommare i tre valori per avere l'effettiva intensita' del campo nel punto del sensore indipendentemente dalla sua rotazione ... bisognerebbe averne alcuni e farci dei test, ma credo si possa fare.

Si, loro fanno il calcolo compensando per l'imprecisione del singolo sensore, io mi riferivo alla possibile correzione degli scostamenti di 3 sensori per avere una lettura senza doversi preoccupare dell'orientamento del sensore (ma se e' lineare, non serve).

Bisognerebbe per prima cosa rilevare il coefficente di ogni singolo sensore (dove lui usa il solenoide lungo), poi fare le correzioni per ogni singola lettura (leggendo i sensori a rotazione), e poi sommare i tre valori ... resta solo da vedere a quale velocita' al massimo si potrebbe effettuare le letture con 3 sensori rispetto al circuito del post con uno solo.

Ho acquistato 10 sensori 49e dall'Italia per 5,29€ da Shopdoro, da cui avevo già fatto acquisti. Vediamo...

Ecco un brevissimo video del magnetometro finito:

1 Like

Questa è la versione finale del programma (1.0b), con un #define che permette di usare sia un display OLED 1306 che un LCD 1602 I2C:

// Sketch for Arduino magnetometer
// Measures strength of magnetic field with SS49E hall sensor

// #define LCD /* Display LCD 1602, altrimenti OLED SSD1306 */
// E' adatto anche per display OLED  con le 16 righe superiori di colore diverso (p. es. gialle), perché lì scrive nome
// e versione, mentre tutto il resto va nella parte inferiore blu.

const char *percorso=__FILE__; // Dalla macro __FILE__  prende il percorso del file in uso. Questa parte  di programma,
      char ver[12];            // però, si trova  nel file/cartella  dell'IDE c_setup, in cui non è scritta la versione
                               // del programma. La versione è scritta nel file principale, che ha lo stesso nome della
                               // cartella che contiene tutti i file del programma. Questa  riga, quindi, non può stare
                               // nel setup.
#include <EEPROM.h>
#ifdef LCD
  #include <PCF8574_HD44780_I2C.h>
  // LCD 16x2 con indirizzo I2C 0x27:
  PCF8574_HD44780_I2C lcd(0x27,16,2);
#else // OLED

  #include <SPI.h>
  #include <Wire.h>
  #include <Adafruit_SSD1306.h> // v2.5.7 Ok
  #include <Adafruit_GFX.h> // v1.0.6 Ok
  Adafruit_SSD1306 display(128,64,&Wire,-1);
  
  char txtDC_old[10]="         "; // Testo DC precedente per cancellarlo. E' inizializzata con 9 spazi.
  char txtAC_old[10]="         "; // Testo AC precedente per cancellarlo. E' inizializzata con 9 spazi.
#endif
uint32_t sum=0;
uint32_t sumsq=0;
#define nmeas 2000      // stay below 2048 to avoid overflow
uint16_t nmeas_div_2 = nmeas/2;
#define cal_int 0.349*0.582 // calibration constant in mT per ADC count. nominal (5.0/1024)/0.014=0.349:               
#define cal_est 0.349*0.582
float cal=cal_int;
#define per_let_bat 10000 // Periodo di lettura della tensione della batteria in ms: 10 secondi.
bool puls_prec=HIGH;
uint32_t t_press=0; // millis() alla pressione del pulsante.
float zero = 511.0; // nominal 511: halfway 0 and 1023
float temp;
bool zero_fatto=LOW;
bool G_mT=LOW; // HIGH=Gauss; LOW=mT.
bool sonda_int_est=LOW; // HIGH=sonda interna; LOW=sonda esterna.
bool sonda_int_est_prec=HIGH; // HIGH=sonda interna; LOW=sonda esterna.

uint32_t t_int_est=0; // Per la verfica della presenza della sonda esterna.
uint16_t vbat; // Tensione della batteria: lettura di A3: 1023=10V.
uint32_t t_bat=per_let_bat; // Per la lettura periodica della tensione della batteria, impostato per una prima lettura immediata.
uint32_t t_interm_bat; // Per l'intermittenza della batteria scarica.
bool corpo_bat=true; // Corpo della batteria visibile/non visibile per l'intermittenza.


void Bip()  {tone(5,2000,100);}
void Biip() {tone(5,2000,400);}
void PiRiPo() {uint8_t dur=180; tone(5,2000,dur); delay(dur); tone(5,1688,dur); delay(dur); tone(5,1333,dur);}
                               // Do-La-Fa secondo la scala pitagorica.
void batteria()
{
analogReference(INTERNAL);
analogRead(A3); delay(10); vbat=analogRead(A3); // Serve un delay di almeno 7ms.
if(vbat>=860) display.fillRect(111,2,3,7,WHITE);  // Barretta 4.
else         display.fillRect(111,2,3,7,BLACK);
if(vbat>=800) display.fillRect(115,2,3,7,WHITE);  // Barretta 3.
else         display.fillRect(115,2,3,7,BLACK);
if(vbat>=740) display.fillRect(119,2,3,7,WHITE);  // Barretta 2.
else         display.fillRect(119,2,3,7,BLACK);
if(vbat>=680) display.fillRect(123,2,3,7,WHITE);  // Barretta 1.
else         display.fillRect(123,2,3,7,BLACK);
// display.display(); // Non serve qui solo perché lo incontrerà nel loop()!
analogReference(DEFAULT);
analogRead(A3); delay(10); 
}

void test_puls()
{
if(digitalRead(6)==HIGH) // Se il pulsante non è premuto:
  {
  if(puls_prec==LOW && millis()-t_press<1000) // E' stato premuto brevemente:
    {
    G_mT=!G_mT;
    mask_H_Gauss_L_mT();
    EEPROM.update(8,G_mT); // Memorizza lo stato G/mT.
    }
  t_press=millis(); // Prende continuamente il tempo.
  puls_prec=HIGH;
  zero_fatto=LOW;
  }
else // Se è premuto:
  {
  if(puls_prec==HIGH) // Se è appena stato premuto:
    {
    Bip();
    puls_prec=LOW;
    }
  }
    
if(millis()-t_press>=1000 && !zero_fatto) // E' premuto a lungo:
  {
  zero=(float)sum/nmeas;
  zero_fatto=HIGH;
  if(sonda_int_est) EEPROM.put(0, zero); // Memorizza lo zero della sonda interna.
  else EEPROM.put(4, zero); // Memorizza lo zero della sonda esterna.
  Biip();
  }
}


void mask_H_Gauss_L_mT()
{
#ifdef LCD
  lcd.setCursor(11,0);      if(G_mT) lcd.print("G "); else lcd.print("mT");
  lcd.setCursor(11,1);      if(G_mT) lcd.print("G "); else lcd.print("mT");
#else // OLED
  display.setTextSize(1);
  
  display.setCursor(0,29); // DC.
  if(G_mT) // DC - G.
    {
    display.setTextColor(BLACK); display.print("mT");// Cancella.
    display.setCursor(0,29);
    display.setTextColor(WHITE); display.print('G'); // Scrive.
    } 
  else // DC - mT.
    {
    display.setTextColor(BLACK); display.print('G'); // Cancella.
    display.setCursor(0,29);
    display.setTextColor(WHITE); display.print("mT");// Scrive.
    }
    
  display.setCursor(0,56); // AC.
  if(G_mT) // AC - G.
    {
    display.setTextColor(BLACK); display.print("mT");// Cancella.
    display.setCursor(0,56);
    display.setTextColor(WHITE); display.print('G'); // Scrive.
    } 
  else // AC - mT.
    {
    display.setTextColor(BLACK); display.print('G'); // Cancella.
    display.setCursor(0,56);
    display.setTextColor(WHITE); display.print("mT");// Scrive.
    }
#endif
}


void setup()
{
char *ver_ext=strrchr(percorso,'v'); // Va a cercare l'ultima 'v' nel percorso.
byte n_car=strlen(ver_ext)-4; // Calcola la lunghezza escludendo .ino.
strncpy(ver, ver_ext, n_car); // Copia i primi n_car caratteri da ver_ext a ver.
ver[n_car]='\0'; // Mette il terminatore in fondo.

pinMode(A0,INPUT); // Segnale del sensore Hall interno.
pinMode(A1,INPUT); // Segnale del sensore Hall esterno.
pinMode(5, OUTPUT); // Uscita per cicalino passivo.
pinMode(6,INPUT_PULLUP); // Pulsante a GND G/mT e azzeramento.
pinMode(7,INPUT_PULLUP); // Ingresso del consenso a GND della sonda esterna.
pinMode(8, OUTPUT); // Alimentazione per sonda esterna.
analogReference(DEFAULT);

#ifdef LCD
  lcd.init();
  lcd.backlight(); // Backlight ON
  lcd.setCursor(2,0); lcd.print("MAGNETOMETRO");
  lcd.setCursor((16-strlen(ver))/2, 1); lcd.print(ver);
  delay(1500);
  lcd.clear();
  lcd.setCursor(1,0); lcd.print("DC");
  lcd.setCursor(1,1); lcd.print("AC");
  if(digitalRead(7))
    {
    EEPROM.get(0, temp); // float: 0,1,2,3. Legge lo zero della sonda interna.
    if(temp>-1000 && temp<1000) zero=temp;
    }
  else
    {
    EEPROM.get(4, temp); // float: 4,5,6,7. Legge lo zero della sonda esterna.
    if(temp>-200 && temp<200) zero=temp;
    }
  G_mT=EEPROM.read(8); // HIGH=Gauss.
  mask_H_Gauss_L_mT();
#else // OLED
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // 0x3C: Indirizzo I2C.
  // display.clearDisplay(); // Cancella immediatamente il logo automatico Adafruit :)
  display.setTextColor(WHITE);
  display.setTextSize(1);
  display.setCursor(28,0); display.print("MAGNETOMETRO");
  display.setCursor((106-15-6*strlen(ver))/2 +20, 8); display.print(ver);

  display.setCursor(0,16); display.print("Press.breve: G/mT RMS");
  display.setCursor(80,28); display.print(">>");
  display.setCursor(0,40); display.print("Press.lunga: Zero DC");
  display.display();
  display.startscrollright(3,4); // Dalla terza alla quarta riga, per come le intende la
                                 // libreria, per spostare tutto il simbolo ">>".
  delay(2500);
  while(!digitalRead(6)); // Premendo il pulsante durante lo scorrimento di ">>", si prolunga
  display.stopscroll();   // la visualizzazione delle informazioni iniziali.
  display.setTextColor(BLACK);
  display.setCursor(80,28); display.print(">>"); // Cancella >> che, non so perché, riappare
  display.setTextColor(WHITE);                   // nella posizione iniziale dello scroll!
  for(uint8_t x=0; x<17; x+=4) {display.fillRect(x,16,4,48,BLACK); display.display();}
  if(digitalRead(7))
    {
    display.setCursor(0,0); display.print("Int"); sonda_int_est_prec=LOW;
    }
  else
    {
    display.setCursor(0,8); display.print("Est"); sonda_int_est_prec=HIGH;
    }
  display.setCursor(0,16); display.print("DC");
  display.setCursor(0,43); display.print("AC");
  if(digitalRead(7))
    {
    EEPROM.get(0, temp); // float: 0,1,2,3. Legge lo zero della sonda interna.
    if(temp>-1000 && temp<1000) zero=temp;
    }
  else
    {
    EEPROM.get(4, temp); // float: 4,5,6,7. Legge lo zero della sonda esterna.
    if(temp>-200 && temp<200) zero=temp;
    }
  G_mT=EEPROM.read(8); // HIGH=Gauss.
  mask_H_Gauss_L_mT();
  for(uint8_t x=17; x<125; x+=4) {display.fillRect(x,16,4,48,BLACK); display.display();}
  display.drawRect(106,3,3,5,WHITE);   // Contatto +.
  display.drawRect(109,0,19,11,WHITE); // Corpo.
  display.display();
#endif
}


void loop()
{
if(millis()/1000==0 || millis()-t_bat>per_let_bat)
  {
  t_bat=millis();
  batteria();
  }
if(vbat<680)
  {
  if(millis()-t_interm_bat>500)
    {
    t_interm_bat=millis();
    if(corpo_bat)
      {
      display.drawRect(109,0,19,11,BLACK); // Cancella il corpo.
      display.drawRect(106,3,3,5,BLACK);   // Cancella il contatto +.
      }
    else
      {
      display.drawRect(109,0,19,11,WHITE); // Disegna il corpo.
      display.drawRect(106,3,3,5,WHITE);   // Disegna il contatto +.
      }
    corpo_bat=!corpo_bat;
    }
  }

if(millis()-t_int_est>=500) // Ogni mezzo secondo controlla se è stata inserita la sonda esterna.
  {
  t_int_est=millis();
  sonda_int_est=digitalRead(7);
  if(sonda_int_est!=sonda_int_est_prec)
    {
    display.setTextSize(1);
    if(sonda_int_est) // Sonda interna.
      {
      digitalWrite(8, HIGH); // Alimenta la sonda interna.
      EEPROM.get(0, temp); // float: 0,1,2,3. Legge lo zero della sonda interna.
      if(temp>-1000 && temp<1000) zero=temp;
      cal=cal_int; // Carica la calibrazione della sonda interna.
      display.setCursor(0,8); display.setTextColor(BLACK); display.print("Est"); // Cancella.
      display.setCursor(0,0); display.setTextColor(WHITE); display.print("Int"); // Scrive.
      display.display();
      Bip();
      }
    else // Sonda esterna.
      { 
      digitalWrite(8, LOW); // Toglie tensione alla sonda interna per risparmiare energia.
      EEPROM.get(4, temp); // float: 4,5,6,7. Legge lo zero della sonda esterna.
      if(temp>-1000 && temp<1000) zero=temp;
      cal=cal_est; // Carica la calibrazione della sonda esterna.
      display.setCursor(0,0); display.setTextColor(BLACK); display.print("Int"); // Cancella.
      display.setCursor(0,8); display.setTextColor(WHITE); display.print("Est"); // Scrive.
      display.display();
      PiRiPo();
      }
    delay(200);
    }
  sonda_int_est_prec=sonda_int_est;
  }
  
sum=0;
sumsq=0;
for(uint16_t n=0; n<nmeas; n++) // Impiega 224ms (112us a lettura), pari a 11 cicli a 50Hz.
  {
  uint32_t val;
  if(sonda_int_est) val=analogRead(A0); // Sonda interna.
  else val=analogRead(A1); // Sonda esterna.
  sum+=val;
  sumsq+=val*val;
  }
  
test_puls();

// Calculate DC and AC field strength. AC corresponds to RMS of the fluctuations
float B_DC = ((float)sum/nmeas - zero)*cal;
float B_ACsq = ((float)sumsq/nmeas - pow((float)sum/nmeas, 2))*pow(cal, 2);
float B_AC = 0.0;
if (B_ACsq>0.000025) B_AC=pow(B_ACsq, 0.5);
B_AC=pow(B_ACsq, 0.5);

char txt[10]; // Text buffer needed to print formatted float  
#ifdef LCD
  lcd.setCursor( 3, 0);
#else
  display.setTextSize(3);
#endif

if(!G_mT) // DC in mT.
  {
  dtostrf(B_DC,7,2,txt);
  }
else // DC in G.
  {
  dtostrf(B_DC*10,7,1,txt);
  }

#ifdef LCD
  lcd.print(txt);
#else // OLED
  display.setCursor(0,16);
  display.setTextColor(BLACK); display.print(txtDC_old); // Cancella.
  display.setCursor(0,16);
  display.setTextColor(WHITE); display.print(txt); // Scrive.
#endif

#ifdef LCD
  lcd.setCursor( 3, 1);
#else // OLED
  strcpy(txtDC_old, txt);
#endif

if(!G_mT) // AC in mT.
  {
  dtostrf(B_AC,7,2,txt);
  }
else // AC in G.
  {
  dtostrf(B_AC*10,7,1,txt);
  }
  
#ifdef LCD
  lcd.print(txt);
#else // OLED
  display.setCursor(0,43);
  display.setTextColor(BLACK); display.print(txtAC_old); // Cancella.
  display.setCursor(0,43);
  display.setTextColor(WHITE); display.print(txt); // Scrive.
  display.display();
  strcpy(txtAC_old, txt);
#endif
}


/* 
Fis.I/OPorta
 1   - RS
 2   0 PD0
 3   1 PD1
 4   2 PD2 
 5   3 PD3 
 6   4 PD4 Consenso sonda esterna (a GND).
--
11   5 PD5 Uscita per cicalino passivo.
12   6 PD6 Pulsante di azzeramento DC (con INPUT_PULLUP) verso GND con 100nF in parallelo.
13   7 PD7
14   8 PB0 Alimentazione per sonda interna.
----------
15   9 PB1 
16  10 PB2    
17  11 PB3    
18  12 PB4
19  13 PB5
--
23  14 PC0 A0 SS49E int.
24  15 PC1 A1 SS49E est.
25  16 PC2 A2 
26  17 PC3 A3       Vbat/2
27  18 PC4 A4 SDA | Al display
28  19 PC5 A5 SCL |  SSD1306.
29  20     A6 (solo in Arduino Nano)
          
Fis
 9     PB6 XTAL 16MHz In : compensatore a GND (ho messo 22+10pF).
10     PB7 XTAL 16MHz Out: 22pF a GND.
21    ARef
7,20: Vcc
8,22: GND

EEPROM
0-3: float zero_int.
4-7: float zero_est.
  8: bool Gauss/mT.
*/


/*
v0.4    16/3/23 Introduco la commutazione milliTesla/Gauss con pressione breve e zero con pressione lunga.
                Introduco la memorizzazione Gauss/milliTesla.
v0.5    16/3/23 Divido il programma in schede (file).
v0.6    20/3/23 Introduco degli #ifdef LCD per selezionare un display LCD 1602 oppure un OLED SSD1306.
                Aggiungo la scheda (file) a_funzioni.
v0.7    21/3/23 Ho corretto la funzione mask_H_Gauss_L_mT(), dove avevo scambiato i G con i mT.
                Ho corretto le scritte sul display OLED.
                Metto  il controllo del pulsante  in una funzione e lo richiamo anche  a metà misura: in questo
              sto modo, viene controllato ogni 112ms anziché ogni 224ms e risponde anche a brevi pressioni.
v0.8    22/3/23 Sposto tutte le scritte un po' più in basso per farle entrare  nell'area blu dei display che ho
              acquistato da Amazon, che hanno le prime 16 righe gialle e le successive 48 blu.
                Aggiungo nell'area gialla il nome e la versione (con centramento automatico).
                Aggiungo nome e versione anche nell'LCD.
v0.9    26/3/23 Introduco l'icona della batteria e la lettura della batteria.
                Introduco le due sonde: interna ed esterna, ognuna con il proprio zero.
v0.10   29/3/23 Aggiungo un #if per avere la lettura del pulsante ogni 112ms anche se uso un clock a 8MHz.

v0.11    3/4/23 Riparto dalla v0.9, avendo deciso di andare a 16MHz con il quarzo.
                Metto la commutazione  Gauss/milliTesla  tenendo premuto il pulsante all'accensione. La cancel-
              lazione delle scritte sull'OLED, quindi, non è più necessaria.
                La pressione breve commuterà AC RMS, min, MAX. 
v0.12    3/4/23 Comincio a introdurre le misure RMS, min, MAX.
                Scrivo Int e Est sulla stessa riga, mentre prima Int appariva sopra e Est appariva sotto.
                Adesso, nella riga gialla inferiore appare un'unica scritta Gauss o mT.
v0.14    3/4/23 Ho completato RMS, min e MAX.
                Ho spostato il cicalino su PD5 (pin 11) e modificato il programma per uno attivo a 5V.
                Mi sa che min e MAX sono proprio inutili!
                
v0.15    3/4/23 Ritorno alla v0.9, 16MHz con il quarzo.
                Correggo la posizione verticale di G e mT da 55 a 56.
                Correggo anche la posizione verticale di AC DA 42 A 43.
v0.16    4/4/23 Aggiungo il Bip breve per la pressione del pulsante, che nella v0.9 era assente.
                Sposto il cicalino passivo sull'I/O 5, PD5 (pin 11) per liberare i pin 17, 18 e 19 dell'ICSP.
                Sposto l'alim. per la sonda interna sull'I/O 8, PB0 (pin 14) per liberare i pin dell'ICSP.
                Aggiungo un delay(200) dopo la commutazione della sonda (ma forse è inutile).
v0.17    5/4/23 Nel setup() metto  sonda_int_est_prec  uguale all'opposto delvalore in EEPROM  per fare in modo
              che al controllo  nel loop (riga 13) sia diversa dal valore corrente e faccia l'impostazione del-
              la sonda interna/esterna.
v0.18    6/4/23 Con la tensione di riferimento DEFAULT, pari alla tensione di alimentazione del 328P (5V nom.),
              l'ultima barretta della batteria rimaneva  sempre accesa, perché con la batteria a meno di 6V co-
              minciava a calare anche  la tensione dei 5V! Perciò  sono  passato alla tensione di riferimento a
              1,1V nom. (ho misurato 1,0774V), facendo il partitore con 82k (100k//470k) e 10k.
                Poiché, a causa del 100nF su  A3, la tensione su  A3 impiega qualche istante  a salire, a volte
              l'indicatore della batteria restava indietro di una barretta e non poteva più risalire, perché
              non avevo inserito le righe per la riscrittura delle barrette. Ore le ho inserite con degli else.
                Nell'if della temporizzazione della lettura della tensione della batteria ho aggiunto
              millis()/1000==0 per avere un aggiornamento continuo finché millis() non arriva a 1000.
v0.19    6/4/23 Ho aggiunto l'intermittenza del corpo della batteria quando tutte le barrette sono spente.
                Nella v0.18  non mi ero reso conto  che per i sensori mi serve il riferimento a 5V! Perciò, per
              leggere la tensione della batteria devo commutare il riferimento da DEFAULT (5V) a INTERNAL
              (1,1V) e poi ricommmutare alla fine della funzione. NOTA: Già avevo messo un analogRead() a vuoto
              prima di quello effettivo, ma senza almeno 7ms di pausa dal primo NON FUNZIONA! Ho messo 10ms.
v0.20    8/4/23 Ho aggiunto le informazioni iniziali sul pulsante e le animazioni.
v0.21    9/4/23 All'accensione ho anticipato la scrittura di AC/DC e G/mT appena  passata la cancellazione, an-
              ziché dopo aver cancellato tutto.
v0.22   10/4/23 Aggiungo "RMS" nelle informazioni iniziali dopo "Press.breve: G/mT".
v0.23   12/4/23 Ho tolto all'inizio  la nota "Per ATmega328P con clock interno a 8 DIV8=1MHz", poiché lo faccio
              girare a 16MHz quarzato.
                Per eliminare il logo Adafruit all'accensione, eliminando anche l'ingombro dei dati del logo in
              memoria  e il  rallentamento  della programmazione, ho dovuto inserire  #define SSD1306_NO_SPLASH
              all'interno del file Adafruit_SSD1306.cpp della libreria, perché messo nel programma non funziona.
v0.24   13/4/23 Ho aggiunto Bip() e PiRiPo() all'inserimento  e al disinserimento della sonda esterna. Il suono
              viene riprodotto anche all'accensione, secondo la sonda attiva.
                Ho aggiunto  display.display(); nel rilevamento della sonda interna/esterna perché, altrimenti, 
              le scritte Int/Est venivano  aggiornate  con un attimo di ritardo, cioè solo quando veniva incon-
              trato un display.display();.
v1.0    15/4/23 Ora, premendo il pulsante durante lo scorrimento di ">>", si prolunga  la visualizzazione delle
              informazioni iniziali.
v1.0a   27/4/23 Per sicurezza, dopo le esperienze con il Fuse Rescue, porto ver a [12].
                Faccio una funzione per il rilevamento della pressione breve o lunga del pulsante. 
                Ora rilevo la pressione breve al rilascio del pulsante, quindi  non cambia più per un attimo le
              unità di misura, dovendole poi ripristinare.
                Tolgo il rilevamento della  pressione del pulsante  a metà ciclo di calcolo, perché ora, non so
              per quale ragione, a volte sbaglia lo zero e lo mette a 513. Forse, in realtà, ci sarebbe da chi-
              edersi perché prima funzionasse correttamente...
v1.0b   27/4/23 Metto il Bip immediato alla pressione del pulsante, senza attendere che venga lasciato.
*/


/*                                ISTRUZIONI
Premendo brevemente il pulsante, si cambia unità di misura da milliTesla a Gauss e viceversa;
tenendolo premuto per un secondo, si azzera la componente continua del campo (DC).

La componente alternata è RMS, detratto il campo magnetico continuo eventualmente presente.
*/

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.