Funzione vocale per Arduino

Per visualizzare i valori ricavati da alcuni sensori collegati ad Arduino quasi sempre si utilizza il monitor seriale dell’ IDE o un display LCD, ma in alcune applicazioni potrebbe essere utile ascoltare questi valori, magari attraverso una voce che ci indichi ad esempio la temperatura misurata da un sensore, il valore di una tensione che intendiamo tenere sotto controllo oppure lo stato dei pin usati come ingresso o uscita.

Per far questo non c’è bisogno di nessuna shield in particolare, ma solo di una scheda SD con apposito adattatore, della libreria <TMRpcm.h> e di un software per l’editing audio come ad esempio GoldWave oppure Audacity. Infatti, basterà preparare un file audio con tutti i messaggi che intendiamo far riprodurre ad Arduino, darli in pasto alla libreria ed il gioco è fatto.

Il circuito dimostrativo

Qui in basso è riportato lo schema elettrico del circuito di prova col quale è possibile ascoltare la temperatura misurata dal sensore NTC ogni volta che premeremo il pulsante p1 collegato all’entrata A0. Il secondo pulsante invece serve per accendere o spegnere il led collegato al pin 3 e per ascoltare lo stato di questa uscita. Dato che il segnale audio presente in uscita dal pin 9 risulta molto basso, per poterlo ascoltare attraverso un comune altoparlante è necessario amplificarlo adeguatamente attraverso il preamplificatore formato dai due transistor BC547 e dallo stadio finale composto dal modulo amplificatore basato sull’integrato LM386.

 

Schema Arduino play wav

 

 

Come preparare il file audio

La preparazione del file audio che andremo a memorizzare sulla scheda SD può essere suddivisa in tre fasi :

 

  1. Registrazione dei messaggi che vogliamo far riprodurre ad Arduino al verificarsi di un determinato evento
  2. Conversione in un formato adatto per essere riconosciuto ed elaborato dalla libreria<TMRpcm.h>
  3. Creazione di una lista delle pause che si trovano prima e dopo ogni messaggio all’interno della traccia audio

Nel nostro caso, visto che l’obiettivo è quello di ascoltare la temperatura misurata e lo stato del led, la prima cosa che dovremo fare è creare un file audio con tutti i possibili valori rilevabili dall’ NTC, ossia i numeri da -40 a 100 e i messaggi associati allo stato del pin 3. Il metodo più veloce consiste nell’utilizzare un software per sintesi vocale, come ad esempio quello da me utilizzato chiamato Dspeech 1.71 e visibile nella figura sottostante.

 

Arduino dspeech

 

 

Come vedesi, all’interno del programma viene creato un file di testo con tutte le parole che ci servono e distanziate l’una dall’altra da una pausa di 1 secondo. Una volta completato lo dovremo esportare in formato .wav attraverso la voce “Converti in file audio…” dal menù file.

 

Arduino dspeech salva

 

Il passo successivo consiste nell’aprire il file audio appena creato con un software per l’audio editing e convertirlo nel formato .wav PCM unsigned 8 bit mono. Per questa operazione ho usato il software GoldWave che permettere di ottenere questo tipo di conversione senza impostare nulla. Infatti, dopo averlo aperto basta richiamare la voce “Save Sound As…” dal menù file, scegliere il formato appena descritto nella finestra di salvataggio e cliccare sul pulsante “Salva”.

 

Arduino 8 bit mono wav

 

Ottenuto il file del formato giusto, l’ultima operazione che dovremo compiere sarà quella di creare una lista di tutte le pause poste prima e dopo ogni suono all’interno della traccia. In pratica questa lista ci permetterà di stabilire con precisione da quale posizione e per quanto tempo mandare in riproduzione il file audio memorizzato sulla scheda SD e per far questo possiamo utilizzare un altro programma per l’editing audio chiamato Audacity seguendo i seguenti passaggi :

