Coverage for /usr/lib/python3/dist-packages/gpiozero/input_devices.py: 33%

357 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2024-04-05 16:40 +0100

1# vim: set fileencoding=utf-8: 

2# 

3# GPIO Zero: a library for controlling the Raspberry Pi's GPIO pins 

4# 

5# Copyright (c) 2015-2021 Dave Jones <dave@waveform.org.uk> 

6# Copyright (c) 2015-2021 Ben Nuttall <ben@bennuttall.com> 

7# Copyright (c) 2020 Robert Erdin <roberte@depop.com> 

8# Copyright (c) 2020 Dan Jackson <dan@djackson.org> 

9# Copyright (c) 2016-2020 Andrew Scheller <github@loowis.durge.org> 

10# Copyright (c) 2019 Kosovan Sofiia <sofiia.kosovan@gmail.com> 

11# Copyright (c) 2018 Philippe Muller <philippe.muller@gmail.com> 

12# Copyright (c) 2016 Steveis <SteveAmor@users.noreply.github.com> 

13# 

14# SPDX-License-Identifier: BSD-3-Clause 

15 

16from __future__ import ( 

17 unicode_literals, 

18 print_function, 

19 absolute_import, 

20 division, 

21) 

22 

23import warnings 

24from time import sleep, time 

25from threading import Event, Lock 

26from itertools import tee 

27try: 

28 from statistics import median, mean 

29except ImportError: 

30 from .compat import median, mean 

31 

32from .exc import InputDeviceError, DeviceClosed, DistanceSensorNoEcho, \ 

33 PinInvalidState, PWMSoftwareFallback 

34from .devices import GPIODevice, CompositeDevice 

35from .mixins import GPIOQueue, EventsMixin, HoldMixin, event 

36try: 

37 from .pins.pigpio import PiGPIOFactory 

38except ImportError: 

39 PiGPIOFactory = None 

40 

41 

42class InputDevice(GPIODevice): 

43 """ 

44 Represents a generic GPIO input device. 

45 

46 This class extends :class:`GPIODevice` to add facilities common to GPIO 

47 input devices. The constructor adds the optional *pull_up* parameter to 

48 specify how the pin should be pulled by the internal resistors. The 

49 :attr:`is_active` property is adjusted accordingly so that :data:`True` 

50 still means active regardless of the *pull_up* setting. 

51 

52 :type pin: int or str 

53 :param pin: 

54 The GPIO pin that the device is connected to. See :ref:`pin-numbering` 

55 for valid pin numbers. If this is :data:`None` a :exc:`GPIODeviceError` 

56 will be raised. 

57 

58 :type pull_up: bool or None 

59 :param pull_up: 

60 If :data:`True`, the pin will be pulled high with an internal resistor. 

61 If :data:`False` (the default), the pin will be pulled low. If 

62 :data:`None`, the pin will be floating. As gpiozero cannot 

63 automatically guess the active state when not pulling the pin, the 

64 *active_state* parameter must be passed. 

65 

66 :type active_state: bool or None 

67 :param active_state: 

68 If :data:`True`, when the hardware pin state is ``HIGH``, the software 

69 pin is ``HIGH``. If :data:`False`, the input polarity is reversed: when 

70 the hardware pin state is ``HIGH``, the software pin state is ``LOW``. 

71 Use this parameter to set the active state of the underlying pin when 

72 configuring it as not pulled (when *pull_up* is :data:`None`). When 

73 *pull_up* is :data:`True` or :data:`False`, the active state is 

74 automatically set to the proper value. 

75 

76 :type pin_factory: Factory or None 

77 :param pin_factory: 

78 See :doc:`api_pins` for more information (this is an advanced feature 

79 which most users can ignore). 

80 """ 

81 def __init__(self, pin=None, pull_up=False, active_state=None, 

82 pin_factory=None): 

83 super(InputDevice, self).__init__(pin, pin_factory=pin_factory) 

84 try: 

85 self.pin.function = 'input' 

86 pull = {None: 'floating', True: 'up', False: 'down'}[pull_up] 

87 if self.pin.pull != pull: 

88 self.pin.pull = pull 

89 except: 

90 self.close() 

91 raise 

92 

93 if pull_up is None: 93 ↛ 94line 93 didn't jump to line 94, because the condition on line 93 was never true

94 if active_state is None: 

95 raise PinInvalidState( 

96 'Pin %d is defined as floating, but "active_state" is not ' 

97 'defined' % self.pin.number) 

98 self._active_state = bool(active_state) 

99 else: 

100 if active_state is not None: 100 ↛ 101line 100 didn't jump to line 101, because the condition on line 100 was never true

101 raise PinInvalidState( 

102 'Pin %d is not floating, but "active_state" is not None' % 

103 self.pin.number) 

104 self._active_state = False if pull_up else True 

105 self._inactive_state = not self._active_state 

106 

107 @property 

108 def pull_up(self): 

109 """ 

110 If :data:`True`, the device uses a pull-up resistor to set the GPIO pin 

111 "high" by default. 

112 """ 

113 pull = self.pin.pull 

114 if pull == 'floating': 

115 return None 

116 else: 

117 return pull == 'up' 

118 

119 def __repr__(self): 

120 try: 

121 return "<gpiozero.%s object on pin %r, pull_up=%s, is_active=%s>" % ( 

122 self.__class__.__name__, self.pin, self.pull_up, self.is_active) 

123 except: 

124 return super(InputDevice, self).__repr__() 

125 

126 

127class DigitalInputDevice(EventsMixin, InputDevice): 

128 """ 

129 Represents a generic input device with typical on/off behaviour. 

130 

131 This class extends :class:`InputDevice` with machinery to fire the active 

132 and inactive events for devices that operate in a typical digital manner: 

133 straight forward on / off states with (reasonably) clean transitions 

134 between the two. 

135 

136 :type pin: int or str 

137 :param pin: 

138 The GPIO pin that the device is connected to. See :ref:`pin-numbering` 

139 for valid pin numbers. If this is :data:`None` a :exc:`GPIODeviceError` 

140 will be raised. 

141 

142 :type pull_up: bool or None 

143 :param pull_up: 

144 See description under :class:`InputDevice` for more information. 

145 

146 :type active_state: bool or None 

147 :param active_state: 

148 See description under :class:`InputDevice` for more information. 

149 

150 :type bounce_time: float or None 

151 :param bounce_time: 

152 Specifies the length of time (in seconds) that the component will 

153 ignore changes in state after an initial change. This defaults to 

154 :data:`None` which indicates that no bounce compensation will be 

155 performed. 

156 

157 :type pin_factory: Factory or None 

158 :param pin_factory: 

159 See :doc:`api_pins` for more information (this is an advanced feature 

160 which most users can ignore). 

161 """ 

162 def __init__( 

163 self, pin=None, pull_up=False, active_state=None, bounce_time=None, 

164 pin_factory=None): 

165 super(DigitalInputDevice, self).__init__( 

166 pin, pull_up=pull_up, active_state=active_state, 

167 pin_factory=pin_factory) 

168 try: 

169 self.pin.bounce = bounce_time 

