alt_neokey.alt_neokey1x4¶
Alternative CircuitPython API for NeoKey 1x4 i2c Keypad
Author(s): Greg Paris
Implementation Notes¶
Hardware:
Software and Dependencies:
Adafruit CircuitPython firmware for the supported boards: https://github.com/adafruit/circuitpython/releases
Adafruit’s Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice
- class alt_neokey.alt_neokey1x4.NeoKey1x4(i2c_bus, addr=48, *, brightness=0.2, auto_color=None, auto_action=None, blink=False)¶
Alternative API for Adafruit’s i2c keypad with RGB LEDs.
- Parameters
i2c_bus (i2c) – bus the NeoKey 1x4 is connected to
addr (int) – i2c address (or list of addresses) of NeoKey 1x4 module(s)
brightness (float) – RGB LED intensity
auto_color (function) – set colors when keys pressed/released
auto_action (function) – run when keys pressed/released
blink (bool) – blink all keys when they are not pressed
- Raises
RuntimeError – if unsupported features are used
ValueError – for incorrect i2c addresses
TypeError – if auto_color or auto_action is not a function
The intent of this alternative API is to reduce the amount of user code necessary to manage key colors and respond to key-press events. It also simplifies the task of managing more than one NeoKey 1x4 module simultaneously. The cost is that this library uses more memory than the standard does. Comparing simpletest examples from the two libraries, this alternative uses about 8 KB more memory. The all-bells-and-whistles blinktest example uses another 3 KB.
Basic usage is one NeoKey 1x4 module. In that case, supply its i2c address as the
addrargument.To use multiple modules together, supply a list or tuple of i2c addresses as the
addrargument. Sixteen modules can be supported by selectively solder-bridging the four address selectors to give each board a unique address, 0x30 through 0x3F. Key numbers will be assigned to the keys in the order of board addresses in the list, first 0-3, second 4-7, …, sixteenth 60-63. (If you accidentally assemble your project with the modules out of ascending address order, no worries! Just order them the same way in theaddrlist; the keys will be numbered the way you want.)Keys may be referenced by indexing the
NeoKey1x4instance. Each key is represented by aNeoKeyKeyinstance.To dynamically manipulate key colors without coding it into your main loop, define a function that returns a color (24-bit RGB) and pass it to the
NeoKey1x4constructor using theauto_colorparameter. The function will be called for each key press and key release event, as detected by theread()method. That method will call the function with a single argument, aNeoKeyEvent.Similarly, to have
read()run arbitrary code whenever a key is pressed, use theNeoKey1x4constructor’sauto_actionparameter. Any return value from the function will be ignored. As withauto_color, it will be passed a singleNeoKeyEventargument when invoked.The
blinkparameter is provided to initially enable all keys to blink while not being pressed, as sensed byread(). Keys may be set individually to blink or not blink using theirblinkproperty, regardless of theblinkvalue passed to theNeoKey1x4constructor. The blink feature requirestime.monotonic_ns(), which is not available on some boards. In that case, the feature is disabled and attempting to use it will raise aRuntimeErrorexception.Note
The
auto_colorfunction is used to initialize key colors whenever it is set or changed (except when set to None). It is used with blink mode to establish the ‘on’ color in the on/off cycle. In contrast, theauto_actionfunction can be relied upon to be called only on key press and key release events.Any time spent doing anything other than reading keys can detract from the responsiveness of the keys. It is probably a good idea to have keys change color when they are pressed, so that the user gets immediate feedback that the key press has been registered.
The following example code has the keys light white when pressed.
import board from alt_neokey.alt_neokey1x4 import NeoKey1x4 i2c = board.i2c() neokey = NeoKey1x4( i2c, auto_color=lambda e: 0xFFFFFF if e.pressed else 0 ) while True: neokey.read()
- classmethod all(i2c_bus, **kargs)¶
Find all NeoKey 1x4 devices on the bus and create a
NeoKey1x4instance using all of them, in ascending i2c address order. Returns the new instance.- Parameters
- Return type
- Raises
RuntimeError – if no NeoKey 1x4 modules found.
Any other named parameters are passed onto the
NeoKey1x4constructor.
- property auto_action¶
Automatic action function. Function is invoked on key press or release and is passed a single
NeoKeyEventas argument. The return value of this function is ignored. Use None to remove a previously set function.
- property auto_color¶
Automatic color management function. Function is invoked on key press or release and is passed a single
NeoKeyEventas argument. The function must return a 24-bit RGB color integer. Use None to remove a previously setauto_colorfunction. All keys are immediately set to their ‘released’ color whenever this parameter is set to a value other than None.The code snippet below, taken from one of the example programs, shows key 0 being used to toggle between two key-color modes.
while True: for event in neokey.read(): if event.pressed and event.key_num == 0: if neokey.auto_color is normal_mode: neokey.auto_color = special_mode else: neokey.auto_color = normal_mode
- property brightness¶
Float value of brightness shared by all NeoKey 1x4 LEDs.
- fill(color)¶
Set all keys to the specified color. Useful when setting key colors other than with
auto_color.- Parameters
color (int) – 24-bit color integer
- read()¶
At the most basic level,
read()queries all keys via the i2c bus.- Return type
read()compares the states of the keys to the previous time it was run. From that comparison, it generates an event list. Each event in that list corresponds to a key press or key release. This is not the same as the current state of a key, as a key that was neither pressed nor released since the lastread()will not have an event in the list. (To get the current state of a key, use itspressedproperty.) Each event in the list is aNeoKeyEventinstance. The return value is this list.The following code snippet, adapted from the simpletest example, demonstrates responding to the event list returned by
read().while True: for event in neokey.read(): if event.pressed: print(f"key {event.key_num} pressed") do_something_useful() else: print(f"key {event.key_num} released")
The NeoKey 1x4 module has an RGB LED under each key. Many uses would have the keys change colors when keys are pressed and released. This can be achieved in your main program, but cluttering it up with key-color management will make it harder to see the code that’s there for the main purpose of your program.
Instead, define a function that returns a color based on (or ignoring) the single
NeoKeyEventargument that will be passed to the function when it is invoked. Then, either as an argument to theNeoKey1x4constructor or by using itsauto_colorproperty, you inform theNeoKey1x4to use this function to set key colors. It will invoke the function on key press and key release events, setting the color of the key in question.This function in many cases is so simple that it can be expressed as a
lambda, as in the code snippet below, which sets the key color to red when pressed and ‘off’ when released.neokey.auto_color = lambda e: 0xFF0000 if e.pressed else 0
Another thing
NeoKey1x4can do for you is blink your keys. For example, to signal an alert condition associated with a key, theblinkproperty of that key could be set to True. Each timeread()is run, it checks to see whether the key should change from ‘off’ to ‘on’ or vice versa and takes care of it.neokey[2].blink = True # sets key 2 blinking
Finally, similar to
auto_color,read()can execute a function every time a key is pressed or released. This can be set in an argument to theNeoKey1x4constructor or it can be specified using theauto_actionproperty.Although there is no limitation on uses for this function, a suggestion is to use it for other housekeeping functions, such as key-clicks, haptic feedback, key logging, etc. This would allow you to keep such code separate from the main thrust of your program. Any return value from the
auto_actionfunction will be ignored.def sound_on_pressed(kev): if kev.pressed: if kev.key_num == 0: my_ring_bell() else: my_key_click() neokey.auto_action = sound_on_pressed
- read_event(*, timeout=0)¶
Similar to
read()in its calling ofauto_colorandauto_actionfunctions and managingblink, but uses a different approach to gathering and returning events.- Parameters
timeout (int) – yield None if no key activity in 10ths of a second
- Return type
NeoKeyEvent or None
- Raises
ValueError – for negative timeout
This method queries one NeoKey 1x4 module at a time. If there are events from that module, yields those events back to the caller one event at a time. On the next call, it starts where it left off. It continues to the next module, starting over with the first after the last, looping forever.
The principal advantage of
read_event()overread()is latency fairness. Withread(), the first NeoKey 1x4 module gets quicker response than the last becauseauto_color,auto_actionandblinkare processed in module and key order. (This effect also could be present in your main program, if it processes the event list in order.) Withread_event(), latency is shared evenly because it always picks up from where it left off.Note
A possibly critical disadvantage of using
read_event()is that it does not return to the caller until it detects an event.To overcome this behavior, use the
timeoutparameter to causeread_event()to return None whenever the specified time has elapsed without a key event. As withblink,timeoutis supported only when the board supportstime.monotonic_ns().Depending on your application, latency differences might be insignificant or unimportant. In those cases, you probably should use
read(), as it allows for more flexibility in the main program, given that thetimeoutfeature is not universally supported.Here is an example of using
read_event()with a timeout of three tenths of a second.# no need for "while True:" for event in neokey.read_event(timeout=3): if event is not None: ... # handle key event here ... # do other tasks
- class alt_neokey.alt_neokey1x4.NeoKeyEvent(key_num, pressed)¶
Event list element.
- Parameters
- property key_num¶
Alias for field number 0
- property pressed¶
Alias for field number 1
- class alt_neokey.alt_neokey1x4.NeoKeyKey(seesaw, pixel, key_num, *, blink=False)¶
A single key and pixel pairing.
- Parameters
The constructor for this class is not intended to be invoked by anything other than
NeoKey1x4. One :class`NeoKeyKey` instance is created byNeoKey1x4for each key. Keys are numbered 0-3 on the first NeoKey 1x4 module. 4-7 on the second, etc.These instances can be referenced by indexing a
NeoKey1x4object, as shown in the examples below.neokey = NeoKey1x4(i2c, addr=[0x30, 0x31, 0x32]) neokey[0].color = 0xFF0000 # make red neokey[11].blink = True # start blinking key = neokey[0] # reference a NeoKeyKey instance key.color = 0xFF0000 # same as above example # key numbers of keys pressed now pressed = [k for k in neokey if neokey[k].pressed]
- property blink¶
Read-write boolean property, True when key is blinking.
- property color¶
Read-write integer property representing the key’s pixel color. Reads and writes are done over the i2c bus.
- property pressed¶
Immediate read of this key’s state via the i2c bus. Read-only property is True if the key is being pressed. Does not invoke or affect
auto_colororauto_action.