Friday, August 15, 2025

raspberry pi ble to ipad with f310


#!/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()

No comments:

Post a Comment