170 self.pin.edges = 'both' 

171 self.pin.when_changed = self._pin_changed 

172 # Call _fire_events once to set initial state of events 

173 self._fire_events(self.pin_factory.ticks(), self.is_active) 

174 except: 

175 self.close() 

176 raise 

177 

178 def _pin_changed(self, ticks, state): 

179 # XXX This is a bit of a hack; _fire_events takes *is_active* rather 

180 # than *value*. Here we're assuming no-one's overridden the default 

181 # implementation of *is_active*. 

182 self._fire_events(ticks, bool(self._state_to_value(state))) 

183 

184 

185class SmoothedInputDevice(EventsMixin, InputDevice): 

186 """ 

187 Represents a generic input device which takes its value from the average of 

188 a queue of historical values. 

189 

190 This class extends :class:`InputDevice` with a queue which is filled by a 

191 background thread which continually polls the state of the underlying 

192 device. The average (a configurable function) of the values in the queue is 

193 compared to a threshold which is used to determine the state of the 

194 :attr:`is_active` property. 

195 

196 .. note:: 

197 

198 The background queue is not automatically started upon construction. 

199 This is to allow descendents to set up additional components before the 

200 queue starts reading values. Effectively this is an abstract base 

201 class. 

202 

203 This class is intended for use with devices which either exhibit analog 

204 behaviour (such as the charging time of a capacitor with an LDR), or those 

205 which exhibit "twitchy" behaviour (such as certain motion sensors). 

206 

207 :type pin: int or str 

208 :param pin: 

209 The GPIO pin that the device is connected to. See :ref:`pin-numbering` 

210 for valid pin numbers. If this is :data:`None` a :exc:`GPIODeviceError` 

211 will be raised. 

212 

213 :type pull_up: bool or None 

214 :param pull_up: 

215 See description under :class:`InputDevice` for more information. 

216 

217 :type active_state: bool or None 

218 :param active_state: 

219 See description under :class:`InputDevice` for more information. 

220 

221 :param float threshold: 

222 The value above which the device will be considered "on". 

223 

224 :param int queue_len: 

225 The length of the internal queue which is filled by the background 

226 thread. 

227 

228 :param float sample_wait: 

229 The length of time to wait between retrieving the state of the 

230 underlying device. Defaults to 0.0 indicating that values are retrieved 

231 as fast as possible. 

232 

233 :param bool partial: 

234 If :data:`False` (the default), attempts to read the state of the 

235 device (from the :attr:`is_active` property) will block until the queue 

236 has filled. If :data:`True`, a value will be returned immediately, but 

237 be aware that this value is likely to fluctuate excessively. 

238 

239 :param average: 

240 The function used to average the values in the internal queue. This 

241 defaults to :func:`statistics.median` which is a good selection for 

242 discarding outliers from jittery sensors. The function specified must 

243 accept a sequence of numbers and return a single number. 

244 

245 :type ignore: frozenset or None 

246 :param ignore: 

247 The set of values which the queue should ignore, if returned from 

248 querying the device's value. 

249 

250 :type pin_factory: Factory or None 

251 :param pin_factory: 

252 See :doc:`api_pins` for more information (this is an advanced feature 

253 which most users can ignore). 

254 """ 

255 def __init__( 

256 self, pin=None, pull_up=False, active_state=None, threshold=0.5, 

257 queue_len=5, sample_wait=0.0, partial=False, average=median, 

258 ignore=None, pin_factory=None): 

259 self._queue = None 

260 super(SmoothedInputDevice, self).__init__( 

261 pin, pull_up=pull_up, active_state=active_state, 

262 pin_factory=pin_factory) 

263 try: 

264 self._queue = GPIOQueue(self, queue_len, sample_wait, partial, 

265 average, ignore) 

266 self.threshold = float(threshold) 

267 except: 

268 self.close() 

269 raise 

270 

271 def close(self): 

272 try: 

273 self._queue.stop() 

274 except AttributeError: 

275 # If the queue isn't initialized (it's None), or _queue hasn't been 

276 # set ignore the error because we're trying to close anyway 

277 pass 

278 except RuntimeError: 

279 # Cannot join thread before it starts; we don't care about this 

280 # because we're trying to close the thread anyway 

281 pass 

282 self._queue = None 

283 super(SmoothedInputDevice, self).close() 

284 

285 def __repr__(self): 

286 try: 

287 self._check_open() 

288 except DeviceClosed: 

289 return super(SmoothedInputDevice, self).__repr__() 

290 else: 

291 if self.partial or self._queue.full.is_set(): 

292 return super(SmoothedInputDevice, self).__repr__() 

293 else: 

294 return "<gpiozero.%s object on pin %r, pull_up=%s>" % ( 

295 self.__class__.__name__, self.pin, self.pull_up) 

296 

297 @property 

298 def queue_len(self): 

299 """ 

300 The length of the internal queue of values which is averaged to 

301 determine the overall state of the device. This defaults to 5. 

302 """ 

303 self._check_open() 

304 return self._queue.queue.maxlen 

305 

306 @property 

307 def partial(self): 

308 """ 

309 If :data:`False` (the default), attempts to read the 

310 :attr:`~SmoothedInputDevice.value` or 

311 :attr:`~SmoothedInputDevice.is_active` properties will block until the 

312 queue has filled. 

313 """ 

314 self._check_open() 

315 return self._queue.partial 

316 

317 @property 

318 def value(self): 

319 """ 

320 Returns the average of the values in the internal queue. This is 

321 compared to :attr:`~SmoothedInputDevice.threshold` to determine whether 

322 :attr:`is_active` is :data:`True`. 

323 """ 

324 self._check_open() 

325 return self._queue.value 

326 

327 @property 

328 def threshold(self): 

329 """ 

330 If :attr:`~SmoothedInputDevice.value` exceeds this amount, then 

331 :attr:`is_active` will return :data:`True`. 

332 """ 

333 return self._threshold 

334 

335 @threshold.setter 

336 def threshold(self, value): 

337 if not (0.0 < value < 1.0): 

338 raise InputDeviceError( 

339 'threshold must be between zero and one exclusive' 

340 ) 

341 self._threshold = float(value) 

342 

343 @property 

344 def is_active(self): 

345 """ 

346 Returns :data:`True` if the :attr:`~SmoothedInputDevice.value` 

347 currently exceeds :attr:`~SmoothedInputDevice.threshold` and 

348 :data:`False` otherwise. 

349 """ 

350 return self.value > self.threshold 

351 

352 

353class Button(HoldMixin, DigitalInputDevice): 

