LoginSignup
8
8

More than 5 years have passed since last update.

BluemixでRaspberryPi2を制御する (1)仕組みの理解

Last updated at Posted at 2015-10-20

概要

BluemixのRaspberryPi登録レシピに登場するデバイス用のsampleプログラムは、Bluemixからのデータの受信によってシステム停止(Linuxのshutdownコマンド)のみ行える作りになっている。

Raspberry Pi 2にパーツを付け加えてBluemixからパーツを制御するには、sampleプログラムのソースコードを修正してビルドすれば良い。その方法を整理する。

※以下でやったことの反対方向(Bluemix→Raspberry Pi 2)をやってみる。
BluemixでRaspberryPi2のセンサーデータを拾う (1)仕組みの理解
BluemixでRaspberryPi2のセンサーデータを拾う (2)センサーを付けてデータを拾う 温度センサーデジタル編
BluemixでRaspberryPi2のセンサーデータを拾う (3)センサーを付けてデータを拾う 温度センサーアナログ編
BluemixでRaspberryPi2のセンサーデータを拾う (4)センサーを付けてデータを拾う 温度センサーデジタル編(リベンジ)
BluemixでRaspberryPi2のセンサーデータを拾う (5)センサーを付けてデータを拾う 温度・湿度・気圧まとめて採取編
番外編 『ムック「日経Linux ラズパイマガジン 2015年春号 」電子工作入門キット パーツセット』に入っていたもの

これから整理するにあたりきっとまた長い道のりになりそうなので、(1)Sampleプログラムの仕組みの整理と(2)どこをどのように変更すればよいのかを分けることにした。

(1) 仕組みの理解 (これ)
(2) アクチュエータを動かす 光編 RasPi2のオンボードLED
(3) アクチュエータを動かす 光編 外付けのLED
(4) アクチュエータを動かす 音編

前提

RaspberryPi2がBluemixのIoTFサービスに「レシピ」を使って接続済みであること。

構成のイメージ

IoTという構成で考えるとこんな感じ。下段ほどより具体的に。(図にしたい)
Internet         → Things
クラウド        → デバイス      → アクチュエータ
Bluemix(IoT Foundation) → Raspberry Pi 2(sysfs) → アクチュエータ
            ↑            ↑
          ポイント1,4       ポイント2,3

ポイント1 : MQTTプロトコルでJSONデータを受信

MQTTクライアントであるRaspberry Pi 2はMQTTプロトコルでsubscribeする。ソースコードはmqttPublisher.c

MQTTの構成要素
 MQTTブローカー  : BluemixのIoT Foundataionサービス
 MQTTクライアント : デバイス用のsampleプログラム

MQTTの「トピック」はiotmain.cで次の通り指定している。
 publish用  : iot-2/evt/status/fmt/json ※今回(Bluemix→RPi)は使わない。
 subscribe用 : iot-2/cmd/reboot/fmt/json

iotmain.c(29,30行目)
char publishTopic[MAXBUF] = "iot-2/evt/status/fmt/json";
char subscribeTopic[MAXBUF] = "iot-2/cmd/reboot/fmt/json";
iotmain.c(148~150行目)
    if (isRegistered) {
        subscribe(&client, subscribeTopic);
    }
mqttPublisher.c(243~265行目)
int subscribe(MQTTAsync* client, char *topic) {

    MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer;
    int rc = MQTTASYNC_SUCCESS;

    opts.onSuccess = onSubscription;
    opts.context = *client;

    if ((rc = MQTTAsync_subscribe(*client, topic, QOS, &opts))
            != MQTTASYNC_SUCCESS) {
#ifdef ERROR
        syslog(LOG_ERR, "Failed to subscribe, return code %d\n", rc);
#endif
        return rc;
    }

#ifdef INFO
    syslog(LOG_DEBUG, "Waiting for subscription "
            "on topic %s\n", topic);
#endif

    return rc;
}

MQTT Subscribe

iotmain.c(123行目)
    init_mqtt_connection(&client, msproxyUrl, isRegistered, clientId, username, passwd);
mqttPublisher.c(168~207行目)
int init_mqtt_connection(MQTTAsync* client, char *address, int isRegistered,
        char* client_id, char* username, char* passwd) {

    MQTTAsync_connectOptions conn_opts = MQTTAsync_connectOptions_initializer;

    MQTTAsync_SSLOptions sslopts = MQTTAsync_SSLOptions_initializer;

    int rc = MQTTASYNC_SUCCESS;
    MQTTAsync_create(client, address, client_id, MQTTCLIENT_PERSISTENCE_NONE,
    NULL);

    MQTTAsync_setCallbacks(*client, NULL, NULL, subscribeMessage, NULL);

#ifdef INFO
    syslog(LOG_INFO, "Connecting to %s with client Id: %s \n", address,
            client_id);
#endif
    conn_opts.keepAliveInterval = 20;
    conn_opts.cleansession = 1;
    conn_opts.onSuccess = onConnectSuccess;
    conn_opts.onFailure = onConnectFailure;
    conn_opts.context = client;
    //only when in registered mode, set the username/passwd and enable TLS
    if (isRegistered) {
        //currently only supported mech is token based. Need to change this in future.
        conn_opts.username = username;
        conn_opts.password = passwd;
        sslopts.trustStore = TRUSTSTORE;
        sslopts.enableServerCertAuth = 1;

        conn_opts.ssl = &sslopts;
    }

    if ((rc = MQTTAsync_connect(*client, &conn_opts)) != MQTTASYNC_SUCCESS) {
#ifdef ERROR
        syslog(LOG_ERR, "Failed to start connect, return code %d\n", rc);
#endif
    }
    return rc;
}

