mirror of
https://github.com/MrUnknownDE/OpenIris-ESPIDF.git
synced 2026-04-06 00:31:57 +02:00
Reformat project using clang-format
This commit is contained in:
12
.clang-format
Normal file
12
.clang-format
Normal file
@@ -0,0 +1,12 @@
|
||||
BasedOnStyle: Google
|
||||
IndentWidth: 4
|
||||
TabWidth: 4
|
||||
UseTab: Never
|
||||
BreakBeforeBraces: Allman
|
||||
ColumnLimit: 160
|
||||
IncludeBlocks: Preserve
|
||||
IndentCaseLabels: false
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
AllowShortBlocksOnASingleLine: Never
|
||||
@@ -1,4 +1,5 @@
|
||||
// source: https://github.com/espressif/esp-iot-solution/blob/4730d91db70df7e6e0a3191d725ab1c5f98ff9ce/examples/usb/device/usb_webcam/bootloader_components/boot_hooks/boot_hooks.c
|
||||
// source:
|
||||
// https://github.com/espressif/esp-iot-solution/blob/4730d91db70df7e6e0a3191d725ab1c5f98ff9ce/examples/usb/device/usb_webcam/bootloader_components/boot_hooks/boot_hooks.c
|
||||
|
||||
#ifdef CONFIG_GENERAL_INCLUDE_UVC_MODE
|
||||
#include "esp_log.h"
|
||||
@@ -9,16 +10,13 @@
|
||||
* with all its symbols.
|
||||
*/
|
||||
|
||||
void bootloader_hooks_include(void)
|
||||
{
|
||||
}
|
||||
void bootloader_hooks_include(void) {}
|
||||
|
||||
void bootloader_before_init(void)
|
||||
{
|
||||
|
||||
// Disable D+ pullup, to prevent the USB host from retrieving USB-Serial-JTAG's descriptor.
|
||||
SET_PERI_REG_MASK(USB_SERIAL_JTAG_CONF0_REG, USB_SERIAL_JTAG_PAD_PULL_OVERRIDE);
|
||||
CLEAR_PERI_REG_MASK(USB_SERIAL_JTAG_CONF0_REG, USB_SERIAL_JTAG_DP_PULLUP);
|
||||
CLEAR_PERI_REG_MASK(USB_SERIAL_JTAG_CONF0_REG, USB_SERIAL_JTAG_USB_PAD_ENABLE);
|
||||
// Disable D+ pullup, to prevent the USB host from retrieving USB-Serial-JTAG's descriptor.
|
||||
SET_PERI_REG_MASK(USB_SERIAL_JTAG_CONF0_REG, USB_SERIAL_JTAG_PAD_PULL_OVERRIDE);
|
||||
CLEAR_PERI_REG_MASK(USB_SERIAL_JTAG_CONF0_REG, USB_SERIAL_JTAG_DP_PULLUP);
|
||||
CLEAR_PERI_REG_MASK(USB_SERIAL_JTAG_CONF0_REG, USB_SERIAL_JTAG_USB_PAD_ENABLE);
|
||||
}
|
||||
#endif
|
||||
@@ -1,237 +1,229 @@
|
||||
#include "CameraManager.hpp"
|
||||
|
||||
const char *CAMERA_MANAGER_TAG = "[CAMERA_MANAGER]";
|
||||
const char* CAMERA_MANAGER_TAG = "[CAMERA_MANAGER]";
|
||||
|
||||
CameraManager::CameraManager(std::shared_ptr<ProjectConfig> projectConfig, QueueHandle_t eventQueue)
|
||||
: projectConfig(projectConfig), eventQueue(eventQueue) {}
|
||||
CameraManager::CameraManager(std::shared_ptr<ProjectConfig> projectConfig, QueueHandle_t eventQueue) : projectConfig(projectConfig), eventQueue(eventQueue) {}
|
||||
|
||||
void CameraManager::setupCameraPinout()
|
||||
{
|
||||
// Workaround for espM5SStack not having a defined camera
|
||||
// Workaround for espM5SStack not having a defined camera
|
||||
#ifdef CONFIG_CAMERA_MODULE_NAME
|
||||
ESP_LOGI(CAMERA_MANAGER_TAG, "[Camera]: Camera module is %s", CONFIG_CAMERA_MODULE_NAME);
|
||||
ESP_LOGI(CAMERA_MANAGER_TAG, "[Camera]: Camera module is %s", CONFIG_CAMERA_MODULE_NAME);
|
||||
#else
|
||||
ESP_LOGI(CAMERA_MANAGER_TAG, "[Camera]: Camera module is undefined");
|
||||
ESP_LOGI(CAMERA_MANAGER_TAG, "[Camera]: Camera module is undefined");
|
||||
#endif
|
||||
|
||||
// camera external clock signal frequencies
|
||||
// 10000000 stable
|
||||
// 16500000 optimal freq on ESP32-CAM (default)
|
||||
// 20000000 max freq on ESP32-CAM
|
||||
// 24000000 optimal freq on ESP32-S3 // 23MHz same fps
|
||||
int xclk_freq_hz = CONFIG_CAMERA_WIFI_XCLK_FREQ;
|
||||
// camera external clock signal frequencies
|
||||
// 10000000 stable
|
||||
// 16500000 optimal freq on ESP32-CAM (default)
|
||||
// 20000000 max freq on ESP32-CAM
|
||||
// 24000000 optimal freq on ESP32-S3 // 23MHz same fps
|
||||
int xclk_freq_hz = CONFIG_CAMERA_WIFI_XCLK_FREQ;
|
||||
|
||||
#if CONFIG_CAMERA_MODULE_ESP_EYE
|
||||
/* IO13, IO14 is designed for JTAG by default,
|
||||
* to use it as generalized input,
|
||||
* firstly declare it as pullup input
|
||||
**/
|
||||
gpio_reset_pin(13);
|
||||
gpio_reset_pin(14);
|
||||
gpio_set_direction(13, GPIO_MODE_INPUT);
|
||||
gpio_set_direction(14, GPIO_MODE_INPUT);
|
||||
gpio_set_pull_mode(13, GPIO_PULLUP_ONLY);
|
||||
gpio_set_pull_mode(14, GPIO_PULLUP_ONLY);
|
||||
ESP_LOGI(CAMERA_MANAGER_TAG, "ESP_EYE");
|
||||
/* IO13, IO14 is designed for JTAG by default,
|
||||
* to use it as generalized input,
|
||||
* firstly declare it as pullup input
|
||||
**/
|
||||
gpio_reset_pin(13);
|
||||
gpio_reset_pin(14);
|
||||
gpio_set_direction(13, GPIO_MODE_INPUT);
|
||||
gpio_set_direction(14, GPIO_MODE_INPUT);
|
||||
gpio_set_pull_mode(13, GPIO_PULLUP_ONLY);
|
||||
gpio_set_pull_mode(14, GPIO_PULLUP_ONLY);
|
||||
ESP_LOGI(CAMERA_MANAGER_TAG, "ESP_EYE");
|
||||
#elif CONFIG_CAMERA_MODULE_CAM_BOARD
|
||||
/* IO13, IO14 is designed for JTAG by default,
|
||||
* to use it as generalized input,
|
||||
* firstly declare it as pullup input
|
||||
**/
|
||||
/* IO13, IO14 is designed for JTAG by default,
|
||||
* to use it as generalized input,
|
||||
* firstly declare it as pullup input
|
||||
**/
|
||||
|
||||
gpio_reset_pin(13);
|
||||
gpio_reset_pin(14);
|
||||
gpio_set_direction(13, GPIO_MODE_INPUT);
|
||||
gpio_set_direction(14, GPIO_MODE_INPUT);
|
||||
gpio_set_pull_mode(13, GPIO_PULLUP_ONLY);
|
||||
gpio_set_pull_mode(14, GPIO_PULLUP_ONLY);
|
||||
gpio_reset_pin(13);
|
||||
gpio_reset_pin(14);
|
||||
gpio_set_direction(13, GPIO_MODE_INPUT);
|
||||
gpio_set_direction(14, GPIO_MODE_INPUT);
|
||||
gpio_set_pull_mode(13, GPIO_PULLUP_ONLY);
|
||||
gpio_set_pull_mode(14, GPIO_PULLUP_ONLY);
|
||||
|
||||
ESP_LOGI(CAMERA_MANAGER_TAG, "CAM_BOARD");
|
||||
ESP_LOGI(CAMERA_MANAGER_TAG, "CAM_BOARD");
|
||||
#endif
|
||||
#if CONFIG_GENERAL_INCLUDE_UVC_MODE
|
||||
xclk_freq_hz = CONFIG_CAMERA_USB_XCLK_FREQ;
|
||||
xclk_freq_hz = CONFIG_CAMERA_USB_XCLK_FREQ;
|
||||
#endif
|
||||
|
||||
config = {
|
||||
.pin_pwdn = CONFIG_PWDN_GPIO_NUM, // CAM_PIN_PWDN,
|
||||
.pin_reset = CONFIG_RESET_GPIO_NUM, // CAM_PIN_RESET,
|
||||
.pin_xclk = CONFIG_XCLK_GPIO_NUM, // CAM_PIN_XCLK,
|
||||
.pin_sccb_sda = CONFIG_SIOD_GPIO_NUM, // CAM_PIN_SIOD,
|
||||
.pin_sccb_scl = CONFIG_SIOC_GPIO_NUM, // CAM_PIN_SIOC,
|
||||
config = {
|
||||
.pin_pwdn = CONFIG_PWDN_GPIO_NUM, // CAM_PIN_PWDN,
|
||||
.pin_reset = CONFIG_RESET_GPIO_NUM, // CAM_PIN_RESET,
|
||||
.pin_xclk = CONFIG_XCLK_GPIO_NUM, // CAM_PIN_XCLK,
|
||||
.pin_sccb_sda = CONFIG_SIOD_GPIO_NUM, // CAM_PIN_SIOD,
|
||||
.pin_sccb_scl = CONFIG_SIOC_GPIO_NUM, // CAM_PIN_SIOC,
|
||||
|
||||
.pin_d7 = CONFIG_Y9_GPIO_NUM, /// CAM_PIN_D7,
|
||||
.pin_d6 = CONFIG_Y8_GPIO_NUM, /// CAM_PIN_D6,
|
||||
.pin_d5 = CONFIG_Y7_GPIO_NUM, // CAM_PIN_D5,
|
||||
.pin_d4 = CONFIG_Y6_GPIO_NUM, // CAM_PIN_D4,
|
||||
.pin_d3 = CONFIG_Y5_GPIO_NUM, // CAM_PIN_D3,
|
||||
.pin_d2 = CONFIG_Y4_GPIO_NUM, // CAM_PIN_D2,
|
||||
.pin_d1 = CONFIG_Y3_GPIO_NUM, // CAM_PIN_D1,
|
||||
.pin_d0 = CONFIG_Y2_GPIO_NUM, // CAM_PIN_D0,
|
||||
.pin_vsync = CONFIG_VSYNC_GPIO_NUM, // CAM_PIN_VSYNC,
|
||||
.pin_href = CONFIG_HREF_GPIO_NUM, // CAM_PIN_HREF,
|
||||
.pin_pclk = CONFIG_PCLK_GPIO_NUM, // CAM_PIN_PCLK,
|
||||
.pin_d7 = CONFIG_Y9_GPIO_NUM, /// CAM_PIN_D7,
|
||||
.pin_d6 = CONFIG_Y8_GPIO_NUM, /// CAM_PIN_D6,
|
||||
.pin_d5 = CONFIG_Y7_GPIO_NUM, // CAM_PIN_D5,
|
||||
.pin_d4 = CONFIG_Y6_GPIO_NUM, // CAM_PIN_D4,
|
||||
.pin_d3 = CONFIG_Y5_GPIO_NUM, // CAM_PIN_D3,
|
||||
.pin_d2 = CONFIG_Y4_GPIO_NUM, // CAM_PIN_D2,
|
||||
.pin_d1 = CONFIG_Y3_GPIO_NUM, // CAM_PIN_D1,
|
||||
.pin_d0 = CONFIG_Y2_GPIO_NUM, // CAM_PIN_D0,
|
||||
.pin_vsync = CONFIG_VSYNC_GPIO_NUM, // CAM_PIN_VSYNC,
|
||||
.pin_href = CONFIG_HREF_GPIO_NUM, // CAM_PIN_HREF,
|
||||
.pin_pclk = CONFIG_PCLK_GPIO_NUM, // CAM_PIN_PCLK,
|
||||
|
||||
.xclk_freq_hz = xclk_freq_hz, // Set in config
|
||||
.ledc_timer = LEDC_TIMER_0,
|
||||
.ledc_channel = LEDC_CHANNEL_0,
|
||||
.xclk_freq_hz = xclk_freq_hz, // Set in config
|
||||
.ledc_timer = LEDC_TIMER_0,
|
||||
.ledc_channel = LEDC_CHANNEL_0,
|
||||
|
||||
.pixel_format = PIXFORMAT_JPEG, // YUV422,GRAYSCALE,RGB565,JPEG
|
||||
.frame_size = FRAMESIZE_240X240, // QQVGA-UXGA, For ESP32, do not use sizes above QVGA when not JPEG. The performance of the ESP32-S series has improved a lot, but JPEG mode always gives better frame rates.
|
||||
.pixel_format = PIXFORMAT_JPEG, // YUV422,GRAYSCALE,RGB565,JPEG
|
||||
.frame_size = FRAMESIZE_240X240, // QQVGA-UXGA, For ESP32, do not use sizes above QVGA when not JPEG. The performance of the ESP32-S series has
|
||||
// improved a lot, but JPEG mode always gives better frame rates.
|
||||
|
||||
.jpeg_quality = 8, // 0-63, for OV series camera sensors, lower number means higher quality // Below 6 stability problems
|
||||
.fb_count = 2, // When jpeg mode is used, if fb_count more than one, the driver will work in continuous mode.
|
||||
.fb_location = CAMERA_FB_IN_DRAM,
|
||||
.grab_mode = CAMERA_GRAB_WHEN_EMPTY, // was CAMERA_GRAB_LATEST; new mode reduces frame skips at cost of minor latency
|
||||
};
|
||||
.jpeg_quality = 8, // 0-63, for OV series camera sensors, lower number means higher quality // Below 6 stability problems
|
||||
.fb_count = 2, // When jpeg mode is used, if fb_count more than one, the driver will work in continuous mode.
|
||||
.fb_location = CAMERA_FB_IN_DRAM,
|
||||
.grab_mode = CAMERA_GRAB_WHEN_EMPTY, // was CAMERA_GRAB_LATEST; new mode reduces frame skips at cost of minor latency
|
||||
};
|
||||
}
|
||||
|
||||
void CameraManager::setupCameraSensor()
|
||||
{
|
||||
ESP_LOGI(CAMERA_MANAGER_TAG, "Setting up camera sensor");
|
||||
ESP_LOGI(CAMERA_MANAGER_TAG, "Setting up camera sensor");
|
||||
|
||||
camera_sensor = esp_camera_sensor_get();
|
||||
// fixes corrupted jpegs, https://github.com/espressif/esp32-camera/issues/203
|
||||
// documentation https://www.uctronics.com/download/cam_module/OV2640DS.pdf
|
||||
camera_sensor->set_reg(
|
||||
camera_sensor, 0xff, 0xff,
|
||||
0x00); // banksel, here we're directly writing to the registers.
|
||||
// 0xFF==0x00 is the first bank, there's also 0xFF==0x01
|
||||
camera_sensor->set_reg(camera_sensor, 0xd3, 0xff, 5); // clock
|
||||
camera_sensor->set_brightness(camera_sensor, 2); // -2 to 2
|
||||
camera_sensor->set_contrast(camera_sensor, 2); // -2 to 2
|
||||
camera_sensor->set_saturation(camera_sensor, -2); // -2 to 2
|
||||
camera_sensor = esp_camera_sensor_get();
|
||||
// fixes corrupted jpegs, https://github.com/espressif/esp32-camera/issues/203
|
||||
// documentation https://www.uctronics.com/download/cam_module/OV2640DS.pdf
|
||||
camera_sensor->set_reg(camera_sensor, 0xff, 0xff,
|
||||
0x00); // banksel, here we're directly writing to the registers.
|
||||
// 0xFF==0x00 is the first bank, there's also 0xFF==0x01
|
||||
camera_sensor->set_reg(camera_sensor, 0xd3, 0xff, 5); // clock
|
||||
camera_sensor->set_brightness(camera_sensor, 2); // -2 to 2
|
||||
camera_sensor->set_contrast(camera_sensor, 2); // -2 to 2
|
||||
camera_sensor->set_saturation(camera_sensor, -2); // -2 to 2
|
||||
|
||||
// white balance control
|
||||
camera_sensor->set_whitebal(camera_sensor, 1); // 0 = disable , 1 = enable
|
||||
camera_sensor->set_awb_gain(camera_sensor, 0); // 0 = disable , 1 = enable
|
||||
camera_sensor->set_wb_mode(camera_sensor,
|
||||
0); // 0 to 4 - if awb_gain enabled (0 - Auto, 1 -
|
||||
// Sunny, 2 - Cloudy, 3 - Office, 4 - Home)
|
||||
// white balance control
|
||||
camera_sensor->set_whitebal(camera_sensor, 1); // 0 = disable , 1 = enable
|
||||
camera_sensor->set_awb_gain(camera_sensor, 0); // 0 = disable , 1 = enable
|
||||
camera_sensor->set_wb_mode(camera_sensor,
|
||||
0); // 0 to 4 - if awb_gain enabled (0 - Auto, 1 -
|
||||
// Sunny, 2 - Cloudy, 3 - Office, 4 - Home)
|
||||
|
||||
// controls the exposure
|
||||
camera_sensor->set_exposure_ctrl(camera_sensor,
|
||||
0); // 0 = disable , 1 = enable
|
||||
camera_sensor->set_aec2(camera_sensor, 0); // 0 = disable , 1 = enable
|
||||
camera_sensor->set_ae_level(camera_sensor, 0); // -2 to 2
|
||||
camera_sensor->set_aec_value(camera_sensor, 300); // 0 to 1200
|
||||
// controls the exposure
|
||||
camera_sensor->set_exposure_ctrl(camera_sensor,
|
||||
0); // 0 = disable , 1 = enable
|
||||
camera_sensor->set_aec2(camera_sensor, 0); // 0 = disable , 1 = enable
|
||||
camera_sensor->set_ae_level(camera_sensor, 0); // -2 to 2
|
||||
camera_sensor->set_aec_value(camera_sensor, 300); // 0 to 1200
|
||||
|
||||
// controls the gain
|
||||
camera_sensor->set_gain_ctrl(camera_sensor, 0); // 0 = disable , 1 = enable
|
||||
// controls the gain
|
||||
camera_sensor->set_gain_ctrl(camera_sensor, 0); // 0 = disable , 1 = enable
|
||||
|
||||
// automatic gain control gain, controls by how much the resulting image
|
||||
// should be amplified
|
||||
camera_sensor->set_agc_gain(camera_sensor, 2); // 0 to 30
|
||||
camera_sensor->set_gainceiling(camera_sensor, static_cast<gainceiling_t>(6)); // 0 to 6
|
||||
// automatic gain control gain, controls by how much the resulting image
|
||||
// should be amplified
|
||||
camera_sensor->set_agc_gain(camera_sensor, 2); // 0 to 30
|
||||
camera_sensor->set_gainceiling(camera_sensor, static_cast<gainceiling_t>(6)); // 0 to 6
|
||||
|
||||
// black and white pixel correction, averages the white and black spots
|
||||
camera_sensor->set_bpc(camera_sensor, 1); // 0 = disable , 1 = enable
|
||||
camera_sensor->set_wpc(camera_sensor, 1); // 0 = disable , 1 = enable
|
||||
// digital clamp white balance
|
||||
camera_sensor->set_dcw(camera_sensor, 0); // 0 = disable , 1 = enable
|
||||
// black and white pixel correction, averages the white and black spots
|
||||
camera_sensor->set_bpc(camera_sensor, 1); // 0 = disable , 1 = enable
|
||||
camera_sensor->set_wpc(camera_sensor, 1); // 0 = disable , 1 = enable
|
||||
// digital clamp white balance
|
||||
camera_sensor->set_dcw(camera_sensor, 0); // 0 = disable , 1 = enable
|
||||
|
||||
// gamma correction
|
||||
camera_sensor->set_raw_gma(
|
||||
camera_sensor,
|
||||
1); // 0 = disable , 1 = enable (makes much lighter and noisy)
|
||||
// gamma correction
|
||||
camera_sensor->set_raw_gma(camera_sensor,
|
||||
1); // 0 = disable , 1 = enable (makes much lighter and noisy)
|
||||
|
||||
camera_sensor->set_lenc(camera_sensor, 0); // 0 = disable , 1 = enable // 0 =
|
||||
// disable , 1 = enable
|
||||
camera_sensor->set_lenc(camera_sensor, 0); // 0 = disable , 1 = enable // 0 =
|
||||
// disable , 1 = enable
|
||||
|
||||
camera_sensor->set_colorbar(camera_sensor, 0); // 0 = disable , 1 = enable
|
||||
camera_sensor->set_colorbar(camera_sensor, 0); // 0 = disable , 1 = enable
|
||||
|
||||
camera_sensor->set_special_effect(
|
||||
camera_sensor,
|
||||
2); // 0 to 6 (0 - No Effect, 1 - Negative, 2 - Grayscale, 3 - Red Tint,
|
||||
// 4 - Green Tint, 5 - Blue Tint, 6 - Sepia)
|
||||
camera_sensor->set_special_effect(camera_sensor,
|
||||
2); // 0 to 6 (0 - No Effect, 1 - Negative, 2 - Grayscale, 3 - Red Tint,
|
||||
// 4 - Green Tint, 5 - Blue Tint, 6 - Sepia)
|
||||
|
||||
// it gets overriden somewhere somehow
|
||||
camera_sensor->set_framesize(camera_sensor, FRAMESIZE_240X240);
|
||||
ESP_LOGI(CAMERA_MANAGER_TAG, "Setting up camera sensor done");
|
||||
// it gets overriden somewhere somehow
|
||||
camera_sensor->set_framesize(camera_sensor, FRAMESIZE_240X240);
|
||||
ESP_LOGI(CAMERA_MANAGER_TAG, "Setting up camera sensor done");
|
||||
}
|
||||
|
||||
bool CameraManager::setupCamera()
|
||||
{
|
||||
ESP_LOGI(CAMERA_MANAGER_TAG, "Setting up camera pinout");
|
||||
this->setupCameraPinout();
|
||||
ESP_LOGI(CAMERA_MANAGER_TAG, "Initializing camera...");
|
||||
ESP_LOGI(CAMERA_MANAGER_TAG, "Setting up camera pinout");
|
||||
this->setupCameraPinout();
|
||||
ESP_LOGI(CAMERA_MANAGER_TAG, "Initializing camera...");
|
||||
|
||||
if (auto const hasCameraBeenInitialized = esp_camera_init(&config); hasCameraBeenInitialized == ESP_OK)
|
||||
{
|
||||
ESP_LOGI(CAMERA_MANAGER_TAG, "Camera initialized: %s \r\n",
|
||||
esp_err_to_name(hasCameraBeenInitialized));
|
||||
if (auto const hasCameraBeenInitialized = esp_camera_init(&config); hasCameraBeenInitialized == ESP_OK)
|
||||
{
|
||||
ESP_LOGI(CAMERA_MANAGER_TAG, "Camera initialized: %s \r\n", esp_err_to_name(hasCameraBeenInitialized));
|
||||
|
||||
constexpr auto event = SystemEvent{EventSource::CAMERA, CameraState_e::Camera_Success};
|
||||
xQueueSend(this->eventQueue, &event, 10);
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGE(CAMERA_MANAGER_TAG, "Camera initialization failed with error: %s \r\n",
|
||||
esp_err_to_name(hasCameraBeenInitialized));
|
||||
ESP_LOGE(CAMERA_MANAGER_TAG, "Camera most likely not seated properly in the socket. "
|
||||
"Please "
|
||||
"fix the "
|
||||
"camera and reboot the device.\r\n");
|
||||
constexpr auto event = SystemEvent{EventSource::CAMERA, CameraState_e::Camera_Error};
|
||||
xQueueSend(this->eventQueue, &event, 10);
|
||||
return false;
|
||||
}
|
||||
constexpr auto event = SystemEvent{EventSource::CAMERA, CameraState_e::Camera_Success};
|
||||
xQueueSend(this->eventQueue, &event, 10);
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGE(CAMERA_MANAGER_TAG, "Camera initialization failed with error: %s \r\n", esp_err_to_name(hasCameraBeenInitialized));
|
||||
ESP_LOGE(CAMERA_MANAGER_TAG,
|
||||
"Camera most likely not seated properly in the socket. "
|
||||
"Please "
|
||||
"fix the "
|
||||
"camera and reboot the device.\r\n");
|
||||
constexpr auto event = SystemEvent{EventSource::CAMERA, CameraState_e::Camera_Error};
|
||||
xQueueSend(this->eventQueue, &event, 10);
|
||||
return false;
|
||||
}
|
||||
|
||||
#if CONFIG_GENERAL_INCLUDE_UVC_MODE
|
||||
const auto temp_sensor = esp_camera_sensor_get();
|
||||
const auto temp_sensor = esp_camera_sensor_get();
|
||||
|
||||
// Thanks to lick_it, we discovered that OV5640 likes to overheat when
|
||||
// running at higher than usual xclk frequencies.
|
||||
// Hence, why we're limiting the faster ones for OV2640
|
||||
if (const auto camera_id = temp_sensor->id.PID; camera_id == OV5640_PID)
|
||||
{
|
||||
config.xclk_freq_hz = OV5640_XCLK_FREQ_HZ;
|
||||
esp_camera_deinit();
|
||||
esp_camera_init(&config);
|
||||
}
|
||||
// Thanks to lick_it, we discovered that OV5640 likes to overheat when
|
||||
// running at higher than usual xclk frequencies.
|
||||
// Hence, why we're limiting the faster ones for OV2640
|
||||
if (const auto camera_id = temp_sensor->id.PID; camera_id == OV5640_PID)
|
||||
{
|
||||
config.xclk_freq_hz = OV5640_XCLK_FREQ_HZ;
|
||||
esp_camera_deinit();
|
||||
esp_camera_init(&config);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
this->setupCameraSensor();
|
||||
return true;
|
||||
this->setupCameraSensor();
|
||||
return true;
|
||||
}
|
||||
|
||||
void CameraManager::loadConfigData()
|
||||
{
|
||||
ESP_LOGD(CAMERA_MANAGER_TAG, "Loading camera config data");
|
||||
CameraConfig_t cameraConfig = projectConfig->getCameraConfig();
|
||||
this->setHFlip(cameraConfig.href);
|
||||
this->setVFlip(cameraConfig.vflip);
|
||||
this->setCameraResolution(static_cast<framesize_t>(cameraConfig.framesize));
|
||||
camera_sensor->set_quality(camera_sensor, cameraConfig.quality);
|
||||
camera_sensor->set_agc_gain(camera_sensor, cameraConfig.brightness);
|
||||
ESP_LOGD(CAMERA_MANAGER_TAG, "Loading camera config data done");
|
||||
ESP_LOGD(CAMERA_MANAGER_TAG, "Loading camera config data");
|
||||
CameraConfig_t cameraConfig = projectConfig->getCameraConfig();
|
||||
this->setHFlip(cameraConfig.href);
|
||||
this->setVFlip(cameraConfig.vflip);
|
||||
this->setCameraResolution(static_cast<framesize_t>(cameraConfig.framesize));
|
||||
camera_sensor->set_quality(camera_sensor, cameraConfig.quality);
|
||||
camera_sensor->set_agc_gain(camera_sensor, cameraConfig.brightness);
|
||||
ESP_LOGD(CAMERA_MANAGER_TAG, "Loading camera config data done");
|
||||
}
|
||||
|
||||
int CameraManager::setCameraResolution(const framesize_t frameSize)
|
||||
{
|
||||
if (camera_sensor->pixformat == PIXFORMAT_JPEG)
|
||||
{
|
||||
return camera_sensor->set_framesize(camera_sensor, frameSize);
|
||||
}
|
||||
return -1;
|
||||
if (camera_sensor->pixformat == PIXFORMAT_JPEG)
|
||||
{
|
||||
return camera_sensor->set_framesize(camera_sensor, frameSize);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int CameraManager::setVFlip(const int direction)
|
||||
{
|
||||
return camera_sensor->set_vflip(camera_sensor, direction);
|
||||
return camera_sensor->set_vflip(camera_sensor, direction);
|
||||
}
|
||||
|
||||
int CameraManager::setHFlip(const int direction)
|
||||
{
|
||||
return camera_sensor->set_hmirror(camera_sensor, direction);
|
||||
return camera_sensor->set_hmirror(camera_sensor, direction);
|
||||
}
|
||||
|
||||
int CameraManager::setVieWindow(int offsetX,
|
||||
int offsetY,
|
||||
int outputX,
|
||||
int outputY)
|
||||
int CameraManager::setVieWindow(int offsetX, int offsetY, int outputX, int outputY)
|
||||
{
|
||||
|
||||
// todo safariMonkey made a PoC, implement it here
|
||||
return 0;
|
||||
// todo safariMonkey made a PoC, implement it here
|
||||
return 0;
|
||||
}
|
||||
@@ -2,40 +2,40 @@
|
||||
#ifndef CAMERAMANAGER_HPP
|
||||
#define CAMERAMANAGER_HPP
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_camera.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_camera.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_psram.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
|
||||
#include <StateManager.hpp>
|
||||
#include <ProjectConfig.hpp>
|
||||
#include <StateManager.hpp>
|
||||
|
||||
#define OV5640_XCLK_FREQ_HZ CONFIG_CAMERA_WIFI_XCLK_FREQ
|
||||
|
||||
class CameraManager
|
||||
{
|
||||
private:
|
||||
sensor_t *camera_sensor;
|
||||
std::shared_ptr<ProjectConfig> projectConfig;
|
||||
QueueHandle_t eventQueue;
|
||||
camera_config_t config;
|
||||
private:
|
||||
sensor_t* camera_sensor;
|
||||
std::shared_ptr<ProjectConfig> projectConfig;
|
||||
QueueHandle_t eventQueue;
|
||||
camera_config_t config;
|
||||
|
||||
public:
|
||||
CameraManager(std::shared_ptr<ProjectConfig> projectConfig, QueueHandle_t eventQueue);
|
||||
int setCameraResolution(framesize_t frameSize);
|
||||
bool setupCamera();
|
||||
int setVFlip(int direction);
|
||||
int setHFlip(int direction);
|
||||
int setVieWindow(int offsetX, int offsetY, int outputX, int outputY);
|
||||
public:
|
||||
CameraManager(std::shared_ptr<ProjectConfig> projectConfig, QueueHandle_t eventQueue);
|
||||
int setCameraResolution(framesize_t frameSize);
|
||||
bool setupCamera();
|
||||
int setVFlip(int direction);
|
||||
int setHFlip(int direction);
|
||||
int setVieWindow(int offsetX, int offsetY, int outputX, int outputY);
|
||||
|
||||
private:
|
||||
void loadConfigData();
|
||||
void setupCameraPinout();
|
||||
void setupCameraSensor();
|
||||
private:
|
||||
void loadConfigData();
|
||||
void setupCameraPinout();
|
||||
void setupCameraSensor();
|
||||
};
|
||||
|
||||
#endif // CAMERAMANAGER_HPP
|
||||
#endif // CAMERAMANAGER_HPP
|
||||
@@ -30,140 +30,117 @@ std::unordered_map<std::string, CommandType> commandTypeMap = {
|
||||
{"get_who_am_i", CommandType::GET_WHO_AM_I},
|
||||
};
|
||||
|
||||
std::function<CommandResult()> CommandManager::createCommand(const CommandType type, const nlohmann::json &json) const
|
||||
std::function<CommandResult()> CommandManager::createCommand(const CommandType type, const nlohmann::json& json) const
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case CommandType::PING:
|
||||
return {PingCommand};
|
||||
case CommandType::PAUSE:
|
||||
return [json]
|
||||
{ return PauseCommand(json); };
|
||||
case CommandType::UPDATE_OTA_CREDENTIALS:
|
||||
return [this, json]
|
||||
{ return updateOTACredentialsCommand(this->registry, json); };
|
||||
case CommandType::SET_WIFI:
|
||||
return [this, json]
|
||||
{ return setWiFiCommand(this->registry, json); };
|
||||
case CommandType::UPDATE_WIFI:
|
||||
return [this, json]
|
||||
{ return updateWiFiCommand(this->registry, json); };
|
||||
case CommandType::UPDATE_AP_WIFI:
|
||||
return [this, json]
|
||||
{ return updateAPWiFiCommand(this->registry, json); };
|
||||
case CommandType::DELETE_NETWORK:
|
||||
return [this, json]
|
||||
{ return deleteWiFiCommand(this->registry, json); };
|
||||
case CommandType::SET_MDNS:
|
||||
return [this, json]
|
||||
{ return setMDNSCommand(this->registry, json); };
|
||||
case CommandType::GET_MDNS_NAME:
|
||||
return [this]
|
||||
{ return getMDNSNameCommand(this->registry); };
|
||||
case CommandType::UPDATE_CAMERA:
|
||||
return [this, json]
|
||||
{ return updateCameraCommand(this->registry, json); };
|
||||
case CommandType::GET_CONFIG:
|
||||
return [this]
|
||||
{ return getConfigCommand(this->registry); };
|
||||
case CommandType::SAVE_CONFIG:
|
||||
return [this]
|
||||
{ return saveConfigCommand(this->registry); };
|
||||
case CommandType::RESET_CONFIG:
|
||||
return [this, json]
|
||||
{ return resetConfigCommand(this->registry, json); };
|
||||
case CommandType::RESTART_DEVICE:
|
||||
return restartDeviceCommand;
|
||||
case CommandType::SCAN_NETWORKS:
|
||||
return [this, json]
|
||||
{ return scanNetworksCommand(this->registry, json); };
|
||||
case CommandType::START_STREAMING:
|
||||
return startStreamingCommand;
|
||||
case CommandType::GET_WIFI_STATUS:
|
||||
return [this]
|
||||
{ return getWiFiStatusCommand(this->registry); };
|
||||
case CommandType::CONNECT_WIFI:
|
||||
return [this]
|
||||
{ return connectWiFiCommand(this->registry); };
|
||||
case CommandType::SWITCH_MODE:
|
||||
return [this, json]
|
||||
{ return switchModeCommand(this->registry, json); };
|
||||
case CommandType::GET_DEVICE_MODE:
|
||||
return [this]
|
||||
{ return getDeviceModeCommand(this->registry); };
|
||||
case CommandType::SET_LED_DUTY_CYCLE:
|
||||
return [this, json]
|
||||
{ return updateLEDDutyCycleCommand(this->registry, json); };
|
||||
case CommandType::GET_LED_DUTY_CYCLE:
|
||||
return [this]
|
||||
{ return getLEDDutyCycleCommand(this->registry); };
|
||||
case CommandType::GET_SERIAL:
|
||||
return [this]
|
||||
{ return getSerialNumberCommand(this->registry); };
|
||||
case CommandType::GET_LED_CURRENT:
|
||||
return [this]
|
||||
{ return getLEDCurrentCommand(this->registry); };
|
||||
case CommandType::GET_BATTERY_STATUS:
|
||||
return [this]
|
||||
{ return getBatteryStatusCommand(this->registry); };
|
||||
case CommandType::GET_WHO_AM_I:
|
||||
return [this]
|
||||
{ return getInfoCommand(this->registry); };
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
switch (type)
|
||||
{
|
||||
case CommandType::PING:
|
||||
return {PingCommand};
|
||||
case CommandType::PAUSE:
|
||||
return [json] { return PauseCommand(json); };
|
||||
case CommandType::UPDATE_OTA_CREDENTIALS:
|
||||
return [this, json] { return updateOTACredentialsCommand(this->registry, json); };
|
||||
case CommandType::SET_WIFI:
|
||||
return [this, json] { return setWiFiCommand(this->registry, json); };
|
||||
case CommandType::UPDATE_WIFI:
|
||||
return [this, json] { return updateWiFiCommand(this->registry, json); };
|
||||
case CommandType::UPDATE_AP_WIFI:
|
||||
return [this, json] { return updateAPWiFiCommand(this->registry, json); };
|
||||
case CommandType::DELETE_NETWORK:
|
||||
return [this, json] { return deleteWiFiCommand(this->registry, json); };
|
||||
case CommandType::SET_MDNS:
|
||||
return [this, json] { return setMDNSCommand(this->registry, json); };
|
||||
case CommandType::GET_MDNS_NAME:
|
||||
return [this] { return getMDNSNameCommand(this->registry); };
|
||||
case CommandType::UPDATE_CAMERA:
|
||||
return [this, json] { return updateCameraCommand(this->registry, json); };
|
||||
case CommandType::GET_CONFIG:
|
||||
return [this] { return getConfigCommand(this->registry); };
|
||||
case CommandType::SAVE_CONFIG:
|
||||
return [this] { return saveConfigCommand(this->registry); };
|
||||
case CommandType::RESET_CONFIG:
|
||||
return [this, json] { return resetConfigCommand(this->registry, json); };
|
||||
case CommandType::RESTART_DEVICE:
|
||||
return restartDeviceCommand;
|
||||
case CommandType::SCAN_NETWORKS:
|
||||
return [this, json] { return scanNetworksCommand(this->registry, json); };
|
||||
case CommandType::START_STREAMING:
|
||||
return startStreamingCommand;
|
||||
case CommandType::GET_WIFI_STATUS:
|
||||
return [this] { return getWiFiStatusCommand(this->registry); };
|
||||
case CommandType::CONNECT_WIFI:
|
||||
return [this] { return connectWiFiCommand(this->registry); };
|
||||
case CommandType::SWITCH_MODE:
|
||||
return [this, json] { return switchModeCommand(this->registry, json); };
|
||||
case CommandType::GET_DEVICE_MODE:
|
||||
return [this] { return getDeviceModeCommand(this->registry); };
|
||||
case CommandType::SET_LED_DUTY_CYCLE:
|
||||
return [this, json] { return updateLEDDutyCycleCommand(this->registry, json); };
|
||||
case CommandType::GET_LED_DUTY_CYCLE:
|
||||
return [this] { return getLEDDutyCycleCommand(this->registry); };
|
||||
case CommandType::GET_SERIAL:
|
||||
return [this] { return getSerialNumberCommand(this->registry); };
|
||||
case CommandType::GET_LED_CURRENT:
|
||||
return [this] { return getLEDCurrentCommand(this->registry); };
|
||||
case CommandType::GET_BATTERY_STATUS:
|
||||
return [this] { return getBatteryStatusCommand(this->registry); };
|
||||
case CommandType::GET_WHO_AM_I:
|
||||
return [this] { return getInfoCommand(this->registry); };
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
CommandManagerResponse CommandManager::executeFromJson(const std::string_view json) const
|
||||
{
|
||||
if (!nlohmann::json::accept(json))
|
||||
{
|
||||
return CommandManagerResponse(nlohmann::json{{"error", "Initial JSON Parse - Invalid JSON"}});
|
||||
}
|
||||
|
||||
nlohmann::json parsedJson = nlohmann::json::parse(json);
|
||||
if (!parsedJson.contains("commands") || !parsedJson["commands"].is_array() || parsedJson["commands"].empty())
|
||||
{
|
||||
return CommandManagerResponse(CommandResult::getErrorResult("Commands missing"));
|
||||
}
|
||||
|
||||
nlohmann::json results = nlohmann::json::array();
|
||||
|
||||
for (auto &commandObject : parsedJson["commands"].items())
|
||||
{
|
||||
auto commandData = commandObject.value();
|
||||
if (!commandData.contains("command"))
|
||||
if (!nlohmann::json::accept(json))
|
||||
{
|
||||
return CommandManagerResponse({{"command", "Unknown command"}, {"error", "Missing command type"}});
|
||||
return CommandManagerResponse(nlohmann::json{{"error", "Initial JSON Parse - Invalid JSON"}});
|
||||
}
|
||||
|
||||
const auto commandName = commandData["command"].get<std::string>();
|
||||
if (!commandTypeMap.contains(commandName))
|
||||
nlohmann::json parsedJson = nlohmann::json::parse(json);
|
||||
if (!parsedJson.contains("commands") || !parsedJson["commands"].is_array() || parsedJson["commands"].empty())
|
||||
{
|
||||
return CommandManagerResponse({{"command", commandName}, {"error", "Unknown command"}});
|
||||
return CommandManagerResponse(CommandResult::getErrorResult("Commands missing"));
|
||||
}
|
||||
|
||||
const auto commandType = commandTypeMap.at(commandName);
|
||||
const auto commandPayload = commandData.contains("data") ? commandData["data"] : nlohmann::json::object();
|
||||
nlohmann::json results = nlohmann::json::array();
|
||||
|
||||
auto command = createCommand(commandType, commandPayload);
|
||||
results.push_back({
|
||||
{"command", commandName},
|
||||
{"result", command()},
|
||||
});
|
||||
}
|
||||
auto response = nlohmann::json{{"results", results}};
|
||||
return CommandManagerResponse(response);
|
||||
for (auto& commandObject : parsedJson["commands"].items())
|
||||
{
|
||||
auto commandData = commandObject.value();
|
||||
if (!commandData.contains("command"))
|
||||
{
|
||||
return CommandManagerResponse({{"command", "Unknown command"}, {"error", "Missing command type"}});
|
||||
}
|
||||
|
||||
const auto commandName = commandData["command"].get<std::string>();
|
||||
if (!commandTypeMap.contains(commandName))
|
||||
{
|
||||
return CommandManagerResponse({{"command", commandName}, {"error", "Unknown command"}});
|
||||
}
|
||||
|
||||
const auto commandType = commandTypeMap.at(commandName);
|
||||
const auto commandPayload = commandData.contains("data") ? commandData["data"] : nlohmann::json::object();
|
||||
|
||||
auto command = createCommand(commandType, commandPayload);
|
||||
results.push_back({
|
||||
{"command", commandName},
|
||||
{"result", command()},
|
||||
});
|
||||
}
|
||||
auto response = nlohmann::json{{"results", results}};
|
||||
return CommandManagerResponse(response);
|
||||
}
|
||||
|
||||
CommandManagerResponse CommandManager::executeFromType(const CommandType type, const std::string_view json) const
|
||||
{
|
||||
const auto command = createCommand(type, json);
|
||||
const auto command = createCommand(type, json);
|
||||
|
||||
if (command == nullptr)
|
||||
{
|
||||
return CommandManagerResponse({{"command", type}, {"error", "Unknown command"}});
|
||||
}
|
||||
if (command == nullptr)
|
||||
{
|
||||
return CommandManagerResponse({{"command", type}, {"error", "Unknown command"}});
|
||||
}
|
||||
|
||||
return CommandManagerResponse(nlohmann::json{{"result", command()}});
|
||||
return CommandManagerResponse(nlohmann::json{{"result", command()}});
|
||||
}
|
||||
@@ -1,66 +1,66 @@
|
||||
#ifndef COMMANDMANAGER_HPP
|
||||
#define COMMANDMANAGER_HPP
|
||||
|
||||
#include <ProjectConfig.hpp>
|
||||
#include <CameraManager.hpp>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <ProjectConfig.hpp>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <nlohmann-json.hpp>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include "CommandResult.hpp"
|
||||
#include "CommandSchema.hpp"
|
||||
#include "DependencyRegistry.hpp"
|
||||
#include "commands/simple_commands.hpp"
|
||||
#include "commands/camera_commands.hpp"
|
||||
#include "commands/config_commands.hpp"
|
||||
#include "commands/mdns_commands.hpp"
|
||||
#include "commands/wifi_commands.hpp"
|
||||
#include "commands/device_commands.hpp"
|
||||
#include "commands/mdns_commands.hpp"
|
||||
#include "commands/scan_commands.hpp"
|
||||
#include <nlohmann-json.hpp>
|
||||
#include "commands/simple_commands.hpp"
|
||||
#include "commands/wifi_commands.hpp"
|
||||
|
||||
enum class CommandType
|
||||
{
|
||||
None,
|
||||
PING,
|
||||
PAUSE,
|
||||
SET_WIFI,
|
||||
UPDATE_OTA_CREDENTIALS,
|
||||
UPDATE_WIFI,
|
||||
DELETE_NETWORK,
|
||||
UPDATE_AP_WIFI,
|
||||
SET_MDNS,
|
||||
GET_MDNS_NAME,
|
||||
UPDATE_CAMERA,
|
||||
SAVE_CONFIG,
|
||||
GET_CONFIG,
|
||||
RESET_CONFIG,
|
||||
RESTART_DEVICE,
|
||||
SCAN_NETWORKS,
|
||||
START_STREAMING,
|
||||
GET_WIFI_STATUS,
|
||||
CONNECT_WIFI,
|
||||
SWITCH_MODE,
|
||||
GET_DEVICE_MODE,
|
||||
SET_LED_DUTY_CYCLE,
|
||||
GET_LED_DUTY_CYCLE,
|
||||
GET_SERIAL,
|
||||
GET_LED_CURRENT,
|
||||
GET_BATTERY_STATUS,
|
||||
GET_WHO_AM_I,
|
||||
None,
|
||||
PING,
|
||||
PAUSE,
|
||||
SET_WIFI,
|
||||
UPDATE_OTA_CREDENTIALS,
|
||||
UPDATE_WIFI,
|
||||
DELETE_NETWORK,
|
||||
UPDATE_AP_WIFI,
|
||||
SET_MDNS,
|
||||
GET_MDNS_NAME,
|
||||
UPDATE_CAMERA,
|
||||
SAVE_CONFIG,
|
||||
GET_CONFIG,
|
||||
RESET_CONFIG,
|
||||
RESTART_DEVICE,
|
||||
SCAN_NETWORKS,
|
||||
START_STREAMING,
|
||||
GET_WIFI_STATUS,
|
||||
CONNECT_WIFI,
|
||||
SWITCH_MODE,
|
||||
GET_DEVICE_MODE,
|
||||
SET_LED_DUTY_CYCLE,
|
||||
GET_LED_DUTY_CYCLE,
|
||||
GET_SERIAL,
|
||||
GET_LED_CURRENT,
|
||||
GET_BATTERY_STATUS,
|
||||
GET_WHO_AM_I,
|
||||
};
|
||||
|
||||
class CommandManager
|
||||
{
|
||||
std::shared_ptr<DependencyRegistry> registry;
|
||||
std::shared_ptr<DependencyRegistry> registry;
|
||||
|
||||
public:
|
||||
explicit CommandManager(const std::shared_ptr<DependencyRegistry> &DependencyRegistry) : registry(DependencyRegistry) {};
|
||||
std::function<CommandResult()> createCommand(const CommandType type, const nlohmann::json &json) const;
|
||||
public:
|
||||
explicit CommandManager(const std::shared_ptr<DependencyRegistry>& DependencyRegistry) : registry(DependencyRegistry) {};
|
||||
std::function<CommandResult()> createCommand(const CommandType type, const nlohmann::json& json) const;
|
||||
|
||||
CommandManagerResponse executeFromJson(std::string_view json) const;
|
||||
CommandManagerResponse executeFromType(CommandType type, std::string_view json) const;
|
||||
CommandManagerResponse executeFromJson(std::string_view json) const;
|
||||
CommandManagerResponse executeFromType(CommandType type, std::string_view json) const;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,24 +1,24 @@
|
||||
#include "CommandResult.hpp"
|
||||
|
||||
void to_json(nlohmann::json &j, const CommandResult &result)
|
||||
void to_json(nlohmann::json& j, const CommandResult& result)
|
||||
{
|
||||
j = nlohmann::json{{"status", result.isSuccess() ? "success" : "error"}, {"data", result.getData()}};
|
||||
j = nlohmann::json{{"status", result.isSuccess() ? "success" : "error"}, {"data", result.getData()}};
|
||||
}
|
||||
|
||||
// defined only for interface compatibility, should not be used directly
|
||||
void from_json(const nlohmann::json &j, CommandResult &result)
|
||||
void from_json(const nlohmann::json& j, CommandResult& result)
|
||||
{
|
||||
auto message = j.at("message");
|
||||
j.at("status") == "success" ? result = CommandResult::getSuccessResult(message) : result = CommandResult::getErrorResult(message);
|
||||
auto message = j.at("message");
|
||||
j.at("status") == "success" ? result = CommandResult::getSuccessResult(message) : result = CommandResult::getErrorResult(message);
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const CommandManagerResponse &result)
|
||||
void to_json(nlohmann::json& j, const CommandManagerResponse& result)
|
||||
{
|
||||
j = result.getData();
|
||||
j = result.getData();
|
||||
}
|
||||
|
||||
// defined only for interface compatibility, should not be used directly
|
||||
void from_json(const nlohmann::json &j, CommandManagerResponse &result)
|
||||
void from_json(const nlohmann::json& j, CommandManagerResponse& result)
|
||||
{
|
||||
result = CommandManagerResponse(j.at("result"));
|
||||
result = CommandManagerResponse(j.at("result"));
|
||||
}
|
||||
@@ -2,58 +2,67 @@
|
||||
#ifndef COMMAND_RESULT
|
||||
#define COMMAND_RESULT
|
||||
|
||||
#include <format>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <format>
|
||||
#include <nlohmann-json.hpp>
|
||||
#include <string>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
class CommandResult
|
||||
{
|
||||
public:
|
||||
enum class Status
|
||||
{
|
||||
SUCCESS,
|
||||
FAILURE,
|
||||
};
|
||||
public:
|
||||
enum class Status
|
||||
{
|
||||
SUCCESS,
|
||||
FAILURE,
|
||||
};
|
||||
|
||||
private:
|
||||
nlohmann::json data;
|
||||
Status status;
|
||||
private:
|
||||
nlohmann::json data;
|
||||
Status status;
|
||||
|
||||
public:
|
||||
CommandResult(nlohmann::json data, const Status status) : data(data), status(status) {}
|
||||
public:
|
||||
CommandResult(nlohmann::json data, const Status status) : data(data), status(status) {}
|
||||
|
||||
bool isSuccess() const { return status == Status::SUCCESS; }
|
||||
bool isSuccess() const
|
||||
{
|
||||
return status == Status::SUCCESS;
|
||||
}
|
||||
|
||||
static CommandResult getSuccessResult(nlohmann::json message)
|
||||
{
|
||||
return CommandResult(message, Status::SUCCESS);
|
||||
}
|
||||
static CommandResult getSuccessResult(nlohmann::json message)
|
||||
{
|
||||
return CommandResult(message, Status::SUCCESS);
|
||||
}
|
||||
|
||||
static CommandResult getErrorResult(nlohmann::json message)
|
||||
{
|
||||
return CommandResult(message, Status::FAILURE);
|
||||
}
|
||||
static CommandResult getErrorResult(nlohmann::json message)
|
||||
{
|
||||
return CommandResult(message, Status::FAILURE);
|
||||
}
|
||||
|
||||
nlohmann::json getData() const { return this->data; }
|
||||
nlohmann::json getData() const
|
||||
{
|
||||
return this->data;
|
||||
}
|
||||
};
|
||||
|
||||
void to_json(nlohmann::json &j, const CommandResult &result);
|
||||
void from_json(const nlohmann::json &j, CommandResult &result);
|
||||
void to_json(nlohmann::json& j, const CommandResult& result);
|
||||
void from_json(const nlohmann::json& j, CommandResult& result);
|
||||
|
||||
class CommandManagerResponse
|
||||
{
|
||||
private:
|
||||
nlohmann::json data;
|
||||
private:
|
||||
nlohmann::json data;
|
||||
|
||||
public:
|
||||
CommandManagerResponse(nlohmann::json data) : data(data) {}
|
||||
nlohmann::json getData() const { return this->data; }
|
||||
public:
|
||||
CommandManagerResponse(nlohmann::json data) : data(data) {}
|
||||
nlohmann::json getData() const
|
||||
{
|
||||
return this->data;
|
||||
}
|
||||
};
|
||||
|
||||
void to_json(nlohmann::json &j, const CommandManagerResponse &result);
|
||||
void from_json(const nlohmann::json &j, CommandManagerResponse &result);
|
||||
void to_json(nlohmann::json& j, const CommandManagerResponse& result);
|
||||
void from_json(const nlohmann::json& j, CommandManagerResponse& result);
|
||||
|
||||
#endif
|
||||
@@ -1,81 +1,82 @@
|
||||
#include "CommandSchema.hpp"
|
||||
|
||||
void to_json(nlohmann::json &j, const UpdateWifiPayload &payload)
|
||||
void to_json(nlohmann::json& j, const UpdateWifiPayload& payload)
|
||||
{
|
||||
j = nlohmann::json{{"name", payload.name}, {"ssid", payload.ssid}, {"password", payload.password}, {"channel", payload.channel}, {"power", payload.power}};
|
||||
j = nlohmann::json{{"name", payload.name}, {"ssid", payload.ssid}, {"password", payload.password}, {"channel", payload.channel}, {"power", payload.power}};
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, UpdateWifiPayload &payload)
|
||||
void from_json(const nlohmann::json& j, UpdateWifiPayload& payload)
|
||||
{
|
||||
payload.name = j.at("name").get<std::string>();
|
||||
if (j.contains("ssid"))
|
||||
{
|
||||
payload.ssid = j.at("ssid").get<std::string>();
|
||||
}
|
||||
payload.name = j.at("name").get<std::string>();
|
||||
if (j.contains("ssid"))
|
||||
{
|
||||
payload.ssid = j.at("ssid").get<std::string>();
|
||||
}
|
||||
|
||||
if (j.contains("password"))
|
||||
{
|
||||
payload.password = j.at("password").get<std::string>();
|
||||
}
|
||||
if (j.contains("password"))
|
||||
{
|
||||
payload.password = j.at("password").get<std::string>();
|
||||
}
|
||||
|
||||
if (j.contains("channel"))
|
||||
{
|
||||
payload.channel = j.at("channel").get<uint8_t>();
|
||||
}
|
||||
if (j.contains("channel"))
|
||||
{
|
||||
payload.channel = j.at("channel").get<uint8_t>();
|
||||
}
|
||||
|
||||
if (j.contains("power"))
|
||||
{
|
||||
payload.power = j.at("power").get<uint8_t>();
|
||||
}
|
||||
if (j.contains("power"))
|
||||
{
|
||||
payload.power = j.at("power").get<uint8_t>();
|
||||
}
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const UpdateAPWiFiPayload &payload)
|
||||
void to_json(nlohmann::json& j, const UpdateAPWiFiPayload& payload)
|
||||
{
|
||||
j = nlohmann::json{{"ssid", payload.ssid}, {"password", payload.password}, {"channel", payload.channel}};
|
||||
j = nlohmann::json{{"ssid", payload.ssid}, {"password", payload.password}, {"channel", payload.channel}};
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, UpdateAPWiFiPayload &payload)
|
||||
void from_json(const nlohmann::json& j, UpdateAPWiFiPayload& payload)
|
||||
{
|
||||
if (j.contains("ssid"))
|
||||
{
|
||||
payload.ssid = j.at("ssid").get<std::string>();
|
||||
}
|
||||
if (j.contains("ssid"))
|
||||
{
|
||||
payload.ssid = j.at("ssid").get<std::string>();
|
||||
}
|
||||
|
||||
if (j.contains("password"))
|
||||
{
|
||||
payload.password = j.at("password").get<std::string>();
|
||||
}
|
||||
if (j.contains("channel"))
|
||||
{
|
||||
payload.channel = j.at("channel").get<uint8_t>();
|
||||
}
|
||||
if (j.contains("password"))
|
||||
{
|
||||
payload.password = j.at("password").get<std::string>();
|
||||
}
|
||||
if (j.contains("channel"))
|
||||
{
|
||||
payload.channel = j.at("channel").get<uint8_t>();
|
||||
}
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const UpdateCameraConfigPayload &payload)
|
||||
void to_json(nlohmann::json& j, const UpdateCameraConfigPayload& payload)
|
||||
{
|
||||
j = nlohmann::json{{"vflip", payload.vflip}, {"href", payload.href}, {"framesize", payload.framesize}, {"quality", payload.quality}, {"brightness", payload.brightness}};
|
||||
j = nlohmann::json{
|
||||
{"vflip", payload.vflip}, {"href", payload.href}, {"framesize", payload.framesize}, {"quality", payload.quality}, {"brightness", payload.brightness}};
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, UpdateCameraConfigPayload &payload)
|
||||
void from_json(const nlohmann::json& j, UpdateCameraConfigPayload& payload)
|
||||
{
|
||||
if (j.contains("vflip"))
|
||||
{
|
||||
payload.vflip = j.at("vflip").get<uint8_t>();
|
||||
}
|
||||
if (j.contains("href"))
|
||||
{
|
||||
payload.href = j.at("href").get<uint8_t>();
|
||||
}
|
||||
if (j.contains("framesize"))
|
||||
{
|
||||
payload.framesize = j.at("framesize").get<uint8_t>();
|
||||
}
|
||||
if (j.contains("quality"))
|
||||
{
|
||||
payload.quality = j.at("quality").get<uint8_t>();
|
||||
}
|
||||
if (j.contains("brightness"))
|
||||
{
|
||||
payload.brightness = j.at("brightness").get<uint8_t>();
|
||||
}
|
||||
if (j.contains("vflip"))
|
||||
{
|
||||
payload.vflip = j.at("vflip").get<uint8_t>();
|
||||
}
|
||||
if (j.contains("href"))
|
||||
{
|
||||
payload.href = j.at("href").get<uint8_t>();
|
||||
}
|
||||
if (j.contains("framesize"))
|
||||
{
|
||||
payload.framesize = j.at("framesize").get<uint8_t>();
|
||||
}
|
||||
if (j.contains("quality"))
|
||||
{
|
||||
payload.quality = j.at("quality").get<uint8_t>();
|
||||
}
|
||||
if (j.contains("brightness"))
|
||||
{
|
||||
payload.brightness = j.at("brightness").get<uint8_t>();
|
||||
}
|
||||
}
|
||||
@@ -8,59 +8,59 @@ struct BasePayload
|
||||
|
||||
struct WifiPayload : BasePayload
|
||||
{
|
||||
std::string name;
|
||||
std::string ssid;
|
||||
std::string password;
|
||||
uint8_t channel;
|
||||
uint8_t power;
|
||||
std::string name;
|
||||
std::string ssid;
|
||||
std::string password;
|
||||
uint8_t channel;
|
||||
uint8_t power;
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(WifiPayload, name, ssid, password, channel, power)
|
||||
|
||||
struct UpdateWifiPayload : BasePayload
|
||||
{
|
||||
std::string name;
|
||||
std::optional<std::string> ssid;
|
||||
std::optional<std::string> password;
|
||||
std::optional<uint8_t> channel;
|
||||
std::optional<uint8_t> power;
|
||||
std::string name;
|
||||
std::optional<std::string> ssid;
|
||||
std::optional<std::string> password;
|
||||
std::optional<uint8_t> channel;
|
||||
std::optional<uint8_t> power;
|
||||
};
|
||||
|
||||
void to_json(nlohmann::json &j, const UpdateWifiPayload &payload);
|
||||
void from_json(const nlohmann::json &j, UpdateWifiPayload &payload);
|
||||
void to_json(nlohmann::json& j, const UpdateWifiPayload& payload);
|
||||
void from_json(const nlohmann::json& j, UpdateWifiPayload& payload);
|
||||
struct deleteNetworkPayload : BasePayload
|
||||
{
|
||||
std::string name;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(deleteNetworkPayload, name)
|
||||
|
||||
struct UpdateAPWiFiPayload : BasePayload
|
||||
{
|
||||
std::optional<std::string> ssid;
|
||||
std::optional<std::string> password;
|
||||
std::optional<uint8_t> channel;
|
||||
std::optional<std::string> ssid;
|
||||
std::optional<std::string> password;
|
||||
std::optional<uint8_t> channel;
|
||||
};
|
||||
|
||||
void to_json(nlohmann::json &j, const UpdateAPWiFiPayload &payload);
|
||||
void from_json(const nlohmann::json &j, UpdateAPWiFiPayload &payload);
|
||||
void to_json(nlohmann::json& j, const UpdateAPWiFiPayload& payload);
|
||||
void from_json(const nlohmann::json& j, UpdateAPWiFiPayload& payload);
|
||||
struct MDNSPayload : BasePayload
|
||||
{
|
||||
std::string hostname;
|
||||
std::string hostname;
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(MDNSPayload, hostname)
|
||||
|
||||
struct UpdateCameraConfigPayload : BasePayload
|
||||
{
|
||||
std::optional<uint8_t> vflip;
|
||||
std::optional<uint8_t> href;
|
||||
std::optional<uint8_t> framesize;
|
||||
std::optional<uint8_t> quality;
|
||||
std::optional<uint8_t> brightness;
|
||||
// TODO add more options here
|
||||
std::optional<uint8_t> vflip;
|
||||
std::optional<uint8_t> href;
|
||||
std::optional<uint8_t> framesize;
|
||||
std::optional<uint8_t> quality;
|
||||
std::optional<uint8_t> brightness;
|
||||
// TODO add more options here
|
||||
};
|
||||
|
||||
void to_json(nlohmann::json &j, const UpdateCameraConfigPayload &payload);
|
||||
void from_json(const nlohmann::json &j, UpdateCameraConfigPayload &payload);
|
||||
void to_json(nlohmann::json& j, const UpdateCameraConfigPayload& payload);
|
||||
void from_json(const nlohmann::json& j, UpdateCameraConfigPayload& payload);
|
||||
#endif
|
||||
@@ -6,35 +6,35 @@
|
||||
|
||||
enum class DependencyType
|
||||
{
|
||||
project_config,
|
||||
camera_manager,
|
||||
wifi_manager,
|
||||
led_manager,
|
||||
monitoring_manager
|
||||
project_config,
|
||||
camera_manager,
|
||||
wifi_manager,
|
||||
led_manager,
|
||||
monitoring_manager
|
||||
};
|
||||
|
||||
class DependencyRegistry
|
||||
{
|
||||
std::unordered_map<DependencyType, std::shared_ptr<void>> services;
|
||||
std::unordered_map<DependencyType, std::shared_ptr<void>> services;
|
||||
|
||||
public:
|
||||
template <typename ServiceType>
|
||||
void registerService(DependencyType dependencyType, std::shared_ptr<ServiceType> service)
|
||||
{
|
||||
this->services[dependencyType] = std::static_pointer_cast<void>(service);
|
||||
}
|
||||
|
||||
template <typename ServiceType>
|
||||
std::shared_ptr<ServiceType> resolve(DependencyType dependencyType)
|
||||
{
|
||||
auto serviceIT = this->services.find(dependencyType);
|
||||
if (serviceIT != this->services.end())
|
||||
public:
|
||||
template <typename ServiceType>
|
||||
void registerService(DependencyType dependencyType, std::shared_ptr<ServiceType> service)
|
||||
{
|
||||
return std::static_pointer_cast<ServiceType>(serviceIT->second);
|
||||
this->services[dependencyType] = std::static_pointer_cast<void>(service);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
template <typename ServiceType>
|
||||
std::shared_ptr<ServiceType> resolve(DependencyType dependencyType)
|
||||
{
|
||||
auto serviceIT = this->services.find(dependencyType);
|
||||
if (serviceIT != this->services.end())
|
||||
{
|
||||
return std::static_pointer_cast<ServiceType>(serviceIT->second);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,17 +1,15 @@
|
||||
#include "camera_commands.hpp"
|
||||
|
||||
CommandResult updateCameraCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json &json)
|
||||
CommandResult updateCameraCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json& json)
|
||||
{
|
||||
auto payload = json.get<UpdateCameraConfigPayload>();
|
||||
auto payload = json.get<UpdateCameraConfigPayload>();
|
||||
|
||||
std::shared_ptr<ProjectConfig> projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config);
|
||||
auto oldConfig = projectConfig->getCameraConfig();
|
||||
projectConfig->setCameraConfig(
|
||||
payload.vflip.has_value() ? payload.vflip.value() : oldConfig.vflip,
|
||||
payload.framesize.has_value() ? payload.framesize.value() : oldConfig.framesize,
|
||||
payload.href.has_value() ? payload.href.value() : oldConfig.href,
|
||||
payload.quality.has_value() ? payload.quality.value() : oldConfig.quality,
|
||||
payload.brightness.has_value() ? payload.brightness.value() : oldConfig.brightness);
|
||||
std::shared_ptr<ProjectConfig> projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config);
|
||||
auto oldConfig = projectConfig->getCameraConfig();
|
||||
projectConfig->setCameraConfig(
|
||||
payload.vflip.has_value() ? payload.vflip.value() : oldConfig.vflip, payload.framesize.has_value() ? payload.framesize.value() : oldConfig.framesize,
|
||||
payload.href.has_value() ? payload.href.value() : oldConfig.href, payload.quality.has_value() ? payload.quality.value() : oldConfig.quality,
|
||||
payload.brightness.has_value() ? payload.brightness.value() : oldConfig.brightness);
|
||||
|
||||
return CommandResult::getSuccessResult("Config updated");
|
||||
return CommandResult::getSuccessResult("Config updated");
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
#ifndef CAMERA_COMMANDS_HPP
|
||||
#define CAMERA_COMMANDS_HPP
|
||||
#include <CameraManager.hpp>
|
||||
#include <ProjectConfig.hpp>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <nlohmann-json.hpp>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include "CommandResult.hpp"
|
||||
#include "CommandSchema.hpp"
|
||||
#include "DependencyRegistry.hpp"
|
||||
#include <CameraManager.hpp>
|
||||
#include <nlohmann-json.hpp>
|
||||
|
||||
CommandResult updateCameraCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json &json);
|
||||
CommandResult updateCameraCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json& json);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -2,45 +2,45 @@
|
||||
|
||||
CommandResult saveConfigCommand(std::shared_ptr<DependencyRegistry> registry)
|
||||
{
|
||||
std::shared_ptr<ProjectConfig> projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config);
|
||||
std::shared_ptr<ProjectConfig> projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config);
|
||||
|
||||
projectConfig->save();
|
||||
return CommandResult::getSuccessResult("Config saved");
|
||||
projectConfig->save();
|
||||
return CommandResult::getSuccessResult("Config saved");
|
||||
}
|
||||
|
||||
CommandResult getConfigCommand(std::shared_ptr<DependencyRegistry> registry)
|
||||
{
|
||||
std::shared_ptr<ProjectConfig> projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config);
|
||||
std::shared_ptr<ProjectConfig> projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config);
|
||||
|
||||
auto configRepresentation = projectConfig->getTrackerConfig().toRepresentation();
|
||||
return CommandResult::getSuccessResult(configRepresentation);
|
||||
auto configRepresentation = projectConfig->getTrackerConfig().toRepresentation();
|
||||
return CommandResult::getSuccessResult(configRepresentation);
|
||||
}
|
||||
|
||||
CommandResult resetConfigCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json &json)
|
||||
CommandResult resetConfigCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json& json)
|
||||
{
|
||||
std::array<std::string, 4> supported_sections = {
|
||||
"all",
|
||||
};
|
||||
std::array<std::string, 4> supported_sections = {
|
||||
"all",
|
||||
};
|
||||
|
||||
if (!json.contains("section"))
|
||||
{
|
||||
return CommandResult::getErrorResult("Invalid payload - missing section");
|
||||
}
|
||||
if (!json.contains("section"))
|
||||
{
|
||||
return CommandResult::getErrorResult("Invalid payload - missing section");
|
||||
}
|
||||
|
||||
auto section = json["section"].get<std::string>();
|
||||
auto section = json["section"].get<std::string>();
|
||||
|
||||
if (std::find(supported_sections.begin(), supported_sections.end(), section) == supported_sections.end())
|
||||
{
|
||||
return CommandResult::getErrorResult("Selected section is unsupported");
|
||||
}
|
||||
if (std::find(supported_sections.begin(), supported_sections.end(), section) == supported_sections.end())
|
||||
{
|
||||
return CommandResult::getErrorResult("Selected section is unsupported");
|
||||
}
|
||||
|
||||
// we cannot match on string, and making a map would be overkill right now, sooo
|
||||
// todo, add more granular control for other sections, like only reset camera, or only reset wifi
|
||||
if (section == "all")
|
||||
{
|
||||
std::shared_ptr<ProjectConfig> projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config);
|
||||
projectConfig->reset();
|
||||
}
|
||||
// we cannot match on string, and making a map would be overkill right now, sooo
|
||||
// todo, add more granular control for other sections, like only reset camera, or only reset wifi
|
||||
if (section == "all")
|
||||
{
|
||||
std::shared_ptr<ProjectConfig> projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config);
|
||||
projectConfig->reset();
|
||||
}
|
||||
|
||||
return CommandResult::getSuccessResult("Config reset");
|
||||
return CommandResult::getSuccessResult("Config reset");
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
#include <ProjectConfig.hpp>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <nlohmann-json.hpp>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include "CommandResult.hpp"
|
||||
#include "CommandSchema.hpp"
|
||||
#include "DependencyRegistry.hpp"
|
||||
#include <nlohmann-json.hpp>
|
||||
|
||||
CommandResult saveConfigCommand(std::shared_ptr<DependencyRegistry> registry);
|
||||
CommandResult getConfigCommand(std::shared_ptr<DependencyRegistry> registry);
|
||||
|
||||
CommandResult resetConfigCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json &json);
|
||||
CommandResult resetConfigCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json& json);
|
||||
@@ -1,10 +1,10 @@
|
||||
#include "device_commands.hpp"
|
||||
#include <cstdio>
|
||||
#include "LEDManager.hpp"
|
||||
#include "MonitoringManager.hpp"
|
||||
#include "esp_mac.h"
|
||||
#include <cstdio>
|
||||
|
||||
CommandResult setDeviceModeCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json &json)
|
||||
CommandResult setDeviceModeCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json& json)
|
||||
{
|
||||
if (!json.contains("mode") || !json["mode"].is_number_integer())
|
||||
{
|
||||
@@ -23,9 +23,8 @@ CommandResult setDeviceModeCommand(std::shared_ptr<DependencyRegistry> registry,
|
||||
return CommandResult::getSuccessResult("Device mode set");
|
||||
}
|
||||
|
||||
CommandResult updateOTACredentialsCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json &json)
|
||||
CommandResult updateOTACredentialsCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json& json)
|
||||
{
|
||||
|
||||
const auto projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config);
|
||||
const auto oldDeviceConfig = projectConfig->getDeviceConfig();
|
||||
auto OTALogin = oldDeviceConfig.OTALogin;
|
||||
@@ -57,7 +56,7 @@ CommandResult updateOTACredentialsCommand(std::shared_ptr<DependencyRegistry> re
|
||||
return CommandResult::getSuccessResult("OTA Config set");
|
||||
}
|
||||
|
||||
CommandResult updateLEDDutyCycleCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json &json)
|
||||
CommandResult updateLEDDutyCycleCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json& json)
|
||||
{
|
||||
if (!json.contains("dutyCycle") || !json["dutyCycle"].is_number_integer())
|
||||
{
|
||||
@@ -105,10 +104,7 @@ CommandResult startStreamingCommand()
|
||||
// from *inside* the serial handler, we'd deadlock.
|
||||
// we can just pass nullptr to the vtaskdelete(),
|
||||
// but then we won't get any response, so we schedule a timer instead
|
||||
esp_timer_create_args_t args{
|
||||
.callback = activateStreaming,
|
||||
.arg = nullptr,
|
||||
.name = "activateStreaming"};
|
||||
esp_timer_create_args_t args{.callback = activateStreaming, .arg = nullptr, .name = "activateStreaming"};
|
||||
|
||||
esp_timer_handle_t activateStreamingTimer;
|
||||
esp_timer_create(&args, &activateStreamingTimer);
|
||||
@@ -117,9 +113,8 @@ CommandResult startStreamingCommand()
|
||||
return CommandResult::getSuccessResult("Streaming starting");
|
||||
}
|
||||
|
||||
CommandResult switchModeCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json &json)
|
||||
CommandResult switchModeCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json& json)
|
||||
{
|
||||
|
||||
if (!json.contains("mode") || !json["mode"].is_string())
|
||||
{
|
||||
return CommandResult::getErrorResult("Invalid payload - missing mode");
|
||||
@@ -159,7 +154,7 @@ CommandResult getDeviceModeCommand(std::shared_ptr<DependencyRegistry> registry)
|
||||
const auto projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config);
|
||||
StreamingMode currentMode = projectConfig->getDeviceMode();
|
||||
|
||||
const char *modeStr = "unknown";
|
||||
const char* modeStr = "unknown";
|
||||
switch (currentMode)
|
||||
{
|
||||
case StreamingMode::UVC:
|
||||
@@ -188,13 +183,11 @@ CommandResult getSerialNumberCommand(std::shared_ptr<DependencyRegistry> /*regis
|
||||
|
||||
char serial_no_sep[13];
|
||||
// Serial without separators (12 hex chars)
|
||||
std::snprintf(serial_no_sep, sizeof(serial_no_sep), "%02X%02X%02X%02X%02X%02X",
|
||||
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
std::snprintf(serial_no_sep, sizeof(serial_no_sep), "%02X%02X%02X%02X%02X%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
|
||||
char mac_colon[18];
|
||||
// MAC with colons
|
||||
std::snprintf(mac_colon, sizeof(mac_colon), "%02X:%02X:%02X:%02X:%02X:%02X",
|
||||
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
std::snprintf(mac_colon, sizeof(mac_colon), "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
|
||||
const auto json = nlohmann::json{
|
||||
{"serial", serial_no_sep},
|
||||
@@ -246,8 +239,8 @@ CommandResult getBatteryStatusCommand(std::shared_ptr<DependencyRegistry> regist
|
||||
|
||||
CommandResult getInfoCommand(std::shared_ptr<DependencyRegistry> /*registry*/)
|
||||
{
|
||||
const char *who = CONFIG_GENERAL_BOARD;
|
||||
const char *ver = CONFIG_GENERAL_VERSION;
|
||||
const char* who = CONFIG_GENERAL_BOARD;
|
||||
const char* ver = CONFIG_GENERAL_VERSION;
|
||||
// Ensure non-null strings
|
||||
if (!who)
|
||||
who = "";
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
#include "CommandResult.hpp"
|
||||
#include "ProjectConfig.hpp"
|
||||
#include "OpenIrisTasks.hpp"
|
||||
#include "DependencyRegistry.hpp"
|
||||
#include "OpenIrisTasks.hpp"
|
||||
#include "ProjectConfig.hpp"
|
||||
#include "esp_timer.h"
|
||||
#include "main_globals.hpp"
|
||||
|
||||
#include <format>
|
||||
#include <string>
|
||||
#include <nlohmann-json.hpp>
|
||||
#include <string>
|
||||
|
||||
CommandResult updateOTACredentialsCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json &json);
|
||||
CommandResult updateOTACredentialsCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json& json);
|
||||
|
||||
CommandResult updateLEDDutyCycleCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json &json);
|
||||
CommandResult updateLEDDutyCycleCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json& json);
|
||||
CommandResult getLEDDutyCycleCommand(std::shared_ptr<DependencyRegistry> registry);
|
||||
|
||||
CommandResult restartDeviceCommand();
|
||||
|
||||
CommandResult startStreamingCommand();
|
||||
|
||||
CommandResult switchModeCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json &json);
|
||||
CommandResult switchModeCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json& json);
|
||||
|
||||
CommandResult getDeviceModeCommand(std::shared_ptr<DependencyRegistry> registry);
|
||||
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
#include "mdns_commands.hpp"
|
||||
|
||||
CommandResult setMDNSCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json &json)
|
||||
CommandResult setMDNSCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json& json)
|
||||
{
|
||||
const auto payload = json.get<MDNSPayload>();
|
||||
if (payload.hostname.empty())
|
||||
return CommandResult::getErrorResult("Invalid payload - empty hostname");
|
||||
const auto payload = json.get<MDNSPayload>();
|
||||
if (payload.hostname.empty())
|
||||
return CommandResult::getErrorResult("Invalid payload - empty hostname");
|
||||
|
||||
std::shared_ptr<ProjectConfig> projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config);
|
||||
projectConfig->setMDNSConfig(payload.hostname);
|
||||
std::shared_ptr<ProjectConfig> projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config);
|
||||
projectConfig->setMDNSConfig(payload.hostname);
|
||||
|
||||
return CommandResult::getSuccessResult("Config updated");
|
||||
return CommandResult::getSuccessResult("Config updated");
|
||||
}
|
||||
|
||||
CommandResult getMDNSNameCommand(std::shared_ptr<DependencyRegistry> registry)
|
||||
{
|
||||
const auto projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config);
|
||||
auto mdnsConfig = projectConfig->getMDNSConfig();
|
||||
const auto projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config);
|
||||
auto mdnsConfig = projectConfig->getMDNSConfig();
|
||||
|
||||
const auto json = nlohmann::json{{"hostname", mdnsConfig.hostname}};
|
||||
return CommandResult::getSuccessResult(json);
|
||||
const auto json = nlohmann::json{{"hostname", mdnsConfig.hostname}};
|
||||
return CommandResult::getSuccessResult(json);
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
#include <ProjectConfig.hpp>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <nlohmann-json.hpp>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include "CommandResult.hpp"
|
||||
#include "CommandSchema.hpp"
|
||||
#include "DependencyRegistry.hpp"
|
||||
#include <nlohmann-json.hpp>
|
||||
|
||||
CommandResult setMDNSCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json &json);
|
||||
CommandResult setMDNSCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json& json);
|
||||
CommandResult getMDNSNameCommand(std::shared_ptr<DependencyRegistry> registry);
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "scan_commands.hpp"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
CommandResult scanNetworksCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json &json)
|
||||
CommandResult scanNetworksCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json& json)
|
||||
{
|
||||
#if !CONFIG_GENERAL_ENABLE_WIRELESS
|
||||
return CommandResult::getErrorResult("Not supported by current firmware");
|
||||
@@ -24,16 +24,14 @@ CommandResult scanNetworksCommand(std::shared_ptr<DependencyRegistry> registry,
|
||||
nlohmann::json result;
|
||||
std::vector<nlohmann::json> networksJson;
|
||||
|
||||
for (const auto &network : networks)
|
||||
for (const auto& network : networks)
|
||||
{
|
||||
nlohmann::json networkItem;
|
||||
networkItem["ssid"] = network.ssid;
|
||||
networkItem["channel"] = network.channel;
|
||||
networkItem["rssi"] = network.rssi;
|
||||
char mac_str[18];
|
||||
sprintf(mac_str, "%02x:%02x:%02x:%02x:%02x:%02x",
|
||||
network.mac[0], network.mac[1], network.mac[2],
|
||||
network.mac[3], network.mac[4], network.mac[5]);
|
||||
sprintf(mac_str, "%02x:%02x:%02x:%02x:%02x:%02x", network.mac[0], network.mac[1], network.mac[2], network.mac[3], network.mac[4], network.mac[5]);
|
||||
networkItem["mac_address"] = mac_str;
|
||||
networkItem["auth_mode"] = network.auth_mode;
|
||||
networksJson.push_back(networkItem);
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#ifndef SCAN_COMMANDS_HPP
|
||||
#define SCAN_COMMANDS_HPP
|
||||
|
||||
#include <nlohmann-json.hpp>
|
||||
#include <string>
|
||||
#include <wifiManager.hpp>
|
||||
#include "CommandResult.hpp"
|
||||
#include "DependencyRegistry.hpp"
|
||||
#include "esp_log.h"
|
||||
#include <wifiManager.hpp>
|
||||
#include <string>
|
||||
#include <nlohmann-json.hpp>
|
||||
|
||||
CommandResult scanNetworksCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json &json);
|
||||
CommandResult scanNetworksCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json& json);
|
||||
|
||||
#endif
|
||||
@@ -1,32 +1,32 @@
|
||||
#include "simple_commands.hpp"
|
||||
|
||||
static const char *TAG = "SimpleCommands";
|
||||
static const char* TAG = "SimpleCommands";
|
||||
|
||||
CommandResult PingCommand()
|
||||
{
|
||||
return CommandResult::getSuccessResult("pong");
|
||||
return CommandResult::getSuccessResult("pong");
|
||||
};
|
||||
|
||||
CommandResult PauseCommand(const nlohmann::json &json)
|
||||
CommandResult PauseCommand(const nlohmann::json& json)
|
||||
{
|
||||
auto pause = true;
|
||||
auto pause = true;
|
||||
|
||||
if (json.contains("pause") && json["pause"].is_boolean())
|
||||
{
|
||||
pause = json["pause"].get<bool>();
|
||||
}
|
||||
if (json.contains("pause") && json["pause"].is_boolean())
|
||||
{
|
||||
pause = json["pause"].get<bool>();
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Pause command received: %s", pause ? "true" : "false");
|
||||
ESP_LOGI(TAG, "Pause command received: %s", pause ? "true" : "false");
|
||||
|
||||
setStartupPaused(pause);
|
||||
if (pause)
|
||||
{
|
||||
ESP_LOGI(TAG, "Startup paused - device will remain in configuration mode");
|
||||
return CommandResult::getSuccessResult("Startup paused");
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGI(TAG, "Startup resumed");
|
||||
return CommandResult::getSuccessResult("Startup resumed");
|
||||
}
|
||||
setStartupPaused(pause);
|
||||
if (pause)
|
||||
{
|
||||
ESP_LOGI(TAG, "Startup paused - device will remain in configuration mode");
|
||||
return CommandResult::getSuccessResult("Startup paused");
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGI(TAG, "Startup resumed");
|
||||
return CommandResult::getSuccessResult("Startup resumed");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#ifndef SIMPLE_COMMANDS
|
||||
#define SIMPLE_COMMANDS
|
||||
|
||||
#include <nlohmann-json.hpp>
|
||||
#include <string>
|
||||
#include "CommandResult.hpp"
|
||||
#include "main_globals.hpp"
|
||||
#include "esp_log.h"
|
||||
#include <nlohmann-json.hpp>
|
||||
#include "main_globals.hpp"
|
||||
|
||||
CommandResult PingCommand();
|
||||
CommandResult PauseCommand(const nlohmann::json &json);
|
||||
CommandResult PauseCommand(const nlohmann::json& json);
|
||||
|
||||
#endif
|
||||
@@ -2,176 +2,165 @@
|
||||
#include "esp_netif.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
CommandResult setWiFiCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json &json)
|
||||
CommandResult setWiFiCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json& json)
|
||||
{
|
||||
#if !CONFIG_GENERAL_ENABLE_WIRELESS
|
||||
return CommandResult::getErrorResult("Not supported by current firmware");
|
||||
return CommandResult::getErrorResult("Not supported by current firmware");
|
||||
#endif
|
||||
|
||||
auto payload = json.get<WifiPayload>();
|
||||
if (payload.name.empty())
|
||||
{
|
||||
payload.name = std::string("main");
|
||||
}
|
||||
auto payload = json.get<WifiPayload>();
|
||||
if (payload.name.empty())
|
||||
{
|
||||
payload.name = std::string("main");
|
||||
}
|
||||
|
||||
if (payload.ssid.empty())
|
||||
{
|
||||
return CommandResult::getErrorResult("Invalid payload: missing SSID");
|
||||
}
|
||||
if (payload.ssid.empty())
|
||||
{
|
||||
return CommandResult::getErrorResult("Invalid payload: missing SSID");
|
||||
}
|
||||
|
||||
std::shared_ptr<ProjectConfig> projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config);
|
||||
projectConfig->setWifiConfig(
|
||||
payload.name,
|
||||
payload.ssid,
|
||||
payload.password,
|
||||
payload.channel,
|
||||
payload.power);
|
||||
|
||||
return CommandResult::getSuccessResult("Config updated");
|
||||
}
|
||||
|
||||
CommandResult deleteWiFiCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json &json)
|
||||
{
|
||||
#if !CONFIG_GENERAL_ENABLE_WIRELESS
|
||||
return CommandResult::getErrorResult("Not supported by current firmware");
|
||||
#endif
|
||||
|
||||
const auto payload = json.get<deleteNetworkPayload>();
|
||||
if (payload.name.empty())
|
||||
return CommandResult::getErrorResult("Invalid payload");
|
||||
|
||||
auto projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config);
|
||||
|
||||
projectConfig->deleteWifiConfig(payload.name);
|
||||
return CommandResult::getSuccessResult("Config updated");
|
||||
}
|
||||
|
||||
CommandResult updateWiFiCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json &json)
|
||||
{
|
||||
#if !CONFIG_GENERAL_ENABLE_WIRELESS
|
||||
return CommandResult::getErrorResult("Not supported by current firmware");
|
||||
#endif
|
||||
|
||||
auto payload = json.get<UpdateWifiPayload>();
|
||||
if (payload.name.empty())
|
||||
{
|
||||
return CommandResult::getErrorResult("Invalid payload - missing network name");
|
||||
}
|
||||
|
||||
auto projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config);
|
||||
auto storedNetworks = projectConfig->getWifiConfigs();
|
||||
if (const auto networkToUpdate = std::ranges::find_if(
|
||||
storedNetworks,
|
||||
[&](auto &network)
|
||||
{ return network.name == payload.name; });
|
||||
networkToUpdate != storedNetworks.end())
|
||||
{
|
||||
projectConfig->setWifiConfig(
|
||||
payload.name,
|
||||
payload.ssid.has_value() ? payload.ssid.value() : networkToUpdate->ssid,
|
||||
payload.password.has_value() ? payload.password.value() : networkToUpdate->password,
|
||||
payload.channel.has_value() ? payload.channel.value() : networkToUpdate->channel,
|
||||
payload.power.has_value() ? payload.power.value() : networkToUpdate->power);
|
||||
std::shared_ptr<ProjectConfig> projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config);
|
||||
projectConfig->setWifiConfig(payload.name, payload.ssid, payload.password, payload.channel, payload.power);
|
||||
|
||||
return CommandResult::getSuccessResult("Config updated");
|
||||
}
|
||||
else
|
||||
return CommandResult::getErrorResult("Requested network does not exist");
|
||||
}
|
||||
|
||||
CommandResult updateAPWiFiCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json &json)
|
||||
CommandResult deleteWiFiCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json& json)
|
||||
{
|
||||
#if !CONFIG_GENERAL_ENABLE_WIRELESS
|
||||
return CommandResult::getErrorResult("Not supported by current firmware");
|
||||
return CommandResult::getErrorResult("Not supported by current firmware");
|
||||
#endif
|
||||
|
||||
const auto payload = json.get<UpdateAPWiFiPayload>();
|
||||
const auto payload = json.get<deleteNetworkPayload>();
|
||||
if (payload.name.empty())
|
||||
return CommandResult::getErrorResult("Invalid payload");
|
||||
|
||||
auto projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config);
|
||||
const auto previousAPConfig = projectConfig->getAPWifiConfig();
|
||||
auto projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config);
|
||||
|
||||
projectConfig->setAPWifiConfig(
|
||||
payload.ssid.has_value() ? payload.ssid.value() : previousAPConfig.ssid,
|
||||
payload.password.has_value() ? payload.password.value() : previousAPConfig.password,
|
||||
payload.channel.has_value() ? payload.channel.value() : previousAPConfig.channel);
|
||||
projectConfig->deleteWifiConfig(payload.name);
|
||||
return CommandResult::getSuccessResult("Config updated");
|
||||
}
|
||||
|
||||
return CommandResult::getSuccessResult("Config updated");
|
||||
CommandResult updateWiFiCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json& json)
|
||||
{
|
||||
#if !CONFIG_GENERAL_ENABLE_WIRELESS
|
||||
return CommandResult::getErrorResult("Not supported by current firmware");
|
||||
#endif
|
||||
|
||||
auto payload = json.get<UpdateWifiPayload>();
|
||||
if (payload.name.empty())
|
||||
{
|
||||
return CommandResult::getErrorResult("Invalid payload - missing network name");
|
||||
}
|
||||
|
||||
auto projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config);
|
||||
auto storedNetworks = projectConfig->getWifiConfigs();
|
||||
if (const auto networkToUpdate = std::ranges::find_if(storedNetworks, [&](auto& network) { return network.name == payload.name; });
|
||||
networkToUpdate != storedNetworks.end())
|
||||
{
|
||||
projectConfig->setWifiConfig(payload.name, payload.ssid.has_value() ? payload.ssid.value() : networkToUpdate->ssid,
|
||||
payload.password.has_value() ? payload.password.value() : networkToUpdate->password,
|
||||
payload.channel.has_value() ? payload.channel.value() : networkToUpdate->channel,
|
||||
payload.power.has_value() ? payload.power.value() : networkToUpdate->power);
|
||||
|
||||
return CommandResult::getSuccessResult("Config updated");
|
||||
}
|
||||
else
|
||||
return CommandResult::getErrorResult("Requested network does not exist");
|
||||
}
|
||||
|
||||
CommandResult updateAPWiFiCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json& json)
|
||||
{
|
||||
#if !CONFIG_GENERAL_ENABLE_WIRELESS
|
||||
return CommandResult::getErrorResult("Not supported by current firmware");
|
||||
#endif
|
||||
|
||||
const auto payload = json.get<UpdateAPWiFiPayload>();
|
||||
|
||||
auto projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config);
|
||||
const auto previousAPConfig = projectConfig->getAPWifiConfig();
|
||||
|
||||
projectConfig->setAPWifiConfig(payload.ssid.has_value() ? payload.ssid.value() : previousAPConfig.ssid,
|
||||
payload.password.has_value() ? payload.password.value() : previousAPConfig.password,
|
||||
payload.channel.has_value() ? payload.channel.value() : previousAPConfig.channel);
|
||||
|
||||
return CommandResult::getSuccessResult("Config updated");
|
||||
}
|
||||
|
||||
CommandResult getWiFiStatusCommand(std::shared_ptr<DependencyRegistry> registry)
|
||||
{
|
||||
#if !CONFIG_GENERAL_ENABLE_WIRELESS
|
||||
return CommandResult::getErrorResult("Not supported by current firmware");
|
||||
return CommandResult::getErrorResult("Not supported by current firmware");
|
||||
#endif
|
||||
|
||||
auto wifiManager = registry->resolve<WiFiManager>(DependencyType::wifi_manager);
|
||||
auto projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config);
|
||||
auto wifiManager = registry->resolve<WiFiManager>(DependencyType::wifi_manager);
|
||||
auto projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config);
|
||||
|
||||
auto wifiState = wifiManager->GetCurrentWiFiState();
|
||||
auto networks = projectConfig->getWifiConfigs();
|
||||
nlohmann::json result;
|
||||
auto wifiState = wifiManager->GetCurrentWiFiState();
|
||||
auto networks = projectConfig->getWifiConfigs();
|
||||
nlohmann::json result;
|
||||
|
||||
switch (wifiState)
|
||||
{
|
||||
case WiFiState_e::WiFiState_NotInitialized:
|
||||
result["status"] = "not_initialized";
|
||||
break;
|
||||
case WiFiState_e::WiFiState_Initialized:
|
||||
result["status"] = "initialized";
|
||||
break;
|
||||
case WiFiState_e::WiFiState_ReadyToConnect:
|
||||
result["status"] = "ready";
|
||||
break;
|
||||
case WiFiState_e::WiFiState_Connecting:
|
||||
result["status"] = "connecting";
|
||||
break;
|
||||
case WiFiState_e::WiFiState_WaitingForIp:
|
||||
result["status"] = "waiting_for_ip";
|
||||
break;
|
||||
case WiFiState_e::WiFiState_Connected:
|
||||
result["status"] = "connected";
|
||||
break;
|
||||
case WiFiState_e::WiFiState_Disconnected:
|
||||
result["status"] = "disconnected";
|
||||
break;
|
||||
case WiFiState_e::WiFiState_Error:
|
||||
result["status"] = "error";
|
||||
break;
|
||||
}
|
||||
|
||||
if (wifiState == WiFiState_e::WiFiState_Connected)
|
||||
{
|
||||
// Get IP address from ESP32
|
||||
esp_netif_ip_info_t ip_info;
|
||||
esp_netif_t *netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF");
|
||||
if (netif && esp_netif_get_ip_info(netif, &ip_info) == ESP_OK)
|
||||
switch (wifiState)
|
||||
{
|
||||
char ip_str[16];
|
||||
sprintf(ip_str, IPSTR, IP2STR(&ip_info.ip));
|
||||
result["ip_address"] = ip_str;
|
||||
case WiFiState_e::WiFiState_NotInitialized:
|
||||
result["status"] = "not_initialized";
|
||||
break;
|
||||
case WiFiState_e::WiFiState_Initialized:
|
||||
result["status"] = "initialized";
|
||||
break;
|
||||
case WiFiState_e::WiFiState_ReadyToConnect:
|
||||
result["status"] = "ready";
|
||||
break;
|
||||
case WiFiState_e::WiFiState_Connecting:
|
||||
result["status"] = "connecting";
|
||||
break;
|
||||
case WiFiState_e::WiFiState_WaitingForIp:
|
||||
result["status"] = "waiting_for_ip";
|
||||
break;
|
||||
case WiFiState_e::WiFiState_Connected:
|
||||
result["status"] = "connected";
|
||||
break;
|
||||
case WiFiState_e::WiFiState_Disconnected:
|
||||
result["status"] = "disconnected";
|
||||
break;
|
||||
case WiFiState_e::WiFiState_Error:
|
||||
result["status"] = "error";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return CommandResult::getSuccessResult(result);
|
||||
if (wifiState == WiFiState_e::WiFiState_Connected)
|
||||
{
|
||||
// Get IP address from ESP32
|
||||
esp_netif_ip_info_t ip_info;
|
||||
esp_netif_t* netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF");
|
||||
if (netif && esp_netif_get_ip_info(netif, &ip_info) == ESP_OK)
|
||||
{
|
||||
char ip_str[16];
|
||||
sprintf(ip_str, IPSTR, IP2STR(&ip_info.ip));
|
||||
result["ip_address"] = ip_str;
|
||||
}
|
||||
}
|
||||
|
||||
return CommandResult::getSuccessResult(result);
|
||||
}
|
||||
|
||||
CommandResult connectWiFiCommand(std::shared_ptr<DependencyRegistry> registry)
|
||||
{
|
||||
#if !CONFIG_GENERAL_ENABLE_WIRELESS
|
||||
return CommandResult::getErrorResult("Not supported by current firmware");
|
||||
return CommandResult::getErrorResult("Not supported by current firmware");
|
||||
#endif
|
||||
|
||||
auto wifiManager = registry->resolve<WiFiManager>(DependencyType::wifi_manager);
|
||||
auto projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config);
|
||||
auto wifiManager = registry->resolve<WiFiManager>(DependencyType::wifi_manager);
|
||||
auto projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config);
|
||||
|
||||
auto networks = projectConfig->getWifiConfigs();
|
||||
if (networks.empty())
|
||||
{
|
||||
return CommandResult::getErrorResult("No WiFi networks configured");
|
||||
}
|
||||
auto networks = projectConfig->getWifiConfigs();
|
||||
if (networks.empty())
|
||||
{
|
||||
return CommandResult::getErrorResult("No WiFi networks configured");
|
||||
}
|
||||
|
||||
// Trigger WiFi connection attempt
|
||||
wifiManager->TryConnectToStoredNetworks();
|
||||
// Trigger WiFi connection attempt
|
||||
wifiManager->TryConnectToStoredNetworks();
|
||||
|
||||
return CommandResult::getSuccessResult("WiFi connection attempt started");
|
||||
return CommandResult::getSuccessResult("WiFi connection attempt started");
|
||||
}
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
#include <ProjectConfig.hpp>
|
||||
#include <wifiManager.hpp>
|
||||
#include <StateManager.hpp>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <nlohmann-json.hpp>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <wifiManager.hpp>
|
||||
#include "CommandResult.hpp"
|
||||
#include "CommandSchema.hpp"
|
||||
#include "DependencyRegistry.hpp"
|
||||
#include <nlohmann-json.hpp>
|
||||
|
||||
CommandResult setWiFiCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json &json);
|
||||
CommandResult setWiFiCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json& json);
|
||||
|
||||
CommandResult deleteWiFiCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json &json);
|
||||
CommandResult deleteWiFiCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json& json);
|
||||
|
||||
CommandResult updateWiFiCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json &json);
|
||||
CommandResult updateWiFiCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json& json);
|
||||
|
||||
CommandResult updateAPWiFiCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json &json);
|
||||
CommandResult updateAPWiFiCommand(std::shared_ptr<DependencyRegistry> registry, const nlohmann::json& json);
|
||||
|
||||
CommandResult getWiFiStatusCommand(std::shared_ptr<DependencyRegistry> registry);
|
||||
CommandResult connectWiFiCommand(std::shared_ptr<DependencyRegistry> registry);
|
||||
|
||||
@@ -1,85 +1,85 @@
|
||||
#include "helpers.hpp"
|
||||
|
||||
char *Helpers::itoa(int value, char *result, int base)
|
||||
char* Helpers::itoa(int value, char* result, int base)
|
||||
{
|
||||
// check that the base if valid
|
||||
if (base < 2 || base > 36)
|
||||
{
|
||||
*result = '\0';
|
||||
return result;
|
||||
}
|
||||
|
||||
char *ptr = result, *ptr1 = result, tmp_char;
|
||||
int tmp_value;
|
||||
|
||||
do
|
||||
{
|
||||
tmp_value = value;
|
||||
value /= base;
|
||||
*ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz"[35 + (tmp_value - value * base)];
|
||||
} while (value);
|
||||
|
||||
// Apply negative sign
|
||||
if (tmp_value < 0)
|
||||
*ptr++ = '-';
|
||||
*ptr-- = '\0';
|
||||
while (ptr1 < ptr)
|
||||
{
|
||||
tmp_char = *ptr;
|
||||
*ptr-- = *ptr1;
|
||||
*ptr1++ = tmp_char;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void split(const std::string &str, const std::string &splitBy, std::vector<std::string> &tokens)
|
||||
{
|
||||
/* Store the original string in the array, so we can loop the rest
|
||||
* of the algorithm. */
|
||||
tokens.emplace_back(str);
|
||||
|
||||
// Store the split index in a 'size_t' (unsigned integer) type.
|
||||
size_t splitAt;
|
||||
// Store the size of what we're splicing out.
|
||||
size_t splitLen = splitBy.size();
|
||||
// Create a string for temporarily storing the fragment we're processing.
|
||||
std::string frag;
|
||||
// Loop infinitely - break is internal.
|
||||
while (true)
|
||||
{
|
||||
/* Store the last string in the vector, which is the only logical
|
||||
* candidate for processing. */
|
||||
frag = tokens.back();
|
||||
/* The index where the split is. */
|
||||
splitAt = frag.find(splitBy);
|
||||
// If we didn't find a new split point...
|
||||
if (splitAt == std::string::npos)
|
||||
// check that the base if valid
|
||||
if (base < 2 || base > 36)
|
||||
{
|
||||
// Break the loop and (implicitly) return.
|
||||
break;
|
||||
*result = '\0';
|
||||
return result;
|
||||
}
|
||||
/* Put everything from the left side of the split where the string
|
||||
* being processed used to be. */
|
||||
tokens.back() = frag.substr(0, splitAt);
|
||||
/* Push everything from the right side of the split to the next empty
|
||||
* index in the vector. */
|
||||
tokens.emplace_back(frag.substr(splitAt + splitLen, frag.size() - (splitAt + splitLen)));
|
||||
}
|
||||
|
||||
char *ptr = result, *ptr1 = result, tmp_char;
|
||||
int tmp_value;
|
||||
|
||||
do
|
||||
{
|
||||
tmp_value = value;
|
||||
value /= base;
|
||||
*ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz"[35 + (tmp_value - value * base)];
|
||||
} while (value);
|
||||
|
||||
// Apply negative sign
|
||||
if (tmp_value < 0)
|
||||
*ptr++ = '-';
|
||||
*ptr-- = '\0';
|
||||
while (ptr1 < ptr)
|
||||
{
|
||||
tmp_char = *ptr;
|
||||
*ptr-- = *ptr1;
|
||||
*ptr1++ = tmp_char;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::string> Helpers::split(const std::string &s, char delimiter)
|
||||
void split(const std::string& str, const std::string& splitBy, std::vector<std::string>& tokens)
|
||||
{
|
||||
std::vector<std::string> parts;
|
||||
std::string part;
|
||||
std::istringstream tokenStream(s);
|
||||
while (std::getline(tokenStream, part, delimiter))
|
||||
{
|
||||
parts.push_back(part);
|
||||
}
|
||||
return parts;
|
||||
/* Store the original string in the array, so we can loop the rest
|
||||
* of the algorithm. */
|
||||
tokens.emplace_back(str);
|
||||
|
||||
// Store the split index in a 'size_t' (unsigned integer) type.
|
||||
size_t splitAt;
|
||||
// Store the size of what we're splicing out.
|
||||
size_t splitLen = splitBy.size();
|
||||
// Create a string for temporarily storing the fragment we're processing.
|
||||
std::string frag;
|
||||
// Loop infinitely - break is internal.
|
||||
while (true)
|
||||
{
|
||||
/* Store the last string in the vector, which is the only logical
|
||||
* candidate for processing. */
|
||||
frag = tokens.back();
|
||||
/* The index where the split is. */
|
||||
splitAt = frag.find(splitBy);
|
||||
// If we didn't find a new split point...
|
||||
if (splitAt == std::string::npos)
|
||||
{
|
||||
// Break the loop and (implicitly) return.
|
||||
break;
|
||||
}
|
||||
/* Put everything from the left side of the split where the string
|
||||
* being processed used to be. */
|
||||
tokens.back() = frag.substr(0, splitAt);
|
||||
/* Push everything from the right side of the split to the next empty
|
||||
* index in the vector. */
|
||||
tokens.emplace_back(frag.substr(splitAt + splitLen, frag.size() - (splitAt + splitLen)));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> Helpers::split(const std::string& s, char delimiter)
|
||||
{
|
||||
std::vector<std::string> parts;
|
||||
std::string part;
|
||||
std::istringstream tokenStream(s);
|
||||
while (std::getline(tokenStream, part, delimiter))
|
||||
{
|
||||
parts.push_back(part);
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
|
||||
int64_t Helpers::getTimeInMillis()
|
||||
{
|
||||
return (esp_timer_get_time() / 1000);
|
||||
return (esp_timer_get_time() / 1000);
|
||||
}
|
||||
@@ -1,40 +1,40 @@
|
||||
#pragma once
|
||||
#ifndef HELPERS_HPP
|
||||
#define HELPERS_HPP
|
||||
#include "esp_timer.h"
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "esp_timer.h"
|
||||
|
||||
namespace Helpers
|
||||
{
|
||||
char *itoa(int value, char *result, int base);
|
||||
void split(std::string str, std::string splitBy, std::vector<std::string> &tokens);
|
||||
std::vector<std::string> split(const std::string &s, char delimiter);
|
||||
char* itoa(int value, char* result, int base);
|
||||
void split(std::string str, std::string splitBy, std::vector<std::string>& tokens);
|
||||
std::vector<std::string> split(const std::string& s, char delimiter);
|
||||
|
||||
/// @brief
|
||||
/// @tparam ...Args
|
||||
/// @param format
|
||||
/// @param ...args
|
||||
/// @return
|
||||
template <typename... Args>
|
||||
std::string format_string(const std::string &format, Args... args)
|
||||
{
|
||||
int size_s = std::snprintf(nullptr, 0, format.c_str(), args...) + 1; // Extra space for '\0'
|
||||
/// @brief
|
||||
/// @tparam ...Args
|
||||
/// @param format
|
||||
/// @param ...args
|
||||
/// @return
|
||||
template <typename... Args>
|
||||
std::string format_string(const std::string& format, Args... args)
|
||||
{
|
||||
int size_s = std::snprintf(nullptr, 0, format.c_str(), args...) + 1; // Extra space for '\0'
|
||||
if (size_s <= 0)
|
||||
{
|
||||
std::cout << "Error during formatting.";
|
||||
return "";
|
||||
std::cout << "Error during formatting.";
|
||||
return "";
|
||||
}
|
||||
auto size = static_cast<size_t>(size_s);
|
||||
std::unique_ptr<char[]> buf(new char[size]);
|
||||
std::snprintf(buf.get(), size, format.c_str(), args...);
|
||||
return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside
|
||||
}
|
||||
|
||||
int64_t getTimeInMillis();
|
||||
return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside
|
||||
}
|
||||
|
||||
#endif // HELPERS_HPP
|
||||
int64_t getTimeInMillis();
|
||||
} // namespace Helpers
|
||||
|
||||
#endif // HELPERS_HPP
|
||||
|
||||
@@ -28,12 +28,18 @@ void setStartupPaused(bool startupPaused)
|
||||
}
|
||||
|
||||
// Function to manually activate streaming
|
||||
void activateStreaming(void *arg)
|
||||
void activateStreaming(void* arg)
|
||||
{
|
||||
force_activate_streaming();
|
||||
}
|
||||
|
||||
// USB handover state
|
||||
static bool s_usbHandoverDone = false;
|
||||
bool getUsbHandoverDone() { return s_usbHandoverDone; }
|
||||
void setUsbHandoverDone(bool done) { s_usbHandoverDone = done; }
|
||||
bool getUsbHandoverDone()
|
||||
{
|
||||
return s_usbHandoverDone;
|
||||
}
|
||||
void setUsbHandoverDone(bool done)
|
||||
{
|
||||
s_usbHandoverDone = done;
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
// Function to manually activate streaming
|
||||
// designed to be scheduled as a task
|
||||
// so that the serial manager has time to return the response
|
||||
void activateStreaming(void *arg);
|
||||
void activateStreaming(void* arg);
|
||||
|
||||
bool getStartupCommandReceived();
|
||||
void setStartupCommandReceived(bool startupCommandReceived);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "LEDManager.hpp"
|
||||
|
||||
const char *LED_MANAGER_TAG = "[LED_MANAGER]";
|
||||
const char* LED_MANAGER_TAG = "[LED_MANAGER]";
|
||||
|
||||
// Pattern design rules:
|
||||
// - Error states: isError=true, repeat indefinitely, easily distinguishable (avoid overlap).
|
||||
@@ -8,25 +8,25 @@ const char *LED_MANAGER_TAG = "[LED_MANAGER]";
|
||||
// - Non-repeating notification (e.g. Connected) gives user a brief confirmation burst then turns off.
|
||||
// Durations in ms.
|
||||
ledStateMap_t LEDManager::ledStateMap = {
|
||||
{ LEDStates_e::LedStateNone, { /*isError*/false, /*repeat*/false, {{LED_OFF, 1000}} } },
|
||||
{ LEDStates_e::LedStateStreaming, { false, /*repeat steady*/true, {{LED_ON, 1000}} } },
|
||||
{ LEDStates_e::LedStateStoppedStreaming, { false, true, {{LED_OFF, 1000}} } },
|
||||
{LEDStates_e::LedStateNone, {/*isError*/ false, /*repeat*/ false, {{LED_OFF, 1000}}}},
|
||||
{LEDStates_e::LedStateStreaming, {false, /*repeat steady*/ true, {{LED_ON, 1000}}}},
|
||||
{LEDStates_e::LedStateStoppedStreaming, {false, true, {{LED_OFF, 1000}}}},
|
||||
// CameraError: double blink pattern repeating
|
||||
{ LEDStates_e::CameraError, { true, true, {{ {LED_ON,300}, {LED_OFF,300}, {LED_ON,300}, {LED_OFF,700} }} } },
|
||||
{LEDStates_e::CameraError, {true, true, {{{LED_ON, 300}, {LED_OFF, 300}, {LED_ON, 300}, {LED_OFF, 700}}}}},
|
||||
// WiFiStateConnecting: balanced slow blink 400/400
|
||||
{ LEDStates_e::WiFiStateConnecting, { false, true, {{ {LED_ON,400}, {LED_OFF,400} }} } },
|
||||
{LEDStates_e::WiFiStateConnecting, {false, true, {{{LED_ON, 400}, {LED_OFF, 400}}}}},
|
||||
// WiFiStateConnected: short 3 quick flashes then done (was long noisy burst before)
|
||||
{ LEDStates_e::WiFiStateConnected, { false, false, {{ {LED_ON,150}, {LED_OFF,150}, {LED_ON,150}, {LED_OFF,150}, {LED_ON,150}, {LED_OFF,600} }} } },
|
||||
{LEDStates_e::WiFiStateConnected, {false, false, {{{LED_ON, 150}, {LED_OFF, 150}, {LED_ON, 150}, {LED_OFF, 150}, {LED_ON, 150}, {LED_OFF, 600}}}}},
|
||||
// WiFiStateError: asymmetric attention pattern (fast, pause, long, pause, fast)
|
||||
{ LEDStates_e::WiFiStateError, { true, true, {{ {LED_ON,200}, {LED_OFF,100}, {LED_ON,500}, {LED_OFF,300} }} } },
|
||||
{LEDStates_e::WiFiStateError, {true, true, {{{LED_ON, 200}, {LED_OFF, 100}, {LED_ON, 500}, {LED_OFF, 300}}}}},
|
||||
};
|
||||
|
||||
LEDManager::LEDManager(gpio_num_t pin, gpio_num_t illumninator_led_pin,
|
||||
QueueHandle_t ledStateQueue, std::shared_ptr<ProjectConfig> deviceConfig) : blink_led_pin(pin),
|
||||
illumninator_led_pin(illumninator_led_pin),
|
||||
ledStateQueue(ledStateQueue),
|
||||
currentState(LEDStates_e::LedStateNone),
|
||||
deviceConfig(deviceConfig)
|
||||
LEDManager::LEDManager(gpio_num_t pin, gpio_num_t illumninator_led_pin, QueueHandle_t ledStateQueue, std::shared_ptr<ProjectConfig> deviceConfig)
|
||||
: blink_led_pin(pin),
|
||||
illumninator_led_pin(illumninator_led_pin),
|
||||
ledStateQueue(ledStateQueue),
|
||||
currentState(LEDStates_e::LedStateNone),
|
||||
deviceConfig(deviceConfig)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -52,22 +52,17 @@ void LEDManager::setup()
|
||||
ESP_LOGI(LED_MANAGER_TAG, "Setting dutyCycle to: %lu ", dutyCycle);
|
||||
|
||||
ledc_timer_config_t ledc_timer = {
|
||||
.speed_mode = LEDC_LOW_SPEED_MODE,
|
||||
.duty_resolution = resolution,
|
||||
.timer_num = LEDC_TIMER_0,
|
||||
.freq_hz = freq,
|
||||
.clk_cfg = LEDC_AUTO_CLK};
|
||||
.speed_mode = LEDC_LOW_SPEED_MODE, .duty_resolution = resolution, .timer_num = LEDC_TIMER_0, .freq_hz = freq, .clk_cfg = LEDC_AUTO_CLK};
|
||||
|
||||
ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));
|
||||
|
||||
ledc_channel_config_t ledc_channel = {
|
||||
.gpio_num = this->illumninator_led_pin,
|
||||
.speed_mode = LEDC_LOW_SPEED_MODE,
|
||||
.channel = LEDC_CHANNEL_0,
|
||||
.intr_type = LEDC_INTR_DISABLE,
|
||||
.timer_sel = LEDC_TIMER_0,
|
||||
.duty = dutyCycle,
|
||||
.hpoint = 0};
|
||||
ledc_channel_config_t ledc_channel = {.gpio_num = this->illumninator_led_pin,
|
||||
.speed_mode = LEDC_LOW_SPEED_MODE,
|
||||
.channel = LEDC_CHANNEL_0,
|
||||
.intr_type = LEDC_INTR_DISABLE,
|
||||
.timer_sel = LEDC_TIMER_0,
|
||||
.duty = dutyCycle,
|
||||
.hpoint = 0};
|
||||
|
||||
ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
|
||||
#endif
|
||||
@@ -190,14 +185,14 @@ void LEDManager::setExternalLEDDutyCycle(uint8_t dutyPercent)
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, dutyCycle));
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0));
|
||||
#else
|
||||
(void)dutyPercent; // unused
|
||||
(void)dutyPercent; // unused
|
||||
ESP_LOGW(LED_MANAGER_TAG, "CONFIG_LED_EXTERNAL_CONTROL not enabled; ignoring duty update");
|
||||
#endif
|
||||
}
|
||||
|
||||
void HandleLEDDisplayTask(void *pvParameter)
|
||||
void HandleLEDDisplayTask(void* pvParameter)
|
||||
{
|
||||
auto *ledManager = static_cast<LEDManager *>(pvParameter);
|
||||
auto* ledManager = static_cast<LEDManager*>(pvParameter);
|
||||
TickType_t lastWakeTime = xTaskGetTickCount();
|
||||
|
||||
while (true)
|
||||
|
||||
@@ -11,13 +11,13 @@
|
||||
#endif
|
||||
|
||||
#include <esp_log.h>
|
||||
#include <ProjectConfig.hpp>
|
||||
#include <StateManager.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <helpers.hpp>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <StateManager.hpp>
|
||||
#include <ProjectConfig.hpp>
|
||||
#include <helpers.hpp>
|
||||
|
||||
// it kinda looks like different boards have these states swapped
|
||||
#define LED_OFF 1
|
||||
@@ -25,57 +25,62 @@
|
||||
|
||||
struct BlinkPatterns_t
|
||||
{
|
||||
int state;
|
||||
int delayTime;
|
||||
int state;
|
||||
int delayTime;
|
||||
};
|
||||
|
||||
struct LEDStage
|
||||
{
|
||||
bool isError;
|
||||
bool isRepeatable;
|
||||
std::vector<BlinkPatterns_t> patterns;
|
||||
bool isError;
|
||||
bool isRepeatable;
|
||||
std::vector<BlinkPatterns_t> patterns;
|
||||
};
|
||||
|
||||
typedef std::unordered_map<LEDStates_e, LEDStage>
|
||||
ledStateMap_t;
|
||||
typedef std::unordered_map<LEDStates_e, LEDStage> ledStateMap_t;
|
||||
|
||||
class LEDManager
|
||||
{
|
||||
public:
|
||||
LEDManager(gpio_num_t blink_led_pin, gpio_num_t illumninator_led_pin, QueueHandle_t ledStateQueue, std::shared_ptr<ProjectConfig> deviceConfig);
|
||||
public:
|
||||
LEDManager(gpio_num_t blink_led_pin, gpio_num_t illumninator_led_pin, QueueHandle_t ledStateQueue, std::shared_ptr<ProjectConfig> deviceConfig);
|
||||
|
||||
void setup();
|
||||
void handleLED();
|
||||
size_t getTimeToDelayFor() const { return timeToDelayFor; }
|
||||
void setup();
|
||||
void handleLED();
|
||||
size_t getTimeToDelayFor() const
|
||||
{
|
||||
return timeToDelayFor;
|
||||
}
|
||||
|
||||
// Apply new external LED PWM duty cycle immediately (0-100)
|
||||
void setExternalLEDDutyCycle(uint8_t dutyPercent);
|
||||
uint8_t getExternalLEDDutyCycle() const { return deviceConfig ? deviceConfig->getDeviceConfig().led_external_pwm_duty_cycle : 0; }
|
||||
// Apply new external LED PWM duty cycle immediately (0-100)
|
||||
void setExternalLEDDutyCycle(uint8_t dutyPercent);
|
||||
uint8_t getExternalLEDDutyCycle() const
|
||||
{
|
||||
return deviceConfig ? deviceConfig->getDeviceConfig().led_external_pwm_duty_cycle : 0;
|
||||
}
|
||||
|
||||
private:
|
||||
void toggleLED(bool state) const;
|
||||
void displayCurrentPattern();
|
||||
void updateState(LEDStates_e newState);
|
||||
private:
|
||||
void toggleLED(bool state) const;
|
||||
void displayCurrentPattern();
|
||||
void updateState(LEDStates_e newState);
|
||||
|
||||
gpio_num_t blink_led_pin;
|
||||
gpio_num_t illumninator_led_pin;
|
||||
QueueHandle_t ledStateQueue;
|
||||
gpio_num_t blink_led_pin;
|
||||
gpio_num_t illumninator_led_pin;
|
||||
QueueHandle_t ledStateQueue;
|
||||
|
||||
static ledStateMap_t ledStateMap;
|
||||
static ledStateMap_t ledStateMap;
|
||||
|
||||
LEDStates_e buffer;
|
||||
LEDStates_e currentState;
|
||||
std::shared_ptr<ProjectConfig> deviceConfig;
|
||||
LEDStates_e buffer;
|
||||
LEDStates_e currentState;
|
||||
std::shared_ptr<ProjectConfig> deviceConfig;
|
||||
|
||||
size_t currentPatternIndex = 0;
|
||||
size_t timeToDelayFor = 100;
|
||||
bool finishedPattern = false;
|
||||
size_t currentPatternIndex = 0;
|
||||
size_t timeToDelayFor = 100;
|
||||
bool finishedPattern = false;
|
||||
|
||||
#if defined(CONFIG_LED_EXTERNAL_CONTROL) && defined(CONFIG_LED_EXTERNAL_AS_DEBUG)
|
||||
bool hasStoredExternalDuty = false;
|
||||
uint32_t storedExternalDuty = 0; // raw 0-255
|
||||
bool hasStoredExternalDuty = false;
|
||||
uint32_t storedExternalDuty = 0; // raw 0-255
|
||||
#endif
|
||||
};
|
||||
|
||||
void HandleLEDDisplayTask(void *pvParameter);
|
||||
void HandleLEDDisplayTask(void* pvParameter);
|
||||
#endif
|
||||
@@ -1,55 +1,55 @@
|
||||
#include "MDNSManager.hpp"
|
||||
|
||||
static const char *MDNS_MANAGER_TAG = "[MDNS MANAGER]";
|
||||
static const char* MDNS_MANAGER_TAG = "[MDNS MANAGER]";
|
||||
|
||||
MDNSManager::MDNSManager(std::shared_ptr<ProjectConfig> projectConfig, QueueHandle_t eventQueue) : projectConfig(projectConfig), eventQueue(eventQueue) {}
|
||||
|
||||
esp_err_t MDNSManager::start()
|
||||
{
|
||||
const std::string &mdnsName = "_openiristracker";
|
||||
const std::string& mdnsName = "_openiristracker";
|
||||
|
||||
{
|
||||
SystemEvent event = {EventSource::MDNS, MDNSState_e::MDNSState_Starting};
|
||||
xQueueSend(this->eventQueue, &event, 10);
|
||||
}
|
||||
{
|
||||
SystemEvent event = {EventSource::MDNS, MDNSState_e::MDNSState_Starting};
|
||||
xQueueSend(this->eventQueue, &event, 10);
|
||||
}
|
||||
|
||||
esp_err_t result = mdns_init();
|
||||
if (result != ESP_OK)
|
||||
{
|
||||
SystemEvent event = {EventSource::MDNS, MDNSState_e::MDNSState_Error};
|
||||
esp_err_t result = mdns_init();
|
||||
if (result != ESP_OK)
|
||||
{
|
||||
SystemEvent event = {EventSource::MDNS, MDNSState_e::MDNSState_Error};
|
||||
xQueueSend(this->eventQueue, &event, 10);
|
||||
ESP_LOGE(MDNS_MANAGER_TAG, "Failed to initialize mDNS server: %s", esp_err_to_name(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
auto mdnsConfig = projectConfig->getMDNSConfig();
|
||||
result = mdns_hostname_set(mdnsConfig.hostname.c_str());
|
||||
if (result != ESP_OK)
|
||||
{
|
||||
SystemEvent event = {EventSource::MDNS, MDNSState_e::MDNSState_Error};
|
||||
xQueueSend(this->eventQueue, &event, 10);
|
||||
ESP_LOGE(MDNS_MANAGER_TAG, "Failed to set hostname: %s", esp_err_to_name(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
mdns_txt_item_t serviceTxtData[3] = {
|
||||
{"stream_port", "80"},
|
||||
{"api_port", "81"},
|
||||
};
|
||||
|
||||
mdns_service_add(nullptr, mdnsName.c_str(), "_tcp", 80, serviceTxtData, 2);
|
||||
result = mdns_service_instance_name_set(mdnsName.c_str(), "_tcp", mdnsName.c_str());
|
||||
if (result != ESP_OK)
|
||||
{
|
||||
SystemEvent event = {EventSource::MDNS, MDNSState_e::MDNSState_Error};
|
||||
xQueueSend(this->eventQueue, &event, 10);
|
||||
|
||||
ESP_LOGE(MDNS_MANAGER_TAG, "Failed to set mDNS instance name: %s", esp_err_to_name(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
SystemEvent event = {EventSource::MDNS, MDNSState_e::MDNSState_Started};
|
||||
xQueueSend(this->eventQueue, &event, 10);
|
||||
ESP_LOGE(MDNS_MANAGER_TAG, "Failed to initialize mDNS server: %s", esp_err_to_name(result));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
auto mdnsConfig = projectConfig->getMDNSConfig();
|
||||
result = mdns_hostname_set(mdnsConfig.hostname.c_str());
|
||||
if (result != ESP_OK)
|
||||
{
|
||||
SystemEvent event = {EventSource::MDNS, MDNSState_e::MDNSState_Error};
|
||||
xQueueSend(this->eventQueue, &event, 10);
|
||||
ESP_LOGE(MDNS_MANAGER_TAG, "Failed to set hostname: %s", esp_err_to_name(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
mdns_txt_item_t serviceTxtData[3] = {
|
||||
{"stream_port", "80"},
|
||||
{"api_port", "81"},
|
||||
};
|
||||
|
||||
mdns_service_add(nullptr, mdnsName.c_str(), "_tcp", 80, serviceTxtData, 2);
|
||||
result = mdns_service_instance_name_set(mdnsName.c_str(), "_tcp", mdnsName.c_str());
|
||||
if (result != ESP_OK)
|
||||
{
|
||||
SystemEvent event = {EventSource::MDNS, MDNSState_e::MDNSState_Error};
|
||||
xQueueSend(this->eventQueue, &event, 10);
|
||||
|
||||
ESP_LOGE(MDNS_MANAGER_TAG, "Failed to set mDNS instance name: %s", esp_err_to_name(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
SystemEvent event = {EventSource::MDNS, MDNSState_e::MDNSState_Started};
|
||||
xQueueSend(this->eventQueue, &event, 10);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
#pragma once
|
||||
#ifndef MDNSMANAGER_HPP
|
||||
#define MDNSMANAGER_HPP
|
||||
#include <string>
|
||||
#include <ProjectConfig.hpp>
|
||||
#include <StateManager.hpp>
|
||||
#include <string>
|
||||
#include "esp_log.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
@@ -11,13 +11,13 @@
|
||||
|
||||
class MDNSManager
|
||||
{
|
||||
private:
|
||||
std::shared_ptr<ProjectConfig> projectConfig;
|
||||
QueueHandle_t eventQueue;
|
||||
private:
|
||||
std::shared_ptr<ProjectConfig> projectConfig;
|
||||
QueueHandle_t eventQueue;
|
||||
|
||||
public:
|
||||
MDNSManager(std::shared_ptr<ProjectConfig> projectConfig, QueueHandle_t eventQueue);
|
||||
esp_err_t start();
|
||||
public:
|
||||
MDNSManager(std::shared_ptr<ProjectConfig> projectConfig, QueueHandle_t eventQueue);
|
||||
esp_err_t start();
|
||||
};
|
||||
|
||||
#endif // MDNSMANAGER_HPP
|
||||
#endif // MDNSMANAGER_HPP
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32)
|
||||
#include <esp_log.h>
|
||||
|
||||
static const char *TAG = "[AdcSampler]";
|
||||
static const char* TAG = "[AdcSampler]";
|
||||
|
||||
// Static member initialization
|
||||
adc_oneshot_unit_handle_t AdcSampler::shared_unit_ = nullptr;
|
||||
@@ -22,11 +22,11 @@ AdcSampler::~AdcSampler()
|
||||
{
|
||||
if (cali_handle_)
|
||||
{
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
adc_cali_delete_scheme_curve_fitting(cali_handle_);
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32)
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32)
|
||||
adc_cali_delete_scheme_line_fitting(cali_handle_);
|
||||
#endif
|
||||
#endif
|
||||
cali_handle_ = nullptr;
|
||||
}
|
||||
}
|
||||
@@ -68,8 +68,8 @@ bool AdcSampler::init(int gpio, adc_atten_t atten, adc_bitwidth_t bitwidth, size
|
||||
// Try calibration (requires eFuse data)
|
||||
// ESP32-S3 uses curve-fitting, ESP32 uses line-fitting
|
||||
esp_err_t cal_err = ESP_FAIL;
|
||||
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
// ESP32-S3 curve fitting calibration
|
||||
adc_cali_curve_fitting_config_t cal_cfg = {
|
||||
.unit_id = unit_,
|
||||
@@ -78,7 +78,7 @@ bool AdcSampler::init(int gpio, adc_atten_t atten, adc_bitwidth_t bitwidth, size
|
||||
.bitwidth = bitwidth_,
|
||||
};
|
||||
cal_err = adc_cali_create_scheme_curve_fitting(&cal_cfg, &cali_handle_);
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32)
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32)
|
||||
// ESP32 line-fitting calibration is per-unit, not per-channel
|
||||
adc_cali_line_fitting_config_t cal_cfg = {
|
||||
.unit_id = unit_,
|
||||
@@ -86,8 +86,8 @@ bool AdcSampler::init(int gpio, adc_atten_t atten, adc_bitwidth_t bitwidth, size
|
||||
.bitwidth = bitwidth_,
|
||||
};
|
||||
cal_err = adc_cali_create_scheme_line_fitting(&cal_cfg, &cali_handle_);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
if (cal_err == ESP_OK)
|
||||
{
|
||||
cali_inited_ = true;
|
||||
@@ -187,11 +187,10 @@ bool AdcSampler::configure_channel(int gpio, adc_atten_t atten, adc_bitwidth_t b
|
||||
esp_err_t err = adc_oneshot_config_channel(shared_unit_, channel_, &chan_cfg);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "adc_oneshot_config_channel failed (GPIO %d, CH %d): %s",
|
||||
gpio, static_cast<int>(channel_), esp_err_to_name(err));
|
||||
ESP_LOGE(TAG, "adc_oneshot_config_channel failed (GPIO %d, CH %d): %s", gpio, static_cast<int>(channel_), esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32
|
||||
#endif // CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32
|
||||
|
||||
@@ -21,10 +21,10 @@
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32)
|
||||
#include "esp_adc/adc_oneshot.h"
|
||||
#include <vector>
|
||||
#include "esp_adc/adc_cali.h"
|
||||
#include "esp_adc/adc_cali_scheme.h"
|
||||
#include <vector>
|
||||
#include "esp_adc/adc_oneshot.h"
|
||||
|
||||
/**
|
||||
* @class AdcSampler
|
||||
@@ -35,15 +35,15 @@
|
||||
*/
|
||||
class AdcSampler
|
||||
{
|
||||
public:
|
||||
public:
|
||||
AdcSampler() = default;
|
||||
~AdcSampler();
|
||||
|
||||
// Non-copyable, non-movable (owns hardware resources)
|
||||
AdcSampler(const AdcSampler &) = delete;
|
||||
AdcSampler &operator=(const AdcSampler &) = delete;
|
||||
AdcSampler(AdcSampler &&) = delete;
|
||||
AdcSampler &operator=(AdcSampler &&) = delete;
|
||||
AdcSampler(const AdcSampler&) = delete;
|
||||
AdcSampler& operator=(const AdcSampler&) = delete;
|
||||
AdcSampler(AdcSampler&&) = delete;
|
||||
AdcSampler& operator=(AdcSampler&&) = delete;
|
||||
|
||||
/**
|
||||
* @brief Initialize the ADC channel on the shared ADC1 oneshot unit
|
||||
@@ -53,10 +53,7 @@ public:
|
||||
* @param window_size Moving average window size (>=1)
|
||||
* @return true on success, false on failure
|
||||
*/
|
||||
bool init(int gpio,
|
||||
adc_atten_t atten = ADC_ATTEN_DB_12,
|
||||
adc_bitwidth_t bitwidth = ADC_BITWIDTH_DEFAULT,
|
||||
size_t window_size = 1);
|
||||
bool init(int gpio, adc_atten_t atten = ADC_ATTEN_DB_12, adc_bitwidth_t bitwidth = ADC_BITWIDTH_DEFAULT, size_t window_size = 1);
|
||||
|
||||
/**
|
||||
* @brief Perform one ADC conversion and update filtered value
|
||||
@@ -68,15 +65,21 @@ public:
|
||||
* @brief Get the filtered ADC reading in millivolts
|
||||
* @return Filtered voltage in mV
|
||||
*/
|
||||
int getFilteredMilliVolts() const { return filtered_mv_; }
|
||||
int getFilteredMilliVolts() const
|
||||
{
|
||||
return filtered_mv_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if ADC sampling is supported on current platform
|
||||
* @return true if supported
|
||||
*/
|
||||
static constexpr bool isSupported() { return true; }
|
||||
static constexpr bool isSupported()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
// Hardware initialization helpers
|
||||
bool ensure_unit();
|
||||
bool configure_channel(int gpio, adc_atten_t atten, adc_bitwidth_t bitwidth);
|
||||
@@ -85,7 +88,7 @@ private:
|
||||
* @brief Platform-specific GPIO to ADC channel mapping
|
||||
* @note Implemented separately in AdcSampler_esp32.cpp and AdcSampler_esp32s3.cpp
|
||||
*/
|
||||
static bool map_gpio_to_channel(int gpio, adc_unit_t &unit, adc_channel_t &channel);
|
||||
static bool map_gpio_to_channel(int gpio, adc_unit_t& unit, adc_channel_t& channel);
|
||||
|
||||
// Shared ADC1 oneshot handle (single instance for all AdcSampler objects)
|
||||
static adc_oneshot_unit_handle_t shared_unit_;
|
||||
@@ -110,10 +113,22 @@ private:
|
||||
// Stub for unsupported targets to keep interfaces consistent
|
||||
class AdcSampler
|
||||
{
|
||||
public:
|
||||
bool init(int /*gpio*/, int /*atten*/ = 0, int /*bitwidth*/ = 0, size_t /*window_size*/ = 1) { return false; }
|
||||
bool sampleOnce() { return false; }
|
||||
int getFilteredMilliVolts() const { return 0; }
|
||||
static constexpr bool isSupported() { return false; }
|
||||
public:
|
||||
bool init(int /*gpio*/, int /*atten*/ = 0, int /*bitwidth*/ = 0, size_t /*window_size*/ = 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool sampleOnce()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int getFilteredMilliVolts() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static constexpr bool isSupported()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32)
|
||||
|
||||
bool AdcSampler::map_gpio_to_channel(int gpio, adc_unit_t &unit, adc_channel_t &channel)
|
||||
bool AdcSampler::map_gpio_to_channel(int gpio, adc_unit_t& unit, adc_channel_t& channel)
|
||||
{
|
||||
unit = ADC_UNIT_1; // Only use ADC1 to avoid Wi-Fi conflict
|
||||
unit = ADC_UNIT_1; // Only use ADC1 to avoid Wi-Fi conflict
|
||||
|
||||
// ESP32: ADC1 GPIO mapping (GPIO32-39)
|
||||
switch (gpio)
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
|
||||
bool AdcSampler::map_gpio_to_channel(int gpio, adc_unit_t &unit, adc_channel_t &channel)
|
||||
bool AdcSampler::map_gpio_to_channel(int gpio, adc_unit_t& unit, adc_channel_t& channel)
|
||||
{
|
||||
unit = ADC_UNIT_1; // Only use ADC1 to avoid Wi-Fi conflict
|
||||
unit = ADC_UNIT_1; // Only use ADC1 to avoid Wi-Fi conflict
|
||||
|
||||
// ESP32-S3: ADC1 on GPIO1–10 → CH0–CH9
|
||||
if (gpio >= 1 && gpio <= 10)
|
||||
@@ -36,4 +36,4 @@ bool AdcSampler::map_gpio_to_channel(int gpio, adc_unit_t &unit, adc_channel_t &
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif // CONFIG_IDF_TARGET_ESP32S3
|
||||
#endif // CONFIG_IDF_TARGET_ESP32S3
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "BatteryMonitor.hpp"
|
||||
#include <esp_log.h>
|
||||
|
||||
static const char *TAG = "[BatteryMonitor]";
|
||||
static const char* TAG = "[BatteryMonitor]";
|
||||
|
||||
bool BatteryMonitor::setup()
|
||||
{
|
||||
@@ -86,8 +86,8 @@ float BatteryMonitor::voltageToPercentage(int voltage_mv)
|
||||
// Linear interpolation between lookup table points
|
||||
for (size_t i = 0; i < soc_lookup_.size() - 1; ++i)
|
||||
{
|
||||
const auto &high = soc_lookup_[i];
|
||||
const auto &low = soc_lookup_[i + 1];
|
||||
const auto& high = soc_lookup_[i];
|
||||
const auto& low = soc_lookup_[i + 1];
|
||||
|
||||
if (volts <= high.voltage_mv && volts >= low.voltage_mv)
|
||||
{
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include "AdcSampler.hpp"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
|
||||
/**
|
||||
* @struct BatteryStatus
|
||||
* @brief Battery status information
|
||||
@@ -48,7 +47,7 @@ struct BatteryStatus
|
||||
*/
|
||||
class BatteryMonitor
|
||||
{
|
||||
public:
|
||||
public:
|
||||
BatteryMonitor() = default;
|
||||
~BatteryMonitor() = default;
|
||||
|
||||
@@ -87,7 +86,7 @@ public:
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
/**
|
||||
* @brief Li-ion/Li-Po voltage to SOC lookup table entry
|
||||
*/
|
||||
@@ -102,7 +101,7 @@ private:
|
||||
* Based on typical 3.7V nominal Li-ion/Li-Po cell characteristics
|
||||
*/
|
||||
static constexpr std::array<VoltageSOC, 12> soc_lookup_ = {{
|
||||
{4200.0f, 100.0f}, // Fully charged
|
||||
{4200.0f, 100.0f}, // Fully charged
|
||||
{4060.0f, 90.0f},
|
||||
{3980.0f, 80.0f},
|
||||
{3920.0f, 70.0f},
|
||||
@@ -112,10 +111,10 @@ private:
|
||||
{3770.0f, 30.0f},
|
||||
{3740.0f, 20.0f},
|
||||
{3680.0f, 10.0f},
|
||||
{3450.0f, 5.0f}, // Low battery warning
|
||||
{3300.0f, 0.0f}, // Empty / cutoff voltage
|
||||
{3450.0f, 5.0f}, // Low battery warning
|
||||
{3300.0f, 0.0f}, // Empty / cutoff voltage
|
||||
}};
|
||||
|
||||
float scale_{1.0f}; // Voltage divider scaling factor
|
||||
mutable AdcSampler adc_; // ADC sampler instance (BSP layer)
|
||||
float scale_{1.0f}; // Voltage divider scaling factor
|
||||
mutable AdcSampler adc_; // ADC sampler instance (BSP layer)
|
||||
};
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "CurrentMonitor.hpp"
|
||||
#include <esp_log.h>
|
||||
|
||||
static const char *TAG = "[CurrentMonitor]";
|
||||
static const char* TAG = "[CurrentMonitor]";
|
||||
|
||||
void CurrentMonitor::setup()
|
||||
{
|
||||
@@ -40,7 +40,7 @@ float CurrentMonitor::getCurrentMilliAmps() const
|
||||
if (!AdcSampler::isSupported())
|
||||
return 0.0f;
|
||||
|
||||
const int shunt_milliohm = CONFIG_MONITORING_LED_SHUNT_MILLIOHM; // mΩ
|
||||
const int shunt_milliohm = CONFIG_MONITORING_LED_SHUNT_MILLIOHM; // mΩ
|
||||
if (shunt_milliohm <= 0)
|
||||
return 0.0f;
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
*/
|
||||
|
||||
#include <cstdint>
|
||||
#include "sdkconfig.h"
|
||||
#include "AdcSampler.hpp"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
/**
|
||||
* @class CurrentMonitor
|
||||
@@ -34,7 +34,7 @@
|
||||
*/
|
||||
class CurrentMonitor
|
||||
{
|
||||
public:
|
||||
public:
|
||||
CurrentMonitor() = default;
|
||||
~CurrentMonitor() = default;
|
||||
|
||||
@@ -54,8 +54,8 @@ public:
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
mutable AdcSampler adc_; // ADC sampler instance (BSP layer)
|
||||
private:
|
||||
mutable AdcSampler adc_; // ADC sampler instance (BSP layer)
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include <esp_log.h>
|
||||
#include "sdkconfig.h"
|
||||
|
||||
static const char *TAG = "[MonitoringManager]";
|
||||
static const char* TAG = "[MonitoringManager]";
|
||||
|
||||
void MonitoringManager::setup()
|
||||
{
|
||||
@@ -18,11 +18,8 @@ void MonitoringManager::setup()
|
||||
if (CurrentMonitor::isEnabled())
|
||||
{
|
||||
cm_.setup();
|
||||
ESP_LOGI(TAG, "LED current monitoring enabled. Interval=%dms, Samples=%d, Gain=%d, R=%dmΩ",
|
||||
CONFIG_MONITORING_LED_INTERVAL_MS,
|
||||
CONFIG_MONITORING_LED_SAMPLES,
|
||||
CONFIG_MONITORING_LED_GAIN,
|
||||
CONFIG_MONITORING_LED_SHUNT_MILLIOHM);
|
||||
ESP_LOGI(TAG, "LED current monitoring enabled. Interval=%dms, Samples=%d, Gain=%d, R=%dmΩ", CONFIG_MONITORING_LED_INTERVAL_MS,
|
||||
CONFIG_MONITORING_LED_SAMPLES, CONFIG_MONITORING_LED_GAIN, CONFIG_MONITORING_LED_SHUNT_MILLIOHM);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -36,11 +33,8 @@ void MonitoringManager::setup()
|
||||
if (BatteryMonitor::isEnabled())
|
||||
{
|
||||
bm_.setup();
|
||||
ESP_LOGI(TAG, "Battery monitoring enabled. Interval=%dms, Samples=%d, R-Top=%dΩ, R-Bottom=%dΩ",
|
||||
CONFIG_MONITORING_BATTERY_INTERVAL_MS,
|
||||
CONFIG_MONITORING_BATTERY_SAMPLES,
|
||||
CONFIG_MONITORING_BATTERY_DIVIDER_R_TOP_OHM,
|
||||
CONFIG_MONITORING_BATTERY_DIVIDER_R_BOTTOM_OHM);
|
||||
ESP_LOGI(TAG, "Battery monitoring enabled. Interval=%dms, Samples=%d, R-Top=%dΩ, R-Bottom=%dΩ", CONFIG_MONITORING_BATTERY_INTERVAL_MS,
|
||||
CONFIG_MONITORING_BATTERY_SAMPLES, CONFIG_MONITORING_BATTERY_DIVIDER_R_TOP_OHM, CONFIG_MONITORING_BATTERY_DIVIDER_R_BOTTOM_OHM);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -77,9 +71,9 @@ void MonitoringManager::stop()
|
||||
}
|
||||
}
|
||||
|
||||
void MonitoringManager::taskEntry(void *arg)
|
||||
void MonitoringManager::taskEntry(void* arg)
|
||||
{
|
||||
static_cast<MonitoringManager *>(arg)->run();
|
||||
static_cast<MonitoringManager*>(arg)->run();
|
||||
}
|
||||
|
||||
void MonitoringManager::run()
|
||||
@@ -105,7 +99,7 @@ void MonitoringManager::run()
|
||||
while (true)
|
||||
{
|
||||
now_tick = xTaskGetTickCount();
|
||||
TickType_t wait_ticks = pdMS_TO_TICKS(50); // Default wait time
|
||||
TickType_t wait_ticks = pdMS_TO_TICKS(50); // Default wait time
|
||||
|
||||
#if CONFIG_MONITORING_LED_CURRENT
|
||||
if (CurrentMonitor::isEnabled() && now_tick >= next_tick_led)
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
*/
|
||||
class MonitoringManager
|
||||
{
|
||||
public:
|
||||
public:
|
||||
MonitoringManager() = default;
|
||||
~MonitoringManager() = default;
|
||||
|
||||
@@ -57,8 +57,8 @@ public:
|
||||
return CurrentMonitor::isEnabled() || BatteryMonitor::isEnabled();
|
||||
}
|
||||
|
||||
private:
|
||||
static void taskEntry(void *arg);
|
||||
private:
|
||||
static void taskEntry(void* arg);
|
||||
void run();
|
||||
|
||||
TaskHandle_t task_{nullptr};
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
#include "OpenIrisTasks.hpp"
|
||||
|
||||
void restart_the_board(void *arg) {
|
||||
esp_restart();
|
||||
void restart_the_board(void* arg)
|
||||
{
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
void OpenIrisTasks::ScheduleRestart(const int milliseconds)
|
||||
{
|
||||
esp_timer_handle_t timerHandle;
|
||||
constexpr esp_timer_create_args_t args = {
|
||||
.callback = &restart_the_board,
|
||||
.arg = nullptr,
|
||||
.name = "restartBoard"};
|
||||
esp_timer_handle_t timerHandle;
|
||||
constexpr esp_timer_create_args_t args = {.callback = &restart_the_board, .arg = nullptr, .name = "restartBoard"};
|
||||
|
||||
if (const auto result = esp_timer_create(&args, &timerHandle); result == ESP_OK)
|
||||
{
|
||||
esp_timer_start_once(timerHandle, milliseconds);
|
||||
}
|
||||
if (const auto result = esp_timer_create(&args, &timerHandle); result == ESP_OK)
|
||||
{
|
||||
esp_timer_start_once(timerHandle, milliseconds);
|
||||
}
|
||||
}
|
||||
@@ -2,14 +2,14 @@
|
||||
#ifndef OPENIRISTASKS_HPP
|
||||
#define OPENIRISTASKS_HPP
|
||||
|
||||
#include "helpers.hpp"
|
||||
#include "esp_system.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_system.h"
|
||||
#include "helpers.hpp"
|
||||
|
||||
namespace OpenIrisTasks
|
||||
{
|
||||
void ScheduleRestart(int milliseconds);
|
||||
void ScheduleRestart(int milliseconds);
|
||||
};
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,83 +5,83 @@
|
||||
#ifndef PREFERENCES_HPP
|
||||
#define PREFERENCES_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include "esp_log.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "nvs.h"
|
||||
#include "nvs_flash.h"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
PT_I8,
|
||||
PT_U8,
|
||||
PT_I16,
|
||||
PT_U16,
|
||||
PT_I32,
|
||||
PT_U32,
|
||||
PT_I64,
|
||||
PT_U64,
|
||||
PT_STR,
|
||||
PT_BLOB,
|
||||
PT_INVALID
|
||||
PT_I8,
|
||||
PT_U8,
|
||||
PT_I16,
|
||||
PT_U16,
|
||||
PT_I32,
|
||||
PT_U32,
|
||||
PT_I64,
|
||||
PT_U64,
|
||||
PT_STR,
|
||||
PT_BLOB,
|
||||
PT_INVALID
|
||||
} PreferenceType;
|
||||
|
||||
class Preferences
|
||||
{
|
||||
protected:
|
||||
uint32_t _handle;
|
||||
bool _started;
|
||||
bool _readOnly;
|
||||
protected:
|
||||
uint32_t _handle;
|
||||
bool _started;
|
||||
bool _readOnly;
|
||||
|
||||
public:
|
||||
Preferences();
|
||||
~Preferences();
|
||||
public:
|
||||
Preferences();
|
||||
~Preferences();
|
||||
|
||||
bool begin(const char *name, bool readOnly = false, const char *partition_label = nullptr);
|
||||
void end();
|
||||
bool begin(const char* name, bool readOnly = false, const char* partition_label = nullptr);
|
||||
void end();
|
||||
|
||||
bool clear();
|
||||
bool remove(const char *key);
|
||||
bool clear();
|
||||
bool remove(const char* key);
|
||||
|
||||
size_t putChar(const char *key, int8_t value);
|
||||
size_t putUChar(const char *key, uint8_t value);
|
||||
size_t putShort(const char *key, int16_t value);
|
||||
size_t putUShort(const char *key, uint16_t value);
|
||||
size_t putInt(const char *key, int32_t value);
|
||||
size_t putUInt(const char *key, uint32_t value);
|
||||
size_t putLong(const char *key, int32_t value);
|
||||
size_t putULong(const char *key, uint32_t value);
|
||||
size_t putLong64(const char *key, int64_t value);
|
||||
size_t putULong64(const char *key, uint64_t value);
|
||||
size_t putFloat(const char *key, float_t value);
|
||||
size_t putDouble(const char *key, double_t value);
|
||||
size_t putBool(const char *key, bool value);
|
||||
size_t putString(const char *key, const char *value);
|
||||
size_t putString(const char *key, std::string value);
|
||||
size_t putBytes(const char *key, const void *value, size_t len);
|
||||
size_t putChar(const char* key, int8_t value);
|
||||
size_t putUChar(const char* key, uint8_t value);
|
||||
size_t putShort(const char* key, int16_t value);
|
||||
size_t putUShort(const char* key, uint16_t value);
|
||||
size_t putInt(const char* key, int32_t value);
|
||||
size_t putUInt(const char* key, uint32_t value);
|
||||
size_t putLong(const char* key, int32_t value);
|
||||
size_t putULong(const char* key, uint32_t value);
|
||||
size_t putLong64(const char* key, int64_t value);
|
||||
size_t putULong64(const char* key, uint64_t value);
|
||||
size_t putFloat(const char* key, float_t value);
|
||||
size_t putDouble(const char* key, double_t value);
|
||||
size_t putBool(const char* key, bool value);
|
||||
size_t putString(const char* key, const char* value);
|
||||
size_t putString(const char* key, std::string value);
|
||||
size_t putBytes(const char* key, const void* value, size_t len);
|
||||
|
||||
bool isKey(const char *key);
|
||||
PreferenceType getType(const char *key);
|
||||
int8_t getChar(const char *key, int8_t defaultValue = 0);
|
||||
uint8_t getUChar(const char *key, uint8_t defaultValue = 0);
|
||||
int16_t getShort(const char *key, int16_t defaultValue = 0);
|
||||
uint16_t getUShort(const char *key, uint16_t defaultValue = 0);
|
||||
int32_t getInt(const char *key, int32_t defaultValue = 0);
|
||||
uint32_t getUInt(const char *key, uint32_t defaultValue = 0);
|
||||
int32_t getLong(const char *key, int32_t defaultValue = 0);
|
||||
uint32_t getULong(const char *key, uint32_t defaultValue = 0);
|
||||
int64_t getLong64(const char *key, int64_t defaultValue = 0);
|
||||
uint64_t getULong64(const char *key, uint64_t defaultValue = 0);
|
||||
float_t getFloat(const char *key, float_t defaultValue = NAN);
|
||||
double_t getDouble(const char *key, double_t defaultValue = NAN);
|
||||
bool getBool(const char *key, bool defaultValue = false);
|
||||
size_t getString(const char *key, char *value, size_t maxLen);
|
||||
std::string getString(const char *key, std::string defaultValue = std::string());
|
||||
size_t getBytesLength(const char *key);
|
||||
size_t getBytes(const char *key, void *buf, size_t maxLen);
|
||||
size_t freeEntries();
|
||||
bool isKey(const char* key);
|
||||
PreferenceType getType(const char* key);
|
||||
int8_t getChar(const char* key, int8_t defaultValue = 0);
|
||||
uint8_t getUChar(const char* key, uint8_t defaultValue = 0);
|
||||
int16_t getShort(const char* key, int16_t defaultValue = 0);
|
||||
uint16_t getUShort(const char* key, uint16_t defaultValue = 0);
|
||||
int32_t getInt(const char* key, int32_t defaultValue = 0);
|
||||
uint32_t getUInt(const char* key, uint32_t defaultValue = 0);
|
||||
int32_t getLong(const char* key, int32_t defaultValue = 0);
|
||||
uint32_t getULong(const char* key, uint32_t defaultValue = 0);
|
||||
int64_t getLong64(const char* key, int64_t defaultValue = 0);
|
||||
uint64_t getULong64(const char* key, uint64_t defaultValue = 0);
|
||||
float_t getFloat(const char* key, float_t defaultValue = NAN);
|
||||
double_t getDouble(const char* key, double_t defaultValue = NAN);
|
||||
bool getBool(const char* key, bool defaultValue = false);
|
||||
size_t getString(const char* key, char* value, size_t maxLen);
|
||||
std::string getString(const char* key, std::string defaultValue = std::string());
|
||||
size_t getBytesLength(const char* key);
|
||||
size_t getBytes(const char* key, void* buf, size_t maxLen);
|
||||
size_t freeEntries();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2,166 +2,163 @@
|
||||
#ifndef PROJECT_CONFIG_MODELS_HPP
|
||||
#define PROJECT_CONFIG_MODELS_HPP
|
||||
|
||||
#include <Preferences.hpp>
|
||||
#include <helpers.hpp>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <helpers.hpp>
|
||||
#include "sdkconfig.h"
|
||||
#include <Preferences.hpp>
|
||||
#include "esp_log.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
struct BaseConfigModel
|
||||
{
|
||||
BaseConfigModel(Preferences *pref) : pref(pref) {}
|
||||
BaseConfigModel(Preferences* pref) : pref(pref) {}
|
||||
|
||||
void load();
|
||||
void save();
|
||||
std::string toRepresentation();
|
||||
void load();
|
||||
void save();
|
||||
std::string toRepresentation();
|
||||
|
||||
Preferences *pref;
|
||||
Preferences* pref;
|
||||
};
|
||||
|
||||
enum class StreamingMode
|
||||
{
|
||||
SETUP,
|
||||
UVC,
|
||||
WIFI,
|
||||
SETUP,
|
||||
UVC,
|
||||
WIFI,
|
||||
};
|
||||
|
||||
struct DeviceMode_t : BaseConfigModel
|
||||
{
|
||||
StreamingMode mode;
|
||||
explicit DeviceMode_t(Preferences *pref) : BaseConfigModel(pref), mode(StreamingMode::SETUP) {}
|
||||
StreamingMode mode;
|
||||
explicit DeviceMode_t(Preferences* pref) : BaseConfigModel(pref), mode(StreamingMode::SETUP) {}
|
||||
|
||||
void load()
|
||||
{
|
||||
// Default mode can be controlled via sdkconfig:
|
||||
// - If CONFIG_START_IN_UVC_MODE is enabled, default to UVC
|
||||
// - Otherwise default to SETUP
|
||||
int default_mode =
|
||||
void load()
|
||||
{
|
||||
// Default mode can be controlled via sdkconfig:
|
||||
// - If CONFIG_START_IN_UVC_MODE is enabled, default to UVC
|
||||
// - Otherwise default to SETUP
|
||||
int default_mode =
|
||||
#if CONFIG_START_IN_UVC_MODE
|
||||
static_cast<int>(StreamingMode::UVC);
|
||||
static_cast<int>(StreamingMode::UVC);
|
||||
#else
|
||||
static_cast<int>(StreamingMode::SETUP);
|
||||
static_cast<int>(StreamingMode::SETUP);
|
||||
#endif
|
||||
|
||||
int stored_mode = this->pref->getInt("mode", default_mode);
|
||||
this->mode = static_cast<StreamingMode>(stored_mode);
|
||||
ESP_LOGI("DeviceMode", "Loaded device mode: %d", stored_mode);
|
||||
}
|
||||
int stored_mode = this->pref->getInt("mode", default_mode);
|
||||
this->mode = static_cast<StreamingMode>(stored_mode);
|
||||
ESP_LOGI("DeviceMode", "Loaded device mode: %d", stored_mode);
|
||||
}
|
||||
|
||||
void save() const
|
||||
{
|
||||
this->pref->putInt("mode", static_cast<int>(this->mode));
|
||||
ESP_LOGI("DeviceMode", "Saved device mode: %d", static_cast<int>(this->mode));
|
||||
}
|
||||
void save() const
|
||||
{
|
||||
this->pref->putInt("mode", static_cast<int>(this->mode));
|
||||
ESP_LOGI("DeviceMode", "Saved device mode: %d", static_cast<int>(this->mode));
|
||||
}
|
||||
};
|
||||
|
||||
struct DeviceConfig_t : BaseConfigModel
|
||||
{
|
||||
DeviceConfig_t(Preferences *pref) : BaseConfigModel(pref) {}
|
||||
DeviceConfig_t(Preferences* pref) : BaseConfigModel(pref) {}
|
||||
|
||||
std::string OTALogin;
|
||||
std::string OTAPassword;
|
||||
int led_external_pwm_duty_cycle;
|
||||
int OTAPort;
|
||||
std::string OTALogin;
|
||||
std::string OTAPassword;
|
||||
int led_external_pwm_duty_cycle;
|
||||
int OTAPort;
|
||||
|
||||
void load()
|
||||
{
|
||||
this->OTALogin = this->pref->getString("OTALogin", "openiris");
|
||||
this->OTAPassword = this->pref->getString("OTAPassword", "openiris");
|
||||
this->OTAPort = this->pref->getInt("OTAPort", 3232);
|
||||
void load()
|
||||
{
|
||||
this->OTALogin = this->pref->getString("OTALogin", "openiris");
|
||||
this->OTAPassword = this->pref->getString("OTAPassword", "openiris");
|
||||
this->OTAPort = this->pref->getInt("OTAPort", 3232);
|
||||
#if CONFIG_LED_EXTERNAL_PWM_DUTY_CYCLE
|
||||
this->led_external_pwm_duty_cycle = this->pref->getInt("led_ext_pwm", CONFIG_LED_EXTERNAL_PWM_DUTY_CYCLE);
|
||||
this->led_external_pwm_duty_cycle = this->pref->getInt("led_ext_pwm", CONFIG_LED_EXTERNAL_PWM_DUTY_CYCLE);
|
||||
#else
|
||||
this->led_external_pwm_duty_cycle = this->pref->getInt("led_ext_pwm", 100);
|
||||
this->led_external_pwm_duty_cycle = this->pref->getInt("led_ext_pwm", 100);
|
||||
#endif
|
||||
};
|
||||
};
|
||||
|
||||
void save() const
|
||||
{
|
||||
this->pref->putString("OTALogin", this->OTALogin.c_str());
|
||||
this->pref->putString("OTAPassword", this->OTAPassword.c_str());
|
||||
this->pref->putInt("OTAPort", this->OTAPort);
|
||||
this->pref->putInt("led_ext_pwm", this->led_external_pwm_duty_cycle);
|
||||
};
|
||||
void save() const
|
||||
{
|
||||
this->pref->putString("OTALogin", this->OTALogin.c_str());
|
||||
this->pref->putString("OTAPassword", this->OTAPassword.c_str());
|
||||
this->pref->putInt("OTAPort", this->OTAPort);
|
||||
this->pref->putInt("led_ext_pwm", this->led_external_pwm_duty_cycle);
|
||||
};
|
||||
|
||||
std::string toRepresentation() const
|
||||
{
|
||||
return Helpers::format_string(
|
||||
"\"device_config\": {\"OTALogin\": \"%s\", \"OTAPassword\": \"%s\", "
|
||||
"\"OTAPort\": %u, \"led_external_pwm_duty_cycle\": %u}",
|
||||
this->OTALogin.c_str(), this->OTAPassword.c_str(), this->OTAPort, this->led_external_pwm_duty_cycle);
|
||||
};
|
||||
std::string toRepresentation() const
|
||||
{
|
||||
return Helpers::format_string(
|
||||
"\"device_config\": {\"OTALogin\": \"%s\", \"OTAPassword\": \"%s\", "
|
||||
"\"OTAPort\": %u, \"led_external_pwm_duty_cycle\": %u}",
|
||||
this->OTALogin.c_str(), this->OTAPassword.c_str(), this->OTAPort, this->led_external_pwm_duty_cycle);
|
||||
};
|
||||
};
|
||||
|
||||
struct MDNSConfig_t : BaseConfigModel
|
||||
{
|
||||
MDNSConfig_t(Preferences *pref) : BaseConfigModel(pref) {}
|
||||
MDNSConfig_t(Preferences* pref) : BaseConfigModel(pref) {}
|
||||
|
||||
std::string hostname;
|
||||
std::string hostname;
|
||||
|
||||
void load()
|
||||
{
|
||||
// Default hostname comes from GENERAL_ADVERTISED_NAME (unified advertised name)
|
||||
std::string default_hostname = CONFIG_GENERAL_ADVERTISED_NAME;
|
||||
if (default_hostname.empty())
|
||||
void load()
|
||||
{
|
||||
default_hostname = "openiristracker";
|
||||
}
|
||||
// Default hostname comes from GENERAL_ADVERTISED_NAME (unified advertised name)
|
||||
std::string default_hostname = CONFIG_GENERAL_ADVERTISED_NAME;
|
||||
if (default_hostname.empty())
|
||||
{
|
||||
default_hostname = "openiristracker";
|
||||
}
|
||||
|
||||
this->hostname = this->pref->getString("hostname", default_hostname);
|
||||
};
|
||||
this->hostname = this->pref->getString("hostname", default_hostname);
|
||||
};
|
||||
|
||||
void save() const
|
||||
{
|
||||
this->pref->putString("hostname", this->hostname.c_str());
|
||||
};
|
||||
void save() const
|
||||
{
|
||||
this->pref->putString("hostname", this->hostname.c_str());
|
||||
};
|
||||
|
||||
std::string toRepresentation()
|
||||
{
|
||||
return Helpers::format_string(
|
||||
"\"mdns_config\": {\"hostname\": \"%s\"}",
|
||||
this->hostname.c_str());
|
||||
};
|
||||
std::string toRepresentation()
|
||||
{
|
||||
return Helpers::format_string("\"mdns_config\": {\"hostname\": \"%s\"}", this->hostname.c_str());
|
||||
};
|
||||
};
|
||||
|
||||
struct CameraConfig_t : BaseConfigModel
|
||||
{
|
||||
CameraConfig_t(Preferences *pref) : BaseConfigModel(pref) {}
|
||||
CameraConfig_t(Preferences* pref) : BaseConfigModel(pref) {}
|
||||
|
||||
uint8_t vflip;
|
||||
uint8_t href;
|
||||
uint8_t framesize;
|
||||
uint8_t quality;
|
||||
uint8_t brightness;
|
||||
uint8_t vflip;
|
||||
uint8_t href;
|
||||
uint8_t framesize;
|
||||
uint8_t quality;
|
||||
uint8_t brightness;
|
||||
|
||||
void load()
|
||||
{
|
||||
this->vflip = this->pref->getInt("vflip", 0);
|
||||
this->href = this->pref->getInt("href", 0);
|
||||
this->framesize = this->pref->getInt("framesize", 5);
|
||||
this->quality = this->pref->getInt("quality", 7);
|
||||
this->brightness = this->pref->getInt("brightness", 2);
|
||||
};
|
||||
void load()
|
||||
{
|
||||
this->vflip = this->pref->getInt("vflip", 0);
|
||||
this->href = this->pref->getInt("href", 0);
|
||||
this->framesize = this->pref->getInt("framesize", 5);
|
||||
this->quality = this->pref->getInt("quality", 7);
|
||||
this->brightness = this->pref->getInt("brightness", 2);
|
||||
};
|
||||
|
||||
void save() const
|
||||
{
|
||||
this->pref->putInt("vflip", this->vflip);
|
||||
this->pref->putInt("href", this->href);
|
||||
this->pref->putInt("framesize", this->framesize);
|
||||
this->pref->putInt("quality", this->quality);
|
||||
this->pref->putInt("brightness", this->brightness);
|
||||
};
|
||||
void save() const
|
||||
{
|
||||
this->pref->putInt("vflip", this->vflip);
|
||||
this->pref->putInt("href", this->href);
|
||||
this->pref->putInt("framesize", this->framesize);
|
||||
this->pref->putInt("quality", this->quality);
|
||||
this->pref->putInt("brightness", this->brightness);
|
||||
};
|
||||
|
||||
std::string toRepresentation()
|
||||
{
|
||||
return Helpers::format_string(
|
||||
"\"camera_config\": {\"vflip\": %d,\"framesize\": %d,\"href\": "
|
||||
"%d,\"quality\": %d,\"brightness\": %d}",
|
||||
this->vflip, this->framesize, this->href, this->quality,
|
||||
this->brightness);
|
||||
};
|
||||
std::string toRepresentation()
|
||||
{
|
||||
return Helpers::format_string(
|
||||
"\"camera_config\": {\"vflip\": %d,\"framesize\": %d,\"href\": "
|
||||
"%d,\"quality\": %d,\"brightness\": %d}",
|
||||
this->vflip, this->framesize, this->href, this->quality, this->brightness);
|
||||
};
|
||||
};
|
||||
|
||||
// with wifi, we have to work a bit differently
|
||||
@@ -170,177 +167,154 @@ struct CameraConfig_t : BaseConfigModel
|
||||
// save them under an indexed name and load them as such.
|
||||
struct WiFiConfig_t : BaseConfigModel
|
||||
{
|
||||
// default constructor used for loading
|
||||
WiFiConfig_t(Preferences *pref) : BaseConfigModel(pref) {}
|
||||
// default constructor used for loading
|
||||
WiFiConfig_t(Preferences* pref) : BaseConfigModel(pref) {}
|
||||
|
||||
WiFiConfig_t(
|
||||
Preferences *pref,
|
||||
const uint8_t index,
|
||||
std::string name,
|
||||
std::string ssid,
|
||||
std::string password,
|
||||
const uint8_t channel,
|
||||
const uint8_t power)
|
||||
: BaseConfigModel(pref),
|
||||
index(index),
|
||||
name(std::move(name)),
|
||||
ssid(std::move(ssid)),
|
||||
password(std::move(password)),
|
||||
channel(channel),
|
||||
power(power) {}
|
||||
WiFiConfig_t(Preferences* pref, const uint8_t index, std::string name, std::string ssid, std::string password, const uint8_t channel, const uint8_t power)
|
||||
: BaseConfigModel(pref), index(index), name(std::move(name)), ssid(std::move(ssid)), password(std::move(password)), channel(channel), power(power)
|
||||
{
|
||||
}
|
||||
|
||||
uint8_t index;
|
||||
std::string name;
|
||||
std::string ssid;
|
||||
std::string password;
|
||||
uint8_t channel;
|
||||
uint8_t power;
|
||||
uint8_t index;
|
||||
std::string name;
|
||||
std::string ssid;
|
||||
std::string password;
|
||||
uint8_t channel;
|
||||
uint8_t power;
|
||||
|
||||
void load(const uint8_t index)
|
||||
{
|
||||
this->index = index;
|
||||
char buffer[2];
|
||||
void load(const uint8_t index)
|
||||
{
|
||||
this->index = index;
|
||||
char buffer[2];
|
||||
|
||||
auto const iter_str = std::string(Helpers::itoa(index, buffer, 10));
|
||||
this->name = this->pref->getString(("name" + iter_str).c_str(), "");
|
||||
this->ssid = this->pref->getString(("ssid" + iter_str).c_str(), "");
|
||||
this->password = this->pref->getString(("password" + iter_str).c_str(), "");
|
||||
this->channel = this->pref->getUInt(("channel" + iter_str).c_str());
|
||||
this->power = this->pref->getUInt(("power" + iter_str).c_str());
|
||||
auto const iter_str = std::string(Helpers::itoa(index, buffer, 10));
|
||||
this->name = this->pref->getString(("name" + iter_str).c_str(), "");
|
||||
this->ssid = this->pref->getString(("ssid" + iter_str).c_str(), "");
|
||||
this->password = this->pref->getString(("password" + iter_str).c_str(), "");
|
||||
this->channel = this->pref->getUInt(("channel" + iter_str).c_str());
|
||||
this->power = this->pref->getUInt(("power" + iter_str).c_str());
|
||||
|
||||
ESP_LOGI("WiFiConfig", "Loaded network %d: name=%s, ssid=%s, channel=%d",
|
||||
index, this->name.c_str(), this->ssid.c_str(), this->channel);
|
||||
};
|
||||
ESP_LOGI("WiFiConfig", "Loaded network %d: name=%s, ssid=%s, channel=%d", index, this->name.c_str(), this->ssid.c_str(), this->channel);
|
||||
};
|
||||
|
||||
void save() const
|
||||
{
|
||||
char buffer[2];
|
||||
auto const iter_str = std::string(Helpers::itoa(this->index, buffer, 10));
|
||||
void save() const
|
||||
{
|
||||
char buffer[2];
|
||||
auto const iter_str = std::string(Helpers::itoa(this->index, buffer, 10));
|
||||
|
||||
this->pref->putString(("name" + iter_str).c_str(), this->name.c_str());
|
||||
this->pref->putString(("ssid" + iter_str).c_str(), this->ssid.c_str());
|
||||
this->pref->putString(("password" + iter_str).c_str(), this->password.c_str());
|
||||
this->pref->putUInt(("channel" + iter_str).c_str(), this->channel);
|
||||
this->pref->putUInt(("power" + iter_str).c_str(), this->power);
|
||||
this->pref->putString(("name" + iter_str).c_str(), this->name.c_str());
|
||||
this->pref->putString(("ssid" + iter_str).c_str(), this->ssid.c_str());
|
||||
this->pref->putString(("password" + iter_str).c_str(), this->password.c_str());
|
||||
this->pref->putUInt(("channel" + iter_str).c_str(), this->channel);
|
||||
this->pref->putUInt(("power" + iter_str).c_str(), this->power);
|
||||
|
||||
ESP_LOGI("WiFiConfig", "Saved network %d: name=%s, ssid=%s, channel=%d",
|
||||
this->index, this->name.c_str(), this->ssid.c_str(), this->channel);
|
||||
};
|
||||
ESP_LOGI("WiFiConfig", "Saved network %d: name=%s, ssid=%s, channel=%d", this->index, this->name.c_str(), this->ssid.c_str(), this->channel);
|
||||
};
|
||||
|
||||
std::string toRepresentation()
|
||||
{
|
||||
return Helpers::format_string(
|
||||
"{\"name\": \"%s\", \"ssid\": \"%s\", \"password\": \"%s\", \"channel\": %u, \"power\": %u}",
|
||||
this->name.c_str(), this->ssid.c_str(), this->password.c_str(),
|
||||
this->channel, this->power);
|
||||
};
|
||||
std::string toRepresentation()
|
||||
{
|
||||
return Helpers::format_string("{\"name\": \"%s\", \"ssid\": \"%s\", \"password\": \"%s\", \"channel\": %u, \"power\": %u}", this->name.c_str(),
|
||||
this->ssid.c_str(), this->password.c_str(), this->channel, this->power);
|
||||
};
|
||||
};
|
||||
|
||||
struct AP_WiFiConfig_t : BaseConfigModel
|
||||
{
|
||||
AP_WiFiConfig_t(Preferences *pref) : BaseConfigModel(pref) {}
|
||||
AP_WiFiConfig_t(Preferences* pref) : BaseConfigModel(pref) {}
|
||||
|
||||
std::string ssid;
|
||||
std::string password;
|
||||
uint8_t channel;
|
||||
std::string ssid;
|
||||
std::string password;
|
||||
uint8_t channel;
|
||||
|
||||
void load()
|
||||
{
|
||||
this->ssid = this->pref->getString("apSSID", CONFIG_WIFI_AP_SSID);
|
||||
this->password = this->pref->getString("apPassword", CONFIG_WIFI_AP_PASSWORD);
|
||||
};
|
||||
void load()
|
||||
{
|
||||
this->ssid = this->pref->getString("apSSID", CONFIG_WIFI_AP_SSID);
|
||||
this->password = this->pref->getString("apPassword", CONFIG_WIFI_AP_PASSWORD);
|
||||
};
|
||||
|
||||
void save() const
|
||||
{
|
||||
this->pref->putString("apSSID", this->ssid.c_str());
|
||||
this->pref->putString("apPass", this->password.c_str());
|
||||
this->pref->putUInt("apChannel", this->channel);
|
||||
};
|
||||
void save() const
|
||||
{
|
||||
this->pref->putString("apSSID", this->ssid.c_str());
|
||||
this->pref->putString("apPass", this->password.c_str());
|
||||
this->pref->putUInt("apChannel", this->channel);
|
||||
};
|
||||
|
||||
std::string toRepresentation()
|
||||
{
|
||||
return Helpers::format_string(
|
||||
"\"ap_wifi_config\": {\"ssid\": \"%s\", \"password\": \"%s\", "
|
||||
"\"channel\": %u}",
|
||||
this->ssid.c_str(), this->password.c_str(), this->channel);
|
||||
};
|
||||
std::string toRepresentation()
|
||||
{
|
||||
return Helpers::format_string(
|
||||
"\"ap_wifi_config\": {\"ssid\": \"%s\", \"password\": \"%s\", "
|
||||
"\"channel\": %u}",
|
||||
this->ssid.c_str(), this->password.c_str(), this->channel);
|
||||
};
|
||||
};
|
||||
|
||||
struct WiFiTxPower_t : BaseConfigModel
|
||||
{
|
||||
WiFiTxPower_t(Preferences *pref) : BaseConfigModel(pref) {}
|
||||
WiFiTxPower_t(Preferences* pref) : BaseConfigModel(pref) {}
|
||||
|
||||
uint8_t power;
|
||||
uint8_t power;
|
||||
|
||||
void load()
|
||||
{
|
||||
this->power = this->pref->getUInt("txpower", 52);
|
||||
};
|
||||
void load()
|
||||
{
|
||||
this->power = this->pref->getUInt("txpower", 52);
|
||||
};
|
||||
|
||||
void save() const
|
||||
{
|
||||
this->pref->putUInt("txpower", this->power);
|
||||
};
|
||||
void save() const
|
||||
{
|
||||
this->pref->putUInt("txpower", this->power);
|
||||
};
|
||||
|
||||
std::string toRepresentation()
|
||||
{
|
||||
return Helpers::format_string("\"wifi_tx_power\": {\"power\": %u}", this->power);
|
||||
};
|
||||
std::string toRepresentation()
|
||||
{
|
||||
return Helpers::format_string("\"wifi_tx_power\": {\"power\": %u}", this->power);
|
||||
};
|
||||
};
|
||||
|
||||
class TrackerConfig_t
|
||||
{
|
||||
public:
|
||||
DeviceConfig_t device;
|
||||
DeviceMode_t device_mode;
|
||||
CameraConfig_t camera;
|
||||
std::vector<WiFiConfig_t> networks;
|
||||
AP_WiFiConfig_t ap_network;
|
||||
MDNSConfig_t mdns;
|
||||
WiFiTxPower_t txpower;
|
||||
public:
|
||||
DeviceConfig_t device;
|
||||
DeviceMode_t device_mode;
|
||||
CameraConfig_t camera;
|
||||
std::vector<WiFiConfig_t> networks;
|
||||
AP_WiFiConfig_t ap_network;
|
||||
MDNSConfig_t mdns;
|
||||
WiFiTxPower_t txpower;
|
||||
|
||||
TrackerConfig_t(
|
||||
DeviceConfig_t device,
|
||||
DeviceMode_t device_mode,
|
||||
CameraConfig_t camera,
|
||||
std::vector<WiFiConfig_t> networks,
|
||||
AP_WiFiConfig_t ap_network,
|
||||
MDNSConfig_t mdns,
|
||||
WiFiTxPower_t txpower) : device(std::move(device)),
|
||||
device_mode(std::move(device_mode)),
|
||||
camera(std::move(camera)),
|
||||
networks(std::move(networks)),
|
||||
ap_network(std::move(ap_network)),
|
||||
mdns(std::move(mdns)),
|
||||
txpower(std::move(txpower)) {}
|
||||
|
||||
std::string toRepresentation()
|
||||
{
|
||||
std::string WifiConfigRepresentation;
|
||||
|
||||
// we need a valid json representation, so we can't have a dangling comma at the end
|
||||
if (!this->networks.empty())
|
||||
TrackerConfig_t(DeviceConfig_t device, DeviceMode_t device_mode, CameraConfig_t camera, std::vector<WiFiConfig_t> networks, AP_WiFiConfig_t ap_network,
|
||||
MDNSConfig_t mdns, WiFiTxPower_t txpower)
|
||||
: device(std::move(device)),
|
||||
device_mode(std::move(device_mode)),
|
||||
camera(std::move(camera)),
|
||||
networks(std::move(networks)),
|
||||
ap_network(std::move(ap_network)),
|
||||
mdns(std::move(mdns)),
|
||||
txpower(std::move(txpower))
|
||||
{
|
||||
if (this->networks.size() > 1)
|
||||
{
|
||||
for (auto i = 0; i < this->networks.size() - 1; i++)
|
||||
{
|
||||
WifiConfigRepresentation += Helpers::format_string("%s, ", this->networks[i].toRepresentation().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
WifiConfigRepresentation += Helpers::format_string("%s", this->networks[networks.size() - 1].toRepresentation().c_str());
|
||||
}
|
||||
|
||||
return Helpers::format_string(
|
||||
"{%s, %s, %s, \"networks\": [%s], %s, %s}",
|
||||
this->device.toRepresentation().c_str(),
|
||||
this->mdns.toRepresentation().c_str(),
|
||||
this->camera.toRepresentation().c_str(),
|
||||
WifiConfigRepresentation.c_str(),
|
||||
this->ap_network.toRepresentation().c_str(),
|
||||
this->txpower.toRepresentation().c_str());
|
||||
}
|
||||
std::string toRepresentation()
|
||||
{
|
||||
std::string WifiConfigRepresentation;
|
||||
|
||||
// we need a valid json representation, so we can't have a dangling comma at the end
|
||||
if (!this->networks.empty())
|
||||
{
|
||||
if (this->networks.size() > 1)
|
||||
{
|
||||
for (auto i = 0; i < this->networks.size() - 1; i++)
|
||||
{
|
||||
WifiConfigRepresentation += Helpers::format_string("%s, ", this->networks[i].toRepresentation().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
WifiConfigRepresentation += Helpers::format_string("%s", this->networks[networks.size() - 1].toRepresentation().c_str());
|
||||
}
|
||||
|
||||
return Helpers::format_string("{%s, %s, %s, \"networks\": [%s], %s, %s}", this->device.toRepresentation().c_str(),
|
||||
this->mdns.toRepresentation().c_str(), this->camera.toRepresentation().c_str(), WifiConfigRepresentation.c_str(),
|
||||
this->ap_network.toRepresentation().c_str(), this->txpower.toRepresentation().c_str());
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -2,90 +2,88 @@
|
||||
|
||||
static auto CONFIGURATION_TAG = "[CONFIGURATION]";
|
||||
|
||||
int getNetworkCount(Preferences *pref)
|
||||
int getNetworkCount(Preferences* pref)
|
||||
{
|
||||
return pref->getInt("networkcount", 0);
|
||||
return pref->getInt("networkcount", 0);
|
||||
}
|
||||
|
||||
void saveNetworkCount(Preferences *pref, const int count)
|
||||
void saveNetworkCount(Preferences* pref, const int count)
|
||||
{
|
||||
pref->putInt("networkcount", count);
|
||||
pref->putInt("networkcount", count);
|
||||
}
|
||||
|
||||
ProjectConfig::ProjectConfig(Preferences *pref) : pref(pref),
|
||||
_already_loaded(false),
|
||||
config(DeviceConfig_t(pref),
|
||||
DeviceMode_t(pref),
|
||||
CameraConfig_t(pref),
|
||||
std::vector<WiFiConfig_t>{},
|
||||
AP_WiFiConfig_t(pref),
|
||||
MDNSConfig_t(pref),
|
||||
WiFiTxPower_t(pref)) {}
|
||||
ProjectConfig::ProjectConfig(Preferences* pref)
|
||||
: pref(pref),
|
||||
_already_loaded(false),
|
||||
config(DeviceConfig_t(pref), DeviceMode_t(pref), CameraConfig_t(pref), std::vector<WiFiConfig_t>{}, AP_WiFiConfig_t(pref), MDNSConfig_t(pref),
|
||||
WiFiTxPower_t(pref))
|
||||
{
|
||||
}
|
||||
|
||||
ProjectConfig::~ProjectConfig() = default;
|
||||
|
||||
void ProjectConfig::save() const
|
||||
{
|
||||
ESP_LOGD(CONFIGURATION_TAG, "Saving project config");
|
||||
this->config.device.save();
|
||||
this->config.device_mode.save();
|
||||
this->config.camera.save();
|
||||
this->config.mdns.save();
|
||||
this->config.txpower.save();
|
||||
this->config.ap_network.save();
|
||||
ESP_LOGD(CONFIGURATION_TAG, "Saving project config");
|
||||
this->config.device.save();
|
||||
this->config.device_mode.save();
|
||||
this->config.camera.save();
|
||||
this->config.mdns.save();
|
||||
this->config.txpower.save();
|
||||
this->config.ap_network.save();
|
||||
|
||||
auto const networks_count = static_cast<int>(this->config.networks.size());
|
||||
for (int i = 0; i < networks_count; i++)
|
||||
{
|
||||
this->config.networks[i].save();
|
||||
}
|
||||
auto const networks_count = static_cast<int>(this->config.networks.size());
|
||||
for (int i = 0; i < networks_count; i++)
|
||||
{
|
||||
this->config.networks[i].save();
|
||||
}
|
||||
|
||||
saveNetworkCount(this->pref, networks_count);
|
||||
this->pref->end(); // we call end() here to close the connection to the NVS partition, we
|
||||
// only do this because we call ESP.restart() next.
|
||||
saveNetworkCount(this->pref, networks_count);
|
||||
this->pref->end(); // we call end() here to close the connection to the NVS partition, we
|
||||
// only do this because we call ESP.restart() next.
|
||||
|
||||
// TODO add the restart task
|
||||
// https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/freertos_idf.html
|
||||
// OpenIrisTasks::ScheduleRestart(2000);
|
||||
// TODO add the restart task
|
||||
// https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/freertos_idf.html
|
||||
// OpenIrisTasks::ScheduleRestart(2000);
|
||||
}
|
||||
|
||||
void ProjectConfig::load()
|
||||
{
|
||||
ESP_LOGD(CONFIGURATION_TAG, "Loading project config");
|
||||
if (this->_already_loaded)
|
||||
{
|
||||
ESP_LOGW(CONFIGURATION_TAG, "Project config already loaded");
|
||||
return;
|
||||
}
|
||||
ESP_LOGD(CONFIGURATION_TAG, "Loading project config");
|
||||
if (this->_already_loaded)
|
||||
{
|
||||
ESP_LOGW(CONFIGURATION_TAG, "Project config already loaded");
|
||||
return;
|
||||
}
|
||||
|
||||
const bool success = this->pref->begin("openiris");
|
||||
const bool success = this->pref->begin("openiris");
|
||||
|
||||
ESP_LOGI(CONFIGURATION_TAG, "Config name: openiris");
|
||||
ESP_LOGI(CONFIGURATION_TAG, "Config loaded: %s", success ? "true" : "false");
|
||||
ESP_LOGI(CONFIGURATION_TAG, "Config name: openiris");
|
||||
ESP_LOGI(CONFIGURATION_TAG, "Config loaded: %s", success ? "true" : "false");
|
||||
|
||||
this->config.device.load();
|
||||
this->config.device_mode.load();
|
||||
this->config.camera.load();
|
||||
this->config.mdns.load();
|
||||
this->config.txpower.load();
|
||||
this->config.ap_network.load();
|
||||
this->config.device.load();
|
||||
this->config.device_mode.load();
|
||||
this->config.camera.load();
|
||||
this->config.mdns.load();
|
||||
this->config.txpower.load();
|
||||
this->config.ap_network.load();
|
||||
|
||||
const auto networks_count = getNetworkCount(this->pref);
|
||||
ESP_LOGD(CONFIGURATION_TAG, "Loading networks: %d", networks_count);
|
||||
for (int i = 0; i < getNetworkCount(this->pref); i++)
|
||||
{
|
||||
auto networkConfig = WiFiConfig_t(this->pref);
|
||||
networkConfig.load(i);
|
||||
this->config.networks.push_back(networkConfig);
|
||||
}
|
||||
const auto networks_count = getNetworkCount(this->pref);
|
||||
ESP_LOGD(CONFIGURATION_TAG, "Loading networks: %d", networks_count);
|
||||
for (int i = 0; i < getNetworkCount(this->pref); i++)
|
||||
{
|
||||
auto networkConfig = WiFiConfig_t(this->pref);
|
||||
networkConfig.load(i);
|
||||
this->config.networks.push_back(networkConfig);
|
||||
}
|
||||
|
||||
this->_already_loaded = true;
|
||||
this->_already_loaded = true;
|
||||
}
|
||||
|
||||
bool ProjectConfig::reset()
|
||||
{
|
||||
ESP_LOGW(CONFIGURATION_TAG, "Resetting project config");
|
||||
return this->pref->clear();
|
||||
ESP_LOGW(CONFIGURATION_TAG, "Resetting project config");
|
||||
return this->pref->clear();
|
||||
}
|
||||
|
||||
//**********************************************************************************************************************
|
||||
@@ -93,148 +91,128 @@ bool ProjectConfig::reset()
|
||||
//! DeviceConfig
|
||||
//*
|
||||
//**********************************************************************************************************************
|
||||
void ProjectConfig::setOTAConfig(const std::string &OTALogin,
|
||||
const std::string &OTAPassword,
|
||||
const int OTAPort)
|
||||
void ProjectConfig::setOTAConfig(const std::string& OTALogin, const std::string& OTAPassword, const int OTAPort)
|
||||
{
|
||||
ESP_LOGD(CONFIGURATION_TAG, "Updating device config");
|
||||
this->config.device.OTALogin.assign(OTALogin);
|
||||
this->config.device.OTAPassword.assign(OTAPassword);
|
||||
this->config.device.OTAPort = OTAPort;
|
||||
this->config.device.save();
|
||||
ESP_LOGD(CONFIGURATION_TAG, "Updating device config");
|
||||
this->config.device.OTALogin.assign(OTALogin);
|
||||
this->config.device.OTAPassword.assign(OTAPassword);
|
||||
this->config.device.OTAPort = OTAPort;
|
||||
this->config.device.save();
|
||||
}
|
||||
|
||||
void ProjectConfig::setLEDDUtyCycleConfig(int led_external_pwm_duty_cycle)
|
||||
{
|
||||
this->config.device.led_external_pwm_duty_cycle = led_external_pwm_duty_cycle;
|
||||
ESP_LOGI(CONFIGURATION_TAG, "Setting duty cycle to %d", led_external_pwm_duty_cycle);
|
||||
this->config.device.save();
|
||||
this->config.device.led_external_pwm_duty_cycle = led_external_pwm_duty_cycle;
|
||||
ESP_LOGI(CONFIGURATION_TAG, "Setting duty cycle to %d", led_external_pwm_duty_cycle);
|
||||
this->config.device.save();
|
||||
}
|
||||
|
||||
void ProjectConfig::setMDNSConfig(const std::string &hostname)
|
||||
void ProjectConfig::setMDNSConfig(const std::string& hostname)
|
||||
{
|
||||
ESP_LOGD(CONFIGURATION_TAG, "Updating MDNS config");
|
||||
this->config.mdns.hostname.assign(hostname);
|
||||
this->config.mdns.save();
|
||||
ESP_LOGD(CONFIGURATION_TAG, "Updating MDNS config");
|
||||
this->config.mdns.hostname.assign(hostname);
|
||||
this->config.mdns.save();
|
||||
}
|
||||
|
||||
void ProjectConfig::setCameraConfig(const uint8_t vflip,
|
||||
const uint8_t framesize,
|
||||
const uint8_t href,
|
||||
const uint8_t quality,
|
||||
const uint8_t brightness)
|
||||
void ProjectConfig::setCameraConfig(const uint8_t vflip, const uint8_t framesize, const uint8_t href, const uint8_t quality, const uint8_t brightness)
|
||||
{
|
||||
ESP_LOGD(CONFIGURATION_TAG, "Updating camera config");
|
||||
this->config.camera.vflip = vflip;
|
||||
this->config.camera.href = href;
|
||||
this->config.camera.framesize = framesize;
|
||||
this->config.camera.quality = quality;
|
||||
this->config.camera.brightness = brightness;
|
||||
this->config.camera.save();
|
||||
ESP_LOGD(CONFIGURATION_TAG, "Updating camera config");
|
||||
this->config.camera.vflip = vflip;
|
||||
this->config.camera.href = href;
|
||||
this->config.camera.framesize = framesize;
|
||||
this->config.camera.quality = quality;
|
||||
this->config.camera.brightness = brightness;
|
||||
this->config.camera.save();
|
||||
|
||||
ESP_LOGD(CONFIGURATION_TAG, "Updating Camera config");
|
||||
ESP_LOGD(CONFIGURATION_TAG, "Updating Camera config");
|
||||
}
|
||||
|
||||
void ProjectConfig::setWifiConfig(const std::string &networkName,
|
||||
const std::string &ssid,
|
||||
const std::string &password,
|
||||
uint8_t channel,
|
||||
uint8_t power)
|
||||
void ProjectConfig::setWifiConfig(const std::string& networkName, const std::string& ssid, const std::string& password, uint8_t channel, uint8_t power)
|
||||
{
|
||||
const auto size = this->config.networks.size();
|
||||
const auto size = this->config.networks.size();
|
||||
|
||||
const auto it = std::ranges::find_if(this->config.networks,
|
||||
[&](const WiFiConfig_t &network)
|
||||
{ return network.name == networkName; });
|
||||
const auto it = std::ranges::find_if(this->config.networks, [&](const WiFiConfig_t& network) { return network.name == networkName; });
|
||||
|
||||
if (it != this->config.networks.end())
|
||||
{
|
||||
if (it != this->config.networks.end())
|
||||
{
|
||||
ESP_LOGI(CONFIGURATION_TAG, "Found network %s, updating it ...", it->name.c_str());
|
||||
|
||||
ESP_LOGI(CONFIGURATION_TAG, "Found network %s, updating it ...",
|
||||
it->name.c_str());
|
||||
it->name = networkName;
|
||||
it->ssid = ssid;
|
||||
it->password = password;
|
||||
it->channel = channel;
|
||||
it->power = power;
|
||||
// Save the updated network immediately
|
||||
it->save();
|
||||
return;
|
||||
}
|
||||
|
||||
it->name = networkName;
|
||||
it->ssid = ssid;
|
||||
it->password = password;
|
||||
it->channel = channel;
|
||||
it->power = power;
|
||||
// Save the updated network immediately
|
||||
it->save();
|
||||
return;
|
||||
}
|
||||
if (size == 0)
|
||||
{
|
||||
ESP_LOGI(CONFIGURATION_TAG, "No networks, We're adding a new network");
|
||||
this->config.networks.emplace_back(this->pref, static_cast<uint8_t>(0), networkName, ssid, password, channel, power);
|
||||
// Save the new network immediately
|
||||
this->config.networks.back().save();
|
||||
saveNetworkCount(this->pref, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (size == 0)
|
||||
{
|
||||
ESP_LOGI(CONFIGURATION_TAG, "No networks, We're adding a new network");
|
||||
this->config.networks.emplace_back(this->pref, static_cast<uint8_t>(0), networkName, ssid, password, channel,
|
||||
power);
|
||||
// Save the new network immediately
|
||||
this->config.networks.back().save();
|
||||
saveNetworkCount(this->pref, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
// we're allowing to store up to three additional networks
|
||||
if (size < 3)
|
||||
{
|
||||
ESP_LOGI(CONFIGURATION_TAG, "We're adding a new network");
|
||||
// we don't have that network yet, we can add it as we still have some
|
||||
// space we're using emplace_back as push_back will create a copy of it,
|
||||
// we want to avoid that
|
||||
uint8_t last_index = getNetworkCount(this->pref);
|
||||
this->config.networks.emplace_back(this->pref, last_index, networkName, ssid, password, channel,
|
||||
power);
|
||||
// Save the new network immediately
|
||||
this->config.networks.back().save();
|
||||
saveNetworkCount(this->pref, static_cast<int>(this->config.networks.size()));
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGE(CONFIGURATION_TAG, "No more space for additional networks");
|
||||
}
|
||||
// we're allowing to store up to three additional networks
|
||||
if (size < 3)
|
||||
{
|
||||
ESP_LOGI(CONFIGURATION_TAG, "We're adding a new network");
|
||||
// we don't have that network yet, we can add it as we still have some
|
||||
// space we're using emplace_back as push_back will create a copy of it,
|
||||
// we want to avoid that
|
||||
uint8_t last_index = getNetworkCount(this->pref);
|
||||
this->config.networks.emplace_back(this->pref, last_index, networkName, ssid, password, channel, power);
|
||||
// Save the new network immediately
|
||||
this->config.networks.back().save();
|
||||
saveNetworkCount(this->pref, static_cast<int>(this->config.networks.size()));
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGE(CONFIGURATION_TAG, "No more space for additional networks");
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectConfig::deleteWifiConfig(const std::string &networkName)
|
||||
void ProjectConfig::deleteWifiConfig(const std::string& networkName)
|
||||
{
|
||||
if (const auto size = this->config.networks.size(); size == 0)
|
||||
{
|
||||
ESP_LOGI(CONFIGURATION_TAG, "No networks, nothing to delete");
|
||||
}
|
||||
if (const auto size = this->config.networks.size(); size == 0)
|
||||
{
|
||||
ESP_LOGI(CONFIGURATION_TAG, "No networks, nothing to delete");
|
||||
}
|
||||
|
||||
const auto it = std::ranges::find_if(this->config.networks,
|
||||
[&](const WiFiConfig_t &network)
|
||||
{ return network.name == networkName; });
|
||||
const auto it = std::ranges::find_if(this->config.networks, [&](const WiFiConfig_t& network) { return network.name == networkName; });
|
||||
|
||||
if (it != this->config.networks.end())
|
||||
{
|
||||
ESP_LOGI(CONFIGURATION_TAG, "Found network %s", it->name.c_str());
|
||||
this->config.networks.erase(it);
|
||||
ESP_LOGI(CONFIGURATION_TAG, "Deleted network %s", networkName.c_str());
|
||||
}
|
||||
if (it != this->config.networks.end())
|
||||
{
|
||||
ESP_LOGI(CONFIGURATION_TAG, "Found network %s", it->name.c_str());
|
||||
this->config.networks.erase(it);
|
||||
ESP_LOGI(CONFIGURATION_TAG, "Deleted network %s", networkName.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectConfig::setWiFiTxPower(uint8_t power)
|
||||
{
|
||||
this->config.txpower.power = power;
|
||||
this->config.txpower.save();
|
||||
ESP_LOGD(CONFIGURATION_TAG, "Updating wifi tx power");
|
||||
this->config.txpower.power = power;
|
||||
this->config.txpower.save();
|
||||
ESP_LOGD(CONFIGURATION_TAG, "Updating wifi tx power");
|
||||
}
|
||||
|
||||
void ProjectConfig::setAPWifiConfig(const std::string &ssid,
|
||||
const std::string &password,
|
||||
const uint8_t channel)
|
||||
void ProjectConfig::setAPWifiConfig(const std::string& ssid, const std::string& password, const uint8_t channel)
|
||||
{
|
||||
this->config.ap_network.ssid.assign(ssid);
|
||||
this->config.ap_network.password.assign(password);
|
||||
this->config.ap_network.channel = channel;
|
||||
this->config.ap_network.save();
|
||||
ESP_LOGD(CONFIGURATION_TAG, "Updating access point config");
|
||||
this->config.ap_network.ssid.assign(ssid);
|
||||
this->config.ap_network.password.assign(password);
|
||||
this->config.ap_network.channel = channel;
|
||||
this->config.ap_network.save();
|
||||
ESP_LOGD(CONFIGURATION_TAG, "Updating access point config");
|
||||
}
|
||||
|
||||
void ProjectConfig::setDeviceMode(const StreamingMode deviceMode)
|
||||
{
|
||||
this->config.device_mode.mode = deviceMode;
|
||||
this->config.device_mode.save(); // Save immediately
|
||||
this->config.device_mode.mode = deviceMode;
|
||||
this->config.device_mode.save(); // Save immediately
|
||||
}
|
||||
|
||||
//**********************************************************************************************************************
|
||||
@@ -243,41 +221,41 @@ void ProjectConfig::setDeviceMode(const StreamingMode deviceMode)
|
||||
//*
|
||||
//**********************************************************************************************************************
|
||||
|
||||
DeviceConfig_t &ProjectConfig::getDeviceConfig()
|
||||
DeviceConfig_t& ProjectConfig::getDeviceConfig()
|
||||
{
|
||||
return this->config.device;
|
||||
return this->config.device;
|
||||
}
|
||||
CameraConfig_t &ProjectConfig::getCameraConfig()
|
||||
CameraConfig_t& ProjectConfig::getCameraConfig()
|
||||
{
|
||||
return this->config.camera;
|
||||
return this->config.camera;
|
||||
}
|
||||
std::vector<WiFiConfig_t> &ProjectConfig::getWifiConfigs()
|
||||
std::vector<WiFiConfig_t>& ProjectConfig::getWifiConfigs()
|
||||
{
|
||||
return this->config.networks;
|
||||
return this->config.networks;
|
||||
}
|
||||
AP_WiFiConfig_t &ProjectConfig::getAPWifiConfig()
|
||||
AP_WiFiConfig_t& ProjectConfig::getAPWifiConfig()
|
||||
{
|
||||
return this->config.ap_network;
|
||||
return this->config.ap_network;
|
||||
}
|
||||
MDNSConfig_t &ProjectConfig::getMDNSConfig()
|
||||
MDNSConfig_t& ProjectConfig::getMDNSConfig()
|
||||
{
|
||||
return this->config.mdns;
|
||||
return this->config.mdns;
|
||||
}
|
||||
WiFiTxPower_t &ProjectConfig::getWiFiTxPowerConfig()
|
||||
WiFiTxPower_t& ProjectConfig::getWiFiTxPowerConfig()
|
||||
{
|
||||
return this->config.txpower;
|
||||
return this->config.txpower;
|
||||
}
|
||||
TrackerConfig_t &ProjectConfig::getTrackerConfig()
|
||||
TrackerConfig_t& ProjectConfig::getTrackerConfig()
|
||||
{
|
||||
return this->config;
|
||||
return this->config;
|
||||
}
|
||||
|
||||
DeviceMode_t &ProjectConfig::getDeviceModeConfig()
|
||||
DeviceMode_t& ProjectConfig::getDeviceModeConfig()
|
||||
{
|
||||
return this->config.device_mode;
|
||||
return this->config.device_mode;
|
||||
}
|
||||
|
||||
StreamingMode ProjectConfig::getDeviceMode()
|
||||
{
|
||||
return this->config.device_mode.mode;
|
||||
return this->config.device_mode.mode;
|
||||
}
|
||||
@@ -1,67 +1,55 @@
|
||||
#pragma once
|
||||
#ifndef PROJECT_CONFIG_HPP
|
||||
#define PROJECT_CONFIG_HPP
|
||||
#include "esp_log.h"
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <helpers.hpp>
|
||||
#include "Models.hpp"
|
||||
#include <Preferences.hpp>
|
||||
#include <algorithm>
|
||||
#include <helpers.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "Models.hpp"
|
||||
#include "esp_log.h"
|
||||
|
||||
int getNetworkCount(Preferences *pref);
|
||||
int getNetworkCount(Preferences* pref);
|
||||
|
||||
void saveNetworkCount(Preferences *pref, int count);
|
||||
void saveNetworkCount(Preferences* pref, int count);
|
||||
|
||||
class ProjectConfig
|
||||
{
|
||||
public:
|
||||
explicit ProjectConfig(Preferences *pref);
|
||||
virtual ~ProjectConfig();
|
||||
public:
|
||||
explicit ProjectConfig(Preferences* pref);
|
||||
virtual ~ProjectConfig();
|
||||
|
||||
void load();
|
||||
void save() const;
|
||||
void load();
|
||||
void save() const;
|
||||
|
||||
bool reset();
|
||||
bool reset();
|
||||
|
||||
DeviceConfig_t &getDeviceConfig();
|
||||
DeviceMode_t &getDeviceModeConfig();
|
||||
CameraConfig_t &getCameraConfig();
|
||||
std::vector<WiFiConfig_t> &getWifiConfigs();
|
||||
AP_WiFiConfig_t &getAPWifiConfig();
|
||||
MDNSConfig_t &getMDNSConfig();
|
||||
WiFiTxPower_t &getWiFiTxPowerConfig();
|
||||
TrackerConfig_t &getTrackerConfig();
|
||||
DeviceConfig_t& getDeviceConfig();
|
||||
DeviceMode_t& getDeviceModeConfig();
|
||||
CameraConfig_t& getCameraConfig();
|
||||
std::vector<WiFiConfig_t>& getWifiConfigs();
|
||||
AP_WiFiConfig_t& getAPWifiConfig();
|
||||
MDNSConfig_t& getMDNSConfig();
|
||||
WiFiTxPower_t& getWiFiTxPowerConfig();
|
||||
TrackerConfig_t& getTrackerConfig();
|
||||
|
||||
void setOTAConfig(const std::string &OTALogin,
|
||||
const std::string &OTAPassword,
|
||||
int OTAPort);
|
||||
void setLEDDUtyCycleConfig(int led_external_pwm_duty_cycle);
|
||||
void setMDNSConfig(const std::string &hostname);
|
||||
void setCameraConfig(uint8_t vflip,
|
||||
uint8_t framesize,
|
||||
uint8_t href,
|
||||
uint8_t quality,
|
||||
uint8_t brightness);
|
||||
void setWifiConfig(const std::string &networkName,
|
||||
const std::string &ssid,
|
||||
const std::string &password,
|
||||
uint8_t channel,
|
||||
uint8_t power);
|
||||
void setOTAConfig(const std::string& OTALogin, const std::string& OTAPassword, int OTAPort);
|
||||
void setLEDDUtyCycleConfig(int led_external_pwm_duty_cycle);
|
||||
void setMDNSConfig(const std::string& hostname);
|
||||
void setCameraConfig(uint8_t vflip, uint8_t framesize, uint8_t href, uint8_t quality, uint8_t brightness);
|
||||
void setWifiConfig(const std::string& networkName, const std::string& ssid, const std::string& password, uint8_t channel, uint8_t power);
|
||||
|
||||
void deleteWifiConfig(const std::string &networkName);
|
||||
void deleteWifiConfig(const std::string& networkName);
|
||||
|
||||
void setAPWifiConfig(const std::string &ssid,
|
||||
const std::string &password,
|
||||
uint8_t channel);
|
||||
void setWiFiTxPower(uint8_t power);
|
||||
void setDeviceMode(StreamingMode deviceMode);
|
||||
StreamingMode getDeviceMode();
|
||||
void setAPWifiConfig(const std::string& ssid, const std::string& password, uint8_t channel);
|
||||
void setWiFiTxPower(uint8_t power);
|
||||
void setDeviceMode(StreamingMode deviceMode);
|
||||
StreamingMode getDeviceMode();
|
||||
|
||||
private:
|
||||
Preferences *pref;
|
||||
bool _already_loaded;
|
||||
TrackerConfig_t config;
|
||||
private:
|
||||
Preferences* pref;
|
||||
bool _already_loaded;
|
||||
TrackerConfig_t config;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -7,140 +7,136 @@
|
||||
#define GET_METHOD "GET"
|
||||
#define DELETE_METHOD "DELETE"
|
||||
|
||||
bool getIsSuccess(const nlohmann::json &response)
|
||||
bool getIsSuccess(const nlohmann::json& response)
|
||||
{
|
||||
// since the commandManager will be returning CommandManagerResponse to simplify parsing on the clients end
|
||||
// we can slightly its json representation, and extract the status from there
|
||||
// note: This will only work for commands executed with CommandManager::executeFromType().
|
||||
if (!response.contains("result"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// since the commandManager will be returning CommandManagerResponse to simplify parsing on the clients end
|
||||
// we can slightly its json representation, and extract the status from there
|
||||
// note: This will only work for commands executed with CommandManager::executeFromType().
|
||||
if (!response.contains("result"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return response.at("result").at("status").get<std::string>() == "success";
|
||||
return response.at("result").at("status").get<std::string>() == "success";
|
||||
}
|
||||
|
||||
RestAPI::RestAPI(std::string url, std::shared_ptr<CommandManager> commandManager) : command_manager(commandManager)
|
||||
{
|
||||
// until we stumble on a simpler way to handle the commands over the rest api
|
||||
// the formula will be like this:
|
||||
// each command gets its own endpoint
|
||||
// each endpoint must include the action it performs in its path
|
||||
// for example
|
||||
// /get/ for getters
|
||||
// /set/ for posts
|
||||
// /delete/ for deletes
|
||||
// /update/ for updates
|
||||
// additional actions on the resource should be appended after the resource name
|
||||
// like for example /api/set/config/save/
|
||||
//
|
||||
// one endpoint must not contain more than one action
|
||||
// until we stumble on a simpler way to handle the commands over the rest api
|
||||
// the formula will be like this:
|
||||
// each command gets its own endpoint
|
||||
// each endpoint must include the action it performs in its path
|
||||
// for example
|
||||
// /get/ for getters
|
||||
// /set/ for posts
|
||||
// /delete/ for deletes
|
||||
// /update/ for updates
|
||||
// additional actions on the resource should be appended after the resource name
|
||||
// like for example /api/set/config/save/
|
||||
//
|
||||
// one endpoint must not contain more than one action
|
||||
|
||||
this->url = std::move(url);
|
||||
// updates via PATCH
|
||||
routes.emplace("/api/update/wifi/", RequestBaseData(PATCH_METHOD, CommandType::UPDATE_WIFI, 200, 400));
|
||||
routes.emplace("/api/update/device/mode/", RequestBaseData(PATCH_METHOD, CommandType::SWITCH_MODE, 200, 400));
|
||||
routes.emplace("/api/update/camera/", RequestBaseData(PATCH_METHOD, CommandType::UPDATE_CAMERA, 200, 400));
|
||||
routes.emplace("/api/update/ota/credentials", RequestBaseData(PATCH_METHOD, CommandType::UPDATE_OTA_CREDENTIALS, 200, 400));
|
||||
routes.emplace("/api/update/ap/", RequestBaseData(PATCH_METHOD, CommandType::UPDATE_AP_WIFI, 200, 400));
|
||||
routes.emplace("/api/update/led_duty_cycle/", RequestBaseData(PATCH_METHOD, CommandType::SET_LED_DUTY_CYCLE, 200, 400));
|
||||
this->url = std::move(url);
|
||||
// updates via PATCH
|
||||
routes.emplace("/api/update/wifi/", RequestBaseData(PATCH_METHOD, CommandType::UPDATE_WIFI, 200, 400));
|
||||
routes.emplace("/api/update/device/mode/", RequestBaseData(PATCH_METHOD, CommandType::SWITCH_MODE, 200, 400));
|
||||
routes.emplace("/api/update/camera/", RequestBaseData(PATCH_METHOD, CommandType::UPDATE_CAMERA, 200, 400));
|
||||
routes.emplace("/api/update/ota/credentials", RequestBaseData(PATCH_METHOD, CommandType::UPDATE_OTA_CREDENTIALS, 200, 400));
|
||||
routes.emplace("/api/update/ap/", RequestBaseData(PATCH_METHOD, CommandType::UPDATE_AP_WIFI, 200, 400));
|
||||
routes.emplace("/api/update/led_duty_cycle/", RequestBaseData(PATCH_METHOD, CommandType::SET_LED_DUTY_CYCLE, 200, 400));
|
||||
|
||||
// POST will set the data
|
||||
routes.emplace("/api/set/pause/", RequestBaseData(POST_METHOD, CommandType::PAUSE, 200, 400));
|
||||
routes.emplace("/api/set/wifi/", RequestBaseData(POST_METHOD, CommandType::SET_WIFI, 200, 400));
|
||||
routes.emplace("/api/set/mdns/", RequestBaseData(POST_METHOD, CommandType::SET_MDNS, 200, 400));
|
||||
routes.emplace("/api/set/config/save/", RequestBaseData(POST_METHOD, CommandType::SAVE_CONFIG, 200, 400));
|
||||
routes.emplace("/api/set/wifi/connect/", RequestBaseData(POST_METHOD, CommandType::CONNECT_WIFI, 200, 400));
|
||||
// POST will set the data
|
||||
routes.emplace("/api/set/pause/", RequestBaseData(POST_METHOD, CommandType::PAUSE, 200, 400));
|
||||
routes.emplace("/api/set/wifi/", RequestBaseData(POST_METHOD, CommandType::SET_WIFI, 200, 400));
|
||||
routes.emplace("/api/set/mdns/", RequestBaseData(POST_METHOD, CommandType::SET_MDNS, 200, 400));
|
||||
routes.emplace("/api/set/config/save/", RequestBaseData(POST_METHOD, CommandType::SAVE_CONFIG, 200, 400));
|
||||
routes.emplace("/api/set/wifi/connect/", RequestBaseData(POST_METHOD, CommandType::CONNECT_WIFI, 200, 400));
|
||||
|
||||
// resets via POST as well
|
||||
routes.emplace("/api/reset/config/", RequestBaseData(POST_METHOD, CommandType::RESET_CONFIG, 200, 400));
|
||||
// resets via POST as well
|
||||
routes.emplace("/api/reset/config/", RequestBaseData(POST_METHOD, CommandType::RESET_CONFIG, 200, 400));
|
||||
|
||||
// gets via GET
|
||||
routes.emplace("/api/get/config/", RequestBaseData(GET_METHOD, CommandType::GET_CONFIG, 200, 400));
|
||||
routes.emplace("/api/get/mdns/", RequestBaseData(GET_METHOD, CommandType::GET_MDNS_NAME, 200, 400));
|
||||
routes.emplace("/api/get/led_duty_cycle/", RequestBaseData(GET_METHOD, CommandType::GET_LED_DUTY_CYCLE, 200, 400));
|
||||
routes.emplace("/api/get/serial_number/", RequestBaseData(GET_METHOD, CommandType::GET_SERIAL, 200, 400));
|
||||
routes.emplace("/api/get/led_current/", RequestBaseData(GET_METHOD, CommandType::GET_LED_CURRENT, 200, 400));
|
||||
routes.emplace("/api/get/who_am_i/", RequestBaseData(GET_METHOD, CommandType::GET_WHO_AM_I, 200, 400));
|
||||
// gets via GET
|
||||
routes.emplace("/api/get/config/", RequestBaseData(GET_METHOD, CommandType::GET_CONFIG, 200, 400));
|
||||
routes.emplace("/api/get/mdns/", RequestBaseData(GET_METHOD, CommandType::GET_MDNS_NAME, 200, 400));
|
||||
routes.emplace("/api/get/led_duty_cycle/", RequestBaseData(GET_METHOD, CommandType::GET_LED_DUTY_CYCLE, 200, 400));
|
||||
routes.emplace("/api/get/serial_number/", RequestBaseData(GET_METHOD, CommandType::GET_SERIAL, 200, 400));
|
||||
routes.emplace("/api/get/led_current/", RequestBaseData(GET_METHOD, CommandType::GET_LED_CURRENT, 200, 400));
|
||||
routes.emplace("/api/get/who_am_i/", RequestBaseData(GET_METHOD, CommandType::GET_WHO_AM_I, 200, 400));
|
||||
|
||||
// deletes via DELETE
|
||||
routes.emplace("/api/delete/wifi", RequestBaseData(DELETE_METHOD, CommandType::DELETE_NETWORK, 200, 400));
|
||||
// deletes via DELETE
|
||||
routes.emplace("/api/delete/wifi", RequestBaseData(DELETE_METHOD, CommandType::DELETE_NETWORK, 200, 400));
|
||||
|
||||
// reboots via POST
|
||||
routes.emplace("/api/reboot/device/", RequestBaseData(GET_METHOD, CommandType::RESTART_DEVICE, 200, 500));
|
||||
// reboots via POST
|
||||
routes.emplace("/api/reboot/device/", RequestBaseData(GET_METHOD, CommandType::RESTART_DEVICE, 200, 500));
|
||||
|
||||
// heartbeat via GET
|
||||
routes.emplace("/api/ping/", RequestBaseData(GET_METHOD, CommandType::PING, 200, 400));
|
||||
// heartbeat via GET
|
||||
routes.emplace("/api/ping/", RequestBaseData(GET_METHOD, CommandType::PING, 200, 400));
|
||||
}
|
||||
|
||||
void RestAPI::begin()
|
||||
{
|
||||
mg_log_set(MG_LL_DEBUG);
|
||||
mg_mgr_init(&mgr);
|
||||
// every route is handled through this class, with commands themselves by a command manager
|
||||
// hence we pass a pointer to this in mg_http_listen
|
||||
mg_http_listen(&mgr, this->url.c_str(), (mg_event_handler_t)RestAPIHelpers::event_handler, this);
|
||||
mg_log_set(MG_LL_DEBUG);
|
||||
mg_mgr_init(&mgr);
|
||||
// every route is handled through this class, with commands themselves by a command manager
|
||||
// hence we pass a pointer to this in mg_http_listen
|
||||
mg_http_listen(&mgr, this->url.c_str(), (mg_event_handler_t)RestAPIHelpers::event_handler, this);
|
||||
}
|
||||
|
||||
void RestAPI::handle_request(struct mg_connection *connection, int event, void *event_data)
|
||||
void RestAPI::handle_request(struct mg_connection* connection, int event, void* event_data)
|
||||
{
|
||||
if (event == MG_EV_HTTP_MSG)
|
||||
{
|
||||
auto const *message = static_cast<struct mg_http_message *>(event_data);
|
||||
auto const uri = std::string(message->uri.buf, message->uri.len);
|
||||
|
||||
if (this->routes.find(uri) == this->routes.end())
|
||||
if (event == MG_EV_HTTP_MSG)
|
||||
{
|
||||
mg_http_reply(connection, 404, "", "Wrong URL");
|
||||
return;
|
||||
auto const* message = static_cast<struct mg_http_message*>(event_data);
|
||||
auto const uri = std::string(message->uri.buf, message->uri.len);
|
||||
|
||||
if (this->routes.find(uri) == this->routes.end())
|
||||
{
|
||||
mg_http_reply(connection, 404, "", "Wrong URL");
|
||||
return;
|
||||
}
|
||||
|
||||
auto const base_request_params = this->routes.at(uri);
|
||||
|
||||
auto* context = new RequestContext{
|
||||
.connection = connection,
|
||||
.method = std::string(message->method.buf, message->method.len),
|
||||
.body = std::string(message->body.buf, message->body.len),
|
||||
};
|
||||
this->handle_endpoint_command(context, base_request_params.allowed_method, base_request_params.command_type, base_request_params.success_code,
|
||||
base_request_params.error_code);
|
||||
}
|
||||
|
||||
auto const base_request_params = this->routes.at(uri);
|
||||
|
||||
auto *context = new RequestContext{
|
||||
.connection = connection,
|
||||
.method = std::string(message->method.buf, message->method.len),
|
||||
.body = std::string(message->body.buf, message->body.len),
|
||||
};
|
||||
this->handle_endpoint_command(context,
|
||||
base_request_params.allowed_method,
|
||||
base_request_params.command_type,
|
||||
base_request_params.success_code,
|
||||
base_request_params.error_code);
|
||||
}
|
||||
}
|
||||
|
||||
void RestAPIHelpers::event_handler(struct mg_connection *connection, int event, void *event_data)
|
||||
void RestAPIHelpers::event_handler(struct mg_connection* connection, int event, void* event_data)
|
||||
{
|
||||
auto *rest_api_handler = static_cast<RestAPI *>(connection->fn_data);
|
||||
rest_api_handler->handle_request(connection, event, event_data);
|
||||
auto* rest_api_handler = static_cast<RestAPI*>(connection->fn_data);
|
||||
rest_api_handler->handle_request(connection, event, event_data);
|
||||
}
|
||||
|
||||
void RestAPI::poll()
|
||||
{
|
||||
mg_mgr_poll(&mgr, 100);
|
||||
mg_mgr_poll(&mgr, 100);
|
||||
}
|
||||
|
||||
void HandleRestAPIPollTask(void *pvParameter)
|
||||
void HandleRestAPIPollTask(void* pvParameter)
|
||||
{
|
||||
auto *rest_api_handler = static_cast<RestAPI *>(pvParameter);
|
||||
while (true)
|
||||
{
|
||||
rest_api_handler->poll();
|
||||
vTaskDelay(1000);
|
||||
}
|
||||
auto* rest_api_handler = static_cast<RestAPI*>(pvParameter);
|
||||
while (true)
|
||||
{
|
||||
rest_api_handler->poll();
|
||||
vTaskDelay(1000);
|
||||
}
|
||||
}
|
||||
|
||||
void RestAPI::handle_endpoint_command(RequestContext *context, std::string allowed_method, CommandType command_type, int success_code, int error_code)
|
||||
void RestAPI::handle_endpoint_command(RequestContext* context, std::string allowed_method, CommandType command_type, int success_code, int error_code)
|
||||
{
|
||||
if (context->method != allowed_method)
|
||||
{
|
||||
mg_http_reply(context->connection, 401, JSON_RESPONSE, "{%m:%m}", MG_ESC("error"), "Method not allowed");
|
||||
return;
|
||||
}
|
||||
|
||||
if (context->method != allowed_method)
|
||||
{
|
||||
mg_http_reply(context->connection, 401, JSON_RESPONSE, "{%m:%m}", MG_ESC("error"), "Method not allowed");
|
||||
return;
|
||||
}
|
||||
|
||||
const nlohmann::json result = command_manager->executeFromType(command_type, context->body);
|
||||
const auto code = getIsSuccess(result) ? success_code : error_code;
|
||||
mg_http_reply(context->connection, code, JSON_RESPONSE, result.dump().c_str());
|
||||
const nlohmann::json result = command_manager->executeFromType(command_type, context->body);
|
||||
const auto code = getIsSuccess(result) ? success_code : error_code;
|
||||
mg_http_reply(context->connection, code, JSON_RESPONSE, result.dump().c_str());
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
#pragma once
|
||||
#ifndef RESTAPI_HPP
|
||||
#define RESTAPI_HPP
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <mongoose.h>
|
||||
#include <CommandManager.hpp>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "esp_log.h"
|
||||
|
||||
@@ -13,45 +13,46 @@
|
||||
|
||||
struct RequestContext
|
||||
{
|
||||
mg_connection *connection;
|
||||
std::string method;
|
||||
std::string body;
|
||||
mg_connection* connection;
|
||||
std::string method;
|
||||
std::string body;
|
||||
};
|
||||
|
||||
struct RequestBaseData
|
||||
{
|
||||
std::string allowed_method;
|
||||
CommandType command_type;
|
||||
int success_code;
|
||||
int error_code;
|
||||
RequestBaseData(std::string allowed_method, CommandType command_type, int success_code, int error_code) : allowed_method(allowed_method), command_type(command_type), success_code(success_code), error_code(error_code) {};
|
||||
std::string allowed_method;
|
||||
CommandType command_type;
|
||||
int success_code;
|
||||
int error_code;
|
||||
RequestBaseData(std::string allowed_method, CommandType command_type, int success_code, int error_code)
|
||||
: allowed_method(allowed_method), command_type(command_type), success_code(success_code), error_code(error_code) {};
|
||||
};
|
||||
|
||||
class RestAPI
|
||||
{
|
||||
typedef std::unordered_map<std::string, RequestBaseData> route_map;
|
||||
std::string url;
|
||||
route_map routes;
|
||||
typedef std::unordered_map<std::string, RequestBaseData> route_map;
|
||||
std::string url;
|
||||
route_map routes;
|
||||
|
||||
mg_mgr mgr;
|
||||
std::shared_ptr<CommandManager> command_manager;
|
||||
mg_mgr mgr;
|
||||
std::shared_ptr<CommandManager> command_manager;
|
||||
|
||||
private:
|
||||
void handle_endpoint_command(RequestContext *context, std::string allowed_method, CommandType command_type, int success_code, int error_code);
|
||||
private:
|
||||
void handle_endpoint_command(RequestContext* context, std::string allowed_method, CommandType command_type, int success_code, int error_code);
|
||||
|
||||
public:
|
||||
// this will also need command manager
|
||||
RestAPI(std::string url, std::shared_ptr<CommandManager> command_manager);
|
||||
void begin();
|
||||
void handle_request(struct mg_connection *connection, int event, void *event_data);
|
||||
void poll();
|
||||
public:
|
||||
// this will also need command manager
|
||||
RestAPI(std::string url, std::shared_ptr<CommandManager> command_manager);
|
||||
void begin();
|
||||
void handle_request(struct mg_connection* connection, int event, void* event_data);
|
||||
void poll();
|
||||
};
|
||||
|
||||
namespace RestAPIHelpers
|
||||
{
|
||||
void event_handler(struct mg_connection *connection, int event, void *event_data);
|
||||
void event_handler(struct mg_connection* connection, int event, void* event_data);
|
||||
};
|
||||
|
||||
void HandleRestAPIPollTask(void *pvParameter);
|
||||
void HandleRestAPIPollTask(void* pvParameter);
|
||||
|
||||
#endif
|
||||
@@ -2,34 +2,34 @@
|
||||
#include "esp_log.h"
|
||||
#include "main_globals.hpp"
|
||||
|
||||
SerialManager::SerialManager(std::shared_ptr<CommandManager> commandManager, esp_timer_handle_t *timerHandle)
|
||||
SerialManager::SerialManager(std::shared_ptr<CommandManager> commandManager, esp_timer_handle_t* timerHandle)
|
||||
: commandManager(commandManager), timerHandle(timerHandle)
|
||||
{
|
||||
this->data = static_cast<uint8_t *>(malloc(BUF_SIZE));
|
||||
this->temp_data = static_cast<uint8_t *>(malloc(256));
|
||||
this->data = static_cast<uint8_t*>(malloc(BUF_SIZE));
|
||||
this->temp_data = static_cast<uint8_t*>(malloc(256));
|
||||
}
|
||||
|
||||
// Function to notify that a command was received during startup
|
||||
void SerialManager::notify_startup_command_received()
|
||||
{
|
||||
setStartupCommandReceived(true);
|
||||
setStartupCommandReceived(true);
|
||||
|
||||
// Cancel the startup timer if it's still running
|
||||
if (timerHandle != nullptr && *timerHandle != nullptr)
|
||||
{
|
||||
esp_timer_stop(*timerHandle);
|
||||
esp_timer_delete(*timerHandle);
|
||||
*timerHandle = nullptr;
|
||||
ESP_LOGI("[MAIN]", "Startup timer cancelled, staying in heartbeat mode");
|
||||
}
|
||||
// Cancel the startup timer if it's still running
|
||||
if (timerHandle != nullptr && *timerHandle != nullptr)
|
||||
{
|
||||
esp_timer_stop(*timerHandle);
|
||||
esp_timer_delete(*timerHandle);
|
||||
*timerHandle = nullptr;
|
||||
ESP_LOGI("[MAIN]", "Startup timer cancelled, staying in heartbeat mode");
|
||||
}
|
||||
}
|
||||
|
||||
// we can cancel this task once we're in cdc
|
||||
void HandleSerialManagerTask(void *pvParameters)
|
||||
void HandleSerialManagerTask(void* pvParameters)
|
||||
{
|
||||
auto const serialManager = static_cast<SerialManager *>(pvParameters);
|
||||
while (true)
|
||||
{
|
||||
serialManager->try_receive();
|
||||
}
|
||||
auto const serialManager = static_cast<SerialManager*>(pvParameters);
|
||||
while (true)
|
||||
{
|
||||
serialManager->try_receive();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,18 +3,18 @@
|
||||
#define SERIALMANAGER_HPP
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <CommandManager.hpp>
|
||||
#include <ProjectConfig.hpp>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_log.h"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_vfs_dev.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_mac.h"
|
||||
#include "esp_vfs_dev.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/task.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#ifndef BUF_SIZE
|
||||
#define BUF_SIZE (1024)
|
||||
@@ -27,26 +27,26 @@ extern QueueHandle_t cdcMessageQueue;
|
||||
|
||||
struct cdc_command_packet_t
|
||||
{
|
||||
uint8_t len;
|
||||
uint8_t data[64];
|
||||
uint8_t len;
|
||||
uint8_t data[64];
|
||||
};
|
||||
|
||||
class SerialManager
|
||||
{
|
||||
public:
|
||||
explicit SerialManager(std::shared_ptr<CommandManager> commandManager, esp_timer_handle_t *timerHandle);
|
||||
void setup();
|
||||
void try_receive();
|
||||
void notify_startup_command_received();
|
||||
void shutdown();
|
||||
public:
|
||||
explicit SerialManager(std::shared_ptr<CommandManager> commandManager, esp_timer_handle_t* timerHandle);
|
||||
void setup();
|
||||
void try_receive();
|
||||
void notify_startup_command_received();
|
||||
void shutdown();
|
||||
|
||||
private:
|
||||
std::shared_ptr<CommandManager> commandManager;
|
||||
esp_timer_handle_t *timerHandle;
|
||||
uint8_t *data;
|
||||
uint8_t *temp_data;
|
||||
private:
|
||||
std::shared_ptr<CommandManager> commandManager;
|
||||
esp_timer_handle_t* timerHandle;
|
||||
uint8_t* data;
|
||||
uint8_t* temp_data;
|
||||
};
|
||||
|
||||
void HandleSerialManagerTask(void *pvParameters);
|
||||
void HandleCDCSerialManagerTask(void *pvParameters);
|
||||
void HandleSerialManagerTask(void* pvParameters);
|
||||
void HandleCDCSerialManagerTask(void* pvParameters);
|
||||
#endif
|
||||
@@ -1,102 +1,98 @@
|
||||
#include "SerialManager.hpp"
|
||||
#include "driver/uart.h"
|
||||
#include "esp_log.h"
|
||||
#include "main_globals.hpp"
|
||||
#include "driver/uart.h"
|
||||
|
||||
void SerialManager::setup()
|
||||
{
|
||||
uart_config_t uart_config = {
|
||||
.baud_rate = 115200,
|
||||
.data_bits = UART_DATA_8_BITS,
|
||||
.parity = UART_PARITY_DISABLE,
|
||||
.stop_bits = UART_STOP_BITS_1,
|
||||
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
|
||||
};
|
||||
uart_config_t uart_config = {
|
||||
.baud_rate = 115200,
|
||||
.data_bits = UART_DATA_8_BITS,
|
||||
.parity = UART_PARITY_DISABLE,
|
||||
.stop_bits = UART_STOP_BITS_1,
|
||||
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
|
||||
};
|
||||
|
||||
const auto uart_num = static_cast<uart_port_t>(CONFIG_UART_PORT_NUMBER);
|
||||
const auto uart_num = static_cast<uart_port_t>(CONFIG_UART_PORT_NUMBER);
|
||||
|
||||
uart_driver_install(uart_num, BUF_SIZE, BUF_SIZE, 0, NULL, 0);
|
||||
uart_param_config(uart_num, &uart_config);
|
||||
uart_driver_install(uart_num, BUF_SIZE, BUF_SIZE, 0, NULL, 0);
|
||||
uart_param_config(uart_num, &uart_config);
|
||||
|
||||
uart_set_pin(uart_num,
|
||||
CONFIG_UART_TX_PIN,
|
||||
CONFIG_UART_RX_PIN,
|
||||
UART_PIN_NO_CHANGE,
|
||||
UART_PIN_NO_CHANGE);
|
||||
uart_set_pin(uart_num, CONFIG_UART_TX_PIN, CONFIG_UART_RX_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
|
||||
|
||||
gpio_set_pull_mode(static_cast<gpio_num_t>(CONFIG_UART_RX_PIN), GPIO_PULLDOWN_ONLY);
|
||||
gpio_set_pull_mode(static_cast<gpio_num_t>(CONFIG_UART_RX_PIN), GPIO_PULLDOWN_ONLY);
|
||||
|
||||
// ----- Startup Flush -----
|
||||
uart_flush(uart_num);
|
||||
// ----- Startup Flush -----
|
||||
uart_flush(uart_num);
|
||||
|
||||
uint8_t dump_buf[256];
|
||||
// clean up initial onslaught of logs
|
||||
while (uart_read_bytes(uart_num, dump_buf, sizeof(dump_buf), 10 / portTICK_PERIOD_MS) > 0)
|
||||
{
|
||||
}
|
||||
uint8_t dump_buf[256];
|
||||
// clean up initial onslaught of logs
|
||||
while (uart_read_bytes(uart_num, dump_buf, sizeof(dump_buf), 10 / portTICK_PERIOD_MS) > 0)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
void uart_write_bytes_chunked(uart_port_t uart_num, const void *src, size_t size)
|
||||
void uart_write_bytes_chunked(uart_port_t uart_num, const void* src, size_t size)
|
||||
{
|
||||
while (size > 0)
|
||||
{
|
||||
auto to_write = size > BUF_SIZE ? BUF_SIZE : size;
|
||||
auto written = uart_write_bytes(uart_num, src, to_write);
|
||||
src += written;
|
||||
size -= written;
|
||||
}
|
||||
while (size > 0)
|
||||
{
|
||||
auto to_write = size > BUF_SIZE ? BUF_SIZE : size;
|
||||
auto written = uart_write_bytes(uart_num, src, to_write);
|
||||
src += written;
|
||||
size -= written;
|
||||
}
|
||||
}
|
||||
|
||||
void SerialManager::try_receive()
|
||||
{
|
||||
static auto current_position = 0;
|
||||
const auto uart_num = static_cast<uart_port_t>(CONFIG_UART_PORT_NUMBER);
|
||||
int len = uart_read_bytes(uart_num, this->temp_data, BUF_SIZE, 1000 / 20);
|
||||
static auto current_position = 0;
|
||||
const auto uart_num = static_cast<uart_port_t>(CONFIG_UART_PORT_NUMBER);
|
||||
int len = uart_read_bytes(uart_num, this->temp_data, BUF_SIZE, 1000 / 20);
|
||||
|
||||
// If driver is uninstalled or an error occurs, abort read gracefully
|
||||
if (len <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (len > 0)
|
||||
{
|
||||
notify_startup_command_received();
|
||||
}
|
||||
|
||||
// since we've got something on the serial port
|
||||
// we gotta keep reading until we've got the whole message
|
||||
// we will submit the command once we get a newline, a return or the buffer is full
|
||||
for (auto i = 0; i < len; i++)
|
||||
{
|
||||
this->data[current_position++] = this->temp_data[i];
|
||||
// if we're at the end of the buffer, try to process the command anyway
|
||||
// if we've got a new line, we've finished sending the commands, process them
|
||||
if (current_position >= BUF_SIZE || this->data[current_position - 1] == '\n' || this->data[current_position - 1] == '\r')
|
||||
// If driver is uninstalled or an error occurs, abort read gracefully
|
||||
if (len <= 0)
|
||||
{
|
||||
data[current_position] = '\0';
|
||||
current_position = 0;
|
||||
|
||||
const nlohmann::json result = this->commandManager->executeFromJson(std::string_view(reinterpret_cast<const char *>(this->data)));
|
||||
const auto resultMessage = result.dump();
|
||||
// todo check if this works
|
||||
// uart_write_bytes_chunked(uart_num, resultMessage.c_str(), resultMessage.length())s
|
||||
uart_write_bytes(uart_num, resultMessage.c_str(), resultMessage.length());
|
||||
return;
|
||||
}
|
||||
|
||||
if (len > 0)
|
||||
{
|
||||
notify_startup_command_received();
|
||||
}
|
||||
|
||||
// since we've got something on the serial port
|
||||
// we gotta keep reading until we've got the whole message
|
||||
// we will submit the command once we get a newline, a return or the buffer is full
|
||||
for (auto i = 0; i < len; i++)
|
||||
{
|
||||
this->data[current_position++] = this->temp_data[i];
|
||||
// if we're at the end of the buffer, try to process the command anyway
|
||||
// if we've got a new line, we've finished sending the commands, process them
|
||||
if (current_position >= BUF_SIZE || this->data[current_position - 1] == '\n' || this->data[current_position - 1] == '\r')
|
||||
{
|
||||
data[current_position] = '\0';
|
||||
current_position = 0;
|
||||
|
||||
const nlohmann::json result = this->commandManager->executeFromJson(std::string_view(reinterpret_cast<const char*>(this->data)));
|
||||
const auto resultMessage = result.dump();
|
||||
// todo check if this works
|
||||
// uart_write_bytes_chunked(uart_num, resultMessage.c_str(), resultMessage.length())s
|
||||
uart_write_bytes(uart_num, resultMessage.c_str(), resultMessage.length());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SerialManager::shutdown()
|
||||
{
|
||||
// Uninstall the UART driver to free the internal to keep compatibility with JTAG implementation.
|
||||
const auto uart_num = static_cast<uart_port_t>(CONFIG_UART_PORT_NUMBER);
|
||||
esp_err_t err = uart_driver_delete(uart_num);
|
||||
if (err == ESP_OK)
|
||||
{
|
||||
ESP_LOGI("[SERIAL]", "usb_serial_jtag driver uninstalled");
|
||||
}
|
||||
else if (err != ESP_ERR_INVALID_STATE)
|
||||
{
|
||||
ESP_LOGW("[SERIAL]", "usb_serial_jtag_driver_uninstall returned %s", esp_err_to_name(err));
|
||||
}
|
||||
// Uninstall the UART driver to free the internal to keep compatibility with JTAG implementation.
|
||||
const auto uart_num = static_cast<uart_port_t>(CONFIG_UART_PORT_NUMBER);
|
||||
esp_err_t err = uart_driver_delete(uart_num);
|
||||
if (err == ESP_OK)
|
||||
{
|
||||
ESP_LOGI("[SERIAL]", "usb_serial_jtag driver uninstalled");
|
||||
}
|
||||
else if (err != ESP_ERR_INVALID_STATE)
|
||||
{
|
||||
ESP_LOGW("[SERIAL]", "usb_serial_jtag_driver_uninstall returned %s", esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,114 +1,114 @@
|
||||
#include "SerialManager.hpp"
|
||||
#include "esp_log.h"
|
||||
#include "main_globals.hpp"
|
||||
#include "driver/usb_serial_jtag.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_vfs_usb_serial_jtag.h"
|
||||
#include "main_globals.hpp"
|
||||
|
||||
#include "tusb.h"
|
||||
|
||||
void SerialManager::setup()
|
||||
{
|
||||
#ifndef CONFIG_USE_UART_FOR_COMMUNICATION
|
||||
usb_serial_jtag_driver_config_t usb_serial_jtag_config;
|
||||
usb_serial_jtag_config.rx_buffer_size = BUF_SIZE;
|
||||
usb_serial_jtag_config.tx_buffer_size = BUF_SIZE;
|
||||
usb_serial_jtag_driver_install(&usb_serial_jtag_config);
|
||||
usb_serial_jtag_driver_config_t usb_serial_jtag_config;
|
||||
usb_serial_jtag_config.rx_buffer_size = BUF_SIZE;
|
||||
usb_serial_jtag_config.tx_buffer_size = BUF_SIZE;
|
||||
usb_serial_jtag_driver_install(&usb_serial_jtag_config);
|
||||
#endif
|
||||
}
|
||||
|
||||
void usb_serial_jtag_write_bytes_chunked(const char *data, size_t len, size_t timeout)
|
||||
void usb_serial_jtag_write_bytes_chunked(const char* data, size_t len, size_t timeout)
|
||||
{
|
||||
#ifndef CONFIG_USE_UART_FOR_COMMUNICATION
|
||||
while (len > 0)
|
||||
{
|
||||
auto to_write = len > BUF_SIZE ? BUF_SIZE : len;
|
||||
auto written = usb_serial_jtag_write_bytes(data, to_write, timeout);
|
||||
data += written;
|
||||
len -= written;
|
||||
}
|
||||
while (len > 0)
|
||||
{
|
||||
auto to_write = len > BUF_SIZE ? BUF_SIZE : len;
|
||||
auto written = usb_serial_jtag_write_bytes(data, to_write, timeout);
|
||||
data += written;
|
||||
len -= written;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void SerialManager::try_receive()
|
||||
{
|
||||
static auto current_position = 0;
|
||||
int len = usb_serial_jtag_read_bytes(this->temp_data, 256, 1000 / 20);
|
||||
static auto current_position = 0;
|
||||
int len = usb_serial_jtag_read_bytes(this->temp_data, 256, 1000 / 20);
|
||||
|
||||
// If driver is uninstalled or an error occurs, abort read gracefully
|
||||
if (len < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (len > 0)
|
||||
{
|
||||
// Notify main that a command was received during startup
|
||||
notify_startup_command_received();
|
||||
}
|
||||
|
||||
// since we've got something on the serial port
|
||||
// we gotta keep reading until we've got the whole message
|
||||
// we will submit the command once we get a newline, a return or the buffer is full
|
||||
for (auto i = 0; i < len; i++)
|
||||
{
|
||||
this->data[current_position++] = this->temp_data[i];
|
||||
// if we're at the end of the buffer, try to process the command anyway
|
||||
// if we've got a new line, we've finished sending the commands, process them
|
||||
if (current_position >= BUF_SIZE || this->data[current_position - 1] == '\n' || this->data[current_position - 1] == '\r')
|
||||
// If driver is uninstalled or an error occurs, abort read gracefully
|
||||
if (len < 0)
|
||||
{
|
||||
data[current_position] = '\0';
|
||||
current_position = 0;
|
||||
|
||||
const nlohmann::json result = this->commandManager->executeFromJson(std::string_view(reinterpret_cast<const char *>(this->data)));
|
||||
const auto resultMessage = result.dump();
|
||||
usb_serial_jtag_write_bytes_chunked(resultMessage.c_str(), resultMessage.length(), 1000 / 20);
|
||||
return;
|
||||
}
|
||||
|
||||
if (len > 0)
|
||||
{
|
||||
// Notify main that a command was received during startup
|
||||
notify_startup_command_received();
|
||||
}
|
||||
|
||||
// since we've got something on the serial port
|
||||
// we gotta keep reading until we've got the whole message
|
||||
// we will submit the command once we get a newline, a return or the buffer is full
|
||||
for (auto i = 0; i < len; i++)
|
||||
{
|
||||
this->data[current_position++] = this->temp_data[i];
|
||||
// if we're at the end of the buffer, try to process the command anyway
|
||||
// if we've got a new line, we've finished sending the commands, process them
|
||||
if (current_position >= BUF_SIZE || this->data[current_position - 1] == '\n' || this->data[current_position - 1] == '\r')
|
||||
{
|
||||
data[current_position] = '\0';
|
||||
current_position = 0;
|
||||
|
||||
const nlohmann::json result = this->commandManager->executeFromJson(std::string_view(reinterpret_cast<const char*>(this->data)));
|
||||
const auto resultMessage = result.dump();
|
||||
usb_serial_jtag_write_bytes_chunked(resultMessage.c_str(), resultMessage.length(), 1000 / 20);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SerialManager::shutdown()
|
||||
{
|
||||
// Uninstall the USB Serial JTAG driver to free the internal USB for TinyUSB.
|
||||
esp_err_t err = usb_serial_jtag_driver_uninstall();
|
||||
if (err == ESP_OK)
|
||||
{
|
||||
ESP_LOGI("[SERIAL]", "usb_serial_jtag driver uninstalled");
|
||||
}
|
||||
else if (err != ESP_ERR_INVALID_STATE)
|
||||
{
|
||||
ESP_LOGW("[SERIAL]", "usb_serial_jtag_driver_uninstall returned %s", esp_err_to_name(err));
|
||||
}
|
||||
// Uninstall the USB Serial JTAG driver to free the internal USB for TinyUSB.
|
||||
esp_err_t err = usb_serial_jtag_driver_uninstall();
|
||||
if (err == ESP_OK)
|
||||
{
|
||||
ESP_LOGI("[SERIAL]", "usb_serial_jtag driver uninstalled");
|
||||
}
|
||||
else if (err != ESP_ERR_INVALID_STATE)
|
||||
{
|
||||
ESP_LOGW("[SERIAL]", "usb_serial_jtag_driver_uninstall returned %s", esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
|
||||
void HandleCDCSerialManagerTask(void *pvParameters)
|
||||
void HandleCDCSerialManagerTask(void* pvParameters)
|
||||
{
|
||||
#ifndef CONFIG_USE_UART_FOR_COMMUNICATION
|
||||
auto const commandManager = static_cast<CommandManager *>(pvParameters);
|
||||
static char buffer[BUF_SIZE];
|
||||
auto idx = 0;
|
||||
auto const commandManager = static_cast<CommandManager*>(pvParameters);
|
||||
static char buffer[BUF_SIZE];
|
||||
auto idx = 0;
|
||||
|
||||
cdc_command_packet_t packet;
|
||||
while (true)
|
||||
{
|
||||
if (xQueueReceive(cdcMessageQueue, &packet, portMAX_DELAY) == pdTRUE)
|
||||
cdc_command_packet_t packet;
|
||||
while (true)
|
||||
{
|
||||
for (auto i = 0; i < packet.len; i++)
|
||||
{
|
||||
buffer[idx++] = packet.data[i];
|
||||
// if we're at the end of the buffer, try to process the command anyway
|
||||
// if we've got a new line, we've finished sending the commands, process them
|
||||
if (idx >= BUF_SIZE || buffer[idx - 1] == '\n' || buffer[idx - 1] == '\r')
|
||||
if (xQueueReceive(cdcMessageQueue, &packet, portMAX_DELAY) == pdTRUE)
|
||||
{
|
||||
buffer[idx - 1] = '\0';
|
||||
const nlohmann::json result = commandManager->executeFromJson(std::string_view(reinterpret_cast<const char *>(buffer)));
|
||||
const auto resultMessage = result.dump();
|
||||
tud_cdc_write(resultMessage.c_str(), resultMessage.length());
|
||||
tud_cdc_write_flush();
|
||||
idx = 0;
|
||||
for (auto i = 0; i < packet.len; i++)
|
||||
{
|
||||
buffer[idx++] = packet.data[i];
|
||||
// if we're at the end of the buffer, try to process the command anyway
|
||||
// if we've got a new line, we've finished sending the commands, process them
|
||||
if (idx >= BUF_SIZE || buffer[idx - 1] == '\n' || buffer[idx - 1] == '\r')
|
||||
{
|
||||
buffer[idx - 1] = '\0';
|
||||
const nlohmann::json result = commandManager->executeFromJson(std::string_view(reinterpret_cast<const char*>(buffer)));
|
||||
const auto resultMessage = result.dump();
|
||||
tud_cdc_write(resultMessage.c_str(), resultMessage.length());
|
||||
tud_cdc_write_flush();
|
||||
idx = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -117,36 +117,35 @@ void HandleCDCSerialManagerTask(void *pvParameters)
|
||||
// grab the data and send it to a queue, a special task will process it and handle with the command manager
|
||||
extern "C" void tud_cdc_rx_cb(uint8_t itf)
|
||||
{
|
||||
// we can void the interface number
|
||||
(void)itf;
|
||||
cdc_command_packet_t packet;
|
||||
auto len = tud_cdc_available();
|
||||
// we can void the interface number
|
||||
(void)itf;
|
||||
cdc_command_packet_t packet;
|
||||
auto len = tud_cdc_available();
|
||||
|
||||
if (len > 0)
|
||||
{
|
||||
auto read = tud_cdc_read(packet.data, sizeof(packet.data));
|
||||
if (read > 0)
|
||||
if (len > 0)
|
||||
{
|
||||
// we should be safe here, given that the max buffer size is 64
|
||||
packet.len = static_cast<uint8_t>(read);
|
||||
xQueueSend(cdcMessageQueue, &packet, 1);
|
||||
auto read = tud_cdc_read(packet.data, sizeof(packet.data));
|
||||
if (read > 0)
|
||||
{
|
||||
// we should be safe here, given that the max buffer size is 64
|
||||
packet.len = static_cast<uint8_t>(read);
|
||||
xQueueSend(cdcMessageQueue, &packet, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts)
|
||||
{
|
||||
(void)itf;
|
||||
(void)dtr;
|
||||
(void)rts;
|
||||
(void)itf;
|
||||
(void)dtr;
|
||||
(void)rts;
|
||||
|
||||
ESP_LOGI("[SERIAL]", "CDC line state changed: DTR=%d, RTS=%d", dtr, rts);
|
||||
ESP_LOGI("[SERIAL]", "CDC line state changed: DTR=%d, RTS=%d", dtr, rts);
|
||||
}
|
||||
|
||||
void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const *p_line_coding)
|
||||
void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding)
|
||||
{
|
||||
(void)itf;
|
||||
ESP_LOGI("[SERIAL]", "CDC line coding: %" PRIu32 " bps, %d stop bits, %d parity, %d data bits",
|
||||
p_line_coding->bit_rate, p_line_coding->stop_bits,
|
||||
p_line_coding->parity, p_line_coding->data_bits);
|
||||
(void)itf;
|
||||
ESP_LOGI("[SERIAL]", "CDC line coding: %" PRIu32 " bps, %d stop bits, %d parity, %d data bits", p_line_coding->bit_rate, p_line_coding->stop_bits,
|
||||
p_line_coding->parity, p_line_coding->data_bits);
|
||||
}
|
||||
@@ -4,100 +4,99 @@ StateManager::StateManager(QueueHandle_t eventQueue, QueueHandle_t ledStateQueue
|
||||
|
||||
void StateManager::HandleUpdateState()
|
||||
{
|
||||
SystemEvent eventBuffer;
|
||||
auto ledStreamState = LEDStates_e::LedStateNone;
|
||||
SystemEvent eventBuffer;
|
||||
auto ledStreamState = LEDStates_e::LedStateNone;
|
||||
|
||||
if (xQueueReceive(this->eventQueue, &eventBuffer, portMAX_DELAY))
|
||||
{
|
||||
switch (eventBuffer.source)
|
||||
if (xQueueReceive(this->eventQueue, &eventBuffer, portMAX_DELAY))
|
||||
{
|
||||
switch (eventBuffer.source)
|
||||
{
|
||||
case EventSource::WIFI:
|
||||
{
|
||||
this->wifi_state = std::get<WiFiState_e>(eventBuffer.value);
|
||||
|
||||
case EventSource::WIFI:
|
||||
{
|
||||
this->wifi_state = std::get<WiFiState_e>(eventBuffer.value);
|
||||
if (this->wifi_state == WiFiState_e::WiFiState_Connecting)
|
||||
{
|
||||
ledStreamState = LEDStates_e::WiFiStateConnecting;
|
||||
xQueueSend(this->ledStateQueue, &ledStreamState, 10);
|
||||
}
|
||||
if (this->wifi_state == WiFiState_e::WiFiState_Connected)
|
||||
{
|
||||
ledStreamState = LEDStates_e::WiFiStateConnected;
|
||||
xQueueSend(this->ledStateQueue, &ledStreamState, 10);
|
||||
}
|
||||
if (this->wifi_state == WiFiState_e::WiFiState_Error)
|
||||
{
|
||||
ledStreamState = LEDStates_e::WiFiStateError;
|
||||
xQueueSend(this->ledStateQueue, &ledStreamState, 10);
|
||||
}
|
||||
|
||||
if (this->wifi_state == WiFiState_e::WiFiState_Connecting)
|
||||
{
|
||||
ledStreamState = LEDStates_e::WiFiStateConnecting;
|
||||
xQueueSend(this->ledStateQueue, &ledStreamState, 10);
|
||||
}
|
||||
if (this->wifi_state == WiFiState_e::WiFiState_Connected)
|
||||
{
|
||||
ledStreamState = LEDStates_e::WiFiStateConnected;
|
||||
xQueueSend(this->ledStateQueue, &ledStreamState, 10);
|
||||
}
|
||||
if (this->wifi_state == WiFiState_e::WiFiState_Error)
|
||||
{
|
||||
ledStreamState = LEDStates_e::WiFiStateError;
|
||||
xQueueSend(this->ledStateQueue, &ledStreamState, 10);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
case EventSource::MDNS:
|
||||
{
|
||||
this->mdns_state = std::get<MDNSState_e>(eventBuffer.value);
|
||||
break;
|
||||
}
|
||||
|
||||
case EventSource::CAMERA:
|
||||
{
|
||||
this->camera_state = std::get<CameraState_e>(eventBuffer.value);
|
||||
|
||||
if (this->camera_state == CameraState_e::Camera_Error)
|
||||
{
|
||||
ledStreamState = LEDStates_e::CameraError;
|
||||
xQueueSend(this->ledStateQueue, &ledStreamState, 10);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case EventSource::STREAM:
|
||||
{
|
||||
this->stream_state = std::get<StreamState_e>(eventBuffer.value);
|
||||
|
||||
if (this->stream_state == StreamState_e::Stream_ON)
|
||||
{
|
||||
ledStreamState = LEDStates_e::LedStateStreaming;
|
||||
xQueueSend(this->ledStateQueue, &ledStreamState, 10);
|
||||
}
|
||||
else if (this->stream_state == StreamState_e::Stream_OFF)
|
||||
{
|
||||
ledStreamState = LEDStates_e::LedStateStoppedStreaming;
|
||||
xQueueSend(this->ledStateQueue, &ledStreamState, 10);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
case EventSource::MDNS:
|
||||
{
|
||||
this->mdns_state = std::get<MDNSState_e>(eventBuffer.value);
|
||||
break;
|
||||
}
|
||||
|
||||
case EventSource::CAMERA:
|
||||
{
|
||||
this->camera_state = std::get<CameraState_e>(eventBuffer.value);
|
||||
|
||||
if (this->camera_state == CameraState_e::Camera_Error)
|
||||
{
|
||||
ledStreamState = LEDStates_e::CameraError;
|
||||
xQueueSend(this->ledStateQueue, &ledStreamState, 10);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case EventSource::STREAM:
|
||||
{
|
||||
this->stream_state = std::get<StreamState_e>(eventBuffer.value);
|
||||
|
||||
if (this->stream_state == StreamState_e::Stream_ON)
|
||||
{
|
||||
ledStreamState = LEDStates_e::LedStateStreaming;
|
||||
xQueueSend(this->ledStateQueue, &ledStreamState, 10);
|
||||
}
|
||||
else if (this->stream_state == StreamState_e::Stream_OFF)
|
||||
{
|
||||
ledStreamState = LEDStates_e::LedStateStoppedStreaming;
|
||||
xQueueSend(this->ledStateQueue, &ledStreamState, 10);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WiFiState_e StateManager::GetWifiState()
|
||||
{
|
||||
return this->wifi_state;
|
||||
return this->wifi_state;
|
||||
}
|
||||
|
||||
CameraState_e StateManager::GetCameraState()
|
||||
{
|
||||
return this->camera_state;
|
||||
return this->camera_state;
|
||||
}
|
||||
|
||||
QueueHandle_t StateManager::GetEventQueue() const
|
||||
{
|
||||
return this->eventQueue;
|
||||
return this->eventQueue;
|
||||
}
|
||||
|
||||
void HandleStateManagerTask(void *pvParameters)
|
||||
void HandleStateManagerTask(void* pvParameters)
|
||||
{
|
||||
auto *stateManager = static_cast<StateManager *>(pvParameters);
|
||||
auto* stateManager = static_cast<StateManager*>(pvParameters);
|
||||
|
||||
while (true)
|
||||
{
|
||||
stateManager->HandleUpdateState();
|
||||
}
|
||||
while (true)
|
||||
{
|
||||
stateManager->HandleUpdateState();
|
||||
}
|
||||
}
|
||||
@@ -10,93 +10,93 @@
|
||||
// Naming kept stable for existing queues; documented meanings added.
|
||||
enum class LEDStates_e
|
||||
{
|
||||
LedStateNone, // Idle / no indication (LED off)
|
||||
LedStateStreaming, // Active streaming (UVC or WiFi) – steady ON
|
||||
LedStateStoppedStreaming, // Streaming stopped intentionally – steady OFF (could differentiate later)
|
||||
CameraError, // Camera init / runtime failure – double blink pattern
|
||||
WiFiStateError, // WiFi connection error – distinctive blink sequence
|
||||
WiFiStateConnecting, // WiFi association / DHCP pending – slow blink
|
||||
WiFiStateConnected // WiFi connected (momentary confirmation burst)
|
||||
LedStateNone, // Idle / no indication (LED off)
|
||||
LedStateStreaming, // Active streaming (UVC or WiFi) - steady ON
|
||||
LedStateStoppedStreaming, // Streaming stopped intentionally - steady OFF (could differentiate later)
|
||||
CameraError, // Camera init / runtime failure - double blink pattern
|
||||
WiFiStateError, // WiFi connection error - distinctive blink sequence
|
||||
WiFiStateConnecting, // WiFi association / DHCP pending - slow blink
|
||||
WiFiStateConnected // WiFi connected (momentary confirmation burst)
|
||||
};
|
||||
|
||||
enum class WiFiState_e
|
||||
{
|
||||
WiFiState_NotInitialized,
|
||||
WiFiState_Initialized,
|
||||
WiFiState_ReadyToConnect,
|
||||
WiFiState_Connecting,
|
||||
WiFiState_WaitingForIp,
|
||||
WiFiState_Connected,
|
||||
WiFiState_Disconnected,
|
||||
WiFiState_Error
|
||||
WiFiState_NotInitialized,
|
||||
WiFiState_Initialized,
|
||||
WiFiState_ReadyToConnect,
|
||||
WiFiState_Connecting,
|
||||
WiFiState_WaitingForIp,
|
||||
WiFiState_Connected,
|
||||
WiFiState_Disconnected,
|
||||
WiFiState_Error
|
||||
};
|
||||
|
||||
enum class MDNSState_e
|
||||
{
|
||||
MDNSState_Stopped,
|
||||
MDNSState_Starting,
|
||||
MDNSState_Started,
|
||||
MDNSState_Stopping,
|
||||
MDNSState_Error,
|
||||
MDNSState_QueryStarted,
|
||||
MDNSState_QueryComplete
|
||||
MDNSState_Stopped,
|
||||
MDNSState_Starting,
|
||||
MDNSState_Started,
|
||||
MDNSState_Stopping,
|
||||
MDNSState_Error,
|
||||
MDNSState_QueryStarted,
|
||||
MDNSState_QueryComplete
|
||||
};
|
||||
|
||||
enum class CameraState_e
|
||||
{
|
||||
Camera_Disconnected,
|
||||
Camera_Success,
|
||||
Camera_Error
|
||||
Camera_Disconnected,
|
||||
Camera_Success,
|
||||
Camera_Error
|
||||
};
|
||||
|
||||
enum class StreamState_e
|
||||
{
|
||||
Stream_OFF,
|
||||
Stream_ON,
|
||||
Stream_OFF,
|
||||
Stream_ON,
|
||||
};
|
||||
|
||||
enum class EventSource
|
||||
{
|
||||
WIFI,
|
||||
MDNS,
|
||||
CAMERA,
|
||||
STREAM
|
||||
WIFI,
|
||||
MDNS,
|
||||
CAMERA,
|
||||
STREAM
|
||||
};
|
||||
|
||||
struct SystemEvent
|
||||
{
|
||||
EventSource source;
|
||||
std::variant<WiFiState_e, MDNSState_e, CameraState_e, StreamState_e> value;
|
||||
EventSource source;
|
||||
std::variant<WiFiState_e, MDNSState_e, CameraState_e, StreamState_e> value;
|
||||
};
|
||||
|
||||
class StateManager
|
||||
{
|
||||
public:
|
||||
StateManager(QueueHandle_t eventQueue, QueueHandle_t ledStateQueue);
|
||||
void HandleUpdateState();
|
||||
WiFiState_e GetWifiState();
|
||||
CameraState_e GetCameraState();
|
||||
QueueHandle_t GetEventQueue() const;
|
||||
public:
|
||||
StateManager(QueueHandle_t eventQueue, QueueHandle_t ledStateQueue);
|
||||
void HandleUpdateState();
|
||||
WiFiState_e GetWifiState();
|
||||
CameraState_e GetCameraState();
|
||||
QueueHandle_t GetEventQueue() const;
|
||||
|
||||
private:
|
||||
QueueHandle_t eventQueue;
|
||||
QueueHandle_t ledStateQueue;
|
||||
private:
|
||||
QueueHandle_t eventQueue;
|
||||
QueueHandle_t ledStateQueue;
|
||||
|
||||
WiFiState_e wifi_state;
|
||||
MDNSState_e mdns_state;
|
||||
CameraState_e camera_state;
|
||||
StreamState_e stream_state;
|
||||
WiFiState_e wifi_state;
|
||||
MDNSState_e mdns_state;
|
||||
CameraState_e camera_state;
|
||||
StreamState_e stream_state;
|
||||
};
|
||||
|
||||
// Lightweight helper to publish stream state changes to the shared event queue.
|
||||
static inline bool SendStreamEvent(QueueHandle_t queue, StreamState_e state)
|
||||
{
|
||||
if (!queue)
|
||||
return false;
|
||||
SystemEvent evt{EventSource::STREAM, state};
|
||||
return xQueueSend(queue, &evt, 0) == pdTRUE;
|
||||
if (!queue)
|
||||
return false;
|
||||
SystemEvent evt{EventSource::STREAM, state};
|
||||
return xQueueSend(queue, &evt, 0) == pdTRUE;
|
||||
}
|
||||
|
||||
void HandleStateManagerTask(void *pvParameters);
|
||||
void HandleStateManagerTask(void* pvParameters);
|
||||
|
||||
#endif // STATEMANAGER_HPP
|
||||
#endif // STATEMANAGER_HPP
|
||||
|
||||
@@ -1,174 +1,172 @@
|
||||
#include "StreamServer.hpp"
|
||||
|
||||
constexpr static const char *STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
|
||||
constexpr static const char *STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
|
||||
constexpr static const char *STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\nX-Timestamp: %lli.%06li\r\n\r\n";
|
||||
constexpr static const char* STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
|
||||
constexpr static const char* STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
|
||||
constexpr static const char* STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\nX-Timestamp: %lli.%06li\r\n\r\n";
|
||||
|
||||
static const char *STREAM_SERVER_TAG = "[STREAM_SERVER]";
|
||||
static const char* STREAM_SERVER_TAG = "[STREAM_SERVER]";
|
||||
|
||||
StreamServer::StreamServer(const int STREAM_PORT, StateManager *stateManager) : STREAM_SERVER_PORT(STREAM_PORT), stateManager(stateManager)
|
||||
StreamServer::StreamServer(const int STREAM_PORT, StateManager* stateManager) : STREAM_SERVER_PORT(STREAM_PORT), stateManager(stateManager) {}
|
||||
|
||||
esp_err_t StreamHelpers::stream(httpd_req_t* req)
|
||||
{
|
||||
}
|
||||
camera_fb_t* fb = nullptr;
|
||||
struct timeval _timestamp;
|
||||
|
||||
esp_err_t StreamHelpers::stream(httpd_req_t *req)
|
||||
{
|
||||
camera_fb_t *fb = nullptr;
|
||||
struct timeval _timestamp;
|
||||
esp_err_t response = ESP_OK;
|
||||
size_t _jpg_buf_len = 0;
|
||||
uint8_t* _jpg_buf = nullptr;
|
||||
|
||||
esp_err_t response = ESP_OK;
|
||||
size_t _jpg_buf_len = 0;
|
||||
uint8_t *_jpg_buf = nullptr;
|
||||
// Buffer for multipart header
|
||||
char part_buf[256];
|
||||
static int64_t last_frame = 0;
|
||||
if (!last_frame)
|
||||
last_frame = esp_timer_get_time();
|
||||
|
||||
// Buffer for multipart header; was mistakenly declared as array of pointers
|
||||
char part_buf[256];
|
||||
static int64_t last_frame = 0;
|
||||
if (!last_frame)
|
||||
last_frame = esp_timer_get_time();
|
||||
// Pull event queue from user_ctx to send STREAM on/off notifications
|
||||
auto* stateManager = static_cast<StateManager*>(req->user_ctx);
|
||||
QueueHandle_t eventQueue = stateManager ? stateManager->GetEventQueue() : nullptr;
|
||||
bool stream_on_sent = false;
|
||||
|
||||
// Pull event queue from user_ctx to send STREAM on/off notifications
|
||||
auto *stateManager = static_cast<StateManager *>(req->user_ctx);
|
||||
QueueHandle_t eventQueue = stateManager ? stateManager->GetEventQueue() : nullptr;
|
||||
bool stream_on_sent = false;
|
||||
|
||||
response = httpd_resp_set_type(req, STREAM_CONTENT_TYPE);
|
||||
if (response != ESP_OK)
|
||||
return response;
|
||||
|
||||
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
|
||||
httpd_resp_set_hdr(req, "X-Framerate", "60");
|
||||
|
||||
if (SendStreamEvent(eventQueue, StreamState_e::Stream_ON))
|
||||
stream_on_sent = true;
|
||||
|
||||
while (true)
|
||||
{
|
||||
fb = esp_camera_fb_get();
|
||||
|
||||
if (!fb)
|
||||
{
|
||||
ESP_LOGE(STREAM_SERVER_TAG, "Camera capture failed");
|
||||
response = ESP_FAIL;
|
||||
// Don't break immediately, try to recover
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
_timestamp.tv_sec = fb->timestamp.tv_sec;
|
||||
_timestamp.tv_usec = fb->timestamp.tv_usec;
|
||||
_jpg_buf_len = fb->len;
|
||||
_jpg_buf = fb->buf;
|
||||
}
|
||||
if (response == ESP_OK)
|
||||
response = httpd_resp_send_chunk(req, STREAM_BOUNDARY, strlen(STREAM_BOUNDARY));
|
||||
if (response == ESP_OK)
|
||||
{
|
||||
size_t hlen = snprintf((char *)part_buf, sizeof(part_buf), STREAM_PART, _jpg_buf_len, _timestamp.tv_sec, _timestamp.tv_usec);
|
||||
response = httpd_resp_send_chunk(req, (const char *)part_buf, hlen);
|
||||
}
|
||||
if (response == ESP_OK)
|
||||
response = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len);
|
||||
if (fb)
|
||||
{
|
||||
esp_camera_fb_return(fb);
|
||||
fb = NULL;
|
||||
_jpg_buf = NULL;
|
||||
}
|
||||
else if (_jpg_buf)
|
||||
{
|
||||
free(_jpg_buf);
|
||||
_jpg_buf = NULL;
|
||||
}
|
||||
response = httpd_resp_set_type(req, STREAM_CONTENT_TYPE);
|
||||
if (response != ESP_OK)
|
||||
break;
|
||||
return response;
|
||||
|
||||
if (esp_log_level_get(STREAM_SERVER_TAG) >= ESP_LOG_INFO)
|
||||
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
|
||||
httpd_resp_set_hdr(req, "X-Framerate", "60");
|
||||
|
||||
if (SendStreamEvent(eventQueue, StreamState_e::Stream_ON))
|
||||
stream_on_sent = true;
|
||||
|
||||
while (true)
|
||||
{
|
||||
static long last_request_time = 0;
|
||||
if (last_request_time == 0)
|
||||
{
|
||||
last_request_time = Helpers::getTimeInMillis();
|
||||
}
|
||||
fb = esp_camera_fb_get();
|
||||
|
||||
// Only log every 100 frames to reduce overhead
|
||||
const int frame_window = 100;
|
||||
static int frame_count = 0;
|
||||
if (++frame_count % frame_window == 0)
|
||||
{
|
||||
long request_end = Helpers::getTimeInMillis();
|
||||
long window_ms = request_end - last_request_time;
|
||||
last_request_time = request_end;
|
||||
long fps = 0;
|
||||
if (window_ms > 0)
|
||||
if (!fb)
|
||||
{
|
||||
fps = (frame_window * 1000) / window_ms;
|
||||
ESP_LOGE(STREAM_SERVER_TAG, "Camera capture failed");
|
||||
response = ESP_FAIL;
|
||||
// Don't break immediately, try to recover
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
_timestamp.tv_sec = fb->timestamp.tv_sec;
|
||||
_timestamp.tv_usec = fb->timestamp.tv_usec;
|
||||
_jpg_buf_len = fb->len;
|
||||
_jpg_buf = fb->buf;
|
||||
}
|
||||
if (response == ESP_OK)
|
||||
response = httpd_resp_send_chunk(req, STREAM_BOUNDARY, strlen(STREAM_BOUNDARY));
|
||||
if (response == ESP_OK)
|
||||
{
|
||||
size_t hlen = snprintf((char*)part_buf, sizeof(part_buf), STREAM_PART, _jpg_buf_len, _timestamp.tv_sec, _timestamp.tv_usec);
|
||||
response = httpd_resp_send_chunk(req, (const char*)part_buf, hlen);
|
||||
}
|
||||
if (response == ESP_OK)
|
||||
response = httpd_resp_send_chunk(req, (const char*)_jpg_buf, _jpg_buf_len);
|
||||
if (fb)
|
||||
{
|
||||
esp_camera_fb_return(fb);
|
||||
fb = NULL;
|
||||
_jpg_buf = NULL;
|
||||
}
|
||||
else if (_jpg_buf)
|
||||
{
|
||||
free(_jpg_buf);
|
||||
_jpg_buf = NULL;
|
||||
}
|
||||
if (response != ESP_OK)
|
||||
break;
|
||||
|
||||
if (esp_log_level_get(STREAM_SERVER_TAG) >= ESP_LOG_INFO)
|
||||
{
|
||||
static long last_request_time = 0;
|
||||
if (last_request_time == 0)
|
||||
{
|
||||
last_request_time = Helpers::getTimeInMillis();
|
||||
}
|
||||
|
||||
// Only log every 100 frames to reduce overhead
|
||||
const int frame_window = 100;
|
||||
static int frame_count = 0;
|
||||
if (++frame_count % frame_window == 0)
|
||||
{
|
||||
long request_end = Helpers::getTimeInMillis();
|
||||
long window_ms = request_end - last_request_time;
|
||||
last_request_time = request_end;
|
||||
long fps = 0;
|
||||
if (window_ms > 0)
|
||||
{
|
||||
fps = (frame_window * 1000) / window_ms;
|
||||
}
|
||||
ESP_LOGI(STREAM_SERVER_TAG, "%i Frames Size: %uKB, Time: %lims (%lifps)", frame_window, _jpg_buf_len / 1024, window_ms, fps);
|
||||
}
|
||||
}
|
||||
ESP_LOGI(STREAM_SERVER_TAG, "%i Frames Size: %uKB, Time: %lims (%lifps)",frame_window, _jpg_buf_len / 1024, window_ms, fps);
|
||||
}
|
||||
}
|
||||
}
|
||||
last_frame = 0;
|
||||
last_frame = 0;
|
||||
|
||||
if (stream_on_sent)
|
||||
SendStreamEvent(eventQueue, StreamState_e::Stream_OFF);
|
||||
if (stream_on_sent)
|
||||
SendStreamEvent(eventQueue, StreamState_e::Stream_OFF);
|
||||
|
||||
return response;
|
||||
return response;
|
||||
}
|
||||
|
||||
esp_err_t StreamHelpers::ws_logs_handle(httpd_req_t *req)
|
||||
esp_err_t StreamHelpers::ws_logs_handle(httpd_req_t* req)
|
||||
{
|
||||
auto ret = webSocketLogger.register_socket_client(req);
|
||||
return ret;
|
||||
auto ret = webSocketLogger.register_socket_client(req);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t StreamServer::startStreamServer()
|
||||
{
|
||||
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
|
||||
config.stack_size = 20480;
|
||||
config.max_uri_handlers = 10;
|
||||
config.server_port = STREAM_SERVER_PORT;
|
||||
config.ctrl_port = STREAM_SERVER_PORT;
|
||||
config.recv_wait_timeout = 5; // 5 seconds for receiving
|
||||
config.send_wait_timeout = 5; // 5 seconds for sending
|
||||
config.lru_purge_enable = true; // Enable LRU purge for better connection handling
|
||||
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
|
||||
config.stack_size = 20480;
|
||||
config.max_uri_handlers = 10;
|
||||
config.server_port = STREAM_SERVER_PORT;
|
||||
config.ctrl_port = STREAM_SERVER_PORT;
|
||||
config.recv_wait_timeout = 5; // 5 seconds for receiving
|
||||
config.send_wait_timeout = 5; // 5 seconds for sending
|
||||
config.lru_purge_enable = true; // Enable LRU purge for better connection handling
|
||||
|
||||
httpd_uri_t stream_page = {
|
||||
.uri = "/",
|
||||
.method = HTTP_GET,
|
||||
.handler = &StreamHelpers::stream,
|
||||
.user_ctx = this->stateManager,
|
||||
};
|
||||
httpd_uri_t stream_page = {
|
||||
.uri = "/",
|
||||
.method = HTTP_GET,
|
||||
.handler = &StreamHelpers::stream,
|
||||
.user_ctx = this->stateManager,
|
||||
};
|
||||
|
||||
httpd_uri_t logs_ws = {
|
||||
.uri = "/ws",
|
||||
.method = HTTP_GET,
|
||||
.handler = &StreamHelpers::ws_logs_handle,
|
||||
.user_ctx = nullptr,
|
||||
.is_websocket = true,
|
||||
};
|
||||
httpd_uri_t logs_ws = {
|
||||
.uri = "/ws",
|
||||
.method = HTTP_GET,
|
||||
.handler = &StreamHelpers::ws_logs_handle,
|
||||
.user_ctx = nullptr,
|
||||
.is_websocket = true,
|
||||
};
|
||||
|
||||
int status = httpd_start(&camera_stream, &config);
|
||||
int status = httpd_start(&camera_stream, &config);
|
||||
|
||||
if (status != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(STREAM_SERVER_TAG, "Cannot start stream server.");
|
||||
return status;
|
||||
}
|
||||
if (status != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(STREAM_SERVER_TAG, "Cannot start stream server.");
|
||||
return status;
|
||||
}
|
||||
|
||||
httpd_register_uri_handler(camera_stream, &logs_ws);
|
||||
if (this->stateManager->GetCameraState() != CameraState_e::Camera_Success)
|
||||
{
|
||||
ESP_LOGE(STREAM_SERVER_TAG, "Camera not initialized. Cannot start stream server. Logs server will be running.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
httpd_register_uri_handler(camera_stream, &logs_ws);
|
||||
if (this->stateManager->GetCameraState() != CameraState_e::Camera_Success)
|
||||
{
|
||||
ESP_LOGE(STREAM_SERVER_TAG, "Camera not initialized. Cannot start stream server. Logs server will be running.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
httpd_register_uri_handler(camera_stream, &stream_page);
|
||||
httpd_register_uri_handler(camera_stream, &stream_page);
|
||||
|
||||
// Initial state is OFF
|
||||
if (this->stateManager)
|
||||
SendStreamEvent(this->stateManager->GetEventQueue(), StreamState_e::Stream_OFF);
|
||||
// Initial state is OFF
|
||||
if (this->stateManager)
|
||||
SendStreamEvent(this->stateManager->GetEventQueue(), StreamState_e::Stream_OFF);
|
||||
|
||||
ESP_LOGI(STREAM_SERVER_TAG, "Stream server started on port %d", STREAM_SERVER_PORT);
|
||||
ESP_LOGI(STREAM_SERVER_TAG, "Stream server started on port %d", STREAM_SERVER_PORT);
|
||||
|
||||
return ESP_OK;
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -4,35 +4,35 @@
|
||||
|
||||
#define PART_BOUNDARY "123456789000000000000987654321"
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_camera.h"
|
||||
#include "esp_http_server.h"
|
||||
#include "esp_timer.h"
|
||||
#include <StateManager.hpp>
|
||||
#include <WebSocketLogger.hpp>
|
||||
#include <helpers.hpp>
|
||||
#include "esp_camera.h"
|
||||
#include "esp_http_server.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h"
|
||||
|
||||
extern WebSocketLogger webSocketLogger;
|
||||
|
||||
namespace StreamHelpers
|
||||
{
|
||||
esp_err_t stream(httpd_req_t *req);
|
||||
esp_err_t ws_logs_handle(httpd_req_t *req);
|
||||
}
|
||||
esp_err_t stream(httpd_req_t* req);
|
||||
esp_err_t ws_logs_handle(httpd_req_t* req);
|
||||
} // namespace StreamHelpers
|
||||
|
||||
class StreamServer
|
||||
{
|
||||
private:
|
||||
int STREAM_SERVER_PORT;
|
||||
StateManager *stateManager;
|
||||
httpd_handle_t camera_stream = nullptr;
|
||||
private:
|
||||
int STREAM_SERVER_PORT;
|
||||
StateManager* stateManager;
|
||||
httpd_handle_t camera_stream = nullptr;
|
||||
|
||||
public:
|
||||
StreamServer(const int STREAM_PORT, StateManager *StateManager);
|
||||
esp_err_t startStreamServer();
|
||||
public:
|
||||
StreamServer(const int STREAM_PORT, StateManager* StateManager);
|
||||
esp_err_t startStreamServer();
|
||||
|
||||
esp_err_t stream(httpd_req_t *req);
|
||||
esp_err_t ws_logs_handle(httpd_req_t *req);
|
||||
esp_err_t stream(httpd_req_t* req);
|
||||
esp_err_t ws_logs_handle(httpd_req_t* req);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,11 +1,11 @@
|
||||
#include "UVCStream.hpp"
|
||||
|
||||
#ifdef CONFIG_GENERAL_INCLUDE_UVC_MODE
|
||||
#include <cstdio> // for snprintf
|
||||
#include <cstdio> // for snprintf
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
static const char *UVC_STREAM_TAG = "[UVC DEVICE]";
|
||||
static const char* UVC_STREAM_TAG = "[UVC DEVICE]";
|
||||
|
||||
// Tracks whether a frame has been handed to TinyUSB and not yet returned.
|
||||
// File scope so both get_cb and return_cb can access it safely.
|
||||
@@ -13,207 +13,207 @@ static bool s_frame_inflight = false;
|
||||
|
||||
extern "C"
|
||||
{
|
||||
static char serial_number_str[13];
|
||||
static char serial_number_str[13];
|
||||
|
||||
const char *get_uvc_device_name()
|
||||
{
|
||||
return deviceConfig->getMDNSConfig().hostname.c_str();
|
||||
}
|
||||
|
||||
const char *get_serial_number(void)
|
||||
{
|
||||
if (serial_number_str[0] == '\0')
|
||||
const char* get_uvc_device_name()
|
||||
{
|
||||
uint8_t mac_address[6];
|
||||
esp_err_t result = esp_efuse_mac_get_default(mac_address);
|
||||
if (result != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(UVC_STREAM_TAG, "Failed to get MAC address of the board, returning default serial number");
|
||||
return CONFIG_TUSB_SERIAL_NUM;
|
||||
}
|
||||
|
||||
// 12 hex chars without separators
|
||||
snprintf(serial_number_str, sizeof(serial_number_str), "%02X%02X%02X%02X%02X%02X",
|
||||
mac_address[0], mac_address[1], mac_address[2], mac_address[3], mac_address[4], mac_address[5]);
|
||||
return deviceConfig->getMDNSConfig().hostname.c_str();
|
||||
}
|
||||
|
||||
const char* get_serial_number(void)
|
||||
{
|
||||
if (serial_number_str[0] == '\0')
|
||||
{
|
||||
uint8_t mac_address[6];
|
||||
esp_err_t result = esp_efuse_mac_get_default(mac_address);
|
||||
if (result != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(UVC_STREAM_TAG, "Failed to get MAC address of the board, returning default serial number");
|
||||
return CONFIG_TUSB_SERIAL_NUM;
|
||||
}
|
||||
|
||||
// 12 hex chars without separators
|
||||
snprintf(serial_number_str, sizeof(serial_number_str), "%02X%02X%02X%02X%02X%02X", mac_address[0], mac_address[1], mac_address[2], mac_address[3],
|
||||
mac_address[4], mac_address[5]);
|
||||
}
|
||||
return serial_number_str;
|
||||
}
|
||||
return serial_number_str;
|
||||
}
|
||||
}
|
||||
|
||||
// single definition of shared framebuffer storage
|
||||
UVCStreamHelpers::fb_t UVCStreamHelpers::s_fb = {};
|
||||
|
||||
static esp_err_t UVCStreamHelpers::camera_start_cb(uvc_format_t format, int width, int height, int rate, void *cb_ctx)
|
||||
static esp_err_t UVCStreamHelpers::camera_start_cb(uvc_format_t format, int width, int height, int rate, void* cb_ctx)
|
||||
{
|
||||
ESP_LOGI(UVC_STREAM_TAG, "Camera Start");
|
||||
ESP_LOGI(UVC_STREAM_TAG, "Format: %d, width: %d, height: %d, rate: %d", format, width, height, rate);
|
||||
framesize_t frame_size = FRAMESIZE_QVGA;
|
||||
ESP_LOGI(UVC_STREAM_TAG, "Camera Start");
|
||||
ESP_LOGI(UVC_STREAM_TAG, "Format: %d, width: %d, height: %d, rate: %d", format, width, height, rate);
|
||||
framesize_t frame_size = FRAMESIZE_QVGA;
|
||||
|
||||
if (format != UVC_FORMAT_JPEG)
|
||||
{
|
||||
ESP_LOGE(UVC_STREAM_TAG, "Only support MJPEG format");
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
if (format != UVC_FORMAT_JPEG)
|
||||
{
|
||||
ESP_LOGE(UVC_STREAM_TAG, "Only support MJPEG format");
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
if (width == 240 && height == 240)
|
||||
{
|
||||
frame_size = FRAMESIZE_240X240;
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGE(UVC_STREAM_TAG, "Unsupported frame size %dx%d", width, height);
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
if (width == 240 && height == 240)
|
||||
{
|
||||
frame_size = FRAMESIZE_240X240;
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGE(UVC_STREAM_TAG, "Unsupported frame size %dx%d", width, height);
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
cameraHandler->setCameraResolution(frame_size);
|
||||
cameraHandler->setCameraResolution(frame_size);
|
||||
|
||||
SendStreamEvent(eventQueue, StreamState_e::Stream_ON);
|
||||
SendStreamEvent(eventQueue, StreamState_e::Stream_ON);
|
||||
|
||||
return ESP_OK;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void UVCStreamHelpers::camera_stop_cb(void *cb_ctx)
|
||||
static void UVCStreamHelpers::camera_stop_cb(void* cb_ctx)
|
||||
{
|
||||
(void)cb_ctx;
|
||||
if (s_fb.cam_fb_p)
|
||||
{
|
||||
esp_camera_fb_return(s_fb.cam_fb_p);
|
||||
s_fb.cam_fb_p = nullptr;
|
||||
}
|
||||
(void)cb_ctx;
|
||||
if (s_fb.cam_fb_p)
|
||||
{
|
||||
esp_camera_fb_return(s_fb.cam_fb_p);
|
||||
s_fb.cam_fb_p = nullptr;
|
||||
}
|
||||
|
||||
SendStreamEvent(eventQueue, StreamState_e::Stream_OFF);
|
||||
SendStreamEvent(eventQueue, StreamState_e::Stream_OFF);
|
||||
}
|
||||
|
||||
static uvc_fb_t *UVCStreamHelpers::camera_fb_get_cb(void *cb_ctx)
|
||||
static uvc_fb_t* UVCStreamHelpers::camera_fb_get_cb(void* cb_ctx)
|
||||
{
|
||||
auto *mgr = static_cast<UVCStreamManager *>(cb_ctx);
|
||||
auto* mgr = static_cast<UVCStreamManager*>(cb_ctx);
|
||||
|
||||
// Guard against requesting a new frame while previous is still in flight.
|
||||
// This was causing intermittent corruption/glitches because the pointer
|
||||
// to the underlying camera buffer was overwritten before TinyUSB returned it.
|
||||
// Guard against requesting a new frame while previous is still in flight.
|
||||
// This was causing intermittent corruption/glitches because the pointer
|
||||
// to the underlying camera buffer was overwritten before TinyUSB returned it.
|
||||
|
||||
// --- Frame pacing BEFORE grabbing a new camera frame ---
|
||||
static int64_t next_deadline_us = 0; // next permitted capture time
|
||||
static int rem_acc = 0; // fractional remainder accumulator
|
||||
static const int target_fps = 60; // desired FPS
|
||||
static const int64_t us_per_sec = 1000000; // 1e6 microseconds
|
||||
static const int base_interval_us = us_per_sec / target_fps; // 16666
|
||||
static const int rem_us = us_per_sec % target_fps; // 40 (distributed)
|
||||
// --- Frame pacing BEFORE grabbing a new camera frame ---
|
||||
static int64_t next_deadline_us = 0; // next permitted capture time
|
||||
static int rem_acc = 0; // fractional remainder accumulator
|
||||
static const int target_fps = 60; // desired FPS
|
||||
static const int64_t us_per_sec = 1000000; // 1e6 microseconds
|
||||
static const int base_interval_us = us_per_sec / target_fps; // 16666
|
||||
static const int rem_us = us_per_sec % target_fps; // 40 (distributed)
|
||||
|
||||
const int64_t now_us = esp_timer_get_time();
|
||||
if (next_deadline_us == 0)
|
||||
{
|
||||
// First allowed capture immediately
|
||||
next_deadline_us = now_us;
|
||||
}
|
||||
const int64_t now_us = esp_timer_get_time();
|
||||
if (next_deadline_us == 0)
|
||||
{
|
||||
// First allowed capture immediately
|
||||
next_deadline_us = now_us;
|
||||
}
|
||||
|
||||
// If a frame is still being transmitted or we are too early, just signal no frame
|
||||
if (s_frame_inflight || now_us < next_deadline_us)
|
||||
{
|
||||
return nullptr; // host will poll again
|
||||
}
|
||||
// If a frame is still being transmitted or we are too early, just signal no frame
|
||||
if (s_frame_inflight || now_us < next_deadline_us)
|
||||
{
|
||||
return nullptr; // host will poll again
|
||||
}
|
||||
|
||||
// Acquire a fresh frame only when allowed and no frame in flight
|
||||
camera_fb_t *cam_fb = esp_camera_fb_get();
|
||||
if (!cam_fb)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
// Acquire a fresh frame only when allowed and no frame in flight
|
||||
camera_fb_t* cam_fb = esp_camera_fb_get();
|
||||
if (!cam_fb)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
s_fb.cam_fb_p = cam_fb;
|
||||
s_fb.uvc_fb.buf = cam_fb->buf;
|
||||
s_fb.uvc_fb.len = cam_fb->len;
|
||||
s_fb.uvc_fb.width = cam_fb->width;
|
||||
s_fb.uvc_fb.height = cam_fb->height;
|
||||
s_fb.uvc_fb.format = UVC_FORMAT_JPEG;
|
||||
s_fb.uvc_fb.timestamp = cam_fb->timestamp;
|
||||
s_fb.cam_fb_p = cam_fb;
|
||||
s_fb.uvc_fb.buf = cam_fb->buf;
|
||||
s_fb.uvc_fb.len = cam_fb->len;
|
||||
s_fb.uvc_fb.width = cam_fb->width;
|
||||
s_fb.uvc_fb.height = cam_fb->height;
|
||||
s_fb.uvc_fb.format = UVC_FORMAT_JPEG;
|
||||
s_fb.uvc_fb.timestamp = cam_fb->timestamp;
|
||||
|
||||
// Validate size fits into transfer buffer
|
||||
if (mgr && s_fb.uvc_fb.len > mgr->getUvcBufferSize())
|
||||
{
|
||||
ESP_LOGE(UVC_STREAM_TAG, "Frame size %d exceeds UVC buffer size %u", (int)s_fb.uvc_fb.len, (unsigned)mgr->getUvcBufferSize());
|
||||
esp_camera_fb_return(cam_fb);
|
||||
s_fb.cam_fb_p = nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
// Validate size fits into transfer buffer
|
||||
if (mgr && s_fb.uvc_fb.len > mgr->getUvcBufferSize())
|
||||
{
|
||||
ESP_LOGE(UVC_STREAM_TAG, "Frame size %d exceeds UVC buffer size %u", (int)s_fb.uvc_fb.len, (unsigned)mgr->getUvcBufferSize());
|
||||
esp_camera_fb_return(cam_fb);
|
||||
s_fb.cam_fb_p = nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Schedule next frame time (distribute remainder for exact long‑term 60.000 fps)
|
||||
rem_acc += rem_us;
|
||||
int extra_us = 0;
|
||||
if (rem_acc >= target_fps)
|
||||
{
|
||||
rem_acc -= target_fps;
|
||||
extra_us = 1;
|
||||
}
|
||||
const int64_t candidate_next = next_deadline_us + base_interval_us + extra_us;
|
||||
next_deadline_us = (candidate_next < now_us) ? now_us : candidate_next;
|
||||
// Schedule next frame time (distribute remainder for exact long‑term 60.000 fps)
|
||||
rem_acc += rem_us;
|
||||
int extra_us = 0;
|
||||
if (rem_acc >= target_fps)
|
||||
{
|
||||
rem_acc -= target_fps;
|
||||
extra_us = 1;
|
||||
}
|
||||
const int64_t candidate_next = next_deadline_us + base_interval_us + extra_us;
|
||||
next_deadline_us = (candidate_next < now_us) ? now_us : candidate_next;
|
||||
|
||||
s_frame_inflight = true;
|
||||
return &s_fb.uvc_fb;
|
||||
s_frame_inflight = true;
|
||||
return &s_fb.uvc_fb;
|
||||
}
|
||||
|
||||
static void UVCStreamHelpers::camera_fb_return_cb(uvc_fb_t *fb, void *cb_ctx)
|
||||
static void UVCStreamHelpers::camera_fb_return_cb(uvc_fb_t* fb, void* cb_ctx)
|
||||
{
|
||||
(void)cb_ctx;
|
||||
assert(fb == &s_fb.uvc_fb);
|
||||
if (s_fb.cam_fb_p)
|
||||
{
|
||||
esp_camera_fb_return(s_fb.cam_fb_p);
|
||||
s_fb.cam_fb_p = nullptr;
|
||||
}
|
||||
s_frame_inflight = false;
|
||||
(void)cb_ctx;
|
||||
assert(fb == &s_fb.uvc_fb);
|
||||
if (s_fb.cam_fb_p)
|
||||
{
|
||||
esp_camera_fb_return(s_fb.cam_fb_p);
|
||||
s_fb.cam_fb_p = nullptr;
|
||||
}
|
||||
s_frame_inflight = false;
|
||||
}
|
||||
|
||||
esp_err_t UVCStreamManager::setup()
|
||||
{
|
||||
ESP_LOGI(UVC_STREAM_TAG, "Setting up UVC Stream");
|
||||
// Allocate a fixed-size transfer buffer (compile-time constant)
|
||||
uvc_buffer_size = UVCStreamManager::UVC_MAX_FRAMESIZE_SIZE;
|
||||
uvc_buffer = static_cast<uint8_t *>(malloc(uvc_buffer_size));
|
||||
if (uvc_buffer == nullptr)
|
||||
{
|
||||
ESP_LOGE(UVC_STREAM_TAG, "Allocating buffer for UVC Device failed");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
ESP_LOGI(UVC_STREAM_TAG, "Setting up UVC Stream");
|
||||
// Allocate a fixed-size transfer buffer (compile-time constant)
|
||||
uvc_buffer_size = UVCStreamManager::UVC_MAX_FRAMESIZE_SIZE;
|
||||
uvc_buffer = static_cast<uint8_t*>(malloc(uvc_buffer_size));
|
||||
if (uvc_buffer == nullptr)
|
||||
{
|
||||
ESP_LOGE(UVC_STREAM_TAG, "Allocating buffer for UVC Device failed");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
uvc_device_config_t config = {
|
||||
.uvc_buffer = uvc_buffer,
|
||||
.uvc_buffer_size = UVCStreamManager::UVC_MAX_FRAMESIZE_SIZE,
|
||||
.start_cb = UVCStreamHelpers::camera_start_cb,
|
||||
.fb_get_cb = UVCStreamHelpers::camera_fb_get_cb,
|
||||
.fb_return_cb = UVCStreamHelpers::camera_fb_return_cb,
|
||||
.stop_cb = UVCStreamHelpers::camera_stop_cb,
|
||||
.cb_ctx = this,
|
||||
};
|
||||
uvc_device_config_t config = {
|
||||
.uvc_buffer = uvc_buffer,
|
||||
.uvc_buffer_size = UVCStreamManager::UVC_MAX_FRAMESIZE_SIZE,
|
||||
.start_cb = UVCStreamHelpers::camera_start_cb,
|
||||
.fb_get_cb = UVCStreamHelpers::camera_fb_get_cb,
|
||||
.fb_return_cb = UVCStreamHelpers::camera_fb_return_cb,
|
||||
.stop_cb = UVCStreamHelpers::camera_stop_cb,
|
||||
.cb_ctx = this,
|
||||
};
|
||||
|
||||
esp_err_t ret = uvc_device_config(0, &config);
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(UVC_STREAM_TAG, "Configuring UVC Device failed: %s", esp_err_to_name(ret));
|
||||
return ret;
|
||||
}
|
||||
ESP_LOGI(UVC_STREAM_TAG, "Configured UVC Device");
|
||||
esp_err_t ret = uvc_device_config(0, &config);
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(UVC_STREAM_TAG, "Configuring UVC Device failed: %s", esp_err_to_name(ret));
|
||||
return ret;
|
||||
}
|
||||
ESP_LOGI(UVC_STREAM_TAG, "Configured UVC Device");
|
||||
|
||||
ESP_LOGI(UVC_STREAM_TAG, "Initializing UVC Device");
|
||||
ret = uvc_device_init();
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(UVC_STREAM_TAG, "Initializing UVC Device failed: %s", esp_err_to_name(ret));
|
||||
return ret;
|
||||
}
|
||||
ESP_LOGI(UVC_STREAM_TAG, "Initialized UVC Device");
|
||||
ESP_LOGI(UVC_STREAM_TAG, "Initializing UVC Device");
|
||||
ret = uvc_device_init();
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(UVC_STREAM_TAG, "Initializing UVC Device failed: %s", esp_err_to_name(ret));
|
||||
return ret;
|
||||
}
|
||||
ESP_LOGI(UVC_STREAM_TAG, "Initialized UVC Device");
|
||||
|
||||
// Initial state is OFF
|
||||
SendStreamEvent(eventQueue, StreamState_e::Stream_OFF);
|
||||
// Initial state is OFF
|
||||
SendStreamEvent(eventQueue, StreamState_e::Stream_OFF);
|
||||
|
||||
return ESP_OK;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t UVCStreamManager::start()
|
||||
{
|
||||
ESP_LOGI(UVC_STREAM_TAG, "Starting UVC streaming");
|
||||
// UVC device is already initialized in setup(), just log that we're starting
|
||||
return ESP_OK;
|
||||
ESP_LOGI(UVC_STREAM_TAG, "Starting UVC streaming");
|
||||
// UVC device is already initialized in setup(), just log that we're starting
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -5,15 +5,15 @@
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#ifdef CONFIG_GENERAL_INCLUDE_UVC_MODE
|
||||
#include "esp_timer.h"
|
||||
#include "esp_mac.h"
|
||||
#include "esp_camera.h"
|
||||
#include <CameraManager.hpp>
|
||||
#include <StateManager.hpp>
|
||||
#include "esp_camera.h"
|
||||
#include "esp_log.h"
|
||||
#include "usb_device_uvc.h"
|
||||
#include "esp_mac.h"
|
||||
#include "esp_timer.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "usb_device_uvc.h"
|
||||
|
||||
// we need access to the camera manager
|
||||
// in order to update the frame settings
|
||||
@@ -25,8 +25,8 @@ extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
const char *get_uvc_device_name();
|
||||
const char *get_serial_number(void);
|
||||
const char* get_uvc_device_name();
|
||||
const char* get_serial_number(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
@@ -37,33 +37,36 @@ extern QueueHandle_t eventQueue;
|
||||
|
||||
namespace UVCStreamHelpers
|
||||
{
|
||||
typedef struct
|
||||
{
|
||||
camera_fb_t *cam_fb_p;
|
||||
typedef struct
|
||||
{
|
||||
camera_fb_t* cam_fb_p;
|
||||
uvc_fb_t uvc_fb;
|
||||
} fb_t;
|
||||
} fb_t;
|
||||
|
||||
// single storage is defined in UVCStream.cpp
|
||||
extern fb_t s_fb;
|
||||
// single storage is defined in UVCStream.cpp
|
||||
extern fb_t s_fb;
|
||||
|
||||
static esp_err_t camera_start_cb(uvc_format_t format, int width, int height, int rate, void *cb_ctx);
|
||||
static void camera_stop_cb(void *cb_ctx);
|
||||
static uvc_fb_t *camera_fb_get_cb(void *cb_ctx);
|
||||
static void camera_fb_return_cb(uvc_fb_t *fb, void *cb_ctx);
|
||||
}
|
||||
static esp_err_t camera_start_cb(uvc_format_t format, int width, int height, int rate, void* cb_ctx);
|
||||
static void camera_stop_cb(void* cb_ctx);
|
||||
static uvc_fb_t* camera_fb_get_cb(void* cb_ctx);
|
||||
static void camera_fb_return_cb(uvc_fb_t* fb, void* cb_ctx);
|
||||
} // namespace UVCStreamHelpers
|
||||
|
||||
class UVCStreamManager
|
||||
{
|
||||
uint8_t *uvc_buffer = nullptr;
|
||||
uint32_t uvc_buffer_size = 0;
|
||||
uint8_t* uvc_buffer = nullptr;
|
||||
uint32_t uvc_buffer_size = 0;
|
||||
|
||||
public:
|
||||
// Compile-time buffer size; keep conservative headroom for MJPEG QVGA
|
||||
static constexpr uint32_t UVC_MAX_FRAMESIZE_SIZE = 75 * 1024;
|
||||
esp_err_t setup();
|
||||
esp_err_t start();
|
||||
uint32_t getUvcBufferSize() const { return uvc_buffer_size; }
|
||||
public:
|
||||
// Compile-time buffer size; keep conservative headroom for MJPEG QVGA
|
||||
static constexpr uint32_t UVC_MAX_FRAMESIZE_SIZE = 75 * 1024;
|
||||
esp_err_t setup();
|
||||
esp_err_t start();
|
||||
uint32_t getUvcBufferSize() const
|
||||
{
|
||||
return uvc_buffer_size;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // UVCSTREAM_HPP
|
||||
#endif // UVCSTREAM_HPP
|
||||
#endif
|
||||
@@ -2,70 +2,70 @@
|
||||
|
||||
WebSocketLogger::WebSocketLogger()
|
||||
{
|
||||
this->connected_socket_client = async_resp_arg{
|
||||
.hd = nullptr,
|
||||
.fd = -1,
|
||||
};
|
||||
this->connected_socket_client = async_resp_arg{
|
||||
.hd = nullptr,
|
||||
.fd = -1,
|
||||
};
|
||||
|
||||
this->ws_log_buffer[0] = '\0';
|
||||
this->ws_log_buffer[0] = '\0';
|
||||
}
|
||||
|
||||
void LoggerHelpers::ws_async_send(void *arg)
|
||||
void LoggerHelpers::ws_async_send(void* arg)
|
||||
{
|
||||
char *log_buffer = webSocketLogger.get_websocket_log_buffer();
|
||||
char* log_buffer = webSocketLogger.get_websocket_log_buffer();
|
||||
|
||||
const auto *resp_arg = static_cast<struct async_resp_arg *>(arg);
|
||||
const auto hd = resp_arg->hd;
|
||||
const auto fd = resp_arg->fd;
|
||||
const auto* resp_arg = static_cast<struct async_resp_arg*>(arg);
|
||||
const auto hd = resp_arg->hd;
|
||||
const auto fd = resp_arg->fd;
|
||||
|
||||
auto websocket_packet = httpd_ws_frame_t{};
|
||||
auto websocket_packet = httpd_ws_frame_t{};
|
||||
|
||||
websocket_packet.payload = reinterpret_cast<uint8_t *>(log_buffer);
|
||||
websocket_packet.len = strlen(log_buffer);
|
||||
websocket_packet.type = HTTPD_WS_TYPE_TEXT;
|
||||
websocket_packet.payload = reinterpret_cast<uint8_t*>(log_buffer);
|
||||
websocket_packet.len = strlen(log_buffer);
|
||||
websocket_packet.type = HTTPD_WS_TYPE_TEXT;
|
||||
|
||||
httpd_ws_send_frame_async(hd, fd, &websocket_packet);
|
||||
httpd_ws_send_frame_async(hd, fd, &websocket_packet);
|
||||
}
|
||||
|
||||
esp_err_t WebSocketLogger::log_message(const char *format, va_list args)
|
||||
esp_err_t WebSocketLogger::log_message(const char* format, va_list args)
|
||||
{
|
||||
vsnprintf(this->ws_log_buffer, 100, format, args);
|
||||
vsnprintf(this->ws_log_buffer, 100, format, args);
|
||||
|
||||
if (connected_socket_client.fd == -1 || connected_socket_client.hd == nullptr)
|
||||
{
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (connected_socket_client.fd == -1 || connected_socket_client.hd == nullptr)
|
||||
{
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t ret = httpd_queue_work(connected_socket_client.hd, LoggerHelpers::ws_async_send, &connected_socket_client);
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
connected_socket_client.fd = -1;
|
||||
connected_socket_client.hd = nullptr;
|
||||
}
|
||||
esp_err_t ret = httpd_queue_work(connected_socket_client.hd, LoggerHelpers::ws_async_send, &connected_socket_client);
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
connected_socket_client.fd = -1;
|
||||
connected_socket_client.hd = nullptr;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t WebSocketLogger::register_socket_client(httpd_req_t *req)
|
||||
esp_err_t WebSocketLogger::register_socket_client(httpd_req_t* req)
|
||||
{
|
||||
if (connected_socket_client.fd != -1 && connected_socket_client.hd != nullptr)
|
||||
{
|
||||
// we're already connected
|
||||
if (connected_socket_client.fd != -1 && connected_socket_client.hd != nullptr)
|
||||
{
|
||||
// we're already connected
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
connected_socket_client.hd = req->handle;
|
||||
connected_socket_client.fd = httpd_req_to_sockfd(req);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
connected_socket_client.hd = req->handle;
|
||||
connected_socket_client.fd = httpd_req_to_sockfd(req);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void WebSocketLogger::unregister_socket_client()
|
||||
{
|
||||
connected_socket_client.fd = -1;
|
||||
connected_socket_client.hd = nullptr;
|
||||
connected_socket_client.fd = -1;
|
||||
connected_socket_client.hd = nullptr;
|
||||
}
|
||||
|
||||
char *WebSocketLogger::get_websocket_log_buffer()
|
||||
char* WebSocketLogger::get_websocket_log_buffer()
|
||||
{
|
||||
return this->ws_log_buffer;
|
||||
return this->ws_log_buffer;
|
||||
}
|
||||
@@ -9,28 +9,28 @@
|
||||
|
||||
struct async_resp_arg
|
||||
{
|
||||
httpd_handle_t hd;
|
||||
int fd;
|
||||
httpd_handle_t hd;
|
||||
int fd;
|
||||
};
|
||||
|
||||
namespace LoggerHelpers
|
||||
{
|
||||
void ws_async_send(void *arg);
|
||||
void ws_async_send(void* arg);
|
||||
}
|
||||
|
||||
class WebSocketLogger
|
||||
{
|
||||
async_resp_arg connected_socket_client{};
|
||||
char ws_log_buffer[WS_LOG_BUFFER_LEN]{};
|
||||
async_resp_arg connected_socket_client{};
|
||||
char ws_log_buffer[WS_LOG_BUFFER_LEN]{};
|
||||
|
||||
public:
|
||||
WebSocketLogger();
|
||||
public:
|
||||
WebSocketLogger();
|
||||
|
||||
esp_err_t log_message(const char *format, va_list args);
|
||||
esp_err_t register_socket_client(httpd_req_t *req);
|
||||
void unregister_socket_client();
|
||||
bool is_client_connected();
|
||||
char *get_websocket_log_buffer();
|
||||
esp_err_t log_message(const char* format, va_list args);
|
||||
esp_err_t register_socket_client(httpd_req_t* req);
|
||||
void unregister_socket_client();
|
||||
bool is_client_connected();
|
||||
char* get_websocket_log_buffer();
|
||||
};
|
||||
|
||||
extern WebSocketLogger webSocketLogger;
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
|
||||
namespace Logo
|
||||
{
|
||||
static const char *LOGO_TAG = "[LOGO]";
|
||||
static const char* LOGO_TAG = "[LOGO]";
|
||||
|
||||
inline static void printASCII()
|
||||
{
|
||||
inline static void printASCII()
|
||||
{
|
||||
ESP_LOGI(LOGO_TAG, " : === WELCOME === TO === : ");
|
||||
ESP_LOGI(LOGO_TAG, " <===========================================================================================================================> ");
|
||||
ESP_LOGI(LOGO_TAG, " ██████╗ ██████╗ ███████╗███╗ ██╗██╗██████╗ ██╗███████╗ ");
|
||||
@@ -70,7 +70,7 @@ namespace Logo
|
||||
ESP_LOGI(LOGO_TAG, " ████████ ");
|
||||
ESP_LOGI(LOGO_TAG, " ");
|
||||
ESP_LOGI(LOGO_TAG, " <============================================================================================================================> ");
|
||||
}
|
||||
};
|
||||
}
|
||||
}; // namespace Logo
|
||||
|
||||
#endif
|
||||
@@ -2,14 +2,13 @@
|
||||
#include <cstring>
|
||||
#include "esp_timer.h"
|
||||
|
||||
static const char *TAG = "WiFiScanner";
|
||||
static const char* TAG = "WiFiScanner";
|
||||
|
||||
WiFiScanner::WiFiScanner() {}
|
||||
|
||||
void WiFiScanner::scanResultCallback(void *arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void *event_data)
|
||||
void WiFiScanner::scanResultCallback(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data)
|
||||
{
|
||||
auto *scanner = static_cast<WiFiScanner *>(arg);
|
||||
auto* scanner = static_cast<WiFiScanner*>(arg);
|
||||
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_SCAN_DONE)
|
||||
{
|
||||
uint16_t ap_count = 0;
|
||||
@@ -21,14 +20,14 @@ void WiFiScanner::scanResultCallback(void *arg, esp_event_base_t event_base,
|
||||
return;
|
||||
}
|
||||
|
||||
wifi_ap_record_t *ap_records = new wifi_ap_record_t[ap_count];
|
||||
wifi_ap_record_t* ap_records = new wifi_ap_record_t[ap_count];
|
||||
ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&ap_count, ap_records));
|
||||
|
||||
scanner->networks.clear();
|
||||
for (uint16_t i = 0; i < ap_count; i++)
|
||||
{
|
||||
WiFiNetwork network;
|
||||
network.ssid = std::string(reinterpret_cast<char *>(ap_records[i].ssid));
|
||||
network.ssid = std::string(reinterpret_cast<char*>(ap_records[i].ssid));
|
||||
network.channel = ap_records[i].primary;
|
||||
network.rssi = ap_records[i].rssi;
|
||||
memcpy(network.mac, ap_records[i].bssid, 6);
|
||||
@@ -63,7 +62,7 @@ std::vector<WiFiNetwork> WiFiScanner::scanNetworks(int timeout_ms)
|
||||
esp_wifi_scan_stop();
|
||||
|
||||
// Try sequential channel scanning as a workaround
|
||||
bool try_sequential_scan = true; // Enable sequential scan
|
||||
bool try_sequential_scan = true; // Enable sequential scan
|
||||
|
||||
if (!try_sequential_scan)
|
||||
{
|
||||
@@ -71,17 +70,17 @@ std::vector<WiFiNetwork> WiFiScanner::scanNetworks(int timeout_ms)
|
||||
wifi_scan_config_t scan_config = {
|
||||
.ssid = nullptr,
|
||||
.bssid = nullptr,
|
||||
.channel = 0, // 0 means scan all channels
|
||||
.channel = 0, // 0 means scan all channels
|
||||
.show_hidden = true,
|
||||
.scan_type = WIFI_SCAN_TYPE_ACTIVE, // Active scan
|
||||
.scan_time = {
|
||||
.active = {
|
||||
.min = 120, // Min per channel
|
||||
.max = 300 // Max per channel
|
||||
},
|
||||
.passive = 360},
|
||||
.home_chan_dwell_time = 0, // 0 for default
|
||||
.channel_bitmap = 0 // 0 for all channels
|
||||
.scan_type = WIFI_SCAN_TYPE_ACTIVE, // Active scan
|
||||
.scan_time = {.active =
|
||||
{
|
||||
.min = 120, // Min per channel
|
||||
.max = 300 // Max per channel
|
||||
},
|
||||
.passive = 360},
|
||||
.home_chan_dwell_time = 0, // 0 for default
|
||||
.channel_bitmap = 0 // 0 for all channels
|
||||
};
|
||||
|
||||
err = esp_wifi_scan_start(&scan_config, false);
|
||||
@@ -95,7 +94,7 @@ std::vector<WiFiNetwork> WiFiScanner::scanNetworks(int timeout_ms)
|
||||
{
|
||||
// Sequential channel scan - scan each channel individually with timeout tracking
|
||||
std::vector<wifi_ap_record_t> all_records;
|
||||
int64_t start_time = esp_timer_get_time() / 1000; // Convert to ms
|
||||
int64_t start_time = esp_timer_get_time() / 1000; // Convert to ms
|
||||
|
||||
for (uint8_t ch = 1; ch <= 13; ch++)
|
||||
{
|
||||
@@ -109,28 +108,23 @@ std::vector<WiFiNetwork> WiFiScanner::scanNetworks(int timeout_ms)
|
||||
break;
|
||||
}
|
||||
|
||||
wifi_scan_config_t scan_config = {
|
||||
.ssid = nullptr,
|
||||
.bssid = nullptr,
|
||||
.channel = ch,
|
||||
.show_hidden = true,
|
||||
.scan_type = WIFI_SCAN_TYPE_ACTIVE,
|
||||
.scan_time = {
|
||||
.active = {
|
||||
.min = 100,
|
||||
.max = 200},
|
||||
.passive = 300},
|
||||
.home_chan_dwell_time = 0,
|
||||
.channel_bitmap = 0};
|
||||
wifi_scan_config_t scan_config = {.ssid = nullptr,
|
||||
.bssid = nullptr,
|
||||
.channel = ch,
|
||||
.show_hidden = true,
|
||||
.scan_type = WIFI_SCAN_TYPE_ACTIVE,
|
||||
.scan_time = {.active = {.min = 100, .max = 200}, .passive = 300},
|
||||
.home_chan_dwell_time = 0,
|
||||
.channel_bitmap = 0};
|
||||
|
||||
err = esp_wifi_scan_start(&scan_config, true); // Blocking scan
|
||||
err = esp_wifi_scan_start(&scan_config, true); // Blocking scan
|
||||
if (err == ESP_OK)
|
||||
{
|
||||
uint16_t ch_count = 0;
|
||||
esp_wifi_scan_get_ap_num(&ch_count);
|
||||
if (ch_count > 0)
|
||||
{
|
||||
wifi_ap_record_t *ch_records = new wifi_ap_record_t[ch_count];
|
||||
wifi_ap_record_t* ch_records = new wifi_ap_record_t[ch_count];
|
||||
if (esp_wifi_scan_get_ap_records(&ch_count, ch_records) == ESP_OK)
|
||||
{
|
||||
for (uint16_t i = 0; i < ch_count; i++)
|
||||
@@ -145,10 +139,10 @@ std::vector<WiFiNetwork> WiFiScanner::scanNetworks(int timeout_ms)
|
||||
}
|
||||
|
||||
// Process all collected records
|
||||
for (const auto &record : all_records)
|
||||
for (const auto& record : all_records)
|
||||
{
|
||||
WiFiNetwork network;
|
||||
network.ssid = std::string(reinterpret_cast<const char *>(record.ssid));
|
||||
network.ssid = std::string(reinterpret_cast<const char*>(record.ssid));
|
||||
network.channel = record.primary;
|
||||
network.rssi = record.rssi;
|
||||
memcpy(network.mac, record.bssid, 6);
|
||||
@@ -164,7 +158,7 @@ std::vector<WiFiNetwork> WiFiScanner::scanNetworks(int timeout_ms)
|
||||
}
|
||||
|
||||
// Wait for scan completion with timeout
|
||||
int64_t start_time = esp_timer_get_time() / 1000; // Convert to ms
|
||||
int64_t start_time = esp_timer_get_time() / 1000; // Convert to ms
|
||||
int64_t elapsed_ms = 0;
|
||||
bool scan_done = false;
|
||||
|
||||
@@ -206,7 +200,7 @@ std::vector<WiFiNetwork> WiFiScanner::scanNetworks(int timeout_ms)
|
||||
return scan_results;
|
||||
}
|
||||
|
||||
wifi_ap_record_t *ap_records = new wifi_ap_record_t[ap_count];
|
||||
wifi_ap_record_t* ap_records = new wifi_ap_record_t[ap_count];
|
||||
err = esp_wifi_scan_get_ap_records(&ap_count, ap_records);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
@@ -216,12 +210,12 @@ std::vector<WiFiNetwork> WiFiScanner::scanNetworks(int timeout_ms)
|
||||
}
|
||||
|
||||
// Build the results vector and track channels found
|
||||
bool channels_found[15] = {false}; // Track channels 0-14
|
||||
bool channels_found[15] = {false}; // Track channels 0-14
|
||||
|
||||
for (uint16_t i = 0; i < ap_count; i++)
|
||||
{
|
||||
WiFiNetwork network;
|
||||
network.ssid = std::string(reinterpret_cast<char *>(ap_records[i].ssid));
|
||||
network.ssid = std::string(reinterpret_cast<char*>(ap_records[i].ssid));
|
||||
network.channel = ap_records[i].primary;
|
||||
network.rssi = ap_records[i].rssi;
|
||||
memcpy(network.mac, ap_records[i].bssid, 6);
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
#ifndef WIFI_SCANNER_HPP
|
||||
#define WIFI_SCANNER_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "esp_wifi.h"
|
||||
#include <vector>
|
||||
#include "esp_log.h"
|
||||
#include "esp_wifi.h"
|
||||
|
||||
struct WiFiNetwork
|
||||
{
|
||||
@@ -18,12 +18,12 @@ struct WiFiNetwork
|
||||
|
||||
class WiFiScanner
|
||||
{
|
||||
public:
|
||||
public:
|
||||
WiFiScanner();
|
||||
std::vector<WiFiNetwork> scanNetworks(int timeout_ms = 15000);
|
||||
static void scanResultCallback(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data);
|
||||
static void scanResultCallback(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data);
|
||||
|
||||
private:
|
||||
private:
|
||||
std::vector<WiFiNetwork> networks;
|
||||
};
|
||||
|
||||
|
||||
@@ -5,409 +5,392 @@ static auto WIFI_MANAGER_TAG = "[WIFI_MANAGER]";
|
||||
int s_retry_num = 0;
|
||||
EventGroupHandle_t s_wifi_event_group;
|
||||
|
||||
void WiFiManagerHelpers::event_handler(void *arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void *event_data)
|
||||
void WiFiManagerHelpers::event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data)
|
||||
{
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "Trying to connect, got event: %d", (int)event_id);
|
||||
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START)
|
||||
{
|
||||
if (const auto err = esp_wifi_connect(); err != ESP_OK)
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "Trying to connect, got event: %d", (int)event_id);
|
||||
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START)
|
||||
{
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "esp_wifi_connect() failed: %s", esp_err_to_name(err));
|
||||
if (const auto err = esp_wifi_connect(); err != ESP_OK)
|
||||
{
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "esp_wifi_connect() failed: %s", esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED)
|
||||
{
|
||||
const auto *disconnected = static_cast<wifi_event_sta_disconnected_t *>(event_data);
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "Disconnect reason: %d", disconnected->reason);
|
||||
|
||||
if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY)
|
||||
else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED)
|
||||
{
|
||||
esp_wifi_connect();
|
||||
s_retry_num++;
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "retry to connect to the AP");
|
||||
const auto* disconnected = static_cast<wifi_event_sta_disconnected_t*>(event_data);
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "Disconnect reason: %d", disconnected->reason);
|
||||
|
||||
if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY)
|
||||
{
|
||||
esp_wifi_connect();
|
||||
s_retry_num++;
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "retry to connect to the AP");
|
||||
}
|
||||
else
|
||||
{
|
||||
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
|
||||
}
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "connect to the AP fail");
|
||||
}
|
||||
|
||||
else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)
|
||||
{
|
||||
const auto* event = static_cast<ip_event_got_ip_t*>(event_data);
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
|
||||
s_retry_num = 0;
|
||||
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
WiFiManager::WiFiManager(std::shared_ptr<ProjectConfig> deviceConfig, QueueHandle_t eventQueue, StateManager* stateManager)
|
||||
: deviceConfig(deviceConfig), eventQueue(eventQueue), stateManager(stateManager), wifiScanner(std::make_unique<WiFiScanner>())
|
||||
{
|
||||
}
|
||||
|
||||
void WiFiManager::SetCredentials(const char* ssid, const char* password)
|
||||
{
|
||||
// Clear the config first
|
||||
memset(&_wifi_cfg, 0, sizeof(_wifi_cfg));
|
||||
|
||||
// Copy SSID with null termination
|
||||
size_t ssid_len = std::min(strlen(ssid), sizeof(_wifi_cfg.sta.ssid) - 1);
|
||||
memcpy(_wifi_cfg.sta.ssid, ssid, ssid_len);
|
||||
_wifi_cfg.sta.ssid[ssid_len] = '\0';
|
||||
|
||||
// Copy password with null termination
|
||||
size_t pass_len = std::min(strlen(password), sizeof(_wifi_cfg.sta.password) - 1);
|
||||
memcpy(_wifi_cfg.sta.password, password, pass_len);
|
||||
_wifi_cfg.sta.password[pass_len] = '\0';
|
||||
|
||||
// Set other required fields
|
||||
// Use open auth if no password, otherwise allow any WPA variant
|
||||
if (strlen(password) == 0)
|
||||
{
|
||||
_wifi_cfg.sta.threshold.authmode = WIFI_AUTH_OPEN;
|
||||
}
|
||||
else
|
||||
{
|
||||
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
|
||||
// IMPORTANT: Set threshold to WEP to accept ANY security mode >= WEP
|
||||
// This allows WPA, WPA2, WPA3, etc. The driver will negotiate the highest common mode
|
||||
_wifi_cfg.sta.threshold.authmode = WIFI_AUTH_WEP;
|
||||
}
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "connect to the AP fail");
|
||||
}
|
||||
|
||||
else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)
|
||||
{
|
||||
const auto *event = static_cast<ip_event_got_ip_t *>(event_data);
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
|
||||
s_retry_num = 0;
|
||||
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
|
||||
}
|
||||
}
|
||||
// CRITICAL: Disable PMF completely - this often causes handshake timeouts
|
||||
_wifi_cfg.sta.pmf_cfg.capable = false;
|
||||
_wifi_cfg.sta.pmf_cfg.required = false;
|
||||
|
||||
WiFiManager::WiFiManager(std::shared_ptr<ProjectConfig> deviceConfig, QueueHandle_t eventQueue, StateManager *stateManager)
|
||||
: deviceConfig(deviceConfig), eventQueue(eventQueue), stateManager(stateManager), wifiScanner(std::make_unique<WiFiScanner>()) {}
|
||||
// OPTIMIZATION: Use fast scan instead of all channel scan for quicker connection
|
||||
_wifi_cfg.sta.scan_method = WIFI_FAST_SCAN;
|
||||
_wifi_cfg.sta.bssid_set = 0; // Don't use specific BSSID
|
||||
_wifi_cfg.sta.channel = 0; // Auto channel detection
|
||||
|
||||
void WiFiManager::SetCredentials(const char *ssid, const char *password)
|
||||
{
|
||||
// Clear the config first
|
||||
memset(&_wifi_cfg, 0, sizeof(_wifi_cfg));
|
||||
// Additional settings that might help with compatibility
|
||||
_wifi_cfg.sta.listen_interval = 0; // Default listen interval
|
||||
_wifi_cfg.sta.sort_method = WIFI_CONNECT_AP_BY_SIGNAL; // Connect to strongest signal
|
||||
|
||||
// Copy SSID with null termination
|
||||
size_t ssid_len = std::min(strlen(ssid), sizeof(_wifi_cfg.sta.ssid) - 1);
|
||||
memcpy(_wifi_cfg.sta.ssid, ssid, ssid_len);
|
||||
_wifi_cfg.sta.ssid[ssid_len] = '\0';
|
||||
// IMPORTANT: For WPA/WPA2 Personal networks
|
||||
_wifi_cfg.sta.threshold.rssi = -127; // Accept any signal strength
|
||||
_wifi_cfg.sta.sae_pwe_h2e = WPA3_SAE_PWE_UNSPECIFIED; // Let driver decide SAE mode
|
||||
|
||||
// Copy password with null termination
|
||||
size_t pass_len = std::min(strlen(password), sizeof(_wifi_cfg.sta.password) - 1);
|
||||
memcpy(_wifi_cfg.sta.password, password, pass_len);
|
||||
_wifi_cfg.sta.password[pass_len] = '\0';
|
||||
|
||||
// Set other required fields
|
||||
// Use open auth if no password, otherwise allow any WPA variant
|
||||
if (strlen(password) == 0)
|
||||
{
|
||||
_wifi_cfg.sta.threshold.authmode = WIFI_AUTH_OPEN;
|
||||
}
|
||||
else
|
||||
{
|
||||
// IMPORTANT: Set threshold to WEP to accept ANY security mode >= WEP
|
||||
// This allows WPA, WPA2, WPA3, etc. The driver will negotiate the highest common mode
|
||||
_wifi_cfg.sta.threshold.authmode = WIFI_AUTH_WEP;
|
||||
}
|
||||
|
||||
// CRITICAL: Disable PMF completely - this often causes handshake timeouts
|
||||
_wifi_cfg.sta.pmf_cfg.capable = false;
|
||||
_wifi_cfg.sta.pmf_cfg.required = false;
|
||||
|
||||
// OPTIMIZATION: Use fast scan instead of all channel scan for quicker connection
|
||||
_wifi_cfg.sta.scan_method = WIFI_FAST_SCAN;
|
||||
_wifi_cfg.sta.bssid_set = 0; // Don't use specific BSSID
|
||||
_wifi_cfg.sta.channel = 0; // Auto channel detection
|
||||
|
||||
// Additional settings that might help with compatibility
|
||||
_wifi_cfg.sta.listen_interval = 0; // Default listen interval
|
||||
_wifi_cfg.sta.sort_method = WIFI_CONNECT_AP_BY_SIGNAL; // Connect to strongest signal
|
||||
|
||||
// IMPORTANT: For WPA/WPA2 Personal networks
|
||||
_wifi_cfg.sta.threshold.rssi = -127; // Accept any signal strength
|
||||
_wifi_cfg.sta.sae_pwe_h2e = WPA3_SAE_PWE_UNSPECIFIED; // Let driver decide SAE mode
|
||||
|
||||
// Log what we're trying to connect to with detailed debugging
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "Setting credentials for SSID: '%s' (length: %d)", ssid, (int)strlen(ssid));
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "Password: '%s' (length: %d)", password, (int)strlen(password));
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "Auth mode: %d, PMF capable: %d",
|
||||
_wifi_cfg.sta.threshold.authmode, _wifi_cfg.sta.pmf_cfg.capable);
|
||||
// Log what we're trying to connect to with detailed debugging
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "Setting credentials for SSID: '%s' (length: %d)", ssid, (int)strlen(ssid));
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "Password: '%s' (length: %d)", password, (int)strlen(password));
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "Auth mode: %d, PMF capable: %d", _wifi_cfg.sta.threshold.authmode, _wifi_cfg.sta.pmf_cfg.capable);
|
||||
}
|
||||
|
||||
void WiFiManager::ConnectWithHardcodedCredentials()
|
||||
{
|
||||
SystemEvent event = {EventSource::WIFI, WiFiState_e::WiFiState_ReadyToConnect};
|
||||
this->SetCredentials(CONFIG_WIFI_SSID, CONFIG_WIFI_PASSWORD);
|
||||
SystemEvent event = {EventSource::WIFI, WiFiState_e::WiFiState_ReadyToConnect};
|
||||
this->SetCredentials(CONFIG_WIFI_SSID, CONFIG_WIFI_PASSWORD);
|
||||
|
||||
wifi_mode_t mode;
|
||||
if (esp_wifi_get_mode(&mode) == ESP_OK) {
|
||||
esp_wifi_stop();
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &_wifi_cfg));
|
||||
|
||||
xQueueSend(this->eventQueue, &event, 10);
|
||||
esp_wifi_start();
|
||||
|
||||
event.value = WiFiState_e::WiFiState_Connecting;
|
||||
xQueueSend(this->eventQueue, &event, 10);
|
||||
|
||||
// Use shorter timeout for faster startup - 8 seconds should be enough for most networks
|
||||
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
|
||||
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
|
||||
pdFALSE,
|
||||
pdFALSE,
|
||||
pdMS_TO_TICKS(8000));
|
||||
|
||||
/* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
|
||||
* happened. */
|
||||
if (bits & WIFI_CONNECTED_BIT)
|
||||
{
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "connected to ap SSID:%p password:%p",
|
||||
_wifi_cfg.sta.ssid, _wifi_cfg.sta.password);
|
||||
|
||||
event.value = WiFiState_e::WiFiState_Connected;
|
||||
xQueueSend(this->eventQueue, &event, 10);
|
||||
}
|
||||
|
||||
else if (bits & WIFI_FAIL_BIT)
|
||||
{
|
||||
ESP_LOGE(WIFI_MANAGER_TAG, "Failed to connect to SSID:%p, password:%p",
|
||||
_wifi_cfg.sta.ssid, _wifi_cfg.sta.password);
|
||||
|
||||
event.value = WiFiState_e::WiFiState_Error;
|
||||
xQueueSend(this->eventQueue, &event, 10);
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGE(WIFI_MANAGER_TAG, "UNEXPECTED EVENT");
|
||||
}
|
||||
}
|
||||
|
||||
void WiFiManager::ConnectWithStoredCredentials()
|
||||
{
|
||||
SystemEvent event = {EventSource::WIFI, WiFiState_e::WiFiState_ReadyToConnect};
|
||||
|
||||
auto const networks = this->deviceConfig->getWifiConfigs();
|
||||
|
||||
if (networks.empty())
|
||||
{
|
||||
event.value = WiFiState_e::WiFiState_Disconnected;
|
||||
xQueueSend(this->eventQueue, &event, 10);
|
||||
ESP_LOGE(WIFI_MANAGER_TAG, "No networks stored, cannot connect");
|
||||
return;
|
||||
}
|
||||
|
||||
// Stop WiFi once before the loop
|
||||
esp_wifi_stop();
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
// Ensure we're in STA mode
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
||||
|
||||
for (const auto &network : networks)
|
||||
{
|
||||
// Reset retry counter for each network attempt
|
||||
s_retry_num = 0;
|
||||
xEventGroupClearBits(s_wifi_event_group, WIFI_FAIL_BIT | WIFI_CONNECTED_BIT);
|
||||
this->SetCredentials(network.ssid.c_str(), network.password.c_str());
|
||||
|
||||
// Update config without stopping WiFi again
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "Attempting to connect to SSID: '%s'", network.ssid.c_str());
|
||||
|
||||
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &_wifi_cfg));
|
||||
xQueueSend(this->eventQueue, &event, 10);
|
||||
|
||||
// Start WiFi if not already started
|
||||
esp_err_t start_err = esp_wifi_start();
|
||||
if (start_err != ESP_OK && start_err != ESP_ERR_WIFI_STATE)
|
||||
wifi_mode_t mode;
|
||||
if (esp_wifi_get_mode(&mode) == ESP_OK)
|
||||
{
|
||||
ESP_LOGE(WIFI_MANAGER_TAG, "Failed to start WiFi: %s", esp_err_to_name(start_err));
|
||||
continue;
|
||||
esp_wifi_stop();
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &_wifi_cfg));
|
||||
|
||||
xQueueSend(this->eventQueue, &event, 10);
|
||||
esp_wifi_start();
|
||||
|
||||
event.value = WiFiState_e::WiFiState_Connecting;
|
||||
xQueueSend(this->eventQueue, &event, 10);
|
||||
|
||||
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
|
||||
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
|
||||
pdFALSE,
|
||||
pdFALSE,
|
||||
pdMS_TO_TICKS(10000)); // 10 second timeout for faster failover
|
||||
// Use shorter timeout for faster startup - 8 seconds should be enough for most networks
|
||||
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, pdFALSE, pdFALSE, pdMS_TO_TICKS(8000));
|
||||
|
||||
/* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
|
||||
* happened. */
|
||||
if (bits & WIFI_CONNECTED_BIT)
|
||||
{
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "connected to ap SSID:%s",
|
||||
network.ssid.c_str());
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "connected to ap SSID:%p password:%p", _wifi_cfg.sta.ssid, _wifi_cfg.sta.password);
|
||||
|
||||
event.value = WiFiState_e::WiFiState_Connected;
|
||||
xQueueSend(this->eventQueue, &event, 10);
|
||||
|
||||
return;
|
||||
event.value = WiFiState_e::WiFiState_Connected;
|
||||
xQueueSend(this->eventQueue, &event, 10);
|
||||
}
|
||||
ESP_LOGE(WIFI_MANAGER_TAG, "Failed to connect to SSID:%s, trying next stored network",
|
||||
network.ssid.c_str());
|
||||
|
||||
// Disconnect before trying next network
|
||||
esp_wifi_disconnect();
|
||||
else if (bits & WIFI_FAIL_BIT)
|
||||
{
|
||||
ESP_LOGE(WIFI_MANAGER_TAG, "Failed to connect to SSID:%p, password:%p", _wifi_cfg.sta.ssid, _wifi_cfg.sta.password);
|
||||
|
||||
event.value = WiFiState_e::WiFiState_Error;
|
||||
xQueueSend(this->eventQueue, &event, 10);
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGE(WIFI_MANAGER_TAG, "UNEXPECTED EVENT");
|
||||
}
|
||||
}
|
||||
|
||||
void WiFiManager::ConnectWithStoredCredentials()
|
||||
{
|
||||
SystemEvent event = {EventSource::WIFI, WiFiState_e::WiFiState_ReadyToConnect};
|
||||
|
||||
auto const networks = this->deviceConfig->getWifiConfigs();
|
||||
|
||||
if (networks.empty())
|
||||
{
|
||||
event.value = WiFiState_e::WiFiState_Disconnected;
|
||||
xQueueSend(this->eventQueue, &event, 10);
|
||||
ESP_LOGE(WIFI_MANAGER_TAG, "No networks stored, cannot connect");
|
||||
return;
|
||||
}
|
||||
|
||||
// Stop WiFi once before the loop
|
||||
esp_wifi_stop();
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
|
||||
event.value = WiFiState_e::WiFiState_Error;
|
||||
xQueueSend(this->eventQueue, &event, 10);
|
||||
ESP_LOGE(WIFI_MANAGER_TAG, "Failed to connect to all saved networks");
|
||||
// Ensure we're in STA mode
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
||||
|
||||
for (const auto& network : networks)
|
||||
{
|
||||
// Reset retry counter for each network attempt
|
||||
s_retry_num = 0;
|
||||
xEventGroupClearBits(s_wifi_event_group, WIFI_FAIL_BIT | WIFI_CONNECTED_BIT);
|
||||
this->SetCredentials(network.ssid.c_str(), network.password.c_str());
|
||||
|
||||
// Update config without stopping WiFi again
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "Attempting to connect to SSID: '%s'", network.ssid.c_str());
|
||||
|
||||
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &_wifi_cfg));
|
||||
xQueueSend(this->eventQueue, &event, 10);
|
||||
|
||||
// Start WiFi if not already started
|
||||
esp_err_t start_err = esp_wifi_start();
|
||||
if (start_err != ESP_OK && start_err != ESP_ERR_WIFI_STATE)
|
||||
{
|
||||
ESP_LOGE(WIFI_MANAGER_TAG, "Failed to start WiFi: %s", esp_err_to_name(start_err));
|
||||
continue;
|
||||
}
|
||||
|
||||
event.value = WiFiState_e::WiFiState_Connecting;
|
||||
xQueueSend(this->eventQueue, &event, 10);
|
||||
|
||||
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, pdFALSE, pdFALSE,
|
||||
pdMS_TO_TICKS(10000)); // 10 second timeout for faster failover
|
||||
if (bits & WIFI_CONNECTED_BIT)
|
||||
{
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "connected to ap SSID:%s", network.ssid.c_str());
|
||||
|
||||
event.value = WiFiState_e::WiFiState_Connected;
|
||||
xQueueSend(this->eventQueue, &event, 10);
|
||||
|
||||
return;
|
||||
}
|
||||
ESP_LOGE(WIFI_MANAGER_TAG, "Failed to connect to SSID:%s, trying next stored network", network.ssid.c_str());
|
||||
|
||||
// Disconnect before trying next network
|
||||
esp_wifi_disconnect();
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
|
||||
event.value = WiFiState_e::WiFiState_Error;
|
||||
xQueueSend(this->eventQueue, &event, 10);
|
||||
ESP_LOGE(WIFI_MANAGER_TAG, "Failed to connect to all saved networks");
|
||||
}
|
||||
|
||||
void WiFiManager::SetupAccessPoint()
|
||||
{
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "Connection to stored credentials failed, starting AP");
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "Connection to stored credentials failed, starting AP");
|
||||
|
||||
esp_netif_create_default_wifi_ap();
|
||||
wifi_init_config_t esp_wifi_ap_init_config = WIFI_INIT_CONFIG_DEFAULT();
|
||||
esp_netif_create_default_wifi_ap();
|
||||
wifi_init_config_t esp_wifi_ap_init_config = WIFI_INIT_CONFIG_DEFAULT();
|
||||
|
||||
ESP_ERROR_CHECK(esp_wifi_init(&esp_wifi_ap_init_config));
|
||||
ESP_ERROR_CHECK(esp_wifi_init(&esp_wifi_ap_init_config));
|
||||
|
||||
wifi_config_t ap_wifi_config = {
|
||||
.ap = {
|
||||
.ssid = CONFIG_WIFI_AP_SSID,
|
||||
.password = CONFIG_WIFI_AP_PASSWORD,
|
||||
.max_connection = 1,
|
||||
wifi_config_t ap_wifi_config = {
|
||||
.ap =
|
||||
{
|
||||
.ssid = CONFIG_WIFI_AP_SSID,
|
||||
.password = CONFIG_WIFI_AP_PASSWORD,
|
||||
.max_connection = 1,
|
||||
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &ap_wifi_config));
|
||||
ESP_ERROR_CHECK(esp_wifi_start());
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "AP started.");
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &ap_wifi_config));
|
||||
ESP_ERROR_CHECK(esp_wifi_start());
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "AP started.");
|
||||
}
|
||||
|
||||
std::vector<WiFiNetwork> WiFiManager::ScanNetworks(int timeout_ms)
|
||||
{
|
||||
wifi_mode_t current_mode;
|
||||
esp_err_t err = esp_wifi_get_mode(¤t_mode);
|
||||
wifi_mode_t current_mode;
|
||||
esp_err_t err = esp_wifi_get_mode(¤t_mode);
|
||||
|
||||
if (err == ESP_ERR_WIFI_NOT_INIT)
|
||||
{
|
||||
ESP_LOGE(WIFI_MANAGER_TAG, "WiFi not initialized for scanning");
|
||||
return std::vector<WiFiNetwork>();
|
||||
}
|
||||
|
||||
// If we're in AP-only mode, we need STA interface for scanning
|
||||
if (current_mode == WIFI_MODE_AP)
|
||||
{
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "AP mode detected, checking for STA interface");
|
||||
|
||||
// Check if STA netif already exists
|
||||
esp_netif_t *sta_netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF");
|
||||
bool sta_netif_exists = (sta_netif != nullptr);
|
||||
|
||||
if (!sta_netif_exists)
|
||||
if (err == ESP_ERR_WIFI_NOT_INIT)
|
||||
{
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "Creating STA interface for scanning");
|
||||
sta_netif = esp_netif_create_default_wifi_sta();
|
||||
ESP_LOGE(WIFI_MANAGER_TAG, "WiFi not initialized for scanning");
|
||||
return std::vector<WiFiNetwork>();
|
||||
}
|
||||
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "Switching to APSTA mode for scanning");
|
||||
err = esp_wifi_set_mode(WIFI_MODE_APSTA);
|
||||
if (err != ESP_OK)
|
||||
// If we're in AP-only mode, we need STA interface for scanning
|
||||
if (current_mode == WIFI_MODE_AP)
|
||||
{
|
||||
ESP_LOGE(WIFI_MANAGER_TAG, "Failed to set APSTA mode: %s", esp_err_to_name(err));
|
||||
if (!sta_netif_exists && sta_netif)
|
||||
{
|
||||
esp_netif_destroy(sta_netif);
|
||||
}
|
||||
return std::vector<WiFiNetwork>();
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "AP mode detected, checking for STA interface");
|
||||
|
||||
// Check if STA netif already exists
|
||||
esp_netif_t* sta_netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF");
|
||||
bool sta_netif_exists = (sta_netif != nullptr);
|
||||
|
||||
if (!sta_netif_exists)
|
||||
{
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "Creating STA interface for scanning");
|
||||
sta_netif = esp_netif_create_default_wifi_sta();
|
||||
}
|
||||
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "Switching to APSTA mode for scanning");
|
||||
err = esp_wifi_set_mode(WIFI_MODE_APSTA);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(WIFI_MANAGER_TAG, "Failed to set APSTA mode: %s", esp_err_to_name(err));
|
||||
if (!sta_netif_exists && sta_netif)
|
||||
{
|
||||
esp_netif_destroy(sta_netif);
|
||||
}
|
||||
return std::vector<WiFiNetwork>();
|
||||
}
|
||||
|
||||
// Configure STA with empty config to prevent auto-connect
|
||||
wifi_config_t empty_config = {};
|
||||
esp_wifi_set_config(WIFI_IF_STA, &empty_config);
|
||||
|
||||
// Ensure STA is disconnected and not trying to connect
|
||||
esp_wifi_disconnect();
|
||||
// Longer delay for mode to stabilize and enable all channels
|
||||
vTaskDelay(pdMS_TO_TICKS(2000));
|
||||
|
||||
// Perform scan
|
||||
auto networks = wifiScanner->scanNetworks(timeout_ms);
|
||||
|
||||
// Restore AP-only mode
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "Restoring AP-only mode");
|
||||
esp_wifi_set_mode(WIFI_MODE_AP);
|
||||
|
||||
// Clean up STA interface if we created it
|
||||
if (!sta_netif_exists && sta_netif)
|
||||
{
|
||||
esp_netif_destroy(sta_netif);
|
||||
}
|
||||
|
||||
return networks;
|
||||
}
|
||||
|
||||
// Configure STA with empty config to prevent auto-connect
|
||||
wifi_config_t empty_config = {};
|
||||
esp_wifi_set_config(WIFI_IF_STA, &empty_config);
|
||||
|
||||
// Ensure STA is disconnected and not trying to connect
|
||||
esp_wifi_disconnect();
|
||||
// Longer delay for mode to stabilize and enable all channels
|
||||
vTaskDelay(pdMS_TO_TICKS(2000));
|
||||
|
||||
// Perform scan
|
||||
auto networks = wifiScanner->scanNetworks(timeout_ms);
|
||||
|
||||
// Restore AP-only mode
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "Restoring AP-only mode");
|
||||
esp_wifi_set_mode(WIFI_MODE_AP);
|
||||
|
||||
// Clean up STA interface if we created it
|
||||
if (!sta_netif_exists && sta_netif)
|
||||
{
|
||||
esp_netif_destroy(sta_netif);
|
||||
}
|
||||
|
||||
return networks;
|
||||
}
|
||||
|
||||
// If already in STA or APSTA mode, scan directly
|
||||
return wifiScanner->scanNetworks(timeout_ms);
|
||||
// If already in STA or APSTA mode, scan directly
|
||||
return wifiScanner->scanNetworks(timeout_ms);
|
||||
}
|
||||
|
||||
WiFiState_e WiFiManager::GetCurrentWiFiState()
|
||||
{
|
||||
return this->stateManager->GetWifiState();
|
||||
return this->stateManager->GetWifiState();
|
||||
}
|
||||
|
||||
void WiFiManager::TryConnectToStoredNetworks()
|
||||
{
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "Manual WiFi connection attempt requested");
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "Manual WiFi connection attempt requested");
|
||||
|
||||
// Check current WiFi mode
|
||||
wifi_mode_t current_mode;
|
||||
esp_err_t err = esp_wifi_get_mode(¤t_mode);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(WIFI_MANAGER_TAG, "Failed to get WiFi mode: %s", esp_err_to_name(err));
|
||||
return;
|
||||
}
|
||||
|
||||
// If in AP mode, we need to properly transition to STA mode
|
||||
if (current_mode == WIFI_MODE_AP || current_mode == WIFI_MODE_APSTA)
|
||||
{
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "Currently in AP mode, transitioning to STA mode");
|
||||
|
||||
// Stop WiFi first
|
||||
esp_wifi_stop();
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
// Check if STA interface exists, create if needed
|
||||
esp_netif_t *sta_netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF");
|
||||
if (sta_netif == nullptr)
|
||||
// Check current WiFi mode
|
||||
wifi_mode_t current_mode;
|
||||
esp_err_t err = esp_wifi_get_mode(¤t_mode);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "Creating STA interface");
|
||||
sta_netif = esp_netif_create_default_wifi_sta();
|
||||
ESP_LOGE(WIFI_MANAGER_TAG, "Failed to get WiFi mode: %s", esp_err_to_name(err));
|
||||
return;
|
||||
}
|
||||
|
||||
// Set to STA mode
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
// If in AP mode, we need to properly transition to STA mode
|
||||
if (current_mode == WIFI_MODE_AP || current_mode == WIFI_MODE_APSTA)
|
||||
{
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "Currently in AP mode, transitioning to STA mode");
|
||||
|
||||
// Reset retry counter and clear all event bits
|
||||
s_retry_num = 0;
|
||||
xEventGroupClearBits(s_wifi_event_group, WIFI_FAIL_BIT | WIFI_CONNECTED_BIT);
|
||||
this->ConnectWithStoredCredentials();
|
||||
// Stop WiFi first
|
||||
esp_wifi_stop();
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
// Check if STA interface exists, create if needed
|
||||
esp_netif_t* sta_netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF");
|
||||
if (sta_netif == nullptr)
|
||||
{
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "Creating STA interface");
|
||||
sta_netif = esp_netif_create_default_wifi_sta();
|
||||
}
|
||||
|
||||
// Set to STA mode
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
|
||||
// Reset retry counter and clear all event bits
|
||||
s_retry_num = 0;
|
||||
xEventGroupClearBits(s_wifi_event_group, WIFI_FAIL_BIT | WIFI_CONNECTED_BIT);
|
||||
this->ConnectWithStoredCredentials();
|
||||
}
|
||||
|
||||
void WiFiManager::Begin()
|
||||
{
|
||||
s_wifi_event_group = xEventGroupCreate();
|
||||
s_wifi_event_group = xEventGroupCreate();
|
||||
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
auto netif = esp_netif_create_default_wifi_sta();
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
auto netif = esp_netif_create_default_wifi_sta();
|
||||
|
||||
wifi_init_config_t esp_wifi_init_config = WIFI_INIT_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK(esp_wifi_init(&esp_wifi_init_config));
|
||||
wifi_init_config_t esp_wifi_init_config = WIFI_INIT_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK(esp_wifi_init(&esp_wifi_init_config));
|
||||
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
|
||||
ESP_EVENT_ANY_ID,
|
||||
&WiFiManagerHelpers::event_handler,
|
||||
nullptr,
|
||||
&instance_any_id));
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
|
||||
IP_EVENT_STA_GOT_IP,
|
||||
&WiFiManagerHelpers::event_handler,
|
||||
nullptr,
|
||||
&instance_got_ip));
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &WiFiManagerHelpers::event_handler, nullptr, &instance_any_id));
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &WiFiManagerHelpers::event_handler, nullptr, &instance_got_ip));
|
||||
|
||||
_wifi_cfg = {};
|
||||
_wifi_cfg.sta.threshold.authmode = WIFI_AUTH_OPEN; // Start with open, will be set properly by SetCredentials
|
||||
_wifi_cfg.sta.pmf_cfg.capable = false; // Disable PMF by default
|
||||
_wifi_cfg.sta.pmf_cfg.required = false;
|
||||
_wifi_cfg = {};
|
||||
_wifi_cfg.sta.threshold.authmode = WIFI_AUTH_OPEN; // Start with open, will be set properly by SetCredentials
|
||||
_wifi_cfg.sta.pmf_cfg.capable = false; // Disable PMF by default
|
||||
_wifi_cfg.sta.pmf_cfg.required = false;
|
||||
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
||||
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "Beginning setup");
|
||||
const auto hasHardcodedCredentials = strlen(CONFIG_WIFI_SSID) > 0;
|
||||
if (hasHardcodedCredentials)
|
||||
{
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "Detected hardcoded credentials, trying them out");
|
||||
this->ConnectWithHardcodedCredentials();
|
||||
}
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "Beginning setup");
|
||||
const auto hasHardcodedCredentials = strlen(CONFIG_WIFI_SSID) > 0;
|
||||
if (hasHardcodedCredentials)
|
||||
{
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "Detected hardcoded credentials, trying them out");
|
||||
this->ConnectWithHardcodedCredentials();
|
||||
}
|
||||
|
||||
if (this->stateManager->GetWifiState() != WiFiState_e::WiFiState_Connected || !hasHardcodedCredentials)
|
||||
{
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "Hardcoded credentials failed or missing, trying stored credentials");
|
||||
xEventGroupClearBits(s_wifi_event_group, WIFI_FAIL_BIT);
|
||||
this->ConnectWithStoredCredentials();
|
||||
}
|
||||
if (this->stateManager->GetWifiState() != WiFiState_e::WiFiState_Connected || !hasHardcodedCredentials)
|
||||
{
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "Hardcoded credentials failed or missing, trying stored credentials");
|
||||
xEventGroupClearBits(s_wifi_event_group, WIFI_FAIL_BIT);
|
||||
this->ConnectWithStoredCredentials();
|
||||
}
|
||||
|
||||
if (this->stateManager->GetWifiState() != WiFiState_e::WiFiState_Connected)
|
||||
{
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "Stored netoworks failed or hardcoded credentials missing, starting AP");
|
||||
xEventGroupClearBits(s_wifi_event_group, WIFI_FAIL_BIT);
|
||||
esp_netif_destroy(netif);
|
||||
this->SetupAccessPoint();
|
||||
}
|
||||
if (this->stateManager->GetWifiState() != WiFiState_e::WiFiState_Connected)
|
||||
{
|
||||
ESP_LOGI(WIFI_MANAGER_TAG, "Stored netoworks failed or hardcoded credentials missing, starting AP");
|
||||
xEventGroupClearBits(s_wifi_event_group, WIFI_FAIL_BIT);
|
||||
esp_netif_destroy(netif);
|
||||
this->SetupAccessPoint();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,16 +2,16 @@
|
||||
#ifndef WIFIHANDLER_HPP
|
||||
#define WIFIHANDLER_HPP
|
||||
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <StateManager.hpp>
|
||||
#include <ProjectConfig.hpp>
|
||||
#include <StateManager.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include "WiFiScanner.hpp"
|
||||
|
||||
#include "esp_event.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
@@ -21,37 +21,36 @@
|
||||
|
||||
namespace WiFiManagerHelpers
|
||||
{
|
||||
void event_handler(void *arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void *event_data);
|
||||
void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data);
|
||||
}
|
||||
|
||||
class WiFiManager
|
||||
{
|
||||
private:
|
||||
uint8_t channel;
|
||||
std::shared_ptr<ProjectConfig> deviceConfig;
|
||||
QueueHandle_t eventQueue;
|
||||
StateManager *stateManager;
|
||||
wifi_init_config_t _wifi_init_cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
wifi_config_t _wifi_cfg = {};
|
||||
std::unique_ptr<WiFiScanner> wifiScanner;
|
||||
private:
|
||||
uint8_t channel;
|
||||
std::shared_ptr<ProjectConfig> deviceConfig;
|
||||
QueueHandle_t eventQueue;
|
||||
StateManager* stateManager;
|
||||
wifi_init_config_t _wifi_init_cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
wifi_config_t _wifi_cfg = {};
|
||||
std::unique_ptr<WiFiScanner> wifiScanner;
|
||||
|
||||
esp_event_handler_instance_t instance_any_id;
|
||||
esp_event_handler_instance_t instance_got_ip;
|
||||
esp_event_handler_instance_t instance_any_id;
|
||||
esp_event_handler_instance_t instance_got_ip;
|
||||
|
||||
int8_t power;
|
||||
int8_t power;
|
||||
|
||||
void SetCredentials(const char *ssid, const char *password);
|
||||
void ConnectWithHardcodedCredentials();
|
||||
void ConnectWithStoredCredentials();
|
||||
void SetupAccessPoint();
|
||||
void SetCredentials(const char* ssid, const char* password);
|
||||
void ConnectWithHardcodedCredentials();
|
||||
void ConnectWithStoredCredentials();
|
||||
void SetupAccessPoint();
|
||||
|
||||
public:
|
||||
WiFiManager(std::shared_ptr<ProjectConfig> deviceConfig, QueueHandle_t eventQueue, StateManager *stateManager);
|
||||
void Begin();
|
||||
std::vector<WiFiNetwork> ScanNetworks(int timeout_ms = 15000);
|
||||
WiFiState_e GetCurrentWiFiState();
|
||||
void TryConnectToStoredNetworks();
|
||||
public:
|
||||
WiFiManager(std::shared_ptr<ProjectConfig> deviceConfig, QueueHandle_t eventQueue, StateManager* stateManager);
|
||||
void Begin();
|
||||
std::vector<WiFiNetwork> ScanNetworks(int timeout_ms = 15000);
|
||||
WiFiState_e GetCurrentWiFiState();
|
||||
void TryConnectToStoredNetworks();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,27 +1,27 @@
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/task.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#include <openiris_logo.hpp>
|
||||
#include <wifiManager.hpp>
|
||||
#include <ProjectConfig.hpp>
|
||||
#include <StateManager.hpp>
|
||||
#include <CameraManager.hpp>
|
||||
#include <CommandManager.hpp>
|
||||
#include <LEDManager.hpp>
|
||||
#include <MDNSManager.hpp>
|
||||
#include <CameraManager.hpp>
|
||||
#include <WebSocketLogger.hpp>
|
||||
#include <StreamServer.hpp>
|
||||
#include <CommandManager.hpp>
|
||||
#include <SerialManager.hpp>
|
||||
#include <ProjectConfig.hpp>
|
||||
#include <RestAPI.hpp>
|
||||
#include <SerialManager.hpp>
|
||||
#include <StateManager.hpp>
|
||||
#include <StreamServer.hpp>
|
||||
#include <WebSocketLogger.hpp>
|
||||
#include <main_globals.hpp>
|
||||
#include <openiris_logo.hpp>
|
||||
#include <wifiManager.hpp>
|
||||
|
||||
#if CONFIG_MONITORING_LED_CURRENT || CONFIG_MONITORING_BATTERY_ENABLE
|
||||
#include <MonitoringManager.hpp>
|
||||
@@ -50,7 +50,7 @@ QueueHandle_t eventQueue = xQueueCreate(10, sizeof(SystemEvent));
|
||||
QueueHandle_t ledStateQueue = xQueueCreate(10, sizeof(uint32_t));
|
||||
QueueHandle_t cdcMessageQueue = xQueueCreate(3, sizeof(cdc_command_packet_t));
|
||||
|
||||
auto *stateManager = new StateManager(eventQueue, ledStateQueue);
|
||||
auto* stateManager = new StateManager(eventQueue, ledStateQueue);
|
||||
auto dependencyRegistry = std::make_shared<DependencyRegistry>();
|
||||
auto commandManager = std::make_shared<CommandManager>(dependencyRegistry);
|
||||
|
||||
@@ -76,7 +76,7 @@ auto ledManager = std::make_shared<LEDManager>(BLINK_GPIO, CONFIG_LED_C_PIN_GPIO
|
||||
std::shared_ptr<MonitoringManager> monitoringManager = std::make_shared<MonitoringManager>();
|
||||
#endif
|
||||
|
||||
auto *serialManager = new SerialManager(commandManager, &timerHandle);
|
||||
auto* serialManager = new SerialManager(commandManager, &timerHandle);
|
||||
|
||||
void startWiFiMode();
|
||||
void startWiredMode(bool shouldCloseSerialManager);
|
||||
@@ -92,7 +92,7 @@ static void initNVSStorage()
|
||||
ESP_ERROR_CHECK(ret);
|
||||
}
|
||||
|
||||
int websocket_logger(const char *format, va_list args)
|
||||
int websocket_logger(const char* format, va_list args)
|
||||
{
|
||||
webSocketLogger.log_message(format, args);
|
||||
return vprintf(format, args);
|
||||
@@ -129,10 +129,9 @@ void launch_streaming()
|
||||
}
|
||||
|
||||
// Callback for automatic startup after delay
|
||||
void startup_timer_callback(void *arg)
|
||||
void startup_timer_callback(void* arg)
|
||||
{
|
||||
ESP_LOGI("[MAIN]", "Startup timer fired, startupCommandReceived=%s, startupPaused=%s",
|
||||
getStartupCommandReceived() ? "true" : "false",
|
||||
ESP_LOGI("[MAIN]", "Startup timer fired, startupCommandReceived=%s, startupPaused=%s", getStartupCommandReceived() ? "true" : "false",
|
||||
getStartupPaused() ? "true" : "false");
|
||||
|
||||
if (!getStartupCommandReceived() && !getStartupPaused())
|
||||
@@ -199,13 +198,7 @@ void startWiredMode(bool shouldCloseSerialManager)
|
||||
}
|
||||
|
||||
ESP_LOGI("[MAIN]", "Starting CDC Serial Manager Task");
|
||||
xTaskCreate(
|
||||
HandleCDCSerialManagerTask,
|
||||
"HandleCDCSerialManagerTask",
|
||||
1024 * 6,
|
||||
commandManager.get(),
|
||||
1,
|
||||
nullptr);
|
||||
xTaskCreate(HandleCDCSerialManagerTask, "HandleCDCSerialManagerTask", 1024 * 6, commandManager.get(), 1, nullptr);
|
||||
|
||||
ESP_LOGI("[MAIN]", "Starting UVC streaming");
|
||||
|
||||
@@ -227,13 +220,9 @@ void startWiFiMode()
|
||||
{
|
||||
streamServer.startStreamServer();
|
||||
}
|
||||
xTaskCreate(
|
||||
HandleRestAPIPollTask,
|
||||
"HandleRestAPIPollTask",
|
||||
2024 * 2,
|
||||
restAPI.get(),
|
||||
1, // it's the rest API, we only serve commands over it so we don't really need a higher priority
|
||||
nullptr);
|
||||
xTaskCreate(HandleRestAPIPollTask, "HandleRestAPIPollTask", 2024 * 2, restAPI.get(),
|
||||
1, // it's the rest API, we only serve commands over it so we don't really need a higher priority
|
||||
nullptr);
|
||||
#else
|
||||
ESP_LOGW("[MAIN]", "Wireless is disabled by configuration; skipping WiFi/mDNS/REST startup.");
|
||||
#endif
|
||||
@@ -251,11 +240,7 @@ void startSetupMode()
|
||||
|
||||
// Create a one-shot timer for 20 seconds
|
||||
const esp_timer_create_args_t startup_timer_args = {
|
||||
.callback = &startup_timer_callback,
|
||||
.arg = nullptr,
|
||||
.dispatch_method = ESP_TIMER_TASK,
|
||||
.name = "startup_timer",
|
||||
.skip_unhandled_events = false};
|
||||
.callback = &startup_timer_callback, .arg = nullptr, .dispatch_method = ESP_TIMER_TASK, .name = "startup_timer", .skip_unhandled_events = false};
|
||||
|
||||
ESP_ERROR_CHECK(esp_timer_create(&startup_timer_args, &timerHandle));
|
||||
ESP_ERROR_CHECK(esp_timer_start_once(timerHandle, startup_delay_s * 1000000));
|
||||
@@ -290,35 +275,18 @@ extern "C" void app_main(void)
|
||||
monitoringManager->start();
|
||||
#endif
|
||||
|
||||
xTaskCreate(
|
||||
HandleStateManagerTask,
|
||||
"HandleStateManagerTask",
|
||||
1024 * 2,
|
||||
stateManager,
|
||||
3,
|
||||
nullptr // it's fine for us not get a handle back, we don't need it
|
||||
xTaskCreate(HandleStateManagerTask, "HandleStateManagerTask", 1024 * 2, stateManager, 3,
|
||||
nullptr // it's fine for us not get a handle back, we don't need it
|
||||
);
|
||||
|
||||
xTaskCreate(
|
||||
HandleLEDDisplayTask,
|
||||
"HandleLEDDisplayTask",
|
||||
1024 * 2,
|
||||
ledManager.get(),
|
||||
3,
|
||||
nullptr);
|
||||
xTaskCreate(HandleLEDDisplayTask, "HandleLEDDisplayTask", 1024 * 2, ledManager.get(), 3, nullptr);
|
||||
|
||||
cameraHandler->setupCamera();
|
||||
|
||||
// let's keep the serial manager running for the duration of the setup
|
||||
// we'll clean it up later if need be
|
||||
serialManager->setup();
|
||||
xTaskCreate(
|
||||
HandleSerialManagerTask,
|
||||
"HandleSerialManagerTask",
|
||||
1024 * 6,
|
||||
serialManager,
|
||||
1,
|
||||
&serialManagerHandle);
|
||||
xTaskCreate(HandleSerialManagerTask, "HandleSerialManagerTask", 1024 * 6, serialManager, 1, &serialManagerHandle);
|
||||
|
||||
StreamingMode mode = deviceConfig->getDeviceMode();
|
||||
if (mode == StreamingMode::UVC)
|
||||
|
||||
Reference in New Issue
Block a user