Home‎ > ‎DroidBuilder's Tutorials‎ > ‎

Advanced SpeakJet Shield Basic Programming

Please note:
As of Arduino 1.0 some changes were made to the print function and byte keyword. Therefore some changes are required in the code presented below to get them to run in Arduino 1.0! Lines such as
speakJet.print(223, BYTE); need to be changed to speakJet.write(223); (essentially "print" changes to "write"; and "BYTE" is removed). Also note that the .pde extension in the filename has been changed to .ino in Arduino 1.0 (the .pde extension was also used in Processing sketches and the extension changed  in Arduino 1.0 to avoid further confusion)!

Notes for SpeakJet Shield v1.3 or later:
wherever #define txPin 2  appears, change it to #define txPin 6
wherever #define rxPin 3 appears, change it to #define rxPin 5
wherever #define busyPin 4 appears, change it to #define busyPin 7

This tutorial will cover a few more advanced programming techniques for the SpeakJet Shield Basic. This will include some simple examples of using handshaking, changing voices, sound effects, and singing.

If you have not gone through the "Programming the SpeakJet Shield Basic" tutorial, I suggest doing so now. This tutorial expands upon that tutorial; and I don't wish to repeat much of the information already given.


Check the SpeakJet Shield Basic configuration:

SpeakJet Shield Basic configuration (click to enlarge image).

Note that this configuration can also be used for the SpeakJet Shield TTS, but that text-to-speech capability is not available in this configuration.



SpeakJet Shield Basic Sound Effects:

The SpeakJet Shield Basic (or the TTS configured as a Basic) is capable of an almost infinite variety of sound effects. If you look at page 16 of the SpeakJet User Manual (in Table E); you will note that MSA codes 128 through 199 are speech sounds, and that codes 200 through 254 are predefined sound effects. These predefined codes make it very easy to teach your SpeakJet Shield Basic to make some very cool sound effects!

This first demo is very simple. MSA code 252 is predefined to create a "sonar ping" sound effect. When looped with a short delay, it  sounds rather convincing! 
Note that in the following sketch I highlighted the "ping" code in yellow to show how simple this can be. The code bytes previous to that are my "standard setup" codes for the SpeakJet. I always use these so that my SpeakJet is always in a known state before I create a sound or speech effect.

This sketch is complete; ready to compile and upload to your Arduino (you can also download the "SonarDemo.pde" sketch from the "Attachments" section at the bottom of this page).

// SonarDemo.pde
// A demo showing a minimal sketch to create a sound effect for the SpeakJet
// Basic Shield

// written by Galen Raben / www.droidbuilder.com 5-5-2010 updated 1-8-2013 for SJ shield v1.3

// set up a new software serial port
#include <SoftwareSerial.h>
//rxPin: the pin on which to receive serial data
//txPin: the pin on which to transmit serial data
// SpeakJet Shield v1.0-1.2 uses these pins:
//#define txPin 2
//
#define rxPin 3
// SpeakJet Shield v1.3 or later uses these pins:
#define rxPin 5
#define txPin 6



// set up the SoftwareSerial port to connect to the SpeakJet
SoftwareSerial speakJet SoftwareSerial(rxPin, txPin);

// Sonar Ping (MSA code 252) 
/* 20, 96, 21, 114, 22, 88, 23, 5, 252
   v       s        p       b       M
   o       p        i       e       0
   l       e        t       n
           e        c       d 
           d        h
 */  //byte array holding sound/speech data
byte pingSound[] = {20, 96, 21, 114, 22, 88, 23, 5, 252}; // sonar ping

void setup() {
  // define pin modes for tx, rx pins:
  // pinMode(rxPin, INPUT); //not needed for this demo
  pinMode(txPin, OUTPUT);
  speakJet.begin(9600);
  delay(1000); // wait a second for the Arduino resets to finish (says "ready")
}

void loop() {
  // continuous sonar ping sounds
  for (int i=0; i<sizeof(pingSound); i++) {
    speakJet.print(pingSound[i], BYTE);  // send it to the speakJet
  }
  delay(2000); // delay two seconds before making ping again...
}

