On-Robot JavaScript API Tutorials

In these tutorials you will learn everything you need to know to begin writing skills using Misty's on-robot JavaScript API. Each tutorial introduces a new aspect of skill development to expose the full breadth of Misty's capabilities and potential.

Time-of-Flight

In this tutorial we create a simple skill that changes Misty’s chest LED, drives her forward for 10 seconds, and tells her to stop if she detects an object in her path. We go over how to send commands, subscribe to sensor events, and structure your skill data. Let’s get started!

When you write a skill using Misty's on-robot JavaScript API, the following elements are required:

  • a .js "code" file with the logic used to define how the skill functions
  • a .json "meta" file with rules that describe how Misty should execute the code in the corresponding "code" file.

The JavaScript "code" and JSON "meta" files for a skill must be given the same name.

To begin, create a .js file and call it HelloWorld_TimeOfFlight.js. Then create a .json file and give it the same name. When the skill is complete, we use Skill Runner to upload these files to the directories specified above.

Writing the Meta File

The .json file includes fields that set certain specifications for your skill. You need to create a GUID to uniquely identify your skill, which can be generated here. Use this to set the value of UniqueId. In the Name field, enter the name used for the .js file and the .json file (HelloWorld_TimeOfFlight) to ensure they are referenced correctly. Many of the parameters below have default values that are automatically set. For now, use the values shown in the example for the remaining fields. Save this file with the name HelloWorld_TimeOfFlight.json.

{
    "Name": "HelloWorld_TimeOfFlight",
    "UniqueId" : "652d4346-1b17-4515-974d-ce7ff901e3a1",
    "Description": "Time-of-flight tutorial skill",
    "StartupRules": ["Manual", "Robot"],
    "Language": "javascript",
    "BroadcastMode": "verbose",
    "TimeoutInSeconds": 300,
    "CleanupOnCancel": false,
    "SkillStorageLifetime": "Reboot",
    "WriteToLog": false,
    "Parameters": { }
}

Writing the Code File

To issue any command to Misty in the local environment, we call methods on the misty object. Start by writing a debug message so we’re notified when the skill is started. To do this, call misty.Debug() and pass in a meaningful message. These messages will show up in your browser’s JavaScript console if you’re using the Skill Runner tool to run the skill.

misty.Debug("starting skill helloworld_timeofflight");

Now let’s use the very simple misty.ChangeLED() function to control the color of your robot’s chest LED. The method takes three arguments, which correspond to the RGB parameters required to specify the LED color as described here. The code below will turn the LED green (for go!)

misty.ChangeLED(0, 255, 0);

Then, we issue one of Misty’s drive commands, misty.DriveTime(). The misty.DriveTime() command accepts three parameters: linearVelocity, angularVelocity, and time. You can learn more about how these parameters will affect Misty’s movement in the documentation. In this case, we want Misty to drive forward slowly in a straight line for 10 seconds, so we set linearVelocity = 50, angularVelocity = 0, and time = 10000 (the unit of measure for this parameter is milliseconds).

misty.DriveTime(50, 0, 10000);

We’ve instructed Misty to drive forward for 10 seconds and come to a stop. But there’s a major flaw in this code. What if there is an object in Misty’s way? We want her to come to a stop, rather than plowing through it. In order to handle this type of event, we need to look at data coming back from Misty’s sensors. To do this we will subscribe to events from one of Misty’s many websocket streams available to us: namely TimeOfFlight.

Once we have subscribed to TimeOfFlight, we’ll receive event data back from Misty telling us how far objects are away from her. See the template for registering for an event here:

misty.RegisterEvent(string eventName, string messageType, int debounce, [bool keepAlive = false], [string callbackRule = “synchronous”], [string skillToCall = null]);

We call misty.RegisterEvent() and pass in the name we want to designate for the event ("FrontTOF"), the name of the WebSocket stream we are subscribing to ("TimeOFFlight"), and the debounce time in milliseconds (250). By default, when a callback triggers for an event, the event is automatically unregistered. Make sure your register event method matches the code snippet below.

misty.RegisterEvent("FrontTOF", "TimeOfFlight", 250);

Before we register to the event in our code, we can add property comparison tests to filter the data we receive. In this example, the first property test checks that we are only looking at data from the time-of-flight sensor we’re concerned with. The field we’re testing is SensorPosition and we’re checking that the data received is only coming from the time-of-flight sensor in the front center of Misty’s base, pointing in her direction of travel. Therefore, we only let through messages where SensorPosition == Center.

misty.AddPropertyTest("FrontTOF", "SensorPosition", "==", "Center", "string");

The second property test ensures we are only looking at data where the distance to the object detected is less than 0.2m. We don’t want our skill to react to things further away than about 6 inches.

misty.AddPropertyTest("FrontTOF", "DistanceInMeters", "<=", 0.2, "double");

