Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for LED matrix per layer #551

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 69 additions & 40 deletions docs/peg_rgb_matrix.md
Original file line number Diff line number Diff line change
@@ -1,44 +1,50 @@
# Peg RGB Matrix

### What you can and cannot do with this extension:
## What you can and cannot do with this extension:

### Can Do

#### Can Do
* Set any key's LED to be any color in a syntax very similar to your keymap
* Allows specific keys to be set to OFF
* Allows underglow LEDs to be a different color than per-key LEDs
* Allows modifier keys to be set to a different color than alpha keys
* Full split keyboard support
* Change LED color based on current layer

### Cannot Do (currently in progress)

#### Cannot Do (currently in progress)
* Adjust color at runtime. Currently the extension requires changes to main.py in order to make changes to your LEDs.
* Animations
* Change LED color based on current layer

### Keycodes
## Keycodes

Currently this extension does not support changing LEDs at runtime, as a result there is only a single keycode available to interact with this extension and that is KC.RGB_TOG. This keycode simply toggles all your LEDs on and off.

## Required Libraries

The following libraries must be frozen in your CircuitPython distribution or in a 'lib' folder at the root of your drive.

* [Adafruit_CircuitPython_NeoPixel](https://github.com/adafruit/Adafruit_CircuitPython_NeoPixel)
* [Download .mpy versions from here](https://github.com/adafruit/Adafruit_CircuitPython_Bundle/releases/download/20220415/adafruit-circuitpython-bundle-7.x-mpy-20220415.zip)

# Required Changes to main.py and kb.py
In order to use this extension the user must make changes to both their kb.py and main.py files. Below you will find a more comprehensive list of changes required in order to use this extension.
## Required Changes to main.py and kb.py

In order to use this extension the user must make changes to both their kb.py and main.py files. Below you will find a more comprehensive list of changes required in order to use this extension.

### kb.py

## kb.py
It is possible your chosen board may already have these changes made, if not you will need to make these additions:

The board's kb.py needs 3 fields:
* LED Key Position `led_key_pos`
* Much like `coord_mapping` this tells the extension where the LEDs are on your board.
The board's kb.py needs 3 fields:

* LED Key Position `led_key_pos`
* Much like `coord_mapping` this tells the extension where the LEDs are on your board.
* Brightness Limit `brightness_limit`
* Limits your brightness and may be required in order to stabilize performance.
* Limits your brightness and may be required in order to stabilize performance.
* Number of LEDs `num_pixels`
* Used for calculations in order to ensure the LEDs map to the correct keys.

* Used for calculations in order to ensure the LEDs map to the correct keys.

### Non-split Example:
#### Non-split Example:

Below shows a simple non-split example for a board containing 48 LEDs total and 38 keys with per-key LEDs.
This means we will have 10 underglow LEDs and 38 per-key LEDs.
Expand All @@ -62,11 +68,10 @@ Underglow LEDs always appear at the end of the `led_key_pos` array, because the
num_pixels = 48
```

#### Split Example:

### Split Example:

Below shows a 58 key split keyboard's `led_key_pos` array for a board containing 70 LEDs in total.
The board has 58 keys, meaning we are left with 12 underglow LEDs total.
Below shows a 58 key split keyboard's `led_key_pos` array for a board containing 70 LEDs in total.
The board has 58 keys, meaning we are left with 12 underglow LEDs total.
Since the board is a split and we can assume the LEDs are mirrored, that means each half has 29 per-key LEDs and 6 underglow LEDs.

Let's first focus on the left half of the board.
Expand Down Expand Up @@ -95,8 +100,8 @@ Underglow LEDs always appear at the end of the `led_key_pos` array, because the

```

### main.py

## main.py
It is possible your chosen board may already have these changes made, if not you will need to make these additions:

```python
Expand All @@ -106,45 +111,49 @@ rgb_ext = Rgb_matrix(...per key color data)
keyboard.extensions.append(rgb_ext)
```

Rgb_matrix extension requires one argument (`Rgb_matrix_data`), although additional arguments can be passed, here are all arguments that can be passed to

Rgb_matrix:
Rgb_matrix extension requires one argument (`Rgb_matrix_data`), although additional arguments can be passed, here are all arguments that can be passed to Rgb_matrix:

* LED Display `ledDisplay`
* This is our primary and only required field, this takes a `Rgb_matrix_data` class.
* Rgb_matrix_data only takes two fields:
* Keys: an array of colors with a length equal to the number of keys on your keyboard
* Underglow: an array of colors with a length equal to the number of underglow leds on your keyboard
* This is our primary and only required field, this takes a `Rgb_matrix_data`, `Rgb_matrix_layers` or an array of LED colors.
* An array of LED color values should be the size of LEDs on the keyboard and follows the `led_key_pos` described above.
* `Rgb_matrix_data` only takes two fields:
* `keys`: an array of colors with a length equal to the number of keys on your keyboard
* `underglow`: an array of colors with a length equal to the number of underglow leds on your keyboard
* `Rgb_matrix_layers` only takes one field:
* Layers: an array of `{ 0: <layer>, 1: <ledDisplay> }` objects. These consists of an integer `layer` and an array `ledDisplay` with following the same syntax as the keys and underglow arrays described above.
* Split `split`
* This is an optional boolean and only to be used if the keyboard is a split.
* This is an optional boolean and only to be used if the keyboard is a split.
* Right Side `rightSide`
* This is optional boolean only to be used if the keyboard is split. This signals that this configuration is targetting the right side (off side).
* This is optional boolean only to be used if the keyboard is split. This signals that this configuration is targetting the right side (off side).
* RGB Order `rgb_order`
* This is optional and only needs to be set if you are not using a WS2812 based LED.
* This is optional and only needs to be set if you are not using a WS2812 based LED.
* Disable Auto Write `disable_auto_write`
* This is optional and only serves to make all your LEDs turn on at once instead of animate to their on state.
* This is optional and only serves to make all your LEDs turn on at once instead of animate to their on state.

### Colors
Colors are RGB and can be provided in one of two ways.
## Colors

Colors are RGB and can be provided in one of two ways.
Colors can be defined as an array of three numbers (0-255) or you can use the `Color` class with its default colors, see example below.

### Passing RGB Codes

```python
Rgb_matrix_data(
keys=[[255,55,55],[55,55,55],[55,55,55],[55,55,55],[55,55,55],[55,55,55],"""... rest of colors""" ],
underglow=[[0,0,55],[0,0,55],"""... rest of colors""" ]
)
```
```

### Using `Color` Class

```python
Rgb_matrix_data(
keys=[Color.RED, Color.GREEN, Color.BLUE, Color.WHITE, Color.YELLOW, Color.ORANGE,"""... rest of colors""" ],
underglow=[Color.PURPLE, Color.TEAL, Color.PINK, Color.OFF,"""... rest of colors""" ]
)
```
```

### Full Examples
## Full Examples

```python
rgb_ext = Rgb_matrix(ledDisplay=Rgb_matrix_data(
Expand All @@ -163,7 +172,27 @@ rgb_ext = Rgb_matrix(ledDisplay=Rgb_matrix_data(
disable_auto_write=True)
```

### Bonus
```python
rgb_ext = Rgb_matrix(ledDisplay=Rgb_matrix_layers(
layers=[
{
0: 0,
1: [
[255,55,55],[55,55,55],[55,55,55],[55,55,55],[55,55,55],[55,55,55], [55,55,55],[55,55,55],[55,55,55],[55,55,55],[55,55,55],[255,55,55],
[255,55,55],[55,55,55],[55,55,55],[55,55,55],[55,55,55],[55,55,55], [55,55,55],[55,55,55],[55,55,55],[55,55,55],[55,55,55],[255,55,55],
[255,55,55],[55,55,55],[55,55,55],[55,55,55],[55,55,55],[55,55,55], [55,55,55],[55,55,55],[55,55,55],[55,55,55],[55,55,55],[255,55,55],
[255,55,55],[55,55,55],[55,55,55],[55,55,55],[55,55,55],[55,55,55],[255,55,55],[255,55,55],[55,55,55],[55,55,55],[55,55,55],[55,55,55],[55,55,55],[255,55,55],
[255,55,55],[55,55,55],[55,55,55],[255,55,55],[255,55,55],[55,55,55],[55,55,55],[255,55,55],
[0,0,55],[0,0,55],[0,0,55],[0,0,55],[0,0,55],[0,0,55],[0,0,55],[0,0,55],[0,0,55],[0,0,55],[0,0,55],[0,0,55]],
},
]),
split=True,
rightSide=True,
disable_auto_write=True)
```

## Bonus

Because creating `ledDisplay` can be time consuming, there is a utility avaiable that will generate a basic framework for you.

```python
Expand All @@ -173,7 +202,8 @@ Rgb_matrix_data.generate_led_map(58,10,Color.WHITE,Color.BLUE)
Call `Rgb_matrix_data.generate_led_map` before you do any configuration beyond imports and it will print an `Rgb_matrix_data` class to your CircuitPython REPL which you can view by using a tool like "screen" or "PUTTY".

Generate LED Map Arguments:
* Number of Keys

* Number of Keys
* Number of Underglow
* Key Color
* Underglow Color
Expand All @@ -184,6 +214,5 @@ Example Using Above Arguments:
Rgb_matrix_data(keys=[[249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249]],
underglow=[[0, 0, 255], [0, 0, 255], [0, 0, 255], [0, 0, 255], [0, 0, 255], [0, 0, 255], [0, 0, 255], [0, 0, 255], [0, 0, 255], [0, 0, 255]])
```
[Connecting to the Serial Console](https://learn.adafruit.com/welcome-to-circuitpython/kattni-connecting-to-the-serial-console)


[Connecting to the Serial Console](https://learn.adafruit.com/welcome-to-circuitpython/kattni-connecting-to-the-serial-console)
40 changes: 40 additions & 0 deletions kmk/extensions/peg_rgb_matrix.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import gc
import neopixel

from storage import getmount
Expand Down Expand Up @@ -37,6 +38,24 @@ def generate_led_map(
keys = [key_color] * number_of_keys
underglow = [underglow_color] * number_of_underglow
print(f'Rgb_matrix_data(keys={keys},\nunderglow={underglow})')
return Rgb_matrix_data(keys, underglow)


class Rgb_matrix_layers:
def __init__(self, layers):
self.layers = layers if layers else []

@staticmethod
def generate_layer(number_of_leds, layer_color):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One of these needs removed. Having two methods with the same name is failing the test.

layers = [layer_color] * number_of_leds
print(f'Rgb_matrix_layers(layers={layers})')

@staticmethod
def generate_layer(layer, ledDisplay):
return {
0: layer,
1: ledDisplay.data if type(ledDisplay) == Rgb_matrix_data else ledDisplay,
}


class Rgb_matrix(Extension):
Expand All @@ -53,12 +72,20 @@ def __init__(
self.disable_auto_write = disable_auto_write
self.split = split
self.rightSide = rightSide
self.layerSensitiveLayers = False
self.layerLeds = None
self._prevLayers = 0

if name.endswith('L'):
self.rightSide = False
elif name.endswith('R'):
self.rightSide = True
if type(ledDisplay) is Rgb_matrix_data:
self.ledDisplay = ledDisplay.data
elif type(ledDisplay) is Rgb_matrix_layers:
self.layerSensitiveLayers = True
self.layerLeds = ledDisplay.layers
self.ledDisplay = self.layerLeds[0][1] # Default to first layer
else:
self.ledDisplay = ledDisplay

Expand Down Expand Up @@ -105,6 +132,16 @@ def setBasedOffDisplay(self):
for i, val in enumerate(self.ledDisplay):
self.neopixel[self.keyPos[i]] = (val[0], val[1], val[2])

def updateLEDs(self, active_layer):
if self.layerLeds != None:
kdb424 marked this conversation as resolved.
Show resolved Hide resolved
for _, val in enumerate(self.layerLeds):
if val[0] == active_layer:
self.ledDisplay = val[1]
self.setBasedOffDisplay()
self.neopixel.show()
break
gc.collect()

def on_runtime_enable(self, sandbox):
return

Expand All @@ -125,6 +162,9 @@ def during_bootup(self, board):
return

def before_matrix_scan(self, sandbox):
if sandbox.active_layers[0] != self._prevLayers:
self._prevLayers = sandbox.active_layers[0]
self.updateLEDs(sandbox.active_layers[0])
return

def after_matrix_scan(self, sandbox):
Expand Down