|
| 1 | +--- |
| 2 | +title: Controlling a WeDo 2.0 motor |
| 3 | +subject: Hardware - WeDo |
| 4 | +author: "@JorgePe" |
| 5 | +--- |
| 6 | + |
| 7 | +* Table of Contents |
| 8 | +{:toc} |
| 9 | + |
| 10 | +## Intro |
| 11 | + |
| 12 | +LEGO Education released the second version of WeDo in the beginning of 2016. |
| 13 | +We'll show how to use the bluez, the linux bluetooth stack, to wireless control a |
| 14 | +WeDo 2.0 motor. |
| 15 | + |
| 16 | +The first WeDo version uses USB so every robot needs to be tethered to a *host* |
| 17 | +(usually a computer but can also be a Mindstorms EV3 running ev3dev) |
| 18 | +The second WeDo version uses [BLE](https://en.wikipedia.org/wiki/Bluetooth_low_energy) (Bluetooth Low Energy, a sub-set of the Bluetooth |
| 19 | +4.0 standard) so robots can now be totally autonomous. |
| 20 | + |
| 21 | +## Requirements |
| 22 | + |
| 23 | +The EV3 internal bluetooth isn't compliant with the BT 4.0 BLE subset so we need an |
| 24 | +USB Bluetooth 4.0 dongle supported by ev3dev. If it works with Ubuntu or with a |
| 25 | +Raspberry Pi then most probably will also work with ev3dev. |
| 26 | + |
| 27 | +Since we'll probably use Wi-Fi, an USB hub will be required aswell. Most (but not |
| 28 | +all) USB 2.0 hubs work fine with ev3dev. |
| 29 | + |
| 30 | +If ev3dev recognizes our Bluetooth 4.0 dongle, we'll have two hci devices - the |
| 31 | +internal bluetooth and the new USB one: |
| 32 | + |
| 33 | + robot@ev3dev:~# hciconfig -a |
| 34 | + hci1: Type: BR/EDR Bus: UART |
| 35 | + BD Address: 00:17:EC:48:44:6B ACL MTU: 1021:4 SCO MTU: 180:4 |
| 36 | + UP RUNNING |
| 37 | + RX bytes:863 acl:0 sco:0 events:32 errors:0 |
| 38 | + TX bytes:1396 acl:0 sco:0 commands:32 errors:0 |
| 39 | + Features: 0xff 0xff 0x2d 0xfe 0x9b 0xff 0x79 0x83 |
| 40 | + Packet type: DM1 DM3 DM5 DH1 DH3 DH5 HV1 HV2 HV3 |
| 41 | + Link policy: RSWITCH HOLD SNIFF PARK |
| 42 | + Link mode: SLAVE ACCEPT |
| 43 | + Name: 'ev3dev' |
| 44 | + Class: 0x000000 |
| 45 | + Service Classes: Unspecified |
| 46 | + Device Class: Miscellaneous, |
| 47 | + HCI Version: 2.1 (0x4) Revision: 0x0 |
| 48 | + LMP Version: 2.1 (0x4) Subversion: 0x191f |
| 49 | + Manufacturer: Texas Instruments Inc. (13) |
| 50 | + |
| 51 | + hci0: Type: BR/EDR Bus: USB |
| 52 | + BD Address: 00:19:0E:16:3F:EA ACL MTU: 1021:8 SCO MTU: 64:1 |
| 53 | + UP RUNNING |
| 54 | + RX bytes:11809 acl:120 sco:0 events:872 errors:0 |
| 55 | + TX bytes:8004 acl:120 sco:0 commands:470 errors:0 |
| 56 | + Features: 0xbf 0xfe 0xcf 0xfe 0xdb 0xff 0x7b 0x87 |
| 57 | + Packet type: DM1 DM3 DM5 DH1 DH3 DH5 HV1 HV2 HV3 |
| 58 | + Link policy: RSWITCH SNIFF |
| 59 | + Link mode: SLAVE ACCEPT |
| 60 | + Name: 'ev3dev #1' |
| 61 | + Class: 0x000000 |
| 62 | + Service Classes: Unspecified |
| 63 | + Device Class: Miscellaneous, |
| 64 | + HCI Version: 4.0 (0x6) Revision: 0x1000 |
| 65 | + LMP Version: 4.0 (0x6) Subversion: 0x220e |
| 66 | + Manufacturer: Broadcom Corporation (15) |
| 67 | + |
| 68 | +In the above situation, `hci0` is our Bluetooth 4.0 BLE device (note "BUS: USB" and |
| 69 | +"HCI version: 4.0"). If you don't see status "UP RUNNING" you need to activate |
| 70 | +Bluetooth first (one easy way is using the Brickman User Interface: choose "Wireless |
| 71 | +and Networks" at the main screen then "Bluetooth") |
| 72 | + |
| 73 | +We also need a recent bluez version for BLE support. Most recent builds of ev3dev |
| 74 | +will have it already (checked with "ev3-ev3dev-jessie-2015-12-30.img.xz"). |
| 75 | + |
| 76 | +Now we need to find the bluetooth address of our WeDo 2.0 hub. |
| 77 | +For that we press it's button to put it in descovery mode and run this |
| 78 | +command: |
| 79 | + |
| 80 | + robot@ev3dev:~# sudo hcitool -i hci0 lescan |
| 81 | + LE Scan ... |
| 82 | + A0:E6:F8:1E:58:57 (unknown) |
| 83 | + A0:E6:F8:1E:58:57 |
| 84 | + |
| 85 | +In the example above, `A0:E6:F8:1E:58:57` is the bluetooth address of our WeDo 2.0 |
| 86 | +hub. We can use other tools, including a smartphone with BLE support - the WeDo 2.0 |
| 87 | +will probably show up as `LPF2 Smart Hub 2 I/O`, just look for the address in its |
| 88 | +properties. |
| 89 | + |
| 90 | + |
| 91 | +## Shell script example |
| 92 | + |
| 93 | +This short shell script makes a motor connected to the first port spin for one second |
| 94 | +then stop: |
| 95 | + |
| 96 | + #!/bin/bash |
| 97 | + gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write -a 0x003d -n 01010164 |
| 98 | + sleep 1 |
| 99 | + gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write -a 0x003d -n 01010100 |
| 100 | + |
| 101 | +(You need to run this script with sudo, unless you already have root previleges) |
| 102 | + |
| 103 | +We see that it uses the gatttool command to send a sequence of 4 bytes to one specific |
| 104 | +handler (0x003d). The WeDo 2.0 has several handlers but until LEGO Education releases |
| 105 | +the promised SDK this is the only handler we "know" how to use: |
| 106 | + |
| 107 | +This is meaning of those 4 bytes: |
| 108 | +* the first byte defines the port (01 or 02) |
| 109 | +* the second byte defines the command (01 = motor speed) |
| 110 | +* the third byte defines the length of the following argument(s) (01) |
| 111 | +* the fouth byte is the argument, in this case the speed percentage |
| 112 | + |
| 113 | +To spin in one direction we send a positive value from 1 to 100 (or 01 to 64 in |
| 114 | +hexadecimal). |
| 115 | +To spin in the opposite direction we send a "negative" value from |
| 116 | +255 to 156 (or FF to 9C in hexadecimal). |
| 117 | +To stop the motor we set the speed as zero (00). |
| 118 | +Please note that for small speed values (less than 20%) the motor will not respond. |
| 119 | + |
| 120 | + |
| 121 | +## Python example |
| 122 | + |
| 123 | +To use pyhton with the WeDo 2.0 we need a BLE library. Unfortunately BLE |
| 124 | +support in python is still quite imature but there is at least one library that |
| 125 | +works in ev3dev - [gattlib](https://bitbucket.org/OscarAcena/pygattlib) |
| 126 | + |
| 127 | + sudo apt-get install pkg-config libboost-python-dev libboost-thread-dev \ |
| 128 | + libbluetooth-dev libglib2.0-dev python-dev |
| 129 | + |
| 130 | + sudo pip install gattlib |
| 131 | + |
| 132 | +This library is also used as an extension for a more known library, [pybluez](https://pypi.python.org/pypi/PyBluez) |
| 133 | +so if you want a library for both bluetooth "Classic" and BLE this would be better: |
| 134 | + |
| 135 | + pip install pybluez |
| 136 | + pip install pybluez[ble] |
| 137 | + |
| 138 | +Unfortunately I couldn't make it work in my ev3dev system. |
| 139 | + |
| 140 | +Please note that it takes **a lot** of memory and around 2 hours to install gattlib. |
| 141 | +After some failures ("virtual memory exhausted: Cannot allocate memory") I finally |
| 142 | +succeeded extending my ev3dev swapfile to almost 1 GB (please first check if you have |
| 143 | +enough free space in your SD card): |
| 144 | + |
| 145 | + robot@ev3dev:~$ sudo dd if=/dev/zero of=/swapfile1 bs=1024 count=917504 |
| 146 | + 917504+0 records in |
| 147 | + 917504+0 records out |
| 148 | + 939524096 bytes (940 MB) copied, 442.332 s, 2.1 MB/s |
| 149 | + |
| 150 | +it will take 5 to 10 minutes to allocate space for such a big file |
| 151 | + |
| 152 | + robot@ev3dev:~$ sudo mkswap /swapfile1 |
| 153 | + Setting up swapspace version 1, size = 917500 KiB |
| 154 | + no label, UUID=55fcb430-451b-4699-955c-5754bf65999b |
| 155 | + |
| 156 | + robot@ev3dev:~$ sudo swapon /swapfile1 |
| 157 | + swapon: /swapfile1: insecure permissions 0644, 0600 suggested. |
| 158 | + |
| 159 | +this is a temporary measure so we'll skip security warning |
| 160 | + |
| 161 | + robot@ev3dev:~$ sudo swapon -s |
| 162 | + Filename Type Size Used Priority |
| 163 | + /dev/zram0 partition 98300 8188 16383 |
| 164 | + /swapfile1 file 917500 0 -1 |
| 165 | + |
| 166 | +We don't want to use the swapfile in memory (it will overflow) so we disable it: |
| 167 | + |
| 168 | + sudo systemctl stop zram_swap.service |
| 169 | + |
| 170 | +After installation completes we reset the swapfile configuration: |
| 171 | + |
| 172 | + sudo systemctl start zram_swap.service |
| 173 | + sudo swapoff /swapfile1 |
| 174 | + sudo rm /swapfile1 |
| 175 | + |
| 176 | +This short python script makes the motor spin 2 second in each direction then stop: |
| 177 | + |
| 178 | +{% highlight python %} |
| 179 | +#!/usr/bin/python |
| 180 | +from gattlib import GATTRequester |
| 181 | +from time import sleep |
| 182 | + |
| 183 | +req = GATTRequester("A0:E6:F8:1E:58:57",True,"hci0") |
| 184 | +req.write_by_handle(0x3d, "\x01\x01\x01\x64") |
| 185 | +sleep(2) |
| 186 | +req.write_by_handle(0x3d, "\x01\x01\x01\x9C") |
| 187 | +sleep(2) |
| 188 | +req.write_by_handle(0x3d, "\x01\x01\x01\x00") |
| 189 | +{% endhighlight %} |
| 190 | + |
| 191 | +## A more practical example |
| 192 | + |
| 193 | +A BLE connection is not permanent - it drops after a few seconds. And the WeDO 2.0 |
| 194 | +hub also enters in sleep mode a few seconds after the connection drops so we need |
| 195 | +to assure this never happens. |
| 196 | + |
| 197 | +We will use an EV3 touch sensor to control the direction of the WeDo 2.0 motor and |
| 198 | +periodically refresh the connection. |
| 199 | + |
| 200 | +{% highlight python %} |
| 201 | +#!/usr/bin/python |
| 202 | + |
| 203 | +from ev3dev.auto import * |
| 204 | +from gattlib import GATTRequester |
| 205 | +from time import sleep |
| 206 | + |
| 207 | +address = "A0:E6:F8:1E:58:57" |
| 208 | +HANDLE = 0x3d |
| 209 | +SPIN_LEFT = "\x01\x01\x01\x64" |
| 210 | +SPIN_RIGHT = "\x01\x01\x01\x9C" |
| 211 | +SPIN_STOP = "\x01\x01\x01\x00" |
| 212 | +DELAY = 0.3 # this is empiric - the WeDo seems to need this delay |
| 213 | + # between each command |
| 214 | + |
| 215 | +ts = TouchSensor(); |
| 216 | + |
| 217 | +req = GATTRequester(address,True,"hci0") |
| 218 | +sleep(DELAY) |
| 219 | + |
| 220 | +command = SPIN_LEFT |
| 221 | +while True: |
| 222 | + if ts.value(): |
| 223 | + if(req.is_connected() == True): |
| 224 | + print("Already connected") |
| 225 | + sleep(DELAY) |
| 226 | + else: |
| 227 | + print("Connecting...") |
| 228 | + req.connect(True) |
| 229 | + print("OK") |
| 230 | + sleep(DELAY) |
| 231 | + |
| 232 | + req.write_by_handle(HANDLE, command) |
| 233 | + |
| 234 | + if (command == SPIN_LEFT): |
| 235 | + command = SPIN_RIGHT |
| 236 | + else: |
| 237 | + command = SPIN_LEFT |
| 238 | + sleep(DELAY) |
| 239 | + |
| 240 | + if(req.is_connected() == True): |
| 241 | + print("Still connected") |
| 242 | + else: |
| 243 | + print("Reconnecting...") |
| 244 | + req.connect(True) |
| 245 | + print("OK") |
| 246 | + sleep(DELAY) |
| 247 | +{% endhighlight %} |
| 248 | + |
| 249 | +This video shows the script in action: |
| 250 | +{% include youtube-embed.html youtube_video_id="0d3MdZuDOTc" %} |
| 251 | + |
| 252 | +## Final notes |
| 253 | + |
| 254 | +We still need to know all motor commands and option. But until LEGO releases |
| 255 | +its SDK there is at list this way to extend the number of motors available to the EV3. |
| 256 | +And if rumours are true, the next generation of LEGO Power Functions and Mindstorms |
| 257 | +will both share some components with the WeDo 2.0 (the Hub is already announcing |
| 258 | +itself as "LEGO Power Functions 2" device) so this might be just the start. |
0 commit comments