Una volta aperto il file, selezioniamo l’intera traccia audio e scegliamo la voce “Silence Finder…” dal menù “Analizza”. Questa funzione permette trovare tutti i silenzi presenti nella traccia dandoci la possibilità di scegliere la durata minima che questi devono avere per poter essere presi in considerazione.

 

Audacity silence finder

 

Nel mio caso, poichè tra un suono e l’altro c’è un silenzio di 1 secondo, nella finestra delle impostazioni indico di considerare tutte le pause che hanno una durata minima di 1/2 secondo (0,500 mS) attraverso il cursore centrale denominato “Minimum duration of silence”.

 

Audacity silence finder setput

 

Confermando col pulsante “OK”, sotto la traccia verrà aggiunto un riquadro con tante “S” quanti sono i silenzi trovati e ognuna preceduta da un marcatore che ne indica l’esatta posizione. Questi marcatori svolgono due funzioni : presi singolarmente indicano il punto esatto in cui ha inizio un suono mentre in coppia ne indicano la durata.

 

 

Audacity silence finder example

 

L’insieme di questi marcatori lo dovremo esportare come file di testo cliccando sul menù “File” poi su “Export” ed infine scegliere la voce “Esporta etichette”.

 

Audacity tag export

 

Come risultato finale avremo un file di testo con tutti i valori trovati come visibile qui sotto. Le due colonne sono identiche per cui una può essere cancellata tranquillamente.

 

Per comodità sarebbe utile crearsi  una tabella simile a quella riportata qui in basso in modo da avere uno schema della traccia.  Ad esempio, qui vediamo come ad ogni numero corrisponde un valore (in secondi) e una posizione nell’array usato nello sketch.

Arduino tabella sintesi vocale

Lo sketch

Di seguito è riportato lo sketch usato nell’esempio e nel quale si pone l’attenzione sul funzionamento della  libreria “TMRpcm.h”. Dopo averla scaricata ed importata , la prima operazione sarà quella di impostare il pin che andrà collegato al terminale CS della scheda SD attraverso la variabile SD_ChipSelectPin,  nello specifico è il numero 10 ma se ne sarebbe potuto scegliere un altro. Subito dopo definiamo un oggetto “audio” col quale sarà possibile riprodurre i messaggi vocali attraverso il metodo “play” ed infine con il metodo audio.speakerPin impostiamo il pin da cui prelevare il segnale audio. Al metodo play() dell’oggetto audio dovremo passare come argomenti sia il nome del file audio sia il punto ( espresso in secondi ) da cui far partire la riproduzione. Se quest’ultimo parametro non viene specificato il file verrà eseguito per intero partendo dall’inizio.

Più avanti troviamo un Array tipo float contenente la lista creata precedentemente con Audacity. Ad esempio, facendo riferimento alla tabella vista sopra, vediamo che per riprodurre il numero -20 dovremo iniziare la riproduzione a 47.89 secondi e farla durare un tempo di :

Durata = marcatore succ. – marcatore inizio  –> (50.015 – 47.89) x 1000 = 2125 millisecondi.

Nel caso preso come esempio il codice da scrivere sarà :

 

unsigned long durata = (pos[21] - pos[20]) * 1000; 
audio.play("nomeFile", pos[20]); 
delay(durata); 
audio.stopPlayback();

 

In pratica, la durata si ottiene dalla differenza fra quei valori che indicano le pause presenti prima e dopo il numero ( parola o frase ) che si vuole riprodurre. Così se volessimo ascoltare il numero -37, che sulla traccia inizia a 7.90 secondi, dovremo considerare il suo successivo, in questo caso -36  che inizia a 10.46, e fare la differenza tra i due, cioè  (10.46 – 7.90 ) = 2.56 secondi. Il valore ottenuto lo moltiplichiamo per mille in modo da avere i millisecondi da passare come argomento al metodo delay() posto subito dopo il metodo play(…) : 2.56 x 1000 = 2560 mS.

Il valore rilevato dal sensore NTC viene calcolato per mezzo della funzione valore_NTC() , la quale andrà ad aggiornare la variabile tm ( tipo byte per cui il si considera solo la parte intera ) utilizzata a sua volta nella funzione riproduci_Temp() per riprodurre la temperatura misurata.

 

