├── images ├── example01.png ├── example02.png ├── read_tilt.png ├── move1motor.png ├── move2motors.png ├── read_color.png └── read_distance.png ├── LICENSE ├── README.md ├── snap-boost.py └── snap-boost-blocks-v2.xml /images/example01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JorgePe/SnapBOOST/HEAD/images/example01.png -------------------------------------------------------------------------------- /images/example02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JorgePe/SnapBOOST/HEAD/images/example02.png -------------------------------------------------------------------------------- /images/read_tilt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JorgePe/SnapBOOST/HEAD/images/read_tilt.png -------------------------------------------------------------------------------- /images/move1motor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JorgePe/SnapBOOST/HEAD/images/move1motor.png -------------------------------------------------------------------------------- /images/move2motors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JorgePe/SnapBOOST/HEAD/images/move2motors.png -------------------------------------------------------------------------------- /images/read_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JorgePe/SnapBOOST/HEAD/images/read_color.png -------------------------------------------------------------------------------- /images/read_distance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JorgePe/SnapBOOST/HEAD/images/read_distance.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Jorge Pereira 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SnapBOOST 2 | This is a BOOST extension for [Snap!](http://snap.berkeley.edu/) 3 | 4 | It is based on Connor Hudson (AKA technoboy10) [snap-server](https://github.com/technoboy10/snap-server). 5 | 6 | I just converted it to python3 and added the BOOST methods from own library, [pyb00st](https://github.com/JorgePe/pyb00st). 7 | 8 | It still needs some cleaning and improvements but it already works on my Ubuntu laptop and my Raspbery Pi Zero W. 9 | 10 | The idea is to use a Raspberry Pi with BLE (like the Raspberry Pi 3 or the Raspberry Pi Zero W, but any 11 | model with a USB BT 4.x BLE dongle will work) as a gateway between a browser (on Windows, OSX, Linux 12 | Android, iOS...) and a LEGO BOOST Move Hub. 13 | 14 | Altough it uses my own python library, it can be easily adapted for any other python library, like [pylgbst](https://github.com/undera/pylgbst) 15 | 16 | For now, this extension only controls the motors by time and only read the internal tilt sensor 17 | and the Color/Dist sensor, will add the rest of the commands later. 18 | 19 | 20 | # Requirements 21 | 22 | - python 3.x 23 | - pyb00st 24 | 25 | 26 | ## Installation: 27 | 28 | Create a working directory on the Raspberry Pi, like 'snap-boost'. 29 | 30 | Save the Snap! extension script ('snap-boost.py') inside this directory and give it execution permissions. 31 | 32 | Save the XML with the block definitions ('snap-boost-blocks-v#.xml') to the computer/tablet/phone 33 | from where you will use a browser to run Snap! 34 | 35 | Create a subdirectory named 'pyb00st'. 36 | 37 | Download my [pyb00st library](https://github.com/JorgePe/pyb00st/archive/master.zip) 38 | Only 3 files are realy needed, they are inside 'pyb00st-master/pyb00st/': 39 | 40 | - movehub.my 41 | - constants.py 42 | - \_\_init\_\_.py 43 | 44 | Put those 3 files in the 'pyb00st' subdirectory of your working directory. 45 | 46 | 47 | ## Usage: 48 | 49 | The extension assumes a Color/Distance sensor connected to port C, you can change to port D 50 | by changing the variable 'colordist_port': 51 | 52 | `colordist_port = PORT_C` 53 | 54 | 55 | Start the extension: 56 | 57 | `./snap-boost.py` 58 | 59 | You'll get: 60 | 61 | ``` 62 | Snap! BOOST extension by JorgePe 63 | Serving at port 8001 64 | Go ahead and launch Snap! 65 | http://snap.berkeley.edu/snapsource/snap.html 66 | Then import the 'snap-boost-blocks-v#.xml' containing block definitions for motor and sensors. 67 | System: linux 68 | ``` 69 | 70 | On your browser, start the [Snap!](http://snap.berkeley.edu/snapsource/snap.html) runtime and use the menu option 71 | 'Import...' and specify the XML file you saved before. 72 | 73 | You will now have blocks for motors (under 'Motion' category) and sensors (under 'Sensing' category). 74 | 75 | 76 | # Available blocks 77 | 78 | The XML file includes definitions of 2 Motion blocks and 3 Sensing blocks: 79 | 80 | ![](https://github.com/JorgePe/SnapBOOST/blob/master/images/move1motor.png) 81 | 82 | ![](https://github.com/JorgePe/SnapBOOST/blob/master/images/move2motors.png) 83 | 84 | ![](https://github.com/JorgePe/SnapBOOST/blob/master/images/read_color.png) 85 | 86 | ![](https://github.com/JorgePe/SnapBOOST/blob/master/images/read_distance.png) 87 | 88 | ![](https://github.com/JorgePe/SnapBOOST/blob/master/images/read_tilt.png) 89 | 90 | For each block you need to specify the IP Address of your Raspberry Pi to use it (you could define a 91 | variable and use it with every block). If you change the port (8001 by default) on the python code you 92 | should also specify it. 93 | 94 | `move1motor` requires a motor (`a`, `b`, `c` or `d`), a `time` value (between 0 and 999 milliseconds) 95 | and a `speed` value (between -100% and 100%) 96 | 97 | `move2motors` is simillar but doesn't require a motor (as it works only with A+B motors) and requires 98 | two `speed` values (one for motor A and the other for motor B) 99 | 100 | `read color` returns one of these colors: 'BLACK', 'BLUE', 'GREEN', 'YELLOW', 'RED', 'WHITE'. 101 | 102 | `read distance` returns a number between 0 and 10. 103 | 104 | `read tilt` returns of these values: 'TILT_HORIZ', 'TILT_UP', 'TILT_DOWN', 'TILT_RIGHT', 'TILT_LEFT', 'TILT_INVERT'. 105 | 106 | 107 | # First Example 108 | 109 | This example makes the two internal Interactive Motors turn for 0.25 seconds at full speed whenever the 110 | distance sensor detects something at less than '6' whatever-units-LEGO-uses. 111 | 112 | ![](https://github.com/JorgePe/SnapBOOST/blob/master/images/example01.png) 113 | 114 | On my Vernie, that makes it runaway for a bit. 115 | 116 | 117 | # Second Example 118 | 119 | When this example is used with Vernie it explores the world, moving forward until it founds an obstacle, then it turns right and keeps doing that, designing a path on the screen: 120 | 121 | ![](https://github.com/JorgePe/SnapBOOST/blob/master/images/example02.png) 122 | 123 | Here a video showing Vernie controlled with this code: https://youtu.be/9VF77k3UVa4 124 | -------------------------------------------------------------------------------- /snap-boost.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # #Snap! extension base by Technoboy10 4 | # https://github.com/technoboy10/snap-server/blob/master/snap-server.py 5 | # Adapted for python3 and LEGO BOOST by JorgePe 6 | # October 2017 7 | # tested on: 8 | # - x64 laptop running Ubuntu 17.04 (kernel 4.10.0-35) 9 | 10 | import http.server 11 | import re 12 | import os 13 | import socketserver 14 | 15 | #from . import movehub, constants 16 | #from pyb00st import movehub, constants 17 | 18 | from pyb00st.movehub import MoveHub 19 | from pyb00st.constants import * 20 | 21 | from time import sleep 22 | 23 | MY_MOVEHUB_ADD = '00:16:53:A4:CD:7E' 24 | MY_BTCTRLR_HCI = 'hci0' 25 | 26 | colordist_port = PORT_C 27 | 28 | class CORSHTTPRequestHandler(http.server.SimpleHTTPRequestHandler): 29 | 30 | def send_head(self): 31 | path = self.path 32 | ospath = os.path.abspath('') 33 | 34 | if 'move1motor' in path: 35 | regex = re.compile("\/move1motor([ab])x([0-9]+)x([0-9]+)x([+-])") 36 | m = regex.match(path) 37 | # print('Regex: ', m.group(1), m.group(2), m.group(3), m.group(4)) 38 | if m.group(4) == '-': 39 | dutycycle = -1 * int(m.group(3)) 40 | elif m.group(4) == "+": 41 | dutycycle = int(m.group(3)) 42 | milliseconds = int(m.group(2)) 43 | motor = m.group(1) 44 | # print('Motor: ',motor) 45 | # print('Ms: ',milliseconds) 46 | # print('DC: ',dutycycle) 47 | # print('Ms/1000:',milliseconds/1000) 48 | if motor == "a": 49 | mymovehub.run_motor_for_time(MOTOR_A, milliseconds, dutycycle) 50 | sleep(milliseconds/1000) 51 | elif motor == "b": 52 | mymovehub.run_motor_for_time(MOTOR_B, milliseconds, dutycycle) 53 | sleep(milliseconds/1000) 54 | self.send_response(200) 55 | self.send_header('Content-type', 'text/html') 56 | self.end_headers() 57 | 58 | elif 'move2motors' in path: 59 | regex = re.compile("\/move2motors(([0-9]+)x([0-9]+)x([+-])x([0-9]+)x([+-]))") 60 | m = regex.match(path) 61 | # print('Regex: ', m.group(1), m.group(2), m.group(3), m.group(4), m.group(5), m.group(6)) 62 | milliseconds = int(m.group(2)) 63 | if m.group(4) == '-': 64 | dutycycle1 = -1 * int(m.group(3)) 65 | elif m.group(4) == '+': 66 | dutycycle1 = int(m.group(3)) 67 | if m.group(6) == '-': 68 | dutycycle2 = -1 * int(m.group(5)) 69 | elif m.group(6) == '+': 70 | dutycycle2 = int(m.group(5)) 71 | 72 | # print('Ms: ',milliseconds) 73 | # print('DC1: ', dutycycle1) 74 | # print('DC2: ', dutycycle2) 75 | # print('Ms/1000:',milliseconds/1000) 76 | 77 | mymovehub.run_motors_for_time(MOTOR_AB, milliseconds, dutycycle1, dutycycle2) 78 | sleep(milliseconds/1000) 79 | 80 | self.send_response(200) 81 | self.send_header('Content-type', 'text/html') 82 | self.end_headers() 83 | 84 | 85 | # Warning: 86 | # this lefts files 'tilt', 'dist' and 'color' in the current directory 87 | # containing the last value read from the BOOT Move Hub 88 | 89 | if path=='/tilt': 90 | f = open(ospath + '/tilt', 'w+') 91 | tilt = mymovehub.last_hubtilt 92 | print('Tilt:',tilt) 93 | if tilt in TILT_BASIC_VALUES: 94 | f.write(TILT_BASIC_TEXT[tilt]) 95 | else: 96 | f.write('?') 97 | f.close() 98 | f = open(ospath + '/tilt', 'rb') 99 | ctype = self.guess_type(ospath + '/tilt') 100 | self.send_response(200) 101 | self.send_header("Content-type", ctype) 102 | fs = os.fstat(f.fileno()) 103 | self.send_header("Content-Length", str(fs[6])) 104 | self.send_header("Last-Modified", self.date_time_string(fs.st_mtime)) 105 | self.send_header("Access-Control-Allow-Origin", "*") 106 | self.end_headers() 107 | return f 108 | 109 | elif path=='/dist': 110 | f = open(ospath + '/dist', 'w+') 111 | f.write(str(mymovehub.last_distance_C)) 112 | f.close() 113 | f = open(ospath + '/dist', 'rb') 114 | ctype = self.guess_type(ospath + '/dist') 115 | self.send_response(200) 116 | self.send_header("Content-type", ctype) 117 | fs = os.fstat(f.fileno()) 118 | self.send_header("Content-Length", str(fs[6])) 119 | self.send_header("Last-Modified", self.date_time_string(fs.st_mtime)) 120 | self.send_header("Access-Control-Allow-Origin", "*") 121 | self.end_headers() 122 | return f 123 | 124 | elif path=='/color': 125 | f = open(ospath + '/color', 'w+') 126 | f.write(mymovehub.last_color_C) 127 | f.close() 128 | f = open(ospath + '/color', 'rb') 129 | ctype = self.guess_type(ospath + '/color') 130 | self.send_response(200) 131 | self.send_header("Content-type", ctype) 132 | fs = os.fstat(f.fileno()) 133 | self.send_header("Content-Length", str(fs[6])) 134 | self.send_header("Last-Modified", self.date_time_string(fs.st_mtime)) 135 | self.send_header("Access-Control-Allow-Origin", "*") 136 | self.end_headers() 137 | return f 138 | 139 | if __name__ == "__main__": 140 | print('Snap! BOOST extension by JorgePe') 141 | 142 | PORT = 8001 143 | 144 | Handler = CORSHTTPRequestHandler 145 | httpd = socketserver.TCPServer(("", PORT), Handler, bind_and_activate=False) 146 | httpd.allow_reuse_address = True 147 | try: 148 | httpd.server_bind() 149 | httpd.server_activate() 150 | except: 151 | httpd.server_close() 152 | raise 153 | 154 | print('Serving at port', PORT) 155 | print('Go ahead and launch Snap!') 156 | print('http://snap.berkeley.edu/snapsource/snap.html') 157 | print('Then import the 'snap-boost-blocks-v#.xml' containing block definitions for motor and sensors.') 158 | 159 | mymovehub = MoveHub(MY_MOVEHUB_ADD, 'BlueZ', MY_BTCTRLR_HCI) 160 | 161 | try: 162 | mymovehub.start() 163 | mymovehub.subscribe_all() 164 | mymovehub.listen_hubtilt(MODE_HUBTILT_BASIC) 165 | mymovehub.listen_colordist_sensor(colordist_port) 166 | httpd.serve_forever() 167 | finally: 168 | mymovehub.stop() 169 | -------------------------------------------------------------------------------- /snap-boost-blocks-v2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 192.168.1.100 7 | 8001 8 | 9 | 33 |
34 | 37 |
38 | 39 | 40 | a 41 | 100 42 | 50 43 | 192.168.1.100 44 | 8001 45 | 46 | 136 | 166 | 167 | 168 | 169 | 170 |
171 | 174 |
175 | 176 | 177 | 100 178 | 50 179 | 50 180 | 192.168.1.100 181 | 8001 182 | 183 | 246 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 271 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | move2motors 284 | 285 | x 286 | 287 | 288 | 289 | 290 | 291 | 292 | x 293 | 294 | x 295 | 296 | 297 | 298 | 299 | 300 | 301 | x 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 |
312 | 313 |
314 | 315 | 316 | 192.168.1.100 317 | 8001 318 | 319 | 343 |
344 | 345 |
346 | 347 | 348 | 192.168.1.100 349 | 8001 350 | 351 | 375 |
376 |
--------------------------------------------------------------------------------