diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4ec8f25..c8d3e2a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -94,6 +94,31 @@ before_script: - echo Generating zip file for binaries generated - tar -zcvf esp-rainmaker-bins-${CI_JOB_ID}.zip esp-rainmaker-bins-${CI_JOB_ID}/ +.build_camera_examples: &build_camera_examples + # Clone KVS WebRTC SDK + - cd $CI_PROJECT_DIR + - echo Cloning amazon-kinesis-video-streams-webrtc-sdk-c + - git clone --recursive --depth 1 -b beta-reference-esp-port https://github.com/awslabs/amazon-kinesis-video-streams-webrtc-sdk-c.git + - export KVS_SDK_PATH=$CI_PROJECT_DIR/amazon-kinesis-video-streams-webrtc-sdk-c + # start building examples + - for EXAMPLE in $EXAMPLES; do + - cd $CI_PROJECT_DIR/examples/$EXAMPLE + - echo Building $EXAMPLE + # build examples for the targets + - for TARGET in $EXAMPLE_TARGETS; do + - echo Building for $TARGET + - idf.py set-target $TARGET + - idf.py build + - mkdir -p $CI_PROJECT_DIR/esp-rainmaker-bins-${CI_JOB_ID}/$EXAMPLE/$TARGET/ + - cp $CI_PROJECT_DIR/examples/$EXAMPLE/build/*.bin $CI_PROJECT_DIR/esp-rainmaker-bins-${CI_JOB_ID}/$EXAMPLE/$TARGET/ + - done + - echo Build Complete for $EXAMPLE + - done + # Generating zip file for binaries generated + - cd $CI_PROJECT_DIR + - echo Generating zip file for binaries generated + - tar -zcvf esp-rainmaker-bins-${CI_JOB_ID}.zip esp-rainmaker-bins-${CI_JOB_ID}/ + .build_template: stage: build image: espressif/idf:latest @@ -138,6 +163,26 @@ build_idf_v5.5: variables: EXAMPLE_TARGETS: "esp32 esp32s2 esp32c3 esp32s3 esp32c6 esp32c2 esp32h2" +build_standalone_camera: + extends: .build_template + image: espressif/idf:release-v5.5 + variables: + PEDANTIC_FLAGS: "-Werror -Werror=deprecated-declarations -Wno-error=cpp -Wno-error=int-to-pointer-cast -Wno-error=pointer-to-int-cast" + EXAMPLE_TARGETS: "esp32p4 esp32s3 esp32" + EXAMPLES: "camera/standalone" + script: + - *build_camera_examples + +build_split_camera: + extends: .build_template + image: espressif/idf:release-v5.5 + variables: + PEDANTIC_FLAGS: "-Werror -Werror=deprecated-declarations -Wno-error=cpp -Wno-error=int-to-pointer-cast -Wno-error=pointer-to-int-cast" + EXAMPLE_TARGETS: "esp32c6" + EXAMPLES: "camera/split_mode/rmaker_split_camera" + script: + - *build_camera_examples + build_thread_br: extends: .build_template image: espressif/idf:release-v5.3 diff --git a/components/esp_rainmaker/idf_component.yml b/components/esp_rainmaker/idf_component.yml index f8efb5a..ce12f02 100644 --- a/components/esp_rainmaker/idf_component.yml +++ b/components/esp_rainmaker/idf_component.yml @@ -1,5 +1,5 @@ ## IDF Component Manager Manifest File -version: "1.8.2" +version: "1.8.3" description: ESP RainMaker firmware agent url: https://github.com/espressif/esp-rainmaker/tree/master/components/esp_rainmaker repository: https://github.com/espressif/esp-rainmaker.git @@ -9,7 +9,7 @@ discussion: https://www.esp32.com/viewforum.php?f=41 dependencies: idf: ">=5.1" espressif/mdns: - version: "^1.2.0" + version: "^1.9.0" espressif/esp_secure_cert_mgr: version: "^2.2.1" espressif/rmaker_common: diff --git a/examples/camera/README.md b/examples/camera/README.md new file mode 100644 index 0000000..5a5c338 --- /dev/null +++ b/examples/camera/README.md @@ -0,0 +1,135 @@ +# ESP RainMaker Camera Examples + +This directory contains ESP RainMaker camera examples that demonstrate building smart cameras using ESP chipsets with [AWS Kinesis Video Streams](https://docs.aws.amazon.com/kinesisvideostreams-webrtc-dg/latest/devguide/what-is-kvswebrtc.html) (KVS) [WebRTC SDK](https://github.com/awslabs/amazon-kinesis-video-streams-webrtc-sdk-c/tree/beta-reference-esp-port) integration and ESP RainMaker for device management. + +**Key Features of the RainMaker Camera:** +- Complete WebRTC stack with STUN and TURN capabilities +- KVS peer connection with media streaming +- Audio/Video capture and playback interfaces + +## Available Examples + +### 1. Standalone Mode (`standalone/`) + +Complete WebRTC implementation including both signaling and media streaming on a single device. + +**Use Case:** +- Single device deployment +- Direct camera streaming with WebRTC +- Faster Peer connection setup + +**Supported Devices:** +- ESP32-P4 (FHD live streaming) +- ESP32-S3 (QVGA live streaming) +- Other ESP32 variants (file-based streaming) + +[Learn more →](standalone/README.md) + +### 2. Split Mode (`split_mode/`) + +Split architecture where streaming is handled by a separate MCU and Signaling is handled by the RainMaker Camera. + +**Use Case:** +- Distributed architecture (signaling on one device, streaming on another) +- This enables manufactures to swap streaming implementation swiftly +- Power-optimized deployments + +**Supported Devices:** + - ESP32-P4 Function Ev Board + - ESP32-C6 (signaling side RainMaker integration) + - ESP32-P4 (streaming side - uses KVS streaming_only example) + +[Learn more →](split_mode/README.md) + +## Common Component: `rmaker_camera` + +Both examples use a shared `rmaker_camera` component located in `components/rmaker_camera/`. The component behavior is controlled via Kconfig: + +- `CONFIG_RMAKER_CAMERA_MODE_STANDALONE`: Complete WebRTC with media streaming +- `CONFIG_RMAKER_CAMERA_MODE_SPLIT_MODE`: Signaling-only for split architecture + +The mode is set via each example's `sdkconfig.defaults` files, so no manual configuration is needed. + +**The main difference in two modes is standalone provides the single board application with instantaneous peer connection, whereas, split mode provides a way to make the media_adapter to be put in deep sleep when not in use and hence targetting power sensitive deployments** + +## Prerequisites + +- **IDF version**: release/v5.5 (v5.5.x) +- **Amazon Kinesis Video Streams WebRTC SDK**: Clone the `beta-reference-esp-port` branch + + ```bash + git clone -b beta-reference-esp-port git@github.com:awslabs/amazon-kinesis-video-streams-webrtc-sdk-c.git + export KVS_SDK_PATH=/path/to/amazon-kinesis-video-streams-webrtc-sdk-c + ``` +- **ESP RainMaker App**: Latest iOS/Android apps with Camera support + - Android: v3.9.2 or later + - iOS: v3.5.2 or later + +## Getting Started + +1. Choose the appropriate example based on your use case: + - Device for complete experience → **Standalone Mode** + - Distributed setup with power saving feature → **Split Mode** + +2. Follow the detailed instructions in the respective example's README: + - [Standalone Mode README](standalone/README.md) + - [Split Mode README](split_mode/README.md) + +3. Both examples use the same RainMaker camera component, which automatically adapts based on the configured mode. + +## Architecture + +### Standalone Mode + +**Single Chip Solution** + +``` +┌────────────────────────────────┐ +│ ESP32-S3 (Single Device) │ +│ │ +│ ┌──────────────────────────┐ │ +│ │ RainMaker Camera │ │ +│ │ (Standalone Mode) │ │ +│ ├──────────────────────────┤ │ +│ │ • Signaling │ │ +│ │ • Media Streaming │ │ +│ │ • Audio/Video Capture │ │ +│ │ • Audio Playback | | +│ └──────────────────────────┘ │ +└────────────────────────────────┘ +``` + +**Dual Chip Solution** + +### Standalone Mode + +``` +┌────────────────────────────────┐ ┌─────────────────────────────┐ +│ ESP32-P4 │ SDIO │ ESP32-C6 │ +│ │◄────►│ (Network Adapter) │ +│ ┌──────────────────────────┐ │ └─────────────────────────────┘ +│ │ RainMaker Camera │ │ +│ │ (Standalone Mode) │ │ +│ ├──────────────────────────┤ │ +│ │ • Signaling │ │ +│ │ • Media Streaming │ │ +│ │ • Audio/Video Capture │ │ +│ │ • Audio Playback │ │ +│ └──────────────────────────┘ │ +└────────────────────────────────┘ +``` + +### Split Mode + +``` +┌────────────────────────────────┐ ┌─────────────────────────────┐ +│ ESP32-C6 │ SDIO │ ESP32-P4 │ +│ (RainMaker Camera) │◄────►│ (Streaming Device) │ +│ ┌──────────────────────────┐ │ ├─────────────────────────────┤ +│ │ Camera Split Mode │ │ │ │ +│ ├──────────────────────────┤ │ │ • Media Streaming │ +│ │ • RainMaker Device Mgmt │ │ │ • Audio/Video Capture │ +│ │ • Signaling │ │ │ • Audio Playback │ +│ └──────────────────────────┘ │ └─────────────────────────────┘ +└────────────────────────────────┘ +``` diff --git a/examples/camera/components/README.md b/examples/camera/components/README.md new file mode 100644 index 0000000..82f380b --- /dev/null +++ b/examples/camera/components/README.md @@ -0,0 +1 @@ +# Components for RainMaker Camera examples diff --git a/examples/camera/components/rmaker_camera/CMakeLists.txt b/examples/camera/components/rmaker_camera/CMakeLists.txt new file mode 100644 index 0000000..3fe6625 --- /dev/null +++ b/examples/camera/components/rmaker_camera/CMakeLists.txt @@ -0,0 +1,17 @@ +set(srcs + "src/credentials_provider.c" + "src/rmaker_camera.c" + "src/sleep_command.c" +) + +idf_component_register( + SRCS ${srcs} + INCLUDE_DIRS "include" + REQUIRES "nvs_flash" + "esp_wifi" + "esp_timer" + "esp_netif" + "esp_event" + "mbedtls" + "lwip" + ) diff --git a/examples/camera/components/rmaker_camera/README.md b/examples/camera/components/rmaker_camera/README.md new file mode 100644 index 0000000..cd80da8 --- /dev/null +++ b/examples/camera/components/rmaker_camera/README.md @@ -0,0 +1,38 @@ +# rmaker_camera Component + +Shared ESP RainMaker component providing WebRTC camera functionality for ESP RainMaker camera examples. Handles RainMaker device creation, AWS credentials management, and WebRTC initialization. + +## Features + +- **Device Creation**: `esp_rmaker_camera_device_create()` - Creates camera device with name and channel parameters +- **WebRTC Integration**: `rmaker_webrtc_camera_init()` - Initializes WebRTC stack with KVS signaling +- **Credentials Management**: Automatic AWS credentials fetching using RainMaker core functions +- **Power Management**: Sleep commands for split-mode architectures + +## Usage + +```c +#include "rmaker_camera.h" + +/* Create camera device */ +esp_rmaker_device_t *device = esp_rmaker_camera_device_create("ESP32 Camera", NULL); + +/* Initialize WebRTC */ +rmaker_webrtc_camera_config_t config = { + .peer_connection_if = kvs_peer_connection_if_get(), + .video_capture = media_stream_get_video_capture_if(), + .audio_capture = media_stream_get_audio_capture_if(), + .init_callback = NULL, + .init_callback_user_data = NULL, +}; +rmaker_webrtc_camera_init(&config); +``` + +## API + +See `include/rmaker_camera.h` for complete API documentation. + +## Supported Modes + +- **Standalone**: Complete WebRTC with media streaming on single device +- **Split Mode**: Signaling-only for split architecture deployments diff --git a/examples/camera/components/rmaker_camera/idf_component.yml b/examples/camera/components/rmaker_camera/idf_component.yml new file mode 100644 index 0000000..af39628 --- /dev/null +++ b/examples/camera/components/rmaker_camera/idf_component.yml @@ -0,0 +1,43 @@ +dependencies: + espressif/esp_rainmaker: + version: ">=1.0" + + espressif/json_parser: + version: "~1.0.3" + + # Common dependencies + esp_webrtc_utils: + path: ${KVS_SDK_PATH}/esp_port/components/esp_webrtc_utils + version: "*" + + app_webrtc: + path: ${KVS_SDK_PATH}/esp_port/components/app_webrtc + version: "*" + + kvs_signaling: + path: ${KVS_SDK_PATH}/esp_port/components/kvs_signaling + version: "*" + + # Split mode dependencies + network_coprocessor: + path: ${KVS_SDK_PATH}/esp_port/components/network_coprocessor + version: "*" + rules: + - if: "target in [esp32c6]" + + signaling_bridge_adapter: + path: ${KVS_SDK_PATH}/esp_port/examples/signaling_only/components/signaling_bridge_adapter + version: "*" + rules: + - if: "target in [esp32c6]" + + # Standalone mode dependencies + kvs_webrtc: + path: ${KVS_SDK_PATH}/esp_port/components/kvs_webrtc + version: "*" + rules: + - if: "target not in [esp32c6]" + + media_stream: + path: ${KVS_SDK_PATH}/esp_port/components/media_stream + version: "*" diff --git a/examples/camera/components/rmaker_camera/include/rmaker_camera.h b/examples/camera/components/rmaker_camera/include/rmaker_camera.h new file mode 100644 index 0000000..2284e26 --- /dev/null +++ b/examples/camera/components/rmaker_camera/include/rmaker_camera.h @@ -0,0 +1,71 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Channel name prefix for KVS WebRTC */ +#define ESP_RMAKER_CHANNEL_NAME_PREFIX "esp-v1-" + +/* RainMaker device and parameter definitions */ +#define ESP_RMAKER_DEF_CHANNEL_NAME "Channel" +#define ESP_RMAKER_DEVICE_CAMERA "esp.device.camera" +#define ESP_RMAKER_PARAM_CHANNEL "esp.param.channel" + +/** + * @brief Optional initialization callback function type + * + * This callback is called during rmaker_webrtc_camera_init() to allow + * mode-specific initialization (e.g., work queue setup, sleep command registration). + * + * @param user_data User data passed during config setup + * @return 0 on success, non-zero on failure + */ +typedef int (*rmaker_webrtc_camera_init_callback_t)(void *user_data); + +/** + * @brief Configuration structure for WebRTC camera initialization + */ +typedef struct { + void *peer_connection_if; /*!< Required: Peer connection interface implementation (opaque handle) */ + void *video_capture; /*!< Optional: Video capture interface (NULL for signaling-only mode) */ + void *audio_capture; /*!< Optional: Audio capture interface (NULL for signaling-only mode) */ + void *video_player; /*!< Optional: Video player interface (NULL for signaling-only mode) */ + void *audio_player; /*!< Optional: Audio player interface (NULL for signaling-only mode) */ + rmaker_webrtc_camera_init_callback_t init_callback; /*!< Optional: Mode-specific initialization callback */ + void *init_callback_user_data; /*!< Optional: User data passed to init_callback */ +} rmaker_webrtc_camera_config_t; + +/** + * @brief Create a RainMaker camera device with name and channel parameters + * + * This function creates a camera device and automatically adds the name + * parameter and channel parameter (based on node ID) to it. + * + * @param dev_name Device name + * @param priv_data Private data to associate with the device + * @return Pointer to the created device, or NULL on failure + */ +esp_rmaker_device_t *esp_rmaker_camera_device_create(const char *dev_name, void *priv_data); + +/** + * @brief Initialize and start the WebRTC stack + * + * This function initializes the WebRTC stack with RainMaker credentials + * and starts the WebRTC application in a separate task. + * + * @param config Configuration structure containing peer connection and media interfaces + */ +void rmaker_webrtc_camera_init(rmaker_webrtc_camera_config_t *config); + +#ifdef __cplusplus +} +#endif diff --git a/examples/camera/components/rmaker_camera/include/sleep_command.h b/examples/camera/components/rmaker_camera/include/sleep_command.h new file mode 100644 index 0000000..393cd90 --- /dev/null +++ b/examples/camera/components/rmaker_camera/include/sleep_command.h @@ -0,0 +1,25 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Register sleep-related CLI commands + * + * This function registers CLI commands for power management, such as + * wake-up commands for split mode architectures. + * + * @return 0 on success, non-zero on failure + */ +int sleep_command_register_cli(void); + +#ifdef __cplusplus +} +#endif diff --git a/examples/camera/components/rmaker_camera/src/credentials_provider.c b/examples/camera/components/rmaker_camera/src/credentials_provider.c new file mode 100644 index 0000000..48f8f74 --- /dev/null +++ b/examples/camera/components/rmaker_camera/src/credentials_provider.c @@ -0,0 +1,133 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "credentials_provider.h" +#include "esp_log.h" +#include +#include +#include +#include +#include + +static const char *TAG = "credentials"; + +/* Global credentials pointer - allows direct access without copying */ +static esp_rmaker_aws_credentials_t *g_current_credentials = NULL; + +/* Timeout for work queue operations (milliseconds) */ +#define SECURITY_TOKEN_TIMEOUT_MS 10000 + +/* Context structure for get_aws_security_token work queue operation */ +typedef struct { + const char *role_alias; + esp_rmaker_aws_credentials_t *result; + SemaphoreHandle_t semaphore; +} security_token_ctx_t; + +/* Work function to perform get_aws_security_token in work queue context */ +static void get_security_token_work_fn(void *priv_data) +{ + security_token_ctx_t *ctx = (security_token_ctx_t *)priv_data; + if (!ctx) { + return; + } + + /* Perform get_aws_security_token in work queue context (runs in internal RAM) */ + ctx->result = esp_rmaker_get_aws_security_token(ctx->role_alias); + + /* Signal completion */ + if (ctx->semaphore) { + xSemaphoreGive(ctx->semaphore); + } +} + +/* Wrapper function to get AWS security token using work queue */ +static esp_rmaker_aws_credentials_t *esp_rmaker_get_aws_security_token_safe(const char *role_alias) +{ + if (!role_alias) { + return NULL; + } + + /* Create semaphore for synchronization */ + SemaphoreHandle_t semaphore = xSemaphoreCreateBinary(); + if (!semaphore) { + ESP_LOGE(TAG, "Failed to create semaphore for get_aws_security_token"); + return NULL; + } + + /* Allocate context on heap so it persists until work function completes */ + security_token_ctx_t *ctx = (security_token_ctx_t *)malloc(sizeof(security_token_ctx_t)); + if (!ctx) { + ESP_LOGE(TAG, "Failed to allocate context for get_aws_security_token"); + vSemaphoreDelete(semaphore); + return NULL; + } + + ctx->role_alias = role_alias; + ctx->result = NULL; + ctx->semaphore = semaphore; + + /* Try to submit work to queue - if queue is not initialized, fall back to direct call */ + esp_err_t err = esp_rmaker_work_queue_add_task(get_security_token_work_fn, ctx); + if (err != ESP_OK) { + /* Work queue not available, fall back to direct call */ + ESP_LOGW(TAG, "Work queue not available (err: %d), using direct get_aws_security_token", err); + free(ctx); + vSemaphoreDelete(semaphore); + return esp_rmaker_get_aws_security_token(role_alias); + } + + /* Wait for completion */ + if (xSemaphoreTake(semaphore, pdMS_TO_TICKS(SECURITY_TOKEN_TIMEOUT_MS)) != pdTRUE) { + ESP_LOGE(TAG, "Timeout waiting for get_aws_security_token to complete"); + free(ctx); + vSemaphoreDelete(semaphore); + return NULL; + } + + /* Get result and cleanup */ + esp_rmaker_aws_credentials_t *result = ctx->result; + free(ctx); + vSemaphoreDelete(semaphore); + return result; +} + +/* Assume the videostream role and get the aws credentials */ +int rmaker_fetch_aws_credentials(uint64_t user_data, + const char **pAK, uint32_t *pAKLen, + const char **pSK, uint32_t *pSKLen, + const char **pTok, uint32_t *pTokLen, + uint32_t *pExp) +{ + (void)user_data; + + /* Free previous credentials if any */ + if (g_current_credentials) { + esp_rmaker_free_aws_credentials(g_current_credentials); + g_current_credentials = NULL; + } + + /* Get fresh credentials using work queue to avoid SPIRAM issues */ + g_current_credentials = esp_rmaker_get_aws_security_token_safe("esp-videostream-v1-NodeRole"); + if (!g_current_credentials) { + ESP_LOGE(TAG, "Failed to get AWS security token"); + return -1; + } + + /* Return pointers directly to credential fields */ + *pAK = g_current_credentials->access_key; + *pAKLen = (uint32_t)strlen(g_current_credentials->access_key); + *pSK = g_current_credentials->secret_key; + *pSKLen = (uint32_t)strlen(g_current_credentials->secret_key); + *pTok = g_current_credentials->session_token; + *pTokLen = (uint32_t)strlen(g_current_credentials->session_token); + *pExp = g_current_credentials->expiration; + + ESP_LOGI(TAG, "Successfully fetched AWS credentials using RainMaker core function"); + return 0; +} diff --git a/examples/camera/components/rmaker_camera/src/credentials_provider.h b/examples/camera/components/rmaker_camera/src/credentials_provider.h new file mode 100644 index 0000000..f388cc9 --- /dev/null +++ b/examples/camera/components/rmaker_camera/src/credentials_provider.h @@ -0,0 +1,28 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "esp_err.h" + +/** + * @brief Fetch AWS temporary credentials using IoT Core mutual TLS. + * + * @param user_data user data + * @param pAK access key + * @param pAKLen access key length + * @param pSK secret key + * @param pSKLen secret key length + * @param pTok session token + * @param pTokLen session token length + * @param pExp expiration time in seconds from now + * @return int 0 on success, -1 on failure + */ +int rmaker_fetch_aws_credentials(uint64_t user_data, + const char **pAK, uint32_t *pAKLen, + const char **pSK, uint32_t *pSKLen, + const char **pTok, uint32_t *pTokLen, + uint32_t *pExp); diff --git a/examples/camera/components/rmaker_camera/src/rmaker_camera.c b/examples/camera/components/rmaker_camera/src/rmaker_camera.c new file mode 100644 index 0000000..dde3e38 --- /dev/null +++ b/examples/camera/components/rmaker_camera/src/rmaker_camera.c @@ -0,0 +1,208 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "esp_log.h" +#include +#include + +#include "rmaker_camera.h" +#include "credentials_provider.h" +#include "app_webrtc.h" +#include "kvs_signaling.h" + +static const char *TAG = "rmaker_camera"; + +/* Create a RainMaker camera device with name and channel parameters */ +esp_rmaker_device_t *esp_rmaker_camera_device_create(const char *dev_name, void *priv_data) +{ + esp_rmaker_device_t *device = esp_rmaker_device_create(dev_name, ESP_RMAKER_DEVICE_CAMERA, priv_data); + if (device) { + /* Add name parameter */ + esp_rmaker_device_add_param(device, esp_rmaker_name_param_create(ESP_RMAKER_DEF_NAME_PARAM, dev_name)); + + /* Create and add channel parameter using the node ID */ + const char *node_id = esp_rmaker_get_node_id(); + if (node_id) { + char channel_name[32] = {0}; + snprintf(channel_name, sizeof(channel_name), "%s%s", ESP_RMAKER_CHANNEL_NAME_PREFIX, node_id); + + esp_rmaker_param_t *channel_param = esp_rmaker_param_create( + ESP_RMAKER_DEF_CHANNEL_NAME, ESP_RMAKER_PARAM_CHANNEL, esp_rmaker_str(channel_name), PROP_FLAG_READ); + if (channel_param) { + esp_rmaker_device_add_param(device, channel_param); + } + } + } + return device; +} + +/* Event handler for WebRTC events */ +static void app_webrtc_event_handler(app_webrtc_event_data_t *event_data, void *user_ctx) +{ + if (event_data == NULL) { + return; + } + + switch (event_data->event_id) { + case APP_WEBRTC_EVENT_INITIALIZED: + ESP_LOGI(TAG, "[KVS Event] WebRTC Initialized."); + break; + case APP_WEBRTC_EVENT_DEINITIALIZING: + ESP_LOGI(TAG, "[KVS Event] WebRTC Deinitialized."); + break; + case APP_WEBRTC_EVENT_SIGNALING_CONNECTING: + ESP_LOGI(TAG, "[KVS Event] Signaling Connecting."); + break; + case APP_WEBRTC_EVENT_SIGNALING_CONNECTED: + ESP_LOGI(TAG, "[KVS Event] Signaling Connected."); + break; + case APP_WEBRTC_EVENT_SIGNALING_DISCONNECTED: + ESP_LOGI(TAG, "[KVS Event] Signaling Disconnected."); + break; + case APP_WEBRTC_EVENT_SIGNALING_DESCRIBE: + ESP_LOGI(TAG, "[KVS Event] Signaling Describe."); + break; + case APP_WEBRTC_EVENT_SIGNALING_GET_ENDPOINT: + ESP_LOGI(TAG, "[KVS Event] Signaling Get Endpoint."); + break; + case APP_WEBRTC_EVENT_SIGNALING_GET_ICE: + ESP_LOGI(TAG, "[KVS Event] Signaling Get ICE."); + break; + case APP_WEBRTC_EVENT_PEER_CONNECTION_REQUESTED: + ESP_LOGI(TAG, "[KVS Event] Peer Connection Requested."); + break; + case APP_WEBRTC_EVENT_PEER_CONNECTED: + ESP_LOGI(TAG, "[KVS Event] Peer Connected: %s", event_data->peer_id); + break; + case APP_WEBRTC_EVENT_PEER_DISCONNECTED: + ESP_LOGI(TAG, "[KVS Event] Peer Disconnected: %s", event_data->peer_id); + break; + case APP_WEBRTC_EVENT_STREAMING_STARTED: + ESP_LOGI(TAG, "[KVS Event] Streaming Started for Peer: %s", event_data->peer_id); + break; + case APP_WEBRTC_EVENT_STREAMING_STOPPED: + ESP_LOGI(TAG, "[KVS Event] Streaming Stopped for Peer: %s", event_data->peer_id); + break; + case APP_WEBRTC_EVENT_RECEIVED_OFFER: + ESP_LOGI(TAG, "[KVS Event] Received Offer."); + break; + case APP_WEBRTC_EVENT_SENT_ANSWER: + ESP_LOGI(TAG, "[KVS Event] Sent Answer."); + break; + case APP_WEBRTC_EVENT_ERROR: + case APP_WEBRTC_EVENT_SIGNALING_ERROR: + case APP_WEBRTC_EVENT_PEER_CONNECTION_FAILED: + ESP_LOGE(TAG, "[KVS Event] Error Event %d: Code %d, Message: %s", + (int)event_data->event_id, (int)event_data->status_code, event_data->message); + break; + default: + ESP_LOGI(TAG, "[KVS Event] Unhandled Event ID: %d", (int) event_data->event_id); + break; + } +} + +void rmaker_webrtc_camera_init(rmaker_webrtc_camera_config_t *config) +{ + if (config == NULL) { + ESP_LOGE(TAG, "Config parameter cannot be NULL"); + return; + } + + if (config->peer_connection_if == NULL) { + ESP_LOGE(TAG, "peer_connection_if cannot be NULL"); + return; + } + + /* Register the event callback */ + if (app_webrtc_register_event_callback(app_webrtc_event_handler, NULL) != 0) { + ESP_LOGE(TAG, "Failed to register KVS event callback."); + } + + /* Call mode-specific initialization callback if provided */ + if (config->init_callback) { + if (config->init_callback(config->init_callback_user_data) != 0) { + ESP_LOGE(TAG, "Mode-specific initialization callback failed"); + return; + } + } + + /* Create channel name using the node ID */ + const char *node_id = esp_rmaker_get_node_id(); + static char channel_name[32] = {0}; + snprintf(channel_name, sizeof(channel_name), "%s%s", ESP_RMAKER_CHANNEL_NAME_PREFIX, node_id); + + /* Get AWS region using new RainMaker core function (Never freed) */ + char *aws_region = esp_rmaker_get_aws_region(); + if (!aws_region) { + ESP_LOGE(TAG, "Failed to get AWS region"); + return; + } + + /* Set up KVS signaling configuration (all KVS-specific details) */ + static kvs_signaling_config_t kvs_signaling_cfg = {0}; + + /* Channel configuration */ + kvs_signaling_cfg.pChannelName = channel_name; + + /* Prefer callback-based credentials supplier; the callback will handle fetching tokens */ + kvs_signaling_cfg.useIotCredentials = false; + kvs_signaling_cfg.iotCoreCredentialEndpoint = NULL; + kvs_signaling_cfg.iotCoreCert = NULL; + kvs_signaling_cfg.iotCorePrivateKey = NULL; + kvs_signaling_cfg.iotCoreRoleAlias = NULL; + kvs_signaling_cfg.iotCoreThingName = NULL; + + /* Common AWS options */ + kvs_signaling_cfg.awsRegion = aws_region; /* Region is needed for STUN URL */ + kvs_signaling_cfg.fetch_credentials_cb = rmaker_fetch_aws_credentials; + kvs_signaling_cfg.fetch_credentials_user_data = 0; + + /* Configure WebRTC app with our new simplified API */ + app_webrtc_config_t app_webrtc_config = APP_WEBRTC_CONFIG_DEFAULT(); + + /* Signaling configuration (passed as opaque pointers) */ + app_webrtc_config.signaling_client_if = kvs_signaling_client_if_get(); + app_webrtc_config.signaling_cfg = &kvs_signaling_cfg; + + /* Use peer connection and media interfaces from config */ + app_webrtc_config.peer_connection_if = (webrtc_peer_connection_if_t *)config->peer_connection_if; + app_webrtc_config.video_capture = config->video_capture; + app_webrtc_config.audio_capture = config->audio_capture; + app_webrtc_config.video_player = config->video_player; + app_webrtc_config.audio_player = config->audio_player; + + /* Initialize WebRTC application with simplified API and reasonable defaults */ + ESP_LOGI(TAG, "Initializing WebRTC application with simplified configuration:"); + ESP_LOGI(TAG, " - Role: MASTER (Rainmaker cameras initiate connections)"); + ESP_LOGI(TAG, " - Media type: auto-detected (audio+video from interfaces)"); + ESP_LOGI(TAG, " - Codecs: OPUS/H264 (optimal for streaming)"); + ESP_LOGI(TAG, " - ICE: trickle enabled, TURN enabled (best connectivity)"); + ESP_LOGI(TAG, " - Channel: %s", kvs_signaling_cfg.pChannelName); + ESP_LOGI(TAG, " - Region: %s", aws_region); + if (kvs_signaling_cfg.fetch_credentials_cb) { + ESP_LOGI(TAG, " - Credentials: callback-based (RainMaker core function)"); + } else { + ESP_LOGI(TAG, " - Endpoint: %s", kvs_signaling_cfg.iotCoreCredentialEndpoint); + } + + WEBRTC_STATUS status = app_webrtc_init(&app_webrtc_config); + if (status != WEBRTC_STATUS_SUCCESS) { + ESP_LOGE(TAG, "Failed to initialize WebRTC application: 0x%08" PRIx32, (uint32_t)status); + return; + } + + /* Run WebRTC application (this will create a task internally) */ + ESP_LOGI(TAG, "Starting WebRTC application"); + status = app_webrtc_run(); + if (status != WEBRTC_STATUS_SUCCESS) { + ESP_LOGE(TAG, "Failed to start WebRTC application: 0x%08" PRIx32, (uint32_t)status); + app_webrtc_terminate(); + } else { + ESP_LOGI(TAG, "WebRTC application started successfully"); + } +} diff --git a/examples/camera/components/rmaker_camera/src/sleep_command.c b/examples/camera/components/rmaker_camera/src/sleep_command.c new file mode 100644 index 0000000..3478697 --- /dev/null +++ b/examples/camera/components/rmaker_camera/src/sleep_command.c @@ -0,0 +1,42 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#if CONFIG_IDF_TARGET_ESP32C6 +#include "host_power_save.h" +#endif + +static const char *TAG = "sleep_cmd"; + +static int wakeup_cli_handler(int argc, char *argv[]) +{ +#if CONFIG_IDF_TARGET_ESP32C6 + ESP_LOGI(TAG, "Asking P4 to wake-up..."); + wakeup_host(500); +#endif + return 0; +} + +static esp_console_cmd_t sleep_cmds[] = { + { + .command = "wake-up", + .help = "Wake-up P4 from deep sleep", + .func = wakeup_cli_handler, + } +}; + +int sleep_command_register_cli() +{ + int cmds_num = sizeof(sleep_cmds) / sizeof(esp_console_cmd_t); + for (int i = 0; i < cmds_num; i++) { + ESP_LOGI(TAG, "Registering command: %s", sleep_cmds[i].command); + esp_console_cmd_register(&sleep_cmds[i]); + } + return 0; +} diff --git a/examples/camera/split_mode/README.md b/examples/camera/split_mode/README.md new file mode 100644 index 0000000..60e5037 --- /dev/null +++ b/examples/camera/split_mode/README.md @@ -0,0 +1,144 @@ +# Split Mode Camera Example + +This pair of examples demonstrates a **two-device split architecture** for ESP RainMaker Camera, where signaling and media streaming are separated across two processors for optimal power efficiency. + +## Architecture Overview + +The split mode consists of two separate firmware images: + +### 1. **rmaker_split_camera** (ESP32-C6) +- **Role**: ESP RainMaker device with KVS signaling integration +- **Responsibilities**: + - AWS KVS WebRTC signaling + - ESP RainMaker device management + - Bridge communication with media adapter + - Always-on connectivity for instant responsiveness + +### 2. **media_adapter** (ESP32-P4) +- **Role**: Media streaming device +- **Implementation**: Uses the `streaming_only` example from `${KVS_SDK_PATH}/esp_port/examples/streaming_only` +- **Responsibilities**: + - Video/audio capture and encoding + - WebRTC media streaming + - Power-optimized operation (sleeps when not streaming) + - Receives signaling commands via bridge from rmaker_split_camera + +## Hardware Requirements + +- **ESP32-P4 Function EV Board** (required) + - Contains both ESP32-P4 and ESP32-C6 processors + - Built-in camera support + - SDIO communication between processors + +## System Architecture + +``` +┌─────────────────┐ SDIO Bridge ┌─────────────────┐ +│ ESP32-C6 │◄────────────────────►│ ESP32-P4 │ +│ (rmaker_camera)│ Communication │ (media_adapter) │ +│ │ │ │ +│ ┌─────────────┐ │ │ ┌─────────────┐ │ +│ │ ESP │ │ │ │ H.264 │ │ +│ │ RainMaker │ │ │ │ Encoder │ │ +│ │ │ │ │ │ │ │ +│ │ AWS KVS │ │ │ │ Camera │ │ +│ │ Signaling │ │ │ │ Interface │ │ +│ └─────────────┘ │ │ └─────────────┘ │ +└─────────────────┘ └─────────────────┘ + ▲ ▲ + │ │ + ▼ ▼ + Internet/AWS Video/Audio + (Signaling) Hardware +``` + +## Quick Start + +### Prerequisites + +- IDF version: release/v5.5 (v5.5.x) +- [ESP32-P4 Function EV Board](https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32p4/esp32-p4-function-ev-board/user_guide.html) +- Amazon Kinesis Video Streams WebRTC SDK repository: Clone the `beta-reference-esp-port` branch + + ```bash + git clone --recursive --single-branch --branch beta-reference-esp-port git@github.com:awslabs/amazon-kinesis-video-streams-webrtc-sdk-c.git amazon-kinesis-video-streams-webrtc-sdk-c + export KVS_SDK_PATH=/path/to/amazon-kinesis-video-streams-webrtc-sdk-c + ``` + +- ESP RainMaker App (iOS/Android) with KVS streaming + +### Build and Flash Instructions + +⚠️ **Important**: This requires **TWO separate firmware flashes** on the same ESP32-P4 Function EV Board. + +#### Step 1: Flash rmaker_split_camera (ESP32-C6) + +This handles AWS KVS signaling and ESP RainMaker integration. + +```bash +cd split_mode/rmaker_split_camera +idf.py set-target esp32c6 +idf.py build +idf.py -p [PORT] flash monitor +``` + +**Note**: ESP32-C6 does not have an onboard UART port. You will need to use ESP-Prog or any other JTAG. + +#### Step 2: Flash media_adapter (ESP32-P4) + +This handles video/audio streaming. The firmware is the `streaming_only` example from the KVS SDK. + +```bash +cd ${KVS_SDK_PATH}/esp_port/examples/streaming_only +idf.py set-target esp32p4 +idf.py menuconfig +# Go to Component config -> ESP System Settings -> Channel for console output +# (X) USB Serial/JTAG Controller # For ESP32-P4 Function_EV_Board V1.2 OR V1.5 +# (X) Default: UART0 # For ESP32-P4 Function_EV_Board V1.4 +idf.py build +idf.py -p [PORT] flash monitor +``` + +**Note**: If the console selection is wrong, you will only see the initial bootloader logs. Please change the console as instructed above and reflash the app to see the complete logs. + +### Device Configuration + +After flashing both firmwares: + +1. Use the ESP RainMaker phone app to provision Wi-Fi credentials on the ESP32-C6 (rmaker_split_camera) +2. The camera device should show up in the RainMaker app +3. You can start streaming from the device details page to view the camera feed + +### Deep Sleep Feature + + - ESP32-P4 will automatically go into deep sleep once it detects no streaming activity for some time + - Alternatively, on ESP32-P4's console, simply type `deep-sleep` command. This will put ESP32-P4 into deep sleep +- Start streaming from the ESP RainMaker app (Android/iOS) +- You will see that P4 boots up, and the streaming session is established + +## Directory Structure + +``` +split_mode/ +├── README.md # This file - overview of split mode +├── rmaker_split_camera/ # ESP32-C6 firmware (signaling + RainMaker) +│ ├── README.md # Detailed instructions for rmaker_split_camera +│ ├── main/ # Main application code +│ └── ... +└── media_adapter/ # Documentation for ESP32-P4 firmware + └── README.md # Instructions pointing to streaming_only example + # Actual firmware: ${KVS_SDK_PATH}/esp_port/examples/streaming_only +``` + +## Related Documentation + +- [rmaker_split_camera README](rmaker_split_camera/README.md) - Detailed setup for ESP32-C6 signaling device +- [media_adapter README](media_adapter/README.md) - Detailed setup for ESP32-P4 streaming device +- [Standalone Mode](../standalone/README.md) - Single-device alternative + +## Key Benefits + +- **Power Efficiency**: ESP32-P4 can sleep when not streaming, saving significant power +- **Always-On Connectivity**: ESP32-C6 maintains AWS KVS connection 24/7 +- **Instant Wake-up**: Shared network stack enables immediate wake-up of ESP32-P4 +- **Separation of Concerns**: Signaling and media handling are cleanly separated diff --git a/examples/camera/split_mode/media_adapter/README.md b/examples/camera/split_mode/media_adapter/README.md new file mode 100644 index 0000000..5751a0e --- /dev/null +++ b/examples/camera/split_mode/media_adapter/README.md @@ -0,0 +1,58 @@ +# Media Adapter - Split Mode (ESP32-P4) + +**Power-optimized media streaming device** - This firmware handles only media streaming while the separate `rmaker_camera` firmware (on ESP32-C6) manages AWS KVS communication and ESP RainMaker integration. + +## Implementation + +The media adapter firmware is the **streaming_only** example from the Amazon Kinesis Video Streams WebRTC SDK. + +**Location**: `${KVS_SDK_PATH}/esp_port/examples/streaming_only` + +This is not a separate ESP RainMaker example - it's the standard KVS SDK streaming_only example that works in conjunction with the `rmaker_camera` firmware. + +## Hardware Requirements + +- **ESP32-P4 Function EV Board** (required - contains both P4 + C6 processors) +- **Camera**: OV2640, OV3660, OV5640, etc. (built-in on Function EV Board) + +## Prerequisites + +- IDF version: release/v5.5 (v5.5.x) +- Amazon Kinesis Video Streams WebRTC SDK: Clone the `beta-reference-esp-port` branch + + ```bash + git clone --recursive --single-branch --branch beta-reference-esp-port git@github.com:awslabs/amazon-kinesis-video-streams-webrtc-sdk-c.git amazon-kinesis-video-streams-webrtc-sdk-c + export KVS_SDK_PATH=/path/to/amazon-kinesis-video-streams-webrtc-sdk-c + ``` + +## Build and Flash + +⚠️ **Important**: Flash `rmaker_camera` firmware on ESP32-C6 first (see [rmaker_camera README](../rmaker_camera/README.md)). + +```bash +cd ${KVS_SDK_PATH}/esp_port/examples/streaming_only +idf.py set-target esp32p4 + +# Configure console output (required for different board versions) +idf.py menuconfig +# Go to Component config -> ESP System Settings -> Channel for console output +# (X) USB Serial/JTAG Controller # For ESP32-P4 Function_EV_Board V1.2 OR V1.5 +# (X) Default: UART0 # For ESP32-P4 Function_EV_Board V1.4 + +idf.py build +idf.py -p [PORT] flash monitor +``` + +**Note**: If the console selection is wrong, you will only see the initial bootloader logs. Change the console as instructed above and reflash. + +## Deep Sleep Feature + +- ESP32-P4 will automatically go into deep sleep once it detects no streaming activity for some time +- Alternatively, on ESP32-P4's console, type `deep-sleep` command +- Start streaming from the ESP RainMaker app to wake up ESP32-P4 and establish streaming session + +## Related Documentation + +- [rmaker_camera README](../rmaker_camera/README.md) - Partner device documentation +- [Split Mode Overview](../README.md) - Complete split mode architecture +- [streaming_only README](../../../../amazon-kinesis-video-streams-webrtc-sdk-c/esp_port/examples/streaming_only/README.md) - Full documentation of the streaming_only example diff --git a/examples/camera/split_mode/rmaker_split_camera/CMakeLists.txt b/examples/camera/split_mode/rmaker_split_camera/CMakeLists.txt new file mode 100644 index 0000000..7cad86a --- /dev/null +++ b/examples/camera/split_mode/rmaker_split_camera/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.5) + +# Set KVS_SDK_PATH if not already set in environment +if(NOT DEFINED ENV{KVS_SDK_PATH}) + message(FATAL_ERROR "KVS_SDK_PATH not set in environment. Please set KVS_SDK_PATH to your local clone of amazon-kinesis-video-streams-webrtc-sdk-c") +endif() + +# Enable signaling only mode (This lets webrtc_bridge to what role to play) +option(ENABLE_SIGNALLING_ONLY "enable only the signalling component" ON) +option(ENABLE_STREAMING_ONLY "enable only the streaming component" OFF) + +set(PROJECT_VER "1.0") + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +set(COMPONENTS main) + +project(rmaker_split_camera) diff --git a/examples/camera/split_mode/rmaker_split_camera/README.md b/examples/camera/split_mode/rmaker_split_camera/README.md new file mode 100644 index 0000000..eec1352 --- /dev/null +++ b/examples/camera/split_mode/rmaker_split_camera/README.md @@ -0,0 +1,106 @@ +# RainMaker Camera - Split Mode (ESP32-C6) + +This example demonstrates the **signaling-only** part of the ESP RainMaker Camera in split mode architecture. This firmware runs on **ESP32-C6** and handles AWS KVS WebRTC signaling along with ESP RainMaker device management. + +The **media_adapter** firmware (from `split_mode/media_adapter/`) must be flashed on ESP32-P4 to handle the media streaming part. + +## Prerequisites + +- IDF version: release/v5.5 (v5.5.x) +- [ESP32-P4-Function Ev Board](https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32p4/esp32-p4-function-ev-board/user_guide.html) +- Amazon Kinesis Video Streams WebRTC SDK C repository: Please clone the `beta-reference-esp-port` branch of https://github.com/awslabs/amazon-kinesis-video-streams-webrtc-sdk-c/tree/beta-reference-esp-port +- ESP RainMaker App (iOS/Android) with KVS streaming + +## Setup ESP-IDF + +Please follow the [official guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32p4/) to install and setup the IDF environment: + +Main steps involved for Mac/Linux after downloading/cloning the SDK are + + ```bash + ./install.sh # Installs the essential tools + . $IDF_PATH/export.sh # Sets up the environment + ``` +More comprehensive documentation for setup: + - [MacOS/Linux](https://docs.espressif.com/projects/esp-idf/en/latest/esp32p4/get-started/linux-macos-setup.html) + - [Windows](https://docs.espressif.com/projects/esp-idf/en/latest/esp32p4/get-started/windows-setup.html) + + +## Claiming the Device + - The firmware supports self-claiming and assisted claiming for seamless experience. So no additional configuration is required while using with public RainMaker. + - For host-driven claiming OR for production use-case, please follow camera options in `esp-rainmaker-cli` and `esp-rainmaker-admin-cli` respectively. Certificates must be flashed on ESP32-C6. + +## BUILD + +- Set up the KVS_SDK_PATH environment variable with clone of the Amazon KVS WebRTC SDK from [here](https://github.com/awslabs/amazon-kinesis-video-streams-webrtc-sdk-c/tree/beta-reference-esp-port) with `--recursive` option: + +```bash + git clone --recursive --single-branch --branch beta-reference-esp-port git@github.com:awslabs/amazon-kinesis-video-streams-webrtc-sdk-c.git amazon-kinesis-video-streams-webrtc-sdk-c + export KVS_SDK_PATH=/path/to/amazon-kinesis-video-streams-webrtc-sdk-c +``` + +**__NOTE__**: + 1. Confirm that you cloned the `beta-reference-esp-port` branch + 2. If you missed the `--recursive` option during cloning, run `git submodule update --init --recursive` + +- Go to the example directory and follow the steps below: +```bash +cd esp-rainmaker/examples/camera/split_mode/rmaker_camera +idf.py set-target esp32c6 +``` + +*__NOTE__*: +- ESP32-C6 does not have an onboard UART port. You will need to use ESP-Prog or any other JTAG. +- Use following Pin configuration: + +| ESP32-C6 (J2/Prog-C6) | ESP-Prog | +|----------|----------| +| IO0 | IO9 | +| TX0 | TXD0 | +| RX0 | RXD0 | +| EN | EN | +| GND | GND | + +- Build and flash the example +```bash +idf.py build +idf.py -p [PORT] flash monitor +``` + +- Build and flash the streaming_only example from KVS SDK on ESP32-P4: +```bash + cd ${KVS_SDK_PATH}/esp_port/examples/streaming_only + idf.py set-target esp32p4 +``` + +- For ESP32-P4, different versions of the Dev boards have different options for CONSOLE and LOGs +- You may want to do menuconfig and change it as per your board + +```bash + idf.py menuconfig + # Go to Component config -> ESP System Settings -> Channel for console output + # (X) USB Serial/JTAG Controller # For ESP32-P4 Function_EV_Board V1.2 OR V1.5 + # (X) Default: UART0 # For ESP32-P4 Function_EV_Board V1.4 +``` +- If the console selection is wrong, you will only see the initial bootloader logs. + - Please change the console as instructed above and reflash the app to see the complete logs. + +- Now build and flash the `streaming_only` example on ESP32-P4 +```bash +idf.py build +idf.py -p [PORT] flash monitor +``` + +## Device Configuration + +After flashing the firmware, configure the device: + +1. Use the ESP RainMaker phone app to provision Wi-Fi credentials. The step also involves associating the user with the device +2. Once done, thhe camera device should show up in the RainMaker app +3. You can start streaming from the device details page to view the camera feed. + +### Trying out Deep Sleep feature + - ESP32-P4 will automatically go into deep sleep once it detects no streaming activity for some time + - Alternatively, on ESP32-P4's console, simply type `deep-sleep` command. This will put ESP32-P4 into deep sleep + - Start streaming from the ESP RainMaker app (Android/iOS) + - You will see that, P4 boots up, and the streaming session is established diff --git a/examples/camera/split_mode/rmaker_split_camera/main/CMakeLists.txt b/examples/camera/split_mode/rmaker_split_camera/main/CMakeLists.txt new file mode 100644 index 0000000..30a518f --- /dev/null +++ b/examples/camera/split_mode/rmaker_split_camera/main/CMakeLists.txt @@ -0,0 +1,13 @@ + +idf_component_register( + SRCS + "app_main.c" + INCLUDE_DIRS "." + REQUIRES "nvs_flash" + "esp_wifi" + "esp_timer" + "esp_netif" + "esp_event" + "mbedtls" + "lwip" + ) diff --git a/examples/camera/split_mode/rmaker_split_camera/main/Kconfig.projbuild b/examples/camera/split_mode/rmaker_split_camera/main/Kconfig.projbuild new file mode 100644 index 0000000..4f52efe --- /dev/null +++ b/examples/camera/split_mode/rmaker_split_camera/main/Kconfig.projbuild @@ -0,0 +1,12 @@ +menu "WebRTC Example Configuration" + + config EXAMPLE_BOARD_BUTTON_GPIO + int "Boot Button GPIO" + default 9 if IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32C6 || IDF_TARGET_ESP32C2 || IDF_TARGET_ESP32H2 + default 35 if IDF_TARGET_ESP32P4 + default 0 + help + GPIO number on which the "Boot" button is connected. This is generally used + by the application for custom operations like toggling states, resetting to defaults, etc. + +endmenu diff --git a/examples/camera/split_mode/rmaker_split_camera/main/app_main.c b/examples/camera/split_mode/rmaker_split_camera/main/app_main.c new file mode 100644 index 0000000..e752a24 --- /dev/null +++ b/examples/camera/split_mode/rmaker_split_camera/main/app_main.c @@ -0,0 +1,292 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "esp_err.h" +#include "esp_log.h" +#include "esp_event.h" +#include "nvs_flash.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include "esp_rmaker_console.h" +#include "esp_rmaker_common_console.h" + +#include "rmaker_camera.h" +#include "esp_cli.h" +#include "network_coprocessor.h" +#include "bridge_peer_connection.h" +#include "esp_work_queue.h" +#include "sleep_command.h" + +static const char *TAG = "app_main"; + +/* Configuration */ +#define BUTTON_GPIO CONFIG_EXAMPLE_BOARD_BUTTON_GPIO +#define BUTTON_ACTIVE_LEVEL 0 +#define WIFI_RESET_BUTTON_TIMEOUT 5 +#define FACTORY_RESET_BUTTON_TIMEOUT 10 + +/* Device name prefix */ +static const char *device_name = "WebRTC Camera"; + +/* Global reference to camera device for parameter updates */ +static esp_rmaker_device_t *g_camera_device = NULL; + +/* Update channel parameter with new node ID */ +static void update_channel_param(void) +{ + if (!g_camera_device) { + ESP_LOGW(TAG, "Camera device not initialized, cannot update channel parameter"); + return; + } + + const char *node_id = esp_rmaker_get_node_id(); + if (!node_id) { + ESP_LOGE(TAG, "Failed to get node ID for channel parameter update"); + return; + } + + /* Get the channel parameter */ + esp_rmaker_param_t *channel_param = esp_rmaker_device_get_param_by_name( + g_camera_device, ESP_RMAKER_DEF_CHANNEL_NAME); + if (!channel_param) { + ESP_LOGE(TAG, "Channel parameter not found"); + return; + } + + /* Create new channel name with updated node ID */ + char channel_name[32] = {0}; + snprintf(channel_name, sizeof(channel_name), "%s%s", ESP_RMAKER_CHANNEL_NAME_PREFIX, node_id); + + /* Update and report the parameter */ + esp_err_t err = esp_rmaker_param_update_and_report(channel_param, esp_rmaker_str(channel_name)); + if (err == ESP_OK) { + ESP_LOGI(TAG, "Channel parameter updated to: %s", channel_name); + } else { + ESP_LOGE(TAG, "Failed to update channel parameter: %d", err); + } +} + +/* Event handler for RainMaker common and RainMaker events */ +static void rainmaker_event_handler(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + if (event_base == RMAKER_COMMON_EVENT) { + switch (event_id) { + case RMAKER_EVENT_REBOOT: + ESP_LOGI(TAG, "Rebooting in %d seconds.", *((uint8_t *)event_data)); + break; + case RMAKER_EVENT_WIFI_RESET: + ESP_LOGI(TAG, "Wi-Fi credentials reset."); + break; + case RMAKER_EVENT_FACTORY_RESET: + ESP_LOGI(TAG, "Node reset to factory defaults."); + break; + case RMAKER_MQTT_EVENT_CONNECTED: + ESP_LOGI(TAG, "MQTT Connected."); + break; + case RMAKER_MQTT_EVENT_DISCONNECTED: + ESP_LOGI(TAG, "MQTT Disconnected."); + break; + case RMAKER_MQTT_EVENT_PUBLISHED: + ESP_LOGI(TAG, "MQTT Published. Msg id: %d.", *((int *)event_data)); + break; + default: + ESP_LOGW(TAG, "Unhandled RainMaker Common Event: %"PRIi32, event_id); + break; + } + } else if (event_base == RMAKER_EVENT) { + switch (event_id) { + case RMAKER_EVENT_CLAIM_SUCCESSFUL: + ESP_LOGI(TAG, "RainMaker Claim Successful. Updating channel parameter..."); + /* Node ID may have changed during claiming, update channel parameter */ + update_channel_param(); + break; + default: + ESP_LOGW(TAG, "Unhandled RainMaker Event: %"PRIi32, event_id); + break; + } + } else { + ESP_LOGW(TAG, "Invalid event received!"); + } +} + +#if CONFIG_IDF_TARGET_ESP32P4 +/* Work-around for BLE transport when using hosted on ESP32P4 */ +void ble_transport_ll_init(void) +{ + /* No-op for hosted workaround */ +} + +void ble_transport_ll_deinit(void) +{ + /* No-op for hosted workaround */ +} +#endif + +/* Initialize the RainMaker node and camera device */ +static esp_err_t initialize_rainmaker_device(void) +{ + esp_rmaker_config_t config = { + .enable_time_sync = false, + }; + + /* Initialize the RainMaker node */ + esp_rmaker_node_t *node = esp_rmaker_node_init(&config, device_name, "Camera"); + if (!node) { + ESP_LOGE(TAG, "Failed to initialize RainMaker node"); + return ESP_FAIL; + } + + /* Create the device name with chip target */ + char chip_target[32] = {0}; + strcpy(chip_target, CONFIG_IDF_TARGET); + for (int i = 0; chip_target[i]; i++) { + chip_target[i] = toupper((unsigned char)chip_target[i]); + } + strcat(chip_target, " Camera"); + + /* Create the camera device (automatically includes name and channel parameters) */ + g_camera_device = esp_rmaker_camera_device_create(chip_target, NULL); + if (!g_camera_device) { + ESP_LOGE(TAG, "Failed to create camera device"); + return ESP_FAIL; + } + + /* Add the device to the node */ + if (esp_rmaker_node_add_device(node, g_camera_device) != ESP_OK) { + ESP_LOGE(TAG, "Failed to add device to node"); + return ESP_FAIL; + } + + return ESP_OK; +} + +/* Mode-specific initialization callback for split mode */ +static int split_mode_init_callback(void *user_data) +{ + (void)user_data; + + /* Initialize work queue in advance with lower (than default) stack size */ + esp_work_queue_config_t work_queue_config = ESP_WORK_QUEUE_CONFIG_DEFAULT(); + work_queue_config.stack_size = 12 * 1024; + if (esp_work_queue_init_with_config(&work_queue_config) != ESP_OK) { + ESP_LOGE(TAG, "Failed to initialize work queue"); + return -1; + } + + if (esp_work_queue_start() != ESP_OK) { + ESP_LOGE(TAG, "Failed to start work queue"); + return -1; + } + + /* Register sleep commands for power management */ + if (sleep_command_register_cli() != 0) { + ESP_LOGE(TAG, "Failed to register sleep commands"); + return -1; + } + + return 0; +} + +/* Initialize and register the reset button */ +static void initialize_reset_button(void) +{ + button_config_t btn_cfg = { + .long_press_time = 0, /* Use default */ + .short_press_time = 0, /* Use default */ + }; + button_gpio_config_t gpio_cfg = { + .gpio_num = BUTTON_GPIO, + .active_level = BUTTON_ACTIVE_LEVEL, + .enable_power_save = false, + }; + button_handle_t btn_handle = NULL; + if (iot_button_new_gpio_device(&btn_cfg, &gpio_cfg, &btn_handle) == ESP_OK && btn_handle) { + /* Register reset button */ + app_reset_button_register(btn_handle, WIFI_RESET_BUTTON_TIMEOUT, FACTORY_RESET_BUTTON_TIMEOUT); + } +} + +void app_main(void) +{ + /* Initialize NVS */ + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK(ret); + + ESP_LOGI(TAG, "ESP32 WebRTC Camera Example"); + + /* Initialize ESP CLI */ + esp_cli_start(); + + /* Initialize Wi-Fi - must be called before network_coprocessor_init */ + app_network_init(); + + /* Initialize the network coprocessor */ + network_coprocessor_init(); + + /* Register an event handler to catch RainMaker common events */ + ESP_ERROR_CHECK(esp_event_handler_register(RMAKER_COMMON_EVENT, ESP_EVENT_ANY_ID, + &rainmaker_event_handler, NULL)); + + /* Register an event handler for RainMaker events (including claim events) */ + ESP_ERROR_CHECK(esp_event_handler_register(RMAKER_EVENT, ESP_EVENT_ANY_ID, + &rainmaker_event_handler, NULL)); + + /* Initialize RainMaker device */ + initialize_rainmaker_device(); + + /* Start RainMaker */ + if (esp_rmaker_start() != ESP_OK) { + ESP_LOGE(TAG, "Failed to start RainMaker"); + return; + } + + /* Initialize the RainMaker console */ + esp_rmaker_console_init(); + esp_rmaker_common_register_commands(); + + /* Initialize reset button */ + initialize_reset_button(); + + /* Start the Wi-Fi and wait for connection */ + ESP_LOGI(TAG, "Starting Wi-Fi provisioning or connection..."); + ret = app_network_start(POP_TYPE_RANDOM); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Could not start Wi-Fi. Aborting!"); + vTaskDelay(5000 / portTICK_PERIOD_MS); + abort(); + } + ESP_LOGI(TAG, "Network connection established"); + + /* Initialize and start WebRTC */ + ESP_LOGI(TAG, "Starting WebRTC..."); + + /* Configure WebRTC camera with split mode interfaces */ + rmaker_webrtc_camera_config_t camera_config = { + .peer_connection_if = bridge_peer_connection_if_get(), + .video_capture = NULL, + .audio_capture = NULL, + .init_callback = split_mode_init_callback, + .init_callback_user_data = NULL, + }; + + rmaker_webrtc_camera_init(&camera_config); + + ESP_LOGI(TAG, "Main application continuing after starting WebRTC"); +} diff --git a/examples/camera/split_mode/rmaker_split_camera/main/idf_component.yml b/examples/camera/split_mode/rmaker_split_camera/main/idf_component.yml new file mode 100644 index 0000000..c8f3699 --- /dev/null +++ b/examples/camera/split_mode/rmaker_split_camera/main/idf_component.yml @@ -0,0 +1,41 @@ +## IDF Component Manager Manifest File +dependencies: + ## Required IDF version + idf: + version: ">=5.4.0" + espressif/esp_rainmaker: + version: ">=1.0" + override_path: ../../../../../components/esp_rainmaker + espressif/button: + version: "^4.1.4" + espressif/json_parser: + version: "~1.0.3" + espressif/rmaker_common: + version: "*" + espressif/rmaker_app_reset: + version: "*" + espressif/rmaker_app_network: + version: "*" + espressif/rmaker_app_insights: + version: "*" + + # Components from KVS SDK + network_coprocessor: + version: "*" + path: ${KVS_SDK_PATH}/esp_port/components/network_coprocessor + rules: + - if: "target in [esp32c6]" + esp_webrtc_utils: + path: ${KVS_SDK_PATH}/esp_port/components/esp_webrtc_utils + version: "*" + app_webrtc: + path: ${KVS_SDK_PATH}/esp_port/components/app_webrtc + version: "*" + signaling_bridge_adapter: + path: ${KVS_SDK_PATH}/esp_port/examples/signaling_only/components/signaling_bridge_adapter + version: "*" + + # Local components + rmaker_camera: + path: ../../../components/rmaker_camera + version: "*" diff --git a/examples/camera/split_mode/rmaker_split_camera/partitions.csv b/examples/camera/split_mode/rmaker_split_camera/partitions.csv new file mode 100644 index 0000000..bbba681 --- /dev/null +++ b/examples/camera/split_mode/rmaker_split_camera/partitions.csv @@ -0,0 +1,11 @@ +# Name, Type, SubType, Offset, Size, Flags +# Note: Firmware partition offset needs to be 64K aligned, initial 36K (9 sectors) are reserved for bootloader and partition table +esp_secure_cert, 0x3F, , 0xD000, 0x2000, encrypted +nvs_key, data, nvs_keys, 0xF000, 0x1000, encrypted +nvs, data, nvs, 0x10000, 0x6000, +otadata, data, ota, , 0x2000 +phy_init, data, phy, , 0x1000, +ota_0, app, ota_0, 0x20000, 0x1E0000, +ota_1, app, ota_1, 0x200000, 0x1E0000, +reserved, 0x06, , 0x3E0000, 0x1A000, +fctry, data, nvs, 0x3FA000, 0x6000 diff --git a/examples/camera/split_mode/rmaker_split_camera/sdkconfig.defaults b/examples/camera/split_mode/rmaker_split_camera/sdkconfig.defaults new file mode 100644 index 0000000..0ce9703 --- /dev/null +++ b/examples/camera/split_mode/rmaker_split_camera/sdkconfig.defaults @@ -0,0 +1,143 @@ +# signaling_only side of the split (default: ESP32-C6) +CONFIG_IDF_TARGET="esp32c6" +CONFIG_IDF_TARGET_ESP32C6=y + +# +# Serial flasher config +# +CONFIG_ESPTOOLPY_FLASHMODE_QIO=y +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y + +# +# Partition Table +# +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_OFFSET=0xC000 + +# +# SPI RAM config +# +CONFIG_SPIRAM=y +CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y + +CONFIG_SPIRAM_USE_MALLOC=y +CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=0 +CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=32768 +CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y +CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY=y +CONFIG_SPIRAM_SPEED_80M=y + +CONFIG_FATFS_LFN_HEAP=y +CONFIG_FATFS_MAX_LFN=255 +CONFIG_FATFS_ALLOC_PREFER_EXTRAM=y + +# +# SNTP +# +CONFIG_LWIP_SNTP_MAX_SERVERS=3 + +#CONFIG_LWIP_SO_LINGER=y +#CONFIG_LWIP_SO_RCVBUF=y +#CONFIG_LWIP_NETBUF_RECVINFO=y +#CONFIG_LWIP_IP_FRAG=y +#CONFIG_LWIP_STATS=y + +#CONFIG_LWIP_MULTICAST_PING=y +#CONFIG_LWIP_BROADCAST_PING=y + +CONFIG_LWIP_DHCP_MAX_NTP_SERVERS=1 + +CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC=y + +# +# FreeRTOS +# +CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y +CONFIG_FREERTOS_PLACE_SNAPSHOT_FUNS_INTO_FLASH=y + +# +# ESP Ringbuf +# +CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH=y +CONFIG_RINGBUF_PLACE_ISR_FUNCTIONS_INTO_FLASH=y + +# +# ESP System Settings +# +CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP=y + +# +# PThreads +# +CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT=8000 +CONFIG_PTHREAD_STACK_MIN=4096 + +CONFIG_IPC_TASK_STACK_SIZE=1024 +CONFIG_TIMER_TASK_STACK_SIZE=3584 +CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y + +# +# Wi-Fi +# +CONFIG_ESP_WIFI_STATIC_TX_BUFFER=n +CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER=y + +CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=4000 + +# mbedtls dynamic buffer for memory optimization +CONFIG_MBEDTLS_DYNAMIC_BUFFER=y +CONFIG_MBEDTLS_ASYMMETRIC_CONTENT_LEN=y + +CONFIG_MBEDTLS_DYNAMIC_FREE_CONFIG_DATA=y +CONFIG_MBEDTLS_DYNAMIC_FREE_CA_CERT=y +CONFIG_MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH=n +CONFIG_MBEDTLS_DYNAMIC_FREE_PEER_CERT=y + +CONFIG_FREERTOS_HZ=1000 + +CONFIG_LWIP_TCP_SND_BUF_DEFAULT=5760 +CONFIG_LWIP_TCP_WND_DEFAULT=5760 +CONFIG_LWIP_TCP_RECVMBOX_SIZE=6 +CONFIG_LWIP_UDP_RECVMBOX_SIZE=6 +CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=64 + +# For BLE Provisioning using NimBLE stack (Not applicable for ESP32-S2) +CONFIG_BT_ENABLED=y +CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y +CONFIG_BT_NIMBLE_ENABLED=y + +# +# KVS WebRTC options +# +CONFIG_PREFER_DYNAMIC_ALLOCS=y +CONFIG_USE_ESP_WEBSOCKET_CLIENT=y +CONFIG_ENABLE_DATA_CHANNEL=n + +# +# Websockets +# +CONFIG_WS_BUFFER_SIZE=2048 +CONFIG_WS_DYNAMIC_BUFFER=y + +# +# MDNS Memory Configuration +# +CONFIG_MDNS_TASK_CREATE_FROM_SPIRAM=y +CONFIG_MDNS_MEMORY_ALLOC_SPIRAM=y + +# +# ESP RainMaker Configuration +# +ESP_RMAKER_USER_ID_CHECK=y +CONFIG_ESP_RMAKER_CLAIM_VIDEOSTREAM_SUPPORT=y +CONFIG_ESP_RMAKER_CONSOLE_ENABLED=n +CONFIG_ESP_RMAKER_ENABLE_CHALLENGE_RESPONSE=y +CONFIG_ESP_RMAKER_ASSISTED_CLAIM=y + +# +# RainMaker Camera Mode Configuration +# +CONFIG_ENABLE_SIGNALLING_ONLY=y diff --git a/examples/camera/split_mode/rmaker_split_camera/sdkconfig.defaults.esp32c6 b/examples/camera/split_mode/rmaker_split_camera/sdkconfig.defaults.esp32c6 new file mode 100644 index 0000000..5c0545a --- /dev/null +++ b/examples/camera/split_mode/rmaker_split_camera/sdkconfig.defaults.esp32c6 @@ -0,0 +1,54 @@ +# perform the size optimized builds for C6 to avoid app size overflow +CONFIG_COMPILER_OPTIMIZATION_SIZE=y + +# SPI Configuration +CONFIG_SPI_MASTER_ISR_IN_IRAM=n +CONFIG_SPI_SLAVE_ISR_IN_IRAM=n + +# SPI Flash driver +CONFIG_SPI_FLASH_ROM_DRIVER_PATCH=n +CONFIG_SPI_FLASH_ROM_IMPL=y + +# Peripheral Control +CONFIG_PERIPH_CTRL_FUNC_IN_IRAM=n + +# +# ESP System Settings +# +CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP=y + +# Event Loop Library +CONFIG_ESP_EVENT_POST_FROM_ISR=n + +# +# PThreads +# +CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT=8000 +CONFIG_PTHREAD_STACK_MIN=4096 + +CONFIG_MAIN_TASK_STACK_SIZE=6000 +CONFIG_IPC_TASK_STACK_SIZE=1024 +CONFIG_TIMER_TASK_STACK_SIZE=3584 +CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y + +# +# Wi-Fi +# +CONFIG_ESP_WIFI_IRAM_OPT=n +CONFIG_ESP_WIFI_RX_IRAM_OPT=n +CONFIG_ESP_WIFI_EXTRA_IRAM_OPT=n +CONFIG_ESP_WIFI_SLP_IRAM_OPT=n + +CONFIG_FREERTOS_HZ=1000 + +CONFIG_LWIP_TCP_SND_BUF_DEFAULT=5760 +CONFIG_LWIP_TCP_WND_DEFAULT=5760 +CONFIG_LWIP_TCP_RECVMBOX_SIZE=6 +CONFIG_LWIP_UDP_RECVMBOX_SIZE=6 +CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=64 + +# +# ESP-Hosted - Network coprocessor +# +CONFIG_SLAVE_LWIP_ENABLED=y +CONFIG_ESP_SDIO_RX_Q_SIZE=10 diff --git a/examples/camera/standalone/CMakeLists.txt b/examples/camera/standalone/CMakeLists.txt new file mode 100644 index 0000000..a306188 --- /dev/null +++ b/examples/camera/standalone/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.5) + +# Set KVS_SDK_PATH if not already set in environment +if(NOT DEFINED ENV{KVS_SDK_PATH}) + message(FATAL_ERROR "KVS_SDK_PATH not set in environment. Please set KVS_SDK_PATH to your local clone of amazon-kinesis-video-streams-webrtc-sdk-c") +endif() + +set(PROJECT_VER "1.0") +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +set(COMPONENTS main) +project(standalone_camera) diff --git a/examples/camera/standalone/README.md b/examples/camera/standalone/README.md new file mode 100644 index 0000000..92a5f44 --- /dev/null +++ b/examples/camera/standalone/README.md @@ -0,0 +1,100 @@ +# RainMaker Camera + +This example demonstrates how to build a smart camera using ESP chipsets with AWS Kinesis Video Streams (KVS) WebRTC SDK integration and ESP RainMaker for device management. + + - The purpose of this example is to showcase the Real-time video streaming over WebRTC with the help of the device management via ESP RainMaker + +## Prerequisites + +- IDF version: release/v5.5 (v5.5.x) +- ESP32-P4, ESP-S3, or other ESP32 variants + - We recommend using ESP32-P4 or ESP-S3 as they have better camera support and hence live streaming is possible + - For other chipsets, file based streaming is provided +- Tested on the following Dev boards: + 1. [ESP32-P4-Function Ev Board](https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32p4/esp32-p4-function-ev-board/user_guide.html) + 2. [ESP-S3-EYE](https://github.com/espressif/esp-who/blob/master/docs/en/get-started/ESP32-S3-EYE_Getting_Started_Guide.md) +- Amazon Kinesis Video Streams WebRTC SDK C repository: Please clone the `beta-reference-esp-port` branch of https://github.com/awslabs/amazon-kinesis-video-streams-webrtc-sdk-c/tree/beta-reference-esp-port +- ESP RainMaker App (iOS/Android) with KVS streaming + +## Setup ESP-IDF + +Please follow the [official guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32p4/) to install and setup the IDF environment: + +Main steps involved form Mac/Linux after downloading/cloning the SDK are + +```bash + ./install.sh # Installs the essential tools + . $IDF_PATH/export.sh # Sets up the environment +``` +More comprehensive documentation for setup: + - [MacOS/Linux](https://docs.espressif.com/projects/esp-idf/en/latest/esp32p4/get-started/linux-macos-setup.html) + - [Windows](https://docs.espressif.com/projects/esp-idf/en/latest/esp32p4/get-started/windows-setup.html) + +## Claiming the Device + - The firmware supports self-claiming and assisted claiming for seamless experience. So no additional configuration is required while using with public RainMaker. + - For host-driven claiming OR for production use-case, please follow camera options in `esp-rainmaker-cli` and `esp-rainmaker-admin-cli` respectively. + +## BUILD + +- Set up the KVS_SDK_PATH environment variable with clone of the Amazon KVS WebRTC SDK from [here](https://github.com/awslabs/amazon-kinesis-video-streams-webrtc-sdk-c/tree/beta-reference-esp-port) with `--recursive` option: + +```bash + git clone --recursive --single-branch --branch beta-reference-esp-port git@github.com:awslabs/amazon-kinesis-video-streams-webrtc-sdk-c.git amazon-kinesis-video-streams-webrtc-sdk-c + export KVS_SDK_PATH=/path/to/amazon-kinesis-video-streams-webrtc-sdk-c +``` + +*__NOTE__*: + 1. Confirm that you cloned the `beta-reference-esp-port` branch + 2. If you missed the `--recursive` option during cloning, run `git submodule update --init --recursive` + +Go to the example directory and follow the steps below: +```bash + cd esp-rainmaker/examples/camera/standalone + idf.py set-target [esp32p4/esp32s3/esp32] +``` + +- Different Development boards have different options for CONSOLE and LOGs +- You may want to do menuconfig and change it as per your board + +```bash + idf.py menuconfig + # Go to Component config -> ESP System Settings -> Channel for console output + # (X) USB Serial/JTAG Controller # For ESP32-P4 Function_EV_Board V1.2 OR V1.5 + # (X) Default: UART0 # For ESP32-P4 Function_EV_Board V1.4 +``` +- If the console selection is wrong, you will only see the initial bootloader logs. Please change the console as instructed above and reflash the app to see the complete logs. + + +- Build and flash the example +```bash + idf.py build + idf.py -p [PORT] flash monitor +``` + +*__NOTE__*: +- While using P4+C6 setup, please build and flash the network_adapter example from `${KVS_SDK_PATH}/esp_port/examples/network_adapter` on ESP32-C6. +- ESP32-C6 does not have an onboard UART port. You will need to use [ESP-Prog](https://docs.espressif.com/projects/esp-iot-solution/en/latest/hw-reference/ESP-Prog_guide.html) board or any other JTAG. +- Use following Pin Connections: + +| ESP32-C6 (J2/Prog-C6) | ESP-Prog | +|----------|----------| +| IO0 | IO9 | +| TX0 | TXD0 | +| RX0 | RXD0 | +| EN | EN | +| GND | GND | + +```bash + cd ${KVS_SDK_PATH}/esp_port/examples/network_adapter + idf.py set-target esp32c6 + idf.py build + idf.py -p [ESP32-C6-PORT] flash monitor +``` + +## Device Configuration + +After flashing the firmware, configure the device: + +1. Use the ESP RainMaker phone app to provision Wi-Fi credentials. The step also involves associating the user with the device +2. Once done, thhe camera device should show up in the RainMaker app +3. You can start streaming from the device details page to view the camera feed. diff --git a/examples/camera/standalone/main/CMakeLists.txt b/examples/camera/standalone/main/CMakeLists.txt new file mode 100644 index 0000000..30a518f --- /dev/null +++ b/examples/camera/standalone/main/CMakeLists.txt @@ -0,0 +1,13 @@ + +idf_component_register( + SRCS + "app_main.c" + INCLUDE_DIRS "." + REQUIRES "nvs_flash" + "esp_wifi" + "esp_timer" + "esp_netif" + "esp_event" + "mbedtls" + "lwip" + ) diff --git a/examples/camera/standalone/main/Kconfig.projbuild b/examples/camera/standalone/main/Kconfig.projbuild new file mode 100644 index 0000000..4f52efe --- /dev/null +++ b/examples/camera/standalone/main/Kconfig.projbuild @@ -0,0 +1,12 @@ +menu "WebRTC Example Configuration" + + config EXAMPLE_BOARD_BUTTON_GPIO + int "Boot Button GPIO" + default 9 if IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32C6 || IDF_TARGET_ESP32C2 || IDF_TARGET_ESP32H2 + default 35 if IDF_TARGET_ESP32P4 + default 0 + help + GPIO number on which the "Boot" button is connected. This is generally used + by the application for custom operations like toggling states, resetting to defaults, etc. + +endmenu diff --git a/examples/camera/standalone/main/app_main.c b/examples/camera/standalone/main/app_main.c new file mode 100644 index 0000000..701b562 --- /dev/null +++ b/examples/camera/standalone/main/app_main.c @@ -0,0 +1,257 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "esp_err.h" +#include "esp_log.h" +#include "nvs_flash.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include "esp_rmaker_console.h" + +#include "rmaker_camera.h" +#include "esp_cli.h" +#include "kvs_peer_connection.h" +#include "media_stream.h" + +static const char *TAG = "app_main"; + +/* Configuration */ +#define BUTTON_GPIO CONFIG_EXAMPLE_BOARD_BUTTON_GPIO +#define BUTTON_ACTIVE_LEVEL 0 +#define WIFI_RESET_BUTTON_TIMEOUT 5 +#define FACTORY_RESET_BUTTON_TIMEOUT 10 + +/* Device name prefix */ +static const char *device_name = "WebRTC Camera"; + +/* Global reference to camera device for parameter updates */ +static esp_rmaker_device_t *g_camera_device = NULL; + +/* Update channel parameter with new node ID */ +static void update_channel_param(void) +{ + if (!g_camera_device) { + ESP_LOGW(TAG, "Camera device not initialized, cannot update channel parameter"); + return; + } + + const char *node_id = esp_rmaker_get_node_id(); + if (!node_id) { + ESP_LOGE(TAG, "Failed to get node ID for channel parameter update"); + return; + } + + /* Get the channel parameter */ + esp_rmaker_param_t *channel_param = esp_rmaker_device_get_param_by_name( + g_camera_device, ESP_RMAKER_DEF_CHANNEL_NAME); + if (!channel_param) { + ESP_LOGE(TAG, "Channel parameter not found"); + return; + } + + /* Create new channel name with updated node ID */ + char channel_name[32] = {0}; + snprintf(channel_name, sizeof(channel_name), "%s%s", ESP_RMAKER_CHANNEL_NAME_PREFIX, node_id); + + /* Update and report the parameter */ + esp_err_t err = esp_rmaker_param_update_and_report(channel_param, esp_rmaker_str(channel_name)); + if (err == ESP_OK) { + ESP_LOGI(TAG, "Channel parameter updated to: %s", channel_name); + } else { + ESP_LOGE(TAG, "Failed to update channel parameter: %d", err); + } +} + +/* Event handler for RainMaker common and RainMaker events */ +static void rainmaker_event_handler(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + if (event_base == RMAKER_COMMON_EVENT) { + switch (event_id) { + case RMAKER_EVENT_REBOOT: + ESP_LOGI(TAG, "Rebooting in %d seconds.", *((uint8_t *)event_data)); + break; + case RMAKER_EVENT_WIFI_RESET: + ESP_LOGI(TAG, "Wi-Fi credentials reset."); + break; + case RMAKER_EVENT_FACTORY_RESET: + ESP_LOGI(TAG, "Node reset to factory defaults."); + break; + case RMAKER_MQTT_EVENT_CONNECTED: + ESP_LOGI(TAG, "MQTT Connected."); + break; + case RMAKER_MQTT_EVENT_DISCONNECTED: + ESP_LOGI(TAG, "MQTT Disconnected."); + break; + case RMAKER_MQTT_EVENT_PUBLISHED: + ESP_LOGI(TAG, "MQTT Published. Msg id: %d.", *((int *)event_data)); + break; + default: + ESP_LOGW(TAG, "Unhandled RainMaker Common Event: %"PRIi32, event_id); + break; + } + } else if (event_base == RMAKER_EVENT) { + switch (event_id) { + case RMAKER_EVENT_CLAIM_SUCCESSFUL: + ESP_LOGI(TAG, "RainMaker Claim Successful. Updating channel parameter..."); + /* Node ID may have changed during claiming, update channel parameter */ + update_channel_param(); + break; + default: + ESP_LOGW(TAG, "Unhandled RainMaker Event: %"PRIi32, event_id); + break; + } + } else { + ESP_LOGW(TAG, "Invalid event received!"); + } +} + +#if CONFIG_IDF_TARGET_ESP32P4 +/* Work-around for BLE transport when using hosted on ESP32P4 */ +void ble_transport_ll_init(void) +{ + /* No-op for hosted workaround */ +} + +void ble_transport_ll_deinit(void) +{ + /* No-op for hosted workaround */ +} +#endif + +/* Initialize the RainMaker node and camera device */ +static esp_err_t initialize_rainmaker_device(void) +{ + esp_rmaker_config_t config = { + .enable_time_sync = false, + }; + + /* Initialize the RainMaker node */ + esp_rmaker_node_t *node = esp_rmaker_node_init(&config, device_name, "Camera"); + if (!node) { + ESP_LOGE(TAG, "Failed to initialize RainMaker node"); + return ESP_FAIL; + } + + /* Create the device name with chip target */ + char chip_target[32] = {0}; + strcpy(chip_target, CONFIG_IDF_TARGET); + for (int i = 0; chip_target[i]; i++) { + chip_target[i] = toupper((unsigned char)chip_target[i]); + } + strcat(chip_target, " Camera"); + + /* Create the camera device (automatically includes name and channel parameters) */ + g_camera_device = esp_rmaker_camera_device_create(chip_target, NULL); + if (!g_camera_device) { + ESP_LOGE(TAG, "Failed to create camera device"); + return ESP_FAIL; + } + + /* Add the device to the node */ + if (esp_rmaker_node_add_device(node, g_camera_device) != ESP_OK) { + ESP_LOGE(TAG, "Failed to add device to node"); + return ESP_FAIL; + } + + return ESP_OK; +} + +/* Initialize and register the reset button */ +static void initialize_reset_button(void) +{ + button_config_t btn_cfg = { + .long_press_time = 0, /* Use default */ + .short_press_time = 0, /* Use default */ + }; + button_gpio_config_t gpio_cfg = { + .gpio_num = BUTTON_GPIO, + .active_level = BUTTON_ACTIVE_LEVEL, + .enable_power_save = false, + }; + button_handle_t btn_handle = NULL; + if (iot_button_new_gpio_device(&btn_cfg, &gpio_cfg, &btn_handle) == ESP_OK && btn_handle) { + /* Register reset button */ + app_reset_button_register(btn_handle, WIFI_RESET_BUTTON_TIMEOUT, FACTORY_RESET_BUTTON_TIMEOUT); + } +} + +void app_main(void) +{ + /* Initialize NVS */ + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK(ret); + + ESP_LOGI(TAG, "ESP32 WebRTC Camera Example"); + + /* Initialize ESP CLI */ + esp_cli_start(); + + /* Initialize Wi-Fi */ + app_network_init(); + + /* Register an event handler to catch RainMaker common events */ + ESP_ERROR_CHECK(esp_event_handler_register(RMAKER_COMMON_EVENT, ESP_EVENT_ANY_ID, + &rainmaker_event_handler, NULL)); + + /* Register an event handler for RainMaker events (including claim events) */ + ESP_ERROR_CHECK(esp_event_handler_register(RMAKER_EVENT, ESP_EVENT_ANY_ID, + &rainmaker_event_handler, NULL)); + + /* Initialize RainMaker device */ + initialize_rainmaker_device(); + + /* Start RainMaker */ + if (esp_rmaker_start() != ESP_OK) { + ESP_LOGE(TAG, "Failed to start RainMaker"); + return; + } + + /* Initialize the RainMaker console */ + esp_rmaker_console_init(); + + /* Initialize reset button */ + initialize_reset_button(); + + /* Start the Wi-Fi and wait for connection */ + ESP_LOGI(TAG, "Starting Wi-Fi provisioning or connection..."); + ret = app_network_start(POP_TYPE_RANDOM); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Could not start Wi-Fi. Aborting!"); + vTaskDelay(5000 / portTICK_PERIOD_MS); + abort(); + } + ESP_LOGI(TAG, "Network connection established"); + + /* Initialize and start WebRTC */ + ESP_LOGI(TAG, "Starting WebRTC..."); + + /* Configure WebRTC camera with standalone mode interfaces */ + rmaker_webrtc_camera_config_t camera_config = { + .peer_connection_if = kvs_peer_connection_if_get(), + .video_capture = media_stream_get_video_capture_if(), + .audio_capture = media_stream_get_audio_capture_if(), + .init_callback = NULL, + .init_callback_user_data = NULL, + }; + + rmaker_webrtc_camera_init(&camera_config); + + ESP_LOGI(TAG, "Main application continuing after starting WebRTC"); +} diff --git a/examples/camera/standalone/main/idf_component.yml b/examples/camera/standalone/main/idf_component.yml new file mode 100644 index 0000000..c164889 --- /dev/null +++ b/examples/camera/standalone/main/idf_component.yml @@ -0,0 +1,49 @@ +## IDF Component Manager Manifest File +dependencies: + ## Required IDF version + idf: + version: ">=5.4.0" + espressif/esp_rainmaker: + version: ">=1.0" + override_path: ../../../../components/esp_rainmaker + espressif/button: + version: "^4.1.4" + espressif/json_parser: + version: "~1.0.3" + espressif/rmaker_common: + version: "*" + espressif/rmaker_app_reset: + version: "*" + espressif/rmaker_app_network: + version: "*" + espressif/rmaker_app_insights: + version: "*" + + # Components from KVS SDK + esp_webrtc_utils: + path: ${KVS_SDK_PATH}/esp_port/components/esp_webrtc_utils + version: "*" + app_webrtc: + path: ${KVS_SDK_PATH}/esp_port/components/app_webrtc + version: "*" + kvs_webrtc: + path: ${KVS_SDK_PATH}/esp_port/components/kvs_webrtc + version: "*" + media_stream: + path: ${KVS_SDK_PATH}/esp_port/components/media_stream + version: "*" + espressif/esp_hosted: + override_path: ${KVS_SDK_PATH}/esp_port/components/esp_hosted + version: "*" + rules: + - if: "target in [esp32p4]" + espressif/esp_wifi_remote: + override_path: ${KVS_SDK_PATH}/esp_port/components/esp_wifi_remote + version: "*" + rules: + - if: "target in [esp32p4]" + + # Local components + rmaker_camera: + path: ../../components/rmaker_camera + version: "*" diff --git a/examples/camera/standalone/partitions.csv b/examples/camera/standalone/partitions.csv new file mode 100644 index 0000000..c64f0b5 --- /dev/null +++ b/examples/camera/standalone/partitions.csv @@ -0,0 +1,12 @@ +# Name, Type, SubType, Offset, Size, Flags +# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap +esp_secure_cert, 0x3F, , 0xD000, 0x2000, encrypted +nvs_key, data, nvs_keys, 0xF000, 0x1000, encrypted +nvs, data, nvs, 0x10000, 0x6000, +otadata, data, ota, , 0x2000 +phy_init, data, phy, , 0x1000, +ota_0, app, ota_0, 0x20000, 0x2E0000, +reserved, 0x06, , 0x3E0000, 0x1A000, +fctry, data, nvs, 0x3FA000, 0x6000 +ota_1, app, ota_1, 0x420000, 0x2E0000, +storage, data, spiffs, 0x700000, 1M diff --git a/examples/camera/standalone/sdkconfig.defaults b/examples/camera/standalone/sdkconfig.defaults new file mode 100644 index 0000000..06dc59f --- /dev/null +++ b/examples/camera/standalone/sdkconfig.defaults @@ -0,0 +1,169 @@ +# Streaming only defaults to P4 +CONFIG_IDF_TARGET="esp32p4" +CONFIG_IDF_TARGET_ESP32P4=y + +# +# Serial flasher config +# +CONFIG_ESPTOOLPY_FLASHMODE_QIO=y +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y + +# +# Partition Table +# +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_OFFSET=0xC000 + +# +# Compiler options +# +CONFIG_COMPILER_OPTIMIZATION_SIZE=y + +# +# ESP-TLS +# +CONFIG_ESP_TLS_USING_MBEDTLS=y + +# +# SPI RAM config +# +CONFIG_SPIRAM=y +CONFIG_SPIRAM_BOOT_INIT=y +CONFIG_SPIRAM_RODATA=y +CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y + +CONFIG_SPIRAM_USE_MALLOC=y +CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=0 +CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=32768 +CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y +CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY=y +CONFIG_SPIRAM_SPEED_80M=y + +CONFIG_FATFS_LFN_HEAP=y +CONFIG_FATFS_MAX_LFN=255 +CONFIG_FATFS_API_ENCODING_ANSI_OEM=y +CONFIG_FATFS_FS_LOCK=10 + +CONFIG_FATFS_ALLOC_PREFER_EXTRAM=y + +# +# SNTP +# +CONFIG_LWIP_SNTP_MAX_SERVERS=3 + +# +# LWIP +# +CONFIG_LWIP_MAX_SOCKETS=10 +CONFIG_LWIP_DHCP_MAX_NTP_SERVERS=1 +CONFIG_LWIP_L2_TO_L3_COPY=y +CONFIG_TCP_SND_BUF_DEFAULT=65534 +CONFIG_TCP_WND_DEFAULT=65535 +CONFIG_TCP_RECVMBOX_SIZE=64 +CONFIG_LWIP_TCP_SND_BUF_DEFAULT=65534 +CONFIG_LWIP_TCP_WND_DEFAULT=65535 +CONFIG_LWIP_TCP_RECVMBOX_SIZE=64 + +# +# mbedTLS +# +CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC=y +CONFIG_MBEDTLS_SSL_PROTO_DTLS=y +CONFIG_MBEDTLS_SSL_ALPN=y + +# mbedtls dynamic buffer for memory optimization +CONFIG_MBEDTLS_ASYMMETRIC_CONTENT_LEN=y + +CONFIG_MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH=n +CONFIG_MBEDTLS_DYNAMIC_BUFFER=y +CONFIG_MBEDTLS_DYNAMIC_FREE_CONFIG_DATA=y +CONFIG_MBEDTLS_DYNAMIC_FREE_CA_CERT=y +CONFIG_MBEDTLS_DYNAMIC_FREE_PEER_CERT=y + +# +# ESP Ringbuf +# +CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH=y +CONFIG_RINGBUF_PLACE_ISR_FUNCTIONS_INTO_FLASH=y + +# +# FreeRTOS +# +CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y +CONFIG_FREERTOS_PLACE_SNAPSHOT_FUNS_INTO_FLASH=y +CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY=y +CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=4096 + +# +# ESP System Settings +# +CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP=y + +# +# PThreads +# +CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT=8000 +CONFIG_PTHREAD_STACK_MIN=4096 + + +CONFIG_MAIN_TASK_STACK_SIZE=8192 +CONFIG_IPC_TASK_STACK_SIZE=1024 +CONFIG_TIMER_TASK_STACK_SIZE=3584 + +CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y + +CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192 +CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=4096 + +CONFIG_FREERTOS_HZ=1000 + +CONFIG_LWIP_TCP_SND_BUF_DEFAULT=5760 +CONFIG_LWIP_TCP_WND_DEFAULT=5760 +CONFIG_LWIP_TCP_RECVMBOX_SIZE=6 +CONFIG_LWIP_UDP_RECVMBOX_SIZE=6 +CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=64 + +# For BLE Provisioning using NimBLE stack (Not applicable for ESP32-S2) +CONFIG_BT_ENABLED=y +CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y +CONFIG_BT_NIMBLE_ENABLED=y + +# +# KVS WebRTC options +# +CONFIG_PREFER_DYNAMIC_ALLOCS=y +CONFIG_USE_ESP_WEBSOCKET_CLIENT=y + +# +# Websockets +# +CONFIG_WS_BUFFER_SIZE=2048 +CONFIG_WS_DYNAMIC_BUFFER=y + +# Since we use esp_video, which needs latest I2C driver +CONFIG_CODEC_I2C_BACKWARD_COMPATIBLE=n + +# +# MDNS Memory Configuration +# +CONFIG_MDNS_TASK_CREATE_FROM_SPIRAM=y +CONFIG_MDNS_MEMORY_ALLOC_SPIRAM=y + +# +# esp-video and isp config +# +CONFIG_ESP_VIDEO_ENABLE_ISP_VIDEO_DEVICE=y +CONFIG_ESP_VIDEO_ENABLE_ISP_PIPELINE_CONTROLLER=y +CONFIG_ISP_PIPELINE_CONTROLLER_TASK_STACK_USE_PSRAM=y + +# +# ESP RainMaker Configuration +# +ESP_RMAKER_USER_ID_CHECK=y +CONFIG_ESP_RMAKER_CLAIM_VIDEOSTREAM_SUPPORT=y +CONFIG_ESP_RMAKER_CONSOLE_ENABLED=n +CONFIG_ESP_RMAKER_ENABLE_CHALLENGE_RESPONSE=y +CONFIG_ESP_RMAKER_ASSISTED_CLAIM=y diff --git a/examples/camera/standalone/sdkconfig.defaults.esp32 b/examples/camera/standalone/sdkconfig.defaults.esp32 new file mode 100644 index 0000000..3529c03 --- /dev/null +++ b/examples/camera/standalone/sdkconfig.defaults.esp32 @@ -0,0 +1,20 @@ +# +# ESP32-Specific +# +CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y +CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=240 + +# +# Wi-Fi +# +CONFIG_ESP32_WIFI_STATIC_TX_BUFFER=y +CONFIG_ESP32_WIFI_TX_BUFFER_TYPE=0 +CONFIG_ESP32_WIFI_STATIC_TX_BUFFER_NUM=16 +CONFIG_ESP32_WIFI_IRAM_OPT=n +CONFIG_ESP32_WIFI_RX_IRAM_OPT=n + +# +# SPI RAM config +# +CONFIG_SPIRAM_BANKSWITCH_ENABLE=y +CONFIG_SPIRAM_BANKSWITCH_RESERVE=8 diff --git a/examples/camera/standalone/sdkconfig.defaults.esp32p4 b/examples/camera/standalone/sdkconfig.defaults.esp32p4 new file mode 100644 index 0000000..f38f178 --- /dev/null +++ b/examples/camera/standalone/sdkconfig.defaults.esp32p4 @@ -0,0 +1,102 @@ + +CONFIG_IDF_EXPERIMENTAL_FEATURES=y + +# Enable BT via VHCI +CONFIG_BT_NIMBLE_ENABLED=y +CONFIG_BT_NIMBLE_TRANSPORT_UART=n +CONFIG_ESP_ENABLE_BT=y + +# +# Serial flasher config +# +CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y + +# +# ESP System Settings +# +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=360 +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_360=y + +# +# ESP NETIF Adapter +# +CONFIG_ESP_NETIF_IP_LOST_TIMER_INTERVAL=120 +CONFIG_ESP_NETIF_TCPIP_LWIP=y +# CONFIG_ESP_NETIF_LOOPBACK is not set +CONFIG_ESP_NETIF_USES_TCPIP_WITH_BSD_API=y + +# +# ESP PSRAM +# +CONFIG_SPIRAM_MODE_HEX=y +CONFIG_SPIRAM_SPEED_200M=y +CONFIG_SPIRAM_SPEED=200 +CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=n +CONFIG_SPIRAM_USE_CAPS_ALLOC=y + +# +# Cache config +# +CONFIG_CACHE_L2_CACHE_128KB=y +CONFIG_CACHE_L2_CACHE_SIZE=0x20000 +CONFIG_CACHE_L2_CACHE_LINE_64B=y +CONFIG_CACHE_L2_CACHE_LINE_SIZE=64 +CONFIG_CACHE_L1_CACHE_LINE_SIZE=64 + +# +# Wi-Fi +# +CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER=y +CONFIG_ESP_WIFI_TX_BUFFER_TYPE=1 +CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=32 +CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=80 +CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER_NUM=80 + +# +# SDIO +# +#CONFIG_ESP_SDIO_OPTIMIZATION_RX_NONE=y +#CONFIG_ESP_SDIO_OPTIMIZATION_RX_MAX_SIZE=y +#CONFIG_ESP_SDIO_OPTIMIZATION_RX_STREAMING_MODE=y +CONFIG_ESP_SDIO_CLOCK_FREQ=40000 +#CONFIG_ESP_SDIO_CHECKSUM=y +CONFIG_ESP_PKT_STATS=y + +CONFIG_SLAVE_LWIP_ENABLED=y +CONFIG_HOST_USES_STATIC_NETIF=n +#CONFIG_LWIP_TCP_LOCAL_PORT_RANGE_START=49152 +#CONFIG_LWIP_TCP_LOCAL_PORT_RANGE_END=61439 +#CONFIG_LWIP_UDP_LOCAL_PORT_RANGE_START=49152 +#CONFIG_LWIP_UDP_LOCAL_PORT_RANGE_END=61439 + +# +# Use Hosted lib +# +CONFIG_ESP_WIFI_REMOTE_LIBRARY_HOSTED=y +CONFIG_H2S_WIFI_FLOW_CTRL=n +CONFIG_SDIO_DISABLE_GPIO_BASED_SLAVE_RESET=n + +# For V1.0B board +# CONFIG_BSP_BOARD_TYPE_SAMPLE_V10B=y +# CONFIG_ESP_SDIO_PIN_D0=49 +# CONFIG_ESP_SDIO_PIN_D1=50 + +# For V1.2 Board +CONFIG_BSP_BOARD_TYPE_SAMPLE_V12=y +CONFIG_ESP_SDIO_PIN_D0=14 +CONFIG_ESP_SDIO_PIN_D1=15 +CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y +CONFIG_ESP_CONSOLE_SECONDARY_NONE=y +CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED=y + +# Uncomment only one of the camera group of options + +CONFIG_CAMERA_SC2336=y +CONFIG_CAMERA_SC2336_AUTO_DETECT=y +CONFIG_CAMERA_SC2336_AUTO_DETECT_MIPI_INTERFACE_SENSOR=y +CONFIG_CAMERA_SC2336_MIPI_RAW8_1920x1080_30FPS=y + +# CONFIG_CAMERA_OV5647=y +# CONFIG_CAMERA_OV5647_AUTO_DETECT=y +# CONFIG_CAMERA_OV5647_MIPI_RAW10_1920x1080_30FPS=y +# CONFIG_CAMERA_OV5647_AUTO_DETECT_MIPI_INTERFACE_SENSOR=y diff --git a/examples/camera/standalone/sdkconfig.defaults.esp32s3 b/examples/camera/standalone/sdkconfig.defaults.esp32s3 new file mode 100644 index 0000000..f70bf93 --- /dev/null +++ b/examples/camera/standalone/sdkconfig.defaults.esp32s3 @@ -0,0 +1,42 @@ +# Defaults for ESP32S3 + +CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240=y +CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ=240 + +# +# Serial flasher config +# +CONFIG_ESPTOOLPY_FLASHMODE_QIO=y +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y + +CONFIG_ESP32S3_SPIRAM_SUPPORT=y +CONFIG_SPIRAM_MODE_OCT=y + +CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=0 + +# +# ESP32-S3-EYE console +# +CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y +CONFIG_ESP_CONSOLE_SECONDARY_NONE=y +CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED=y + +# +# Cache config +# +CONFIG_ESP32S3_INSTRUCTION_CACHE_32KB=y +CONFIG_ESP32S3_INSTRUCTION_CACHE_SIZE=0x8000 +CONFIG_ESP32S3_DATA_CACHE_64KB=y +CONFIG_ESP32S3_DATA_CACHE_SIZE=0x10000 + +# +# Camera Configuration +# +CONFIG_CAMERA_MODULE_ESP_S3_EYE=y +CONFIG_CAMERA_DMA_BUFFER_SIZE_MAX=16384 + +# +# GDB Stub +# +CONFIG_ESP_GDBSTUB_ENABLED=y +CONFIG_ESP_SYSTEM_PANIC_GDBSTUB=y