Giving the voice to a toy always has been a dream when I was a child. There weren’t talking toys and the dream was destinated to stay a dream. With the power of the modern microcontrollers, like Arduino, to have a talking robot is one of the things that always I desidered to do. You can build a talking robot in two ways:
- with the tex-to-speech, writing a text that the robot says
- recording some audio files with inside the voice already recorded that the robot repeats
The text-to-speech is the best way to do a robot talking. You write a text, and he reads the text: this is the fastest way, but you need a chip that implements the text-to-speech. It is easy if your language is English, not easy if your language is different. For me, the language is the Italian and I haven’t found any cheap chip for italian text-to-speech.
So I used the second method, recording the voice. The method is more complex, but at the end the result is the same.
I built a simple robot, that can be made by every beginner, that does 2 things:
- it goes around, avoiding obstacles with an ultrasound sensor
- it talks, thanks to an audio chip
The robot is basically a toy, that fascinates my son, a little child of few months old. He enjoys to see the robot turn around, talking with robotic voice. I did it just for that. Let me to introduce Geronimo 2, the little talking robot:
Geronimo in action:
The little robot is based on two projects already presented in the past: this and this. In these links you can find some details on the building and the hardware. It uses a DFRobot URM37 Ultrasound sensor and a SOMO-14D audio chip, used to talk. This audio chip is low cost (20 euros) and it works with few components (only one 220uf capacitor) and it can be set with an Arduino.
The SOMO-14D plays only files codified with a proprietary format (AD4). But it is available a practical sw that converts MP3 in AD4 format.
In order to allows to talk to the robot, I used a software for italian speech(called Er Finestra), but you can use the speech software you want. For almost all the languages a similar little utility is available.
One problem resolved is due to the fact that Er Finestra can only say something. It can’t at the same time to say and to record. So I downloaded Audacity, an editor audio free, that can record every sound that pass in the sound card.
So you have to start Er Finestra and at the same time to start Audacity that records the phrases read by Er Finestra. Then you can convert the Phrase from MP3 to AD4. The process could be seem complex, but it is more difficult to say than to do.
I read with Er Finestra 40 pieces, I converted them and the robot can say them randomly.
It is possible to use an intelligent algorithm in order to say something responding to external stimulus. For example, to say ‘turn right’, ‘turn left’ or something of similar. The audio chip can play also musical pieces, songs, so it is possible to see the robot going around singing !
The code used for Arduino is simple and it is organized in two parts:
– one for the robot navigation
– one for audio playing
The code is commented, so I don’t think you will have comprehension problems:
//Semplice robot autonomo con sensore ad ultrasuoni per evitare gli ostacoli e chip audio per parlare o suonare una canzone. //Simple autonomous robot with ultrasonic sensor for obstacles avoidance and audio player chip. It can speak or play a song.
// written by alegiaco // Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License //include URM Library - Includi la libreria URM #include "URMSerial.h" #include//include servo library - includi la libreria del servo Servo sservo; Servo dservo; Servo uservo; int ang=0; int angolomax=0; int distmax=0; int value=100; int ind=0; int rotazione=0; // Definizione delle misure per il sensore ad ultrasuoni URM37 // Measures definition for ultrasound URM37 #define DISTANCE 1 #define TEMPERATURE 2 #define ERROR 3 #define NOTREADY 4 #define TIMEOUT 5 //angolo di riposo dei servi delle ruote // standing angle for servos #define RIPOSOS 93 #define RIPOSOD 93 // inizializza l'oggetto della libreria urm del sensore ultrasuoni // initialize object for urm library URMSerial urm; //parte relativa al SOMO-14D - Chip Audio - SOMO-14D initial variables assegnation const unsigned int minVol= 0xfff0; // Minimum volume - Volume minimo const unsigned int maxVol= 0xfff7; // Max Volume - Volume massimo const unsigned int PlyPse = 0xfffe; // Play or Pause const unsigned int Stop= 0xFFFF; // Stop const int pinClock= 4; // clock sul pin dell'arduino - Clock on Arduino pin const int pinData =5; // data sul pin dell'arduino - Data on arduino pin const int pinBusy =8; // busy sul pin dell'arduino - Byte on Arduino pin const int pinReset=12; // reset sul pin dell'arduino - Reset on arduino pin unsigned int volLevel=0x0005; // imposta massimo livello volume - volume max level int Song; // current Song - canzone attuale unsigned int vol; // volume int secondi=0, ultimotempo=0; // varibili per durata brani - variables for songs time int brano=0; // brano riprodotto - song played int durata[40]={ 7,8,14,19,5,8,33,28,5,5,5,5,6,5,5,5,5,6,6,7,6,5,7,8,8,10,4,6,5,6,5,10,5,6,7,6,4,5,6,5}; // durata singoli brani - single songs time //****************** fine variabili iniziali ************************************ //****************** end initial variables ************************************** void setup() { Serial.begin(9600); // baud rate - 9600 urm.begin(6,7,9600); // RX Pin, TX Pin, Baud Rate sservo.attach(10); // Attacca il servo sinistro al pin 10 - servo left to pin 10 dservo.attach(9); // Attacca il servo destro al pin 9 - - servo right to pin 9 uservo.attach(3); // Attacca il servo degli ultrasuoni al pin 3 - ultrasonic sensor servo to pin 3 delay(100); uservo.write(90); // metti il servo degli ultrasuoni dritto - ultrasonic servo straight pinMode(pinData,OUTPUT); // set pin 4 to output for Data - pinMode(pinClock,OUTPUT); // set pin 3 to output for clock pinMode(pinReset,OUTPUT); // set pin 7 to allow software reset - imposta pin 7 per il sw reset pinMode(pinBusy,INPUT); // set pin 6 to monitor while Song is playing - pin 6 di monitoraggio mentre il brano è suonato Reset(); // resetta il somo-14D chip audio - somo-14d chip audio reset } void loop() { playSOMO14D(); // suona un brano con chip audio - play song with chip audio delay(30); // attendi allineamento dei servi - wait for servo alignement sempreDritto(); // fai andare il robot sempre dritto - always straight getMeasurement(DISTANCE); // richiedi una nuova lettura della distanza - ask for new distance reading if (value 0) // se il valore e' < 20 cm stiamo per sbattere - if the value is < 20 we are crashing { fermaServi(); // ferma il robot mentre controlla gli ostacoli intorno - stop servos scandisciAmbiente(); // controlla gli ostacoli ogni 30 gradi - control obstacles every 30 degrees ruotaServi(); // fai girare i servi nella direzione della max strada libera - turn servos in the free direction } } // ------------------ ferma i servi dei motori: robot fermo ----------------------- // ------------------ stop robot - stop servos ----------------------- void fermaServi() { sservo.write(RIPOSOS); // ferma ruota sinistra - stop left wheel dservo.write(RIPOSOD); // ferma ruota destra - stop right wheel } // ------------------ fai andare il robot sempre dritto ----------------------- // ------------------ go straight ----------------------- void sempreDritto() { sservo.write(RIPOSOS + 80); // fai andare il robot dritto - go straight dservo.write(RIPOSOD - 80); // fai andare il robot dritto - go straight } // ------------------ controlla gli ostacoli ogni 30 gradi ----------------------- // ------------------ control obstacles every 30 degrees ----------------------- void scandisciAmbiente() { angolomax=0; // imposta l'angolo di migliore uscita a zero - set exit angle to zero distmax=0; // imposta la distanza di uscita migliore a zero - set max distance read to zero for (ang=0; ang<=6; ang++) // fai un ciclo per controllare dove sono gli ostacoli - cycle to control where are the obstacles { delay(100); //attendi allineamento servo ultrasuoni - wait for ultrasonic sensor alignment uservo.write(ang*30); // imposta il servo degli ultrasuoni sul valore assunto da ang - set to ultrasonic sensor servo to value of ang variable float sommadist=0; int contavalori=0; // 5 letture della distanza. sommale e fai la media // read 5 time the distance, sum and average for (int lettura=1; lettura<=5; lettura++) { value=-2; getMeasurement(DISTANCE); //richiedi una nuova lettura della distanza - ask for a new distance read delay(20); if (value >= -1 && value < 300) // solo se la distanza è tra -1 e 300 - only if the distance is between -1 and 300 { if (value==-1) value=1000; //-1 = infinito sommadist = sommadist + value; contavalori++; } } int dist=sommadist/contavalori; if (dist > distmax) // verifica che la distanza letta sia maggiore del max - distance read > max { angolomax=ang; // se e' maggiore del max imposta il nuovo angolo max e - if > max, set new angle max distmax=dist; // la nuova distanza max - new max distance } } uservo.write(90); //rimetti il servo degli ultrasuoni dritto - ultrasonic servo straight } // ------------------ ruota i servi nella direzione di massimo spazio libero ----------------------- // ------------------ turn servos to max free distance ----------------------- void ruotaServi() { for (ind=1; ind < (abs(angolomax-3))*3; ind++) { if (angolomax <= 3) { //tra 0 e 90 gradi. between 0 and 90 degrees sservo.write(RIPOSOS-90); dservo.write(RIPOSOD-90); } else // tra 90 e 180 gradi - between 90 and 180 degrees { sservo.write(RIPOSOS+90); dservo.write(RIPOSOD+90); } delay (50); // attendi allineamento servi - wait for servos alignment } } // ------------------ acquisisci misurazione distanza ----------------------- // ------------------ take distance ----------------------- int getMeasurement(int mode) { // Request a distance reading from the URM37 - richiedi distanza da URM37 switch(urm.requestMeasurementOrTimeout(mode, value)) // Find out the type of request - verifica tipo richiesta { case DISTANCE: // Double check the reading we recieve is of DISTANCE type - doppio check che sia una distanza return value; break; case TEMPERATURE: return value; break; case ERROR: Serial.println("Error"); break; case NOTREADY: Serial.println("Not Ready"); break; case TIMEOUT: Serial.println("Timeout"); break; } return -2; }
This is the SOMO-14D code part:
// ------------------ play song on SOMO-14D ----------------------- void playSOMO14D() { if (int(millis()/1000) > ultimotempo + durata[brano]) { //verifica se il brano precedente è terminato - verify if previous song is finished ultimotempo=int(millis()/1000); brano=int(random(1, 41)); // prendi un brano a caso - take random song PlaySong(brano); // suona brano - play song } } /************************************************** ******************************** Send Sequence - Invia sequenza ************************************************** ********************************/ void sendData(int ThisSong) { int TheSong = ThisSong; int ClockCounter=0; int ClockCycle=15;//0x0f; digitalWrite(pinClock,HIGH); // Hold (idle) for 300msec to prepare data start delay(300); digitalWrite(pinClock,LOW); //Hold for 2msec to signal data start delay(2); //2msec delay while(ClockCounter <= ClockCycle) { digitalWrite(pinClock,LOW); if (TheSong & 0x8000) {digitalWrite(pinData,HIGH); } else {digitalWrite(pinData,LOW); } TheSong = TheSong << 1; delayMicroseconds(200); //Clock cycle period is 200 uSec - LOW digitalWrite(pinClock,HIGH); ClockCounter++; delayMicroseconds(200); //Clock cycle period is 200 uSec - HIGH } digitalWrite(pinData,LOW); digitalWrite(pinClock,HIGH); // Place clock high to signal end of data } /************************************************** ******************************** Plays Stored Song by Number - Suona un brano per numero del brano ************************************************** ********************************/ void PlaySong(int numero) { sendData(Stop); int SongNumber=numero; if (SongNumber >= 0 && SongNumber < 512){ // Song is within range limit Serial.print("-> Song No."); Serial.println(SongNumber); sendData(SongNumber);} } /************************************************** ******************************** Reset SOMO ************************************************** ********************************/ void Reset() { //Serial.println("RESET."); digitalWrite(pinReset,LOW); delay(50); digitalWrite(pinReset,HIGH); Song=0; }