keiske-hongyo が 2025年08月17日18時14分38秒 に編集
初版
タイトルの変更
ESP8266とBME280を使用した温度・湿度・気圧のRESTful APIサーバの構築
タグの変更
ESP8266
Arduino
BME280
記事種類の変更
製作品
ライセンスの変更
(CC BY-NC-SA 4+) Creative Commons Attribution-NonCommercial-ShareAlike CC BY-NC-SA version 4.0 or later
本文の変更
# はじめに 以前にESP8266を購入し、何かネットワークを使用した製作をしたいと考えていました。そのときに秋月で購入したBME280のモジュールがあったのでこれを使用して、温度、湿度、気圧をネットワーク内から取得するものを製作しようと考えました。そこでArduinoを使用してESP8266をサーバにしてBME280から得た温度、湿度、気圧をRESTful APIで提供できるようにしました。 # 使用部品 今回は下の表の部品を使用して回路を製作しました。 | 部品名 | 型番 | 備考 | |:---:|:---:|:---:| | ESP-WROOM-02開発ボード | AE-ESP-WROOM-02-DEV | 秋月電子で購入 | | BME280センサモジュール | AE-BME280 | 秋月電子で購入| | トランジスタ | 2SC1815 | LEDドライブ | | LED | 3mm | 動作確認用 | | 抵抗 | 1kΩ | LED、トランジスタへの電流制限用 | | スイッチ | タクトスイッチ | 入力受付用 | | コネクタ | XHコネクタ | 2ピン(電源用)、3ピン(出力用) | # 回路図 回路図は下のようになります。  BME280とはI2Cで通信をしており、Wireライブラリを使用しているのでGPIO4端子がSDA、GPIO5端子がSCLになります。 データの受信の確認をするために、GPIO14端子でLEDを点滅させ、I2Cの処理ができているかをモニタしています。 # プログラム 今回はArduinoを使用して、作成しています。 データの取得は1秒ごとにタイマー割込を発生させ、センサの情報を取得し、LEDをON、OFF制御しています。また、クライアントからデータの取得要求があれば、取得したデータをJSON形式にして、送信しています。また、接続が ```arduino:Lチカの例 #include <ArduinoJson.h> #include <ESP8266WiFi.h> #include <ESP8266WebServer.h> #include <Wire.h> #include <FS.h> #define BME280_ADDRESS 0x76 #define MSEC2CLOCK(ms) (ms * 80000L) // ミリ秒をクロック数に換算 (@80MHz) #define INTERVAL 1000 // 割込間隔 (ms) unsigned long int hum_raw, temp_raw, pres_raw; signed long int t_fine; unsigned char md; uint16_t dig_T1; int16_t dig_T2, dig_T3; uint16_t dig_P1; int16_t dig_P2, dig_P3, dig_P4, dig_P5, dig_P6, dig_P7, dig_P8, dig_P9; int8_t dig_H1, dig_H3, dig_H6; int16_t dig_H2, dig_H4, dig_H5; double temp_act = 0.0, press_act = 0.0, hum_act = 0.0; /* WebServerの設定 */ ESP8266WebServer server(8081); /* 使用するポート番号*/ IPAddress ip(192, 168, 1, 2); /* ESP8266のIPアドレス*/ IPAddress dns(192, 168, 1, 3); /* DNS */ IPAddress subnet(255, 255, 255, 0); /* Netmask */ IPAddress gateway(192, 168, 1, 1); /* デフォルトゲートウェイ */ /* Wi-Fi設定保存ファイル */ const char *settings = "/wifi_settings.txt"; /* ルート -> 説明を表示 */ void handleRoot() { // Serial.println("receive req: /"); server.send(200, "text/html", "<h1>BME280 Server access to /sensor </h1>"); } /* データ送信 */ void sensorTojson() { String out; StaticJsonDocument<500> doc; JsonObject root = doc.to<JsonObject>(); /* データの格納 */ root["Temp"] = temp_act; root["Press"] = press_act; root["Hum"] = hum_act; /* JSONデータの作成*/ serializeJson(root, out); /* データの送信 */ server.send(200, "application/json", out); } /** * WiFi設定 */ /* 設定画面 */ void handleSettingGet() { String html = ""; html += "<h1>WiFi Settings</h1>"; html += "<form method='post'>"; html += " ssid : <input type='text' name='ssid' placeholder='ssid'><br>"; html += " pass phrase : <input type='text' name='pass' placeholder='pass'><br>"; html += " <input type='submit'><br>"; html += "</form>"; server.send(200, "text/html", html); } /* 設定を書き込み */ void handleSettingPost() { String ssid = server.arg("ssid"); String pass = server.arg("pass"); File f = SPIFFS.open(settings, "w"); f.println(ssid); f.println(pass); f.close(); String html = ""; html += "<h1>WiFi Settings</h1>"; html += ssid + "<br>"; html += pass + "<br>"; server.send(200, "text/html", html); } /**************************************************************/ /* BME280関係 サブルーチン */ /**************************************************************/ /* 補正データの読み込み*/ void readTrim() { uint8_t data[32], i = 0; Wire.beginTransmission(BME280_ADDRESS); Wire.write(0x88); Wire.endTransmission(); Wire.requestFrom(BME280_ADDRESS, 24); while (Wire.available()) { data[i] = Wire.read(); i++; } Wire.beginTransmission(BME280_ADDRESS); Wire.write(0xA1); Wire.endTransmission(); Wire.requestFrom(BME280_ADDRESS, 1); data[i] = Wire.read(); i++; Wire.beginTransmission(BME280_ADDRESS); Wire.write(0xE1); Wire.endTransmission(); Wire.requestFrom(BME280_ADDRESS, 7); while (Wire.available()) { data[i] = Wire.read(); i++; } dig_T1 = (data[1] << 8) | data[0]; dig_T2 = (data[3] << 8) | data[2]; dig_T3 = (data[5] << 8) | data[4]; dig_P1 = (data[7] << 8) | data[6]; dig_P2 = (data[9] << 8) | data[8]; dig_P3 = (data[11] << 8) | data[10]; dig_P4 = (data[13] << 8) | data[12]; dig_P5 = (data[15] << 8) | data[14]; dig_P6 = (data[17] << 8) | data[16]; dig_P7 = (data[19] << 8) | data[18]; dig_P8 = (data[21] << 8) | data[20]; dig_P9 = (data[23] << 8) | data[22]; dig_H1 = data[24]; dig_H2 = (data[26] << 8) | data[25]; dig_H3 = data[27]; dig_H4 = (data[28] << 4) | (0x0F & data[29]); dig_H5 = (data[30] << 4) | ((data[29] >> 4) & 0x0F); dig_H6 = data[31]; } /* センサからデータの読み込み */ void readData() { int i = 0; uint32_t data[8]; Wire.beginTransmission(BME280_ADDRESS); Wire.write(0xF7); Wire.endTransmission(); Wire.requestFrom(BME280_ADDRESS, 8); while (Wire.available()) { data[i] = Wire.read(); i++; } pres_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4); temp_raw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4); hum_raw = (data[6] << 8) | data[7]; } /* 温度の計算 */ signed long int calibration_T(signed long int adc_T) { signed long int var1, var2, T; var1 = ((((adc_T >> 3) - ((signed long int)dig_T1 << 1))) * ((signed long int)dig_T2)) >> 11; var2 = (((((adc_T >> 4) - ((signed long int)dig_T1)) * ((adc_T >> 4) - ((signed long int)dig_T1))) >> 12) * ((signed long int)dig_T3)) >> 14; t_fine = var1 + var2; T = (t_fine * 5 + 128) >> 8; return T; } /* 気圧の計算 */ unsigned long int calibration_P(signed long int adc_P) { signed long int var1, var2; unsigned long int P; var1 = (((signed long int)t_fine) >> 1) - (signed long int)64000; var2 = (((var1 >> 2) * (var1 >> 2)) >> 11) * ((signed long int)dig_P6); var2 = var2 + ((var1 * ((signed long int)dig_P5)) << 1); var2 = (var2 >> 2) + (((signed long int)dig_P4) << 16); var1 = (((dig_P3 * (((var1 >> 2) * (var1 >> 2)) >> 13)) >> 3) + ((((signed long int)dig_P2) * var1) >> 1)) >> 18; var1 = ((((32768 + var1)) * ((signed long int)dig_P1)) >> 15); if (var1 == 0) { return 0; } P = (((unsigned long int)(((signed long int)1048576) - adc_P) - (var2 >> 12))) * 3125; if (P < 0x80000000) { P = (P << 1) / ((unsigned long int)var1); } else { P = (P / (unsigned long int)var1) * 2; } var1 = (((signed long int)dig_P9) * ((signed long int)(((P >> 3) * (P >> 3)) >> 13))) >> 12; var2 = (((signed long int)(P >> 2)) * ((signed long int)dig_P8)) >> 13; P = (unsigned long int)((signed long int)P + ((var1 + var2 + dig_P7) >> 4)); return P; } /* 湿度の計算 */ unsigned long int calibration_H(signed long int adc_H) { signed long int v_x1; v_x1 = (t_fine - ((signed long int)76800)); v_x1 = (((((adc_H << 14) - (((signed long int)dig_H4) << 20) - (((signed long int)dig_H5) * v_x1)) + ((signed long int)16384)) >> 15) * (((((((v_x1 * ((signed long int)dig_H6)) >> 10) * (((v_x1 * ((signed long int)dig_H3)) >> 11) + ((signed long int)32768))) >> 10) + ((signed long int)2097152)) * ((signed long int)dig_H2) + 8192) >> 14)); v_x1 = (v_x1 - (((((v_x1 >> 15) * (v_x1 >> 15)) >> 7) * ((signed long int)dig_H1)) >> 4)); v_x1 = (v_x1 < 0 ? 0 : v_x1); v_x1 = (v_x1 > 419430400 ? 419430400 : v_x1); return (unsigned long int)(v_x1 >> 12); } /* 温度・湿度・気圧のデータ処理 */ void readBME280() { signed long int temp_cal; unsigned long int press_cal, hum_cal; readData(); temp_cal = calibration_T(temp_raw); press_cal = calibration_P(pres_raw); hum_cal = calibration_H(hum_raw); temp_act = (double)temp_cal / 100.0; press_act = (double)press_cal / 100.0; hum_act = (double)hum_cal / 1024.0; Serial.print("TEMP : "); Serial.print(temp_act); Serial.print(" DegC PRESS : "); Serial.print(press_act); Serial.print(" hPa HUM : "); Serial.print(hum_act); Serial.println(" %"); } /* レジスタへの書き込み */ void writeReg(uint8_t reg_address, uint8_t data) { Wire.beginTransmission(BME280_ADDRESS); Wire.write(reg_address); Wire.write(data); Wire.endTransmission(); } /**************************************************************/ /* 初期化ルーチン */ /**************************************************************/ void setup() { uint8_t osrs_t = 1; // Temperature oversampling x 1 uint8_t osrs_p = 1; // Pressure oversampling x 1 uint8_t osrs_h = 1; // Humidity oversampling x 1 uint8_t mode = 3; // Normal mode uint8_t t_sb = 5; // Tstandby 1000ms uint8_t filter = 0; // Filter off uint8_t spi3w_en = 0; // 3-wire SPI Disable uint8_t ctrl_meas_reg = (osrs_t << 5) | (osrs_p << 2) | mode; uint8_t config_reg = (t_sb << 5) | (filter << 2) | spi3w_en; uint8_t ctrl_hum_reg = osrs_h; String ssid; String pwd; // Srial Start Serial.begin(9600); // ファイルシステム初期化 SPIFFS.begin(); File f = SPIFFS.open(settings, "r"); if (!f) { Serial.println("Default SSID And PassWord!"); ssid = "使用するSSID"; pwd = "SSIDのパスワード"; } else { ssid = f.readStringUntil('\n'); pwd = f.readStringUntil('\n'); ssid.trim(); pwd.trim(); } Serial.println("SSID: " + ssid); Serial.println("PASS: " + pwd); f.close(); // I2C Start Wire.begin(); // WIFI Start Serial.println("ap setup..."); WiFi.mode(WIFI_STA); WiFi.config(ip, dns, gateway, subnet); WiFi.begin(ssid.c_str(), pwd.c_str()); // Wifi 接続確認 while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected"); // IPアドレス表示 Serial.print("IP address: "); Serial.println(WiFi.localIP()); server.on("/", handleRoot); server.on("/sensor", sensorTojson); server.on("/setting", HTTP_GET, handleSettingGet); server.on("/setting", HTTP_POST, handleSettingPost); server.begin(); // BME280 Initilaize writeReg(0xF2, ctrl_hum_reg); writeReg(0xF4, ctrl_meas_reg); writeReg(0xF5, config_reg); readTrim(); // pinMode(2, INPUT); pinMode(14, OUTPUT); md = HIGH; // タイマ割り込みの設定 noInterrupts(); timer0_isr_init(); timer0_attachInterrupt(timer0_ISR); timer0_write(ESP.getCycleCount() + MSEC2CLOCK(INTERVAL)); // 1000msec interrupts(); } /**************************************************************/ /* メインルーチン */ /**************************************************************/ void loop() { server.handleClient(); } /**************************************************************/ /* タイマ割り込みハンドラ */ /**************************************************************/ void timer0_ISR(void) { timer0_write(ESP.getCycleCount() + MSEC2CLOCK(INTERVAL)); // 500msec // TODO 割り込み処理をここに if (md == HIGH) { md = LOW; } else { md = HIGH; } digitalWrite(14, md); readBME280(); } ``` # 最後に かなり、前に製作したものなのでライブラリが新しくなっていると動かないことがあるかもしれません。ESP8266とArduinoでネットワークを活用したものを製作の参考になれば幸いです。