Raspberry Pi で Swift を動かす
少し前に、SwiftyGPIO というライブラリを発見し、ついに Swift が電子工作に使えるようになったのか、と試してみたいと思い、着手していました。
ただ、Apple が公式に Swift.org で配布している Snapshot バイナリは、x86_64 環境でビルドされたもので、ARM CPU を使っている Raspberry Pi では実行することができません。
SwiftyGPIO の作者である、uraimo 氏のブログ記事 に記載されている方法でさくっとサンプルを動かすところまでは、とても簡単に実践できます。
環境設定
使用機材は Raspberry Pi Type B、OS は Raspbian Jessie 2016-05-27 です。
clang
をインストールし、バイナリーをダウンロードし、解答します。前述のブログ記事からのコピペです。
$ sudo apt-get update
$ sudo apt-get install clang
$ wget https://www.dropbox.com/s/smk3ek5lfa8ae09/swift-2016-02-15.tar.gz
$ tar xzf swift-2016-02-15.tar.gz
Hello world
で動作確認します。
$ export PATH=$HOME/usr/bin:$PATH
$ which swiftc
/home/pi/usr/bin/swiftc
$ echo 'print("Hello world")' > helloworld.swift
$ swiftc helloworld.swift
$ ./helloworld
Hello world
簡単なLチカ
README の Example コードのボードと Pin の値だけ修正すると、すんなりLチカができます。
// main.swift
import Glibc
let gpios = SwiftyGPIO.getGPIOsForBoard(.RaspberryPiRev2)
var gp = gpios[.P9]!
gp.direction = .OUT
gp.value = 1
repeat{
gp.value = (gp.value == 0) ? 1 : 0
usleep(150*1000)
}while(true)
$ wget https://raw.githubusercontent.com/uraimo/SwiftyGPIO/master/Sources/SwiftyGPIO.swift
$ swiftc main.swift SwiftyGPIO.swift
$ sudo ./main
SwiftyGPIO.getGPIOsForBoard(board: SupportedBoard)
で指定可能なボードは SwiftyGPIO.swift に入っている Enum で定義されています。
public enum SupportedBoard {
case RaspberryPiRev1 // Pi A,B Revision 1
case RaspberryPiRev2 // Pi A,B Revision 2
case RaspberryPiPlusZero // Pi A+,B+,Zero with 40 pin header
case RaspberryPi2 // Pi 2 with 40 pin header
case CHIP
case BeagleBoneBlack
}
なんと、Raspberry Pi 3 がありません。 Cortex-A53 ARMv8 64bit で動く Swift のバイナリがないので、動作確認ができないのでしょうか。
入力値に応じて出力値を変更
Usage に記載されているとおり、入力モード Pin の値を変更をクロージャーでハンドリングできます。
GPIO
クラスのインスタンスには、以下の関数が用意されており、引数でクロージャーを渡すことで、値の変更の通知を受け取ります。
onRaising
0
->1
に値が変わったonFalling
1
->0
に値が変わったonChange
何かしら値が変わった
import Glibc
let gpios = SwiftyGPIO.getGPIOsForBoard(.RaspberryPiRev2)
var led = gpios[.P9]!
led.direction = .OUT
led.value = 0
var button = gpios[.P2]!
button.direction = .IN
button.value = 1
button.onChange { button in
led.value = button.value == 1 ? 0 : 1
print(button.value)
}
while(true) {}
Slimane と連携
以上の様に、簡単に GPIO の機能が Swift であつかえます。Raspberry Pi 単体で完結する仕組みを作るには、実用に足るライブラリだと思います。
ここまで実践すると、Internet of Things な、例えば WebSocket のイベントを受け取ると水やりをする、みたいな活用を行いたいと思い始めます。
参考: Raspberry Pi と Hubot で観葉植物の水やりを自動化する
OSS Swift に対応した WebSocket クライアントが見つけられなかったので、手始めに、Tokyo Server-Side Swift Meetup の共同主催者、@noppoman 氏作の Web フレームワーク、Slimane を使って、以下の様に HTTP で PUT
リクエストがあれば、LED と On / Off するサンプルを書いて、ビルドしてみました。
$ curl -XPUT my-raspberry-pi.local:3000/toggle
Package.swift
import PackageDescription
let package = Package(
name: "SlimaneGPIO",
dependencies: [
.Package(url: "https://github.com/noppoMan/Slimane.git", majorVersion: 0, minor: 4),
]
)
Sources/main.swift
import Slimane
import Glibc
let app = Slimane()
let gpios = SwiftyGPIO.getGPIOsForBoard(.RaspberryPiRev2)
var gp = gpios[.P9]!
gp.direction = .OUT
gp.value = 0
app.put("/toggle") { req, responder in
responder {
let wasOn = gp.value == 1
gp.value = wasOn ? 0 : 1
Response(body: "Tuned \(wasOn ? "off" : "on")!")
}
}
try! app.listen()
こちらを swift build
コマンドでビルドしようとしましたが、build
サブコマンドが存在しないので、失敗しました。
$ swift build
error: unable to invoke subcommand: /home/pi/usr/bin/swift-build (No such file or directory)
ダウンロードしてきた、Prebuilt バイナリには、swift-build が含まれておらず、Swift Package Manager の資産は流用できないようです。
Swift on ARM について、深く研究を行われている、@iachievedit 氏が提供されている Jenkins でできた成果物 (Artifacts) のバイナリも swift-build を含んでいませんでした。
参照
TODOs
これから、Raspberry Pi 遊びにも Swift を活用すべく、引き続き研究を続けていき、Tokyo Server-Side Swift Meetup などで経過発表を続けていけたらな、と思います。
- 自前で全部入りの Swift バイナリをビルドする
- クロスコンパイルがうまくできないか OS X 環境でもためしてみる
あたり、試してみたいと思っています。どうぞよろしくお願いいたします。