Azure Sphere MT3620 Grove ShieldでIoT Hub Direct Method でGrove LED点灯させるメモです。
サンプルのDirect Method部分を改造してGrove LED点灯させてみました。
IoTHubとAzure Sphereの準備
Azure Sphere MT3620 Grove ShieldでGrove LEDを光らすメモをベースにGrove LEDをGPIO 0に挿し込んでおきます。
Azure Sphereのチュートリアルで、IoT Hubの接続とその設定した認証情報をAzure Sphereに反映してソースコードが書ける状態にしておきます。
- Set up an Azure IoT Hub to work with Azure Sphere | Microsoft Docs
- Azure Sphere with Azure IoT Hub – sample application | Microsoft Docs
Visual Studioプロジェクトを作る
上記のチュートリアルですでに一度成功していることを確認したあと、今回用のVisual Studioプロジェクトを作ります。
まず新規プロジェクトを作成し基本的なセットアップをします。Mt3620AzureIoTHubGrove的な名前にしてテンションをアゲていきます。
IoT Hubのセットアップ
Azure Sphere with Azure IoT Hub – sample application | Microsoft Docs
こちらを参考に進めます。
接続済みサービスの追加を行います。
自分のIoT Hubとの関係性がつながっている状態なので選んでいきます。
チュートリアルに合わせてDevice Provisioning Serviceを追加を押して準備します。
設定がない状態だと丁寧にエラーが出る仕様になっているので、準備ができたらコメントアウトします。このあたりもチュートリアルに書いてあります。
これってコンパイルエラーになる前に意識的にエラーがでるので迷いが少なくなりいいですね。親切。
DirectMethodCallに修正を加える
さて、いよいよIoT Hub Direct Methodで指示が出せるように修正します。
main.c のソースコードに手を加えていきます。
IoT HubのIoT DeviceにあるDirectMethodが受信するのは、main.cのDirectMethodCallがやり取りしている部分です。
static int DirectMethodCall(const char *methodName, const char *payload, size_t payloadSize, char **responsePayload, size_t *responsePayloadSize) { // Prepare the payload for the response. This is a heap allocated null terminated string. // The Azure IoT Hub SDK is responsible of freeing it. *responsePayload = NULL; // Reponse payload content. *responsePayloadSize = 0; // Response payload content size. int result = 404; // HTTP status code if (strcmp(methodName, "LedColorControlMethod") == 0) { LedBlinkUtility_Colors ledColor = LedBlinkUtility_Colors_Unknown; // The payload should contains JSON such as: { "color": "red"} char *directMethodCallContent = malloc(payloadSize + 1); // +1 to store null char at the end. if (directMethodCallContent == NULL) { Log_Debug("ERROR: Could not allocate buffer for direct method request payload.\n"); abort(); } memcpy(directMethodCallContent, payload, payloadSize); directMethodCallContent[payloadSize] = 0; // Null terminated string. JSON_Value *payloadJson = json_parse_string(directMethodCallContent); if (payloadJson != NULL) { JSON_Object *colorJson = json_value_get_object(payloadJson); if (colorJson != NULL) { const char *colorName = json_object_get_string(colorJson, "color"); if (colorName != NULL) { ledColor = LedBlinkUtility_GetColorFromString(colorName, strlen(colorName)); } } } // If color's name has not been identified. if (ledColor == LedBlinkUtility_Colors_Unknown) { result = 400; Log_Debug("INFO: Unrecognised direct method payload format.\n"); static const char noColorResponse[] = "{ \"success\" : false, \"message\" : \"request does not contain an identifiable " "color\" }"; size_t responseMaxLength = sizeof(noColorResponse); *responsePayload = SetupHeapMessage(noColorResponse, responseMaxLength); if (*responsePayload == NULL) { Log_Debug("ERROR: Could not allocate buffer for direct method response payload.\n"); abort(); } *responsePayloadSize = strlen(*responsePayload); } else { // Color's name has been identified. result = 200; const char *colorString = LedBlinkUtility_GetStringFromColor(ledColor); Log_Debug("INFO: LED color set to: '%s'.\n", colorString); // Set the blinking LED color. ledBlinkColor = ledColor; LedBlinkUtility_SetBlinkingLedHandleAndPeriodAndColor( &ledBlink, blinkIntervals[blinkIntervalIndex], ledColor); static const char colorOkResponse[] = "{ \"success\" : true, \"message\" : \"led color set to %s\" }"; size_t responseMaxLength = sizeof(colorOkResponse) + strlen(payload); *responsePayload = SetupHeapMessage(colorOkResponse, responseMaxLength, colorString); if (*responsePayload == NULL) { Log_Debug("ERROR: Could not allocate buffer for direct method response payload.\n"); abort(); } *responsePayloadSize = strlen(*responsePayload); } } else { result = 404; Log_Debug("INFO: Method not found called: '%s'.\n", methodName); static const char noMethodFound[] = "\"method not found '%s'\""; size_t responseMaxLength = sizeof(noMethodFound) + strlen(methodName); *responsePayload = SetupHeapMessage(noMethodFound, responseMaxLength, methodName); if (*responsePayload == NULL) { Log_Debug("ERROR: Could not allocate buffer for direct method response payload.\n"); abort(); } *responsePayloadSize = strlen(*responsePayload); } return result; }
これが元ソース、コメントに
// Direct Method related notes: // - Invoking the method named "LedColorControlMethod" with a payload containing '{"color":"red"}' // will set the color of LED 1 to red;
というLedColorControlMethodというのがありましてこれを参考に進めていきます
Initでポートのオープン
初期化のところでGroveのポートが開くようにします。
// Groveのポートが開くようにしている // gpioLedFdの変数でGPIO_SetValueで操作できるようになっている gpioLedFd = GPIO_OpenAsOutput(MT3620_GPIO0, GPIO_OutputMode_PushPull, GPIO_Value_High);
この返り値の値をGPIO_SetValueに使ってやると、GPIOの出力を操作できます。
GPIO_SetValue function – Azure Sphere | Microsoft Docs
忘れず、どこかで宣言もしておきましょう。
int gpioLedFd;
こんなかんじで。
DirectMethodCallにGroveONOFFControlMethodを加える
LedColorControlMethodの書かれている分岐の上に、以下のようなGroveONOFFControlMethodを加えて機能追加します。
if (strcmp(methodName, "GroveONOFFControlMethod") == 0) { // HTTP status code OK result = 200; char x; char *onoffStr = &x; // char *onoffStr; // これだけだとWarningが出るので。 // The payload should contains JSON such as: { "status": "on" } char *directMethodCallContent = malloc(payloadSize + 1); // +1 to store null char at the end. if (directMethodCallContent == NULL) { Log_Debug("ERROR: Could not allocate buffer for direct method request payload.\n"); abort(); } memcpy(directMethodCallContent, payload, payloadSize); directMethodCallContent[payloadSize] = 0; // Null terminated string. // JSON convert JSON_Value *payloadJson = json_parse_string(directMethodCallContent); if (payloadJson != NULL) { JSON_Object *onoffJson = json_value_get_object(payloadJson); if (onoffJson != NULL) { const char *onoffStatus = json_object_get_string(onoffJson, "status"); if (onoffStatus != NULL) { strcpy( onoffStr, onoffStatus ); } } } Log_Debug("Grove GPIO: %s.\n", onoffStr); // Grove GPIO 変更 // GPIO_OpenAsOutputで取得している返り値でピンが操作可能になっている if (strcmp(onoffStr, "on") == 0) { GPIO_SetValue(gpioLedFd, GPIO_Value_High); } else if (strcmp(onoffStr, "off") == 0) { GPIO_SetValue(gpioLedFd, GPIO_Value_Low); } // Response static const char groveOkResponse[] = "{ \"success\" : true, \"message\" : \"status %s\" }"; size_t responseMaxLength = sizeof(groveOkResponse) + strlen(payload); *responsePayload = SetupHeapMessage(groveOkResponse, responseMaxLength, onoffStr); if (*responsePayload == NULL) { Log_Debug("ERROR: Could not allocate buffer for direct method response payload.\n"); abort(); } *responsePayloadSize = strlen(*responsePayload); } else if (strcmp(methodName, "LedColorControlMethod") == 0) {
LedColorControlMethodとの違いは、
- 今回は、Grove LEDなので
{"color":"red"}
のように色ではなく、
{ "status": "on" }
で on ならLED点灯、 off ならLED消灯を行います
* charのポインタに苦労しつつなんとか実装
* var might be used uninitialized in this function – nelnalog.note
* 文言の比較も苦労しつつなんとか実装
* char*文字列を==で比較しては駄目
* C言語入門:文字列を比較する(strcmp):Geekなぺーじ
など乗り越えて実装できました。
app_manifest.json も修正
Azure Sphere MT3620 Grove ShieldでGrove LEDを光らすメモを参考にしてGroveを挿したGPIO 0が使えるように修正します。
{ "SchemaVersion": 1, "Name": "Mt3620AzureIoTHubGrove", "ComponentId": " ComponentId ", "EntryPoint": "/bin/app", "CmdArgs": [], "TargetApplicationRuntimeVersion": 1, "Capabilities": { "AllowedConnections": [ "global.azure-devices-provisioning.net", "azure-sphere-onefootseabass.azure-devices.net" ], "Gpio": [ 0, 8, 9, 10, 15, 16, 17, 18, 19, 20, 12, 13 ], "Uart": [], "WifiConfig": true, "DeviceAuthentication": "1b87e52a-9b0f-46bb-841b-47f680df1e97" } }
Gpioのところに 0 が加わってますね。
動かしてみる
では、IoT HubのIoT Deviceにある
Direct methodで今回のAzure Sphereデバイスを選択して行います。
Method Nameには、
GroveONOFFControlMethod
を、Payload
{ "status": "on" }
を入力して、 Invoke Method ボタンを押します。
以下が動く様子です。
無事動きました!
これで Azure Sphere が遠隔で操作できるようになるはずなので、Node.js SDKなど他連携を試してみます。
それでは、よき Azure Sphere & IoT Hub Life を!