-
Notifications
You must be signed in to change notification settings - Fork 59
Serial device communication
Artem edited this page Mar 10, 2018
·
2 revisions
Create SerialDeviceMonitor
object globally and run monitor in new thread for listen Serial devices
import Cocoa
import USBDeviceSwift
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
//make sure that cfDeviceMonitor always exist
let cfDeviceMonitor = SerialDeviceMonitor()
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
// Adding own function to filter serial devices that we need
cfDeviceMonitor.filterDevices = {(devices: [SerialDevice]) -> [SerialDevice] in
return devices.filter({$0.vendorId == 1155 && $0.productId == 22336})
}
let cfDeviceDaemon = Thread(target: self.cfDeviceMonitor, selector:#selector(self.cfDeviceMonitor.start), object: nil)
cfDeviceDaemon.start()
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
}
You can also specify a filter function for serial devices to do not receive not required notifications when serial device added or removed.
cfDeviceMonitor.filterDevices = {(devices: [SerialDevice]) -> [SerialDevice] in
return devices.filter({$0.vendorId == 1155 && $0.productId == 22336})
}
Also you can set portUsageIntervalTime
. For example:
let cfDeviceMonitor = SerialDeviceMonitor()
cfDeviceMonitor.portUsageIntervalTime = 1 // Monitoring will be every 1 sec, default: 0.25
note - start
function using RunLoop
that blocks thread don't run monitor in Main
thread
There are two global notifications:
SerialDeviceAdded
SerialDeviceRemoved
Listen them in our ViewController:
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
NotificationCenter.default.addObserver(self, selector: #selector(self.serialDeviceAdded), name: .SerialDeviceAdded, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.serialDeviceRemoved), name: .SerialDeviceRemoved, object: nil)
}
@objc func serialDeviceAdded(notification: NSNotification) {
guard let nobj = notification.object as? NSDictionary else {
return
}
guard let deviceInfo:SerialDevice = nobj["device"] as? SerialDevice else {
return
}
}
@objc func serialDeviceRemoved(notification: NSNotification) {
guard let nobj = notification.object as? NSDictionary else {
return
}
guard let deviceInfo:SerialDevice = nobj["device"] as? SerialDevice else {
return
}
}
}
SerialDeviceAdded
and SerialDeviceRemoved
notifications - return SerialDevice
with all basic info
- path - IOCalloutDeviceKey
- name(optional) - USB Product Name
- vendorName(optional) - USB Vendor Name
- serialNumber(optional) - USB Serial Number
- vendorId(optional) - USB Vendor id
- productId(optional) - USB Product id
After this you need to open connection and manipulate with your device as you want. There are a several libraries that could help with this like:
public enum PortError: Int32, Error {
case failedToOpen = -1 // refer to open()
case invalidPath
case mustReceiveOrTransmit
case mustBeOpen
case stringsMustBeUTF8
}
class CleanFlightDevice {
var deviceInfo:SerialDevice
var fileDescriptor:Int32?
required init(_ deviceInfo:SerialDevice) {
self.deviceInfo = deviceInfo
}
func openPort(toReceive receive: Bool, andTransmit transmit: Bool) throws {
guard !deviceInfo.path.isEmpty else {
throw PortError.invalidPath
}
guard receive || transmit else {
throw PortError.mustReceiveOrTransmit
}
var readWriteParam : Int32
if receive && transmit {
readWriteParam = O_RDWR
} else if receive {
readWriteParam = O_RDONLY
} else if transmit {
readWriteParam = O_WRONLY
} else {
fatalError()
}
fileDescriptor = open(deviceInfo.path, readWriteParam | O_NOCTTY | O_EXLOCK)
// Throw error if open() failed
if fileDescriptor == PortError.failedToOpen.rawValue {
throw PortError.failedToOpen
}
}
public func closePort() {
if let fileDescriptor = fileDescriptor {
close(fileDescriptor)
}
fileDescriptor = nil
}
}
See full example in CleanFlightSerialExample
folder