Emic Text-To-Speech Module + Arduino

A while back, I bought the Emic TTS module from Parallax, but I ended up not using it. Well, now I need to implement it into a project, but this time I am going to be interfacing it with the Arduino instead of the BASIC Stamp. I have tried and tried and tried to get it to work with the Arduino, but each time I have failed. I haven't been able to find code on the web that uses the Arduino with the Emic TTS, so I had to try to 'translate' the BS2 code that talks to the Emic, into Arduino code. Here's the BS2 Code:

' =========================================================================
'
'   File...... EasyEmic.BS2
'   Purpose... Simple Emic TTS demonstration
'   Author.... Parallax, Inc. (Copyright (c) 2004, All Rights Reserved)
'   E-mail.... support@parallax.com
'   Started...
'   Updated... 28 APR 2004
'
'   {$STAMP BS2}
'   {$PBASIC 2.5}
'
' =========================================================================

' -----[ Program Description ]---------------------------------------------
'
' Simple Emic TTS speech demo.  Be sure to set both Emic DIP switches to
' the OFF (down) position.


' -----[ I/O Definitions ]-------------------------------------------------

Tx              PIN     0                       ' connects to Emic SIn
Rx              PIN     1                       ' connects to Emic SOut
Busy            PIN     2                       ' 1 = busy


' -----[ Constants ]-------------------------------------------------------

#SELECT $STAMP
  #CASE BS2, BS2E, BS2PE
    Baud        CON     396         ' 2400 baud, N81
  #CASE BS2SX, BS2P
    Baud        CON     1021
#ENDSELECT

Yes             CON     1
No              CON     0

' Emic Commands (Hex Mode)

Say             CON     $00                     ' say Engish text
Volume          CON     $01                     ' set volume, 0 - 7
Speed           CON     $02                     ' set speed, 0 - 4
Pitch           CON     $03                     ' set pitch, 0 - 6
AddAbbr         CON     $04                     ' add abbreviation
DelAbbr         CON     $05                     ' delete abbreviation
ListAbbr        CON     $06                     ' list abbreviations
Version         CON     $07                     ' get version
Reset           CON     $08                     ' soft reset
Audio           CON     $09                     ' enable audio in
PhT             CON     $10                     ' start of phonetic text
Help            CON     $FE                     ' display help
EOM             CON     $AA                     ' end of message

OK              CON     $55                     ' "okay" for hex mode


' -----[ Program Code ]----------------------------------------------------

Main:
  DO
    GOSUB Check_Busy
    SEROUT Tx, Baud, [Say, "Hello world. I am Emic. Hear me roar.", EOM]
    PAUSE 2000
  LOOP

  END


' -----[ Subroutines ]-----------------------------------------------------

' Check status of Emic TTS module
' -- wait until Busy line released by Emic
' -- code as written does not timeout

Check_Busy:
  PAUSE 1                                       ' allow busy to activate
  DO WHILE (Busy = Yes) : LOOP                  ' wait until not busy
  RETURN

...And here's my implementation of that code in an Arduino sketch:

// Emic TTS Commands
#define Say 0x00
#define Volume 0x01
#define Speed 0x02
#define Reset 0x08
#define PhT 0x10
#define Help 0xFE
#define EOM 0xAA

#define OK 0x55
#define Yes 1
#define No 0

byte vol;
byte spd;
byte ptch;
boolean Busy;
int timeout;
int Data;

void setup() {
     Serial.begin(2400); //Emic communicates at 2400Baud
     pinMode(BusyPin, INPUT);
}

void loop() {
  Check_Busy();
  Serial.begin(2400);
  Serial.print(Say);
  Serial.print("Hello");
  Serial.print(EOM);
  delay(5000);
}

void Check_Busy() {
  Serial.begin(9600);
  Serial.println("Checking Busy...");
  //Serial.begin(2400);
  delay(1);
  Busy = digitalRead(BusyPin);
  do {
    Serial.println(Busy, DEC);
    Busy = digitalRead(BusyPin);
  } while(Busy, DEC == 1);
  Serial.begin(9600);
  Serial.println("Busy Done");
}

You don't say what pins you have the emic hooked to but it looks like you have the serial terminal and the emic talking on the same port. The emic has three pins that I think should hook to digital pins and you will need to use software serial to talk to it.

