--Scratch 2 offline version allows you to load additional blocks from a JSON-formatted file. --The block makes a GET request to an HTTP server outside of Scratch. --You can extend the functionality of Scratch by launching a helper app that listens for GET. ――However, it seems that the overhead is large to generate threads for each request that arrives in large numbers from Scratch loops. --So, let's try to process GET requests in parallel with a single thread.
As an example of the explanation here, the method of using speech synthesis and speech recognition in the Scratch 2 offline version is summarized below. -Scratch 2.0 Offline Voice Dialogue Robot / Block for Voice Dialogue System
Also, put the code that appears in the description below on github.
Explanation PDF In Scratch Extension Page If you read HTTP-9-11.pdf), it says that you can add blocks by creating a JSON format file with the extension .s2e
and reading it. (It doesn't matter what the extension is, but it seems that you should use .s2e
.) Below, I will try using the example given in this explanation.
test.s2e
{ "extensionName": "Extension Example",
"extensionPort": 12345,
"blockSpecs": [
[" ", "beep", "playBeep"],
[" ", "set beep volume to %n", "setVolume", 5],
["r", "beep volume", "volume"],
]
}
Save this in a file called test.s2e
. If you hold down Shift
and select [File] in Scratch, [Load experimental HTTP extension] will be displayed, so if you select and load test.s2e
saved there, it will certainly block. Will be added!
that? The circle next to ʻExtension Example` is red.
According to the Explanation PDF, Scratch requests polling about 30 times per second GET / poll I'm sending
to the helper, but it seems to turn red if there is no response to it. If there is a reaction from the helper, it will turn green.
--First, try to return something to GET / poll. --In this example, it seems to communicate with port 12345, so let's create an HTTP server that listens on port 12345. --You can easily create a lightweight HTTP server by using aiohttp, which is based on the asynchronous I / O library asyncio. --Since Python 3.5, you can use familiar modifiers in C # such as async and await. The following assumes Python 3.5.
Install aiohttp with $ pip install aiohttp
and create the following program (testhelper.py).
testhelper.py
from aiohttp import web
async def handle_poll(request):
return web.Response(text="OK")
app = web.Application()
app.router.add_get('/poll', handle_poll)
web.run_app(app, host='127.0.0.1', port=12345)
I will try it.
> python testhelper.py
======== Running on http://127.0.0.1:12345 ========
(Press CTRL+C to quit)
By the way, when I look at the Scratch screen ... Oh, it's green!
Just in case, try GET from bash with nc.
$ nc localhost 12345
GET /poll HTTP/1.1
If you press return with this ...
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Content-Length: 2
Date: Tue, 19 Sep 2017 13:41:21 GMT
Server: Python/3.5 aiohttp/2.1.0
OK
It seems that aiohttp is replying.
First, let's implement the beep
block as a review. The block that sends commands from Scratch to the helper is called a "command block". If you look at s2e, you can see that this is associated with the command name playBeep
. At this time, GET / playBeep
is sent to the helper, and the helper should take this and do something. Try ringing the bell with \ 007
.
async def handle_beep(request):
print("play beep!")
print("\007")
return web.Response(text="OK")
#abridgement
app.router.add_get('/playBeep', handle_beep)
web.run_app(app, host='127.0.0.1', port=12345)
If you press the beep
block in Scratch 2, you will certainly hear a sound.
Next, try implementing the command block set beep volume to (...)
that sends the value. In the following example, vol
has a value, so you can retrieve it withrequest.match_info ['vol']
.
async def handle_setvolume(request):
volume = int(request.match_info['vol'])
if volume >= 0 and volume <= 10:
print("set volume= " + str(volume))
else:
print("out of range: " + str(volume))
return web.Response(text="OK")
#abridgement
app.router.add_get('/setVolume/{vol}', handle_setvolume)
web.run_app(app, host='127.0.0.1', port=12345)
The display changes depending on whether you enter a value from 0 to 10 and press the block, or if you enter any other value. (It is also necessary to check ʻisinteger () before ʻint ()
, but omit it.)
Finally, let's return the value from the helper to Scratch. In Scratch, a block that can receive this value is called a reporter block, and when it receives a number or string, it is prefixed with " r "
in the s2e file. On the other hand, when receiving a boolean value, add " b "
to distinguish it.
To return a value to the reporter block, use the response to GET / poll
. beep volume
is associated with the name" volume ", so if you return volume 10
to GET / poll
, the reporter block will be populated.
In the following, the value specified by set beep volume to (...)
will be returned as it is. Here is the entire code so far.
testhelper.py
from aiohttp import web
volume = 0
async def handle_poll(request):
text = "volume " + str(volume) + "\n"
return web.Response(text=text)
async def handle_beep(request):
print("play beep!")
print("\007")
return web.Response(text="OK")
async def handle_setvolume(request):
global volume #Don't forget
tmp_volume = int(request.match_info['vol']) #Once to another variable
if tmp_volume >= 0 and tmp_volume <= 10:
volume = tmp_volume
print("set volume= " + str(volume))
else:
print("out of range: " + str(tmp_volume))
return web.Response(text="OK")
app = web.Application()
app.router.add_get('/poll', handle_poll)
app.router.add_get('/playBeep', handle_beep)
app.router.add_get('/setVolume/{vol}', handle_setvolume)
web.run_app(app, host='127.0.0.1', port=12345)
It seems better to make it a class and access it like self.volume
. Is it like this in class?
Try it with Scratch. First, check the value next to the reporter block beep volume
to display the value. Next, when you enter a number in the command block set beep volume to (...)
and execute it (click the block), the value of the reporter block beep volume
changes to the value specified in the command block. You can see that.
If there are multiple reporter blocks, return the response separated by the line feed code 0A
.
Some command blocks wait to be executed, and are specified by prefixing " w "
in s2e. To implement this block, you need to tell Scratch that the command is running.
For example, if you add " w "
to set beep volume to (...)
, what was GET / setVolume / 5
becomes the command ID like GET / setVolume / 2574/5
. Is added (2574 is an example, the value actually varies from command request to request). Adding this command ID to the response to poll tells Scratch which block is running. At this time, add it in the format _busy 2574
. If you need to return multiple command IDs, separate them with a space.
In addition, there are various topics such as how to make a block select from a pull-down without entering a value, how to receive a character string, how to reset, cross-domain policy, etc. [Explanation PDF](https://wiki. It is mentioned in scratch.mit.edu/w/images/ExtensionsDoc.HTTP-9-11.pdf).
The above method may be a connection to Scratch 3 (I'm not sure what happens to the offline version of Scratch 3 and how to extend it), but with Python there are various things such as OpenCV, machine learning, numerical calculation, etc. Since the library is ready to use, it seems to be quite free to expand.
Of course, Node.js can also set up a lightweight server with similar asynchronous I / O, so if you are not particular about Python modules, you can write it in JavaScript. (I tried to summarize in this article)
If you want to keep the HTTP extension in Scratch 3 (and if it's currently being removed), you might change Scratch 3 as well.
Recommended Posts