App sync code, handles button presses from mixer and syncs changes from VM. Included python code from the mixer. Some other PCB changes, but still needs improvements

This commit is contained in:
2024-05-16 23:42:27 -05:00
parent efdbb11f92
commit 700e87f807
39 changed files with 4246 additions and 3514 deletions

View File

@@ -0,0 +1,12 @@
import board
import digitalio
import usb_mixer
recovery_pin = digitalio.DigitalInOut(board.D3)
recovery_pin.direction = digitalio.Direction.INPUT
recovery_pin.pull = digitalio.Pull.UP
if recovery_pin.value == True:
print("Booting normal mode")
usb_mixer.initUsb()
else:
print("Booting recovery mode")

View File

@@ -0,0 +1,5 @@
Adafruit CircuitPython 9.0.4 on 2024-04-16; Adafruit ItsyBitsy M4 Express with samd51g19
Board ID:itsybitsy_m4_express
UID:A9C26FCF53324853202020472A3908FF
boot.py output:
Booting normal mode

View File

@@ -0,0 +1,63 @@
import usb_mixer
import board
import adafruit_dotstar
import analogio
import time
import math
import ulab.numpy
import supervisor
import key_interface
import storage
ANALOG_SAMPLE_SIZE=150
pixel = adafruit_dotstar.DotStar(board.DOTSTAR_CLOCK, board.DOTSTAR_DATA, 1)
slider_in = [
analogio.AnalogIn(board.A0),
analogio.AnalogIn(board.A1),
analogio.AnalogIn(board.A2),
analogio.AnalogIn(board.A3),
analogio.AnalogIn(board.A4)
]
i2c = board.I2C()
keys = key_interface.KeyInterface(i2c)
slider_vals = [0,0,0,0,0]
buttons = [0,0,0,0,0]
def readSlider(num):
vals = []
for i in range(0, ANALOG_SAMPLE_SIZE):
vals.append((slider_in[num].value / 0xFFFF) * 0xFFF) # Scale analogio back to ADC resolution
val_median = ulab.numpy.mean(ulab.numpy.ndarray(vals))
return math.floor((val_median / 0xFFF) * usb_mixer.RESOLUTION)
try:
pixel.fill((0,0,32))
while not supervisor.runtime.usb_connected:
time.sleep(0.5)
mixer = usb_mixer.UsbMixer()
pixel.fill((0,32,0))
while True:
new_values = [
readSlider(0),
readSlider(1),
readSlider(2),
readSlider(3),
readSlider(4)
]
new_buttons = keys.scan()
if new_values != slider_vals or new_buttons != buttons:
mixer.send_values(new_values[0], new_values[1], new_values[2], new_values[3], new_values[4], new_buttons)
pixel.fill((32,32,32))
slider_vals = new_values
buttons = new_buttons
pixel.fill((0,32,0))
time.sleep(0.001)
except Exception as ex:
pixel.fill((255,0,0))
with open("/runtime_ex.txt", "w") as fp:
fp.write(ex)
print(ex)

View File

@@ -0,0 +1,40 @@
import board
import busio
import digitalio
from adafruit_mcp230xx.mcp23017 import MCP23017
class KeyInterface:
_MCP23017_OLATA = 0x14
_MCP23017_OLATB = 0x15
def set_olata(self, val: int):
self.mcp._write_u8(self._MCP23017_OLATA, val)
def set_olatb(self, val: int):
self.mcp._write_u8(self._MCP23017_OLATB, val)
def __init__(self, i2c):
self._i2c = i2c
# Turn on MCP
self._mcp_reset_pin = digitalio.DigitalInOut(board.D4)
self._mcp_reset_pin.direction = digitalio.Direction.OUTPUT
self._mcp_reset_pin.value = True
# Initialize MCP
self.mcp = MCP23017(self._i2c)
# Set up MCP pins
self.mcp.iodirb = 255 # GPIO B (rows) all input
self.mcp.ipolb = 255 # GPIO B inverted polarity
self.mcp.gppub = 255 # GPIO B all pulled up
self.set_olata(0) # GPIO A all output low
def scan(self):
result = []
for i in range(0, 6):
self.mcp.iodira = 0xFF ^ (0x01 << i)
result.append(self.mcp.gpiob)
self.mcp.iodira = 0xFF
return result

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1 @@
SD cards mounted at /sd will hide this file from Python. SD cards are not visible via USB CIRCUITPY.

