15.8K Views
September 03, 23
スライド概要
iOSDC Japan 2023 Day 2 Lightning Talkの発表資料です。
https://fortee.jp/iosdc-japan-2023/proposal/b48dda55-90e2-4da8-a11e-cd21b5e8ebc5
CircuitPythonとSwift Playgroundsのコード
https://github.com/bricklife/MIDI-Servo-Control/tree/main
SwiftとLEGOとBluetooth LEが好きなプログラマ
MFi認証不要! iPhoneとMIDIで双方向通信できる USBデバイスを作ろう 大庭 慎一郎 @ooba / @bricklife iOSDC Japan 2023 - Lightning Talk
自己紹介 大庭 慎一郎(おおば しんいちろう) ID: @ooba / @bricklife SwiftとLEGOが好きなプログラマ メルカリ1人目のiOSアプリエンジニア
Scratch専用ブラウザ「Scrub」 https://apps.apple.com/jp/app/scrub-web-browser/id1569777095
Scrubから制御できる 教育向けのUSBデバイス を作りたい
教育向けデバイスといえばBBC micro:bit https://microbit.org/ iPadやScratchとBluetooth 接続をしてプログラミング
Bluetooth接続だとつらいところ ペアリングが必要 電源が必要 複数台あると混乱の元 →トラブルが起きやすい
もしUSB接続できたら USBケーブルで繋げるだけ 電源もUSBから供給 確実に1対1接続できる →トラブルが起きづらい
iPhoneやiPadにUSB接続できるデバイス MFi認証を受けているデバイス MFi認証が不要な一般的なUSBデバイス
MFi認証デバイス シリアル通信(UART)による独 自プロトコルだとMFi認証が必須 認証の費用 Appleデバイス向けの専用実装 # UARTは現在新規申請不可?
MFi認証が不要なUSBデバイス 種類 デバイスの例 HID キーボード、マウス、ゲームパッド、etc UAC マイク、オーディオインターフェイス、etc MSC USBメモリ、HDD、etc UVC Webカメラ、ビデオスイッチャー、etc MIDI 鍵盤、音源、etc 入力 出力 ○ ○ ○ ○ ○ ○ API iOS 17+ ○ ○
MIDIデバイスのフリを すればよさそう!
MIDIメッセージを別の意味とし てやりとりする
ドの音を送信でLED点灯 iPhone /iPad USBデバイス MIDIメッセージ
ピッチベンドでサーボが回転 iPhone /iPad USBデバイス MIDIメッセージ
ボタンが押されたらファの音を受信 iPhone /iPad USBデバイス MIDIメッセージ
MIDIメッセージはたくさんの種類がある チャンネルメッセージ システムメッセージ チャンネルボイスメッ チャンネルモードメッ システムエクスクルー セージ セージ シブメッセージ システムコモンメッ セージ ノートオフ ノートオン MTCクォーターフレー タイミングクロック スタート ムメッセージ ソングポジションポイ コンティニュー ストップ ンタ アクティブセンシング ソングセレクト システムリセット チューンリクエスト オールサウンドオフ SysEx リセットオールコント ローラ ポリフォニック キー ローカルコントロール プレッシャー コントロールチェンジ オールノートオフ MIDIモード設定 プログラムチェンジ チャンネルプレッ シャー ピッチベンド エンドオブエクスク ルーシブ (EOX) システムリアルタイム メッセージ
実際に作ってみた
キリンさんのカタパルト
システム構成 FT90B (3V動作サーボ) iPad Pro Raspberry Pi Pico
Swift Playgroundsによるデモ
作り方
USBデバイス側 最近のマイコンは簡単に USB MIDIデバイスになれる プログラミング環境も色々 今回はRaspberry Pi Picoと CircuitPythonを利用
CircuitPythonのコード import board import usb_midi import adafruit_midi from adafruit_midi.pitch_bend import PitchBend import pwmio from adafruit_motor import servo midi = adafruit_midi.MIDI(midi_in=usb_midi.ports[0], midi_out=usb_midi.ports[1], debug=True) pins = [board.GP15, board.GP16] servos = [] for pin in pins: pwm = pwmio.PWMOut(pin, frequency=50) servos.append(servo.Servo(pwm, actuation_range=180, min_pulse=500, max_pulse=2500)) # FT90B while True: msg = midi.receive() if isinstance(msg, PitchBend): if msg.channel < len(servos) and msg.pitch_bend <= servos[msg.channel].actuation_range: servos[msg.channel].angle = msg.pitch_bend
iOS側 Core MIDIフレームワーク 全Appleデバイスで使える C言語ベースのAPIはつらい
Core MIDIのコード init() { MIDIClientCreateWithBlock("Client" as CFString, &clientRef) { [weak self] _ in Task { @MainActor in self?.updateDestination() } } MIDIOutputPortCreate(clientRef, "Output" as CFString, &outputPortRef) } updateDestination()
Core MIDIのコード
func updateDestination() {
let numberOfDestinations = MIDIGetNumberOfDestinations()
for i in 0 ..< numberOfDestinations {
let ref = MIDIGetDestination(i)
var value: Unmanaged<CFString>?
MIDIObjectGetStringProperty(ref, kMIDIPropertyManufacturer, &value)
let manufacturer = value?.takeRetainedValue() as String?
if manufacturer == "Raspberry Pi" {
destinationRef = ref
isConnected = true
return
}
}
}
destinationRef = nil
isConnected = false
Core MIDIのコード func sendPitchBend(channel: UInt, pitchBend: UInt) { guard channel <= 0x0f, pitchBend <= 0x3fff else { return } let message = MIDI1UPPitchBend(0, UInt8(channel), UInt8(pitchBend & 0x7f), UInt8(pitchBend >> 7)) } sendMIDI1Message(message)
Core MIDIのコード func sendMIDI1Message(_ message: MIDIMessage_32) { guard let destinationRef else { return } var packet = MIDIEventPacket() packet.words.0 = message packet.wordCount = 1 var eventList = MIDIEventList(protocol: ._1_0, numPackets: 1, packet: packet) } MIDISendEventList(outputPortRef, destinationRef, &eventList)
これは「MIDIデバイス」か?
Akadako https://akadako.com/
もっと知りたい人のためのキーワード MIDIの規格 - JIS X 6054-1、JIS X 6054-2、IEC63035:2017 Firmata - MIDIメッセージベースのプロトコル SysExメッセージ - 独自の可変長データを送るためのMIDIメッセージ
ありがとうございました