#include <SD.h>
#include <TMRpcm.h>
#include <SPI.h>
#define SD_ChipSelectPin 10  // pin 10 da collegare al terminale CS della scheda SD         
#define led  3
#define p1 4
#define p2 7

#define pinNTC A0                         // pin dov'è collegata la resistenza NTC
#define resistenza_In_Serie 10000         // resistenza posta in serie al sensore NTC ( in Ohm )
#define resistenza_Nominale_NTC 22000     // valore nominale del sensore NTC ( in Ohm )
#define temp_Nominale_NTC 25              // valore della temperatura nominale ( in gradi centigradi )
#define coeff_Thermistore 3950            // costante materiale del termistore ( è un valore comune )
float temperatura;                        // valore temperatura in formato numerico
char strTemp[6];                          // valore temperatura in formato stringa
byte tm;                                  // parte intera della temperatura


TMRpcm audio;   // crea l'oggetto della libreria TMRpcm per riprodurre l'audio


float pos[] = { // Array con i valori delle pause all'interno della traccia audio
  0.847392  ,
  3.192063  ,
  5.506803  ,
  7.901361  ,
  10.465533 ,
  12.830159 ,
  15.414286 ,
  17.938549 ,
  20.103628 ,
  22.538095 ,
  24.782993 ,
  26.978005 ,
  29.282766 ,
  31.647392 ,
  34.151701 ,
  36.366667 ,
  38.821088 ,
  41.255556 ,
  43.310884 ,
  45.685488 ,
  47.890476 ,
  50.015646 ,
  52.370295 ,
  54.704989 ,
  57.139456 ,
  59.404308 ,
  61.519501 ,
  63.943991 ,
  66.129025 ,
  68.314059 ,
  70.439229 ,
  72.534467 ,
  74.639683 ,
  76.724943 ,
  78.959864 ,
  80.975283 ,
  83.280045 ,
  85.495011 ,
  87.241043 ,
  89.306349 ,
  91.202041 ,
  92.798413 ,
  94.384807 ,
  96.110884 ,
  97.557596 ,
  99.353515 ,
  101.169388  ,
  102.865533  ,
  104.771202  ,
  106.497279  ,
  108.263265  ,
  110.129025  ,
  112.044671  ,
  113.980272  ,
  115.836054  ,
  117.861451  ,
  119.677324  ,
  121.553061  ,
  123.638322  ,
  125.643764  ,
  127.739002  ,
  129.604762  ,
  131.460544  ,
  133.475964  ,
  135.291837  ,
  137.426984  ,
  139.592063  ,
  141.587528  ,
  143.762585  ,
  145.708163  ,
  147.673696  ,
  149.539456  ,
  151.395238  ,
  153.490476  ,
  155.286395  ,
  157.491383  ,
  159.696372  ,
  161.681859  ,
  163.936735  ,
  165.982086  ,
  167.967574  ,
  169.943084  ,
  171.958503  ,
  173.973923  ,
  175.849660  ,
  178.124490  ,
  180.399320  ,
  182.494558  ,
  184.859184  ,
  186.974376  ,
  188.959864  ,
  190.775737  ,
  192.890930  ,
  195.185714  ,
  197.201134  ,
  199.535828  ,
  201.920408  ,
  204.125397  ,
  206.529932  ,
  208.754875  ,
  210.919955  ,
  212.995238  ,
  215.130385  ,
  217.455102  ,
  219.480499  ,
  221.805215  ,
  224.199773  ,
  226.404762  ,
  228.839229  ,
  231.094104  ,
  233.279138  ,
  235.444218  ,
  237.609297  ,
  239.924036  ,
  241.929478  ,
  244.383900  ,
  246.858277  ,
  249.053288  ,
  251.577551  ,
  253.932200  ,
  256.157143  ,
  258.132653  ,
  260.168027  ,
  262.373016  ,
  264.268707  ,
  266.573469  ,
  268.918141  ,
  271.013379  ,
  273.378005  ,
  275.602948  ,
  277.708163  ,
  279.763492  ,
  281.788889  ,
  283.874150  ,
  285.839683  ,
  288.174376  ,
  290.509070  ,
  292.644218  ,
  295.018821  ,
  297.203855  ,
  299.378912  ,
  301.214739  ,
  303.150340  ,
  304.906349  ,
  307.859637  ,
  312, 868714

};


