Coverage for /usr/lib/python3/dist-packages/gpiozero/boards.py: 18%

650 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2024-02-10 12:38 +0000

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 Ryan Walmsley <ryanteck@gmail.com> 

8# Copyright (c) 2020 Jack Wearden <jack@jackwearden.co.uk> 

9# Copyright (c) 2019 tuftii <3215045+tuftii@users.noreply.github.com> 

10# Copyright (c) 2019 ForToffee <ForToffee@users.noreply.github.com> 

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

12# Copyright (c) 2018 SteveAmor <steveamor@users.noreply.github.com> 

13# Copyright (c) 2018 Rick Ansell <rick@nbinvincible.org.uk> 

14# Copyright (c) 2018 Claire Pollard <claire.r.pollard@gmail.com> 

15# Copyright (c) 2016 Ian Harcombe <ian.harcombe@gmail.com> 

16# Copyright (c) 2016 Andrew Scheller <lurch@durge.org> 

17# 

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

19 

20from __future__ import ( 

21 unicode_literals, 

22 print_function, 

23 absolute_import, 

24 division, 

25 ) 

26try: 

27 from itertools import izip as zip 

28except ImportError: 

29 pass 

30 

31from time import sleep 

32from itertools import repeat, cycle, chain, tee 

33from threading import Lock 

34from collections import OrderedDict, Counter, namedtuple 

35try: 

36 from collections.abc import MutableMapping 

37except ImportError: 

38 from collections import MutableMapping 

39 

40from .exc import ( 

41 DeviceClosed, 

42 PinInvalidPin, 

43 GPIOPinMissing, 

44 EnergenieSocketMissing, 

45 EnergenieBadSocket, 

46 OutputDeviceBadValue, 

47 CompositeDeviceBadDevice, 

48 BadWaitTime, 

49 ) 

50from .input_devices import Button 

51from .output_devices import ( 

52 OutputDevice, 

53 LED, 

54 PWMLED, 

55 RGBLED, 

56 Buzzer, 

57 Motor, 

58 PhaseEnableMotor, 

59 TonalBuzzer, 

60 ) 

61from .threads import GPIOThread 

62from .devices import Device, CompositeDevice 

63from .mixins import SharedMixin, SourceMixin, HoldMixin 

64from .fonts import load_font_7seg, load_font_14seg 

65 

66 

67def pairwise(it): 

68 a, b = tee(it) 

69 next(b, None) 

70 return zip(a, b) 

71 

72 

73class CompositeOutputDevice(SourceMixin, CompositeDevice): 

74 """ 

75 Extends :class:`CompositeDevice` with :meth:`on`, :meth:`off`, and 

76 :meth:`toggle` methods for controlling subordinate output devices. Also 

77 extends :attr:`value` to be writeable. 

78 

79 :param Device \\*args: 

80 The un-named devices that belong to the composite device. The 

81 :attr:`~Device.value` attributes of these devices will be represented 

82 within the composite device's tuple :attr:`value` in the order 

83 specified here. 

84 

85 :type _order: list or None 

86 :param _order: 

87 If specified, this is the order of named items specified by keyword 

88 arguments (to ensure that the :attr:`value` tuple is constructed with a 

89 specific order). All keyword arguments *must* be included in the 

90 collection. If omitted, an alphabetically sorted order will be selected 

91 for keyword arguments. 

92 

93 :type pin_factory: Factory or None 

94 :param pin_factory: 

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

96 which most users can ignore). 

97 

98 :param Device \\*\\*kwargs: 

99 The named devices that belong to the composite device. These devices 

100 will be accessible as named attributes on the resulting device, and 

101 their :attr:`value` attributes will be accessible as named elements of 

102 the composite device's tuple :attr:`value`. 

103 """ 

104 

105 def on(self): 

106 """ 

107 Turn all the output devices on. 

108 """ 

109 for device in self: 

110 if isinstance(device, (OutputDevice, CompositeOutputDevice)): 

111 device.on() 

112 

113 def off(self): 

114 """ 

115 Turn all the output devices off. 

116 """ 

117 for device in self: 

118 if isinstance(device, (OutputDevice, CompositeOutputDevice)): 

119 device.off() 

120 

121 def toggle(self): 

122 """ 

123 Toggle all the output devices. For each device, if it's on, turn it 

124 off; if it's off, turn it on. 

125 """ 

126 for device in self: 

127 if isinstance(device, (OutputDevice, CompositeOutputDevice)): 

128 device.toggle() 

129 

130 @property 

131 def value(self): 

132 """ 

133 A tuple containing a value for each subordinate device. This property 

134 can also be set to update the state of all subordinate output devices. 

135 """ 

136 return super(CompositeOutputDevice, self).value 

137 

138 @value.setter 

139 def value(self, value): 

140 for device, v in zip(self, value): 

141 if isinstance(device, (OutputDevice, CompositeOutputDevice)): 

142 device.value = v 

143 # Simply ignore values for non-output devices 

144 

145 

146class ButtonBoard(HoldMixin, CompositeDevice): 

147 """ 

148 Extends :class:`CompositeDevice` and represents a generic button board or 

149 collection of buttons. The :attr:`value` of the button board is a tuple 

150 of all the buttons states. This can be used to control all the LEDs in a 

151 :class:`LEDBoard` with a :class:`ButtonBoard`:: 

152 

153 from gpiozero import LEDBoard, ButtonBoard 

154 from signal import pause 

155 

156 leds = LEDBoard(2, 3, 4, 5) 

157 btns = ButtonBoard(6, 7, 8, 9) 

158 leds.source = btns 

159 

160 pause() 

161 

162 Alternatively you could represent the number of pressed buttons with an 

163 :class:`LEDBarGraph`:: 

164 

165 from gpiozero import LEDBarGraph, ButtonBoard 

166 from statistics import mean 

167 from signal import pause 

168 

169 graph = LEDBarGraph(2, 3, 4, 5) 

170 bb = ButtonBoard(6, 7, 8, 9) 

171 graph.source = (mean(values) for values in bb.values) 

172 

173 pause() 

174 

175 :type pins: int or str 

176 :param \\*pins: 

177 Specify the GPIO pins that the buttons of the board are attached to. 

178 See :ref:`pin-numbering` for valid pin numbers. You can designate as 

179 many pins as necessary. 

180 

181 :type pull_up: bool or None 

182 :param pull_up: 

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

184 default. In this case, connect the other side of the buttons to 

185 ground. If :data:`False`, the GPIO pins will be pulled low by default. 

186 In this case, connect the other side of the buttons to 3V3. If 

187 :data:`None`, the pin will be floating, so it must be externally pulled 

188 up or down and the ``active_state`` parameter must be set accordingly. 

189 

190 :type active_state: bool or None 

191 :param active_state: 

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

193 

194 :param float bounce_time: 

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

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

197 buttons will ignore changes in state after an initial change. 

198 

199 :param float hold_time: 

200 The length of time (in seconds) to wait after any button is pushed, 

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

202 

203 :param bool hold_repeat: 

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

205 executed as long as any buttons remain held, every *hold_time* seconds. 

206 If :data:`False` (the default) the :attr:`when_held` handler will be 

207 only be executed once per hold. 

208 

209 :type pin_factory: Factory or None 

210 :param pin_factory: 

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

212 which most users can ignore). 

213 

214 :type named_pins: int or str 

215 :param \\*\\*named_pins: 

216 Specify GPIO pins that buttons of the board are attached to, 

217 associating each button with a property name. You can designate as 

218 many pins as necessary and use any names, provided they're not already 

219 in use by something else. 

220 """ 

221 def __init__(self, *args, **kwargs): 

222 pull_up = kwargs.pop('pull_up', True) 

223 active_state = kwargs.pop('active_state', None) 

224 bounce_time = kwargs.pop('bounce_time', None) 

225 hold_time = kwargs.pop('hold_time', 1) 

226 hold_repeat = kwargs.pop('hold_repeat', False) 

227 pin_factory = kwargs.pop('pin_factory', None) 

228 order = kwargs.pop('_order', None) 

229 super(ButtonBoard, self).__init__( 

230 *( 

231 Button(pin, pull_up=pull_up, active_state=active_state, 

232 bounce_time=bounce_time, hold_time=hold_time, 

233 hold_repeat=hold_repeat) 

234 for pin in args 

235 ), 

236 _order=order, 

237 pin_factory=pin_factory, 

238 **{ 

239 name: Button(pin, pull_up=pull_up, active_state=active_state, 

240 bounce_time=bounce_time, hold_time=hold_time, 

241 hold_repeat=hold_repeat) 

242 for name, pin in kwargs.items() 

243 } 

244 ) 

245 if len(self) == 0: 

246 raise GPIOPinMissing('No pins given') 

247 def get_new_handler(device): 

248 def fire_both_events(ticks, state): 

249 device._fire_events(ticks, device._state_to_value(state)) 

250 self._fire_events(ticks, self.is_active) 

251 return fire_both_events 

252 # _handlers only exists to ensure that we keep a reference to the 

253 # generated fire_both_events handler for each Button (remember that 

254 # pin.when_changed only keeps a weak reference to handlers) 

255 self._handlers = tuple(get_new_handler(device) for device in self) 

256 for button, handler in zip(self, self._handlers): 

257 button.pin.when_changed = handler 

258 self._when_changed = None 

259 self._last_value = None 

260 # Call _fire_events once to set initial state of events 

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

262 self.hold_time = hold_time 

263 self.hold_repeat = hold_repeat 

264 

265 @property 

266 def pull_up(self): 

267 """ 

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

269 "high" by default. 

270 """ 

271 return self[0].pull_up 

272 

273 @property 

274 def when_changed(self): 

275 return self._when_changed 

276 

277 @when_changed.setter 

278 def when_changed(self, value): 

279 self._when_changed = self._wrap_callback(value) 

280 

281 def _fire_changed(self): 

282 if self.when_changed: 

283 self.when_changed() 

284 

285 def _fire_events(self, ticks, new_value): 

286 super(ButtonBoard, self)._fire_events(ticks, new_value) 

287 old_value, self._last_value = self._last_value, new_value 

288 if old_value is None: 

289 # Initial "indeterminate" value; don't do anything 

290 pass 

291 elif old_value != new_value: 

292 self._fire_changed() 

293 

294ButtonBoard.is_pressed = ButtonBoard.is_active 

295ButtonBoard.pressed_time = ButtonBoard.active_time 

296ButtonBoard.when_pressed = ButtonBoard.when_activated 

297ButtonBoard.when_released = ButtonBoard.when_deactivated 

298ButtonBoard.wait_for_press = ButtonBoard.wait_for_active 

299ButtonBoard.wait_for_release = ButtonBoard.wait_for_inactive 

300 

301 

302class LEDCollection(CompositeOutputDevice): 

303 """ 

304 Extends :class:`CompositeOutputDevice`. Abstract base class for 

305 :class:`LEDBoard` and :class:`LEDBarGraph`. 

306 """ 

307 def __init__(self, *args, **kwargs): 

308 pwm = kwargs.pop('pwm', False) 

309 active_high = kwargs.pop('active_high', True) 

310 initial_value = kwargs.pop('initial_value', False) 

311 pin_factory = kwargs.pop('pin_factory', None) 

312 order = kwargs.pop('_order', None) 

313 LEDClass = PWMLED if pwm else LED 

314 super(LEDCollection, self).__init__( 

315 *( 

316 pin_or_collection 

317 if isinstance(pin_or_collection, LEDCollection) else 

318 LEDClass( 

319 pin_or_collection, active_high, initial_value, 

320 pin_factory=pin_factory 

321 ) 

322 for pin_or_collection in args 

323 ), 

324 _order=order, 

325 pin_factory=pin_factory, 

326 **{ 

327 name: pin_or_collection 

328 if isinstance(pin_or_collection, LEDCollection) else 

329 LEDClass( 

330 pin_or_collection, active_high, initial_value, 

331 pin_factory=pin_factory 

332 ) 

333 for name, pin_or_collection in kwargs.items() 

334 } 

335 ) 

336 if len(self) == 0: 

337 raise GPIOPinMissing('No pins given') 

338 leds = [] 

339 for item in self: 

340 if isinstance(item, LEDCollection): 

341 for subitem in item.leds: 

342 leds.append(subitem) 

343 else: 

344 leds.append(item) 

345 self._leds = tuple(leds) 

346 

347 @property 

348 def leds(self): 

349 """ 

350 A flat tuple of all LEDs contained in this collection (and all 

351 sub-collections). 

352 """ 

353 return self._leds 

354 

355 @property 

356 def active_high(self): 

357 return self[0].active_high 

358 

359 

360LEDCollection.is_lit = LEDCollection.is_active 

361 

362 

363class LEDBoard(LEDCollection): 

364 """ 

365 Extends :class:`LEDCollection` and represents a generic LED board or 

366 collection of LEDs. 

367 

368 The following example turns on all the LEDs on a board containing 5 LEDs 

369 attached to GPIO pins 2 through 6:: 

370 

371 from gpiozero import LEDBoard 

372 

373 leds = LEDBoard(2, 3, 4, 5, 6) 

374 leds.on() 

375 

376 :type pins: int or str or LEDCollection 

377 :param \\*pins: 

378 Specify the GPIO pins that the LEDs of the board are attached to. See 

379 :ref:`pin-numbering` for valid pin numbers. You can designate as many 

380 pins as necessary. You can also specify :class:`LEDBoard` instances to 

381 create trees of LEDs. 

382 

383 :param bool pwm: 

384 If :data:`True`, construct :class:`PWMLED` instances for each pin. If 

385 :data:`False` (the default), construct regular :class:`LED` instances. 

386 

387 :param bool active_high: 

388 If :data:`True` (the default), the :meth:`on` method will set all the 

389 associated pins to HIGH. If :data:`False`, the :meth:`on` method will 

390 set all pins to LOW (the :meth:`off` method always does the opposite). 

391 

392 :type initial_value: bool or None 

393 :param initial_value: 

394 If :data:`False` (the default), all LEDs will be off initially. If 

395 :data:`None`, each device will be left in whatever state the pin is 

396 found in when configured for output (warning: this can be on). If 

397 :data:`True`, the device will be switched on initially. 

398 

399 :type pin_factory: Factory or None 

400 :param pin_factory: 

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

402 which most users can ignore). 

403 

404 :type named_pins: int or str 

405 :param \\*\\*named_pins: 

406 Specify GPIO pins that LEDs of the board are attached to, associating 

407 each LED with a property name. You can designate as many pins as 

408 necessary and use any names, provided they're not already in use by 

409 something else. You can also specify :class:`LEDBoard` instances to 

410 create trees of LEDs. 

411 """ 

