Wi-FiマイコンXIAO ESP32S3のWebサーバ動作
温度センサBMP280ブレイクアウトボード使用

2023.9.11 Coskx Lab  

1 はじめに

Wi-FiマイコンXIAO ESP32S3はArduinoIDEでの開発が便利です。
ボードとしてXIAO ESP32S3を選んでおくと,XIAO ESP32S3がWi-Fi接続のWebサーバ動作をするスケッチ例が既に使えるようになっています。
ここでは,温度センサの値を発信するXIAO ESP32S3がWebサーバとして働くようにします。 温度センサはBMP280を使用します。
このWebサーバから温度データをもらうためには,PCなどからWebブラウザでアクセスします。

    "マイコンのサーバ"と"PCのWebブラウザ"のふるまい
    閲覧するのはスマホやタブレットのWebブラウザでもよい

2 使用環境

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のスケッチコード(サーバプログラム)

クライアント(例えばPCのwebブラウザ)からのリクエストで,サーバであるXIAO ESP32S3は,温度センサBMP280の値を取得し,クライアントに返します。
送信ではHTTP通信の手順に従います。

setupでは,自分が接続すべきWi-FiアクセスポイントのSSIDとパスワードを設定して,LANの一員になろうと試みます。
成功すると,LAN内のDHCPサーバからIPアドレス(一次的な)をもらいます。
またmDNSプロトコルで,自分のホスト名を有効にします。
LAN内機器についてDNSサーバはホスト名の解決ができないので,WebブラウザはIPアドレスでアクセスしてくるのですが,mDNSのおかげでホスト名+".local"(ブラウザが賢いのでホスト名だけでもOK)としてアクセスすることができます。
ここでは,"http://esp32a.local/"でアクセスできます。IPアドレスはDHCPサーバによりマイコン起動時に自動で割り当てられるので,いつも同じIPアドレスになっているとは限らないため,サーバ名(ホスト名)でアクセス可能になるmDNSの仕組みはありがたいです。
アクセスがあると,温度センサBMP280から,温度と気圧を取得し,標高を計算し,"ホスト名:レスポンス回数","標高","温度","気圧"の文字列をスペース区切りで返すようにしています。

次のコードは,IDEの既存スケッチ例"HelloServer.ino"を元に作ったものです。
ライブラリ"Grove_-_Barometer_Sensor_BMP280"を予め追加しておいてください。

#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <Seeed_BMP280PP.h>

#define STASSID "REPLACE_WITH_YOUR_SSID"
#define STAPSK "REPLACE_WITH_YOUR_PASSWORD"

const char* ssid = STASSID;
const char* password = STAPSK;
const char* servername = "esp32a";
int count = 0;

WebServer server(80);

const int led = LED_BUILTIN;
int ledonoff = 0;
BMP280PP bmp280;

void handleRoot() {
  digitalWrite(led, ledonoff = 1 - ledonoff);
  float temperature = bmp280.getTemperature();
  float pressure = bmp280.getPressure();
  float altitude = bmp280.calcAltitude(pressure);
  char chrs[128];
  sprintf(chrs, "%s:%d %.1f %.1f %.1f", servername, count++, altitude, temperature, pressure);
  String message = String(chrs);

  server.send(200, "text/plain", message);
}

void setup(void) {
  Serial.begin(115200);

  Serial.print("\n\n\n");

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);

  // Wait for connection
  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 (MDNS.begin(servername)) {
    Serial.println("MDNS responder started");
    Serial.print("server name: ");
    Serial.println(servername);
  }

  server.on("/", handleRoot);

  server.begin();
  Serial.println("HTTP server started");

  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(void) {
  server.handleClient();
  delay(2);//allow the cpu to switch to other tasks
}

補足
「server.on("/", handleRoot);」は,ブラウザから「http://サーバ名/」というアクセスがあったら,handleRoot()を実行してね,という設定です。
通常,ブラウザには「http://サーバ名」と入力しますが,ブラウザはその後ろに「/」を加えて「http://サーバ名/」のように送信します。

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
}

6 PCのWebブラウザからアクセス

プログラムがXIAO ESP32S3に転送された直後に,ArduinoIDEのシリアルモニタには次のように表示されます。 (XIAO ESP32S3をリセットした直後も同じように表示されます。文字化けするときは,115200baudであることを確認してください)
ここでXIAO ESP32S3に割り当てられたIPアドレスは192.168.1.16であり,サーバー名(ホスト名)はesp32aであることがわかります。

Connected to xxxxxxxxxxxxx
IP address: 192.168.1.16
MDNS responder started
server name: esp32a
HTTP server started
Device bmp280 started

まず最初にgoogle chromeからIPアドレスでアクセスしてみました。
正確には"http://192.168.1.16/"のように書きます。
169.9が標高,35.3が気温,98982.0が気圧です。

次にサーバー名でアクセスしてみました。
正確には"http://esp32a.local/"のように書きます。
167.0が標高,35.2が気温,98981.0が気圧です。

ただし,標高は海面気圧が1013.25hPaとして,ライブラリ内で気圧と気温から計算されています。

7 WinPCのExcelから2つのXIAO ESP32S3にアクセス

