ESP32 environment sensor node and data logger on WiFi LAN
Overview
In order to investigate the temperature distribution in the indoor space, we created a system that records data from multiple environmental sensor nodes wirelessly to a data logger. The environmental sensor node performs intermittent operation by connecting the BME280 environmental sensor to the ESP32 as a battery drive to facilitate installation. The data logger connects the real-time clock and SD card to the ESP 32 and records the received time and data in a file for each sender. Assign a fixed IP address with the ESP32 MAC address using a dedicated WiFi router. Data is sent from the environmental sensor node to the data logger’s IP address using UDP, and the data logger learns the environmental sensor node number from the sender IP address.
概要
屋内空間の温度分布を調べるために、複数の環境センサーノードから無線でデータロガーに記録するシステムを作りました。環境センサーノードは設置を容易にするために電池駆動としてESP32にBME280環境センサーを接続して、電池の交換頻度を減らすために間欠動作を行います。データロガーはESP32にリアルタイムクロックとSDカードを接続して、受け取った時刻とデータを送信元ごとのファイルに記録します。専用のWiFi ルーターを使って使用するESP32のMACアドレスで固定IPアドレスを割り当てます。環境センサーノードからデータロガーのIPアドレスに向けてUDPでデータを送り、データロガーは送信元IPアドレスから環境センサーノードの番号を知ります。
使用した機材
ESP32: MH-ET LIVE ESP-32 Mini kit
10ピン4列のピンソケットには分割ロングピンソケット 1x42 [FHU-1x42SG] (秋月電子通商)を使用しました。
http://mh.nodebb.com/topic/5/mh-et-live-esp-32-devkit-mini-kit-user-guide-updating
環境センサー: BME280 GYBMEP
ライブラリ:BlueDot BME280 Library
BME280 : ESP32
VIN : 3.3V
GND :GND
SCL :IO22
SDA :IO21
マイクロSDモジュール:
ライブラリ: ESP32標準 SD(esp32)
配線
SD : ESP32
CS : IO5
SCK : IO18
MOSI : IO23
MISO : IO19
VCC : VCC
GND : GND
リアルタイムクロック: DS3231 ZS-042モジュール
充電できないCR2032を使用するため充電用パターンをカットしました。
ライブラリ:DS3231FS
配線
DS3231 : ESP32
32K : NC
SQW : NC
SCL : IO22
SDA : IO21
VCC : VCC
GND : GND
無線LANポータブルルーター: エレコムWRH-300BK3-S
ESP32のMACアドレスを調べて、MACアドレスとIPアドレスを紐付けにします。SSIDのスティルス機能を使い、登録されたMACアドレス以外は接続を拒否しています。
電池ケース: マルツGB-BHS-3X4C-LW(単3×4本 スイッチ・フタ・リード線付き)
ESP32のVDDに+6V、ESP32のGNDに0Vを接続します。
動作の安定性について
ESP32環境センサーノードは送信時に毎回起動するような動作のため安定して動作します。ESP32データロガーは連続動作のためWiFi LANへの接続不良の場合やSDへの記録が正常に行えない場合はESP32をリスタートすることにしました。
ESP32環境センサーノードのスケッチ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
//スリープから指定時間後に起動し、 //温度・湿度・気圧を測定し、 //UDP通信でIPアドレス***,***,***,***の50021ポートに送る。 //その後スリープする。 //BME280環境センサー #include <Wire.h> #include "BlueDot_BME280.h" BlueDot_BME280 bme280 = BlueDot_BME280(); #define BUFF_MAX 128 char message[BUFF_MAX]; //WiFi設定 #include "WiFi.h" #include "AsyncUDP.h" const char * ssid = "Your SSID"; const char * password = "Your Password"; AsyncUDP udp; //タイマー起動 #define uS_TO_S_FACTOR 1000000 /* Conversion factor for micro seconds to seconds */ #define TIME_TO_SLEEP 60 /* Time ESP32 will go to sleep (in seconds) */ RTC_DATA_ATTR int bootCount = 0; void setup(){ Serial.begin(9600); WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); delay(1000); //Take some time to open up the Serial Monitor ++bootCount; //Increment boot number and print it every reboot Serial.println("Boot number: " + String(bootCount)); esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR); Serial.println("Setup ESP32 to sleep for every " + String(TIME_TO_SLEEP) + " Seconds"); BME280_parameter(); bme280.init(); delay(5000); float temp=bme280.readTempC(); float hum=bme280.readHumidity(); float pres=bme280.readPressure(); snprintf(message, BUFF_MAX, "%.1f\t%.1f\t%.1f\n", temp, hum, pres); Serial.print(message); if(udp.connect(IPAddress(***,***,***,***), 50021)){ udp.print(message); } delay(3000); esp_deep_sleep_start(); } void BME280_parameter(){ bme280.parameter.communication = 0; bme280.parameter.I2CAddress = 0x76; bme280.parameter.sensorMode = 0b11; bme280.parameter.IIRfilter = 0b100; bme280.parameter.humidOversampling = 0b101; bme280.parameter.tempOversampling = 0b101; bme280.parameter.pressOversampling = 0b101; bme280.parameter.pressureSeaLevel = 1013.25; bme280.parameter.tempOutsideCelsius = 15; } void loop(){ //This is not going to be called } |
ESP32データロガーのスケッチ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
//WiFi #include "WiFi.h" #include "AsyncUDP.h" const char * ssid = "MaDAesp32NET"; const char * password = "1655arduino32"; AsyncUDP udp; //SD #include "FS.h" #include "SD.h" #include "SPI.h" //RTC #include <Wire.h> #include <ds3231.h> ts t; //ts is a struct findable in ds3231.h #define BUFF_MAX 128 char buff[BUFF_MAX]; char dateString[BUFF_MAX]; char dataFileName[BUFF_MAX]; char timeString[BUFF_MAX]; char recString[BUFF_MAX]; char ipString[BUFF_MAX]; int led_r =16; int led_g =17; int myTimer =0; int udpCount; void setup() { Serial.begin(9600); pinMode(led_r, OUTPUT); pinMode(led_g, OUTPUT); WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); if (WiFi.waitForConnectResult() != WL_CONNECTED) { Serial.println("WiFi Failed"); delay(10000); /* while(1) { delay(1000); } */ } udpCount=0; Wire.begin(); DS3231_init(DS3231_INTCN); if(!SD.begin()){ Serial.println("Card Mount Failed"); return; } checkSD(); } void loop() { if(udp.listen(50021)) { udp.onPacket([](AsyncUDPPacket packet) { myTimer=0; IPAddress ipadr = packet.remoteIP(); snprintf(ipString, BUFF_MAX, "%d.%d.%d.%d", ipadr[0], ipadr[1], ipadr[2], ipadr[3]); int snrNum=ipadr[3]-20; //IPアドレスの末尾からセンサー番号を求める DS3231_get(&t); //時刻を求める snprintf(dateString, BUFF_MAX, "%02d%02d%02d", t.year%100, t.mon, t.mday); snprintf(timeString, BUFF_MAX, "%02d:%02d:%02d", t.hour, t.min, t.sec); sprintf(recString, "%d\t%s\t%s\t%s\t%s",udpCount,ipString,dateString,timeString,packet.data()); sprintf(dataFileName,"/T_files/%s%02d.txt", dateString,snrNum); Serial.println(dataFileName); if(SD.exists(dataFileName)){ appendFile(SD, dataFileName, recString); Serial.print("Append "); }else{ writeFile(SD, dataFileName, recString); Serial.print("Create "); } Serial.println(recString); udpCount++; }); } if(WiFi.status() != WL_CONNECTED){ ESP.restart(); } if(myTimer<1000){ digitalWrite(led_r, HIGH); } else{ digitalWrite(led_r, LOW); } delay(10); myTimer++; } void checkSD(){ uint8_t cardType = SD.cardType(); if(cardType == CARD_NONE){ Serial.println("No SD card attached"); return; } } void writeFile(fs::FS &fs, const char * path, const char * message){ //Serial.printf("Writing file: %s\n", path); File file = fs.open(path, FILE_WRITE); if(!file){ Serial.println("Failed to open file for writing"); digitalWrite(led_g, LOW); ESP.restart(); return; } if(file.print(message)){ //Serial.println("File written"); digitalWrite(led_g, HIGH); } else { Serial.println("Write failed"); digitalWrite(led_g, LOW); ESP.restart(); } file.close(); } void appendFile(fs::FS &fs, const char * path, const char * message){ //Serial.printf("Appending to file: %s\n", path); File file = fs.open(path, FILE_APPEND); if(!file){ Serial.println("Failed to open file for appending"); digitalWrite(led_g, LOW); ESP.restart(); return; } if(file.print(message)){ //Serial.println("Message appended"); digitalWrite(led_g, HIGH); } else { Serial.println("Append failed"); digitalWrite(led_g, LOW); ESP.restart(); } file.close(); } |