412 def __init__(self, *args, **kwargs): 

413 self._blink_thread = None 

414 self._blink_leds = [] 

415 self._blink_lock = Lock() 

416 super(LEDBoard, self).__init__(*args, **kwargs) 

417 

418 def close(self): 

419 try: 

420 self._stop_blink() 

421 except AttributeError: 

422 pass 

423 super(LEDBoard, self).close() 

424 

425 def on(self, *args): 

426 """ 

427 If no arguments are specified, turn all the LEDs on. If arguments are 

428 specified, they must be the indexes of the LEDs you wish to turn on. 

429 For example:: 

430 

431 from gpiozero import LEDBoard 

432 

433 leds = LEDBoard(2, 3, 4, 5) 

434 leds.on(0) # turn on the first LED (pin 2) 

435 leds.on(-1) # turn on the last LED (pin 5) 

436 leds.on(1, 2) # turn on the middle LEDs (pins 3 and 4) 

437 leds.off() # turn off all LEDs 

438 leds.on() # turn on all LEDs 

439 

440 If :meth:`blink` is currently active, it will be stopped first. 

441 

442 :param int args: 

443 The index(es) of the LED(s) to turn on. If no indexes are specified 

444 turn on all LEDs. 

445 """ 

446 self._stop_blink() 

447 if args: 

448 for index in args: 

449 self[index].on() 

450 else: 

451 super(LEDBoard, self).on() 

452 

453 def off(self, *args): 

454 """ 

455 If no arguments are specified, turn all the LEDs off. If arguments are 

456 specified, they must be the indexes of the LEDs you wish to turn off. 

457 For example:: 

458 

459 from gpiozero import LEDBoard 

460 

461 leds = LEDBoard(2, 3, 4, 5) 

462 leds.on() # turn on all LEDs 

463 leds.off(0) # turn off the first LED (pin 2) 

464 leds.off(-1) # turn off the last LED (pin 5) 

465 leds.off(1, 2) # turn off the middle LEDs (pins 3 and 4) 

466 leds.on() # turn on all LEDs 

467 

468 If :meth:`blink` is currently active, it will be stopped first. 

469 

470 :param int args: 

471 The index(es) of the LED(s) to turn off. If no indexes are 

472 specified turn off all LEDs. 

473 """ 

474 self._stop_blink() 

475 if args: 

476 for index in args: 

477 self[index].off() 

478 else: 

479 super(LEDBoard, self).off() 

480 

481 def toggle(self, *args): 

482 """ 

483 If no arguments are specified, toggle the state of all LEDs. If 

484 arguments are specified, they must be the indexes of the LEDs you wish 

485 to toggle. For example:: 

486 

487 from gpiozero import LEDBoard 

488 

489 leds = LEDBoard(2, 3, 4, 5) 

490 leds.toggle(0) # turn on the first LED (pin 2) 

491 leds.toggle(-1) # turn on the last LED (pin 5) 

492 leds.toggle() # turn the first and last LED off, and the 

493 # middle pair on 

494 

495 If :meth:`blink` is currently active, it will be stopped first. 

496 

497 :param int args: 

498 The index(es) of the LED(s) to toggle. If no indexes are specified 

499 toggle the state of all LEDs. 

500 """ 

501 self._stop_blink() 

502 if args: 

503 for index in args: 

504 self[index].toggle() 

505 else: 

506 super(LEDBoard, self).toggle() 

507 

508 def blink( 

509 self, on_time=1, off_time=1, fade_in_time=0, fade_out_time=0, 

510 n=None, background=True): 

511 """ 

512 Make all the LEDs turn on and off repeatedly. 

513 

514 :param float on_time: 

515 Number of seconds on. Defaults to 1 second. 

516 

517 :param float off_time: 

518 Number of seconds off. Defaults to 1 second. 

519 

520 :param float fade_in_time: 

521 Number of seconds to spend fading in. Defaults to 0. Must be 0 if 

522 ``pwm`` was :data:`False` when the class was constructed 

523 (:exc:`ValueError` will be raised if not). 

524 

525 :param float fade_out_time: 

526 Number of seconds to spend fading out. Defaults to 0. Must be 0 if 

527 ``pwm`` was :data:`False` when the class was constructed 

528 (:exc:`ValueError` will be raised if not). 

529 

530 :type n: int or None 

531 :param n: 

532 Number of times to blink; :data:`None` (the default) means forever. 

533 

534 :param bool background: 

535 If :data:`True`, start a background thread to continue blinking and 

536 return immediately. If :data:`False`, only return when the blink is 

537 finished (warning: the default value of *n* will result in this 

538 method never returning). 

539 """ 

540 for led in self.leds: 

541 if isinstance(led, LED): 

542 if fade_in_time: 

543 raise ValueError('fade_in_time must be 0 with non-PWM LEDs') 

544 if fade_out_time: 

545 raise ValueError('fade_out_time must be 0 with non-PWM LEDs') 

546 self._stop_blink() 

547 self._blink_thread = GPIOThread( 

548 self._blink_device, 

549 (on_time, off_time, fade_in_time, fade_out_time, n)) 

550 self._blink_thread.start() 

551 if not background: 

552 self._blink_thread.join() 

553 self._blink_thread = None 

554 

555 def _stop_blink(self, led=None): 

556 if led is None: 

557 if self._blink_thread: 

558 self._blink_thread.stop() 

559 self._blink_thread = None 

560 else: 

561 with self._blink_lock: 

562 self._blink_leds.remove(led) 

563 

564 def pulse(self, fade_in_time=1, fade_out_time=1, n=None, background=True): 

565 """ 

566 Make all LEDs fade in and out repeatedly. Note that this method will 

567 only work if the *pwm* parameter was :data:`True` at construction time. 

568 

569 :param float fade_in_time: 

570 Number of seconds to spend fading in. Defaults to 1. 

571 

572 :param float fade_out_time: 

573 Number of seconds to spend fading out. Defaults to 1. 

574 

575 :type n: int or None 

576 :param n: 

577 Number of times to blink; :data:`None` (the default) means forever. 

578 

579 :param bool background: 

580 If :data:`True` (the default), start a background thread to 

581 continue blinking and return immediately. If :data:`False`, only 

582 return when the blink is finished (warning: the default value of 

583 *n* will result in this method never returning). 

584 """ 

585 on_time = off_time = 0 

586 self.blink( 

587 on_time, off_time, fade_in_time, fade_out_time, n, background) 

588 

589 def _blink_device( 

590 self, on_time, off_time, fade_in_time, fade_out_time, n, fps=25): 

591 sequence = [] 

592 if fade_in_time > 0: 

593 sequence += [ 

594 (i * (1 / fps) / fade_in_time, 1 / fps) 

595 for i in range(int(fps * fade_in_time)) 

596 ] 

597 sequence.append((1, on_time)) 

598 if fade_out_time > 0: 

599 sequence += [ 

600 (1 - (i * (1 / fps) / fade_out_time), 1 / fps) 

601 for i in range(int(fps * fade_out_time)) 

602 ] 

603 sequence.append((0, off_time)) 

604 if n is None: 

605 sequence = cycle(sequence) 

606 else: 

607 sequence = chain.from_iterable(repeat(sequence, n)) 

608 with self._blink_lock: 

609 self._blink_leds = list(self.leds) 

610 for led in self._blink_leds: 

611 if led._controller not in (None, self): 

612 led._controller._stop_blink(led) 

613 led._controller = self 

614 for value, delay in sequence: 

615 with self._blink_lock: 

616 if not self._blink_leds: 

617 break 

618 for led in self._blink_leds: 

619 led._write(value) 

620 if self._blink_thread.stopping.wait(delay): 

621 break 

622 

623 

624class LEDBarGraph(LEDCollection): 

625 """ 

626 Extends :class:`LEDCollection` to control a line of LEDs representing a 

627 bar graph. Positive values (0 to 1) light the LEDs from first to last. 

628 Negative values (-1 to 0) light the LEDs from last to first. 

629 

630 The following example demonstrates turning on the first two and last two 

631 LEDs in a board containing five LEDs attached to GPIOs 2 through 6:: 

632 

633 from gpiozero import LEDBarGraph 

634 from time import sleep 

635 

636 graph = LEDBarGraph(2, 3, 4, 5, 6) 

637 graph.value = 2/5 # Light the first two LEDs only 

638 sleep(1) 

639 graph.value = -2/5 # Light the last two LEDs only 

640 sleep(1) 

641 graph.off() 

642 

643 As with all other output devices, :attr:`source` and :attr:`values` are 

644 supported:: 

645 

646 from gpiozero import LEDBarGraph, MCP3008 

647 from signal import pause 

648 

649 graph = LEDBarGraph(2, 3, 4, 5, 6, pwm=True) 

650 pot = MCP3008(channel=0) 

651 

652 graph.source = pot 

653 

654 pause() 

655 

656 :type pins: int or str 

657 :param \\*pins: 

658 Specify the GPIO pins that the LEDs of the bar graph are attached to. 

659 See :ref:`pin-numbering` for valid pin numbers. You can designate as 

660 many pins as necessary. 

661 

662 :param bool pwm: 

663 If :data:`True`, construct :class:`PWMLED` instances for each pin. If 

664 :data:`False` (the default), construct regular :class:`LED` instances. 

665 This parameter can only be specified as a keyword parameter. 

666 

667 :param bool active_high: 

668 If :data:`True` (the default), the :meth:`on` method will set all the 

669 associated pins to HIGH. If :data:`False`, the :meth:`on` method will 

670 set all pins to LOW (the :meth:`off` method always does the opposite). 

671 This parameter can only be specified as a keyword parameter. 

672 

673 :param float initial_value: 

674 The initial :attr:`value` of the graph given as a float between -1 and 

675 +1. Defaults to 0.0. This parameter can only be specified as a 

676 keyword parameter. 

677 

678 :type pin_factory: Factory or None 

679 :param pin_factory: 

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

681 which most users can ignore). 

682 """ 

683 def __init__(self, *pins, **kwargs): 

684 # Don't allow graphs to contain collections 

685 for pin in pins: 

686 if isinstance(pin, Device): 

687 raise CompositeDeviceBadDevice( 

688 'Only pins may be specified for LEDBarGraph') 

689 pwm = kwargs.pop('pwm', False) 

690 active_high = kwargs.pop('active_high', True) 

691 initial_value = kwargs.pop('initial_value', 0.0) 

692 pin_factory = kwargs.pop('pin_factory', None) 

693 if kwargs: 

694 raise TypeError( 

695 'unexpected keyword argument: %s' % kwargs.popitem()[0]) 

696 super(LEDBarGraph, self).__init__( 

697 *pins, pwm=pwm, active_high=active_high, pin_factory=pin_factory) 

698 try: 

699 self.value = initial_value 

700 except: 

701 self.close() 

702 raise 

703 

704 @property 

705 def value(self): 

706 """ 

707 The value of the LED bar graph. When no LEDs are lit, the value is 0. 

708 When all LEDs are lit, the value is 1. Values between 0 and 1 

709 light LEDs linearly from first to last. Values between 0 and -1 

710 light LEDs linearly from last to first. 

711 

712 To light a particular number of LEDs, simply divide that number by 

713 the number of LEDs. For example, if your graph contains 3 LEDs, the 

714 following will light the first:: 

715 

716 from gpiozero import LEDBarGraph 

717 

718 graph = LEDBarGraph(12, 16, 19) 

719 graph.value = 1/3 

720 

721 .. note:: 

722 

723 Setting value to -1 will light all LEDs. However, querying it 

724 subsequently will return 1 as both representations are the same in 

725 hardware. The readable range of :attr:`value` is effectively 

726 -1 < value <= 1. 

727 """ 

728 result = sum(led.value for led in self) 

729 if self[0].value < self[-1].value: 

730 result = -result 

731 return result / len(self) 

732 

733 @value.setter 

734 def value(self, value): 

735 if not -1 <= value <= 1: 

736 raise OutputDeviceBadValue( 

737 'LEDBarGraph value must be between -1 and 1') 

738 count = len(self) 

739 leds = self 

740 if value < 0: 

741 leds = reversed(leds) 

742 value = -value 

743 if isinstance(self[0], PWMLED): 

744 calc_value = lambda index: min(1, max(0, count * value - index)) 

745 else: 

746 calc_value = lambda index: value >= ((index + 1) / count) 

747 for index, led in enumerate(leds): 

748 led.value = calc_value(index) 

749 

750 @property 

751 def lit_count(self): 

752 """ 

753 The number of LEDs on the bar graph actually lit up. Note that just 

754 like :attr:`value`, this can be negative if the LEDs are lit from last 

755 to first. 

756 """ 

757 lit_value = self.value * len(self) 

758 if not isinstance(self[0], PWMLED): 

759 lit_value = int(lit_value) 

760 return lit_value 

761 

762 @lit_count.setter 

763 def lit_count(self, value): 

764 self.value = value / len(self) 

765 

766 

767class LEDCharFont(MutableMapping): 

768 """ 

769 Contains a mapping of values to tuples of LED states. 

770 

771 This effectively acts as a "font" for :class:`LEDCharDisplay`, and two 

772 default fonts (for 7-segment and 14-segment displays) are shipped with GPIO 

773 Zero by default. You can construct your own font instance from a 

774 :class:`dict` which maps values (usually single-character strings) to 

775 a tuple of LED states:: 

776 

777 from gpiozero import LEDCharDisplay, LEDCharFont 

778 

779 my_font = LEDCharFont({ 

780 ' ': (0, 0, 0, 0, 0, 0, 0), 

781 'D': (1, 1, 1, 1, 1, 1, 0), 

782 'A': (1, 1, 1, 0, 1, 1, 1), 

783 'd': (0, 1, 1, 1, 1, 0, 1), 

784 'a': (1, 1, 1, 1, 1, 0, 1), 

785 }) 

786 display = LEDCharDisplay(26, 13, 12, 22, 17, 19, 6, dp=5, font=my_font) 

787 display.value = 'D' 

788 

789 Font instances are mutable and can be changed while actively in use by 

790 an instance of :class:`LEDCharDisplay`. However, changing the font will 

791 *not* change the state of the LEDs in the display (though it may change 

792 the :attr:`~LEDCharDisplay.value` of the display when next queried). 

793 

794 .. note:: 

795 

796 Your custom mapping should always include a value (typically space) 

797 which represents all the LEDs off. This will usually be the default 

798 value for an instance of :class:`LEDCharDisplay`. 

799 

800 You may also wish to load fonts from a friendly text-based format. A simple 

801 parser for such formats (supporting an arbitrary number of segments) is 

802 provided by :func:`gpiozero.fonts.load_segment_font`. 

803 """ 

