はじめに
VALU Advent Calendar 2018 の8日目です!
VALU では iOS 人材ですが,趣味では電子工作も好きだったりします。今回は,Swift の魅力をもっとお伝えしようと,Swift を用いてシリアル通信が簡単に実現可能なライブラリ,ORSSerialPort をご紹介致します。
今回は,シリアル通信で Arduino (Arduino UNO R3) と通信をするプログラムを作成していこうと思います。
Arduino とは
https://www.arduino.cc:
Open-source electronic prototyping platform enabling users to create interactive electronic objects.
Arduino とは,基盤がオープンソースで公開されている,安価な開発用マイコンボードを指します。Amazon でも 3,000 円程度で入手できるので試作用にはとても便利です。内部には AVR が載っていて,5-12 VといくつかのI/Oボードとクロックが搭載されています。
それでは以下より,プロジェクトを作成していきます。
プロジェクトファイルの作成 (CLIの場合)
最初に,プロジェクトファイルを作成します。今回は Swift Package Manager を利用しました。
$ mkdir (プロジェクト名) && cd $_ && swift package init --type executable && swift build && swift package generate-xcodeproj
残念ながらライブラリが Swift PM に対応していないので,Wiki に習い,submodule として組み込むようにします。
$ git submodule add https://github.com/armadsen/ORSSerialPort.git Frameworks/ORSSerialPort
submodule を追加したら,次に (プロジェクト名).xcworkspace
を作成します。
そして,自身のプロジェクトおよび Frameworks/ORSSerialPort/Framework Project/ORSSerialPort.xcodeproj
を workspace に追加します。以下のようであれば良いでしょう。また,この段階で一度 ORSSerial
scheme をビルドしておきましょう。
プロジェクトファイルの作成 (macOS Appの場合)
macOS アプリの場合は,CocoaPods を利用します。プロジェクトを作成した後,Podfile
に以下を入力します。
pod 'ORSSerialPort'
Terminal にて以下のコマンドでインストールして完了です。
$ pod install
.swift ファイルの編集 (macOS)
接続状態を監視するための ORSSerialPortDelegate
を継承した Delegate
クラスを用意しました。「ポート名」と「ボーレート」によって接続し,テキストの送受信ができるようにしました。
import ORSSerial
// ポート名
let portName: String = "(ポート名)"
// ボーレート
let baudRate: NSNumber = 9600
// 通知用デリゲート
let delegate = Delegate()
// ORSSerialPortManager を利用することでポート名が取得できます。
// print(ORSSerialPortManager.shared().availablePorts.map { $0.name })
// ORSSerialPortManager.shared().availablePorts[0].open()
// ポート名を元に初期化する
guard let port = ORSSerialPort(path: portName) else { exit(1) }
// デリゲートの指定
port.delegate = delegate
// ボーレートの指定
port.baudRate = baudRate
// 接続を開始
port.open()
RunLoop.current.run()
import ORSSerial
class Delegate: NSObject, ORSSerialPortDelegate {
/// 改行が入力されるまでの文字列を格納するテキストバッファ
private var textBuffer = ""
/// 入力
private func input(_ serialPort: ORSSerialPort) {
print("<< ", terminator: "")
let input = readLine() ?? ""
guard
input != "q",
let data = input.data(using: String.Encoding.utf8)
else {
serialPort.close()
exit(0)
}
serialPort.send(data)
}
/// 出力
private func output() {
print(">> \(textBuffer)")
textBuffer = ""
}
/// ポートからの入力を検知した際に呼ばれる
func serialPort(_ serialPort: ORSSerialPort, didReceive data: Data) {
guard let string: String = NSString(data: data, encoding: String.Encoding.utf8.rawValue) as String? else { return }
guard string.contains("\r") || string.contains("\n") else {
textBuffer += string
return
}
textBuffer += string
.replacingOccurrences(of: "\r", with: "")
.replacingOccurrences(of: "\n", with: "")
output()
input(serialPort)
}
/// ポート接続がシステムから開放された際に呼ばれる
func serialPortWasRemovedFromSystem(_ serialPort: ORSSerialPort) {
print("Serial port \(serialPort) was removed from system")
}
/// ポート接続にエラーが生じた際に呼ばれる
func serialPort(_ serialPort: ORSSerialPort, didEncounterError error: Error) {
print("Serial port (\(serialPort)) encountered error: \(error)")
}
/// ポート接続が完了した際に呼ばれる
func serialPortWasOpened(_ serialPort: ORSSerialPort) {
print("Serial port \(serialPort) was opened")
}
}
.ino ファイルの編集 (Arduino)
Arduino を mac に接続し,以下のコードを upload します。
今回は例として入力ピン番号に応じて 1秒間点灯させるプログラムを書いてみました。
// 全てOutputモードに設定
void pinModeAllOutput() {
int UnoPinNum = 13;
for(int i=0; i <= UnoPinNum; i++) {
pinMode(i, OUTPUT);
}
}
// String to Int
int convertNum(String str) {
return str.toInt();
}
// 1秒間点灯
void flash(int num) {
digitalWrite(num, HIGH);
delay(1000);
digitalWrite(num, LOW);
}
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
pinModeAllOutput();
Serial.println("Start!");
}
void loop() {
// put your main code here, to run repeatedly:
if (Serial.available()) {
String str = Serial.readStringUntil('\n');
String strx = str + '\n';
int len = strx.length() + 1;
char buf[len];
strx.toCharArray(buf, len);
int num = convertNum(buf);
Serial.println(num);
flash(num);
}
Serial.flush();
delay(2);
}
接続先の選択
Terminal から以下を入力し,有効なポートを検索します。
$ ls -l /dev/cu.*
crw-rw-rw- 1 root wheel 21, 3 12 8 00:00 /dev/cu.usbmodem140001
上記の場合 /dev/cu.usbmodem141001
がポート名となります。
main.swift
の var portName: String = "(ポート名)"
に代入します。
また,ORSSerialPort
にある ORSSerialPortManager
クラスを利用しすることでも,現在有効なポート一覧を取得することが可能です。
ORSSerialPortManager.shared().availablePorts
にて取得可能です。
いざ接続 !
USB 接続を確認したら,Xcode から project をビルドして実行します。
Arduino UNO は 13 番ピンに LED が接続されており,LED を接続せずとも L チカが可能です。
Xcode の デバッグエリアに「13」と送信して Enter を押すと,デバッグエリアに「>> 13」と返り,13 番ピンに隣接した LED が 1 秒間点灯します。
Serial port usbmodem141001 was opened
>> Start!
<< 13
>> 13�
<< q
Serial port usbmodem141001 was removed from system
Program ended with exit code: 0
おわりに
いかがでしたでしょうか。
今回は,Swift で書くシリアル通信についての記事でした。
実行可能なコードは全て掲載しておりますので,GitHub 等のリンクは特にありません。もし利用される場合はライブラリに合わせ MIT としておきます。
ライブラリの Example projects に複数のサンプル例がありますので,こちらも参照してみてください。
IoT 機器を自作するのもとても楽しいですよ。
明日の VALU Advent Calendar 2018 は,セルオートマトンについてだそうです。どんな内容なのでしょうか。。