これまでの節で、各機能について分けて説明してきた。最後に、セットアップとメインループに関するプログラムを掲載する。
セットアップの主要項目は、SPIFFS初期設定、アクセスポイントモード起動、小型有機LED初期化、タイマー割込み設定である。これまで触れてこなかったSPIFFSとは、SPI Flash File Systemの略で、フラッシュメモリをファイル保存用に利用する機能である。ウェブページを生成するファイル群をフラッシュメモリに保存し、接続するブラウザに供給する目的で用いる。
// クライアント状態変数初期化
void initClientStatus() {
for (uint8_t num = 0; num < CLIENT_MAX; num++) {
clientSTAT.Address[num] = 0; // IPアドレス末尾
clientSTAT.Station[num] = 0; // 未確認, ブラウザ, デバイス 識別
clientSTAT.Connect[num] = 0; // 切断, 接続, データ受信 識別
clientSTAT.Elapsed[num] = 0; // 連続接続時間
clientSTAT.GetData[num] = 0; // 受信1byteデータ
clientSTAT.TimeOut[num] = 0; // 非応答タイムアウトカウンター
}
}
// セットアップ
void setup() {
Serial.begin(115200);
while(!Serial) {} // シリアルポート準備待ち
Serial.println("");
pinMode (STATPin, OUTPUT); // 動作状態出力ピン設定
digitalWrite(STATPin, LOW);
SPIFFS.begin(); // SPIFFS初期設定
initClientStatus(); // クライアント状態変数初期化
setupAPMode(); // APモード起動
displayInit(); // OLED初期化
displayAPMode(); // OLED表示開始
// タイマー割込み設定(ソケット通信に対して割込み優先を確認済み)
timer = timerBegin(0, 80, true); // timer = 1usec
timerAttachInterrupt(timer, &baseInterrupt, true);
timerAlarmWrite(timer, unitTime * 1000, true);
timerAlarmEnable(timer);
}
メインループから定期的に呼び出される関数は3つある。2つはアクセスポイント関連、1つはブラウザへの配信である。アクセスポイント関連の関数を以下に記載する。
// APモードループ
void loopAPMode() {
uint8_t payloadBIN[2];
// インターバル更新告知
if (intervalFlag) {
intervalFlag = false;
payloadBIN[0] = 'I'; // "I"nterval
payloadBIN[1] = (uint8_t)intervalBase;
// 全接続クライアントへインターバル告知
for (uint8_t num = 0; num < CLIENT_MAX; num++) {
if (clientSTAT.Connect[num] != 0) webSocket.sendBIN(num, payloadBIN, 2);
}
Serial.printf("Interval Broadcast %d x 100msec\n", payloadBIN[1]);
}
// データサイズ更新告知
if (datasizeFlag) {
datasizeFlag = false;
payloadBIN[0] = 'S'; // "S"ize
payloadBIN[1] = datasizeBase;
// 全接続クライアントへデータサイズ告知
for (uint8_t num = 0; num < CLIENT_MAX; num++) {
if (clientSTAT.Connect[num] != 0) webSocket.sendBIN(num, payloadBIN, 2);
}
if (datasizeBase == 0) Serial.printf("DataSize Broadcast 1byte\n");
else Serial.printf("DataSize Broadcast %d x 1024byte\n", payloadBIN[1]);
}
}
// APモード動作確認
void confirmAPMode() {
if (!WiFi.enableAP(true)) setupAPMode(); // 再稼働
}
次は、ブラウザへの配信関数である。「WebSocket通信(概要)」の節で述べたが、不用意にブラウザが切断した場合、アクセスポイントはそれを感知できない。そこで、送信処理が失敗したときは、ブラウザ切断と判断しなければならない。この機能を盛り込まないと、アクセスポイントがロック状態に陥ってしまう。
// ウェブブラウザ配信
void webAPMode() {
char payloadTXT[PayloadTXTSize], buf[PayloadTXTSize];
uint8_t num;
int size, pnt = 0;
// 各クライアントの状態抽出
for (num = 0; num < CLIENT_MAX; num++) {
size = snprintf_P(&buf[pnt], sizeof(buf), "%d,", clientSTAT.Address[num]); // IPアドレス末尾
pnt += size;
size = snprintf_P(&buf[pnt], sizeof(buf), "%d,", clientSTAT.Station[num]); // 未確認:0, ブラウザ:1, デバイス:2
pnt += size;
size = snprintf_P(&buf[pnt], sizeof(buf), "%d,", clientSTAT.Connect[num]); // 切断:0, 接続:1, データ受信:2
pnt += size;
size = snprintf_P(&buf[pnt], sizeof(buf), "%d,", clientSTAT.Elapsed[num]); // 連続接続時間
pnt += size;
if (num < CLIENT_MAX - 1) {
size = snprintf_P(&buf[pnt], sizeof(buf), "%d,", clientSTAT.GetData[num]); // 受信先頭1byteデータ
pnt += size;
}
else {
snprintf_P(&buf[pnt], sizeof(buf), "%d", clientSTAT.GetData[num]); // 最終データ
}
}
// PROGMEM文字列使用フォーマッティング
snprintf_P(payloadTXT, sizeof(payloadTXT), SERVER_JSON, buf);
// 各ウェブブラウザへ配信
for (num = 0; num < CLIENT_MAX; num++) {
if (clientSTAT.Station[num] == 1 && clientSTAT.Connect[num] == 1) {
// ブラウザ切断時には送信結果確認に10秒程度要する
// この期間はタイマー割込み発生せず(サーバーはロック状態)
bool send = webSocket.sendTXT(num, payloadTXT, strlen(payloadTXT));
if (!send) {
clientSTAT.Connect[num] = 0; // ウェブブラウザへの配信エラー(強制切断)
Serial.printf("Web Send Error %d\n", num);
}
}
}
}
最後にメインループを掲載する。内容はプログラムのコメントを参照して欲しい。
void loop() {
bool timeout = false;
webSocket.loop(); // WebSocketクライアント接続待ち
webServer.handleClient(); // WebSocket新規クライアント対応
loopAPMode(); // イベント発生確認
if (webFlag) { // ウェブブラウザ定期配信処理
webFlag = false;
webAPMode(); // ウェブブラウザ配信
}
if (oneSecFlag) { // 1秒間隔処理
oneSecFlag = false;
setupCounter++; // ESP32起動カウンター更新
for (uint8_t num = 0; num < CLIENT_MAX; num++) {
// クライアント連続接続時間更新
if (clientSTAT.Connect[num] != 0) ++clientSTAT.Elapsed[num];
}
confirmAPMode(); // APモード動作確認
timeout = timeoutDisconnect(); // タイムアウト切断処理
if (timeout) webAPMode(); // タイムアウト即時配信
displayAPMode(); // 表示更新
}
}
これまで説明してきたセンサネットワークの動作をブラウザの動画画面で紹介する。