search
LoginSignup
12

More than 3 years have passed since last update.

posted at

Swiftで書くシリアル通信 〜macOS - Arduino を例に〜

はじめに

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 をビルドしておきましょう。

ss 2018-12-06 at 12.25.39.png

プロジェクトファイルの作成 (macOS Appの場合)

macOS アプリの場合は,CocoaPods を利用します。プロジェクトを作成した後,Podfile に以下を入力します。

Podfile
pod 'ORSSerialPort'

Terminal にて以下のコマンドでインストールして完了です。

$ pod install

.swift ファイルの編集 (macOS)

接続状態を監視するための ORSSerialPortDelegate を継承した Delegate クラスを用意しました。「ポート名」と「ボーレート」によって接続し,テキストの送受信ができるようにしました。

main.swift
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()
Delegate.swift
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秒間点灯させるプログラムを書いてみました。

serial.ino
// 全て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.swiftvar portName: String = "(ポート名)" に代入します。

また,ORSSerialPort にある ORSSerialPortManager クラスを利用しすることでも,現在有効なポート一覧を取得することが可能です。
ORSSerialPortManager.shared().availablePorts にて取得可能です。

いざ接続 !

USB 接続を確認したら,Xcode から project をビルドして実行します。
Arduino UNO は 13 番ピンに LED が接続されており,LED を接続せずとも L チカが可能です。

Xcode の デバッグエリアに「13」と送信して Enter を押すと,デバッグエリアに「>> 13」と返り,13 番ピンに隣接した LED が 1 秒間点灯します。

log
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 は,セルオートマトンについてだそうです。どんな内容なのでしょうか。。

References

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
12