354 """ 

355 Extends :class:`DigitalInputDevice` and represents a simple push button 

356 or switch. 

357 

358 Connect one side of the button to a ground pin, and the other to any GPIO 

359 pin. Alternatively, connect one side of the button to the 3V3 pin, and the 

360 other to any GPIO pin, then set *pull_up* to :data:`False` in the 

361 :class:`Button` constructor. 

362 

363 The following example will print a line of text when the button is pushed:: 

364 

365 from gpiozero import Button 

366 

367 button = Button(4) 

368 button.wait_for_press() 

369 print("The button was pressed!") 

370 

371 :type pin: int or str 

372 :param pin: 

373 The GPIO pin which the button is connected to. See :ref:`pin-numbering` 

374 for valid pin numbers. If this is :data:`None` a :exc:`GPIODeviceError` 

375 will be raised. 

376 

377 :type pull_up: bool or None 

378 :param pull_up: 

379 If :data:`True` (the default), the GPIO pin will be pulled high by 

380 default. In this case, connect the other side of the button to ground. 

381 If :data:`False`, the GPIO pin will be pulled low by default. In this 

382 case, connect the other side of the button to 3V3. If :data:`None`, the 

383 pin will be floating, so it must be externally pulled up or down and 

384 the ``active_state`` parameter must be set accordingly. 

385 

386 :type active_state: bool or None 

387 :param active_state: 

388 See description under :class:`InputDevice` for more information. 

389 

390 :type bounce_time: float or None 

391 :param bounce_time: 

392 If :data:`None` (the default), no software bounce compensation will be 

393 performed. Otherwise, this is the length of time (in seconds) that the 

394 component will ignore changes in state after an initial change. 

395 

396 :param float hold_time: 

397 The length of time (in seconds) to wait after the button is pushed, 

398 until executing the :attr:`when_held` handler. Defaults to ``1``. 

399 

400 :param bool hold_repeat: 

401 If :data:`True`, the :attr:`when_held` handler will be repeatedly 

402 executed as long as the device remains active, every *hold_time* 

403 seconds. If :data:`False` (the default) the :attr:`when_held` handler 

404 will be only be executed once per hold. 

405 

406 :type pin_factory: Factory or None 

407 :param pin_factory: 

408 See :doc:`api_pins` for more information (this is an advanced feature 

409 which most users can ignore). 

410 """ 

411 def __init__( 

412 self, pin=None, pull_up=True, active_state=None, bounce_time=None, 

413 hold_time=1, hold_repeat=False, pin_factory=None): 

414 super(Button, self).__init__( 

415 pin, pull_up=pull_up, active_state=active_state, 

416 bounce_time=bounce_time, pin_factory=pin_factory) 

417 self.hold_time = hold_time 

418 self.hold_repeat = hold_repeat 

419 

420 @property 

421 def value(self): 

422 """ 

423 Returns 1 if the button is currently pressed, and 0 if it is not. 

424 """ 

425 return super(Button, self).value 

426 

427Button.is_pressed = Button.is_active 

428Button.pressed_time = Button.active_time 

429Button.when_pressed = Button.when_activated 

430Button.when_released = Button.when_deactivated 

431Button.wait_for_press = Button.wait_for_active 

432Button.wait_for_release = Button.wait_for_inactive 

433 

434 

435class LineSensor(SmoothedInputDevice): 

436 """ 

437 Extends :class:`SmoothedInputDevice` and represents a single pin line 

438 sensor like the TCRT5000 infra-red proximity sensor found in the `CamJam #3 

439 EduKit`_. 

440 

441 A typical line sensor has a small circuit board with three pins: VCC, GND, 

442 and OUT. VCC should be connected to a 3V3 pin, GND to one of the ground 

443 pins, and finally OUT to the GPIO specified as the value of the *pin* 

444 parameter in the constructor. 

445 

446 The following code will print a line of text indicating when the sensor 

447 detects a line, or stops detecting a line:: 

448 

449 from gpiozero import LineSensor 

450 from signal import pause 

451 

452 sensor = LineSensor(4) 

453 sensor.when_line = lambda: print('Line detected') 

454 sensor.when_no_line = lambda: print('No line detected') 

455 pause() 

456 

457 :type pin: int or str 

458 :param pin: 

459 The GPIO pin which the sensor is connected to. See :ref:`pin-numbering` 

460 for valid pin numbers. If this is :data:`None` a :exc:`GPIODeviceError` 

461 will be raised. 

462 

463 :type pull_up: bool or None 

464 :param pull_up: 

465 See description under :class:`InputDevice` for more information. 

466 

467 :type active_state: bool or None 

468 :param active_state: 

469 See description under :class:`InputDevice` for more information. 

470 

471 :param int queue_len: 

472 The length of the queue used to store values read from the sensor. This 

473 defaults to 5. 

474 

475 :param float sample_rate: 

476 The number of values to read from the device (and append to the 

477 internal queue) per second. Defaults to 100. 

478 

479 :param float threshold: 

480 Defaults to 0.5. When the average of all values in the internal queue 

481 rises above this value, the sensor will be considered "active" by the 

482 :attr:`~SmoothedInputDevice.is_active` property, and all appropriate 

483 events will be fired. 

484 

485 :param bool partial: 

486 When :data:`False` (the default), the object will not return a value 

487 for :attr:`~SmoothedInputDevice.is_active` until the internal queue has 

488 filled with values. Only set this to :data:`True` if you require 

489 values immediately after object construction. 

490 

491 :type pin_factory: Factory or None 

492 :param pin_factory: 

493 See :doc:`api_pins` for more information (this is an advanced feature 

494 which most users can ignore). 

495 

496 .. _CamJam #3 EduKit: http://camjam.me/?page_id=1035 

497 """ 

498 def __init__( 

499 self, pin=None, pull_up=False, active_state=None, queue_len=5, 

500 sample_rate=100, threshold=0.5, partial=False, pin_factory=None): 

501 super(LineSensor, self).__init__( 

502 pin, pull_up=pull_up, active_state=active_state, 

503 threshold=threshold, queue_len=queue_len, 

504 sample_wait=1 / sample_rate, partial=partial, 

505 pin_factory=pin_factory) 

506 self._queue.start() 

507 

508 @property 

509 def value(self): 

510 """ 

511 Returns a value representing the average of the queued values. This 

512 is nearer 0 for black under the sensor, and nearer 1 for white under 

513 the sensor. 

514 """ 

515 return super(LineSensor, self).value 

516 

517 @property 

518 def line_detected(self): 

519 return not self.is_active 

520 

521LineSensor.when_line = LineSensor.when_deactivated 

522LineSensor.when_no_line = LineSensor.when_activated 

523LineSensor.wait_for_line = LineSensor.wait_for_inactive 

524LineSensor.wait_for_no_line = LineSensor.wait_for_active 

525 

526 

527class MotionSensor(SmoothedInputDevice): 

