Eng:Communicating to Scratch via ActionScript

ActionScript 3.0 (AS3) is a language developed by Adobe for use on the Flash platform. This tutorial teaches how to communicate to and from Scratch with AS3.

Setup: The Scratch side
Scratch has long had a "sensors" block which allowed users to connect to a PicoBoard, but in version 1.3 a feature was added to let any application talk to Scratch. To enable this feature, a user must enter the Sensing Blocks palette, right click the sensors block and choose "enable remote sensor connections". The application will then open itself up to communication on port 42001. If you're using Windows Vista or a similar operating system you may have to confirm this action through a system dialog.

Setup: The ActionScript side
To write ActionScript a user needs an ActionScript editor. This tutorial will be using Flash CS4, however free alternatives like FlashDevelop are available that can accomplish the same thing.

Communicating
As mentioned before, Scratch allows other applications to communicate with it on port 42001. A port can be thought of as an outlet for an application that Scratch can send and receive data through. Here's what Scratch and other languages can send to each other:

However, another application cannot set Scratch global variables through the port. For the purposes of this tutorial, an overview of the Scratch Extension Protocol is given. This basically describes what an application needs to do to send messages Scratch will understand.
 * Scratch will send a message through the socket whenever a global variable is updated or a broadcast is issued.
 * Another program can broadcast a message to Scratch, or can add custom values to the sensor block below the built-in ones.

The Protocol
All messages to Scratch must be sent to port 42001 in this format: Like this: [size][size][size][size][string message (size bytes long)]
 * After the zero bytes is the length of the string message to pass, given as a 32-bit big-Endian number (4 bytes).
 * Lastly, there is the string message, whose length is the length of the message size.

Receiving Data From Scratch
To communicate with Scratch, we'll need to make use of the Socket class. So let's create a new Socket and connect it to port 42001. var host:String = "localhost" //This means we're connecting to our own computer (127.0.0.1) var sock:Socket = new Socket //This is our socket Now that we have a Socket to work with, we need to add some event listeners with the socket's addEventListener method. When the socket dispatches an event we're listening for, it will call a handler function we specify. sock.addEventListener(Event.CONNECT,onConnect) //Called when the socket connects sock.addEventListener(Event.CLOSE,onClose) //Called when the socket is closed sock.addEventListener(IOErrorEvent.IO_ERROR,onError) //Called on a connection problem sock.addEventListener(ProgressEvent.SOCKET_DATA,onDataIn) //Called when data is received Now we write the event handlers for these listeners: function onConnect(e:Event):void {  trace("Connected!") } function onClose(e:Event):void {  trace("Socket has been closed.") } function onError(e:IOErrorEvent):void {  trace("Oh no! Trouble connecting!") } function onDataIn(e:ProgressEvent):void {  //We'll just call another function to do our dirty work getData } Now we know when the socket connects, closes, and throws an error. Now all we need to do is connect! sock.connect(host,42001) //Pass the host variable from above and 42001 to connect Test the movie. If you've done everything right and Scratch is open with remote sensor connections enabled you should see "Connected!" trace to the output panel after a few seconds. Now that we're connected, we can send and receive data with Scratch!

Remember the getData function we called from the onDataIn event handler? We're going to write that now. function getData:void {  var data:String="" while (sock.bytesAvailable) { data+=sock.readUTF; }  trace(data) //More code here in a bit! } Everything Scratch sent is now in the variable named data. We can trace that if we want, but instead let's write some code to see what Scratch is trying to tell us. sensor-update "variable name" value broadcast "broadcast name" To see this in action, test the movie again, then head over to Scratch and change some global variables or broadcast some things. The messages will show up in the output panel. Next, we need to use the messages from Scratch. For this, Regular Expressions (RegExps) will be used. Basically RegExps define a certain pattern a string must follow, and can be checked against a string to extract data from it. We're using two RegExps here: /broadcast\s\"(.*)"/ (This matches a string that follows the 'broadcast' pattern) /sensor-update\s\"(.*?)\"\s\"(.*?)\"/ (This matches a string that follows the 'sensor-update' pattern) Information on RegExps can be found here. /* -- I GO WITHIN THE getData FUNCTION! -- */ //We're still within the previous getData function here. Place this code after 'trace(data)' var broadcast_pattern:RegExp=/broadcast\s\"(.*)"/; var sensor_pattern:RegExp=/sensor-update\s\"(.*?)\"\s\"(.*?)\"/; var sensor_num_pattern:RegExp=/sensor-update\s\"(.*?)\"\s([-|\d|.]+)/; var result:Object; //See those parentheses in the regexes above? The values within those are actually stored //in the 'result' object below at indices 1,2,etc. result=broadcast_pattern.exec(data); if (result != null) { //It's a broadcast! Call the broadcasted function with the name of the broadcast broadcasted(result[1]) } result=sensor_pattern.exec(data); if (result != null) { //It's a sensor-update! Call the updated function with the variable name and the new value updated(result[1],result[2]) } result=sensor_num_pattern.exec(data) if(result != null){ //It's...the same as before, only Scratch passed a numerical value. updated(result[1],result[2]) } This is what this code does: Now, of course, we have to write these functions: function broadcasted(broadcast:String):void {  trace("Scratch broadcasted",broadcast) } function updated(variable:String,value:String):void {  trace("Scratch set",variable,"to",value) } Test the movie and start playing with Scratch! As you broadcast and update, you should see this happen in the output panel.
 * If a variable was updated, we'll get a message in this form:
 * If a broadcast was dispatched, we'll see this:
 * If Scratch broadcasted something, it calls the broadcasted function with the broadcast name.
 * If Scratch updated a variable, it calls the updated function with the updated variable and the new value.

