1
2

More than 3 years have passed since last update.

M5StackでAmazon FreeRTOSを使用する5(shadow編)

Posted at

はじめに

Amazon FreeRTOSでデバイスを管理しましょう。シャドウ(AWS IoT Device Shadows)を使用してデバイスの状態を参照できるようにしてみます。Amazon FreeRTOSをインストールしたM5Stackの、ファームウェアのバージョンと最後にネットワークに接続した日時が参照できるようにしてみましょう。

今回は、起動時にAmazon FreeRTOSで提供されているHTTPSライブラリーを使用して、HTTPでntpサーバーから時刻を取得してみます。ntpは 国立研究開発法人 情報通信研究機構のntpサーバー を利用します。

最初に接続します。ほぼFreeRTOSのサンプルそのままですが、HTTPSではなくHTTPでアクセスしているのでシンプルです。

const char HTTPS_SERVER_ADDRESS[18] = "ntp-a1.nict.go.jp";
const int HTTP_TIMEOUT_MS = 10000;

IotHttpsConnectionInfo_t connInfo = IOT_HTTPS_CONNECTION_INFO_INITIALIZER;
IotHttpsConnectionHandle_t connHandle = IOT_HTTPS_CONNECTION_HANDLE_INITIALIZER;

/*-----------------------------------------------------------*/
/**
 * @brief HTTP connect.
 *
 * @param[in] pNetworkInterface Network Interface.
 *
 * @return void.
 */
static void prvHTTPSConnect( IotNetworkInterface_t* pNetworkInterface )
{
    IotHttpsClient_Init();

// An initialized network interface.
//    IotNetworkInterface_t* pNetworkInterface;


// Parameters to HTTPS Client connect.
    uint8_t* pConnUserBuffer = (uint8_t*)malloc(connectionUserBufferMinimumSize + 512);
    // Set the connection configuration information.
    connInfo.pAddress = HTTPS_SERVER_ADDRESS;
    connInfo.addressLen = strlen(HTTPS_SERVER_ADDRESS);
    connInfo.port = 80;
    connInfo.flags = IOT_HTTPS_IS_NON_TLS_FLAG;
    connInfo.pAlpnProtocols = NULL;
    connInfo.alpnProtocolsLen = 0;
    connInfo.pCaCert = NULL;
    connInfo.caCertLen = 0;
    connInfo.userBuffer.pBuffer = pConnUserBuffer;
    connInfo.userBuffer.bufferLen = connectionUserBufferMinimumSize + 512;
    connInfo.pClientCert = NULL;
    connInfo.clientCertLen = 0;
    connInfo.pPrivateKey = NULL;
    connInfo.privateKeyLen = 0;
    connInfo.pNetworkInterface = pNetworkInterface;
    IotHttpsReturnCode_t returnCode = IotHttpsClient_Connect(&connHandle, &connInfo);
    if( returnCode == IOT_HTTPS_OK )
    {
        configPRINTF( ( "Connected.\r\n" ) );
    }
    else
    {
        configPRINTF( ( "Connection error.\r\n" ) );
        configPRINTF( ( "Return code: %d\r\n", returnCode ) );
    }
}

接続に成功したら、時刻をサーバーから取得します。

/*-----------------------------------------------------------*/
/**
 * @brief HTTP request.
 *
 * @param[in] xMethod Request method.
 * @param[in] pcPath Request path.
 * @param[in] ulPathLength Request path length.
 *
 * @return void.
 */
