#include <Arduino.h> #include "BLEDevice.h" #include "Regexp.h" #include "SimpleMap.h" static int timerLog = 5; // seconds static int factorMsToPpm = 700; // US: 500, EU: 640, AU: 700 (= device default) static BLEUUID serviceUUID("0000ffe0-0000-1000-8000-00805f9b34fb"); static BLEUUID charUUID("0000ffe1-0000-1000-8000-00805f9b34fb"); static boolean doConnect = false; static boolean doScan = true; static BLERemoteCharacteristic *pRemoteCharacteristic; static BLEAdvertisedDevice *myDevice; SimpleMap<String, double> *sensorData; void bleDataCallback(const char *match, const unsigned int length, const MatchState &ms) { char cap[20]; String value = ms.GetCapture(cap, 0); String unit = ms.GetCapture(cap, 1); String temp = ms.GetCapture(cap, 2); if (unit != "ppt") { sensorData->put("temperature", temp.toFloat()); } //Serial.println(unit + " = " + value + " @ " + temp + " °C"); if (unit == "pH") { sensorData->put("ph", value.toFloat()); } else if (unit == "mS") { sensorData->put("ec_ms", value.toFloat()); sensorData->put("ec_us", value.toFloat() * 1000); sensorData->put("ec_ppm", value.toFloat() * factorMsToPpm); sensorData->put("ec_ppt", value.toFloat() * factorMsToPpm / 1000); } else if (unit == "uS") { sensorData->put("ec_ms", value.toFloat() / 1000); sensorData->put("ec_us", value.toFloat()); sensorData->put("ec_ppm", value.toFloat() / 1000 * factorMsToPpm); sensorData->put("ec_ppt", value.toFloat() / 1000 * factorMsToPpm / 1000); } else if (unit == "ppm") { sensorData->put("ec_ms", value.toFloat() / factorMsToPpm); sensorData->put("ec_us", value.toFloat() / factorMsToPpm * 1000); sensorData->put("ec_ppm", value.toFloat()); sensorData->put("ec_ppt", value.toFloat() / 1000); } else if (unit == "ppt") { sensorData->put("ec_ms", value.toFloat() / factorMsToPpm * 1000); sensorData->put("ec_us", value.toFloat() / factorMsToPpm * 1000 * 1000); sensorData->put("ec_ppm", value.toFloat() * 1000); sensorData->put("ec_ppt", value.toFloat()); } } void bleParseData(uint8_t *bleData) { MatchState ms((char *)bleData); ms.GlobalMatch("([%d%.?]+)%s+(%a+)%c+([%d%.]+)", bleDataCallback); } static void notifyCallback( BLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify) { //Serial.println((char *)pData); //Serial.println("---"); bleParseData(pData); } class MyClientCallback : public BLEClientCallbacks { void onConnect(BLEClient *pclient) { doConnect = false; } void onDisconnect(BLEClient *pclient) { Serial.println("[BLE] Disconnected"); ESP.restart(); } }; bool connectToServer() { Serial.println("[BLE] Connecting to: " + String(myDevice->getAddress().toString().c_str())); BLEClient *pClient = BLEDevice::createClient(); pClient->setClientCallbacks(new MyClientCallback()); if (pClient->connect(myDevice)) { Serial.println("[BLE] Connected"); } else { Serial.println("[BLE] Could not connect"); return false; } BLERemoteService *pRemoteService = pClient->getService(serviceUUID); if (pRemoteService == nullptr) { Serial.print("[BLE] Failed to find service UUID"); pClient->disconnect(); return false; } pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID); if (pRemoteCharacteristic == nullptr) { Serial.print("[BLE] Failed to find characteristic UUID"); pClient->disconnect(); return false; } else { Serial.println("[BLE] Enabling data sending"); // magic number: "0003000000144414"; byte tmp[8] = {0x00, 0x03, 0x00, 0x00, 0x00, 0x14, 0x44, 0x14}; pRemoteCharacteristic->writeValue(tmp, sizeof(tmp)); } if (pRemoteCharacteristic->canNotify()) { pRemoteCharacteristic->registerForNotify(notifyCallback); } return true; } class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks { void onResult(BLEAdvertisedDevice advertisedDevice) { if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) { BLEDevice::getScan()->stop(); myDevice = new BLEAdvertisedDevice(advertisedDevice); doConnect = true; doScan = false; } } }; void bleScan() { Serial.println("[BLE] Scanning..."); BLEScan *pBLEScan = BLEDevice::getScan(); pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); pBLEScan->setInterval(1349); pBLEScan->setWindow(449); pBLEScan->setActiveScan(true); pBLEScan->start(5, false); Serial.println("[BLE] Scan finished"); } void logSensorData() { Serial.printf("Temp : %.1f °C\r\n", sensorData->get("temperature")); Serial.printf("pH : %.2f\r\n", sensorData->get("ph")); Serial.printf("mS/cm: %.2f\r\n", sensorData->get("ec_ms")); Serial.printf("uS/cm: %.0f\r\n", sensorData->get("ec_us")); Serial.printf("ppm : %.0f\r\n", sensorData->get("ec_ppm")); Serial.printf("ppt : %.2f\r\n", sensorData->get("ec_ppt")); Serial.println("---"); } void setup() { Serial.begin(115200); Serial.println(); BLEDevice::init(""); bleScan(); sensorData = new SimpleMap<String, double>([](String &a, String &b) -> int { if (a == b) return 0; if (a > b) return 1; /*if (a < b)*/ return -1; }); } void loop() { if (doConnect && !connectToServer()) { ESP.restart(); } if (doScan) { BLEDevice::getScan()->start(0); } static unsigned long logTimer = millis(); if (millis() - logTimer >= timerLog * 1000) { logSensorData(); logTimer = millis(); } }