Thymio and Scratch using Python

Previously I managed to communicate between Python and the Thymio-II using dbus and asebamedulla, using that code I have been able to communicate between Scratch and the Thymio using python. As before this will only work on Linux due to dbus and asebamedulla compatibility.

Here is a video of everything working:

Scratch 1.4 allows for RSC (remote sensor connections) which means it acts as a sever of sorts and can deal with broadcasts and sensor values. As I understand it this was intended for some Lego robot but with python we can make good use of it. I will be following on from my previous post for those looking to follow along, one thing to note, this isn't especially clean, I only started using python a few days ago.

All of the python and the scratch files will be available for download at the bottom, they might be worth looking at for reference to help follow what I'm saying as I'm explaining in the order it was done in and not a top to bottom order.

To communicate to scratch you need to use sockets which can be complicated, however there is a nice library that deals with the communication between python and scratch called scratchpy. Scratchpy can be installed using pip, you'll want to run sudo apt-get install python-pip and then type sudo pip install scratchpy.

Scratchpy makes things a lot easier, to set it up you need to add the following import

import scratchpy

and then this code will set up a connection to scratch

s = scratch.Scratch(host='localhost')

And that's in on the python side. Bear in mind that you will need to go in to scratch, click on the sensing tab and then right click on the sensor value block to bring up a menu, then click on "enable remote sensor connections". With that done everything will be set.

We want to be able to see the values from the proximity sensors on the Thymio in scratch, the following code will send 5 sensor values to scratch to set them up

 s.sensorupdate({'prox0': '0', 'prox1': '0','prox2': '0','prox3': '0','prox4': '0'})

If you were to run this code you would find that in scratch on the sensor value drop down list all of the prox variable are present. In order to update the values in scratch we will need to make use of the main loop from last time. remove all code related to key presses from that function and enter

 s.sensorupdate({'prox0': proxSensorsVal[0], 'prox1': proxSensorsVal[1],'prox2': proxSensorsVal[2],'prox3': proxSensorsVal[3],'prox4': proxSensorsVal[4]})
    s.broadcast("setprox")

This will update all of the prox values in scratch with the values from the Thymio but only with some code in scratch. I added a list called prox sensors and gave it 5 items, the code below sets the values of those item to the sensor values from the Thymio. Note how above I broadcast "setprox" and below it responds to that broadcast.

With that done we want to be able to tell the thymio what to do based upon those values using scratch. Scratch can broadcast messages which can be read internally in scratch and externally in python. As well as this scratchpy has a function for receiving broadcasts, however it cause the thread to wait for data to be received before moving on. If we put that code in our main loop all of the sensor data would stop. To fix this we need to create another thread to deal with receiving and then to pass the data from scratch to the main loop and then to the thymio. We will need to define a global variable so data can be shared between threads.

broad = ' '

This just sets it up so it can be used later. Then we need the function for the main thread to run.

def thymioscratchcontrol():
    while True:
        res = s.receive()
        for r in res:
            if r == 'forward':
                global broad
                broad = r
                time.sleep(1)
                broad = 'null'
            if r == 'left':
                global broad
                broad = r
                time.sleep(0.2)
                broad = 'null'
            if r == 'right':
                global broad
                broad = r
                time.sleep(0.2)
                broad = 'null'
            if r == 'back':
                global broad
                broad = r
                time.sleep(1)
                broad = 'null'
            print r

This loops constantly and waits for a signal from the thymio. It then loops through every value it receives. the values are the names that are broadcast from scratch. If the name is one we expect we set the global variable to that name and then let the thread sleep for as long as we want the command to execute for.

To run this you will need to import thread and then run this code before you call loop.run().

thread.start_new_thread(thymioscratchcontrol,())

this will create the thread and run the method, all we need to do now is send the commands to the thymio. In the control loop add the following code.

if broad == "forward":
        network.SetVariable("thymio-II", "motor.left.target", [300])
            network.SetVariable("thymio-II", "motor.right.target", [300])
    elif broad == "left":
        network.SetVariable("thymio-II", "motor.left.target", [-300])
            network.SetVariable("thymio-II", "motor.right.target", [300])
    elif broad == "right":
        network.SetVariable("thymio-II", "motor.left.target", [300])
            network.SetVariable("thymio-II", "motor.right.target", [-300])
    elif broad == "back":
        network.SetVariable("thymio-II", "motor.left.target", [-300])
            network.SetVariable("thymio-II", "motor.right.target", [-300])
    else:
            network.SetVariable("thymio-II", "motor.left.target", [0])
            network.SetVariable("thymio-II", "motor.right.target", [0])

This will set the motor speed for the Thymio depending on the broadcast received, and the loop in the other thread determines how long to run these for. This just about covers all of the python, the .py can be downloaded at the bottom of this post.

In order to send commands you can use the broadcast block, at this point code like shown below should work.

This sends a list of commands, the python code will loop through this and send the commands to the thymio.

To use the sensors wee need to be a bit more careful so that it doesn't send 2 messages at the same time for instance, or create a huge queue in the python code which will clog up the thymio for a few minutes.

To do this we will need a variable to act as a flag for when it's okay to send new commands. I called the variable catch, and used it in the code below. This waits until the middle proximity sensor has a value greater than 1000 and then triggers.

This isn't all though, we need to increase catch by an appropriate amount. Luckily its broadcasting so we can just use those messages.

the amount catch is increased by is the same as the length of the command so when catch counts down to 0 the commands should have finished running. To get catch to count down we need some more code.

This will reduce catch by 0.2 every 0.2 seconds and ensure it's never smaller than 0.

All of this with my other post should allow you to use scratch to control the Thymio-II robot.

Some final thoughts, it should be possible to run asebamedulla and the python code on a raspberry pi using a battery and wifi and connect ot to scratch on a computer on the network, but I don't have the equipment and it might be slow due to the constant updating of the proximity sensors.

Downloads: python    Scratch

GitHub: https://github.com/lazerduck/Thymio_python_interface