Whenever we subscribe to an event, we receive the data back within a callback function. This function is triggered whenever messages are sent that pass the property tests. The callback is automatically given the name _<event>. So in this case, the callback name is automatically set to _FrontTOF(). The data is passed directly into the callback and can accessed through an argument passed into _FrontTOF(), which we’ll call data.

function _FrontTOF(data) {

}

Once we receive the data via the callback, we can access the distance the object was detected and the sensor position it was detected at.

function _FrontTOF(data) {
   let frontTOF = data.PropertyTestResults[0].PropertyParent;
   misty.Debug(“Distance: ” + frontTOF.DistanceInMeters);
   misty.Debug(“Sensor Position: ” + frontTOF.SensorPosition);
}

Call misty.Stop() to issue a stop command to Misty, then misty.ChangeLED() and pass in the values (255, 0, 0) to turn the LED red (for stop!) and log a message to notify us that the skill has finished.

misty.Stop();
misty.ChangeLED(255, 0, 0);
misty.Debug("ending skill helloworld_timeofflight");

Save the code file with the name HelloWorld_TimeOfFlight.js. See the documentation on using Misty Skill Runner or the REST API to load your skill data onto Misty and run the skill from the browser.

See the full JavaScript code file below or download the code from GitHub.

// Print a message to indicate the skill has started
misty.Debug("starting skill helloworld_timeofflight");

// Issue commands to change LED and start driving
misty.ChangeLED(0, 255, 0); // green, GO!
misty.DriveTime(10, 0, 10000);

// Register for TimeOfFlight data and add property tests
misty.AddPropertyTest("FrontTOF", "SensorPosition", "==", "Center", "string");
misty.AddPropertyTest("FrontTOF", "DistanceInMeters", "<=", 0.2, "double");
misty.RegisterEvent("FrontTOF", "TimeOfFlight", 250);

// FrontTOF callback function
function _FrontTOF(data) {
    // Get property test results
    let frontTOF = data.PropertyTestResults[0].PropertyParent;

    // Print distance object was detected and sensor
    misty.Debug(frontTOF.DistanceInMeters);
    misty.Debug(frontTOF.SensorPosition);
    // Issue commands to change LED and stop driving
    misty.Stop();
    misty.ChangeLED(255, 0, 0); // red, STOP!
    misty.Debug("ending skill helloworld_timeofflight ");
}

Play Audio

In this tutorial, we get the list of audio files stored on Misty and play one at random.

Writing the Meta File

Create a new .json meta file for this skill. Set the value of Name to "HelloWorld_PlayAudio". Use the values in the example to fill out the remaining parameters. Save this file with the name HelloWorld_PlayAudio.json.

{
    "Name": "HelloWorld_PlayAudio",
    "UniqueId": "a3190228-c92d-40ec-8014-32dbbae84f74",
    "Description": "Local 'Hello, World!' tutorial series.",
    "StartupRules": [ "Manual", "Robot" ],
    "Language": "javascript",
    "BroadcastMode": "verbose",
    "TimeoutInSeconds": 300,
    "CleanupOnCancel": false,
    "WriteToLog": false
}

Writing the Code File

We start by creating a debug message so we’re notified when the skill starts. Then we call the GetAudioList() method to fetch the list of audio files currently stored on the robot.

misty.GetAudioList();

Each command’s callback is automatically set to be _<COMMAND>. When the data is returned, the callback runs.

function _GetAudioList(data) { 

}

We should check if the data has been received successfully (e.g. is not empty or null) with an if statement. The array of audio file data will be located in the callback response under Result. Let’s save this to a variable: audioArr. Then, we use the JavaScript methods Math.random() and Math.floor() to generate a random whole number from 0 and one less than the length of the audio list:

if(data) {
    let audioArr = data.Result;
    let randNum = Math.floor(Math.random() * audioArr.length);
}

Use the random number to pick a file at random from the list and assign the name of that file to a variable. To see the name of the file that was chosen use misty.Debug() and pass in the audio file name saved in the randSound variable:

let randSound = audioArr[randNum].Name;
misty.Debug(randSound);

Finally, we call another Misty command, PlayAudio(), and pass in the random file name to play the audio clip.

misty.PlayAudio(randSound);

Note: All of this logic needs to be contained within _GetAudioList() to ensure that it does not run until the audio list has been populated.

Save the code file with the name HelloWorld_PlayAudio.js. See the documentation on using Misty Skill Runner or the REST API to load your skill data onto Misty and run the skill from the browser.

See the full JavaScript code file below or download the code from GitHub.

// Print a debug message to indicate the skill has started
misty.Debug("starting skill helloworld_playaudio");

// Issue command to fetch list of audio clips
misty.GetAudioList();

