Wi-FiマイコンXIAO ESP32S3のhttpsWebクライアント
温度センサBMP280ブレイクアウトボード使用
2023.9.11 Coskx Lab
1 はじめに
Wi-FiマイコンXIAO ESP32S3でhttpクライアントを作りました。≫
これに手を加えてhttpsクライアントにします。
Wi-FiマイコンXIAO ESP32S3,温度センサBMP280はそのまま使います。
またWebサーバの受け皿も同じphpプログラムを使います。
httpsを受け取るWebサーバはインターネット上にあることにします。
XIAO ESP32S3のプログラムだけ差し替えます。
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 HTTPS
HTTPS(Hytertext Transfer Protocol Secure)は,HTTPで通信を安全に行うための仕組みです。
HTTPSでは「SSLサーバ証明書」によるサーバの認証と,SSL(Secure Sockets Layer)/TLS(Transport Layer Security)と呼ばれるプロトコルによる暗号化の2つの仕組みを持っています。
「SSLサーバ証明書」はhttps接続時にサーバからクライアントに送られてきます。
その「SSLサーバ証明書」が信頼できるかどうかを確かめる手順もあり,そのためには,クライアントは「SSLサーバ証明書」を事前に入手して,持っている必要があります。
「SSLサーバ証明書」には暗号化に関する情報も含まれていますので,それを使って暗号通信をします。暗号化・復号化の作業を自分でプログラムする必要はありません。
SSLサーバ証明書の取得
アクセス対象のWebサーバの「SSLサーバ証明書」の事前入手はChromeなどのWebブラウザで出来ます。
Chromeを使用する場合,アクセス対象のWebサイトを閲覧し,ブラウザのアドレスバーにある鍵マークをクリックし,「この接続は保護されています」→「証明書は有効です」→「詳細」へ進んでいくと「SSLサーバ証明書」の情報が見えます。
「SSLサーバ証明書」は複数の階層になっていて,ルート CA 証明書,中間 CA 証明書,サーバー証明書があり,「証明書の階層」欄に上から順番に並んでいます。
SSLサーバ証明書取得
この後の作業で使用するのは,ルート CA 証明書(「証明書の階層」欄の一番上)です。それを選び,exportで受け取っておきます。(一番下のサーバの名前で表示されているのはサーバー証明書なのでこれではありません。)
取得した「SSLサーバ証明書」の中身はテキストなので,エディタで内容が見られます。
-----BEGIN CERTIFICATE-----で始まり,-----END CERTIFICATE-----で終わっているはずです。
4 XIAO ESP32S3と温度センサBMP280の配線
httpクライアント制作記事を参照してください。≫
5 XIAO ESP32S3のスケッチコード(クライアントプログラム)
クライアントであるXIAO ESP32S3は,一定の時間間隔で温度センサBMP280の値を取得し,Weサーバに送信します。
送信ではHTTPS通信の手順に従います。
setupでは,自分が接続すべきWi-FiアクセスポイントのSSIDとパスワードを設定して,LANの一員になろうと試みます。
成功すると,LAN内のDHCPサーバーからIPアドレス(一次的な)をもらいます。
XIAO ESP32S3は,クライアントなので他の機器からアクセスされることはないので,このIPアドレスもらって,自分が使うだけです。
一定時間ごとに,温度センサBMP280から,温度を取得し,"ホスト名","標高","温度","気圧"の文字列をjson型式のデータとしてWebサーバにpostメソッドで送信します。
HTTPSの手順はWi-FiClientSecure *clientが行ってくれます。
client->setCACert(Root_CaStr);
で「SSLサーバ証明書」の確認をするように設定します。
「SSLサーバ証明書」の確認を省略することもできますが,その場合は
client->setInsecure();
を使います。
次のコードがXIAO ESP32S3で使用されています。
ライブラリ"Grove_-_Barometer_Sensor_BMP280"を予め追加しておいてください。
「Root_CaStr」のところは,「ISRG Root X1」というLet's Encrypt(レンタルサーバの無料sslでよく使われています。)のルート CA 証明書が貼り付けてあります。
対象webサーバのルート CA 証明書に貼り替えてください。
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <HTTPClient.h>
#include <Seeed_BMP280PP.h>
#include <Wire.h>
#include <Arduino_JSON.h>
//IP address of Web Server
#define SERVER_IP "REPLACE_WITH_YOUR_TARGET_SERVER_NAME_OR_IP_ADDRESS"
//access target path in the Web Server
#define SERVER_PATH "jsontest/jsontest.php"
#define STASSID "REPLACE_WITH_YOUR_SSID"
#define STAPSK "REPLACE_WITH_YOUR_PASSWORD"
#define CLIENTNAME "esp32a"
const char* Root_CaStr=
"-----BEGIN CERTIFICATE-----\n"
"MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw\n"
"TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n"
"cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4\n"
"WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu\n"
"ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY\n"
"MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc\n"
"h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+\n"
"0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U\n"
"A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW\n"
"T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH\n"
"B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC\n"
"B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv\n"
"KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn\n"
"OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn\n"
"jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw\n"
"qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI\n"
"rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV\n"
"HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq\n"
"hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL\n"
"ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ\n"
"3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK\n"
"NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5\n"
"ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur\n"
"TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC\n"
"jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc\n"
"oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq\n"
"4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA\n"
"mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d\n"
"emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=\n"
"-----END CERTIFICATE-----\n";
// ISRG Root X1.crt
const char* ssid = STASSID;
const char* password = STAPSK;
const String clientname = CLIENTNAME;
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");
delay(1000); //For stable operation of the device
}
pinMode(led, OUTPUT);
digitalWrite(led, ledonoff); //turn LED on
}
void loop() {
WiFiClientSecure *client = new WiFiClientSecure;
if(client) {
//// Use one or the other.
//// set secure client with certificate (HTTPS Request with Certificate)
client->setCACert(Root_CaStr);
//// set secure client without certificate (HTTPS Request without Certificate)
//client->setInsecure();
//create an HTTPClient instance
HTTPClient https;
String url = String("https://") + SERVER_IP + SERVER_PATH;
Serial.print("[HTTPS] begin...\n");
if (https.begin(*client, url)) { // HTTPS
https.addHeader("Content-Type", "application/json");
Serial.print("[HTTPS] POST...\n");
// start connection and send HTTPS 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 = https.POST(jsonstr);
// httpCode will be negative on error
if (httpCode > 0) {
// HTTPS header has been sent and Server response header has been handled
Serial.printf("[HTTPS] POST... code: %d\n", httpCode);
// file found at server
if (httpCode == HTTP_CODE_OK) {
const String& payload = https.getString();
Serial.println("received payload:\n<<");
Serial.println(payload);
Serial.println(">>");
}
} else {
Serial.printf("[HTTPS] POST... failed, error: %s\n", https.errorToString(httpCode).c_str());
}
https.end();
digitalWrite(led, ledonoff = 1 - ledonoff);
}
} else {
Serial.printf("[HTTPS] Unable to connect\n");
}
delay(60000);
}
BMP280PPに関する説明はhttpクライアント制作記事を参照してください。≫
6 HTTPS接続のWebサーバ
HTTPS接続のWebサーバはレンタルサーバを用いました。
使用したphpプログラムに関してはhttpクライアント制作記事を参照してください。≫
7 まとめ
Wi-FiマイコンXIAO ESP32S3をHTTPS接続のWebクライアントとして動作させ,XIAO ESP32S3に接続された温度センサBMP280の値を,をHTTPS接続のWebサーバの受け皿URLに60秒間隔でデータを送信するようにしました。
Webサーバの受け皿であるphpプログラムは受け取ったデータをファイルに追記するようにしました。