static void prvHTTPRequest( IotHttpsMethod_t xMethod, 
                                   const char *pcPath, 
                                   uint32_t ulPathLength )
{
    IotHttpsReturnCode_t xHTTPClientResult;
    IotHttpsRequestInfo_t xHTTPRequestInfo = IOT_HTTPS_REQUEST_INFO_INITIALIZER;
    IotHttpsResponseInfo_t xHTTPResponseInfo = IOT_HTTPS_RESPONSE_INFO_INITIALIZER;
    IotHttpsRequestHandle_t xHTTPRequest = IOT_HTTPS_REQUEST_HANDLE_INITIALIZER;
    IotHttpsResponseHandle_t xHTTPResponse = IOT_HTTPS_RESPONSE_HANDLE_INITIALIZER;
    IotHttpsSyncInfo_t xHTTPSyncRequestInfo = IOT_HTTPS_SYNC_INFO_INITIALIZER;
    IotHttpsSyncInfo_t xHTTPSyncResponseInfo = IOT_HTTPS_SYNC_INFO_INITIALIZER;

    uint8_t ucHTTPRequestUserBuffer[ requestUserBufferMinimumSize + 512 ];
    uint8_t cHTTPRequestBodyBuffer[ 512 ];
    uint8_t ucHTTPResponseUserBuffer[ responseUserBufferMinimumSize + 512 ];
    uint8_t ucHTTPResponseBodyBuffer[ 256 ];

    /************************** HTTP request setup. ***************************/

    if( ( xMethod == IOT_HTTPS_METHOD_PUT ) || ( xMethod == IOT_HTTPS_METHOD_POST ) )
    {
        xHTTPSyncRequestInfo.pBody = ( uint8_t* )cHTTPRequestBodyBuffer;
        xHTTPSyncRequestInfo.bodyLen = sizeof( cHTTPRequestBodyBuffer );
    }
    else
    {
        xHTTPSyncRequestInfo.pBody = NULL;
        xHTTPSyncRequestInfo.bodyLen = 0;
    }

    xHTTPRequestInfo.pHost = HTTPS_SERVER_ADDRESS;
    xHTTPRequestInfo.hostLen = sizeof( HTTPS_SERVER_ADDRESS ) - 1;
    xHTTPRequestInfo.pPath = pcPath;
    xHTTPRequestInfo.pathLen = ulPathLength;
    xHTTPRequestInfo.method = xMethod;
    xHTTPRequestInfo.isNonPersistent = false;
    xHTTPRequestInfo.userBuffer.pBuffer = ucHTTPRequestUserBuffer;
    xHTTPRequestInfo.userBuffer.bufferLen = sizeof( ucHTTPRequestUserBuffer ) - 1;
    xHTTPRequestInfo.isAsync = false;
    xHTTPRequestInfo.u.pSyncInfo = &xHTTPSyncRequestInfo;

    xHTTPClientResult = IotHttpsClient_InitializeRequest( &xHTTPRequest, &xHTTPRequestInfo );

    if( xHTTPClientResult == IOT_HTTPS_OK )
    {
        configPRINTF( ( "Initialize ok.\r\n" ) );
    }
    else
    {
        configPRINTF( ( "Initialize error.\r\n" ) );
        configPRINTF( ( "Result: %d\r\n", xHTTPClientResult ) );
    }

    /************************** HTTP response setup. **************************/

    memset( ucHTTPResponseBodyBuffer, 0, sizeof( ucHTTPResponseBodyBuffer ) );

    if( xMethod == IOT_HTTPS_METHOD_HEAD )
    {
        xHTTPSyncResponseInfo.pBody = NULL;
        xHTTPSyncResponseInfo.bodyLen = 0;
    }
    else
    {
        xHTTPSyncResponseInfo.pBody = ucHTTPResponseBodyBuffer;
        xHTTPSyncResponseInfo.bodyLen = sizeof( ucHTTPResponseBodyBuffer );
    }

    xHTTPResponseInfo.userBuffer.pBuffer = ucHTTPResponseUserBuffer;
    xHTTPResponseInfo.userBuffer.bufferLen = sizeof( ucHTTPResponseUserBuffer );
    xHTTPResponseInfo.pSyncInfo = &xHTTPSyncResponseInfo;

    /*************************** Send HTTP request. ***************************/

    /* This synchronous send function blocks until the full response is received
     * from the network. */
    xHTTPClientResult = IotHttpsClient_SendSync( connHandle, 
                                       xHTTPRequest, 
                                       &xHTTPResponse, 
                                       &xHTTPResponseInfo, 
                                       HTTP_TIMEOUT_MS );

    char    *lines[ MAXITEM ];
    int cnt = split( (char*)&ucHTTPResponseBodyBuffer, "\n" , lines );
    for( int i = 0; i < cnt; i++ ) {
        if( lines[i][0] != '<' )
        {
            set_clock( lines[i] );
        }
    }

}

取得した時刻や、ファームウェアのバージョンでシャドウを更新します。

/**
 * @brief Send the Shadow updates that will trigger the Shadow callbacks.
 *
 * @param[in] pDeltaSemaphore Used to synchronize Shadow updates with the delta
 * callback.
 * @param[in] mqttConnection The MQTT connection used for Shadows.
 * @param[in] pThingName The Thing Name for Shadows in this demo.
 * @param[in] thingNameLength The length of `pThingName`.
 *
 * @return `EXIT_SUCCESS` if all Shadow updates were sent; `EXIT_FAILURE`
 * otherwise.
 */