Now lets try doing something a bit more interesting, combining speech with a sound effect!

Note that I couldn't get the explanation of the SpeakJet codes to fit the format of my web site! The Arduino demo sketch shown here will compile and run properly, but if you want to see the SpeakJet code explanations,  you will need to download "AlertDemo.pde" attached to the bottom of this page.

// AlertDemo.pde
// A demo demonstrating combined sound and speech effect for the SpeakJet Basic Shield
// written by Galen Raben / www.droidbuilder.com 5-8-2010 updated 1-8-2013 for SJ shield v1.3


// set up a new software serial port
#include <SoftwareSerial.h>
//rxPin: the pin on which to receive serial data
//txPin: the pin on which to transmit serial data
// SpeakJet Shield v1.0-1.2 uses these pins:
//#define txPin 2
//
#define rxPin 3
// SpeakJet Shield v1.3 or later uses these pins:
#define rxPin 5
#define txPin 6

// set up the SoftwareSerial port to connect to the SpeakJet
SoftwareSerial speakJet =  SoftwareSerial(rxPin, txPin);

//intruder alert
byte alert[] = {20, 96, 21, 114, 22, 88, 23, 5, 26, 3, 217, 129, 141, 191, 148, 139, 7, 174, 7, 133, 7, 151, 133, 145, 151, 191};

void setup() {
  // define pin modes for tx, rx pins:
  // pinMode(rxPin, INPUT); //not needed for this demo
  pinMode(txPin, OUTPUT);
  speakJet.begin(9600);
  delay(1000); // wait a second for the Arduino resets to finish
}

void loop() {
  // continuous alert sounds
  for (int i=0; i<sizeof(alert); i++) {
    speakJet.print(alert[i], BYTE);  // send it to the speakJet
  }
  delay(5000); // delay five seconds before playing alert again...
}

It is also possible to program the SpeakJet ICs' synthesizers and mixers using SCP (Serial Control Protocol) commands to directly mix your own sound effects (see pages 5-11 of the SpeakJet User Manual). I have not yet had time to play with this, but I expect it is worthy of being its own tutorial! If someone has done this with their SpeakJet Shield Basic, and cares to send me some working examples, I would be able to get a tutorial up sooner rather than later!

Handshaking on the SpeakJet Shield Basic:

Two types of handshaking is available on the SpeakJet Shield Basic and TTS; "Buffer-half" and "Busy". Handshaking is often needed when you have long sequences of speech and sound effects. If the speech you are trying to generate stutters , stops or makes unexpected noises partway through the sequence; it is likely the buffer of the SpeakJet IC has overflowed, this indicates you need to use handshaking. For the examples shown in this tutorial, I generally use "Busy" handshaking, but you may also use "Buffer-Half"; the effect on the resulting speech is similar.

To implement handshaking, you will need to add the following code to your Arduino speech sketch.
Add to the sketch defines:
#define busyPin 4
note on SpeakJet v1.3 and later this has changed to 
#define busyPin 7

Add to sketch setup section:
pinMode(busyPin, INPUT);

Add this function to the sketch:
void SJBusy(){
  delay(20); // wait 12ms minimum before checking SpeakJet busy pin
  while(digitalRead(busyPin)){
    delay(250); // wait here while SpeakJet is busy (pin 4 is true)

  }
  delay(250); // a bit more delay
}

And wherever in your sketch you need to wait for the SpeakJet to catch up:
SJBusy();

The following is a short example using a modified version of my "HelloWorld.pde" sketch showing this technique. Normally, this sketch would overflow the SpeakJet buffer and start stuttering within several  iterations (about 10 seconds); but the added handshake code allows it to loop forever without stuttering. Following is the complete sketch ready to compile and upload to your Arduino (you can also download the "BusyHelloWorld.pde" sketch from the "Attachments" section at the bottom of this page).

// BusyHelloWorld.pde - a modified version of HelloWorld.pde
// a demo showing example usage of SpeakJet Busy line monitoring
// written by Galen Raben / www.droidbuilder.com 5-4-2010 updated 1-8-2013 for SJ shield v1.3

