JenkinsのビルドステータスをArduinoで表現する

いわゆるパトランプというやつですね。最初USB対応の製品を探したのですが、なんせ3万円とかする…これは買えない…orz というわけで、別件で試作したArduinoのシールドにフルカラーLEDを載せたのを思い出し、これを活用してしまおうということにしました。

用意する物とか

  • Arduino (今回は手元にあったUno)
  • USBケーブル(ArduinoとサーバPCを接続します)
  • Jenkinsが動いてるサーバ(Ubuntu 12.04)
    • Jenkinsはサーバ上でlocalhost:9090/jenkins/で動いてます
  • Node.js + node-serialport
    • Node.jsはサーバ上でlocalhost:3000/で動かします

構成

今回悩んだのはシリアルのコントロールでした。Jenkinsのジョブでシェルスクリプトを使って、Arduinoにシリアル経由でコマンドを投げたかったのですが、うまい方法を思いつかず、手っ取り早くNode.js + node-serialportを使って実装することにしました。

f:id:tomo_watanabe:20130716233556p:plain

Jenkinsにプラグインを導入

Jenkinsのビルドのステータス通知を取得すること。ビルド実行後にシェルスクリプトをジョブから実行する必要があります。 このサイトにズバリ書いてありました。

ステータス通知は

RESULT=`curl http://localhost:9090/jenkins/job/[HogeProject]/lastBuild/api/xml | perl -le '$_=<>;print [/<result>(.+?)</]->[0]'`
(HogeProjectの場合)

で取れます。そしてビルド実行後の処理にシェルスクリプトを使うためにPost build taskプラグインを導入しておきます。

Node.jsサーバを立てる

Node.jsサーバをポート3000番で立てます。Expressを使って作成し、node-serialportを組込みます。この辺はRaspberry PiでNode.jsを動かす(※Raspberry Piですが、Ubuntuでも基本は変わりません)とかSerialportを使ってNode.jsでArduinoと通信するを参考にしてください。ちなみに今回はSocket.IOは必要ありません。Expressでサーバだけ作って、シリアルにデータ出力さえできればいいので。さて、UbuntuではArduinoは"/dev/ttyACM0"と認識されるようなので、app.jsにこんな感じでシリアルを設定します。

// Serial Port
var portName = '/dev/ttyACM0'; // Ubuntu環境
var sp = new serialport.SerialPort(portName, {
    baudRate: 9600,
    dataBits: 8,
    parity: 'none',
    stopBits: 1,
    flowControl: false,
    parser: serialport.parsers.readline("\n")
});

そして、JenkinsのジョブのスクリプトからステータスをNode.jsで受け取るためのWebAPIを作成します。これでhttp ://localhost:3000/status?status=hogeで受け取った、パラメータhogeをspのシリアルに送信することができます。

// Web APIエントリポイント
app.get('/status', function(req, res) {
    res.writeHead(200, {'Content-Type':'text/html; charset=utf-8'});
    res.end('OK');

    var msg = new Object();
    msg.status = req.query.status;
    console.log('status ' + msg.status);

    sp.write(msg.status, function(err, bytesWritten) {
        console.log('bytes written: ', bytesWritten);
    });
});

