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

#<i class="fa fa-cubes" style="font-size:1em;"></i>概要
[BluemixのRaspberryPi登録レシピ](https://developer.ibm.com/recipes/tutorials/raspberry-pi-4/)に登場する[デバイス用のsampleプログラム](https://github.com/ibm-messaging/iot-raspberrypi/releases/tag/1.0.2)は、Bluemixからのデータの受信によってシステム停止(Linuxのshutdownコマンド)のみ行える作りになっている。

Raspberry Pi 2にパーツを付け加えてBluemixからパーツを制御するには、[sampleプログラムのソースコード](https://github.com/ibm-messaging/iot-raspberrypi/blob/master/samples/c/)を修正してビルドすれば良い。その方法を整理する。

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


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

(1)　仕組みの理解　(これ)
[(2)　アクチュエータを動かす　光編　RasPi2のオンボードLED](http://qiita.com/getapojim/items/b1f29ad5b9a8166ff887)
[(3)　アクチュエータを動かす　光編　外付けのLED](http://qiita.com/getapojim/items/b8028d1aeb975432afda)
[(4)　アクチュエータを動かす　音編](http://qiita.com/getapojim/items/9440de8ee37e6d8e7e63)

#<i class="fa fa-cube" style="font-size:1em;"></i>前提
RaspberryPi2がBluemixのIoTFサービスに[「レシピ」](https://developer.ibm.com/recipes/tutorials/raspberry-pi-4/)を使って接続済みであること。

#<i class="fa fa-cube" style="font-size:1em;"></i>構成のイメージ
IoTという構成で考えるとこんな感じ。下段ほどより具体的に。<font color=red>(図にしたい)</font>
Internet　　　　　　　　　→　Things
クラウド　　　　　　　　→　デバイス　　　　　　→　アクチュエータ
Bluemix(IoT Foundation)　→　Raspberry Pi 2(sysfs)　→　アクチュエータ
　　　　　　　　　　　　↑　　　　　　　　　　　　↑
　　　　　　　　　　ポイント1,4　　　　　　　ポイント2,3

#<i class="fa fa-cube" style="font-size:1em;"></i>ポイント1　：　MQTTプロトコルでJSONデータを受信
MQTTクライアントであるRaspberry Pi 2はMQTTプロトコルでsubscribeする。ソースコードは[mqttPublisher.c](https://github.com/ibm-messaging/iot-raspberrypi/blob/master/samples/c/mqttPublisher.c)

MQTTの構成要素
　MQTTブローカー　　：　Bluemixの[IoT Foundataionサービス](https://internetofthings.ibmcloud.com/)
　MQTTクライアント　：　[デバイス用のsampleプログラム](https://github.com/ibm-messaging/iot-raspberrypi/releases/tag/1.0.2)

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

```c:iotmain.c(29,30行目)
char publishTopic[MAXBUF] = "iot-2/evt/status/fmt/json";
char subscribeTopic[MAXBUF] = "iot-2/cmd/reboot/fmt/json";
```

```c:iotmain.c(148～150行目)
	if (isRegistered) {
		subscribe(&client, subscribeTopic);
	}
```

```c: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

```c:iotmain.c(123行目)
	init_mqtt_connection(&client, msproxyUrl, isRegistered, clientId, username, passwd);
```

```c: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;
}
```


#<i class="fa fa-cube" style="font-size:1em;"></i>ポイント2　：　JSONデータの分解
```c: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);
//省略
```


```c: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);
//省略
}
```

```c: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;
}
```

#<i class="fa fa-cube" style="font-size:1em;"></i>ポイント3　：　システム(Raspberry Pi 2)の制御
```c: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コマンドが実行される](https://github.com/ibm-messaging/iot-raspberrypi/tree/master/samples/c#command-support)ようになっている。

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

#<i class="fa fa-cube" style="font-size:1em;"></i>ポイント4　：　BluemixからMQTTプロトコルでJSONデータを送信
##MQTTの構成

BluemixのNode-REDアプリからMQTTプロトコルでpublishし、Raspberry Pi 2上のsampleプログラムからsubscribeする。
　MQTTブローカー　　　　：　Bluemixの[IoT Foundataionサービス](https://internetofthings.ibmcloud.com/)
　MQTTクライアント(pub)　：　Bluemixの[Node-RED](http://nodered.org/)アプリ　※Node-REDでなくても良い。
　MQTTクライアント(sub)　：　[デバイス用のsampleプログラム](https://github.com/ibm-messaging/iot-raspberrypi/releases/tag/1.0.2)
　MQTTのトピック　　　　　：　iot-2/cmd/reboot/fmt/json
　MQTTのメッセージ　　　　：　`{"delay":1}`

##MQTTクライアント(publish)の構成
今回はNode-REDアプリとする。
「inject」ノードと「ibmiot out」ノードを使う。
![Node-RED_MQTT_pub_001.png](https://qiita-image-store.s3.amazonaws.com/0/85506/5b6867a2-4b43-f221-4a27-bf9378d9d9b1.png)


injectノードの中身
着火するだけなので空っぽでよい。
![Node-RED_MQTT_pub_002.png](https://qiita-image-store.s3.amazonaws.com/0/85506/01955da7-8847-38ef-4eb1-a25c6b3ff7c2.png)

ibmiot outノードの中身
![Node-RED_MQTT_pub_003.png](https://qiita-image-store.s3.amazonaws.com/0/85506/6caf8ae9-c78d-ff4b-f48b-c50de9eda3c0.png)


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

上記2つのノードを線でつないだら、右上の「Deploy」ボタンを押してデプロイする。
![Node-RED_MQTT_pub_004.png](https://qiita-image-store.s3.amazonaws.com/0/85506/82b4339e-41fa-b597-914a-236d1d336a83.png)

injectノードをポチッとすると、RasPi上でshutdownコマンドが実行される。
![Node-RED_MQTT_pub_005.png](https://qiita-image-store.s3.amazonaws.com/0/85506/a8a9343a-861c-0a73-5cd1-8457215b3126.png)


この例の場合は1分後にRasPi上でshutdown実施。
![IMG_3080.png](https://qiita-image-store.s3.amazonaws.com/0/85506/1c061685-6407-cdeb-1103-054e73203651.png)

<!--
#<i class="fa fa-cube" style="font-size:1em;"></i>続き
<font color=red>(2)　センサーを付けてデータを拾う　温度センサーデジタル編　（これから書く）</font>
-->

#<i class="fa fa-cube" style="font-size:1em;"></i>環境

[Raspberry Pi 2 Model B](https://www.raspberrypi.org/products/raspberry-pi-2-model-b/)
[Raspbian](https://www.raspberrypi.org/downloads/raspbian/)
[Bluemix IoT Foundation](https://internetofthings.ibmcloud.com/)
[Node-RED](http://nodered.org/)

#<i class="fa fa-book" style="font-size:1em;"></i>参考
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/


https://github.com/ibm-messaging/iot-raspberrypi/tree/master/samples/c#command-support