// set up a new software serial port
#include <SoftwareSerial.h>
//rxPin: the pin on which to receive serial data
//txPin: the pin on which to transmit serial data
//busyPin: the pin used to monitor SpeakJet Busy line
// SpeakJet Shield v1.0-1.2 uses these pins:
#define txPin 2
#define rxPin 3
#define busyPin 4
// SpeakJet Shield v1.3 or later uses these pins:
#define rxPin 5
#define txPin 6
#define busyPin 7

// set up the SoftwareSerial port to connect to the SpeakJet
SoftwareSerial speakJet = SoftwareSerial(rxPin, txPin);

/* HELLO WORLD SpeakJet MSA phonemes (see page 16 of SpeakJet User Manual)
 20, 96, 21, 114, 22, 88, 23, 5, 183, 7, 159, 146, 164, 147, 151, 145, 176
 v       s        p       b       H   f   E    L    O    W    A    L    E
 o       p        i       e       E   a   H    O    W    W    X    E    D
 l       e        t       n           s   L         W         R
         e        c       d           t   L         W         R
         d        h
*/
// char array holding string we want to speak
byte sayThis[] = {20, 96, 21, 114, 22, 88, 23, 5, 183, 7, 159, 146, 164, 147, 151, 145, 176}; //say "hello world"

void setup()
{
  // define pin modes for tx, rx pins:
  //pinMode(rxPin, INPUT); //not needed for this demo
  pinMode(txPin, OUTPUT);
  speakJet.begin(9600);
  delay(1000); // wait a second for the Arduino resets to complete
}

void SJBusy(){
  delay(20); // wait 12ms minimum before checking SpeakJet busy pin
  while(digitalRead(busyPin)){
    delay(250); // wait here while SpeakJet is busy (pin 4 is true)
  }
  delay(250); // a bit more delay
}

void loop()
{
  // send it to the SpeakJet
  for (int i=0; i<sizeof(sayThis); i++){
    speakJet.print(sayThis[i], BYTE);
  }
  SJBusy();
}
Note: If you would like to see an example of what happens when the SpeakJet buffer overflows; comment out "SJBusy();" in the next-to-last line of this sketch. Re-compile and re-upload the sketch to your Arduino. The SpeakJet will typically start stuttering within 10 seconds after the sketch starts running.


Changing Voices on the SpeakJet Shield Basic:

The SpeakJet Shield Basic is capable of creating a number of different voices. These voices can be changed on-the-fly and could be used to make the SpeakJet Shield speech less robotic or even (as seen further below) for singing!

The following is (almost) a minimal Arduino sketch for the SpeakJet Shield Basic illustrating a voice effect. If you are a fan of the StarWars "CloneWars" cartoon series, you may recognize the voice (at least I think I got pretty close). This sketch is complete and ready to upload and compile to your Arduino (you can also download "DroidDemo.pde" sketch from the "Attachments" section at the bottom of this page). As written, this sketch will repeat the phrase continuously.

// DroidDemo.pde
// SpeakJet StarWars Droid voice effect demo
// shows how to do a simple voice effect
// written by Galen Raben / www.droidbuilder.com 4-29-2010 updated 1-8-2013 for SJ shield v1.3

// set up a new software serial port
//rxPin: the pin on which to receive serial data
//txPin: the pin on which to transmit serial data
#include <SoftwareSerial.h>
#define txPin 2
#define rxPin 3
// set up the SoftwareSerial port to connect to the SpeakJet
SoftwareSerial speakJet =  SoftwareSerial(rxPin, txPin);

/* ROGER-ROGER (STARWARS DROID)
20, 96, 21, 120, 22, 120, 23, 12, 148, 135, 165, 151, 8, 148, 135, 165, 151
v       s        p        b       \RR  \OH  \JH \AXRR s  \RR  \OH  \JH  \AXRR
o       p        i        e
l       e        t        n
        e        c        d
        d        h
*/
// byte array holding speech data
byte sayThis[] = {20, 96, 21, 120, 22, 120, 23, 12, 148, 135, 165, 151, 8, 148, 135, 165, 151}; //"roger roger"

