Wattmetro audio da 100W, carico fittizio e misuratore di smorzamento basato su arduino.

Nota: lo strumento può essere usato con qualsiasi tipo di amplificatore purchè abbia il negativo degli altoparlanti riferito a massa, oppure con valvolari con entrambe le uscite flottanti senza riferimento a massa. Nel caso di uso con amplificatori che non abbiano il polo negativo degli altoparlanti riferito massa come certi valvolari con reazione catodica, OTL o certi amplificatori a stato solito non potranno essere misurati in modalità stereo e solo 1 canale per volta potrà essere collegato allo strumento, in ogni caso SB-LAB pubblica questo progetto senza nessuna garanzia e non si ritiene responsabile di eventuali danni di qualsiasi tipo derivati da esso.

Durante le vacanze di Agosto ho voluto sviluppare questo strumentino che ha la triplice funzione di carico resistivo stereo, wattmetro fino a 100watt e misuratore della Rout/Smorzamento di un’amplificatore ad esso connesso.

Avevo visto un’abbozzo del wattmetro arduino che potete trovare a questo indirizzo: https://andydoz.blogspot.com/2016/04/arduino-audio-wattmeter.html questo progetto è limitato alla misurazione massima di 10watt.

Sono poi venuto a conoscenza che un mio cliente aveva già sviluppato qualcosa di molto simile in grado di misurare fino a 100watt mono. Mi ha passato il suo schema e il codice sorgente che non è troppo dissimile da quello sopra. Ho quindi deciso di sviluppare una mia versione avanzata che fosse anche stereofonico, in modo che potessi usarlo contemporaneamente come carico fittizio durante i test sugli amplificatori e al contempo avere sott’occhio la potenza in uscita di entrambe i canali e che mi permettesse anche premendo solamente un bottone di avere immediatamente la misura della Rout/DF (smorzamento) di entrambe i canali. La misura della potenza risulta piuttosto precisa e la misura della Rout ha una tolleranza del 10% dovuta in buona parte alla precisione dei calcoli in virgola mobile di arduino, il che alla fine resta accettabile perchè comunque su misure di valvolari con DF tipicamente vanno da 2 a 8 si traduce in uno scarto di +/- mezzo punto di DF che sicuramente è migliore di quanto fattibile a occhio usando l’oscilloscopio o paragonabile quando fattibile guardando i decimali ballerini di un tester rms. Durante la misura dello smorzamento il carico non viene completamente scollegato dall’amplificatore.

L’apparecchio ha 2 scale selezionabili tramite un deviatore, una scala fa da 0 a 18watt rappresentata con 2 decimali, e la seconda che fa da 0 a 100watt rappresentata con 1 solo decimale, ovviamente la scala da 18watt risulta più precisa nella lettura della piccole potenze e questo è il motivo per averne 2.

Il carico è formato da una grossa resistenza da 8ohm 100watt, in serie ad essa c’è una resistenza da 47ohm e un grosso relè che la cortocircuita. In condizioni normali l’amplificatore risulta connesso alla sola resistenza da 8ohm, durante la misura del DF il relè scatta per 2 secondi eliminando il cortocircuito sulla resistenza da 47ohm, quindi il carico momentaneo dell’amplificatore diventa 8+47=55ohm, valore abbastanza basso per garantire la stabilità della maggiorparte anche degli amplificatori meno stabili. La resistenza “ancora” è da 10watt, e la misura del DF deve essere effettuata nella sola scala dei 18watt ad una potenza inferiore a 9watt. Questo è necessario a garantire che il salto di tensione alla disconnessione del carico resti dentro il range misurabile dall’ADC di arduino, se avvenisse una saturazione dell’ADC si avrebbe a display un messaggio di errore più un segnale acustico e sarebbe ancora possibile diminuire la potenza dell’amplificatore per riuscire ad effettuare la misura. Ho misurato senza problemi Rout fino a 22/27ohm con rout inferiori a 0,5… Ulteriori messaggi di sovraccarico con relativi segnali acustici sono implementati nel software. Essendo i carichi chiusi in una scatola e volendo io usare l’apparecchio anche come carico fittizio durante le prove a banco, non solo per una veloce misura, è presente una ventola che si attiva ogni qual volta la potenza misurata raggiunga i 10watt.

Le resistenze di carico e ancora dovrebbe essere piuttosto precise, io non sono stato a cercare resistenze di potenza all’1% ma ho semplicemente selezionato 4 resistenze del valore esatto da un mucchio.

