SwiftSerial

(此仓库现已存档,因为我不再维护它。您可以考虑使用 mredig 对此库的分支 https://github.com/mredig/SwiftSerial

一个用于在 Linux 和 Mac 上读写串口的 Swift 库。此库已在 macOS Mojave、Linux Mint 18(基于 Ubuntu 16.04)以及 Ubuntu 16.04 上的 Raspberry Pi 3 和 Raspian Buster 上的 Raspberry Pi 4 上测试通过。其他使用 Ubuntu 的平台,如 Beaglebone,也可能适用。

这个库是我之前已弃用的库 SwiftLinuxSerial 的改进版本,之前的库 Swift 风格较弱,且仅支持 Linux。这个库很大程度上要感谢 Jay Jun。他的原始拉取请求可以在这里找到。

关于此库的演讲

我做了一个关于此库及其示例 SwiftSerialIM 的演讲。点击以下链接查看幻灯片和视频。

My slides on slideshare

Mac OS 准备工作

您应该安装 Xcode 8 或更高版本,并 साथ में 命令行工具。

您可能需要启用 USB 和/或 串口授权。

要使用 Xcode 开发应用程序,请在 Xcode 中启用 App Sandbox 功能,并在硬件下选择 USB。(Mac 应用程序是沙盒化的,您需要 USB 授权。)Swift 3.0

对于串口授权,没有复选框。因此,需要将 com.apple.security.device.serial 手动添加到 Xcode 项目中的 "YourApp.entitlements" 文件中。感谢 @doHernandezM 在 此问题 中提出的。

Linux 系统准备工作

因系统而异...

直接进入示例代码

要快速开始,您可以查看我的示例项目,点击这里

示例 1:环回测试

为了正确运行此示例,您需要以环回方式连接一个(USB/UART)串口。基本上,您将串口的 TX 和 RX 引脚短接。此库目前仅支持 Mac 上的 /dev/cu.* 变体。阅读 API 用法部分的开头以了解更多详情。

git clone https://github.com/yeokm1/SwiftSerial.git
cd SwiftSerial/Examples/SwiftSerialExample/
swift build

#For Linux: You need root to access the serial port. Replace /dev/ttyUSB0 with the name of your serial port under test
sudo ./.build/debug/SwiftSerialExample /dev/ttyUSB0

#For Mac: Root is not required
./.build/debug/SwiftSerialExample /dev/cu.usbserial

#If all goes well you should see a series of messages informing you that data transmitted has been received properly.

示例 2:二进制环回测试

示例 1 的变体,但专门测试二进制数据的传输,确保 0x0D 位不会转换为另一个字符。

git clone https://github.com/yeokm1/SwiftSerial.git
cd SwiftSerial/Examples/SwiftSerialBinary/
swift build

#For Linux: You need root to access the serial port. Replace /dev/ttyUSB0 with the name of your serial port under test
sudo ./.build/debug/SwiftSerialBinary /dev/ttyUSB0

#For Mac: Root is not required
./.build/debug/SwiftSerialBinary /dev/cu.usbserial

#If all goes well you should see a series of messages informing you that data transmitted has been received properly.

示例 3:2 台机器之间的聊天应用程序

为了正确运行此示例,您需要 2 台通过 零调制解调器电缆 或 2 个 USB-串口适配器连接的机器,并将 TX-RX 引脚相互连接。在两台机器上运行我的程序副本。

git clone https://github.com/yeokm1/SwiftSerial.git
cd SwiftSerial/Examples/SwiftSerialIM/
swift build

#For Linux: You need root to access the serial port. Replace /dev/ttyUSB0 with the name of your serial port under test
sudo ./.build/debug/SwiftSerialIM /dev/ttyUSB0

#For Mac: Root is not required
./.build/debug/SwiftSerialIM /dev/cu.usbserial

现在两台机器的人可以互相“聊天”了。

与您的项目集成

通过编辑 Package.swift 文件,将 SwiftSerial 添加为项目的依赖项。

let package = Package(
    name: "NameOfMyProject",
    dependencies: [
        .package(url: "https://github.com/yeokm1/SwiftSerial.git", from: "0.1.2"),
        ...
    ]
    ...
)

确保在使用我的 API 的源文件中 import SwiftSerial

然后运行 swift build 来下载依赖项并编译您的项目。您的可执行文件将在 ./.build/debug/ 目录中找到。

API 用法

初始化类

let serialPort: SerialPort = SerialPort(path: portName)

提供您希望打开的端口名,例如 /dev/ttyUSB0/dev/cu.usbserial

对于 Mac,此库目前仅适用于 /dev/cu.* 端口,而不是 /dev/tty.*。我已在串口上启用阻塞以防止高 CPU 使用率,这将阻止 /dev/tty.* 工作。阅读 这里 了解两者之间的区别。如果出现问题,请打开一个 issue 描述您的情况,让我来调查。

打开串口

func openPort()
func openPort(toReceive receive: Bool, andTransmit transmit: Bool)

不带任何参数打开端口将默认设置为接收和发送。您仍然可以选择仅接收、仅发送或两者都选。如果您将两个参数都设置为 false,则会抛出 PortError.mustReceiveOrTransmit。也可能抛出 PortError.failedToOpenPortError.invalidPath

设置端口设置

serialPort.setSettings(receiveRate: .baud9600, transmitRate: .baud9600, minimumBytesToRead: 1)

端口设置调用可以像上面一样简单。对于波特率,即使您只打算使用一个传输方向,也要同时提供发送和接收的波特率。例如,如果您在打开端口时指定了 andTransmit : false,则 transmitRate 将被忽略。

minimumBytesToRead 决定了系统必须等待接收多少个字符,然后才会从 read() 函数返回。如果不确定,只需设置为 1 即可。

此函数已定义了默认设置,如函数定义中所示。

func setSettings(receiveRate: BaudRate,
                 transmitRate: BaudRate,
                 minimumBytesToRead: Int,
                 timeout: Int = 0, /* 0 means wait indefinitely */
                 parityType: ParityType = .none,
                 sendTwoStopBits: Bool = false, /* 1 stop bit is the default */
                 dataBitsSize: DataBitsSize = .bits8,
                 useHardwareFlowControl: Bool = false,
                 useSoftwareFlowControl: Bool = false,
                 processOutput: Bool = false)

如果默认设置不适合您,只需传入额外的参数来覆盖它们。

从端口读取数据

您可以使用几个函数来读取数据。这里的所有函数都是阻塞的,直到收到预期的字节数或满足某个条件。所有函数都可能抛出 PortError.mustBeOpen

func readString(ofLength length: Int) throws -> String

如果您发送的是文本数据,这是最容易使用的。只需提供您期望读取的字节数。结果将以典型的 Swift String 形式返回。此函数内部调用 readData()

func readData(ofLength length: Int) throws -> Data

如果您打算接收二进制数据,则使用此函数。此函数内部调用 readBytes()

func readBytes(into buffer: UnsafeMutablePointer<UInt8>, size: Int) throws -> Int

如果您打算直接使用不安全指针,这就是适合您的函数!将返回读取的字节数。请注意,您负责在传递到此函数之前分配指针,并在完成后释放指针。

func readLine() throws -> String

逐字节读取,直到遇到换行符 \n。将返回包含到目前为止结果的字符串,但不包含换行符。此函数内部调用 readUntilChar()。可能抛出 PortError.stringsMustBeUTF8

func readUntilChar(_ terminator: CChar) throws -> String

持续读取,直到遇到指定的 CChar。返回到目前为止读取的字符串,但不包含该值。

func readByte() throws -> UInt8

仅读取一个字节。如果在打开端口时将 minimumBytesToRead 设置为 1,则此函数效果最佳。此函数内部调用 readBytes()

func readChar() throws -> UnicodeScalar

仅读取一个字符。如果在打开端口时将 minimumBytesToRead 设置为 1,则此函数效果最佳。此函数内部调用 readByte()

向端口写入数据

您可以使用几个函数来写入数据。这里的所有函数都是阻塞的,直到所有数据都已写入。所有函数都可能抛出 PortError.mustBeOpen

func writeString(_ string: String) throws -> Int

最直接的函数,输入 String 然后发送!将返回实际写入的字节数。内部调用 writeData()

func writeData(_ data: Data) throws -> Int

输入二进制数据,然后发送!将返回实际写入的字节数。内部调用 writeBytes()

func writeBytes(from buffer: UnsafeMutablePointer<UInt8>, size: Int) throws -> Int

适用于那些想要使用不安全指针的人的函数。您必须指定要写入的字节数。将返回实际写入的字节数。

func writeChar(_ character: UnicodeScalar) throws -> Int{

仅写入一个字符。如果成功,将返回 1。此函数内部调用 writeString()。欢迎提出更好的实现方式的 pull request。

关闭端口

只需在完成使用后执行 serialPort.closePort() 即可关闭端口。

外部参考

如果没有我所依赖的优秀参考代码,就无法编写此库。

  1. Xanthium 的 Linux 上的串口编程
  2. Chrishey Drick 的从串口读取数据