void setup() {
  // define pin modes for tx, rx pins:
  // pinMode(rxPin, INPUT); //not needed for this simple demo
  pinMode(txPin, OUTPUT);
  speakJet.begin(9600);
  delay(1000); // wait a second for the Arduino resets to complete
}

void loop() {
  // send it to the SpeakJet
  for (int i=0; i<sizeof(sayThis); i++) {
    speakJet.print(sayThis[i], BYTE);
  }
  delay(3000); // wait 3 seconds
}

The following example sketch demonstrates the effects of first varying the "pitch" value and then varying the "bend" value. This sketch is complete and ready to upload and compile to your Arduino (you can also download the speakjet_pitch.pde sketch from the "Attachments" section at the bottom of this page).

//speakjet_pitch.pde
// a demo showing the effect of changing pitch and bend values
// 5-4-2010 by Galen Raben / droidbuilder.com updated 1-8-2013 for SJ shield 1.3 
// modify pitch variable

// set up a new software serial port
#include <SoftwareSerial.h>
//rxPin: the pin on which to receive serial data
//txPin: the pin on which to transmit serial data
//busyPin: the pin used to monitor SpeakJet Busy line
// SpeakJet Shield v1.0-1.2 uses these pins:
#define txPin 2
#define rxPin 3
#define busyPin 4
// SpeakJet Shield v1.3 or later uses these pins:
#define rxPin 5
#define txPin 6
#define busyPin 7


// set up SoftwareSerial port
SoftwareSerial speakJet =  SoftwareSerial(rxPin, txPin);

void setup()
{
  // define pin modes for tx, rx pins:
  //pinMode(rxPin, INPUT);
  pinMode(txPin, OUTPUT);
  speakJet.begin(9600);
}

/* HELLO WORLD SpeakJet MSA phonemes (see page 16 of SpeakJet User Manual)
 20, 96, 21, 114, 22, 88, 23, 5, 183, 7, 159, 146, 164, 147, 151, 145, 176
 v       s        p       b       H   f   E    L    O    W    A    L    E
 o       p        i       e       E   a   H    O    W    W    X    E    D
 l       e        t       n           s   L         W         R
         e        c       d           t   L         W         R
         d        h
default pitch=88 */
byte prefix[] = {20, 96, 21, 114, 22, 88,  23, 5}; // this is the normal values for setup
byte message[] = {183, 7, 159, 146, 164, 147, 151, 145, 176}; //say "hello world"

void loop() {
  // reset to normal settings
  speakJet.print(20, BYTE); // vol
  speakJet.print(96, BYTE);
  speakJet.print(21, BYTE); // speed
  speakJet.print(114, BYTE);
  speakJet.print(22, BYTE); // pitch
  speakJet.print(88, BYTE);
  speakJet.print(23, BYTE); // bend
  speakJet.print(5, BYTE);
  for (int i=0; i<sizeof(message); i++){ // rest of speech data
    speakJet.print(message[i], BYTE);
  }
  SJBusy();
 
  // vary pitch
  for (byte pitch=1; pitch<16; pitch++){
    speakJet.print(20, BYTE); // vol
    speakJet.print(96, BYTE);
    speakJet.print(21, BYTE); // speed
    speakJet.print(114, BYTE);
    speakJet.print(22, BYTE); // pitch
    speakJet.print(pitch*16, BYTE);
    speakJet.print(23, BYTE); // bend
    speakJet.print(5, BYTE);
    for (int i=0; i<sizeof(message); i++){ // rest of speech data
      speakJet.print(message[i], BYTE);
    }
    SJBusy();
  }
 
  // vary bend
  for (byte bend=0; bend<15; bend++){
    speakJet.print(20, BYTE); // vol
    speakJet.print(96, BYTE);
    speakJet.print(21, BYTE); // speed
    speakJet.print(114, BYTE);
    speakJet.print(22, BYTE); // pitch
    speakJet.print(88, BYTE);
    speakJet.print(23, BYTE); // bend
    speakJet.print(bend, BYTE);
    for (int i=0; i<sizeof(message); i++){ // rest of speech data
      speakJet.print(message[i], BYTE);
    }
    SJBusy();
  }
  delay(3000); // wait 3 seconds before looping again
}

