This project demonstrates a real-time Ethernet-based JPEG streaming system built using the WIZnet Pico (RP2040/RP2350) board combined with the ArduCAM Quick-Bootup 3MP DVP Camera.
Two main components make up the system:
Camera side (Pico): Captures JPEG frames via an 8-bit DVP interface and transmits them over Ethernet by splitting the data into UDP packets.
PC side (Python): Receives packets, reassembles complete frames using header information, and decodes the JPEGs in real time with OpenCV.
Through this project, makers can gain a complete understanding of real-time embedded camera streaming over Ethernet, forming a foundation for IoT vision, robotics, and remote monitoring applications.
[ArduCam X WIZnet Pico UDP Streaming Github Project Link]
🧠 Core Keywords:RP2040 DVP PIO DMA / UDP JPEG Streaming / Frame Reassembly / OpenCV Real-Time Decoding
🧠 Core Keywords:RP2040 DVP PIO DMA / UDP JPEG Streaming / Frame Reassembly / OpenCV Real-Time Decoding2. Hardware ModulesWIZnet Pico (RP2040 / RP2350)
[WIZnet Pico RP2040 Based Link]
[WIZnet Pico RP2350 Based Link]
Supports both RP2040 and RP2350 (up to 200MHz system clock)
Embedded Ethernet options:
W5100S / W5500 / W6100 — SPI (40MHz)
W6300 — QSPI Quad (37.5MHz)
Fully compatible with Pico SDK 1.5.1
Supports simultaneous use of DVP Camera + Ethernet + SPI Flash + UART
Arducam Quick-Bootup 3MP DVP Camera for IoT[Arducam Quick-Bootup 3MP DVP Camera for IoT]
💡 With its 300ms instant-on capability and ultra-low power design,this module is ideal for “instant-response IoT vision applications.”
💡 With its 300ms instant-on capability and ultra-low power design,this module is ideal for “instant-response IoT vision applications.”3. Performance Comparison (Sys Clock 200MHz)
✅ The W6300 QSPI variant delivers smooth HD streaming even in real-time applications.
These values represent typical JPEG-compressed frame sizes and throughput measured under 37.5 MHz QSPI operation on the W6300-EVB-Pico2 board.4. Pin Mapping (Pico ↔ ArduCAM)
📸 Each PCLK rising edge samples one pixel (8-bit data),while VSYNC HIGH defines the active frame transmission window.
📸 Each PCLK rising edge samples one pixel (8-bit data),while VSYNC HIGH defines the active frame transmission window.5. JPEG Frame Capture Sequence (on Pico)
① VSYNC ↑ → Frame Start
② HSYNC ↑ → New Line Start
③ PCLK ↑ → Sample D0–D7 (8-bit pixel)
④ DMA stores 32-bit chunks into line buffer
⑤ HSYNC ↓ → Line End
⑥ VSYNC ↓ → Frame End
The PIO handles the timing logic, while DMA continuously transfers 32-bit data blocks (512 bytes per line) into RAM.
📥 If the SOI marker (0xFFD8) isn’t detected within the first 4 lines (512B × 4),the system retries until a valid frame is captured.
📥 If the SOI marker (0xFFD8) isn’t detected within the first 4 lines (512B × 4),the system retries until a valid frame is captured.6. Final Streaming Process (UDP-based Transmission)
Since JPEG frames vary in size (a few KB to tens of KB),a single UDP packet cannot contain the entire image.
Therefore, the Pico splits each frame into ≤1, 400-byte payloads,attaching a small 4-byte header to each.
total_packets = (jpeg_size + PAYLOAD_SIZE - 1) / PAYLOAD_SIZE;
for (pkt_id = 0; pkt_id < total_packets; pkt_id++) {
tx_packet[0] = frame_id;
tx_packet[1] = pkt_id;
tx_packet[2] = total_packets;
tx_packet[3] = (pkt_id == total_packets - 1) ? 0x01 : 0x00;
memcpy(tx_packet + 4, jpeg_data + offset, chunk_size);
sendto(socket, tx_packet, chunk_size + 4, destip, destport);
}
🧩 R eassembly (Python)On the receiver side, each packet is reassembled based on Frame ID and Packet ID.The Assembler
class reconstructs full JPEG frames as follows:
fid, pid, tot = pkt[0], pkt[1], pkt[2]
self.buf.setdefault(fid, {})[pid] = pkt[4:]
if len(self.buf[fid]) == tot:
data = b"".join(self.buf[fid][i] for i in range(tot))
return data # Complete JPEG frame restored
OpenCV then decodes and displays the frame in real time.
🖥️ Even with UDP transport, this design achieves lossless frame assembly through intelligent ID-based reordering.
🖥️ Even with UDP transport, this design achieves lossless frame assembly through intelligent ID-based reordering.7. How to Run( Execution Guide )
Follow these steps to build, flash, and run the ArduCAM × WIZnet Pico JPEG Streaming demo.
① Select Your Board in the Top-Level CMakeLists.txt
Open the project’s root CMakeLists.txt
.
- Open the project’s root
CMakeLists.txt
.
Uncomment your target board (for example, W6300-EVB-PICO2
) and comment out the others.
- Uncomment your target board (for example,
W6300-EVB-PICO2
) and comment out the others.
This ensures the correct pin mapping, SPI/QSPI mode, and driver are used.
- This ensures the correct pin mapping, SPI/QSPI mode, and driver are used.
Example:
# set(BOARD_NAME "W5100S-EVB-PICO")
# set(BOARD_NAME "W5500-EVB-PICO")
# set(BOARD_NAME "W6100-EVB-PICO")
set(BOARD_NAME "W6300-EVB-PICO2") # ← active target board
Then, open
example/WIZnet_ArduCAMMega_UDP_Streaming/main.c
and modify the following parameters as needed:
IP address / Gateway / Subnet
- IP address / Gateway / Subnet
UDP port number (default: 5000)
- UDP port number (default: 5000)
System clock frequency (default: 200 MHz)
- System clock frequency (default: 200 MHz)
Save the file after editing.
② Build the Firmware
Open a terminal in the project directory and run:
mkdir build
cd build
cmake ..
make -j4
This will generate the compiled firmware file:
build/example/WIZnet_ArduCAMMega_UDP_Streaming/main.uf2
③ Flash the Firmware to the Pico
Connect your WIZnet Pico (RP2040/RP2350) via USB while holding the BOOTSEL button.
- Connect your WIZnet Pico (RP2040/RP2350) via USB while holding the BOOTSEL button.
The board will appear as a USB drive (e.g., RPI-RP2
).
- The board will appear as a USB drive (e.g.,
RPI-RP2
).
Copy the generated main.uf2
file into this drive.
- Copy the generated
main.uf2
file into this drive.
The board will automatically reboot and start running the streaming firmware.
- The board will automatically reboot and start running the streaming firmware.
④ Run the Python GUI on Your PC
Move to the example directory and run the GUI streaming viewer:
cd example/WIZnet_ArduCAMMega_UDP_Streaming
python.exe jpeg_stream_gui.py
This GUI will:
Connect to the Pico’s UDP streaming server
- Connect to the Pico’s UDP streaming server
Reassemble incoming JPEG packets
- Reassemble incoming JPEG packets
Decode and display them in real time using OpenCV
- Decode and display them in real time using OpenCV
Comments