It isn’t necessary to bring in a pod just to talk to the serial port. On macOS, like any UNIX-based system, the serial port is exposed as a character device file. You can access that using UNIX standard
open
,
close
,
read
and
write
system calls. Calling those from Swift is a bit challenging, so here’s the simplest example:
import Foundation
let fd = open("/dev/cu.usbmodem12345671", O_RDWR)
assert(fd >= 0)
let bytesWritten = write(fd, "AT\r\n", 4)
assert(bytesWritten == 3)
var dataRead = [UInt8](repeating: 0, count: 32)
let bytesRead = read(fd, &dataRead, dataRead.count)
assert(bytesRead >= 0)
dataRead.removeLast(32 - bytesRead)
print(dataRead) // prints: [65, 84, 13, 13, 10, 79, 75, 13, 10]
let junk = close(fd)
assert(junk == 0)
Some things to note:
If you interpret the output from line 14 as ASCII, you get
AT\r\r\nOK\r\n
, which is what you’d expect.In line 3 I’ve hard-coded the character device path. In a real app you’d want to discover the serial port in the I/O Registry, which is an entirely separate kettle of (complex) fish.
I’m talking to a USB modem, so I’ve sent it a simple
AT
command with a trailing CR (\r
) and LF (\n
). In a real app you’d want to wrap write
with something that accepts a Data
value. Here’s an example of what that might look like:
func write(fd: Int32, data: Data) throws -> Int {
let dataCount = data.count
return try data.withUnsafeBytes { (dataPtr: UnsafePointer<UInt8>) -> Int in
let bytesWritten = write(fd, dataPtr, dataCount)
guard bytesWritten >= 0 else {
let err = errno
throw NSError(domain: NSPOSIXErrorDomain, code: Int(err), userInfo: nil)
}
return Int(bytesWritten)
}
}
.
Lines 9 and 10 read the data into a
[UInt8]
. Again, you’d probably want a wrapper that works in terms of Data
.
Good luck!
Share and Enjoy
—
Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
let myEmail = "eskimo" + "1" + "@apple.com"