Serial Port Programming in Swift for Mac OS X

This tutorial explains how you can program the serial port in Swift. We guide you through a reference implementation for your own projects including all practical steps. The main prerequisite for this tutorial is that you have installed CocoaPods.

1. Background Information

A simple serial port program has three main functions. First it should init the serial port, and then it should read data from the serial port and transmit data read from the serial port. The latter requirement means that two processes are necessary running simultaneously.

In a Unix environment a serial port is accessed via a file descriptor. This means a terminal program uses the file access API to read and write data to a serial port. The serial port handle is available in /dev and has the prefix cu, which stands for calling-unit. To access the serial port, this handle is just opened as an ordinary file.  The opened serial port file descriptor also conforms to the TTY API for setting serial port specific settings like baudrate.

This tutorial describes how to implement the simple serial port program in OS X in Swift. Accessing a serial port in OS X requires the use of three API’s: IOKit, common BSD file handling APIs and BSD TTY APIs. The first two are reasonably easy to use from Swift, but the third does not import quite well. The BSD TTY APIs uses complex macros and varargs which are not supported by Swift directly. The obvious choice is to use an Objective-C or C wrapper as we also will do in this tutorial. We could write our own wrapper, but luckily, an excellent serial port library exists. This library is ORSSerialPort which provides a great API for using serial ports in OS X. The last nice feature of the ORSSerialPort library is that it also handles the threading related to using the serial port.

2. Setting up Xcode

Start Xcode, and create a new Command Line project for OS X.

Start Project

start_swift_serial_project

3. Import ORSSerialPort

There are several ways to include ORSSerialPort to your project. We use the CocoaPods method, which requires you have installed CocoaPods.
First, create a Podfile in the root directory of your Xcode project. We did it via the command line:

touch Podfile

create_POD_file_2

 

Edit the file and add the line:

pod "ORSSerialPort"

add_ORSSerialPort_to_Podfile_2
Run CocoaPods to download ORSSerialPort:

pod update

run_pod_update

During downloading the dependencies CocoaPods creates an Xcode workspace project. Close your earlier created project, and reopen the workspace file. Xcode should now open with a new folder “Pods” containing the ORSSerialPort.

reopen_project_as workspace

Try to run the project, it should print “Hello World!”. Note, the ORSSerialPort library uses the newest API functionality of Apple, like NS_ASSUME_NONNULL and nullable.  in case you use XCode prior to 6.3.2 you need to disable the NS_ASSUME_NONNULL_BEGIN and NS_ASSUME_NONNULL_END macros and remove the ‘nullable’ property in de ORSSerialPort source code.

4. Hooking Objective-C code to your Swift project

To use the ORSSerialPort library in your Swift code you need to link it via a bridge header. First add a new header file to your project.

add_bridging_header

Add the following lines to this bridge header file:

#import "ORSSerialPort.h"
#import "ORSSerialPortManager.h"

Second, you need to tell the compiler to use this bridge header in the linking process. Under the project Build Settings, set the Objective-C Bridging Header build setting under Swift Compiler – Code Generation to the path of the header relative to the project root.

set_bridging_header_in_build_settings

Try tool compile the project to see if it still works.

5. The Serial Terminal Program

You are now ready to access the serial port from Swift. We removed all the serial device detection code in this example, but it is not hard to add our example, or use the ORSSerialPortManager provided by ORSSerialPort.

The complete source of the simple serial program for Swift:

class SerialHandler : NSObject, ORSSerialPortDelegate {
    let standardInputFileHandle = NSFileHandle.fileHandleWithStandardInput()
    var serialPort: ORSSerialPort?
    
    func runProcessingInput() {
        setbuf(stdout, nil)
        
        standardInputFileHandle.readabilityHandler = { (fileHandle: NSFileHandle!) in
            let data = fileHandle.availableData
            dispatch_async(dispatch_get_main_queue(), { () -> Void in
                self.handleUserInput(data)
            })
        }
        
        self.serialPort = ORSSerialPort(path: "/dev/cu.Repleo-PL2303-00401414") // please adjust to your handle
        self.serialPort?.baudRate = 9600
        self.serialPort?.delegate = self
        serialPort?.open()

        NSRunLoop.currentRunLoop().run() // loop
    }

    
    func handleUserInput(dataFromUser: NSData) {
        if let string = NSString(data: dataFromUser, encoding: NSUTF8StringEncoding) as? String {
            
            if string.lowercaseString.hasPrefix("exit") ||
                string.lowercaseString.hasPrefix("quit") {
                    println("Quitting...")
                    exit(EXIT_SUCCESS)
            }
            self.serialPort?.sendData(dataFromUser)
        }
    }
    
    // ORSSerialPortDelegate
    
