Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
25600e48fb | ||
|
|
7c06c99bd1 | ||
|
|
ad3142da95 | ||
|
|
0c14b6d2b9 | ||
|
|
4522b9261e | ||
|
|
144c9bbf5a | ||
|
|
20cf246eda | ||
|
|
02e0d4837e | ||
|
|
816720e15c | ||
|
|
c60e0334bd | ||
|
|
ade0cdc3cd | ||
|
|
3e4b905e7b | ||
|
|
e73ea6056d | ||
|
|
640cf2f7c3 | ||
|
|
21adc2c8f4 |
@@ -11,7 +11,7 @@ let test3Strings: String = testString + "\n" + testString + "\n" + testString +
|
||||
|
||||
let arguments = CommandLine.arguments
|
||||
guard arguments.count >= 2 else {
|
||||
print("Need serial port name, e.g. /dev/ttyUSB0 as the first argument.")
|
||||
print("Need serial port name, e.g. /dev/ttyUSB0 or /dev/cu.usbserial as the first argument.")
|
||||
exit(1)
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ do {
|
||||
print("Serial port \(portName) opened successfully.")
|
||||
defer {
|
||||
serialPort.closePort()
|
||||
print("Port Closed")
|
||||
}
|
||||
|
||||
serialPort.setSettings(receiveRate: .baud9600,
|
||||
@@ -74,8 +75,8 @@ do {
|
||||
}
|
||||
}
|
||||
|
||||
print("End of example");
|
||||
|
||||
print("We successfully received back \(numberOfMultiNewLineTest) lines")
|
||||
|
||||
} catch PortError.failedToOpen {
|
||||
print("Serial port \(portName) failed to open. You might need root permissions.")
|
||||
|
||||
8
Examples/SwiftSerialIM/Package.swift
Normal file
8
Examples/SwiftSerialIM/Package.swift
Normal file
@@ -0,0 +1,8 @@
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "SwiftSerialIM",
|
||||
dependencies: [
|
||||
.Package(url: "https://github.com/yeokm1/SwiftSerial.git", majorVersion: 0)
|
||||
]
|
||||
)
|
||||
128
Examples/SwiftSerialIM/Sources/main.swift
Normal file
128
Examples/SwiftSerialIM/Sources/main.swift
Normal file
@@ -0,0 +1,128 @@
|
||||
import Foundation
|
||||
import SwiftSerial
|
||||
|
||||
|
||||
let arguments = CommandLine.arguments
|
||||
guard arguments.count >= 2 else {
|
||||
print("Need serial port name, e.g. /dev/ttyUSB0 or /dev/cu.usbserial as the first argument.")
|
||||
exit(1)
|
||||
}
|
||||
|
||||
print("Connect a null modem serial cable between two machines before you continue to use this program")
|
||||
|
||||
let portName = arguments[1]
|
||||
let serialPort: SerialPort = SerialPort(path: portName)
|
||||
|
||||
var myturn = true
|
||||
|
||||
// Prepares the stdin so we can getchar() without echoing
|
||||
func prepareStdin() {
|
||||
|
||||
// Set up the control structure
|
||||
var settings = termios()
|
||||
|
||||
// Get options structure for stdin
|
||||
tcgetattr(STDIN_FILENO, &settings)
|
||||
|
||||
//Turn off ICANON and ECHO
|
||||
settings.c_lflag &= ~tcflag_t(ICANON | ECHO)
|
||||
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &settings)
|
||||
}
|
||||
|
||||
func getKeyPress () -> UnicodeScalar {
|
||||
let valueRead: Int = Int(getchar())
|
||||
|
||||
guard let charRead = UnicodeScalar(valueRead) else{
|
||||
return UnicodeScalar("")!
|
||||
}
|
||||
|
||||
return charRead
|
||||
}
|
||||
|
||||
func printToScreenFrom(myself: Bool, characterToPrint: UnicodeScalar){
|
||||
|
||||
if(myturn && !myself){
|
||||
myturn = false
|
||||
print("\n\nOther: ", terminator:"")
|
||||
} else if (!myturn && myself){
|
||||
myturn = true
|
||||
print("\n\nMe: ", terminator:"")
|
||||
}
|
||||
|
||||
print(characterToPrint, terminator:"")
|
||||
}
|
||||
|
||||
func backgroundRead() {
|
||||
while true{
|
||||
do{
|
||||
let readCharacter = try serialPort.readChar()
|
||||
printToScreenFrom(myself: false, characterToPrint: readCharacter)
|
||||
} catch {
|
||||
print("Error: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
do {
|
||||
|
||||
print("Attempting to open port: \(portName)")
|
||||
try serialPort.openPort()
|
||||
print("Serial port \(portName) opened successfully.")
|
||||
defer {
|
||||
serialPort.closePort()
|
||||
print("Port Closed")
|
||||
}
|
||||
|
||||
serialPort.setSettings(receiveRate: .baud9600,
|
||||
transmitRate: .baud9600,
|
||||
minimumBytesToRead: 1)
|
||||
|
||||
prepareStdin()
|
||||
|
||||
|
||||
//Turn off output buffering if not multiple threads will have problems printing
|
||||
setbuf(stdout, nil);
|
||||
|
||||
|
||||
//Run the serial port reading function in another thread
|
||||
#if os(Linux)
|
||||
var readingThread = pthread_t()
|
||||
|
||||
let pthreadFunc: @convention(c) (UnsafeMutableRawPointer?) -> UnsafeMutableRawPointer? = {
|
||||
observer in
|
||||
|
||||
backgroundRead()
|
||||
return nil
|
||||
}
|
||||
|
||||
pthread_create(&readingThread, nil, pthreadFunc, nil)
|
||||
|
||||
#elseif os(OSX)
|
||||
DispatchQueue.global(qos: .userInitiated).async {
|
||||
backgroundRead()
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
print("\nReady to send and receive messages in realtime!")
|
||||
print("\nMe: ", terminator:"")
|
||||
|
||||
|
||||
while true {
|
||||
var enteredKey = getKeyPress()
|
||||
printToScreenFrom(myself: true, characterToPrint: enteredKey)
|
||||
var _ = try serialPort.writeChar(enteredKey)
|
||||
}
|
||||
|
||||
|
||||
} catch PortError.failedToOpen {
|
||||
print("Serial port \(portName) failed to open. You might need root permissions.")
|
||||
} catch {
|
||||
print("Error: \(error)")
|
||||
}
|
||||
|
||||
|
||||
42
README.md
42
README.md
@@ -59,19 +59,43 @@ nano ~/.profile
|
||||
export PATH=$HOME/swift-3.0/usr/bin:$PATH
|
||||
```
|
||||
## Jumping straight into sample code
|
||||
To get started quickly, you can take a look at my example projects [here](Examples/).
|
||||
|
||||
To get started quickly, you can take a look at my example project [here](Examples/SwiftSerialExample). In order to run the example properly, you need to connect one of your (USB/UART) serial ports in a loopback manner. Basically, you short the TX and RX pins of the serial port.
|
||||
### Example 1: Loopback Test
|
||||
|
||||
In order to run this example properly, you need to connect one of your (USB/UART) serial ports in a loopback manner. Basically, you short the TX and RX pins of the serial port. This library currently only support the `/dev/cu.*` variant on Mac. Read the beginning of the API usage section for more details.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/yeokm1/SwiftSerial.git
|
||||
cd SwiftSerial/Examples/SwiftSerialExample/
|
||||
swift build
|
||||
#You need root to access the serial port. Replace /dev/ttyUSB0 with the name of your serial port under test
|
||||
|
||||
#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.
|
||||
```
|
||||
|
||||
### Example 2: A chat app between 2 machines
|
||||
|
||||
In order to run this example properly, you need 2 machines connected by a [null-modem cable](https://en.wikipedia.org/wiki/Null_modem) or 2 USB-Serial adapters with the TX-RX pins connected to each other. Run a copy of my program on both machines.
|
||||
|
||||
```bash
|
||||
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
|
||||
```
|
||||
People at both machines can now "chat" with each other.
|
||||
|
||||
## Integrating with your project
|
||||
|
||||
Add SwiftSerial as a dependency to your project by editing the `Package.swift` file.
|
||||
@@ -98,7 +122,9 @@ Then run `swift build` to download the dependencies and compile your project. Yo
|
||||
```swift
|
||||
let serialPort: SerialPort = SerialPort(path: portName)
|
||||
```
|
||||
Supply the portname that you wish to open like `/dev/ttyUSB0` or `/dev/tty.usbserial`.
|
||||
Supply the portname that you wish to open like `/dev/ttyUSB0` or `/dev/cu.usbserial`.
|
||||
|
||||
For Macs, this library currently only works with the `/dev/cu.*` ports instead of the `/dev/tty.*`. I have enabled blocking on the serial port to prevent high CPU usage which will prevent the `/dev/tty.*` from working. Read more about the differences between the two [here](http://stackoverflow.com/questions/8632586/macos-whats-the-difference-between-dev-tty-and-dev-cu). If there is a problem, open an issue describing your situation and let me look into it.
|
||||
|
||||
### Opening the Serial Port
|
||||
|
||||
@@ -162,6 +188,11 @@ func readUntilChar(_ terminator: CChar) throws -> String
|
||||
```
|
||||
Keep reading until the specified CChar is encountered. Return the string read so far without that value.
|
||||
|
||||
```swift
|
||||
func readChar() throws -> UnicodeScalar
|
||||
```
|
||||
Read only one character. This works best if `minimumBytesToRead` has been set to `1` when opening the port. This function internally calls `readBytes()`.
|
||||
|
||||
### Writing data to the port
|
||||
|
||||
There are several functions you can use to write data. All functions here are blocking till all the data has been written. All functions can throw `PortError.mustBeOpen`.
|
||||
@@ -181,6 +212,11 @@ func writeBytes(from buffer: UnsafeMutablePointer<UInt8>, size: Int) throws -> I
|
||||
```
|
||||
Function for those that want to mess with unsafe pointers. You have to specify how many bytes have to be written. Will return how many bytes actually written.
|
||||
|
||||
```swift
|
||||
func writeChar(_ character: UnicodeScalar) throws -> Int{
|
||||
```
|
||||
Writes only one character. Will return `1` if successful. This function internally calls `writeString()`. Pull requests for a better way of doing this is appreciated.
|
||||
|
||||
### Closing the port
|
||||
|
||||
Just do `serialPort.closePort()` to close the port once you are done using it.
|
||||
|
||||
@@ -191,7 +191,7 @@ public enum ParityType {
|
||||
case even
|
||||
case odd
|
||||
|
||||
var parityValue: tcflag_t {
|
||||
var parityValue: tcflag_t {
|
||||
switch self {
|
||||
case .none:
|
||||
return 0
|
||||
@@ -248,7 +248,7 @@ public class SerialPort {
|
||||
#if os(Linux)
|
||||
fileDescriptor = open(path, readWriteParam | O_NOCTTY)
|
||||
#elseif os(OSX)
|
||||
fileDescriptor = open(path, readWriteParam | O_NOCTTY | O_EXLOCK | O_NONBLOCK)
|
||||
fileDescriptor = open(path, readWriteParam | O_NOCTTY | O_EXLOCK)
|
||||
#endif
|
||||
|
||||
// Throw error if open() failed
|
||||
@@ -389,7 +389,7 @@ extension SerialPort {
|
||||
data = Data(bytes: buffer, count: bytesRead)
|
||||
} else {
|
||||
//This is to avoid the case where bytesRead can be negative causing problems allocating the Data buffer
|
||||
data = Data(bytes: buffer, count: 0)
|
||||
data = Data(bytes: buffer, count: 0)
|
||||
}
|
||||
|
||||
return data
|
||||
@@ -421,17 +421,17 @@ extension SerialPort {
|
||||
}
|
||||
|
||||
while true {
|
||||
let bytesRead = try readBytes(into: buffer, size: 1)
|
||||
let bytesRead = try readBytes(into: buffer, size: 1)
|
||||
|
||||
if bytesRead > 0 {
|
||||
let character = CChar(buffer[0])
|
||||
if bytesRead > 0 {
|
||||
let character = CChar(buffer[0])
|
||||
|
||||
if character == terminator {
|
||||
break
|
||||
} else {
|
||||
data.append(buffer, count: 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let string = String(data: data, encoding: String.Encoding.utf8) {
|
||||
@@ -445,6 +445,25 @@ extension SerialPort {
|
||||
let newlineChar = CChar(10) // Newline/Line feed character `\n` is 10
|
||||
return try readUntilChar(newlineChar)
|
||||
}
|
||||
|
||||
public func readChar() throws -> UnicodeScalar {
|
||||
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: 1)
|
||||
|
||||
defer {
|
||||
buffer.deallocate(capacity: 1)
|
||||
}
|
||||
|
||||
while true {
|
||||
let bytesRead = try readBytes(into: buffer, size: 1)
|
||||
|
||||
if bytesRead > 0 {
|
||||
let character = UnicodeScalar(buffer[0])
|
||||
return character
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: Transmitting
|
||||
@@ -480,4 +499,10 @@ extension SerialPort {
|
||||
|
||||
return try writeData(data)
|
||||
}
|
||||
|
||||
public func writeChar(_ character: UnicodeScalar) throws -> Int{
|
||||
let stringEquiv = String(character)
|
||||
let bytesWritten = try writeString(stringEquiv)
|
||||
return bytesWritten
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user