804 def __init__(self, font): 

805 super(LEDCharFont, self).__init__() 

806 self._map = OrderedDict([ 

807 (char, tuple(int(bool(pin)) for pin in pins)) 

808 for char, pins in font.items() 

809 ]) 

810 self._refresh_rmap() 

811 

812 def __repr__(self): 

813 return '{self.__class__.__name__}({{\n{content}\n}})'.format( 

814 self=self, content='\n'.join( 

815 ' {key!r}: {value!r},'.format(key=key, value=value) 

816 for key, value in sorted(self.items()) 

817 )) 

818 

819 def _refresh_rmap(self): 

820 # The reverse mapping is pre-calculated for speed of lookup. Given that 

821 # the font mapping can be 1:n, we cannot guarantee the reverse is 

822 # unique. In case the provided font is an ordered dictionary, we 

823 # explicitly take only the first definition of each non-unique pin 

824 # definition so that value lookups are predictable 

825 rmap = {} 

826 for char, pins in self._map.items(): 

827 rmap.setdefault(pins, char) 

828 self._rmap = rmap 

829 

830 def __len__(self): 

831 return len(self._map) 

832 

833 def __iter__(self): 

834 return iter(self._map) 

835 

836 def __getitem__(self, char): 

837 return self._map[char] 

838 

839 def __setitem__(self, char, pins): 

840 try: 

841 # This is necessary to ensure that _rmap is correct in the case 

842 # that we're overwriting an existing char->pins mapping 

843 del self[char] 

844 except KeyError: 

845 pass 

846 pins = tuple(int(bool(pin)) for pin in pins) 

847 self._map[char] = pins 

848 self._rmap.setdefault(pins, char) 

849 

850 def __delitem__(self, char): 

851 pins = self._map[char] 

852 del self._map[char] 

853 # If the reverse mapping of the char's pins maps to the char we need 

854 # to find if it now maps to another char (given the n:1 mapping) 

855 if self._rmap[pins] == char: 

856 del self._rmap[pins] 

857 for char, char_pins in self._map.items(): 

858 if pins == char_pins: 

859 self._rmap[pins] = char 

860 break 

861 

862 

863class LEDCharDisplay(LEDCollection): 

864 """ 

865 Extends :class:`LEDCollection` for a multi-segment LED display. 

866 

867 `Multi-segment LED displays`_ typically have 7 pins (labelled "a" through 

868 "g") representing 7 LEDs layed out in a figure-of-8 fashion. Frequently, an 

869 eigth pin labelled "dp" is included for a trailing decimal-point: 

870 

871 .. code-block:: text 

872 

873 a 

874 ━━━━━ 

875 f ┃ ┃ b 

876 ┃ g ┃ 

877 ━━━━━ 

878 e ┃ ┃ c 

879 ┃ ┃ 

880 ━━━━━ • dp 

881 d 

882 

883 Other common layouts are 9, 14, and 16 segment displays which include 

884 additional segments permitting more accurate renditions of alphanumerics. 

885 For example: 

886 

887 .. code-block:: text 

888 

889 a 

890 ━━━━━ 

891 f ┃╲i┃j╱┃ b 

892 ┃ ╲┃╱k┃ 

893 g━━ ━━h 

894 e ┃ ╱┃╲n┃ c 

895 ┃╱l┃m╲┃ 

896 ━━━━━ • dp 

897 d 

898 

899 Such displays have either a common anode, or common cathode pin. This class 

900 defaults to the latter; when using a common anode display *active_high* 

901 should be set to :data:`False`. 

902 

903 Instances of this class can be used to display characters or control 

904 individual LEDs on the display. For example:: 

905 

906 from gpiozero import LEDCharDisplay 

907 

908 char = LEDCharDisplay(4, 5, 6, 7, 8, 9, 10, active_high=False) 

909 char.value = 'C' 

910 

911 If the class is constructed with 7 or 14 segments, a default :attr:`font` 

912 will be loaded, mapping some ASCII characters to typical layouts. In other 

913 cases, the default mapping will simply assign " " (space) to all LEDs off. 

914 You can assign your own mapping at construction time or after 

915 instantiation. 

916 

917 While the example above shows the display with a :class:`str` value, 

918 theoretically the *font* can map any value that can be the key in a 

919 :class:`dict`, so the value of the display can be likewise be any valid 

920 key value (e.g. you could map integer digits to LED patterns). That said, 

921 there is one exception to this: when *dp* is specified to enable the 

922 decimal-point, the :attr:`value` must be a :class:`str` as the presence 

923 or absence of a "." suffix indicates whether the *dp* LED is lit. 

924 

925 :type pins: int or str 

926 :param \\*pins: 

927 Specify the GPIO pins that the multi-segment display is attached to. 

928 Pins should be in the LED segment order A, B, C, D, E, F, G, and will 

929 be named automatically by the class. If a decimal-point pin is 

930 present, specify it separately as the *dp* parameter. 

931 

932 :type dp: int or str 

933 :param dp: 

934 If a decimal-point segment is present, specify it as this named 

935 parameter. 

936 

937 :type font: dict or None 

938 :param font: 

939 A mapping of values (typically characters, but may also be numbers) to 

940 tuples of LED states. A default mapping for ASCII characters is 

941 provided for 7 and 14 segment displays. 

942 

943 :param bool pwm: 

944 If :data:`True`, construct :class:`PWMLED` instances for each pin. If 

945 :data:`False` (the default), construct regular :class:`LED` instances. 

946 

947 :param bool active_high: 

948 If :data:`True` (the default), the :meth:`on` method will set all the 

949 associated pins to HIGH. If :data:`False`, the :meth:`on` method will 

950 set all pins to LOW (the :meth:`off` method always does the opposite). 

951 

952 :param initial_value: 

953 The initial value to display. Defaults to space (" ") which typically 

954 maps to all LEDs being inactive. If :data:`None`, each device will be 

955 left in whatever state the pin is found in when configured for output 

956 (warning: this can be on). 

957 

958 :type pin_factory: Factory or None 

959 :param pin_factory: 

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

961 which most users can ignore). 

962 

963 .. _Multi-segment LED displays: https://en.wikipedia.org/wiki/Seven-segment_display 

964 """ 

965 def __init__(self, *pins, **kwargs): 

966 dp = kwargs.pop('dp', None) 

967 font = kwargs.pop('font', None) 

968 pwm = kwargs.pop('pwm', False) 

969 active_high = kwargs.pop('active_high', True) 

970 initial_value = kwargs.pop('initial_value', " ") 

971 pin_factory = kwargs.pop('pin_factory', None) 

972 if kwargs: 

973 raise TypeError( 

974 'unexpected keyword argument: %s' % kwargs.popiem()[0]) 

975 if not 1 < len(pins) <= 26: 

976 raise PinInvalidPin( 

977 'Must have between 2 and 26 LEDs in LEDCharDisplay') 

978 for pin in pins: 

979 if isinstance(pin, LEDCollection): 

980 raise PinInvalidPin( 

981 'Cannot use LEDCollection in LEDCharDisplay') 

982 

983 if font is None: 

984 if len(pins) in (7, 14): 

985 # Only import pkg_resources here as merely importing it is 

986 # slooooow! 

987 from pkg_resources import resource_stream 

988 font = { 

989 7: lambda: load_font_7seg( 

990 resource_stream(__name__, 'fonts/7seg.txt')), 

991 14: lambda: load_font_14seg( 

992 resource_stream(__name__, 'fonts/14seg.txt')), 

993 }[len(pins)]() 

994 else: 

995 # Construct a default dict containing a definition for " " 

996 font = {" ": (0,) * len(pins)} 

997 self._font = LEDCharFont(font) 

998 

999 pins = {chr(ord('a') + i): pin for i, pin in enumerate(pins)} 

1000 order = sorted(pins.keys()) 

1001 if dp is not None: 

1002 pins['dp'] = dp 

1003 order.append('dp') 

1004 super(LEDCharDisplay, self).__init__( 

1005 pwm=pwm, active_high=active_high, initial_value=None, 

1006 _order=order, pin_factory=pin_factory, **pins) 

1007 if initial_value is not None: 

1008 self.value = initial_value 

1009 

1010 @property 

1011 def font(self): 

1012 """ 

1013 An :class:`LEDCharFont` mapping characters to tuples of LED states. 

1014 The font is mutable after construction. You can assign a tuple of LED 

1015 states to a character to modify the font, delete an existing character 

1016 in the font, or assign a mapping of characters to tuples to replace the 

1017 entire font. 

1018 

1019 Note that modifying the :attr:`font` never alters the underlying LED 

1020 states. Only assignment to :attr:`value`, or calling the inherited 

1021 :class:`LEDCollection` methods (:meth:`on`, :meth:`off`, etc.) modifies 

1022 LED states. However, modifying the font may alter the character 

1023 returned by querying :attr:`value`. 

1024 """ 

1025 return self._font 

1026 

1027 @font.setter 

1028 def font(self, value): 

1029 self._font = LEDCharFont(value) 

1030 

1031 @property 

1032 def value(self): 

1033 """ 

1034 The character the display should show. This is mapped by the current 

1035 :attr:`font` to a tuple of LED states which is applied to the 

1036 underlying LED objects when this attribute is set. 

1037 

1038 When queried, the current LED states are looked up in the font to 

1039 determine the character shown. If the current LED states do not 

1040 correspond to any character in the :attr:`font`, the value is 

1041 :data:`None`. 

1042 

1043 It is possible for multiple characters in the font to map to the same 

1044 LED states (e.g. S and 5). In this case, if the font was constructed 

1045 from an ordered mapping (which is the default), then the first matching 

1046 mapping will always be returned. This also implies that the value 

1047 queried need not match the value set. 

1048 """ 

1049 state = super(LEDCharDisplay, self).value 

1050 if hasattr(self, 'dp'): 

1051 state, dp = state[:-1], state[-1] 

1052 else: 

1053 dp = False 

1054 try: 

1055 result = self._font._rmap[state] 

1056 except KeyError: 

1057 # Raising exceptions on lookup is problematic; in case the LED 

1058 # state is not representable we simply return None (although 

1059 # technically that is a valid item we can map :) 

1060 return None 

1061 else: 

1062 if dp: 

1063 return result + '.' 

1064 else: 

1065 return result 

1066 

1067 @value.setter 

1068 def value(self, value): 

1069 for led, v in zip(self, self._parse_state(value)): 

1070 led.value = v 

1071 

1072 def _parse_state(self, value): 

1073 if hasattr(self, 'dp'): 

1074 if len(value) > 1 and value.endswith('.'): 

1075 value = value[:-1] 

1076 dp = 1 

1077 else: 

1078 dp = 0 

1079 return self._font[value] + (dp,) 

1080 else: 

1081 return self._font[value] 

1082 

1083 

1084class LEDMultiCharDisplay(CompositeOutputDevice): 

1085 """ 

1086 Wraps :class:`LEDCharDisplay` for multi-character `multiplexed`_ LED 

1087 character displays. 

1088 

1089 The class is constructed with a *char* which is an instance of the 

1090 :class:`LEDCharDisplay` class, capable of controlling the LEDs in one 

1091 character of the display, and an additional set of *pins* that represent 

1092 the common cathode (or anode) of each character. 

1093 

1094 .. warning:: 

1095 

1096 You should not attempt to connect the common cathode (or anode) off 

1097 each character directly to a GPIO. Rather, use a set of transistors (or 

1098 some other suitable component capable of handling the current of all 

1099 the segment LEDs simultaneously) to connect the common cathode to 

1100 ground (or the common anode to the supply) and control those 

1101 transistors from the GPIOs specified under *pins*. 

1102 

1103 The *active_high* parameter defaults to :data:`True`. Note that it only 

1104 applies to the specified *pins*, which are assumed to be controlling a set 

1105 of transistors (hence the default). The specified *char* will use its own 

1106 *active_high* parameter. Finally, *initial_value* defaults to a tuple of 

1107 :attr:`~LEDCharDisplay.value` attribute of the specified display multiplied 

1108 by the number of *pins* provided. 

1109 

1110 When the :attr:`value` is set such that one or more characters in the 

1111 display differ in value, a background thread is implicitly started to 

1112 rotate the active character, relying on `persistence of vision`_ to display 

1113 the complete value. 

1114 

1115 .. _multiplexed: https://en.wikipedia.org/wiki/Multiplexed_display 

1116 .. _persistence of vision: https://en.wikipedia.org/wiki/Persistence_of_vision 

1117 """ 

1118 def __init__(self, char, *pins, **kwargs): 

1119 active_high = kwargs.pop('active_high', True) 

1120 initial_value = kwargs.pop('initial_value', None) 

1121 pin_factory = kwargs.pop('pin_factory', None) 

1122 if kwargs: 

1123 raise TypeError( 

1124 'unexpected keyword argument: %s' % kwargs.popiem()[0]) 

1125 if not isinstance(char, LEDCharDisplay): 

1126 raise ValueError('char must be an LEDCharDisplay') 

1127 if initial_value is None: 

1128 initial_value = (char.value,) * len(pins) 

1129 if pin_factory is None: 

1130 pin_factory = char.pin_factory 

1131 self._plex_thread = None 

1132 self._plex_delay = 0.005 

1133 plex = CompositeOutputDevice(*( 

1134 OutputDevice( 

1135 pin, active_high=active_high, initial_value=None, 

1136 pin_factory=pin_factory) 

1137 for pin in pins 

1138 )) 

1139 super(LEDMultiCharDisplay, self).__init__( 

1140 plex=plex, char=char, pin_factory=pin_factory) 

