Skip to content

Commit 32eecac

Browse files
authored
Make v1.0, based on official docs (#27)
* It's HUB ID * Rename file * Working with official doc * Some progress * AttachedIO msg * device action impl * some better device alert impl * restructuring * Some port commands handled * Some command feedback waiting * Some more request-reply things * Some more request-reply things * Reworked msg classes * Getting back to UTs * Getting back to UTs * Facing sync lock problems * Facing sync lock problems * Testing it * Covering more with tests * handle actions * Hub class is almost covered * done coverage for Hub * done coverage for MoveHub * Button is tested * remove debug server from examples * Current and voltage tested * color sensor basic test * cover tilt sensor * motor sensor tested * constant motor * motor is tested * hold_speed impl for motor * motor commands recorded * some cleanup * some cleanup * some cleanup * debug * debug * FIX a bug * acc/dec profiles figured out * UT motor ops * UT motor ops * Get rid of weird piggyback * fix UT * Fix encoding? * fix test mb * More robust * Checked demo works * cosmetics * cosmetics * Maybe better test * fetching and decoding some device caps * describing devs * describing devs works * Applying modes we've learned * Simple and extensible dev attach * Reworking peripherals based on modes * Applying modes we've learned * implemented getting sensor data * fixed port subscribe * Added led out cmds on vision sensor * Worked on color-distance sensor * Introduce some locking for consistency * Improved it all * Travis flags * improve * improve * improve docs
1 parent 0e7457e commit 32eecac

40 files changed

+3781
-1235
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
*.pyc
44
build
55
*.avi
6+
test_real.py

.travis.yml

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,33 +5,33 @@ virtualenv:
55

66
matrix:
77
include:
8-
- os: linux
9-
python: 2.7
10-
- os: linux
11-
python: 3.4
8+
- os: linux
9+
python: 2.7
10+
- os: linux
11+
python: 3.4
1212

1313
addons:
1414
apt:
1515
packages:
16-
- libboost-python-dev
17-
- libboost-thread-dev
18-
- libbluetooth-dev
16+
- libboost-python-dev
17+
- libboost-thread-dev
18+
- libbluetooth-dev
1919

20-
- libglib2.0-dev
21-
- libdbus-1-dev
22-
- libdbus-glib-1-dev
23-
- libgirepository-1.0-1
20+
- libglib2.0-dev
21+
- libdbus-1-dev
22+
- libdbus-glib-1-dev
23+
- libgirepository-1.0-1
2424

25-
- python-dbus
26-
- python-gi
27-
- python3-dbus
28-
- python3-gi
25+
- python-dbus
26+
- python-gi
27+
- python3-dbus
28+
- python3-gi
2929
install:
30-
- pip install codecov nose-exclude gattlib pygatt gatt pexpect bluepy
30+
- pip install codecov nose-exclude gattlib pygatt gatt pexpect bluepy
3131

3232

33-
script: coverage run --source=. `which nosetests` tests --nocapture --exclude-dir=examples
33+
script: coverage run --source=. -m nose tests -v --exclude-dir=examples
3434

3535
after_success:
36-
- coverage report -m
37-
- codecov
36+
- coverage report -m
37+
- codecov

README.md

Lines changed: 20 additions & 248 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Best way to start is to look into [`demo.py`](examples/demo.py) file, and run it
88

99
If you have Vernie assembled, you might run scripts from [`examples/vernie`](examples/vernie) directory.
1010

11-
Demonstrational videos:
11+
## Demonstrational Videos
1212

1313
[![Vernie Programmed](http://img.youtube.com/vi/oqsmgZlVE8I/0.jpg)](http://www.youtube.com/watch?v=oqsmgZlVE8I)
1414
[![Laser Engraver](http://img.youtube.com/vi/ZbKmqVBBMhM/0.jpg)](https://youtu.be/ZbKmqVBBMhM)
@@ -18,16 +18,17 @@ Demonstrational videos:
1818

1919
## Features
2020

21-
- auto-detect and connect to Move Hub device
22-
- auto-detects peripheral devices connected to Hub
23-
- constant, angled and timed movement for motors, rotation sensor subscription
24-
- color & distance sensor: several modes to measure distance, color and luminosity
25-
- tilt sensor subscription: 2 axis, 3 axis, bump detect modes
26-
- LED color change
27-
- push button status subscription
28-
- battery voltage subscription available
21+
- auto-detect and connect to [Move Hub](docs/MoveHub.md) device
22+
- auto-detects [peripheral devices](docs/Peripherals.md) connected to Hub
23+
- constant, angled and timed movement for [motors](docs/Motor.md), rotation sensor subscription
24+
- [vision sensor](docs/VisionSensor.md): several modes to measure distance, color and luminosity
25+
- [tilt sensor](docs/TiltSensor.md) subscription: 2 axis, 3 axis, bump detect modes
26+
- [RGB LED](docs/LED.md) color change
27+
- [push button](docs/MoveHub.md#push-button) status subscription
28+
- [battery voltage and current](docs/VoltageCurrent.md) subscription available
2929
- permanent Bluetooth connection server for faster debugging
3030

31+
3132
## Usage
3233

3334
_Please note that this library requires one of Bluetooth backend libraries to be installed, please read section [here](#bluetooth-backend-prerequisites) for details._
@@ -40,217 +41,17 @@ pip install https://github.com/undera/pylgbst/archive/1.0.tar.gz
4041
Then instantiate MoveHub object and start invoking its methods. Following is example to just print peripherals detected on Hub:
4142

4243
```python
43-
from pylgbst.movehub import MoveHub
44+
from pylgbst.hub import MoveHub
4445

4546
hub = MoveHub()
4647

47-
for device in hub.devices:
48+
for device in hub.peripherals:
4849
print(device)
4950
```
5051

51-
### Controlling Motors
52-
53-
MoveHub provides motors via following fields:
54-
- `motor_A` - port A
55-
- `motor_B` - port B
56-
- `motor_AB` - motor group of A+B manipulated together
57-
- `motor_external` - external motor attached to port C or D
58-
59-
Methods to activate motors are:
60-
- `constant(speed_primary, speed_secondary)` - enables motor with specified speed forever
61-
- `timed(time, speed_primary, speed_secondary)` - enables motor with specified speed for `time` seconds, float values accepted
62-
- `angled(angle, speed_primary, speed_secondary)` - makes motor to rotate to specified angle, `angle` value is integer degrees, can be negative and can be more than 360 for several rounds
63-
- `stop()` - stops motor at once, it is equivalent for `constant(0)`
64-
65-
Parameter `speed_secondary` is used when it is motor group of `motor_AB` running together. By default, `speed_secondary` equals `speed_primary`. Speed values range is `-1.0` to `1.0`, float values. _Note: In group angled mode, total rotation angle is distributed across 2 motors according to motor speeds ratio._
66-
67-
All these methods are synchronous by default, means method does not return untill it gets confirmation from Hub that command has completed. You can pass `async=True` parameter to any of methods to switch into asynchronous, which means command will return immediately, without waiting for rotation to complete. Be careful with asynchronous calls, as they make Hub to stop reporting synchronizing statuses.
68-
69-
An example:
70-
```python
71-
from pylgbst.movehub import MoveHub
72-
import time
73-
74-
hub = MoveHub()
75-
76-
hub.motor_A.timed(0.5, 0.8)
77-
hub.motor_A.timed(0.5, -0.8)
78-
79-
hub.motor_B.angled(90, 0.8)
80-
hub.motor_B.angled(-90, 0.8)
81-
82-
hub.motor_AB.timed(1.5, 0.8, -0.8)
83-
hub.motor_AB.angled(90, 0.8, -0.8)
84-
85-
hub.motor_external.constant(0.2)
86-
time.sleep(2)
87-
hub.motor_external.stop()
88-
```
89-
90-
91-
### Motor Rotation Sensors
92-
93-
Any motor allows to subscribe to its rotation sensor. Two sensor modes are available: rotation angle (`EncodedMotor.SENSOR_ANGLE`) and rotation speed (`EncodedMotor.SENSOR_SPEED`). Example:
94-
95-
```python
96-
from pylgbst.movehub import MoveHub, EncodedMotor
97-
import time
98-
99-
def callback(angle):
100-
print("Angle: %s" % angle)
101-
102-
hub = MoveHub()
103-
104-
hub.motor_A.subscribe(callback, mode=EncodedMotor.SENSOR_ANGLE)
105-
time.sleep(60) # rotate motor A
106-
hub.motor_A.unsubscribe(callback)
107-
```
108-
109-
### Tilt Sensor
110-
111-
MoveHub's internal tilt sensor is available through `tilt_sensor` field. There are several modes to subscribe to sensor, providing 2-axis, 3-axis and bump detect data.
112-
113-
An example:
114-
115-
```python
116-
from pylgbst.movehub import MoveHub, TiltSensor
117-
import time
118-
119-
def callback(pitch, roll, yaw):
120-
print("Pitch: %s / Roll: %s / Yaw: %s" % (pitch, roll, yaw))
121-
122-
hub = MoveHub()
123-
124-
hub.tilt_sensor.subscribe(callback, mode=TiltSensor.MODE_3AXIS_FULL)
125-
time.sleep(60) # turn MoveHub block in different ways
126-
hub.tilt_sensor.unsubscribe(callback)
127-
```
128-
129-
`TiltSensor` sensor mode constants:
130-
- `MODE_2AXIS_SIMPLE` - use `callback(state)` for 2-axis simple state detect
131-
- `MODE_2AXIS_FULL` - use `callback(roll, pitch)` for 2-axis roll&pitch degree values
132-
- `MODE_3AXIS_SIMPLE` - use `callback(state)` for 3-axis simple state detect
133-
- `MODE_3AXIS_FULL` - use `callback(roll, pitch)` for 2-axis roll&pitch degree values
134-
- `MODE_BUMP_COUNT` - use `callback(count)` to detect bumps
135-
136-
There are tilt sensor constants for "simple" states, for 2-axis mode their names are also available through `TiltSensor.DUO_STATES`:
137-
- `DUO_HORIZ` - "HORIZONTAL"
138-
- `DUO_DOWN` - "DOWN"
139-
- `DUO_LEFT` - "LEFT"
140-
- `DUO_RIGHT` - "RIGHT"
141-
- `DUO_UP` - "UP"
142-
143-
For 3-axis simple mode map name is `TiltSensor.TRI_STATES` with values:
144-
- `TRI_BACK` - "BACK"
145-
- `TRI_UP` - "UP"
146-
- `TRI_DOWN` - "DOWN"
147-
- `TRI_LEFT` - "LEFT"
148-
- `TRI_RIGHT` - "RIGHT"
149-
- `TRI_FRONT` - "FRONT"
150-
151-
152-
### Color & Distance Sensor
153-
154-
Field named `color_distance_sensor` holds instance of `ColorDistanceSensor`, if one is attached to MoveHub. Sensor has number of different modes to subscribe.
155-
156-
Colors that are detected are part of `COLORS` map (see [LED](#led) section). Only several colors are possible to detect: `BLACK`, `BLUE`, `CYAN`, `YELLOW`, `RED`, `WHITE`. Sensor does its best to detect best color, but only works when sample is very close to sensor.
157-
158-
Distance works in range of 0-10 inches, with ability to measure last inch in higher detail.
159-
160-
Simple example of subscribing to sensor:
161-
162-
```python
163-
from pylgbst.movehub import MoveHub, ColorDistanceSensor
164-
import time
165-
166-
def callback(clr, distance):
167-
print("Color: %s / Distance: %s" % (clr, distance))
168-
169-
hub = MoveHub()
170-
171-
hub.color_distance_sensor.subscribe(callback, mode=ColorDistanceSensor.COLOR_DISTANCE_FLOAT)
172-
time.sleep(60) # play with sensor while it waits
173-
hub.color_distance_sensor.unsubscribe(callback)
174-
```
175-
176-
Subscription mode constants in class `ColorDistanceSensor` are:
177-
- `COLOR_DISTANCE_FLOAT` - default mode, use `callback(color, distance)` where `distance` is float value in inches
178-
- `COLOR_ONLY` - use `callback(color)`
179-
- `DISTANCE_INCHES` - use `callback(color)` measures distance in integer inches count
180-
- `COUNT_2INCH` - use `callback(count)` - it counts crossing distance ~2 inches in front of sensor
181-
- `DISTANCE_HOW_CLOSE` - use `callback(value)` - value of 0 to 255 for 30 inches, larger with closer distance
182-
- `DISTANCE_SUBINCH_HOW_CLOSE` - use `callback(value)` - value of 0 to 255 for 1 inch, larger with closer distance
183-
- `LUMINOSITY` - use `callback(luminosity)` where `luminosity` is float value from 0 to 1
184-
- `OFF1` and `OFF2` - seems to turn sensor LED and notifications off
185-
- `STREAM_3_VALUES` - use `callback(val1, val2, val3)`, sends some values correlating to distance, not well understood at the moment
186-
187-
Tip: laser pointer pointing to sensor makes it to trigger distance sensor
188-
189-
### LED
190-
191-
`MoveHub` class has field `led` to access color LED near push button. To change its color, use `set_color(color)` method.
192-
193-
You can obtain colors are present as constants `COLOR_*` and also a map of available color-to-name as `COLORS`. There are 12 color values, including `COLOR_BLACK` and `COLOR_NONE` which turn LED off.
194-
195-
Additionally, you can subscribe to LED color change events, using callback function as shown in example below.
196-
197-
```python
198-
from pylgbst.movehub import MoveHub, COLORS, COLOR_NONE, COLOR_RED
199-
import time
52+
Each peripheral kind has own methods to do actions and/or get sensor data. See [features](#features) list for individual doc pages.
20053

201-
def callback(clr):
202-
print("Color has changed: %s" % clr)
203-
204-
hub = MoveHub()
205-
hub.led.subscribe(callback)
206-
207-
hub.led.set_color(COLOR_RED)
208-
for color in COLORS:
209-
hub.led.set_color(color)
210-
time.sleep(0.5)
211-
212-
hub.led.set_color(COLOR_NONE)
213-
hub.led.unsubscribe(callback)
214-
```
215-
216-
Tip: blinking orange color of LED means battery is low.
217-
218-
### Push Button
219-
220-
`MoveHub` class has field `button` to subscribe to button press and release events.
221-
222-
Note that `Button` class is not real `Peripheral`, as it has no port and not listed in `devices` field of Hub. Still, subscribing to button is done usual way:
223-
224-
```python
225-
from pylgbst.movehub import MoveHub
226-
227-
def callback(is_pressed):
228-
print("Btn pressed: %s" % is_pressed)
229-
230-
hub = MoveHub()
231-
hub.button.subscribe(callback)
232-
```
233-
234-
### Power Voltage & Battery
235-
236-
`MoveHub` class has field `voltage` to subscribe to battery voltage status. Callback accepts single parameter with current value. The range of values is float between `0` and `1.0`. Every time data is received, value is also written into `last_value` field of `Voltage` object. Values less than `0.2` are known as lowest values, when unit turns off.
237-
238-
```python
239-
from pylgbst.movehub import MoveHub
240-
import time
241-
242-
def callback(value):
243-
print("Voltage: %s" % value)
244-
245-
hub = MoveHub()
246-
hub.voltage.subscribe(callback)
247-
time.sleep(1)
248-
print ("Value: " % hub.voltage.last_value)
249-
```
250-
251-
## General Notes
252-
253-
### Bluetooth Backend Prerequisites
54+
## Bluetooth Backend Prerequisites
25455

25556
You have following options to install as Bluetooth backend:
25657

@@ -278,43 +79,17 @@ There is optional parameter for `MoveHub` class constructor, accepting instance
27879

27980
All the functions above have optional arguments to specify adapter name and MoveHub mac address. Please look function source code for details.
28081

281-
If you want to specify name for Bluetooth interface to use on local computer, you can passthat to class or function of getting a connection. Then pass connection object to `MoveHub` constructor. Like this:
82+
If you want to specify name for Bluetooth interface to use on local computer, you can pass that to class or function of getting a connection. Then pass connection object to `MoveHub` constructor. Like this:
28283
```python
283-
from pylgbst.movehub import MoveHub
84+
from pylgbst.hub import MoveHub
28485
from pylgbst.comms.cgatt import GattConnection
28586

28687
conn = GattConnection("hci1")
287-
conn.connect() # you can pass MoveHub mac address as parameter here, like 'AA:BB:CC:DD:EE:FF'
88+
conn.connect() # you can pass Hub mac address as parameter here, like 'AA:BB:CC:DD:EE:FF'
28889

28990
hub = MoveHub(conn)
29091
```
29192

292-
### Use Disconnect in `finally`
293-
294-
It is recommended to make sure `disconnect()` method is called on connection object after you have finished your program. This ensures Bluetooth subsystem is cleared and avoids problems for subsequent re-connects of MoveHub. The best way to do that in Python is to use `try ... finally` clause:
295-
296-
```python
297-
from pylgbst import get_connection_auto
298-
from pylgbst.movehub import MoveHub
299-
300-
conn=get_connection_auto() # ! don't put this into `try` block
301-
try:
302-
hub = MoveHub(conn)
303-
finally:
304-
conn.disconnect()
305-
```
306-
307-
### Devices Detecting
308-
As part of instantiating process, `MoveHub` waits up to 1 minute for all builtin devices to appear, such as motors on ports A and B, tilt sensor, button and battery. This not guarantees that external motor and/or color sensor will be present right after `MoveHub` instantiated. Usually, sleeping for couple of seconds gives it enough time to detect everything.
309-
310-
### Subscribing to Sensors
311-
Each sensor usually has several different "subscription modes", differing with callback parameters and value semantics.
312-
313-
There is optional `granularity` parameter for each subscription call, by default it is `1`. This parameter tells Hub when to issue sensor data notification. Value of notification has to change greater or equals to `granularity` to issue notification. This means that specifying `0` will cause it to constantly send notifications, and specifying `5` will cause less frequent notifications, only when values change for more than `5` (inclusive).
314-
315-
It is possible to subscribe with multiple times for the same sensor. Only one, very last subscribe mode is in effect, with many subscriber callbacks allowed to receive notifications.
316-
317-
Good practice for any program is to unsubscribe from all sensor subscriptions before ending, especially when used with `DebugServer`.
31893

31994
## Debug Server
32095
Running debug server opens permanent BLE connection to Hub and listening on TCP port for communications. This avoids the need to re-start Hub all the time.
@@ -331,18 +106,15 @@ Then push green button on MoveHub, so permanent BLE connection will be establish
331106

332107
## Roadmap & TODO
333108

109+
- validate operations with other Hub types (train, PUP etc)
110+
- make connections to detect hub by UUID instead of name
334111
- document all API methods
335-
- make sure unit tests cover all important code
336112
- make debug server to re-establish BLE connection on loss
337113

338114
## Links
339115

340116
- https://github.com/LEGO/lego-ble-wireless-protocol-docs - true docs for LEGO BLE protocol
341117
- https://github.com/JorgePe/BOOSTreveng - initial source of protocol knowledge
118+
- https://github.com/nathankellenicki/node-poweredup - JavaScript version of library
342119
- https://github.com/spezifisch/sphero-python/blob/master/BB8joyDrive.py - example with another approach to bluetooth libs
343120

344-
Some things around visual programming:
345-
- https://github.com/RealTimeWeb/blockpy
346-
- https://ru.wikipedia.org/wiki/App_Inventor
347-
- https://en.wikipedia.org/wiki/Blockly
348-

0 commit comments

Comments
 (0)