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:
12
ControlMixerPython/boot.py
Normal file
12
ControlMixerPython/boot.py
Normal 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")
|
||||
5
ControlMixerPython/boot_out.txt
Normal file
5
ControlMixerPython/boot_out.txt
Normal 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
|
||||
[2K[0Gboot.py output:
|
||||
Booting normal mode
|
||||
63
ControlMixerPython/code.py
Normal file
63
ControlMixerPython/code.py
Normal 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)
|
||||
40
ControlMixerPython/key_interface.py
Normal file
40
ControlMixerPython/key_interface.py
Normal 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
|
||||
|
||||
BIN
ControlMixerPython/lib/adafruit_bus_device/i2c_device.mpy
Normal file
BIN
ControlMixerPython/lib/adafruit_bus_device/i2c_device.mpy
Normal file
Binary file not shown.
BIN
ControlMixerPython/lib/adafruit_bus_device/spi_device.mpy
Normal file
BIN
ControlMixerPython/lib/adafruit_bus_device/spi_device.mpy
Normal file
Binary file not shown.
BIN
ControlMixerPython/lib/adafruit_dotstar.mpy
Normal file
BIN
ControlMixerPython/lib/adafruit_dotstar.mpy
Normal file
Binary file not shown.
BIN
ControlMixerPython/lib/adafruit_husb238.mpy
Normal file
BIN
ControlMixerPython/lib/adafruit_husb238.mpy
Normal file
Binary file not shown.
BIN
ControlMixerPython/lib/adafruit_mcp230xx/__init__.mpy
Normal file
BIN
ControlMixerPython/lib/adafruit_mcp230xx/__init__.mpy
Normal file
Binary file not shown.
BIN
ControlMixerPython/lib/adafruit_mcp230xx/digital_inout.mpy
Normal file
BIN
ControlMixerPython/lib/adafruit_mcp230xx/digital_inout.mpy
Normal file
Binary file not shown.
BIN
ControlMixerPython/lib/adafruit_mcp230xx/mcp23008.mpy
Normal file
BIN
ControlMixerPython/lib/adafruit_mcp230xx/mcp23008.mpy
Normal file
Binary file not shown.
BIN
ControlMixerPython/lib/adafruit_mcp230xx/mcp23016.mpy
Normal file
BIN
ControlMixerPython/lib/adafruit_mcp230xx/mcp23016.mpy
Normal file
Binary file not shown.
BIN
ControlMixerPython/lib/adafruit_mcp230xx/mcp23017.mpy
Normal file
BIN
ControlMixerPython/lib/adafruit_mcp230xx/mcp23017.mpy
Normal file
Binary file not shown.
BIN
ControlMixerPython/lib/adafruit_mcp230xx/mcp230xx.mpy
Normal file
BIN
ControlMixerPython/lib/adafruit_mcp230xx/mcp230xx.mpy
Normal file
Binary file not shown.
BIN
ControlMixerPython/lib/adafruit_mcp230xx/mcp23s08.mpy
Normal file
BIN
ControlMixerPython/lib/adafruit_mcp230xx/mcp23s08.mpy
Normal file
Binary file not shown.
BIN
ControlMixerPython/lib/adafruit_mcp230xx/mcp23s17.mpy
Normal file
BIN
ControlMixerPython/lib/adafruit_mcp230xx/mcp23s17.mpy
Normal file
Binary file not shown.
BIN
ControlMixerPython/lib/adafruit_mcp230xx/mcp23sxx.mpy
Normal file
BIN
ControlMixerPython/lib/adafruit_mcp230xx/mcp23sxx.mpy
Normal file
Binary file not shown.
BIN
ControlMixerPython/lib/adafruit_mcp230xx/mcp23xxx.mpy
Normal file
BIN
ControlMixerPython/lib/adafruit_mcp230xx/mcp23xxx.mpy
Normal file
Binary file not shown.
BIN
ControlMixerPython/lib/adafruit_register/i2c_bcd_alarm.mpy
Normal file
BIN
ControlMixerPython/lib/adafruit_register/i2c_bcd_alarm.mpy
Normal file
Binary file not shown.
BIN
ControlMixerPython/lib/adafruit_register/i2c_bcd_datetime.mpy
Normal file
BIN
ControlMixerPython/lib/adafruit_register/i2c_bcd_datetime.mpy
Normal file
Binary file not shown.
BIN
ControlMixerPython/lib/adafruit_register/i2c_bit.mpy
Normal file
BIN
ControlMixerPython/lib/adafruit_register/i2c_bit.mpy
Normal file
Binary file not shown.
BIN
ControlMixerPython/lib/adafruit_register/i2c_bits.mpy
Normal file
BIN
ControlMixerPython/lib/adafruit_register/i2c_bits.mpy
Normal file
Binary file not shown.
BIN
ControlMixerPython/lib/adafruit_register/i2c_struct.mpy
Normal file
BIN
ControlMixerPython/lib/adafruit_register/i2c_struct.mpy
Normal file
Binary file not shown.
BIN
ControlMixerPython/lib/adafruit_register/i2c_struct_array.mpy
Normal file
BIN
ControlMixerPython/lib/adafruit_register/i2c_struct_array.mpy
Normal file
Binary file not shown.
1
ControlMixerPython/sd/placeholder.txt
Normal file
1
ControlMixerPython/sd/placeholder.txt
Normal file
@@ -0,0 +1 @@
|
||||
SD cards mounted at /sd will hide this file from Python. SD cards are not visible via USB CIRCUITPY.
|
||||
0
ControlMixerPython/settings.toml
Normal file
0
ControlMixerPython/settings.toml
Normal file
86
ControlMixerPython/usb_mixer.py
Normal file
86
ControlMixerPython/usb_mixer.py
Normal 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)
|
||||
Reference in New Issue
Block a user