From f9ee7a91cda1c5803373c18c98d3ea5b33afecd8 Mon Sep 17 00:00:00 2001 From: Thomas Farstrike Date: Tue, 24 Jun 2025 10:39:20 +0200 Subject: [PATCH] Power off camera after boot and before deepsleep to conserve power --- CHANGELOG.md | 5 ++++ .../assets/camera_app.py | 14 +++++++++++ internal_filesystem/boot_unix.py | 24 +++++++++++++++++++ internal_filesystem/lib/mpos/ui/__init__.py | 13 ++++++---- 4 files changed, 51 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3248e3c7..da8674cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +0.0.8 +===== +- Move wifi icon to the right-hand side +- Power off camera after boot and before deepsleep to conserve power + 0.0.7 ===== - Update battery icon every 5 seconds depending on VBAT/BAT_ADC diff --git a/internal_filesystem/apps/com.micropythonos.camera/assets/camera_app.py b/internal_filesystem/apps/com.micropythonos.camera/assets/camera_app.py index 4e8be902..a3725bda 100644 --- a/internal_filesystem/apps/com.micropythonos.camera/assets/camera_app.py +++ b/internal_filesystem/apps/com.micropythonos.camera/assets/camera_app.py @@ -138,6 +138,20 @@ class CameraApp(Activity): webcam.deinit(self.cam) elif self.cam: self.cam.deinit() + # Power off, otherwise it keeps using a lot of current + try: + from machine import Pin, I2C + i2c = I2C(1, scl=Pin(16), sda=Pin(21)) # Adjust pins and frequency + #devices = i2c.scan() + #print([hex(addr) for addr in devices]) # finds it on 60 = 0x3C after init + camera_addr = 0x3C # for OV5640 + reg_addr = 0x3008 + reg_high = (reg_addr >> 8) & 0xFF # 0x30 + reg_low = reg_addr & 0xFF # 0x08 + power_off_command = 0x42 # Power off command + i2c.writeto(camera_addr, bytes([reg_high, reg_low, power_off_command])) + except Exception as e: + print(f"Warning: powering off camera got exception: {e}") print("camera app cleanup done.") def set_image_size(self): diff --git a/internal_filesystem/boot_unix.py b/internal_filesystem/boot_unix.py index 410c6de5..9a9b123e 100644 --- a/internal_filesystem/boot_unix.py +++ b/internal_filesystem/boot_unix.py @@ -71,4 +71,28 @@ sdlkeyboard.set_paste_text_callback(mpos.clipboard.paste_text) # print(f"boot_unix: code={event_code}") # target={event.get_target()}, user_data={event.get_user_data()}, param={event.get_param()} #keyboard.add_event_cb(keyboard_cb, lv.EVENT.ALL, None) +# On the Waveshare ESP32-S3-Touch-LCD-2, the camera is hard-wired to power on, +# so it needs a software power off to prevent it from staying hot all the time and quickly draining the battery. +# 1) Initialize camera, otherwise it doesn't reply to I2C commands: +try: + from camera import Camera, GrabMode, PixelFormat, FrameSize, GainCeiling + cam = Camera(data_pins=[12,13,15,11,14,10,7,2],vsync_pin=6,href_pin=4,sda_pin=21,scl_pin=16,pclk_pin=9,xclk_pin=8,xclk_freq=20000000,powerdown_pin=-1,reset_pin=-1,pixel_format=PixelFormat.RGB565,frame_size=FrameSize.R240X240,grab_mode=GrabMode.LATEST) + cam.deinit() +except Exception as e: + print(f"camera init for power off got exception: {e}") +# 2) Soft-power off camera, otherwise it uses a lot of current for nothing: +try: + from machine import Pin, I2C + i2c = I2C(1, scl=Pin(16), sda=Pin(21)) # Adjust pins and frequency + #devices = i2c.scan() + #print([hex(addr) for addr in devices]) # finds it on 60 = 0x3C after init + camera_addr = 0x3C # for OV5640 + reg_addr = 0x3008 + reg_high = (reg_addr >> 8) & 0xFF # 0x30 + reg_low = reg_addr & 0xFF # 0x08 + power_off_command = 0x42 # Power off command + i2c.writeto(camera_addr, bytes([reg_high, reg_low, power_off_command])) +except Exception as e: + print(f"Warning: powering off camera got exception: {e}") + print("boot_unix.py finished") diff --git a/internal_filesystem/lib/mpos/ui/__init__.py b/internal_filesystem/lib/mpos/ui/__init__.py index 9e0e9246..ae59f9fb 100644 --- a/internal_filesystem/lib/mpos/ui/__init__.py +++ b/internal_filesystem/lib/mpos/ui/__init__.py @@ -382,6 +382,7 @@ def create_drawer(display=None): poweroff_label.center() def poweroff_cb(e): print("Power off action...") + remove_and_stop_current_activity() # make sure current app, like camera, does cleanup, saves progress, stops hardware etc. import sys if sys.platform == "esp32": #On ESP32, there's no power off but there is a forever sleep @@ -548,6 +549,12 @@ def setContentView(new_activity, new_screen): end_time = utime.ticks_diff(utime.ticks_ms(), start_time) print(f"ui.py setContentView: new_activity.onResume took {end_time}ms") +def remove_and_stop_current_activity(): + current_activity, current_screen = screen_stack.pop() # Remove current screen + if current_activity: + current_activity.onPause(current_screen) + current_activity.onStop(current_screen) + current_activity.onDestroy(current_screen) def back_screen(): print("back_screen() running") @@ -556,11 +563,7 @@ def back_screen(): print("Warning: can't go back because screen_stack is empty.") return False # No previous screen #close_top_layer_msgboxes() # would be nicer to "cancel" all input events - current_activity, current_screen = screen_stack.pop() # Remove current screen - if current_activity: - current_activity.onPause(current_screen) - current_activity.onStop(current_screen) - current_activity.onDestroy(current_screen) + remove_and_stop_current_activity() prev_activity, prev_screen = screen_stack[-1] # load previous screen print("loading prev_screen with animation") lv.screen_load_anim(prev_screen, lv.SCR_LOAD_ANIM.OVER_RIGHT, 500, 0, True) # True means delete the old screen, which is fine as we're going back and current_activity.onDestroy() was called