#!/usr/bin/env python3
import evdev
from evdev import ecodes
import time
from ble_midi import Peripheral, MidiMessage
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
import os
import sys
DEVICE_PATH = '/dev/input/by-id/usb-Logitech_Logitech_Dual_Action_DF5CB332-event-joystick'
# ----------------------------
# Human-readable button mappings
# ----------------------------
buttons = {
'A': 61, # physical A triggers MIDI note B
'B': 62, # physical B triggers MIDI note X
'X': 60, # physical X triggers MIDI note A
'Y': 63,
'L': 64,
'R': 65,
'Select': 66,
'Start': 67,
'Up': 68,
'Down': 69,
'Left': 70,
'Right': 71
}
evdev_button_map = {
288: 'A', 289: 'B', 290: 'X', 291: 'Y',
292: 'L', 293: 'R', 296: 'Select', 297: 'Start',
294: 'Up', 295: 'Down', 298: 'Left', 299: 'Right'
}
# ----------------------------
# Axis mappings
# ----------------------------
axes = {
'LX': (10, -32768, 32767),
'LY': (11, -32768, 32767),
'LT': (12, 0, 255),
'RT': (13, 0, 255),
'RX': (14, -32768, 32767),
'RY': (15, -32768, 32767)
}
evdev_axis_map = {0: 'LX', 1: 'LY', 2: 'LT', 5: 'RT', 3: 'RX', 4: 'RY'}
# ----------------------------
# BLE MIDI Peripheral
# ----------------------------
ble_peripheral = Peripheral(name="F310 BLE MIDI")
ble_peripheral.start_advertising()
time.sleep(2) # allow time to pair with iPad
def scale(value, min_val, max_val):
return int((value - min_val) / (max_val - min_val) * 127)
# ----------------------------
# Main gamepad loop
# ----------------------------
def run():
gamepad = evdev.InputDevice(DEVICE_PATH)
print(f"Listening on {gamepad.name} ({gamepad.path})")
for event in gamepad.read_loop():
if event.type == ecodes.EV_KEY and event.code in evdev_button_map:
label = evdev_button_map[event.code]
note = buttons[label]
if event.value == 1:
ble_peripheral.send(MidiMessage.note_on(0, note, 112))
print(f"Button pressed: {label}")
elif event.value == 0:
ble_peripheral.send(MidiMessage.note_off(0, note, 0))
elif event.type == ecodes.EV_ABS and event.code in evdev_axis_map:
label = evdev_axis_map[event.code]
cc, min_val, max_val = axes[label]
val = scale(event.value, min_val, max_val)
ble_peripheral.send(MidiMessage.cc(0, cc, val))
# ----------------------------
# Watchdog for hot-reload
# ----------------------------
class ReloadHandler(FileSystemEventHandler):
def on_modified(self, event):
if event.src_path.endswith(os.path.basename(__file__)):
print("Script changed. Restarting...")
os.execv(sys.executable, ['python3'] + sys.argv)
observer = Observer()
observer.schedule(ReloadHandler(), path='.', recursive=False)
observer.start()
# ----------------------------
# Run forever
# ----------------------------
try:
while True:
run()
except KeyboardInterrupt:
observer.stop()
observer.join()
Friday, August 15, 2025
raspberry pi ble to ipad with f310
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment