From c77a16867d25b3d7f334a8f44695a6bff4c8bb27 Mon Sep 17 00:00:00 2001 From: DigiH Date: Wed, 1 Mar 2023 22:09:45 +0100 Subject: [PATCH] [SSD1306] BLE devices display - Take 2 (#1492) * BLE Display - THB, THBX, PLANT, AIR, BATT, BODY * SCALE * BBQ * Filter MS-CDP, GAEN and IBEACON * Mopeka & Smart Dry * ACEL ACEL sensor, as most also have temp and hum, but no acceleration info display included so far. * Only display when properties are present Only display when properties are present, to avoid model only display for devices which require active scanning at a larger interval, e.g. Mi Band and similar. * LilyGo-BLE environment * BLE display relocated BLE display relocated to have pubadvdata "type" in message, required for the different type display functions. --- .github/workflows/build.yml | 2 + environments.ini | 54 +++++- main/ZdisplaySSD1306.ino | 348 +++++++++++++++++++++++++++++++----- main/ZgatewayBT.ino | 7 +- platformio.ini | 2 + 5 files changed, 362 insertions(+), 51 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c5a26e2..eebcbc6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -39,7 +39,9 @@ jobs: - "esp32dev-rtl_433" - "esp32doitv1-aithinker-r01-sx1278" - "heltec-rtl_433" + - "heltec-ble" - "lilygo-rtl_433" + - "lilygo-ble" - "esp32dev-multi_receiver" - "tinypico-ble" - "ttgo-lora32-v1-868" diff --git a/environments.ini b/environments.ini index 297e296..5dd3867 100644 --- a/environments.ini +++ b/environments.ini @@ -744,9 +744,34 @@ build_flags = ; '-DJSON_TO_LCD=true' ; '-DLOG_LEVEL_LCD=LOG_LEVEL_NOTICE' ; '-DDISPLAY_IDLE_LOGO=false' +; '-DDISPLAY_BRIGHTNESS=80' ; '-DDISPLAY_METRIC=false' custom_description = Gateway using RTL_433_ESP and RadioLib +[env:heltec-ble] +platform = ${com.esp32_platform} +board = heltec_wifi_lora_32_V2 +; ~/.platformio/packages/framework-arduinoespressif32/variants/.../pins_arduino.h +board_build.partitions = min_spiffs.csv +lib_deps = + ${com-esp.lib_deps} + ${libraries.wifimanager32} + ${libraries.ssd1306} + ${libraries.ble} + ${libraries.decoder} +build_flags = + ${com-esp.build_flags} +; *** OpenMQTTGateway Config *** + ;'-UZmqttDiscovery' ; disables MQTT Discovery + '-DZgatewayBT="BT"' + '-DGateway_Name="OpenMQTTGateway_heltec_ble"' +; *** ssd1306 Display Options *** + '-DZdisplaySSD1306="HELTEC_SSD1306"' +; '-DDISPLAY_IDLE_LOGO=false' +; '-DDISPLAY_BRIGHTNESS=80' +; '-DDISPLAY_METRIC=false' +custom_description = Heltec BLE gateway with adaptive scanning activated, automatically adapts the scan parameters depending on your devices + [env:lilygo-rtl_433] platform = ${com.esp32_platform} board = ttgo-lora32-v21 @@ -772,11 +797,36 @@ build_flags = ; '-DJSON_TO_LCD=true' ; '-DLOG_LEVEL_LCD=LOG_LEVEL_NOTICE' ; '-DDISPLAY_IDLE_LOGO=false' -; '-DDISPLAY_BRIGHTNESS=100' +; '-DDISPLAY_BRIGHTNESS=80' ; '-DDISPLAY_METRIC=false' custom_description = For ESP32, Gateway using RTL_433_ESP and RadioLib custom_hardware = ESP32 LILYGO LoRa32 V2.1 +[env:lilygo-ble] +platform = ${com.esp32_platform} +board = ttgo-lora32-v21 +; ~/.platformio/packages/framework-arduinoespressif32/variants/.../pins_arduino.h +board_build.partitions = min_spiffs.csv +lib_deps = + ${com-esp.lib_deps} + ${libraries.wifimanager32} + ${libraries.ssd1306} + ${libraries.ble} + ${libraries.decoder} +build_flags = + ${com-esp.build_flags} +; *** OpenMQTTGateway Config *** +;'-UZmqttDiscovery' ; disables MQTT Discovery + '-DZgatewayBT="BT"' + '-DGateway_Name="OpenMQTTGateway_lilygo_ble"' +; *** ssd1306 Display Options *** + '-DZdisplaySSD1306="LilyGo_SSD1306"' +; '-DDISPLAY_IDLE_LOGO=false' +; '-DDISPLAY_BRIGHTNESS=80' +; '-DDISPLAY_METRIC=false' +custom_description = LilyGo BLE gateway with adaptive scanning activated, automatically adapts the scan parameters depending on your devices +custom_hardware = ESP32 LILYGO LoRa32 V2.1 + [env:esp32dev-multi_receiver] platform = ${com.esp32_platform} board = esp32dev @@ -914,6 +964,7 @@ build_flags = ; '-DJSON_TO_LCD=true' ; '-DLOG_LEVEL_LCD=LOG_LEVEL_NOTICE' ; '-DDISPLAY_IDLE_LOGO=false' +; '-DDISPLAY_BRIGHTNESS=80' ; '-DDISPLAY_METRIC=false' custom_description = LORA communication 868Mhz using arduino-LoRA custom_hardware = ESP32 HELTEC LORA32 @@ -941,6 +992,7 @@ build_flags = ; '-DJSON_TO_LCD=true' ; '-DLOG_LEVEL_LCD=LOG_LEVEL_NOTICE' ; '-DDISPLAY_IDLE_LOGO=false' +; '-DDISPLAY_BRIGHTNESS=80' ; '-DDISPLAY_METRIC=false' custom_description = LORA communication 915Mhz using arduino-LoRA custom_hardware = ESP32 HELTEC LORA32 diff --git a/main/ZdisplaySSD1306.ino b/main/ZdisplaySSD1306.ino index eed36a0..00f78cc 100644 --- a/main/ZdisplaySSD1306.ino +++ b/main/ZdisplaySSD1306.ino @@ -373,63 +373,317 @@ void ssd1306PubPrint(const char* topicori, JsonObject& data) { # endif # ifdef ZgatewayBT case hash("BTtoMQTT"): { - // {"id":"49:22:07:07:1C:F6","mac_type":0,"name":"sps","rssi":-87,"brand":"Inkbird","model":"TH Sensor","model_id":"IBS-TH1","cidc":false,"tempc":0.8,"tempf":33.44,"hum":29.61,"batt":100} + // {"id":"AA:BB:CC:DD:EE:FF","mac_type":0,"adv_type":0,"name":"sps","manufacturerdata":"de071f1000b1612908","rssi":-70,"brand":"Inkbird","model":"T(H) Sensor","model_id":"IBS-TH1/TH2/P01B","type":"THBX","cidc":false,"acts":true,"tempc":20.14,"tempf":68.252,"hum":41.27,"batt":41} - // Line 1 + if (data["model_id"] != "MS-CDP" && data["model_id"] != "GAEN" && data["model_id"] != "IBEACON") { + // Line 2, 3, 4 + String line2 = ""; + String line3 = ""; + String line4 = ""; - strlcpy(message->line1, data["brand"], OLED_TEXT_WIDTH); + // Properties + String properties[6] = {"", "", "", "", "", ""}; + int property = -1; - // Line 2 + if (data["type"] == "THB" || data["type"] == "THBX" || data["type"] == "PLANT" || data["type"] == "AIR" || data["type"] == "BATT" || data["type"] == "ACEL" || (data["type"] == "UNIQ" && data["model_id"] == "SDLS")) { + if (data.containsKey("tempc")) { + property++; + char temp[5]; + if (displayMetric) { + float temperature = data["tempc"]; + dtostrf(temperature, 3, 1, temp); + properties[property] = "temp: " + (String)temp + "°C "; + } else { + float temperature = data["tempf"]; + dtostrf(temperature, 3, 1, temp); + properties[property] = "temp: " + (String)temp + "°F "; + } + } - strlcpy(message->line2, data["model_id"], OLED_TEXT_WIDTH); + if (data.containsKey("tempc2_dp")) { + property++; + char tempdp[5]; + if (displayMetric) { + float temperature = data["tempc2_dp"]; + dtostrf(temperature, 3, 1, tempdp); + properties[property] = "dewp: " + (String)tempdp + "°C "; + } else { + float temperature = data["tempf2_dp"]; + dtostrf(temperature, 3, 1, tempdp); + properties[property] = "dewp: " + (String)tempdp + "°F "; + } + } - // Line 3 + if (data.containsKey("extprobe")) { + property++; + properties[property] = " ext. probe"; + } - String line3 = ""; + if (data.containsKey("hum")) { + property++; + float humidity = data["hum"]; + char hum[5]; - if (data.containsKey("tempc")) { - float temperature_C = data["tempc"]; - char temp[5]; + dtostrf(humidity, 3, 1, hum); + properties[property] = "hum: " + (String)hum + "% "; + } - if (displayMetric) { - dtostrf(temperature_C, 3, 1, temp); - line3 = "temp: " + (String)temp + "°C "; - } else { - dtostrf(convertTemp_CtoF(temperature_C), 3, 1, temp); - line3 = "temp: " + (String)temp + "°F "; + if (data.containsKey("pm25")) { + property++; + int pm25int = data["pm25"]; + char pm25[3]; + itoa(pm25int, pm25, 10); + if ((data.containsKey("pm10"))) { + properties[property] = "PM 2.5: " + (String)pm25 + " "; + + } else { + properties[property] = "pm2.5: " + (String)pm25 + "μg/m³ "; + } + } + + if (data.containsKey("pm10")) { + property++; + int pm10int = data["pm10"]; + char pm10[3]; + itoa(pm10int, pm10, 10); + if ((data.containsKey("pm25"))) { + properties[property] = "/ 10: " + (String)pm10 + "μg/m³ "; + + } else { + properties[property] = "pm10: " + (String)pm10 + "μg/m³ "; + } + } + + if (data.containsKey("for")) { + property++; + int formint = data["for"]; + char form[3]; + itoa(formint, form, 10); + properties[property] = "CH₂O: " + (String)form + "mg/m³ "; + } + + if (data.containsKey("co2")) { + property++; + int co2int = data["co2"]; + char co2[4]; + itoa(co2int, co2, 10); + properties[property] = "co2: " + (String)co2 + "ppm "; + } + + if (data.containsKey("moi")) { + property++; + int moiint = data["moi"]; + char moi[4]; + itoa(moiint, moi, 10); + properties[property] = "moi: " + (String)moi + "% "; + } + + if (data.containsKey("lux")) { + property++; + int luxint = data["lux"]; + char lux[5]; + itoa(luxint, lux, 10); + properties[property] = "lux: " + (String)lux + "lx "; + } + + if (data.containsKey("fer")) { + property++; + int ferint = data["fer"]; + char fer[7]; + itoa(ferint, fer, 10); + properties[property] = "fer: " + (String)fer + "µS/cm "; + } + + if (data.containsKey("pres")) { + property++; + int presint = data["pres"]; + char pres[4]; + itoa(presint, pres, 10); + properties[property] = "pres: " + (String)pres + "hPa "; + } + + if (data.containsKey("batt")) { + property++; + int battery = data["batt"]; + char batt[5]; + itoa(battery, batt, 10); + properties[property] = "batt: " + (String)batt + "% "; + } + + if (data.containsKey("shake")) { + property++; + int shakeint = data["shake"]; + char shake[3]; + itoa(shakeint, shake, 10); + properties[property] = "shake: " + (String)shake + " "; + } + + if (data.containsKey("volt")) { + property++; + float voltf = data["volt"]; + char volt[5]; + dtostrf(voltf, 3, 1, volt); + properties[property] = "volt: " + (String)volt + "V "; + } + + if (data.containsKey("wake")) { + property++; + String wakestr = data["wake"]; + properties[property] = "wake: " + wakestr + " "; + } + + } else if (data["type"] == "BBQ") { + String tempcstr = ""; + int j = 7; + if (data["model_id"] == "IBT-2X(S)") { + j = 3; + } else if (data["model_id"] == "IBT-4X(S/C)") { + j = 5; + } + + for (int i = 0; i < j; i++) { + if (i == 0) { + if (displayMetric) { + tempcstr = "tempc"; + } else { + tempcstr = "tempf"; + } + i++; + } else { + if (displayMetric) { + tempcstr = "tempc" + (String)i; + } else { + tempcstr = "tempf" + (String)i; + } + } + + if (data.containsKey(tempcstr)) { + char temp[5]; + float temperature = data[tempcstr]; + dtostrf(temperature, 3, 1, temp); + properties[i - 1] = "tp" + (String)i + ": " + (String)temp; + if (displayMetric) { + properties[i - 1] += "°C "; + } else { + properties[i - 1] += "°F "; + } + } else { + properties[i - 1] = "tp" + (String)i + ": " + "off "; + } + } + } else if (data["type"] == "BODY") { + if (data.containsKey("steps")) { + property++; + int stepsint = data["steps"]; + char steps[5]; + itoa(stepsint, steps, 10); + properties[property] = "steps: " + (String)steps + " "; + // next line + property++; + } + + if (data.containsKey("act_bpm")) { + property++; + int actbpmint = data["act_bpm"]; + char actbpm[3]; + itoa(actbpmint, actbpm, 10); + properties[property] = "activity bpm: " + (String)actbpm + " "; + } + + if (data.containsKey("bpm")) { + property++; + int bpmint = data["bpm"]; + char bpm[3]; + itoa(bpmint, bpm, 10); + properties[property] = "bpm: " + (String)bpm + " "; + } + } else if (data["type"] == "SCALE") { + if (data.containsKey("weighing_mode")) { + property++; + String mode = data["weighing_mode"]; + properties[property] = mode + " "; + // next line + property++; + } + + if (data.containsKey("weight")) { + property++; + float weightf = data["weight"]; + char weight[7]; + dtostrf(weightf, 3, 1, weight); + if (data.containsKey("unit")) { + String unit = data["unit"]; + properties[property] = "weight: " + (String)weight + unit + " "; + } else { + properties[property] = "weight: " + (String)weight; + } + // next line + property++; + } + + if (data.containsKey("impedance")) { + property++; + int impint = data["impedance"]; + char imp[3]; + itoa(impint, imp, 10); + properties[property] = "impedance: " + (String)imp + "ohm "; + } + } else if (data["type"] == "UNIQ") { + if (data["model_id"] == "M1017") { + if (data.containsKey("lvl_cm")) { + property++; + char lvl[5]; + if (displayMetric) { + float lvlf = data["lvl_cm"]; + dtostrf(lvlf, 3, 1, lvl); + properties[property] = "level: " + (String)lvl + "cm "; + } else { + float lvlf = data["lvl_in"]; + dtostrf(lvlf, 3, 1, lvl); + properties[property] = "level: " + (String)lvl + "\" "; + } + } + + if (data.containsKey("quality")) { + property++; + int qualint = data["quality"]; + char qual[3]; + itoa(qualint, qual, 10); + properties[property] = "qy: " + (String)qual + " "; + } + + if (data.containsKey("batt")) { + property++; + int battery = data["batt"]; + char batt[5]; + itoa(battery, batt, 10); + properties[property] = "batt: " + (String)batt + "% "; + } + } } + + line2 = properties[0] + properties[1]; + line3 = properties[2] + properties[3]; + line4 = properties[4] + properties[5]; + + if (!(line2 == "" && line3 == "" && line4 == "")) { + // Line 1 + strlcpy(message->line1, data["model"], OLED_TEXT_WIDTH); + + line2.toCharArray(message->line2, OLED_TEXT_WIDTH); + line3.toCharArray(message->line3, OLED_TEXT_WIDTH); + line4.toCharArray(message->line4, OLED_TEXT_WIDTH); + + if (xQueueSend(displayQueue, (void*)&message, 0) != pdTRUE) { + Log.error(F("[ SSD1306 ] displayQueue full, discarding signal %s" CR), message->title); + free(message); + } else { + // Log.notice(F("Queued %s" CR), message->title); + } + } + + break; } - - float humidity = data["hum"]; - if (data.containsKey("hum") && humidity <= 100 && humidity >= 0) { - char hum[5]; - dtostrf(humidity, 3, 1, hum); - line3 += "hum: " + (String)hum + "% "; - } - - line3.toCharArray(message->line3, OLED_TEXT_WIDTH); - - // Line 4 - - String rssi = data["rssi"]; - String battery_ok = data["batt"]; - - String line4; - if (data.containsKey("batt")) - line4 += "batt: " + battery_ok + " "; - - line4 += "rssi: " + rssi; - line4.toCharArray(message->line4, OLED_TEXT_WIDTH); - - // Queue completed message - - if (xQueueSend(displayQueue, (void*)&message, 0) != pdTRUE) { - Log.error(F("[ SSD1306 ] displayQueue full, discarding signal %s" CR), message->title); - free(message); - } else { - // Log.notice(F("Queued %s" CR), message->title); - } - break; } # endif default: diff --git a/main/ZgatewayBT.ino b/main/ZgatewayBT.ino index 11c540f..f531cfc 100644 --- a/main/ZgatewayBT.ino +++ b/main/ZgatewayBT.ino @@ -257,9 +257,6 @@ void pubBTMainCore(JsonObject& data, bool haPresenceEnabled = true) { topic = BTConfig.extDecoderTopic; // If external decoder, use this topic to send data topic = subjectBTtoMQTT + String("/") + topic; pub((char*)topic.c_str(), data); - if (data.containsKey("model") || data.containsKey("distance")) { // Only display sensor data - pubOled((char*)topic.c_str(), data); - } } if (haPresenceEnabled && data.containsKey("distance")) { if (data.containsKey("servicedatauuid")) @@ -982,6 +979,10 @@ void launchBTDiscovery(bool overrideDiscovery) { void PublishDeviceData(JsonObject& BLEdata, bool processBLEData) { if (abs((int)BLEdata["rssi"] | 0) < abs(BTConfig.minRssi)) { // process only the devices close enough if (processBLEData) process_bledata(BLEdata); + String topic = subjectBTtoMQTT; + if (BLEdata.containsKey("model") || BLEdata.containsKey("distance")) { // Only display sensor data + pubOled((char*)topic.c_str(), BLEdata); + } if (!BTConfig.pubAdvData) { BLEdata.remove("servicedatauuid"); BLEdata.remove("servicedata"); diff --git a/platformio.ini b/platformio.ini index 89a02e2..cbf9930 100644 --- a/platformio.ini +++ b/platformio.ini @@ -61,7 +61,9 @@ extra_configs = ;default_envs = esp32doitv1-aithinker-r01-sx1278 ;default_envs = esp32dev-rtl_433 ;default_envs = heltec-rtl_433 +;default_envs = heltec-ble ;default_envs = lilygo-rtl_433 +;default_envs = lilygo-ble ;default_envs = esp32dev-multi_receiver ;default_envs = tinypico-ble ;default_envs = ttgo-lora32-v1-868