ポイント2 : JSONデータの分解

mqttPublisher.c(168~207行目)
int init_mqtt_connection(MQTTAsync* client, char *address, int isRegistered,
        char* client_id, char* username, char* passwd) {
//省略
    MQTTAsync_setCallbacks(*client, NULL, NULL, subscribeMessage, NULL);
//省略
mqttPublisher.c(115~135行目)
int subscribeMessage(void *context, char *topicName, int topicLen,
        MQTTAsync_message *message) {
    int i;
    char* payloadptr;
    char* command;
    int time_delay = 0;

    payloadptr = message->payload;

    time_delay = getDelay(payloadptr);
//省略
}
jsonReader.c(26~39行目)
int getDelay(char *text) {
    cJSON *json;
    int delay = -1;

    json = cJSON_Parse(text);
    if (!json) {
        syslog(LOG_ERR, "JSON Parsing error : [%s]\n", cJSON_GetErrorPtr());
    } else {
        delay = cJSON_GetObjectItem(json, "delay")->valueint;
        cJSON_Delete(json);
    }

    return delay;
}

ポイント3 : システム(Raspberry Pi 2)の制御

mqttPublisher.c(124~130行目)
    time_delay = getDelay(payloadptr);
    if(time_delay != -1) {
        sprintf(command,"sudo /sbin/shutdown -r %d", time_delay);
        syslog(LOG_INFO, "Received command to restart in %d minutes.",time_delay);
        system(command);
    } else
        syslog(LOG_ERR, "Invalid command received.");

BluemixからMQTTプロトコルを使って、トピック"iot-2/cmd/reboot/fmt/json"に以下のデータがpublishされるとshutdownコマンドが実行されるようになっている。

この例だと1分後にshutdown
{"delay":1}

ポイント4 : BluemixからMQTTプロトコルでJSONデータを送信

MQTTの構成

BluemixのNode-REDアプリからMQTTプロトコルでpublishし、Raspberry Pi 2上のsampleプログラムからsubscribeする。
 MQTTブローカー    : BluemixのIoT Foundataionサービス
 MQTTクライアント(pub) : BluemixのNode-REDアプリ ※Node-REDでなくても良い。
 MQTTクライアント(sub) : デバイス用のsampleプログラム
 MQTTのトピック     : iot-2/cmd/reboot/fmt/json
 MQTTのメッセージ    : {"delay":1}

MQTTクライアント(publish)の構成

今回はNode-REDアプリとする。
「inject」ノードと「ibmiot out」ノードを使う。
Node-RED_MQTT_pub_001.png

injectノードの中身
着火するだけなので空っぽでよい。
Node-RED_MQTT_pub_002.png

ibmiot outノードの中身
Node-RED_MQTT_pub_003.png

項目名 設定値 備考
Output Type Device Command
Device Type iotsample-raspberrypi IoT Foundationで登録した通りに。
IoTFダッシュボードで確認可能
Device Id xxxxxxxxxxxx RasPi上で以下のコマンドの出力する値
# service iot getdeviceid
IoTFダッシュボードで確認可能
Command Type reboot トピックの一部になるため固定
Format json トピックの一部になるため固定
Data {"delay":1} 数字は何分後にシャットダウンするかで可変
Name IoTF Node-RED上の表示名なだけなので何でもよい

上記2つのノードを線でつないだら、右上の「Deploy」ボタンを押してデプロイする。
Node-RED_MQTT_pub_004.png

injectノードをポチッとすると、RasPi上でshutdownコマンドが実行される。
Node-RED_MQTT_pub_005.png

この例の場合は1分後にRasPi上でshutdown実施。
IMG_3080.png

環境

Raspberry Pi 2 Model B
Raspbian
Bluemix IoT Foundation
Node-RED

参考

BluemixのRaspberryPi登録レシピ
https://developer.ibm.com/recipes/tutorials/raspberry-pi-4/

BluemixのRaspberryPi登録レシピに登場するデバイス用のsampleプログラム
(Linux用のdaemonになっている)
https://github.com/ibm-messaging/iot-raspberrypi/blob/master/samples/c/

8
8
0

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
  3. You can use dark theme
What you can do with signing up
8
8