static int _sendShadowUpdates( IotSemaphore_t * pDeltaSemaphore,
                               IotMqttConnection_t mqttConnection,
                               const char * const pThingName,
                               size_t thingNameLength )
{
    int status = EXIT_SUCCESS;
    int32_t i = 0, desiredState = 0;
    AwsIotShadowError_t updateStatus = AWS_IOT_SHADOW_STATUS_PENDING;
    AwsIotShadowDocumentInfo_t updateDocument = AWS_IOT_SHADOW_DOCUMENT_INFO_INITIALIZER;

    /* A buffer containing the update document. It has static duration to prevent
     * it from being placed on the call stack. */
    static char pUpdateDocument[ 100 ] = { 0 };

    /* Set the common members of the Shadow update document info. */
    updateDocument.pThingName = pThingName;
    updateDocument.thingNameLength = thingNameLength;
    updateDocument.u.update.pUpdateDocument = pUpdateDocument;
    updateDocument.u.update.updateDocumentLength = EXPECTED_REPORTED_JSON_SIZE;

    /* Publish Shadow updates at a set period. */
    for( i = 1; i <= AWS_IOT_DEMO_SHADOW_UPDATE_COUNT; i++ )
    {
        /* Toggle the desired state. */
        desiredState = !( desiredState );

        /* Generate a Shadow desired state document, using a timestamp for the client
         * token. To keep the client token within 6 characters, it is modded by 1000000. */
        status = sprintf( pUpdateDocument,
                          SHADOW_REPORTED_JSON,
                          APP_VERSION_MAJOR,
                          APP_VERSION_MINOR,
                          APP_VERSION_BUILD,
                          ctime(&tv_now.tv_sec),
                          ( long unsigned ) ( IotClock_GetTimeMs() % 1000000 ) );
        updateDocument.u.update.updateDocumentLength = status;

        IotLogInfo( "Sending Shadow update %d of %d: %s",
                    i,
                    AWS_IOT_DEMO_SHADOW_UPDATE_COUNT,
                    pUpdateDocument );

        /* Send the Shadow update. Because the Shadow is constantly updated in
         * this demo, the "Keep Subscriptions" flag is passed to this function.
         * Note that this flag only needs to be passed on the first call, but
         * passing it for subsequent calls is fine.
         */
        updateStatus = AwsIotShadow_TimedUpdate( mqttConnection,
                                                 &updateDocument,
                                                 AWS_IOT_SHADOW_FLAG_KEEP_SUBSCRIPTIONS,
                                                 TIMEOUT_MS );

        /* Check the status of the Shadow update. */
        if( updateStatus != AWS_IOT_SHADOW_SUCCESS )
        {
            IotLogError( "Failed to send Shadow update %d of %d, error %s.",
                         i,
                         AWS_IOT_DEMO_SHADOW_UPDATE_COUNT,
                         AwsIotShadow_strerror( updateStatus ) );

            status = EXIT_FAILURE;
            break;
        }
        else
        {
            IotLogInfo( "Successfully sent Shadow update %d of %d.",
                        i,
                        AWS_IOT_DEMO_SHADOW_UPDATE_COUNT );

            /* Wait for the delta callback to change its state before continuing. */
            if( IotSemaphore_TimedWait( pDeltaSemaphore, TIMEOUT_MS ) == false )
            {
                IotLogError( "Timed out waiting on delta callback to change state." );

                status = EXIT_FAILURE;
                break;
            }
        }

        IotClock_SleepMs( AWS_IOT_DEMO_SHADOW_UPDATE_PERIOD_MS );
    }

    return status;
}

AWS IoTの管理画面で確認します。

aws_shadow.png

サンプルソースとビルド

サンプルソースは こちら を参照してください。

ビルドは、

git clone https://github.com/fukuen/m5stack-freertos-shadow --recursive

ダウンロードしたディレクトリーに移動して、アプリケーションをビルドします。

cd m5stack-freertos-tft
cmake -DCMAKE_TOOLCHAIN_FILE=amazon-freertos/tools/cmake/toolchains/xtensa-esp32.cmake -GNinja -S . -B build
cd build
ninja

ビルドが完了したら、フラッシュします。

cd ..
idf.py flash

シリアルコンソールでモニターしてみます。

idf.py monitor

メモ

第1回 Amazon FreeRTOSインストール
第2回 Lチカ
第3回 TFT
第4回 SDカード

1
2
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
1
2