void valore_NTC() // Ricavo il valore della temperatura rilevata dal sensore NTC
{
  float valore_ADC;
  float Resistenza;
  float steinhart;
  valore_ADC = analogRead(pinNTC);
  Resistenza = (1023 / valore_ADC) - 1;
  Resistenza = resistenza_In_Serie / Resistenza;
  steinhart = Resistenza / resistenza_Nominale_NTC;
  steinhart = log(steinhart);
  steinhart /= coeff_Thermistore;
  steinhart += 1.0 / (temp_Nominale_NTC  + 273.15);
  steinhart = 1.0 / steinhart;
  steinhart -= 273.15;
  temperatura = steinhart;   // valore della temperatura in gradi centigradi
  dtostrf(temperatura, 6, 2, strTemp);
  tm = (byte) temperatura;
  Serial.println(strTemp);

}

void riproduci_Temp() //
{

  // variabili per calcolare la durata della riproduzione dei messaggi

  unsigned long d1 = (pos[142] - pos[141]) * 1000; // durata temperatura
  unsigned long d2 = (pos[tm + 40 + 1] - pos[tm + 40]) * 1000; // durata valore misurato
  unsigned long d3 = (pos[143] - pos[142]) * 1000; // durata gradi

  audio.play("silvia", pos[141]);       // riproduce "Temperatura"
  delay(d1);

  audio.play("silvia", pos[tm + 40]);   // riproduce il valore misurato
  delay(d2);

  audio.play("silvia", pos[142]);     // riproduce "Gradi"
  delay(d3);
  audio.stopPlayback();


}

void setup() {

  pinMode(led, OUTPUT);
  pinMode(p1, INPUT);
  pinMode(p2, INPUT);
  pinMode(pinNTC, INPUT);

  audio.speakerPin = 9; // pin uscita audio
  audio.quality(1);     // imposta la qualità dell'audio, può essere 0 oppure 1

  // messaggi relativi alla scheda SD inviati sulla seriale
  Serial.begin(115200);

  if (!SD.begin(SD_ChipSelectPin)) {
    Serial.println("Errore lettura scheda SD!");
    return;
  }
  else {
    Serial.println("Scheda SD riconosciuta...");
  }

}

void loop() {


  /*
      Pulsante per accendere o spegnere il led sull'uscita 3
  */
  if (digitalRead(p1) == LOW)
  {

    digitalWrite(led, !digitalRead(led));
    byte stato = digitalRead(led);
    long ps;

    switch (stato)
    {
      case 1:
        ps = (pos[144] - pos[143]) * 1000;
        audio.play("silvia", pos[143]);    // messaggio riprodotto quando il led è acceso
        delay(ps);
        audio.stopPlayback();
        break;

      case 0:

        ps = (pos[145] - pos[144]) * 1000;
        audio.play("silvia", pos[144]);    // messaggio riprodotto quando il led è spento
        delay(ps);
        audio.stopPlayback();
        break;
    }
  }



  if (digitalRead(p2) == LOW) // pulsante per ascoltare il valore della temperatura rilevata
  {
    valore_NTC();
    riproduci_Temp();
  }



}

 

Per il download e la documentazione della libreria fare riferimento al seguente link : https://github.com/TMRh20/TMRpcm

Si ricorda, infine, che il tipo di formattazione per la scheda SD è il FAT32 e che il file audio andrà salvato senza estensione.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

L’archivio scaricabile qui in basso contiene lo sketch, lo schema, il file audio per la scheda SD e la tabella vista sopra.

File progetto