    func serialPort(serialPort: ORSSerialPort, didReceiveData data: NSData) {
        if let string = NSString(data: data, encoding: NSUTF8StringEncoding) {
            print("\(string)")
        }
    }
    
    func serialPortWasRemovedFromSystem(serialPort: ORSSerialPort) {
        self.serialPort = nil
    }
    
    func serialPort(serialPort: ORSSerialPort, didEncounterError error: NSError) {
        println("Serial port (\(serialPort)) encountered error: \(error)")
    }
    
    func serialPortWasOpened(serialPort: ORSSerialPort) {
        println("Serial port \(serialPort) was opened")
    }
}


println("Starting serial test program")
println("To quit type: 'exit' or 'quit'")
SerialHandler().runProcessingInput()

How does it works: First, a SerialHandler class is defined, which implements the ORSSerialPortDelegates. When this class is initialized, it setups a stdin reader. This reader calls the “handleUserInput” function to send the received data to the serial port. Second, it setups the serial port and adds itself as delegate to the serial port object provided by ORSSerialPort. This Delegate handles all the events of the serial port. The function “serialPort” will be called if the serial port receives data. Last step is that it keeps itself in a loop to listen to all incoming data.

You should adjust the handle in this code to the filename of your serial device. When you run the tool you should be able to send and receive data:

transmit_receive_data

 

6. Downloads

You can download the code from GitHub via: https://github.com/bjarnoldus/osx-swift-serial

Serial Port Access in VirtualBox Machines running on Mac OS X

This tutorial explains how to enable serial port access in virtual machines running in a VirtualBox environment on Mac OS X. The presented approach uses the serial ports available on the OS X system and will propagate them to the virtual machine.

1. Find the Serial Ports

First, you need to find the serial ports on your OS X machine. Open a terminal, and provide the following command: ls /dev | grep Repleo

Terminal_Find_Serial_Ports

The handles for the serial port we will use later on are:

  • /dev/cu.Repleo-CH341-00302414
  • /dev/cu.Repleo-PL2303-00002314

Note, that in this case two serial adapters are connected on the machine, and the tail of the handle is dependent on the port to which the adapter is connected. This tail may change if the USB to Serial cable is connected to a different port.

2. Configure VirtualBox

Start VirtualBox and before firing up a virtual machine, select the settings menu. Within the settings menu, select the ports tab. Please make sure the virtual machine is really turned off, and not in a saved state, otherwise you won’t be able to make changes to this configuration. Enable the serial port and select the mode: Host device and fill in the device path.

 

Enable_Serial_Port_in_VirtualBox

3. Start the Virtual Machine

Start the virtual machine, make sure that the virtual ports are not used by other applications. The virtual machine will now show you the serial ports. For example after a linux boot, run the following command: dmesg | grep serial

Serial_Port_in_Linux_Virtual_Machine

In our case we configured both serial ports.

4. Access the Serial Ports

In Linux, open for example Minicom on the command line (minicom -c on) and configure the port.

MiniCom_in_VirtualBox

Enter the local device handle, and please make sure hardware handshake is turned off (except if you have connected all the RTS/CTS lines). Save and close the configuration window. You will now be able to send and receiver data via the serial port in your virtual machine running on an OS X host.

Sending_Bytes_in_Minicom_VirtualBox

 5. Notes on Propagating the USB device to the Virtual Machine

VirtualBox allows to propagate a USB device to the virtual machine. Within OS X this sometimes causes an error message that the device is busy. Currently, the only solution to this problem is to toy around with kextunload,  kextload and rebooting.

USB_Attach_VirtualBox_Fails

Detect Serial Devices in Swift

This tutorial explains how you can automatically detect serial devices using Swift on OS X. Mac OS X automatically enumerates serial devices connected to a Mac. This functionality is provided by the  I/O Kit. These devices are categorized, so you can easily filter for the devices where you are interested in. We guide you how to use this mechanism in setting up a simple implementation.

1. Setting up Xcode

Start Xcode, and create a new Command Line project for OS X.

Start Project

Start Project Name

Add the following imports to main.swift:

import IOKit
import IOKit.serial

Run the code program, and “Hello World” should be printed on your screen. We are now ready for detecting serial devices.

Hello World

The Serial Device Discover Function

The function findSerialDevices does the actual discovery of serial devices. It calls the kernel to match against the service property of serial device. The deviceType can be used to filter the results for more specific devices, like modems. The seriapPortIterator is a pointer to the iterator, which findSerialDevices will set. The return value is a kernel return type, and may contain an error message.