Doing Something With This Data
Now that we can understand Scratch's messages, it would be nice to be able to do something with them. The following explains how to launch a browser window from Scratch!

First, for the Scratch side. Create a new project and add a script like this:

when gf clicked forever ask [URL?] and wait if > set [new url v] to (answer)

This repeatedly asks for a URL. Now on the AS3 side we'll modify the updated function to look like this: function updated(variable:String,value:String):void {  trace("Scratch set",variable,"to",value) if(variable=="new url"){ navigateToURL(new URLRequest(value)) } } Now when the "new url" Scratch variable is updated, Flash will open a browser window at the specified URL.

Sending data to Scratch
Let's look at how you send data to Scratch. If you recall from above, all messages must be formatted like so: [0][0][0][message size](string message) Let's look at the (string message) part. The way we send these messages is identical to the way we receive them: sensor-update "variable name" value broadcast "broadcast name" This is simple enough. Knowing how to format a message to Scratch, we can then write the following function: function sendScratchMessage(s:String):void {  var bytes:ByteArray=new ByteArray; bytes[0]=0; bytes[1]=0; bytes[2]=0; bytes[3]=s.length; for (var i:Number=0; i<4; i++) { sock.writeByte(bytes[i]); }  sock.writeMultiByte(s,"us-ascii"); sock.flush; } Basically this takes whatever is in the string "s" and wraps it in a ByteArray with the appropriate preceders. Notice the three zero bytes and the length of the message all go before the message itself. It then pushes these into the socket. With this function, we can write two more that make broadcasts and sensor updates a breeze: function update(variable:String,value:*):void { if(!sock.connected) return value=String(value); sendScratchMessage('sensor-update "'+variable+'" "'+value+'"'); } function broadcast(broadcast:String):void { if(!sock.connected) return sendScratchMessage('broadcast "'+broadcast+'"'); } Now all we have to do is call these functions to send things through the socket.
 * If we're updating a remote sensor, it'll look like this:
 * If we're broadcasting, it is

AS3 to Scratch Example: Time
Like the URL block, something Scratch users often request is a time block. Let's replicate that functionality. This bit of code will create a 'time' item in the sensor block dropdown. var timer:Timer = new Timer(1000) timer.addEventListener(TimerEvent.TIMER,everySecond) timer.start function everySecond(e:TimerEvent):void {  var d:Date=new Date update('time',d.toTimeString) } Now head over to Scratch and on a new sprite do something like this:

when gf clicked forever say ([time v] sensor value)

And...that's it! We now have access to time functionality in Scratch (at least with this Flash file running)

Conclusion
The entire script can be copied into a Flash movie: var sock:Socket = new Socket; sock.addEventListener(ProgressEvent.SOCKET_DATA,onDataIn); sock.addEventListener(Event.CONNECT,onConnect) sock.addEventListener(Event.CLOSE,onClose) sock.addEventListener(IOErrorEvent.IO_ERROR,onError) sock.connect('localhost',42001) function onDataIn(e:ProgressEvent):void { 	getData; } function onConnect(e:Event):void {  trace("Connected!") } function onClose(e:Event):void {  trace("Socket has been closed.") } function onError(e:IOErrorEvent):void {  trace("Oh no! Trouble connecting!") } function getData:void { 	var data:String=""; while (sock.bytesAvailable) { 		data+=sock.readUTF; } 	trace(data) var broadcast_pattern:RegExp=/broadcast\s\"(.*)"/; var sensor_pattern:RegExp=/sensor-update\s\"(.*)\"\s\"(.*)\"/; var sensor_num_pattern:RegExp=/sensor-update\s\"(.*?)\"\s([-|\d|.]+)/; var result:Object; //See those parentheses in the regexes above? The values within those are actually stored //in the 'result' object below at indices 1,2,etc. result=broadcast_pattern.exec(data); if (result!=null) { 		//It's a broadcast! Call the broadcasted function with the name of the broadcast broadcasted(result[1]); } 	result=sensor_pattern.exec(data); if (result!=null) { 		//It's a sensor-update! Call the updated function with the variable name and the new value updated(result[1],result[2]); } 	result=sensor_num_pattern.exec(data) if(result != null){ updated(result[1],result[2]) } } function broadcasted(broadcast:String):void {  trace("Scratch broadcasted",broadcast) } function updated(variable:String,value:String):void {  trace("Scratch set",variable,"to",value) if(variable=="new url"){ navigateToURL(new URLRequest(value)) } } function sendScratchMessage(s:String):void {  var bytes:ByteArray=new ByteArray; bytes[0]=0; bytes[1]=0; bytes[2]=0; bytes[3]=s.length; for (var i:Number=0; i<4; i++) { sock.writeByte(bytes[i]); }  sock.writeMultiByte(s,"us-ascii"); sock.flush; } function update(variable:String,value:*):void { if(!sock.connected) return value=String(value); sendScratchMessage('sensor-update "'+variable+'" "'+value+'"'); } function broadcast(broadcast:String):void { if(!sock.connected) return sendScratchMessage('broadcast "'+broadcast+'"'); } var timer:Timer=new Timer(1000) timer.addEventListener(TimerEvent.TIMER,everySecond) timer.start function everySecond(e:TimerEvent):void { 	var d:Date=new Date update('time',d.toTimeString) }