SwiftSerial.swift 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  1. import Foundation
  2. public enum BaudRate {
  3. case baud0
  4. case baud50
  5. case baud75
  6. case baud110
  7. case baud134
  8. case baud150
  9. case baud200
  10. case baud300
  11. case baud600
  12. case baud1200
  13. case baud1800
  14. case baud2400
  15. case baud4800
  16. case baud9600
  17. case baud19200
  18. case baud38400
  19. case baud57600
  20. case baud115200
  21. case baud230400
  22. #if os(Linux)
  23. case baud460800
  24. case baud500000
  25. case baud576000
  26. case baud921600
  27. case baud1000000
  28. case baud1152000
  29. case baud1500000
  30. case baud2000000
  31. case baud2500000
  32. case baud3500000
  33. case baud4000000
  34. #endif
  35. var speedValue: speed_t {
  36. switch self {
  37. case .baud0:
  38. return speed_t(B0)
  39. case .baud50:
  40. return speed_t(B50)
  41. case .baud75:
  42. return speed_t(B75)
  43. case .baud110:
  44. return speed_t(B110)
  45. case .baud134:
  46. return speed_t(B134)
  47. case .baud150:
  48. return speed_t(B150)
  49. case .baud200:
  50. return speed_t(B200)
  51. case .baud300:
  52. return speed_t(B300)
  53. case .baud600:
  54. return speed_t(B600)
  55. case .baud1200:
  56. return speed_t(B1200)
  57. case .baud1800:
  58. return speed_t(B1800)
  59. case .baud2400:
  60. return speed_t(B2400)
  61. case .baud4800:
  62. return speed_t(B4800)
  63. case .baud9600:
  64. return speed_t(B9600)
  65. case .baud19200:
  66. return speed_t(B19200)
  67. case .baud38400:
  68. return speed_t(B38400)
  69. case .baud57600:
  70. return speed_t(B57600)
  71. case .baud115200:
  72. return speed_t(B115200)
  73. case .baud230400:
  74. return speed_t(B230400)
  75. #if os(Linux)
  76. case .baud460800:
  77. return speed_t(B460800)
  78. case .baud500000:
  79. return speed_t(B500000)
  80. case .baud576000:
  81. return speed_t(B576000)
  82. case .baud921600:
  83. return speed_t(B921600)
  84. case .baud1000000:
  85. return speed_t(B1000000)
  86. case .baud1152000:
  87. return speed_t(B1152000)
  88. case .baud1500000:
  89. return speed_t(B1500000)
  90. case .baud2000000:
  91. return speed_t(B2000000)
  92. case .baud2500000:
  93. return speed_t(B2500000)
  94. case .baud3500000:
  95. return speed_t(B3500000)
  96. case .baud4000000:
  97. return speed_t(B4000000)
  98. #endif
  99. }
  100. }
  101. }
  102. public enum DataBitsSize {
  103. case bits5
  104. case bits6
  105. case bits7
  106. case bits8
  107. var flagValue: tcflag_t {
  108. switch self {
  109. case .bits5:
  110. return tcflag_t(CS5)
  111. case .bits6:
  112. return tcflag_t(CS6)
  113. case .bits7:
  114. return tcflag_t(CS7)
  115. case .bits8:
  116. return tcflag_t(CS8)
  117. }
  118. }
  119. }
  120. public enum ParityType {
  121. case none
  122. case even
  123. case odd
  124. var parityValue: tcflag_t {
  125. switch self {
  126. case .none:
  127. return 0
  128. case .even:
  129. return tcflag_t(PARENB)
  130. case .odd:
  131. return tcflag_t(PARENB | PARODD)
  132. }
  133. }
  134. }
  135. public enum PortError: Int32, Error {
  136. case failedToOpen = -1 // refer to open()
  137. case invalidPath
  138. case mustReceiveOrTransmit
  139. case mustBeOpen
  140. case stringsMustBeUTF8
  141. case unableToConvertByteToCharacter
  142. case deviceNotConnected
  143. }
  144. public class SerialPort {
  145. var path: String
  146. var fileDescriptor: Int32?
  147. private var pollSource: DispatchSourceRead?
  148. private var readDataStream: AsyncStream<Data>?
  149. private var readBytesStream: AsyncStream<UInt8>?
  150. private var readLinesStream: AsyncStream<String>?
  151. public init(path: String) {
  152. self.path = path
  153. }
  154. public func openPort() throws {
  155. try openPort(toReceive: true, andTransmit: true)
  156. }
  157. public func openPort(toReceive receive: Bool, andTransmit transmit: Bool) throws {
  158. guard !path.isEmpty else {
  159. throw PortError.invalidPath
  160. }
  161. guard receive || transmit else {
  162. throw PortError.mustReceiveOrTransmit
  163. }
  164. var readWriteParam : Int32
  165. if receive && transmit {
  166. readWriteParam = O_RDWR
  167. } else if receive {
  168. readWriteParam = O_RDONLY
  169. } else if transmit {
  170. readWriteParam = O_WRONLY
  171. } else {
  172. fatalError()
  173. }
  174. #if os(Linux)
  175. fileDescriptor = open(path, readWriteParam | O_NOCTTY)
  176. #elseif os(OSX)
  177. fileDescriptor = open(path, readWriteParam | O_NOCTTY | O_EXLOCK)
  178. #endif
  179. // Throw error if open() failed
  180. if fileDescriptor == PortError.failedToOpen.rawValue {
  181. throw PortError.failedToOpen
  182. }
  183. guard
  184. receive,
  185. let fileDescriptor
  186. else { return }
  187. let pollSource = DispatchSource.makeReadSource(fileDescriptor: fileDescriptor, queue: .global(qos: .default))
  188. let stream = AsyncStream<Data> { continuation in
  189. pollSource.setEventHandler {
  190. let bufferSize = 1024
  191. let buffer = UnsafeMutableRawPointer
  192. .allocate(byteCount: bufferSize, alignment: 8)
  193. let bytesRead = read(fileDescriptor, buffer, bufferSize)
  194. guard bytesRead > 0 else { return }
  195. let bytes = Data(bytes: buffer, count: bytesRead)
  196. continuation.yield(bytes)
  197. }
  198. pollSource.setCancelHandler {
  199. continuation.finish()
  200. }
  201. }
  202. self.readDataStream = stream
  203. }
  204. public func setSettings(receiveRate: BaudRate,
  205. transmitRate: BaudRate,
  206. minimumBytesToRead: Int,
  207. timeout: Int = 0, /* 0 means wait indefinitely */
  208. parityType: ParityType = .none,
  209. sendTwoStopBits: Bool = false, /* 1 stop bit is the default */
  210. dataBitsSize: DataBitsSize = .bits8,
  211. useHardwareFlowControl: Bool = false,
  212. useSoftwareFlowControl: Bool = false,
  213. processOutput: Bool = false) {
  214. guard let fileDescriptor = fileDescriptor else {
  215. return
  216. }
  217. // Set up the control structure
  218. var settings = termios()
  219. // Get options structure for the port
  220. tcgetattr(fileDescriptor, &settings)
  221. // Set baud rates
  222. cfsetispeed(&settings, receiveRate.speedValue)
  223. cfsetospeed(&settings, transmitRate.speedValue)
  224. // Enable parity (even/odd) if needed
  225. settings.c_cflag |= parityType.parityValue
  226. // Set stop bit flag
  227. if sendTwoStopBits {
  228. settings.c_cflag |= tcflag_t(CSTOPB)
  229. } else {
  230. settings.c_cflag &= ~tcflag_t(CSTOPB)
  231. }
  232. // Set data bits size flag
  233. settings.c_cflag &= ~tcflag_t(CSIZE)
  234. settings.c_cflag |= dataBitsSize.flagValue
  235. //Disable input mapping of CR to NL, mapping of NL into CR, and ignoring CR
  236. settings.c_iflag &= ~tcflag_t(ICRNL | INLCR | IGNCR)
  237. // Set hardware flow control flag
  238. #if os(Linux)
  239. if useHardwareFlowControl {
  240. settings.c_cflag |= tcflag_t(CRTSCTS)
  241. } else {
  242. settings.c_cflag &= ~tcflag_t(CRTSCTS)
  243. }
  244. #elseif os(OSX)
  245. if useHardwareFlowControl {
  246. settings.c_cflag |= tcflag_t(CRTS_IFLOW)
  247. settings.c_cflag |= tcflag_t(CCTS_OFLOW)
  248. } else {
  249. settings.c_cflag &= ~tcflag_t(CRTS_IFLOW)
  250. settings.c_cflag &= ~tcflag_t(CCTS_OFLOW)
  251. }
  252. #endif
  253. // Set software flow control flags
  254. let softwareFlowControlFlags = tcflag_t(IXON | IXOFF | IXANY)
  255. if useSoftwareFlowControl {
  256. settings.c_iflag |= softwareFlowControlFlags
  257. } else {
  258. settings.c_iflag &= ~softwareFlowControlFlags
  259. }
  260. // Turn on the receiver of the serial port, and ignore modem control lines
  261. settings.c_cflag |= tcflag_t(CREAD | CLOCAL)
  262. // Turn off canonical mode
  263. settings.c_lflag &= ~tcflag_t(ICANON | ECHO | ECHOE | ISIG)
  264. // Set output processing flag
  265. if processOutput {
  266. settings.c_oflag |= tcflag_t(OPOST)
  267. } else {
  268. settings.c_oflag &= ~tcflag_t(OPOST)
  269. }
  270. //Special characters
  271. //We do this as c_cc is a C-fixed array which is imported as a tuple in Swift.
  272. //To avoid hardcoding the VMIN or VTIME value to access the tuple value, we use the typealias instead
  273. #if os(Linux)
  274. typealias specialCharactersTuple = (VINTR: cc_t, VQUIT: cc_t, VERASE: cc_t, VKILL: cc_t, VEOF: cc_t, VTIME: cc_t, VMIN: cc_t, VSWTC: cc_t, VSTART: cc_t, VSTOP: cc_t, VSUSP: cc_t, VEOL: cc_t, VREPRINT: cc_t, VDISCARD: cc_t, VWERASE: cc_t, VLNEXT: cc_t, VEOL2: cc_t, spare1: cc_t, spare2: cc_t, spare3: cc_t, spare4: cc_t, spare5: cc_t, spare6: cc_t, spare7: cc_t, spare8: cc_t, spare9: cc_t, spare10: cc_t, spare11: cc_t, spare12: cc_t, spare13: cc_t, spare14: cc_t, spare15: cc_t)
  275. var specialCharacters: specialCharactersTuple = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) // NCCS = 32
  276. #elseif os(OSX)
  277. typealias specialCharactersTuple = (VEOF: cc_t, VEOL: cc_t, VEOL2: cc_t, VERASE: cc_t, VWERASE: cc_t, VKILL: cc_t, VREPRINT: cc_t, spare1: cc_t, VINTR: cc_t, VQUIT: cc_t, VSUSP: cc_t, VDSUSP: cc_t, VSTART: cc_t, VSTOP: cc_t, VLNEXT: cc_t, VDISCARD: cc_t, VMIN: cc_t, VTIME: cc_t, VSTATUS: cc_t, spare: cc_t)
  278. var specialCharacters: specialCharactersTuple = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) // NCCS = 20
  279. #endif
  280. specialCharacters.VMIN = cc_t(minimumBytesToRead)
  281. specialCharacters.VTIME = cc_t(timeout)
  282. settings.c_cc = specialCharacters
  283. // Commit settings
  284. tcsetattr(fileDescriptor, TCSANOW, &settings)
  285. }
  286. public func closePort() {
  287. pollSource?.cancel()
  288. pollSource = nil
  289. readDataStream = nil
  290. readBytesStream = nil
  291. readLinesStream = nil
  292. if let fileDescriptor = fileDescriptor {
  293. close(fileDescriptor)
  294. }
  295. fileDescriptor = nil
  296. }
  297. }
  298. // MARK: Receiving
  299. extension SerialPort {
  300. public func readBytes(into buffer: UnsafeMutablePointer<UInt8>, size: Int) throws -> Int {
  301. guard let fileDescriptor = fileDescriptor else {
  302. throw PortError.mustBeOpen
  303. }
  304. var s: stat = stat()
  305. fstat(fileDescriptor, &s)
  306. if s.st_nlink != 1 {
  307. throw PortError.deviceNotConnected
  308. }
  309. let bytesRead = read(fileDescriptor, buffer, size)
  310. return bytesRead
  311. }
  312. public func readData(ofLength length: Int) throws -> Data {
  313. let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: length)
  314. defer {
  315. buffer.deallocate()
  316. }
  317. let bytesRead = try readBytes(into: buffer, size: length)
  318. var data : Data
  319. if bytesRead > 0 {
  320. data = Data(bytes: buffer, count: bytesRead)
  321. } else {
  322. //This is to avoid the case where bytesRead can be negative causing problems allocating the Data buffer
  323. data = Data(bytes: buffer, count: 0)
  324. }
  325. return data
  326. }
  327. public func readString(ofLength length: Int) throws -> String {
  328. var remainingBytesToRead = length
  329. var result = ""
  330. while remainingBytesToRead > 0 {
  331. let data = try readData(ofLength: remainingBytesToRead)
  332. if let string = String(data: data, encoding: String.Encoding.utf8) {
  333. result += string
  334. remainingBytesToRead -= data.count
  335. } else {
  336. return result
  337. }
  338. }
  339. return result
  340. }
  341. public func readUntilChar(_ terminator: CChar) throws -> String {
  342. var data = Data()
  343. let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: 1)
  344. defer {
  345. buffer.deallocate()
  346. }
  347. while true {
  348. let bytesRead = try readBytes(into: buffer, size: 1)
  349. if bytesRead > 0 {
  350. if ( buffer[0] > 127) {
  351. throw PortError.unableToConvertByteToCharacter
  352. }
  353. let character = CChar(buffer[0])
  354. if character == terminator {
  355. break
  356. } else {
  357. data.append(buffer, count: 1)
  358. }
  359. }
  360. }
  361. if let string = String(data: data, encoding: String.Encoding.utf8) {
  362. return string
  363. } else {
  364. throw PortError.stringsMustBeUTF8
  365. }
  366. }
  367. public func readLine() throws -> String {
  368. let newlineChar = CChar(10) // Newline/Line feed character `\n` is 10
  369. return try readUntilChar(newlineChar)
  370. }
  371. public func readByte() throws -> UInt8 {
  372. let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: 1)
  373. defer {
  374. buffer.deallocate()
  375. }
  376. while true {
  377. let bytesRead = try readBytes(into: buffer, size: 1)
  378. if bytesRead > 0 {
  379. return buffer[0]
  380. }
  381. }
  382. }
  383. public func readChar() throws -> UnicodeScalar {
  384. let byteRead = try readByte()
  385. let character = UnicodeScalar(byteRead)
  386. return character
  387. }
  388. public func asyncData() throws -> AsyncStream<Data> {
  389. guard
  390. fileDescriptor != nil,
  391. let readDataStream
  392. else {
  393. throw PortError.mustBeOpen
  394. }
  395. return readDataStream
  396. }
  397. public func asyncBytes() throws -> AsyncStream<UInt8> {
  398. guard
  399. fileDescriptor != nil,
  400. let readDataStream
  401. else {
  402. throw PortError.mustBeOpen
  403. }
  404. if let existing = readBytesStream {
  405. return existing
  406. } else {
  407. let new = AsyncStream<UInt8> { continuation in
  408. Task {
  409. for try await data in readDataStream {
  410. for byte in data {
  411. continuation.yield(byte)
  412. }
  413. }
  414. continuation.finish()
  415. }
  416. }
  417. readBytesStream = new
  418. return new
  419. }
  420. }
  421. public func asyncLines() throws -> AsyncStream<String> {
  422. guard
  423. fileDescriptor != nil
  424. else {
  425. throw PortError.mustBeOpen
  426. }
  427. if let existing = readLinesStream {
  428. return existing
  429. } else {
  430. let byteStream = try asyncBytes()
  431. let new = AsyncStream<String> { continuation in
  432. Task {
  433. var accumulator = Data()
  434. for try await byte in byteStream {
  435. accumulator.append(byte)
  436. guard
  437. UnicodeScalar(byte) == "\n".unicodeScalars.first
  438. else { continue }
  439. defer { accumulator = Data() }
  440. guard
  441. let string = String(data: accumulator, encoding: .utf8)
  442. else {
  443. continuation.yield("Error: Non string data. Perhaps you wanted data or bytes output?")
  444. continue
  445. }
  446. continuation.yield(string)
  447. }
  448. continuation.finish()
  449. }
  450. }
  451. readLinesStream = new
  452. return new
  453. }
  454. }
  455. }
  456. // MARK: Transmitting
  457. extension SerialPort {
  458. public func writeBytes(from buffer: UnsafeMutablePointer<UInt8>, size: Int) throws -> Int {
  459. guard let fileDescriptor = fileDescriptor else {
  460. throw PortError.mustBeOpen
  461. }
  462. let bytesWritten = write(fileDescriptor, buffer, size)
  463. return bytesWritten
  464. }
  465. public func writeData(_ data: Data) throws -> Int {
  466. let size = data.count
  467. let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: size)
  468. defer {
  469. buffer.deallocate()
  470. }
  471. data.copyBytes(to: buffer, count: size)
  472. let bytesWritten = try writeBytes(from: buffer, size: size)
  473. return bytesWritten
  474. }
  475. public func writeString(_ string: String) throws -> Int {
  476. guard let data = string.data(using: String.Encoding.utf8) else {
  477. throw PortError.stringsMustBeUTF8
  478. }
  479. return try writeData(data)
  480. }
  481. public func writeChar(_ character: UnicodeScalar) throws -> Int{
  482. let stringEquiv = String(character)
  483. let bytesWritten = try writeString(stringEquiv)
  484. return bytesWritten
  485. }
  486. }