Skip to content

Commit d7eaf87

Browse files
authored
Merge pull request #53 from bp1222/break-modes-add-tornado
Break out thread implementation, add Tornado Support
2 parents c0ad6f1 + b1ae5df commit d7eaf87

45 files changed

Lines changed: 2543 additions & 822 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.travis.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,7 @@ python:
33
- "2.7"
44
- "3.4"
55
- "3.5"
6+
install:
7+
pip install -e .[tornado]
68
# command to run tests
7-
script: nosetests
9+
script: py.test

README.rst

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,13 @@ in the root of the source tree:
5959

6060
This will automatically install the package for you.
6161

62+
Install with Tornado Support
63+
----------------------------
64+
65+
::
66+
pip install xbee[tornado]
67+
68+
6269
Documentation
6370
=============
6471

@@ -80,7 +87,9 @@ PySerial
8087
Additional Dependencies
8188
-----------------------
8289

83-
To run automated tests: `Nose <https://github.com/nose-devs/nose/>`_
90+
If wanting to use the Tornado IOLoop: `Tornado <http://www.tornadoweb.org/>`_
91+
92+
To run automated tests: `pytest <https://docs.pytest.org>`_
8493

8594
To build the documentation: `Sphinx <http://sphinx-doc.org/>`_
8695

@@ -101,3 +110,4 @@ Contributors
101110
* Amit Synderman
102111
* Marco Sangalli
103112
* James Saunders james@saunders-family.net
113+
* David Walker dwalker@n.io

docs/source/index.rst

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,12 @@ Usage
4242
must be enabled. For instructions about how to do this, see
4343
the documentation for your XBee device.
4444

45-
Synchonous Mode
45+
.. note::
46+
The default implementation of the python-xbee library is to
47+
use threads. There is an implementation that utilizes the
48+
Tornado IOLoop, if imported.
49+
50+
Threaded Synchonous Mode
4651
~~~~~~~~~~~~~~~
4752

4853
The following code demonstrates a minimal use-case for the
@@ -67,7 +72,7 @@ print out any data frames which arrive from a connected XBee
6772
device. Be aware that wait_read_frame() will block until
6873
a valid frame is received from the associated XBee device.
6974

70-
Asynchronous Mode
75+
Threaded Asynchronous Mode
7176
~~~~~~~~~~~~~~~~~
7277

7378
The xbee package is used only slightly differently when
@@ -110,6 +115,46 @@ to handle receiving and processing incoming data from an
110115
XBee device. This example is functionally equivalent to the non-asyncronous
111116
example above.
112117

118+
Tornado IOLoop
119+
~~~~~~~~~~~~~~~~~~
120+
Tornado provides a simple and easy to use IOLoop for asynchronous listening
121+
for XBee communications. The library usage is seemingly identical to the
122+
threaded implementation, excepting importing and yielding. The example
123+
highlights the key differences::
124+
125+
import serial
126+
from tornado import ioloop, gen
127+
from xbee.tornado import XBee
128+
129+
serial_port = serial.Serial('/dev/ttyUSB0', 9600)
130+
131+
def print_data(data):
132+
"""
133+
This method is called whenever data is received
134+
from the associated XBee device. Its first and
135+
only argument is the data contained within the
136+
frame.
137+
"""
138+
print data
139+
140+
@gen.coroutine
141+
def main():
142+
xbee = XBee(serial_port, callback=print_data)
143+
144+
try:
145+
while True:
146+
yield gen.sleep(0.001)
147+
except KeyboardInterrupt:
148+
ioloop.IOLoop.current().stop()
149+
finally
150+
xbee.halt()
151+
serial_port.close()
152+
153+
ioloop.IOLoop.current().spawn_callback(main)
154+
ioloop.IOLoop.current().start()
155+
ioloop.IOLoop.current().close()
156+
157+
113158
Additional Examples
114159
~~~~~~~~~~~~~~~~~~~
115160

examples/alarm.py

Lines changed: 53 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66
By Paul Malmsten, 2010
77
pmalmsten@gmail.com
88
9-
This module will communicate with a remote XBee device in order to
9+
This module will communicate with a remote XBee device in order to
1010
implement a simple alarm clock with bed occupancy detection.
1111
"""
1212
import serial
13-
from xbee import XBee
13+
from xbee.thread import XBee
1414

1515
class DataSource(object):
1616
"""
@@ -20,133 +20,133 @@ class DataSource(object):
2020
def next_alarm_time(self, current_time):
2121
"""
2222
next_alarm_time: datetime -> datetime
23-
23+
2424
Returns the next time at which the alarm should activate
2525
"""
2626
raise NotImplemented()
27-
27+
2828
class AlarmDevice(object):
2929
"""
30-
Represents alarm harware, such as input and output to an from
30+
Represents alarm harware, such as input and output to an from
3131
the real world
3232
"""
33-
33+
3434
def __init__(self, hw):
3535
self.hw = hw
36-
36+
3737
def activate(self):
3838
"""
3939
activate: None -> None
40-
40+
4141
Activates noise-making features
4242
"""
4343
raise NotImplementedError()
44-
44+
4545
def deactivate(self):
4646
"""
4747
deactivate: None -> None
48-
48+
4949
Deactivates noise-making features
5050
"""
5151
raise NotImplementedError()
52-
52+
5353
def bed_occupied(self):
5454
"""
5555
bed_occupied: None -> Boolean
56-
56+
5757
Determines whether the bed is currently occupied
5858
"""
5959
raise NotImplementedError()
60-
60+
6161
class WakeupRoutine(object):
6262
"""
6363
Represents a process by which a user should be awoken with a
6464
particular AlarmDevice
6565
"""
66-
66+
6767
def __init__(self, device):
6868
self.device = device
69-
69+
7070
def trigger(self):
7171
"""
7272
trigger: None -> None
73-
74-
Begins the specified wakeup process with the given hardware
73+
74+
Begins the specified wakeup process with the given hardware
7575
device. Does not relinquish control until the wakeup process is
7676
complete.
7777
"""
7878
raise NotImplementedError()
79-
79+
8080
# ================= Custom Classes =============================
8181