Come relè disponevo di alcuni relè a 3 scambi da 3A per ogni contatto. Ho collegato i contatti in parallelo per un totale di 9A sopportabili (resistenza del contatto circa 0,02ohm) e cablato il pezzo di circuito con filo grosso.

Ho quindi cablato il circuito su un pezzo di 1000 fori…

Altre foto dell’apparecchio finito…

Nel video qui sotto si può vedere una demo del funzionamento del wattmentro, ero ancora aperto, collegato al computer perchè stavo rifinendo il firmware. Come sorgente di potenza stavo usando un trasformatore 230/24volt alimentato tramite variac con una resistenza in serie al secondario.

Qui sotto potete trovare lo schema elettrico del wattmetro:

Clicca qui per il download dello schema wattmetro-arduino

Cliccando qui invece c’è il download del firmware wattmetro.zip

Ecco il codice sorgente del firmware

/*  Wattmetro 0/100 Watt Vers 2.0 SB-LAB di Stefano Bianchini */
/*  www.sb-lab.eu 04/09/2021 Basato sul firmware 1.0 di Marco Ricobelli */
/*  Distribuito sotto licenza GPL V3.0 */
/*  gnu.org/licenses/gpl-3.0.html  */

/* definizione dei vari pin */
#define ventola 13
#define rel_dx 6
#define rel_sx 7
#define buzzer 16
#define allarme 8
/* ritardo sulla comunicazione con il display */
#define bug 20
/* Definizioni pin LCD */
#define lrs 12
#define ee 11
#define db4 5
#define db5 4
#define db6 3
#define db7 2
/* Uso questi piedini analogici per leggere un pulsante e un'interruttore */
int df = A7;
int scala = A6;
/* Uso questi piedini analogici per leggere la tensione sui carichi */
int destra = A0;
int sinistra = A1;
/* Costanti usate per il calcolo della potenza */
#define R1 300.0 /* partitore 10k / 5k (18w max) */
#define R2 935.0 /* partitore 10k / 1,2k (137w max)*/
/* Questa constante dichiara la caduta di tensione del ponte di diodi */
#define d_drop 0.7
/* Valore della resistenza di carico */
#define load 8
/* Valore della resistenza ancora */
#define ancora 47
/* simbolo ohm per hd44780 */
#define omega 0xF4

/* libreria per pilotare l' LCD */
#include LiquidCrystal lcd(lrs, ee, db4, db5, db6, db7);

void setup()
{
  pinMode(ventola, OUTPUT);
  pinMode(rel_dx, OUTPUT);
  pinMode(rel_sx, OUTPUT);
  pinMode(buzzer, OUTPUT);
  pinMode(allarme, INPUT);
  lcd.begin(16, 2);
  lcd.clear();
}

/* Facilita i comandi di scrittura sul dispay */
int printl(char text[16], char pos, char riga)
{
  /* Calcola la lunghezza della stringa */
  char lung = strlen(text);
  /* Sovrascrive i caratteri non usati della stringa con degli spazi per evitare sporcature dello schermo */
  for(char i = lung; i < 16; i++)
    {
      text[i] = 0x20;
    }
  delay(bug);
  lcd.setCursor(pos, !riga); /* inverto l'operatore perche' il mio display inverte le 2 righe */
  delay(bug);
  lcd.print(text);
}

/* Cancella lo schermo */
int cancella()
{
  lcd.clear();
  delay(bug);
}

int cicalino()
{
  printl("    ALLARME!    ",0,0);
  printl("  SOVRACCARICO  ",0,1);
  /* continua l'allarme acustico finche' il pin allarme non torna a 1 */
  for(;;)
    {
      digitalWrite(buzzer, HIGH);
      delay(250);
      digitalWrite(buzzer, LOW);
      delay(250);
      if (digitalRead(allarme) == 1)
        { 
          break;
        }
    }
}

int vent_on()
{
  digitalWrite(ventola, HIGH);
}

int vent_off()
{
  digitalWrite(ventola, LOW);
}

int beep()
{
      digitalWrite(buzzer, HIGH);
      delay(100);
      digitalWrite(buzzer, LOW);
      delay(100);
}

