serial.bmx 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569
  1. ' Copyright (c) 2013-2024 Bruce A Henderson
  2. '
  3. ' Permission is hereby granted, free of charge, to any person obtaining a copy
  4. ' of this software and associated documentation files (the "Software"), to deal
  5. ' in the Software without restriction, including without limitation the rights
  6. ' to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. ' copies of the Software, and to permit persons to whom the Software is
  8. ' furnished to do so, subject to the following conditions:
  9. '
  10. ' The above copyright notice and this permission notice shall be included in
  11. ' all copies or substantial portions of the Software.
  12. '
  13. ' THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. ' IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. ' FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. ' AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. ' LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. ' OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19. ' THE SOFTWARE.
  20. '
  21. SuperStrict
  22. Rem
  23. bbdoc: Serial port interface.
  24. End Rem
  25. Module IO.Serial
  26. ModuleInfo "Version: 1.04"
  27. ModuleInfo "License: MIT"
  28. ModuleInfo "Copyright: Serial Library - 2012 William Woodall, John Harrison"
  29. ModuleInfo "Copyright: BlitzMax wrapper - 2013-2024 Bruce A Henderson"
  30. ModuleInfo "History: 1.04"
  31. ModuleInfo "History: Update to woollybah/serial rev 6cec39e"
  32. ModuleInfo "History: Use PurgeComm on win32 flush"
  33. ModuleInfo "History: 1.03"
  34. ModuleInfo "History: Changed to use https://github.com/woollybah/serial rev 28d33dd"
  35. ModuleInfo "History: Converted consts to enums"
  36. ModuleInfo "History: Fixed timeout implementation"
  37. ModuleInfo "History: 1.02"
  38. ModuleInfo "History: Update to latest 1.2.1 rev 5a354ea"
  39. ModuleInfo "History: 1.01"
  40. ModuleInfo "History: Update to latest 1.2.1 rev 827c4a7"
  41. ModuleInfo "History: Fixes for NG"
  42. ModuleInfo "History: 1.00 Initial Release"
  43. ModuleInfo "CC_OPTS: -fexceptions"
  44. Import "common.bmx"
  45. Rem
  46. bbdoc: A serial port interface.
  47. End Rem
  48. Type TSerial
  49. Field serialPtr:Byte Ptr
  50. Rem
  51. bbdoc: Creates a new serial port instance with the given parameters.
  52. End Rem
  53. Method New(port:String = "", baudrate:Int = 9600, bytesize:EByteSize = EByteSize.EightBits, ..
  54. parity:EParityType = EParityType.None, stopbits:EStopBits = EStopBits.One, flowcontrol:EFlowControl = EFlowControl.None, ..
  55. dtrcontrol:EDTRControl = EDTRControl.Disable)
  56. serialPtr = bmx_serial_create_nt(port, baudrate, bytesize, parity, stopbits, flowcontrol, dtrcontrol)
  57. End Method
  58. Rem
  59. bbdoc: Creates a new serial port instance with the given @timeout and parameters.
  60. End Rem
  61. Method New(timeout:STimeout, port:String = "", baudrate:Int = 9600, bytesize:EByteSize = EByteSize.EightBits, ..
  62. parity:EParityType = EParityType.None, stopbits:EStopBits = EStopBits.One, flowcontrol:EFlowControl = EFlowControl.None, ..
  63. dtrcontrol:EDTRControl = EDTRControl.Disable)
  64. serialPtr = bmx_serial_create(timeout, port, baudrate, bytesize, parity, stopbits, flowcontrol, dtrcontrol)
  65. End Method
  66. Rem
  67. bbdoc: Opens the serial port as long as the port is set and the port isn't already open.
  68. about: If the port is provided to the constructor then an explicit call to open is not needed.
  69. End Rem
  70. Method Open()
  71. bmx_serial_open(serialPtr)
  72. End Method
  73. Rem
  74. bbdoc: Closes the serial port.
  75. End Rem
  76. Method Close()
  77. bmx_serial_close(serialPtr)
  78. End Method
  79. Rem
  80. bbdoc: Gets the open status of the serial port.
  81. returns: True if the port is open, False otherwise.
  82. End Rem
  83. Method IsOpen:Int()
  84. Return bmx_serial_isopen(serialPtr)
  85. End Method
  86. Rem
  87. bbdoc: Returns the number of characters in the buffer.
  88. End Rem
  89. Method Available:Int()
  90. Return bmx_serial_available(serialPtr)
  91. End Method
  92. Rem
  93. bbdoc: Read a given amount of bytes from the serial port into a given buffer.
  94. returns: A value representing the number of bytes read as a result of the call to read.
  95. about: The read method will return in one of three cases:
  96. <ul>
  97. <li>The number of requested bytes was read.
  98. <ul>
  99. <li>In this case the number of bytes requested will match the value returned by read.</li>
  100. </ul>
  101. </li>
  102. <li>A timeout occurred, in this case the number of bytes read will not match the amount requested, but no exception
  103. will be thrown. One of two possible timeouts occurred:
  104. <ul>
  105. <li>The inter byte timeout expired, this means that number of milliseconds elapsed between receiving bytes
  106. from the serial port exceeded the inter byte timeout.</li>
  107. <li>The total timeout expired, which is calculated by multiplying the read timeout multiplier by the number
  108. of requested bytes and then added to the read timeout constant. If that total number of milliseconds elapses
  109. after the initial call to read a timeout will occur.</li>
  110. </ul>
  111. </li>
  112. <li>An exception occurred, in this case an actual exception will be thrown
  113. </li>
  114. </ul>
  115. End Rem
  116. Method Read:Int(buffer:Byte Ptr, size:Int)
  117. Return bmx_serial_read(serialPtr, buffer, size)
  118. End Method
  119. Rem
  120. bbdoc: Reads in a line or until a given delimiter has been processed.
  121. returns: A string containing the line.
  122. about: Reads from the serial port until a single line has been read.
  123. End Rem
  124. Method ReadLine:String(size:Int = 65536, eol:String = "~n")
  125. Return bmx_serial_readline(serialPtr, size, eol)
  126. End Method
  127. Rem
  128. bbdoc: Write a given amount of bytes to the serial port.
  129. returns: A value representing the number of bytes actually written to the serial port.
  130. End Rem
  131. Method Write:Int(data:Byte Ptr, size:Int)
  132. Return bmx_serial_write(serialPtr, data, size)
  133. End Method
  134. Rem
  135. bbdoc: Write a string to the serial port.
  136. returns: A value representing the number of bytes actually written to the serial port.
  137. End Rem
  138. Method WriteString:Int(data:String)
  139. Return bmx_serial_writestring(serialPtr, data)
  140. End Method
  141. Rem
  142. bbdoc: Sets the serial port identifier.
  143. about: A value containing the address of the serial port, which would be something like 'COM1' on Windows and '/dev/ttyS0' on Linux.
  144. End Rem
  145. Method SetPort(port:String)
  146. bmx_serial_setport(serialPtr, port)
  147. End Method
  148. Rem
  149. bbdoc: Gets the serial port identifier.
  150. End Rem
  151. Method GetPort:String()
  152. Return bmx_serial_getport(serialPtr)
  153. End Method
  154. Rem
  155. bbdoc: Sets the timeout for reads and writes.
  156. End Rem
  157. Method SetTimeout(timeout:STimeout)
  158. bmx_serial_timeout_settimeout(serialPtr, timeout.interByteTimeout, timeout.readTimeoutConstant, timeout.readTimeoutMultiplier, timeout.writeTimeoutConstant, timeout.writeTimeoutMultiplier)
  159. End Method
  160. Rem
  161. bbdoc:
  162. End Rem
  163. Method SetTimeoutValues(interByteTimeout:UInt, readTimeoutConstant:UInt, readTimeoutMultiplier:UInt, writeTimeoutConstant:UInt, writeTimeoutMultiplier:UInt)
  164. bmx_serial_timeout_settimeout(serialPtr, interByteTimeout, readTimeoutConstant, readTimeoutMultiplier, writeTimeoutConstant, writeTimeoutMultiplier)
  165. End Method
  166. Rem
  167. bbdoc:
  168. End Rem
  169. Method GetTimeout:STimeout()
  170. Local timeout:STimeout = New STimeout
  171. bmx_serial_timeout_gettimeout(serialPtr, timeout)
  172. Return timeout
  173. End Method
  174. Rem
  175. bbdoc: Sets the baudrate for the serial port.
  176. about: Possible baudrates depends on the system but some safe baudrates include: 110, 300, 600, 1200, 2400, 4800, 9600,
  177. 14400, 19200, 28800, 38400, 56000, 57600, 115200.
  178. Some other baudrates that are supported by some comports: 128000, 153600, 230400, 256000, 460800, 500000, 921600
  179. End Rem
  180. Method SetBaudrate(baudrate:Int)
  181. bmx_serial_setbaudrate(serialPtr, baudrate)
  182. End Method
  183. Rem
  184. bbdoc: Gets the baudrate for the serial port.
  185. returns: An integer that sets the baud rate for the serial port.
  186. End Rem
  187. Method GetBaudrate:Int()
  188. Return bmx_serial_getbaudrate(serialPtr)
  189. End Method
  190. Rem
  191. bbdoc: Sets the bytesize for the serial port.
  192. about: A value for the size of each byte in the serial transmission of data, default is EightBits, possible values are FiveBIts, SixBits, SevenBits or EightBits.
  193. End Rem
  194. Method SetBytesize(bytesize:EByteSize)
  195. bmx_serial_setbytesize(serialPtr, bytesize)
  196. End Method
  197. Rem
  198. bbdoc: Gets the bytesize for the serial port.
  199. returns: One of FiveBIts, SixBits, SevenBits or EightBits.
  200. End Rem
  201. Method GetBytesize:EByteSize()
  202. Return bmx_serial_getbytesize(serialPtr)
  203. End Method
  204. Rem
  205. bbdoc: Sets the parity for the serial port.
  206. about: A value for the method of parity, default is `None`, possible values are None, Odd, Even, Mark or Space.
  207. End Rem
  208. Method SetParity(parity:EParityType)
  209. bmx_serial_setparity(serialPtr, parity)
  210. End Method
  211. Rem
  212. bbdoc: Gets the parity for the serial port.
  213. returns: One of None, Oddm Even, Mark or Space.
  214. End Rem
  215. Method GetParity:EParityType()
  216. Return bmx_serial_getparity(serialPtr)
  217. End Method
  218. Rem
  219. bbdoc: Sets the stop bits for the serial port.
  220. about: A value for the number of stop bits used, default is One, possible values are One, OnePointFive or Two.
  221. End Rem
  222. Method SetStopbits(stopbits:EStopBits)
  223. bmx_serial_setstopbits(serialPtr, stopbits)
  224. End Method
  225. Rem
  226. bbdoc: Gets the stopbits for the serial port.
  227. returns: One of One, OnePointFive or Two.
  228. End Rem
  229. Method GetStopbits:EStopBits()
  230. Return bmx_serial_getstopbits(serialPtr)
  231. End Method
  232. Rem
  233. bbdoc: Sets the flow control for the serial port.
  234. about: A value for the type of flow control used, default is None, possible values are None, Software or Hardware.
  235. End Rem
  236. Method SetFlowcontrol(flowcontrol:EFlowControl)
  237. bmx_serial_setflowcontrol(serialPtr, flowcontrol)
  238. End Method
  239. Rem
  240. bbdoc: Gets the flow control for the serial port.
  241. returns: One of None, Software or Hardware.
  242. End Rem
  243. Method GetFlowcontrol:EFlowControl()
  244. Return bmx_serial_getflowcontrol(serialPtr)
  245. End Method
  246. Rem
  247. bbdoc: Flush the input and output buffers.
  248. End Rem
  249. Method Flush()
  250. bmx_serial_flush(serialPtr)
  251. End Method
  252. Rem
  253. bbdoc: Flush only the input buffer.
  254. End Rem
  255. Method FlushInput()
  256. bmx_serial_flushinput(serialPtr)
  257. End Method
  258. Rem
  259. bbdoc: Flush only the output buffer.
  260. End Rem
  261. Method FlushOutput()
  262. bmx_serial_flushoutput(serialPtr)
  263. End Method
  264. Rem
  265. bbdoc: Sends the RS-232 break signal.
  266. about: See tcsendbreak(3).
  267. End Rem
  268. Method SendBreak(duration:Int)
  269. bmx_serial_sendbreak(serialPtr, duration)
  270. End Method
  271. Rem
  272. bbdoc: Sets the break condition to a given level.
  273. about: Defaults to #True.
  274. End Rem
  275. Method SetBreak(level:Int = True)
  276. bmx_serial_setbreak(serialPtr, level)
  277. End Method
  278. Rem
  279. bbdoc: Sets the RTS handshaking line to the given level.
  280. about: Defaults to #True.
  281. End Rem
  282. Method SetRTS(level:Int = True)
  283. bmx_serial_setrts(serialPtr, level)
  284. End Method
  285. Rem
  286. bbdoc: Sets the DTR handshaking line to the given level.
  287. about: Defaults to Enable.
  288. End Rem
  289. Method SetDTR(dtrcontrol:EDTRControl = EDTRControl.Enable)
  290. bmx_serial_setdtr(serialPtr, dtrcontrol)
  291. End Method
  292. Rem
  293. bbdoc: Blocks until CTS, DSR, RI, CD changes or something interrupts it.
  294. returns: True if one of the lines changed, False if something else occurred.
  295. about: Can throw an exception if an error occurs while waiting.
  296. You can check the status of CTS, DSR, RI, and CD once this returns.
  297. Uses TIOCMIWAIT via ioctl if available (mostly only on Linux) with a resolution of less than +-1ms and as good as +-0.2ms.
  298. Otherwise a polling method is used which can give +-2ms.
  299. End Rem
  300. Method WaitForChange()
  301. bmx_serial_waitforchange(serialPtr)
  302. End Method
  303. Rem
  304. bbdoc: Returns the current status of the CTS line.
  305. End Rem
  306. Method GetCTS:Int()
  307. Return bmx_serial_getcts(serialPtr)
  308. End Method
  309. Rem
  310. bbdoc: Returns the current status of the DSR line.
  311. End Rem
  312. Method GetDSR:Int()
  313. Return bmx_serial_getdsr(serialPtr)
  314. End Method
  315. Rem
  316. bbdoc: Returns the current status of the RI line.
  317. End Rem
  318. Method GetRI:Int()
  319. Return bmx_serial_getri(serialPtr)
  320. End Method
  321. Rem
  322. bbdoc: Returns the current status of the CD line.
  323. End Rem
  324. Method GetCD:Int()
  325. Return bmx_serial_getcd(serialPtr)
  326. End Method
  327. Rem
  328. bbdoc: Returns an array of available serial ports.
  329. End Rem
  330. Function ListPorts:TList()
  331. Local list:TList = New TList
  332. bmx_serial_listports(list)
  333. Return list
  334. End Function
  335. End Type
  336. Extern
  337. Function bmx_serial_create:Byte Ptr(timeout:STimeout Var, port:String, baudrate:Int, bytesize:EByteSize, parity:EParityType, stopbits:EStopBits, flowcontrol:EFlowControl, drtcontrol:EDTRControl)
  338. Function bmx_serial_listports(list:TList)
  339. Function bmx_serial_timeout_gettimeout(handle:Byte Ptr, timeout:STimeout Var)
  340. End Extern
  341. Rem
  342. bbdoc: Struct for setting the timeout of the serial port, times are in milliseconds.
  343. abotu: In order to disable the interbyte timeout, set it to TTimeout::maxTime().
  344. End Rem
  345. Struct STimeout
  346. Rem
  347. bbdoc: Number of milliseconds between bytes received to timeout on.
  348. End Rem
  349. Field interByteTimeout:UInt
  350. Rem
  351. bbdoc: A constant number of milliseconds to wait after calling read.
  352. End Rem
  353. Field readTimeoutConstant:UInt
  354. Rem
  355. bbdoc: A multiplier against the number of requested bytes to wait after calling read.
  356. End Rem
  357. Field readTimeoutMultiplier:UInt
  358. Rem
  359. bbdoc: A constant number of milliseconds to wait after calling write.
  360. End Rem
  361. Field writeTimeoutConstant:UInt
  362. Rem
  363. bbdoc: A multiplier against the number of requested bytes to wait after calling write.
  364. End Rem
  365. Field writeTimeoutMultiplier:UInt
  366. Rem
  367. bbdoc: Returns the maximum timeout value.
  368. End Rem
  369. Function MaxTime:UInt()
  370. Return bmx_serial_timeout_max()
  371. End Function
  372. Rem
  373. bbdoc:
  374. End Rem
  375. Method New(interByteTimeout:Int = 0, readTimeoutConstant:Int = 0, readTimeoutMultiplier:Int = 0, writeTimeoutConstant:Int = 0, ..
  376. writeTimeoutMultiplier:Int = 0)
  377. Self.interByteTimeout = interByteTimeout
  378. Self.readTimeoutConstant = readTimeoutConstant
  379. Self.readTimeoutMultiplier = readTimeoutMultiplier
  380. Self.writeTimeoutConstant = writeTimeoutConstant
  381. Self.writeTimeoutMultiplier = writeTimeoutMultiplier
  382. End Method
  383. End Struct
  384. Rem
  385. bbdoc:
  386. End Rem
  387. Type TSerialException Extends TRuntimeException
  388. Field what:String
  389. Function _create:TSerialException(what:String) { nomangle }
  390. Return New TSerialException.CreateException(what)
  391. End Function
  392. Method CreateException:TSerialException(what:String)
  393. Self.what = what
  394. Return Self
  395. End Method
  396. Method ToString:String()
  397. Return what
  398. End Method
  399. End Type
  400. Rem
  401. bbdoc:
  402. End Rem
  403. Type TIOException Extends TSerialException
  404. Function _create:TSerialException(what:String) { nomangle }
  405. Return New TIOException.CreateException(what)
  406. End Function
  407. End Type
  408. Rem
  409. bbdoc:
  410. End Rem
  411. Type TPortNotOpenedException Extends TSerialException
  412. Function _create:TSerialException(what:String) { nomangle }
  413. Return New TPortNotOpenedException.CreateException(what)
  414. End Function
  415. End Type
  416. Rem
  417. bbdoc:
  418. End Rem
  419. Type TSerialPortInfo
  420. Rem
  421. bbdoc:
  422. End Rem
  423. Field portName:String
  424. Rem
  425. bbdoc:
  426. End Rem
  427. Field physicalName:String
  428. Rem
  429. bbdoc:
  430. End Rem
  431. Field productName:String
  432. Rem
  433. bbdoc:
  434. End Rem
  435. Field enumeratorName:String
  436. Rem
  437. bbdoc:
  438. End Rem
  439. Field vendorId:Int
  440. Rem
  441. bbdoc:
  442. End Rem
  443. Field productId:Int
  444. Function _create:TSerialPortInfo(portName:String, physicalName:String, productName:String, enumeratorName:String, ..
  445. vendorId:Int, productId:Int) { nomangle }
  446. ?win32
  447. If portName.StartsWith("LPT") Then
  448. Return Null
  449. End If
  450. ?
  451. Local this:TSerialPortInfo = New TSerialPortInfo
  452. this.portName = portName
  453. this.physicalName = physicalName
  454. this.productName = productName
  455. this.enumeratorName = enumeratorName
  456. this.vendorId = vendorId
  457. this.productId = productId
  458. Return this
  459. End Function
  460. Function _addInfo(list:TList, info:TSerialPortInfo) { nomangle }
  461. list.AddLast(info)
  462. End Function
  463. ?win32
  464. Function _getIds(hids:String, vendorId:Int Ptr, productId:Int Ptr) { nomangle }
  465. Local regex:TRegEx = TRegEx.Create( "VID_(\w+)&PID_(\w+)")
  466. Try
  467. Local match:TRegExMatch = regex.Find(hids)
  468. If match Then
  469. vendorId[0] = hexToInt(match.SubExp(1))
  470. productId[0] = hexToInt(match.SubExp(2))
  471. End If
  472. Catch e:TRegExException
  473. End Try
  474. End Function
  475. Function hexToInt:Int(Text:String)
  476. Local val:Int
  477. Local length:Int = Text.length
  478. For Local i:Int = 0 Until length
  479. val :+ Int(Text[length - i - 1..length - i]) Shl (i * 4)
  480. Next
  481. Return val
  482. End Function
  483. ?linux
  484. Function _listPorts(list:TList) { nomangle }
  485. If FileType("/dev/serial") = FILETYPE_DIR Then
  486. If FileType("/dev/serial/by-id") = FILETYPE_DIR Then
  487. Local ports:String[] = LoadDir("/dev/serial/by-id")
  488. For Local port:String = EachIn ports
  489. Local info:TSerialPortInfo = New TSerialPortInfo
  490. info.portName = "/dev/serial/by-id/" + port
  491. list.AddLast(info)
  492. Next
  493. End If
  494. End If
  495. End Function
  496. ?
  497. End Type