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
This commit is contained in:
OMGeeky
2025-05-18 00:45:08 +02:00
parent 80423925d0
commit c33edc9e68
4 changed files with 110 additions and 55 deletions

1
.gitignore vendored
View File

@@ -4,3 +4,4 @@ config.json
/deploy/upload/
/deploy/last_upload/
/deploy/actual_upload/
__pycache__

View File

@@ -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:

View File

@@ -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

View File

@@ -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