void loop()
{
  /* qui viene memorizzato il campionamento dell'ADC */
  unsigned int valore_dx = 0;
  unsigned int valore_sx = 0;
  /* vout_?? e' la tensione in uscita dal partitore, letta dall'ADC */
  float vout_dx = 0.0;
  float vout_sx = 0.0;
  /* vin_?? e' la tensione calcolata che dovrebbe essere presente in ingresso dal carico */
  float vin_dx = 0.0;
  float vin_sx = 0.0;
  /* w_?? e' la ponteza in watt RMS che dovrebbe essere presente sul carico */
  float w_dx = 0.0;
  float w_sx = 0.0;
  
  /* variabili per calcolare lo smorzamento*/
  /* contiene la tensione letta sul carico */
  float load_vin_dx;
  float load_vin_sx;
  /* contiene la corrente letta sul carico */
  float i_load_dx;
  float i_load_sx;
  /* contiene la tensione letta senza carico */
  float off_load_vin_dx;
  float off_load_vin_sx;
  /* contiene la corrente letta senza carico */
  float i_off_load_dx;
  float i_off_load_sx;
  /* contiene la differenza tra le correnti */
  float i_diff_dx;
  float i_diff_sx;
  /* contiene la differenza tra le tensioni */
  float v_diff_dx;
  float v_diff_sx;
  /* contiene la rout calcolata */
  float rout_dx;
  float rout_sx;
  /* contiene il DF calcolato */
  float df_dx;
  float df_sx;
  /* stringhe per la conversione dei valori in stringa */
  char str_tempROUT[6];
  char str_tempDF[6];
  char str_tempV[6];
  char str_tempW[6];
  /* stringhe per la scrittura sul display */
  char stringa0[16];
  char stringa1[16];
  /* byte che servono per l'emissione del tono acustino di OL una sola volta */
  unsigned char a;
  unsigned char b;        

  printl("   Watt Metro" ,0,0);
  printl("     SB-LAB" ,0,1);
  delay(1000);
  cancella();
  for(;;)
    {
      if(digitalRead(allarme) == 0)
        { 
          cicalino();
        }
      /* lettura degli ADC */
      valore_dx = analogRead(destra);
      valore_sx = analogRead(sinistra);
      /* calcola la tensione letta dagli ADC */
      vout_dx = (valore_dx * 5.0) / 1024.0;
      vout_sx = (valore_sx * 5.0) / 1024.0;
      /* lettura dell'interruttore cambio scala e calcolo della tensione in ingresso */
      if(analogRead(scala) < 512) /* interruttore chiuso quindi 140w*/
        {
          /* calcolo tensione RMS */
          vin_dx = (((vout_dx * R2)/141)+d_drop);
          vin_sx = (((vout_sx * R2)/141)+d_drop);
          /* sotto il valore d_drop ci sono solo misurazioni spurie quindi azzero */
          if (vin_dx <= d_drop + 0.05) { vin_dx = 0.0; }
          if (vin_sx <= d_drop + 0.05) { vin_sx = 0.0; }
          /* calcolo ponteza RMS */
          w_dx = (vin_dx * vin_dx) / load;
          w_sx = (vin_sx * vin_sx) / load;
        }
      if(analogRead(scala) > 512) /* interruttore aperto quindi 18w*/
        {
          /* calcolo tensione RMS */
          vin_dx = (((vout_dx * R1)/141)+d_drop);
          vin_sx = (((vout_sx * R1)/141)+d_drop);
          /* sotto il valore d_drop ci sono solo misurazioni spurie quindi azzero */
          if (vin_dx <= d_drop + 0.05) { vin_dx = 0.0; }
          if (vin_sx <= d_drop + 0.05) { vin_sx = 0.0; }
          /* calcolo ponteza RMS */
          w_dx = (vin_dx * vin_dx) / load;
          w_sx = (vin_sx * vin_sx) / load;
        }  
      if(valore_dx < 1023)
        { 
          if(analogRead(scala) > 512) /* interruttore aperto quindi 18w setto 2 decimali */
            {
              dtostrf(w_dx, 2, 2, str_tempW);
              dtostrf(vin_dx, 2, 2, str_tempV);
            }
          else /* interruttore chiuso quindi 140watt setto 1 decimale */
            {
              dtostrf(w_dx, 3, 1, str_tempW);
              dtostrf(vin_dx, 3, 1, str_tempV);
            }
          sprintf(stringa0, "D:%sv %sw", str_tempV, str_tempW);
          if(a == 1) { a = 0; }
        }
      if(valore_dx == 1023)
        { 
          sprintf(stringa0, "D: - OL -");
          if(a == 0) { beep(); a = 1; }
        }
      if(valore_sx < 1023)
        {
          if (analogRead(scala) > 512) /* interruttore aperto quindi 18w setto 2 decimali */
            {
              dtostrf(w_sx, 2, 2, str_tempW);
              dtostrf(vin_sx, 2, 2, str_tempV);
            }
          else /* interruttore chiuso quindi 140watt setto 1 decimale */
            {
              dtostrf(w_sx, 3, 1, str_tempW);
              dtostrf(vin_sx, 3, 1, str_tempV);
            }
          sprintf(stringa1, "S:%sv %sw", str_tempV, str_tempW);
          if(b == 1) { b = 0; }
        }
      if(valore_sx == 1023)
        {
          sprintf(stringa1, "S: - OL -");
          if(b == 0) { beep(); b = 1; }
        }
      /* se uno dei 2 canali supera i 10 watt accende la ventola per raffreddare i carichi */
      if(w_sx > 10.0 || w_dx > 10.0) { vent_on(); }
      /* spenge la ventola solo se entrambe i carichi sono sotto i 10watt */
      if(w_sx < 10.0 && w_dx < 10.0) { vent_off(); }
      printl(stringa0,0,0);
      printl(stringa1,0,1);
      delay(100); /* aggiorna il display ogni 100ms */
      if(analogRead(df) < 512) /* lettura del pulsante DF */
        {
          /* La misura dello smorzamento deve essere fatta nella scala dei 18 watt ed entro i 9 watt misurati */
          if(analogRead(scala) > 512 && w_dx <= 9.00 && w_sx <= 9.00)
            { 
              /* calcolo del canale destro */
              if(vout_dx > 0)
                {
                  /* calcolo tensione RMS con il carico inserito */
                  load_vin_dx = (((vout_dx * R1)/141)+d_drop);
                  /* calcolo la corrente che scorre nel carico */
                  i_load_dx = load_vin_dx / load;
                  /* campionatura della tensione scollegando il carico, un canale per volta */
                  digitalWrite(rel_dx, HIGH);
                  delay(2000);
                  valore_dx = analogRead(destra);
                  digitalWrite(rel_dx, LOW);
                  /* Se il DF dell'amplificatore e' troppo basso la misura con il carico scollegato potrebbe portare in saturazione */
                  /*  l'ADC e si otterebbe un calcolo errato quindi rilevo il fondo scala e genero un messaggio di errore */
                  if(valore_dx < 1023) /* il valore e' entro il limite quindi calcolo il DF */
                    {
                      /* calcola la tensione letta dagli ADC */
                      vout_dx = (valore_dx * 5.0) / 1024.0;
                      /* calcolo tensione RMS con il carico disinserito */
                      off_load_vin_dx = (((vout_dx * R1)/141)+d_drop);
                      /* calcolo la corrente che scorre nel col carico scollegato */
                      i_off_load_dx = off_load_vin_dx / (load + ancora);
                      /* calcolo la differenza di corrente con carico e senza carico */
                      i_diff_dx = i_load_dx - i_off_load_dx;
                      /* calcolo la differenza di tensione con carico e senza carico */
                      v_diff_dx = off_load_vin_dx - load_vin_dx;
                      /* calcolo la resistenza incognita */
                      rout_dx = v_diff_dx / i_diff_dx;
                      /* calcolo il DF */
                      df_dx = load / rout_dx;
                      /* Formatto i dati */
                      dtostrf(rout_dx, 3, 1, str_tempROUT);
                      dtostrf(df_dx, 2, 1, str_tempDF);
                      sprintf(stringa0, "D:%s%c DF=%s", str_tempROUT, omega, str_tempDF);
                    }
                  /* il valore e' superiore o uguale a 1023 quindi l'ADC era in saturazione, genero l'errore */
                  else { sprintf(stringa0, "D: Fuori scala!"); }
               }
            else { sprintf(stringa0, "D: - N/A -"); }
            /* calcolo del canale sinistro */
            if(vout_sx > 0)
               {
                  /* calcolo tensione RMS con il carico inserito */
                  load_vin_sx = (((vout_sx * R1)/141)+d_drop);                  
                  /* calcolo la corrente che scorre nel carico */
                  i_load_sx = load_vin_sx / load;                  
                  /* campionatura della tensione scollegando il carico, un canale per volta */
                  digitalWrite(rel_sx, HIGH);
                  delay(2000);
                  valore_sx = analogRead(sinistra);
                  digitalWrite(rel_sx, LOW);
                  /* Se il DF dell'amplificatore e' troppo basso la misura con il carico scollegato potrebbe portare in saturazione */
                  /*  l'ADC e si otterebbe un calcolo errato quindi rilevo il fondo scala e genero un messaggio di errore */
                  if(valore_sx < 1023) /* il valore e' entro il limite quindi calcolo il DF */
                    {
                      /* calcola la tensione letta dagli ADC */
                      vout_sx = (valore_sx * 5.0) / 1024.0;
                      /* calcolo tensione RMS con il carico disinserito */
                      off_load_vin_sx = (((vout_sx * R1)/141)+d_drop);
                      /* calcolo la corrente che scorre nel col carico scollegato */
                      i_off_load_sx = off_load_vin_sx / (load + ancora);
                      /* calcolo la differenza di corrente con carico e senza carico */
                      i_diff_sx = i_load_sx - i_off_load_sx;                  
                      /* calcolo la differenza di tensione con carico e senza carico */
                      v_diff_sx = off_load_vin_sx - load_vin_sx;
                      /* calcolo la resistenza incognita */
                      rout_sx = v_diff_sx / i_diff_sx;
                      /* calcolo il DF */
                      df_sx = load / rout_sx;
                      /* Formatto i dati */
                      dtostrf(rout_sx, 3, 1, str_tempROUT);
                      dtostrf(df_sx, 2, 1, str_tempDF);            
                      sprintf(stringa1, "S:%s%c DF=%s", str_tempROUT, omega, str_tempDF);
                    }
                  /* il valore e' superiore o uguale a 1023 quindi l'ADC era in saturazione, genero l'errore */
                  else { sprintf(stringa1, "S: Fuori scala!"); }
               }
            else { sprintf(stringa1, "S: - N/A -"); }
            /* Visualizzo a display */
            printl(stringa0,0,0);
            printl(stringa1,0,1);
            /* avvio un ciclo infinito finche' non viene ripremuto il pulsante DF */
            for(;;)
              {
                if(analogRead(df) < 512)
                  { 
                    delay(250);
                    break;
                  }
              }
            }
          else
            {
              printl("Scala Sbagliata" ,0,0);
              printl("O troppa potenza" ,0,1);
              beep();
              delay(3000);
            }
        }
    }
}
Subscribe
Notificami
guest

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.