1141 self.value = initial_value 

1142 

1143 def close(self): 

1144 try: 

1145 self._stop_plex() 

1146 except AttributeError: 

1147 pass 

1148 super(LEDMultiCharDisplay, self).close() 

1149 

1150 def _stop_plex(self): 

1151 if self._plex_thread: 

1152 self._plex_thread.stop() 

1153 self._plex_thread = None 

1154 

1155 @property 

1156 def plex_delay(self): 

1157 """ 

1158 The delay (measured in seconds) in the loop used to switch each 

1159 character in the multiplexed display on. Defaults to 0.005 seconds 

1160 which is generally sufficient to provide a "stable" (non-flickery) 

1161 display. 

1162 """ 

1163 return self._plex_delay 

1164 

1165 @plex_delay.setter 

1166 def plex_delay(self, value): 

1167 if value < 0: 

1168 raise BadWaitTime('plex_delay must be 0 or greater') 

1169 self._plex_delay = float(value) 

1170 

1171 @property 

1172 def value(self): 

1173 """ 

1174 The sequence of values to display. 

1175 

1176 This can be any sequence containing keys from the 

1177 :attr:`~LEDCharDisplay.font` of the associated character display. For 

1178 example, if the value consists only of single-character strings, it's 

1179 valid to assign a string to this property (as a string is simply a 

1180 sequence of individual character keys):: 

1181 

1182 from gpiozero import LEDCharDisplay, LEDMultiCharDisplay 

1183 

1184 c = LEDCharDisplay(4, 5, 6, 7, 8, 9, 10) 

1185 d = LEDMultiCharDisplay(c, 19, 20, 21, 22) 

1186 d.value = 'LEDS' 

1187 

1188 However, things get more complicated if a decimal point is in use as 

1189 then this class needs to know explicitly where to break the value for 

1190 use on each character of the display. This can be handled by simply 

1191 assigning a sequence of strings thus:: 

1192 

1193 from gpiozero import LEDCharDisplay, LEDMultiCharDisplay 

1194 

1195 c = LEDCharDisplay(4, 5, 6, 7, 8, 9, 10) 

1196 d = LEDMultiCharDisplay(c, 19, 20, 21, 22) 

1197 d.value = ('L.', 'E', 'D', 'S') 

1198 

1199 This is how the value will always be represented when queried (as a 

1200 tuple of individual values) as it neatly handles dealing with 

1201 heterogeneous types and the aforementioned decimal point issue. 

1202 

1203 .. note:: 

1204 

1205 The value also controls whether a background thread is in use to 

1206 multiplex the display. When all positions in the value are equal 

1207 the background thread is disabled and all characters are 

1208 simultaneously enabled. 

1209 """ 

1210 return self._value 

1211 

1212 @value.setter 

1213 def value(self, value): 

1214 if len(value) > len(self.plex): 

1215 raise ValueError( 

1216 'length of value must not exceed the number of characters in ' 

1217 'the display') 

1218 elif len(value) < len(self.plex): 

1219 # Right-align the short value on the display 

1220 value = (' ',) * (len(self.plex) - len(value)) + tuple(value) 

1221 else: 

1222 value = tuple(value) 

1223 

1224 # Get the list of tuples of states that the character LEDs will pass 

1225 # through. Prune any entirely blank state (which we can skip by never 

1226 # activating the plex for them) but remember which plex index each 

1227 # (non-blank) state is associated with 

1228 states = {} 

1229 for index, char in enumerate(value): 

1230 state = self.char._parse_state(char) 

1231 if any(state): 

1232 states.setdefault(state, set()).add(index) 

1233 # Calculate the transitions between states for an ordering of chars 

1234 # based on activated LEDs. This a vague attempt at minimizing the 

1235 # number of LEDs that need flipping between chars; to do this 

1236 # "properly" is O(n!) which gets silly quickly so ... fudge it 

1237 order = sorted(states) 

1238 if len(order) > 1: 

1239 transitions = [ 

1240 [(self.plex[index], 0) for index in states[old]] + 

1241 [ 

1242 (led, new_value) 

1243 for led, old_value, new_value in zip(self.char, old, new) 

1244 if old_value ^ new_value 

1245 ] + 

1246 [(self.plex[index], 1) for index in states[new]] 

1247 for old, new in pairwise(order + [order[0]]) 

1248 ] 

1249 else: 

1250 transitions = [] 

1251 

1252 # Stop any current display thread and disable the display 

1253 self._stop_plex() 

1254 self.plex.off() 

1255 

1256 # If there's any characters to display, set the character LEDs to the 

1257 # state of the first character in the display order. If there's 

1258 # transitions to display, activate the plex thread; otherwise, just 

1259 # switch on each plex with a char to display 

1260 if order: 

1261 for led, state in zip(self.char, order[0]): 

1262 led.value = state 

1263 if transitions: 

1264 self._plex_thread = GPIOThread(self._show_chars, (transitions,)) 

1265 self._plex_thread.start() 

1266 else: 

1267 for index in states[order[0]]: 

1268 self.plex[index].on() 

1269 self._value = value 

1270 

1271 def _show_chars(self, transitions): 

1272 for transition in cycle(transitions): 

1273 for device, value in transition: 

1274 device.value = value 

1275 if self._plex_thread.stopping.wait(self._plex_delay): 

1276 break 

1277 

1278 

1279class PiHutXmasTree(LEDBoard): 

1280 """ 

1281 Extends :class:`LEDBoard` for `The Pi Hut's Xmas board`_: a 3D Christmas 

1282 tree board with 24 red LEDs and a white LED as a star on top. 

1283 

1284 The 24 red LEDs can be accessed through the attributes led0, led1, led2, 

1285 and so on. The white star LED is accessed through the :attr:`star` 

1286 attribute. Alternatively, as with all descendents of :class:`LEDBoard`, 

1287 you can treat the instance as a sequence of LEDs (the first element is the 

1288 :attr:`star`). 

1289 

1290 The Xmas Tree board pins are fixed and therefore there's no need to specify 

1291 them when constructing this class. The following example turns all the LEDs 

1292 on one at a time:: 

1293 

1294 from gpiozero import PiHutXmasTree 

1295 from time import sleep 

1296 

1297 tree = PiHutXmasTree() 

1298 

1299 for light in tree: 

1300 light.on() 

1301 sleep(1) 

1302 

1303 The following example turns the star LED on and sets all the red LEDs to 

1304 flicker randomly:: 

1305 

1306 from gpiozero import PiHutXmasTree 

1307 from gpiozero.tools import random_values 

1308 from signal import pause 

1309 

1310 tree = PiHutXmasTree(pwm=True) 

1311 

1312 tree.star.on() 

1313 

1314 for led in tree[1:]: 

1315 led.source_delay = 0.1 

1316 led.source = random_values() 

1317 

1318 pause() 

1319 

1320 :param bool pwm: 

1321 If :data:`True`, construct :class:`PWMLED` instances for each pin. If 

1322 :data:`False` (the default), construct regular :class:`LED` instances. 

1323 

1324 :type initial_value: bool or None 

1325 :param initial_value: 

1326 If :data:`False` (the default), all LEDs will be off initially. If 

1327 :data:`None`, each device will be left in whatever state the pin is 

1328 found in when configured for output (warning: this can be on). If 

1329 :data:`True`, the device will be switched on initially. 

1330 

1331 :type pin_factory: Factory or None 

1332 :param pin_factory: 

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

1334 which most users can ignore). 

1335 

1336 .. _The Pi Hut's Xmas board: https://thepihut.com/xmas 

1337 

1338 .. attribute:: star 

1339 

1340 Returns the :class:`LED` or :class:`PWMLED` representing the white 

1341 star on top of the tree. 

1342 

1343 .. attribute:: led0, led1, led2, ... 

1344 

1345 Returns the :class:`LED` or :class:`PWMLED` representing one of the red 

1346 LEDs. There are actually 24 of these properties named led0, led1, and 

1347 so on but for the sake of brevity we represent all 24 under this 

1348 section. 

1349 """ 

1350 def __init__(self, pwm=False, initial_value=False, pin_factory=None): 

1351 pins_dict = OrderedDict(star=2) 

1352 pins = (4, 15, 13, 21, 25, 8, 5, 10, 16, 17, 27, 26, 

1353 24, 9, 12, 6, 20, 19, 14, 18, 11, 7, 23, 22) 

1354 for i, pin in enumerate(pins): 

1355 pins_dict['led%d' % (i+1)] = pin 

1356 super(PiHutXmasTree, self).__init__( 

1357 pwm=pwm, initial_value=initial_value, 

1358 _order=pins_dict.keys(), 

1359 pin_factory=pin_factory, 

1360 **pins_dict 

1361 ) 

1362 

1363 

1364class LedBorg(RGBLED): 

1365 """ 

1366 Extends :class:`RGBLED` for the `PiBorg LedBorg`_: an add-on board 

1367 containing a very bright RGB LED. 

1368 

1369 The LedBorg pins are fixed and therefore there's no need to specify them 

1370 when constructing this class. The following example turns the LedBorg 

1371 purple:: 

1372 

1373 from gpiozero import LedBorg 

1374 

1375 led = LedBorg() 

1376 led.color = (1, 0, 1) 

1377 

1378 :type initial_value: ~colorzero.Color or tuple 

1379 :param initial_value: 

1380 The initial color for the LedBorg. Defaults to black ``(0, 0, 0)``. 

1381 

1382 :param bool pwm: 

1383 If :data:`True` (the default), construct :class:`PWMLED` instances for 

1384 each component of the LedBorg. If :data:`False`, construct regular 

1385 :class:`LED` instances, which prevents smooth color graduations. 

1386 

1387 :type pin_factory: Factory or None 

1388 :param pin_factory: 

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

1390 which most users can ignore). 

1391 

1392 .. _PiBorg LedBorg: https://www.piborg.org/ledborg 

1393 """ 

1394 

1395 def __init__(self, initial_value=(0, 0, 0), pwm=True, pin_factory=None): 

1396 super(LedBorg, self).__init__( 

1397 red='BOARD11', green='BOARD13', blue='BOARD15', 

1398 pwm=pwm, initial_value=initial_value, pin_factory=pin_factory 

1399 ) 

1400 

1401 

1402class PiLiter(LEDBoard): 

1403 """ 

1404 Extends :class:`LEDBoard` for the `Ciseco Pi-LITEr`_: a strip of 8 very 

1405 bright LEDs. 

1406 

1407 The Pi-LITEr pins are fixed and therefore there's no need to specify them 

1408 when constructing this class. The following example turns on all the LEDs 

1409 of the Pi-LITEr:: 

1410 

1411 from gpiozero import PiLiter 

1412 

1413 lite = PiLiter() 

1414 lite.on() 

1415 

1416 :param bool pwm: 

1417 If :data:`True`, construct :class:`PWMLED` instances for each pin. If 

1418 :data:`False` (the default), construct regular :class:`LED` instances. 

1419 

1420 :type initial_value: bool or None 

1421 :param initial_value: 

1422 If :data:`False` (the default), all LEDs will be off initially. If 

1423 :data:`None`, each LED will be left in whatever state the pin is found 

1424 in when configured for output (warning: this can be on). If 

1425 :data:`True`, the each LED will be switched on initially. 

1426 

1427 :type pin_factory: Factory or None 

1428 :param pin_factory: 

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

1430 which most users can ignore). 

1431 

1432 .. _Ciseco Pi-LITEr: http://shop.ciseco.co.uk/pi-liter-8-led-strip-for-the-raspberry-pi/ 

1433 """ 

1434 

1435 def __init__(self, pwm=False, initial_value=False, pin_factory=None): 

1436 pins = ('BOARD7', 'BOARD11', 'BOARD13', 'BOARD12', 

1437 'BOARD15', 'BOARD16', 'BOARD18', 'BOARD22') 

1438 super(PiLiter, self).__init__( 

1439 *pins, pwm=pwm, initial_value=initial_value, pin_factory=pin_factory 

1440 ) 

1441 

1442 

1443class PiLiterBarGraph(LEDBarGraph): 

1444 """ 

1445 Extends :class:`LEDBarGraph` to treat the `Ciseco Pi-LITEr`_ as an 

1446 8-segment bar graph. 

1447 

1448 The Pi-LITEr pins are fixed and therefore there's no need to specify them 

1449 when constructing this class. The following example sets the graph value 

1450 to 0.5:: 

1451 

1452 from gpiozero import PiLiterBarGraph 

1453 

1454 graph = PiLiterBarGraph() 

1455 graph.value = 0.5 

1456 

1457 :param bool pwm: 

1458 If :data:`True`, construct :class:`PWMLED` instances for each pin. If 

1459 :data:`False` (the default), construct regular :class:`LED` instances. 

1460 

1461 :param float initial_value: 

1462 The initial :attr:`value` of the graph given as a float between -1 and 

1463 +1. Defaults to ``0.0``. 

1464 

1465 :type pin_factory: Factory or None 

1466 :param pin_factory: 

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

1468 which most users can ignore). 

1469 

1470 .. _Ciseco Pi-LITEr: http://shop.ciseco.co.uk/pi-liter-8-led-strip-for-the-raspberry-pi/ 

1471 """ 

1472 

1473 def __init__(self, pwm=False, initial_value=0.0, pin_factory=None): 

1474 pins = ('BOARD7', 'BOARD11', 'BOARD13', 'BOARD12', 

1475 'BOARD15', 'BOARD16', 'BOARD18', 'BOARD22') 

1476 super(PiLiterBarGraph, self).__init__( 

1477 *pins, pwm=pwm, initial_value=initial_value, pin_factory=pin_factory 

1478 ) 

1479 

1480 

1481class TrafficLights(LEDBoard): 

1482 """ 

1483 Extends :class:`LEDBoard` for devices containing red, yellow, and green 

1484 LEDs. 

1485 

1486 The following example initializes a device connected to GPIO pins 2, 3, 

1487 and 4, then lights the amber (yellow) LED attached to GPIO 3:: 

1488 

1489 from gpiozero import TrafficLights 

1490 

1491 traffic = TrafficLights(2, 3, 4) 

1492 traffic.amber.on() 

1493 

1494 :type red: int or str 

1495 :param red: 

1496 The GPIO pin that the red LED is attached to. See :ref:`pin-numbering` 

1497 for valid pin numbers. 

1498 

1499 :type amber: int or str or None 

1500 :param amber: 

1501 The GPIO pin that the amber LED is attached to. See 

1502 :ref:`pin-numbering` for valid pin numbers. 

1503 

1504 :type yellow: int or str or None 

1505 :param yellow: 

1506 The GPIO pin that the yellow LED is attached to. This is merely an 

1507 alias for the ``amber`` parameter; you can't specify both ``amber`` and 

1508 ``yellow``. See :ref:`pin-numbering` for valid pin numbers. 

1509 

1510 :type green: int or str 

1511 :param green: 

1512 The GPIO pin that the green LED is attached to. See 

1513 :ref:`pin-numbering` for valid pin numbers. 

1514 

1515 :param bool pwm: 

1516 If :data:`True`, construct :class:`PWMLED` instances to represent each 

1517 LED. If :data:`False` (the default), construct regular :class:`LED` 

1518 instances. 

1519 

1520 :type initial_value: bool or None 

1521 :param initial_value: 

1522 If :data:`False` (the default), all LEDs will be off initially. If 

1523 :data:`None`, each device will be left in whatever state the pin is 

1524 found in when configured for output (warning: this can be on). If 

1525 :data:`True`, the device will be switched on initially. 

1526 

1527 :type pin_factory: Factory or None 

1528 :param pin_factory: 

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

1530 which most users can ignore). 

1531 

1532 .. attribute:: red 

1533 

1534 The red :class:`LED` or :class:`PWMLED`. 

1535 

1536 .. attribute:: amber 

1537 

1538 The amber :class:`LED` or :class:`PWMLED`. Note that this attribute 

1539 will not be present when the instance is constructed with the 

1540 *yellow* keyword parameter. 

1541 

1542 .. attribute:: yellow 

1543 

1544 The yellow :class:`LED` or :class:`PWMLED`. Note that this attribute 

1545 will only be present when the instance is constructed with the 

1546 *yellow* keyword parameter. 

1547 

1548 .. attribute:: green 

1549 

1550 The green :class:`LED` or :class:`PWMLED`. 

1551 """ 

1552 def __init__(self, red=None, amber=None, green=None, 

1553 pwm=False, initial_value=False, yellow=None, 

1554 pin_factory=None): 

1555 if amber is not None and yellow is not None: 

1556 raise OutputDeviceBadValue( 

1557 'Only one of amber or yellow can be specified') 

1558 devices = OrderedDict((('red', red), )) 

1559 self._display_yellow = amber is None and yellow is not None 

1560 if self._display_yellow: 

1561 devices['yellow'] = yellow 

1562 else: 

1563 devices['amber'] = amber 

1564 devices['green'] = green 

1565 if not all(p is not None for p in devices.values()): 

1566 raise GPIOPinMissing('%s pins must be provided' % 

1567 ', '.join(devices.keys())) 

1568 super(TrafficLights, self).__init__( 

1569 pwm=pwm, initial_value=initial_value, 

1570 _order=devices.keys(), pin_factory=pin_factory, 

1571 **devices) 

1572 

1573 def __getattr__(self, name): 

1574 if name == 'amber' and self._display_yellow: 

1575 name = 'yellow' 

1576 elif name == 'yellow' and not self._display_yellow: 

1577 name = 'amber' 

1578 return super(TrafficLights, self).__getattr__(name) 

1579 

1580 

1581class PiTraffic(TrafficLights): 

1582 """ 

1583 Extends :class:`TrafficLights` for the `Low Voltage Labs PI-TRAFFIC`_ 

1584 vertical traffic lights board when attached to GPIO pins 9, 10, and 11. 

1585 

1586 There's no need to specify the pins if the PI-TRAFFIC is connected to the 

1587 default pins (9, 10, 11). The following example turns on the amber LED on 

1588 the PI-TRAFFIC:: 

1589 

1590 from gpiozero import PiTraffic 

1591 

1592 traffic = PiTraffic() 

1593 traffic.amber.on() 

1594 

1595 To use the PI-TRAFFIC board when attached to a non-standard set of pins, 

1596 simply use the parent class, :class:`TrafficLights`. 

1597 

1598 :param bool pwm: 

1599 If :data:`True`, construct :class:`PWMLED` instances to represent each 

1600 LED. If :data:`False` (the default), construct regular :class:`LED` 

1601 instances. 

1602 

1603 :type initial_value: bool or None 

1604 :param bool initial_value: 

1605 If :data:`False` (the default), all LEDs will be off initially. If 

1606 :data:`None`, each device will be left in whatever state the pin is 

1607 found in when configured for output (warning: this can be on). If 

1608 :data:`True`, the device will be switched on initially. 

1609 

1610 :type pin_factory: Factory or None 

1611 :param pin_factory: 

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

1613 which most users can ignore). 

1614 

1615 .. _Low Voltage Labs PI-TRAFFIC: http://lowvoltagelabs.com/products/pi-traffic/ 

1616 """ 

1617 def __init__(self, pwm=False, initial_value=False, pin_factory=None): 

1618 super(PiTraffic, self).__init__( 

1619 'BOARD21', 'BOARD19', 'BOARD23', 

1620 pwm=pwm, initial_value=initial_value, pin_factory=pin_factory 

1621 ) 

1622 

1623 

1624class PiStop(TrafficLights): 

1625 """ 

1626 Extends :class:`TrafficLights` for the `PiHardware Pi-Stop`_: a vertical 

1627 traffic lights board. 

1628 

1629 The following example turns on the amber LED on a Pi-Stop connected to 

1630 location ``A+``:: 

1631 

1632 from gpiozero import PiStop 

1633 

1634 traffic = PiStop('A+') 

1635 traffic.amber.on() 

1636 

1637 :param str location: 

1638 The `location`_ on the GPIO header to which the Pi-Stop is connected. 

1639 Must be one of: ``A``, ``A+``, ``B``, ``B+``, ``C``, ``D``. 

1640 

1641 :param bool pwm: 

1642 If :data:`True`, construct :class:`PWMLED` instances to represent each 

1643 LED. If :data:`False` (the default), construct regular :class:`LED` 

1644 instances. 

1645 

1646 :type initial_value: bool or None 

1647 :param bool initial_value: 

1648 If :data:`False` (the default), all LEDs will be off initially. If 

1649 :data:`None`, each device will be left in whatever state the pin is 

1650 found in when configured for output (warning: this can be on). If 

1651 :data:`True`, the device will be switched on initially. 

1652 

1653 :type pin_factory: Factory or None 

1654 :param pin_factory: 

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

1656 which most users can ignore). 

1657 

1658 .. _PiHardware Pi-Stop: https://pihw.wordpress.com/meltwaters-pi-hardware-kits/pi-stop/ 

1659 .. _location: https://github.com/PiHw/Pi-Stop/blob/master/markdown_source/markdown/Discover-PiStop.md 

1660 """ 

1661 LOCATIONS = { 

1662 'A': ('BOARD26', 'BOARD24', 'BOARD22'), 

1663 'A+': ('BOARD40', 'BOARD38', 'BOARD36'), 

1664 'B': ('BOARD19', 'BOARD21', 'BOARD23'), 

1665 'B+': ('BOARD33', 'BOARD35', 'BOARD37'), 

1666 'C': ('BOARD12', 'BOARD10', 'BOARD8'), 

1667 'D': ('BOARD3', 'BOARD5', 'BOARD7'), 

1668 } 

1669 

1670 def __init__( 

1671 self, location=None, pwm=False, initial_value=False, 

1672 pin_factory=None): 

1673 gpios = self.LOCATIONS.get(location, None) 

1674 if gpios is None: 

1675 raise ValueError('location must be one of: %s' % 

1676 ', '.join(sorted(self.LOCATIONS.keys()))) 

1677 super(PiStop, self).__init__( 

1678 *gpios, pwm=pwm, initial_value=initial_value, 

1679 pin_factory=pin_factory) 

1680 

1681 

1682class StatusZero(LEDBoard): 

1683 """ 

1684 Extends :class:`LEDBoard` for The Pi Hut's `STATUS Zero`_: a Pi Zero sized 

1685 add-on board with three sets of red/green LEDs to provide a status 

1686 indicator. 

1687 

1688 The following example designates the first strip the label "wifi" and the 

1689 second "raining", and turns them green and red respectfully:: 

1690 

1691 from gpiozero import StatusZero 

1692 

1693 status = StatusZero('wifi', 'raining') 

1694 status.wifi.green.on() 

1695 status.raining.red.on() 

1696 

1697 Each designated label will contain two :class:`LED` objects named "red" 

1698 and "green". 

1699 

1700 :param str \\*labels: 

1701 Specify the names of the labels you wish to designate the strips to. 

1702 You can list up to three labels. If no labels are given, three strips 

1703 will be initialised with names 'one', 'two', and 'three'. If some, but 

1704 not all strips are given labels, any remaining strips will not be 

1705 initialised. 

1706 

1707 :type pin_factory: Factory or None 

1708 :param pin_factory: 

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

1710 which most users can ignore). 

1711 

1712 .. _STATUS Zero: https://thepihut.com/statuszero 

1713 

1714 .. attribute:: your-label-here, your-label-here, ... 

1715 

1716 This entry represents one of the three labelled attributes supported on 

1717 the STATUS Zero board. It is an :class:`LEDBoard` which contains: 

1718 

1719 .. attribute:: red 

1720 

1721 The :class:`LED` or :class:`PWMLED` representing the red LED 

1722 next to the label. 

1723 

1724 .. attribute:: green 

1725 

1726 The :class:`LED` or :class:`PWMLED` representing the green LED 

1727 next to the label. 

1728 """ 

1729 default_labels = ('one', 'two', 'three') 

1730 

1731 def __init__(self, *labels, **kwargs): 

1732 pins = ( 

1733 ('BOARD11', 'BOARD7'), 

1734 ('BOARD15', 'BOARD13'), 

1735 ('BOARD21', 'BOARD19'), 

1736 ) 

1737 pin_factory = kwargs.pop('pin_factory', None) 

1738 if len(labels) == 0: 

1739 labels = self.default_labels 

1740 elif len(labels) > len(pins): 

1741 raise ValueError("StatusZero doesn't support more than three labels") 

1742 dup, count = Counter(labels).most_common(1)[0] 

1743 if count > 1: 

1744 raise ValueError("Duplicate label %s" % dup) 

1745 super(StatusZero, self).__init__( 

1746 _order=labels, pin_factory=pin_factory, **{ 

1747 label: LEDBoard( 

1748 red=red, green=green, _order=('red', 'green'), 

1749 pin_factory=pin_factory, **kwargs 

1750 ) 

1751 for (green, red), label in zip(pins, labels) 

1752 } 

1753 ) 

1754 

1755 

1756class StatusBoard(CompositeOutputDevice): 

1757 """ 

1758 Extends :class:`CompositeOutputDevice` for The Pi Hut's `STATUS`_ board: a 

1759 HAT sized add-on board with five sets of red/green LEDs and buttons to 

1760 provide a status indicator with additional input. 

1761 

1762 The following example designates the first strip the label "wifi" and the 

1763 second "raining", turns the wifi green and then activates the button to 

1764 toggle its lights when pressed:: 

1765 

1766 from gpiozero import StatusBoard 

1767 

1768 status = StatusBoard('wifi', 'raining') 

1769 status.wifi.lights.green.on() 

1770 status.wifi.button.when_pressed = status.wifi.lights.toggle 

1771 

1772 Each designated label will contain a "lights" :class:`LEDBoard` containing 

1773 two :class:`LED` objects named "red" and "green", and a :class:`Button` 

1774 object named "button". 

1775 

1776 :param str \\*labels: 

1777 Specify the names of the labels you wish to designate the strips to. 

1778 You can list up to five labels. If no labels are given, five strips 

1779 will be initialised with names 'one' to 'five'. If some, but not all 

1780 strips are given labels, any remaining strips will not be initialised. 

1781 

1782 :type pin_factory: Factory or None 

1783 :param pin_factory: 

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

1785 which most users can ignore). 

1786 

1787 .. _STATUS: https://thepihut.com/status 

1788 

1789 .. attribute:: your-label-here, your-label-here, ... 

1790 

1791 This entry represents one of the five labelled attributes supported on 

1792 the STATUS board. It is an :class:`CompositeOutputDevice` which 

1793 contains: 

1794 

1795 .. attribute:: lights 

1796 

1797 A :class:`LEDBoard` representing the lights next to the label. It 

1798 contains: 

1799 

1800 .. attribute:: red 

1801 

1802 The :class:`LED` or :class:`PWMLED` representing the red LED 

1803 next to the label. 

1804 

1805 .. attribute:: green 

1806 

1807 The :class:`LED` or :class:`PWMLED` representing the green LED 

1808 next to the label. 

1809 

1810 .. attribute:: button 

1811 

1812 A :class:`Button` representing the button next to the label. 

1813 """ 

1814 default_labels = ('one', 'two', 'three', 'four', 'five') 

1815 

1816 def __init__(self, *labels, **kwargs): 

1817 pins = ( 

1818 ('BOARD11', 'BOARD7', 'BOARD8'), 

1819 ('BOARD15', 'BOARD13', 'BOARD35'), 

1820 ('BOARD21', 'BOARD19', 'BOARD10'), 

1821 ('BOARD29', 'BOARD23', 'BOARD37'), 

1822 ('BOARD33', 'BOARD31', 'BOARD12'), 

1823 ) 

1824 pin_factory = kwargs.pop('pin_factory', None) 

1825 if len(labels) == 0: 

1826 labels = self.default_labels 

1827 elif len(labels) > len(pins): 

1828 raise ValueError("StatusBoard doesn't support more than five labels") 

1829 dup, count = Counter(labels).most_common(1)[0] 

1830 if count > 1: 

1831 raise ValueError("Duplicate label %s" % dup) 

1832 super(StatusBoard, self).__init__( 

1833 _order=labels, pin_factory=pin_factory, **{ 

1834 label: CompositeOutputDevice( 

1835 button=Button(button, pin_factory=pin_factory), 

1836 lights=LEDBoard( 

1837 red=red, green=green, _order=('red', 'green'), 

1838 pin_factory=pin_factory, **kwargs 

1839 ), _order=('button', 'lights'), pin_factory=pin_factory 

1840 ) 

1841 for (green, red, button), label in zip(pins, labels) 

1842 } 

1843 ) 

