Keybow 2040 - on key numbering rotation

I noticed in the keybow2040.py code there is what looks like an unfinished bit of code commented out for rotating the key/led numbering.

I didn’t have time to dive into it, so I went with a more direct editing method that I’d though I’d share

With the cable at the top/away the default numbering goes from 0 bottom to 3 top, left column first
To change the order to 0 lef to 3 right, top row first (english reading order) two changes must be made
Change #1

# Old code in /lib/keybow2040.py
_PINS = [board.SW0,
        board.SW1,
        board.SW2,
        board.SW3,
        board.SW4,
        board.SW5,
        board.SW6,
        board.SW7,
        board.SW8,
        board.SW9,
        board.SW10,
        board.SW11,
        board.SW12,
        board.SW13,
        board.SW14,
        board.SW15]

becomes

# New code in /lib/keybow2040.py
_PINS = [board.SW3,
        board.SW7,
        board.SW11,
        board.SW15,
        board.SW2,
        board.SW6,
        board.SW10,
        board.SW14,
        board.SW1,
        board.SW5,
        board.SW9,
        board.SW13,
        board.SW0,
        board.SW4,
        board.SW8,
        board.SW12]

and Change #2

# Old code in /lib/keybow2040.py
def number_to_xy(number):
    # Convert a number to an x/y coordinate.
    # uncomment for Bottom left = 0, up = 1+
    x = number % 4 
    y = number // 4

    return (x, y)

becomes

# New code in /lib/keybow2040.py
def number_to_xy(number):
    # Convert a number to an x/y coordinate.
    # uncomment for Bottom left = 0, up = 1+
    x = (15-number) // 4 
    y = number % 4

    return (x, y)

similar manipulations can get you other orientations
I’m not sure why the second change is necessary but I believe it’s because the LEDs are enumerated by the I2C library. if I get around to it I may write a function that will allow arbitrary numbering of keys to allow more options (I’m thinking any arbitrary layout)

So the above works on the shipped version, but not on the most recent version, so I dug into the code on the newer PMK library

the rotation code in PMK/init.py almost works, if you leave the last 2 lines commented, but doesn’t update the xy coordinates (and the rotation is also backwards). Most things don’t use those coordinates so it’s an easy solution.

if you need a solution that does both, you can replace the commented function with the following (which works for both the shipped and most recent version)

	def rotate( self, quarter_turns: int = 1 ):
		'''
		rotates the button numbering by quarter turns (90deg).
		Clockwise by default, counter-clockwise with negative values.
		Coordinates need no adjustment since they follow the key index.
		'''
		quarter_turns %= 4
		if 0 == quarter_turns:
			return
		# Shift the value towards zero to get +/-1 or 0 for 180
		quarter_turns = quarter_turns + 2 * (1 - 2 * (quarter_turns > 0))
		# Simpler rotation for 180
		if 0 == quarter_turns:
			for _i in self.keys:
				self.keys[_i].number = len( self.keys ) - 1 - _i
		else:
			_side = range( 4 )
			for _y1 in _side:
				for _x1 in _side:
					# Exchange x for y and invert the count
					# x inverted = clockwise, y inverted = counter-clockwise
					self.keys[_x1 + 4 * _y1].number = \
					3 * (quarter_turns < 0) + _y1 * quarter_turns \
					+ 4 * (3 * (quarter_turns > 0) + _x1 * -quarter_turns)
		# Make the hardware coordinates match the hardware button numbers
		for _i in range( len( self.keys ) ):
			self.keys[_i].x, self.keys[_i].y = self.keys[_i].xy = self.keys[_i].get_xy()

for those that care, the code is released as Unlicense (that means do what you want with it, change it, steal it, pretend you wrote it, IDC)

Hey,

I’m trying to figure out how I can get your rotation fix working. If I just replace the commented park in the PMK/init.py file and call this new function via keybow.rotate(1) in my code.py file the code crashes somehow.

Do you have any suggestions or more detailed instructions?

Just reloaded the defaults and swapped out the function on my keybow2040, not getting any crashes on my end.

When you grabbed the latest PMK, did you also grab the most recent circuit python version? currently I’m running the 7.3.3 which you can grab from adafruit for keybow2040… not sure about the current microPython build for Pico RGB Keypad Base

if you’re up to date try running the code.py that’s on your device from Mu or Thonny or whatever you have for REPL and see what error messages it gives you and where. it may be something as simple as tabs instead of spaces (if it complains about inconsistent use of tabs and spaces, open the file in a text editor copy a tab, hit ctrl+h paste the tab in the first box, and in the second put 4 spaces then press replace all)

I got it working - it actually was because of the tabs/spaces problem. Unfortunately I still have problems with the numbering of the keys.

I have an initialization lightup pattern where each button lights up after each other. The direction of this pattern is actually rotated by 90 degrees. Strangely enough the numbering of the buttons is still not changed when I try to access it with a handler on a keypress.

My code looks like this:

for key in keys:
    @keybow.on_press(key)
    def press_handler(key):
        print(key.number)

If I now press the buttons on the 4x4 matrix from left to right and top to bottom I get the following result: 3, 7, 11, 15, 2, 6, 10, 14, 1, 5, 9, 13, 0, 4, 8, 12

Technically there should now be values from 0 through 15 - what’s wrong with this?

will check this evening to see which interfaces report what, in the meantime, it might help to know which orientation you’re using. the default orientation is
cable right = top(away) row 0(left) to 3(rigtht)
and after 1 default rotation it should be
cable top(away) = top(away) row 0(left) to 3(rigtht)
I’ll assume those are the expectations unless you post a correction

ok, had time to look at it to be sure, and yes that is correct behavior when pinging the key.number interface (how the hardware sees the key), what changes is the key[index] interface (how the user indexing sees the key).

Omitting the last 2 lines of my solution can give you the keys new index on the key.get_number() interface which uses xy_to_number() not used anywhere else so should be safe as long as you aren’t using the key.xy coordinates directly… if you are, instead you’d have to go through key.get_xy() which pulls from the hardware number

all the rotation code does (mine or original) is changes which hardware button is pointed to in the keys[index] list, mine just additionally moves the coordinates around in the list the same way, the original just has that sort at the end that actually undoes it all (not sure what the intention was).

As part of a project idea I’ve been playing around with a more flexible bit of code, but it’s larger and not quite as intuitive (swap_xy, flip_x, and flip_y) so I’ve been hesitant to post it. Unlike the original or the above it actually supports rectangular matrices, and works recursively rather than statically (calling a rotation of a quarter turn twice results in a 180 from original rather than the same position)