はじめに
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の管理画面で確認します。
サンプルソースとビルド
サンプルソースは こちら を参照してください。
ビルドは、
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