18 Commenti
Inline Feedbacks
View all comments
Diego
Diego
5 mesi fa

Mi confermi che nella riga 22 e 23 del codice arduino gli ingressi analogici df A7 e scala A6 sono invertiti rispetto allo schema? Quindi in realtà sono df A6 e scala A7.

Diego
Diego
5 mesi fa

Grazie per la risposta, ho provveduto a fare la modifica al codice, ho modificato anche l’indirizzamento del display e di conseguenza degli altri pin perchè ho sotto mano un modello differente di display per arduino. Ora devo mettere insieme il minimo indispensabile per iniziare a fare qualche test, vedere se la modifica per l’utilizzo dei due carichi funziona effettivamente e iniziare a pensare ad un assemblaggio definitivo.

Diego
Diego
5 mesi fa

Progetto interessantissimo che mi accingerò a realizzare. Mi chiedevo se fosse possibile inserire anche dei carichi fittizi aggiuntivi da 4 ohm magari selezionabili da un commutatore all’occorrenza e se si eventualmente quali modifiche apportare al circuito, se fosse necessario, per quanto riguarda la misura del fattore si smorzamento.

GIORGIO
GIORGIO
11 mesi fa

OTTIMO FINALMENTE UN WATTMETRO

Marco
Marco
1 anno fa

forse perché ho ancora un pc con Win7 e Chrome non l’hanno più aggiornato ?

Marco
Marco
1 anno fa

Scaricato, grazie!

Marco
Marco
1 anno fa

Ciao,
ho provato più volte, sia con Chrome che con Firefox, ma non copia nulla.
Grazie.

Marco
Marco
1 anno fa

Ciao,
cosa si deve fare per scaricare il codice dello sketch?

Gioegio
Gioegio
1 anno fa

SEMBRA OTTIMO volevo chiedere de ai il formato HEX e eventualmente dove posso acquistare tutti i componenti non riesco a mettermi in contatto con la ditta ARDUINO ti ringrazio vivamente