1844 

1845 

1846class SnowPi(LEDBoard): 

1847 """ 

1848 Extends :class:`LEDBoard` for the `Ryanteck SnowPi`_ board. 

1849 

1850 The SnowPi pins are fixed and therefore there's no need to specify them 

1851 when constructing this class. The following example turns on the eyes, sets 

1852 the nose pulsing, and the arms blinking:: 

1853 

1854 from gpiozero import SnowPi 

1855 

1856 snowman = SnowPi(pwm=True) 

1857 snowman.eyes.on() 

1858 snowman.nose.pulse() 

1859 snowman.arms.blink() 

1860 

1861 :param bool pwm: 

1862 If :data:`True`, construct :class:`PWMLED` instances to represent each 

1863 LED. If :data:`False` (the default), construct regular :class:`LED` 

1864 instances. 

1865 

1866 :type initial_value: bool or None 

1867 :param bool initial_value: 

1868 If :data:`False` (the default), all LEDs will be off initially. If 

1869 :data:`None`, each device will be left in whatever state the pin is 

1870 found in when configured for output (warning: this can be on). If 

1871 :data:`True`, the device will be switched on initially. 

1872 

1873 :type pin_factory: Factory or None 

1874 :param pin_factory: 

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

1876 which most users can ignore). 

1877 

1878 .. _Ryanteck SnowPi: https://ryanteck.uk/raspberry-pi/114-snowpi-the-gpio-snowman-for-raspberry-pi-0635648608303.html 

1879 

1880 .. attribute:: arms 

1881 

1882 A :class:`LEDBoard` representing the arms of the snow man. It contains 

1883 the following attributes: 

1884 

1885 .. attribute:: left, right 

1886 

1887 Two :class:`LEDBoard` objects representing the left and right arms 

1888 of the snow-man. They contain: 

1889 

1890 .. attribute:: top, middle, bottom 

1891 

1892 The :class:`LED` or :class:`PWMLED` down the snow-man's arms. 

1893 

1894 .. attribute:: eyes 

1895 

1896 A :class:`LEDBoard` representing the eyes of the snow-man. It contains: 

1897 

1898 .. attribute:: left, right 

1899 

1900 The :class:`LED` or :class:`PWMLED` for the snow-man's eyes. 

1901 

1902 .. attribute:: nose 

1903 

1904 The :class:`LED` or :class:`PWMLED` for the snow-man's nose. 

1905 """ 

1906 def __init__(self, pwm=False, initial_value=False, pin_factory=None): 

1907 super(SnowPi, self).__init__( 

1908 arms=LEDBoard( 

1909 left=LEDBoard( 

1910 top='BOARD11', middle='BOARD12', bottom='BOARD15', 

1911 pwm=pwm, initial_value=initial_value, 

1912 _order=('top', 'middle', 'bottom'), 

1913 pin_factory=pin_factory), 

1914 right=LEDBoard( 

1915 top='BOARD26', middle='BOARD24', bottom='BOARD21', 

1916 pwm=pwm, initial_value=initial_value, 

1917 _order=('top', 'middle', 'bottom'), 

1918 pin_factory=pin_factory), 

1919 _order=('left', 'right'), 

1920 pin_factory=pin_factory 

1921 ), 

1922 eyes=LEDBoard( 

1923 left='BOARD16', right='BOARD18', 

1924 pwm=pwm, initial_value=initial_value, 

1925 _order=('left', 'right'), 

1926 pin_factory=pin_factory 

1927 ), 

1928 nose='BOARD22', 

1929 pwm=pwm, initial_value=initial_value, 

1930 _order=('eyes', 'nose', 'arms'), 

1931 pin_factory=pin_factory 

1932 ) 

1933 

1934 

1935class TrafficLightsBuzzer(CompositeOutputDevice): 

1936 """ 

1937 Extends :class:`CompositeOutputDevice` and is a generic class for HATs with 

1938 traffic lights, a button and a buzzer. 

1939 

1940 :param TrafficLights lights: 

1941 An instance of :class:`TrafficLights` representing the traffic lights 

1942 of the HAT. 

1943 

1944 :param Buzzer buzzer: 

1945 An instance of :class:`Buzzer` representing the buzzer on the HAT. 

1946 

1947 :param Button button: 

1948 An instance of :class:`Button` representing the button on the HAT. 

1949 

1950 :type pin_factory: Factory or None 

1951 :param pin_factory: 

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

1953 which most users can ignore). 

1954 

1955 .. attribute:: lights 

1956 

1957 The :class:`TrafficLights` instance passed as the *lights* parameter. 

1958 

1959 .. attribute:: buzzer 

1960 

1961 The :class:`Buzzer` instance passed as the *buzzer* parameter. 

1962 

1963 .. attribute:: button 

1964 

1965 The :class:`Button` instance passed as the *button* parameter. 

1966 """ 

1967 def __init__(self, lights, buzzer, button, pin_factory=None): 

1968 super(TrafficLightsBuzzer, self).__init__( 

1969 lights=lights, buzzer=buzzer, button=button, 

1970 _order=('lights', 'buzzer', 'button'), 

1971 pin_factory=pin_factory 

1972 ) 

1973 

1974 

1975class FishDish(CompositeOutputDevice): 

1976 """ 

1977 Extends :class:`CompositeOutputDevice` for the `Pi Supply FishDish`_: traffic 

1978 light LEDs, a button and a buzzer. 

1979 

1980 The FishDish pins are fixed and therefore there's no need to specify them 

1981 when constructing this class. The following example waits for the button 

1982 to be pressed on the FishDish, then turns on all the LEDs:: 

1983 

1984 from gpiozero import FishDish 

1985 

1986 fish = FishDish() 

1987 fish.button.wait_for_press() 

1988 fish.lights.on() 

1989 

1990 :param bool pwm: 

1991 If :data:`True`, construct :class:`PWMLED` instances to represent each 

1992 LED. If :data:`False` (the default), construct regular :class:`LED` 

1993 instances. 

1994 

1995 :type pin_factory: Factory or None 

1996 :param pin_factory: 

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

1998 which most users can ignore). 

1999 

2000 .. _Pi Supply FishDish: https://www.pi-supply.com/product/fish-dish-raspberry-pi-led-buzzer-board/ 

2001 """ 

2002 def __init__(self, pwm=False, pin_factory=None): 

2003 super(FishDish, self).__init__( 

2004 lights=TrafficLights( 

2005 'BOARD21', 'BOARD15', 'BOARD7', pwm=pwm, pin_factory=pin_factory 

2006 ), 

2007 buzzer=Buzzer('BOARD24', pin_factory=pin_factory), 

2008 button=Button('BOARD26', pull_up=False, pin_factory=pin_factory), 

2009 _order=('lights', 'buzzer', 'button'), 

2010 pin_factory=pin_factory 

2011 ) 

2012 

2013 

2014class TrafficHat(CompositeOutputDevice): 

2015 """ 

2016 Extends :class:`CompositeOutputDevice` for the `Pi Supply Traffic HAT`_: a 

2017 board with traffic light LEDs, a button and a buzzer. 

2018 

2019 The Traffic HAT pins are fixed and therefore there's no need to specify 

2020 them when constructing this class. The following example waits for the 

2021 button to be pressed on the Traffic HAT, then turns on all the LEDs:: 

2022 

2023 from gpiozero import TrafficHat 

2024 

2025 hat = TrafficHat() 

2026 hat.button.wait_for_press() 

2027 hat.lights.on() 

2028 

2029 :param bool pwm: 

2030 If :data:`True`, construct :class:`PWMLED` instances to represent each 

2031 LED. If :data:`False` (the default), construct regular :class:`LED` 

2032 instances. 

2033 

2034 :type pin_factory: Factory or None 

2035 :param pin_factory: 

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

2037 which most users can ignore). 

2038 

2039 .. _Pi Supply Traffic HAT: https://uk.pi-supply.com/products/traffic-hat-for-raspberry-pi 

2040 """ 

2041 def __init__(self, pwm=False, pin_factory=None): 

2042 super(TrafficHat, self).__init__( 

2043 lights=TrafficLights( 

2044 'BOARD18', 'BOARD16', 'BOARD15', 

2045 pwm=pwm, pin_factory=pin_factory 

2046 ), 

2047 buzzer=Buzzer('BOARD29', pin_factory=pin_factory), 

2048 button=Button('BOARD22', pin_factory=pin_factory), 

2049 _order=('lights', 'buzzer', 'button'), 

2050 pin_factory=pin_factory 

2051 ) 

2052 

2053 

2054class TrafficpHat(TrafficLights): 

2055 """ 

2056 Extends :class:`TrafficLights` for the `Pi Supply Traffic pHAT`_: a small 

2057 board with traffic light LEDs. 

2058 

2059 The Traffic pHAT pins are fixed and therefore there's no need to specify 

2060 them when constructing this class. The following example then turns on all 

2061 the LEDs:: 

2062 

2063 from gpiozero import TrafficpHat 

2064 phat = TrafficpHat() 

2065 phat.red.on() 

2066 phat.blink() 

2067 

2068 :param bool pwm: 

2069 If :data:`True`, construct :class:`PWMLED` instances to represent each 

2070 LED. If :data:`False` (the default), construct regular :class:`LED` 

2071 instances. 

2072 

2073 :type initial_value: bool or None 

2074 :param initial_value: 

2075 If :data:`False` (the default), all LEDs will be off initially. If 

2076 :data:`None`, each device will be left in whatever state the pin is 

2077 found in when configured for output (warning: this can be on). If 

2078 :data:`True`, the device will be switched on initially. 

2079 

2080 :type pin_factory: Factory or None 

2081 :param pin_factory: 

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

2083 which most users can ignore). 

2084 

2085 .. _Pi Supply Traffic pHAT: http://pisupp.ly/trafficphat 

2086 """ 

2087 def __init__(self, pwm=False, initial_value=False, pin_factory=None): 

2088 super(TrafficpHat, self).__init__( 

2089 red='BOARD22', amber='BOARD18', green='BOARD16', 

2090 pwm=pwm, initial_value=initial_value, pin_factory=pin_factory 

2091 ) 

2092 

2093 

2094class Robot(SourceMixin, CompositeDevice): 

2095 """ 

2096 Extends :class:`CompositeDevice` to represent a generic dual-motor robot. 

2097 

2098 This class is constructed with two tuples representing the forward and 

2099 backward pins of the left and right controllers respectively. For example, 

2100 if the left motor's controller is connected to GPIOs 4 and 14, while the 

2101 right motor's controller is connected to GPIOs 17 and 18 then the following 

2102 example will drive the robot forward:: 

2103 

2104 from gpiozero import Robot 

2105 

2106 robot = Robot(left=(4, 14), right=(17, 18)) 

2107 robot.forward() 

2108 

2109 :param tuple left: 

2110 A tuple of two (or three) GPIO pins representing the forward and 

2111 backward inputs of the left motor's controller. Use three pins if your 

2112 motor controller requires an enable pin. 

2113 

2114 :param tuple right: 

2115 A tuple of two (or three) GPIO pins representing the forward and 

2116 backward inputs of the right motor's controller. Use three pins if your 

2117 motor controller requires an enable pin. 

2118 

2119 :param bool pwm: 

2120 If :data:`True` (the default), construct :class:`PWMOutputDevice` 

2121 instances for the motor controller pins, allowing both direction and 

2122 variable speed control. If :data:`False`, construct 

2123 :class:`DigitalOutputDevice` instances, allowing only direction 

2124 control. 

2125 

2126 :type pin_factory: Factory or None 

2127 :param pin_factory: 

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

2129 which most users can ignore). 

2130 

2131 .. attribute:: left_motor 

2132 

2133 The :class:`Motor` on the left of the robot. 

2134 

2135 .. attribute:: right_motor 

2136 

2137 The :class:`Motor` on the right of the robot. 

2138 """ 

2139 def __init__(self, left=None, right=None, pwm=True, pin_factory=None, *args): 

2140 # *args is a hack to ensure a useful message is shown when pins are 

2141 # supplied as sequential positional arguments e.g. 2, 3, 4, 5 

2142 if not isinstance(left, tuple) or not isinstance(right, tuple): 

2143 raise GPIOPinMissing('left and right motor pins must be given as ' 

2144 'tuples') 

2145 super(Robot, self).__init__( 

2146 left_motor=Motor(*left, pwm=pwm, pin_factory=pin_factory), 

2147 right_motor=Motor(*right, pwm=pwm, pin_factory=pin_factory), 

2148 _order=('left_motor', 'right_motor'), 

2149 pin_factory=pin_factory 

2150 ) 

2151 

2152 @property 

2153 def value(self): 

2154 """ 

2155 Represents the motion of the robot as a tuple of (left_motor_speed, 

2156 right_motor_speed) with ``(-1, -1)`` representing full speed backwards, 

2157 ``(1, 1)`` representing full speed forwards, and ``(0, 0)`` 

2158 representing stopped. 

2159 """ 

2160 return super(Robot, self).value 

2161 

2162 @value.setter 

2163 def value(self, value): 

2164 self.left_motor.value, self.right_motor.value = value 

2165 

2166 def forward(self, speed=1, **kwargs): 

2167 """ 

2168 Drive the robot forward by running both motors forward. 

2169 

2170 :param float speed: 

2171 Speed at which to drive the motors, as a value between 0 (stopped) 

2172 and 1 (full speed). The default is 1. 

2173 

2174 :param float curve_left: 

2175 The amount to curve left while moving forwards, by driving the 

2176 left motor at a slower speed. Maximum *curve_left* is 1, the 

2177 default is 0 (no curve). This parameter can only be specified as a 

2178 keyword parameter, and is mutually exclusive with *curve_right*. 

2179 

2180 :param float curve_right: 

2181 The amount to curve right while moving forwards, by driving the 

2182 right motor at a slower speed. Maximum *curve_right* is 1, the 

2183 default is 0 (no curve). This parameter can only be specified as a 

2184 keyword parameter, and is mutually exclusive with *curve_left*. 

2185 """ 

2186 curve_left = kwargs.pop('curve_left', 0) 

2187 curve_right = kwargs.pop('curve_right', 0) 

2188 if kwargs: 

