Wiznet makers

ronpang

Published February 04, 2026 ©

160 UCC

90 WCC

34 VAR

0 Contests

1 Followers

0 Following

Original Link

Object-Triggered Image Capture

An object-triggered camera system that automatically captures and serves images over Ethernet when objects are detected within 20 centimeter

COMPONENTS Hardware components

WIZnet - W6300-EVB-Pico2

x 1


sparkfun - Ultrasonic Sensor - HC-SR04

x 1


PROJECT DESCRIPTION

Introduction

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 Components

For 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

Step 2: Hardware Setup

Connections

HC-SR04 Ultrasonic SensorW6300-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

Step 3: Software Libraries Setup

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_hcsr04

 

Network Configuration

MY_MAC = "00:08:DC:03:04:05"
# Using DHCP for automatic IP assignment

 

Hardware 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 captures

 

Core 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 False

 

 

send_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 request

 

Step 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 request

 

Web 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)

Step 5: System Features and Benefits

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: Demonstration

Conclusion

This 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.


 

Documents
  • code

Comments Write