528 """ 

529 Extends :class:`SmoothedInputDevice` and represents a passive infra-red 

530 (PIR) motion sensor like the sort found in the `CamJam #2 EduKit`_. 

531 

532 .. _CamJam #2 EduKit: http://camjam.me/?page_id=623 

533 

534 A typical PIR device has a small circuit board with three pins: VCC, OUT, 

535 and GND. VCC should be connected to a 5V pin, GND to one of the ground 

536 pins, and finally OUT to the GPIO specified as the value of the *pin* 

537 parameter in the constructor. 

538 

539 The following code will print a line of text when motion is detected:: 

540 

541 from gpiozero import MotionSensor 

542 

543 pir = MotionSensor(4) 

544 pir.wait_for_motion() 

545 print("Motion detected!") 

546 

547 :type pin: int or str 

548 :param pin: 

549 The GPIO pin which the sensor is connected to. See :ref:`pin-numbering` 

550 for valid pin numbers. If this is :data:`None` a :exc:`GPIODeviceError` 

551 will be raised. 

552 

553 :type pull_up: bool or None 

554 :param pull_up: 

555 See description under :class:`InputDevice` for more information. 

556 

557 :type active_state: bool or None 

558 :param active_state: 

559 See description under :class:`InputDevice` for more information. 

560 

561 :param int queue_len: 

562 The length of the queue used to store values read from the sensor. This 

563 defaults to 1 which effectively disables the queue. If your motion 

564 sensor is particularly "twitchy" you may wish to increase this value. 

565 

566 :param float sample_rate: 

567 The number of values to read from the device (and append to the 

568 internal queue) per second. Defaults to 10. 

569 

570 :param float threshold: 

571 Defaults to 0.5. When the average of all values in the internal queue 

572 rises above this value, the sensor will be considered "active" by the 

573 :attr:`~SmoothedInputDevice.is_active` property, and all appropriate 

574 events will be fired. 

575 

576 :param bool partial: 

577 When :data:`False` (the default), the object will not return a value 

578 for :attr:`~SmoothedInputDevice.is_active` until the internal queue has 

579 filled with values. Only set this to :data:`True` if you require 

580 values immediately after object construction. 

581 

582 :type pin_factory: Factory or None 

583 :param pin_factory: 

584 See :doc:`api_pins` for more information (this is an advanced feature 

585 which most users can ignore). 

586 """ 

587 def __init__( 

588 self, pin=None, pull_up=False, active_state=None, queue_len=1, 

589 sample_rate=10, threshold=0.5, partial=False, pin_factory=None): 

590 super(MotionSensor, self).__init__( 

591 pin, pull_up=pull_up, active_state=active_state, 

592 threshold=threshold, queue_len=queue_len, sample_wait=1 / 

593 sample_rate, partial=partial, pin_factory=pin_factory, average=mean) 

594 self._queue.start() 

595 

596 @property 

597 def value(self): 

598 """ 

599 With the default *queue_len* of 1, this is effectively boolean where 0 

600 means no motion detected and 1 means motion detected. If you specify 

601 a *queue_len* greater than 1, this will be an averaged value where 

602 values closer to 1 imply motion detection. 

603 """ 

604 return super(MotionSensor, self).value 

605 

606MotionSensor.motion_detected = MotionSensor.is_active 

607MotionSensor.when_motion = MotionSensor.when_activated 

608MotionSensor.when_no_motion = MotionSensor.when_deactivated 

609MotionSensor.wait_for_motion = MotionSensor.wait_for_active 

610MotionSensor.wait_for_no_motion = MotionSensor.wait_for_inactive 

611 

612 

613class LightSensor(SmoothedInputDevice): 

614 """ 

615 Extends :class:`SmoothedInputDevice` and represents a light dependent 

616 resistor (LDR). 

617 

618 Connect one leg of the LDR to the 3V3 pin; connect one leg of a 1µF 

619 capacitor to a ground pin; connect the other leg of the LDR and the other 

620 leg of the capacitor to the same GPIO pin. This class repeatedly discharges 

621 the capacitor, then times the duration it takes to charge (which will vary 

622 according to the light falling on the LDR). 

623 

624 The following code will print a line of text when light is detected:: 

625 

626 from gpiozero import LightSensor 

627 

628 ldr = LightSensor(18) 

629 ldr.wait_for_light() 

630 print("Light detected!") 

631 

632 :type pin: int or str 

633 :param pin: 

634 The GPIO pin which the sensor is attached to. See :ref:`pin-numbering` 

635 for valid pin numbers. If this is :data:`None` a :exc:`GPIODeviceError` 

636 will be raised. 

637 

638 :param int queue_len: 

639 The length of the queue used to store values read from the circuit. 

640 This defaults to 5. 

641 

642 :param float charge_time_limit: 

643 If the capacitor in the circuit takes longer than this length of time 

644 to charge, it is assumed to be dark. The default (0.01 seconds) is 

645 appropriate for a 1µF capacitor coupled with the LDR from the 

646 `CamJam #2 EduKit`_. You may need to adjust this value for different 

647 valued capacitors or LDRs. 

648 

649 :param float threshold: 

650 Defaults to 0.1. When the average of all values in the internal queue 

651 rises above this value, the area will be considered "light", and all 

652 appropriate events will be fired. 

653 

654 :param bool partial: 

655 When :data:`False` (the default), the object will not return a value 

656 for :attr:`~SmoothedInputDevice.is_active` until the internal queue has 

657 filled with values. Only set this to :data:`True` if you require 

658 values immediately after object construction. 

659 

660 :type pin_factory: Factory or None 

661 :param pin_factory: 

662 See :doc:`api_pins` for more information (this is an advanced feature 

663 which most users can ignore). 

664 

665 .. _CamJam #2 EduKit: http://camjam.me/?page_id=623 

666 """ 

667 def __init__( 

668 self, pin=None, queue_len=5, charge_time_limit=0.01, 

669 threshold=0.1, partial=False, pin_factory=None): 

670 super(LightSensor, self).__init__( 

671 pin, pull_up=False, threshold=threshold, queue_len=queue_len, 

672 sample_wait=0.0, partial=partial, pin_factory=pin_factory) 

673 try: 

674 self._charge_time_limit = charge_time_limit 

675 self._charge_time = None 

676 self._charged = Event() 

677 self.pin.edges = 'rising' 

678 self.pin.bounce = None 

679 self.pin.when_changed = self._cap_charged 

680 self._queue.start() 

681 except: 

682 self.close() 

683 raise 

684 

685 @property 

686 def charge_time_limit(self): 

687 return self._charge_time_limit 

688 

689 def _cap_charged(self, ticks, state): 

690 self._charge_time = ticks 

691 self._charged.set() 

692 

693 def _read(self): 

694 # Drain charge from the capacitor 

695 self.pin.function = 'output' 

696 self.pin.state = False 

697 sleep(0.1) 

698 self._charge_time = None 

699 self._charged.clear() 

700 # Time the charging of the capacitor 

701 start = self.pin_factory.ticks() 

702 self.pin.function = 'input' 

703 self._charged.wait(self.charge_time_limit) 

704 if self._charge_time is None: 

705 return 0.0 

706 else: 

707 return 1.0 - min(1.0, 

708 (self.pin_factory.ticks_diff(self._charge_time, start) / 

709 self.charge_time_limit)) 

710 

711 @property 

712 def value(self): 

713 """ 

714 Returns a value between 0 (dark) and 1 (light). 

715 """ 

716 return super(LightSensor, self).value 

717 

718LightSensor.light_detected = LightSensor.is_active 

719LightSensor.when_light = LightSensor.when_activated 

720LightSensor.when_dark = LightSensor.when_deactivated 

721LightSensor.wait_for_light = LightSensor.wait_for_active 

722LightSensor.wait_for_dark = LightSensor.wait_for_inactive 

