Controlling the Maplin Robot Arm with Scratch V2 Offline on Microsoft Windows 10

In a complete departure from my previous posts, and as a result of buying one of these Arms for my kids for Christmas I thought I would write-up what I have done with it.

This is the Maplin Robot Arm  (~£29 discounted), I think originally it is made by a company called OWI from the US.

Maplin USB Robot Arm

Maplin USB Robot Arm

This is a basic arm with 5 motors and can be controlled from Windows with the supplied software or from Linux (i.e. Raspberry Pi, using some basic Python)

I was looking to see if the arm could be controlled/programmed from MIT’s Scratch programming language so that my youngest kid to join in too, and the answer is yes!

Most of the information, work (and credit) I needed to do was already done at a great blog post by Franco Raimondi, which can be found here:  Using the Maplin Arm with Scratch.

I have updated the instructions for Microsoft Windows 10 and also modified the Python code needed so that it runs on the latest version of Python (at this time) for Windows  – Python 3.51

Credits:

Franco Raimondi – for the work on how to extend scratch and the Python server code

Adafruit Industries – for the article on using lib-usb on Windows (hacking the connect article)

Instructions

Step 1 – preliminary steps

If your Windows 10 machine has an encrypted drive (using bitlocker) then follow the steps below to print out the recovery key for the drive.  This will be needed in Step 2.

If your drive is not encrypted then go straight to Step 2.

Printing your Recovery Key

Open Control Panel (search for it)  à System & Security à Drive Encryption

Choose Backup your recovery key à Print the recovery key

Step 2 – device driver prep

Follow the instructions below to enable your machine to install un-signed device drivers. If your machine has an encrypted disk drive you will be asked to enter the drive recovery key during this process.

  1. Press the Win + C and click on PC settings.
  2. Switch over to the “Update & recovery” section.
  3. Then click on the Recovery option on the left hand side.
  4. Once selected, you will see an advanced startup section appear on the right hand side. You will need to click on the “Restart now” button.
  5. Once your Computer has rebooted you will need to choose the Troubleshoot option.
  6. Then head into Advanced options.
  7. Then Startup Settings.
  8. Since we are modifying boot time configuration settings, you will need to restart your computer one last time.
  9. Here you will be given a list of startup settings that you can change. The one we are looking for is “Disable driver signature enforcement”. To choose the setting, you will need to press theF7key (or 7).

Step 3 – installing libusb device driver

We need a device driver so that Scratch (via a Python handling script) can talk to the Robot Arm.  For this we will use “libusb”.  The Maplin Robot Arm has the following USB Vendor and Device ID:

Vendor ID=0x1267

Product ID=0x000

This information is needed for this step.

  1. Download the libusb-win32 software from Sourceforge:
  2. Unzip the software, navigate into the bin folder of the unzipped software.
  3. Plug in the Maplin Robot Arm and switch it on.
  4. Run the inf-wizard.exe.
  5. At the Device Configuration Dialog box enter the following information.
  • Vendor ID:          0x1267
  • Product ID:         0x0000
  • Device Name:    Maplin Robot Arm

Other fields can be left unchanged.  Press Next.

  1. Check the information on the next screen and press the Install Now.. button
  2. (choose the directory to save the package)
  3. This will create a device driver install package in the directory in (7).
  4. Open Device Manager – Unknown device (with the exclamation mark)
  5. Right click the device, choose Update Driver, Install from my Hard drive, navigate to the folder from (7).  Ignore any warnings.
  6. The driver should install.  The exclamation mark should disappear and the device should be found under “libusb Devices” and have the description of “Maplin Robot Arm”

Step 4 – installing Python and Handling Scripts

Python

Navigate to https://www.python.org/downloads/ and download and install Python (3.5.1)

Accept all of the defaults and ensure that the location of the Python software is added to the PATH of the computer.

PyUSB

Navigate to http://sourceforge.net/projects/pyusb/ and download the PyUSB software

Unzip the software into the correct Python subdirectory.

From a command prompt, navigate to the directory the PyUSB software has been unzipped in, and run:

python setup.py install

Note: you should be in the directory where the “setup.py” script is.

This will install PyUSB.

Scratch Handling Script

Create a directory called Robot and save the following source code in it as: pymarash_http_server.py

 

"""
@author: Franco Raimondi

Inspired by the code available at https://github.com/MrYsLab/s2a_fm

Modified by Gareth Naylor to be compatible with Python 3.51

"""

import logging
import usb.core, usb.util, time
from http.server import BaseHTTPRequestHandler
from http.server import HTTPServer


# Port for the HTTP server (it should match what is declared in the .json file)
port = 14275

# A dictionary associating the command received from Scratch with a bit pattern
# to be sent to the USB link 
armcmds = dict()
armcmds["rotateccw"] = [0,2,0]
armcmds["rotatecw"] = [0,1,0]
armcmds["shoulderup"] = [64,0,0]
armcmds["shoulderdown"] = [128,0,0]
armcmds["elbowup"] = [16,0,0]
armcmds["elbowdown"] = [32,0,0]
armcmds["wristup"] = [4,0,0]
armcmds["wristdown"] = [8,0,0]
armcmds["gripopen"] = [1,0,0]
armcmds["gripclose"] = [2,0,0]