void SJBusy(){
  delay(20); // wait 12ms minimum before checking SpeakJet busy pin
  while(digitalRead(busyPin)){
    delay(250); // wait here while SpeakJet is busy (busyPin is true)
  }
  delay(250); // a bit more delay
}


The SpeakJet Shield Basic can Sing, Rap and Rock n' Roll!

One of the questions I am sometimes asked is how I did the Daisy Bell song in my original demo video. The answer is about to be revealed! Essentially, this is a variation of "Changing Voices" sketch shown earlier in this tutorial. I also must give credit to "cryahoo" for posting their Daisy Bell song code to Yahoo's SpeakJet Group. The sketch shown here was adapted for the Arduino from that example. Note that this technique is useful for much more than just singing!

Note that I am showing only pieces of the sketch here to illustrate the concepts; the entire sketch is rather lengthy! You can download the entire "DaisyDemo.pde" sketch which is attached to the bottom of this page.

First we define frequencies based on the diatonic musical scale, these will be used for our voices:
#define C1 33
#define Cs1 35 // C#1
#define D1 37
#define Ds1 39 // D#1
#define E1 41
... and so on.

Next we define the SpeakJet speech elements (as seen in the SpeakJet User Manual):
#define IY 128
#define IH 129
#define EY 130
#define EH 131
#define AY 132
... and so on.

Next we define the SpeakJet control codes (as seen in the SpeakJet User Manual):
#define P0 0
#define P1 1
#define P2 2
#define P3 3
#define P4 4
... and so on.

Then we define the speech patterns for our song:
// daisy, daisy
byte daisy1[] = {20,96,21,114,22,88,23,5,31,SPD,114,PIT,D3,DE,SPD,22,EYIY,SPD,114,PIT,B2,
ZZ,SPD,5,IY,SPD,114,2,1,PIT,G2,DE,SPD,22,EYIY,SPD,114,PIT,D2,ZZ,SPD,5,IY,
SPD,114,2,1};

... and so on.

And finally the playback loop looks like this:
for (int i=0; i<sizeof(daisy1); i++){
  speakJet.print(daisy1[i], BYTE);
}
SJBusy();

Yes, we use the SJBusy() function I mentioned earlier. Singing requires a large number of speech sounds, which can easily overwhelm the SpeakJets' buffer. You could put delay() functions in, but each one has to be carefully timed or the song will sound "broken". Use of the SJBusy() function makes this much easier. I generally put them in wherever there is normally a pause in the song anyway (I believe the buffer can hold 64 bytes, so an SJBusy()  should be placed at some point prior to the 64 byte boundary).



Creative Commons License
SpeakJet Shield TTS/Basic by Galen Raben is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License.
Permissions beyond the scope of this license may be available at http://www.droidbuilder.com.

Code samples given in this document are released into the public domain.

© 2011 Galen Raben/DroidBuilder.com

ċ
AlertDemo.pde
(2k)
Galen Raben,
May 8, 2010, 3:12 PM
ċ
BusyHelloWorld.pde
(2k)
Galen Raben,
May 5, 2010, 8:31 AM
ċ
DaisyDemo.pde
(6k)
Galen Raben,
May 4, 2010, 9:03 PM
ċ
DroidDemo.pde
(1k)
Galen Raben,
May 4, 2010, 10:10 PM
ċ
R2D2demo.pde
(2k)
Galen Raben,
May 5, 2010, 8:29 PM
ċ
SJSounds.pde
(2k)
Galen Raben,
May 8, 2010, 9:45 PM
ċ
SonarDemo.pde
(1k)
Galen Raben,
May 5, 2010, 8:29 PM
ċ
speakjet_pitch.pde
(3k)
Galen Raben,
May 4, 2010, 9:02 PM