8282
class TestSource(DataSource):
8383
def __init__(self, time):
8484
super(TestSource, self).__init__()
8585
self.next_time = time
86-
86+
8787
def next_alarm_time(self, current_time):
8888
return self.next_time
89-
89+
9090
class XBeeAlarm(AlarmDevice):
9191
DETECT_THRESH = 350
92-
92+
9393
def __init__(self, serial_port, remote_addr):
9494
# Open serial port, construct XBee1, configure remote device,
9595
# store as hardware
9696
self.remote_addr = remote_addr
97-
97+
9898
ser = serial.Serial(serial_port)
9999
xbee = XBee(ser)
100-
100+
101101
super(XBeeAlarm, self).__init__(xbee)
102-
102+
103103
# Reset remote device
104104
self._reset()
105-
105+
106106
def _reset(self):
107107
"""
108108
reset: None -> None
109-
109+
110110
Resets the remote XBee device to a standard configuration
111111
"""
112112
# Analog pin 0
113113
self.hw.remote_at(
114114
dest_addr=self.remote_addr,
115115
command='D0',
116116
parameter='\x02')
117-
117+
118118
# Disengage remote LED, buzzer
119119
self.deactivate()
120120
self._set_send_samples(False)
121-
121+
122122
def _set_LED(self, status):
123123
"""
124124
_set_LED: boolean -> None
125-
125+
126126
Sets the status of the remote LED
127127
"""
128128
# DIO pin 1 (LED), active low
129129
self.hw.remote_at(
130130
dest_addr=self.remote_addr,
131131
command='D1',
132132
parameter='\x04' if status else '\x05')
133-
133+
134134
def _set_buzzer(self, status):
135135
"""
136136
_set_buzzer: boolean -> None
137-
137+
138138
Sets the status of the remote buzzer
139139
"""
140140
# DIO pin 1 (LED), active low
141141
self.hw.remote_at(
142142
dest_addr=self.remote_addr,
143143
command='D2',
144144
parameter='\x05' if status else '\x04')
145-
145+
146146
def _set_send_samples(self, status):
147147
"""
148148
_set_send_samples: boolean -> None
149-
149+
150150
Sets whether the remote device will send data samples once every
151151
second.
152152
"""
@@ -159,83 +159,83 @@ def _set_send_samples(self, status):
159159
def activate(self):
160160
"""
161161
activate: None -> None
162-
162+
163163
Remote XBee starts making noise and turns on LED
164164
"""
165165
self._set_LED(True)
166166
self._set_buzzer(True)
167-
167+
168168
def deactivate(self):
169169
"""
170170
activate: None -> None
171-
171+
172172
Remote XBee starts making noise and turns on LED
173173
"""
174174
self._set_LED(False)
175175
self._set_buzzer(False)
176-
176+
177177
def bed_occupied(self):
178178
"""
179179
bed_occupied: None -> boolean
180-
180+
181181
Determines whether the bed is currently occupied by requesting
182182
data from the remote XBee and comparing the analog value with
183183
a threshold.
184184
"""
185-
185+
186186
# Receive samples from the remote device
187187
self._set_send_samples(True)
188-
188+
189189
while True:
190190
packet = self.hw.wait_read_frame()
191-
191+
192192
if 'adc-0' in packet['samples'][0]:
193193
# Stop receiving samples from the remote device
194194
self._set_send_samples(False)
195195
return packet['samples'][0]['adc-0'] > XBeeAlarm.DETECT_THRESH
196-
197-
196+
197+
198198
class SimpleWakeupRoutine(WakeupRoutine):
199199
"""
200-
When triggered, activates the alarm if the bed is occupied. The
200+
When triggered, activates the alarm if the bed is occupied. The
201201
alarm continues until the bed is no longer occupied.
202202
"""
203-
203+
204204
def trigger(self):
205205
from time import sleep
206-
206+
207207
pulse_delay = 0.1
208-
209-
208+
209+
210210
if self.device.bed_occupied():
211211
# Initial alarm
212212
for x in range(0, 5):
213213
self.device.activate()
214214
sleep(pulse_delay)
215215
self.device.deactivate()
216216
sleep(pulse_delay)
217-
217+
218218
# Allow time to escape
219219
sleep(30)
220-
220+
221221
# Extended alarm
222222
duration = 1
223223
pause = 10
224-
224+
225225
while self.device.bed_occupied():
226226
self.device.activate()
227227
sleep(duration)
228228
self.device.deactivate()
229229
sleep(pause)
230230
duration *= 2
231-
231+
232232
def main():
233233
"""
234234
Run through simple demonstration of alarm concept
235235
"""
236236
alarm = XBeeAlarm('/dev/ttyUSB0', '\x56\x78')
237237
routine = SimpleWakeupRoutine(alarm)
238-
238+
239239
from time import sleep
240240
while True:
241241
"""

0 commit comments

Comments
 (0)