Controlling LEGO Wedo Motor with Python GUI App in Linux

LEGO Wedo 2.0 is a fantastic starting point for learning robotics. I developed a simple GUI application to control the LEGO motor using Python, and in this article, I’ll share my journey of selecting the development environment and building the Python app.

Learning Resources

During my initial research, I came across two helpful articles: Controlling a WeDo 2.0 motor and WeDo 2.0 – reverse engineering. Both articles highlight pygattlib, a Python library for interfacing with BLE (Bluetooth Low Energy) devices on Linux. The LEGO Wedo 2.0 Smart Hub is a BLE device, making this library essential for the project.

Command Line Tools and Development Environment

Before diving into coding, you can use command-line tools like hcitool and gatttool to scan for Bluetooth devices and test connections.

  • Scan for BLE Devices:

      sudo hcitool -i hci0 lescan
      LE Scan ...
      98:07:2D:DD:98:56 (unknown)
      98:07:2D:DD:98:56 LPF2 Smart Hub
    
  • Connect to the Device:

      gatttool -I
      [                 ][LE]> connect <ble address>
        
    

Can I Use Windows Subsystem for Linux (WSL)?

No. You will get the following error message:

Invalid device: Address family not supported by protocol

Can I use Linux in VMWare Workstation for development?

No, BLE support is not available in Linux virtual machines on VMware Workstation, as confirmed by this this StackOverflow answer

hcitool lescan fail

Building a Python GUI App to Control LEGO Wedo Motor on Raspberry Pi

Ultimately, I chose to develop the Python app on a Raspberry Pi, as I didn’t have access to a dedicated Linux PC.

Installing Dependencies

Install the necessary libraries and gattlib:

sudo apt-get update
sudo apt-get install libbluetooth-dev bluez bluez-hcidump libboost-python-dev libboost-thread-dev libglib2.0-dev
sudo pip install gattlib

Creating the GUI Application

The GUI app features a simple window with a label and buttons for motor control:

import Tkinter as tk
from gattlib import DiscoveryService
from gattlib import GATTRequester
from time import sleep

def run():
    global button_run
    button_run.after(DELAY, motor_run)

def stop():
    global button_stop
    button_stop.after(DELAY, motor_stop)

def connect():
    global button_disconnect
    button_disconnect.after(DELAY, smart_hub_connect)

def disconnect():
    global button_disconnect
    button_disconnect.after(DELAY, smart_hub_disconnect)

def up():
    global button_up
    button_up.after(DELAY, motor_up)

def down():
    global button_down
    button_down.after(DELAY, motor_down)

root = tk.Tk()
root.title("Lego Wedo 2.0 Motor Control")

label = tk.Label(root, fg="dark green", text='N/A')
label.pack()

button_connect = tk.Button(root, text='Connect Smart Hub', width=BUTTON_WIDTH, command=connect)
button_connect.pack()

button_disconnect = tk.Button(root, text='Disconnect Smart Hub', width=BUTTON_WIDTH, command=disconnect, state='disabled')
button_disconnect.pack()

button_run = tk.Button(root, text='Run motor', width=BUTTON_WIDTH, command=run, state='disabled')
button_run.pack()

button_up = tk.Button(root, text='Speed up', width=BUTTON_WIDTH, command=up, state='disabled')
button_up.pack()

button_down = tk.Button(root, text='Speed down', width=BUTTON_WIDTH, command=down, state='disabled')
button_down.pack()

button_stop = tk.Button(root, text='Stop motor', width=BUTTON_WIDTH, command=stop, state='disabled')
button_stop.pack()

root.mainloop()

Connecting to the LEGO Smart Hub

When you click the connect button, the app uses DiscoveryService to list available devices and GATTRequester to connect to the Smart Hub:

def smart_hub_connect():
    service = DiscoveryService("hci0")
    devices = service.discover(2)

    for address, name in devices.items():
        if name != '' and 'Smart Hub' in name:
            label['text'] = address

            global button_run, button_stop, button_disconnect, req
            button_connect['state'] = 'disabled'
            button_run['state'] = 'normal'
            button_stop['state'] = 'normal'
            button_disconnect['state'] = 'normal'
            button_up['state'] = 'normal'
            button_down['state'] = 'normal'

            req = GATTRequester(address, True, "hci0")
            break

Controlling the Motor

To run the motor, send a 4-byte sequence to the specific handler:

def motor_run():
    global req
    if req != None:
        req.write_by_handle(0x3d, str(bytearray([0x01, 0x01, 0x01, 0x64]))
)

The first byte represents the motor port (01 or 02).

lego wedo motor port

Use gatttool to test port changes and see how they affect the motor:

[98:07:2D:DD:98:56][LE]> char-read-hnd 0015                                                                     
Characteristic value/descriptor: 01 01 00 01 01 00 00 00 01 00 00 00                                                    
[98:07:2D:DD:98:56][LE]> char-read-hnd 0015                                                                             
Characteristic value/descriptor: 02 01 01 01 01 00 00 00 01 00 00 00

Speed control logic:

MAX_SPEED = 100
MIN_SPEED = 1
SPEED_CHANGE = 4

current_speed = 100
req = None

def motor_up():
    global req, current_speed
    if req != None:
        if current_speed == MAX_SPEED:
            return

        current_speed += SPEED_CHANGE
        req.write_by_handle(HANDLE, str(bytearray([0x01, 0x01, 0x01, current_speed])))
        sleep(WEDO_DELAY)

def motor_down():
    global req, current_speed
    if req != None:
        if current_speed == MIN_SPEED:
            return

        current_speed -= SPEED_CHANGE
        req.write_by_handle(HANDLE, str(bytearray([0x01, 0x01, 0x01, current_speed])))
        sleep(WEDO_DELAY)

Running the App with Proper Permissions

Running the app without root privileges will result in errors:

python lego wedo fail

Instead of using sudo, launch the app with gksudo:

gksudo python app.py

python lego wedo success

Tip for Windows Users: You can use SmarTTY to display the GUI of the remote Linux app.

LEGO Wedo 2.0 motor control

Source Code

https://github.com/yushulx/lego-wedo-motor-control