M5StickCの送信先をAmbientからThingSpeakへ移行する実践ガイド ― コードの書き換えとハマりどころ

みなさん、こんにちは。

前回の記事では、長年愛用してきたIoTデータ可視化サービス「Ambient」の停止に伴い、移行先としてなぜ「ThingSpeak」を選んだのか、その理由と比較検証についてお伝えしました。

今回は実践編です。

我が家で稼働していた「M5StickC + ENV Hat(DHT12 / BMP280)」の環境センサーから、温度・湿度・気圧・バッテリー電圧を定期送信するプログラム(スケッチ)を、AmbientからThingSpeakへと書き換えた際の具体的な手順と記録をまとめました。

両サービスともにArduino向けの公式ライブラリが提供されているため、移行作業自体は非常にスムーズです。しかし、実際にコードを移植する中でいくつか「コンパイルエラー」や「ハマりどころ」がありましたので、ポイントを押さえながら解説していきます。

せっかちな方のために、最終的なコードを先に示しておきます。おそらくM5StickC Plusでも動作するはずですが、その場合はヘッダファイルをM5StickC.h から M5StickCPlus.h に修正してお使いください。

#include <M5StickC.h>
#include "DHT12.h"
#include <Wire.h>
#include "Adafruit_Sensor.h"
#include <Adafruit_BMP280.h>
#include <WiFi.h>
#include "ThingSpeak.h"

DHT12 dht12;
Adafruit_BMP280 bme;

#define uS_TO_S_FACTOR 1000000  /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP  6        /* Time ESP32 will go to sleep (in seconds) */

WiFiClient client;

const char* ssid = "your_ssid"; // WiFiのSSID
const char* password = "your_password"; // WiFiのパスフレーズ

unsigned long channelId = 123456; // ThingSpeakのチャネルID
const char* writeKey = "YOUR_WRITE_API_KEY"; // ThingSpeakのライトAPIキー

void setup() {
    M5.begin();
    M5.Axp.ScreenBreath(30);    // 画面の輝度を少し下げる
    M5.Lcd.setRotation(3);      // 左を上にする
    M5.Lcd.setTextSize(2);      // 文字サイズを2にする
    M5.Lcd.fillScreen(BLACK);   // 背景を黒にする
    M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK); // 文字色を白色、背景を黒に明示的に設定

    Wire.begin(0,26);               // I2Cを初期化する
    while (!bme.begin(0x76)) {  // BMP280を初期化する
        M5.Lcd.println("BMP280 init fail");
    }

    WiFi.begin(ssid, password);  //  Wi-Fi APに接続 ----A
    while (WiFi.status() != WL_CONNECTED) {  //  Wi-Fi AP接続待ち
        delay(500);
        Serial.print("Now, Connecting...");
    }
    Serial.print("WiFi connected\r\nIP address: ");

    ThingSpeak.begin(client); // ThingSpeakの初期化

    //esp_deep_sleep(TIME_TO_SLEEP * uS_TO_S_FACTOR); //  ----C
}

void loop() {
    float tmp = dht12.readTemperature();
    float hum = dht12.readHumidity();
    float pressure = bme.readPressure();
    float vbat = M5.Axp.GetVbatData() * 1.1 / 1000;  // バッテリー電圧を取得

    M5.Lcd.setCursor(0, 0, 1);
    M5.Lcd.printf("temp: %4.1f'C\r\n", tmp);
    M5.Lcd.printf("humid:%4.1f%%\r\n", hum);
    M5.Lcd.printf("press:%4.0fhPa\r\n", pressure / 100);
    M5.Lcd.printf("vbat: %4.2fV\r\n", vbat);
    M5.Lcd.println(WiFi.localIP()); // IPアドレスを表示

    // 温度、湿度、気圧、バッテリー電圧の値をThingSpeakに送信する ----B
    ThingSpeak.setField(1, tmp);
    ThingSpeak.setField(2, hum);
    ThingSpeak.setField(3, pressure / 100);
    ThingSpeak.setField(4, vbat);

    ThingSpeak.writeFields(channelId, writeKey);

    // 5分待機
    delay(300000);
}

移行時の主なポイントとコード変更

1. ライブラリとヘッダファイルの置き換え

まずは使用するライブラリの変更です。Ambient用の Ambient.h を外し、ThingSpeak用の ThingSpeak.h を読み込みます。

Arduino IDEのライブラリマネージャーを開き、「ThingSpeak」(MathWorks公式)を検索してインストールしておきましょう。

// Before (Ambient)
#include "Ambient.h"

// After (ThingSpeak)
#include "ThingSpeak.h"

2. 【ハマりどころ①】WiFi.h の明示的なインクルード

Ambientライブラリを使っていた時は、ヘッダ内部で WiFi.h が読み込まれていたためか、Ambient.h だけの記述で WiFiClientWiFi.begin() が問題なく動作していました。

しかし、ThingSpeakライブラリは WiFi.h を自動でインクルードしてくれません。そのため、単純にヘッダを差し替えただけだと以下のようなコンパイルエラーが発生してしまいます。

error: 'WiFiClient' does not name a type
error: 'WiFi' was not declared in this scope

ESP32系ボード(M5StickC含む)で開発する場合は、必ず明示的に #include <WiFi.h> を追加してください。

#include <WiFi.h>
#include "ThingSpeak.h"

3. 初期化処理と送信APIの違い

データのセットから送信までの流れ自体は非常によく似ていますが、オブジェクトの扱いと初期化のタイミングが異なります。

  • Ambient
    チャネルごとに Ambient オブジェクトを生成し、ambient.begin() を呼び出して set()send()
  • ThingSpeak
    シングルトン的なグローバルオブジェクト ThingSpeak に対し、setup() 内で ThingSpeak.begin() を1度だけ実行。その後 setField()writeFields()

