From c33edc9e68a394366a5da1c76a4b2b90c5ae0518 Mon Sep 17 00:00:00 2001 From: OMGeeky Date: Sun, 18 May 2025 00:45:08 +0200 Subject: [PATCH] improve display & reorder operations also switch to umqtt.robust form umqtt.simple for basic reconnects this should change it, so it shows the values quickly after startup and only then tries to connect to wifi and mqtt for uploading, while still showing the values also has a status bar now --- .gitignore | 1 + src/esp_sensors/mqtt.py | 2 +- src/esp_sensors/oled_display.py | 77 +++++++++++++++++++++++------ src/main.py | 85 ++++++++++++++++++--------------- 4 files changed, 110 insertions(+), 55 deletions(-) diff --git a/.gitignore b/.gitignore index d111e43..0c30bf9 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ config.json /deploy/upload/ /deploy/last_upload/ /deploy/actual_upload/ +__pycache__ diff --git a/src/esp_sensors/mqtt.py b/src/esp_sensors/mqtt.py index 72e46f2..f9351e1 100644 --- a/src/esp_sensors/mqtt.py +++ b/src/esp_sensors/mqtt.py @@ -10,7 +10,7 @@ import json # Import hardware-specific modules if available (for ESP32/ESP8266) try: - from umqtt.simple import MQTTClient + from umqtt.robust import MQTTClient SIMULATION = False except ImportError: diff --git a/src/esp_sensors/oled_display.py b/src/esp_sensors/oled_display.py index 4b233ba..5fe0d44 100644 --- a/src/esp_sensors/oled_display.py +++ b/src/esp_sensors/oled_display.py @@ -1,6 +1,11 @@ """ OLED display module for ESP32 using SSD1306 controller. """ +LINE_HEIGHT = 8 # Height of each line in pixels + +HEADER_LINE = 0 +STATUS_LINE = 1 +VALUE_LINES_START = 2 try: from machine import Pin, I2C @@ -18,16 +23,16 @@ class OLEDDisplay(Sensor): """SSD1306 OLED display implementation.""" def __init__( - self, - name: str = None, - scl_pin: int = None, - sda_pin: int = None, - width: int = None, - height: int = None, - address: int | str = None, - interval: int = None, - on_time: int = None, - display_config = None, + self, + name: str = None, + scl_pin: int = None, + sda_pin: int = None, + width: int = None, + height: int = None, + address: int | str = None, + interval: int = None, + on_time: int = None, + display_config=None, ): """ Initialize a new OLED display. @@ -84,7 +89,7 @@ class OLEDDisplay(Sensor): if not SIMULATION: try: print("Initializing OLED display...") - print(f" SCL pin: {self.scl_pin }, SDA pin: {self.sda_pin}") + print(f" SCL pin: {self.scl_pin}, SDA pin: {self.sda_pin}") # print('initializing scl pin', type(self.scl_pin), self.scl_pin) scl = Pin(self.scl_pin) # print('initializing sda pin', type(self.sda_pin), self.sda_pin) @@ -106,6 +111,7 @@ class OLEDDisplay(Sensor): print(f"Simulated OLED display initialized: {width}x{height}") self._display = None + # region basic display methods def clear(self): """ Clear the display. @@ -134,6 +140,23 @@ class OLEDDisplay(Sensor): self._display.text(text, x, y, color) self._display.show() + def set_line_text(self, i, value): + if SIMULATION: + print(f"Simulated OLED display line {i}: {value}") + else: + if self._display: + y = i * LINE_HEIGHT + if y < self.height: # Make sure we don't go off the screen + x = 0 + self._display.fill_rect(x, y, self.width, LINE_HEIGHT, 0) # Clear the line + self._display.text(str(value), x, y, 1) + else: + print(f"Line {i} exceeds display height, skipping") + + # endregion + + # region easy setter methods + def display_values(self, values: list): """ Display a list of values on the OLED screen. @@ -149,15 +172,38 @@ class OLEDDisplay(Sensor): print(f" Line {i}: {value}") else: if self._display: - self._display.fill(0) # Clear the display - + # self._display.fill(0) # Clear the display + x = 0 + y = VALUE_LINES_START * LINE_HEIGHT + self._display.fill_rect(x, y, self.width, self.height-y, 0) # Clear the line # Display each value on a new line (8 pixels per line) for i, value in enumerate(values): - if i * 10 < self.height: # Make sure we don't go off the screen - self._display.text(str(value), 0, i * 10, 1) + self.set_line_text(VALUE_LINES_START + i, value) self._display.show() + def set_header(self, value): + """ + Display a header on the OLED screen. + + Args: + value: The header to display + """ + self.set_line_text(HEADER_LINE, value) + + def set_status(self, status: str): + """ + Display a status message on the OLED screen. + + Args: + status: The status message to display + """ + self.set_line_text(STATUS_LINE, status) + self._display.show() + + # endregion + + # region Sensor interface methods def read(self) -> float: """ Update the display (placeholder to satisfy Sensor interface). @@ -185,3 +231,4 @@ class OLEDDisplay(Sensor): metadata["type"] = "SSD1306" metadata["values_count"] = len(self._values) return metadata + # endregion diff --git a/src/main.py b/src/main.py index e4720b8..542041a 100644 --- a/src/main.py +++ b/src/main.py @@ -98,19 +98,13 @@ def main(): # Initialize an OLED display using configuration display = OLEDDisplay(display_config=display_config) - display.clear() - display.display_text("Initializing...") + # display.clear() + name_str = f"N: {dht_sensor.name}" + display.set_header(name_str) + display.set_status("Initializing...") - # Initialize wifi connection - display.clear() - display.display_text("Connecting to WiFi...") - connect_wifi(network_config) - - # Set up MQTT client if enabled - display.clear() - display.display_text("Setting up MQTT...") - mqtt_client = setup_mqtt(mqtt_config) # mqtt_client = None + mqtt_enabled = mqtt_config.get("enabled", False) mqtt_publish_interval = mqtt_config.get("publish_interval", 60) # # Set up button using configuration @@ -124,50 +118,57 @@ def main(): # Main loop - sleep until button press, then read and display sensor data try: # while True: - print('sleeping for 5 seconds for debugging') - display.clear() - display.display_text('debug sleeping') - time.sleep(5) + # print('sleeping for 5 seconds for debugging') + # display.set_status('debug sleeping') + # time.sleep(5) # Read sensor values - display.clear() - display.display_text("Reading sensor values...") + display.set_status("Reading sensor values...") temperature = dht_sensor.read_temperature() humidity = dht_sensor.read_humidity() - # Publish to MQTT - display.clear() - display.display_values(['Publishing to MQTT...', '', mqtt_client.server, mqtt_client.port]) - publish_sensor_data(mqtt_client, mqtt_config, dht_sensor, temperature, humidity) - if mqtt_client: - try: - mqtt_client.disconnect() - print("MQTT client disconnected") - except Exception as e: - print(f"Error disconnecting MQTT client: {e}") - # # Format values for display - name_str = f"Sensor: {dht_sensor.name}" temp_str = f"Temp: {temperature:.1f} C" hum_str = f"Humidity: {humidity:.1f}%" time_str = f"Time: {time.time():.0f}" - # Display values - ## TODO: only display values, if the button has been clicked - display.clear() - display.display_values( - [name_str, '', temp_str, hum_str, time_str, "Press button again"] - ) - time.sleep(display.on_time) - # Print to console print('='*20) print(f"{temp_str}, {hum_str}") print('='*20) + # Display values + ## TODO: only display values, if the button has been clicked + display.display_values( + [temp_str, hum_str, time_str] + ) + + # Publish to MQTT + if mqtt_enabled: + # Initialize wifi connection + display.set_status("Connecting WiFi...") + connect_wifi(network_config) + + # Set up MQTT client if enabled + display.set_status("Setting up MQTT...") + mqtt_client = setup_mqtt(mqtt_config) + display.set_status("Publishing to MQTT...") + # display.display_values([mqtt_client.server, mqtt_client.port]) + publish_sensor_data(mqtt_client, mqtt_config, dht_sensor, temperature, humidity) + try: + if mqtt_client: + mqtt_client.disconnect() + print("MQTT client disconnected") + except Exception as e: + print(f"Error disconnecting MQTT client: {e}") + + display.set_status("...") + # sleep, to be able to do something, before going into deepsleep + time.sleep(display.on_time) time_until_next_read = mqtt_publish_interval - (time.time() - last_read_time) + display.set_status(f"Sleeping {time_until_next_read}s") print('sleeping for', time_until_next_read, 'seconds') if not SIMULATION: deepsleep(time_until_next_read * 1000) @@ -176,7 +177,6 @@ def main(): print(f"Simulated deep sleep for {time_until_next_read:.1f} seconds") time.sleep(time_until_next_read) - except KeyboardInterrupt: # Clean up on exit display.clear() @@ -222,4 +222,11 @@ def connect_wifi(network_config: dict): if __name__ == "__main__": - main() + try: + main() + except Exception as e: + print(f"An error occurred: {e}") + time.sleep(5) # give time to read the error message and respond + deepsleep(1) # dummy deepsleep to basically reset the system + +