func findSerialDevices(deviceType: String, inout serialPortIterator: io_iterator_t ) -> kern_return_t { 
  var result: kern_return_t = KERN_FAILURE 
  var classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue).takeUnretainedValue() 
  var classesToMatchDict = (classesToMatch as NSDictionary) 
      as Dictionary<String, AnyObject> 
  classesToMatchDict[kIOSerialBSDTypeKey] = deviceType 
  let classesToMatchCFDictRef = (classesToMatchDict as NSDictionary) as CFDictionaryRef 
  result = IOServiceGetMatchingServices(kIOMasterPortDefault, classesToMatchCFDictRef, &serialPortIterator); 
  return result 
}

2. The Serial Device Discover Function

The printSerialPaths function shows how an returned iterator of findSerialDevices can be used. This function print all the discovered callout paths to the output screen. In real applications this code can be adapted to return a list of discovered devices.

func printSerialPaths(portIterator: io_iterator_t) {
  var serialService: io_object_t
  do {
    serialService = IOIteratorNext(portIterator)
    if (serialService != 0) {
      let key: CFString! = "IOCalloutDevice"
      let bsdPathAsCFtring: AnyObject? = 
          IORegistryEntryCreateCFProperty(serialService, key, kCFAllocatorDefault, 0).takeUnretainedValue()
      var bsdPath = bsdPathAsCFtring as String?
      if let path = bsdPath {
        println(path)
      }
    }
  } while serialService != 0;
}
The Main Example

The previous functions can be called in the following order. The portIterator is an empty variable used as pointer for the iterator. This example searches for all serial devices by applying kIOSerialBSDAllTypes, other options are:

  • kIOSerialBSDRS232Type – Generic Serial RS-232 devices
  • kIOSerialBSDModemType – Devices enumerated as modem
var portIterator: io_iterator_t = 0
let kernResult = findSerialDevices(kIOSerialBSDAllTypes, &portIterator)
if kernResult == KERN_SUCCESS {
  printSerialPaths(portIterator)
}

Detect Devices

3. Downloads

You can download the code from GitHub via: https://github.com/bjarnoldus/swift-detect-serial

General Serial Port Basics

A serial port is a physical communication interface through which information transfers in or out one bit at a time. Since the introduction of the Personal Computer in the 80’s, the serial port is used for long distance communication, for example via modems, and communication with peripherals, like industrial automation systems, scientific instruments, point of sale systems, console for servers and network equipment and consumer products like GPS trackers. Implementing serial ports in equipment is still one of the easiest and cheapest ways to enable control by another computer and the console function of serial port communication is highly standardized and widespread. Most times serial port access require minor configuration and very little supporting software from the host system.

The Serial Stream

The serial port is a full-duplex device: It can send and receive data at the same time using two separated lines for receiving and transmitting data, and separated buffers. It is implement in hardware using a shift-register controlled by a internal clock. Therefor, both, receiver and transmitter, must be configured with the same baudrate. To initiate receiving, the transmitting device initiates with a start bit, followed by the actual data bits. There may either be 5, 6, 7, or 8 data bits, depending on the configuration. Both receiver and the transmitter must be configured in the same way for successful transfer.

Serial Signal

After the data has been transmitted, a stop bit is sent. A stop bit is a mark state before a new start bit can be created. Stop bits can be 1, 1.5, or 2 bit periods in length.

The RS-232 Connector (DB-9 version)

RS-232 is a standard for serial communication and transmission of data and titled “Interface Between Data Terminal Equipment (DTE) and Data Circuit-Terminating Equipment (DCE) Employing Serial Binary Data Interchange”. The serial ports on most computers use a subset of this standard, as the RS-232 standard specifies a 25-pin  connector, while most serial PC connectors only have 9 pins.

The RS-232 standard is defined for long distance communication, so it not only defines data signal lines, but also control lines. Two terms are important in this context: DTE and DCE to define the direction of the control lines. A computer is normally used as DTE. The DTE device uses a male connector and DCE a female connector. A cable between those devices is a straight pin-for-pin connection.

db9 pinout

 

  1. Carrier Detect (CD) – incoming
  2. Received Data (RD) – incoming
  3. Transmitted Data (TD) – outgoing
  4. Data Terminal Ready (DTR) – outgoing
  5. Electrical Ground
  6. Data Set Ready (DSR) – incoming
  7. Request To Send (RTS) – outgoing
  8. Clear To Send (CTS) – incoming
  9. Ring Indicator (RI) – incoming

The Carrier Detect is used by a modem to signal that it has a made a connection with another modem. In case no carrier is available, the serial data is used to send commands to the modem.

The Received Data and Transmit Data are the actual communication wires. In case of the RD line, it is incoming on the DTE, and the DCE should keep it in the mark (stop bit) condition till it sends a byte. For the TD line, the DCE is the sender.