723 

724 

725class DistanceSensor(SmoothedInputDevice): 

726 """ 

727 Extends :class:`SmoothedInputDevice` and represents an HC-SR04 ultrasonic 

728 distance sensor, as found in the `CamJam #3 EduKit`_. 

729 

730 The distance sensor requires two GPIO pins: one for the *trigger* (marked 

731 TRIG on the sensor) and another for the *echo* (marked ECHO on the sensor). 

732 However, a voltage divider is required to ensure the 5V from the ECHO pin 

733 doesn't damage the Pi. Wire your sensor according to the following 

734 instructions: 

735 

736 1. Connect the GND pin of the sensor to a ground pin on the Pi. 

737 

738 2. Connect the TRIG pin of the sensor a GPIO pin. 

739 

740 3. Connect one end of a 330Ω resistor to the ECHO pin of the sensor. 

741 

742 4. Connect one end of a 470Ω resistor to the GND pin of the sensor. 

743 

744 5. Connect the free ends of both resistors to another GPIO pin. This forms 

745 the required `voltage divider`_. 

746 

747 6. Finally, connect the VCC pin of the sensor to a 5V pin on the Pi. 

748 

749 Alternatively, the 3V3 tolerant HC-SR04P sensor (which does not require a 

750 voltage divider) will work with this class. 

751 

752 

753 .. note:: 

754 

755 If you do not have the precise values of resistor specified above, 

756 don't worry! What matters is the *ratio* of the resistors to each 

757 other. 

758 

759 You also don't need to be absolutely precise; the `voltage divider`_ 

760 given above will actually output ~3V (rather than 3.3V). A simple 2:3 

761 ratio will give 3.333V which implies you can take three resistors of 

762 equal value, use one of them instead of the 330Ω resistor, and two of 

763 them in series instead of the 470Ω resistor. 

764 

765 .. _voltage divider: https://en.wikipedia.org/wiki/Voltage_divider 

766 

767 The following code will periodically report the distance measured by the 

768 sensor in cm assuming the TRIG pin is connected to GPIO17, and the ECHO 

769 pin to GPIO18:: 

770 

771 from gpiozero import DistanceSensor 

772 from time import sleep 

773 

774 sensor = DistanceSensor(echo=18, trigger=17) 

775 while True: 

776 print('Distance: ', sensor.distance * 100) 

777 sleep(1) 

778 

779 .. note:: 

780 

781 For improved accuracy, use the pigpio pin driver rather than the default 

782 RPi.GPIO driver (pigpio uses DMA sampling for much more precise edge 

783 timing). This is particularly relevant if you're using Pi 1 or Pi Zero. 

784 See :ref:`changing-pin-factory` for further information. 

785 

786 :type echo: int or str 

787 :param echo: 

788 The GPIO pin which the ECHO pin is connected to. See 

789 :ref:`pin-numbering` for valid pin numbers. If this is :data:`None` a 

790 :exc:`GPIODeviceError` will be raised. 

791 

792 :type trigger: int or str 

793 :param trigger: 

794 The GPIO pin which the TRIG pin is connected to. See 

795 :ref:`pin-numbering` for valid pin numbers. If this is :data:`None` a 

796 :exc:`GPIODeviceError` will be raised. 

797 

798 :param int queue_len: 

799 The length of the queue used to store values read from the sensor. 

800 This defaults to 9. 

801 

802 :param float max_distance: 

803 The :attr:`value` attribute reports a normalized value between 0 (too 

804 close to measure) and 1 (maximum distance). This parameter specifies 

805 the maximum distance expected in meters. This defaults to 1. 

806 

807 :param float threshold_distance: 

808 Defaults to 0.3. This is the distance (in meters) that will trigger the 

809 ``in_range`` and ``out_of_range`` events when crossed. 

810 

811 :param bool partial: 

812 When :data:`False` (the default), the object will not return a value 

813 for :attr:`~SmoothedInputDevice.is_active` until the internal queue has 

814 filled with values. Only set this to :data:`True` if you require 

815 values immediately after object construction. 

816 

817 :type pin_factory: Factory or None 

818 :param pin_factory: 

819 See :doc:`api_pins` for more information (this is an advanced feature 

820 which most users can ignore). 

821 

822 .. _CamJam #3 EduKit: http://camjam.me/?page_id=1035 

823 """ 

824 ECHO_LOCK = Lock() 

825 

826 def __init__( 

827 self, echo=None, trigger=None, queue_len=9, max_distance=1, 

828 threshold_distance=0.3, partial=False, pin_factory=None): 

829 self._trigger = None 

830 super(DistanceSensor, self).__init__( 

831 echo, pull_up=False, queue_len=queue_len, sample_wait=0.06, 

832 partial=partial, ignore=frozenset({None}), pin_factory=pin_factory 

833 ) 

834 try: 

835 if max_distance <= 0: 

836 raise ValueError('invalid maximum distance (must be positive)') 

837 self._max_distance = max_distance 

838 self.threshold = threshold_distance / max_distance 

839 self.speed_of_sound = 343.26 # m/s 

840 self._trigger = GPIODevice(trigger, pin_factory=pin_factory) 

841 self._echo = Event() 

842 self._echo_rise = None 

843 self._echo_fall = None 

844 self._trigger.pin.function = 'output' 

845 self._trigger.pin.state = False 

846 self.pin.edges = 'both' 

847 self.pin.bounce = None 

848 self.pin.when_changed = self._echo_changed 

849 self._queue.start() 

850 except: 

851 self.close() 

852 raise 

853 

854 if PiGPIOFactory is None or not isinstance(self.pin_factory, PiGPIOFactory): 

855 warnings.warn(PWMSoftwareFallback( 

856 'For more accurate readings, use the pigpio pin factory.' 

857 'See https://gpiozero.readthedocs.io/en/stable/api_input.html#distancesensor-hc-sr04 for more info' 

858 )) 

859 

860 def close(self): 

861 try: 

862 self._trigger.close() 

863 except AttributeError: 

864 pass 

865 self._trigger = None 

866 super(DistanceSensor, self).close() 

867 

868 @property 

869 def max_distance(self): 

870 """ 

871 The maximum distance that the sensor will measure in meters. This value 

872 is specified in the constructor and is used to provide the scaling for 

873 the :attr:`~SmoothedInputDevice.value` attribute. When :attr:`distance` 

874 is equal to :attr:`max_distance`, :attr:`~SmoothedInputDevice.value` 

875 will be 1. 

876 """ 

877 return self._max_distance 

878 

879 @max_distance.setter 

880 def max_distance(self, value): 

881 if value <= 0: 

882 raise ValueError('invalid maximum distance (must be positive)') 

883 t = self.threshold_distance 

884 self._max_distance = value 

885 self.threshold_distance = t 

886 

887 @property 

888 def threshold_distance(self): 

889 """ 

890 The distance, measured in meters, that will trigger the 

891 :attr:`when_in_range` and :attr:`when_out_of_range` events when 

892 crossed. This is simply a meter-scaled variant of the usual 

893 :attr:`~SmoothedInputDevice.threshold` attribute. 

894 """ 

