Azure Sphere MT3620 Grove ShieldでIoT Hub Direct Method でGrove LED点灯させるメモ

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に反映してソースコードが書ける状態にしておきます。

Visual Studioプロジェクトを作る

上記のチュートリアルですでに一度成功していることを確認したあと、今回用のVisual Studioプロジェクトを作ります。

image

まず新規プロジェクトを作成し基本的なセットアップをします。Mt3620AzureIoTHubGrove的な名前にしてテンションをアゲていきます。

IoT Hubのセットアップ

Azure Sphere with Azure IoT Hub – sample application | Microsoft Docs

こちらを参考に進めます。

image

接続済みサービスの追加を行います。

image

自分のIoT Hubとの関係性がつながっている状態なので選んでいきます。

image

チュートリアルに合わせてDevice Provisioning Serviceを追加を押して準備します。

image

設定がない状態だと丁寧にエラーが出る仕様になっているので、準備ができたらコメントアウトします。このあたりもチュートリアルに書いてあります。

これってコンパイルエラーになる前に意識的にエラーがでるので迷いが少なくなりいいですね。親切。

DirectMethodCallに修正を加える

さて、いよいよIoT Hub Direct Methodで指示が出せるように修正します。

image

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にある

image

Direct methodで今回のAzure Sphereデバイスを選択して行います。

image

Method Nameには、

GroveONOFFControlMethod

を、Payload

{ "status": "on" }

を入力して、 Invoke Method ボタンを押します。

以下が動く様子です。

無事動きました!

これで Azure Sphere が遠隔で操作できるようになるはずなので、Node.js SDKなど他連携を試してみます。

それでは、よき Azure Sphere & IoT Hub Life を!