View File

View File

@@ -0,0 +1,86 @@
import usb_hid
import supervisor
import storage
import usb_midi
DEFAULT_REPORT_ID = 1
HANDSHAKE_REPORT_ID = 2
VENDOR_ID = 0x0359
PRODUCT_ID = 0x6497 # T9 MIXR
RESOLUTION = 100 # Voicemeeter sliders go from 12.0 to -60.0 in 0.1 increments
REPORT_DESCRIPTOR = bytes([
0x05, 0x01, # Usage Page (Generic Desktop Controls)
0x09, 0x08, # Usage (Multi-Axis Controller)
#0x09, 0x04, # Joystick usage (for windows to pick up as usb controller)
0xA1, 0x01, # Collection (Application)
# BUTTONS
0x05, 0x09, # Usage Page (Button)
0x19, 0x01, # Usage Minimum (Button 1)
0x29, 0x30, # Usage Maximum (Button 48)
0x15, 0x00, # Logical Minimum (0)
0x25, 0x01, # Logical Maximum (1)
0x75, 0x01, # Report Size (1 bit)
0x95, 0x30, # Report Count (48)
0x81, 0x02, # Input (Data, Variable, Absolute)
# SLIDERS
0x15, 0x00, # Logical Minimum (0)
0x26, 0xFF, 0xFF, # Logical Maximum (RESOLUTION (720))
0x75, 0x10, # Report Size (16-bit)
#0x85, DEFAULT_REPORT_ID, # Report ID (DEFAULT_REPORT_ID)
0x95, 0x05, # Report Count (5)
0x09, 0x30, # Usage (X)
0x09, 0x31, # Usage (Y)
0x09, 0x32, # Usage (Z)
0x09, 0x33, # Usage (Rx)
0x09, 0x34, # Usage (Ry)
0x81, 0x00, # Input (Data, Array, Absolute)
0xC0, # End Collection
#0x05, 0x06, # Usage Page (Generic Device Controls)
#0x09, 0x01, # Usage (Background/Nonuser controls)
#0xA1, 0x01, # Collection (Application)
#0x75, 0x20, # Report Size (32-bit)
#0x95, 0x01, # Report Count (1)
#0x85, HANDSHAKE_REPORT_ID,# Report ID (HANDSHAKE_REPORT_ID)
#0x81, 0x01, # Input (Constant, Array, Absolute)
#0xC0, # End Collection
])
def initUsb():
if supervisor.runtime.usb_connected:
raise RuntimeError("USB cannot be initialized post-boot")
supervisor.set_usb_identification(manufacturer="TechAbsol", product="USB Mixer", vid=VENDOR_ID, pid=PRODUCT_ID)
usb_midi.disable()
usb_hid.set_interface_name("CircuitPython Mixer")
sliders = usb_hid.Device(
report_descriptor = REPORT_DESCRIPTOR,
usage_page=0x01,
usage=0x08,
report_ids=(0,),
in_report_lengths=(16,),
out_report_lengths=(0,),
)
#storage.disable_usb_drive()
#storage.remount("/", False)
usb_hid.enable((sliders,))
class UsbMixer():
def __init__(self):
if not supervisor.runtime.usb_connected:
raise RuntimeError("USB is not connected")
self._device = next((dev for dev in usb_hid.devices if dev.usage_page == 0x01 and dev.usage == 0x08))
if self._device is None:
raise RuntimeError("USB mixer doesn't appear to be initialized")
def _valToBytes(self, value):
return bytes([value & 0xFF, (value >> 8) & 0xFF])
def _keysToBytes(self, keys):
return bytes([keys[0], keys[1], keys[2], keys[3], keys[4], keys[5]])
def send_values(self, slider1, slider2, slider3, slider4, slider5, keys):
report = self._keysToBytes(keys) + self._valToBytes(slider1) + self._valToBytes(slider2) + self._valToBytes(slider3) + self._valToBytes(slider4) + self._valToBytes(slider5)
self._device.send_report(report)