895 return self.threshold * self.max_distance 

896 

897 @threshold_distance.setter 

898 def threshold_distance(self, value): 

899 self.threshold = value / self.max_distance 

900 

901 @property 

902 def distance(self): 

903 """ 

904 Returns the current distance measured by the sensor in meters. Note 

905 that this property will have a value between 0 and 

906 :attr:`max_distance`. 

907 """ 

908 return self.value * self._max_distance 

909 

910 @property 

911 def value(self): 

912 """ 

913 Returns a value between 0, indicating the reflector is either touching 

914 the sensor or is sufficiently near that the sensor can't tell the 

915 difference, and 1, indicating the reflector is at or beyond the 

916 specified *max_distance*. 

917 """ 

918 return super(DistanceSensor, self).value 

919 

920 @property 

921 def trigger(self): 

922 """ 

923 Returns the :class:`Pin` that the sensor's trigger is connected to. 

924 """ 

925 return self._trigger.pin 

926 

927 @property 

928 def echo(self): 

929 """ 

930 Returns the :class:`Pin` that the sensor's echo is connected to. This 

931 is simply an alias for the usual :attr:`~GPIODevice.pin` attribute. 

932 """ 

933 return self.pin 

934 

935 def _echo_changed(self, ticks, level): 

936 if level: 

937 self._echo_rise = ticks 

938 else: 

939 self._echo_fall = ticks 

940 self._echo.set() 

941 

942 def _read(self): 

943 # Wait up to 50ms for the echo pin to fall to low (the maximum echo 

944 # pulse is 35ms so this gives some leeway); if it doesn't something is 

945 # horribly wrong (most likely at the hardware level) 

946 if self.pin.state: 

947 if not self._echo.wait(0.05): 

948 warnings.warn(DistanceSensorNoEcho('echo pin set high')) 

949 return None 

950 self._echo.clear() 

951 self._echo_fall = None 

952 self._echo_rise = None 

953 # Obtain the class-level ECHO_LOCK to ensure multiple distance sensors 

954 # don't listen for each other's "pings" 

955 with DistanceSensor.ECHO_LOCK: 

956 # Fire the trigger 

957 self._trigger.pin.state = True 

958 sleep(0.00001) 

959 self._trigger.pin.state = False 

960 # Wait up to 100ms for the echo pin to rise and fall (35ms is the 

961 # maximum pulse time, but the pre-rise time is unspecified in the 

962 # "datasheet"; 100ms seems sufficiently long to conclude something 

963 # has failed) 

964 if self._echo.wait(0.1): 

965 if self._echo_fall is not None and self._echo_rise is not None: 

966 distance = ( 

967 self.pin_factory.ticks_diff( 

968 self._echo_fall, self._echo_rise) * 

969 self.speed_of_sound / 2.0) 

970 return min(1.0, distance / self._max_distance) 

971 else: 

972 # If we only saw the falling edge it means we missed 

973 # the echo because it was too fast 

974 return None 

975 else: 

976 # The echo pin never rose or fell; something's gone horribly 

977 # wrong 

978 warnings.warn(DistanceSensorNoEcho('no echo received')) 

979 return None 

980 

981 @property 

982 def in_range(self): 

983 return not self.is_active 

984 

985DistanceSensor.when_out_of_range = DistanceSensor.when_activated 

986DistanceSensor.when_in_range = DistanceSensor.when_deactivated 

987DistanceSensor.wait_for_out_of_range = DistanceSensor.wait_for_active 

988DistanceSensor.wait_for_in_range = DistanceSensor.wait_for_inactive 

989 

990 

991class RotaryEncoder(EventsMixin, CompositeDevice): 

992 """ 

993 Represents a simple two-pin incremental `rotary encoder`_ device. 

994 

995 These devices typically have three pins labelled "A", "B", and "C". Connect 

996 A and B directly to two GPIO pins, and C ("common") to one of the ground 

997 pins on your Pi. Then simply specify the A and B pins as the arguments when 

998 constructing this classs. 

999 

1000 For example, if your encoder's A pin is connected to GPIO 21, and the B 

1001 pin to GPIO 20 (and presumably the C pin to a suitable GND pin), while an 

1002 LED (with a suitable 300Ω resistor) is connected to GPIO 5, the following 

1003 session will result in the brightness of the LED being controlled by 

1004 dialling the rotary encoder back and forth:: 

1005 

1006 >>> from gpiozero import RotaryEncoder 

1007 >>> from gpiozero.tools import scaled_half 

1008 >>> rotor = RotaryEncoder(21, 20) 

1009 >>> led = PWMLED(5) 

1010 >>> led.source = scaled_half(rotor.values) 

1011 

1012 :type a: int or str 

1013 :param a: 

1014 The GPIO pin connected to the "A" output of the rotary encoder. 

1015 

1016 :type b: int or str 

1017 :param b: 

1018 The GPIO pin connected to the "B" output of the rotary encoder. 

1019 

1020 :type bounce_time: float or None 

1021 :param bounce_time: 

1022 If :data:`None` (the default), no software bounce compensation will be 

1023 performed. Otherwise, this is the length of time (in seconds) that the 

1024 component will ignore changes in state after an initial change. 

1025 

1026 :type max_steps: int 

1027 :param max_steps: 

1028 The number of steps clockwise the encoder takes to change the 

1029 :attr:`value` from 0 to 1, or counter-clockwise from 0 to -1. 

1030 If this is 0, then the encoder's :attr:`value` never changes, but you 

1031 can still read :attr:`steps` to determine the integer number of steps 

1032 the encoder has moved clockwise or counter clockwise. 

1033 

1034 :type threshold_steps: tuple of int 

1035 :param threshold_steps: 

1036 A (min, max) tuple of steps between which the device will be considered 

1037 "active", inclusive. In other words, when :attr:`steps` is greater than 

1038 or equal to the *min* value, and less than or equal the *max* value, 

1039 the :attr:`active` property will be :data:`True` and the appropriate 

1040 events (:attr:`when_activated`, :attr:`when_deactivated`) will be 

1041 fired. Defaults to (0, 0). 

1042 

1043 :type wrap: bool 

1044 :param wrap: 

1045 If :data:`True` and *max_steps* is non-zero, when the :attr:`steps` 

1046 reaches positive or negative *max_steps* it wraps around by negation. 

1047 Defaults to :data:`False`. 

1048 

1049 :type pin_factory: Factory or None 

1050 :param pin_factory: 

1051 See :doc:`api_pins` for more information (this is an advanced feature 

1052 which most users can ignore). 

1053 

1054 .. _rotary encoder: https://en.wikipedia.org/wiki/Rotary_encoder 

1055 """ 

1056 # The rotary encoder's two pins move through the following sequence when 

1057 # the encoder is rotated one step clockwise: 

1058 # 

1059 # ────┐ ┌─────┐ ┌──────── 

1060 # _ │ │ │ │ counter ┌───┐ 

1061 # A │ │ │ │ clockwise ┌─── │ 0 │ ───┐ clockwise 

