セクションの複数ページをまとめています。 印刷またはPDF形式で保存...
TWELITE SPOT / ESP32
- 1: 導入済みアプリケーションの概要
- 2: 無線性能に配慮した設置方法
- 3: ファームウェア開発環境の構築方法
- 3.1: Arduino IDE 1.x による開発環境の構築方法
- 3.1.1: Arduino IDE 1.x の導入
- 3.1.2: Arduino core for the ESP32 の導入
- 3.1.3: Arduino core for the ESP32 の設定
- 3.1.4: MWings ライブラリの導入
- 4: ファームウェアの書き込み方法
- 4.1: ESP32 へのファームウェアの書き込み方法
- 4.1.1: ESP32 へのスケッチの書き込み方法
- 4.1.2: ESP32 へのファイルの書き込み方法
- 4.1.3: ESP32 のパーティションテーブルを指定した書き込み方法
- 4.2: TWELITE へのファームウェアの書き込み方法
- 4.3: ファームウェアの初期化方法
- 4.3.1: ESP32 のファームウェアの初期化方法
- 4.3.2: TWELITE のファームウェアの初期化方法
- 5: サンプルスケッチの解説
- 5.1: TWELITE と通信するスケッチの解説
- 5.1.1: 超簡単!標準アプリのデータを取得・操作
- 5.1.1.1: 超簡単!標準アプリのデータを取得・操作
- 5.1.1.2: 超簡単!標準アプリのデータを取得・操作
- 5.1.1.3: 超簡単!標準アプリのデータを取得・操作
- 5.1.2: キューアプリのデータを取得
- 5.1.2.1: キューアプリのデータを取得
- 5.1.2.2: キューアプリのデータを取得
- 5.1.2.3: キューアプリのデータを取得
- 5.1.3: アリアアプリのデータを取得
- 5.1.3.1: アリアアプリのデータを取得
- 5.1.3.2: アリアアプリのデータを取得
- 5.1.3.3: アリアアプリのデータを取得
- 5.2: TWELITE に加えて無線 LAN を活用するスケッチの解説
- 5.2.1: プリインストール済みスケッチ
- 5.2.1.1: プリインストール済みスケッチ
- 5.2.1.2: プリインストール済みスケッチ
- 5.2.2: WebSocketによる中継
- 5.2.2.1: WebSocketによる中継
- 5.2.3: REST API の使用
- 5.2.3.1: REST API の使用
- 5.2.3.2: REST API の使用
- 5.2.4: Google スプレッドシートの利用
- 5.2.4.1: Google スプレッドシートの利用
1 - 導入済みアプリケーションの概要
使用方法
TWELITE SPOT スタートガイド まずは使ってみる をご覧ください。
ビューア画面
それぞれのビューアを選択すると、対応した TWELITE 子機 から受信したデータを表示します。
Signal Viewer
TWELITE DIP (超簡単!標準アプリ) から受信したデータを表示します。 AI1-4 に入力した電圧や、DI1-4 の入力状態を確認できます。
CUE Viewer
TWELITE CUE (キューアプリ、TWELITE CUE モード) から受信したデータを表示します。 加速度センサや磁気センサのデータを確認できます。
ARIA Viewer
TWELITE ARIA (アリアアプリ、TWELITE ARIA モード) から受信したデータを表示します。 温湿度センサや磁気センサのデータを確認できます。
Serial Viewer
TWELITE SPOT が受信したパケットの電文を表示します。
工場出荷時のアプリの詳細
ESP32
ESP32 に書き込んでいるスケッチは、spot-server
です。
TWELITE
TWELITE に書き込んでいるアプリは、App_Wings_SPOT_BLUE
です。
2 - 無線性能に配慮した設置方法
無線性能に配慮した設置
アンテナ方向マークを天地方向にする
TWELITE SPOT で使用しているアンテナは、アンテナ方向マークを天地方向に向けると、TWELITE SPOT を上面から見たときに、TWELITE SPOT を中心に円状に電波が放射されるため、広い範囲の電波を受信することができます。
TWELITE SPOT と TWELITE 子機のアンテナ方向マークを同じ方向にする
電波には波の振動方向があり、この方向を偏波と呼びます。 送信側と受信側の偏波が同一でない場合感度が低くなり、通信距離が短くなります。 TWELITE SPOT に表記されているアンテナ方向マークはこの偏波の向きを示しており、通信するアンテナ同士のアンテナ方向マークを合わせることにより通信感度が良くなります。
周辺に障害物がない場所に設置する
TWELITE SPOT の周辺に障害物があると電波が減衰するため、通信距離が短くなるため、この特性をご理解の上、設置場所を決めてください。 特に金属が TWELITE SPOT の周辺にあると著しく通信距離が短くなるため、TWELITE SPOT の周辺には金属を含む障害物を置かないようにしてください。 目安として TWELITE SPOT から半径 10cm 以内に金属を配置しないようにしてください。
壁面への設置
M3 ビスを 2 本使用しますが、金属製の部品は無線性能に影響を与える可能性があることに注意してください。
3 - ファームウェア開発環境の構築方法
3.1 - Arduino IDE 1.x による開発環境の構築方法
arduino-esp32fs-plugin
や EspExceptionDecoder
などの Java 製プラグインが動作しないため、Legacy IDE (1.x) を推奨しています。3.1.1 - Arduino IDE 1.x の導入
ダウンロード
Web ブラウザで Arduino 公式ダウンロードページを開き、Legacy IDE (1.8.X) をダウンロードしてください。
インストール
ダウンロードしたファイルを実行して指示に従い、Arduino IDE 1.x をインストールしてください。
3.1.2 - Arduino core for the ESP32 の導入
ボード情報の追加
Arduino IDE 1.x を起動し、ツールバーの ファイル -> 環境設定 を開いてください。
追加のボードマネージャーのURL
に下記の URL を入力し、OKボタンを押してください。
https://espressif.github.io/arduino-esp32/package_esp32_index.json
インストール
ツールバーの ツール -> ボード: “Arduino Uno” -> ボードマネージャ を開いてください。
検索ボックスに “ESP32” と入力して、esp32
ボード定義をインストールしてください。
2.0.5
以降で動作確認しています。3.1.3 - Arduino core for the ESP32 の設定
ボード種別の選択
ツールバーの ツール ―> ボード -> ESP32 Arduino -> ESP32 Dev Module を選択してください。
ボード設定
以下の画像のように設定してください。
デフォルトでは Flash size
が 4MB (32Mb)
となっています。
これを 16MB (128Mb)
に変更してください。
3.1.4 - MWings ライブラリの導入
インストール
スケッチ -> ライブラリをインクルード -> ライブラリを管理… を開いてください。
検索ボックスに MWings
と入力し、MWings をインストールしてください。
4 - ファームウェアの書き込み方法
4.1 - ESP32 へのファームウェアの書き込み方法
4.1.1 - ESP32 へのスケッチの書き込み方法
ホストとの接続
TWELITE R3 / R2 を接続
7P インターフェイス(ESP32
と記載のある側)に TWELITE R3 / R2 を接続してください。
電源を接続
側面の USB-C コネクタ に 5V 電源を供給してください。
Arduino IDE の操作
スケッチを開く
Arduino IDE を起動して、書き込むスケッチを開いてください。
シリアルポートを選択
ツール -> シリアルポート メニューから、 TWELITE R3 / R2 のポートを選択してください。
COM?
といった名称に、macOS / Linux では /dev/tty?
といった名称になります。ESP32 をプログラムモードで起動
TWELITE SPOT の ESP32 リセットスイッチ EN(RST)
と ESP32 ブートスイッチ BOOT
を押し、EN(RST)
-> BOOT
の順で離してください。
BOOT
を押した状態でリセットすることで、ESP32 をプログラムモードに移行できます。書き込みを実行
Arduino IDEの マイコンボードに書き込む ボタンをクリックしてください。
Hard resetting via RTS pin...
と表示されます。書き込みに失敗し、下記のメッセージが表示された場合は、使用する USB ポートや USB ケーブルを変えてみてください。
A serial exception error occurred: Could not configure port: (6, 'Device not configured')
ESP32 をリセット
書き込みが完了したら、TWELITE SPOT の ESP32 リセットスイッチ EN(RST)
を押して離し、ESP32 をリセットしてください。
4.1.2 - ESP32 へのファイルの書き込み方法
data/
フォルダ以下のファイル群)を書き込む方法をご案内します。本稿では、応用的な内容(TWELITE SPOT に搭載された ESP32 のフラッシュ領域をファイルシステムとして扱い、HTML などのファイルを書き込む方法)を紹介しております。
例えば TWELITE SPOT に HTML ファイルを書き込んでWebサーバとして振る舞わせたい(spot-server サンプルで実現しています)、TWELITE SPOT に暗号鍵ファイルを書き込んでおきたいといった要求がない場合には、本稿の内容を無視していただいて構いません。
本稿では、サードパーティのオープンソースソフトウェアを使用します。
サードパーティのソフトウェアについて、その詳しい使用方法を弊社からご案内することはいたしかねます。また、サードパーティのソフトウェアを使用されたことによるいかなる損害についても、弊社は一切の責任を負いません。
本稿で紹介する方法では、Arduino IDE 1.x を必要とします。Arduino IDE 2.x の技術的な制約により、2023年5月現在、Arduino IDE 2.x には対応していません。
本稿で使用するプラグインは Java で書かれているため、Arduino IDE 1.x とは異なり、Java ベースではない Arduino IDE 2.x では動作しないからです。問題の詳細については、Arduino IDE GitHub ページの Issue (Missing support for external tools / plugins · Issue #58 · arduino/arduino-ide) をご覧ください(英語)。
プラグインの導入
ESP32 のフラッシュ領域へファイルを書き込むための Arduino プラグイン (arduino-esp32fs-plugin) をインストールします。
プラグインのダウンロード
以下のページから、esp32fs.zip
を入手します。
Release Update to support Big Sur · lorol/arduino-esp32fs-plugin
プラグインのインストール
ダウンロードした
esp32fs.zip
を展開してください。Arduino のスケッチブックの保存場所(Arduino IDE 環境設定に記載。例:
C:\Users\foo\Documents\Arduino
)にtools
フォルダがない場合は、これを作成してください。tools
フォルダにESP32FS/tool
フォルダを作成し、zipファイルから得たesp32ffs.jar
ファイルを配置してください。(配置例:C:\Users\foo\Documents\Arduino\tools\ESP32FS\tool\esp32fs.jar
)。Arduino IDE を次に起動した際にプラグインが使用できるようになります。
ホストとの接続
TWELITE R3 / R2 を接続
7P インターフェイス(ESP32
と記載のある側)に TWELITE R3 / R2 を接続してください。
電源を接続
側面の USB-C コネクタ に 5V 電源を供給してください。
Arduino IDE の操作
スケッチを開く
Arduino IDE を起動して、スケッチを開いてください。
書き込むファイルの配置
スケッチ -> スケッチのフォルダを表示 から、スケッチのフォルダを開いてください。
スケッチファイル(
.ino
)と同じ階層に、data
フォルダを作成してください。data
フォルダ内に、書き込むファイルを配置してください。
data
フォルダ内の階層構造は維持されます。シリアルポートを選択
ツール -> シリアルポート メニューから、 TWELITE R3 / R2 のポートを選択してください。
COM?
といった名称に、macOS / Linux では /dev/tty?
といった名称になります。ESP32 をプログラムモードで起動
TWELITE SPOT の ESP32 リセットスイッチ EN(RST)
と ESP32 ブートスイッチ BOOT
を押し、EN(RST)
-> BOOT
の順で離してください。
BOOT
を押した状態でリセットすることで、ESP32 をプログラムモードに移行できます。書き込みを実行
ツール -> ESP32 Sketch Data Upload をクリックしてください。
Select FS for <スケッチ名> /data folder にて、LittleFS を選択してください。
- OK を押してください。
Hard resetting via RTS pin...
と表示されます。環境によっては、以下のように表示されて書き込みに失敗することがあります(macOS で確認)。
`Error: esptool not found!`
その際には、esptool.py を Arduino15フォルダ
/packages/esp32/tools/esptool_py/<バージョン>/esptool.py
へ配置することで解決できるかもしれません。
例えば、macOS の場合には、下記のようにして esptool.py を取得し、シンボリックリンクを作成します。
/usr/bin/pip3 install esptool
ln -s ~/Library/Python/3.9/bin/esptool.py ~/Library/Arduino15/packages/esp32/tools/esptool_py/4.5.1/esptool.py
注:
/usr/bin/pip3
と指定しないと、Homebrew のディレクトリにインストールされてしまうことがあります
ESP32 をリセット
書き込みが完了したら、TWELITE SPOT の ESP32 リセットスイッチ EN(RST)
を押して離し、ESP32 をリセットしてください。
4.1.3 - ESP32 のパーティションテーブルを指定した書き込み方法
本稿では、応用的な内容(フラッシュ領域のパーティションテーブルを指定する方法)を紹介しております。
ESP32 Arduino Core に最初から含まれているパーティションテーブルの設定(例:Default 4MB with spiffs)を使用される場合は、本稿を無視していただいて構いません。
定義ファイルの作成
パーティションテーブルの定義は、csv ファイルに記述します。
下記の例では、16MB のフラッシュ領域のうち、8MB をファイルシステムで使うように指定しています。
# TWELITE SPOT 16MB with 8MB LittleFS
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, 0xE000, 0x2000,
app0, app, ota_0, 0x10000, 0x7F0000,
spiffs, data, spiffs, 0x800000, 0x800000,
TWELITE SPOT 16MB with 8MB LittleFS
Arduino IDE に表示される名前です。nvs
システムで使用する領域です。変更しません。otadata
OTA を使う際に使用する領域です。変更しません。app0
ファームウェアを書き込む領域です。spiffs
LittleFS ファイルシステムで使用する領域です。
csv ファイル中の Offset
および Size
列の単位はバイトで、16進数です。
したがって、上記の例では、ファームウェアとファイルシステムが使えるサイズは下記のように計算できます。
app0
のサイズ:0x7F0000 = 8323072
より、7.875MB
spiffs
のサイズ:0x800000 = 8388608
より、8MB
定義ファイルの登録
Arduino15フォルダ を開き、下記のパスに csv ファイルを追加します。
Arduino15/packages/esp32/hardware/esp32/x.x.x/tools/partitions
x.x.x
は Arduino core for the ESP32 のバージョン
パーティションテーブルの適用
Arduino IDE のツールバーから ツール -> Partition Scheme を開き、追加したパーティションテーブルを選びます。
選択したパーティションテーブルが次回以降のファームウェアの書き込みやファイルシステムの書き込みに反映されます。
partitions.csv
という名前で設定ファイルを置くと、そちらが優先されます。ただし、Arduino IDE 上の表記は変わらないため、紛らわしい場合があります。4.2 - TWELITE へのファームウェアの書き込み方法
TWELITE SPOT に搭載される TWELITE では、インタラクティブモードによる設定変更ができません。
TWELITE の周波数チャネルやアプリケーションIDを設定するには、ESP32 からシリアル通信でコマンドを送信します。Arduino 環境では、Twelite.begin()
を使用してください。
TWELITE STAGE APP をインストール
TWELITE STAGE SDK をダウンロードし、ダウンロードしたファイルをCドライブ直下に展開してください。
ホストとの接続
TWELITE R3 / R2 を接続
7P インターフェイス(TWELITE
と記載のある側)に TWELITE R3 / R2 を接続してください。
電源を接続
側面の USB-C コネクタ に 5V 電源を供給してください。
TWELITE STAGE APP の操作
TWELITE STAGE APP (
TWELITE_Stage.exe
) を起動してください。シリアルポート選択画面で TWELITE R3 / TWELITE R2 を選択してください。
メインメニュー -> アプリ書換 を選択し、書換えたいアプリを選択してください。
4.3 - ファームウェアの初期化方法
4.3.1 - ESP32 のファームウェアの初期化方法
このページでは、TWELITE 無線モジュールと ESP32 を搭載した製品 TWELITE SPOT を工場出荷時に戻す方法を解説しています。一般的な ESP32 を工場出荷時へ戻す方法は案内していません。
ESP32 のプログラムを消去するだけならば、Espressifの公式Webツール を使用できます(TWELITE SPOTも同様)。
検索エンジンからお越しいただいたみなさま、申し訳ございません。
超低消費電力が特徴の無線モジュール、TWELITE を覚えていただけると幸いでございます。
esptool をインストール
Python をインストール
Python 3.7 以降がインストールされていない場合は、Python 3.7 以降をインストールしてください。
https://www.python.org/downloads/
esptool 本体をインストール
PyPI から esptool をインストールしてください。
pip install esptool
既存のPython環境に影響を与えたくない場合は、pipx の使用を推奨します。
pipx install esptool
ホストとの接続
TWELITE R3 / R2 を接続
7P インターフェイス(ESP32
と記載のある側)に TWELITE R3 / R2 を接続してください。
電源を接続
側面の USB-C コネクタ に 5V 電源を供給してください。
バイナリファイルの取得
下記のリンクから、spot-server-2023-05-bin.zip
をダウンロードしてください。
ダウンロードしたら、zipファイルを展開してください。
ESP32 をプログラムモードで起動
TWELITE SPOT の ESP32 リセットスイッチ EN(RST)
と ESP32 ブートスイッチ BOOT
を押し、EN(RST)
-> BOOT
の順で離してください。
BOOT
を押した状態でリセットすることで、ESP32 をプログラムモードに移行できます。esptool による書き込み
esptool をインストールした端末でspot-server-2023-05-bin.zip
を展開したフォルダに移動し、下記を実行してください。
esptool --chip esp32 --port <シリアルポート> --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode qio --flash_freq 80m --flash_size 16MB 0x1000 spot-server.ino.bootloader.bin 0x8000 spot-server.ino.partitions.bin 0xe000 boot_app0.bin 0x10000 spot-server.ino.bin 0x100000 spot-server.littlefs.bin
COM?
や /dev/tty?
といったポート名を入力します。もし失敗する場合は、下記をお試しください。
--flash_mode qio
を--flash_mode dio
に変更--flash_freq 80m
を--flash_freq 40m
に変更--baud 921600
を--baud 460800
に変更
ESP32 をリセット
書き込みが完了したら、TWELITE SPOT の ESP32 リセットスイッチ EN(RST)
を押して離し、ESP32 をリセットしてください。
4.3.2 - TWELITE のファームウェアの初期化方法
TWELITE SPOT に搭載される TWELITE では、インタラクティブモードによる設定変更ができません。
TWELITE の周波数チャネルやアプリケーションIDを設定するには、ESP32 からシリアル通信でコマンドを送信します。Arduino 環境では、Twelite.begin()
を使用してください。
TWELITE STAGE APP をインストール
TWELITE STAGE SDK をダウンロードし、ダウンロードしたファイルをCドライブ直下に展開(Windowsの場合)してください。
ファームウェアの入手
下記のリンクからバイナリファイルをダウンロードし、MWSTAGE
フォルダ内の BIN
フォルダに配置してください。
ホストとの接続
TWELITE R3 / R2 を接続
7P インターフェイス(TWELITE
と記載のある側)に TWELITE R3 / R2 を接続してください。
電源を接続
側面の USB-C コネクタ に 5V 電源を供給してください。
TWELITE STAGE APP の操作
TWELITE STAGE APP (
TWELITE_Stage.exe
) を起動してください。シリアルポート選択画面で TWELITE R3 / TWELITE R2 を選択してください。
メインメニュー -> アプリ書換 -> BIN から選択 を選び、先ほど入手したバイナリ(
App_Wings_TWELITESPOT_BLUE_L1305_V1-3-0.bin
)を書き込んでください。
5 - サンプルスケッチの解説
5.1 - TWELITE と通信するスケッチの解説
5.1.1 - 超簡単!標準アプリのデータを取得・操作
monitor_spot_app_twelite
の解説monitor_spot_app_twelite
の解説です。最後に、相手先の出力ポートを操作する改変を行います。5.1.1.1 - 超簡単!標準アプリのデータを取得・操作
monitor_spot_app_twelite
の解説です。最後に、相手先の出力ポートを操作する改変を行います。サンプルスケッチの保存場所
MWings ライブラリを導入していれば、Arduino IDE の ファイル -> スケッチ例 -> MWings -> TWELITE SPOT -> Receive -> monitor_spot_app_twelite からスケッチを開くことができます。
スケッチ
以下はソースコード本体です。
// Monitor example for TWELITE SPOT: Receive data from App_Twelite
#include <Arduino.h>
#include "MWings.h"
const int RST_PIN = 5;
const int PRG_PIN = 4;
const int LED_PIN = 18;
const int8_t RX1_PIN = 16;
const int8_t TX1_PIN = 17;
const uint8_t TWE_CHANNEL = 18;
const uint32_t TWE_APP_ID = 0x67720102;
void setup()
{
// Initialize serial ports
Serial.begin(115200);
Serial.println("Monitor example for TWELITE SPOT: App_Twelite");
Serial2.begin(115200, SERIAL_8N1, RX1_PIN, TX1_PIN);
// Initialize TWELITE
Twelite.begin(Serial2,
LED_PIN, RST_PIN, PRG_PIN,
TWE_CHANNEL, TWE_APP_ID);
// Attach an event handler to process packets from App_Twelite
Twelite.on([](const ParsedAppTwelitePacket& packet) {
Serial.println("");
Serial.print("Packet Timestamp: ");
Serial.print(packet.u16SequenceNumber / 64.0f, 1); Serial.println(" sec");
Serial.print("Source Logical ID: 0x");
Serial.println(packet.u8SourceLogicalId, HEX);
Serial.print("LQI: ");
Serial.println(packet.u8Lqi, DEC);
Serial.print("Supply Voltage: ");
Serial.print(packet.u16SupplyVoltage, DEC); Serial.println(" mV");
Serial.print("Digital Input: ");
Serial.print(packet.bDiState[0] ? " DI1:Lo" : " DI1:Hi");
Serial.print(packet.bDiState[1] ? " DI2:Lo" : " DI2:Hi");
Serial.print(packet.bDiState[2] ? " DI3:Lo" : " DI3:Hi");
Serial.println(packet.bDiState[3] ? " DI4:Lo" : " DI4:Hi");
Serial.print("Analog Input: ");
Serial.print(" AI1:"); Serial.print(packet.u16AiVoltage[0]); Serial.print(" mV");
Serial.print(" AI2:"); Serial.print(packet.u16AiVoltage[1]); Serial.print(" mV");
Serial.print(" AI3:"); Serial.print(packet.u16AiVoltage[2]); Serial.print(" mV");
Serial.print(" AI4:"); Serial.print(packet.u16AiVoltage[3]); Serial.println(" mV");
});
}
void loop()
{
// Update TWELITE
Twelite.update();
}
ライブラリのインクルード
4行目では、MWings ライブラリをインクルードしています。
#include "MWings.h"
ピン番号の定義
6-11行目では、ピン番号を定義しています。
const int RST_PIN = 5;
const int PRG_PIN = 4;
const int LED_PIN = 18;
const int8_t RX1_PIN = 16;
const int8_t TX1_PIN = 17;
名称 | 内容 |
---|---|
RST_PIN | TWELITE の RST ピンが接続されているピンの番号 |
PRG_PIN | TWELITE の PRG ピンが接続されているピンの番号 |
LED_PIN | 基板上の ESP32 用 LED が接続されているピンの番号 |
RX1_PIN | TWELITE の RX1 ピンが接続されているピンの番号 |
TX1_PIN | TWELITE の TX1 ピンが接続されているピンの番号 |
TWELITE 設定の定義
13-14行目では、TWELITE SPOT に搭載された TWELITE 親機に適用する設定を定義しています。
const uint8_t TWE_CHANNEL = 18;
const uint32_t TWE_APP_ID = 0x67720102;
名称 | 内容 |
---|---|
TWE_CHANNEL | TWELITE の 周波数チャネル |
TWE_APP_ID | TWELITE の アプリケーション ID |
シリアルポートの設定
19-21行目では、使用するシリアルポートを初期化するとともに、シリアルモニタへ起動メッセージを出力しています。
Serial.begin(115200);
Serial.println("Monitor example for TWELITE SPOT: App_Twelite");
Serial2.begin(115200, SERIAL_8N1, RX1_PIN, TX1_PIN);
Serial
は、Arduino IDE の シリアルモニタとの通信に使います。シリアルモニタの設定に合わせて、ボーレートを 115200
bps としています。
一方、Serial2
は、TWELITE SPOT に搭載された TWELITE 親機との通信に使います。こちらも TWELITE 親機の初期設定に合わせて、ボーレートを 115200
bps としています。
TWELITE の設定
24-27行目では、Twelite.begin()
を呼び出し、TWELITE SPOT に搭載された TWELITE 親機の設定と起動を行っています。
Twelite.begin(Serial2,
LED_PIN, RST_PIN, PRG_PIN,
TWE_CHANNEL, TWE_APP_ID);
パケット受信時のイベントの登録
29-49行目では、Twelite.on()
を呼び出し、送られたデータに対して行う処理を登録しています。
ここでは、受信したパケットの内容をシリアルモニタに出力しています。
Twelite.on([](const ParsedAppTwelitePacket& packet) {
Serial.println("");
Serial.print("Packet Timestamp: ");
Serial.print(packet.u16SequenceNumber / 64.0f, 1); Serial.println(" sec");
Serial.print("Source Logical ID: 0x");
Serial.println(packet.u8SourceLogicalId, HEX);
Serial.print("LQI: ");
Serial.println(packet.u8Lqi, DEC);
Serial.print("Supply Voltage: ");
Serial.print(packet.u16SupplyVoltage, DEC); Serial.println(" mV");
Serial.print("Digital Input: ");
Serial.print(packet.bDiState[0] ? " DI1:Lo" : " DI1:Hi");
Serial.print(packet.bDiState[1] ? " DI2:Lo" : " DI2:Hi");
Serial.print(packet.bDiState[2] ? " DI3:Lo" : " DI3:Hi");
Serial.println(packet.bDiState[3] ? " DI4:Lo" : " DI4:Hi");
Serial.print("Analog Input: ");
Serial.print(" AI1:"); Serial.print(packet.u16AiVoltage[0]); Serial.print(" mV");
Serial.print(" AI2:"); Serial.print(packet.u16AiVoltage[1]); Serial.print(" mV");
Serial.print(" AI3:"); Serial.print(packet.u16AiVoltage[2]); Serial.print(" mV");
Serial.print(" AI4:"); Serial.print(packet.u16AiVoltage[3]); Serial.println(" mV");
});
上記のイベントは、超簡単!標準アプリからのパケットを受信したときにだけ呼び出されます。
受信したパケットの内容は ParsedAppTwelitePacket
型の引数 packet
に格納されています。
メッセージの内容
メッセージ | 内容 |
---|---|
Packet Timestamp | パケットのタイムスタンプ |
Source Logical ID | 送信元 TWELITE の論理デバイスID |
LQI | 無線通信品質(0~255) |
Supply Voltage | 電源電圧 (mV) |
Digital Input | デジタル入力の状態 |
Analog Input | アナログ入力の状態 |
TWELITE のデータの更新
55行目では、Twelite.update()
を呼び出しています。
Twelite.update();
相手先の出力ポートの操作
超簡単!標準アプリの入力ポートの状態を取得するだけでなく、超簡単!標準アプリの出力ポートを操作してみましょう。
ここでは、TWELITE SPOT が受信した際の LQI(無線通信品質)に基づき、相手端末が TWELITE SPOT に近づいた際に、相手端末のデジタル出力ポートを点灯させてみます。
スケッチの改変
改変内容
はじめに、16行目へ下記のコードを追加します。
AppTweliteCommand command;
上記のコードでは、送信するコマンドの内容を格納する AppTweliteCommand
を作成しています。
次に、52-54行目へ下記のコードを追加します。
command.u8DestinationLogicalId = packet.u8SourceLogicalId; // LID
command.bDiState[0] = (packet.u8Lqi >= 100) ? true : false; // DI1
Twelite.send(command);
上記のコードでは、AppTweliteCommand
を操作し、Twelite.send()
でコマンドを送信しています。
ここでは、送信先の論理デバイス ID を設定し、出力ポート(DO1)の状態を指定しています。
データの詳細は AppTweliteCommand
のリファレンス をご覧ください。
これでスケッチの改変は終了です。改変後のコードを示します。
// Monitor example for TWELITE SPOT: Receive data from and send data to App_Twelite
#include <Arduino.h>
#include "MWings.h"
const int RST_PIN = 5;
const int PRG_PIN = 4;
const int LED_PIN = 18;
const int8_t RX1_PIN = 16;
const int8_t TX1_PIN = 17;
const uint8_t TWE_CHANNEL = 18;
const uint32_t TWE_APP_ID = 0x67720102;
AppTweliteCommand command;
void setup()
{
// Initialize serial ports
Serial.begin(115200);
Serial.println("Monitor example for TWELITE SPOT: App_Twelite");
Serial2.begin(115200, SERIAL_8N1, RX1_PIN, TX1_PIN);
// Initialize TWELITE
Twelite.begin(Serial2,
LED_PIN, RST_PIN, PRG_PIN,
TWE_CHANNEL, TWE_APP_ID);
// Attach an event handler to process packets from App_Twelite
Twelite.on([](const ParsedAppTwelitePacket& packet) {
Serial.println("");
Serial.print("Packet Timestamp: ");
Serial.print(packet.u16SequenceNumber / 64.0f, 1); Serial.println(" sec");
Serial.print("Source Logical ID: 0x");
Serial.println(packet.u8SourceLogicalId, HEX);
Serial.print("LQI: ");
Serial.println(packet.u8Lqi, DEC);
Serial.print("Supply Voltage: ");
Serial.print(packet.u16SupplyVoltage, DEC); Serial.println(" mV");
Serial.print("Digital Input: ");
Serial.print(packet.bDiState[0] ? " DI1:Lo" : " DI1:Hi");
Serial.print(packet.bDiState[1] ? " DI2:Lo" : " DI2:Hi");
Serial.print(packet.bDiState[2] ? " DI3:Lo" : " DI3:Hi");
Serial.println(packet.bDiState[3] ? " DI4:Lo" : " DI4:Hi");
Serial.print("Analog Input: ");
Serial.print(" AI1:"); Serial.print(packet.u16AiVoltage[0]); Serial.print(" mV");
Serial.print(" AI2:"); Serial.print(packet.u16AiVoltage[1]); Serial.print(" mV");
Serial.print(" AI3:"); Serial.print(packet.u16AiVoltage[2]); Serial.print(" mV");
Serial.print(" AI4:"); Serial.print(packet.u16AiVoltage[3]); Serial.println(" mV");
command.u8DestinationLogicalId = packet.u8SourceLogicalId; // LID
command.bDiState[0] = (packet.u8Lqi >= 100) ? true : false; // DI1
Twelite.send(command);
});
}
void loop()
{
// Update TWELITE
Twelite.update();
}
動作確認
子機の TWELITE DIP の DO1 ピンと VCC ピンの間に LED と電流制限抵抗を接続してください。
改変したスケッチを書き込むと、TWELITE DIP が TWELITE SPOT に近づいた際(=通信品質がよいとき)に LED が点灯します。
5.1.1.2 - 超簡単!標準アプリのデータを取得・操作
monitor_spot_app_twelite
の解説です。最後に、相手先の出力ポートを操作する改変を行います。サンプルスケッチの保存場所
MWings ライブラリを導入していれば、Arduino IDE の ファイル -> スケッチ例 -> MWings -> monitor_spot_app_twelite からスケッチを開くことができます。
スケッチ
以下はソースコード本体です。
// Monitor example for TWELITE SPOT: Receive data from App_Twelite
#include <Arduino.h>
#include "MWings.h"
const int RST_PIN = 5;
const int PRG_PIN = 4;
const int LED_PIN = 18;
const uint8_t TWE_CHANNEL = 18;
const uint32_t TWE_APP_ID = 0x67720102;
void setup()
{
// Initialize serial ports
Serial.begin(115200);
Serial.println("Monitor example for TWELITE SPOT: App_Twelite");
Serial2.begin(115200, SERIAL_8N1);
// Initialize TWELITE
Twelite.begin(Serial2,
LED_PIN, RST_PIN, PRG_PIN,
TWE_CHANNEL, TWE_APP_ID);
// Attach an event handler to process packets from App_Twelite
Twelite.on([](const ParsedAppTwelitePacket& packet) {
Serial.println("");
Serial.print("Packet Timestamp: ");
Serial.print(packet.u16SequenceNumber / 64.0f, 1); Serial.println(" sec");
Serial.print("Source Logical ID: 0x");
Serial.println(packet.u8SourceLogicalId, HEX);
Serial.print("LQI: ");
Serial.println(packet.u8Lqi, DEC);
Serial.print("Supply Voltage: ");
Serial.print(packet.u16SupplyVoltage, DEC); Serial.println(" mV");
Serial.print("Digital Input: ");
Serial.print(packet.bDiState[0] ? " DI1:Lo" : " DI1:Hi");
Serial.print(packet.bDiState[1] ? " DI2:Lo" : " DI2:Hi");
Serial.print(packet.bDiState[2] ? " DI3:Lo" : " DI3:Hi");
Serial.println(packet.bDiState[3] ? " DI4:Lo" : " DI4:Hi");
Serial.print("Analog Input: ");
Serial.print(" AI1:"); Serial.print(packet.u16AiVoltage[0]); Serial.print(" mV");
Serial.print(" AI2:"); Serial.print(packet.u16AiVoltage[1]); Serial.print(" mV");
Serial.print(" AI3:"); Serial.print(packet.u16AiVoltage[2]); Serial.print(" mV");
Serial.print(" AI4:"); Serial.print(packet.u16AiVoltage[3]); Serial.println(" mV");
});
}
void loop()
{
// Update TWELITE
Twelite.update();
}
ライブラリのインクルード
4行目では、MWings ライブラリをインクルードしています。
#include "MWings.h"
ピン番号の定義
6-8行目では、ピン番号を定義しています。
const int RST_PIN = 5;
const int PRG_PIN = 4;
const int LED_PIN = 18;
名称 | 内容 |
---|---|
RST_PIN | TWELITE の RST ピンが接続されているピンの番号 |
PRG_PIN | TWELITE の PRG ピンが接続されているピンの番号 |
LED_PIN | 基板上の ESP32 用 LED が接続されているピンの番号 |
TWELITE 設定の定義
10-11行目では、TWELITE SPOT に搭載された TWELITE 親機に適用する設定を定義しています。
const uint8_t TWE_CHANNEL = 18;
const uint32_t TWE_APP_ID = 0x67720102;
名称 | 内容 |
---|---|
TWE_CHANNEL | TWELITE の 周波数チャネル |
TWE_APP_ID | TWELITE の アプリケーション ID |
シリアルポートの設定
16-18行目では、使用するシリアルポートを初期化するとともに、シリアルモニタへ起動メッセージを出力しています。
Serial.begin(115200);
Serial.println("Monitor example for TWELITE SPOT: App_Twelite");
Serial2.begin(115200, SERIAL_8N1);
Serial
は、Arduino IDE の シリアルモニタとの通信に使います。シリアルモニタの設定に合わせて、ボーレートを 115200
bps としています。
一方、Serial2
は、TWELITE SPOT に搭載された TWELITE 親機との通信に使います。こちらも TWELITE 親機の初期設定に合わせて、ボーレートを 115200
bps としています。
TWELITE の設定
21-23行目では、Twelite.begin()
を呼び出し、TWELITE SPOT に搭載された TWELITE 親機の設定と起動を行っています。
Twelite.begin(Serial2,
LED_PIN, RST_PIN, PRG_PIN,
TWE_CHANNEL, TWE_APP_ID);
パケット受信時のイベントの登録
26-46行目では、Twelite.on()
を呼び出し、送られたデータに対して行う処理を登録しています。
ここでは、受信したパケットの内容をシリアルモニタに出力しています。
Twelite.on([](const ParsedAppTwelitePacket& packet) {
Serial.println("");
Serial.print("Packet Timestamp: ");
Serial.print(packet.u16SequenceNumber / 64.0f, 1); Serial.println(" sec");
Serial.print("Source Logical ID: 0x");
Serial.println(packet.u8SourceLogicalId, HEX);
Serial.print("LQI: ");
Serial.println(packet.u8Lqi, DEC);
Serial.print("Supply Voltage: ");
Serial.print(packet.u16SupplyVoltage, DEC); Serial.println(" mV");
Serial.print("Digital Input: ");
Serial.print(packet.bDiState[0] ? " DI1:Lo" : " DI1:Hi");
Serial.print(packet.bDiState[1] ? " DI2:Lo" : " DI2:Hi");
Serial.print(packet.bDiState[2] ? " DI3:Lo" : " DI3:Hi");
Serial.println(packet.bDiState[3] ? " DI4:Lo" : " DI4:Hi");
Serial.print("Analog Input: ");
Serial.print(" AI1:"); Serial.print(packet.u16AiVoltage[0]); Serial.print(" mV");
Serial.print(" AI2:"); Serial.print(packet.u16AiVoltage[1]); Serial.print(" mV");
Serial.print(" AI3:"); Serial.print(packet.u16AiVoltage[2]); Serial.print(" mV");
Serial.print(" AI4:"); Serial.print(packet.u16AiVoltage[3]); Serial.println(" mV");
});
上記のイベントは、超簡単!標準アプリからのパケットを受信したときにだけ呼び出されます。
受信したパケットの内容は ParsedAppTwelitePacket
型の引数 packet
に格納されています。
メッセージの内容
メッセージ | 内容 |
---|---|
Packet Timestamp | パケットのタイムスタンプ |
Source Logical ID | 送信元 TWELITE の論理デバイスID |
LQI | 無線通信品質(0~255) |
Supply Voltage | 電源電圧 (mV) |
Digital Input | デジタル入力の状態 |
Analog Input | アナログ入力の状態 |
TWELITE のデータの更新
52行目では、Twelite.update()
を呼び出しています。
Twelite.update();
相手先の出力ポートの操作
超簡単!標準アプリの入力ポートの状態を取得するだけでなく、超簡単!標準アプリの出力ポートを操作してみましょう。
ここでは、TWELITE SPOT が受信した際の LQI(無線通信品質)に基づき、相手端末が TWELITE SPOT に近づいた際に、相手端末のデジタル出力ポートを点灯させてみます。
スケッチの改変
改変内容
はじめに、13行目へ下記のコードを追加します。
AppTweliteCommand command;
上記のコードでは、送信するコマンドの内容を格納する AppTweliteCommand
を作成しています。
次に、49-51行目へ下記のコードを追加します。
command.u8DestinationLogicalId = packet.u8SourceLogicalId; // LID
command.bDiState[0] = (packet.u8Lqi >= 100) ? true : false; // DI1
Twelite.send(command);
上記のコードでは、AppTweliteCommand
を操作し、Twelite.send()
でコマンドを送信しています。
ここでは、送信先の論理デバイス ID を設定し、出力ポート(DO1)の状態を指定しています。
データの詳細は AppTweliteCommand
のリファレンス をご覧ください。
これでスケッチの改変は終了です。改変後のコードを示します。
// Monitor example for TWELITE SPOT: Receive data from and send data to App_Twelite
#include <Arduino.h>
#include "MWings.h"
const int RST_PIN = 5;
const int PRG_PIN = 4;
const int LED_PIN = 18;
const uint8_t TWE_CHANNEL = 18;
const uint32_t TWE_APP_ID = 0x67720102;
AppTweliteCommand command;
void setup()
{
// Initialize serial ports
Serial.begin(115200);
Serial.println("Monitor example for TWELITE SPOT: App_Twelite");
Serial2.begin(115200, SERIAL_8N1);
// Initialize TWELITE
Twelite.begin(Serial2,
LED_PIN, RST_PIN, PRG_PIN,
TWE_CHANNEL, TWE_APP_ID);
// Attach an event handler to process packets from App_Twelite
Twelite.on([](const ParsedAppTwelitePacket& packet) {
Serial.println("");
Serial.print("Packet Timestamp: ");
Serial.print(packet.u16SequenceNumber / 64.0f, 1); Serial.println(" sec");
Serial.print("Source Logical ID: 0x");
Serial.println(packet.u8SourceLogicalId, HEX);
Serial.print("LQI: ");
Serial.println(packet.u8Lqi, DEC);
Serial.print("Supply Voltage: ");
Serial.print(packet.u16SupplyVoltage, DEC); Serial.println(" mV");
Serial.print("Digital Input: ");
Serial.print(packet.bDiState[0] ? " DI1:Lo" : " DI1:Hi");
Serial.print(packet.bDiState[1] ? " DI2:Lo" : " DI2:Hi");
Serial.print(packet.bDiState[2] ? " DI3:Lo" : " DI3:Hi");
Serial.println(packet.bDiState[3] ? " DI4:Lo" : " DI4:Hi");
Serial.print("Analog Input: ");
Serial.print(" AI1:"); Serial.print(packet.u16AiVoltage[0]); Serial.print(" mV");
Serial.print(" AI2:"); Serial.print(packet.u16AiVoltage[1]); Serial.print(" mV");
Serial.print(" AI3:"); Serial.print(packet.u16AiVoltage[2]); Serial.print(" mV");
Serial.print(" AI4:"); Serial.print(packet.u16AiVoltage[3]); Serial.println(" mV");
command.u8DestinationLogicalId = packet.u8SourceLogicalId; // LID
command.bDiState[0] = (packet.u8Lqi >= 100) ? true : false; // DI1
Twelite.send(command);
});
}
void loop()
{
// Update TWELITE
Twelite.update();
}
動作確認
子機の TWELITE DIP の DO1 ピンと VCC ピンの間に LED と電流制限抵抗を接続してください。
改変したスケッチを書き込むと、TWELITE DIP が TWELITE SPOT に近づいた際(=通信品質がよいとき)に LED が点灯します。
5.1.1.3 - 超簡単!標準アプリのデータを取得・操作
monitor_spot_app_twelite
の解説です。最後に、相手先の出力ポートを操作する改変を行います。サンプルスケッチの保存場所
MWings ライブラリを導入していれば、Arduino IDE の ファイル -> スケッチ例 -> MWings -> TWELITE SPOT -> Receive -> monitor_spot_app_twelite からスケッチを開くことができます。
スケッチ
以下はソースコード本体です。
// Monitor example for TWELITE SPOT: Receive data from App_Twelite
#include <Arduino.h>
#include "MWings.h"
const int RST_PIN = 5;
const int PRG_PIN = 4;
const int LED_PIN = 18;
const uint8_t TWE_CHANNEL = 18;
const uint32_t TWE_APP_ID = 0x67720102;
void setup()
{
// Initialize serial ports
Serial.begin(115200);
Serial.println("Monitor example for TWELITE SPOT: App_Twelite");
Serial2.begin(115200, SERIAL_8N1);
// Initialize TWELITE
Twelite.begin(Serial2,
LED_PIN, RST_PIN, PRG_PIN,
TWE_CHANNEL, TWE_APP_ID);
// Attach an event handler to process packets from App_Twelite
Twelite.on([](const ParsedAppTwelitePacket& packet) {
Serial.println("");
Serial.print("Packet Timestamp: ");
Serial.print(packet.u16SequenceNumber / 64.0f, 1); Serial.println(" sec");
Serial.print("Source Logical ID: 0x");
Serial.println(packet.u8SourceLogicalId, HEX);
Serial.print("LQI: ");
Serial.println(packet.u8Lqi, DEC);
Serial.print("Supply Voltage: ");
Serial.print(packet.u16SupplyVoltage, DEC); Serial.println(" mV");
Serial.print("Digital Input: ");
Serial.print(packet.bDiState[0] ? " DI1:Lo" : " DI1:Hi");
Serial.print(packet.bDiState[1] ? " DI2:Lo" : " DI2:Hi");
Serial.print(packet.bDiState[2] ? " DI3:Lo" : " DI3:Hi");
Serial.println(packet.bDiState[3] ? " DI4:Lo" : " DI4:Hi");
Serial.print("Analog Input: ");
Serial.print(" AI1:"); Serial.print(packet.u16AiVoltage[0]); Serial.print(" mV");
Serial.print(" AI2:"); Serial.print(packet.u16AiVoltage[1]); Serial.print(" mV");
Serial.print(" AI3:"); Serial.print(packet.u16AiVoltage[2]); Serial.print(" mV");
Serial.print(" AI4:"); Serial.print(packet.u16AiVoltage[3]); Serial.println(" mV");
});
}
void loop()
{
// Update TWELITE
Twelite.update();
}
ライブラリのインクルード
4行目では、MWings ライブラリをインクルードしています。
#include "MWings.h"
ピン番号の定義
6-8行目では、ピン番号を定義しています。
const int RST_PIN = 5;
const int PRG_PIN = 4;
const int LED_PIN = 18;
名称 | 内容 |
---|---|
RST_PIN | TWELITE の RST ピンが接続されているピンの番号 |
PRG_PIN | TWELITE の PRG ピンが接続されているピンの番号 |
LED_PIN | 基板上の ESP32 用 LED が接続されているピンの番号 |
TWELITE 設定の定義
10-11行目では、TWELITE SPOT に搭載された TWELITE 親機に適用する設定を定義しています。
const uint8_t TWE_CHANNEL = 18;
const uint32_t TWE_APP_ID = 0x67720102;
名称 | 内容 |
---|---|
TWE_CHANNEL | TWELITE の 周波数チャネル |
TWE_APP_ID | TWELITE の アプリケーション ID |
シリアルポートの設定
16-18行目では、使用するシリアルポートを初期化するとともに、シリアルモニタへ起動メッセージを出力しています。
Serial.begin(115200);
Serial.println("Monitor example for TWELITE SPOT: App_Twelite");
Serial2.begin(115200, SERIAL_8N1);
Serial
は、Arduino IDE の シリアルモニタとの通信に使います。シリアルモニタの設定に合わせて、ボーレートを 115200
bps としています。
一方、Serial2
は、TWELITE SPOT に搭載された TWELITE 親機との通信に使います。こちらも TWELITE 親機の初期設定に合わせて、ボーレートを 115200
bps としています。
TWELITE の設定
21-23行目では、Twelite.begin()
を呼び出し、TWELITE SPOT に搭載された TWELITE 親機の設定と起動を行っています。
Twelite.begin(Serial2,
LED_PIN, RST_PIN, PRG_PIN,
TWE_CHANNEL, TWE_APP_ID);
パケット受信時のイベントの登録
26-46行目では、Twelite.on()
を呼び出し、送られたデータに対して行う処理を登録しています。
ここでは、受信したパケットの内容をシリアルモニタに出力しています。
Twelite.on([](const ParsedAppTwelitePacket& packet) {
Serial.println("");
Serial.print("Packet Timestamp: ");
Serial.print(packet.u16SequenceNumber / 64.0f, 1); Serial.println(" sec");
Serial.print("Source Logical ID: 0x");
Serial.println(packet.u8SourceLogicalId, HEX);
Serial.print("LQI: ");
Serial.println(packet.u8Lqi, DEC);
Serial.print("Supply Voltage: ");
Serial.print(packet.u16SupplyVoltage, DEC); Serial.println(" mV");
Serial.print("Digital Input: ");
Serial.print(packet.bDiState[0] ? " DI1:Lo" : " DI1:Hi");
Serial.print(packet.bDiState[1] ? " DI2:Lo" : " DI2:Hi");
Serial.print(packet.bDiState[2] ? " DI3:Lo" : " DI3:Hi");
Serial.println(packet.bDiState[3] ? " DI4:Lo" : " DI4:Hi");
Serial.print("Analog Input: ");
Serial.print(" AI1:"); Serial.print(packet.u16AiVoltage[0]); Serial.print(" mV");
Serial.print(" AI2:"); Serial.print(packet.u16AiVoltage[1]); Serial.print(" mV");
Serial.print(" AI3:"); Serial.print(packet.u16AiVoltage[2]); Serial.print(" mV");
Serial.print(" AI4:"); Serial.print(packet.u16AiVoltage[3]); Serial.println(" mV");
});
上記のイベントは、超簡単!標準アプリからのパケットを受信したときにだけ呼び出されます。
受信したパケットの内容は ParsedAppTwelitePacket
型の引数 packet
に格納されています。
メッセージの内容
メッセージ | 内容 |
---|---|
Packet Timestamp | パケットのタイムスタンプ |
Source Logical ID | 送信元 TWELITE の論理デバイスID |
LQI | 無線通信品質(0~255) |
Supply Voltage | 電源電圧 (mV) |
Digital Input | デジタル入力の状態 |
Analog Input | アナログ入力の状態 |
TWELITE のデータの更新
52行目では、Twelite.update()
を呼び出しています。
Twelite.update();
相手先の出力ポートの操作
超簡単!標準アプリの入力ポートの状態を取得するだけでなく、超簡単!標準アプリの出力ポートを操作してみましょう。
ここでは、TWELITE SPOT が受信した際の LQI(無線通信品質)に基づき、相手端末が TWELITE SPOT に近づいた際に、相手端末のデジタル出力ポートを点灯させてみます。
スケッチの改変
改変内容
はじめに、13行目へ下記のコードを追加します。
AppTweliteCommand command;
上記のコードでは、送信するコマンドの内容を格納する AppTweliteCommand
を作成しています。
次に、49-51行目へ下記のコードを追加します。
command.u8DestinationLogicalId = packet.u8SourceLogicalId; // LID
command.bDiState[0] = (packet.u8Lqi >= 100) ? true : false; // DI1
Twelite.send(command);
上記のコードでは、AppTweliteCommand
を操作し、Twelite.send()
でコマンドを送信しています。
ここでは、送信先の論理デバイス ID を設定し、出力ポート(DO1)の状態を指定しています。
データの詳細は AppTweliteCommand
のリファレンス をご覧ください。
これでスケッチの改変は終了です。改変後のコードを示します。
// Monitor example for TWELITE SPOT: Receive data from and send data to App_Twelite
#include <Arduino.h>
#include "MWings.h"
const int RST_PIN = 5;
const int PRG_PIN = 4;
const int LED_PIN = 18;
const uint8_t TWE_CHANNEL = 18;
const uint32_t TWE_APP_ID = 0x67720102;
AppTweliteCommand command;
void setup()
{
// Initialize serial ports
Serial.begin(115200);
Serial.println("Monitor example for TWELITE SPOT: App_Twelite");
Serial2.begin(115200, SERIAL_8N1);
// Initialize TWELITE
Twelite.begin(Serial2,
LED_PIN, RST_PIN, PRG_PIN,
TWE_CHANNEL, TWE_APP_ID);
// Attach an event handler to process packets from App_Twelite
Twelite.on([](const ParsedAppTwelitePacket& packet) {
Serial.println("");
Serial.print("Packet Timestamp: ");
Serial.print(packet.u16SequenceNumber / 64.0f, 1); Serial.println(" sec");
Serial.print("Source Logical ID: 0x");
Serial.println(packet.u8SourceLogicalId, HEX);
Serial.print("LQI: ");
Serial.println(packet.u8Lqi, DEC);
Serial.print("Supply Voltage: ");
Serial.print(packet.u16SupplyVoltage, DEC); Serial.println(" mV");
Serial.print("Digital Input: ");
Serial.print(packet.bDiState[0] ? " DI1:Lo" : " DI1:Hi");
Serial.print(packet.bDiState[1] ? " DI2:Lo" : " DI2:Hi");
Serial.print(packet.bDiState[2] ? " DI3:Lo" : " DI3:Hi");
Serial.println(packet.bDiState[3] ? " DI4:Lo" : " DI4:Hi");
Serial.print("Analog Input: ");
Serial.print(" AI1:"); Serial.print(packet.u16AiVoltage[0]); Serial.print(" mV");
Serial.print(" AI2:"); Serial.print(packet.u16AiVoltage[1]); Serial.print(" mV");
Serial.print(" AI3:"); Serial.print(packet.u16AiVoltage[2]); Serial.print(" mV");
Serial.print(" AI4:"); Serial.print(packet.u16AiVoltage[3]); Serial.println(" mV");
command.u8DestinationLogicalId = packet.u8SourceLogicalId; // LID
command.bDiState[0] = (packet.u8Lqi >= 100) ? true : false; // DI1
Twelite.send(command);
});
}
void loop()
{
// Update TWELITE
Twelite.update();
}
動作確認
子機の TWELITE DIP の DO1 ピンと VCC ピンの間に LED と電流制限抵抗を接続してください。
改変したスケッチを書き込むと、TWELITE DIP が TWELITE SPOT に近づいた際(=通信品質がよいとき)に LED が点灯します。
5.1.2 - キューアプリのデータを取得
monitor_spot_app_cue
の解説monitor_spot_app_cue
の解説です。5.1.2.1 - キューアプリのデータを取得
monitor_spot_app_cue
の解説です。サンプルスケッチの保存場所
MWings ライブラリを導入していれば、Arduino IDE の ファイル -> スケッチ例 -> MWings -> TWELITE SPOT -> Receive -> monitor_spot_app_cue からスケッチを開くことができます。
スケッチ
以下はソースコード本体です。
// Monitor example for TWELITE SPOT: Receive data from App_CUE (CUE Mode)
#include <Arduino.h>
#include "MWings.h"
const int RST_PIN = 5;
const int PRG_PIN = 4;
const int LED_PIN = 18;
const int8_t RX1_PIN = 16;
const int8_t TX1_PIN = 17;
const uint8_t TWE_CHANNEL = 18;
const uint32_t TWE_APP_ID = 0x67720102;
void printAccelEvent(const uint8_t event);
void printMagnetState(const uint8_t state, const bool changed);
void setup()
{
// Initialize serial ports
Serial.begin(115200);
Serial.println("Monitor example for TWELITE SPOT: App_CUE (CUE Mode)");
Serial2.begin(115200, SERIAL_8N1, RX1_PIN, TX1_PIN);
// Initialize TWELITE
Twelite.begin(Serial2,
LED_PIN, RST_PIN, PRG_PIN,
TWE_CHANNEL, TWE_APP_ID);
// Attach an event handler to process packets from App_CUE
Twelite.on([](const ParsedAppCuePacket& packet) {
Serial.println("");
Serial.print("Packet Number: #");
Serial.println(packet.u16SequenceNumber, DEC);
Serial.print("Source Logical ID: 0x");
Serial.println(packet.u8SourceLogicalId, HEX);
Serial.print("LQI: ");
Serial.println(packet.u8Lqi, DEC);
Serial.print("Supply Voltage: ");
Serial.print(packet.u16SupplyVoltage, DEC); Serial.println(" mV");
Serial.print("Accel Event: ");
printAccelEvent(packet.u8AccelEvent);
Serial.print("Accel X Axis [0]: ");
Serial.print(packet.i16SamplesX[0], DEC); Serial.println(" mG");
Serial.print("Accel Y Axis [0]: ");
Serial.print(packet.i16SamplesY[0], DEC); Serial.println(" mG");
Serial.print("Accel Z Axis [0]: ");
Serial.print(packet.i16SamplesZ[0], DEC); Serial.println(" mG");
Serial.print("Magnet State: ");
printMagnetState(packet.u8MagnetState, packet.bMagnetStateChanged);
});
}
void loop()
{
// Update TWELITE
Twelite.update();
}
void printAccelEvent(const uint8_t event)
{
switch (event) {
case 0x01: { Serial.print("Dice (1)"); break; }
case 0x02: { Serial.print("Dice (2)"); break; }
case 0x03: { Serial.print("Dice (3)"); break; }
case 0x04: { Serial.print("Dice (4)"); break; }
case 0x05: { Serial.print("Dice (5)"); break; }
case 0x06: { Serial.print("Dice (6)"); break; }
case 0x08: { Serial.print("Shake"); break; }
case 0x10: { Serial.print("Move"); break; }
default: break;
}
Serial.println("");
}
void printMagnetState(const uint8_t state, const bool changed)
{
if (changed) {
switch (state) {
case 0x0: { Serial.print("Leaving or Not found"); break; }
case 0x1: { Serial.print("N-pole is getting closer"); break; }
case 0x2: { Serial.print("S-pole is getting closer"); break; }
default: break;
}
} else {
switch (state) {
case 0x0: { Serial.print("Not found"); break; }
case 0x1: { Serial.print("N-pole is close"); break; }
case 0x2: { Serial.print("S-pole is close"); break; }
default: break;
}
Serial.print(" (Periodic packet)");
}
Serial.println("");
}
ライブラリのインクルード
4行目では、MWings ライブラリをインクルードしています。
#include "MWings.h"
ピン番号の定義
6-11行目では、ピン番号を定義しています。
const int RST_PIN = 5;
const int PRG_PIN = 4;
const int LED_PIN = 18;
const int8_t RX1_PIN = 16;
const int8_t TX1_PIN = 17;
名称 | 内容 |
---|---|
RST_PIN | TWELITE の RST ピンが接続されているピンの番号 |
PRG_PIN | TWELITE の PRG ピンが接続されているピンの番号 |
LED_PIN | 基板上の ESP32 用 LED が接続されているピンの番号 |
RX1_PIN | TWELITE の RX1 ピンが接続されているピンの番号 |
TX1_PIN | TWELITE の TX1 ピンが接続されているピンの番号 |
TWELITE 設定の定義
13-14行目では、TWELITE SPOT に搭載された TWELITE 親機に適用する設定を定義しています。
const uint8_t TWE_CHANNEL = 18;
const uint32_t TWE_APP_ID = 0x67720102;
名称 | 内容 |
---|---|
TWE_CHANNEL | TWELITE の 周波数チャネル |
TWE_APP_ID | TWELITE の アプリケーション ID |
シリアルポートの設定
22-24行目では、使用するシリアルポートを初期化するとともに、シリアルモニタへ起動メッセージを出力しています。
Serial.begin(115200);
Serial.println("Monitor example for TWELITE SPOT: App_CUE (CUE Mode)");
Serial2.begin(115200);
Serial
は、Arduino IDE の シリアルモニタとの通信に使います。シリアルモニタの設定に合わせて、ボーレートを 115200
bps としています。
一方、Serial2
は、TWELITE SPOT に搭載された TWELITE 親機との通信に使います。こちらも TWELITE 親機の初期設定に合わせて、ボーレートを 115200
bps としています。
TWELITE の設定
27-29行目では、Twelite.begin()
を呼び出し、TWELITE SPOT に搭載された TWELITE 親機の設定と起動を行っています。
Twelite.begin(Serial2,
LED_PIN, RST_PIN, PRG_PIN,
TWE_CHANNEL, TWE_APP_ID);
パケット受信時のイベントの登録
32-52行目では、Twelite.on()
を呼び出し、送られたデータに対して行う処理を登録しています。
ここでは、受信したパケットの内容をシリアルモニタに出力しています。
Twelite.on([](const ParsedAppCuePacket& packet) {
Serial.println("");
Serial.print("Packet Number: #");
Serial.println(packet.u16SequenceNumber, DEC);
Serial.print("Source Logical ID: 0x");
Serial.println(packet.u8SourceLogicalId, HEX);
Serial.print("LQI: ");
Serial.println(packet.u8Lqi, DEC);
Serial.print("Supply Voltage: ");
Serial.print(packet.u16SupplyVoltage, DEC); Serial.println(" mV");
Serial.print("Accel Event: ");
printAccelEvent(packet.u8AccelEvent);
Serial.print("Accel X Axis [0]: ");
Serial.print(packet.i16SamplesX[0], DEC); Serial.println(" mG");
Serial.print("Accel Y Axis [0]: ");
Serial.print(packet.i16SamplesY[0], DEC); Serial.println(" mG");
Serial.print("Accel Z Axis [0]: ");
Serial.print(packet.i16SamplesZ[0], DEC); Serial.println(" mG");
Serial.print("Magnet State: ");
printMagnetState(packet.u8MagnetState, packet.bMagnetStateChanged);
});
上記のイベントは、キューアプリ(TWELITE CUE モード)からのパケットを受信したときにだけ呼び出されます。
受信したパケットの内容は ParsedAppCuePacket
型の引数 packet
に格納されています。
メッセージの内容
メッセージ | 内容 |
---|---|
Packet Number | パケットの続き番号 |
Source Logical ID | 送信元 TWELITE の論理デバイスID |
LQI | 無線通信品質(0~255) |
Supply Voltage | 電源電圧 (mV) |
Accel Event | 加速度センサーの状態 |
Accel X Axis | X 軸加速度(1サンプル目) |
Accel Y Axis | Y 軸加速度(1サンプル目) |
Accel Z Axis | Z 軸加速度(1サンプル目) |
Magnet State | 磁気センサーの状態 |
加速度センサーの状態
出力される加速度センサーの状態は以下の通りです。
Dice (1)
-Dice (6)
サイコロの目(姿勢)を検知した。Shake
揺さぶるような動きを検知した。Move
ゆっくりとした動きを検知した。
磁気センサーの状態
出力される磁気センサーの状態は以下の通りです。
S-pole is getting closer
新たに磁石のS極を検知した。N-pole is getting closer
新たに磁石のN極を検知した。Leaving or Not found
磁石が検知できなかった。S-pole is close (Periodic packet)
磁石のS極を検知している。N-pole is close (Periodic packet)
磁石のN極を検知している。Not found (Periodic packet)
磁石を連続で検知できていない(定期送信パケット)。
TWELITE のデータの更新
58行目では、Twelite.update()
を呼び出しています。
Twelite.update();
5.1.2.2 - キューアプリのデータを取得
monitor_spot_app_cue
の解説です。サンプルスケッチの保存場所
MWings ライブラリを導入していれば、Arduino IDE の ファイル -> スケッチ例 -> MWings -> monitor_spot_app_cue からスケッチを開くことができます。
スケッチ
以下はソースコード本体です。
// Monitor example for TWELITE SPOT: Receive data from App_CUE (CUE Mode)
#include <Arduino.h>
#include "MWings.h"
const int RST_PIN = 5;
const int PRG_PIN = 4;
const int LED_PIN = 18;
const uint8_t TWE_CHANNEL = 18;
const uint32_t TWE_APP_ID = 0x67720102;
void printAccelEvent(const uint8_t event);
void printMagnetState(const uint8_t state, const bool changed);
void setup()
{
// Initialize serial ports
Serial.begin(115200);
Serial.println("Monitor example for TWELITE SPOT: App_CUE (CUE Mode)");
Serial2.begin(115200);
// Initialize TWELITE
Twelite.begin(Serial2,
LED_PIN, RST_PIN, PRG_PIN,
TWE_CHANNEL, TWE_APP_ID);
// Attach an event handler to process packets from App_CUE
Twelite.on([](const ParsedAppCuePacket& packet) {
Serial.println("");
Serial.print("Packet Number: #");
Serial.println(packet.u16SequenceNumber, DEC);
Serial.print("Source Logical ID: 0x");
Serial.println(packet.u8SourceLogicalId, HEX);
Serial.print("LQI: ");
Serial.println(packet.u8Lqi, DEC);
Serial.print("Supply Voltage: ");
Serial.print(packet.u16SupplyVoltage, DEC); Serial.println(" mV");
Serial.print("Accel Event: ");
printAccelEvent(packet.u8AccelEvent);
Serial.print("Accel X Axis [0]: ");
Serial.print(packet.i16SamplesX[0], DEC); Serial.println(" mG");
Serial.print("Accel Y Axis [0]: ");
Serial.print(packet.i16SamplesY[0], DEC); Serial.println(" mG");
Serial.print("Accel Z Axis [0]: ");
Serial.print(packet.i16SamplesZ[0], DEC); Serial.println(" mG");
Serial.print("Magnet State: ");
printMagnetState(packet.u8MagnetState, packet.bMagnetStateChanged);
});
}
void loop()
{
// Update TWELITE
Twelite.update();
}
void printAccelEvent(const uint8_t event)
{
switch (event) {
case 0x01: { Serial.print("Dice (1)"); break; }
case 0x02: { Serial.print("Dice (2)"); break; }
case 0x03: { Serial.print("Dice (3)"); break; }
case 0x04: { Serial.print("Dice (4)"); break; }
case 0x05: { Serial.print("Dice (5)"); break; }
case 0x06: { Serial.print("Dice (6)"); break; }
case 0x08: { Serial.print("Shake"); break; }
case 0x10: { Serial.print("Move"); break; }
default: break;
}
Serial.println("");
}
void printMagnetState(const uint8_t state, const bool changed)
{
if (changed) {
switch (state) {
case 0x0: { Serial.print("Leaving or Not found"); break; }
case 0x1: { Serial.print("N-pole is getting closer"); break; }
case 0x2: { Serial.print("S-pole is getting closer"); break; }
default: break;
}
} else {
switch (state) {
case 0x0: { Serial.print("Not found"); break; }
case 0x1: { Serial.print("N-pole is close"); break; }
case 0x2: { Serial.print("S-pole is close"); break; }
default: break;
}
Serial.print(" (Periodic packet)");
}
Serial.println("");
}
ライブラリのインクルード
4行目では、MWings ライブラリをインクルードしています。
#include "MWings.h"
ピン番号の定義
6-8行目では、ピン番号を定義しています。
const int RST_PIN = 5;
const int PRG_PIN = 4;
const int LED_PIN = 18;
名称 | 内容 |
---|---|
RST_PIN | TWELITE の RST ピンが接続されているピンの番号 |
PRG_PIN | TWELITE の PRG ピンが接続されているピンの番号 |
LED_PIN | 基板上の ESP32 用 LED が接続されているピンの番号 |
TWELITE 設定の定義
10-11行目では、TWELITE SPOT に搭載された TWELITE 親機に適用する設定を定義しています。
const uint8_t TWE_CHANNEL = 18;
const uint32_t TWE_APP_ID = 0x67720102;
名称 | 内容 |
---|---|
TWE_CHANNEL | TWELITE の 周波数チャネル |
TWE_APP_ID | TWELITE の アプリケーション ID |
シリアルポートの設定
19-21行目では、使用するシリアルポートを初期化するとともに、シリアルモニタへ起動メッセージを出力しています。
Serial.begin(115200);
Serial.println("Monitor example for TWELITE SPOT: App_CUE (CUE Mode)");
Serial2.begin(115200);
Serial
は、Arduino IDE の シリアルモニタとの通信に使います。シリアルモニタの設定に合わせて、ボーレートを 115200
bps としています。
一方、Serial2
は、TWELITE SPOT に搭載された TWELITE 親機との通信に使います。こちらも TWELITE 親機の初期設定に合わせて、ボーレートを 115200
bps としています。
TWELITE の設定
24-26行目では、Twelite.begin()
を呼び出し、TWELITE SPOT に搭載された TWELITE 親機の設定と起動を行っています。
Twelite.begin(Serial2,
LED_PIN, RST_PIN, PRG_PIN,
TWE_CHANNEL, TWE_APP_ID);
パケット受信時のイベントの登録
29-49行目では、Twelite.on()
を呼び出し、送られたデータに対して行う処理を登録しています。
ここでは、受信したパケットの内容をシリアルモニタに出力しています。
Twelite.on([](const ParsedAppCuePacket& packet) {
Serial.println("");
Serial.print("Packet Number: #");
Serial.println(packet.u16SequenceNumber, DEC);
Serial.print("Source Logical ID: 0x");
Serial.println(packet.u8SourceLogicalId, HEX);
Serial.print("LQI: ");
Serial.println(packet.u8Lqi, DEC);
Serial.print("Supply Voltage: ");
Serial.print(packet.u16SupplyVoltage, DEC); Serial.println(" mV");
Serial.print("Accel Event: ");
printAccelEvent(packet.u8AccelEvent);
Serial.print("Accel X Axis [0]: ");
Serial.print(packet.i16SamplesX[0], DEC); Serial.println(" mG");
Serial.print("Accel Y Axis [0]: ");
Serial.print(packet.i16SamplesY[0], DEC); Serial.println(" mG");
Serial.print("Accel Z Axis [0]: ");
Serial.print(packet.i16SamplesZ[0], DEC); Serial.println(" mG");
Serial.print("Magnet State: ");
printMagnetState(packet.u8MagnetState, packet.bMagnetStateChanged);
});
上記のイベントは、キューアプリ(TWELITE CUE モード)からのパケットを受信したときにだけ呼び出されます。
受信したパケットの内容は ParsedAppCuePacket
型の引数 packet
に格納されています。
メッセージの内容
メッセージ | 内容 |
---|---|
Packet Number | パケットの続き番号 |
Source Logical ID | 送信元 TWELITE の論理デバイスID |
LQI | 無線通信品質(0~255) |
Supply Voltage | 電源電圧 (mV) |
Accel Event | 加速度センサーの状態 |
Accel X Axis | X 軸加速度(1サンプル目) |
Accel Y Axis | Y 軸加速度(1サンプル目) |
Accel Z Axis | Z 軸加速度(1サンプル目) |
Magnet State | 磁気センサーの状態 |
加速度センサーの状態
出力される加速度センサーの状態は以下の通りです。
Dice (1)
-Dice (6)
サイコロの目(姿勢)を検知した。Shake
揺さぶるような動きを検知した。Move
ゆっくりとした動きを検知した。
磁気センサーの状態
出力される磁気センサーの状態は以下の通りです。
S-pole is getting closer
新たに磁石のS極を検知した。N-pole is getting closer
新たに磁石のN極を検知した。Leaving or Not found
磁石が検知できなかった。S-pole is close (Periodic packet)
磁石のS極を検知している。N-pole is close (Periodic packet)
磁石のN極を検知している。Not found (Periodic packet)
磁石を連続で検知できていない(定期送信パケット)。
TWELITE のデータの更新
55行目では、Twelite.update()
を呼び出しています。
Twelite.update();
5.1.2.3 - キューアプリのデータを取得
monitor_spot_app_cue
の解説です。サンプルスケッチの保存場所
MWings ライブラリを導入していれば、Arduino IDE の ファイル -> スケッチ例 -> MWings -> TWELITE SPOT -> Receive -> monitor_spot_app_cue からスケッチを開くことができます。
スケッチ
以下はソースコード本体です。
// Monitor example for TWELITE SPOT: Receive data from App_CUE (CUE Mode)
#include <Arduino.h>
#include "MWings.h"
const int RST_PIN = 5;
const int PRG_PIN = 4;
const int LED_PIN = 18;
const uint8_t TWE_CHANNEL = 18;
const uint32_t TWE_APP_ID = 0x67720102;
void printAccelEvent(const uint8_t event);
void printMagnetState(const uint8_t state, const bool changed);
void setup()
{
// Initialize serial ports
Serial.begin(115200);
Serial.println("Monitor example for TWELITE SPOT: App_CUE (CUE Mode)");
Serial2.begin(115200);
// Initialize TWELITE
Twelite.begin(Serial2,
LED_PIN, RST_PIN, PRG_PIN,
TWE_CHANNEL, TWE_APP_ID);
// Attach an event handler to process packets from App_CUE
Twelite.on([](const ParsedAppCuePacket& packet) {
Serial.println("");
Serial.print("Packet Number: #");
Serial.println(packet.u16SequenceNumber, DEC);
Serial.print("Source Logical ID: 0x");
Serial.println(packet.u8SourceLogicalId, HEX);
Serial.print("LQI: ");
Serial.println(packet.u8Lqi, DEC);
Serial.print("Supply Voltage: ");
Serial.print(packet.u16SupplyVoltage, DEC); Serial.println(" mV");
Serial.print("Accel Event: ");
printAccelEvent(packet.u8AccelEvent);
Serial.print("Accel X Axis [0]: ");
Serial.print(packet.i16SamplesX[0], DEC); Serial.println(" mG");
Serial.print("Accel Y Axis [0]: ");
Serial.print(packet.i16SamplesY[0], DEC); Serial.println(" mG");
Serial.print("Accel Z Axis [0]: ");
Serial.print(packet.i16SamplesZ[0], DEC); Serial.println(" mG");
Serial.print("Magnet State: ");
printMagnetState(packet.u8MagnetState, packet.bMagnetStateChanged);
});
}
void loop()
{
// Update TWELITE
Twelite.update();
}
void printAccelEvent(const uint8_t event)
{
switch (event) {
case 0x01: { Serial.print("Dice (1)"); break; }
case 0x02: { Serial.print("Dice (2)"); break; }
case 0x03: { Serial.print("Dice (3)"); break; }
case 0x04: { Serial.print("Dice (4)"); break; }
case 0x05: { Serial.print("Dice (5)"); break; }
case 0x06: { Serial.print("Dice (6)"); break; }
case 0x08: { Serial.print("Shake"); break; }
case 0x10: { Serial.print("Move"); break; }
default: break;
}
Serial.println("");
}
void printMagnetState(const uint8_t state, const bool changed)
{
if (changed) {
switch (state) {
case 0x0: { Serial.print("Leaving or Not found"); break; }
case 0x1: { Serial.print("N-pole is getting closer"); break; }
case 0x2: { Serial.print("S-pole is getting closer"); break; }
default: break;
}
} else {
switch (state) {
case 0x0: { Serial.print("Not found"); break; }
case 0x1: { Serial.print("N-pole is close"); break; }
case 0x2: { Serial.print("S-pole is close"); break; }
default: break;
}
Serial.print(" (Periodic packet)");
}
Serial.println("");
}
ライブラリのインクルード
4行目では、MWings ライブラリをインクルードしています。
#include "MWings.h"
ピン番号の定義
6-8行目では、ピン番号を定義しています。
const int RST_PIN = 5;
const int PRG_PIN = 4;
const int LED_PIN = 18;
名称 | 内容 |
---|---|
RST_PIN | TWELITE の RST ピンが接続されているピンの番号 |
PRG_PIN | TWELITE の PRG ピンが接続されているピンの番号 |
LED_PIN | 基板上の ESP32 用 LED が接続されているピンの番号 |
TWELITE 設定の定義
10-11行目では、TWELITE SPOT に搭載された TWELITE 親機に適用する設定を定義しています。
const uint8_t TWE_CHANNEL = 18;
const uint32_t TWE_APP_ID = 0x67720102;
名称 | 内容 |
---|---|
TWE_CHANNEL | TWELITE の 周波数チャネル |
TWE_APP_ID | TWELITE の アプリケーション ID |
シリアルポートの設定
19-21行目では、使用するシリアルポートを初期化するとともに、シリアルモニタへ起動メッセージを出力しています。
Serial.begin(115200);
Serial.println("Monitor example for TWELITE SPOT: App_CUE (CUE Mode)");
Serial2.begin(115200);
Serial
は、Arduino IDE の シリアルモニタとの通信に使います。シリアルモニタの設定に合わせて、ボーレートを 115200
bps としています。
一方、Serial2
は、TWELITE SPOT に搭載された TWELITE 親機との通信に使います。こちらも TWELITE 親機の初期設定に合わせて、ボーレートを 115200
bps としています。
TWELITE の設定
24-26行目では、Twelite.begin()
を呼び出し、TWELITE SPOT に搭載された TWELITE 親機の設定と起動を行っています。
Twelite.begin(Serial2,
LED_PIN, RST_PIN, PRG_PIN,
TWE_CHANNEL, TWE_APP_ID);
パケット受信時のイベントの登録
29-49行目では、Twelite.on()
を呼び出し、送られたデータに対して行う処理を登録しています。
ここでは、受信したパケットの内容をシリアルモニタに出力しています。
Twelite.on([](const ParsedAppCuePacket& packet) {
Serial.println("");
Serial.print("Packet Number: #");
Serial.println(packet.u16SequenceNumber, DEC);
Serial.print("Source Logical ID: 0x");
Serial.println(packet.u8SourceLogicalId, HEX);
Serial.print("LQI: ");
Serial.println(packet.u8Lqi, DEC);
Serial.print("Supply Voltage: ");
Serial.print(packet.u16SupplyVoltage, DEC); Serial.println(" mV");
Serial.print("Accel Event: ");
printAccelEvent(packet.u8AccelEvent);
Serial.print("Accel X Axis [0]: ");
Serial.print(packet.i16SamplesX[0], DEC); Serial.println(" mG");
Serial.print("Accel Y Axis [0]: ");
Serial.print(packet.i16SamplesY[0], DEC); Serial.println(" mG");
Serial.print("Accel Z Axis [0]: ");
Serial.print(packet.i16SamplesZ[0], DEC); Serial.println(" mG");
Serial.print("Magnet State: ");
printMagnetState(packet.u8MagnetState, packet.bMagnetStateChanged);
});
上記のイベントは、キューアプリ(TWELITE CUE モード)からのパケットを受信したときにだけ呼び出されます。
受信したパケットの内容は ParsedAppCuePacket
型の引数 packet
に格納されています。
メッセージの内容
メッセージ | 内容 |
---|---|
Packet Number | パケットの続き番号 |
Source Logical ID | 送信元 TWELITE の論理デバイスID |
LQI | 無線通信品質(0~255) |
Supply Voltage | 電源電圧 (mV) |
Accel Event | 加速度センサーの状態 |
Accel X Axis | X 軸加速度(1サンプル目) |
Accel Y Axis | Y 軸加速度(1サンプル目) |
Accel Z Axis | Z 軸加速度(1サンプル目) |
Magnet State | 磁気センサーの状態 |
加速度センサーの状態
出力される加速度センサーの状態は以下の通りです。
Dice (1)
-Dice (6)
サイコロの目(姿勢)を検知した。Shake
揺さぶるような動きを検知した。Move
ゆっくりとした動きを検知した。
磁気センサーの状態
出力される磁気センサーの状態は以下の通りです。
S-pole is getting closer
新たに磁石のS極を検知した。N-pole is getting closer
新たに磁石のN極を検知した。Leaving or Not found
磁石が検知できなかった。S-pole is close (Periodic packet)
磁石のS極を検知している。N-pole is close (Periodic packet)
磁石のN極を検知している。Not found (Periodic packet)
磁石を連続で検知できていない(定期送信パケット)。
TWELITE のデータの更新
55行目では、Twelite.update()
を呼び出しています。
Twelite.update();
5.1.3 - アリアアプリのデータを取得
monitor_spot_app_aria
の解説monitor_spot_app_aria
の解説です。5.1.3.1 - アリアアプリのデータを取得
monitor_spot_app_aria
の解説です。サンプルスケッチの保存場所
MWings ライブラリを導入していれば、Arduino IDE の ファイル -> スケッチ例 -> MWings -> TWELITE SPOT -> Receive -> monitor_spot_app_aria からスケッチを開くことができます。
スケッチ
以下はソースコード本体です。
// Monitor example for TWELITE SPOT: Receive data from App_ARIA (ARIA Mode)
#include <Arduino.h>
#include "MWings.h"
const int RST_PIN = 5;
const int PRG_PIN = 4;
const int LED_PIN = 18;
const int8_t RX1_PIN = 16;
const int8_t TX1_PIN = 17;
const uint8_t TWE_CHANNEL = 18;
const uint32_t TWE_APP_ID = 0x67720102;
void printMagnetState(const uint8_t state, const bool changed);
void setup()
{
// Initialize serial ports
Serial.begin(115200);
Serial.println("Monitor example for TWELITE SPOT: App_ARIA (ARIA Mode)");
Serial2.begin(115200, SERIAL_8N1, RX1_PIN, TX1_PIN);
// Initialize TWELITE
Twelite.begin(Serial2,
LED_PIN, RST_PIN, PRG_PIN,
TWE_CHANNEL, TWE_APP_ID);
// Attach an event handler to process packets from App_ARIA
Twelite.on([](const ParsedAppAriaPacket& packet) {
Serial.println("");
Serial.print("Packet Number: #");
Serial.println(packet.u16SequenceNumber, DEC);
Serial.print("Source Logical ID: 0x");
Serial.println(packet.u8SourceLogicalId, HEX);
Serial.print("LQI: ");
Serial.println(packet.u8Lqi, DEC);
Serial.print("Supply Voltage: ");
Serial.print(packet.u16SupplyVoltage, DEC); Serial.println(" mV");
Serial.print("Air Temperature: ");
Serial.print(packet.i16Temp100x / 100.0f, 2); Serial.println(" C");
Serial.print("Relative Humidity: ");
Serial.print(packet.u16Humid100x / 100.0f, 2); Serial.println(" %");
Serial.print("Magnet State: ");
printMagnetState(packet.u8MagnetState, packet.bMagnetStateChanged);
});
}
void loop()
{
// Update TWELITE
Twelite.update();
}
void printMagnetState(const uint8_t state, const bool changed)
{
if (changed) {
switch (state) {
case 0x0: { Serial.print("Leaving or not found"); break; }
case 0x1: { Serial.print("N-pole is getting closer"); break; }
case 0x2: { Serial.print("S-pole is getting closer"); break; }
default: break;
}
} else {
switch (state) {
case 0x0: { Serial.print("Not found"); break; }
case 0x1: { Serial.print("N-pole is close"); break; }
case 0x2: { Serial.print("S-pole is close"); break; }
default: break;
}
Serial.print(" (Periodic packet)");
}
Serial.println("");
}
ライブラリのインクルード
4行目では、MWings ライブラリをインクルードしています。
#include "MWings.h"
ピン番号の定義
6-11行目では、ピン番号を定義しています。
const int RST_PIN = 5;
const int PRG_PIN = 4;
const int LED_PIN = 18;
const int8_t RX1_PIN = 16;
const int8_t TX1_PIN = 17;
名称 | 内容 |
---|---|
RST_PIN | TWELITE の RST ピンが接続されているピンの番号 |
PRG_PIN | TWELITE の PRG ピンが接続されているピンの番号 |
LED_PIN | 基板上の ESP32 用 LED が接続されているピンの番号 |
RX1_PIN | TWELITE の RX1 ピンが接続されているピンの番号 |
TX1_PIN | TWELITE の TX1 ピンが接続されているピンの番号 |
TWELITE 設定の定義
13-14行目では、TWELITE SPOT に搭載された TWELITE 親機に適用する設定を定義しています。
const uint8_t TWE_CHANNEL = 18;
const uint32_t TWE_APP_ID = 0x67720102;
名称 | 内容 |
---|---|
TWE_CHANNEL | TWELITE の 周波数チャネル |
TWE_APP_ID | TWELITE の アプリケーション ID |
シリアルポートの設定
21-23行目では、使用するシリアルポートを初期化するとともに、シリアルモニタへ起動メッセージを出力しています。
Serial.begin(115200);
Serial.println("Monitor example for TWELITE SPOT: App_ARIA (ARIA Mode)");
Serial2.begin(115200, SERIAL_8N1, RX1_PIN, TX1_PIN);
Serial
は、Arduino IDE の シリアルモニタとの通信に使います。シリアルモニタの設定に合わせて、ボーレートを 115200
bps としています。
一方、Serial2
は、TWELITE SPOT に搭載された TWELITE 親機との通信に使います。こちらも TWELITE 親機の初期設定に合わせて、ボーレートを 115200
bps としています。
TWELITE の設定
26-28行目では、Twelite.begin()
を呼び出し、TWELITE SPOT に搭載された TWELITE 親機の設定と起動を行っています。
Twelite.begin(Serial2,
LED_PIN, RST_PIN, PRG_PIN,
TWE_CHANNEL, TWE_APP_ID);
パケット受信時のイベントの登録
31-47行目では、Twelite.on()
を呼び出し、送られたデータに対して行う処理を登録しています。
ここでは、受信したパケットの内容をシリアルモニタに出力しています。
Twelite.on([](const ParsedAppAriaPacket& packet) {
Serial.println("");
Serial.print("Packet Number: #");
Serial.println(packet.u16SequenceNumber, DEC);
Serial.print("Source Logical ID: 0x");
Serial.println(packet.u8SourceLogicalId, HEX);
Serial.print("LQI: ");
Serial.println(packet.u8Lqi, DEC);
Serial.print("Supply Voltage: ");
Serial.print(packet.u16SupplyVoltage, DEC); Serial.println(" mV");
Serial.print("Air Temperature: ");
Serial.print(packet.i16Temp100x / 100.0f, 2); Serial.println(" C");
Serial.print("Relative Humidity: ");
Serial.print(packet.u16Humid100x / 100.0f, 2); Serial.println(" %");
Serial.print("Magnet State: ");
printMagnetState(packet.u8MagnetState, packet.bMagnetStateChanged);
});
上記のイベントは、アリアアプリ(TWELITE ARIA モード)からのパケットを受信したときにだけ呼び出されます。
受信したパケットの内容は ParsedAppAriaPacket
型の引数 packet
に格納されています。
メッセージの内容
メッセージ | 内容 |
---|---|
Packet Number | パケットの続き番号 |
Source Logical ID | 送信元 TWELITE の論理デバイスID |
LQI | 無線通信品質(0~255) |
Supply Voltage | 電源電圧 (mV) |
Air Temperature | TWELITE ARIA が計測した気温 (°C) |
Relative Humidity | TWELITE ARIA が計測した相対湿度 (%) |
Magnet State | 磁気センサーの状態 |
磁気センサーの状態
出力される磁気センサーの状態は以下の通りです。
S-pole is getting closer
新たに磁石のS極を検知した。N-pole is getting closer
新たに磁石のN極を検知した。Leaving or Not found
磁石が検知できなかった。S-pole is close (Periodic packet)
磁石のS極を検知している。N-pole is close (Periodic packet)
磁石のN極を検知している。Not found (Periodic packet)
磁石を連続で検知できていない(定期送信パケット)。
TWELITE のデータの更新
53行目では、Twelite.update()
を呼び出しています。
Twelite.update();
5.1.3.2 - アリアアプリのデータを取得
monitor_spot_app_aria
の解説です。サンプルスケッチの保存場所
MWings ライブラリを導入していれば、Arduino IDE の ファイル -> スケッチ例 -> MWings -> monitor_spot_app_aria からスケッチを開くことができます。
スケッチ
以下はソースコード本体です。
// Monitor example for TWELITE SPOT: Receive data from App_ARIA (ARIA Mode)
#include <Arduino.h>
#include "MWings.h"
const int RST_PIN = 5;
const int PRG_PIN = 4;
const int LED_PIN = 18;
const uint8_t TWE_CHANNEL = 18;
const uint32_t TWE_APP_ID = 0x67720102;
void printMagnetState(const uint8_t state, const bool changed);
void setup()
{
// Initialize serial ports
Serial.begin(115200);
Serial.println("Monitor example for TWELITE SPOT: App_ARIA (ARIA Mode)");
Serial2.begin(115200, SERIAL_8N1);
// Initialize TWELITE
Twelite.begin(Serial2,
LED_PIN, RST_PIN, PRG_PIN,
TWE_CHANNEL, TWE_APP_ID);
// Attach an event handler to process packets from App_ARIA
Twelite.on([](const ParsedAppAriaPacket& packet) {
Serial.println("");
Serial.print("Packet Number: #");
Serial.println(packet.u16SequenceNumber, DEC);
Serial.print("Source Logical ID: 0x");
Serial.println(packet.u8SourceLogicalId, HEX);
Serial.print("LQI: ");
Serial.println(packet.u8Lqi, DEC);
Serial.print("Supply Voltage: ");
Serial.print(packet.u16SupplyVoltage, DEC); Serial.println(" mV");
Serial.print("Air Temperature: ");
Serial.print(packet.i16Temp100x / 100.0f, 2); Serial.println(" C");
Serial.print("Relative Humidity: ");
Serial.print(packet.u16Humid100x / 100.0f, 2); Serial.println(" %");
Serial.print("Magnet State: ");
printMagnetState(packet.u8MagnetState, packet.bMagnetStateChanged);
});
}
void loop()
{
// Update TWELITE
Twelite.update();
}
void printMagnetState(const uint8_t state, const bool changed)
{
if (changed) {
switch (state) {
case 0x0: { Serial.print("Leaving or not found"); break; }
case 0x1: { Serial.print("N-pole is getting closer"); break; }
case 0x2: { Serial.print("S-pole is getting closer"); break; }
default: break;
}
} else {
switch (state) {
case 0x0: { Serial.print("Not found"); break; }
case 0x1: { Serial.print("N-pole is close"); break; }
case 0x2: { Serial.print("S-pole is close"); break; }
default: break;
}
Serial.print(" (Periodic packet)");
}
Serial.println("");
}
ライブラリのインクルード
4行目では、MWings ライブラリをインクルードしています。
#include "MWings.h"
ピン番号の定義
6-8行目では、ピン番号を定義しています。
const int RST_PIN = 5;
const int PRG_PIN = 4;
const int LED_PIN = 18;
名称 | 内容 |
---|---|
RST_PIN | TWELITE の RST ピンが接続されているピンの番号 |
PRG_PIN | TWELITE の PRG ピンが接続されているピンの番号 |
LED_PIN | 基板上の ESP32 用 LED が接続されているピンの番号 |
TWELITE 設定の定義
10-11行目では、TWELITE SPOT に搭載された TWELITE 親機に適用する設定を定義しています。
const uint8_t TWE_CHANNEL = 18;
const uint32_t TWE_APP_ID = 0x67720102;
名称 | 内容 |
---|---|
TWE_CHANNEL | TWELITE の 周波数チャネル |
TWE_APP_ID | TWELITE の アプリケーション ID |
シリアルポートの設定
18-20行目では、使用するシリアルポートを初期化するとともに、シリアルモニタへ起動メッセージを出力しています。
Serial.begin(115200);
Serial.println("Monitor example for TWELITE SPOT: App_ARIA (ARIA Mode)");
Serial2.begin(115200, SERIAL_8N1);
Serial
は、Arduino IDE の シリアルモニタとの通信に使います。シリアルモニタの設定に合わせて、ボーレートを 115200
bps としています。
一方、Serial2
は、TWELITE SPOT に搭載された TWELITE 親機との通信に使います。こちらも TWELITE 親機の初期設定に合わせて、ボーレートを 115200
bps としています。
TWELITE の設定
23-25行目では、Twelite.begin()
を呼び出し、TWELITE SPOT に搭載された TWELITE 親機の設定と起動を行っています。
Twelite.begin(Serial2,
LED_PIN, RST_PIN, PRG_PIN,
TWE_CHANNEL, TWE_APP_ID);
パケット受信時のイベントの登録
28-44行目では、Twelite.on()
を呼び出し、送られたデータに対して行う処理を登録しています。
ここでは、受信したパケットの内容をシリアルモニタに出力しています。
Twelite.on([](const ParsedAppAriaPacket& packet) {
Serial.println("");
Serial.print("Packet Number: #");
Serial.println(packet.u16SequenceNumber, DEC);
Serial.print("Source Logical ID: 0x");
Serial.println(packet.u8SourceLogicalId, HEX);
Serial.print("LQI: ");
Serial.println(packet.u8Lqi, DEC);
Serial.print("Supply Voltage: ");
Serial.print(packet.u16SupplyVoltage, DEC); Serial.println(" mV");
Serial.print("Air Temperature: ");
Serial.print(packet.i16Temp100x / 100.0f, 2); Serial.println(" C");
Serial.print("Relative Humidity: ");
Serial.print(packet.u16Humid100x / 100.0f, 2); Serial.println(" %");
Serial.print("Magnet State: ");
printMagnetState(packet.u8MagnetState, packet.bMagnetStateChanged);
});
上記のイベントは、アリアアプリ(TWELITE ARIA モード)からのパケットを受信したときにだけ呼び出されます。
受信したパケットの内容は ParsedAppAriaPacket
型の引数 packet
に格納されています。
メッセージの内容
メッセージ | 内容 |
---|---|
Packet Number | パケットの続き番号 |
Source Logical ID | 送信元 TWELITE の論理デバイスID |
LQI | 無線通信品質(0~255) |
Supply Voltage | 電源電圧 (mV) |
Air Temperature | TWELITE ARIA が計測した気温 (°C) |
Relative Humidity | TWELITE ARIA が計測した相対湿度 (%) |
Magnet State | 磁気センサーの状態 |
磁気センサーの状態
出力される磁気センサーの状態は以下の通りです。
S-pole is getting closer
新たに磁石のS極を検知した。N-pole is getting closer
新たに磁石のN極を検知した。Leaving or Not found
磁石が検知できなかった。S-pole is close (Periodic packet)
磁石のS極を検知している。N-pole is close (Periodic packet)
磁石のN極を検知している。Not found (Periodic packet)
磁石を連続で検知できていない(定期送信パケット)。
TWELITE のデータの更新
50行目では、Twelite.update()
を呼び出しています。
Twelite.update();
5.1.3.3 - アリアアプリのデータを取得
monitor_spot_app_aria
の解説です。サンプルスケッチの保存場所
MWings ライブラリを導入していれば、Arduino IDE の ファイル -> スケッチ例 -> MWings -> TWELITE SPOT -> Receive -> monitor_spot_app_aria からスケッチを開くことができます。
スケッチ
以下はソースコード本体です。
// Monitor example for TWELITE SPOT: Receive data from App_ARIA (ARIA Mode)
#include <Arduino.h>
#include "MWings.h"
const int RST_PIN = 5;
const int PRG_PIN = 4;
const int LED_PIN = 18;
const uint8_t TWE_CHANNEL = 18;
const uint32_t TWE_APP_ID = 0x67720102;
void printMagnetState(const uint8_t state, const bool changed);
void setup()
{
// Initialize serial ports
Serial.begin(115200);
Serial.println("Monitor example for TWELITE SPOT: App_ARIA (ARIA Mode)");
Serial2.begin(115200, SERIAL_8N1);
// Initialize TWELITE
Twelite.begin(Serial2,
LED_PIN, RST_PIN, PRG_PIN,
TWE_CHANNEL, TWE_APP_ID);
// Attach an event handler to process packets from App_ARIA
Twelite.on([](const ParsedAppAriaPacket& packet) {
Serial.println("");
Serial.print("Packet Number: #");
Serial.println(packet.u16SequenceNumber, DEC);
Serial.print("Source Logical ID: 0x");
Serial.println(packet.u8SourceLogicalId, HEX);
Serial.print("LQI: ");
Serial.println(packet.u8Lqi, DEC);
Serial.print("Supply Voltage: ");
Serial.print(packet.u16SupplyVoltage, DEC); Serial.println(" mV");
Serial.print("Air Temperature: ");
Serial.print(packet.i16Temp100x / 100.0f, 2); Serial.println(" C");
Serial.print("Relative Humidity: ");
Serial.print(packet.u16Humid100x / 100.0f, 2); Serial.println(" %");
Serial.print("Magnet State: ");
printMagnetState(packet.u8MagnetState, packet.bMagnetStateChanged);
});
}
void loop()
{
// Update TWELITE
Twelite.update();
}
void printMagnetState(const uint8_t state, const bool changed)
{
if (changed) {
switch (state) {
case 0x0: { Serial.print("Leaving or not found"); break; }
case 0x1: { Serial.print("N-pole is getting closer"); break; }
case 0x2: { Serial.print("S-pole is getting closer"); break; }
default: break;
}
} else {
switch (state) {
case 0x0: { Serial.print("Not found"); break; }
case 0x1: { Serial.print("N-pole is close"); break; }
case 0x2: { Serial.print("S-pole is close"); break; }
default: break;
}
Serial.print(" (Periodic packet)");
}
Serial.println("");
}
ライブラリのインクルード
4行目では、MWings ライブラリをインクルードしています。
#include "MWings.h"
ピン番号の定義
6-8行目では、ピン番号を定義しています。
const int RST_PIN = 5;
const int PRG_PIN = 4;
const int LED_PIN = 18;
名称 | 内容 |
---|---|
RST_PIN | TWELITE の RST ピンが接続されているピンの番号 |
PRG_PIN | TWELITE の PRG ピンが接続されているピンの番号 |
LED_PIN | 基板上の ESP32 用 LED が接続されているピンの番号 |
TWELITE 設定の定義
10-11行目では、TWELITE SPOT に搭載された TWELITE 親機に適用する設定を定義しています。
const uint8_t TWE_CHANNEL = 18;
const uint32_t TWE_APP_ID = 0x67720102;
名称 | 内容 |
---|---|
TWE_CHANNEL | TWELITE の 周波数チャネル |
TWE_APP_ID | TWELITE の アプリケーション ID |
シリアルポートの設定
18-20行目では、使用するシリアルポートを初期化するとともに、シリアルモニタへ起動メッセージを出力しています。
Serial.begin(115200);
Serial.println("Monitor example for TWELITE SPOT: App_ARIA (ARIA Mode)");
Serial2.begin(115200, SERIAL_8N1);
Serial
は、Arduino IDE の シリアルモニタとの通信に使います。シリアルモニタの設定に合わせて、ボーレートを 115200
bps としています。
一方、Serial2
は、TWELITE SPOT に搭載された TWELITE 親機との通信に使います。こちらも TWELITE 親機の初期設定に合わせて、ボーレートを 115200
bps としています。
TWELITE の設定
23-25行目では、Twelite.begin()
を呼び出し、TWELITE SPOT に搭載された TWELITE 親機の設定と起動を行っています。
Twelite.begin(Serial2,
LED_PIN, RST_PIN, PRG_PIN,
TWE_CHANNEL, TWE_APP_ID);
パケット受信時のイベントの登録
28-44行目では、Twelite.on()
を呼び出し、送られたデータに対して行う処理を登録しています。
ここでは、受信したパケットの内容をシリアルモニタに出力しています。
Twelite.on([](const ParsedAppAriaPacket& packet) {
Serial.println("");
Serial.print("Packet Number: #");
Serial.println(packet.u16SequenceNumber, DEC);
Serial.print("Source Logical ID: 0x");
Serial.println(packet.u8SourceLogicalId, HEX);
Serial.print("LQI: ");
Serial.println(packet.u8Lqi, DEC);
Serial.print("Supply Voltage: ");
Serial.print(packet.u16SupplyVoltage, DEC); Serial.println(" mV");
Serial.print("Air Temperature: ");
Serial.print(packet.i16Temp100x / 100.0f, 2); Serial.println(" C");
Serial.print("Relative Humidity: ");
Serial.print(packet.u16Humid100x / 100.0f, 2); Serial.println(" %");
Serial.print("Magnet State: ");
printMagnetState(packet.u8MagnetState, packet.bMagnetStateChanged);
});
上記のイベントは、アリアアプリ(TWELITE ARIA モード)からのパケットを受信したときにだけ呼び出されます。
受信したパケットの内容は ParsedAppAriaPacket
型の引数 packet
に格納されています。
メッセージの内容
メッセージ | 内容 |
---|---|
Packet Number | パケットの続き番号 |
Source Logical ID | 送信元 TWELITE の論理デバイスID |
LQI | 無線通信品質(0~255) |
Supply Voltage | 電源電圧 (mV) |
Air Temperature | TWELITE ARIA が計測した気温 (°C) |
Relative Humidity | TWELITE ARIA が計測した相対湿度 (%) |
Magnet State | 磁気センサーの状態 |
磁気センサーの状態
出力される磁気センサーの状態は以下の通りです。
S-pole is getting closer
新たに磁石のS極を検知した。N-pole is getting closer
新たに磁石のN極を検知した。Leaving or Not found
磁石が検知できなかった。S-pole is close (Periodic packet)
磁石のS極を検知している。N-pole is close (Periodic packet)
磁石のN極を検知している。Not found (Periodic packet)
磁石を連続で検知できていない(定期送信パケット)。
TWELITE のデータの更新
50行目では、Twelite.update()
を呼び出しています。
Twelite.update();
5.2 - TWELITE に加えて無線 LAN を活用するスケッチの解説
5.2.1 - プリインストール済みスケッチ
spot-server
の解説です。5.2.1.1 - プリインストール済みスケッチ
spot-server
の解説です。本稿では、Arduino IDE 1.x を必要とします。Arduino IDE 2.x の技術的な制約により、2023年5月現在、Arduino IDE 2.x には対応していません。
本稿で使用するプラグインは Java で書かれているため、Arduino IDE 1.x とは異なり、Java ベースではない Arduino IDE 2.x では動作しないからです。問題の詳細については、Arduino IDE GitHub ページの Issue (Missing support for external tools / plugins · Issue #58 · arduino/arduino-ide) をご覧ください(英語)。
2024年10月追記:未検証ですが、Arduino IDE 2.x では、プラグインearlephilhower/arduino-littlefs-uploadを代わりに使用できるかもしれません。
本稿では、サードパーティのオープンソースソフトウェアを使用します。
サードパーティのソフトウェアについて、その詳しい使用方法を弊社からご案内することはいたしかねます。また、サードパーティのソフトウェアを使用されたことによるいかなる損害についても、弊社は一切の責任を負いません。
ソースコードの入手
GitHub (monowireless/spot-server) から入手できます。
システムの概要
spot-server は、TWELITE からのデータ受信と転送を行う Arduino スケッチ (.ino
) と、スマホに配信する Web ページ (.html
/ .css
/ .js
) で構成しています。
TWELITE 子機が送信したデータは Arduino スケッチで受信され、Arduino スケッチは公開中の Web ページに対してイベントを発火します。公開された Web ページでは、発火されたイベントに応じて HTML の内容を動的に書き換えています。
開発に必要なもの
-
無線LANゲートウェイ TWELITE SPOT
- 電源用 USB-C ケーブル
- USB AC アダプタ(1A 以上供給できるもの)
-
加速度センサー無線タグ TWELITE CUEなどの子機 (お持ちでない場合はご購入ください 👉 販売店一覧)
- CR2032 コイン電池 などの電源
-
USBアダプター TWELITE R3 (お持ちでない場合はご購入ください 👉 販売店一覧)
- 通信用 USB-C ケーブル
- Grove - OLED Display 1.12 (なくてもスケッチは動きます)
- Grove ケーブル
- 💻 コンピュータ
環境整備
IDE とツールチェインの導入
Arduino IDE 1.x による開発環境の構築方法 をご覧ください。
ライブラリの導入
はじめに、Arduino のスケッチブックの保存場所(Arduino IDE 環境設定に記載。例:C:\Users\foo\Documents\Arduino
) に libraries
フォルダがない場合は、これを作成します。
非同期 TCP 通信ライブラリ
- GitHub (me-no-dev/AsyncTCP) から Zip ファイルをダウンロードします
- Zip ファイルを展開し、フォルダ名を
AsyncTCP-master
からAsyncTCP
に変更します libraries
フォルダにAsyncTCP
フォルダを配置します
非同期 Web サーバライブラリ
- GitHub (me-no-dev/ESPAsyncWebServer) から Zip ファイルをダウンロードします
- Zip ファイルを展開し、フォルダ名を
AsyncWebServer-master
からAsyncWebServer
に変更します libraries
フォルダにAsyncWebServer
フォルダを配置します
OLED ディスプレイライブラリ
- GitHub (Seeed-Studio/OLED_Display_96X96) から Zip ファイルをダウンロードします
- Zip ファイルを展開し、フォルダ名を
OLED_Display_96X96-master
からOLED_Display_96X96
に変更します libraries
フォルダにOLED_Display_96X96
フォルダを配置します
JSON ライブラリ
ライブラリマネージャを開き、Arduino_JSON
をインストールします。
ArduinoJson
ではなく、公式の Arduino_JSON
を使用します。プラグインの導入
ファイルシステム書き込みプラグイン
HTML などのファイルを ESP32 のフラッシュ領域に書き込むには、Arduino プラグインが必要です。
ここでは、lorol/arduino-esp32fs-plugin: Arduino plugin for uploading files to ESP32 file system を利用します(LittleFSに対応したプラグイン)。
インストール方法は TWELITE SPOT マニュアル ESP32 へのファイルの書き込み方法 をご覧ください。
プロジェクトファイルの入手
- GitHub (monowireless/spot-server) から Zip ファイルをダウンロードします
- Zip ファイルを展開し、フォルダ名を
spot-server-main
からspot-server
に変更します - Arduino のスケッチブックの保存場所(Arduino IDE 環境設定に記載。例:
C:\Users\foo\Documents\Arduino
)にspot-server
フォルダを配置します
プロジェクトファイルの書き込み方法
スケッチ
ESP32 へのスケッチの書き込み方法 をご覧ください。
Web ページ
ESP32 へのファイルの書き込み方法 をご覧ください。
スケッチ
Arduino スケッチ spot-server.ino の解説です。
ライブラリのインクルード
Arduino および ESP32 公式ライブラリ
4-9行目では、Arduino および ESP32 の公式ライブラリをインクルードしています。
#include <Arduino.h>
#include <Arduino_JSON.h>
#include <ESPmDNS.h>
#include <LittleFS.h>
#include <WiFi.h>
#include "esp_wifi.h"
#include <Wire.h>
ヘッダファイル | 内容 | 備考 |
---|---|---|
Arduino.h | Arduino の基本ライブラリ | 省略できる場合もあるが念のため記載 |
Arduino_JSON.h | JSON 文字列を扱う | ArduinoJson とは異なる |
ESPmDNS.h | mDNS を使う | ホスト名を使うために必要 |
LittleFS.h | LittleFS ファイルシステムを扱う | ページ公開に必要 |
WiFi.h | ESP32 の WiFi を使う | |
esp_wifi.h | WiFiの高度な設定を行う | ロケール設定に必要 |
Wire.h | I2C を使う | OLED ディスプレイ用 |
サードパーティのライブラリ
13-15行目では、サードパーティのライブラリをインクルードしています。
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <SeeedGrayOLED.h>
ヘッダファイル | 内容 | 備考 |
---|---|---|
AsyncTCP.h | 非同期 TCP 通信を行う | |
ESPAsyncWebServer.h | 非同期 Web サーバを立てる | AsyncTCP に依存 |
SeeedGrayOLED.h | OLED ディスプレイを使う |
MWings ライブラリ
18行目では、MWings ライブラリをインクルードしています。
#include <MWings.h>
ピン番号の定義
21-25行目では、ピン番号を定義しています。
const uint8_t TWE_RST = 5;
const uint8_t TWE_PRG = 4;
const uint8_t LED = 18;
const uint8_t ESP_RXD1 = 16;
const uint8_t ESP_TXD1 = 17;
名称 | 内容 |
---|---|
TWE_RST | TWELITE の RST ピンが接続されているピンの番号 |
TWE_PRG | TWELITE の PRG ピンが接続されているピンの番号 |
LED | 基板上の ESP32 用 LED が接続されているピンの番号 |
ESP_RXD1 | TWELITE の TX ピンが接続されているピンの番号 |
ESP_TXD1 | TWELITE の RX ピンが接続されているピンの番号 |
TWELITE 設定の定義
28-31行目では、TWELITE SPOT に搭載された TWELITE 親機に適用する設定を定義しています。
const uint8_t TWE_CH = 18;
const uint32_t TWE_APPID = 0x67720102;
const uint8_t TWE_RETRY = 2;
const uint8_t TWE_POWER = 3;
名称 | 内容 |
---|---|
TWE_CH | TWELITE の 周波数チャネル |
TWE_APPID | TWELITE の アプリケーション ID |
TWE_RETRY | TWELITE の 再送回数(送信時) |
TWE_POWER | TWELITE の 送信出力 |
無線 LAN 設定の定義
34-46行目では、TWELITE SPOT に搭載された ESP32 に適用する無線 LAN 設定を定義しています。
wifi_country_t WIFI_COUNTRY_JP = {
cc: "JP", // Contry code
schan: 1, // Starting channel
nchan: 14, // Number of channels
max_tx_power: 20, // Maximum power in dBm
policy: WIFI_COUNTRY_POLICY_MANUAL
};
const char* WIFI_SSID_BASE = "TWELITE SPOT";
const char* WIFI_PASSWORD = "twelitespot";
const uint8_t WIFI_CH = 13;
const IPAddress WIFI_IP = IPAddress(192, 168, 1, 1);
const IPAddress WIFI_MASK = IPAddress(255, 255, 255, 0);
const char* HOSTNAME = "spot"; // spot.local
名称 | 内容 |
---|---|
WIFI_COUNTRY_JP | ロケール設定(日本) |
WIFI_SSID_BASE | SSID の共通部分の文字列 |
WIFI_PASSWORD | パスワード |
WIFI_CH | ESP32 の周波数チャネル |
WIFI_IP | IP アドレス |
WIFI_MASK | サブネットマスク |
HOSTNAME | ホスト名 |
ESP32 Arduino Core v3.x.x では、ロケール設定を行わないとチャネル13などの機能を使用できないことがあります。
グローバルオブジェクトの宣言
49-50行目では、グローバルオブジェクトを宣言しています。
AsyncWebServer server(80);
AsyncEventSource events("/events");
名称 | 内容 |
---|---|
server | 80番ポートで開く非同期 Web サーバのインタフェース |
events | /events で開くサーバー送信イベント ?のインタフェース |
関数プロトタイプの宣言
53-57行目では、関数プロトタイプを宣言しています。
uint16_t createUidFromMac();
String createJsonFrom(const ParsedAppTwelitePacket& packet);
String createJsonFrom(const ParsedAppAriaPacket& packet);
String createJsonFrom(const ParsedAppCuePacket& packet);
String createJsonFrom(const BarePacket& packet);
名称 | 内容 |
---|---|
createUidFromMac() | MAC アドレスから SSID に使う識別子を作ります |
createJsonFrom() <ParsedAppTwelitePacket&> | App_Twelite の |
createJsonFrom() <ParsedAppAriaPacket&> | App_ARIA の |
createJsonFrom() <ParsedAppCuePacket&> | App_CUE の |
createJsonFrom() <BarePacket&> | すべての |
TWELITE の設定
66-71行目では、Twelite.begin()
を呼び出し、TWELITE SPOT に搭載された TWELITE 親機の設定と起動を行っています。
Serial2.begin(115200, SERIAL_8N1, ESP_RXD1, ESP_TXD1);
if (Twelite.begin(Serial2,
LED, TWE_RST, TWE_PRG,
TWE_CH, TWE_APPID, TWE_RETRY, TWE_POWER)) {
Serial.println("Started TWELITE.");
}
引数 | 型 | 内容 |
---|---|---|
Serial2 | HardwareSerial& | TWELITE との通信に使うシリアルポート |
LED | int | ステータス LED を接続したピンの番号 |
TWE_RST | int | TWELITE の RST ピンを接続したピンの番号 |
TWE_PRG | int | TWELITE の PRG ピンを接続したピンの番号 |
TWE_CHANNEL | uint8_t | TWELITE の 周波数チャネル |
TWE_APP_ID | uint32_t | TWELITE の アプリケーション ID |
TWE_RETRY | uint8_t | TWELITE の 再送回数(送信時) |
TWE_POWER | uint8_t | TWELITE の 送信出力 |
App_Twelite:イベントハンドラの登録
73-80行目では、Twelite.on() <ParsedAppTwelitePacket>
を呼び出し、超簡単!標準アプリの子機からのパケットを受信した際に行う処理を登録しています。
Twelite.on([](const ParsedAppTwelitePacket& packet) {
Serial.println("Received a packet from App_Twelite");
String jsonStr = createJsonFrom(packet);
if (not(jsonStr.length() <= 0)) {
events.send(jsonStr.c_str(), "data_app_twelite", millis());
}
events.send("parsed_app_twelite", "data_parsing_result", millis());
});
JSON 文字列の作成
75行目では、受信したデータからJSON 文字列を生成しています。
String jsonStr = createJsonFrom(packet);
受信したデータをWeb ページに表示するにはクライアント側の JavaScript にデータを送る必要がありますが、このとき文字列データのほうが扱いやすいため、JSON 文字列としています。
Web ページへのイベント送信
76-78行目では、生成した JSON 文字列を “Signal Viewer” ページへ送信しています。
if (not(jsonStr.length() <= 0)) {
events.send(jsonStr.c_str(), "data_app_twelite", millis());
}
イベント名は data_app_twelite
です。
millis()
で取得した現在時刻を使用しています。79行目では、App_Twelite からのパケットを受信したことを “Serial Viewer” ページへ送信しています。
events.send("parsed_app_twelite", "data_parsing_result", millis());
App_ARIA:イベントハンドラの登録
82-92行目では、Twelite.on() <ParsedAppAriaPacket>
を呼び出し、アリアアプリ(TWELITE ARIA モード)の子機からのパケットを受信した際に行う処理を登録しています。
Twelite.on([](const ParsedAppAriaPacket& packet) {
Serial.println("Received a packet from App_ARIA");
static uint32_t firstSourceSerialId = packet.u32SourceSerialId;
if (packet.u32SourceSerialId == firstSourceSerialId) {
String jsonStr = createJsonFrom(packet);
if (not(jsonStr.length() <= 0)) {
events.send(jsonStr.c_str(), "data_app_aria_twelite_aria_mode", millis());
}
}
events.send("parsed_app_aria_twelite_aria_mode", "data_parsing_result", millis());
});
対象の絞り込み
84-85行目では、処理の対象を 最初に受信した子機 に限定しています。
static uint32_t firstSourceSerialId = packet.u32SourceSerialId;
if (packet.u32SourceSerialId == firstSourceSerialId) {
こうしておかないと、複数の子機があった際にグラフの一貫性が失われてしまうからです。
JSON 文字列の作成
86行目では、受信したデータからJSON 文字列を生成しています。
String jsonStr = createJsonFrom(packet);
Web ページへのイベント送信
87-89行目では、生成した JSON 文字列を “ARIA Viewer” ページへ送信しています。
if (not(jsonStr.length() <= 0)) {
events.send(jsonStr.c_str(), "data_app_aria_twelite_aria_mode", millis());
}
イベント名は data_app_aria_twelite_aria_mode
です。
91行目では、App_Twelite からのパケットを受信したことを “Serial Viewer” ページへ送信しています。
events.send("parsed_app_aria_twelite_aria_mode", "data_parsing_result", millis());
App_CUE:イベントハンドラの登録
94-104行目では、Twelite.on() <ParsedAppCuePacket>
を呼び出し、キューアプリ(TWELITE CUE モード)の子機からのパケットを受信した際に行う処理を登録しています。
Twelite.on([](const ParsedAppCuePacket& packet) {
Serial.println("Received a packet from App_CUE");
static uint32_t firstSourceSerialId = packet.u32SourceSerialId;
if (packet.u32SourceSerialId == firstSourceSerialId) {
String jsonStr = createJsonFrom(packet);
if (not(jsonStr.length() <= 0)) {
events.send(jsonStr.c_str(), "data_app_cue_twelite_cue_mode", millis());
}
}
events.send("parsed_app_cue_twelite_cue_mode", "data_parsing_result", millis());
});
その他:イベントハンドラの登録
106-134行目では、その他のアプリの子機からのパケットを受信した際に行う処理を登録しています。
アリアアプリ等と同様に “Serial Viewer” へイベントを送信しています。
すべて:イベントハンドラの登録
136-142行目では、すべてのアプリの子機からのパケットを受信した際に行う処理を登録しています。
Twelite.on([](const BarePacket& packet) {
String jsonStr = createJsonFrom(packet);
if (not(jsonStr.length() <= 0)) {
events.send(jsonStr.c_str(), "data_bare_packet", millis());
}
events.send("unparsed_bare_packet", "data_parsing_result", millis());
});
ここでも、“Serial Viewer” に対するパケットデータ文字列の送信を行っています。
OLED ディスプレイの設定
145-150行目では、OLED ディスプレイの設定を行っています。
Wire.begin();
SeeedGrayOled.init(SSD1327);
SeeedGrayOled.setNormalDisplay();
SeeedGrayOled.setVerticalMode();
SeeedGrayOled.setGrayLevel(0x0F);
SeeedGrayOled.clearDisplay();
無線 LAN の設定
154-165行目では、無線 LAN の設定を行っています。
WiFi.mode(WIFI_AP);
esp_wifi_set_country(&WIFI_COUNTRY_JP);
char uidCString[8];
sprintf(uidCString, " (%02X)", createUidFromMac());
char ssidCString[20];
sprintf(ssidCString, "%s%s", WIFI_SSID_BASE, uidCString);
if (not WiFi.softAP(ssidCString, WIFI_PASSWORD, WIFI_CH, false, 10)) {
Serial.println("Failed to start AP");
}
delay(100); // IMPORTANT: Waiting for SYSTEM_EVENT_AP_START
WiFi.softAPConfig(WIFI_IP, WIFI_IP, WIFI_MASK);
MDNS.begin(HOSTNAME);
delay(100)
を省略すると初期化に失敗することがあります。ファイルシステムの設定
198行目では、LittleFS ファイルシステムを設定しています。
if (LittleFS.begin()) { Serial.println("Mounted file system."); }
フラッシュ領域内に書き込んだ HTML などのファイルをページとして取得することができるようになります。
Web サーバの設定
201-228行目では、Web サーバの設定を行っています。
GET リクエストのハンドリング
例えば、206-210行目 では /signal-viewer
への GET リクエストに対して、LittleFS ファイルシステム上の /signal-viewer.html
を返しています。
server.on("/signal-viewer", HTTP_GET,
[](AsyncWebServerRequest* request) {
Serial.println("HTTP_GET: signal-viewer.html");
request->send(LittleFS, "/signal-viewer.html", "text/html");
});
サーバの初期化
226-228行目では、ファイルシステム上のルートをサーバのルートとして設定したあと、イベントソースを登録してサーバを立ち上げています。
server.serveStatic("/", LittleFS, "/");
server.addHandler(&events);
server.begin();
TWELITE のデータの更新
234行目では、Twelite.update()
を呼び出しています。
Twelite.update();
Twelite.update()
は、TWELITE 親機から送信されるパケットデータ(ModBus ASCII 形式)を順次1バイトずつ読み出す関数です。
loop()
内で繰り返し Twelite.update()
を呼ぶことで、TWELITE 親機から送信されるパケットデータの解釈が進みます。パケットデータの解釈を終えた際に 上記 のようなイベントが呼ばれる仕組みです。delay()
などの処理でこの関数の呼び出しをブロックすると、パケットデータ文字列の読み出しが間に合わないことがあります。時間のかかる処理は必ず非同期の実装として、loop()
関数をできるだけ高速回転させるようにしてください。Web ページ
Web ページに関しては、詳しい解説を行いません。重要なポイントに絞って解説します。
HTML:グリッドシステム
このサンプルの HTML では、Flexbox Grid を使っています(ソースファイルは data/css/flexboxgrid.min.css)。
下記のようにして Bootstrap に似た 12 分割のグリッドシステムを使用しています。
<div class="col-xs-6 col-sm-6 col-md-5 col-lg-4">
<div class="neumorphic inset dense row center-xs middle-xs">
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 npr npl">
<img src="./images/logo-lands.svg" class="logo" />
</div>
</div>
</div>
<div class="col-xs-6 col-sm-6 col-md-7 col-lg-8">
<div class="neumorphic inset dense row center-xs middle-xs">
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 nwp npr npl">
<span class="medium bold">TWELITE SPOT</span>
</div>
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 nwp npr npl">
<span class="small bold">CUE Viewer</span>
</div>
</div>
</div>
ここでは、ロゴを中心とした要素の幅を 6/12 、文字列を中心とした要素の幅を 6/12 、すなわち両者を等しい幅で一列に配置しています。また、文字列 TWELITE SPOT
を中心とした要素と CUE Viewer
を中心とした要素の幅はどちらも 12/12 、すなわち1行ずつ2行に分けて配置しています。
xs-
や sm-
などは画面の幅を指定します。レスポンシブデザインに活用できます。HTML:データ表示部
TWELITE 子機から受信したデータを表示する要素には、一意の ID を付与しています。
以下は TWELITE CUE から受信した X 軸加速度を表示する部分の抜粋です。
<div class="col-xs-4 nwp npr npl">
<code class="medium"
id="latest-accel-x">±--.--</code>
<code class="small">G</code>
</div>
ここでは、ID として latest-accel-x
を付与しています。この ID を使って、スクリプトから値を書き換えます。
JS:グローバル変数
ここからは各 HTML ファイルに対応したスクリプトの解説です。
例として、ここでは data/js/cue-viewer.js を取り上げます。
4-8行目では、最新の加速度値を保存するためのグローバル変数を宣言しています。
let latest_accel = {
x: 0.0,
y: 0.0,
z: 0.0
};
この値はグラフからも利用するため、実装を簡素にするためにグローバル変数を使用しています。
JS:グラフ設定
11-133行目では、グラフ描画ライブラリ Chart.js | Chart.js およびそのプラグイン chartjs-plugin-streaming の設定を行っています。
JS:ページ内容の更新
136-235行目の関数 processDataAppCueTweliteCueMode()
は、スケッチから data_app_cue_twelite_cue_mode
イベントを受信した際にページ内容を更新する関数です。
例えば、184-208行目では、TWELITE CUE の電源電圧に応じて電圧値と絵文字を更新しています。
if (data.vcc >= 3000) {
document.getElementById("latest-vcc-icon").innerHTML = "🔋";
document.getElementById("latest-vcc-data").innerHTML = `${(data.vcc / 1000.0).toFixed(2).toString().padStart(4)}`;
document.getElementById("latest-vcc-data").classList.remove("red");
document.getElementById("latest-vcc-data").classList.remove("yellow");
document.getElementById("latest-vcc-data").classList.add("green");
} else if (data.vcc >= 2700) {
document.getElementById("latest-vcc-icon").innerHTML = "🔋";
document.getElementById("latest-vcc-data").innerHTML = `${(data.vcc / 1000.0).toFixed(2).toString().padStart(4)}`;
document.getElementById("latest-vcc-data").classList.remove("red");
document.getElementById("latest-vcc-data").classList.remove("yellow");
document.getElementById("latest-vcc-data").classList.remove("green");
} else if (data.vcc >= 2400) {
document.getElementById("latest-vcc-icon").innerHTML = "🪫";
document.getElementById("latest-vcc-data").innerHTML = `${(data.vcc / 1000.0).toFixed(2).toString().padStart(4)}`;
document.getElementById("latest-vcc-data").classList.remove("red");
document.getElementById("latest-vcc-data").classList.add("yellow");
document.getElementById("latest-vcc-data").classList.remove("green");
} else {
document.getElementById("latest-vcc-icon").innerHTML = "🪫";
document.getElementById("latest-vcc-data").innerHTML = `${(data.vcc / 1000.0).toFixed(2).toString().padStart(4)}`;
document.getElementById("latest-vcc-data").classList.add("red");
document.getElementById("latest-vcc-data").classList.remove("yellow");
document.getElementById("latest-vcc-data").classList.remove("green");
}
ここでは、電源電圧が 2700mV 未満に降下した際に絵文字を 🔋 から 🪫 に変えているほか、3000mV → 2700mV → 2400mV と電圧降下に従って電圧値の文字色を適用する CSS クラスを入れ替えています。
イベントリスナーの登録
254-257行目では、スケッチからのイベントを受信した際の処理を登録しています。
source.addEventListener("data_app_cue_twelite_cue_mode", (e) => {
console.log("data_app_cue_twelite_cue_mode", e.data);
processDataAppCueTweliteCueMode(JSON.parse(e.data));
}, false);
ここでは、スケッチより 受信したイベントメッセージから JSON 文字列を取り出し、パースしたデータを先ほどの関数 processDataAppCueTweliteCueMode()
へ渡しています。
関連情報
Arduino
- 公式サイト:Arduino - Home
ESP32
- 製品情報:ESP32 Wi-Fi & Bluetooth MCU I Espressif Systems
- データシート:esp32_datasheet_en.pdf
- Arduino 向けツールチェイン:espressif/arduino-esp32: Arduino core for the ESP32
- スタートガイド:Getting Started — Arduino-ESP32 documentation
- 導入方法:Installing — Arduino-ESP32 documentation
- API リファレンス:Libraries — Arduino-ESP32 documentation
- Wi-Fi API:Wi-Fi API — Arduino-ESP32 documentation
- チュートリアル:Tutorials — Arduino-ESP32 documentation
- トラブルシューティング:Troubleshooting — Arduino-ESP32 documentation
コミュニティ
ライブラリ
- 非同期 TCP:me-no-dev/AsyncTCP: Async TCP Library for ESP32
- 非同期 Web サーバ:me-no-dev/ESPAsyncWebServer: Async Web Server for ESP8266 and ESP32
- Seeed 96x96 / 128x128 OLED:Seeed-Studio/OLED_Display_96X96: Seeed OLED Display 96*96 library
プラグイン
- ファイル書き込み:me-no-dev/arduino-esp32fs-plugin: Arduino plugin for uploading files to ESP32 file system
- スタックトレース:me-no-dev/EspExceptionDecoder: Exception Stack Trace Decoder for ESP8266 and ESP32
Web関連
ECMAScript (JavaScript)
- API リファレンス:開発者向けのウェブ技術 | MDN
- ES2016以降のバージョン別互換性リスト:ECMAScript 2016+ compatibility table
コミュニティ
- CSS
- Web カラーとその配色例:世界の伝統色 洋色大辞典 - Traditional Colors of World
- Bootstrap のようなグリッドシステム:Flexbox Grid
- ニューモーフィズムな CSS ジェネレータ:Neumorphism/Soft UI CSS shadow generator
- ECMAScript
- グラフ描画:Chart.js | Chart.js
- リアルタイムストリーミングプラグイン:はじめに | chartjs-plugin-streaming
- 時計と時刻:Luxon Home
- グラフ描画:Chart.js | Chart.js
5.2.1.2 - プリインストール済みスケッチ
spot-server
の解説です。本稿では、Arduino IDE 1.x を必要とします。Arduino IDE 2.x の技術的な制約により、2023年5月現在、Arduino IDE 2.x には対応していません。
本稿で使用するプラグインは Java で書かれているため、Arduino IDE 1.x とは異なり、Java ベースではない Arduino IDE 2.x では動作しないからです。問題の詳細については、Arduino IDE GitHub ページの Issue (Missing support for external tools / plugins · Issue #58 · arduino/arduino-ide) をご覧ください(英語)。
2024年10月追記:未検証ですが、Arduino IDE 2.x では、プラグインearlephilhower/arduino-littlefs-uploadを代わりに使用できるかもしれません。
本稿では、サードパーティのオープンソースソフトウェアを使用します。
サードパーティのソフトウェアについて、その詳しい使用方法を弊社からご案内することはいたしかねます。また、サードパーティのソフトウェアを使用されたことによるいかなる損害についても、弊社は一切の責任を負いません。
ソースコードの入手
GitHub (monowireless/spot-server) から入手できます。
システムの概要
spot-server は、TWELITE からのデータ受信と転送を行う Arduino スケッチ (.ino
) と、スマホに配信する Web ページ (.html
/ .css
/ .js
) で構成しています。
TWELITE 子機が送信したデータは Arduino スケッチで受信され、Arduino スケッチは公開中の Web ページに対してイベントを発火します。公開された Web ページでは、発火されたイベントに応じて HTML の内容を動的に書き換えています。
開発に必要なもの
-
無線LANゲートウェイ TWELITE SPOT
- 電源用 USB-C ケーブル
- USB AC アダプタ(1A 以上供給できるもの)
-
加速度センサー無線タグ TWELITE CUEなどの子機 (お持ちでない場合はご購入ください 👉 販売店一覧)
- CR2032 コイン電池 などの電源
-
USBアダプター TWELITE R3 (お持ちでない場合はご購入ください 👉 販売店一覧)
- 通信用 USB-C ケーブル
- Grove - OLED Display 1.12 (なくてもスケッチは動きます)
- Grove ケーブル
- 💻 コンピュータ
環境整備
IDE とツールチェインの導入
Arduino IDE 1.x による開発環境の構築方法 をご覧ください。
ライブラリの導入
はじめに、Arduino のスケッチブックの保存場所(Arduino IDE 環境設定に記載。例:C:\Users\foo\Documents\Arduino
) に libraries
フォルダがない場合は、これを作成します。
非同期 TCP 通信ライブラリ
- GitHub (me-no-dev/AsyncTCP) から Zip ファイルをダウンロードします
- Zip ファイルを展開し、フォルダ名を
AsyncTCP-master
からAsyncTCP
に変更します libraries
フォルダにAsyncTCP
フォルダを配置します
非同期 Web サーバライブラリ
- GitHub (me-no-dev/ESPAsyncWebServer) から Zip ファイルをダウンロードします
- Zip ファイルを展開し、フォルダ名を
AsyncWebServer-master
からAsyncWebServer
に変更します libraries
フォルダにAsyncWebServer
フォルダを配置します
OLED ディスプレイライブラリ
- GitHub (Seeed-Studio/OLED_Display_96X96) から Zip ファイルをダウンロードします
- Zip ファイルを展開し、フォルダ名を
OLED_Display_96X96-master
からOLED_Display_96X96
に変更します libraries
フォルダにOLED_Display_96X96
フォルダを配置します
JSON ライブラリ
ライブラリマネージャを開き、Arduino_JSON
をインストールします。
ArduinoJson
ではなく、公式の Arduino_JSON
を使用します。プラグインの導入
ファイルシステム書き込みプラグイン
HTML などのファイルを ESP32 のフラッシュ領域に書き込むには、Arduino プラグインが必要です。
ここでは、lorol/arduino-esp32fs-plugin: Arduino plugin for uploading files to ESP32 file system を利用します。
インストール方法は TWELITE SPOT マニュアル ESP32 へのファイルの書き込み方法 をご覧ください。
プロジェクトファイルの入手
- GitHub (monowireless/spot-server) から Zip ファイルをダウンロードします
- Zip ファイルを展開し、フォルダ名を
spot-server-main
からspot-server
に変更します - Arduino のスケッチブックの保存場所(Arduino IDE 環境設定に記載。例:
C:\Users\foo\Documents\Arduino
)にspot-server
フォルダを配置します
プロジェクトファイルの書き込み方法
スケッチ
ESP32 へのスケッチの書き込み方法 をご覧ください。
Web ページ
ESP32 へのファイルの書き込み方法 をご覧ください。
スケッチ
Arduino スケッチ spot-server.ino の解説です。
ライブラリのインクルード
Arduino および ESP32 公式ライブラリ
4-9行目では、Arduino および ESP32 の公式ライブラリをインクルードしています。
#include <Arduino.h>
#include <Arduino_JSON.h>
#include <ESPmDNS.h>
#include <LittleFS.h>
#include <WiFi.h>
#include <Wire.h>
ヘッダファイル | 内容 | 備考 |
---|---|---|
Arduino.h | Arduino の基本ライブラリ | 省略できる場合もあるが念のため記載 |
Arduino_JSON.h | JSON 文字列を扱う | ArduinoJson とは異なる |
ESPmDNS.h | mDNS を使う | ホスト名を使うために必要 |
LittleFS.h | LittleFS ファイルシステムを扱う | ページ公開に必要 |
WiFi.h | ESP32 の WiFi を使う | |
Wire.h | I2C を使う | OLED ディスプレイ用 |
サードパーティのライブラリ
12-14行目では、サードパーティのライブラリをインクルードしています。
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <SeeedGrayOLED.h>
ヘッダファイル | 内容 | 備考 |
---|---|---|
AsyncTCP.h | 非同期 TCP 通信を行う | |
ESPAsyncWebServer.h | 非同期 Web サーバを立てる | AsyncTCP に依存 |
SeeedGrayOLED.h | OLED ディスプレイを使う |
MWings ライブラリ
17行目では、MWings ライブラリをインクルードしています。
#include <MWings.h>
ピン番号の定義
20-24行目では、ピン番号を定義しています。
const uint8_t TWE_RST = 5;
const uint8_t TWE_PRG = 4;
const uint8_t LED = 18;
const uint8_t ESP_RXD1 = 16;
const uint8_t ESP_TXD1 = 17;
名称 | 内容 |
---|---|
TWE_RST | TWELITE の RST ピンが接続されているピンの番号 |
TWE_PRG | TWELITE の PRG ピンが接続されているピンの番号 |
LED | 基板上の ESP32 用 LED が接続されているピンの番号 |
ESP_RXD1 | TWELITE の TX ピンが接続されているピンの番号 |
ESP_TXD1 | TWELITE の RX ピンが接続されているピンの番号 |
TWELITE 設定の定義
27-30行目では、TWELITE SPOT に搭載された TWELITE 親機に適用する設定を定義しています。
const uint8_t TWE_CH = 18;
const uint32_t TWE_APPID = 0x67720102;
const uint8_t TWE_RETRY = 2;
const uint8_t TWE_POWER = 3;
名称 | 内容 |
---|---|
TWE_CH | TWELITE の 周波数チャネル |
TWE_APPID | TWELITE の アプリケーション ID |
TWE_RETRY | TWELITE の 再送回数(送信時) |
TWE_POWER | TWELITE の 送信出力 |
無線 LAN 設定の定義
33-38行目では、TWELITE SPOT に搭載された ESP32 に適用する無線 LAN 設定を定義しています。
const char* WIFI_SSID_BASE = "TWELITE SPOT";
const char* WIFI_PASSWORD = "twelitespot";
const uint8_t WIFI_CH = 13;
const IPAddress WIFI_IP = IPAddress(192, 168, 1, 1);
const IPAddress WIFI_MASK = IPAddress(255, 255, 255, 0);
const char* HOSTNAME = "spot"; // spot.local
名称 | 内容 |
---|---|
WIFI_SSID_BASE | SSID の共通部分の文字列 |
WIFI_PASSWORD | パスワード |
WIFI_CH | ESP32 の周波数チャネル |
WIFI_IP | IP アドレス |
WIFI_MASK | サブネットマスク |
HOSTNAME | ホスト名 |
グローバルオブジェクトの宣言
41-42行目では、グローバルオブジェクトを宣言しています。
AsyncWebServer server(80);
AsyncEventSource events("/events");
名称 | 内容 |
---|---|
server | 80番ポートで開く非同期 Web サーバのインタフェース |
events | /events で開くサーバー送信イベント ?のインタフェース |
関数プロトタイプの宣言
45-49行目では、関数プロトタイプを宣言しています。
uint16_t createUidFromMac();
String createJsonFrom(const ParsedAppTwelitePacket& packet);
String createJsonFrom(const ParsedAppAriaPacket& packet);
String createJsonFrom(const ParsedAppCuePacket& packet);
String createJsonFrom(const BarePacket& packet);
名称 | 内容 |
---|---|
createUidFromMac() | MAC アドレスから SSID に使う識別子を作ります |
createJsonFrom() <ParsedAppTwelitePacket&> | App_Twelite の |
createJsonFrom() <ParsedAppAriaPacket&> | App_ARIA の |
createJsonFrom() <ParsedAppCuePacket&> | App_CUE の |
createJsonFrom() <BarePacket&> | すべての |
TWELITE の設定
58-63行目では、Twelite.begin()
を呼び出し、TWELITE SPOT に搭載された TWELITE 親機の設定と起動を行っています。
Serial2.begin(115200, SERIAL_8N1, ESP_RXD1, ESP_TXD1);
if (Twelite.begin(Serial2,
LED, TWE_RST, TWE_PRG,
TWE_CH, TWE_APPID, TWE_RETRY, TWE_POWER)) {
Serial.println("Started TWELITE.");
}
引数 | 型 | 内容 |
---|---|---|
Serial2 | HardwareSerial& | TWELITE との通信に使うシリアルポート |
LED | int | ステータス LED を接続したピンの番号 |
TWE_RST | int | TWELITE の RST ピンを接続したピンの番号 |
TWE_PRG | int | TWELITE の PRG ピンを接続したピンの番号 |
TWE_CHANNEL | uint8_t | TWELITE の 周波数チャネル |
TWE_APP_ID | uint32_t | TWELITE の アプリケーション ID |
TWE_RETRY | uint8_t | TWELITE の 再送回数(送信時) |
TWE_POWER | uint8_t | TWELITE の 送信出力 |
App_Twelite:イベントハンドラの登録
65-72行目では、Twelite.on() <ParsedAppTwelitePacket>
を呼び出し、超簡単!標準アプリの子機からのパケットを受信した際に行う処理を登録しています。
Twelite.on([](const ParsedAppTwelitePacket& packet) {
Serial.println("Received a packet from App_Twelite");
String jsonStr = createJsonFrom(packet);
if (not(jsonStr.length() <= 0)) {
events.send(jsonStr.c_str(), "data_app_twelite", millis());
}
events.send("parsed_app_twelite", "data_parsing_result", millis());
});
JSON 文字列の作成
67行目では、受信したデータからJSON 文字列を生成しています。
String jsonStr = createJsonFrom(packet);
受信したデータをWeb ページに表示するにはクライアント側の JavaScript にデータを送る必要がありますが、このとき文字列データのほうが扱いやすいため、JSON 文字列としています。
Web ページへのイベント送信
68-70行目では、生成した JSON 文字列を “Signal Viewer” ページへ送信しています。
if (not(jsonStr.length() <= 0)) {
events.send(jsonStr.c_str(), "data_app_twelite", millis());
}
イベント名は data_app_twelite
です。
millis()
で取得した現在時刻を使用しています。71行目では、App_Twelite からのパケットを受信したことを “Serial Viewer” ページへ送信しています。
events.send("parsed_app_twelite", "data_parsing_result", millis());
App_ARIA:イベントハンドラの登録
74-84行目では、Twelite.on() <ParsedAppAriaPacket>
を呼び出し、アリアアプリ(TWELITE ARIA モード)の子機からのパケットを受信した際に行う処理を登録しています。
Twelite.on([](const ParsedAppAriaPacket& packet) {
Serial.println("Received a packet from App_ARIA");
static uint32_t firstSourceSerialId = packet.u32SourceSerialId;
if (packet.u32SourceSerialId == firstSourceSerialId) {
String jsonStr = createJsonFrom(packet);
if (not(jsonStr.length() <= 0)) {
events.send(jsonStr.c_str(), "data_app_aria_twelite_aria_mode", millis());
}
}
events.send("parsed_app_aria_twelite_aria_mode", "data_parsing_result", millis());
});
対象の絞り込み
76-77行目では、処理の対象を 最初に受信した子機 に限定しています。
static uint32_t firstSourceSerialId = packet.u32SourceSerialId;
if (packet.u32SourceSerialId == firstSourceSerialId) {
こうしておかないと、複数の子機があった際にグラフの一貫性が失われてしまうからです。
JSON 文字列の作成
78行目では、受信したデータからJSON 文字列を生成しています。
String jsonStr = createJsonFrom(packet);
Web ページへのイベント送信
79-81行目では、生成した JSON 文字列を “ARIA Viewer” ページへ送信しています。
if (not(jsonStr.length() <= 0)) {
events.send(jsonStr.c_str(), "data_app_aria_twelite_aria_mode", millis());
}
イベント名は data_app_aria_twelite_aria_mode
です。
83行目では、App_Twelite からのパケットを受信したことを “Serial Viewer” ページへ送信しています。
events.send("parsed_app_aria_twelite_aria_mode", "data_parsing_result", millis());
App_CUE:イベントハンドラの登録
86-96行目では、Twelite.on() <ParsedAppCuePacket>
を呼び出し、キューアプリ(TWELITE CUE モード)の子機からのパケットを受信した際に行う処理を登録しています。
Twelite.on([](const ParsedAppCuePacket& packet) {
Serial.println("Received a packet from App_CUE");
static uint32_t firstSourceSerialId = packet.u32SourceSerialId;
if (packet.u32SourceSerialId == firstSourceSerialId) {
String jsonStr = createJsonFrom(packet);
if (not(jsonStr.length() <= 0)) {
events.send(jsonStr.c_str(), "data_app_cue_twelite_cue_mode", millis());
}
}
events.send("parsed_app_cue_twelite_cue_mode", "data_parsing_result", millis());
});
その他:イベントハンドラの登録
98-126行目では、その他のアプリの子機からのパケットを受信した際に行う処理を登録しています。
アリアアプリ等と同様に “Serial Viewer” へイベントを送信しています。
すべて:イベントハンドラの登録
128-134行目では、すべてのアプリの子機からのパケットを受信した際に行う処理を登録しています。
Twelite.on([](const BarePacket& packet) {
String jsonStr = createJsonFrom(packet);
if (not(jsonStr.length() <= 0)) {
events.send(jsonStr.c_str(), "data_bare_packet", millis());
}
events.send("unparsed_bare_packet", "data_parsing_result", millis());
});
ここでも、“Serial Viewer” に対するパケットデータ文字列の送信を行っています。
OLED ディスプレイの設定
137-142行目では、OLED ディスプレイの設定を行っています。
Wire.begin();
SeeedGrayOled.init(SSD1327);
SeeedGrayOled.setNormalDisplay();
SeeedGrayOled.setVerticalMode();
SeeedGrayOled.setGrayLevel(0x0F);
SeeedGrayOled.clearDisplay();
無線 LAN の設定
146-154行目では、無線 LAN の設定を行っています。
WiFi.mode(WIFI_AP);
char uidCString[8];
sprintf(uidCString, " (%02X)", createUidFromMac());
char ssidCString[20];
sprintf(ssidCString, "%s%s", WIFI_SSID_BASE, uidCString);
WiFi.softAP(ssidCString, WIFI_PASSWORD, WIFI_CH, false, 8);
delay(100); // IMPORTANT: Waiting for SYSTEM_EVENT_AP_START
WiFi.softAPConfig(WIFI_IP, WIFI_IP, WIFI_MASK);
MDNS.begin(HOSTNAME);
delay(100)
を省略すると初期化に失敗することがあります。ファイルシステムの設定
187行目では、LittleFS ファイルシステムを設定しています。
if (LittleFS.begin()) { Serial.println("Mounted file system."); }
フラッシュ領域内に書き込んだ HTML などのファイルをページとして取得することができるようになります。
Web サーバの設定
190-217行目では、Web サーバの設定を行っています。
GET リクエストのハンドリング
例えば、195-199行目 では /signal-viewer
への GET リクエストに対して、LittleFS ファイルシステム上の /signal-viewer.html
を返しています。
server.on("/signal-viewer", HTTP_GET,
[](AsyncWebServerRequest* request) {
Serial.println("HTTP_GET: signal-viewer.html");
request->send(LittleFS, "/signal-viewer.html", "text/html");
});
サーバの初期化
215-217行目では、ファイルシステム上のルートをサーバのルートとして設定したあと、イベントソースを登録してサーバを立ち上げています。
server.serveStatic("/", LittleFS, "/");
server.addHandler(&events);
server.begin();
TWELITE のデータの更新
223行目では、Twelite.update()
を呼び出しています。
Twelite.update();
Twelite.update()
は、TWELITE 親機から送信されるパケットデータ(ModBus ASCII 形式)を順次1バイトずつ読み出す関数です。
loop()
内で繰り返し Twelite.update()
を呼ぶことで、TWELITE 親機から送信されるパケットデータの解釈が進みます。パケットデータの解釈を終えた際に 上記 のようなイベントが呼ばれる仕組みです。delay()
などの処理でこの関数の呼び出しをブロックすると、パケットデータ文字列の読み出しが間に合わないことがあります。時間のかかる処理は必ず非同期の実装として、loop()
関数をできるだけ高速回転させるようにしてください。Web ページ
Web ページに関しては、詳しい解説を行いません。重要なポイントに絞って解説します。
HTML:グリッドシステム
このサンプルの HTML では、Flexbox Grid を使っています(ソースファイルは data/css/flexboxgrid.min.css)。
下記のようにして Bootstrap に似た 12 分割のグリッドシステムを使用しています。
<div class="col-xs-6 col-sm-6 col-md-5 col-lg-4">
<div class="neumorphic inset dense row center-xs middle-xs">
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 npr npl">
<img src="./images/logo-lands.svg" class="logo" />
</div>
</div>
</div>
<div class="col-xs-6 col-sm-6 col-md-7 col-lg-8">
<div class="neumorphic inset dense row center-xs middle-xs">
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 nwp npr npl">
<span class="medium bold">TWELITE SPOT</span>
</div>
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 nwp npr npl">
<span class="small bold">CUE Viewer</span>
</div>
</div>
</div>
ここでは、ロゴを中心とした要素の幅を 6/12 、文字列を中心とした要素の幅を 6/12 、すなわち両者を等しい幅で一列に配置しています。また、文字列 TWELITE SPOT
を中心とした要素と CUE Viewer
を中心とした要素の幅はどちらも 12/12 、すなわち1行ずつ2行に分けて配置しています。
xs-
や sm-
などは画面の幅を指定します。レスポンシブデザインに活用できます。HTML:データ表示部
TWELITE 子機から受信したデータを表示する要素には、一意の ID を付与しています。
以下は TWELITE CUE から受信した X 軸加速度を表示する部分の抜粋です。
<div class="col-xs-4 nwp npr npl">
<code class="medium"
id="latest-accel-x">±--.--</code>
<code class="small">G</code>
</div>
ここでは、ID として latest-accel-x
を付与しています。この ID を使って、スクリプトから値を書き換えます。
JS:グローバル変数
ここからは各 HTML ファイルに対応したスクリプトの解説です。
例として、ここでは data/js/cue-viewer.js を取り上げます。
4-8行目では、最新の加速度値を保存するためのグローバル変数を宣言しています。
let latest_accel = {
x: 0.0,
y: 0.0,
z: 0.0
};
この値はグラフからも利用するため、実装を簡素にするためにグローバル変数を使用しています。
JS:グラフ設定
11-133行目では、グラフ描画ライブラリ Chart.js | Chart.js およびそのプラグイン chartjs-plugin-streaming の設定を行っています。
JS:ページ内容の更新
136-235行目の関数 processDataAppCueTweliteCueMode()
は、スケッチから data_app_cue_twelite_cue_mode
イベントを受信した際にページ内容を更新する関数です。
例えば、184-208行目では、TWELITE CUE の電源電圧に応じて電圧値と絵文字を更新しています。
if (data.vcc >= 3000) {
document.getElementById("latest-vcc-icon").innerHTML = "🔋";
document.getElementById("latest-vcc-data").innerHTML = `${(data.vcc / 1000.0).toFixed(2).toString().padStart(4)}`;
document.getElementById("latest-vcc-data").classList.remove("red");
document.getElementById("latest-vcc-data").classList.remove("yellow");
document.getElementById("latest-vcc-data").classList.add("green");
} else if (data.vcc >= 2700) {
document.getElementById("latest-vcc-icon").innerHTML = "🔋";
document.getElementById("latest-vcc-data").innerHTML = `${(data.vcc / 1000.0).toFixed(2).toString().padStart(4)}`;
document.getElementById("latest-vcc-data").classList.remove("red");
document.getElementById("latest-vcc-data").classList.remove("yellow");
document.getElementById("latest-vcc-data").classList.remove("green");
} else if (data.vcc >= 2400) {
document.getElementById("latest-vcc-icon").innerHTML = "🪫";
document.getElementById("latest-vcc-data").innerHTML = `${(data.vcc / 1000.0).toFixed(2).toString().padStart(4)}`;
document.getElementById("latest-vcc-data").classList.remove("red");
document.getElementById("latest-vcc-data").classList.add("yellow");
document.getElementById("latest-vcc-data").classList.remove("green");
} else {
document.getElementById("latest-vcc-icon").innerHTML = "🪫";
document.getElementById("latest-vcc-data").innerHTML = `${(data.vcc / 1000.0).toFixed(2).toString().padStart(4)}`;
document.getElementById("latest-vcc-data").classList.add("red");
document.getElementById("latest-vcc-data").classList.remove("yellow");
document.getElementById("latest-vcc-data").classList.remove("green");
}
ここでは、電源電圧が 2700mV 未満に降下した際に絵文字を 🔋 から 🪫 に変えているほか、3000mV → 2700mV → 2400mV と電圧降下に従って電圧値の文字色を適用する CSS クラスを入れ替えています。
イベントリスナーの登録
254-257行目では、スケッチからのイベントを受信した際の処理を登録しています。
source.addEventListener("data_app_cue_twelite_cue_mode", (e) => {
console.log("data_app_cue_twelite_cue_mode", e.data);
processDataAppCueTweliteCueMode(JSON.parse(e.data));
}, false);
ここでは、スケッチより 受信したイベントメッセージから JSON 文字列を取り出し、パースしたデータを先ほどの関数 processDataAppCueTweliteCueMode()
へ渡しています。
関連情報
Arduino
- 公式サイト:Arduino - Home
ESP32
- 製品情報:ESP32 Wi-Fi & Bluetooth MCU I Espressif Systems
- データシート:esp32_datasheet_en.pdf
- Arduino 向けツールチェイン:espressif/arduino-esp32: Arduino core for the ESP32
- スタートガイド:Getting Started — Arduino-ESP32 documentation
- 導入方法:Installing — Arduino-ESP32 documentation
- API リファレンス:Libraries — Arduino-ESP32 documentation
- Wi-Fi API:Wi-Fi API — Arduino-ESP32 documentation
- チュートリアル:Tutorials — Arduino-ESP32 documentation
- トラブルシューティング:Troubleshooting — Arduino-ESP32 documentation
コミュニティ
ライブラリ
- 非同期 TCP:me-no-dev/AsyncTCP: Async TCP Library for ESP32
- 非同期 Web サーバ:me-no-dev/ESPAsyncWebServer: Async Web Server for ESP8266 and ESP32
- Seeed 96x96 / 128x128 OLED:Seeed-Studio/OLED_Display_96X96: Seeed OLED Display 96*96 library
プラグイン
- ファイル書き込み:me-no-dev/arduino-esp32fs-plugin: Arduino plugin for uploading files to ESP32 file system
- スタックトレース:me-no-dev/EspExceptionDecoder: Exception Stack Trace Decoder for ESP8266 and ESP32
Web関連
ECMAScript (JavaScript)
- API リファレンス:開発者向けのウェブ技術 | MDN
- ES2016以降のバージョン別互換性リスト:ECMAScript 2016+ compatibility table
コミュニティ
- CSS
- Web カラーとその配色例:世界の伝統色 洋色大辞典 - Traditional Colors of World
- Bootstrap のようなグリッドシステム:Flexbox Grid
- ニューモーフィズムな CSS ジェネレータ:Neumorphism/Soft UI CSS shadow generator
- ECMAScript
- グラフ描画:Chart.js | Chart.js
- リアルタイムストリーミングプラグイン:はじめに | chartjs-plugin-streaming
- 時計と時刻:Luxon Home
- グラフ描画:Chart.js | Chart.js
5.2.2 - WebSocketによる中継
spot-router
の解説です。5.2.2.1 - WebSocketによる中継
spot-router
の解説です。本稿では、サードパーティのオープンソースソフトウェアを使用します。
サードパーティのソフトウェアについて、その詳しい使用方法を弊社からご案内することはいたしかねます。また、サードパーティのソフトウェアを使用されたことによるいかなる損害についても、弊社は一切の責任を負いません。
ソースコードの入手
GitHub (monowireless/spot-router) から入手できます。
システムの概要
spot-router は、TWELITE 親機が受信したデータに基づき出力した文字列(App_Wings の ModBus ASCII 形式)を WebSocket サーバへ転送します。
開発に必要なもの
-
無線LANゲートウェイ TWELITE SPOT
- 電源用 USB-C ケーブル
- USB AC アダプタ(1A 以上供給できるもの)
-
加速度センサー無線タグ TWELITE CUEなどの子機 (お持ちでない場合はご購入ください 👉 販売店一覧)
- CR2032 コイン電池 などの電源
-
USBアダプター TWELITE R3 (お持ちでない場合はご購入ください 👉 販売店一覧)
- 通信用 USB-C ケーブル
- 💻 WebSocket サーバ
- 💻 開発用コンピュータ
環境整備
IDE とツールチェインの導入
Arduino IDE 1.x による開発環境の構築方法 をご覧ください。
ライブラリの導入
はじめに、Arduino のスケッチブックの保存場所(Arduino IDE 環境設定に記載。例:C:\Users\foo\Documents\Arduino
) に libraries
フォルダがない場合は、これを作成します。
WebSocket ライブラリ
- GitHub (Links2004/arduinoWebSockets) から Zip ファイルをダウンロードします
- Zip ファイルを展開し、
libraries
フォルダにarduinoWebSockets-<バージョン>
フォルダを配置します
プロジェクトファイルの入手
- GitHub (monowireless/spot-router) から Zip ファイルをダウンロードします
- Zip ファイルを展開し、フォルダ名を
spot-router-main
からspot-router
に変更します - Arduino のスケッチブックの保存場所(Arduino IDE 環境設定に記載。例:
C:\Users\foo\Documents\Arduino
)にspot-router
フォルダを配置します
ユーザ設定の変更
Arduino IDE 上部のタブから config.h
を開き、無線 LAN や WebSocket サーバに関する設定( 詳細 )を変更してください。
プロジェクトファイルの書き込み方法
ESP32 へのスケッチの書き込み方法 をご覧ください。
スケッチ
Arduino スケッチ spot-router.ino の解説です。
ライブラリのインクルード
Arduino および ESP32 公式ライブラリ
4-5行目では、Arduino および ESP32 の公式ライブラリをインクルードしています。
#include <Arduino.h>
#include <WiFi.h>
ヘッダファイル | 内容 | 備考 |
---|---|---|
Arduino.h | Arduino の基本ライブラリ | 省略できる場合もあるが念のため記載 |
WiFi.h | ESP32 の WiFi を使う |
サードパーティのライブラリ
8行目では、サードパーティのライブラリをインクルードしています。
#include <WebSocketsClient.h>
ヘッダファイル | 内容 | 備考 |
---|---|---|
WebSocketsClient.h | WebSocket クライアントになる |
MWings ライブラリ
11行目では、MWings ライブラリをインクルードしています。
#include <MWings.h>
ユーザ設定の定義
14行目では、config.h
をインクルードしています。
#include "config.h"
config.h
では、ユーザ設定を定義しています。実行時には、この設定を書き換えてください。無線 LAN 設定の定義
config.h
の4-5行目では、TWELITE SPOT に搭載された ESP32 に適用する無線 LAN 設定を定義しています。
const char* WIFI_SSID = "YOUR SSID"; // Modify it
const char* WIFI_PASSWORD = "YOUR PASSWORD"; // Modify it
名称 | 内容 |
---|---|
WIFI_SSID | 接続するネットワークの SSID |
WIFI_PASSWORD | 接続するネットワークの パスワード |
WebSocket 設定の定義
config.h
の8-10行目では、WebSocket クライアントの設定を定義しています。
const char* WS_SERVER_IP = "YOUR ADDRESS"; // Modify it
const int WS_SERVER_PORT = 8080;
const char* WS_SERVER_PATH = "/";
名称 | 内容 |
---|---|
WS_SERVER_IP | 送信するサーバの IP アドレス |
WS_SERVER_PORT | 送信するサーバのポート番号 |
WS_SERVER_PATH | 送信するサーバの WebSocket サーバのパス |
ピン番号の定義
17-21行目では、ピン番号を定義しています。
const uint8_t TWE_RST = 5;
const uint8_t TWE_PRG = 4;
const uint8_t LED = 18;
const uint8_t ESP_RXD1 = 16;
const uint8_t ESP_TXD1 = 17;
名称 | 内容 |
---|---|
TWE_RST | TWELITE の RST ピンが接続されているピンの番号 |
TWE_PRG | TWELITE の PRG ピンが接続されているピンの番号 |
LED | 基板上の ESP32 用 LED が接続されているピンの番号 |
ESP_RXD1 | TWELITE の TX ピンが接続されているピンの番号 |
ESP_TXD1 | TWELITE の RX ピンが接続されているピンの番号 |
TWELITE 設定の定義
24-27行目では、TWELITE SPOT に搭載された TWELITE 親機に適用する設定を定義しています。
const uint8_t TWE_CH = 18;
const uint32_t TWE_APPID = 0x67720102;
const uint8_t TWE_RETRY = 2;
const uint8_t TWE_POWER = 3;
名称 | 内容 |
---|---|
TWE_CH | TWELITE の 周波数チャネル |
TWE_APPID | TWELITE の アプリケーション ID |
TWE_RETRY | TWELITE の 再送回数(送信時) |
TWE_POWER | TWELITE の 送信出力 |
グローバルオブジェクトの宣言
30行目では、グローバルオブジェクトを宣言しています。
WebSocketsClient webSocket;
名称 | 内容 |
---|---|
webSocket | WebSocket クライアントのインタフェース |
関数プロトタイプの宣言
33行目では、関数プロトタイプを宣言しています。
String createPacketStringFrom(const BarePacket& packet);
名称 | 内容 |
---|---|
createPacketStringFrom() | 受信したパケットデータから書式文字列を再構築します |
TWELITE の設定
42-47行目では、Twelite.begin()
を呼び出し、TWELITE SPOT に搭載された TWELITE 親機の設定と起動を行っています。
Serial2.begin(115200, SERIAL_8N1, ESP_RXD1, ESP_TXD1);
if (Twelite.begin(Serial2,
LED, TWE_RST, TWE_PRG,
TWE_CH, TWE_APPID, TWE_RETRY, TWE_POWER)) {
Serial.println("Started TWELITE.");
}
引数 | 型 | 内容 |
---|---|---|
Serial2 | HardwareSerial& | TWELITE との通信に使うシリアルポート |
LED | int | ステータス LED を接続したピンの番号 |
TWE_RST | int | TWELITE の RST ピンを接続したピンの番号 |
TWE_PRG | int | TWELITE の PRG ピンを接続したピンの番号 |
TWE_CHANNEL | uint8_t | TWELITE の 周波数チャネル |
TWE_APP_ID | uint32_t | TWELITE の アプリケーション ID |
TWE_RETRY | uint8_t | TWELITE の 再送回数(送信時) |
TWE_POWER | uint8_t | TWELITE の 送信出力 |
イベントハンドラの登録
49-54行目では、すべてのアプリの子機からのパケットを受信した際に行う処理を登録しています。
Twelite.on([](const BarePacket& packet) {
String packetStr = createPacketStringFrom(packet);
if (not(packetStr.length() <= 0)) {
webSocket.sendTXT(packetStr.c_str());
}
});
ここでは、パケットデータから書式文字列(ModBus ASCII 形式)を再構成し、WebSocket サーバへ送信しています。
無線 LAN の設定
57-71行目では、無線 LAN の設定を行っています。
WiFi.mode(WIFI_STA);
WiFi.setAutoReconnect(true);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.print("Connecting to WiFi ..");
while (WiFi.status() != WL_CONNECTED) {
static int count = 0;
Serial.print('.');
delay(500);
// Retry every 5 seconds
if (count++ % 10 == 0) {
WiFi.disconnect();
WiFi.reconnect();
Serial.print('!');
}
}
ここでは、無線 LAN 子機として設定したうえで、指定のネットワークへ接続しています。
while
文を出ません。WebSocket の設定
76-77行目では、WebSocket を設定しています。
webSocket.begin(WS_SERVER_IP, WS_SERVER_PORT, WS_SERVER_PATH);
webSocket.setReconnectInterval(5000);
ここでは、WebSocket サーバと再接続間隔を指定しています。
また、78-97行目では、サーバとの接続が切断されたとき、サーバと接続したとき、そしてメッセージを受信したときのイベントを登録しています。
webSocket.onEvent([](WStype_t type, uint8_t* payload, size_t length) {
switch (type) {
case WStype_DISCONNECTED: {
Serial.println("Disconnected!");
break;
}
case WStype_CONNECTED: {
Serial.print("Connected to url: ");
Serial.println(reinterpret_cast<char*>(payload));
webSocket.sendTXT("This is TWELITE SPOT to ground control");
break;
}
case WStype_TEXT: {
Serial.print("Got text: ");
Serial.println(reinterpret_cast<char*>(payload));
break;
}
default: break;
}
});
なかでも、サーバと接続したときには、サーバへメッセージを送るようにしています。
webSocket.sendTXT("This is TWELITE SPOT to ground control");
TWELITE のデータの更新
102行目では、Twelite.update()
を呼び出しています。
Twelite.update();
Twelite.update()
は、TWELITE 親機から送信されるパケットデータ(ModBus ASCII 形式)を順次1バイトずつ読み出す関数です。
loop()
内で繰り返し Twelite.update()
を呼ぶことで、TWELITE 親機から送信されるパケットデータの解釈が進みます。パケットデータの解釈を終えた際に 上記 のようなイベントが呼ばれる仕組みです。delay()
などの処理でこの関数の呼び出しをブロックすると、パケットデータ文字列の読み出しが間に合わないことがあります。時間のかかる処理は必ず非同期の実装として、loop()
関数をできるだけ高速回転させるようにしてください。WebSocket のデータの更新
103行目では、WebSocket のデータを更新する処理を呼び出しています。
webSocket.loop();
Twelite.update()
と同様に、delay()
などの処理でこの関数の呼び出しをブロックすると、データ更新が間に合わないことがあります。時間のかかる処理は必ず非同期の実装として、loop()
関数をできるだけ高速回転させるようにしてください。<付録> WebSocket サーバによる動作確認
extra/python-websocket-server/server.py
は、Python スクリプトによって WebSocket サーバを立て、ESP32 からのパケットデータ文字列を表示するサンプルスクリプトです。このスクリプトを使うことで、スケッチの動作を確認できます。
# -*- coding: utf-8-unix -*-
# Python 3.11
import logging
from websocket_server import WebsocketServer
def new_client(client, server):
server.send_message_to_all("This is ground control to TWELITE SPOT")
def new_message(client, server, message):
print("Received an message:")
print(message)
server = WebsocketServer(host="YOUR IP ADDRESS", port=8080, loglevel=logging.INFO)
server.set_fn_new_client(new_client)
server.set_fn_message_received(new_message)
server.run_forever()
coding
変数を指定しているのは、筆者の環境が Emacs だからです。おまじないではありません。
動作確認の手順
スクリプトの実行
依存モジュール をインストールしてから実行します。
pip3 install websocket-server
python3 server.py
実行すると、下記のようなメッセージが表示されます。
INFO:websocket_server.websocket_server:Listening on port 8080 for clients..
INFO:websocket_server.websocket_server:Starting WebsocketServer on main thread.
クライアントの接続を確認
ESP32 が 無線 LAN への接続に成功すると、WebSocket サーバへの接続を試みます。
接続に成功すると、クライアント側のシリアルコンソールには下記のように出力されます。
Started TWELITE.
Connecting to WiFi .....
Connected. IP: xxx.xxx.xxx.xxx
Connected to url: /
Got text: This is ground control to TWELITE SPOT
一方で、サーバ側のターミナルには下記のように出力されます。
Received an message:
This is TWELITE SPOT to ground control
以降、TWELITE SPOT が子機からのパケットを受信すると、下記のようにサーバ側のターミナルへパケットデータ文字列が出力されます。
Received an message:
:80000000DE10098201BC8201800607003400038135001205350401000000113008020A8C1130010203AF0000000180050100020AC60102000211D7AF30
Received an message:
:80000000E4100A8201BC8201800607003400038135001205350401000000113008020A8C1130010203AC0000000180050100020AC40102000211DB0DCC
関連情報
TWELITE
- パケットデータ文字列の出力形式:親機・中継機アプリ マニュアル 受信メッセージ
Arduino
- 公式サイト:Arduino - Home
ESP32
- 製品情報:ESP32 Wi-Fi & Bluetooth MCU I Espressif Systems
- データシート:esp32_datasheet_en.pdf
- Arduino 向けツールチェイン:espressif/arduino-esp32: Arduino core for the ESP32
- スタートガイド:Getting Started — Arduino-ESP32 documentation
- 導入方法:Installing — Arduino-ESP32 documentation
- API リファレンス:Libraries — Arduino-ESP32 documentation
- Wi-Fi API:Wi-Fi API — Arduino-ESP32 documentation
- チュートリアル:Tutorials — Arduino-ESP32 documentation
- トラブルシューティング:Troubleshooting — Arduino-ESP32 documentation
コミュニティ
ライブラリ
プラグイン
ネットワーク関連
WebSocket
コミュニティ
5.2.3 - REST API の使用
spot-httpbin
の解説です。5.2.3.1 - REST API の使用
spot-httpbin
の解説です。本稿では、サードパーティのオープンソースソフトウェアを使用します。
サードパーティのソフトウェアについて、その詳しい使用方法を弊社からご案内することはいたしかねます。また、サードパーティのソフトウェアを使用されたことによるいかなる損害についても、弊社は一切の責任を負いません。
ソースコードの入手
GitHub リポジトリ monowireless/spot-httpbin から入手できます。
システムの概要
spot-httpbin は、TWELITE 親機が受信したデータの一部と NTP による現在時刻を HTTP GET リクエストとしてモックサーバへ送信し、そのレスポンスをシリアルモニタへ表示します。
開発に必要なもの
-
無線LANゲートウェイ TWELITE SPOT
- 電源用 USB-C ケーブル
- USB AC アダプタ(1A 以上供給できるもの)
-
磁気・温度・湿度センサー無線タグ TWELITE ARIAなどの子機 (お持ちでない場合はご購入ください 👉 販売店一覧)
- CR2032 コイン電池 などの電源
-
USBアダプター TWELITE R3 (お持ちでない場合はご購入ください 👉 販売店一覧)
- 通信用 USB-C ケーブル
- 💻 開発用コンピュータ
環境整備
IDE とツールチェインの導入
Arduino IDE 1.x による開発環境の構築方法 をご覧ください。
ライブラリの導入
このサンプルでは、依存するライブラリをはじめから同梱しています。
src
フォルダの内容は IDE に表示されませんが、再帰的にビルドされます。プロジェクトファイルの入手
- GitHub (monowireless/spot-httpbin) から Zip ファイルをダウンロードします
- Zip ファイルを展開し、フォルダ名を
spot-httpbin-main
からspot-httpbin
に変更します - Arduino のスケッチブックの保存場所(Arduino IDE 環境設定に記載。例:
C:\Users\foo\Documents\Arduino
)にspot-httpbin
フォルダを配置します
ユーザ設定の変更
Arduino IDE 上部のタブから config.h
を開き、Wi-Fi の SSID と パスワードを設定してください。WPA2-PSK ネットワークを想定しています。また、ルート証明書も登録してください。ルート証明書は、Chrome などのウェブブラウザの各ページに対するセキュリティ画面から入手できます。
ルート証明書(拡張子.cer
)は、下記のような形式のテキストファイルです。
-----BEGIN CERTIFICATE-----
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-----END CERTIFICATE-----
プロジェクトファイルの書き込み方法
ESP32 へのスケッチの書き込み方法 をご覧ください。
スケッチ
Arduino スケッチ spot-httpbin.ino
および config.h
の解説です。
ライブラリのインクルード
Arduino および ESP32 公式ライブラリ
4-6行目では、Arduino および ESP32 の公式ライブラリをインクルードしています。
#include <Arduino.h>
#include <WiFiClientSecure.h>
#include <WiFiUdp.h>
ヘッダファイル | 内容 | 備考 |
---|---|---|
Arduino.h | Arduino の基本ライブラリ | 省略できる場合もあるが念のため記載 |
WiFiClientSecure.h | ESP32 で SSL通信を行う | |
WiFiUdp.h | UDP 通信を行う | NTP に必要 |
サードパーティのライブラリ
9-10行目では、同梱されたサードパーティのライブラリをインクルードしています。
#include "src/NTPClient/NTPClient.h"
#include "src/Time/TimeLib.h"
ヘッダファイル | 内容 | 備考 |
---|---|---|
NTPClient.h | NTP サーバへアクセスする | |
TimeLib.h | エポック時間を変換する |
MWings ライブラリ
13行目では、MWings ライブラリをインクルードしています。
#include <MWings.h>
ユーザ設定の定義
16行目では、config.h
をインクルードしています。
#include "config.h"
config.h
では、ユーザ設定を定義しています。実行時には、この設定を書き換えてください。データ型の定義
19-26行目では、子機から受信したデータを保管しておく構造体の型を定義しています。
struct DataFromAria {
uint32_t serialId;
uint8_t logicalId;
uint16_t supplyVoltage;
uint8_t linkQuality;
int16_t temp100x;
uint16_t humid100x;
};
名称 | 内容 |
---|---|
serialId | シリアルID |
logicalId | 論理デバイスID |
supplyVoltage | 電源電圧 |
linkQuality | LQI |
temp100x | 100倍された温度 |
humid100x | 100倍された湿度 |
ここでは、TWELITE ARIA を使用します。
config.h
再起動間隔の定義
config.h
の4行目では、ESP32 の再起動間隔を指定しています。
const uint32_t REBOOT_INTERVAL = 21600; // seconds
ここでは、21600秒=6時間としています。
長期間の運用では、メモリリークが累積して不具合を起こしてしまう場合があります。
そこで Wi-Fi ルータのように定期的な再起動を行うようにしています。
TWELITE 設定の定義
config.h
の7-8行目では、TWELITE SPOT に搭載された TWELITE 親機に適用する設定を定義しています。
const uint8_t TWE_CH = 18;
const uint32_t TWE_APPID = 0x67720102;
名称 | 内容 |
---|---|
TWE_CH | TWELITE の 周波数チャネル |
TWE_APPID | TWELITE の アプリケーション ID |
Wi-Fi 設定の定義
config.h
の11-12行目では、TWELITE SPOT に搭載された ESP32 に適用するWi-Fi 設定を定義しています。
const char* WIFI_SSID = "YOUR SSID";
const char* WIFI_PASSWORD = "YOUR PASSWORD";
名称 | 内容 |
---|---|
WIFI_SSID | 接続するネットワークの SSID |
WIFI_PASSWORD | 接続するネットワークの パスワード |
ルート証明書
config.h
の14-16行目では、ルート証明書の内容を記述するためのテンプレートを用意しています。
const char *CA_CERT =
"-----BEGIN CERTIFICATE-----\n"
"-----END CERTIFICATE-----\n";
ルート証明書は、Chrome などのウェブブラウザの各ページに対するセキュリティ画面から入手してください。
すべての行をダブルクォートで囲い、末尾のダブルクォートの前には改行文字 \n
を追加する必要があります。
ホストの設定
config.h
の18-19行目では、ホストの設定を定義しています。
const char *SERVER_HOST = "www.httpbin.org";
const uint16_t SERVER_PORT = 443;
名称 | 内容 |
---|---|
SERVER_HOST | サーバのホスト名 |
SERVER_PORT | サーバのポート番号 |
各種定数の定義
config.h
の21行目からは、各種定数を定義しています。
const uint32_t NTP_UPDATE_INTERVAL = 10000; // ms
const int QUERIES_MAX_LENGTH = 128; // bytes (without \0)
const int32_t CONNECT_TIMEOUT = 10; // seconds
const uint32_t RECONNECT_MIN_INTERVAL = 5; // seconds
// SEND_MIN_INTERVAL must be longer than NTP_UPDATE_INTERVAL
const uint32_t SEND_MIN_INTERVAL = 10; // seconds
const uint32_t REQUEST_TIMEOUT = 10; // seconds
名称 | 内容 |
---|---|
NTP_UPDATE_INTERVAL | NTP時刻の取得間隔 |
QUERIES_MAX_LENGTH | クエリ文字列の最大長(ヌル文字含まず) |
CONNECT_TIMEOUT | サーバへの接続時のタイムアウト |
RECONNECT_MIN_INTERVAL | Wi-Fiアクセスポイントへ再接続する際の最短間隔 |
SEND_MIN_INTERVAL | リクエスト間隔の最短間隔 |
REQUEST_TIMEOUT | リクエストからレスポンスまでのタイムアウト |
SEND_MIN_INTERVAL
は、NTP_UPDATE_INTERVAL
以上に設定しています。
リクエスト間隔が短いと、リクエスト間でタイムスタンプが重複してしまう可能性があるからです。
SEND_MIN_INTERVAL
を短くすると、連続してパケットを受信した場合にサーバへ負担をかけてしまいます。
必ず適度な間隔を空けてください。
ピン番号の定義
29-34行目では、ピン番号を定義しています。
static const int RST_PIN = 5;
static const int PRG_PIN = 4;
static const int LED_PIN = 18;
static const int8_t RX1_PIN = 16;
static const int8_t TX1_PIN = 17;
名称 | 内容 |
---|---|
RST_PIN | TWELITE の RST ピンが接続されているピンの番号 |
PRG_PIN | TWELITE の PRG ピンが接続されているピンの番号 |
LED_PIN | 基板上の ESP32 用 LED が接続されているピンの番号 |
RX1_PIN | TWELITE の RX1 ピンが接続されているピンの番号 |
TX1_PIN | TWELITE の TX1 ピンが接続されているピンの番号 |
グローバルオブジェクトの宣言
37-40行目では、グローバルオブジェクトを宣言しています。
static WiFiClientSecure client;
static WiFiUDP ntpUDP;
static NTPClient timeClient(ntpUDP, "ntp.nict.jp",
32400, NTP_UPDATE_INTERVAL); // JST(UTC+9)
名称 | 内容 |
---|---|
client | HTTPS通信のインタフェース |
ntpUDP | NTP用のUDP通信のインタフェース |
timeClient | NTPのインタフェース |
グローバル変数の宣言
43-44行目では、グローバル変数を宣言しています。
static DataFromAria LatestDataFromAria;
static bool IsThereNewDataFromAria;
名称 | 内容 |
---|---|
LatestDataFromAria | TWELITE ARIA から受信した最新のデータ |
IsThereNewDataFromAria | TWELITE ARIA から新たなデータを受信したことを示すフラグ |
関数プロトタイプの宣言
47-59行目では、関数プロトタイプを宣言しています。
void anotherLoopForTWELITE();
void anotherLoopForNTP();
名称 | 内容 |
---|---|
anotherLoopForTWELITE | TWELITEのデータを処理するためのループ関数 |
anotherLoopForNTP | NTPで時刻を取得するためのループ関数 |
xTaskCreatePinnedToCore()
により、別のタスクとして登録しています。
void initTWELITE();
void initWiFi();
void initNTP();
名称 | 内容 |
---|---|
initTWELITE | TWELITEの初期化関数 |
initWiFi | Wi-Fiの初期化関数 |
initNTP | NTPの初期化関数 |
void onAppAriaPacket(const ParsedAppAriaPacket& packet);
名称 | 内容 |
---|---|
onAppAriaPacket | TWELITE ARIA からデータを受信した際のコールバック関数 |
void sendAriaData(const DataFromAria& data)
名称 | 内容 |
---|---|
sendAriaData | TWELITE ARIAのデータを HTTP GET リクエストにのせて送る関数 |
setup()
62-90行目では、全体の初期化を行います。
void setup() {
Serial.begin(115200);
initTWELITE();
initWiFi();
initNTP();
// Attach another loop function for TWELITE
// Note: Core 0 is also used for the WiFi task, which priority is 19 (ESP_TASKD_EVENT_PRIO - 1)
xTaskCreatePinnedToCore(
[](void *params) {
while (true) {
anotherLoopForTWELITE();
vTaskDelay(1); // IMPORTANT for Watchdog
}
},
"Task for anotherLoopForTWELITE()", 8192, nullptr, 18, nullptr,
0); // Priority is 18 (lower than WiFi)
// Attach another loop function for NTP
xTaskCreatePinnedToCore(
[](void *params) {
while (true) {
anotherLoopForNTP();
vTaskDelay(1); // IMPORTANT for Watchdog
}
},
"Task for anotherLoopForNTP()", 8192, nullptr, 17, nullptr,
0); // Priority is 17 (lower than WiFi and TWELITE)
}
xTaskCreatePinnedToCore()
により、loop()
とは別のタスクを登録しています。
下記の部分はキャプチャのない無名関数です。不要なグローバル空間の汚染を避けることができます。
[](void *params) {
while (true) {
anotherLoopForTWELITE();
vTaskDelay(1); // IMPORTANT for Watchdog
}
},
vTaskDelay()
を挿入しています。loop()
93-114行目は、主となるループ処理です。
HTTP リクエストの処理、Wi-Fi 切断時の再接続処理、定期リセットの処理を行います。
void loop() {
static uint32_t lastTimeReconnected = 0;
if (WiFi.status() == WL_CONNECTED) {
// Regular operations
// Check for new data
if (IsThereNewDataFromAria) {
IsThereNewDataFromAria = false; // Clear first; data is updated on another thread
DataFromAria data = LatestDataFromAria; // Now, the buffer is open for incoming data
sendAriaData(data);
}
} else if (millis() - lastTimeReconnected > RECONNECT_MIN_INTERVAL * 1000) {
// Lost connection, reconnect periodically
Serial.println("Disconnected. Reconnecting to WiFi...");
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
lastTimeReconnected = millis();
}
// Reboot every x interval
if (millis() > REBOOT_INTERVAL * 1000) {
Serial.println("Rebooting...");
ESP.restart();
}
}
anotherLoopForTWELITE()
117-119行目は、TWELITE のためのループ処理です。
データの受信と解釈を逐次行うため、ブロッキング処理を含む loop()
とは別のタスクとしています。
void anotherLoopForTWELITE() {
Twelite.update();
}
anotherLoopForNTP()
120-123行目は、NTP のためのループ処理です。
こちらについても UDP の通信を行うため、ブロッキング処理を含む loop()
とは別のタスクとしています。
void anotherLoopForNTP() {
timeClient.update();
setTime(timeClient.getEpochTime());
}
initTWELITE()
126-133行目は、TWELITE の初期化処理です。
TWELITE SPOT に搭載された TWELITE を指定された設定で起動し、パケット受信時のコールバック関数を登録しています。
void initTWELITE() {
Serial2.begin(115200);
if (Twelite.begin(Serial2, LED_PIN, RST_PIN, PRG_PIN, TWE_CHANNEL, TWE_APP_ID)) {
Serial.println("Started TWELITE.");
}
// Attach event handlers to process packets
Twelite.on(onAppAriaPacket);
}
下記リファレンスも合わせてご覧ください。
Twelite.begin()
mwings::MWings クラス | MWings API リファレンスTwelite.on()
mwings::MWings クラス | MWings API リファレンス
initWiFi()
136-160行目は、Wi-Fi の初期化処理です。
接続されない場合は、5秒置きに再接続を試みます。
void initWiFi() {
Serial.print("\nConnecting to the WiFi network ");
Serial.print(WIFI_SSID);
Serial.println("...");
// Begin
WiFi.mode(WIFI_STA);
WiFi.setAutoReconnect(true);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
// Wait for connection
Serial.print("Connecting.");
while (WiFi.status() != WL_CONNECTED) x
static int count = 0;
Serial.print('.');
delay(500);
// Retry every 5 seconds
if (count++ % 10 == 0) {
WiFi.disconnect();
WiFi.reconnect();
Serial.print('!');
}
}
Serial.println("\nConnected!");
// Set Root CA certificate
client.setCACert(CA_CERT);
}
initNTP()
163-167行目は、NTP の初期化処理です。
void initNTP() {
timeClient.begin();
timeClient.update();
setTime(timeClient.getEpochTime());
}
onAppAriaPacket()
170-180行目には、TWELITE ARIA からデータを受信した際の処理を記述しています。
ここでは HTTP の送信処理を行わず、グローバル変数へセットしています。
グローバル変数へセットしたデータは、別のタスクで sendAriaData()
によって処理します。
void onAppAriaPacket(const ParsedAppAriaPacket& packet)
{
// Store data
LatestDataFromAria.serialId = packet.u32SourceSerialId;
LatestDataFromAria.logicalId = packet.u8SourceLogicalId;
LatestDataFromAria.supplyVoltage = packet.u16SupplyVoltage;
LatestDataFromAria.linkQuality = packet.u8Lqi;
LatestDataFromAria.temp100x = packet.i16Temp100x;
LatestDataFromAria.humid100x = packet.u16Humid100x;
IsThereNewDataFromAria = true;
}
sendAriaData()
183-240行目は、TWELITE ARIA のデータを HTTP GET リクエストのクエリ文字列にセットして送信する関数です。
コードを簡潔とするために、HTTP GET を使用しています。
HTTP POST を使用される場合は、リクエストボディを追加してください。
サーバへの過度な負荷を防ぐため、高頻度でパケットが到着した際には送信をスキップしています。
void sendAriaData(const DataFromAria& data)
{
static uint32_t lastTimeRequested = 0;
if (millis() - lastTimeRequested > SEND_MIN_INTERVAL * 1000 or lastTimeRequested == 0) {
Serial.println("Connecting to the server...");
if (not client.connect(SERVER_HOST, SERVER_PORT, CONNECT_TIMEOUT * 1000)) {
Serial.println("Connection failed!");
} else {
Serial.println("Connected to the server!");
// Make a query string
char queries[QUERIES_MAX_LENGTH+1];
snprintf(queries, sizeof(queries),
"datetime=%04d%02d%02d%02d%02d%02d&sid=%X&lid=%d&temp=%d&humid=%d&bat=%d&lqi=%d",
// Note that NTP_UPDATE_INTERVAL is set for 10000ms by default; second() delays up to 10s.
// To prevent duplication of datetime, SEND_MIN_INTERVAL is set for 10s.
year(), month(), day(), hour(), minute(), second(),
data.serialId,
data.logicalId,
data.temp100x,
data.humid100x,
data.supplyVoltage,
data.linkQuality);
// Send a request
client.println(String("GET https://") +
SERVER_HOST +
String("/get?") +
queries +
String(" HTTP/1.1"));
client.println("Accept: */*");
client.println(String("Host: ") + SERVER_HOST);
client.println("Connection: close");
client.println();
uint32_t timeSentRequest = millis();
// Handle a response
while (client.connected()) {
String line = client.readStringUntil('\n');
if (line == "\r") {
Serial.println("Headers received");
break;
}
if (millis() - timeSentRequest > REQUEST_TIMEOUT * 1000) {
Serial.println("Request was timed out");
break;
}
}
while (client.available()) {
char c = client.read();
Serial.write(c);
}
client.stop();
}
lastTimeRequested = millis();
} else {
Serial.println("Requests are too frequently; skip.");
}
}
5.2.3.2 - REST API の使用
spot-httpbin
の解説です。本稿では、サードパーティのオープンソースソフトウェアを使用します。
サードパーティのソフトウェアについて、その詳しい使用方法を弊社からご案内することはいたしかねます。また、サードパーティのソフトウェアを使用されたことによるいかなる損害についても、弊社は一切の責任を負いません。
ソースコードの入手
GitHub リポジトリ monowireless/spot-httpbin から入手できます。
システムの概要
spot-httpbin は、TWELITE 親機が受信したデータの一部と NTP による現在時刻を HTTP GET リクエストとしてモックサーバへ送信し、そのレスポンスをシリアルモニタへ表示します。
開発に必要なもの
-
無線LANゲートウェイ TWELITE SPOT
- 電源用 USB-C ケーブル
- USB AC アダプタ(1A 以上供給できるもの)
-
磁気・温度・湿度センサー無線タグ TWELITE ARIAなどの子機 (お持ちでない場合はご購入ください 👉 販売店一覧)
- CR2032 コイン電池 などの電源
-
USBアダプター TWELITE R3 (お持ちでない場合はご購入ください 👉 販売店一覧)
- 通信用 USB-C ケーブル
- 💻 開発用コンピュータ
環境整備
IDE とツールチェインの導入
Arduino IDE 1.x による開発環境の構築方法 をご覧ください。
ライブラリの導入
このサンプルでは、依存するライブラリをはじめから同梱しています。
src
フォルダの内容は IDE に表示されませんが、再帰的にビルドされます。プロジェクトファイルの入手
- GitHub (monowireless/spot-httpbin) から Zip ファイルをダウンロードします
- Zip ファイルを展開し、フォルダ名を
spot-httpbin-main
からspot-httpbin
に変更します - Arduino のスケッチブックの保存場所(Arduino IDE 環境設定に記載。例:
C:\Users\foo\Documents\Arduino
)にspot-httpbin
フォルダを配置します
ユーザ設定の変更
Arduino IDE 上部のタブから config.h
を開き、Wi-Fi の SSID と パスワードを設定してください。WPA2-PSK ネットワークを想定しています。また、ルート証明書も登録してください。ルート証明書は、Chrome などのウェブブラウザの各ページに対するセキュリティ画面から入手できます。
ルート証明書(拡張子.cer
)は、下記のような形式のテキストファイルです。
-----BEGIN CERTIFICATE-----
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-----END CERTIFICATE-----
プロジェクトファイルの書き込み方法
ESP32 へのスケッチの書き込み方法 をご覧ください。
スケッチ
Arduino スケッチ spot-httpbin.ino
および config.h
の解説です。
ライブラリのインクルード
Arduino および ESP32 公式ライブラリ
4-6行目では、Arduino および ESP32 の公式ライブラリをインクルードしています。
#include <Arduino.h>
#include <WiFiClientSecure.h>
#include <WiFiUdp.h>
ヘッダファイル | 内容 | 備考 |
---|---|---|
Arduino.h | Arduino の基本ライブラリ | 省略できる場合もあるが念のため記載 |
WiFiClientSecure.h | ESP32 で SSL通信を行う | |
WiFiUdp.h | UDP 通信を行う | NTP に必要 |
サードパーティのライブラリ
9-10行目では、同梱されたサードパーティのライブラリをインクルードしています。
#include "src/NTPClient/NTPClient.h"
#include "src/Time/TimeLib.h"
ヘッダファイル | 内容 | 備考 |
---|---|---|
NTPClient.h | NTP サーバへアクセスする | |
TimeLib.h | エポック時間を変換する |
MWings ライブラリ
13行目では、MWings ライブラリをインクルードしています。
#include <MWings.h>
ユーザ設定の定義
16行目では、config.h
をインクルードしています。
#include "config.h"
config.h
では、ユーザ設定を定義しています。実行時には、この設定を書き換えてください。データ型の定義
19-26行目では、子機から受信したデータを保管しておく構造体の型を定義しています。
struct DataFromAria {
uint32_t serialId;
uint8_t logicalId;
uint16_t supplyVoltage;
uint8_t linkQuality;
int16_t temp100x;
uint16_t humid100x;
};
名称 | 内容 |
---|---|
serialId | シリアルID |
logicalId | 論理デバイスID |
supplyVoltage | 電源電圧 |
linkQuality | LQI |
temp100x | 100倍された温度 |
humid100x | 100倍された湿度 |
ここでは、TWELITE ARIA を使用します。
config.h
再起動間隔の定義
config.h
の4行目では、ESP32 の再起動間隔を指定しています。
const uint32_t REBOOT_INTERVAL = 21600; // seconds
ここでは、21600秒=6時間としています。
長期間の運用では、メモリリークが累積して不具合を起こしてしまう場合があります。
そこで Wi-Fi ルータのように定期的な再起動を行うようにしています。
TWELITE 設定の定義
config.h
の7-8行目では、TWELITE SPOT に搭載された TWELITE 親機に適用する設定を定義しています。
const uint8_t TWE_CH = 18;
const uint32_t TWE_APPID = 0x67720102;
名称 | 内容 |
---|---|
TWE_CH | TWELITE の 周波数チャネル |
TWE_APPID | TWELITE の アプリケーション ID |
Wi-Fi 設定の定義
config.h
の11-12行目では、TWELITE SPOT に搭載された ESP32 に適用するWi-Fi 設定を定義しています。
const char* WIFI_SSID = "YOUR SSID";
const char* WIFI_PASSWORD = "YOUR PASSWORD";
名称 | 内容 |
---|---|
WIFI_SSID | 接続するネットワークの SSID |
WIFI_PASSWORD | 接続するネットワークの パスワード |
ルート証明書
config.h
の14-16行目では、ルート証明書の内容を記述するためのテンプレートを用意しています。
const char *CA_CERT =
"-----BEGIN CERTIFICATE-----\n"
"-----END CERTIFICATE-----\n";
ルート証明書は、Chrome などのウェブブラウザの各ページに対するセキュリティ画面から入手してください。
すべての行をダブルクォートで囲い、末尾のダブルクォートの前には改行文字 \n
を追加する必要があります。
ホストの設定
config.h
の18-19行目では、ホストの設定を定義しています。
const char *SERVER_HOST = "www.httpbin.org";
const uint16_t SERVER_PORT = 443;
名称 | 内容 |
---|---|
SERVER_HOST | サーバのホスト名 |
SERVER_PORT | サーバのポート番号 |
各種定数の定義
config.h
の21行目からは、各種定数を定義しています。
const uint32_t NTP_UPDATE_INTERVAL = 10000; // ms
const int QUERIES_MAX_LENGTH = 128; // bytes (without \0)
const int32_t CONNECT_TIMEOUT = 10; // seconds
const uint32_t RECONNECT_MIN_INTERVAL = 5; // seconds
// SEND_MIN_INTERVAL must be longer than NTP_UPDATE_INTERVAL
const uint32_t SEND_MIN_INTERVAL = 10; // seconds
const uint32_t REQUEST_TIMEOUT = 10; // seconds
名称 | 内容 |
---|---|
NTP_UPDATE_INTERVAL | NTP時刻の取得間隔 |
QUERIES_MAX_LENGTH | クエリ文字列の最大長(ヌル文字含まず) |
CONNECT_TIMEOUT | サーバへの接続時のタイムアウト |
RECONNECT_MIN_INTERVAL | Wi-Fiアクセスポイントへ再接続する際の最短間隔 |
SEND_MIN_INTERVAL | リクエスト間隔の最短間隔 |
REQUEST_TIMEOUT | リクエストからレスポンスまでのタイムアウト |
SEND_MIN_INTERVAL
は、NTP_UPDATE_INTERVAL
以上に設定しています。
リクエスト間隔が短いと、リクエスト間でタイムスタンプが重複してしまう可能性があるからです。
SEND_MIN_INTERVAL
を短くすると、連続してパケットを受信した場合にサーバへ負担をかけてしまいます。
必ず適度な間隔を空けてください。
ピン番号の定義
29-31行目では、ピン番号を定義しています。
static const int RST_PIN = 5;
static const int PRG_PIN = 4;
static const int LED_PIN = 18;
名称 | 内容 |
---|---|
RST_PIN | TWELITE の RST ピンが接続されているピンの番号 |
PRG_PIN | TWELITE の PRG ピンが接続されているピンの番号 |
LED_PIN | 基板上の ESP32 用 LED が接続されているピンの番号 |
グローバルオブジェクトの宣言
34-37行目では、グローバルオブジェクトを宣言しています。
static WiFiClientSecure client;
static WiFiUDP ntpUDP;
static NTPClient timeClient(ntpUDP, "ntp.nict.jp",
32400, NTP_UPDATE_INTERVAL); // JST(UTC+9)
名称 | 内容 |
---|---|
client | HTTPS通信のインタフェース |
ntpUDP | NTP用のUDP通信のインタフェース |
timeClient | NTPのインタフェース |
グローバル変数の宣言
40-41行目では、グローバル変数を宣言しています。
static DataFromAria LatestDataFromAria;
static bool IsThereNewDataFromAria;
名称 | 内容 |
---|---|
LatestDataFromAria | TWELITE ARIA から受信した最新のデータ |
IsThereNewDataFromAria | TWELITE ARIA から新たなデータを受信したことを示すフラグ |
関数プロトタイプの宣言
44-56行目では、関数プロトタイプを宣言しています。
void anotherLoopForTWELITE();
void anotherLoopForNTP();
名称 | 内容 |
---|---|
anotherLoopForTWELITE | TWELITEのデータを処理するためのループ関数 |
anotherLoopForNTP | NTPで時刻を取得するためのループ関数 |
xTaskCreatePinnedToCore()
により、別のタスクとして登録しています。
void initTWELITE();
void initWiFi();
void initNTP();
名称 | 内容 |
---|---|
initTWELITE | TWELITEの初期化関数 |
initWiFi | Wi-Fiの初期化関数 |
initNTP | NTPの初期化関数 |
void onAppAriaPacket(const ParsedAppAriaPacket& packet);
名称 | 内容 |
---|---|
onAppAriaPacket | TWELITE ARIA からデータを受信した際のコールバック関数 |
void sendAriaData(const DataFromAria& data)
名称 | 内容 |
---|---|
sendAriaData | TWELITE ARIAのデータを HTTP GET リクエストにのせて送る関数 |
setup()
59-87行目では、全体の初期化を行います。
void setup() {
Serial.begin(115200);
initTWELITE();
initWiFi();
initNTP();
// Attach another loop function for TWELITE
// Note: Core 0 is also used for the WiFi task, which priority is 19 (ESP_TASKD_EVENT_PRIO - 1)
xTaskCreatePinnedToCore(
[](void *params) {
while (true) {
anotherLoopForTWELITE();
vTaskDelay(1); // IMPORTANT for Watchdog
}
},
"Task for anotherLoopForTWELITE()", 8192, nullptr, 18, nullptr,
0); // Priority is 18 (lower than WiFi)
// Attach another loop function for NTP
xTaskCreatePinnedToCore(
[](void *params) {
while (true) {
anotherLoopForNTP();
vTaskDelay(1); // IMPORTANT for Watchdog
}
},
"Task for anotherLoopForNTP()", 8192, nullptr, 17, nullptr,
0); // Priority is 17 (lower than WiFi and TWELITE)
}
xTaskCreatePinnedToCore()
により、loop()
とは別のタスクを登録しています。
下記の部分はキャプチャのない無名関数です。不要なグローバル空間の汚染を避けることができます。
[](void *params) {
while (true) {
anotherLoopForTWELITE();
vTaskDelay(1); // IMPORTANT for Watchdog
}
},
vTaskDelay()
を挿入しています。loop()
90-111行目は、主となるループ処理です。
HTTP リクエストの処理、Wi-Fi 切断時の再接続処理、定期リセットの処理を行います。
void loop() {
static uint32_t lastTimeReconnected = 0;
if (WiFi.status() == WL_CONNECTED) {
// Regular operations
// Check for new data
if (IsThereNewDataFromAria) {
IsThereNewDataFromAria = false; // Clear first; data is updated on another thread
DataFromAria data = LatestDataFromAria; // Now, the buffer is open for incoming data
sendAriaData(data);
}
} else if (millis() - lastTimeReconnected > RECONNECT_MIN_INTERVAL * 1000) {
// Lost connection, reconnect periodically
Serial.println("Disconnected. Reconnecting to WiFi...");
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
lastTimeReconnected = millis();
}
// Reboot every x interval
if (millis() > REBOOT_INTERVAL * 1000) {
Serial.println("Rebooting...");
ESP.restart();
}
}
anotherLoopForTWELITE()
114-116行目は、TWELITE のためのループ処理です。
データの受信と解釈を逐次行うため、ブロッキング処理を含む loop()
とは別のタスクとしています。
void anotherLoopForTWELITE() {
Twelite.update();
}
anotherLoopForNTP()
117-120行目は、NTP のためのループ処理です。
こちらについても UDP の通信を行うため、ブロッキング処理を含む loop()
とは別のタスクとしています。
void anotherLoopForNTP() {
timeClient.update();
setTime(timeClient.getEpochTime());
}
initTWELITE()
123-130行目は、TWELITE の初期化処理です。
TWELITE SPOT に搭載された TWELITE を指定された設定で起動し、パケット受信時のコールバック関数を登録しています。
void initTWELITE() {
Serial2.begin(115200);
if (Twelite.begin(Serial2, LED_PIN, RST_PIN, PRG_PIN, TWE_CHANNEL, TWE_APP_ID)) {
Serial.println("Started TWELITE.");
}
// Attach event handlers to process packets
Twelite.on(onAppAriaPacket);
}
下記リファレンスも合わせてご覧ください。
Twelite.begin()
mwings::MWings クラス | MWings API リファレンスTwelite.on()
mwings::MWings クラス | MWings API リファレンス
initWiFi()
133-157行目は、Wi-Fi の初期化処理です。
接続されない場合は、5秒置きに再接続を試みます。
void initWiFi() {
Serial.print("\nConnecting to the WiFi network ");
Serial.print(WIFI_SSID);
Serial.println("...");
// Begin
WiFi.mode(WIFI_STA);
WiFi.setAutoReconnect(true);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
// Wait for connection
Serial.print("Connecting.");
while (WiFi.status() != WL_CONNECTED) x
static int count = 0;
Serial.print('.');
delay(500);
// Retry every 5 seconds
if (count++ % 10 == 0) {
WiFi.disconnect();
WiFi.reconnect();
Serial.print('!');
}
}
Serial.println("\nConnected!");
// Set Root CA certificate
client.setCACert(CA_CERT);
}
initNTP()
160-164行目は、NTP の初期化処理です。
void initNTP() {
timeClient.begin();
timeClient.update();
setTime(timeClient.getEpochTime());
}
onAppAriaPacket()
167-177行目には、TWELITE ARIA からデータを受信した際の処理を記述しています。
ここでは HTTP の送信処理を行わず、グローバル変数へセットしています。
グローバル変数へセットしたデータは、別のタスクで sendAriaData()
によって処理します。
void onAppAriaPacket(const ParsedAppAriaPacket& packet)
{
// Store data
LatestDataFromAria.serialId = packet.u32SourceSerialId;
LatestDataFromAria.logicalId = packet.u8SourceLogicalId;
LatestDataFromAria.supplyVoltage = packet.u16SupplyVoltage;
LatestDataFromAria.linkQuality = packet.u8Lqi;
LatestDataFromAria.temp100x = packet.i16Temp100x;
LatestDataFromAria.humid100x = packet.u16Humid100x;
IsThereNewDataFromAria = true;
}
sendAriaData()
180-237行目は、TWELITE ARIA のデータを HTTP GET リクエストのクエリ文字列にセットして送信する関数です。
コードを簡潔とするために、HTTP GET を使用しています。
HTTP POST を使用される場合は、リクエストボディを追加してください。
サーバへの過度な負荷を防ぐため、高頻度でパケットが到着した際には送信をスキップしています。
void sendAriaData(const DataFromAria& data)
{
static uint32_t lastTimeRequested = 0;
if (millis() - lastTimeRequested > SEND_MIN_INTERVAL * 1000 or lastTimeRequested == 0) {
Serial.println("Connecting to the server...");
if (not client.connect(SERVER_HOST, SERVER_PORT, CONNECT_TIMEOUT * 1000)) {
Serial.println("Connection failed!");
} else {
Serial.println("Connected to the server!");
// Make a query string
char queries[QUERIES_MAX_LENGTH+1];
snprintf(queries, sizeof(queries),
"datetime=%04d%02d%02d%02d%02d%02d&sid=%X&lid=%d&temp=%d&humid=%d&bat=%d&lqi=%d",
// Note that NTP_UPDATE_INTERVAL is set for 10000ms by default; second() delays up to 10s.
// To prevent duplication of datetime, SEND_MIN_INTERVAL is set for 10s.
year(), month(), day(), hour(), minute(), second(),
data.serialId,
data.logicalId,
data.temp100x,
data.humid100x,
data.supplyVoltage,
data.linkQuality);
// Send a request
client.println(String("GET https://") +
SERVER_HOST +
String("/get?") +
queries +
String(" HTTP/1.1"));
client.println("Accept: */*");
client.println(String("Host: ") + SERVER_HOST);
client.println("Connection: close");
client.println();
uint32_t timeSentRequest = millis();
// Handle a response
while (client.connected()) {
String line = client.readStringUntil('\n');
if (line == "\r") {
Serial.println("Headers received");
break;
}
if (millis() - timeSentRequest > REQUEST_TIMEOUT * 1000) {
Serial.println("Request was timed out");
break;
}
}
while (client.available()) {
char c = client.read();
Serial.write(c);
}
client.stop();
}
lastTimeRequested = millis();
} else {
Serial.println("Requests are too frequently; skip.");
}
}
5.2.4 - Google スプレッドシートの利用
spot-google-sheets
の解説です。5.2.4.1 - Google スプレッドシートの利用
spot-google-sheets
の解説です。なお、このスケッチでは ESP32 の Arduino 環境から FreeRTOS の機能を利用しています。本稿では、サードパーティのオープンソースソフトウェアを使用します。
サードパーティのソフトウェアについて、その詳しい使用方法を弊社からご案内することはいたしかねます。また、サードパーティのソフトウェアを使用されたことによるいかなる損害についても、弊社は一切の責任を負いません。
ソースコードの入手
GitHub (monowireless/spot-google-sheets) から入手できます。
システムの概要
TWELITE SPOT は、事前に作成したサービスアカウントを使って自動的にスプレッドシートを作成し、指定したユーザアカウントへ、そのファイルを共有します。
ユーザアカウントへログインすると、Google ドライブの「共有アイテム」ページから、TWELITE SPOT によって作成されたスプレッドシートを閲覧・編集できます。
TWELITE SPOT は、作成したスプレッドシートへデータ行を次々と追加していきます。
2023年5月現在、Google Sheets / Drive API の利用に追加料金はかかりません。
ただし、クエリの数などを定めた使用量上限(Sheets API / Drive API)があります。
例えば Google Sheets API では、リクエストを毎分60回以内に押さえなくてはなりません。超過したリクエストはエラーとなります。
サービスアカウントは、アプリケーションが使用するための Google アカウントです。
詳しくは Google による解説をご覧ください。
開発に必要なもの
-
無線LANゲートウェイ TWELITE SPOT
- 電源用 USB-C ケーブル
- USB AC アダプタ(1A 以上供給できるもの)
-
温湿度センサー無線タグ TWELITE ARIA (お持ちでない場合はご購入ください 👉 販売店一覧)
- CR2032 コイン電池
-
USBアダプター TWELITE R3 (お持ちでない場合はご購入ください 👉 販売店一覧)
- 通信用 USB-C ケーブル
- 💻 開発用コンピュータ
- Google アカウント
本稿では TWELITE ARIA を使用しますが、ソースコードを改変することで TWELITE CUE などの子機にも対応できます。
ただし、シートへ書き込むデータが増える場合は、API の 使用量上限 に気を配る必要があります。
環境整備
IDE とツールチェインの導入
Arduino IDE 1.x による開発環境の構築方法 をご覧ください。
ライブラリの導入
ESP-Google-Sheet-Client ライブラリ
ライブラリマネージャを開き、検索ボックスに esp-google-sheet
と入力してインストールします。
なお、GitHub (mobizt/ESP-Google-Sheet-Client) からも入手できます。
公式 NTP ライブラリ
ライブラリマネージャを開き、検索ボックスに ntpclient
と入力してインストールします。
TimeLib ライブラリ
ライブラリマネージャを開き、検索ボックスに timelib
と入力してインストールします。
事前準備:API のセットアップ
事前に、API を使用できるように準備する必要があります。Google アカウントを使います。
ここでは、下記の作業を行います。
- Google Cloud プロジェクトの作成
- Google Sheets API の有効化
- Google Drive API の有効化
- サービスアカウントの作成と設定
- サービスアカウントの認証情報の取得
プロジェクトの作成
API を使用するにあたって、まずは Google Cloud プロジェクトを作成します。
Google Cloud プロジェクトは、システム全体を束ねるような存在です。構築するシステムの名称をプロジェクト名にするとよいでしょう。ここでは、仮に SPOT-DEV
とします。
下記のリンクにアクセスし、プロジェクトを作成してください。
https://console.cloud.google.com/projectcreate
Sheets API の有効化
TWELITE SPOT からスプレッドシートを操作するために、Sheets API を有効化します。
下記のリンクにアクセスし、API を有効化してください。
https://console.cloud.google.com/apis/library/sheets.googleapis.com
Drive API の有効化
TWELITE SPOT からスプレッドシートを共有するために、Drive API を有効化します。
下記のリンクにアクセスし、API を有効化してください。
https://console.cloud.google.com/apis/library/drive.googleapis.com
サービスアカウントの作成と設定
TWELITE SPOT からスプレッドシートを作成するために、サービスアカウントを作成します。
下記のリンクにアクセスし、プロジェクト名(ここでは SPOT-DEV
)を選択してサービスアカウント一覧画面を表示したのち、ページ上部のボタンからサービスアカウントの作成を開始します。
https://console.cloud.google.com/iam-admin/serviceaccounts
「① サービスアカウントの詳細」では、サービスアカウントの名称を入力します。
下記の例では、spot-dev-sa
としています。
入力したら、「作成して続行」ボタンを押して次へ進みます。
「② このサービスアカウントにプロジェクトへのアクセスを許可する(省略可)」では、サービスアカウントの権限を設定します。
ここでは、下記の例のようにして「オーナー」を選択してください。
選択したら、「続行」ボタンを押して次へ進みます。
「③ ユーザーにこのサービスアカウントへのアクセスを許可(省略可)」では、何も行わずに「完了」を押してスキップします。
サービスアカウントの作成が完了すると、サービスアカウントの一覧画面へ戻ります。作成したサービスアカウントが表示されていることを確認してください。
サービスアカウントの認証情報の取得
作成したサービスアカウントを確認したら、「メール」列のリンクをクリックし、サービスアカウントの詳細画面へ移ります。
上部の「キー」タブを選択して、サービスアカウントの認証に必要な秘密鍵を管理する画面へ移ります。
「鍵を追加」ボタンから「新しい鍵を作成」を選択し、秘密鍵の作成を開始します。
次の画面では、「JSON」を選択した状態のまま「作成」ボタンを押します。
「作成」ボタンを押すと、秘密鍵ファイル(.json
)が自動的にダウンロードされます。
秘密鍵ファイルをテキストエディタで開くと、下記のような構成になっているはずです。
{
"type": "service_account",
"project_id": "???",
"private_key_id": "???",
"private_key": "-----BEGIN PRIVATE KEY-----\n???\n-----END PRIVATE KEY-----\n",
"client_email": "???@???.iam.gserviceaccount.com",
"client_id": "???",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "???",
"universe_domain": "googleapis.com"
}
上記のうち、project_id
/ private_key
/ client_email
の内容を動作確認で使用します。
動作確認
まずは動作確認を行ってみましょう。
プロジェクトファイルを入手
- GitHub (monowireless/spot-google-sheets) から Zip ファイルをダウンロードします
- Zip ファイルを展開し、フォルダ名を
spot-google-sheets-main
からspot-google-sheets
に変更します - Arduino のスケッチブックの保存場所(Arduino IDE 環境設定に記載。例:
C:\Users\foo\Documents\Arduino
)にspot-google-sheets
フォルダを配置します
スケッチの設定ファイルを修正
Arduino スケッチ spot-google-sheets.ino を開き、画面上部の config.h
タブを選択して、4-11行目の値を修正してください。
4-5行目は、無線 LAN 関連の設定です。
const char* WIFI_SSID = "YOUR SSID"; // Modify it
const char* WIFI_PASSWORD = "YOUR PASSWORD"; // Modify it
SSID と パスワードを設定しています。
一方、8-11行目はスプレッドシート関連の設定です。
const char* PROJECT_ID = "YOUR-PROJECT-ID"; // Modify it
const char* SERVICE_ACCOUNT_EMAIL = "YOUR-SERVICE-ACCOUNT@YOUR-PROJECT-ID.iam.gserviceaccount.com"; // Modify it
const char PRIVATE_KEY[] PROGMEM = "-----BEGIN PRIVATE KEY-----\nYOUR-PRIVATE-KEY\n-----END PRIVATE KEY-----\n"; // Modify it
const char* USER_ACCOUNT_EMAIL = "YOUR-ACCOUNT@EMAIL"; // Modify it
最初の3項目には .json
ファイルの内容をコピーして、最後の USER_ACCOUNT_EMAIL
にはあなたがログインしている Google アカウントのメールアドレスを入力してください。
スケッチを書き込み
ESP32 へのスケッチの書き込み方法 を参考に、スケッチを書き込んでください。
ツール -> Flash Size の設定が 16MB
でないと正常に動きません。
パーティションテーブルには
paritions.csv
を使用しますが、全体のサイズが小さいと正しく書き込めないからです。
親機と子機を起動
TWELITE SPOT のリセットボタン(ESP32 側)を押してください。
Arduino のシリアルコンソールに以下のような表示がされたら、起動に成功しています。
Initializing queue...
Completed.
Started TWELITE.
Connecting to WiFi ...!...
Connected. IP: xxx.xxx.xxx.xxx
Initializing NTP...Completed. UNIX time: xxxxxxxxxx
Initializing sheets...
Creating sheets...
OAuth2.0 access token on initializing
OAuth2.0 access token on signing
OAuth2.0 access token on exchange request
OAuth2.0 access token ready
Requesting to create...
Succeeded.
Adding headers for ARIA...
Requesting to add header...
Succeeded.
Formatting the sheet for ARIA...
Requesting to format...
Succeeded.
Extending the sheet for ARIA...
Requesting to extend...
Succeeded.
Completed.
TWELITE ARIA(初期設定)にもコイン電池を挿入し、電源を投入します。
TWELITE SPOT が TWELITE ARIA からのパケットを正常に受信し、データ列の追加に成功すると、下記のような表示がなされます。
Got a new packet from ARIA.
Got a new packet from ARIA.
Requesting to add data...
Got a new packet from ARIA.
Succeeded.
ちなみに、上記の例ではリクエスト中にパケットを受信しています。後述のマルチタスクに成功している証です!
Google へアクセス
Google ドライブの 共有アイテム へアクセスし、SPOT Sheet (xxx)
という名前のスプレッドシートを開きます。
以下のような画面が表示されます。
スクロールしていくと、TWELITE ARIA からのデータを確認できるはずです。
Ctrl (⌘) + ↓
を押します。スケッチ解説
Arduino スケッチ spot-google-sheets.ino の解説です。
ライブラリのインクルード
Arduino および ESP32 公式ライブラリ
4-7行目では、Arduino および ESP32 の公式ライブラリをインクルードしています。
#include <Arduino.h>
#include <NTPClient.h>
#include <WiFi.h>
#include <WiFiUdp.h>
ヘッダファイル | 内容 | 備考 |
---|---|---|
Arduino.h | Arduino の基本ライブラリ | |
NTPClient.h | NTP を使う | ファイル名と受信時刻に使用 |
WiFi.h | ESP32 の WiFi を使う | |
WiFiUdp.h | UDP を使う | NTPClient に必要 |
サードパーティのライブラリ
10-11行目では、サードパーティのライブラリをインクルードしています。
#include <ESP_Google_Sheet_Client.h>
#include <TimeLib.h>
ヘッダファイル | 内容 | 備考 |
---|---|---|
ESP_Google_Sheet_Client.h | Google へアクセスする | |
TimeLib.h | UNIX 時間をフォーマットする |
MWings ライブラリ
14行目では、MWings ライブラリをインクルードしています。
#include <MWings.h>
ユーザ設定の定義
17行目では、config.h
をインクルードしています。
#include "config.h"
config.h
では、ユーザ設定を定義しています。実行時には、この設定を書き換えてください。無線 LAN 設定の定義
config.h
の4-5行目では、TWELITE SPOT に搭載された ESP32 に適用する無線 LAN 設定を定義します。
const char* WIFI_SSID = "YOUR SSID"; // Modify it
const char* WIFI_PASSWORD = "YOUR PASSWORD"; // Modify it
名称 | 内容 |
---|---|
WIFI_SSID | 接続するネットワークの SSID |
WIFI_PASSWORD | 接続するネットワークの パスワード |
API 設定の定義
config.h
の8-11行目では、API の設定を定義しています。
const char* PROJECT_ID = "YOUR-PROJECT-ID"; // Modify it
const char* SERVICE_ACCOUNT_EMAIL = "YOUR-SERVICE-ACCOUNT@YOUR-PROJECT-ID.iam.gserviceaccount.com"; // Modify it
const char PRIVATE_KEY[] PROGMEM = "-----BEGIN PRIVATE KEY-----\nYOUR-PRIVATE-KEY\n-----END PRIVATE KEY-----\n"; // Modify it
const char* USER_ACCOUNT_EMAIL = "YOUR-ACCOUNT@EMAIL"; // Modify it
名称 | 内容 |
---|---|
PROJECT_ID | プロジェクト ID |
SERVICE_ACCOUNT_EMAIL | サービスアカウントのメールアドレス |
PRIVATE_KEY | 秘密鍵の本体 |
USER_ACCOUNT_EMAIL | スプレッドシートを共有するユーザアカウントのメールアドレス |
ピン番号の定義
20-24行目では、ピン番号を定義しています。
const uint8_t TWE_RST = 5;
const uint8_t TWE_PRG = 4;
const uint8_t LED = 18;
const uint8_t ESP_RXD1 = 16;
const uint8_t ESP_TXD1 = 17;
名称 | 内容 |
---|---|
TWE_RST | TWELITE の RST ピンが接続されているピンの番号 |
TWE_PRG | TWELITE の PRG ピンが接続されているピンの番号 |
LED | 基板上の ESP32 用 LED が接続されているピンの番号 |
ESP_RXD1 | TWELITE の TX ピンが接続されているピンの番号 |
ESP_TXD1 | TWELITE の RX ピンが接続されているピンの番号 |
TWELITE 設定の定義
27-30行目では、TWELITE SPOT に搭載された TWELITE 親機に適用する設定を定義しています。
const uint8_t TWE_CH = 18;
const uint32_t TWE_APPID = 0x67720102;
const uint8_t TWE_RETRY = 2;
const uint8_t TWE_POWER = 3;
名称 | 内容 |
---|---|
TWE_CH | TWELITE の 周波数チャネル |
TWE_APPID | TWELITE の アプリケーション ID |
TWE_RETRY | TWELITE の 再送回数(送信時) |
TWE_POWER | TWELITE の 送信出力 |
シート関連の定義
32-43行目では、シートに関連した情報を定義しています。
const char* SPREADSHEET_TITLE_PREFIX = "SPOT Sheet";
const char* SPREADSHEET_LOCALE = "ja_JP";
const char* SPREADSHEET_TIME_ZONE = "Asia/Tokyo";
const int MIN_REQUEST_INTERVAL = 1000; // 60 requests per minute
const int SHEETS_DEFAULT_ROWS = 1000; // Default length is 1000 rows
const int SHEETS_ROWS = 100000; // Max 1,000,000 rows at 10 columns
const int ARIA_SHEET_ID = 1;
const char* ARIA_SHEET_TITLE = "ARIA";
constexpr int ARIA_BUFFER_PACKETS = 32; // Max number of rows per addition request
名称 | 内容 |
---|---|
SPREADSHEET_TITLE_PREFIX | スプレッドシートのファイル名の固定部分 |
SPREADSHEET_LOCALE | スプレッドシートのロケール |
SPREADSHEET_TIME_ZONE | スプレッドシートのタイムゾーン |
MIN_REQUEST_INTERVAL | リクエスト送信の最小間隔 |
SHEETS_DEFAULT_ROWS | 各シートのデフォルトの行数 |
SHEETS_ROWS | 各シートの行数 |
ARIA_SHEET_ID | ARIA 用シートの ID |
ARIA_SHEET_TITLE | ARIA 用シートの名称 |
ARIA_BUFFER_PACKETS | ARIA からのパケットを格納するキューの長さ |
TWELITE ARIA 以外の子機に対応するときには、同一のスプレッドシート(ワークブック)に別のシートを作成することを推奨します。
したがって、ID、タイトル、キューの長さの3点を付け加える必要があります。
型の宣言
46-50行目では、型を宣言しています。
struct ParsedAppAriaPacketWithTime {
ParsedAppAriaPacket packet;
uint32_t elapsedMillis;
uint32_t unixTime;
};
名称 | 内容 |
---|---|
ParsedAppAriaPacketWithTime | 受信したパケットデータを受信時刻と合わせてキューへ格納するための型 |
elapsedMillis
:パケット受信時の起動からの経過時間(ミリ秒)unixTime
:パケット受信時の UNIX 時間(秒)です。
TWELITE ARIA 以外の子機に対応するときには、新たにキューを作成する必要があります。
したがって、ParsedAppAriaPacketWithTime
のようなキュー格納用の型についても付け加える必要があります。
グローバルオブジェクトの宣言
53-61行目では、グローバルオブジェクトを宣言しています。
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "ntp.nict.jp", 32400);
String spreadsheetIdString; // Identifier of newly created file
bool readyForNewRequests = false;
uint32_t lastTimeRequestWasSent = UINT32_MAX;
QueueHandle_t ariaPacketQueue; // Store received data from ARIA
uint32_t rowToAddNewAriaData = 2; // Starting with the Row 2
名称 | 内容 |
---|---|
ntpUDP | NTP のための UDP インタフェース |
timeClient | NTP のインタフェース |
spreadsheetIdString | 作成したスプレッドシートのID |
readyForNewRequests | 新たなリクエストを送信できる状態になったら true |
lastTimeRequestWasSent | 最後にリクエストを送信した時間 |
ariaPacketQueue | ARIA から受信したパケットと受信時刻を格納するキュー |
rowToAddNewAriaData | 次に ARIA から受信したデータを追加する行 |
ariaPacketQueue
は、Arduino core for the ESP32 のベースになっている FreeRTOS の Queue 機能を使います。関数プロトタイプの宣言
64-71行目では、関数プロトタイプを宣言しています。
void anotherLoop();
void waitUntilNewRequestsReady();
String createSpreadsheet();
bool formatSheet(const String spreadsheetId, const int sheetId);
bool extendSheet(const String spreadsheetId, const int sheetId, const int rows);
bool addSheetAriaHeaderRow(const String spreadsheetId, const char* const sheetTitle);
bool addSheetsDataRow(const String spreadsheetId);
名称 | 内容 |
---|---|
anotherLoop() | 非同期で TWELITE の処理を行う、もうひとつの loop() |
waitUntilNewRequestsReady() | 次のリクエストを送信可能になるまで待機する |
createSpreadsheet() | スプレッドシートを新規作成する |
formatSheet() | 指定したシートの書式を設定する |
extendSheet() | 指定したシートの行を増やし、書式を設定する |
addSheetAriaHeaderRow() | 指定したシートへ ARIA 向けのヘッダー行を追加する |
addSheetsDataRow() | シートへデータ行を追加する |
anotherLoop()
を作成し、そのなかで Twelite.update()
を実行しています。TWELITE ARIA 以外の子機に対応するときには、例えば、下記のような方針でリクエストの内容を追加します。
createSpreadsheet()
:スプレッドシートを増やすように改変するformatSheet()
:新たなシートもフォーマットするように改変するextendSheet()
:内容はそのまま、新たなシートに対しても呼び出すaddSheet**HeaderRow()
:関数ごと付け加えるaddSheetsDataRow()
:新たなシートへデータを追加する処理を追加する
キューの設定
82-83行目では、受信したパケットデータを受信時刻と合わせて格納するためのキューを初期化しています。
ariaPacketQueue = xQueueCreate(ARIA_BUFFER_PACKETS, sizeof(ParsedAppAriaPacketWithTime));
if (ariaPacketQueue == 0) { Serial.println("Failed to init a queue."); }
xQueueCreate()
は、ESP32 の内部で動作する FreeRTOS の機能です。マルチタスクに対応したキューを簡単に作成することができます。
TWELITE の設定
88-92行目では、Twelite.begin()
を呼び出し、TWELITE SPOT に搭載された TWELITE 親機の設定と起動を行っています。
Serial2.begin(115200, SERIAL_8N1, ESP_RXD1, ESP_TXD1);
if (Twelite.begin(Serial2,
LED, TWE_RST, TWE_PRG,
TWE_CH, TWE_APPID, TWE_RETRY, TWE_POWER)) {
Serial.println("Started TWELITE.");
}
引数 | 型 | 内容 |
---|---|---|
Serial2 | HardwareSerial& | TWELITE との通信に使うシリアルポート |
LED | int | ステータス LED を接続したピンの番号 |
TWE_RST | int | TWELITE の RST ピンを接続したピンの番号 |
TWE_PRG | int | TWELITE の PRG ピンを接続したピンの番号 |
TWE_CHANNEL | uint8_t | TWELITE の 周波数チャネル |
TWE_APP_ID | uint32_t | TWELITE の アプリケーション ID |
TWE_RETRY | uint8_t | TWELITE の 再送回数(送信時) |
TWE_POWER | uint8_t | TWELITE の 送信出力 |
イベントハンドラの登録
94-103行目では、TWELITE ARIA からのパケットを受信した際に行う処理を登録しています。
Twelite.on([](const ParsedAppAriaPacket& packet) {
Serial.println("Got a new packet from ARIA.");
ParsedAppAriaPacketWithTime packetWithTime;
packetWithTime.elapsedMillis = millis();
packetWithTime.unixTime = timeClient.getEpochTime();
packetWithTime.packet = packet;
if (not(xQueueSend(ariaPacketQueue, &packetWithTime, 0) == pdPASS)) {
Serial.println("Failed to add packet data to the queue.");
}
});
ここでは、xQueueSend()
により、受信したパケットデータを受信時刻と合わせてキューの末尾へ格納しています。
無線 LAN の設定
106-120行目では、無線 LAN の設定を行っています。
WiFi.mode(WIFI_STA);
WiFi.setAutoReconnect(true);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.print("Connecting to WiFi ..");
while (WiFi.status() != WL_CONNECTED) {
static int count = 0;
Serial.print('.');
delay(500);
// Retry every 5 seconds
if (count++ % 10 == 0) {
WiFi.disconnect();
WiFi.reconnect();
Serial.print('!');
}
}
ここでは、無線 LAN 子機として設定したうえで、指定のネットワークへ接続しています。
ネットワークに接続できるまで、while
文を出ません。
上記の実装では、5秒に一度、再接続を試みています。
NTP の設定
126-127行目では、NTP の設定をしています。
timeClient.begin();
timeClient.update();
Google スプレッドシートの設定
132-145行目では、Google スプレッドシートを設定しています。
GSheet.setTokenCallback([](TokenInfo info) {
// Print token initialization states
if (info.status == esp_signer_token_status_error) {
Serial.print("Token error ");
Serial.println(GSheet.getTokenError(info));
}
Serial.print(GSheet.getTokenType(info));
Serial.print(" ");
Serial.println(GSheet.getTokenStatus(info));
});
GSheet.setPrerefreshSeconds(60); // Set refresh rate for auth token
Serial.println("Initializing sheets...");
GSheet.begin(SERVICE_ACCOUNT_EMAIL, PROJECT_ID, PRIVATE_KEY);
132-141行目でサービスアカウントのトークンを取得する際の状態表示の処理を登録したあと、142行目でトークンの再取得間隔を設定、145行目でサービスアカウントの初期化をしています。
また、147-173行目では、スプレッドシートの作成、ARIA 向けヘッダー行の追加、セルの書式設定、そして行の拡張を行っています。
Serial.println("Creating sheets...");
waitUntilNewRequestsReady(); // Wait for token
spreadsheetIdString = createSpreadsheet();
if (not(spreadsheetIdString.length() > 0)) {
Serial.println("Failed to create sheets.");
}
Serial.println("Adding headers for ARIA...");
delay(MIN_REQUEST_INTERVAL);
waitUntilNewRequestsReady();
if (not addSheetAriaHeaderRow(spreadsheetIdString, ARIA_SHEET_TITLE)) {
Serial.println("Failed to add headers.");
}
Serial.println("Formatting the sheet for ARIA...");
delay(MIN_REQUEST_INTERVAL);
waitUntilNewRequestsReady();
if (not formatSheet(spreadsheetIdString, ARIA_SHEET_ID)) {
Serial.println("Failed to format.");
}
Serial.println("Extending the sheet for ARIA...");
delay(MIN_REQUEST_INTERVAL);
waitUntilNewRequestsReady();
if (not extendSheet(spreadsheetIdString, ARIA_SHEET_ID, SHEETS_ROWS - SHEETS_DEFAULT_ROWS)) {
Serial.println("Failed to extend.");
}
デフォルトでは、A-Z 列のセルが 1,000 行続きます。ここでは列を A-J の 10 列に絞り、行数を 100倍の 100,000 行に増やしています。
それでも足りない場合には、さらに行を増やす必要があります。ただし、セルの上限は 10,000,000 個のため、 A-J の10 列の場合、最大 1,000,000 行に制限されます。それ以上はセルを増やせないため、新たなスプレッドシート(ワークブック)を作成する必要があります。
タスクの登録
179-186行目では、TWELITE のデータを非同期で更新するためのタスクを登録しています。
xTaskCreatePinnedToCore(
[](void* params) {
while (true) {
anotherLoop();
vTaskDelay(1);
}
},
"Task for anotherLoop()", 8192, nullptr, 18, nullptr, 0);
xTaskCreatePinnedToCore()
は、FreeRTOS のマルチタスク機能に含まれる関数です。
ここでは、180-185行目のラムダ式を渡し、anotherLoop()
を無限に呼ぶタスクを登録しています。
[](void* params) {
while (true) {
anotherLoop();
vTaskDelay(1); // IMPORTANT for Watchdog
}
},
vTaskDelay(1);
などを挿入し、ウォッチドッグタイマの入る隙を与えてください。タスクの名称は Task for anotherLoop()
で、スタックサイズは 8192
、タスクへのパラメータはなく、優先度は 18
(大きいほど高く、無線 LAN 関連の処理は1個上の 19
)、タスクを操作するインタフェースもなく、実行する CPU コアは無線 LAN などの RF 処理と同じ Core 0
です( loop()
等は Core 1
)。
"Task for anotherLoop()", 8192, nullptr, 18, nullptr, 0); // Priority is 18 (lower than WiFi)
TWELITE のデータの更新
203行目では、anotherLoop()
内にて Twelite.update()
を呼び出しています。
Twelite.update();
Twelite.update()
は、TWELITE 親機から送信されるパケットデータ(ModBus ASCII 形式)を順次1バイトずつ読み出す関数です。
loop()
内で繰り返し Twelite.update()
を呼ぶことで、TWELITE 親機から送信されるパケットデータの解釈が進みます。パケットデータの解釈を終えた際に 上記 のようなイベントが呼ばれる仕組みです。delay()
などの処理でこの関数の呼び出しをブロックすると、パケットデータ文字列の読み出しが間に合わないことがあります。しかし、このスケッチでは FreeRTOS の機能によって anotherLoop()
を切り離しているため、たとえ loop()
内で同期処理があっても問題ありません。スプレッドシートの更新
192-195行目では、スプレッドシートの更新処理を呼び出しています。
if (millis() - lastTimeRequestWasSent > MIN_REQUEST_INTERVAL) {
// Add available data
addSheetsDataRow(spreadsheetIdString);
}
192行目の if 文では、60リクエスト毎分の API 制限を守るために、前回のリクエスト送信から必ず 1 秒以上空けるようにしています(JavaScript の Throttle / Debounce における Throttle に相当します)。
194行目では、必要に応じてスプレッドシートへデータ行を追加します。
addSheetsDataRow(spreadsheetIdString);
API ライブラリの更新
196行目では、Google API 用ライブラリの更新を行い、リクエストの送信可否の状態を取得しています。
readyForNewRequests = GSheet.ready();
NTP ライブラリの更新
197行目では、NTP ライブラリの更新を行っています。
timeClient.update();
スプレッドシートの操作
217行目以降では、Sheets API によるスプレッドシートの操作を行っています。
詳しくは ライブラリの API リファレンス や Sheets API の REST リソース をご覧ください。
関連情報
- Google Cloud コンソール:ようこそ – SPOT-DEV – Google Cloud コンソール
- Sheets REST API リファレンス:Google Sheets API | Google for Developers
- Sheets REST サンプル:サンプル | Google Sheets | Google for Developers
Arduino
- 公式サイト:Arduino - Home
ESP32
- 製品情報:ESP32 Wi-Fi & Bluetooth MCU I Espressif Systems
- データシート:esp32_datasheet_en.pdf
- Arduino 向けツールチェイン:espressif/arduino-esp32: Arduino core for the ESP32
- スタートガイド:Getting Started — Arduino-ESP32 documentation
- 導入方法:Installing — Arduino-ESP32 documentation
- API リファレンス:Libraries — Arduino-ESP32 documentation
- Wi-Fi API:Wi-Fi API — Arduino-ESP32 documentation
- チュートリアル:Tutorials — Arduino-ESP32 documentation
- トラブルシューティング:Troubleshooting — Arduino-ESP32 documentation
- ESP-IDF:espressif/esp-idf: Espressif IoT Development Framework
- ドキュメント:ESP-IDF Programming Guide - ESP32
- APIリファレンス:FreeRTOS (ESP-IDF) - ESP32
- ドキュメント:ESP-IDF Programming Guide - ESP32
コミュニティ
ライブラリ
- Google Sheets / Drive API:mobizt/ESP-Google-Sheet-Client: Arduino Google Sheet REST client library for ESP8266, ESP32 and Raspberry Pi Pico RP2040
- 日付と時刻:PaulStoffregen/Time: Time library for Arduino