Last active
September 23, 2025 21:36
-
-
Save skittleson/e869a039a01b66fb8add218053b663ef to your computer and use it in GitHub Desktop.
esp32s3 xiao sense OV5640 cam
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import network | |
| import urequests | |
| import utime as time | |
| from camera import Camera, GrabMode, PixelFormat, FrameSize | |
| from machine import Pin, RTC, unique_id, deepsleep, sleep | |
| import machine | |
| import ubinascii | |
| import sys | |
| import time | |
| import gc | |
| # ================================ | |
| # Configuration | |
| # ================================ | |
| WIFI_SSID = "redacted" | |
| WIFI_PASSWORD = "redacted" | |
| SERVER_URL = "http://redacted" | |
| MAX_WIFI_RETRIES = 10 | |
| WIFI_RETRY_DELAY = 5 # seconds | |
| # ================================ | |
| # Utility Functions | |
| # ================================ | |
| def log(msg, level="INFO"): | |
| """Basic structured logger with timestamp.""" | |
| ts = time.localtime() | |
| timestamp = "{:04d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}".format(*ts[:6]) | |
| print("[{}] [{}] {}".format(timestamp, level, msg)) | |
| def safe_deinit(cam): | |
| """Ensure camera is safely deinitialized.""" | |
| try: | |
| cam.deinit() | |
| gc.collect() | |
| log("Camera deinitialized.") | |
| except Exception as e: | |
| log("Camera deinit failed: {}".format(e), "WARN") | |
| def get_device_info(): | |
| """Collect useful ESP32 device/system information.""" | |
| device_id = ubinascii.hexlify(unique_id()).decode() | |
| mac = ubinascii.hexlify(network.WLAN().config('mac')).decode() | |
| info = { | |
| "device_id": device_id, | |
| "mac_address": mac, | |
| "cpu_freq_mhz": machine.freq() // 1_000_000, | |
| "free_heap": gc.mem_free(), | |
| "reset_cause": machine.reset_cause(), | |
| "uptime_sec": time.ticks_ms() // 1000, | |
| } | |
| # ESP32 hall sensor (not always accurate, only works if not using WiFi/BT at the same time) | |
| try: | |
| info["hall_sensor"] = esp32.hall_sensor() | |
| except Exception: | |
| info["hall_sensor"] = "N/A" | |
| # ESP32 temperature sensor (raw value, in °C) | |
| try: | |
| info["temperature_c"] = esp32.raw_temperature() | |
| except Exception: | |
| info["temperature_c"] = "N/A" | |
| # Firmware version | |
| try: | |
| info["firmware"] = os.uname().release | |
| except Exception: | |
| info["firmware"] = "N/A" | |
| return info | |
| # ================================ | |
| # Networking | |
| # ================================ | |
| def wifi_connect(): | |
| """Connect to Wi-Fi with retries and logging.""" | |
| station = network.WLAN(network.STA_IF) | |
| station.active(True) | |
| if station.isconnected(): | |
| log("Already connected. IP: {}".format(station.ifconfig()[0])) | |
| return station | |
| retries = 0 | |
| while not station.isconnected() and retries < MAX_WIFI_RETRIES: | |
| log("Connecting to Wi-Fi (attempt {}/{})...".format(retries+1, MAX_WIFI_RETRIES)) | |
| try: | |
| station.connect(WIFI_SSID, WIFI_PASSWORD) | |
| except Exception as e: | |
| log("Wi-Fi connect error: {}".format(e), "ERROR") | |
| time.sleep(WIFI_RETRY_DELAY) | |
| retries += 1 | |
| if station.isconnected(): | |
| log("Connected to Wi-Fi. IP: {}".format(station.ifconfig()[0])) | |
| return station | |
| else: | |
| log("Failed to connect to Wi-Fi after {} attempts.".format(MAX_WIFI_RETRIES), "ERROR") | |
| sys.exit(1) | |
| # ================================ | |
| # Camera Capture | |
| # ================================ | |
| def capture_image(): | |
| """Capture a JPEG image from the camera safely.""" | |
| cam = Camera() | |
| try: | |
| cam.init() | |
| cam.reconfigure( | |
| pixel_format=PixelFormat.JPEG, | |
| frame_size=FrameSize.QSXGA, | |
| grab_mode=GrabMode.LATEST, | |
| fb_count=2 | |
| ) | |
| log("Camera configured for JPEG/QSXGA.") | |
| img = cam.capture() | |
| log("Image captured. Size: {} bytes".format(len(img))) | |
| return img | |
| except Exception as e: | |
| log("Camera capture failed: {}".format(e), "ERROR") | |
| return None | |
| finally: | |
| safe_deinit(cam) | |
| # ================================ | |
| # Send Image | |
| # ================================ | |
| def send_image(img): | |
| """Send captured image to server with detailed headers.""" | |
| if img is None: | |
| log("No image to send.", "WARN") | |
| return | |
| device_id = ubinascii.hexlify(unique_id()).decode() | |
| ts = time.localtime() | |
| timestamp = "{:04d}-{:02d}-{:02d}T{:02d}:{:02d}:{:02d}".format(*ts[:6]) | |
| device_info = get_device_info() | |
| headers = { | |
| "Title": "ESP32 Camera Snapshot", | |
| "Message": "ESP32 Snapshot | Temp: {}°C".format( | |
| device_info["temperature_c"]), | |
| "Tags": "camera,esp32,snapshot", | |
| "X-Filename": "pic.jpg", | |
| "X-Timestamp": timestamp, | |
| "X-Device-ID": device_info["device_id"], | |
| "X-MAC": device_info["mac_address"], | |
| "X-CPU-MHz": str(device_info["cpu_freq_mhz"]), | |
| "X-Free-Heap": str(device_info["free_heap"]), | |
| "X-Reset-Cause": str(device_info["reset_cause"]), | |
| "X-Uptime-Sec": str(device_info["uptime_sec"]), | |
| "X-Hall-Sensor": str(device_info["hall_sensor"]), | |
| "X-Temperature-C": str(device_info["temperature_c"]), | |
| "X-Firmware": str(device_info["firmware"]), | |
| "Content-Type": "image/jpeg", | |
| "Content-Length": str(len(img)), | |
| } | |
| try: | |
| log("Sending image to {}".format(SERVER_URL)) | |
| resp = urequests.post(SERVER_URL, data=img, headers=headers) | |
| log("Server response: {} - {}".format(resp.status_code, resp.text)) | |
| resp.close() | |
| except Exception as e: | |
| log("Failed to send image: {}".format(e), "ERROR") | |
| # ================================ | |
| # Main Execution | |
| # ================================ | |
| def main(): | |
| wifi_connect() | |
| img = capture_image() | |
| send_image(img) | |
| time.sleep(2) | |
| log("Task completed. Entering sleeping for 4 seconds...") | |
| deepsleep(10_000) # 10 seconds | |
| #deepsleep(60_000) # sleep for 60,000 ms = 60 seconds | |
| if __name__ == "__main__": | |
| while True: | |
| main() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment