Hi all,
I'm trying to program the Adafruit Rotary Trinkey to send HID messages to a Sound Devices Mixpre 10II recorder for controlling volumes.
The code itself works as expected on Windows both through the Arduino IDE code and Circuit Python Code without any issues. However, the device I'm trying to use this for isn't getting these messages despite working with other standard USB keyboards. The Arduino version boots but the unit never responds to the HID messages while the Circuit Python Code fails as soon as a message is sent and goes into two red blink mode (Ended with exception).
I've read others have trouble here because the USB Descriptor for the Mixpre is unique and they had trouble getting it to recognize the chip. However, there is a working arduino controller out there for the MixPre so I know there is some Arduino solution here I don't know about.
Is anyone familiar with working around issues with USB Descriptors to allow communication with tricky devices that you can't easily connect to and debug? Perhaps it requires a custom HID program and using voltages instead of relying on USB_HID or HID-Project? Clearly there is something wrong with the handshake between this Rotary Trinkey and this specific device and I'm hoping someone with experience here can lead me to the right solutions.
Here was a previous post that ended up unresolved discussing this: Rotary Encoder confusion - #5 by encodurrr
None of the following made it work with the mixpre: Bootkeyboard and keyboard both fail to send, delays didn't help it recognize the device, US and ASCII layouts both failed, trying write or press calls had no effect.
Here is a previous forum post that went unresolved.
CIRCUIT PYTHON CODE:
import time
import rotaryio import digitalio import board import neopixel import usb_hid import touchio
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keycode import Keycode from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS
# Windows mouse test
from adafruit_hid.mouse import Mouse mouse = Mouse(usb_hid.devices)
# time.sleep(.5)
usb_hid.enable((usb_hid.Device.KEYBOARD,)) # Enable just KEYBOARD now in boot
touch = touchio.TouchIn(board.TOUCH)
pixel = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=1.0) encoder = rotaryio.IncrementalEncoder(board.ROTA, board.ROTB)
switch = digitalio.DigitalInOut(board.SWITCH)
switch.switch_to_input(pull=digitalio.Pull.DOWN)
kbd = Keyboard(usb_hid.devices)
keyboard_layout = KeyboardLayoutUS(kbd)
print("Success")
switch_state = None
touch_state = None last_position = encoder.position
while True:
time.sleep(.1) # .1 feels smoother visually, .02 is seizure inducing
pixel.fill((50, 50, 50)) # Default low white
current_position = encoder.position
position_change = current_position - last_position
if position_change > 0:
for _ in range(position_change):
kbd.send(Keycode.UP_ARROW) # Windows functional up arrow.
# kbd.send(0x60) # Raw HID works on windows, fails mixpre as well
# kbd.press(Keycode.UP) # Wrong message
# kbd.release_all()
#mouse.move(1, 1, 0) # mouse right movement and up
pixel.fill((0, 255, 255)) # Purple
print(f"rotate A:{current_position}, switch:{switch.value}")
print(f"rotate A:touch:{touch.value}")
elif position_change < 0:
for _ in range(-position_change):
kbd.send(Keycode.DOWN_ARROW) # Windows functional down arrow.
# kbd.send(0x5A) # Raw HID works on windows, fails mixpre as well
# kbd.press(Keycode.DOWN) # Wrong message
# kbd.release_all()
# mouse.move(1, 1, 0) # mouse left movement and up
pixel.fill((180, 0, 250)) # Blue
print(f"rotate B:{current_position}, switch:{switch.value}")
print(f"rotateB:touch:{touch.value}")
last_position = current_position
if not switch.value and switch_state is None:
switch_state = "pressed"
if switch.value and switch_state == "pressed":
print("switch pressed.")
kbd.send(Keycode.ENTER)
pixel.fill((255, 99, 16)) # orange
time.sleep(.2) # Extra delay for visual accuracy on confirmation
switch_state = None
if not touch.value and touch_state is None:
touch_state = "pressed"
mouse.release(Mouse.LEFT_BUTTON) #Release
if touch.value and touch_state == "pressed":
print("Pad touched!")
kbd.send(Keycode.ESCAPE)
# mouse.click(Mouse.LEFT_BUTTON) single press
# mouse.press(Mouse.LEFT_BUTTON) #Press and hold mouse left. Ignores plugins popups somehow
pixel.fill((255, 255, 0)) # yellow
time.sleep(.08) # Extra delay for visual accuracy
touch_state = None
ARDUINO CODE
#include <Adafruit_NeoPixel.h>
#include <Adafruit_FreeTouch.h>
#include <RotaryEncoder.h>
#define HID_CUSTOM_LAYOUT
#define LAYOUT_UNITED_KINGDOM
#include <HID-Project.h>
//#include <Keyboard.h>
//#include <KeyboardButton.h>
#define PIN_ENCODER_A 1
#define PIN_ENCODER_B 2
#define PIN_ENCODER_SWITCH 3
// Create the neopixel strip with the built in definitions NUM_NEOPIXEL and PIN_NEOPIXEL
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_NEOPIXEL, PIN_NEOPIXEL, NEO_GRB + NEO_KHZ800);
int16_t neo_brightness = 22; // initialize with 22 brightness (out of 255)
RotaryEncoder encoder(PIN_ENCODER_A, PIN_ENCODER_B, RotaryEncoder::LatchMode::FOUR3);
// This interrupt will do our encoder reading/checking!
void checkPosition() {
encoder.tick(); // just call tick() to check the state.
}
uint8_t wheel_offset = 99;
int last_rotary = 0;
void setup() {
//USBDevice.detach();
//Serial.end();
//BootKeyboard.begin();
Keyboard.begin();
//Serial.begin(115200);
//Serial failed with 9600 115200 and 250000
//while (!Serial);
// start neopixels
strip.begin();
strip.setBrightness(neo_brightness);
strip.show(); // Initialize all pixels to 'off'
attachInterrupt(PIN_ENCODER_A, checkPosition, CHANGE);
attachInterrupt(PIN_ENCODER_B, checkPosition, CHANGE);
// set up the encoder switch, which is separate from the encoder
pinMode(PIN_ENCODER_SWITCH, INPUT_PULLDOWN);
}
void loop() {
// read encoder
int curr_rotary = encoder.getPosition();
RotaryEncoder::Direction direction = encoder.getDirection();
//delay test
delay(10);
if (curr_rotary != last_rotary) {
Serial.print("Encoder value: ");
Serial.print(curr_rotary);
Serial.print(" direction: ");
Serial.print((int)direction);
// behavior differs if switch is pressed
if (!digitalRead(PIN_ENCODER_SWITCH)) {
// update color
if (direction == RotaryEncoder::Direction::CLOCKWISE) {
wheel_offset++;
Keyboard.write(KEY_UP_ARROW);
//BootKeyboard.write(HID_KEYBOARD_UPARROW);
//Keyboard.press(KEY_UP_ARROW); //Press needs release all but write handles both messages
delay(50);
//Keyboard.releaseAll();
}
if (direction == RotaryEncoder::Direction::COUNTERCLOCKWISE) {
wheel_offset--;
Keyboard.write(KEY_DOWN_ARROW);
//BootKeyboard.write(HID_KEYBOARD_DOWNARROW);
//Keyboard.press(KEY_DOWN_ARROW);
delay(50);
//Keyboard.releaseAll();
}
} else {
// update brightness
if (direction == RotaryEncoder::Direction::CLOCKWISE) {
neo_brightness += 10;
}
if (direction == RotaryEncoder::Direction::COUNTERCLOCKWISE) {
neo_brightness -= 10;
}
// ranges between 0 and 255
if (neo_brightness > 255) neo_brightness = 255;
if (neo_brightness < 0) neo_brightness = 0;
}
Serial.print(" wheel color: ");
Serial.print(wheel_offset);
Serial.print(" brightness: ");
Serial.println(neo_brightness);
last_rotary = curr_rotary;
// update pixels!
strip.setBrightness(neo_brightness);
strip.setPixelColor(0, Wheel(wheel_offset));
strip.show();
}
}
// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
if(WheelPos < 85) {
return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
} else if(WheelPos < 170) {
WheelPos -= 85;
return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
} else {
WheelPos -= 170;
return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
}