├── 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 | 
81 |
82 | 
83 |
84 | 
85 |
86 | 
87 |
88 | 
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 | 
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 | 
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 |
--------------------------------------------------------------------------------