みなさん、こんにちは。
前回の記事では、長年愛用してきた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 だけの記述で WiFiClient や WiFi.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回」といった間隔で環境データを送る分には全く問題ありませんが、リアルタイム性を求めて数秒おきにデータを投げたい場合は注意が必要です。
移行作業のまとめ
両サービスの仕様と違いを一覧表にまとめました。
| 項目 | Ambient | ThingSpeak |
| ヘッダ | Ambient.h | ThingSpeak.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への移行が完了し、我が家の軽量なデータ送信環境が再構築できました!

手軽にクラウドへデータを上げられる環境が戻ってくると、やはり安心感が違いますね。Ambientからの移行先に悩んでいる方の参考になれば幸いです。次回はデータを取得して通知するRaspberry Pi 4側の改修ポイントを解説する予定です。
「自分もIoTで何か作ってみたい」と思われた方、そのアイデア、頭の中で寝かせておくのはもったいないかもしれません。
ビューローみかみでは、構想段階の壁打ちからPoC・実装・現場導入まで、現場で「使い続けられる」ものづくりを支援しています。
アイデアを短期間で「動くもの」にし、PoCで終わらせず、実運用まで伴走します。まずは「これ、作る価値ありますか?」という壁打ちからでも大歓迎です。
▶ PoC・MVP開発サービスの詳細はこちら
気になることがあればお気軽にご相談ください。
本日も最後までお読みいただきありがとうございました。
それでは、よいIoTライフを!




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