2189 raise TypeError('unexpected argument %s' % kwargs.popitem()[0]) 

2190 if not 0 <= curve_left <= 1: 

2191 raise ValueError('curve_left must be between 0 and 1') 

2192 if not 0 <= curve_right <= 1: 

2193 raise ValueError('curve_right must be between 0 and 1') 

2194 if curve_left != 0 and curve_right != 0: 

2195 raise ValueError("curve_left and curve_right can't be used at " 

2196 "the same time") 

2197 self.left_motor.forward(speed * (1 - curve_left)) 

2198 self.right_motor.forward(speed * (1 - curve_right)) 

2199 

2200 def backward(self, speed=1, **kwargs): 

2201 """ 

2202 Drive the robot backward by running both motors backward. 

2203 

2204 :param float speed: 

2205 Speed at which to drive the motors, as a value between 0 (stopped) 

2206 and 1 (full speed). The default is 1. 

2207 

2208 :param float curve_left: 

2209 The amount to curve left while moving backwards, by driving the 

2210 left motor at a slower speed. Maximum *curve_left* is 1, the 

2211 default is 0 (no curve). This parameter can only be specified as a 

2212 keyword parameter, and is mutually exclusive with *curve_right*. 

2213 

2214 :param float curve_right: 

2215 The amount to curve right while moving backwards, by driving the 

2216 right motor at a slower speed. Maximum *curve_right* is 1, the 

2217 default is 0 (no curve). This parameter can only be specified as a 

2218 keyword parameter, and is mutually exclusive with *curve_left*. 

2219 """ 

2220 curve_left = kwargs.pop('curve_left', 0) 

2221 curve_right = kwargs.pop('curve_right', 0) 

2222 if kwargs: 

2223 raise TypeError('unexpected argument %s' % kwargs.popitem()[0]) 

2224 if not 0 <= curve_left <= 1: 

2225 raise ValueError('curve_left must be between 0 and 1') 

2226 if not 0 <= curve_right <= 1: 

2227 raise ValueError('curve_right must be between 0 and 1') 

2228 if curve_left != 0 and curve_right != 0: 

2229 raise ValueError("curve_left and curve_right can't be used at " 

2230 "the same time") 

2231 self.left_motor.backward(speed * (1 - curve_left)) 

2232 self.right_motor.backward(speed * (1 - curve_right)) 

2233 

2234 def left(self, speed=1): 

2235 """ 

2236 Make the robot turn left by running the right motor forward and left 

2237 motor backward. 

2238 

2239 :param float speed: 

2240 Speed at which to drive the motors, as a value between 0 (stopped) 

2241 and 1 (full speed). The default is 1. 

2242 """ 

2243 self.right_motor.forward(speed) 

2244 self.left_motor.backward(speed) 

2245 

2246 def right(self, speed=1): 

2247 """ 

2248 Make the robot turn right by running the left motor forward and right 

2249 motor backward. 

2250 

2251 :param float speed: 

2252 Speed at which to drive the motors, as a value between 0 (stopped) 

2253 and 1 (full speed). The default is 1. 

2254 """ 

2255 self.left_motor.forward(speed) 

2256 self.right_motor.backward(speed) 

2257 

2258 def reverse(self): 

2259 """ 

2260 Reverse the robot's current motor directions. If the robot is currently 

2261 running full speed forward, it will run full speed backward. If the 

2262 robot is turning left at half-speed, it will turn right at half-speed. 

2263 If the robot is currently stopped it will remain stopped. 

2264 """ 

2265 self.left_motor.reverse() 

2266 self.right_motor.reverse() 

2267 

2268 def stop(self): 

2269 """ 

2270 Stop the robot. 

2271 """ 

2272 self.left_motor.stop() 

2273 self.right_motor.stop() 

2274 

2275 

2276class RyanteckRobot(Robot): 

2277 """ 

2278 Extends :class:`Robot` for the `Ryanteck motor controller board`_. 

2279 

2280 The Ryanteck MCB pins are fixed and therefore there's no need to specify 

2281 them when constructing this class. The following example drives the robot 

2282 forward:: 

2283 

2284 from gpiozero import RyanteckRobot 

2285 

2286 robot = RyanteckRobot() 

2287 robot.forward() 

2288 

2289 :param bool pwm: 

2290 If :data:`True` (the default), construct :class:`PWMOutputDevice` 

2291 instances for the motor controller pins, allowing both direction and 

2292 variable speed control. If :data:`False`, construct 

2293 :class:`DigitalOutputDevice` instances, allowing only direction 

2294 control. 

2295 

2296 :type pin_factory: Factory or None 

2297 :param pin_factory: 

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

2299 which most users can ignore). 

2300 

2301 .. _Ryanteck motor controller board: https://uk.pi-supply.com/products/ryanteck-rtk-000-001-motor-controller-board-kit-raspberry-pi 

2302 """ 

2303 

2304 def __init__(self, pwm=True, pin_factory=None): 

2305 super(RyanteckRobot, self).__init__( 

2306 left=('BOARD11', 'BOARD12'), right=('BOARD15', 'BOARD16'), 

2307 pwm=pwm, pin_factory=pin_factory 

2308 ) 

2309 

2310 

2311class CamJamKitRobot(Robot): 

2312 """ 

2313 Extends :class:`Robot` for the `CamJam #3 EduKit`_ motor controller board. 

2314 

2315 The CamJam robot controller pins are fixed and therefore there's no need 

2316 to specify them when constructing this class. The following example drives 

2317 the robot forward:: 

2318 

2319 from gpiozero import CamJamKitRobot 

2320 

2321 robot = CamJamKitRobot() 

2322 robot.forward() 

2323 

2324 :param bool pwm: 

2325 If :data:`True` (the default), construct :class:`PWMOutputDevice` 

2326 instances for the motor controller pins, allowing both direction and 

2327 variable speed control. If :data:`False`, construct 

2328 :class:`DigitalOutputDevice` instances, allowing only direction 

2329 control. 

2330 

2331 :type pin_factory: Factory or None 

2332 :param pin_factory: 

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

2334 which most users can ignore). 

2335 

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

2337 """ 

2338 def __init__(self, pwm=True, pin_factory=None): 

2339 super(CamJamKitRobot, self).__init__( 

2340 left=('BOARD21', 'BOARD19'), right=('BOARD26', 'BOARD24'), 

2341 pwm=pwm, pin_factory=pin_factory 

2342 ) 

2343 

2344 

2345class PhaseEnableRobot(SourceMixin, CompositeDevice): 

2346 """ 

2347 Extends :class:`CompositeDevice` to represent a dual-motor robot based 

2348 around a Phase/Enable motor board. 

2349 

2350 This class is constructed with two tuples representing the phase 

2351 (direction) and enable (speed) pins of the left and right controllers 

2352 respectively. For example, if the left motor's controller is connected to 

2353 GPIOs 12 and 5, while the right motor's controller is connected to GPIOs 13 

2354 and 6 so the following example will drive the robot forward:: 

2355 

2356 from gpiozero import PhaseEnableRobot 

2357 

2358 robot = PhaseEnableRobot(left=(5, 12), right=(6, 13)) 

2359 robot.forward() 

2360 

2361 :param tuple left: 

2362 A tuple of two GPIO pins representing the phase and enable inputs 

2363 of the left motor's controller. 

2364 

2365 :param tuple right: 

2366 A tuple of two GPIO pins representing the phase and enable inputs 

2367 of the right motor's controller. 

2368 

2369 :param bool pwm: 

2370 If :data:`True` (the default), construct :class:`PWMOutputDevice` 

2371 instances for the motor controller's enable pins, allowing both 

2372 direction and variable speed control. If :data:`False`, construct 

2373 :class:`DigitalOutputDevice` instances, allowing only direction 

2374 control. 

2375 

2376 :type pin_factory: Factory or None 

2377 :param pin_factory: 

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

2379 which most users can ignore). 

2380 

2381 .. attribute:: left_motor 

2382 

2383 The :class:`PhaseEnableMotor` on the left of the robot. 

2384 

2385 .. attribute:: right_motor 

2386 

2387 The :class:`PhaseEnableMotor` on the right of the robot. 

2388 """ 

2389 def __init__(self, left=None, right=None, pwm=True, pin_factory=None, *args): 

2390 # *args is a hack to ensure a useful message is shown when pins are 

2391 # supplied as sequential positional arguments e.g. 2, 3, 4, 5 

2392 if not isinstance(left, tuple) or not isinstance(right, tuple): 

2393 raise GPIOPinMissing( 

2394 'left and right motor pins must be given as tuples' 

2395 ) 

2396 super(PhaseEnableRobot, self).__init__( 

2397 left_motor=PhaseEnableMotor(*left, pwm=pwm, pin_factory=pin_factory), 

2398 right_motor=PhaseEnableMotor(*right, pwm=pwm, pin_factory=pin_factory), 

2399 _order=('left_motor', 'right_motor'), 

2400 pin_factory=pin_factory 

2401 ) 

2402 

2403 @property 

2404 def value(self): 

2405 """ 

2406 Returns a tuple of two floating point values (-1 to 1) representing the 

2407 speeds of the robot's two motors (left and right). This property can 

2408 also be set to alter the speed of both motors. 

2409 """ 

2410 return super(PhaseEnableRobot, self).value 

2411 

2412 @value.setter 

2413 def value(self, value): 

2414 self.left_motor.value, self.right_motor.value = value 

2415 

2416 def forward(self, speed=1): 

2417 """ 

2418 Drive the robot forward by running both motors forward. 

2419 

2420 :param float speed: 

2421 Speed at which to drive the motors, as a value between 0 (stopped) 

2422 and 1 (full speed). The default is 1. 

2423 """ 

2424 self.left_motor.forward(speed) 

2425 self.right_motor.forward(speed) 

2426 

2427 def backward(self, speed=1): 

2428 """ 

2429 Drive the robot backward by running both motors backward. 

2430 

2431 :param float speed: 

2432 Speed at which to drive the motors, as a value between 0 (stopped) 

2433 and 1 (full speed). The default is 1. 

2434 """ 

2435 self.left_motor.backward(speed) 

2436 self.right_motor.backward(speed) 

2437 

2438 def left(self, speed=1): 

2439 """ 

2440 Make the robot turn left by running the right motor forward and left 

2441 motor backward. 

2442 

2443 :param float speed: 

2444 Speed at which to drive the motors, as a value between 0 (stopped) 

2445 and 1 (full speed). The default is 1. 

2446 """ 

2447 self.right_motor.forward(speed) 

2448 self.left_motor.backward(speed) 

2449 

2450 def right(self, speed=1): 

2451 """ 

2452 Make the robot turn right by running the left motor forward and right 

2453 motor backward. 

2454 

2455 :param float speed: 

2456 Speed at which to drive the motors, as a value between 0 (stopped) 

2457 and 1 (full speed). The default is 1. 

2458 """ 

2459 self.left_motor.forward(speed) 

2460 self.right_motor.backward(speed) 

2461 

2462 def reverse(self): 

2463 """ 

2464 Reverse the robot's current motor directions. If the robot is currently 

2465 running full speed forward, it will run full speed backward. If the 

2466 robot is turning left at half-speed, it will turn right at half-speed. 

2467 If the robot is currently stopped it will remain stopped. 

2468 """ 

2469 self.left_motor.value = -self.left_motor.value 

2470 self.right_motor.value = -self.right_motor.value 

2471 

2472 def stop(self): 

2473 """ 

2474 Stop the robot. 

2475 """ 

2476 self.left_motor.stop() 

2477 self.right_motor.stop() 

2478 

2479 

2480class PololuDRV8835Robot(PhaseEnableRobot): 

2481 """ 

2482 Extends :class:`PhaseEnableRobot` for the `Pololu DRV8835 Dual Motor Driver 

2483 Kit`_. 

2484 

2485 The Pololu DRV8835 pins are fixed and therefore there's no need to specify 

2486 them when constructing this class. The following example drives the robot 

2487 forward:: 

2488 

2489 from gpiozero import PololuDRV8835Robot 

2490 

2491 robot = PololuDRV8835Robot() 

2492 robot.forward() 

2493 

2494 :param bool pwm: 

2495 If :data:`True` (the default), construct :class:`PWMOutputDevice` 

2496 instances for the motor controller's enable pins, allowing both 

2497 direction and variable speed control. If :data:`False`, construct 

2498 :class:`DigitalOutputDevice` instances, allowing only direction 

2499 control. 

2500 

2501 :type pin_factory: Factory or None 

2502 :param pin_factory: 

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

2504 which most users can ignore). 

2505 

2506 .. _Pololu DRV8835 Dual Motor Driver Kit: https://www.pololu.com/product/2753 

2507 """ 

2508 def __init__(self, pwm=True, pin_factory=None): 

2509 super(PololuDRV8835Robot, self).__init__( 

2510 left=('BOARD29', 'BOARD32'), right=('BOARD31', 'BOARD33'), 

2511 pwm=pwm, pin_factory=pin_factory 

2512 ) 

2513 

2514 

2515class _EnergenieMaster(SharedMixin, CompositeOutputDevice): 

2516 def __init__(self, pin_factory=None): 

2517 self._lock = Lock() 

2518 super(_EnergenieMaster, self).__init__( 

2519 *( 

2520 OutputDevice(pin, pin_factory=pin_factory) 

2521 for pin in ('BOARD11', 'BOARD15', 'BOARD16', 'BOARD13') 

2522 ), 

2523 mode=OutputDevice('BOARD18', pin_factory=pin_factory), 

2524 enable=OutputDevice('BOARD22', pin_factory=pin_factory), 

2525 _order=('mode', 'enable'), pin_factory=pin_factory 

2526 ) 

2527 

2528 def close(self): 

2529 if getattr(self, '_lock', None): 

2530 with self._lock: 

2531 super(_EnergenieMaster, self).close() 

2532 self._lock = None 

2533 

2534 @classmethod 

2535 def _shared_key(cls, pin_factory): 

2536 # There's only one Energenie master 

2537 return None 

2538 

2539 def transmit(self, socket, enable): 

2540 with self._lock: 

2541 try: 

2542 code = (8 * bool(enable)) + (8 - socket) 

2543 for bit in self[:4]: 

2544 bit.value = (code & 1) 

2545 code >>= 1 

2546 sleep(0.1) 

2547 self.enable.on() 