1062 # └─────┘ └─────┘ (CCW) │ └───┘ │ (CW) 

1063 # : : : : │ ┌───┐ ┌───┐ │ 

1064 # ───────┐ : ┌─────┐ : ┌───── ▾ │ 1 │ │ 2 │ ▾ 

1065 # _ : │ : │ : │ : │ └───┘ └───┘ 

1066 # B : │ : │ : │ : │ │ ┌───┐ │ 

1067 # : └─────┘ : └─────┘ └─── │ 3 │ ───┘ 

1068 # : : : : : : : : └───┘ 

1069 # 0 2 3 1 0 2 3 1 0 

1070 # 

1071 # Treating the A pin as a "high" bit, and the B pin as a "low" bit, this 

1072 # means that the pins return the sequence 0, 2, 3, 1 for each step that the 

1073 # encoder takes clockwise. Conversely, the pins return the sequence 0, 1, 

1074 # 3, 2 for each step counter-clockwise. 

1075 # 

1076 # We can treat these values as edges to take in a simple state machine, 

1077 # which is represented in the dictionary below: 

1078 

1079 TRANSITIONS = { 

1080 'idle': ['idle', 'ccw1', 'cw1', 'idle'], 

1081 'ccw1': ['idle', 'ccw1', 'ccw3', 'ccw2'], 

1082 'ccw2': ['idle', 'ccw1', 'ccw3', 'ccw2'], 

1083 'ccw3': ['-1', 'idle', 'ccw3', 'ccw2'], 

1084 'cw1': ['idle', 'cw3', 'cw1', 'cw2'], 

1085 'cw2': ['idle', 'cw3', 'cw1', 'cw2'], 

1086 'cw3': ['+1', 'cw3', 'idle', 'cw2'], 

1087 } 

1088 

1089 # The state machine here includes more than just the strictly necessary 

1090 # edges; it also permits "wiggle" between intermediary states so that the 

1091 # overall graph looks like this: 

1092 # 

1093 # ┌──────┐ 

1094 # │ │ 

1095 # ┌─────┤ idle ├────┐ 

1096 # │1 │ │ 2│ 

1097 # │ └──────┘ │ 

1098 # ▾ ▴ ▴ ▾ 

1099 # ┌────────┐ │ │ ┌───────┐ 

1100 # │ │ 0│ │0 │ │ 

1101 # ┌───┤ ccw1 ├──┤ ├──┤ cw1 ├───┐ 

1102 # │2 │ │ │ │ │ │ 1│ 

1103 # │ └─┬──────┘ │ │ └─────┬─┘ │ 

1104 # │ 3│ ▴ │ │ ▴ │3 │ 

1105 # │ ▾ │1 │ │ 2│ ▾ │ 

1106 # │ ┌──────┴─┐ │ │ ┌─┴─────┐ │ 

1107 # │ │ │ 0│ │0 │ │ │ 

1108 # │ │ ccw2 ├──┤ ├──┤ cw2 │ │ 

1109 # │ │ │ │ │ │ │ │ 

1110 # │ └─┬──────┘ │ │ └─────┬─┘ │ 

1111 # │ 2│ ▴ │ │ ▴ │1 │ 

1112 # │ ▾ │3 │ │ 3│ ▾ │ 

1113 # │ ┌──────┴─┐ │ │ ┌─┴─────┐ │ 

1114 # │ │ │ │ │ │ │ │ 

1115 # └──▸│ ccw3 │ │ │ │ cw3 │◂──┘ 

1116 # │ │ │ │ │ │ 

1117 # └───┬────┘ │ │ └───┬───┘ 

1118 # 0│ │ │ │0 

1119 # ▾ │ │ ▾ 

1120 # ┌────────┐ │ │ ┌───────┐ 

1121 # │ │ │ │ │ │ 

1122 # │ -1 ├──┘ └──┤ +1 │ 

1123 # │ │ │ │ 

1124 # └────────┘ └───────┘ 

1125 # 

1126 # Note that, once we start down the clockwise (cw) or counter-clockwise 

1127 # (ccw) path, we don't allow the state to pick the alternate direction 

1128 # without passing through the idle state again. This seems to work well in 

1129 # practice with several encoders, even quite jiggly ones with no debounce 

1130 # hardware or software 

1131 

1132 def __init__( 

1133 self, a, b, bounce_time=None, max_steps=16, threshold_steps=(0, 0), 

1134 wrap=False, pin_factory=None): 

1135 min_thresh, max_thresh = threshold_steps 

1136 if max_thresh < min_thresh: 

1137 raise ValueError('maximum threshold cannot be less than minimum') 

1138 self._steps = 0 

1139 self._max_steps = int(max_steps) 

1140 self._threshold = (int(min_thresh), int(max_thresh)) 

1141 self._wrap = bool(wrap) 

1142 self._state = 'idle' 

1143 self._edge = 0 

1144 self._when_rotated = None 

1145 self._when_rotated_cw = None 

1146 self._when_rotated_ccw = None 

1147 self._rotate_event = Event() 

1148 self._rotate_cw_event = Event() 

1149 self._rotate_ccw_event = Event() 

1150 super(RotaryEncoder, self).__init__( 

1151 a=InputDevice(a, pull_up=True, pin_factory=pin_factory), 

1152 b=InputDevice(b, pull_up=True, pin_factory=pin_factory), 

1153 _order=('a', 'b'), pin_factory=pin_factory) 

1154 self.a.pin.bounce_time = bounce_time 

1155 self.b.pin.bounce_time = bounce_time 

1156 self.a.pin.edges = 'both' 

1157 self.b.pin.edges = 'both' 

1158 self.a.pin.when_changed = self._a_changed 

1159 self.b.pin.when_changed = self._b_changed 

1160 # Call _fire_events once to set initial state of events 

1161 self._fire_events(self.pin_factory.ticks(), self.is_active) 

1162 

1163 def __repr__(self): 

1164 try: 

1165 self._check_open() 

1166 return "<gpiozero.%s object on pins %r and %r>" % ( 

1167 self.__class__.__name__, self.a.pin, self.b.pin) 

1168 except DeviceClosed: 

1169 return super(RotaryEncoder, self).__repr__() 

1170 

1171 def _a_changed(self, ticks, state): 

1172 edge = (self.a._state_to_value(state) << 1) | (self._edge & 0x1) 

1173 self._change_state(ticks, edge) 

1174 

1175 def _b_changed(self, ticks, state): 

1176 edge = (self._edge & 0x2) | self.b._state_to_value(state) 

1177 self._change_state(ticks, edge) 

1178 

1179 def _change_state(self, ticks, edge): 

1180 self._edge = edge 

1181 new_state = RotaryEncoder.TRANSITIONS[self._state][edge] 

1182 if new_state == '+1': 

1183 self._steps = ( 

1184 self._steps + 1 

1185 if not self._max_steps or self._steps < self._max_steps else 

1186 -self._max_steps if self._wrap else self._max_steps 

1187 ) 

1188 self._rotate_cw_event.set() 

1189 self._fire_rotated_cw() 

1190 self._rotate_cw_event.clear() 

1191 elif new_state == '-1': 

