24#include "nxd_dhcp_client.h"
43 "<!doctype html><html><head><meta charset=utf-8>"
44 "<meta name=viewport content='width=device-width,initial-scale=1'>"
45 "<title>EK-RA8D2 Camera</title><style>body{margin:0;background:#111;color:#eee;"
46 "font:16px system-ui;display:grid;place-items:center;min-height:100vh}main{text-align:center}"
47 "img{width:min(96vw,960px);height:auto;image-rendering:auto;border:1px solid #555}"
48 "small{display:block;margin-top:.5rem;color:#aaa}audio{margin-top:1rem}</style></head><body><main>"
49 "<h1>EK-RA8D2 / OV5640</h1><img id=stream src=/stream.mjpg alt='live camera'>"
50 "<small>bounded multipart MJPEG stream with automatic reconnect</small>"
51 "<audio controls src=/audio.wav></audio>"
52 "<small>latest rolling one-second onboard MIC1 recording</small>"
53 "<script>const s=document.getElementById('stream');s.onerror=()=>{"
54 "setTimeout(()=>{s.src='/stream.mjpg?t='+Date.now()},1000)}</script>"
55 "</main></body></html>";
77 if (status != NX_SUCCESS) {
80 status = nx_ip_create(&
s_ip,
82 IP_ADDRESS(0, 0, 0, 0),
83 IP_ADDRESS(0, 0, 0, 0),
89 if (status != NX_SUCCESS) {
93 if (status != NX_SUCCESS) {
96 status = nx_udp_enable(&
s_ip);
97 if (status != NX_SUCCESS) {
100 status = nx_tcp_enable(&
s_ip);
101 if (status != NX_SUCCESS) {
104 return nx_icmp_enable(&
s_ip);
109 if ((link ==
nullptr) || (mac ==
nullptr) || (out ==
nullptr)) {
115 nx_system_initialize();
120 if (status == NX_SUCCESS) {
121 status = nx_dhcp_start(&
s_dhcp);
124 if (status == NX_SUCCESS) {
125 status = nx_ip_status_check(&
s_ip,
126 (
ULONG)NX_IP_ADDRESS_RESOLVED,
130 if (status != NX_SUCCESS) {
137 (void)nx_ip_address_get(&
s_ip, &ip, &mask);
138 (void)nx_ip_gateway_address_get(&
s_ip, &gateway);
139 (void)nx_dhcp_server_address_get(&
s_dhcp, &server);
140 out->
ip = (uint32_t)ip;
141 out->
mask = (uint32_t)mask;
142 out->
gateway = (uint32_t)gateway;
144 out->
bound = (ip != 0U);
165 const uint8_t* cursor = (
const uint8_t*)data;
166 uint32_t remaining = bytes;
167 while (remaining != 0U) {
168 const uint32_t chunk =
170 NX_PACKET* packet = NX_NULL;
171 UINT status = nx_packet_allocate(&
s_pool, &packet, NX_TCP_PACKET, NX_WAIT_FOREVER);
172 if (status != NX_SUCCESS) {
175 status = nx_packet_data_append(packet,
176 (VOID*)(uintptr_t)cursor,
180 if (status != NX_SUCCESS) {
181 (void)nx_packet_release(packet);
184 status = nx_tcp_socket_send(&
s_http_socket, packet, NX_WAIT_FOREVER);
185 if (status != NX_SUCCESS) {
186 (void)nx_packet_release(packet);
214 while ((at < cap) && (*text !=
'\0')) {
221typedef enum : uint32_t {
251 while ((count != 0U) && (at < cap)) {
252 out[at++] = reverse[--count];
279 uint32_t timestamp_ms)
281 char header[320] = {};
285 if (timing !=
nullptr) {
294 if (timestamp_ms != 0U) {
304 "\r\nCache-Control: no-store, no-cache\r\nConnection: close\r\n\r\n");
306 if (status == NX_SUCCESS) {
327 static const char stream_header[] =
"HTTP/1.1 200 OK\r\n"
328 "Content-Type: multipart/x-mixed-replace; boundary=frame\r\n"
329 "Cache-Control: no-store, no-cache\r\n"
330 "Connection: close\r\n\r\n";
332 for (uint32_t frame_index = 0U;
335 const uint8_t* jpeg =
nullptr;
336 uint32_t jpeg_bytes = 0U;
341 return NX_NOT_SUCCESSFUL;
343 char part_header[128] = {};
348 "--frame\r\nContent-Type: image/jpeg\r\nContent-Length: ");
352 if (status == NX_SUCCESS) {
355 if (status == NX_SUCCESS) {
356 static const char part_tail[] =
"\r\n";
359 if (status == NX_SUCCESS) {
397 uint32_t path_index = 0U;
398 while ((index < bytes) && (path[path_index] !=
'\0') && (request[index] == path[path_index])) {
402 if (path[path_index] !=
'\0') {
405 return (index < bytes) && ((request[index] ==
' ') || (request[index] ==
'?'));
421 const uint8_t* jpeg =
nullptr;
422 uint32_t jpeg_bytes = 0U;
439 static const char failed[] =
"camera capture failed\n";
442 (uint32_t)(
sizeof(failed) - 1U),
464 const uint8_t* wav =
nullptr;
465 uint32_t wav_bytes = 0U;
466 uint32_t timestamp_ms = 0U;
477 static const char unavailable[] =
"audio capture not ready\n";
480 (uint32_t)(
sizeof(unavailable) - 1U),
507 static const char healthy[] =
"PASS c6 camera livestream audio=PASS camera=PASS\n";
510 (uint32_t)(
sizeof(healthy) - 1U),
516 (uint32_t)(
sizeof(
s_page) - 1U),
534 NX_PACKET* request_packet = NX_NULL;
535 if (nx_tcp_socket_receive(&
s_http_socket, &request_packet, NX_WAIT_FOREVER) != NX_SUCCESS) {
538 ULONG packet_bytes = 0U;
539 (void)nx_packet_length_get(request_packet, &packet_bytes);
542 : (uint32_t)packet_bytes;
545 (void)nx_packet_data_extract_offset(request_packet, 0U, request, (
ULONG)request_bytes, &copied);
546 (void)nx_packet_release(request_packet);
552 UINT status = nx_tcp_socket_create(&
s_ip,
561 if (status == NX_SUCCESS) {
562 status = nx_tcp_server_socket_listen(&
s_ip,
568 if (status != NX_SUCCESS) {
574 c6_cam_puts(
"c6_cam: PASS HTTP camera server listening\r\n");
576 if (nx_tcp_server_socket_accept(&
s_http_socket, NX_WAIT_FOREVER) == NX_SUCCESS) {
578 (void)nx_tcp_socket_disconnect(&
s_http_socket, NX_WAIT_FOREVER);
static CHAR s_dhcp_name[]
static UINT internal_c6_cam_http_send(const void *data, uint32_t bytes)
Send an exact byte span over the accepted HTTP socket.
c6_cam_net_format_t
Decimal formatting and minimum HTTP request sizes.
@ k_c6_cam_http_get_min
Minimum bytes in an HTTP GET prefix.
@ k_c6_cam_net_decimal_digits
Decimal digits in a 32-bit integer.
void c6_cam_http_serve(void)
Serve the browser UI and fresh JPEG frames forever.
static void internal_c6_cam_http_dispatch(const char *request, uint32_t bytes)
Dispatch a bounded HTTP request to one route handler.
static uint8_t s_pool_mem[k_c6_cam_net_pool_bytes]
static CHAR s_pool_name[]
static void internal_c6_cam_http_audio(void)
Snapshot and serve the current audio window.
static const char s_page[]
static void internal_c6_cam_http_handle(void)
Receive and dispatch one HTTP request.
static void internal_c6_cam_http_frame(void)
Capture and serve one JPEG still response.
static NX_TCP_SOCKET s_http_socket
static bool internal_c6_cam_request_is(const char *request, uint32_t bytes, const char *path)
Match a bounded HTTP GET request path.
static UINT internal_c6_cam_http_response(const char *type, const void *body, uint32_t body_bytes, const c6_cam_frame_timing_t *timing, uint32_t timestamp_ms)
Send one complete close-delimited HTTP response.
static UINT internal_c6_cam_http_stream(void)
Serve a bounded multipart MJPEG sequence.
ra8_err_t c6_cam_net_up(ra8_c6link_t *link, const ra8_c6link_mac_t *mac, c6_cam_lease_t *out)
Bind NetX to an associated raw C6 link and obtain a DHCP lease.
static CHAR s_socket_name[]
static UINT internal_c6_cam_net_create(void)
Create the static NetX packet pool and IP instance.
static uint32_t internal_c6_cam_append_u32(char *out, uint32_t at, uint32_t cap, uint32_t value)
Append an unsigned decimal value to a fixed buffer.
static uint32_t internal_c6_cam_append(char *out, uint32_t at, uint32_t cap, const char *text)
Append bounded text to a fixed HTTP header buffer.
Shared contracts for ESP32-C6 browser camera servers.
void c6_cam_put_u32(uint32_t value)
Write one unsigned decimal value to SCI8.
ra8_err_t c6_cam_audio_snapshot_wav(const uint8_t **out_wav, uint32_t *out_bytes, uint32_t *out_timestamp_ms)
Snapshot the rolling microphone ring as mono PCM-S16LE WAV.
@ k_c6_cam_tcp_window
HTTP socket receive window.
@ k_c6_cam_net_ip_stack
NetX IP thread stack bytes.
@ k_c6_cam_request_max
Maximum parsed request bytes.
@ k_c6_cam_net_pool_bytes
NetX packet pool bytes.
@ k_c6_cam_dhcp_wait_ms
DHCP lease timeout.
@ k_c6_cam_net_arp_bytes
NetX ARP cache bytes.
@ k_c6_cam_net_pkt_payload
NetX packet payload bytes.
@ k_c6_cam_net_ip_prio
NetX IP thread priority.
@ k_c6_cam_listen_backlog
Single-client listen backlog.
@ k_c6_cam_http_chunk
Maximum HTTP send chunk.
@ k_c6_cam_stream_frames
Frames served before client reconnect.
@ k_c6_cam_http_port
Browser server TCP port.
ra8_err_t c6_cam_camera_capture_jpeg(const uint8_t **out_jpeg, uint32_t *out_bytes, c6_cam_frame_timing_t *out_timing)
void c6_cam_puts(const char *text)
Write a bounded NUL-terminated string to SCI8.
void c6_cam_camera_report_last_error(void)
Emit backend-specific diagnostics for the last capture failure.
static ULONG s_ip_stack[k_demo_ip_stack/sizeof(ULONG)]
static ULONG s_arp_cache[k_demo_arp_cache/sizeof(ULONG)]
NetX Duo network driver shim that bridges onto the ESP32-C6 link.
void nx_ether_driver_c6_bind(ra8_c6link_t *link)
Bind an open C6 link handle to the driver and arm its mutex.
void nx_ether_driver_c6(NX_IP_DRIVER *driver_req)
NetX Duo link driver entry point for the ESP32-C6 Wi-Fi station.
void nx_ether_driver_c6_set_mac(const uint8_t mac[6])
Tell the driver the station MAC address to stamp on outgoing frames.
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
The single integration boundary between this firmware and the ESP32-C6.
struct ra8_c6link_mac ra8_c6link_mac_t
struct ra8_c6link ra8_c6link_t
Error Code Definitions for ra8-firmware.
@ k_ra8_err_not_initialized
Module not initialized – _init() not yet called successfully.
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
@ k_ra8_err_null_ptr
Pointer was NULL where a valid pointer was required.
@ k_ra8_err_timeout
Operation exceeded its time budget.
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
const char * ra8_err_to_str(ra8_err_t err)
Human-readable string for an error code.
static ra8_esp_hosted_pool_state_t s_pool
Singleton state of the memory and queue half.
int memcmp(const void *a, const void *b, size_t n)
Compare bytes in two memory areas.
unsigned int UINT
ThreadX-compatible unsigned int (host stub).
char CHAR
ThreadX-compatible CHAR (host stub).
unsigned long ULONG
ThreadX-compatible unsigned long (host stub).
Per-frame pipeline latency exposed in HTTP Server-Timing headers.
uint32_t convert_ms
Pixel conversion latency.
uint32_t timestamp_ms
Capture-start monotonic timestamp.
uint32_t capture_ms
Camera capture latency.
uint32_t encode_ms
Codec latency.
DHCP lease returned by the raw, bench-validated C6/NetX path.
bool bound
True after a lease is bound.
uint32_t dhcp_server
DHCP server IPv4 address.
uint32_t ip
Assigned IPv4 address.
uint32_t gateway
Assigned IPv4 default gateway.
uint32_t mask
Assigned IPv4 network mask.
uint8_t octet[k_ra8_c6link_mac_bytes]
Address octets, first on the wire first.