Send microwave states to lora

This commit is contained in:
2026-08-16 15:50:57 +02:00
parent 1a77794fd3
commit c6e56b58a0
5 changed files with 83 additions and 26 deletions
+31 -14
View File
@@ -2,6 +2,7 @@ import time
from shared.cookingState import CookingState, CookingStates
from shared import config
import framebuf
from shared.microwave_state import MicrowaveState
# Icon and bitmaps
image_weather_frost_bits = bytearray(b'\x01\x00\x13\x901\x18s\x9c\x09 \x05@S\x94\xfe\xfeS\x94\x05@\x09 s\x9c1\x18\x13\x90\x01\x00\x00\x00')
@@ -10,12 +11,16 @@ image_CoolHiSmall_bits = bytearray(b'\x1f\xff\xc0?\xff\xe0\x7f\xff\xf0\xff\xdf\x
image_CoolLoSmall_bits = bytearray(b'\x1f\xff\xc0 \x00 @\x00\x10\x80 \x08\x80\xa8\x08\x82r\x08\x86#\x08\x81$\x08\x88\xa8\x88\x84q\x08\x9f\xff\xc8\x84q\x08\x88\xa8\x88\x81$\x08\x86#\x08\x82r\x08\x80\xa8\x08\x80 \x08@\x00\x10 \x00 \x1f\xff\xc0')
image_music_sound_wave_bits = bytearray(b'\x00\x00\x00\x02\x00\x00\x02 \x00\x02 \x00\x02 \x00\x0a(\x00\x0a\xa8\x00*\xa8\x00\xaa\xaa\x80\x0a\xaa\x00\x0a\xa8\x00\x0a \x00\x02 \x00\x02 \x00\x02\x00\x00\x00\x00\x00')
image_weather_temperature_bits = bytearray(b'\x1c\x00\x22\x00+\x00*\x00+\x00*\x00+\x00*\x00*\x00I\x00\x9c\x80\xae\x80\xbe\x80\x9c\x80A\x00>\x00')
image_camera_bits = bytearray(b'\x00x\x000\x84\x00\x7f{\x00\xc0\x00\x80\x91\xc6\x80\x860\x80\x85\xd0\x80\x8a(\x80\x8a(\x80\x8a(\x80\x85\xd0\x80\x860\x80\x91\xc6\x80\xc0\x00\x80\x7f\xff\x00\x00\x00\x00')
image_cloud_sync_bits = bytearray(b'\x00\x00\x00\x07\xc0\x00\x08 \x00\x10\x10\x000\x08\x00@\x0e\x00\x80\x01\x00\x82 \x80G \x80/\xaf\x00\x02 \x00\x02\xf8\x00\x02p\x00\x02 \x00\x00\x00\x00\x00\x00\x00')
fb_image_weather_frost_bits = framebuf.FrameBuffer(image_weather_frost_bits, 15, 16, framebuf.MONO_HLSB)
fb_image_CoolHiSmall_bits = framebuf.FrameBuffer(image_CoolHiSmall_bits, 21, 21, framebuf.MONO_HLSB)
fb_image_weather_temperature_bits = framebuf.FrameBuffer(image_weather_temperature_bits, 9, 16, framebuf.MONO_HLSB)
fb_image_Alert_bits = framebuf.FrameBuffer(image_Alert_bits, 9, 8, framebuf.MONO_HLSB)
fb_image_music_sound_wave_bits = framebuf.FrameBuffer(image_music_sound_wave_bits, 17, 16, framebuf.MONO_HLSB)
fb_image_CoolLoSmall_bits = framebuf.FrameBuffer(image_CoolLoSmall_bits, 21, 21, framebuf.MONO_HLSB)
fb_image_camera_bits = framebuf.FrameBuffer(image_camera_bits, 17, 16, framebuf.MONO_HLSB)
fb_image_cloud_sync_bits = framebuf.FrameBuffer(image_cloud_sync_bits, 17, 16, framebuf.MONO_HLSB)
# Constants
CHARACTER_WIDTH = 6
@@ -70,44 +75,50 @@ class MicrowaveScreen:
self.display.fill_rect(x, y, fill_width, height, color)
def update(self, cooking_state: CookingState | None, defrost_mode: bool = False):
def update(self, cooking_state: CookingState | None, defrost_mode: bool = False, microwave_state: str | None = None):
self.flick()
# self.lastUpdateTime = time.time() # Not Used right now
if not cooking_state or cooking_state.state == CookingStates.IDLE: # Is not currently cooking
if (not microwave_state or microwave_state == MicrowaveState.DONE) and (not cooking_state or cooking_state.state == CookingStates.IDLE): # Is not currently cooking
self.h_centered_text("Ready !", 28, 1)
else:
# Screen center
if cooking_state.state == CookingStates.STIRRING_REQUIRED:
if cooking_state and cooking_state.state == CookingStates.STIRRING_REQUIRED:
self._message("Pls Stir", True)
elif cooking_state.state == CookingStates.ALERT:
elif cooking_state and cooking_state.state == CookingStates.ALERT:
self._message("Alert !", True)
elif cooking_state.state == CookingStates.DONE:
elif cooking_state and cooking_state.state == CookingStates.DONE:
self._message("Done !", False)
elif microwave_state and microwave_state == MicrowaveState.ANALYZING:
self.display.blit(fb_image_camera_bits, 56, 24)
self._secondary_message("Analyzing...", False)
elif microwave_state and microwave_state == MicrowaveState.WAITING_FOR_CLOUD:
self.display.blit(fb_image_cloud_sync_bits, 55, 24)
self._secondary_message("Receiving...", False)
else: # Progress bar
elapsed_time = cooking_state.get_elapsed_time()
estimated_progress = elapsed_time / (elapsed_time + cooking_state.estimated_remaining_time)
self._progressBar(estimated_progress, 21, 25)
# Estimated remaining time
if cooking_state.paused:
if cooking_state and cooking_state.paused:
self.h_centered_text("Paused", 40, 1)
elif cooking_state.state != CookingStates.DONE:
elif cooking_state and cooking_state.state != CookingStates.DONE:
remaining_time = int(cooking_state.estimated_remaining_time)
minutes = remaining_time // 60
seconds = remaining_time % 60
self.h_centered_text(f"{minutes:02}:{seconds:02}", 40, 1)
# Current dish temperature
if cooking_state.current_dish_temp is not None and cooking_state.current_dish_temp is not 0.0:
if cooking_state and cooking_state.current_dish_temp is not None and cooking_state.current_dish_temp is not 0.0:
self.display.blit(fb_image_weather_temperature_bits, 27, 2)
self.display.text(str(int(cooking_state.current_dish_temp)), 37, 6, 1)
self.display.ellipse(54, 6, 1, 1, 1, False)
self.display.text("C", 56, 6, 1)
# Power level
if cooking_state.power_level is not None:
if cooking_state and cooking_state.power_level is not None:
self.display.blit(fb_image_music_sound_wave_bits, 74, 2)
self.display.text(f"{cooking_state.power_level}W", 92, 6, 1)
if defrost_mode:
@@ -118,12 +129,18 @@ class MicrowaveScreen:
self.show()
pass
def _message(self, text, alert):
self.display.fill_rect(0, 27, self.width, CHARACTER_HEIGHT + 4, 1)
def _message_at(self, y, text, alert):
self.display.fill_rect(0, y, self.width, CHARACTER_HEIGHT + 4, 1)
if alert:
self.display.blit(fb_image_Alert_bits, 10, 29)
self.display.blit(fb_image_Alert_bits, self.width - 10, 29)
self.h_centered_text(text, 29, 0)
self.display.blit(fb_image_Alert_bits, 10, y+2)
self.display.blit(fb_image_Alert_bits, self.width - 10, y+2)
self.h_centered_text(text, y+2, 0)
def _message(self, text, alert):
self._message_at(27, text, alert)
def _secondary_message(self, text, alert):
self._message_at(40, text, alert)
def message(self, text, alert):
# self.flick()
+23 -7
View File
@@ -18,6 +18,7 @@ from shared.sensors import RGBLED
from shared.logging import log
from shared.lora_device import LoraCommands
from shared.alerts import Alert, AlertType, AlertManager
from shared.microwave_state import MicrowaveState
# --- READ DEVICE ID ---
try:
@@ -29,6 +30,7 @@ except Exception:
# --- GLOBAL VARIABLES ---
cooking_state = None
cooking_start_time = None # Track start timestamp
microwave_state: str = None
data_queue = SafeQueue()
lora = None
uart_device = None
@@ -196,12 +198,11 @@ def cooking_state_on_state_change(state):
paused=state.paused
)
microwave_screen.update(cooking_state, defrost_mode)
update_screen()
def cooking_state_on_refresh(state):
# Update OLED display
if microwave_screen:
microwave_screen.update(state, defrost_mode)
update_screen()
def cooking_state_on_pause(state):
# If the cooking is unpaused and was in STIRRING_REQUIRED or ALERT state, we set the state back to COOKING.
@@ -215,6 +216,13 @@ def cooking_state_on_pause(state):
estimated_remaining_time=state.get_remaining_time()
)
# --- SCREEN UPDATE ---
def update_screen():
global microwave_screen, cooking_state, defrost_mode, microwave_state
if microwave_screen:
microwave_screen.update(cooking_state, defrost_mode, microwave_state)
# --- ASYNC TASKS ---
@@ -266,7 +274,7 @@ async def uart_polling_task():
async def lora_process_task():
"""Consumes packets pushed to data_queue by the LoRa hardware thread."""
global cooking_state, defrost_mode, microwave_screen
global cooking_state, defrost_mode, microwave_screen, microwave_state
while True:
while not data_queue.empty():
@@ -293,12 +301,21 @@ async def lora_process_task():
elif data["action"] == LoraCommands.TOGGLE_DEFROST:
print("[LoRa Process] Toggling defrost mode via orchestrator command.")
defrost_mode = data["defrost_state"]
microwave_screen.update(cooking_state, defrost_mode)
update_screen()
elif data["action"] == LoraCommands.NEW_ALERT:
alert = Alert(data.get("alert_type"), data.get("message", "Unsafe area"))
alert.timestamp = data.get("timestamp", time.time())
print(f"[LoRa Process] New alert received via orchestrator: {alert.to_dict()}")
alert_manager.add_alert(alert)
elif data["action"] == LoraCommands.MICROVAVE_STATE_UPDATE:
new_state = data.get("new_microwave_state")
if new_state:
print(f"[LoRa Process] Microwave state update received: {new_state}")
microwave_state = new_state
update_screen()
else:
print("[LoRa Process] Received microwave state update with no state specified.")
await asyncio.sleep_ms(100)
@@ -358,8 +375,7 @@ async def main():
print("[Main] All async tasks running concurrently!")
microwave_screen.update(None, defrost_mode)
update_screen()
# Keep main task alive indefinitely
while True:
await asyncio.sleep(3600)
+17 -5
View File
@@ -81,6 +81,15 @@ def notify_display_update():
except RuntimeError:
pass
def on_microwave_change(state: MicrowaveState, field: MicrowaveStateFields):
if field != MicrowaveStateFields.STATE: # No external screen or lcd uses this field, so we can skip updates for it
notify_display_update()
if field == MicrowaveStateFields.STATE and state.state in (MicrowaveState.DONE, MicrowaveState.ANALYZING, MicrowaveState.WAITING_FOR_CLOUD):
# Send to LoRa device if state changed
lora.send_reliable(payloads.lora_microwave_state(state.state), max_retries=5)
def set_cloud_alert(state: bool):
"""Setter for cloud_alert that triggers a display update when changed."""
global cloud_alert
@@ -91,7 +100,7 @@ def set_cloud_alert(state: bool):
update_lcd_cloud_alert()
# Global state trackers
microwave_states = {"2": MicrowaveState("2", on_change_callback=notify_display_update)}
microwave_states = {"2": MicrowaveState("2", on_change_callback=on_microwave_change)}
button_state = False
cloud_alert = None # Global status flag for screen / UI display
async_event_queue = None
@@ -434,7 +443,7 @@ async def request_cloud_cooking_plan(microwave_id, sensors_data):
if c_time is None or c_power is None or c_temp is None:
print(f"[{microwave_id}] Invalid plan received: {res_json}")
microwave_states[microwave_id].set_state(MicrowaveState.IDLE)
microwave_states[microwave_id].set_state(MicrowaveState.DONE)
return
print(f"[{microwave_id}] Cloud Plan Received! Starting microwave: {c_time}s @ {c_power}W")
@@ -530,7 +539,7 @@ async def process_messages_task():
# Pass change callback to newly connected microwave
microwave_states[component_id] = MicrowaveState(
component_id,
on_change_callback=notify_display_update
on_change_callback=on_microwave_change
)
notify_display_update() # Update the external screens
update_lcd_microwave_count() # Update the LCD with new microwave count
@@ -641,8 +650,11 @@ async def monitor_dish_height_task():
while True:
dist = await get_filtered_dish_height()
current_state = microwave_states.get(mw_id, MicrowaveState.IDLE).state
try:
current_state = microwave_states.get(mw_id).state
except AttributeError:
current_state = MicrowaveState.IDLE
if dist is not None:
# Hysteresis Thresholds:
# - Must be > 2.5 cm to detect dish insertion
+1
View File
@@ -7,6 +7,7 @@ import shared.payloads as payloads
import shared.cookingState as cookingState
import shared.safeQueue as safeQueue
import shared.alerts as alerts
import shared.microwave_state as microwave_state
try:
import shared.lora_device as lora_device
except (ImportError, SyntaxError):
+11
View File
@@ -78,4 +78,15 @@ def lora_new_alert(alert: Alert):
return {
"action": LoraCommands.NEW_ALERT,
"alert_type": alert.alertType
}
def lora_microwave_state(state: str):
try:
from shared.lora_device import LoraCommands
except ImportError:
pass # No need
return {
"action": LoraCommands.MICROVAVE_STATE_UPDATE,
"new_microwave_state": state
}