The Request To Send and Clear To Send lines are used when hardware flow control is enabled in both the DTE and DTE devices. The DTE device puts this line in a mark condition to tell the DCE device that it is ready and able to receive data. If the DTE device is not able to receive data (typically because its receive buffer is almost full), it will put this line in the space condition as a signal to the DCE to stop sending data. When the DTE device is ready to receive more data (i.e. after data has been removed from its receive buffer), it will place this line back in the mark condition. The complement of the RTS wire is CTS. The DCE device puts this line in a mark condition to tell the DTE device that it is ready to receive the data. This flow control can also be implemented in software, called Xon/XOff or “software” flow control. Software flow control uses special control characters transmitted from one device to another to tell the other device to stop or start sending data. With software flow control the RTS and CTS lines are not normally used.

The Data Terminal Ready and Data Set Ready are intended for the same functionality as RTS/CTS.

The Ring Indicator is toggled by the modem if it receives an incoming call.

The Loop Back

A serial loop back is a cable where all data from the PC is directly send back to it. This cable is useful for testing a serial connection, i.e. if the software stack is really sending data, or to test if the hardware is really working. A loop back can be easily constructed by just connecting pin 2 and pin 3 of the DB-9 connector, however, if handshake signals are involved a slightly more complex cable should be used:

serial loop back

The Null Modem

The Null Modem is a cable to connect a DTE device to another DTE device, in other words, to make a connection between to PC’s using the serial device. The Null Modem cable swaps the incoming and outgoing singles and a “poor man’s” version is just to cross lines 2 a 3 and connect pin 5 straight. In case control flow signals are involved the following cable should be used:

Full Null Modem

Upload programs to an Arduino with your Mac in 4 steps

This tutorial is a starting point for programming Arduino boards on your Mac. It shows how to install the software and to upload your first program to an Arduino board.

Arduino is an open-source electronics platform based on easy-to-use hardware and software. It’s intended for anyone making interactive projects. The Arduino hardare is based on the AVR microcontroller family of Atmel and programming these microcontroller uses the STK-500 protocol of Atmel. The STK-500 protocol was originally designed for a serial rs-232 connection, nowadays, a USB to serial converter is used to provide a direct connection with your PC. In case of Arduino clones, most times an alternative chip as CH340 is used instead of the FTDI device.

Prerequisites

Before you can use your Arduino board with your Mac, you need to install the following packages:

Step 1 – Open Serial Example Project

Start the Arduino suite, when it is finished open a serial example project via the File menu. This tutorial uses SerialCallResponseASCII.

Select Serial Example Project

The Arduino suite opens a window containing the example code.
Serial Test Program

Step 2 – Connect the Arduino Board

Before you can upload a program to your Arduino Board you need to configure the suite for the board. First select the correct board:

Select Arduino Type
Next, select the serial handle to which your Arduino board is connected. It is imported to use the .cu handle, otherwise a connection cannot be established. It is possible to connect multiple Arduino boards to your Mac, as the CH34* and PL2303 drivers generate a handle name based on the physical USB location.

Select Arduino's Serial Port

Step 3 – Program the Arduino Board

You are now ready to upload the program to your Arduino. Press the upload button in the editor, you can recognize it by the arrow pointing to right. After pressing this button, the console window shows the compilation process and finally the upload process.

Press Upload Button

Step 3 – Ready

Your are finished, and the Arduino has been programmed. The program you uploaded continuously transmit data from its serial port. If you open the serial monitor, you see it transmits every couple of seconds 0,0,0, when you send a character to the device, it answers with three values.

Open Serial Monitor
Receive Data


Serial Port Access on your Mac with CoolTerm

A basic tool for testing your serial port connection on your Mac is CoolTerm by Roger Meier:

CoolTerm is a simple serial port terminal application (no terminal emulation) that is geared towards hobbyists and professionals with a need to exchange data with hardware connected to serial ports such as servo controllers, robotic kits, GPS receivers, microcontrollers, etc.

You can download CoolTerm here (mirror).

Step 1 – Start CoolTerm

Start CoolTerm

CoolTerm is idle state when you open it. First you need to select and configure the serial port by pressing the options button.

Step 2 – Configure and select the Serial Port

Select Options

Configure the serial port and select the serial port. If CoolTerm doesn’t show your serial port, you need to press the Re-Scan Serial Ports button. The serial port should show up in the selection list, if it doesn’t show, please check if the driver has been installed correctly (see support).

Select Serial Port

Press the ok button when you’re finished.

Step 3 – Use the Serial Port

CoolTerm is still Idle, press the Connect button to open the serial port. After pressing this button the character your type are send to the outer-space via the serial port, and characters it receives are displayed in the window. You can also monitor the handshake signals via the signal LEDS in the right corner, and you can control the RTS and DTR signal by pressing the signal LEDS

Connect