Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c14b6d2b9 | ||
|
|
4522b9261e | ||
|
|
144c9bbf5a | ||
|
|
20cf246eda | ||
|
|
02e0d4837e | ||
|
|
816720e15c | ||
|
|
c60e0334bd | ||
|
|
ade0cdc3cd | ||
|
|
3e4b905e7b | ||
|
|
e73ea6056d |
@@ -11,7 +11,7 @@ let test3Strings: String = testString + "\n" + testString + "\n" + testString +
|
|||||||
|
|
||||||
let arguments = CommandLine.arguments
|
let arguments = CommandLine.arguments
|
||||||
guard arguments.count >= 2 else {
|
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)
|
exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
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)
|
||||||
|
]
|
||||||
|
)
|
||||||
127
Examples/SwiftSerialIM/Sources/main.swift
Normal file
127
Examples/SwiftSerialIM/Sources/main.swift
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
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("\nOther: ", terminator:"")
|
||||||
|
} else if (!myturn && myself){
|
||||||
|
myturn = true
|
||||||
|
print("\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()
|
||||||
|
}
|
||||||
|
|
||||||
|
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)")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
38
README.md
38
README.md
@@ -59,8 +59,11 @@ nano ~/.profile
|
|||||||
export PATH=$HOME/swift-3.0/usr/bin:$PATH
|
export PATH=$HOME/swift-3.0/usr/bin:$PATH
|
||||||
```
|
```
|
||||||
## Jumping straight into sample code
|
## 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.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/yeokm1/SwiftSerial.git
|
git clone https://github.com/yeokm1/SwiftSerial.git
|
||||||
@@ -71,11 +74,28 @@ swift build
|
|||||||
sudo ./.build/debug/SwiftSerialExample /dev/ttyUSB0
|
sudo ./.build/debug/SwiftSerialExample /dev/ttyUSB0
|
||||||
|
|
||||||
#For Mac: Root is not required
|
#For Mac: Root is not required
|
||||||
./.build/debug/SwiftSerialExample /dev/tty.usbserial
|
./.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.
|
#If all goes well you should see a series of messages informing you that data transmitted has been received properly.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Example 2: An 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
|
## Integrating with your project
|
||||||
|
|
||||||
Add SwiftSerial as a dependency to your project by editing the `Package.swift` file.
|
Add SwiftSerial as a dependency to your project by editing the `Package.swift` file.
|
||||||
@@ -102,7 +122,9 @@ Then run `swift build` to download the dependencies and compile your project. Yo
|
|||||||
```swift
|
```swift
|
||||||
let serialPort: SerialPort = SerialPort(path: portName)
|
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 an problem, open an issue describing your situation and let me look into it.
|
||||||
|
|
||||||
### Opening the Serial Port
|
### Opening the Serial Port
|
||||||
|
|
||||||
@@ -166,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.
|
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
|
### 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`.
|
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`.
|
||||||
@@ -185,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.
|
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
|
### Closing the port
|
||||||
|
|
||||||
Just do `serialPort.closePort()` to close the port once you are done using it.
|
Just do `serialPort.closePort()` to close the port once you are done using it.
|
||||||
|
|||||||
@@ -248,7 +248,7 @@ public class SerialPort {
|
|||||||
#if os(Linux)
|
#if os(Linux)
|
||||||
fileDescriptor = open(path, readWriteParam | O_NOCTTY)
|
fileDescriptor = open(path, readWriteParam | O_NOCTTY)
|
||||||
#elseif os(OSX)
|
#elseif os(OSX)
|
||||||
fileDescriptor = open(path, readWriteParam | O_NOCTTY | O_EXLOCK | O_NONBLOCK)
|
fileDescriptor = open(path, readWriteParam | O_NOCTTY | O_EXLOCK)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Throw error if open() failed
|
// Throw error if open() failed
|
||||||
@@ -445,6 +445,25 @@ extension SerialPort {
|
|||||||
let newlineChar = CChar(10) // Newline/Line feed character `\n` is 10
|
let newlineChar = CChar(10) // Newline/Line feed character `\n` is 10
|
||||||
return try readUntilChar(newlineChar)
|
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
|
// MARK: Transmitting
|
||||||
@@ -480,4 +499,10 @@ extension SerialPort {
|
|||||||
|
|
||||||
return try writeData(data)
|
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