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
« 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
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
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
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
67def pairwise(it):
68 a, b = tee(it)
69 next(b, None)
70 return zip(a, b)
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.
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.
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.
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).
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 """
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()
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()
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()
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
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
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`::
153 from gpiozero import LEDBoard, ButtonBoard
154 from signal import pause
156 leds = LEDBoard(2, 3, 4, 5)
157 btns = ButtonBoard(6, 7, 8, 9)
158 leds.source = btns
160 pause()
162 Alternatively you could represent the number of pressed buttons with an
163 :class:`LEDBarGraph`::
165 from gpiozero import LEDBarGraph, ButtonBoard
166 from statistics import mean
167 from signal import pause
169 graph = LEDBarGraph(2, 3, 4, 5)
170 bb = ButtonBoard(6, 7, 8, 9)
171 graph.source = (mean(values) for values in bb.values)
173 pause()
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.
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.
190 :type active_state: bool or None
191 :param active_state:
192 See description under :class:`InputDevice` for more information.
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.
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``.
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.
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).
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
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
273 @property
274 def when_changed(self):
275 return self._when_changed
277 @when_changed.setter
278 def when_changed(self, value):
279 self._when_changed = self._wrap_callback(value)
281 def _fire_changed(self):
282 if self.when_changed:
283 self.when_changed()
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()
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
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)
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
355 @property
356 def active_high(self):
357 return self[0].active_high
360LEDCollection.is_lit = LEDCollection.is_active
363class LEDBoard(LEDCollection):
364 """
365 Extends :class:`LEDCollection` and represents a generic LED board or
366 collection of LEDs.
368 The following example turns on all the LEDs on a board containing 5 LEDs
369 attached to GPIO pins 2 through 6::
371 from gpiozero import LEDBoard
373 leds = LEDBoard(2, 3, 4, 5, 6)
374 leds.on()
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.
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.
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).
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.
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).
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)
418 def close(self):
419 try:
420 self._stop_blink()
421 except AttributeError:
422 pass
423 super(LEDBoard, self).close()
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::
431 from gpiozero import LEDBoard
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
440 If :meth:`blink` is currently active, it will be stopped first.
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()
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::
459 from gpiozero import LEDBoard
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
468 If :meth:`blink` is currently active, it will be stopped first.
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()
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::
487 from gpiozero import LEDBoard
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
495 If :meth:`blink` is currently active, it will be stopped first.
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()
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.
514 :param float on_time:
515 Number of seconds on. Defaults to 1 second.
517 :param float off_time:
518 Number of seconds off. Defaults to 1 second.
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).
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).
530 :type n: int or None
531 :param n:
532 Number of times to blink; :data:`None` (the default) means forever.
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
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)
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.
569 :param float fade_in_time:
570 Number of seconds to spend fading in. Defaults to 1.
572 :param float fade_out_time:
573 Number of seconds to spend fading out. Defaults to 1.
575 :type n: int or None
576 :param n:
577 Number of times to blink; :data:`None` (the default) means forever.
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)
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
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.
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::
633 from gpiozero import LEDBarGraph
634 from time import sleep
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()
643 As with all other output devices, :attr:`source` and :attr:`values` are
644 supported::
646 from gpiozero import LEDBarGraph, MCP3008
647 from signal import pause
649 graph = LEDBarGraph(2, 3, 4, 5, 6, pwm=True)
650 pot = MCP3008(channel=0)
652 graph.source = pot
654 pause()
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.
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.
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.
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.
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
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.
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::
716 from gpiozero import LEDBarGraph
718 graph = LEDBarGraph(12, 16, 19)
719 graph.value = 1/3
721 .. note::
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)
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)
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
762 @lit_count.setter
763 def lit_count(self, value):
764 self.value = value / len(self)
767class LEDCharFont(MutableMapping):
768 """
769 Contains a mapping of values to tuples of LED states.
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::
777 from gpiozero import LEDCharDisplay, LEDCharFont
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'
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).
794 .. note::
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`.
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()
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 ))
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
830 def __len__(self):
831 return len(self._map)
833 def __iter__(self):
834 return iter(self._map)
836 def __getitem__(self, char):
837 return self._map[char]
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)
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
863class LEDCharDisplay(LEDCollection):
864 """
865 Extends :class:`LEDCollection` for a multi-segment LED display.
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:
871 .. code-block:: text
873 a
874 ━━━━━
875 f ┃ ┃ b
876 ┃ g ┃
877 ━━━━━
878 e ┃ ┃ c
879 ┃ ┃
880 ━━━━━ • dp
881 d
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:
887 .. code-block:: text
889 a
890 ━━━━━
891 f ┃╲i┃j╱┃ b
892 ┃ ╲┃╱k┃
893 g━━ ━━h
894 e ┃ ╱┃╲n┃ c
895 ┃╱l┃m╲┃
896 ━━━━━ • dp
897 d
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`.
903 Instances of this class can be used to display characters or control
904 individual LEDs on the display. For example::
906 from gpiozero import LEDCharDisplay
908 char = LEDCharDisplay(4, 5, 6, 7, 8, 9, 10, active_high=False)
909 char.value = 'C'
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.
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.
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.
932 :type dp: int or str
933 :param dp:
934 If a decimal-point segment is present, specify it as this named
935 parameter.
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.
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.
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).
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).
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).
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')
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)
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
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.
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
1027 @font.setter
1028 def font(self, value):
1029 self._font = LEDCharFont(value)
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.
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`.
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
1067 @value.setter
1068 def value(self, value):
1069 for led, v in zip(self, self._parse_state(value)):
1070 led.value = v
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]
1084class LEDMultiCharDisplay(CompositeOutputDevice):
1085 """
1086 Wraps :class:`LEDCharDisplay` for multi-character `multiplexed`_ LED
1087 character displays.
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.
1094 .. warning::
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*.
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.
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.
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
1143 def close(self):
1144 try:
1145 self._stop_plex()
1146 except AttributeError:
1147 pass
1148 super(LEDMultiCharDisplay, self).close()
1150 def _stop_plex(self):
1151 if self._plex_thread:
1152 self._plex_thread.stop()
1153 self._plex_thread = None
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
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)
1171 @property
1172 def value(self):
1173 """
1174 The sequence of values to display.
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)::
1182 from gpiozero import LEDCharDisplay, LEDMultiCharDisplay
1184 c = LEDCharDisplay(4, 5, 6, 7, 8, 9, 10)
1185 d = LEDMultiCharDisplay(c, 19, 20, 21, 22)
1186 d.value = 'LEDS'
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::
1193 from gpiozero import LEDCharDisplay, LEDMultiCharDisplay
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')
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.
1203 .. note::
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
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)
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 = []
1252 # Stop any current display thread and disable the display
1253 self._stop_plex()
1254 self.plex.off()
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
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
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.
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`).
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::
1294 from gpiozero import PiHutXmasTree
1295 from time import sleep
1297 tree = PiHutXmasTree()
1299 for light in tree:
1300 light.on()
1301 sleep(1)
1303 The following example turns the star LED on and sets all the red LEDs to
1304 flicker randomly::
1306 from gpiozero import PiHutXmasTree
1307 from gpiozero.tools import random_values
1308 from signal import pause
1310 tree = PiHutXmasTree(pwm=True)
1312 tree.star.on()
1314 for led in tree[1:]:
1315 led.source_delay = 0.1
1316 led.source = random_values()
1318 pause()
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.
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.
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).
1336 .. _The Pi Hut's Xmas board: https://thepihut.com/xmas
1338 .. attribute:: star
1340 Returns the :class:`LED` or :class:`PWMLED` representing the white
1341 star on top of the tree.
1343 .. attribute:: led0, led1, led2, ...
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 )
1364class LedBorg(RGBLED):
1365 """
1366 Extends :class:`RGBLED` for the `PiBorg LedBorg`_: an add-on board
1367 containing a very bright RGB LED.
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::
1373 from gpiozero import LedBorg
1375 led = LedBorg()
1376 led.color = (1, 0, 1)
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)``.
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.
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).
1392 .. _PiBorg LedBorg: https://www.piborg.org/ledborg
1393 """
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 )
1402class PiLiter(LEDBoard):
1403 """
1404 Extends :class:`LEDBoard` for the `Ciseco Pi-LITEr`_: a strip of 8 very
1405 bright LEDs.
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::
1411 from gpiozero import PiLiter
1413 lite = PiLiter()
1414 lite.on()
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.
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.
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).
1432 .. _Ciseco Pi-LITEr: http://shop.ciseco.co.uk/pi-liter-8-led-strip-for-the-raspberry-pi/
1433 """
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 )
1443class PiLiterBarGraph(LEDBarGraph):
1444 """
1445 Extends :class:`LEDBarGraph` to treat the `Ciseco Pi-LITEr`_ as an
1446 8-segment bar graph.
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::
1452 from gpiozero import PiLiterBarGraph
1454 graph = PiLiterBarGraph()
1455 graph.value = 0.5
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.
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``.
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).
1470 .. _Ciseco Pi-LITEr: http://shop.ciseco.co.uk/pi-liter-8-led-strip-for-the-raspberry-pi/
1471 """
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 )
1481class TrafficLights(LEDBoard):
1482 """
1483 Extends :class:`LEDBoard` for devices containing red, yellow, and green
1484 LEDs.
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::
1489 from gpiozero import TrafficLights
1491 traffic = TrafficLights(2, 3, 4)
1492 traffic.amber.on()
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.
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.
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.
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.
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.
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.
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).
1532 .. attribute:: red
1534 The red :class:`LED` or :class:`PWMLED`.
1536 .. attribute:: amber
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.
1542 .. attribute:: yellow
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.
1548 .. attribute:: green
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)
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)
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.
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::
1590 from gpiozero import PiTraffic
1592 traffic = PiTraffic()
1593 traffic.amber.on()
1595 To use the PI-TRAFFIC board when attached to a non-standard set of pins,
1596 simply use the parent class, :class:`TrafficLights`.
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.
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.
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).
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 )
1624class PiStop(TrafficLights):
1625 """
1626 Extends :class:`TrafficLights` for the `PiHardware Pi-Stop`_: a vertical
1627 traffic lights board.
1629 The following example turns on the amber LED on a Pi-Stop connected to
1630 location ``A+``::
1632 from gpiozero import PiStop
1634 traffic = PiStop('A+')
1635 traffic.amber.on()
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``.
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.
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.
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).
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 }
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)
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.
1688 The following example designates the first strip the label "wifi" and the
1689 second "raining", and turns them green and red respectfully::
1691 from gpiozero import StatusZero
1693 status = StatusZero('wifi', 'raining')
1694 status.wifi.green.on()
1695 status.raining.red.on()
1697 Each designated label will contain two :class:`LED` objects named "red"
1698 and "green".
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.
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).
1712 .. _STATUS Zero: https://thepihut.com/statuszero
1714 .. attribute:: your-label-here, your-label-here, ...
1716 This entry represents one of the three labelled attributes supported on
1717 the STATUS Zero board. It is an :class:`LEDBoard` which contains:
1719 .. attribute:: red
1721 The :class:`LED` or :class:`PWMLED` representing the red LED
1722 next to the label.
1724 .. attribute:: green
1726 The :class:`LED` or :class:`PWMLED` representing the green LED
1727 next to the label.
1728 """
1729 default_labels = ('one', 'two', 'three')
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 )
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.
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::
1766 from gpiozero import StatusBoard
1768 status = StatusBoard('wifi', 'raining')
1769 status.wifi.lights.green.on()
1770 status.wifi.button.when_pressed = status.wifi.lights.toggle
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".
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.
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).
1787 .. _STATUS: https://thepihut.com/status
1789 .. attribute:: your-label-here, your-label-here, ...
1791 This entry represents one of the five labelled attributes supported on
1792 the STATUS board. It is an :class:`CompositeOutputDevice` which
1793 contains:
1795 .. attribute:: lights
1797 A :class:`LEDBoard` representing the lights next to the label. It
1798 contains:
1800 .. attribute:: red
1802 The :class:`LED` or :class:`PWMLED` representing the red LED
1803 next to the label.
1805 .. attribute:: green
1807 The :class:`LED` or :class:`PWMLED` representing the green LED
1808 next to the label.
1810 .. attribute:: button
1812 A :class:`Button` representing the button next to the label.
1813 """
1814 default_labels = ('one', 'two', 'three', 'four', 'five')
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 )
1846class SnowPi(LEDBoard):
1847 """
1848 Extends :class:`LEDBoard` for the `Ryanteck SnowPi`_ board.
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::
1854 from gpiozero import SnowPi
1856 snowman = SnowPi(pwm=True)
1857 snowman.eyes.on()
1858 snowman.nose.pulse()
1859 snowman.arms.blink()
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.
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.
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).
1878 .. _Ryanteck SnowPi: https://ryanteck.uk/raspberry-pi/114-snowpi-the-gpio-snowman-for-raspberry-pi-0635648608303.html
1880 .. attribute:: arms
1882 A :class:`LEDBoard` representing the arms of the snow man. It contains
1883 the following attributes:
1885 .. attribute:: left, right
1887 Two :class:`LEDBoard` objects representing the left and right arms
1888 of the snow-man. They contain:
1890 .. attribute:: top, middle, bottom
1892 The :class:`LED` or :class:`PWMLED` down the snow-man's arms.
1894 .. attribute:: eyes
1896 A :class:`LEDBoard` representing the eyes of the snow-man. It contains:
1898 .. attribute:: left, right
1900 The :class:`LED` or :class:`PWMLED` for the snow-man's eyes.
1902 .. attribute:: nose
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 )
1935class TrafficLightsBuzzer(CompositeOutputDevice):
1936 """
1937 Extends :class:`CompositeOutputDevice` and is a generic class for HATs with
1938 traffic lights, a button and a buzzer.
1940 :param TrafficLights lights:
1941 An instance of :class:`TrafficLights` representing the traffic lights
1942 of the HAT.
1944 :param Buzzer buzzer:
1945 An instance of :class:`Buzzer` representing the buzzer on the HAT.
1947 :param Button button:
1948 An instance of :class:`Button` representing the button on the HAT.
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).
1955 .. attribute:: lights
1957 The :class:`TrafficLights` instance passed as the *lights* parameter.
1959 .. attribute:: buzzer
1961 The :class:`Buzzer` instance passed as the *buzzer* parameter.
1963 .. attribute:: button
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 )
1975class FishDish(CompositeOutputDevice):
1976 """
1977 Extends :class:`CompositeOutputDevice` for the `Pi Supply FishDish`_: traffic
1978 light LEDs, a button and a buzzer.
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::
1984 from gpiozero import FishDish
1986 fish = FishDish()
1987 fish.button.wait_for_press()
1988 fish.lights.on()
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.
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).
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 )
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.
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::
2023 from gpiozero import TrafficHat
2025 hat = TrafficHat()
2026 hat.button.wait_for_press()
2027 hat.lights.on()
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.
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).
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 )
2054class TrafficpHat(TrafficLights):
2055 """
2056 Extends :class:`TrafficLights` for the `Pi Supply Traffic pHAT`_: a small
2057 board with traffic light LEDs.
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::
2063 from gpiozero import TrafficpHat
2064 phat = TrafficpHat()
2065 phat.red.on()
2066 phat.blink()
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.
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.
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).
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 )
2094class Robot(SourceMixin, CompositeDevice):
2095 """
2096 Extends :class:`CompositeDevice` to represent a generic dual-motor robot.
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::
2104 from gpiozero import Robot
2106 robot = Robot(left=(4, 14), right=(17, 18))
2107 robot.forward()
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.
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.
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.
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).
2131 .. attribute:: left_motor
2133 The :class:`Motor` on the left of the robot.
2135 .. attribute:: right_motor
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 )
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
2162 @value.setter
2163 def value(self, value):
2164 self.left_motor.value, self.right_motor.value = value
2166 def forward(self, speed=1, **kwargs):
2167 """
2168 Drive the robot forward by running both motors forward.
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.
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*.
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))
2200 def backward(self, speed=1, **kwargs):
2201 """
2202 Drive the robot backward by running both motors backward.
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.
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*.
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))
2234 def left(self, speed=1):
2235 """
2236 Make the robot turn left by running the right motor forward and left
2237 motor backward.
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)
2246 def right(self, speed=1):
2247 """
2248 Make the robot turn right by running the left motor forward and right
2249 motor backward.
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)
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()
2268 def stop(self):
2269 """
2270 Stop the robot.
2271 """
2272 self.left_motor.stop()
2273 self.right_motor.stop()
2276class RyanteckRobot(Robot):
2277 """
2278 Extends :class:`Robot` for the `Ryanteck motor controller board`_.
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::
2284 from gpiozero import RyanteckRobot
2286 robot = RyanteckRobot()
2287 robot.forward()
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.
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).
2301 .. _Ryanteck motor controller board: https://uk.pi-supply.com/products/ryanteck-rtk-000-001-motor-controller-board-kit-raspberry-pi
2302 """
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 )
2311class CamJamKitRobot(Robot):
2312 """
2313 Extends :class:`Robot` for the `CamJam #3 EduKit`_ motor controller board.
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::
2319 from gpiozero import CamJamKitRobot
2321 robot = CamJamKitRobot()
2322 robot.forward()
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.
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).
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 )
2345class PhaseEnableRobot(SourceMixin, CompositeDevice):
2346 """
2347 Extends :class:`CompositeDevice` to represent a dual-motor robot based
2348 around a Phase/Enable motor board.
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::
2356 from gpiozero import PhaseEnableRobot
2358 robot = PhaseEnableRobot(left=(5, 12), right=(6, 13))
2359 robot.forward()
2361 :param tuple left:
2362 A tuple of two GPIO pins representing the phase and enable inputs
2363 of the left motor's controller.
2365 :param tuple right:
2366 A tuple of two GPIO pins representing the phase and enable inputs
2367 of the right motor's controller.
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.
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).
2381 .. attribute:: left_motor
2383 The :class:`PhaseEnableMotor` on the left of the robot.
2385 .. attribute:: right_motor
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 )
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
2412 @value.setter
2413 def value(self, value):
2414 self.left_motor.value, self.right_motor.value = value
2416 def forward(self, speed=1):
2417 """
2418 Drive the robot forward by running both motors forward.
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)
2427 def backward(self, speed=1):
2428 """
2429 Drive the robot backward by running both motors backward.
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)
2438 def left(self, speed=1):
2439 """
2440 Make the robot turn left by running the right motor forward and left
2441 motor backward.
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)
2450 def right(self, speed=1):
2451 """
2452 Make the robot turn right by running the left motor forward and right
2453 motor backward.
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)
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
2472 def stop(self):
2473 """
2474 Stop the robot.
2475 """
2476 self.left_motor.stop()
2477 self.right_motor.stop()
2480class PololuDRV8835Robot(PhaseEnableRobot):
2481 """
2482 Extends :class:`PhaseEnableRobot` for the `Pololu DRV8835 Dual Motor Driver
2483 Kit`_.
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::
2489 from gpiozero import PololuDRV8835Robot
2491 robot = PololuDRV8835Robot()
2492 robot.forward()
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.
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).
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 )
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 )
2528 def close(self):
2529 if getattr(self, '_lock', None):
2530 with self._lock:
2531 super(_EnergenieMaster, self).close()
2532 self._lock = None
2534 @classmethod
2535 def _shared_key(cls, pin_factory):
2536 # There's only one Energenie master
2537 return None
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()
2553class Energenie(SourceMixin, Device):
2554 """
2555 Extends :class:`Device` to represent an `Energenie socket`_ controller.
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::
2561 from gpiozero import Energenie
2563 lamp = Energenie(1)
2564 lamp.on()
2566 :param int socket:
2567 Which socket this instance should control. This is an integer number
2568 between 1 and 4.
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.
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).
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()
2600 def close(self):
2601 if getattr(self, '_master', None):
2602 self._master.close()
2603 self._master = None
2605 @property
2606 def closed(self):
2607 return self._master is None
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>"
2616 @property
2617 def socket(self):
2618 """
2619 Returns the socket number.
2620 """
2621 return self._socket
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
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
2642 def on(self):
2643 """
2644 Turns the socket on.
2645 """
2646 self.value = True
2648 def off(self):
2649 """
2650 Turns the socket off.
2651 """
2652 self.value = False
2655class PumpkinPi(LEDBoard):
2656 """
2657 Extends :class:`LEDBoard` for the `ModMyPi PumpkinPi`_ board.
2659 There are twelve LEDs connected up to individual pins, so for the PumpkinPi
2660 the pins are fixed. For example::
2662 from gpiozero import PumpkinPi
2664 pumpkin = PumpkinPi(pwm=True)
2665 pumpkin.sides.pulse()
2666 pumpkin.off()
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
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.
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).
2685 .. _ModMyPi PumpkinPi: https://www.modmypi.com/halloween-pumpkin-programmable-kit
2687 .. attribute:: sides
2689 A :class:`LEDBoard` representing the LEDs around the edge of the
2690 pumpkin. It contains:
2692 .. attribute:: left, right
2694 Two :class:`LEDBoard` instances representing the LEDs on the left
2695 and right sides of the pumpkin. They each contain:
2697 .. attribute:: top, midtop, middle, midbottom, bottom
2699 Each :class:`LED` or :class:`PWMLED` around the specified side
2700 of the pumpkin.
2702 .. attribute:: eyes
2704 A :class:`LEDBoard` representing the eyes of the pumpkin. It contains:
2706 .. attribute:: left, right
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 )
2741class JamHat(CompositeOutputDevice):
2742 """
2743 Extends :class:`CompositeOutputDevice` for the `ModMyPi JamHat`_ board.
2745 There are 6 LEDs, two buttons and a tonal buzzer. The pins are fixed.
2746 Usage::
2748 from gpiozero import JamHat
2750 hat = JamHat()
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()
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.
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).
2768 .. _ModMyPi JamHat: https://thepihut.com/products/jam-hat
2770 .. attribute:: lights_1, lights_2
2772 Two :class:`LEDBoard` instances representing the top (lights_1) and
2773 bottom (lights_2) rows of LEDs on the JamHat.
2775 .. attribute:: red, yellow, green
2777 :class:`LED` or :class:`PWMLED` instances representing the red,
2778 yellow, and green LEDs along the top row.
2780 .. attribute:: button_1, button_2
2782 The left (button_1) and right (button_2) :class:`Button` objects on the
2783 JamHat.
2785 .. attribute:: buzzer
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 )
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()
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()
2822class Pibrella(CompositeOutputDevice):
2823 """
2824 Extends :class:`CompositeOutputDevice` for the Cyntech/Pimoroni `Pibrella`_
2825 board.
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).
2830 This class exposes the LEDs, button and buzzer.
2832 Usage::
2834 from gpiozero import Pibrella
2836 pb = Pibrella()
2838 pb.button.wait_for_press()
2839 pb.lights.on()
2840 pb.buzzer.play('A4')
2841 pb.off()
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::
2846 from gpiozero import Pibrella, LED, Button
2848 pb = Pibrella()
2849 btn = Button(pb.inputs.a, pull_up=False)
2850 led = LED(pb.outputs.e)
2852 btn.when_pressed = led.on
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.
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).
2864 .. _Pibrella: http://www.pibrella.com/
2866 .. attribute:: lights
2868 :class:`TrafficLights` instance representing the three LEDs
2870 .. attribute:: red, amber, green
2872 :class:`LED` or :class:`PWMLED` instances representing the red,
2873 amber, and green LEDs
2875 .. attribute:: button
2877 The red :class:`Button` object on the Pibrella
2879 .. attribute:: buzzer
2881 A :class:`TonalBuzzer` object representing the buzzer
2883 .. attribute:: inputs
2885 A :func:`~collections.namedtuple` of the input pin numbers
2887 .. attribute:: a, b, c, d
2889 .. attribute:: outputs
2891 A :func:`~collections.namedtuple` of the output pin numbers
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 )
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()
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()