こんにちは、ENGかぴです。
ESP32-WROOM-32Eをサーバーで起動しアクセスポイントに接続させることでリクエストに応じたレスポンスを返信することができます。WiFiをサーバーで動作させる方法とWebServerを使ってレスポンスを行う方法をまとめています。
動作確認としてデータは温湿度センサー(SHT35-DIS)のデータをレスポンスで返信します。
ESP32-WROOM-32E開発ボード(秋月電子)を使用しArduino IDEで開発を行います。また、AE-SHT35(秋月電子)を使用しています。 アクセスポイントにはWZR-HP-G300NH(バッファロー:生産中止)を使用しています。
ESP32-WROOM-32Eを使って動作確認したことをまとめています。
ESP32-WROOM-32Eで学べるソフト開発と標準ライブラリの使い方
サーバーを実装する
ESP32-WROOM-32Eにサーバーを実装しクライアントからの接続をWebServerライブラリを使ってレスポンスします。WebServerライブラリを使ってレスポンスする方法は下記記事にまとめています。
ESP32-WROOM-32EでWebServerを実装する
本記事ではサーバーを実装する方法を中心に説明します。
WiFiをサーバで開始する
#include <WiFi.h>
const char *ssid = "xxxxxxxxxxxx"; //使用するアクセスポイントのSSID
const char *pass = "aaaabbbbcccc"; //使用アクセスポイントのpassword
const IPAddress ip(192,168,11,20); //IPアドレス
const IPAddress subnet(255,255,255,0); //サブネットマスク
void setup() {
WiFi.mode(WIFI_STA); //ステーションモード
WiFi.begin(ssid,pass);
WiFi.config(ip,ip,subnet); //サーバーのIPを固定する場合
while( WiFi.status() != WL_CONNECTED ){
delay(1000);
Serial.print(".");
if( ++waitcnt >= 60 ){
esp_restart(); //モジュールのリスタート
}
Serial.println(WiFi.localIP()); //接続後のIPアドレスを表示
}
}
WiFiライブラリをインクルードします。文字列に使用するアクセスポイントのSSIDとパスワードの文字列を定数で宣言しています。
WiFi.mode()でWIFI_STAを指定するとステーションモードとなりサーバー動作となります。WIFI_APを指定するとアクセスポイントモードになります。WIFI_AP_STAを指定するとアクセスポイント+ステーションモードになります。
WiFi.begin()でSSIDとPASSを指定してWiFi通信を開始します。アクセスポイントに接続要求を出し接続が確立するまで1秒おきに”.”をモニター表示して待ちます。60秒経過しても接続されない場合はesp_restart()によってESP32-WROOM-32Eをリスタートするようにしています。
アクセスポイントに接続出来たら接続したIPを表示しています。IPアドレスを固定していない場合はアクセスポイントが割り振ったIPアドレスになります。
広告
PR:わからないを放置せず、あなたにあったスキルを身に着けるコツを教える テックジム 「書けるが先で、理解が後」を体験しよう!
mDNSを実装する
ⅿSNSはmulticast DNSはLAN通信を搭載した機器に名前をつけてアクセスできるようにした規格です。mDNSを使用するとIPアドレスによるアクセスを使用せずに機器の名前でアクセスできるようになります。ESP32-WROOM-32EにおいてはESPmDNSライブラリを使用することで実装することができます。
#include <ESPmDNS.h>
void setup() {
if (MDNS.begin("esp32")) {
Serial.println("MDNS responder started");
//MDNS.addService("esp32_ser","tcp",80);
}
}
MDNS.begin()に例のように文字列を追加すると名前を付けることができます。クライアントはhttp://esp32.localのように名前でアクセスすることができるようになります。
WiFiの再接続の検討
void loop() {
auto status = WiFi.status();
if( status != WL_CONNECTED ){
if( timreconnect == TIME_OFF ){
timreconnect = TIME_RECONNCT_MAX;
Serial.println("Re-Connecting-start");
}
}
else{
Wserver.handleClient();
}
if( timreconnect == TIME_UP ){
timreconnect = TIME_OFF;
}
}
loop()内ではアクセスポイントとの接続状態を確認します。接続が切れると自動で再接続するためWiFi.status()でWiFiの接続状態を確認してWL_CONNECTED(接続中)でなければ再接続状態であることをシリアルモニターに表示して通知します。
再接続には少し時間がかかるためtimreconnectでタイマ管理を行い10秒経過毎に接続の様子を繰り返し表示します。
アクセスポイントとの接続に問題がなければ通常通りWebサーバーでクライアントからの接続を待ちます。
広告
動作確認
ESP32-WROOM-32EとSHT35-DISの配線例を示しています。回路図の番号はESP32 -WROOM-32Eの左上を1ピンとした時反時計回りにピンを数えた場合の番号としています。ピン番号横の()内の番号はシルク印刷されているピンの名称です。
アクセスポイントをルータとして動作させます。クライアントはアクセスポイントに対して接続要求を行い接続を確立します。ESP32-WROOM-32Eの電源を入れるとサーバーとして起動しアクセスポイントに対して接続要求を行います。
アクセスポイントへの接続要求が成功するとIPアドレスをシリアルモニタに表示します。メインループに遷移すると温湿度情報が表示されるためブラウザでの表示ができるようになります。AndroidではDNSが実装されていないためmDNSの有効性が確認できていません 。
メインループに入ると温度情報が表示されますが、この時アクセスポイントの電源をOFFにすると接続が遮断しますがステータス確認により接続の遮断を判断してアクセスポイントに対して再接続を行います。
アクセスポイントとの接続が遮断すると「Re-Connecting-start」が表示します。表示後は10秒間接続が確立されるのを待ちます。10秒後に接続が復帰していない場合再度「Re-Connecting-start」を表示して再接続を試みます。
シリアルモニタの表示は1秒ごとに温湿度情報を更新するため温湿度データが10回表示されると10秒経過したことなります。10回を超えても「Re-Connecting-start」が表示されず温湿度情報が表示されれば接続が確立できたことになります。
スマホ(Android)のGoogle ChromeでIPアドレス「192.168.11.20」を入力しリクエストを送るとESP32-WROOM-32Eからスマホに返信し温湿度データを表示されました。
通常の表示ではHTMLで5秒おきに画面をリフレッシュするようにしているため5秒ごとにクライアント(スマホ)からリクエストが送信されます。スマホの温湿度情報が5秒ごとに更新されていることを確認しています。
IPアドレス「192.168.11.20/ti」と存在しないURLを指定するとFile Not Foundが表示されることも確認しています。
ソースコード全体
以下のソースコードはコンパイルして動作確認をしております。コメントなど細かな部分で間違っていたりやライブラリの更新などにより動作しなくなったりする可能性があります。参考としてお使いいただければと思います。
#include <Wire.h>
#include <WiFi.h>
#include <WiFiClient.h>
#include <ESPmDNS.h>
#include <WebServer.h>
#define SLAVE_ADRS 0x45
#define POLYNOMIAL 0x31
#define PIN_DI 22
#define TIME_OFF -1 //タイマーを使用しない場合
#define TIME_UP 0 //タイムアップ
#define BASE_CNT 10 //ベースタイマカウント値
#define TIME_SHT35_MAX 100 //SHT35の計測タイマ
#define TIME_OUT_MAX 20 //SHT35の通信タイムアウト
#define TIME_RECONNCT_MAX 1000
typedef enum{
SHT35_MEASURE = 0,
SHT35_WAIT,
SHT35_READ,
SHT35_MAX
}SHT35_MODE;
const char *ssid = "xxxxxxxxxxxx"; //SSID
const char *pass = "aaaabbbbcccc"; //password
const IPAddress ip(192,168,11,20); //IPアドレス(アドレスを固定する場合)
const IPAddress subnet(255,255,255,0); //サブネットマスク(アドレスを固定する場合)
/* 変数宣言 */
WebServer Wserver(80);
SHT35_MODE mode = SHT35_MEASURE;
uint32_t beforetimCnt = millis();
float temp;
float humi;
int16_t timSht35start;
int16_t timSht35Out = TIME_OFF;
int16_t timreconnect = TIME_OFF;
uint8_t singleshot[2] = { 0x2C, 0x06};
uint8_t chksum[2];
uint8_t rxdata[6];
uint16_t waitcnt;
/* プロトタイプ宣言 */
void mainApp(void);
void Sht35Measure(uint8_t dev_id,uint8_t* cmd, uint16_t len);
bool Sht35GetData(uint8_t dev_id,uint8_t* reg_data, uint16_t len);
uint8_t Crc8Calc(uint8_t *data, uint8_t sz );
void HtmlSet(void);
void handleNotFound(void);
void setup() {
Serial.begin(115200);
Wire.begin();
WiFi.mode(WIFI_STA); //ステーションモード
WiFi.begin(ssid,pass);
WiFi.config(ip,ip,subnet);
Serial.println("");
while( WiFi.status() != WL_CONNECTED ){
delay(1000);
Serial.print(".");
if( ++waitcnt >= 60 ){
esp_restart();
}
Serial.print(waitcnt);
}
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
if (MDNS.begin("esp32")) {
Serial.println("MDNS responder started");
//MDNS.addService("esp32_ser","tcp",80);
}
Wserver.on("/", HTTP_GET, HtmlSet); //URLを指定して処理する関数を指定
Wserver.onNotFound(handleNotFound); //URLが存在しない場合の処理する関数を指定
Wserver.begin(); //Webサーバーの開始
}
void loop() {
mainTimer();
mainApp();
auto status = WiFi.status();
if( status != WL_CONNECTED ){
if( timreconnect == TIME_OFF ){
timreconnect = TIME_RECONNCT_MAX;
Serial.println("Re-Connecting-start");
}
}
else{
Wserver.handleClient();
}
if( timreconnect == TIME_UP ){
timreconnect = TIME_OFF;
}
}
/* タイマ管理 */
void mainTimer(void){
if ( millis() - beforetimCnt > BASE_CNT ){
beforetimCnt = millis();
if( timSht35start > TIME_UP ){
--timSht35start;
}
if( timSht35Out > TIME_UP ){
--timSht35Out;
}
if( timreconnect > TIME_UP ){
--timreconnect;
}
}
}
/* メイン処理関数 */
void mainApp(void){
switch(mode){
case SHT35_MEASURE:
if( timSht35start == TIME_UP ){
timSht35start = TIME_OFF;
timSht35Out = TIME_OUT_MAX;
Sht35Measure(SLAVE_ADRS, &singleshot[0], sizeof(singleshot));
mode = SHT35_WAIT;
}
break;
case SHT35_WAIT:
if( digitalRead(PIN_DI) == 1){
mode = SHT35_READ;
}
break;
case SHT35_READ:
if( Sht35GetData(SLAVE_ADRS, &rxdata[0], sizeof(rxdata))){
uint16_t tempHex;
uint16_t humiHex;
chksum[0] = Crc8Calc(&rxdata[0],2); //tempのCRCチェック
chksum[1] = Crc8Calc(&rxdata[3],2); //humiのCRCチェック
if( chksum[0] == rxdata[2] && chksum[1] == rxdata[5]){
tempHex = ((uint16_t)rxdata[0] << 8) | rxdata[1];
humiHex =((uint16_t)rxdata[3] << 8) | rxdata[4];
temp = (tempHex / 65535.00) * 175 - 45;
humi = (humiHex / 65535.0) * 100.0;
Serial.print("temp: ");
Serial.print(temp);
Serial.print("℃ ");
Serial.print("humi :");
Serial.print(humi);
Serial.print("% ");
Serial.println();
}
timSht35start = TIME_SHT35_MAX;
timSht35Out = TIME_OFF;
mode = SHT35_MEASURE;
}
break;
}
if( timSht35Out == TIME_UP ){
timSht35Out = TIME_OFF;
mode = SHT35_MEASURE;
timSht35start = TIME_SHT35_MAX;
}
}
/* SHT35へコマンド送出 */
void Sht35Measure(uint8_t dev_id,uint8_t* cmd, uint16_t len){
Wire.beginTransmission(dev_id); //スレーブが存在するか確認
byte error = Wire.endTransmission();
if( error == 0){ //スレーブが存在する場合下の処理
Wire.beginTransmission(dev_id);
for( uint16_t i=0; i < len; i++ ){
Wire.write(*cmd); //lenサイズ分だけデータを書き込む
++cmd;
}
Wire.endTransmission(); //ストップ・コンディションの発行
}
}
/* SHT35からデータを取得 */
bool Sht35GetData(uint8_t dev_id,uint8_t* reg_data, uint16_t len){
bool ret = false;
Wire.beginTransmission(dev_id); //スタート・コンディションの発行
byte error = Wire.endTransmission();
if( error == 0){ //スレーブが存在する場合下の処理
if( Wire.requestFrom(dev_id, len) == len ){
for( uint16_t i=0; i < len; i++ ){
*reg_data = Wire.read(); //len分だけデータをリードする
++reg_data;
}
ret = true;
}
}
return ret;
}
/* CRC8計算関数 */
uint8_t Crc8Calc(uint8_t *data, uint8_t sz ){
uint8_t crc = 0xFF;
uint8_t i,j;
for( i = 0; i < sz; i++){
crc ^= *data;
for( j = 0; j < 8; j++ ){
if( crc & 0x80 ){
crc = ( crc << 1 ) ^ POLYNOMIAL;
}
else{
crc = crc << 1;
}
}
++data;
}
return crc;
}
/* クライアントに返信するhtmlデータを生成 */
void HtmlSet(void){
String str = "";
str += "<html lang=\"ja\">";
str += "<head>";
str += "<meta http-equiv=\"refresh\" content=\"5\">";
str += "<meta charset=\"UTF-8\">";
str += "<title>Sensor SHT35</title>";
str += "</head>";
str += "<body>";
str += "<h1>ESP32-SHT35-DIS温湿度センサ</h1>";
str += "<h2>WebServerライブラリを使用</h2>";
str += "<h2>温度: ";
str += temp;
str += "℃";
str += "</h2>";
str += "<h2>湿度: ";
str += humi;
str += "%RH";
str += "</h2>";
str += "</body>";
str += "</html>";
Wserver.send(200,"text/html", str);
//HTTPレスポンス200でhtmlデータとして送信
}
/* URLが存在しない場合の処理 */
void handleNotFound(void) {
String message = "File Not Found\n\n";
message += "URI: ";
message += Wserver.uri();
message += "\nMethod: ";
message += (Wserver.method() == HTTP_GET) ? "GET" : "POST";
message += "\nArguments: ";
message += Wserver.args();
message += "\n";
for (uint8_t i = 0; i < Wserver.args(); i++) {
message += " " + Wserver.argName(i) + ": " + Wserver.arg(i) + "\n";
}
Wserver.send(404, "text/plain", message); //テキストファイルであることを示している。
}
アクセスポイントのSSIDとパスワードはお使いのアクセスポイントのものに置き換えてください。
関連リンク
Arduinoのライブラリを使って動作確認を行ったことを下記リンクにまとめています。
Arduinoで学べるマイコンのソフト開発と標準ライブラリの使い方
Seeeduino XIAOで学べるソフト開発と標準ライブラリの使い方
ESP32-WROOM-32Eで学べるソフト開発と標準ライブラリの使い方
最後まで、読んでいただきありがとうございました。
WiFi.mode()によるモードの指定は特に意識しなくても動作させることができるようになりました。WiFi.begin()でサーバーとして動作します。