Oh sorry. I copied these parts from a larger bit of code, so I forgot to copy those (so that's not the problem here, but thanks for noticing). The code should have been copied to look like this:

// Emic TTS Commands
#define Say 0x00
#define Volume 0x01
#define Speed 0x02
#define Reset 0x08
#define PhT 0x10
#define Help 0xFE
#define EOM 0xAA

#define OK 0x55
#define Yes 1
#define No 0

int BusyPin = 7; // <---- This is what I missed
int Rst = 6;

byte vol;
byte spd;
byte ptch;
boolean Busy;
int timeout;
int Data;

void setup() {
     Serial.begin(2400); //Emic communicates at 2400Baud
     pinMode(BusyPin, INPUT);
}

void loop() {
  Check_Busy();
  Serial.begin(2400);
  Serial.print(Say);
  Serial.print("Hello");
  Serial.print(EOM);
  delay(5000);
}

void Check_Busy() {
  Serial.begin(9600);
  Serial.println("Checking Busy...");
  //Serial.begin(2400);
  delay(1);
  Busy = digitalRead(BusyPin);
  do {
    Serial.println(Busy, DEC);
    Busy = digitalRead(BusyPin);
  } while(Busy, DEC == 1);
  Serial.begin(9600);
  Serial.println("Busy Done");
}

Remember, unlike the BS2 models, the Arduino can't do full serial on every pin, so you don't have to declare a 'TX' pin; SIN on the Emic TTS is connected right to TX on the Arduino. What could be wrong??

Let me add this; it may help.

I know the problem is one of two things:

  1. It has to do with the subroutines (Check_Busy)
    or...
  2. It has to do with the way the data is sent via serial to the TTS module. I know that serial communication is done a little differently between the Arduino (ATmegaxxx) and the BASIC Stamps.

And I don't think the fact that the Emic TTS and the Arduino + my PC share the same TX/RX lines should be a problem...It seems that it would be too easy if that's what is wrong...

I think I would take out all the "junk" in the checkbusy and make it as simple as the BS code. Check the busy line till it's low then continue. Don't change baud as that resets things.

I have to say that calling Serial.begin with different baud rates over and over to talk simultaneously to two different devices looks mighty strange to me.

That aside, there is certainly a bug in Check_Busy. The loop

do {
    Serial.println(Busy, DEC);
    Busy = digitalRead(BusyPin);
  } while(Busy, DEC == 1);

will never execute more than once because the expression "Busy, DEC == 1" evaluates to "Busy, false", which in turn evaluates to "false". You probably would be better off with "while (Busy)" or "while (Busy == HIGH)".

Mikal

A very common mistake when hooking Arduino up to other devices with serial is to forget to connect Arduinos ground to the other device's ground. This is important because the voltage levels on the TX and RX lines are referenced to a common ground. If no common ground exists strange behaviour can happen, like communication not working at all, or communication working only in one direction, or garbeld data being recieved.

Ok. Switch gears


I am now using the BASIC stamp which controls the Emic TTS module. The arduino sends the BS2 commands to play certain text strings. When the BS2 gets a command (it comes as a number 1 to 8), it compares it to an If...Then sort-of statement, then plays the appropriate text. Here's the BS2 code:

' =========================================================================
'
'   File...... EasyEmic.BS2
'   Purpose... Simple Emic TTS demonstration
'   Author.... Parallax, Inc. (Copyright (c) 2004, All Rights Reserved)
'   E-mail.... support@parallax.com
'   Started...
'   Updated... 28 APR 2004
'
'   {$STAMP BS2e}
'   {$PBASIC 2.5}
'
' =========================================================================

' -----[ Program Description ]---------------------------------------------
'
' Simple Emic TTS speech demo.  Be sure to set both Emic DIP switches to
' the OFF (down) position.

' -----[ Variables ]-------------------------------------------------------

vol             VAR     Nib                     ' current volume
spd             VAR     Nib                     ' current speed
ptch            VAR     Nib                     ' current pitch

Dta          VAR     Word
Prev         VAR     Word

' -----[ I/O Definitions ]-------------------------------------------------

ArdRx           PIN     1
Tx              PIN     4                       ' connects to Emic SIn
Rx              PIN     5                       ' connects to Emic SOut
Busy            PIN     6                       ' 1 = busy
Rst             PIN     7

' -----[ Constants ]-------------------------------------------------------

Yes             CON     1
No              CON     0
BaudR           CON    84

' Emic Commands (Hex Mode)

Say             CON     $00                     ' say Engish text
Volume          CON     $01                     ' set volume, 0 - 7
Speed           CON     $02                     ' set speed, 0 - 4
Pitch           CON     $03                     ' set pitch, 0 - 6
AddAbbr         CON     $04                     ' add abbreviation
DelAbbr         CON     $05                     ' delete abbreviation
ListAbbr        CON     $06                     ' list abbreviations
Version         CON     $07                     ' get version
Reset           CON     $08                     ' soft reset
Audio           CON     $09                     ' enable audio in
PhT             CON     $10                     ' start of phonetic text
Help            CON     $FE                     ' display help
EOM             CON     $AA                     ' end of message

OK              CON     $55                     ' "okay" for hex mode

#SELECT $STAMP
  #CASE BS2, BS2E
    T2400       CON     396
    TmAdj       CON     $100                    ' x 1.0 (note time adjust)
    FrAdj       CON     $100                    ' x 1.0 (note freq adjust)

  #CASE BS2SX
    T2400       CON     1021
    TmAdj       CON     $280                    ' x 2.5
    FrAdj       CON     $066                    ' x 0.4

  #CASE BS2P
    T2400       CON     1021
    TmAdj       CON     $3C5                    ' x 3.77
    FrAdj       CON     $044                    ' x 0.265

  #CASE BS2PE
    T2400       CON     396
    TmAdj       CON     $100
    FrAdj       CON     $0AA                    ' x 0.665
#ENDSELECT

Baud            CON    T2400


' -----[ Program Code ]----------------------------------------------------
INPUT 8
DEBUG "Running"
GOSUB Hard_Reset

'INPUT ArdRx

Main:

  GOSUB Check_Busy
  SEROUT Tx, Baud, [Say, "Main menu; please choose a selection.", EOM]
  DO
  SERIN ArdRx, BaudR, [DEC Dta]
  DEBUG DEC Dta
  GOSUB Make_Selection
  LOOP

Make_Selection:

  SELECT Dta
    CASE 1
      GOSUB Check_Busy
      SEROUT Tx, Baud, [Say, "Mood light.", EOM]
      RETURN
    CASE 2
      GOSUB Check_Busy
      SEROUT Tx, Baud, [Say, "Candle.", EOM]
      RETURN
    CASE 3
      GOSUB Check_Busy
      SEROUT Tx, Baud, [Say, "Water Reflections.", EOM]
      RETURN
    CASE 4
      GOSUB Check_Busy
      SEROUT Tx, Baud, [Say, "Seasonal colors.", EOM]
      RETURN
    CASE 5
      GOSUB Check_Busy
      SEROUT Tx, Baud, [Say, "Lightning storm.", EOM]
      RETURN
    CASE 6
      GOSUB Check_Busy
      SEROUT Tx, Baud, [Say, "Stop light.", EOM]
      RETURN
    CASE 7
      GOSUB Check_Busy
      SEROUT Tx, Baud, [Say, "Mimic light.", EOM]
      RETURN
    CASE 8
      GOSUB Check_Busy
      SEROUT Tx, Baud, [Say, "Sound light.", EOM]
      RETURN
  ENDSELECT
  RETURN


' -----[ Subroutines ]-----------------------------------------------------

' Check status of Emic TTS module
' -- wait until Busy line released by Emic
' -- code as written does not timeout

Check_Busy:
  PAUSE 1                                       ' allow busy to activate
  DO WHILE (Busy = Yes) : LOOP                  ' wait until not busy
  RETURN

Soft_Reset:
  GOSUB Check_Busy                              ' wait for busy to release
  SEROUT Tx, Baud, [Reset, EOM]                 ' send soft reset
  GOSUB Wait_OK
  vol = 4                                       ' set pgm vars to defaults
  spd = 2
  ptch = 1
  RETURN

Hard_Reset:                                     ' reset to default values
  LOW Rst                                       ' pull reset line low
  PAUSE 0                                       ' ~100 uS pause
  INPUT Rst                                     ' let reset float
  GOSUB Wait_OK
  vol = 4                                       ' set pgm vars to defaults
  spd = 2
  ptch = 1
  RETURN

Wait_OK:
  SERIN Rx, Baud, 1000, TO_Error, [WAIT(OK)]
  RETURN

TO_Error:
  DEBUG CLS, "No response from Emic TTS."       ' ruh-roh, Shaggy
  END

And the whole code for the arduino is too large to copy, but you only need to see this:

      for(i = 0; i < 5; i++) {
        Serial.println(8, DEC);
          delay(10);
      }

Each time you change a function (on this project; it's a mood light with different functions that can be chnged via user input), the Arduino sends the BS2 the appropriate command. The BS2 is then SUPPOSED to receive it, but it almost never does. Why? It's always 'listening' on the Rx pin (I used pin 1 for receiving the commands)...The odd thing is, if it DOES work, it only receives the first command, then no longer receives any more. Does this have to do with baud rate? Data format (HEX, ASCII, DEC, etc.)? If I could get this problem solved I'd be so relieved!

Again :slight_smile: did you connect the BS and Arduinos grounds together ?

I recently tried getting a serial LCD device working with the Arduino that I had left over from a BS2 project and couldnt get it working.
It was down to the serial voltages.
Arduino works at TTL voltages and the stamp used full RS232 voltages.
I couldnt work it out till someone on this forum pointed this out to me.

Gordon

You don't say how you have things connected. What is attached to the stamp pin 7?

I don't think the BS2 and the arduino operate on different logic levels...they both use TTL... (and BTW MikMo, yes, they are both connected to common GND). In the BS2 manual, it says that the BS2 operates on TTL logic, NOT RS232. (look here: http://www.parallax.com/Portals/0/Downloads/docs/prod/stamps/basicstampfaq.pdf). Please correct me if I'm wrong with any of this; logic level stuff still confuses me.

Pin 7 is connected to the RESET pin on the Emic TTS module.

mikalhart:

"I have to say that calling Serial.begin with different baud rates over and over to talk simultaneously to two different devices looks mighty strange to me."

Ok- good thinking. I changed that, though, and it didn't do any good.

What do I do?

Let me also say that the PDF for the Emic TTS says that it, too uses TTL logic, so if I have to use just the Arduino + the TTS module, I guess it's possible...

For those of you having problems with EMIC text to speech:

(1)Set the EMIC module dip switch 1 to ON position.
This will allow you to send ascii rather than hex variables.

(2) Make sure you connect Arduino "TX" Pin to EMIC "SIN" Pin.

Here is a sample code that loops the sound "Oklahoma":

const int busypin = 2; //the busy pin set to digital pin 2
//to check if TTS is busy

void setup()
{
pinMode (busypin, INPUT); //make the busy pin input

Serial.begin(2400);
}

void loop()
{
{
if (busypin == HIGH) // if the EMIC is busy
{
delay(1); //delay for 1 millisecond
}
else
{
Serial.print("say=OKlahoma;"); //else print OKLAHOMA
delay(1000); //pause for a second
}

}
}

Have FUN!!! ;D

I have much better code now; I'm using the Emic TTS Module now for a robot I am building.

Here's what was wrong: You MUST put the ', BYTE' constructor after the commands (which sends them one byte at a time). That is the ONLY way the Emic will accept commands, otherwise it will not respond. Hence the 'Say' command looks like this:

Serial.print(Say, BYTE);

Here's all my code that I 'translated' from PBASIC to Arduino/Semi-C:

// Emic TTS Commands
#define Say 0x00
#define Volume 0x01
#define Speed 0x02
#define Reset 0x08
#define PhT 0x10
#define Help 0xFE
#define EOM 0xAA

#define OK 0x55

byte vol;
byte spd;
byte ptch;
boolean Busy;
int timeout;
int Data;

int BusyPin = 10;

void setup() {
     delay(100);
     Serial.begin(2400); //Emic communicates at 2400Baud
     pinMode(BusyPin, INPUT);
     Check_Busy();
     Serial.print(Reset, BYTE);
     delay(1000);
}

void loop() {
  Check_Busy();
  Serial.print(Say, BYTE);
  Serial.print("Hello world. This is a test. Test successful.");
  Serial.print(EOM, BYTE);
  delay(6500);
}

void Check_Busy() {
  delay(1);
  Busy = digitalRead(BusyPin);
  do {
    digitalWrite(13, Busy);
    Busy = digitalRead(BusyPin);
  } while(Busy);
}

Anyone who wants to use the Emic TTS Module with the Arduino, that's the code you'll need!

P.S. You need to 'Check_Busy' before each and every block of commands you send.