From 9d6f7a076410bfbaa93db42bdeeb21fb9709b2b0 Mon Sep 17 00:00:00 2001 From: Northern Man <19808920+NorthernMan54@users.noreply.github.com> Date: Wed, 11 Jan 2023 21:31:12 -0500 Subject: [PATCH] ssd1306 display of module messages (#1383) * ssd1306 display of module messages Supported Modules Include - rtl_433 - bme280 * Switch to use convertTemp_CtoF --- docs/use/boards.md | 36 +++- main/User_config.h | 1 + main/ZdisplaySSD1306.ino | 357 ++++++++++++++++++++++++++++++++++++--- main/ZgatewayRTL_433.ino | 1 + main/ZsensorBME280.ino | 4 +- main/config_SSD1306.h | 88 ++++++++-- main/main.ino | 1 + platformio.ini | 46 +++-- 8 files changed, 476 insertions(+), 58 deletions(-) diff --git a/docs/use/boards.md b/docs/use/boards.md index 7f79f457ef..0eea97da76 100644 --- a/docs/use/boards.md +++ b/docs/use/boards.md @@ -83,14 +83,44 @@ If you are already in low power mode 1 or 2 with M5Stack you can wake up the boa ## SSD1306 Display boards ( Heltec SX127X 433Mhz boards and LILYGO® LoRa32 V2.1_1.6.1 433 Mhz ) +Several options are available for the display of information on the SSD1306 Display. These options include display of the OMG logo and setup messages, redirecting of the log output to the display, and display of various module messages on the display. These options are exclusive to each other, and when a different option is enabled, the current option is disabled. + ### Setting the log output -Per default the log of the SSD1306 Display boards is going to the LCD display with Errors and Warnings only, if you want to change the output at build time you can do it with the compiler directive `-DLOG_LEVEL_LCD=LOG_LEVEL_NOTICE`. +The display of serial log messages to the display can be enabled via compiler directive `-DLOG_TO_LCD=true` or via MQTT commands. -You can also change it by MQTT. For example if you want to set to LCD +For example if you want to set the serial log to LCD `mosquitto_pub -t home/OpenMQTTGateway/commands/MQTTtoSSD1306 -m '{"log-lcd":true}'` -you can also revert it to the serial monitor: +you can also revert it back to the serial monitor: `mosquitto_pub -t home/OpenMQTTGateway/commands/MQTTtoSSD1306 -m '{"log-lcd":false}'` + +The log level of the messages displayed is Errors and Warnings, and this can only be changed via the compiler directive `-DLOG_LEVEL_LCD=LOG_LEVEL_NOTICE`. + +### Displaying Module json messages ( default ) + +The display of messages from various modules is also supported. Currently supported modules include `ZgatewayRTL_433` and `ZsensorBME280`. + +This can be enabled with the compiler directive `-DJSON_TO_LCD=true`. + +You can also change it by MQTT. For example if you want to display module json messages: + +`mosquitto_pub -t home/OpenMQTTGateway/commands/MQTTtoSSD1306 -m '{"json-lcd":true}'` + +And to disable the display of module json messages: + +`mosquitto_pub -t home/OpenMQTTGateway/commands/MQTTtoSSD1306 -m '{"display-json":false}'` + +### Units for display, Metric or Imperial + +By default the display uses metric units, and this can be changed either by compiler directive or mqtt command. + +The compiler directive is `-DDISPLAY_METRIC=true` + +The mqtt command to change the units is: + +`mosquitto_pub -t home/OpenMQTTGateway/commands/MQTTtoSSD1306 -m '{"display-metric":false}'` + +Please note that it may take several seconds/display updates for the units to change. This is due to the queueing of messages for display. \ No newline at end of file diff --git a/main/User_config.h b/main/User_config.h index 36167f5e9b..ade826c9d0 100644 --- a/main/User_config.h +++ b/main/User_config.h @@ -566,6 +566,7 @@ CRGB leds[FASTLED_IND_NUM_LEDS]; /*-----------PLACEHOLDERS FOR OLED/LCD DISPLAY--------------*/ // The real definitions are in config_M5.h / config_SSD1306.h +#define pubOled(...) // display the published message onto the OLED display #define displayPrint(...) // only print if not in low power mode #define lpDisplayPrint(...) // print in low power mode diff --git a/main/ZdisplaySSD1306.ino b/main/ZdisplaySSD1306.ino index 696b235c7f..158badf6d8 100644 --- a/main/ZdisplaySSD1306.ino +++ b/main/ZdisplaySSD1306.ino @@ -40,58 +40,108 @@ # include "config_SSD1306.h" SemaphoreHandle_t semaphoreOLEDOperation; +QueueHandle_t displayQueue; +boolean logToLCDDisplay = LOG_TO_LCD; +boolean jsonDisplay = JSON_TO_LCD; +boolean displayMetric = DISPLAY_METRIC; +/* +Toogle log display +*/ void logToLCD(bool display) { + logToLCDDisplay = display; display ? Log.begin(LOG_LEVEL_LCD, &Oled) : Log.begin(LOG_LEVEL, &Serial); // Log on LCD following LOG_LEVEL_LCD } +/* +module setup, for use in Arduino setup +*/ void setupSSD1306() { Log.trace(F("Setup SSD1306 Display" CR)); Log.trace(F("ZdisplaySSD1306 command topic: %s" CR), subjectMQTTtoSSD1306set); + Log.trace(F("ZdisplaySSD1306 log-lcd: %T" CR), logToLCDDisplay); + Log.trace(F("ZdisplaySSD1306 json-lcd: %T" CR), jsonDisplay); + Log.trace(F("ZdisplaySSD1306 DISPLAY_PAGE_INTERVAL: %d" CR), DISPLAY_PAGE_INTERVAL); + Log.trace(F("ZdisplaySSD1306 DISPLAY_IDLE_LOGO: %T" CR), DISPLAY_IDLE_LOGO); + Log.trace(F("ZdisplaySSD1306 DISPLAY_METRIC: %T" CR), displayMetric); + Oled.begin(); Log.notice(F("Setup SSD1306 Display end" CR)); # if LOG_TO_LCD Log.begin(LOG_LEVEL_LCD, &Oled); // Log on LCD following LOG_LEVEL_LCD + jsonDisplay = false; +# else + jsonDisplay = true; # endif } -static int previousLogLevel = 0; +boolean logoDisplayed = false; +unsigned long nextDisplayPage = uptime() + DISPLAY_PAGE_INTERVAL; +/* +module loop, for use in Arduino loop +*/ void loopSSD1306() { - int currentLogLevel = Log.getLastMsgLevel(); - if (previousLogLevel != currentLogLevel && lowpowermode != 2) { - switch (currentLogLevel) { - case 1: - case 2: - // wakeScreen(NORMAL_LCD_BRIGHTNESS); - // M5.Lcd.fillScreen(TFT_RED); // FATAL, ERROR - // M5.Lcd.setTextColor(TFT_BLACK, TFT_RED); - break; - case 3: - // wakeScreen(NORMAL_LCD_BRIGHTNESS); - // M5.Lcd.fillScreen(TFT_ORANGE); // WARNING - // M5.Lcd.setTextColor(TFT_BLACK, TFT_ORANGE); - break; - default: - // TODO: Display splash screen x seconds after last message displayed on OLED - Oled.fillScreen(WHITE); - Oled.drawLogo((int)OLED_WIDTH * 0.24, (int)(OLED_WIDTH / 2) - OLED_WIDTH * 0.2, (int)(OLED_HEIGHT / 2) + OLED_HEIGHT * 0.2, true, true, true, true, true, true); // Name - break; + /* + Function to check if json messages are in the queue and send them for display + + long enough since the last message and display not being used and a queue message waiting + */ + if (jsonDisplay) { + if (uptime() >= nextDisplayPage && uxSemaphoreGetCount(semaphoreOLEDOperation) && uxQueueMessagesWaiting(displayQueue)) { + displayQueueMessage* message = nullptr; + xQueueReceive(displayQueue, &message, portMAX_DELAY); + if (!Oled.displayPage(message)) { + Log.warning(F("[ssd1306] displayPage failed: %s" CR), message->title); + } + free(message); + nextDisplayPage = uptime() + DISPLAY_PAGE_INTERVAL; } } - previousLogLevel = currentLogLevel; + /* + Display logo if it has been more than DISPLAY_PAGE_INTERVAL + */ +# if DISPLAY_IDLE_LOGO + if (uptime() > nextDisplayPage + 1 && !logoDisplayed) { + Oled.fillScreen(WHITE); + Oled.drawLogo((int)OLED_WIDTH * 0.24, (int)(OLED_WIDTH / 2) - OLED_WIDTH * 0.2, (int)(OLED_HEIGHT / 2) + OLED_HEIGHT * 0.2, true, true, true, true, true, true); // Name + logoDisplayed = true; + } else { + logoDisplayed = false; + } +# endif } +/* +Handler for mqtt commands sent to the module +- log-lcd: boolean + Enable / Disable display of log messages on display +*/ void MQTTtoSSD1306(char* topicOri, JsonObject& SSD1306data) { // json object decoding bool success = false; if (cmpToMainTopic(topicOri, subjectMQTTtoSSD1306set)) { Log.trace(F("MQTTtoSSD1306 json set" CR)); // Log display set between SSD1306 lcd (true) and serial monitor (false) if (SSD1306data.containsKey("log-lcd")) { - bool displayOnLCD = SSD1306data["log-lcd"]; - Log.notice(F("Set lcd log: %T" CR), displayOnLCD); - logToLCD(displayOnLCD); + logToLCDDisplay = SSD1306data["log-lcd"]; + Log.notice(F("Set lcd log: %T" CR), logToLCDDisplay); + logToLCD(logToLCDDisplay); + if (logToLCDDisplay) { + jsonDisplay = false; + } + success = true; + } else if (SSD1306data.containsKey("json-lcd")) { + jsonDisplay = SSD1306data["json-lcd"]; + if (jsonDisplay) { + logToLCDDisplay = false; + logToLCD(logToLCDDisplay); + } + Log.notice(F("Set json-lcd: %T" CR), jsonDisplay); + success = true; + } else if (SSD1306data.containsKey("display-metric")) { + displayMetric = SSD1306data["display-metric"]; + Log.notice(F("Set display-metric: %T" CR), jsonDisplay); success = true; } if (success) { @@ -103,8 +153,204 @@ void MQTTtoSSD1306(char* topicOri, JsonObject& SSD1306data) { // json object dec } } +/* +Workaround for c not having a string based switch/case function +*/ +constexpr unsigned int hash(const char* s, int off = 0) { // workaround for switching on a string https://stackoverflow.com/a/46711735/18643696 + return !s[off] ? 5381 : (hash(s, off + 1) * 33) ^ s[off]; +} + +/* +Parse json message from module into a format for displaying on screen, and queue for display +*/ +void ssd1306PubPrint(const char* topicori, JsonObject& data) { + if (jsonDisplay) { + displayQueueMessage* message = (displayQueueMessage*)malloc(sizeof(displayQueueMessage)); + + char* topic = strdup(topicori); + strlcpy(message->title, strtok(topic, "/"), OLED_TEXT_WIDTH); + free(topic); + + switch (hash(message->title)) { + case hash("SYStoMQTT"): { + // {"uptime":456356,"version":"lilygo-rtl_433-test-A-v1.1.1-25-g574177d[lily-cloud]","freemem":125488,"mqttport":"1883","mqttsecure":false,"freestack":3752,"rssi":-36,"SSID":"The_Beach","BSSID":"64:A5:C3:69:C3:38","ip":"192.168.1.239","mac":"4C:75:25:A8:D5:D8","actRec":3,"mhz":433.92,"RTLRssiThresh":-98,"RTLRssi":-108,"RTLAVGRssi":-107,"RTLCnt":121707,"RTLOOKThresh":90,"modules":["LILYGO_OLED","CLOUD","rtl_433"]} + + // Line 1 + + strlcpy(message->line1, data["version"], OLED_TEXT_WIDTH); + + // Line 2 + + String uptime = data["uptime"]; + String line2 = "uptime: " + uptime; + line2.toCharArray(message->line2, OLED_TEXT_WIDTH); + + // Line 3 + + String freemem = data["freemem"]; + String line3 = "freemem: " + freemem; + line3.toCharArray(message->line3, OLED_TEXT_WIDTH); + + // Line 4 + + String ip = data["ip"]; + String line4 = "ip: " + ip; + line4.toCharArray(message->line4, OLED_TEXT_WIDTH); + + // Queue completed message + + if (xQueueSend(displayQueue, (void*)&message, 0) != pdTRUE) { + Log.error(F("ERROR: displayQueue full, discarding signal %s" CR), message->title); + } else { + // Log.notice(F("Queued %s" CR), message->title); + } + break; + } + +# ifdef ZgatewayRTL_433 + case hash("RTL_433toMQTT"): { + // {"model":"Acurite-Tower","id":2043,"channel":"B","battery_ok":1,"temperature_C":5.3,"humidity":81,"mic":"CHECKSUM","protocol":"Acurite 592TXR Temp/Humidity, 5n1 Weather Station, 6045 Lightning, 3N1, Atlas","rssi":-81,"duration":121060} + + // Line 1 + + strlcpy(message->line1, data["model"], OLED_TEXT_WIDTH); + + // Line 2 + + String id = data["id"]; + String channel = data["channel"]; + String line2 = "id: " + id + " channel: " + channel; + line2.toCharArray(message->line2, OLED_TEXT_WIDTH); + + // Line 3 + + String line3 = ""; + + if (data.containsKey("temperature_C")) { + float temperature_C = data["temperature_C"]; + char temp[5]; + + 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 "; + } + } + + float humidity = data["humidity"]; + if (data.containsKey("humidity") && humidity <= 100 && humidity >= 0) { + char hum[5]; + dtostrf(humidity, 3, 1, hum); + line3 += "hum: " + (String)hum + "% "; + } + if (data.containsKey("wind_avg_km_h")) { + float wind_avg_km_h = data["wind_avg_km_h"]; + char wind[6]; + + if (displayMetric) { + dtostrf(wind_avg_km_h, 3, 1, wind); + line3 += "wind: " + (String)wind + "km/h "; + } else { + dtostrf(convert_kmph2mph(wind_avg_km_h), 3, 1, wind); + line3 += "wind: " + (String)wind + "mp/h "; + } + } + + line3.toCharArray(message->line3, OLED_TEXT_WIDTH); + + // Line 4 + + String rssi = data["rssi"]; + String battery_ok = data["battery_ok"]; + + String line4 = "batt: " + battery_ok + " rssi: " + rssi; + line4.toCharArray(message->line4, OLED_TEXT_WIDTH); + + // Queue completed message + + if (xQueueSend(displayQueue, (void*)&message, 0) != pdTRUE) { + Log.error(F("ERROR: displayQueue full, discarding signal %s" CR), message->title); + } else { + // Log.notice(F("Queued %s" CR), message->title); + } + break; + } +# endif +# ifdef ZsensorBME280 + case hash("CLIMAtoMQTT"): { + // {"tempc":17.06,"tempf":62.708,"hum":50.0752,"pa":98876.14,"altim":205.8725,"altift":675.4348} + + // Line 1 + + strlcpy(message->line1, "bme280", OLED_TEXT_WIDTH); + + // Line 2 + + String line2 = ""; + if (data.containsKey("tempc")) { + char temp[5]; + float temperature_C = data["tempc"]; + + if (displayMetric) { + dtostrf(temperature_C, 3, 1, temp); + line2 = "temp: " + (String)temp + "°C "; + } else { + dtostrf(convertTemp_CtoF(temperature_C), 3, 1, temp); + line2 = "temp: " + (String)temp + "°F "; + } + } + line2.toCharArray(message->line2, OLED_TEXT_WIDTH); + + // Line 3 + + String line3 = ""; + 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 + + float pa = (int)data["pa"] / 100; + char pressure[6]; + + String line4 = ""; + if (displayMetric) { + dtostrf(pa, 3, 1, pressure); + line4 = "pressure: " + (String)pressure + " hPa"; + } else { + dtostrf(convert_hpa2inhg(pa), 3, 1, pressure); + line4 = "pressure: " + (String)pressure + " inHg"; + } + line4.toCharArray(message->line4, OLED_TEXT_WIDTH); + + // Queue completed message + + if (xQueueSend(displayQueue, (void*)&message, 0) != pdTRUE) { + Log.error(F("ERROR: displayQueue full, discarding signal %s" CR), message->title); + free(message); + } else { + // Log.notice(F("Queued %s" CR), message->title); + } + break; + } +# endif + default: + Log.error(F("ERROR: unhandled topic %s" CR), message->title); + } + } +} + // Simple print methonds +/* +Display three lines of text on display, scroll if needed +*/ void ssd1306Print(char* line1, char* line2, char* line3) { Oled.println(line1); Oled.println(line2); @@ -112,12 +358,18 @@ void ssd1306Print(char* line1, char* line2, char* line3) { delay(2000); } +/* +Display two lines of text on display, scroll if needed +*/ void ssd1306Print(char* line1, char* line2) { Oled.println(line1); Oled.println(line2); delay(2000); } +/* +Display single line of text on display, scroll if needed +*/ void ssd1306Print(char* line1) { Oled.println(line1); delay(2000); @@ -127,6 +379,7 @@ void ssd1306Print(char* line1) { OledSerial Oled(0); // Not sure about this, came from Hardwareserial OledSerial::OledSerial(int x) { + displayQueue = xQueueCreate(5, sizeof(displayQueueMessage*)); # if defined(WIFI_Kit_32) || defined(WIFI_LoRa_32) || defined(WIFI_LoRa_32_V2) pinMode(RST_OLED, OUTPUT); digitalWrite(RST_OLED, LOW); @@ -148,6 +401,9 @@ OledSerial::OledSerial(int x) { # endif } +/* +Initialize ssd1306 oled display for use, and display animated OMG logo +*/ void OledSerial::begin() { // SSD1306.begin(); // User OMG serial support @@ -166,20 +422,33 @@ void OledSerial::begin() { delay(1000); } -// Dummy virtual's from Serial - +/* +Dummy virtual functions carried over from Serial +*/ int OledSerial::available(void) { } +/* +Dummy virtual functions carried over from Serial +*/ int OledSerial::peek(void) { } +/* +Dummy virtual functions carried over from Serial +*/ int OledSerial::read(void) { } +/* +Dummy virtual functions carried over from Serial +*/ void OledSerial::flush(void) { } +/* +Erase display and paint it with the color. Used to +*/ void OledSerial::fillScreen(OLEDDISPLAY_COLOR color) { if (xSemaphoreTake(semaphoreOLEDOperation, pdMS_TO_TICKS(30000)) == pdTRUE) { display->clear(); @@ -189,9 +458,13 @@ void OledSerial::fillScreen(OLEDDISPLAY_COLOR color) { } } +/* +Write line of text to the display with vertical scrolling of screen +*/ size_t OledSerial::write(const uint8_t* buffer, size_t size) { if (xPortGetCoreID() == CONFIG_ARDUINO_RUNNING_CORE) { if (xSemaphoreTake(semaphoreOLEDOperation, pdMS_TO_TICKS(30000)) == pdTRUE) { + nextDisplayPage = uptime() + DISPLAY_PAGE_INTERVAL; display->clear(); display->setColor(WHITE); display->setFont(ArialMT_Plain_10); @@ -210,9 +483,36 @@ size_t OledSerial::write(const uint8_t* buffer, size_t size) { } /* -Display OpenMQTTGateway logo - borrowed from ZboardM5.ino and tweaked for ssd1306 display ( removed color and tweaked size/location ) +Display full page message on the display. +- Used to display JSON messages published from each gateway module */ +boolean OledSerial::displayPage(displayQueueMessage* message) { + if (xPortGetCoreID() == CONFIG_ARDUINO_RUNNING_CORE) { + if (xSemaphoreTake(semaphoreOLEDOperation, pdMS_TO_TICKS(30000)) == pdTRUE) { + display->clear(); + display->setColor(WHITE); + display->setFont(ArialMT_Plain_10); + display->drawString(0, 0, message->title); + display->drawLine(0, 12, OLED_WIDTH, 12); + display->drawString(0, 13, message->line1); + display->drawString(0, 26, message->line2); + display->drawString(0, 39, message->line3); + display->drawString(0, 52, message->line4); + display->display(); + xSemaphoreGive(semaphoreOLEDOperation); + return true; + } else { + return false; + } + } else { + return false; + } +} +/* +Display Animated OpenMQTTGateway logo, used on inital boot +- borrowed from ZboardM5.ino and tweaked for ssd1306 display ( removed color and tweaked size/location ) +*/ void OledSerial::ssd1306Intro(int scale, int displayWidth, int displayHeight) { drawLogo(scale, displayWidth, displayHeight, false, true, false, false, false, false); // Circle 2 drawLogo(scale, displayWidth, displayHeight, false, false, true, false, false, false); // Circle 3 @@ -222,6 +522,9 @@ void OledSerial::ssd1306Intro(int scale, int displayWidth, int displayHeight) { drawLogo(scale, displayWidth, displayHeight, true, true, true, true, true, true); // Name } +/* +Primitives behind OpenMQTTGateway logo +*/ void OledSerial::drawLogo(int logoSize, int circle1X, int circle1Y, bool circle1, bool circle2, bool circle3, bool line1, bool line2, bool name) { if (xSemaphoreTake(semaphoreOLEDOperation, pdMS_TO_TICKS(30000)) == pdTRUE) { int circle1T = logoSize / 15; diff --git a/main/ZgatewayRTL_433.ino b/main/ZgatewayRTL_433.ino index eb314ef133..8c0fbf6897 100644 --- a/main/ZgatewayRTL_433.ino +++ b/main/ZgatewayRTL_433.ino @@ -245,6 +245,7 @@ void rtl_433_Callback(char* message) { storeRTL_433Discovery(RFrtl_433_ESPdata, (char*)model.c_str(), (char*)uniqueid.c_str()); pub((char*)topic.c_str(), RFrtl_433_ESPdata); storeSignalValue(MQTTvalue); + pubOled((char*)topic.c_str(), RFrtl_433_ESPdata); } # ifdef MEMORY_DEBUG Log.trace(F("Post rtl_433_Callback: %d" CR), ESP.getFreeHeap()); diff --git a/main/ZsensorBME280.ino b/main/ZsensorBME280.ino index da03d04a74..b7d05d3bc9 100644 --- a/main/ZsensorBME280.ino +++ b/main/ZsensorBME280.ino @@ -191,8 +191,10 @@ void MeasureTempHumAndPressure() { } else { Log.trace(F("Same Altitude Feet don't send it" CR)); } - if (BME280data.size() > 0) + if (BME280data.size() > 0) { pub(BMETOPIC, BME280data); + pubOled(BMETOPIC, BME280data); + } } persisted_bme_tempc = BmeTempC; diff --git a/main/config_SSD1306.h b/main/config_SSD1306.h index 4b78e1578d..2aa397cc83 100644 --- a/main/config_SSD1306.h +++ b/main/config_SSD1306.h @@ -40,6 +40,34 @@ #include "SSD1306Wire.h" +/*-------------------DEFINE LOG LEVEL----------------------*/ + +#ifndef LOG_LEVEL_LCD +# define LOG_LEVEL_LCD LOG_LEVEL_WARNING // Default to only display Warning level messages +#endif + +#ifndef LOG_TO_LCD +# define LOG_TO_LCD false // Default to not display log messages on display +#endif + +#ifndef JSON_TO_LCD +# define JSON_TO_LCD true // Default to displaying JSON messages on the display +#endif + +#ifndef DISPLAY_PAGE_INTERVAL +# define DISPLAY_PAGE_INTERVAL 3 // Number of seconds between json message displays +#endif + +#ifndef DISPLAY_IDLE_LOGO +# define DISPLAY_IDLE_LOGO true // Display the OMG logo when idle +#endif + +#ifndef DISPLAY_METRIC +# define DISPLAY_METRIC true // Units used for display of sensor data +#endif + +/*------------------- DEFAULT DISPLAY GEOMETRY ----------------------*/ + #define OLED_TEXT_BUFFER 1000 #define OLED_TEXT_ROWS 5 #define OLED_WIDTH 128 @@ -55,22 +83,19 @@ # define OLED_HEIGHT 64 #endif -extern void setupSSD1306(); -extern void loopSSD1306(); -extern void MQTTtoSSD1306(char*, JsonObject&); -size_t OledPrint(String msg); +#define OLED_TEXT_WIDTH OLED_WIDTH / 4 // This is an approx amount -/*-------------------DEFINE LOG LEVEL----------------------*/ -#ifndef LOG_LEVEL_LCD -# define LOG_LEVEL_LCD LOG_LEVEL_WARNING // Default to only display Warning level messages -#endif -#ifndef LOG_TO_LCD -# define LOG_TO_LCD false // Default to not display log messages on display -#endif /*-------------------DEFINE MQTT TOPIC FOR CONFIG----------------------*/ + #define subjectMQTTtoSSD1306set "/commands/MQTTtoSSD1306" #define subjectSSD1306toMQTTset "/SSD1306toMQTT" +/*-------------------EXTERNAL FUNCTIONS----------------------*/ + +extern void setupSSD1306(); +extern void loopSSD1306(); +extern void MQTTtoSSD1306(char*, JsonObject&); + // Simple construct for displaying message in lcd and oled displays #define displayPrint(...) \ @@ -81,6 +106,28 @@ void ssd1306Print(char*, char*, char*); void ssd1306Print(char*, char*); void ssd1306Print(char*); +#define pubOled(...) ssd1306PubPrint(__VA_ARGS__) +void ssd1306PubPrint(const char*, JsonObject&); + +// Structure for queueing OMG messages to the display + +/* +Structure for queueing OMG messages to the display. +Length of each line is OLED_TEXT_WIDTH +- title +- line1 +- line2 +- line3 +- line4 +*/ +struct displayQueueMessage { + char title[OLED_TEXT_WIDTH]; + char line1[OLED_TEXT_WIDTH]; + char line2[OLED_TEXT_WIDTH]; + char line3[OLED_TEXT_WIDTH]; + char line4[OLED_TEXT_WIDTH]; +}; + // This pattern was borrowed from HardwareSerial and modified to support the ssd1306 display class OledSerial : public Stream { @@ -91,6 +138,7 @@ class OledSerial : public Stream { OledSerial(int); void begin(); void drawLogo(int logoSize, int circle1X, int circle1Y, bool circle1, bool circle2, bool circle3, bool line1, bool line2, bool name); + boolean displayPage(displayQueueMessage*); SSD1306Wire* display; @@ -132,4 +180,22 @@ class OledSerial : public Stream { extern OledSerial Oled; +/*------------------- Unit Conversion Functions ----------------------*/ + +#define convert_kmph2mph(kmph) (kmph * (1.0f / 1.609344f)) + +#define convert_mph2kmph(mph) (mph * 1.609344f) + +#define convert_mm2inch(mm) (mm * 0.039370f) + +#define convert_inch2mm(inch) (inch * 25.4f) + +#define convert_kpa2psi(kpa) (kpa * (1.0f / 6.89475729f)) + +#define convert_psi2kpa(psi) (psi * 6.89475729f) + +#define convert_hpa2inhg(hpa) (hpa * (1.0f / 33.8639f)) + +#define convert_inhg2hpa(inhg) (inhg * 33.8639f) + #endif diff --git a/main/main.ino b/main/main.ino index 996d48dd60..c361a06d40 100644 --- a/main/main.ino +++ b/main/main.ino @@ -1747,6 +1747,7 @@ void stateMeasures() { # endif SYSdata["modules"] = modules; pub(subjectSYStoMQTT, SYSdata); + pubOled(subjectSYStoMQTT, SYSdata); } #endif diff --git a/platformio.ini b/platformio.ini index 6057378659..f1dfb74882 100644 --- a/platformio.ini +++ b/platformio.ini @@ -846,11 +846,14 @@ build_flags = '-DGateway_Name="OpenMQTTGateway_heltec_rtl_433_ESP"' ; *** OpenMQTTGateway Modules *** '-DZgatewayRTL_433="rtl_433"' - '-DZdisplaySSD1306="HELTEC_SSD1306"' '-DZradioSX127x="SX127x"' -; *** ssd1306 Module Options *** - '-DLOG_TO_LCD=true' ; Enable log to LCD - ; '-DLOG_LEVEL_LCD=LOG_LEVEL_NOTICE' // default is WARNING log level +; *** ssd1306 Display Options *** + '-DZdisplaySSD1306="HELTEC_SSD1306"' +; '-DLOG_TO_LCD=false' ; Enable log to LCD +; '-DJSON_TO_LCD=true' +; '-DLOG_LEVEL_LCD=LOG_LEVEL_NOTICE' +; '-DDISPLAY_IDLE_LOGO=false' +; '-DDISPLAY_METRIC=false' [env:lilygo-rtl_433] platform = ${com.esp32_platform} @@ -869,11 +872,14 @@ build_flags = '-DGateway_Name="OpenMQTTGateway_lilygo_rtl_433_ESP"' ; *** OpenMQTTGateway Modules *** '-DZgatewayRTL_433="rtl_433"' - '-DZdisplaySSD1306="LilyGo_SSD1306"' '-DZradioSX127x="SX127x"' -; *** ssd1306 Module Options *** - '-DLOG_TO_LCD=true' ; Enable log to LCD - ; '-DLOG_LEVEL_LCD=LOG_LEVEL_NOTICE' // default is WARNING log level +; *** ssd1306 Display Options *** + '-DZdisplaySSD1306="LilyGo_SSD1306"' +; '-DLOG_TO_LCD=true' ; Enable log to LCD +; '-DJSON_TO_LCD=true' +; '-DLOG_LEVEL_LCD=LOG_LEVEL_NOTICE' +; '-DDISPLAY_IDLE_LOGO=false' +; '-DDISPLAY_METRIC=false' [env:esp32dev-multi_receiver] platform = ${com.esp32_platform} @@ -992,16 +998,20 @@ lib_deps = ${libraries.ssd1306} build_flags = ${com-esp.build_flags} - ; *** OpenMQTTGateway Modules *** - '-DZdisplaySSD1306="HELTEC_SSD1306"' - '-DLOG_TO_LCD=true' ; Enable log to LCD - ; '-DLOG_LEVEL_LCD=LOG_LEVEL_TRACE' // default is WARNING log level +; *** OpenMQTTGateway Modules *** '-DZgatewayLORA="LORA"' '-DLORA_BAND=868E6' '-DGateway_Name="OpenMQTTGateway_ESP32_LORA"' '-DLED_SEND_RECEIVE=25' '-DTimeLedON=0.1' '-DLED_SEND_RECEIVE_ON=1' +; *** ssd1306 Display Options *** + '-DZdisplaySSD1306="HELTEC_SSD1306"' +; '-DLOG_TO_LCD=true' ; Enable log to LCD +; '-DJSON_TO_LCD=true' +; '-DLOG_LEVEL_LCD=LOG_LEVEL_NOTICE' +; '-DDISPLAY_IDLE_LOGO=false' +; '-DDISPLAY_METRIC=false' [env:heltec-wifi-lora-32-915] ; Heltec ESP32 Board with SSD1306 display platform = ${com.esp32_platform} @@ -1013,16 +1023,20 @@ lib_deps = ${libraries.ssd1306} build_flags = ${com-esp.build_flags} - ; *** OpenMQTTGateway Modules *** - '-DZdisplaySSD1306="HELTEC_SSD1306"' - '-DLOG_TO_LCD=true' ; Enable log to LCD - ; '-DLOG_LEVEL_LCD=LOG_LEVEL_TRACE' // default is WARNING log level +; *** OpenMQTTGateway Modules *** '-DZgatewayLORA="LORA"' '-DLORA_BAND=915E6' '-DGateway_Name="OpenMQTTGateway_ESP32_LORA"' '-DLED_SEND_RECEIVE=25' '-DTimeLedON=0.1' '-DLED_SEND_RECEIVE_ON=1' +; *** ssd1306 Display Options *** + '-DZdisplaySSD1306="HELTEC_SSD1306"' +; '-DLOG_TO_LCD=true' ; Enable log to LCD +; '-DJSON_TO_LCD=true' +; '-DLOG_LEVEL_LCD=LOG_LEVEL_NOTICE' +; '-DDISPLAY_IDLE_LOGO=false' +; '-DDISPLAY_METRIC=false' [env:shelly-plus1] platform = ${com.esp32_solo_platform}