Skip to content

Commit e871c33

Browse files
rosterlohclaude
andcommitted
data_collection: add IMX219 MIPI CSI-2 capture
Wire the WaveShare ESP32-P4-Nano's IMX219 (RPi Camera v2) into data_collection over MIPI CSI-2 using the new rosterloh-drivers video_esp32_csi receiver: - Board overlay: imx219@10 on i2c0 (self-clocked module, 2 data lanes), CSI receiver endpoint, chosen zephyr,camera. Schematic-confirmed wiring. - prj.conf: enable VIDEO + VIDEO_ESP32_CSI + the video shell, and PSRAM-backed frame buffers (shared multi-heap, external attribute) for RAW10 frames. - main.c: set format, enqueue buffers, stream, dequeue one frame and log stats. WIP: builds and flashes. Runtime capture not yet verified - blocked by a board-level PSRAM early-boot hang and a pre-existing data_collection console silence over the CH343 UART port (the original app is equally silent). Requires the matching rosterloh-drivers branch esp32p4-csi-imx219. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 2c9ec88 commit e871c33

3 files changed

Lines changed: 151 additions & 0 deletions

File tree

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright (c) 2026 Richard Osterloh
3+
* SPDX-License-Identifier: Apache-2.0
4+
*
5+
* IMX219 (Raspberry Pi Camera v2) over MIPI CSI-2 on the Waveshare
6+
* ESP32-P4-Nano. Mirrors the upstream raspberry_pi_camera_module_2 shield.
7+
*
8+
* ┌─ BOARD WIRING TO CONFIRM AGAINST THE WAVESHARE SCHEMATIC ──────────────┐
9+
* │ - Sensor I2C (SCCB): assumed &i2c0 (the bus enabled on the board). If │
10+
* │ the CSI FPC routes SCCB to a different bus, move the imx219 node. │
11+
* │ - XCLK 24 MHz: the IMX219 needs an external master clock. The RPi cam │
12+
* │ module has no oscillator, so the P4 must drive 24 MHz onto the CSI │
13+
* │ connector clock pin (e.g. via LEDC/CLK_OUT). The fixed-clock below is │
14+
* │ informational only (the sensor driver reads its frequency for timing);│
15+
* │ generating the actual clock is a separate hardware bring-up step. │
16+
* │ - Sensor reset/XSHUTDOWN GPIO and the 2.5 V D-PHY LDO rail may also need │
17+
* │ asserting before probe (see the board &ldo node). │
18+
* └─────────────────────────────────────────────────────────────────────────┘
19+
*/
20+
21+
#include <zephyr/dt-bindings/video/video-interfaces.h>
22+
23+
/ {
24+
imx219_input_clock: imx219-input-clock {
25+
compatible = "fixed-clock";
26+
clock-frequency = <24000000>;
27+
#clock-cells = <0>;
28+
};
29+
30+
chosen {
31+
zephyr,camera = &mipi_csi;
32+
};
33+
};
34+
35+
&mipi_csi {
36+
status = "okay";
37+
38+
port {
39+
csi_ep_in: endpoint {
40+
remote-endpoint-label = "imx219_ep_out";
41+
bus-type = <VIDEO_BUS_TYPE_CSI2_DPHY>;
42+
data-lanes = <1 2>;
43+
};
44+
};
45+
};
46+
47+
&i2c0 {
48+
imx219: imx219@10 {
49+
compatible = "sony,imx219";
50+
reg = <0x10>;
51+
clocks = <&imx219_input_clock>;
52+
53+
port {
54+
imx219_ep_out: endpoint {
55+
remote-endpoint-label = "csi_ep_in";
56+
bus-type = <VIDEO_BUS_TYPE_CSI2_DPHY>;
57+
data-lanes = <1 2>;
58+
};
59+
};
60+
};
61+
};

applications/data_collection/prj.conf

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,19 @@ CONFIG_NET_MGMT_EVENT=y
1818
CONFIG_NET_CONFIG_SETTINGS=y
1919
CONFIG_NET_CONFIG_NEED_IPV4=y
2020

21+
# Camera: IMX219 over MIPI CSI-2. Frames are large RAW10 (~2.5 MB at
22+
# 1640x1232), so video buffers are drawn from PSRAM via the shared multi-heap
23+
# (SMH attribute 2 = external) and aligned to the 64-byte D-cache line.
24+
CONFIG_VIDEO=y
25+
CONFIG_VIDEO_ESP32_CSI=y
26+
CONFIG_VIDEO_SHELL=y
27+
CONFIG_VIDEO_BUFFER_POOL_NUM_MAX=2
28+
CONFIG_VIDEO_BUFFER_POOL_ALIGN=64
29+
CONFIG_VIDEO_BUFFER_USE_SHARED_MULTI_HEAP=y
30+
CONFIG_VIDEO_BUFFER_SMH_ATTRIBUTE=2
31+
CONFIG_ESP_SPIRAM=y
32+
CONFIG_ESP_SPIRAM_HEAP_SIZE=6291456
33+
2134
# MCUmgr: SMP over UDP for remote OS management (reboot / echo / taskstat).
2235
# NOTE: image-upgrade OTA is intentionally NOT enabled here. It requires the
2336
# MCUboot bootloader (sysbuild), which does not boot on the rev v1.3 ESP32-P4

