Fusic Tech Blog

Fusion of Society, IT and Culture

【C言語】AWS IoT EduKitのLチカプログラムを読み解く
2021/08/26

【C言語】AWS IoT EduKitのLチカプログラムを読み解く

こんにちは、IoTチームの岡嵜です。

会社に買ってもらった M5Stack Core2 for AWS を半年近く放置していて、最近ようやく触り始めたところです。

AWS IoT EduKitのLチカを試して、無事動かすことができたのですが、デバイスのプログラムはGitHubからクローンしたものを書き込むだけでした。簡単に動かせるという点ではGoodなのですが、「どういう技術を使って実現されているのか?」「自分でイチから作るにはどうすればよいのか」といった点がどうしても気になるところです。

そんなわけで、Lチカのプログラムをじっくり読んでみることにしました。本記事ではプログラムを読んでわかったことを備忘録としてまとめていきます。

プログラム

Lチカのソースコードはこちらです。

https://github.com/yuuu/Core2-for-AWS-IoT-EduKit/tree/master/Blinky-Hello-World

※コードが更新されると困るので、本家のものからforkしています。

動作としてはIoT CoreのMQTTブローカーをSubscribeして、メッセージがPublichされたタイミングでLEDを点滅する、というものです。

もう一度メッセージをPublishすると、LEDの点滅が停止します。

プログラム構成

Lチカのプログラムは下図のような構成で動作しています。実際にはデバイス制御やWi-Fi、UI制御のプログラムも存在していますが、割愛しています。

ポイントはAWS IoT Coreとの通信を行っていることと、複数のタスクが強調動作しているという2点です。この点を踏まえて以下の解説を読んでください。

platformio.ini

このファイルが存在するということはこのディレクトリ自体がPlatformIOのプロジェクトであることを示しています。

https://github.com/yuuu/Core2-for-AWS-IoT-EduKit/blob/master/Blinky-Hello-World/platformio.ini

frameworkespidf となっていますね。M5Stackは Arduinoespidf が選択できるのですが、今回はより玄人向けの espidf を使っていることがわかります。

[env:core2foraws]
platform = espressif32@3.2.1
framework = espidf
board = m5stack-core2
monitor_speed = 115200
upload_speed = 2000000
board_build.f_flash = 80000000L
board_build.flash_mode = qio
build_unflags = -mfix-esp32-psram-cache-issue

main.c - app_main()

espidf を使う場合、 app_main() がエントリポイントとなります。

ソースコード

  • Core2ForAWS_Init() - これはesp-idfのcomponentsであるcore2forAWS の初期化関数です - サポートするH/Wの初期化関数を順次実行します
  • Core2ForAWS_Display_SetBrightness() - 関数名の通り、ディスプレイの明るさを設定しています
  • ui_init() - 画面に表示するUIを初期化しています - UI周りの処理は同じディレクトリにある ui.c に記述されていますね
  • initialise_wifi() - Wi-Fiの初期化関数です - Wi-Fi周りの処理は同じディレクトリにある wifi.c に記述されていますね
  • xTaskCreatePinnedToCore() - リアルタイムOS(以下RTOS)上で動作する組込みプログラムを読み解く上で重要な関数です - 詳細は後述します

補足: タスク起動

espidfにおけるタスク起動は xTaskCreatePinnedToCore を用います。

タスクとはRTOSにおける実行コンテキストのようなものです。Linuxにおけるプロセスまたはスレッドが概念としては近いです。(厳密にはいろいろ違いがあります)

ここでは2つのタスクを起動しています。

  • aws_iot_task() - AWS IoT Coreとの通信を行うタスクです。 - 詳細は後述します。
  • blink_task() - LEDを点滅させるタスクです。 - こちらも詳細は後述します。

また、タスク構成について特筆すべきポイントがいくつかあります。

  • aws_iot_taskの方がスタックが多く割り当てられています。通信処理でそれなりにメモリを食うためと予想されます。
  • 両方のタスクがコア1に割り当てられています。つまり、この2つのタスクは厳密に同時に処理されることは無いということです。

main.c - aws_iot_task()

AWS IoT CoreのMQTTブローカーに対してサブスクライブするタスクです。

ソースコード

  • aws_iot_mqtt_init() - MQTTクライアントを指定したパラメータで初期化しています。 - この時点ではまだAWS IoT Coreに接続していません
  • xEventGroupWaitBits() - これはFreeRTOSが提供するEventGroupsと呼ばれる仕組みを利用する関数です - ここでは別タスクで行っているWi-Fiの接続を待っています
  • aws_iot_mqtt_connect() - AWS IoT CoreとMQTT接続しています
  • aws_iot_mqtt_subscribe()

    • AWS IoT CoreのMQTTブローカーに対してサブスクライブしています

      • メッセージがPublishされた場合は iot_subscribe_callback_handler() をコールバックします
  • aws_iot_mqtt_yield() - AWS IoT CoreのMQTTクライアントのKeepAliveおよびメッセージの受信処理を行います。 - メッセージの受信頻度よりも高頻度で呼び出す必要があります - この例だと3000msで呼び出し、100msでタイムアウト

LEDの点滅(通称: Lチカ)を行うタスクです。

ソースコード

このタスクは起動直後にサスペンド(中断)しており、aws_iot_task() にて適宜レジューム(再開)されます。

  • vTaskSuspend()

    • FreeRTOSが提供するタスクをサスペンドする関数です
    • 引数がNULLの場合はこの関数を呼出したタスク自信をサスペンドします
  • Core2ForAWS_Sk6812_Clear()

    • LEDコントローラを初期化しています
    • SK6812とはLEDのコントローラです(参考: データシート)
  • Core2ForAWS_Sk6812_SetSideColor()

    • フルカラーLEDの表示色をセットします
    • 今回の場合は 0xffffff(白色点灯) → 0x000000 (消灯) を繰り返しています
  • vTaskDelay()

    • 引数で指定した期間、タスクをBlocked状態とします
    • 要は指定した時間(tick)、タスクを待たせています

main.c - iot_subscribe_callback_handler()

AWS IoT Coreに対してSubscribeしているtopicに対して、Publishが発生したときのコールバック関数です。

ソースコード

blink_task() の状態を取得して、サスペンドしている場合はレジューム、そうでなければサスペンドを実施しています。

感想

単純なLチカプログラムですが、じっくり読むと奥が深いですね。 今回はまだ浅い部分しかコードリーディングできていませんが、さらにコードを読み込んでいくことで学びがありそうです。

yuuu

yuuu

2018年の年明けに組込み畑からやってきた、2児の父 兼 Webエンジニアです。 mockmockの開発・運用を担当しており、組込みエンジニア時代の経験を活かしてデバイスをプログラミングしたり、簡易的なIoTシステムを作ったりしています。主な開発言語はRuby、時々Go。