ここまで出来ると、とりあえずArduinoをPCとUSBケーブルで接続してNode.jsのサーバを起動すると、シリアルとしてArduinoをオープンできるようになります。ただ、デバイスドライバをオープンするにはデフォルトでsu権限が必要になっていますので、/dev/ttyACM0をchmodで777とか設定して、フルアクセス可能にしてしまいました(^^; とにかくこれで、Node.jsとArduinoがシリアル通信できるようになりました。

Arduinoの仕様

Arduinoの仕様も決めておきましょう。基本的にはフルカラーLEDを使い、以下のようなステータスとコマンドインターフェースとしました。statusが1文字なのは、Arduino側で実装を簡単にするためです。

  • 初期化(消灯) : /status?status=r
  • ビルド中(青) : /status?status=g
  • 成功(緑) : /status?status=s
  • 失敗(赤) : /status?status=e

Arduino側のプログラムは簡単で、シリアルの入力を待って比較してLEDを制御するだけです。ちなみにこのプログラムでは点灯しっぱなしになります。実運用では適当な時間が経ったら初期化するとか入れた方がいいでしょう。以下抜粋です(使用したLEDがアノードコモンなので、点灯が0になっています)

void loop() {
  if (Serial.available() > 0) { // シリアル通信でデータが送られてきたら
    char c = Serial.read(); // 一文字分データを取り出す。
    
    if (c == 'g') { // Jenkinsビルド開始
      Serial.println("START");
      analogWrite(LED_RED, 255);
      analogWrite(LED_BLUE, 0); // 青LEDを点灯
      analogWrite(LED_GREEN, 255);
    } else if (c == 'e') { // エラー
      Serial.println("ERROR");
      analogWrite(LED_RED, 0);  // 赤LEDを点灯
      analogWrite(LED_BLUE, 255);
      analogWrite(LED_GREEN, 255);
    } else if ( c == 's') { // 成功
      Serial.println("SUCCESS");
      analogWrite(LED_RED, 255);
      analogWrite(LED_BLUE, 255);
      analogWrite(LED_GREEN, 0);  // 緑LEDを点灯
    } else if (c =='r') { // 初期化
      Serial.println("RESET");
      analogWrite(LED_RED, 255);
      analogWrite(LED_BLUE, 255);
      analogWrite(LED_GREEN, 255);
    } else {
      Serial.println("etc...");
      analogWrite(LED_RED, 255);
      analogWrite(LED_BLUE, 255);
      analogWrite(LED_GREEN, 255);
    }
  }
  delay(1000);
}

ここまで出来たら動作確認ができます。サーバのコンソールで

$ curl http://localhost:3000/status?status=g

とか打ってみて、LEDが希望通りに点灯すればOKです。あとはこのcurlのコマンドをJenkinsのジョブの中からシェルスクリプトとして実行できるように設定してやります。

Jenkinsのジョブの設定

最後にJenkinsのジョブ設定を行います。

  • ビルド開始時に「実行中」
  • ビルド終了時に「成功」「失敗」

というようにします。

ビルド開始時

 curl http://localhost:3000/status?status=g

実際にはこんな感じ

f:id:tomo_watanabe:20130717213030p:plain

ビルド終了時スクリプトに追加

RESULT=`curl http://localhost:9090/jenkins/job/[HogeProject]/lastBuild/api/xml | perl -le '$_=<>;print [/<result>(.+?)</]->[0]'`

echo $RESULT

if [ $RESULT = SUCCESS ]
then
    echo "success"
    curl http://localhost:3000/status?status=s
else
    echo "failure"
    curl http://localhost:3000/status?status=e
fi

実際にはこんな感じ

f:id:tomo_watanabe:20130717213048p:plain

運用中のビルド中表示はこんな感じで青く点灯します。 上の2つのデバイスは、別の試作で載せた温度センサーなので今回は関係ありません。

f:id:tomo_watanabe:20130717213433j:plain

まとめ

何はともあれJenkinsとArduinoを繋げることができました。これでLEDだけではなく、音を慣らしたりもできますね。赤外線リモコンを使って、エラーになるとエアコンを止めてしまって、何が何でも直さないと帰れない・・・とか(むしろ帰りたい気が)なんかテストとかビルドとかが楽しくなる仕掛けを考えると面白いかもしれません。

今回のサンプルコードですが、Githubに置いておきます。環境にも依存するので参考程度に...

※ちなみに、これは某アプリのビルドが40分も掛かるのでステータスが知りたいと思い、休日に構想1時間 + 仮実装1時間、本実装1時間ってとこで作りました\(^o^)/