2 `keypad` - Support for scanning keys and key matrices
3 ===========================================================
4 See `CircuitPython:keypad` in CircuitPython for more details.
6 * Author(s): Melissa LeBlanc-Williams
11 from collections import deque
16 """A key transition event."""
18 def __init__(self, key_number=0, pressed=True):
20 Create a key transition event, which reports a key-pressed or key-released transition.
22 :param int key_number: the key number
23 :param bool pressed: ``True`` if the key was pressed; ``False`` if it was released.
25 self._key_number = key_number
26 self._pressed = pressed
31 return self._key_number
36 ``True`` if the event represents a key down (pressed) transition.
37 The opposite of `released`.
44 ``True`` if the event represents a key up (released) transition.
45 The opposite of `pressed`.
47 return not self._pressed
49 def __eq__(self, other):
51 Two `Event` objects are equal if their `key_number`
52 and `pressed`/`released` values are equal.
54 return self.key_number == other.key_number and self.pressed == other.pressed
57 """Returns a hash for the `Event`, so it can be used in dictionaries, etc.."""
58 return hash(self._key_number)
61 """Return a textual representation of the object"""
62 return "<Event: key_number {} {}>".format(
63 self.key_number, "pressed" if self._pressed else "released"
69 A queue of `Event` objects, filled by a `keypad` scanner such as `Keys` or `KeyMatrix`.
71 You cannot create an instance of `_EventQueue` directly. Each scanner creates an
72 instance when it is created.
75 def __init__(self, max_events):
76 self._events = deque([], max_events)
77 self._overflowed = False
81 Return the next key transition event. Return ``None`` if no events are pending.
83 Note that the queue size is limited; see ``max_events`` in the constructor of
84 a scanner such as `Keys` or `KeyMatrix`.
85 If a new event arrives when the queue is full, the event is discarded, and
86 `overflowed` is set to ``True``.
88 :return: the next queued key transition `Event`
89 :rtype: Optional[Event]
93 return self._events.popleft()
95 def get_into(self, event):
96 """Store the next key transition event in the supplied event, if available,
98 If there are no queued events, do not touch ``event`` and return ``False``.
100 The advantage of this method over ``get()`` is that it does not allocate storage.
101 Instead you can reuse an existing ``Event`` object.
103 Note that the queue size is limited; see ``max_events`` in the constructor of
104 a scanner such as `Keys` or `KeyMatrix`.
106 :return ``True`` if an event was available and stored, ``False`` if not.
111 next_event = self._events.popleft()
112 # pylint: disable=protected-access
113 event._key_number = next_event._key_number
114 event._pressed = next_event._pressed
115 # pylint: enable=protected-access
120 Clear any queued key transition events. Also sets `overflowed` to ``False``.
123 self._overflowed = False
126 """``True`` if `len()` is greater than zero.
127 This is an easy way to check if the queue is empty.
129 return len(self._events) > 0
132 """Return the number of events currently in the queue. Used to implement ``len()``."""
133 return len(self._events)
136 def overflowed(self):
138 ``True`` if an event could not be added to the event queue because it was full. (read-only)
139 Set to ``False`` by `clear()`.
141 return self._overflowed
143 def keypad_eventqueue_record(self, key_number, current):
144 """Record a new event"""
145 if len(self._events) == self._events.maxlen:
146 self._overflowed = True
148 self._events.append(Event(key_number, current))
152 def __init__(self, interval, max_events, scanning_function):
153 self._interval = interval
154 self._last_scan = time.monotonic()
155 self._events = _EventQueue(max_events)
156 self._scanning_function = scanning_function
157 self._scan_thread = threading.Thread(target=self._scanning_loop, daemon=True)
158 self._scan_thread.start()
162 """The EventQueue associated with this Keys object. (read-only)"""
167 if self._scan_thread.is_alive():
168 self._scan_thread.join()
171 """No-op used by Context Managers."""
174 def __exit__(self, exception_type, exception_value, traceback):
176 Automatically deinitializes when exiting a context. See
177 :ref:`lifetime-and-contextmanagers` for more info.
181 def _scanning_loop(self):
183 self._scanning_function()
187 class Keys(_KeysBase):
188 """Manage a set of independent keys."""
191 self, pins, *, value_when_pressed, pull=True, interval=0.02, max_events=64
194 Create a `Keys` object that will scan keys attached to the given sequence of pins.
195 Each key is independent and attached to its own pin.
197 An `EventQueue` is created when this object is created and is available in the
200 :param Sequence[microcontroller.Pin] pins: The pins attached to the keys.
201 The key numbers correspond to indices into this sequence.
202 :param bool value_when_pressed: ``True`` if the pin reads high when the key is pressed.
203 ``False`` if the pin reads low (is grounded) when the key is pressed.
204 All the pins must be connected in the same way.
205 :param bool pull: ``True`` if an internal pull-up or pull-down should be
206 enabled on each pin. A pull-up will be used if ``value_when_pressed`` is ``False``;
207 a pull-down will be used if it is ``True``.
208 If an external pull is already provided for all the pins, you can set
209 ``pull`` to ``False``.
210 However, enabling an internal pull when an external one is already present is not
212 it simply uses slightly more current.
213 :param float interval: Scan keys no more often than ``interval`` to allow for debouncing.
214 ``interval`` is in float seconds. The default is 0.020 (20 msecs).
215 :param int max_events: maximum size of `events` `EventQueue`:
216 maximum number of key transition events that are saved.
218 If a new event arrives when the queue is full, the oldest event is discarded.
220 self._digitalinouts = []
222 dio = digitalio.DigitalInOut(pin)
225 digitalio.Pull.DOWN if value_when_pressed else digitalio.Pull.UP
227 self._digitalinouts.append(dio)
229 self._currently_pressed = [False] * len(pins)
230 self._previously_pressed = [False] * len(pins)
231 self._value_when_pressed = value_when_pressed
233 super().__init__(interval, max_events, self._keypad_keys_scan)
236 """Stop scanning and release the pins."""
238 for dio in self._digitalinouts:
242 """Reset the internal state of the scanner to assume that all keys are now released.
243 Any key that is already pressed at the time of this call will therefore immediately cause
244 a new key-pressed event to occur.
246 self._currently_pressed = self._previously_pressed = [False] * self.key_count
250 """The number of keys that are being scanned. (read-only)"""
251 return len(self._digitalinouts)
253 def _keypad_keys_scan(self):
254 if time.monotonic() - self._last_scan < self._interval:
256 self._last_scan = time.monotonic()
258 for key_number, dio in enumerate(self._digitalinouts):
259 self._previously_pressed[key_number] = self._currently_pressed[key_number]
260 current = dio.value == self._value_when_pressed
261 self._currently_pressed[key_number] = current
262 if self._previously_pressed[key_number] != current:
263 self._events.keypad_eventqueue_record(key_number, current)
266 class KeyMatrix(_KeysBase):
267 """Manage a 2D matrix of keys with row and column pins."""
269 # pylint: disable=too-many-arguments
274 columns_to_anodes=True,
279 Create a `Keys` object that will scan the key matrix attached to the given row and
281 There should not be any external pull-ups or pull-downs on the matrix:
282 ``KeyMatrix`` enables internal pull-ups or pull-downs on the pins as necessary.
284 The keys are numbered sequentially from zero. A key number can be computed
285 by ``row * len(column_pins) + column``.
287 An `EventQueue` is created when this object is created and is available in the `events`
290 :param Sequence[microcontroller.Pin] row_pins: The pins attached to the rows.
291 :param Sequence[microcontroller.Pin] column_pins: The pins attached to the colums.
292 :param bool columns_to_anodes: Default ``True``.
293 If the matrix uses diodes, the diode anodes are typically connected to the column pins,
294 and the cathodes should be connected to the row pins. If your diodes are reversed,
295 set ``columns_to_anodes`` to ``False``.
296 :param float interval: Scan keys no more often than ``interval`` to allow for debouncing.
297 ``interval`` is in float seconds. The default is 0.020 (20 msecs).
298 :param int max_events: maximum size of `events` `EventQueue`:
299 maximum number of key transition events that are saved.
301 If a new event arrives when the queue is full, the oldest event is discarded.
303 self._row_digitalinouts = []
304 for row_pin in row_pins:
305 row_dio = digitalio.DigitalInOut(row_pin)
306 row_dio.switch_to_input(
307 pull=(digitalio.Pull.UP if columns_to_anodes else digitalio.Pull.DOWN)
309 self._row_digitalinouts.append(row_dio)
311 self._column_digitalinouts = []
312 for column_pin in column_pins:
313 col_dio = digitalio.DigitalInOut(column_pin)
314 col_dio.switch_to_input(
315 pull=(digitalio.Pull.UP if columns_to_anodes else digitalio.Pull.DOWN)
317 self._column_digitalinouts.append(col_dio)
318 self._currently_pressed = [False] * len(column_pins) * len(row_pins)
319 self._previously_pressed = [False] * len(column_pins) * len(row_pins)
320 self._columns_to_anodes = columns_to_anodes
322 super().__init__(interval, max_events, self._keypad_keymatrix_scan)
324 # pylint: enable=too-many-arguments
328 """The number of keys that are being scanned. (read-only)"""
329 return len(self._row_digitalinouts) * len(self._column_digitalinouts)
332 """Stop scanning and release the pins."""
334 for row_dio in self._row_digitalinouts:
336 for col_dio in self._column_digitalinouts:
341 Reset the internal state of the scanner to assume that all keys are now released.
342 Any key that is already pressed at the time of this call will therefore immediately cause
343 a new key-pressed event to occur.
345 self._previously_pressed = self._currently_pressed = [False] * self.key_count
347 def _row_column_to_key_number(self, row, column):
348 return row * len(self._column_digitalinouts) + column
350 def _keypad_keymatrix_scan(self):
351 if time.monotonic() - self._last_scan < self._interval:
353 self._last_scan = time.monotonic()
355 for row, row_dio in enumerate(self._row_digitalinouts):
356 row_dio.switch_to_output(
357 value=(not self._columns_to_anodes),
358 drive_mode=digitalio.DriveMode.PUSH_PULL,
360 for col, col_dio in enumerate(self._column_digitalinouts):
361 key_number = self._row_column_to_key_number(row, col)
362 self._previously_pressed[key_number] = self._currently_pressed[
365 current = col_dio.value != self._columns_to_anodes
366 self._currently_pressed[key_number] = current
367 if self._previously_pressed[key_number] != current:
368 self._events.keypad_eventqueue_record(key_number, current)
369 row_dio.value = self._columns_to_anodes
370 row_dio.switch_to_input(
373 if self._columns_to_anodes
374 else digitalio.Pull.DOWN
379 class ShiftRegisterKeys(_KeysBase):
380 """Manage a set of keys attached to an incoming shift register."""
395 Create a `Keys` object that will scan keys attached to a parallel-in serial-out
396 shift register like the 74HC165 or CD4021.
397 Note that you may chain shift registers to load in as many values as you need.
399 Key number 0 is the first (or more properly, the zero-th) bit read. In the
400 74HC165, this bit is labeled ``Q7``. Key number 1 will be the value of ``Q6``, etc.
402 An `EventQueue` is created when this object is created and is available in the
405 :param microcontroller.Pin clock: The shift register clock pin.
406 The shift register should clock on a low-to-high transition.
407 :param microcontroller.Pin data: the incoming shift register data pin
408 :param microcontroller.Pin latch:
409 Pin used to latch parallel data going into the shift register.
410 :param bool value_to_latch: Pin state to latch data being read.
411 ``True`` if the data is latched when ``latch`` goes high
412 ``False`` if the data is latched when ``latch goes low.
413 The default is ``True``, which is how the 74HC165 operates. The CD4021 latch is
414 the opposite. Once the data is latched, it will be shifted out by toggling the
416 :param int key_count: number of data lines to clock in
417 :param bool value_when_pressed: ``True`` if the pin reads high when the key is pressed.
418 ``False`` if the pin reads low (is grounded) when the key is pressed.
419 :param float interval: Scan keys no more often than ``interval`` to allow for debouncing.
420 ``interval`` is in float seconds. The default is 0.020 (20 msecs).
421 :param int max_events: maximum size of `events` `EventQueue`:
422 maximum number of key transition events that are saved.
424 If a new event arrives when the queue is full, the oldest event is discarded.
426 clock_dio = digitalio.DigitalInOut(clock)
427 clock_dio.switch_to_output(
428 value=False, drive_mode=digitalio.DriveMode.PUSH_PULL
430 self._clock = clock_dio
432 data_dio = digitalio.DigitalInOut(data)
433 data_dio.switch_to_input()
434 self._data = data_dio
436 latch_dio = digitalio.DigitalInOut(latch)
437 latch_dio.switch_to_output(value=True, drive_mode=digitalio.DriveMode.PUSH_PULL)
438 self._latch = latch_dio
439 self._value_to_latch = value_to_latch
441 self._currently_pressed = [False] * key_count
442 self._previously_pressed = [False] * key_count
443 self._value_when_pressed = value_when_pressed
444 self._key_count = key_count
446 super().__init__(interval, max_events, self._keypad_shiftregisterkeys_scan)
449 """Stop scanning and release the pins."""
457 Reset the internal state of the scanner to assume that all keys are now released.
458 Any key that is already pressed at the time of this call will therefore immediately cause
459 a new key-pressed event to occur.
461 self._currently_pressed = self._previously_pressed = [False] * self._key_count
465 """The number of keys that are being scanned. (read-only)"""
466 return self._key_count
470 """The `EventQueue` associated with this `Keys` object. (read-only)"""
473 def _keypad_shiftregisterkeys_scan(self):
474 if time.monotonic() - self._last_scan < self._interval:
476 self._last_scan = time.monotonic()
478 self._latch.value = self._value_to_latch
479 for key_number in range(self._key_count):
480 self._clock.value = False
481 self._previously_pressed[key_number] = self._currently_pressed[key_number]
482 current = self._data.value == self._value_when_pressed
483 self._currently_pressed[key_number] = current
484 self._clock.value = True
485 if self._previously_pressed[key_number] != current:
486 self._events.keypad_eventqueue_record(key_number, current)
488 self._latch.value = not self._value_to_latch