Skip to content

HID device communication

Artem edited this page Mar 9, 2018 · 1 revision

Create HIDDeviceMonitor object globally, set vid and pid of devices and reportSize for communication and run monitor in new thread for listen HID devices


import Cocoa
import USBDeviceSwift

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
    //make sure that rfDeviceMonitor always exist
    let rfDeviceMonitor = HIDDeviceMonitor([
        HIDMonitorData(vendorId: 0x0483, productId: 0x5742)
        ], reportSize: 64)


    func applicationDidFinishLaunching(_ aNotification: Notification) {
        // Insert code here to initialize your application
        
        let rfDeviceDaemon = Thread(target: self.rfDeviceMonitor, selector:#selector(self.rfDeviceMonitor.start), object: nil)
        rfDeviceDaemon.start()
    }

}

note - start function using RunLoop that blocks thread don't run monitor in Main thread

There are three global notifications:

HIDDeviceDataReceived

HIDDeviceConnected

HIDDeviceDisconnected

Listen them in our ViewController:

class ViewController: NSViewController {    
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
    
    var connectedDevice:RFDevice?
    var devices:[RFDevice] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Do any additional setup after loading the view.
        
        NotificationCenter.default.addObserver(self, selector: #selector(self.usbConnected), name: .HIDDeviceConnected, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(self.usbDisconnected), name: .HIDDeviceDisconnected, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(self.hidReadData), name: .HIDDeviceDataReceived, object: nil)
    }

    
    func usbConnected(notification: NSNotification) {
        guard let nobj = notification.object as? NSDictionary else {
            return
        }
        
        guard let deviceInfo:HIDDevice = nobj["device"] as? HIDDevice else {
            return
        }
    }
    
    func usbDisconnected(notification: NSNotification) {
        guard let nobj = notification.object as? NSDictionary else {
            return
        }
        
        guard let id:String = nobj["id"] as? String else {
            return
        }
    }
    
    func hidReadData(notification: Notification) {
        let obj = notification.object as! NSDictionary
        let data = obj["data"] as! Data
    }
    
}

HIDDevice has all basic info:

id - kIOHIDLocationIDKey - property converted to `String

vendorId - kIOHIDVendorIDKey - property converted to Int

productId - kIOHIDProductIDKey - property converted to Int

reportSize - device specific

device - reference to IOHIDDevice

name - kIOHIDProductKey - property converted to String

To get additional properites from device use IOHIDDeviceGetProperty. Example:

IOHIDDeviceGetProperty(HIDDevice.device, kIOHIDMaxInputReportSizeKey as CFString) as? Int

Send command to USB device example:

func write(_ data: Data) {
        var bytesArray = [UInt8](data)
        let reportId:UInt8 = 2
        bytesArray.insert(reportId, at: 0)
        bytesArray.append(0)// hack every report should end with 0 byte
        
        if (bytesArray.count > self.deviceInfo.reportSize) {
            print("Output data too large for USB report")
            return
        }
        
        let correctData = Data(bytes: UnsafePointer<UInt8>(bytesArray), count: self.deviceInfo.reportSize)
        
        IOHIDDeviceSetReport(
            self.deviceInfo.device,
            kIOHIDReportTypeOutput,
            CFIndex(reportId),
            (correctData as NSData).bytes.bindMemory(to: UInt8.self, capacity: correctData.count),
            correctData.count
        )
    }

See full example in RaceflightControllerHIDExample folder

Clone this wiki locally