Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7082ea3f8b | |||
| e802386adf | |||
| 289124b7f8 | |||
| 3bead59b23 |
@@ -16,13 +16,13 @@ class DishSafetyResult(BaseModel):
|
|||||||
|
|
||||||
def check_dish_safety(image_path: str) -> dict:
|
def check_dish_safety(image_path: str) -> dict:
|
||||||
prompt = (
|
prompt = (
|
||||||
"You are a microwave safety vision inspector.\n\n"
|
"You are a strict microwave safety vision inspector.\n\n"
|
||||||
"TASK: Check if this dish contains any REAL METAL CUTLERY (metal spoons, forks, knives) or ALUMINUM FOIL.\n\n"
|
"TASK: Inspect the dish for ANY metal or plastic utensils (spoons, forks, knives, metal handles) or aluminum foil.\n\n"
|
||||||
"CRITICAL RULES:\n"
|
"CRITICAL RULES:\n"
|
||||||
"1. Rice, curry, stew, potatoes, herbs, dark sauce, and ceramic/plastic/glass bowls are SAFE food items.\n"
|
"1. Look closely at the edges and interior of the bowl for utensil handles sticking out, even if they appear dark or shadowed.\n"
|
||||||
"2. Camera image noise, shadows, and food textures are NOT metal objects.\n"
|
"2. If you see ANY spoon handle, fork, knife, or utensil body—whether shiny silver, grey, or dark metal—mark is_safe = False.\n"
|
||||||
"3. Unless a shiny, metallic silver/gold utensil or foil sheet is clearly visible, mark is_safe = True and detected_hazards = [].\n"
|
"3. ONLY mark is_safe = True if the bowl contains 100% food and rice with NO protruding utensils or foreign objects.\n"
|
||||||
"4. Do NOT invent or guess utensils if none are clearly visible."
|
"4. If a spoon or utensil is detected, set detected_hazards = ['metal spoon'] and warning_message = 'Please remove the metal utensil before microwaving.'"
|
||||||
)
|
)
|
||||||
|
|
||||||
response_raw = generate(
|
response_raw = generate(
|
||||||
|
|||||||
@@ -344,6 +344,9 @@ async def lora_process_task():
|
|||||||
microwave_state = new_state
|
microwave_state = new_state
|
||||||
if new_state not in (MicrowaveState.ALERT): # Not alert so we can show the error
|
if new_state not in (MicrowaveState.ALERT): # Not alert so we can show the error
|
||||||
update_screen()
|
update_screen()
|
||||||
|
if new_state == MicrowaveState.IDLE:
|
||||||
|
cooking_state = None
|
||||||
|
await asyncio.sleep_ms(20)
|
||||||
|
|
||||||
await asyncio.sleep_ms(100)
|
await asyncio.sleep_ms(100)
|
||||||
|
|
||||||
|
|||||||
+11
-14
@@ -108,6 +108,15 @@ def on_microwave_change(state: MicrowaveState, field: MicrowaveStateFields):
|
|||||||
# Send to LoRa device if state changed
|
# Send to LoRa device if state changed
|
||||||
lora.send_reliable(payloads.lora_microwave_state(state.state), max_retries=5)
|
lora.send_reliable(payloads.lora_microwave_state(state.state), max_retries=5)
|
||||||
|
|
||||||
|
if field == MicrowaveStateFields.COOKING_STATE:
|
||||||
|
update_lcd_microwave_count()
|
||||||
|
if state.cooking_state in (CookingStates.DONE, CookingStates.STIRRING_REQUIRED, CookingStates.ALERT):
|
||||||
|
# Trigger buzzer for user attention
|
||||||
|
beats = 5 if state.cooking_state == CookingStates.ALERT else (1 if state.cooking_state == CookingStates.STIRRING_REQUIRED else 3)
|
||||||
|
buzzer.buzzer_siren(beatsNb=beats)
|
||||||
|
elif state.cooking_state == CookingStates.DONE and state.state == MicrowaveState.COOKING:
|
||||||
|
state.set_state(MicrowaveState.DONE)
|
||||||
|
print(f"[{state.microwave_id}] Cooking finished. Waiting for user to remove dish.")
|
||||||
|
|
||||||
def set_cloud_alert(state: bool):
|
def set_cloud_alert(state: bool):
|
||||||
"""Setter for cloud_alert that triggers a display update when changed."""
|
"""Setter for cloud_alert that triggers a display update when changed."""
|
||||||
@@ -322,20 +331,6 @@ async def process_messages_task():
|
|||||||
data = msg["data"]
|
data = msg["data"]
|
||||||
|
|
||||||
if source == "LoRa":
|
if source == "LoRa":
|
||||||
if "new_cooking_state" in data.get("data", {}):
|
|
||||||
mw_id = data["data"].get("id")
|
|
||||||
n_state = data["data"].get("new_cooking_state")
|
|
||||||
print(f"[LoRa] Microwave {mw_id} state changed to: {n_state}")
|
|
||||||
|
|
||||||
microwave_states[mw_id].set_cooking_state(n_state)
|
|
||||||
update_lcd_microwave_count() # Update the LCD with new microwave count
|
|
||||||
|
|
||||||
if n_state in (CookingStates.DONE, CookingStates.STIRRING_REQUIRED, CookingStates.ALERT):
|
|
||||||
beats = 5 if n_state == CookingStates.ALERT else (1 if n_state == CookingStates.STIRRING_REQUIRED else 3)
|
|
||||||
buzzer.buzzer_siren(beatsNb=beats)
|
|
||||||
if n_state == CookingStates.IDLE and microwave_states.get(mw_id).state == MicrowaveState.COOKING:
|
|
||||||
microwave_states[mw_id].set_state(MicrowaveState.DONE)
|
|
||||||
print(f"[{mw_id}] Cooking finished. Waiting for user to remove dish.")
|
|
||||||
if "action" in data.get("data", {}):
|
if "action" in data.get("data", {}):
|
||||||
if type(data["data"]) != dict:
|
if type(data["data"]) != dict:
|
||||||
print(f"[LoRa] Invalid data format received: {data['data']}")
|
print(f"[LoRa] Invalid data format received: {data['data']}")
|
||||||
@@ -641,6 +636,7 @@ async def request_cloud_cooking_plan(microwave_id, sensors_data):
|
|||||||
for attempt in range(1, max_retries + 1):
|
for attempt in range(1, max_retries + 1):
|
||||||
if microwave_states.get(microwave_id).state != MicrowaveState.WAITING_FOR_CLOUD:
|
if microwave_states.get(microwave_id).state != MicrowaveState.WAITING_FOR_CLOUD:
|
||||||
print(f"[{microwave_id}] Dish removed or state changed. Aborting API request.")
|
print(f"[{microwave_id}] Dish removed or state changed. Aborting API request.")
|
||||||
|
microwave_states[microwave_id].set_state(MicrowaveState.IDLE)
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -657,6 +653,7 @@ async def request_cloud_cooking_plan(microwave_id, sensors_data):
|
|||||||
# Abort if state changed while waiting for cloud AI response
|
# Abort if state changed while waiting for cloud AI response
|
||||||
if microwave_states.get(microwave_id).state != MicrowaveState.WAITING_FOR_CLOUD:
|
if microwave_states.get(microwave_id).state != MicrowaveState.WAITING_FOR_CLOUD:
|
||||||
print(f"[{microwave_id}] Dish removed during API request. Discarding API plan.")
|
print(f"[{microwave_id}] Dish removed during API request. Discarding API plan.")
|
||||||
|
microwave_states[microwave_id].set_state(MicrowaveState.IDLE)
|
||||||
return
|
return
|
||||||
|
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
|
|||||||
@@ -6,7 +6,25 @@ import time
|
|||||||
|
|
||||||
picam2 = Picamera2()
|
picam2 = Picamera2()
|
||||||
|
|
||||||
camera_config = picam2.create_still_configuration()
|
# Get maximum full-sensor dimensions
|
||||||
|
sensor_format = picam2.sensor_modes[0]
|
||||||
|
max_width = sensor_format['size'][0]
|
||||||
|
max_height = sensor_format['size'][1]
|
||||||
|
|
||||||
|
camera_config = picam2.create_still_configuration(
|
||||||
|
raw={"size": (1640, 1232)},
|
||||||
|
main={"size": (1640, 1232)}
|
||||||
|
)
|
||||||
|
picam2.set_controls({
|
||||||
|
"AeEnable": False, # Disable Auto Exposure to lock settings
|
||||||
|
"ExposureTime": 60000, # 40 ms shutter time (brightens without grain)
|
||||||
|
"AnalogueGain": 2.0, # Keep gain low to eliminate sensor noise
|
||||||
|
"AwbEnable": False, # Disable Auto White Balance fluctuation
|
||||||
|
"ColourGains": (1.4, 1.2), # Balanced RGB gains for indoor warm LED
|
||||||
|
"Brightness": 0.2, # Slight boost to shadow detail
|
||||||
|
"Contrast": 1.1, # Crisp edges around metallic surfaces
|
||||||
|
"ScalerCrop": [0, 0, max_width, max_height] # Uncrop the image
|
||||||
|
})
|
||||||
picam2.configure(camera_config)
|
picam2.configure(camera_config)
|
||||||
|
|
||||||
picam2.start()
|
picam2.start()
|
||||||
|
|||||||
Reference in New Issue
Block a user