2548 sleep(0.25) 

2549 finally: 

2550 self.enable.off() 

2551 

2552 

2553class Energenie(SourceMixin, Device): 

2554 """ 

2555 Extends :class:`Device` to represent an `Energenie socket`_ controller. 

2556 

2557 This class is constructed with a socket number and an optional initial 

2558 state (defaults to :data:`False`, meaning off). Instances of this class can 

2559 be used to switch peripherals on and off. For example:: 

2560 

2561 from gpiozero import Energenie 

2562 

2563 lamp = Energenie(1) 

2564 lamp.on() 

2565 

2566 :param int socket: 

2567 Which socket this instance should control. This is an integer number 

2568 between 1 and 4. 

2569 

2570 :type initial_value: bool or None 

2571 :param initial_value: 

2572 The initial state of the socket. As Energenie sockets provide no 

2573 means of reading their state, you may provide an initial state for 

2574 the socket, which will be set upon construction. This defaults to 

2575 :data:`False` which will switch the socket off. 

2576 Specifying :data:`None` will not set any initial state nor transmit any 

2577 control signal to the device. 

2578 

2579 :type pin_factory: Factory or None 

2580 :param pin_factory: 

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

2582 which most users can ignore). 

2583 

2584 .. _Energenie socket: https://energenie4u.co.uk/index.php/catalogue/product/ENER002-2PI 

2585 """ 

2586 def __init__(self, socket=None, initial_value=False, pin_factory=None): 

2587 if socket is None: 

2588 raise EnergenieSocketMissing('socket number must be provided') 

2589 if not (1 <= socket <= 4): 

2590 raise EnergenieBadSocket('socket number must be between 1 and 4') 

2591 self._value = None 

2592 super(Energenie, self).__init__(pin_factory=pin_factory) 

2593 self._socket = socket 

2594 self._master = _EnergenieMaster(pin_factory=pin_factory) 

2595 if initial_value: 

2596 self.on() 

2597 elif initial_value is not None: 

2598 self.off() 

2599 

2600 def close(self): 

2601 if getattr(self, '_master', None): 

2602 self._master.close() 

2603 self._master = None 

2604 

2605 @property 

2606 def closed(self): 

2607 return self._master is None 

2608 

2609 def __repr__(self): 

2610 try: 

2611 self._check_open() 

2612 return "<gpiozero.Energenie object on socket %d>" % self._socket 

2613 except DeviceClosed: 

2614 return "<gpiozero.Energenie object closed>" 

2615 

2616 @property 

2617 def socket(self): 

2618 """ 

2619 Returns the socket number. 

2620 """ 

2621 return self._socket 

2622 

2623 @property 

2624 def value(self): 

2625 """ 

2626 Returns :data:`True` if the socket is on and :data:`False` if the 

2627 socket is off. Setting this property changes the state of the socket. 

2628 Returns :data:`None` only when constructed with :data:`initial_value` 

2629 set to :data:`None` and neither :data:`on()` nor :data:`off()` have 

2630 been called since construction. 

2631 """ 

2632 return self._value 

2633 

2634 @value.setter 

2635 def value(self, value): 

2636 if value is None: 

2637 raise TypeError('value cannot be None') 

2638 value = bool(value) 

2639 self._master.transmit(self._socket, value) 

2640 self._value = value 

2641 

2642 def on(self): 

2643 """ 

2644 Turns the socket on. 

2645 """ 

2646 self.value = True 

2647 

2648 def off(self): 

2649 """ 

2650 Turns the socket off. 

2651 """ 

2652 self.value = False 

2653 

2654 

2655class PumpkinPi(LEDBoard): 

2656 """ 

2657 Extends :class:`LEDBoard` for the `ModMyPi PumpkinPi`_ board. 

2658 

2659 There are twelve LEDs connected up to individual pins, so for the PumpkinPi 

2660 the pins are fixed. For example:: 

2661 

2662 from gpiozero import PumpkinPi 

2663 

2664 pumpkin = PumpkinPi(pwm=True) 

2665 pumpkin.sides.pulse() 

2666 pumpkin.off() 

2667 

2668 :param bool pwm: 

2669 If :data:`True`, construct :class:`PWMLED` instances to represent each 

2670 LED. If :data:`False` (the default), construct regular :class:`LED` 

2671 instances 

2672 

2673 :type initial_value: bool or None 

2674 :param initial_value: 

2675 If :data:`False` (the default), all LEDs will be off initially. If 

2676 :data:`None`, each device will be left in whatever state the pin is 

2677 found in when configured for output (warning: this can be on). If 

2678 :data:`True`, the device will be switched on initially. 

2679 

2680 :type pin_factory: Factory or None 

2681 :param pin_factory: 

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

2683 which most users can ignore). 

2684 

2685 .. _ModMyPi PumpkinPi: https://www.modmypi.com/halloween-pumpkin-programmable-kit 

2686 

2687 .. attribute:: sides 

2688 

2689 A :class:`LEDBoard` representing the LEDs around the edge of the 

2690 pumpkin. It contains: 

2691 

2692 .. attribute:: left, right 

2693 

2694 Two :class:`LEDBoard` instances representing the LEDs on the left 

2695 and right sides of the pumpkin. They each contain: 

2696 

2697 .. attribute:: top, midtop, middle, midbottom, bottom 

2698 

2699 Each :class:`LED` or :class:`PWMLED` around the specified side 

2700 of the pumpkin. 

2701 

2702 .. attribute:: eyes 

2703 

2704 A :class:`LEDBoard` representing the eyes of the pumpkin. It contains: 

2705 

2706 .. attribute:: left, right 

2707 

2708 The :class:`LED` or :class:`PWMLED` for each of the pumpkin's eyes. 

2709 """ 

2710 def __init__(self, pwm=False, initial_value=False, pin_factory=None): 

2711 super(PumpkinPi, self).__init__( 

2712 sides=LEDBoard( 

2713 left=LEDBoard( 

2714 bottom='BOARD12', midbottom='BOARD11', middle='BOARD36', 

2715 midtop='BOARD33', top='BOARD18', 

2716 pwm=pwm, initial_value=initial_value, 

2717 _order=('bottom', 'midbottom', 'middle', 'midtop', 'top'), 

2718 pin_factory=pin_factory), 

2719 right=LEDBoard( 

2720 bottom='BOARD35', midbottom='BOARD38', middle='BOARD40', 

2721 midtop='BOARD15', top='BOARD16', 

2722 pwm=pwm, initial_value=initial_value, 

2723 _order=('bottom', 'midbottom', 'middle', 'midtop', 'top'), 

2724 pin_factory=pin_factory), 

2725 pwm=pwm, initial_value=initial_value, 

2726 _order=('left', 'right'), 

2727 pin_factory=pin_factory 

2728 ), 

2729 eyes=LEDBoard( 

2730 left='BOARD32', right='BOARD31', 

2731 pwm=pwm, initial_value=initial_value, 

2732 _order=('left', 'right'), 

2733 pin_factory=pin_factory 

2734 ), 

2735 pwm=pwm, initial_value=initial_value, 

2736 _order=('eyes', 'sides'), 

2737 pin_factory=pin_factory 

2738 ) 

2739 

2740 

2741class JamHat(CompositeOutputDevice): 

2742 """ 

2743 Extends :class:`CompositeOutputDevice` for the `ModMyPi JamHat`_ board. 

2744 

2745 There are 6 LEDs, two buttons and a tonal buzzer. The pins are fixed. 

2746 Usage:: 

2747 

2748 from gpiozero import JamHat 

2749 

2750 hat = JamHat() 

2751 

2752 hat.button_1.wait_for_press() 

2753 hat.lights_1.on() 

2754 hat.buzzer.play('C4') 

2755 hat.button_2.wait_for_press() 

2756 hat.off() 

2757 

2758 :param bool pwm: 

2759 If :data:`True`, construct :class:`PWMLED` instances to represent each 

2760 LED on the board. If :data:`False` (the default), construct regular 

2761 :class:`LED` instances. 

2762 

2763 :type pin_factory: Factory or None 

2764 :param pin_factory: 

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

2766 which most users can ignore). 

2767 

2768 .. _ModMyPi JamHat: https://thepihut.com/products/jam-hat 

2769 

2770 .. attribute:: lights_1, lights_2 

2771 

2772 Two :class:`LEDBoard` instances representing the top (lights_1) and 

2773 bottom (lights_2) rows of LEDs on the JamHat. 

2774 

2775 .. attribute:: red, yellow, green 

2776 

2777 :class:`LED` or :class:`PWMLED` instances representing the red, 

2778 yellow, and green LEDs along the top row. 

2779 

2780 .. attribute:: button_1, button_2 

2781 

2782 The left (button_1) and right (button_2) :class:`Button` objects on the 

2783 JamHat. 

2784 

2785 .. attribute:: buzzer 

2786 

2787 The :class:`TonalBuzzer` at the bottom right of the JamHat. 

2788 """ 

2789 def __init__(self, pwm=False, pin_factory=None): 

2790 super(JamHat, self).__init__( 

2791 lights_1=LEDBoard( 

2792 red='BOARD29', yellow='BOARD32', green='BOARD36', 

2793 pwm=pwm, _order=('red', 'yellow', 'green'), 

2794 pin_factory=pin_factory 

2795 ), 

2796 lights_2=LEDBoard( 

2797 red='BOARD31', yellow='BOARD33', green='BOARD11', 

2798 pwm=pwm, _order=('red', 'yellow', 'green'), 

2799 pin_factory=pin_factory), 

2800 button_1=Button('BOARD35', pull_up=False, pin_factory=pin_factory), 

2801 button_2=Button('BOARD12', pull_up=False, pin_factory=pin_factory), 

2802 buzzer=TonalBuzzer('BOARD38', pin_factory=pin_factory), 

2803 _order=('lights_1', 'lights_2', 'button_1', 'button_2', 'buzzer'), 

2804 pin_factory=pin_factory 

2805 ) 

2806 

2807 def on(self): 

2808 """ 

2809 Turns all the LEDs on and makes the buzzer play its mid tone. 

2810 """ 

2811 self.buzzer.value = 0 

2812 super(JamHat, self).on() 

2813 

2814 def off(self): 

2815 """ 

2816 Turns all the LEDs off and stops the buzzer. 

2817 """ 

2818 self.buzzer.value = None 

2819 super(JamHat, self).off() 

2820 

2821 

2822class Pibrella(CompositeOutputDevice): 

2823 """ 

2824 Extends :class:`CompositeOutputDevice` for the Cyntech/Pimoroni `Pibrella`_ 

2825 board. 

2826 

2827 The Pibrella board comprises 3 LEDs, a button, a tonal buzzer, four general 

2828 purpose input channels, and four general purpose output channels (with LEDs). 

2829 

2830 This class exposes the LEDs, button and buzzer. 

2831 

2832 Usage:: 

2833 

2834 from gpiozero import Pibrella 

2835 

2836 pb = Pibrella() 

2837 

2838 pb.button.wait_for_press() 

2839 pb.lights.on() 

2840 pb.buzzer.play('A4') 

2841 pb.off() 

2842 

2843 The four input and output channels are exposed so you can create GPIO Zero 

2844 devices using these pins without looking up their respective pin numbers:: 

2845 

2846 from gpiozero import Pibrella, LED, Button 

2847 

2848 pb = Pibrella() 

2849 btn = Button(pb.inputs.a, pull_up=False) 

2850 led = LED(pb.outputs.e) 

2851 

2852 btn.when_pressed = led.on 

2853 

2854 :param bool pwm: 

2855 If :data:`True`, construct :class:`PWMLED` instances to represent each 

2856 LED on the board, otherwise if :data:`False` (the default), construct 

2857 regular :class:`LED` instances. 

2858 

2859 :type pin_factory: Factory or None 

2860 :param pin_factory: 

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

2862 which most users can ignore). 

2863 

2864 .. _Pibrella: http://www.pibrella.com/ 

2865 

2866 .. attribute:: lights 

2867 

2868 :class:`TrafficLights` instance representing the three LEDs 

2869 

2870 .. attribute:: red, amber, green 

2871 

2872 :class:`LED` or :class:`PWMLED` instances representing the red, 

2873 amber, and green LEDs 

2874 

2875 .. attribute:: button 

2876 

2877 The red :class:`Button` object on the Pibrella 

2878 

2879 .. attribute:: buzzer 

2880 

2881 A :class:`TonalBuzzer` object representing the buzzer 

2882 

2883 .. attribute:: inputs 

2884 

2885 A :func:`~collections.namedtuple` of the input pin numbers 

2886 

2887 .. attribute:: a, b, c, d 

2888 

2889 .. attribute:: outputs 

2890 

2891 A :func:`~collections.namedtuple` of the output pin numbers 

2892 

2893 .. attribute:: e, f, g, h 

2894 """ 

2895 def __init__(self, pwm=False, pin_factory=None): 

2896 super(Pibrella, self).__init__( 

2897 lights=TrafficLights( 

2898 red='BOARD13', amber='BOARD11', green='BOARD7', 

2899 pwm=pwm, pin_factory=pin_factory 

2900 ), 

2901 button=Button('BOARD23', pull_up=False, pin_factory=pin_factory), 

2902 buzzer=TonalBuzzer('BOARD12', pin_factory=pin_factory), 

2903 _order=('lights', 'button', 'buzzer'), 

2904 pin_factory=pin_factory 

2905 ) 

2906 InputPins = namedtuple('InputPins', ['a', 'b', 'c', 'd']) 

2907 OutputPins = namedtuple('OutputPins', ['e', 'f', 'g', 'h']) 

2908 self.inputs = InputPins( 

2909 a='BOARD21', b='BOARD26', c='BOARD24', d='BOARD19' 

2910 ) 

2911 self.outputs = OutputPins( 

2912 e='BOARD15', f='BOARD16', g='BOARD18', h='BOARD22' 

2913 ) 

2914 

2915 def on(self): 

2916 """ 

2917 Turns all the LEDs on and makes the buzzer play its mid tone. 

2918 """ 

2919 self.buzzer.value = 0 

2920 super(Pibrella, self).on() 

2921 

2922 def off(self): 

2923 """ 

2924 Turns all the LEDs off and stops the buzzer. 

2925 """ 

2926 self.buzzer.value = None 

2927 super(Pibrella, self).off()