- BLE経由で受信した温湿度と大気圧のデータをWebサーバにアップロードする方法を、備忘録として残しておきます

ソース
/* XIAO_ESP32C3_LED_Receive_Ant.ino Seeed XIAO BLE nRF52840から受信したBLEのアドバタイズデータを、WiFiでWebサーバーにPHPに送信する アンテナ付のハードでは、受信感度が高い OLED付のハードでは、受信感度が低いが、大気圧と温湿度を表示できる ボードマネージャ ESP32 by Espressif Syatem: Version 2.0.17 ツール Patiton Scheme: Huge App(3MB On OTA/1MB SPIFFS) */ #include <Arduino.h> // ------------WiFi------------------------------------ #include <WiFi.h> #include "time.h" #include "sntp.h" #include <HTTPClient.h> const String WEB_API_URL = "http://192.168.10.117/temp_humi_press.php"; // 送信先URL // const String WEB_API_URL_IN = "http://192.168.10.117/temp_humi_press_in.php"; // 送信先URL const char* ssid = "aterm-afc0ef-g"; const char* password = "3fe9c5cabc8c2"; const char* ntpServer1 = "ntp.nict.jp"; const char* ntpServer2 = "time.google.com"; const char* ntpServer3 = "ntp.jst.mfeed.ad.jp"; const long gmtOffset_sec = 9 * 3600; const int daylightOffset_sec = 0; // Callback function (get's called when time adjusts via NTP) void timeavailable(struct timeval *t) { Serial.print("Got time adjustment from NTP! "); } void printLocalTime() { char str[256]; unsigned long m; // timeとlocaltimeを使用して現在時刻取得 time_t t; struct tm *tm; m = millis(); t = time(NULL); tm = localtime(&t); sprintf(str, "\nday:%04d/%02d/%02d, time:%02d:%02d:%02d, ", tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); Serial.print(str); } // ------------U8g2------------------------------------ #include <U8g2lib.h> #ifdef U8X8_HAVE_HW_SPI #include <SPI.h> #endif #ifdef U8X8_HAVE_HW_I2C #include <Wire.h> #endif #define SDA_PIN 5 #define SCL_PIN 6 U8G2_SSD1306_72X40_ER_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE); // EastRising 0.42" OLED //------------BLE関連------------------------------ #include <BLEDevice.h> BLEScan* pBLEScan; // 設定 const size_t RCV_CNT_MAX = 10; // 同時に受信するデバイスの最大数 const int BLE_SCAN_TIME = 5; // BLEのスキャンを行う時間(s) const char XIAO[4] = { 0xFF, 0xFF, 0x12, 0x35 }; // XIAO nRF52840識別値(FFFFは固定) // シーケンス番号の記憶 #include <map> std::map<uint16_t, uint16_t> seqHist; // その他定義 struct RcvData { uint16_t id; // 子機ID uint8_t type; // 子機種別 uint8_t ttl; // TTL (time to live) uint16_t seq; // シーケンス番号 float pres; // 気圧データ float temp; // 気温データ float humi; // 湿度データ float vbat; // 電圧データ int16_t rssi; // 電波強度 RSSI -50非常に強い -80弱い }; // -----------デバッグに便利なマクロ定義 -------- #define sp(x) Serial.print(x) #define spn(x) Serial.print(x) #define spp(k,v) Serial.println(String(k)+"="+String(v)) #define spf(fmt, ...) Serial.printf(fmt, __VA_ARGS__) // LED int ledPin = 8; // LED connected to digital pin 8 // 初期化 void setup() { // auto cfg = M5.config(); // M5.begin(cfg); Serial.begin(115200); delay(1000); spn(" System Start! "); // ディスプレイの設定 Wire.begin(SDA_PIN, SCL_PIN); delay(100); u8g2.begin(); // u8g2.setFont(u8g2_font_helvB10_tr); // choose a suitable font u8g2.setFont(u8g2_font_lubB10_tr); // choose a suitable font u8g2.setContrast(190); // set contrast to maximum 255 u8g2.setBusClock(400000); //400kHz I2C // u8g2.setDisplayRotation(U8G2_R2); delay(100); // // Wi-Fi接続 //connect to WiFi Serial.printf("Connecting to %s ", ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.print(" CONNECTED "); //init and get the time sntp_set_time_sync_notification_cb( timeavailable ); configTime(gmtOffset_sec, daylightOffset_sec, ntpServer1, ntpServer2, ntpServer3); // // disconnect WiFi as it's no longer needed // WiFi.disconnect(true); // WiFi.mode(WIFI_OFF); // BLEの設定 spn("init BLE device..."); BLEDevice::init(""); pBLEScan = BLEDevice::getScan(); pBLEScan->setActiveScan(false); // パッシブスキャンにする } // メイン int BleTimer = 0; void loop() { RcvData rcvdatas[RCV_CNT_MAX]; int rcvcnt = 0; float pres,temp,humi; uint16_t rssi; // BLEのスキャンを行う // sp("\nscanning..."); BLEScanResults foundDevices = pBLEScan->start(BLE_SCAN_TIME); int hit = foundDevices.getCount(); // spf("found", hit); // 受信した一覧から対象デバイスを抽出する for (int i=0; i<hit; i++) { BLEAdvertisedDevice dev = foundDevices.getDevice(i); std::string data = dev.getManufacturerData(); // debug // if (data[0] == XIAO[0] && data[1] == XIAO[1]) { // String tmpinfo = ""; // spf("Scanned: Device_%03d (%02d): ", i, data.length()); // for (int j=0; j<data.length(); j++) spf("%02X ",data[j]); // sp(""); // } // XIAO nRF52840からのデータだったら値を格納する if (data.length() < 18 )continue; if (data[0] == XIAO[0] && data[1] == XIAO[1] && data[2] == XIAO[2] && data[3] == XIAO[3]) { rcvdatas[rcvcnt].id = data[5] << 8 | data[4]; rcvdatas[rcvcnt].type = data[6]; rcvdatas[rcvcnt].ttl = data[7]; rcvdatas[rcvcnt].seq = data[9] << 8 | data[8]; if (seqHist[rcvdatas[rcvcnt].id] == rcvdatas[rcvcnt].seq) continue; // 同じデータは無視 seqHist[rcvdatas[rcvcnt].id] = rcvdatas[rcvcnt].seq; if (rcvdatas[rcvcnt].type == 10) { // 種別10: 実験用 volt(i2) temp(i2) rcvdatas[rcvcnt].pres = (float)(data[11] << 8 | data[10]) / 10.00 ; rcvdatas[rcvcnt].temp = (float)(data[13] << 8 | data[12]) / 10.00; rcvdatas[rcvcnt].humi = (float)(data[15] << 8 | data[14]) / 10.00; rcvdatas[rcvcnt].vbat = (float)(data[17] << 8 | data[16]) / 100; rcvdatas[rcvcnt].rssi = (uint16_t)dev.getRSSI() ; // 電波強度 rcvcnt ++; } if (rcvcnt >= RCV_CNT_MAX) break; } } // ディスプレイに表示 if (rcvcnt > 0) { rcvcnt-- ; if ( BleTimer > 5 ){ //シリアル出力 printLocalTime(); Serial.printf("Pres:%.1f,temp:%.1f,humi:%.1f,vbat:%.1f,rssi:%d", rcvdatas[rcvcnt].pres - 1000, rcvdatas[rcvcnt].temp, rcvdatas[rcvcnt].humi, rcvdatas[rcvcnt].vbat, abs( rcvdatas[rcvcnt].rssi ) ); //液晶出力 u8g2.clearBuffer(); // clear the internal memory u8g2.setCursor(0,14); u8g2.printf("pres:%4d", int (rcvdatas[rcvcnt].pres) ); u8g2.setCursor(0,27); u8g2.printf("temp:%4.1f", rcvdatas[rcvcnt].temp); u8g2.setCursor(0,40); u8g2.printf("hum: %4.1f", rcvdatas[rcvcnt].humi); u8g2.display(); u8g2.sendBuffer(); // transfer internal memory to the display //WiFi出力 postServer(rcvdatas, 1); } // Serial.printf("\nPres:%.1f,temp:%.1f,humi:%.1f,rssi:%d", // rcvdatas[rcvcnt].pres, rcvdatas[rcvcnt].temp, rcvdatas[rcvcnt].humi,-1 * rcvdatas[rcvcnt].rssi ); BleTimer = 0; } else { BleTimer++; if ( (BleTimer % 5 ) == 0 ){ Serial.printf(" %d",BleTimer/5); } } delay(99); } // Webサーバーに送信 void postServer(struct RcvData* td, int cnt) { HTTPClient http; sp(" Send to Web Server "); // POSTデータ作成 String jsonData = "{\"data\":["; char buff[64]; for (int i=0; i<cnt; i++) { sprintf(buff, "{\"id\":%d,", td[i].id); jsonData += buff; sprintf(buff, "\"pres\":%.1f,", td[i].pres); jsonData += buff; sprintf(buff, "\"volt\":%.1f,", td[i].vbat); jsonData += buff; sprintf(buff, "\"temp\":%.1f,", td[i].temp); jsonData += buff; sprintf(buff, "\"humi\":%.1f,", td[i].humi); jsonData += buff; sprintf(buff, "\"rssi\":%d,", td[i].rssi); jsonData += buff; sprintf(buff, "\"seq\":%d}", td[i].seq); jsonData += buff; if (i < cnt-1) jsonData += ","; } jsonData += "],\"count\":"+String(cnt)+"}"; sp(jsonData); // 送信 // if(td[0].id == 101){ // http.begin(WEB_API_URL); //HTTP // } // else{ // http.begin(WEB_API_URL_IN); //HTTP // } http.begin(WEB_API_URL); //HTTP http.addHeader("Content-Type", "application/x-www-form-urlencoded"); http.addHeader("Content-Length", String(jsonData.length())); http.setFollowRedirects(HTTPC_FORCE_FOLLOW_REDIRECTS); int httpCode = http.POST(jsonData); printf("[HTTP] POST... code: %d ",httpCode); // 送信後の処理 if (httpCode == HTTP_CODE_OK) { String payload = http.getString(); printf(" OK %s ", payload); } else { printf("[HTTP] POST... failed, error: %s ",httpCode); } http.end(); }
コンパイルメモリ不足への対応

最後に
ここまでご覧いただき、ありがとうございました。もし誤りがあれば、ご指摘いただけますと幸いです