Wi-FiマイコンXIAO ESP32S3のWebクライアント動作
温度センサBMP280ブレイクアウトボード使用
2023.9.11 Coskx Lab
1 はじめに
Wi-FiマイコンXIAO ESP32S3はArduinoIDEでの開発が便利です。
ボードとしてXIAO ESP32S3を選んでおきます。
ここでは,温度センサの値を発信するXIAO ESP32S3がWebクライアントとして働くようにします。
温度センサはBMP280を使用します。
このWebクライアントはWebサーバ(例えばPC上で動作しているApache2)に,温度データを伴ってアクセスします。Webサーバの受付プログラムがこの温度データを処理します。
"マイコンのクライアント"と"PCのWebサーバ"のふるまい
閲覧するのはスマホやタブレットのWebブラウザでもよい
2 使用環境
- Windows 10 64-bit
- ArduinoIDE 2.2.1
- XIAO ESP32S3
- BMP280 (HW-611 E/P 280)
- WindowsPCもXIAO ESP32S3も同じアクセスポイントでWi-Fi接続可能
- WindowsPCもXIAO ESP32S3も同じLAN内接続
- LAN内でDHCP(Dynamic Host Configuration Protocol)サーバが稼働中
3 準備
3.1 ArduinoIDE
(1)Arduino IDEのメニューバーから[ファイルFile] -> [環境設定(Preferences)]
Additional board manager URLsに
https://files.seeedstudio.com/arduino/package_seeeduino_boards_index.json
および
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
を設定してOK
(2)ツール[Tools] > ボード[Board] > Boards Manager
検索boxに「ESP」を入力すると複数のボード候補が出てくるので
esp32 by Espressif Systems
をinstall
(3)ツール[Tools] > ボード[Board]でESP32を探すとXIAO ESP32S3が見つかるので,これを選ぶ。
3.2 XIAO ESP32S3
XIAO ESP32S3の説明書を読んでプログラムの転送方法を確認します。
(USBケーブルでつないで書き込むだけでした。)
4 XIAO ESP32S3と温度センサBMP280の配線
温度センサBMP280は温度をデジタル値に変換しているので,そのデータをXIAO ESP32S3が読み出します。
XIAO ESP32S3と温度センサBMP280はI2C接続ですので,電源線,GND線の他はSCLとSDAの2本のみで通信できます。
CSBをVCCにつなぐとBMP280はI2C接続になります。(データシートによれば確実にVCCにつなぐべきとなっています)
SDO(シルク印刷は間違えていてSDDとなっている)をVCCにつなぐとBMP280のI2Cアドレスは77になります。GNDにつなぐとBMP280のI2Cアドレスは76になります。Groveライブラリのデフォルトは77なのでVCCにつないでいます。
なおBMP280ではSDA,SCLをpullupせずに動作しています。
XIAO ESP32S3と温度センサBMP280の配線
ブレッドボードには載せないので,XIAO ESP32S3のピンを上向きに付けています。
XIAO ESP32S3と温度センサBMP280
5 XIAO ESP32S3のスケッチコード(クライアントプログラム)
クライアントであるXIAO ESP32S3は,一定の時間間隔で温度センサBMP280の値を取得し,Weサーバに送信します。
送信ではHTTP通信の手順に従います。
setupでは,自分が接続すべきWi-FiアクセスポイントのSSIDとパスワードを設定して,LANの一員になろうと試みます。
成功すると,LAN内のDHCPサーバーからIPアドレス(一次的な)をもらいます。
XIAO ESP32S3は,クライアントなので他の機器からアクセスされることはないので,このIPアドレスもらって,自分が使うだけです。
一定時間ごとに,温度センサBMP280から,温度を取得し,"ホスト名","標高","温度","気圧"の文字列をjson型式のデータとしてWebサーバにpostメソッドで送信します。
次のコードがXIAO ESP32S3で使用されています。
ライブラリ"Grove_-_Barometer_Sensor_BMP280"をArduinoIDEに予め追加しておいてください。
#include <WiFi.h>
#include <WiFiClient.h>
#include <HTTPClient.h>
#include <Seeed_BMP280PP.h>
#include <Wire.h>
#include <Arduino_JSON.h>
#define SERVER_IP "192.168.1.25" //IP address of Web Server
#define SERVER_PATH "/jsontest/jsontest.php" //access target path in the Web Server
#define STASSID "REPLACE_WITH_YOUR_SSID"
#define STAPSK "REPLACE_WITH_YOUR_PASSWORD"
const char* ssid = STASSID;
const char* password = STAPSK;
const String clientname = "esp32a";
const int led = LED_BUILTIN;
BMP280PP bmp280;
int ledonoff = 1;
void setup() {
Serial.begin(115200);
Serial.print("\n\n\n");
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Wire.begin();
int connectcount = 0;
while (WiFi.status() != WL_CONNECTED) {
if (10 <= connectcount++) ESP.restart();
Serial.print(".");
delay(500);
}
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
if (!bmp280.init()) {
Serial.println("Device bmp280 not connected or broken");
} else {
Serial.println("Device bmp280 started");
}
pinMode(led, OUTPUT);
digitalWrite(led, ledonoff); //turn LED on
}
void loop() {
// wait for WiFi connection
if ((WiFi.status() == WL_CONNECTED)) {
WiFiClient client;
HTTPClient http;
String url = String("http://") + SERVER_IP + SERVER_PATH;
//Serial.println("url : " + url);
Serial.print("[HTTP] begin...\n");
http.begin(client, url); // HTTP access with post method
http.addHeader("Content-Type", "application/json");
Serial.print("[HTTP] POST...\n");
// start connection and send HTTP header and body
float temperature = bmp280.getTemperature();
float pressure = bmp280.getPressure();
float altitude = bmp280.calcAltitude(pressure);
//Serial.println(temperature);
//json format data
JSONVar doc;
doc["clientname"] = clientname;
doc["altitude"] = String(altitude);
doc["temperature"] = String(temperature);
doc["pressure"] = String(pressure);
String jsonstr = JSON.stringify(doc);
int httpCode = http.POST(jsonstr);
// httpCode will be negative on error
if (httpCode > 0) {
// HTTP header has been sent and Server response header has been handled
Serial.printf("[HTTP] POST... code: %d\n", httpCode);
// file found at server
if (httpCode == HTTP_CODE_OK) {
const String& payload = http.getString();
Serial.println("received payload:\n<<");
Serial.println(payload);
Serial.println(">>");
}
} else {
Serial.printf("[HTTP] POST... failed, error: %s\n", http.errorToString(httpCode).c_str());
}
http.end();
digitalWrite(led, ledonoff = 1 - ledonoff);
}
delay(60000);//also allow the cpu to switch to other tasks
}
BMP280のオリジナルライブラリでは,BMP280操作のクラス名はBMP280です。
BMP280の初期設定を手直ししたかったので,クラスBMP280の継承クラスBMP280PPを作って,このクラスを使用しています。
#include <Seeed_BMP280PP.h>
BMP280PP bmp280;
の2行が継承クラスBMP280PPを使っているのですが,オリジナルライブラリを使用する場合はこの2行を
#include <Seeed_BMP280.h>
BMP280 bmp280;
のように戻してください。
次の2つのファイルを作りましたがそれらは,Seeed_BMP280.h,Seeed_BMP280.cppと同じフォルダ(Arduino\libraries\Grove_-_Barometer_Sensor_BMP280)内に保存されています。
BMP280PP.h
#include <Seeed_BMP280.h>
class BMP280PP : public BMP280
{
public:
bool init(void);
private:
void writeRegister(uint8_t reg, uint8_t val);
};
BMP280PP.ino
#include "Seeed_BMP280PP.h"
bool BMP280PP::init(void)
{
if (!BMP280::init()) return false;
//changeig Temperature oversampling to index=4
//(oversampling x8)
//100 111 11
writeRegister(BMP280_REG_CONTROL, 0x9F);
//adding, IIR filter coeficient = 8 (index=3)
//000 011 00
writeRegister(BMP280_REG_CONFIG, 0xC);
return true;
}
void BMP280PP::writeRegister(uint8_t reg, uint8_t val)
{
Wire.beginTransmission(BMP280_ADDRESS); // start transmission to device
Wire.write(reg); // send register address
Wire.write(val); // send value to write
Wire.endTransmission(); // end transmission
}
プログラムをXIAO ESP32S3に書き込んで起動させると,直ちにIPアドレスを取得したことをArduinoIDEのシリアルモニターに表示します。
"IP address: 192.168.1.19"は,取得したIPアドレスでアクセスポイントに接続したことを表しています。
しかし,まだWebサーバの準備ができていないので,その後は失敗した(connection refused)と表示されます。
そのまま60秒間隔で失敗したと表示を続けます。
Connected to xxxxxxxxxxxxx
IP address: 192.168.1.16
Device bmp280 started
[HTTP] begin...
[HTTP] POST...
[HTTP] POST... failed, error: connection refused
[HTTP] begin...
[HTTP] POST...
[HTTP] POST... failed, error: connection refused
6 PCのWebサーバの準備
XIAO ESP32S3のクライアントプログラムは単体では意味を成しません。対応するWebサーバ側にxxxxx.phpのような受け皿が必要です。
ここではWindowsPCにWindows Subsystem for Linuxを動かし,apache2をインストール・起動し,PHPもインストールして,Webサーバを構築してあるものとします。このWebサーバのWebDocumentに.phpファイルを作ります。
WindowsPC(Webサーバマシン)のIPアドレスは予め調べておき,192.168.1.25だったらXIAO ESP32S3のプログラムに埋め込んでおきます
#define SERVER_IP "192.168.1.25" //IP address of Web Server
.phpファイルは/jsontest/jsontest.phpにしたことにすると,XIAO ESP32S3のプログラムにこのURLも埋め込みます。
#define SERVER_PATH "/jsontest/jsontest.php" //access target path in the Web Server
注意
Webサーバでは,ディレクトリ・ファイルのオーナやグループを,apache2が操作できるように設定する必要があります。
また,WinPCを使っている場合はWindowsPCのファイアウォールでポート番号80 (HTTP)を通す設定が必要です。
jsontest.phpでは,"ホスト名","標高","温度","気圧"のjson型式のデータを受け取り,それを四つの文字列に分解し,現在時刻とともにファイルsample.txtに追加保存します。
$fp = fopen("sample.txt", "a");
複数のクライアントから非同期でアクセスが来るため,ファイル書き込みでは衝突しないように排他制御を行います。
jsontest.phpは次のようなphpプログラムです。
<?php
print "Hello! from jsontest.php<br>\n";
$body = file_get_contents('php://input'); //BODYの取得
if (is_null($body)) {
# error データが無い
http_response_code(500); //HTTP response code 500
echo "No data (JSON)<br>";
exit();
}
$jsonarry = json_decode($body, true); //連想配列に変換
if (is_null($jsonarry)) {
# error JSONをデコードできない
http_response_code(500); //HTTP response code 500
echo "Cannot decode JSON<br>";
exit();
}
echo $jsonarry;
var_dump($jsonarry);
$str = date("Y/m/d H:i:s");
foreach ($jsonarry as $i => $value) {
$str .= ", " . $value;
}
// ファイルへ書き込み
$fp = fopen("sample.txt", "a");
flock($fp, LOCK_EX); //排他制御ここから
fwrite($fp, $str . "\n");
flock($fp, LOCK_UN); //排他制御ここまで
fclose($fp);
?>
補足
Webブラウザ(client)からのアクセスに対してWebサーバはhtmlデータを返しますが,ここではマイコン(client)からのアクセスなので,htmlデータではなく最小限のデバッグ用データを返しています。
Webサーバの準備ができたところで,XIAO ESP32S3を起動すると,ArduinoIDEのシリアルモニターに成功の様子が表示されるようになります。
うまくアクセスして,データがWebサーバに届くと,届けられたデータを返してくるのでその様子を見ることができます。
jsontest.phpの内容に不具合があると
[HTTP] POST... code: 500
が返ってくることもあります。
[HTTP] begin...
[HTTP] POST...
[HTTP] POST... code: 200
received payload:
<<
Hello! from jsontest.php<br>
Arrayarray(4) {
["clientname"]=>
string(6) "esp32a"
["altitude"]=>
string(6) "168.76"
["temperature"]=>
string(5) "33.39"
["pressure"]=>
string(8) "99314.00"
}
>>
7 3つのXIAO ESP32S3がWebサーバにアクセス
3つのXIAO ESP32S3(それぞれ温度センサBMP280を持っている)がクライアントとしてWebサーバにアクセスします。
"3つのマイコンのクライアント"と"PCのWebサーバ"
XIAO ESP32S3から見ると"http://192.168.1.25/jsontest/jsontest.php"とアクセスしていることになります。
表からは見えませんがpostメソッドでjson型式の"ホスト名","標高","温度","気圧"のデータも同時に送信されます。
3つのXIAO ESP32S3はそれぞれ,60秒間隔でアクセスします。
Webサーバー側のjsontest.phpはいつアクセスしてくるかわからないけれど,アクセスしてきたら,ファイルsample.txtにアクセス内容を保存します。
保存されたファイルsample.txtの内容は次のようになりました。
Webブラウザで
"http://192.168.1.25/jsontest/sample.txt
のようにすればファイルsample.txtの内容が表示されます。
約60秒間隔で3つのクライアントXIAO ESP32S3が,温度データを送信してきているのがわかります。
2023/09/05 12:14:27, esp32a, 171.89, 33.47, 99277.00
2023/09/05 12:14:56, esp32b, 175.02, 33.31, 99240.00
2023/09/05 12:15:03, esp32c, 170.62, 33.93, 99292.00
2023/09/05 12:15:28, esp32a, 171.38, 33.50, 99283.00
2023/09/05 12:15:57, esp32b, 175.45, 33.26, 99235.00
2023/09/05 12:16:04, esp32c, 170.62, 33.94, 99292.00
2023/09/05 12:16:29, esp32a, 171.30, 33.51, 99284.00
2023/09/05 12:16:58, esp32b, 174.94, 33.34, 99241.00
2023/09/05 12:17:05, esp32c, 170.20, 33.99, 99297.00
2023/09/05 12:17:30, esp32a, 171.30, 33.56, 99284.00
2023/09/05 12:17:59, esp32b, 174.77, 33.50, 99243.00
2023/09/05 12:18:06, esp32c, 170.45, 34.06, 99294.00
2023/09/05 12:18:31, esp32a, 171.72, 33.57, 99279.00
2023/09/05 12:19:00, esp32b, 174.86, 33.56, 99242.00
2023/09/05 12:19:07, esp32c, 170.88, 34.15, 99289.00
2023/09/05 12:19:32, esp32a, 171.21, 33.65, 99285.00
2023/09/05 12:20:01, esp32b, 174.52, 33.58, 99246.00
2023/09/05 12:20:08, esp32c, 170.79, 34.18, 99290.00
8 通信距離
Androidスマホをモバイルアクセスポイントとして,XIAO ESP32S3にロッドアンテナをつけた状態で通信距離を測定しました。
見通しの良い直線で75mまでの通信ができました。
それを超えると通信が途切れ始めます。
9 まとめ
Wi-FiマイコンXIAO ESP32S3をWebクライアントとして動作させ,XIAO ESP32S3に接続された温度センサBMP280の値を,Webサーバの受け皿URLに60秒間隔でデータを送信するようにしました。
Webサーバの受け皿であるphpプログラムは受け取ったデータをファイルに追記するようにしました。
XIAO ESP32S3にロッドアンテナをつけた状態での実測通信可能距離は75mでした。