applications/data_collection/src/main.c

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,16 @@
88
#include <zephyr/net/net_if.h>
99
#include <zephyr/net/net_mgmt.h>
1010
#include <zephyr/net/net_event.h>
11+
#include <zephyr/drivers/video.h>
1112

1213
LOG_MODULE_REGISTER(main, LOG_LEVEL_INF);
1314

15+
/* IMX219 2x2-binned full-FoV RAW10 frame. */
16+
#define CAPTURE_WIDTH 1640
17+
#define CAPTURE_HEIGHT 1232
18+
#define CAPTURE_FORMAT VIDEO_PIX_FMT_SBGGR10P
19+
#define CAPTURE_NBUFS 2
20+
1421
static struct net_mgmt_event_callback ipv4_cb;
1522

1623
static void on_ipv4_addr_add(struct net_mgmt_event_callback *cb, uint64_t mgmt_event,
@@ -33,12 +40,82 @@ static void on_ipv4_addr_add(struct net_mgmt_event_callback *cb, uint64_t mgmt_e
3340
}
3441
}
3542

43+
static int capture_one_frame(const struct device *cam)
44+
{
45+
struct video_format fmt = {
46+
.type = VIDEO_BUF_TYPE_OUTPUT,
47+
.pixelformat = CAPTURE_FORMAT,
48+
.width = CAPTURE_WIDTH,
49+
.height = CAPTURE_HEIGHT,
50+
};
51+
struct video_buffer *vbuf;
52+
int ret;
53+
54+
ret = video_set_format(cam, &fmt);
55+
if (ret < 0) {
56+
LOG_ERR("Failed to set format (%d)", ret);
57+
return ret;
58+
}
59+
60+
ret = video_get_format(cam, &fmt);
61+
if (ret < 0) {
62+
return ret;
63+
}
64+
LOG_INF("Capturing %ux%u, pitch %u, %u bytes/frame", fmt.width, fmt.height, fmt.pitch,
65+
fmt.pitch * fmt.height);
66+
67+
for (int i = 0; i < CAPTURE_NBUFS; i++) {
68+
vbuf = video_buffer_aligned_alloc(fmt.pitch * fmt.height,
69+
CONFIG_VIDEO_BUFFER_POOL_ALIGN, K_NO_WAIT);
70+
if (vbuf == NULL) {
71+
LOG_ERR("Failed to allocate video buffer %d", i);
72+
return -ENOMEM;
73+
}
74+
vbuf->type = VIDEO_BUF_TYPE_OUTPUT;
75+
ret = video_enqueue(cam, vbuf);
76+
if (ret < 0) {
77+
LOG_ERR("Failed to enqueue buffer %d (%d)", i, ret);
78+
return ret;
79+
}
80+
}
81+
82+
ret = video_stream_start(cam, VIDEO_BUF_TYPE_OUTPUT);
83+
if (ret < 0) {
84+
LOG_ERR("Failed to start stream (%d)", ret);
85+
return ret;
86+
}
87+
88+
ret = video_dequeue(cam, &vbuf, K_SECONDS(2));
89+
if (ret < 0) {
90+
LOG_ERR("Failed to dequeue frame (%d)", ret);
91+
video_stream_stop(cam, VIDEO_BUF_TYPE_OUTPUT);
92+
return ret;
93+
}
94+
95+
LOG_INF("Frame captured: %u bytes, first pixels %02x %02x %02x %02x", vbuf->bytesused,
96+
vbuf->buffer[0], vbuf->buffer[1], vbuf->buffer[2], vbuf->buffer[3]);
97+
98+
video_stream_stop(cam, VIDEO_BUF_TYPE_OUTPUT);
99+
return 0;
100+
}
101+
36102
int main(void)
37103
{
104+
const struct device *cam = DEVICE_DT_GET(DT_CHOSEN(zephyr_camera));
105+
38106
LOG_INF("data_collection starting (SMP/UDP management ready)");
39107

40108
net_mgmt_init_event_callback(&ipv4_cb, on_ipv4_addr_add, NET_EVENT_IPV4_ADDR_ADD);
41109
net_mgmt_add_event_callback(&ipv4_cb);
42110

111+
if (!device_is_ready(cam)) {
112+
LOG_ERR("Camera %s not ready", cam->name);
113+
return 0;
114+
}
115+
116+
if (capture_one_frame(cam) == 0) {
117+
LOG_INF("Camera capture OK; use the `video` shell for further captures");
118+
}
119+
43120
return 0;
44121
}

0 commit comments

Comments
 (0)