This is an old revision of the document!
În acest laborator ne vom familiariza cu platforma de dezvoltare ESP32-CAM și vom implementa un sistem de supraveghere simplu care transmite imagini către un calculator prin protocolul MQTT.
Sistemul este compus din trei entități principale:
ssproject/images.ssproject/commands.START-LIVE, STOP-LIVE, CAPTURE).Pentru a rula proiectul, trebuie să pregătim infrastructura software.
Dacă nu aveți un broker instalat, descărcați Mosquitto.
Creați în rădăcina proiectului un fișier de configurare mosquitto.conf pentru a permite conexiuni externe (de la ESP32):
listener 1883 allow_anonymous true
Porniți brokerul folosind acest fișier de configurare:
# Opriți serviciul default dacă rulează sudo systemctl stop mosquitto.service # Porniți brokerul cu configurarea noastră /usr/sbin/mosquitto -c mosquitto.conf -v
Aplicația de vizualizare (receiver.py) necesită Python și câteva biblioteci.
# Creați mediul virtual (dacă nu există) python3 -m venv .venv source .venv/bin/activate # Instalați dependențele pip install paho-mqtt opencv-python numpy
Vom crea un proiect PlatformIO de la zero pentru plăcuța ESP32-CAM (AI-Thinker).
Instalați extensia PlatformIO IDE din Visual Studio Code (Extensions → căutați “PlatformIO IDE” → Install).
Puteți crea proiectul din interfața PlatformIO sau din terminal:
mkdir camera && cd camera # Creați un mediu virtual Python și instalați PlatformIO CLI python3 -m venv .venv source .venv/bin/activate pip install platformio # Inițializați proiectul pio project init --board esp32cam
Înlocuiți conținutul fișierului camera/platformio.ini cu următorul:
; PlatformIO Project Configuration File [env] platform = espressif32 framework = arduino lib_deps = espressif/esp32-camera@^2.0.4 WiFi @ ^2.0.0 knolleary/PubSubClient @ ^2.8 monitor_speed = 115200 [env:esp32cam] board = esp32cam build_flags = -D CAMERA_MODEL_AI_THINKER -D MQTT_MAX_PACKET_SIZE=65000
Explicații:
espressif/esp32-camera — biblioteca oficială pentru modulul camerei.knolleary/PubSubClient — client MQTT pentru Arduino.CAMERA_MODEL_AI_THINKER — definește pinii GPIO corespunzători modelului AI-Thinker.MQTT_MAX_PACKET_SIZE=65000 — mărește dimensiunea maximă a unui pachet MQTT (necesar pentru trimiterea imaginilor JPEG).
Creați fișierul camera/include/camera_pins.hpp cu definițiile pinilor GPIO pentru modelul AI-Thinker:
#if defined(CAMERA_MODEL_AI_THINKER) #define PWDN_GPIO_NUM 32 #define RESET_GPIO_NUM -1 #define XCLK_GPIO_NUM 0 #define SIOD_GPIO_NUM 26 #define SIOC_GPIO_NUM 27 #define Y9_GPIO_NUM 35 #define Y8_GPIO_NUM 34 #define Y7_GPIO_NUM 39 #define Y6_GPIO_NUM 36 #define Y5_GPIO_NUM 21 #define Y4_GPIO_NUM 19 #define Y3_GPIO_NUM 18 #define Y2_GPIO_NUM 5 #define VSYNC_GPIO_NUM 25 #define HREF_GPIO_NUM 23 #define PCLK_GPIO_NUM 22 #else #error "Camera model not selected" #endif
După acești pași, structura proiectului ar trebui să arate astfel:
camera/ ├── include/ │ └── camera_pins.hpp # Definițiile pinilor GPIO ├── src/ │ └── main.cpp # Codul principal (de mai jos) ├── platformio.ini # Configurarea proiectului └── .venv/ # Mediul virtual Python
# Compilare pio run # Upload pe placă (asigurați-vă că aveți permisiuni pe portul serial) sudo chmod 666 /dev/ttyACM0 # sau /dev/ttyUSB0, depinde de placă pio run -t upload # Monitor serial (pentru debug) pio device monitor -b 115200
Acesta este codul care rulează pe cameră. Analizați-l cu atenție.
Acesta este clientul care rulează pe calculator, primește imaginile și trimite comenzi.
import paho.mqtt.client as mqtt import cv2 import numpy as np import threading # Configuration BROKER = "192.168.50.239" # TODO: Modificați cu IP-ul brokerului vostru PORT = 1883 TOPIC_IMAGE = "ssproject/images" TOPIC_COMMAND = "ssproject/commands" # Global flags running = True # Shared frame storage (thread-safe via lock) latest_frame = None frame_lock = threading.Lock() def on_connect(client, userdata, flags, rc): if rc == 0: print("Connected to MQTT Broker!") client.subscribe(TOPIC_IMAGE) else: print(f"Failed to connect, return code {rc}") def on_message(client, userdata, msg): global latest_frame try: nparr = np.frombuffer(msg.payload, np.uint8) img = cv2.imdecode(nparr, cv2.IMREAD_COLOR) if img is not None: # Store the frame — do NOT call cv2.imshow here (wrong thread!) with frame_lock: latest_frame = img else: print("Failed to decode image") except Exception as e: print(f"Error processing image: {e}") def main(): global running, latest_frame client = mqtt.Client() client.on_connect = on_connect client.on_message = on_message try: client.connect(BROKER, PORT, 60) client.loop_start() print("\n--- ESP32 Camera Controller ---") print("Controls:") print(" 's' : Take Single Picture") print(" 'b' : Begin Stream") print(" 'e' : End Stream") print(" 'q' : Quit") print("-------------------------------\n") cv2.namedWindow("ESP32-CAM Stream", cv2.WINDOW_AUTOSIZE) while running: # Display the latest frame if available (main thread only) with frame_lock: frame = latest_frame if frame is not None: cv2.imshow("ESP32-CAM Stream", frame) # Handle GUI events and keyboard input (main thread only) key = cv2.waitKey(30) & 0xFF if key == ord('q'): running = False elif key == ord('s'): print("Command: Capture") client.publish(TOPIC_COMMAND, "CAPTURE") elif key == ord('b'): print("Command: Start Stream") client.publish(TOPIC_COMMAND, "START-LIVE") elif key == ord('e'): print("Command: Stop Stream") client.publish(TOPIC_COMMAND, "STOP-LIVE") except KeyboardInterrupt: print("\nExiting...") except Exception as e: print(f"Error: {e}") finally: client.loop_stop() client.disconnect() cv2.destroyAllWindows() if __name__ == "__main__": main()
Codul de pe cameră îndeplinește următoarele funcții:
setup_camera(): Inițializează senzorul OV2640.callback(): Funcția apelată când se primește un mesaj pe ssproject/commands. Interpretează:CAPTURE → Face o poză.START-LIVE → Activează flag-ul streaming.STOP-LIVE → Dezactivează flag-ul streaming.loop(): Verifică starea streaming. Dacă este activ, capturează și trimite o imagine la fiecare 100ms.
În main.cpp, trebuie să modificați următoarele constante pentru a corespunde rețelei voastre:
const char* ssid = "NUME_RETEA"; const char* password = "PAROLA_RETEA"; const char* mqtt_server = "IP_CALCULATOR"; // Rulați `ip addr` sau `ipconfig` pentru a-l afla
Scriptul Python se conectează la broker și afișează imaginile folosind OpenCV. De asemenea, ascultă tastatura pentru a trimite comenzi:
b: Begin Stream (START-LIVE)e: End Stream (STOP-LIVE)s: Single Capture (CAPTURE)q: Quitcamera/src/main.cpp și setați ssid, password și mqtt_server (IP-ul PC-ului vostru).python receiver.py într-un alt terminal.sudo chmod 666 /dev/ttyACM0.Upload din PlatformIO sau terminal: pio run -t upload.b pentru a porni live stream-ul.
ssproject/images, și receiver-ul sau aplicația web este conectată la același broker), ar trebui să puteți vizualiza stream-ul și prin intermediul interfaței web a aplicației (serverul web care face subscribe la același topic MQTT). Aceasta confirmă faptul că sistemul funcționează end-to-end.
STREAM_INTERVAL în main.cpp la o valoare mai mică (ex: 20ms)? Cum afectează asta calitatea imaginii și latența?callback din main.cpp. Adăugați o comandă nouă FLASH-ON care să aprindă LED-ul flash al camerei (GPIO 4).