2つのXIAO ESP32S3(それぞれ温度センサBMP280を持っている)に,同じLAN内にあるWinPCのExcelからアクセスします。
2つのXIAO ESP32S3には,プログラムコード上で
#define SERVERNAME "esp32a"

#define SERVERNAME "esp32b"
のように別々のサーバ名(ホスト名)を与え,起動しておきます。
Excelから,mDNSの恩恵で,2つのURLをホスト名を使ってアクセスします。
ExcelでVBAのプログラムを走らせ,Start,Stopボタンで動作を制御します。

なおVBAでブラウザと同じHTTPアクセスをするためには,Microsoft XMLライブラリを使います。
VBAの環境を開いて,メニューから"ツール"→"参照設定"を選ぶと,"参照設定"ダイアログが開くので,ライブラリファイルの中から"Microsoft XML, v6.0"を探して,チェックを入れてOKで組み込まれます。
Microsoft XMLライブラリからXMLHTTP60のオブジェクトを使って,関数requestHTTP内でHTTPアクセスします。
2つのXIAO ESP32S3からは,それぞれ"ホスト名","標高","温度","気圧"がスペース区切りで送られてくるので,それを指定のセルに表示します。


VBAのプログラムは次のようになりました。

Dim hasRequestToStop As Boolean

Sub go()
hasRequestToStop = False
main
End Sub

Sub stopbtn()
hasRequestToStop = True
End Sub

Function requestHTTP(url As String) As String

    Dim httpReq As XMLHTTP60
    Set httpReq = New XMLHTTP60
    
    Debug.Print httpReq.readyState
    
    httpReq.Open "GET", "http://" & url
    httpReq.setRequestHeader "Pragma", "no-cache"
    httpReq.setRequestHeader "Cache-Control", "no-cache"
    httpReq.setRequestHeader "If-Modified-Since", "Thu, 01 Jun 1970 00:00:00 GMT"
    httpReq.Send
    
    Do While httpReq.readyState < 4
        DoEvents
    Loop
    
    Debug.Print httpReq.responseText
    requestHTTP = httpReq.responseText
    
    Set httpReq = Nothing

End Function

Function extractBody(text As String) As String
    Dim point As Integer
    text = Replace(text, vbCr, "")
    text = Replace(text, vbLf, "")
    text = StrConv(text, vbLowerCase)
    point = InStr(text, "<html>")
    If point <> 0 Then text = Right(text, Len(text) - (point + 5))
    point = InStr(text, "</html>")
    If point <> 0 Then text = Left(text, point - 1)
    extractBody = text
End Function

Sub mainwork()
    Dim res1 As String
    Dim res2 As String
    Dim parts() As String
    
    res1 = requestHTTP(Range("A3"))
    res2 = requestHTTP(Range("A4"))
    res1 = extractBody(res1)
    res2 = extractBody(res2)
    parts = Split(res1, " ")
    If 4 <= UBound(parts) - LBound(parts) + 1 Then
        Cells(3, 2).Value = parts(0)
        Cells(3, 3).Value = parts(1)
        Cells(3, 4).Value = parts(2)
        Cells(3, 5).Value = parts(3)
    End If
    parts = Split(res2, " ")
    If 4 <= UBound(parts) - LBound(parts) + 1 Then
        Cells(4, 2).Value = parts(0)
        Cells(4, 3).Value = parts(1)
        Cells(4, 4).Value = parts(2)
        Cells(4, 5).Value = parts(3)
    End If
End Sub

Sub main()
    Do
        mainwork
        Application.Wait Now + TimeValue("00:00:01")
        If hasRequestToStop = True Then
            Exit Do
            DoEvents
        End If
        DoEvents
    Loop
End Sub

補足
httpReq.setRequestHeader "Pragma", "no-cache"
httpReq.setRequestHeader "Cache-Control", "no-cache"
httpReq.setRequestHeader "If-Modified-Since", "Thu, 01 Jun 1970 00:00:00 GMT"
の3行は,キャッシュに残っているデータは捨てて,必ず新しいデータを受け取るようにしなさいの意味です。
これがないと,キャッシュ内のデータを受け取るだけになってしまい,古いデータが表示されたままになってしまいます。

VBAが動作しているExcelの画面内で次のようにアクセス結果を表示します。
約1秒ごとにアクセスを繰り返します。
センサBMP280の個体差が測定温度と測定気圧に現れています。温度で±0.3度程度,気圧では±100Paの違いが現れます。

8 通信距離

XIAO ESP32S3のWi-Fiでの通信は約70mでした。

9 まとめ

Wi-FiマイコンXIAO ESP32S3をWebサーバとして動作させ,XIAO ESP32S3に接続された温度センサBMP280の値を,クライアントがリクエストしたタイミングでデータを送信するようにしました。
また複数のXIAO ESP32S3サーバからデータを得るためにVBAを使ってExcelに表示する例を示しました。

補足
Excelの画面では見た目が悪いので,Powerpointで作ることもできます。Excelのセルに表示するのではなく,Powerpointの表に表示することなどが考えられます。ただし,表示場所を指示するのが面倒なのと,ExcelVBAとPowerpointVBAに多少違いがあるので,注意が必要です。