1
0

SwiftSerial.swift 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  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. public init(path: String) {
  148. self.path = path
  149. }
  150. public func openPort() throws {
  151. try openPort(toReceive: true, andTransmit: true)
  152. }
  153. public func openPort(toReceive receive: Bool, andTransmit transmit: Bool) throws {
  154. guard !path.isEmpty else {
  155. throw PortError.invalidPath
  156. }
  157. guard receive || transmit else {
  158. throw PortError.mustReceiveOrTransmit
  159. }
  160. var readWriteParam : Int32
  161. if receive && transmit {
  162. readWriteParam = O_RDWR
  163. } else if receive {
  164. readWriteParam = O_RDONLY
  165. } else if transmit {
  166. readWriteParam = O_WRONLY
  167. } else {
  168. fatalError()
  169. }
  170. #if os(Linux)
  171. fileDescriptor = open(path, readWriteParam | O_NOCTTY)
  172. #elseif os(OSX)
  173. fileDescriptor = open(path, readWriteParam | O_NOCTTY | O_EXLOCK)
  174. #endif
  175. // Throw error if open() failed
  176. if fileDescriptor == PortError.failedToOpen.rawValue {
  177. throw PortError.failedToOpen
  178. }
  179. }
  180. public func setSettings(receiveRate: BaudRate,
  181. transmitRate: BaudRate,
  182. minimumBytesToRead: Int,
  183. timeout: Int = 0, /* 0 means wait indefinitely */
  184. parityType: ParityType = .none,
  185. sendTwoStopBits: Bool = false, /* 1 stop bit is the default */
  186. dataBitsSize: DataBitsSize = .bits8,
  187. useHardwareFlowControl: Bool = false,
  188. useSoftwareFlowControl: Bool = false,
  189. processOutput: Bool = false) {
  190. guard let fileDescriptor = fileDescriptor else {
  191. return
  192. }
  193. // Set up the control structure
  194. var settings = termios()
  195. // Get options structure for the port
  196. tcgetattr(fileDescriptor, &settings)
  197. // Set baud rates
  198. cfsetispeed(&settings, receiveRate.speedValue)
  199. cfsetospeed(&settings, transmitRate.speedValue)
  200. // Enable parity (even/odd) if needed
  201. settings.c_cflag |= parityType.parityValue
  202. // Set stop bit flag
  203. if sendTwoStopBits {
  204. settings.c_cflag |= tcflag_t(CSTOPB)
  205. } else {
  206. settings.c_cflag &= ~tcflag_t(CSTOPB)
  207. }
  208. // Set data bits size flag
  209. settings.c_cflag &= ~tcflag_t(CSIZE)
  210. settings.c_cflag |= dataBitsSize.flagValue
  211. //Disable input mapping of CR to NL, mapping of NL into CR, and ignoring CR
  212. settings.c_iflag &= ~tcflag_t(ICRNL | INLCR | IGNCR)
  213. // Set hardware flow control flag
  214. #if os(Linux)
  215. if useHardwareFlowControl {
  216. settings.c_cflag |= tcflag_t(CRTSCTS)
  217. } else {
  218. settings.c_cflag &= ~tcflag_t(CRTSCTS)
  219. }
  220. #elseif os(OSX)
  221. if useHardwareFlowControl {
  222. settings.c_cflag |= tcflag_t(CRTS_IFLOW)
  223. settings.c_cflag |= tcflag_t(CCTS_OFLOW)
  224. } else {
  225. settings.c_cflag &= ~tcflag_t(CRTS_IFLOW)
  226. settings.c_cflag &= ~tcflag_t(CCTS_OFLOW)
  227. }
  228. #endif
  229. // Set software flow control flags
  230. let softwareFlowControlFlags = tcflag_t(IXON | IXOFF | IXANY)
  231. if useSoftwareFlowControl {
  232. settings.c_iflag |= softwareFlowControlFlags
  233. } else {
  234. settings.c_iflag &= ~softwareFlowControlFlags
  235. }
  236. // Turn on the receiver of the serial port, and ignore modem control lines
  237. settings.c_cflag |= tcflag_t(CREAD | CLOCAL)
  238. // Turn off canonical mode
  239. settings.c_lflag &= ~tcflag_t(ICANON | ECHO | ECHOE | ISIG)
  240. // Set output processing flag
  241. if processOutput {
  242. settings.c_oflag |= tcflag_t(OPOST)
  243. } else {
  244. settings.c_oflag &= ~tcflag_t(OPOST)
  245. }
  246. //Special characters
  247. //We do this as c_cc is a C-fixed array which is imported as a tuple in Swift.
  248. //To avoid hardcoding the VMIN or VTIME value to access the tuple value, we use the typealias instead
  249. #if os(Linux)
  250. 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)
  251. 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
  252. #elseif os(OSX)
  253. 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)
  254. 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
  255. #endif
  256. specialCharacters.VMIN = cc_t(minimumBytesToRead)
  257. specialCharacters.VTIME = cc_t(timeout)
  258. settings.c_cc = specialCharacters
  259. // Commit settings
  260. tcsetattr(fileDescriptor, TCSANOW, &settings)
  261. }
  262. public func closePort() {
  263. if let fileDescriptor = fileDescriptor {
  264. close(fileDescriptor)
  265. }
  266. fileDescriptor = nil
  267. }
  268. }
  269. // MARK: Receiving
  270. extension SerialPort {
  271. public func readBytes(into buffer: UnsafeMutablePointer<UInt8>, size: Int) throws -> Int {
  272. guard let fileDescriptor = fileDescriptor else {
  273. throw PortError.mustBeOpen
  274. }
  275. var s: stat = stat()
  276. fstat(fileDescriptor, &s)
  277. if s.st_nlink != 1 {
  278. throw PortError.deviceNotConnected
  279. }
  280. let bytesRead = read(fileDescriptor, buffer, size)
  281. return bytesRead
  282. }
  283. public func readData(ofLength length: Int) throws -> Data {
  284. let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: length)
  285. defer {
  286. buffer.deallocate()
  287. }
  288. let bytesRead = try readBytes(into: buffer, size: length)
  289. var data : Data
  290. if bytesRead > 0 {
  291. data = Data(bytes: buffer, count: bytesRead)
  292. } else {
  293. //This is to avoid the case where bytesRead can be negative causing problems allocating the Data buffer
  294. data = Data(bytes: buffer, count: 0)
  295. }
  296. return data
  297. }
  298. public func readString(ofLength length: Int) throws -> String {
  299. var remainingBytesToRead = length
  300. var result = ""
  301. while remainingBytesToRead > 0 {
  302. let data = try readData(ofLength: remainingBytesToRead)
  303. if let string = String(data: data, encoding: String.Encoding.utf8) {
  304. result += string
  305. remainingBytesToRead -= data.count
  306. } else {
  307. return result
  308. }
  309. }
  310. return result
  311. }
  312. public func readUntilChar(_ terminator: CChar) throws -> String {
  313. var data = Data()
  314. let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: 1)
  315. defer {
  316. buffer.deallocate()
  317. }
  318. while true {
  319. let bytesRead = try readBytes(into: buffer, size: 1)
  320. if bytesRead > 0 {
  321. if ( buffer[0] > 127) {
  322. throw PortError.unableToConvertByteToCharacter
  323. }
  324. let character = CChar(buffer[0])
  325. if character == terminator {
  326. break
  327. } else {
  328. data.append(buffer, count: 1)
  329. }
  330. }
  331. }
  332. if let string = String(data: data, encoding: String.Encoding.utf8) {
  333. return string
  334. } else {
  335. throw PortError.stringsMustBeUTF8
  336. }
  337. }
  338. public func readLine() throws -> String {
  339. let newlineChar = CChar(10) // Newline/Line feed character `\n` is 10
  340. return try readUntilChar(newlineChar)
  341. }
  342. public func readByte() throws -> UInt8 {
  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. return buffer[0]
  351. }
  352. }
  353. }
  354. public func readChar() throws -> UnicodeScalar {
  355. let byteRead = try readByte()
  356. let character = UnicodeScalar(byteRead)
  357. return character
  358. }
  359. }
  360. // MARK: Transmitting
  361. extension SerialPort {
  362. public func writeBytes(from buffer: UnsafeMutablePointer<UInt8>, size: Int) throws -> Int {
  363. guard let fileDescriptor = fileDescriptor else {
  364. throw PortError.mustBeOpen
  365. }
  366. let bytesWritten = write(fileDescriptor, buffer, size)
  367. return bytesWritten
  368. }
  369. public func writeData(_ data: Data) throws -> Int {
  370. let size = data.count
  371. let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: size)
  372. defer {
  373. buffer.deallocate()
  374. }
  375. data.copyBytes(to: buffer, count: size)
  376. let bytesWritten = try writeBytes(from: buffer, size: size)
  377. return bytesWritten
  378. }
  379. public func writeString(_ string: String) throws -> Int {
  380. guard let data = string.data(using: String.Encoding.utf8) else {
  381. throw PortError.stringsMustBeUTF8
  382. }
  383. return try writeData(data)
  384. }
  385. public func writeChar(_ character: UnicodeScalar) throws -> Int{
  386. let stringEquiv = String(character)
  387. let bytesWritten = try writeString(stringEquiv)
  388. return bytesWritten
  389. }
  390. }