# Let's find the arm (the ID is fixed)
RoboArm = usb.core.find(idVendor=0x1267, idProduct=0x000)

print("RoboArm Value", RoboArm)

#Check if the arm is detected and stop here if not
if RoboArm is None:
    raise ValueError("Arm not found")

# A class to handle HTTP requests
class GetHandler(BaseHTTPRequestHandler):

    # This is where we do the translation from HTTP request to USB command
    def do_GET(self):

        # remove the "/" at the beginning
        arguments = self.path[1:]

        # split the URL using the separator character "/"
        # (see Scratch documentation for off-line extensions)
        cmd_list = arguments.split('/')

        s = 'okay'
        # If the command is to turn the light on or off we send directly the instruction
        # I add a small timeout at the end of each instruction in case the client keeps
        # sending messages
        if cmd_list[0] == 'lighton':
            RoboArm.ctrl_transfer(0x40,6,0x100,0,[0,0,1])
            time.sleep(0.1)
        elif cmd_list[0] == 'lightoff':
            RoboArm.ctrl_transfer(0x40,6,0x100,0,[0,0,0])
            time.sleep(0.1)
        # If the command is one of the commands that we know (see dictionary above)
        # then we invoke it. Notice that all these commands come with a duration
        # that is found in cmd_list[2].
        # FIXME: maybe add a check that the duration is in a valid range?
        elif cmd_list[0] in armcmds.keys():
            MoveArm(float(cmd_list[2]),armcmds[cmd_list[0]])

        elif cmd_list[0] == 'poll':
            # We do nothing for poll requests!
            s = 'okay'

        # If the command is not one of the above, then print an error and return.
        else:
            print("Some error with this command!"+cmd_list[0])
            return

        # If we reach this point, everything should be OK and we send an 'okay' message back
        self.send_resp(s)

    def send_resp(self, response):
        """
        HTTP response. It always starts with an heading, then it attached
        """

        crlf = "\r\n"
        http_response = "HTTP/1.1 200 OK" + crlf
        http_response += "Content-Type: text/html; charset=ISO-8859-1" + crlf
        http_response += "Content-Length" + str(len(response)) + crlf
        http_response += "Access-Control-Allow-Origin: *" + crlf
        http_response += crlf

        # send it out the door to Scratch
        self.wfile.write(bytes(http_response,'utf-8'))

# A function to execute an instruction for a certain time
def MoveArm(Duration, ArmCmd):
    #Start the movement
    RoboArm.ctrl_transfer(0x40,6,0x100,0,ArmCmd)
    #Stop the movement after waiting a specified duration
    time.sleep(Duration)
    ArmCmd=[0,0,0]
    RoboArm.ctrl_transfer(0x40,6,0x100,0,ArmCmd)
    # As above: adding a small timeout to avoid flooding
    time.sleep(0.1)

def start_server():

    try:
        server = HTTPServer(('localhost', port), GetHandler)
        print('Starting py-marash HTTP Server!')
        print('Use <Ctrl-C> to exit the extension\n')
        print('You can now start Scratch and import the extension')
    except Exception:
        logging.debug('Something wrong here')
        print('HTTP Socket may already be in use or Arm is not connected - restart Scratch')
        raise
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        logging.info('py-marash HTTP server: keyboard interrupt exception')
        print("Goodbye !")
        raise KeyboardInterrupt
    except Exception:
        logging.debug('py-marash: Exception %s' % str(Exception))
        raise

if __name__ == "__main__":
        start_server()

Scratch Extensions

Also save the JSON file below in the Robot directory as: MaplinArm.json

{ "extensionName": "Maplin Arm",
"extensionPort": 14275,
"blockSpecs": [

["w", "Rotate clock-wise for %n second(s)", "rotatecw", 1],
["w", "Rotate anti clock-wise for %n second(s)", "rotateccw", 1],

["w", "Shoulder up for %n second(s)", "shoulderup", 1],
["w", "Shoulder down for %n second(s)", "shoulderdown", 1],

["w", "Elbow up for %n second(s)", "elbowup", 1],
["w", "Elbow down for %n second(s)", "elbowdown", 1],

["w", "Wrist up for %n second(s)", "wristup", 1],
["w", "Wrist down for %n second(s)", "wristdown", 1],

["w", "Grip close for %n second(s)", "gripopen", 1],
["w", "Grip open for %n second(s)", "gripclose", 1],

[" ", "Light ON", "lighton"],
[" ", "Light OFF", "lightoff"]
]
},
}

 

Step 5 – Installing Scratch

Navigate to https://scratch.mit.edu/scratch2download/ and download and install Adobe Air and Scratch Version 2 Offline.  You need to ensure Adobe Air is installed first.

Step 6 – Reboot

Step 7 – Using the Robot Arm

Preparatory steps:

  1. Plug in the Robot arm and switch on
  2. Open a command prompt
  • a.       Navigate to the Robot directory
  • b.      Run:   python pymarash_http_server.py
  • c.       Check that the script finds the Robot arm and tells you to “You can now start Scratch and import the extension”

Start Scratch

a.       Hold down the <SHIFT> key and click on the File menu – choose the very last menu option “… experimental extensions …”

b.      Navigate to the Robot directory and open the JSON file

This loads the new bricks for controlling the Robot Arm under the “Other Bricks” category.

HAVE FUN!