Modern IoT security and monitoring applications require intelligent systems that can autonomously detect objects and capture visual evidence. This project demonstrates how to build an object-triggered camera system using the W6300-EVB-PICO2 microcontroller with Ethernet capabilities. The system automatically detects objects using ultrasonic sensing and captures images when objects enter the detection zone, serving them through a real-time web interface.
Step 1: Gather ComponentsFor this project, you will need:
- W6300-EVB-PICO2 Microcontroller
- HC-SR04 Ultrasonic Distance Sensor
- OV2640 Camera Module
- 10kΞ© resistors (2 units for voltage divider, 2 units for I2C pull-up)
- Breadboard and jumper wires
Connections
HC-SR04 Ultrasonic Sensor β W6300-EVB-PICO2:
- VCC β 5V
- Trig β GP27
- Echo β GP26 (via voltage divider)
- GND β GND
OV2640 Camera β W6300-EVB-PICO2:
- VSYNC β GP12
- HREF β GP11
- PCLK β GP10
- D0-D7 β GP0-GP7 (data bus)
- SCL β GP9 (I2C clock) WITH 2kΞ© pull-up to 3.3V
- SDA β GP8 (I2C data) WITH 2kΞ© pull-up to 3.3V
- RESET β GP13 (optional)
Important Notes:
- Voltage divider essential for HC-SR04 (5V output β 3.3V safe input)
- I2C pull-up resistors required for reliable camera communication
- Double-check all connections before powering on the board
1. Why Voltage Divider for HC-SR04?
Safety Requirement: HC-SR04 outputs 5V on Echo pin, but RP2040 GPIO maximum is 3.6V
Signal Protection: Prevents damage to microcontroller GPIO pins
Reliable Operation: Ensures proper logic level recognition (2.5V = HIGH, safe margin)
2. Why I2C Pull-up Resistors?
Protocol Requirement: I2C uses open-drain bus requiring pull-up resistors
Signal Integrity: Ensures proper HIGH/LOW logic levels
Camera Detection: Required for camera I2C address (0x30) detection
Required Libraries
Create a `lib` folder on your board with these libraries:
Core Libraries:
- `adafruit_wiznet5k` - Ethernet connectivity
- `adafruit_wiznet5k_socketpool` - Socket management
- `adafruit_ov2640` - Camera control
- `adafruit_hcsr04` - Ultrasonic sensor
Import Required Libraries
import time
import gc
import board
import busio
import digitalio
import wiznet
from adafruit_wiznet5k.adafruit_wiznet5k import WIZNET5K
import adafruit_wiznet5k.adafruit_wiznet5k_socketpool as socketpool
import adafruit_ov2640
import adafruit_hcsr04Network Configuration
MY_MAC = "00:08:DC:03:04:05"
# Using DHCP for automatic IP assignmentHardware Initialization
Ethernet Setup:
# Reset W6300 chip
ethernetRst = digitalio.DigitalInOut(board.W5K_RST)
ethernetRst.direction = digitalio.Direction.OUTPUT
ethernetRst.value = False
time.sleep(1)
ethernetRst.value = True
time.sleep(1)
# SPI setup for Ethernet
spi_bus = wiznet.PIO_SPI(
board.W5K_SCK, # Clock pin
quad_io0=board.W5K_MOSI, # Master Out Slave In (MOSI)
quad_io1=board.W5K_MISO, # Master In Slave Out (MISO)
quad_io2=board.W5K_IO2, # Additional IO for quad-SPI
quad_io3=board.W5K_IO3, # Additional IO for quad-SPI
)
# Initialize Ethernet with DHCP
cs = digitalio.DigitalInOut(board.W5K_CS)
eth = WIZNET5K(spi_bus, cs, is_dhcp=True, mac=MY_MAC, debug=False)
ip_address = eth.pretty_ip(eth.ip_address)
print(f"IP Address: {ip_address}")Camera Setup:
i2c = busio.I2C(board.GP9, board.GP8)
cam = adafruit_ov2640.OV2640(
i2c, # I2C bus for camera control
data_pins=[board.GP0, board.GP1, board.GP2, board.GP3,
board.GP4, board.GP5, board.GP6, board.GP7], # 8-bit parallel data
clock=board.GP10, # Pixel clock (20MHz)
vsync=board.GP12, # Vertical sync (new frame)
href=board.GP11, # Horizontal reference (new line)
reset=board.GP13, # Hardware reset control
mclk_frequency=20_000_000,# Master clock frequency
)
cam.size = adafruit_ov2640.OV2640_SIZE_SVGA
cam.colorspace = adafruit_ov2640.OV2640_COLOR_JPEG
time.sleep(2)Ultrasonic Sensor Setup:
sonar = adafruit_hcsr04.HCSR04(
trigger_pin=board.GP27, # Output: 10Β΅s pulse to start measurement
echo_pin=board.GP26, # Input: Pulse width proportional to distance
timeout=0.5 # Maximum wait time for echo (0.5s = ~85m max)
)System Configuration
# Frame buffer for image capture
frame_buffer = bytearray(40 * 1024)
# Detection threshold (20 cm)
DETECTION_THRESHOLD = 20.0
# Critical state variables
latest_detection_image = None # Buffer: Stores JPEG data (up to 40KB)
current_distance = None # Float: Last measured distance in cm
show_image = False # Boolean: Control image display on webpage
last_detection_time = 0 # Float: Timestamp of last detection
detection_cooldown = 1.0 # Float: Minimum time between capturesCore Functions
Distance Measurement:
def measure_distance():
try:
distance = sonar.distance
if 2 <= distance <= 400:
return distance
except RuntimeError:
pass
return None*Measures distance with validation (2-400cm range)*
Image Capture:
def capture_image():
try:
jpeg_data = cam.capture(frame_buffer)
if jpeg_data:
return bytes(jpeg_data)
except Exception as e:
print(f"Capture error: {e}")
return None*Captures and validates JPEG images*
Web Server Functions:
send_html_response() - Detailed Explanation
HTTP Response Construction Process
def send_html_response(conn, client_ip):
"""Send HTML page"""
global current_distance, latest_detection_image, show_image
# Handle None distance
distance_str = f"{current_distance:.1f} cm" if current_distance is not None else "-- cm"
# Check if we should show the image
show_image = current_distance is not None and current_distance < DETECTION_THRESHOLD
# Border color based on detection
border_color = "red" if show_image else "#ccc"Step 1: Data Gathering
Memory State β HTML Variables
βββββββββββββββββββββββββββββββββββββββββ
current_distance: 8.5 β "8.5 cm"
show_image: True β border_color: "red"
latest_detection_image: [JPEG data] β Image will be shown
Step 2: Conditional HTML Generation
if show_image:
text = """
<img src="/latest.jpg" alt="Camera Image">
"""
else:
text = """
<div id="placeholder">
<div>
<h3>No Object Detected</h3>
<p>Image will appear when object is within 20cm</p>
<p>Current distance: {distance_str}</p>
</div>
</div>
"""Step 3: HTTP Protocol Assembly
response = (
f"HTTP/1.1 200 OK\r\n"
f"Content-Type: text/html; charset=utf-8\r\n"
f"Connection: close\r\n"
f"Content-Length: {len(html)}\r\n"
f"\r\n{html}"
).encode()Exact Bytes Sent to Browser:
Byte Stream (example):
48 54 54 50 2F 31 2E 31 20 32 30 30 20 4F 4B 0D 0A HTTP/1.1 200 OK\r\n
43 6F 6E 74 65 6E 74 2D 54 79 70 65 3A 20 74 65 78 Content-Type: tex
74 2F 68 74 6D 6C 3B 20 63 68 61 72 73 65 74 3D 75 t/html; charset=u
74 66 2D 38 0D 0A 43 6F 6E 6E 65 63 74 69 6F 6E 3A tf-8\r\nConnection:
20 63 6C 6F 73 65 0D 0A 43 6F 6E 74 65 6E 74 2D 4C close\r\nContent-L
65 6E 67 74 68 3A 20 31 35 32 37 0D 0A 0D 0A 3C 21 ength: 1527\r\n\r\n<!
44 4F 43 54 59 50 45 20 68 74 6D 6C 3E 0A 3C 68 74 DOCTYPE html>\n<ht
6D 6C 3E... ml>...Why This Structure Matters:
Browser Expectation: Our Delivery:
1. Status Line HTTP/1.1 200 OK
2. Headers Content-Type, Connection, Content-Length
3. Empty Line \r\n\r\n (CRLF CRLF)
4. Body <html>...</html>Step 4: Network Transmission
try:
conn.send(response)
print(f" Distance: {distance_str}")
return True
except Exception as e:
print(f" Failed to send HTML: {e}")
return Falsesend_image_response() - Detailed Explanation
Binary Image Transmission Process
def send_image_response(conn):
"""Send JPEG image with 2048 byte chunks"""
global latest_detection_image
if latest_detection_image:
image_length = len(latest_detection_image)
response = (
f"HTTP/1.1 200 OK\r\n"
f"Content-Type: image/jpeg\r\n"
f"Content-Length: {image_length}\r\n"
f"Cache-Control: no-cache\r\n"
f"Connection: close\r\n"
f"\r\n"
).encode()Step 1: HTTP Headers for Binary Data
Critical Headers Explained:
Content-Type: image/jpeg
- Tells browser "this is a JPEG image, not HTML/text"
- Browser will render as image, not display raw bytes
Content-Length: 28765
- Exact byte count of JPEG data
- Browser knows when transmission is complete
Cache-Control: no-cache
- Prevents browser from storing old images
- Ensures fresh image on every request
Connection: close
- Close TCP after image sent
- Frees WIZNET socket for next requestStep 2: Chunked Transmission Algorith
# Send in 2048 byte chunks
chunk_size = 2048
for i in range(0, image_length, chunk_size):
end = min(i + chunk_size, image_length)
conn.send(latest_detection_image[i:end])Send the image separately to prevents:
- Buffer overflow (if chunk > free space)
- Deadlock (waiting for ACK with full buffer)
- Fragmentation (inefficient small packets)
Step 3 : Error Handling and Cleanup
Network Failure Scenarios Handled:
except Exception as e:
print(f" Image send error: {e}")Step 4: Browser Reception and Rendering
Browser Processing Pipeline:
1. Receives HTTP/1.1 200 OK
2. Sees Content-Type: image/jpeg
3. Allocates buffer for 28,765 bytes (Content-Length)
4. Receives chunks, fills buffer
5. Validates JPEG (checks for FF D8 ... FF D9)
6. Decodes JPEG to RGB pixels
7. Renders in <img> tag on page
8. Cache-control: no-cache β won't reuse on next requestWeb Server Setup
pool = socketpool.SocketPool(eth)
server = pool.socket()
server.bind((ip_address, 80))
server.listen(1)
print(f"Server ready at http://{ip_address}")Main Loop Logic
The system operates in a continuous loop with three primary functions:
1. DHCP Maintenance (every 30 seconds):
- Renews DHCP lease to maintain network connectivity
2. Object Detection (every 0.5 seconds):
Measures distance using ultrasonic sensor
If object < 20cm AND cooldown period passed:
- Captures JPEG image
- Validates image (checks for JPEG header)
- Stores image in memory
- Updates web display flag
If no detection:
- Clears stored images
- Runs garbage collection
3. HTTP Request Handling:
Listens for browser connections (0.1s timeout)
Processes requests:
- `GET /latest.jpg` β Serves captured image
- Any other request β Serves HTML page with auto-refresh
Closes connections immediately after serving
# Main loop - SINGLE SERVER SOCKET
try:
while True:
current_time = time.monotonic()
# 1. DHCP Maintenance (every 30 seconds)
if current_time - last_dhcp_check > 30:
eth.maintain_dhcp_lease()
last_dhcp_check = current_time
# 2. Object Detection Logic
if connection_active:
if current_time - last_distance_check > 0.5:
distance = measure_distance()
current_distance = distance
if distance and distance < DETECTION_THRESHOLD:
if current_time - last_detection_time > detection_cooldown:
# Detection confirmed - capture image
led.value = True
image_data = capture_image()
if image_data:
# Validate JPEG magic number
if image_data[0] == 0xFF and image_data[1] == 0xD8:
latest_detection_image = image_data
show_image = True
last_detection_time = current_time
print(f"π¨ Detected: {distance:.1f}cm")
time.sleep(0.1)
led.value = False
elif distance is None or distance >= DETECTION_THRESHOLD:
show_image = False
latest_detection_image = None
gc.collect() # Free memory
last_distance_check = current_time
# 3. Web Server Handling
try:
server.settimeout(0.1)
conn, addr = server.accept()
# ... handle HTTP requests ...
except Exception as e:
if "timeout" not in str(e):
connection_active = False
time.sleep(0.01)HTML Interface Features
- Real-time distance display with color coding
- Automatic image display on detection
- 2-second auto-refresh for updates
- Responsive design for different devices
- Visual status indicators (red/green)
Key Features:
1. Automatic Object Detection - Ultrasonic sensor triggers image capture
2. Real-time Web Interface - Live updates without manual refresh
3. Efficient Memory Management - Adaptive buffer usage and garbage collection
4. Network Reliability - Ethernet connection with DHCP support
5. Fail-safe Operation - Comprehensive error handling and recovery
Technical Benefits:
- Low Latency: Image capture within 100ms of detection
- Resource Efficient: Optimized for microcontroller constraints
- Scalable Design: Can be extended with multiple sensors
- Professional Interface: Clean web UI with status indicators
Step 6: DemonstrationConclusionThis object detection camera system successfully demonstrates:
1. Intelligent Automation - Autonomous detection and capture
2. Real-time Monitoring - Instant web interface updates
3. Hardware Integration - Seamless sensor-camera-Ethernet coordination
4. Resource Optimization - Efficient operation on constrained hardware
The system provides a practical foundation for various applications including:
- Security and surveillance systems
- Package delivery monitoring
- Industrial automation
- Smart home applications
By combining reliable Ethernet connectivity with responsive sensing and imaging capabilities, this project showcases how embedded systems can deliver sophisticated IoT functionality with minimal hardware requirements.








Comments