// Callback to handle data returned by GetAudioList()
function _GetAudioList(data) {
    // Check if data was received
    if (data) {
        // Capture the array of files
        let audioArr = data.Result;

        // Generate a random number and use it to choose a filename at 
        // random from the list
        let randNum = Math.floor(Math.random() * audioArr.length);
        let randSound = audioArr[randNum].Name;
        // Print the name of the file
        misty.Debug(randSound);

        // Issue command to play the audio clip
        misty.PlayAudio(randSound);
    }
}

Record Audio

In this tutorial we learn how to record audio and play it back to the user. This involves two very simple commands: StartRecordingAudio() and StopRecordingAudio().

Writing the Meta File

Create a new .json meta file for this skill. Set the value of Name to "HelloWorld_RecordAudio". Use the values in the example to fill out the remaining parameters. Save this file with the name HelloWorld_RecordAudio.json.

{
    "Name": "HelloWorld_RecordAudio",
    "UniqueId": "2697a7f3-0cc5-4180-bb03-84bae827e751",
    "Description": "Local 'Hello, World!' tutorial series.",
    "StartupRules": [ "Manual", "Robot" ],
    "Language": "javascript",
    "BroadcastMode": "verbose",
    "TimeoutInSeconds": 300,
    "CleanupOnCancel": false,
    "WriteToLog": false
}

Writing the Code File

Start by calling StartRecordingAudio() to tell the microphone to start recording. Pass in the name you want to assign to the resulting audio clip. Use Pause() to establish a duration for how long you want the recording to last, then call StopRecordingAudio() to halt the recording process and save the clip. Call Pause() again for 2000ms to give Misty time to save the recording.

   misty.StartRecordingAudio("RecordingExample.wav");
   misty.Pause(5000);
   misty.StopRecordingAudio();
   misty.Pause(2000);

Once the clip has been saved, check that the recording was saved correctly. To do this, call GetAudioList(). As mentioned in previous tutorials, the callback function (automatically named _<COMMAND>) will run once the data is ready to be received.

misty.GetAudioList();
function _GetAudioList(data) {

}

Now that we have the data, we want to check that our recording shows up in the list. Once way to do this is to create a boolean variable to indicate whether the list contains the file. Then, loop through the list and check if the name of any of the audio files match the name of your recording. If it does, change the boolean from false to true. Note: This logic needs to be contained within the callback, as it uses the data received from the GetAudioList() command.

let containsNewFile = false;
for (let i = 0; i < audioArr.length; i++) {
   if (audioArr[i].Name === "RecordingExample.wav") {
      containsNewFile = true;
   }
}

If the list contains the recording, we can call PlayAudio() to play the recording and end the program. Otherwise, use Debug() to print an error message, as something went wrong with the process.

if (containsNewFile) {
   misty.PlayAudio("RecordingExample.wav", 100, 500);
}
else {
   misty.Debug("file was not found");
}

Save the code file with the name HelloWorld_RecordAudio.js. See the documentation on using Misty Skill Runner or the REST API to load your skill data onto Misty and run the skill from the browser.

See the complete JavaScript code below or download the code for this tutorial from GitHub.

// Print a debug message to indicate the skill has started
misty.Debug("starting skill helloworld_recordaudio");

// Send commands to start recording audio, pause for  five seconds 
// to record, then stop recording audio
misty.StartRecordingAudio("RecordingExample.wav");
misty.Pause(5000);
misty.StopRecordingAudio();

// Pause to give Misty time to save the recording
misty.Pause(2000);

// Send request to fetch list of audio files
misty.GetAudioList();

// Define the callback for request
function _GetAudioList(data) {
    // Get the array of audio files from the data returned 
    // by GetAudioList()
    let audioArr = data.Result;

    // Initialize a variable to tell us if the list contains 
    // the recorded audio file
    let containsNewFile = false;
    // Loop through list and compare file names to the
    // name specified for the recording
    for (let i = 0; i < audioArr.length; i++) {
        if (audioArr[i].Name === "RecordingExample.wav") {
            // If there's a match, track it by updating
            // the value of containsNewFile to true
            containsNewFile = true;
        }
    }

    // If list contains recording, issue a command to play the recording
    if (containsNewFile) {
        misty.PlayAudio("RecordingExample.wav", 100, 500);
    }
    else {
        // If the list does not contain the recording, print an error message
        misty.Debug("file was not found");
    }
}

Face Detection

In this tutorial we learn how to use Misty's face detection abilities to trigger an event. If Misty detects a face she will play a sound, change her LED to white, and end the skill. If she does not detect a face within a reasonable amount of time, the LED will turn off, and the skill will end.

Writing the Meta File

Create a new .json meta file for this skill. Set the value of Name to "HelloWorld_FaceDetection". Use the values in the example to fill out the remaining parameters. Save this file with the name HelloWorld_FaceDetection.json.

{
    "Name": "HelloWorld_FaceDetection",
    "UniqueId": "63b2cac5-4674-43ce-a048-670303a339ec",
    "Description": "Local 'Hello, World!' tutorial series.",
    "StartupRules": [ "Manual", "Robot" ],
    "Language": "javascript",
    "BroadcastMode": "verbose",
    "TimeoutInSeconds": 300,
    "CleanupOnCancel": false,
    "WriteToLog": false
}

Writing the Code File

In order to tell if Misty has detected a face, we register an event to receive data from computer vision events. Call misty.RegisterEvent() and pass in a name for the event (this example uses "FaceRecognition" to keep it simple), the data stream we are subscribing to ("FaceRecognition"), and a value specifying how frequently we want to receive data (in this case, every 250 milliseconds).

misty.RegisterEvent("FaceRecognition", "FaceRecognition", 250);

Now that we have the event set up, we can send the command to start face detection. This command is different in that it initiates the process for Misty to start looking for a face, while the event is only set up to trigger if a face is detected. Both parts are necessary to handle skills that include face detection.

misty.StartFaceDetection();

Within the callback (automatically named _FaceRecognition()) we should log a debug message to indicate that a face has been detected, send a command to play an audio clip, and another to change the LED. Then we can send a command to stop face detection. Once the code in this callback finishes, the skill will automatically end after 5 seconds of inactivity.

function _FaceRecognition() {
   misty.Debug("Face detected!”);

   misty.PlayAudio("005-OoAhhh.wav");
   misty.ChangeLED(255, 255, 255); // white
   misty.StopFaceDetection();
};

With what we have so far, the skill will run indefinitely if no face is detected. To make the skill more complete, we need to write code to handle this “no face” situation. To accomplish this, let’s register for a timer event to trigger if no face was detected after 15 seconds. We register for this event just after we register for "FaceRecognition" in our misty.RegisterEvent() method.

misty.RegisterTimerEvent("FaceRecognitionTimeout", 15000);

Then within the callback (again, automatically named _FaceRecognitionTimeout()), we log a debug message to indicate the timeout was called, turn the LED off, and send the command to stop face detection. After this command has been issued, Misty will be inactive and the skill will automatically end after 5 seconds.

function _FaceRecognitionTimeout() {
   misty.Debug("face detection timeout called, it's taking too long...");

   misty.ChangeLED(0, 0, 0); // black
   misty.StopFaceDetection();
};

Save the code file with the name HelloWorld_FaceDetection.js. See the documentation on using Misty Skill Runner or the REST API to load your skill data onto Misty and run the skill from the browser.

See the complete JavaScript code below or download the tutorial code from GitHub.


misty.Debug("starting skill helloworld_facedetection");

// Register for face detection event
misty.RegisterEvent("FaceRecognition", "FaceRecognition", 250);
// Timer event cancels the skill
// if no face is detected after 15 seconds
misty.RegisterTimerEvent("FaceRecognitionTimeout", 15000);

misty.StartFaceDetection();

// FaceRecognition event callback
function _FaceRecognition() {
    misty.Debug("Face detected!");
    // Play an audio clip
    misty.PlayAudio("005-OoAhhh.wav");
    // Change LED to white
    misty.ChangeLED(255, 255, 255);
    // Stop face detection
    misty.StopFaceDetection();
};

// FaceRecognitionTimeout callback
function _FaceRecognitionTimeout() {
    misty.Debug("face detection timeout called, it's taking too long...");

    // Change LED to black
    misty.ChangeLED(0, 0, 0);
    misty.StopFaceDetection();
};

Timer Events

In this tutorial we use timed events to trigger a change in Misty's LED every second. Timed events allow us to specify an amount of time to pass before an event occurs and the callback is triggered. In addition, we introduce global variables and demonstrate how they persist across new threads.

Writing the Meta File

Create a new .json meta file for this skill. Set the value of Name to "HelloWorld_TimerEvent". Use the values in the example to fill out the remaining parameters. Save this file with the name HelloWorld_TimerEvent.json.

{
    "Name": "HelloWorld_TimerEvent",
    "UniqueId": "8a380289-2939-4e81-94d9-86d511b7a8ce",
    "Description": "Local 'Hello, World!' tutorial series.",
    "StartupRules": [ "Manual", "Robot" ],
    "Language": "javascript",
    "BroadcastMode": "verbose",
    "TimeoutInSeconds": 300,
    "CleanupOnCancel": false,
    "WriteToLog": false
}

Writing the Code File

When registering for a timed event use the misty.RegisterTimerEvent() method, we pass in the name of the event we want to create, the amount of time (in ms) we want Misty to wait before triggering the callback function, and we set the keepAlive parameter to true in order to have the event trigger the callback automatically every 3 seconds until it is unregistered. After the line of code to register for the timer event, we send our first command to change Misty’s LED to white below the timer event. This will turn the LED on for the first 3 seconds our skill runs, before the first callback is fired.

misty.RegisterTimerEvent("TimerEvent", 3000, true);
misty.ChangeLED(255, 255, 255); // white

Define a global variable to track the amount of callbacks that have been triggered. In order for the data to persist across new threads created by callbacks, prefix the name of the variable with an underscore. Initialize the value of the variable as 0. Declare it above the misty.RegisterTimerEvent() method. Note: Do not include a type when creating global variables.

_count = 0;

In the callback function (automatically named _TimerEvent()) start by checking if the value of _count is less than 5 using an if...then statement. If so, increment _count by one to keep track of the amount of times we are changing the LED. Then, generate three random values between 0 and 255 and pass them in to misty.ChangeLED() to trigger a change in Misty’s chest LED.

if (_count < 5) {
    _ count = _count + 1;

    let value1 = Math.floor(Math.random() * (256));
    let value2 = Math.floor(Math.random() * (256));
    let value3 = Math.floor(Math.random() * (256));
    misty.ChangeLED(value1, value2, value3);
}
else {

}

The else statement will trigger once the value of _count has reached 5. At this point, we want the skill to end. Start by unregistering for the timer event by calling misty.UnregisterEvent() and passing in the name designated for the event. Then turn the LED off by passing in zero values for misty.ChangeLED() and log a debug message.

else {
    misty.UnregisterEvent("TimerEvent");
    misty.ChangeLED(0, 0, 0); // off
    misty.Debug("ending skill helloworld_timerevent");
}

Using timed events, we have told Misty to change her chest LED to a random color in three-second intervals. We have demonstrated how we can use global variables prefixed with an underscore to have data persist across threads created in our program as callbacks are triggered.

Save the code file with the name HelloWorld_TimerEvent.js. See the documentation on using Misty Skill Runner or the REST API to load your skill data onto Misty and run the skill from the browser.

See the complete JavaScript code below or download the tutorial code from GitHub.


misty.Debug("starting skill helloworld_timerevent");

// global variable to count callbacks
_count = 0;

// Register for TimerEvent
misty.RegisterTimerEvent("TimerEvent", 3000, true);

// TimerEvent callback
function _TimerEvent() {
    if (_count < 5) {
        // Increment _count by 1
        _count = _count + 1;

        // Change LED to random color
        let value1 = Math.floor(Math.random() * (256));
        let value2 = Math.floor(Math.random() * (256));
        let value3 = Math.floor(Math.random() * (256));
        misty.ChangeLED(value1, value2, value3);
    }
    else {
        // Unregister timer event
        misty.UnregisterEvent("TimerEvent");
        // Turn off LED
        misty.ChangeLED(0, 0, 0);
        // Signal skill end
        misty.Debug("ending skill helloworld_timerevent");
    }
}

External Requests

In this tutorial, we write a skill that fetches an audio file from an external resource, saves the file to Misty, and plays it back through Misty's speakers. To do this, we use the misty.SendExternalRequest() method to send a GET request and download a sound from soundbible.com.

Writing the Meta File

Create a new .json meta file for this skill. Copy and paste the code from the example to fill out the parameters. Save this file with the name HelloWorld_ExternalRequest.json.

{
    "Name": "HelloWorld_ExternalRequest",
    "UniqueId": "523c7187-706e-4313-a657-0fa11d8bbdd4",
    "Description": "Local 'Hello, World!' tutorial series.",
    "StartupRules": [ "Manual", "Robot" ],
    "Language": "javascript",
    "BroadcastMode": "verbose",
    "TimeoutInSeconds": 300,
    "CleanupOnCancel": false,
    "WriteToLog": false,
    "Parameters": { }
}

Writing the Code File

The brief code file makes use of the misty.SendExternalRequest() method. The misty.SendExternalRequest() prototype is as follows:

misty.SendExternalRequest(string method, string resourceURL, string authorizationType, string token, string returnType, string jsonArgs, bool saveAssetToRobot, bool applyAssetAfterSaving, string fileName, [string callback], [string callbackRule], [string skillToCallOnCallback], [int prePauseMs], [int postPauseMs]);

In this skill we are sending a GET request, so we use the string GET for the first (method) parameter.

The second parameter (resourceURL) should contain the full URL of the host and resource to access. In this example, the full resourceURL is:

http://soundbible.com/grab.php?id=1949&type=mp3.

For some requests additional authorization may be necessary. This is where the third (authorization) and fourth (token) parameters come into play. In this example, no authorization is required, so we can enter null for the third and fourth parameters.

The fifth required parameter (returnType) indicates the expected media type of the data returned by the request. In this example we expect to receive an .mp3 file, so we enter the string audio/mp3. If you expect to receive an image, you can enter the string image/jpeg (or another image type), or enter a text type if you expect the data to contain text. If the return type is provided by the external resource in the response to the request, you can pass null for this parameter.

Note for Misty II Developers: The misty.SendExternalRequest() method on Misty II robots does not use the returnType parameter. If you are using this tutorial with a Misty II robot, skip this parameter. Do not pass null, undefined, or any other value for this parameter. See the documentation on this method for more information.

The sixth required parameter (jsonArgs) holds the data to send with POST requests. Because this example uses a GET request, jsonArgs can be set to null. Note: When you have no data to send with a POST request, some services may require you to use a string with an empty JSON payload ("{}") instead of null. When you pass null for the jsonArgs parameter to a service where this is the case, the service returns an error message to indicate the value of args cannot be null or empty.

The optional saveAssetToRobot, applyAssetAfterSaving, and fileName parameters tell Misty how to handle images and audio files returned by requests. Pass true for saveAssetToRobot to have Misty save the file to local storage. Pass true for applyAssetAfterSaving to play the audio file immediately after it is saved (or, if the returned file is an image, to display it on Misty's screen). The string you pass for fileName specifies a name for the saved file (this example uses sound).

The optional callback, callbackRule, and skillToCallOnCallback parameters designate a function or skill to receive the data returned by the request and indicate the callback rule Misty should follow to execute the callback. Read more about callbacks and callback rules in Data Handling: Events & Callbacks. This example does not use a callback function, so you can omit these parameters, or pass null if you want to use the prePauseMs and postPauseMs parameters that follow them. Note that prePauseMs and postPauseMs are optional in misty.SendExternalRequest().

The final form of misty.SendExternalRequest() in this tutorial is:

misty.SendExternalRequest(
    "GET", /*method*/
    "http://soundbible.com/grab.php?id=1949&type=mp3", /*resourceURL*/
    null, /*authorizationType*/
    null, /*token*/
    "audio/mp3", /*returnType*/
    null, /*jsonArgs*/
    true, /*saveAssetToRobot*/
    true, /*applyAssetAfterSaving*/
    "sound", /*fileName*/
    null, /*callback*/
    null, /*callbackRule*/
    null, /*skillToCallOnCallback*/
    0, /*prePauseMs*/
    0/*postPauseMs*/
    );

When the response is ready, Misty receives the file, saves it to local storage, and immediately plays through her built-in speakers. The final step is to have Misty send us a debug message to indicate skill execution is complete:

// Debug message to indicate the skill is complete
misty.Debug("The skill is complete!!")

Save the code file with the name HelloWorld_ExternalRequest.js. See the documentation on using Misty Skill Runner or the REST API to load your skill data onto Misty and run the skill from the browser.

See the complete JavaScript code below.

misty.Debug("Starting skill HelloWorld_ExternalRequest");

// Get and play an audio file hosted on soundbible.com.
misty.SendExternalRequest(
    "GET", /*method*/
    "http://soundbible.com/grab.php?id=1949&type=mp3", /*resourceURL*/
    null, /*authorizationType*/
    null, /*token*/
    "audio/mp3", /*returnType*/
    null, /*jsonArgs*/
    true, /*saveAssetToRobot*/
    true, /*applyAssetAfterSaving*/
    "sound", /*fileName*/
    null, /*callback*/
    null, /*callbackRule*/
    null, /*skillToCallOnCallback*/
    0, /*prePauseMs*/
    0/*postPauseMs*/
    );

// Signal skill ccmpletion
misty.Debug("The skill is complete!!")

Trigger Skill

In this tutorial, we learn how a skill can trigger other skills. In this case, the first skill will trigger additional skills when the first skill receives certain external stimuli: face recognition and time-of-flight events. We register for two events and have them trigger two new skills.

Writing the Meta Files

This tutorial covers a total of three skills, so there are a total of three .js code files and three .json meta files. The three meta files used in the tutorial are shown here. Use the values in these example to fill out the parameters, and save each file with the names below:

HelloWorld_TriggerSkill1.json:

{
    "Name": "HelloWorld_TriggerSkill1",
    "UniqueId": "01190e52-3d72-4a9c-ba26-ea483fbdbdea",
    "Description": "Local 'Hello, World!' tutorial series",
    "StartupRules": [ "Manual", "Robot" ],
    "Language": "javascript",
    "BroadcastMode": "verbose",
    "TimeoutInSeconds": 300,
    "CleanupOnCancel": true,
    "WriteToLog": false
}

HelloWorld_TriggerSkill2.json:

{
    "Name": "HelloWorld_TriggerSkill2",
    "UniqueId": "28c7cb66-91d4-4c8f-a8af-bb667ce18099",
    "Description": "Local 'Hello, World!' tutorial series",
    "StartupRules": [ "Manual", "Robot" ],
    "Language": "javascript",
    "BroadcastMode": "verbose",
    "TimeoutInSeconds": 300,
    "CleanupOnCancel": true,
    "WriteToLog": false
}

HelloWorld_TriggerSkill3.json:

{
    "Name": "HelloWorld_TriggerSkill3",
    "UniqueId": "f6cc6095-ae40-4507-a9ef-4c7638bf3ad5",
    "Description": "Local 'Hello, World!' tutorial series",
    "StartupRules": [ "Manual", "Robot" ],
    "Language": "javascript",
    "BroadcastMode": "verbose",
    "TimeoutInSeconds": 300,
    "CleanupOnCancel": true,
    "WriteToLog": false
}

Writing the Code Files

The first skill file we’ll look at, HelloWorld_TriggerSkill1.js, acts as our “parent” skill. The purpose of this skill is to register for our TimeOfFlight and FaceRecognition events. As the “child” skills are triggered by these events, this skill then runs in the background.

We start by calling the misty.RegisterEvent() command. In it, we register for FaceRecognition events with an event name of FaceRecognition. Set debounceMS to 5000, keepAlive to true, and the callback rule to Synchronous. This is all typical for event registration.

The difference from normal event registration happens when we use an optional parameter (skillToCall) to designate which skill we want the FaceRecognition event to trigger. We do that by providing by the GUID of that skill. In this case the face recognition event is going to trigger the HelloWorld_TriggerSkill2.js skill, so we provide the GUID for that skill here. In this case, it’s 28c7cb66-91d4-4c8f-a8af-bb667ce18099.

misty.RegisterEvent("FaceRecognition", "FaceRecognition", 5000, true, "Synchronous", "28c7cb66-91d4-4c8f-a8af-bb667ce18099");

We also want to add a return property check above the registration call, to return just the property PersonName for use in our callback. Pass in the name of the event first, then the property we want.

misty.AddReturnProperty("FaceRecognition", "PersonName");

Finally, we need to send the command to start face recognition as well. This command tells Misty to start looking for a face to recognize (in tandem with the FaceRecognition event subscription).

 misty.StartFaceRecognition();

Putting the pieces together, shown below is first half of the parent skill; the commands relating to face recognition.

// Return only the PersonName property
misty.AddReturnProperty("FaceRecognition", "PersonName");

// Register for FaceRecognition events.
// For the callback, pass in the GUID for
// HelloWorld_TriggerSkill2.
misty.RegisterEvent("FaceRecognition", "FaceRecognition", 5000, true, "Synchronous", "28c7cb66-91d4-4c8f-a8af-bb667ce18099");

misty.StartFaceRecognition();

Next, we want to register for another event, BackTOF. This event triggers whenever the rear time-of-flight sensor is activated and passes our property test. This in turn kicks off the third skill, HelloWorld_TriggerSkill3.js.

Start by calling RegisterEvent and registering for TimeOfFlight as BackTOF. Pass in 5000 for debounceMS, set keepAlive to true, and specify the callback rule as Synchronous. Similar to our previous registration, pass in the GUID for our third skill for the last parameter (in our case, `f6cc6095-ae40-4507-a9ef-4c7638bf3ad5). Above our registration call, add two property tests to confirm we’re only receiving data from our rear-facing time of flight sensor and that the distance an object is detected is less than 0.5 meters.

// Return data only from rear-facing TOF sensors
misty.AddPropertyTest("BackTOF", "SensorPosition", "==", "Back", "string");
// Return data only when an object is closer than 0.5m
misty.AddPropertyTest("BackTOF", "DistanceInMeters", "<", 0.5, "double");

// Register for TimeOfFlight events.
// For the callback, pass in the GUID for
// HelloWorld_TriggerSkill3.
misty.RegisterEvent("BackTOF", "TimeOfFlight", 5000, true, "Synchronous", "f6cc6095-ae40-4507-a9ef-4c7638bf3ad5");

With both of our event registrations finished, our “parent” skill is complete. Note that we don’t create callbacks for the events in the “parent” skill -- those are handled in the “child” skills.

For reference, here is the entire skill file for HelloWorld_TriggerSkill1.js.

// Return only the PersonName property
misty.AddReturnProperty("FaceRecognition", "PersonName");

// Register for FaceRecognition events.
// For the callback, pass in the GUID for
// HelloWorld_TriggerSkill2.
misty.RegisterEvent("FaceRecognition", "FaceRecognition", 5000, true, "Synchronous", "28c7cb66-91d4-4c8f-a8af-bb667ce18099");

misty.StartFaceRecognition();

// Return data only from rear-facing TOF sensors
misty.AddPropertyTest("BackTOF", "SensorPosition", "==", "Back", "string");
// Return data only when an object is closer than 0.5m
misty.AddPropertyTest("BackTOF", "DistanceInMeters", "<", 0.5, "double");

// Register for TimeOfFlight events.
// For the callback, pass in the GUID for
// HelloWorld_TriggerSkill3.
misty.RegisterEvent("BackTOF", "TimeOfFlight", 5000, true, "Synchronous", "f6cc6095-ae40-4507-a9ef-4c7638bf3ad5");

As discussed above, the first “child” skill handles face recognition events. We start by defining a function for the callback (automatically named _<Event>) and passing in an argument to hold the data from the event. Within the callback, send a debug message to notify the user that the new skill has been triggered.

function _FaceRecognition(data) {
   misty.Debug(“TriggerSkill part 2 has been triggered.”);
}

Note: Because we designated the GUID for this skill (HelloWorld_TriggerSkill2.js) as the skill to call back in the registration call for FaceRecognition within our “parent” skill, this skill starts automatically when the event callback is triggered.

Next, define a variable, personName to hold the name of the face detected (or “unknown person” if the face was not recognized). You can access this information within data.AdditionalResults.

// Store the name of the detected face
let personName = data.AdditionalResults[0];

Then, use an if statement to check if personName is equal to unknown person. If so, the face was not recognized. In this case, we want to send a command to change the LED to red and send a debug message from Misty saying “I don’t know you…”. Otherwise, the face was recognized, and we send a command to change the LED to green and send a debug message greeting the user by name.

if (personName == "unknown person") {
    // Change LED
    misty.ChangeLED(255, 0, 0); // red
    misty.Debug("I don't know you...");
}
else {
    // Change LED
    misty.ChangeLED(0, 255, 0); // green
    misty.Debug("Hello there " + personName + "!");
}

You’ll remember that earlier when we registered for our face recognition event, we set debounceMS to 5000. The reason for this is that we need to give the triggered skill time to process the information, then automatically cancel before the callback is triggered again. The triggered skill can be triggered multiple times from the “parent” skill, but only if it isn’t already running. If the event callback is triggered while HelloWorld_TriggerSkill2.js is still running, it does not run again. This is because you cannot have multiple instances of the same skill running at once.

For reference, here is the entire skill file for HelloWorld_TriggerSkill2.js.

// callback for face recognition event
function _FaceRecognition(data) {
    // Signal that new skill has been triggered.
    misty.Debug("TriggerSkill part 2 has been triggered.");
    // Store the name of the detected face
    let personName = data.AdditionalResults[0];
    if (personName == "unknown person") {
        // Change LED
        misty.ChangeLED(255, 0, 0); // red
        misty.Debug("I don't know you...");
    }
    else {
        // Change LED
        misty.ChangeLED(0, 255, 0); // green
        misty.Debug("Hello there " + personName + "!");
    }
}

The third skill is designated for our time-of-flight event. Start by defining a function for the callback for BackTOF. Pass in an argument to access the data. Then, within the callback write a debug message indicating that skill number three has been triggered.

// TimeOfFlight callback
function _BackTOF(data) {
    // Signal that new skill has been triggered
    misty.Debug("TriggerSkill part 3 has been triggered.");
}

Define a variable distance to hold the value of the distance an object was detected. We can access this from our property test results (contained in the response). Dig into the results to locate the value we want.

// Store the distance of the detected object
let distance = data.PropertyTestResults[1].PropertyParent.DistanceInMeters;

Then, use an if statement to check that the distance is less than 0.1m. If so, play an “irritated” sounding audio clip, send a debug message indicating the distance an object detected was ‘too close’, and have Misty drive forward a short distance. Otherwise, send a command to play a ‘happy’ sounding clip, and send a debug message indicating the object is far enough away (it isn’t invading Misty’s personal space).

if (distance < 0.1) {
    // Play irritated audio clip
    misty.PlayAudio("002-Ahhh.wav", 100);
    misty.Debug("An object was detected " + distance + " meters behind me. That's too close!");
    // Drive forward
    misty.DriveTime(50, 0, 1000);
}
else {
    // Play happy audio clip
    misty.PlayAudio("004-WhaooooO.wav", 100);
    misty.Debug("An object was detected " + distance + " meters behind me. That's okay.");
}

Just like before in our second skill, we specified the callback to trigger once every 5000 ms to give the triggered skill time to cancel before being started again.

For reference, here is the entire skill file for HelloWorld_TriggerSkill3.js.

// TimeOfFlight callback
function _BackTOF(data) {
    // Signal that new skill has been triggered
    misty.Debug("TriggerSkill part 3 has been triggered.");
    // Store the distance of the detected object
    let distance = data.PropertyTestResults[1].PropertyParent.DistanceInMeters;
    // Check the value of distance
    if (distance < 0.1) {
        // Play irritated audio clip
        misty.PlayAudio("002-Ahhh.wav", 100);
        misty.Debug("An object was detected " + distance + " meters behind me. That's too close!");
        // Drive forward
        misty.DriveTime(50, 0, 1000);
    }
    else {
        // Play happy audio clip
        misty.PlayAudio("004-WhaooooO.wav", 100);
        misty.Debug("An object was detected " + distance + " meters behind me. That's okay.");
    }
}

Congratulations, triggering callbacks across skills is a valuable tool you can add to your Misty-programming experience! Save the code files, and see the documentation on using Misty Skill Runner or the REST API to load your skill data onto Misty and run the skill from the browser.

Download the code files for this tutorial from GitHub.