1192 self._steps = ( 

1193 self._steps - 1 

1194 if not self._max_steps or self._steps > -self._max_steps else 

1195 self._max_steps if self._wrap else -self._max_steps 

1196 ) 

1197 self._rotate_ccw_event.set() 

1198 self._fire_rotated_ccw() 

1199 self._rotate_ccw_event.clear() 

1200 else: 

1201 self._state = new_state 

1202 return 

1203 self._rotate_event.set() 

1204 self._fire_rotated() 

1205 self._rotate_event.clear() 

1206 self._fire_events(ticks, self.is_active) 

1207 self._state = 'idle' 

1208 

1209 def wait_for_rotate(self, timeout=None): 

1210 """ 

1211 Pause the script until the encoder is rotated at least one step in 

1212 either direction, or the timeout is reached. 

1213 

1214 :type timeout: float or None 

1215 :param timeout: 

1216 Number of seconds to wait before proceeding. If this is 

1217 :data:`None` (the default), then wait indefinitely until the 

1218 encoder is rotated. 

1219 """ 

1220 return self._rotate_event.wait(timeout) 

1221 

1222 def wait_for_rotate_clockwise(self, timeout=None): 

1223 """ 

1224 Pause the script until the encoder is rotated at least one step 

1225 clockwise, or the timeout is reached. 

1226 

1227 :type timeout: float or None 

1228 :param timeout: 

1229 Number of seconds to wait before proceeding. If this is 

1230 :data:`None` (the default), then wait indefinitely until the 

1231 encoder is rotated clockwise. 

1232 """ 

1233 return self._rotate_cw_event.wait(timeout) 

1234 

1235 def wait_for_rotate_counter_clockwise(self, timeout=None): 

1236 """ 

1237 Pause the script until the encoder is rotated at least one step 

1238 counter-clockwise, or the timeout is reached. 

1239 

1240 :type timeout: float or None 

1241 :param timeout: 

1242 Number of seconds to wait before proceeding. If this is 

1243 :data:`None` (the default), then wait indefinitely until the 

1244 encoder is rotated counter-clockwise. 

1245 """ 

1246 return self._rotate_ccw_event.wait(timeout) 

1247 

1248 when_rotated = event( 

1249 """ 

1250 The function to be run when the encoder is rotated in either direction. 

1251 

1252 This can be set to a function which accepts no (mandatory) parameters, 

1253 or a Python function which accepts a single mandatory parameter (with 

1254 as many optional parameters as you like). If the function accepts a 

1255 single mandatory parameter, the device that activated will be passed 

1256 as that parameter. 

1257 

1258 Set this property to :data:`None` (the default) to disable the event. 

1259 """) 

1260 

1261 when_rotated_clockwise = event( 

1262 """ 

1263 The function to be run when the encoder is rotated clockwise. 

1264 

1265 This can be set to a function which accepts no (mandatory) parameters, 

1266 or a Python function which accepts a single mandatory parameter (with 

1267 as many optional parameters as you like). If the function accepts a 

1268 single mandatory parameter, the device that activated will be passed 

1269 as that parameter. 

1270 

1271 Set this property to :data:`None` (the default) to disable the event. 

1272 """) 

1273 

1274 when_rotated_counter_clockwise = event( 

1275 """ 

1276 The function to be run when the encoder is rotated counter-clockwise. 

1277 

1278 This can be set to a function which accepts no (mandatory) parameters, 

1279 or a Python function which accepts a single mandatory parameter (with 

1280 as many optional parameters as you like). If the function accepts a 

1281 single mandatory parameter, the device that activated will be passed 

1282 as that parameter. 

1283 

1284 Set this property to :data:`None` (the default) to disable the event. 

1285 """) 

1286 

1287 @property 

1288 def steps(self): 

1289 """ 

1290 The "steps" value of the encoder starts at 0. It increments by one for 

1291 every step the encoder is rotated clockwise, and decrements by one for 

1292 every step it is rotated counter-clockwise. The steps value is 

1293 limited by :attr:`max_steps`. It will not advance beyond positive or 

1294 negative :attr:`max_steps`, unless :attr:`wrap` is :data:`True` in 

1295 which case it will roll around by negation. If :attr:`max_steps` is 

1296 zero then steps are not limited at all, and will increase infinitely 

1297 in either direction, but :attr:`value` will return a constant zero. 

1298 

1299 Note that, in contrast to most other input devices, because the rotary 

1300 encoder has no absolute position the :attr:`steps` attribute (and 

1301 :attr:`value` by corollary) is writable. 

1302 """ 

1303 return self._steps 

1304 

1305 def _fire_rotated(self): 

1306 if self.when_rotated: 

1307 self.when_rotated() 

1308 

1309 def _fire_rotated_cw(self): 

1310 if self.when_rotated_clockwise: 

1311 self.when_rotated_clockwise() 

1312 

1313 def _fire_rotated_ccw(self): 

1314 if self.when_rotated_counter_clockwise: 

1315 self.when_rotated_counter_clockwise() 

1316 

1317 @steps.setter 

1318 def steps(self, value): 

1319 value = int(value) 

1320 if self._max_steps: 

1321 value = max(-self._max_steps, min(self._max_steps, value)) 

1322 self._steps = value 

1323 

1324 @property 

1325 def value(self): 

1326 """ 

1327 Represents the value of the rotary encoder as a value between -1 and 1. 

1328 The value is calculated by dividing the value of :attr:`steps` into the 

1329 range from negative :attr:`max_steps` to positive :attr:`max_steps`. 

1330 

1331 Note that, in contrast to most other input devices, because the rotary 

1332 encoder has no absolute position the :attr:`value` attribute is 

1333 writable. 

1334 """ 

1335 try: 

1336 return self._steps / self._max_steps 

1337 except ZeroDivisionError: 

1338 return 0 

1339 

1340 @value.setter 

1341 def value(self, value): 

1342 self._steps = int(max(-1, min(1, float(value))) * self._max_steps) 

1343 

1344 @property 

1345 def is_active(self): 

1346 return self._threshold[0] <= self._steps <= self._threshold[1] 

1347 

1348 @property 

1349 def max_steps(self): 

1350 """ 

1351 The number of discrete steps the rotary encoder takes to move 

1352 :attr:`value` from 0 to 1 clockwise, or 0 to -1 counter-clockwise. In 

1353 another sense, this is also the total number of discrete states this 

1354 input can represent. 

1355 """ 

1356 return self._max_steps 

1357 

1358 @property 

1359 def threshold_steps(self): 

1360 """ 

1361 The mininum and maximum number of steps between which :attr:`is_active` 

1362 will return :data:`True`. Defaults to (0, 0). 

1363 """ 

1364 return self._threshold 

1365 

1366 @property 

1367 def wrap(self): 

1368 """ 

1369 If :data:`True`, when :attr:`value` reaches its limit (-1 or 1), it 

1370 "wraps around" to the opposite limit. When :data:`False`, the value 

1371 (and the corresponding :attr:`steps` attribute) simply don't advance 

1372 beyond their limits. 

1373 """ 

1374 return self._wrap