276 lines
7.0 KiB
C
276 lines
7.0 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
|
|
#include "esp_camera.h"
|
|
#include "esp_log.h"
|
|
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/task.h"
|
|
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <unistd.h>
|
|
|
|
#include "command.h"
|
|
#include "utils.h"
|
|
|
|
/* ============================================================
|
|
* CONFIG
|
|
* ============================================================ */
|
|
#define TAG "CAMERA"
|
|
#define MAX_UDP_SIZE 2034
|
|
|
|
#if defined(CONFIG_MODULE_RECON) && defined(CONFIG_RECON_MODE_CAMERA)
|
|
/* ================= CAMERA PINS ================= */
|
|
#define CAM_PIN_PWDN 32
|
|
#define CAM_PIN_RESET -1
|
|
#define CAM_PIN_XCLK 0
|
|
#define CAM_PIN_SIOD 26
|
|
#define CAM_PIN_SIOC 27
|
|
#define CAM_PIN_D7 35
|
|
#define CAM_PIN_D6 34
|
|
#define CAM_PIN_D5 39
|
|
#define CAM_PIN_D4 36
|
|
#define CAM_PIN_D3 21
|
|
#define CAM_PIN_D2 19
|
|
#define CAM_PIN_D1 18
|
|
#define CAM_PIN_D0 5
|
|
#define CAM_PIN_VSYNC 25
|
|
#define CAM_PIN_HREF 23
|
|
#define CAM_PIN_PCLK 22
|
|
|
|
/* ============================================================
|
|
* STATE
|
|
* ============================================================ */
|
|
static volatile bool streaming_active = false;
|
|
static bool camera_initialized = false;
|
|
|
|
static int udp_sock = -1;
|
|
static struct sockaddr_in dest_addr;
|
|
|
|
/* ⚠️ à passer en Kconfig plus tard */
|
|
static const char *token = "Sup3rS3cretT0k3n";
|
|
|
|
/* ============================================================
|
|
* CAMERA INIT
|
|
* ============================================================ */
|
|
static bool init_camera(void)
|
|
{
|
|
camera_config_t cfg = {
|
|
.pin_pwdn = CAM_PIN_PWDN,
|
|
.pin_reset = CAM_PIN_RESET,
|
|
.pin_xclk = CAM_PIN_XCLK,
|
|
.pin_sccb_sda = CAM_PIN_SIOD,
|
|
.pin_sccb_scl = CAM_PIN_SIOC,
|
|
.pin_d7 = CAM_PIN_D7,
|
|
.pin_d6 = CAM_PIN_D6,
|
|
.pin_d5 = CAM_PIN_D5,
|
|
.pin_d4 = CAM_PIN_D4,
|
|
.pin_d3 = CAM_PIN_D3,
|
|
.pin_d2 = CAM_PIN_D2,
|
|
.pin_d1 = CAM_PIN_D1,
|
|
.pin_d0 = CAM_PIN_D0,
|
|
.pin_vsync = CAM_PIN_VSYNC,
|
|
.pin_href = CAM_PIN_HREF,
|
|
.pin_pclk = CAM_PIN_PCLK,
|
|
.xclk_freq_hz = 20000000,
|
|
.ledc_timer = LEDC_TIMER_0,
|
|
.ledc_channel = LEDC_CHANNEL_0,
|
|
.pixel_format = PIXFORMAT_JPEG,
|
|
.frame_size = FRAMESIZE_QQVGA,
|
|
.jpeg_quality = 20,
|
|
.fb_count = 2,
|
|
.fb_location = CAMERA_FB_IN_PSRAM,
|
|
.grab_mode = CAMERA_GRAB_LATEST
|
|
};
|
|
|
|
if (esp_camera_init(&cfg) != ESP_OK) {
|
|
msg_error(TAG, "camera init failed", NULL);
|
|
return false;
|
|
}
|
|
|
|
msg_info(TAG, "camera initialized", NULL);
|
|
vTaskDelay(pdMS_TO_TICKS(200));
|
|
return true;
|
|
}
|
|
|
|
/* ============================================================
|
|
* STREAM TASK
|
|
* ============================================================ */
|
|
static void udp_stream_task(void *arg)
|
|
{
|
|
(void)arg;
|
|
|
|
msg_info(TAG, "stream started", NULL);
|
|
|
|
const size_t token_len = strlen(token);
|
|
uint8_t buf[MAX_UDP_SIZE + 32];
|
|
|
|
while (streaming_active) {
|
|
|
|
camera_fb_t *fb = esp_camera_fb_get();
|
|
if (!fb) {
|
|
msg_error(TAG, "frame capture failed", NULL);
|
|
vTaskDelay(pdMS_TO_TICKS(50));
|
|
continue;
|
|
}
|
|
|
|
/* START */
|
|
memcpy(buf, token, token_len);
|
|
memcpy(buf + token_len, "START", 5);
|
|
sendto(udp_sock, buf, token_len + 5, 0,
|
|
(struct sockaddr *)&dest_addr, sizeof(dest_addr));
|
|
|
|
size_t off = 0;
|
|
size_t rem = fb->len;
|
|
|
|
while (rem > 0 && streaming_active) {
|
|
size_t chunk = rem > MAX_UDP_SIZE ? MAX_UDP_SIZE : rem;
|
|
|
|
memcpy(buf, token, token_len);
|
|
memcpy(buf + token_len, fb->buf + off, chunk);
|
|
|
|
if (sendto(udp_sock, buf, token_len + chunk, 0,
|
|
(struct sockaddr *)&dest_addr,
|
|
sizeof(dest_addr)) < 0) {
|
|
msg_error(TAG, "udp send failed", NULL);
|
|
break;
|
|
}
|
|
|
|
off += chunk;
|
|
rem -= chunk;
|
|
vTaskDelay(1);
|
|
}
|
|
|
|
/* END */
|
|
memcpy(buf, token, token_len);
|
|
memcpy(buf + token_len, "END", 3);
|
|
sendto(udp_sock, buf, token_len + 3, 0,
|
|
(struct sockaddr *)&dest_addr, sizeof(dest_addr));
|
|
|
|
esp_camera_fb_return(fb);
|
|
vTaskDelay(pdMS_TO_TICKS(140)); /* ~7 FPS */
|
|
}
|
|
|
|
if (udp_sock >= 0) {
|
|
close(udp_sock);
|
|
udp_sock = -1;
|
|
}
|
|
|
|
msg_info(TAG, "stream stopped", NULL);
|
|
vTaskDelete(NULL);
|
|
}
|
|
|
|
/* ============================================================
|
|
* STREAM CONTROL
|
|
* ============================================================ */
|
|
static void start_stream(const char *ip, uint16_t port)
|
|
{
|
|
if (streaming_active) {
|
|
msg_error(TAG, "stream already active", NULL);
|
|
return;
|
|
}
|
|
|
|
if (!camera_initialized) {
|
|
if (!init_camera())
|
|
return;
|
|
camera_initialized = true;
|
|
}
|
|
|
|
udp_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
|
|
if (udp_sock < 0) {
|
|
msg_error(TAG, "udp socket failed", NULL);
|
|
return;
|
|
}
|
|
|
|
memset(&dest_addr, 0, sizeof(dest_addr));
|
|
dest_addr.sin_family = AF_INET;
|
|
dest_addr.sin_port = htons(port);
|
|
dest_addr.sin_addr.s_addr = inet_addr(ip);
|
|
|
|
streaming_active = true;
|
|
|
|
xTaskCreatePinnedToCore(
|
|
udp_stream_task,
|
|
"cam_stream",
|
|
8192,
|
|
NULL,
|
|
5,
|
|
NULL,
|
|
0
|
|
);
|
|
}
|
|
|
|
static void stop_stream(void)
|
|
{
|
|
if (!streaming_active) {
|
|
msg_error(TAG, "no active stream", NULL);
|
|
return;
|
|
}
|
|
|
|
streaming_active = false;
|
|
}
|
|
|
|
/* ============================================================
|
|
* COMMAND HANDLERS
|
|
* ============================================================ */
|
|
static int cmd_cam_start(int argc,
|
|
char **argv,
|
|
const char *req,
|
|
void *ctx)
|
|
{
|
|
(void)ctx;
|
|
|
|
if (argc != 2) {
|
|
msg_error(TAG, "usage: cam_start <ip> <port>", req);
|
|
return -1;
|
|
}
|
|
|
|
start_stream(argv[0], (uint16_t)atoi(argv[1]));
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_cam_stop(int argc,
|
|
char **argv,
|
|
const char *req,
|
|
void *ctx)
|
|
{
|
|
(void)argc;
|
|
(void)argv;
|
|
(void)ctx;
|
|
|
|
stop_stream();
|
|
return 0;
|
|
}
|
|
|
|
/* ============================================================
|
|
* REGISTER COMMANDS
|
|
* ============================================================ */
|
|
static const command_t cmd_cam_start_def = {
|
|
.name = "cam_start",
|
|
.min_args = 2,
|
|
.max_args = 2,
|
|
.handler = cmd_cam_start,
|
|
.ctx = NULL,
|
|
.async = false
|
|
};
|
|
|
|
static const command_t cmd_cam_stop_def = {
|
|
.name = "cam_stop",
|
|
.min_args = 0,
|
|
.max_args = 0,
|
|
.handler = cmd_cam_stop,
|
|
.ctx = NULL,
|
|
.async = false
|
|
};
|
|
|
|
void mod_camera_register_commands(void)
|
|
{
|
|
command_register(&cmd_cam_start_def);
|
|
command_register(&cmd_cam_stop_def);
|
|
}
|
|
|
|
#endif |