两块 ESP32-S3-WROOM-1-N16R8 图传板(DVP 接口接 OV2640/OV3660/OV5640), 向队伍客户端与裁判终端推 UDP 图传。固件是跑在 ESP-IDF 上的 Rust,用官方 esp32-camera 组件采集;客户端是 Rust + Slint 桌面程序。 三个 crate 各自独立构建: . firmware,交叉编译到 Xtensa protocol/ 线路格式与帧重组,两端共用,无 ESP-IDF 依赖,有单元测试 client/ Slint 桌面客户端 视频走 UDP 而非 TCP:实测传感器产出 22 fps,TCP MJPEG 只送出 1.9 fps, 且只占用链路能力的 1%——链路是空的,帧堵在队头阻塞后面等一个实时画面 等不起的重传计时器。改为独立数据报、不重传、缺片丢整帧后达到 20.8 fps。 发现是双向的:板子每 2 秒广播 Hello,客户端广播 Subscribe,板子据此定向 推流。板子身份由 MAC 派生,重启不变,所以客户端可以把绑定存进配置。 裁判端同时收两块板子(每个发送方一个重组器),队伍端绑定其中一块并只显示 自己那一边的血量。装甲板接口已预留(ArmorSource trait + ARMOR_PORT), 当前无数据源,界面显示占位符。 vendor 了 esp32-camera 2.1.7:组件注册表的版本求解器找不到它,且需要打一处 补丁(见 components/esp32-camera/target/esp32s3/ll_cam.c)。 .gitignore 三条 target/ 都锚定在根目录,因为 protocol/ 与 client/ 各自也是 独立 crate;而 components/esp32-camera/target/ 是上游的按芯片分目录,里面是 源码,必须保留。
242 lines
6.1 KiB
C
242 lines
6.1 KiB
C
/*
|
|
* OV5640 Autofocus Implementation
|
|
*
|
|
* This file contains the sensor-specific autofocus implementation for OV5640.
|
|
*/
|
|
|
|
#include "sdkconfig.h"
|
|
|
|
#if defined(CONFIG_CAMERA_AF_SUPPORT) && CONFIG_CAMERA_AF_SUPPORT
|
|
#if defined(CONFIG_OV5640_SUPPORT) && CONFIG_OV5640_SUPPORT
|
|
|
|
#include "ov5640.h"
|
|
#include "ov5640_af_firmware.h"
|
|
#include "sensor.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/task.h"
|
|
#include "esp_timer.h"
|
|
|
|
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
|
#include "esp32-hal-log.h"
|
|
#define TAG ""
|
|
#else
|
|
#include "esp_log.h"
|
|
static const char *TAG = "ov5640_af";
|
|
#endif
|
|
|
|
// OV5640 AF registers
|
|
#define OV5640_CMD_MAIN 0x3022
|
|
#define OV5640_CMD_ACK 0x3023
|
|
#define OV5640_CMD_PARA0 0x3024
|
|
#define OV5640_CMD_PARA1 0x3025
|
|
#define OV5640_CMD_PARA2 0x3026
|
|
#define OV5640_CMD_PARA3 0x3027
|
|
#define OV5640_CMD_PARA4 0x3028
|
|
#define OV5640_CMD_FW_STATUS 0x3029
|
|
|
|
#define OV5640_AF_TRIG_SINGLE 0x03
|
|
#define OV5640_AF_CONTINUOUS 0x04
|
|
|
|
#define OV5640_FW_STATUS_IDLE 0x70
|
|
#define OV5640_FW_STATUS_FOCUSED 0x10
|
|
|
|
static esp_err_t ov5640_reg_write(sensor_t *sensor, int reg, int value)
|
|
{
|
|
if (sensor->set_reg(sensor, reg, 0xff, value) < 0) {
|
|
return ESP_FAIL;
|
|
}
|
|
return ESP_OK;
|
|
}
|
|
|
|
static esp_err_t ov5640_reg_read(sensor_t *sensor, int reg, int *out_value)
|
|
{
|
|
int v = sensor->get_reg(sensor, reg, 0xff);
|
|
if (v < 0) {
|
|
return ESP_FAIL;
|
|
}
|
|
*out_value = v;
|
|
return ESP_OK;
|
|
}
|
|
|
|
static esp_err_t ov5640_af_wait_ack_clear(sensor_t *sensor, uint32_t timeout_ms)
|
|
{
|
|
const uint64_t start = esp_timer_get_time();
|
|
while (true) {
|
|
int ack = 0;
|
|
if (ov5640_reg_read(sensor, OV5640_CMD_ACK, &ack) != ESP_OK) {
|
|
return ESP_FAIL;
|
|
}
|
|
if (ack == 0x00) {
|
|
return ESP_OK;
|
|
}
|
|
if (timeout_ms && ((esp_timer_get_time() - start) / 1000ULL) > timeout_ms) {
|
|
return ESP_ERR_TIMEOUT;
|
|
}
|
|
vTaskDelay(pdMS_TO_TICKS(5));
|
|
}
|
|
}
|
|
|
|
static esp_err_t ov5640_af_wait_fw_idle(sensor_t *sensor, uint32_t timeout_ms)
|
|
{
|
|
const uint64_t start = esp_timer_get_time();
|
|
while (true) {
|
|
int st = 0;
|
|
if (ov5640_reg_read(sensor, OV5640_CMD_FW_STATUS, &st) != ESP_OK) {
|
|
return ESP_FAIL;
|
|
}
|
|
if ((uint8_t)st == OV5640_FW_STATUS_IDLE) {
|
|
return ESP_OK;
|
|
}
|
|
if (timeout_ms && ((esp_timer_get_time() - start) / 1000ULL) > timeout_ms) {
|
|
return ESP_ERR_TIMEOUT;
|
|
}
|
|
vTaskDelay(pdMS_TO_TICKS(5));
|
|
}
|
|
}
|
|
|
|
static esp_err_t ov5640_af_firmware_load(sensor_t *sensor, uint32_t timeout_ms)
|
|
{
|
|
esp_err_t ret = ov5640_reg_write(sensor, 0x3000, 0x20); // reset MCU
|
|
if (ret != ESP_OK) {
|
|
return ret;
|
|
}
|
|
|
|
uint16_t addr = 0x8000;
|
|
for (size_t i = 0; i < sizeof(ov5640_af_firmware); i++) {
|
|
ret = ov5640_reg_write(sensor, addr, ov5640_af_firmware[i]);
|
|
if (ret != ESP_OK) {
|
|
return ret;
|
|
}
|
|
addr++;
|
|
}
|
|
|
|
(void)ov5640_reg_write(sensor, OV5640_CMD_MAIN, 0x00);
|
|
(void)ov5640_reg_write(sensor, OV5640_CMD_ACK, 0x00);
|
|
(void)ov5640_reg_write(sensor, OV5640_CMD_PARA0, 0x00);
|
|
(void)ov5640_reg_write(sensor, OV5640_CMD_PARA1, 0x00);
|
|
(void)ov5640_reg_write(sensor, OV5640_CMD_PARA2, 0x00);
|
|
(void)ov5640_reg_write(sensor, OV5640_CMD_PARA3, 0x00);
|
|
(void)ov5640_reg_write(sensor, OV5640_CMD_PARA4, 0x00);
|
|
|
|
(void)ov5640_reg_write(sensor, OV5640_CMD_FW_STATUS, 0x7f);
|
|
(void)ov5640_reg_write(sensor, 0x3000, 0x00); // start MCU
|
|
|
|
return ov5640_af_wait_fw_idle(sensor, timeout_ms);
|
|
}
|
|
|
|
static esp_err_t ov5640_af_start(sensor_t *sensor, bool continuous, uint32_t timeout_ms)
|
|
{
|
|
esp_err_t ret = ov5640_reg_write(sensor, OV5640_CMD_MAIN, 0x01);
|
|
if (ret != ESP_OK) {
|
|
return ret;
|
|
}
|
|
|
|
ret = ov5640_reg_write(sensor, OV5640_CMD_MAIN, 0x08);
|
|
if (ret != ESP_OK) {
|
|
return ret;
|
|
}
|
|
|
|
ret = ov5640_af_wait_ack_clear(sensor, timeout_ms);
|
|
if (ret != ESP_OK) {
|
|
return ret;
|
|
}
|
|
|
|
ret = ov5640_reg_write(sensor, OV5640_CMD_ACK, 0x01);
|
|
if (ret != ESP_OK) {
|
|
return ret;
|
|
}
|
|
|
|
ret = ov5640_reg_write(sensor, OV5640_CMD_MAIN, continuous ? OV5640_AF_CONTINUOUS : OV5640_AF_TRIG_SINGLE);
|
|
if (ret != ESP_OK) {
|
|
return ret;
|
|
}
|
|
|
|
return ov5640_af_wait_ack_clear(sensor, timeout_ms);
|
|
}
|
|
|
|
// Sensor function pointer implementations
|
|
int ov5640_af_is_supported(sensor_t *sensor)
|
|
{
|
|
(void)sensor;
|
|
return 1; // OV5640 always supports AF
|
|
}
|
|
|
|
int ov5640_af_init(sensor_t *sensor, uint32_t timeout_ms)
|
|
{
|
|
if (!sensor) {
|
|
return -1;
|
|
}
|
|
|
|
esp_err_t ret = ov5640_af_firmware_load(sensor, timeout_ms);
|
|
if (ret != ESP_OK) {
|
|
ESP_LOGE(TAG, "AF firmware init failed: %s", esp_err_to_name(ret));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ov5640_af_set_mode(sensor_t *sensor, int mode)
|
|
{
|
|
if (!sensor) {
|
|
return -1;
|
|
}
|
|
|
|
esp_err_t ret;
|
|
switch (mode) {
|
|
case 0: // ESP_CAMERA_AF_MODE_AUTO
|
|
ret = ov5640_af_start(sensor, true, 2000);
|
|
break;
|
|
case 1: // ESP_CAMERA_AF_MODE_MANUAL
|
|
ret = ov5640_reg_write(sensor, OV5640_CMD_MAIN, 0x00);
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
return (ret == ESP_OK) ? 0 : -1;
|
|
}
|
|
|
|
int ov5640_af_trigger(sensor_t *sensor)
|
|
{
|
|
if (!sensor) {
|
|
return -1;
|
|
}
|
|
|
|
esp_err_t ret = ov5640_af_start(sensor, false, 2000);
|
|
return (ret == ESP_OK) ? 0 : -1;
|
|
}
|
|
|
|
int ov5640_af_get_status(sensor_t *sensor, uint8_t *out_raw, bool *out_focused, bool *out_busy)
|
|
{
|
|
if (!sensor || !out_raw || !out_focused || !out_busy) {
|
|
return -1;
|
|
}
|
|
|
|
int st = 0;
|
|
esp_err_t ret = ov5640_reg_read(sensor, OV5640_CMD_FW_STATUS, &st);
|
|
if (ret != ESP_OK) {
|
|
return -1;
|
|
}
|
|
|
|
*out_raw = (uint8_t)st;
|
|
*out_focused = ((uint8_t)st == OV5640_FW_STATUS_FOCUSED);
|
|
*out_busy = !(((uint8_t)st == OV5640_FW_STATUS_IDLE) || *out_focused);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ov5640_af_set_manual_position(sensor_t *sensor, uint16_t position)
|
|
{
|
|
(void)sensor;
|
|
(void)position;
|
|
// Manual lens position is not exposed safely in this driver yet.
|
|
return -1;
|
|
}
|
|
|
|
#endif // CONFIG_OV5640_SUPPORT
|
|
#endif // CONFIG_CAMERA_AF_SUPPORT
|