具体的には以下のように書き換えます。

▼ 変更前(Ambient)
WiFiClient client;
Ambient ambient;
unsigned int channelId = CHANNEL_ID;
const char* writeKey = "WRITE_KEY";

void setup() {
    // WiFi接続処理など
}

void loop() {
    ambient.begin(channelId, writeKey, &client);
    ambient.set(1, tmp);
    ambient.set(2, hum);
    ambient.set(3, pressure / 100);
    ambient.set(4, vbat);
    ambient.send();
}
▼ 変更後(ThingSpeak)
WiFiClient client;
unsigned long channelId = 123456;
const char* writeKey = "YOUR_WRITE_API_KEY";

void setup() {
    // WiFi接続処理など
    ThingSpeak.begin(client); // setup内で1度呼べばOK
}

void loop() {
    ThingSpeak.setField(1, tmp);
    ThingSpeak.setField(2, hum);
    ThingSpeak.setField(3, pressure / 100);
    ThingSpeak.setField(4, vbat);
    ThingSpeak.writeFields(channelId, writeKey);
}

Ambientのサンプルコードでは loop() 内で毎回 ambient.begin() を呼ぶ実装が多く見られますが、ThingSpeakの begin() は通信クライアント(WiFiClient)を登録するだけなので、setup() で一度呼び出せば十分です。

4. チャネルIDとAPIキーの確認場所

管理画面における名称と取得場所が少々異なります。

  • Ambient
    チャネル作成後のダッシュボード画面等に「チャネルID」と「ライトキー」が表示される
  • ThingSpeak
    チャネル作成後、「API Keys」タブ内に「Channel ID」と「Write API Key」が表示される

名前の表記が少し違うだけで、機能・対応関係は1対1となっています。

5. 【ハマりどころ②】setField() の型オーバーロード問題

今回一番焦ったのが、変数の型指定によるコンパイルエラーです。

ThingSpeakライブラリの setField() には、int / long / float 型のオーバーロードしか用意されていません。そのため、M5StickCの電源管理IC(AXP192等)からバッテリー電圧を取得する際、以下のように double 型で計算・保持していると型不一致エラーになります。

error: call of overloaded 'setField(int, double&)' is ambiguous

Ambientの set()double 型をそのまま受け付けてくれたため、旧コードをそのまま移植した際に引っかかりやすいポイントです。対処法としては、引数に渡すタイミングで float にキャストするか、変数宣言自体を float に変更します。

// ❌ エラーになる例(double型)
double vbat = M5.Axp.GetVbatData() * 1.1 / 1000;
ThingSpeak.setField(4, vbat); // ambiguousエラー発生

// ⭕ 対処法:float型で宣言・計算する
float vbat = M5.Axp.GetVbatData() * 1.1 / 1000;
ThingSpeak.setField(4, vbat); // OK!

6. 送信間隔の制限について

ThingSpeakの無料プランでは、チャネルへの書き込み間隔が最短15秒に制限されています。

我が家のように「5分に1回」といった間隔で環境データを送る分には全く問題ありませんが、リアルタイム性を求めて数秒おきにデータを投げたい場合は注意が必要です。


移行作業のまとめ

両サービスの仕様と違いを一覧表にまとめました。

項目AmbientThingSpeak
ヘッダAmbient.hThingSpeak.h(要 #include <WiFi.h>
オブジェクトAmbient ambient;グローバルの ThingSpeak
初期化ambient.begin(channelId, writeKey, &client)ThingSpeak.begin(client)setupで1回)
値のセットambient.set(field, value)double可)ThingSpeak.setField(field, value)float等のみ)
送信関数ambient.send()ThingSpeak.writeFields(channelId, writeKey)
認証情報チャネルID + ライトキーChannel ID + Write API Key
最短送信間隔サービス依存無料プランは15秒制限

ライブラリのAPI思想自体は「フィールド番号に値をセットして送信する」という点で双方非常に良く似ています。

実際に必要だった作業は、実質的に「ヘッダとオブジェクトの置き換え」「WiFi.h の明示的追加」「浮動小数点数を float 型に統一する」という3点のみでした。

これで無事にAmbientからThingSpeakへの移行が完了し、我が家の軽量なデータ送信環境が再構築できました!

ThingSpeakでグラフ化できた
ThingSpeakでグラフ化できた

手軽にクラウドへデータを上げられる環境が戻ってくると、やはり安心感が違いますね。Ambientからの移行先に悩んでいる方の参考になれば幸いです。次回はデータを取得して通知するRaspberry Pi 4側の改修ポイントを解説する予定です。


「自分もIoTで何か作ってみたい」と思われた方、そのアイデア、頭の中で寝かせておくのはもったいないかもしれません。

ビューローみかみでは、構想段階の壁打ちからPoC・実装・現場導入まで、現場で「使い続けられる」ものづくりを支援しています。

アイデアを短期間で「動くもの」にし、PoCで終わらせず、実運用まで伴走します。まずは「これ、作る価値ありますか?」という壁打ちからでも大歓迎です。

▶ PoC・MVP開発サービスの詳細はこちら

気になることがあればお気軽にご相談ください。

本日も最後までお読みいただきありがとうございました。

それでは、よいIoTライフを!

カテゴリ: IoT

「M5StickCの送信先をAmbientからThingSpeakへ移行する実践ガイド ― コードの書き換えとハマりどころ」への1件のフィードバック

  1. ピンバック: AmbientからThingSpeakへ ― PythonのIoTデータ取得コードを移行する際の6つのポイント - ビューローみかみ

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール