diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml deleted file mode 100644 index 51bbaec..0000000 --- a/.github/workflows/build.yaml +++ /dev/null @@ -1,28 +0,0 @@ -name: Build -on: - push: - branches: - - master - pull_request: - -jobs: - build: - strategy: - matrix: - target: [esp32s3, linux] - - runs-on: ubuntu-latest - - steps: - - name: Checkout repo - uses: actions/checkout@v2 - with: - submodules: 'recursive' - - - name: Build - run: | - docker run -v $PWD:/project -w /project -u 0 \ - -e HOME=/tmp -e WIFI_SSID=A -e WIFI_PASSWORD=B -e OPENAI_API_KEY=X \ - espressif/idf:latest \ - /bin/bash -c 'idf.py --preview set-target ${{ matrix.target }} && idf.py build' - shell: bash diff --git a/.github/workflows/clang-format-check.yml b/.github/workflows/clang-format-check.yml deleted file mode 100644 index 4042212..0000000 --- a/.github/workflows/clang-format-check.yml +++ /dev/null @@ -1,13 +0,0 @@ -name: clang-format Check -on: [push, pull_request] -jobs: - formatting-check: - name: Formatting Check - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Run clang-format style check for C/C++/Protobuf programs. - uses: jidicula/clang-format-action@v4.13.0 - with: - clang-format-version: '17' - check-path: 'src' diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index 84ad50c..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,36 +0,0 @@ -cmake_minimum_required(VERSION 3.19) - -if(NOT IDF_TARGET STREQUAL linux) - if(NOT DEFINED ENV{WIFI_SSID} OR NOT DEFINED ENV{WIFI_PASSWORD}) - message(FATAL_ERROR "Env variables WIFI_SSID and WIFI_PASSWORD must be set") - endif() - - add_compile_definitions(WIFI_SSID="$ENV{WIFI_SSID}") - add_compile_definitions(WIFI_PASSWORD="$ENV{WIFI_PASSWORD}") -endif() - -if(NOT DEFINED ENV{OPENAI_API_KEY}) - message(FATAL_ERROR "Env variable OPENAI_API_KEY must be set") -endif() - -if(DEFINED ENV{LOG_DATACHANNEL_MESSAGES}) - add_compile_definitions(LOG_DATACHANNEL_MESSAGES="1") -endif() - -add_compile_definitions(OPENAI_API_KEY="$ENV{OPENAI_API_KEY}") -add_compile_definitions(OPENAI_REALTIMEAPI="https://api.openai.com/v1/realtime?model=gpt-4o-mini-realtime-preview-2024-12-17") - -set(COMPONENTS src) -set(EXTRA_COMPONENT_DIRS "src" "components/srtp" "components/peer" "components/esp-libopus") - -if(IDF_TARGET STREQUAL linux) - add_compile_definitions(LINUX_BUILD=1) - list(APPEND EXTRA_COMPONENT_DIRS - $ENV{IDF_PATH}/examples/protocols/linux_stubs/esp_stubs - "components/esp-protocols/common_components/linux_compat/esp_timer" - "components/esp-protocols/common_components/linux_compat/freertos" - ) -endif() - -include($ENV{IDF_PATH}/tools/cmake/project.cmake) -project(src) diff --git a/README.md b/README.md index e0faf87..60e638e 100644 --- a/README.md +++ b/README.md @@ -1,46 +1,11 @@ -# Open RealtimeAPI Embedded SDK +# OpenAI Realtime API Embedded SDK -# Table of Contents +This repo provides instructions on how to use the [OpenAI Realtime API](https://platform.openai.com/docs/guides/realtime) on Microcontrollers and Embedded Platforms. This repository will be expanded as we support more platforms. -- [Docs](#docs) -- [Installation](#installation) -- [Usage](#usage) +### Espressif -## Platform/Device Support +Espressif provides an example of using the Realtime API with their [esp_webrtc](https://github.com/espressif/esp-webrtc-solution/tree/main/solutions/openai_demo) library. -This SDK has been developed tested on a `esp32s3` and `linux`. You don't need any physical hardware -to run this SDK. You can use it from Linux directly. +## License -To use it on hardware purchase either of these microcontrollers. Others may work, but this is what -has been developed against. - -* [Freenove ESP32-S3-WROOM](https://www.amazon.com/gp/product/B0BMQ8F7FN) -* [Sonatino - ESP32-S3 Audio Development Board](https://www.amazon.com/gp/product/B0BVY8RJNP) - -You can get a ESP32S3 for much less money on eBay/AliExpress. - -## Installation - -Call `set-target` with the platform you are targetting. Today only `linux` and `esp32s3` are supported. -* `idf.py set-target esp32s3` - -Configure device specific settings. None needed at this time -* `idf.py menuconfig` - -Set your Wifi SSID + Password as env variables -* `export WIFI_SSID=foo` -* `export WIFI_PASSWORD=bar` -* `export OPENAI_API_KEY=bing` - -Build -* `idf.py build` - -If you built for `esp32s3` run the following to flash to the device -* `sudo -E idf.py flash` - -If you built for `linux` you can run the binary directly -* `./build/src.elf` - -See [build.yaml](.github/workflows/build.yaml) for a Docker command to do this all in one step. - -## Usage +MIT diff --git a/components/esp-libopus b/components/esp-libopus deleted file mode 160000 index 260b16c..0000000 --- a/components/esp-libopus +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 260b16cc540285e84220c709de1bb2796ea7ec41 diff --git a/components/esp-protocols b/components/esp-protocols deleted file mode 160000 index b65cff3..0000000 --- a/components/esp-protocols +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b65cff3a0b7c666501884c9eae4416616ce26493 diff --git a/components/peer/CMakeLists.txt b/components/peer/CMakeLists.txt deleted file mode 100644 index d965443..0000000 --- a/components/peer/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -set(PEER_PROJECT_PATH "../../deps/libpeer") -file(GLOB CODES "${PEER_PROJECT_PATH}/src/*.c") - -idf_component_register( - SRCS ${CODES} - INCLUDE_DIRS "${PEER_PROJECT_PATH}/src" - REQUIRES mbedtls srtp json esp_netif -) - -# Disable KeepAlives -file(READ ${CMAKE_CURRENT_SOURCE_DIR}/../../deps/libpeer/src/config.h INPUT_CONTENT) -string(REPLACE "#define KEEPALIVE_CONNCHECK 10000" "#define KEEPALIVE_CONNCHECK 0" MODIFIED_CONTENT ${INPUT_CONTENT}) -file(WRITE ${CMAKE_CURRENT_SOURCE_DIR}/../../deps/libpeer/src/config.h ${MODIFIED_CONTENT}) - -if(NOT IDF_TARGET STREQUAL linux) - add_definitions("-DESP32 -DCONFIG_USE_LWIP=1 -DCONFIG_AUDIO_BUFFER_SIZE=8096 -DCONFIG_DATA_BUFFER_SIZE=102400 -D__BYTE_ORDER=__LITTLE_ENDIAN") -endif() - -add_definitions("-DHTTP_DO_NOT_USE_CUSTOM_CONFIG -DMQTT_DO_NOT_USE_CUSTOM_CONFIG -DCONFIG_USE_USRSCTP=0 -DDISABLE_PEER_SIGNALING=0") diff --git a/components/srtp b/components/srtp deleted file mode 160000 index f39a4a2..0000000 --- a/components/srtp +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f39a4a2c70a4b7d09f9f852a58b5ea39b9c1182c diff --git a/dependencies.lock b/dependencies.lock deleted file mode 100644 index 9b916ea..0000000 --- a/dependencies.lock +++ /dev/null @@ -1,10 +0,0 @@ -dependencies: - idf: - source: - type: idf - version: 5.5.0 -direct_dependencies: -- idf -manifest_hash: 655e4ae2c4a00dc0e9b6d66aa2a909e40e81c57604a11f1553343408aeddfb41 -target: esp32s3 -version: 2.0.0 diff --git a/deps/libpeer b/deps/libpeer deleted file mode 160000 index 3c8a8be..0000000 --- a/deps/libpeer +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3c8a8be11ab573e2d5aa75473bb85cbf5902f0ee diff --git a/partitions.csv b/partitions.csv deleted file mode 100644 index 03d5d13..0000000 --- a/partitions.csv +++ /dev/null @@ -1,6 +0,0 @@ -# ESP-IDF Partition Table -# Name, Type, SubType, Offset, Size, Flags -nvs, data, nvs, 0x9000, 0x6000, -phy_init, data, phy, 0xf000, 0x1000, -factory, app, factory, 0x10000, 0x180000, - diff --git a/sdkconfig.defaults b/sdkconfig.defaults deleted file mode 100644 index 16712df..0000000 --- a/sdkconfig.defaults +++ /dev/null @@ -1,31 +0,0 @@ -# ESP Event Loop on Linux -CONFIG_ESP_EVENT_POST_FROM_ISR=n -CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR=n - -# Disable TLS verification -# Production needs to include specific cert chain you care about -CONFIG_ESP_TLS_INSECURE=y -CONFIG_ESP_TLS_SKIP_SERVER_CERT_VERIFY=y - -# Enable DTLS-SRTP -CONFIG_MBEDTLS_SSL_PROTO_DTLS=y - -# libpeer requires large stack allocations -CONFIG_ESP_MAIN_TASK_STACK_SIZE=16384 - -# Defaults to partitions.csv -CONFIG_PARTITION_TABLE_CUSTOM=y - -# Set highest CPU Freq -CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y - -CONFIG_SPIRAM=y -CONFIG_SPIRAM_MODE_OCT=y - -# Disable Watchdog -# CONFIG_ESP_INT_WDT is not set -# CONFIG_ESP_TASK_WDT_EN is not set - -# Enable Compiler Optimization -CONFIG_COMPILER_OPTIMIZATION_PERF=y -CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE=y diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt deleted file mode 100644 index df7f328..0000000 --- a/src/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -set(COMMON_SRC "webrtc.cpp" "main.cpp" "http.cpp") - -if(IDF_TARGET STREQUAL linux) - idf_component_register( - SRCS ${COMMON_SRC} - REQUIRES peer esp-libopus esp_http_client) -else() - idf_component_register( - SRCS ${COMMON_SRC} "wifi.cpp" "media.cpp" - REQUIRES driver esp_wifi nvs_flash peer esp_psram esp-libopus esp_http_client) -endif() - -idf_component_get_property(lib peer COMPONENT_LIB) -target_compile_options(${lib} PRIVATE -Wno-error=restrict) -target_compile_options(${lib} PRIVATE -Wno-error=stringop-truncation) - -idf_component_get_property(lib srtp COMPONENT_LIB) -target_compile_options(${lib} PRIVATE -Wno-error=incompatible-pointer-types) - -idf_component_get_property(lib esp-libopus COMPONENT_LIB) -target_compile_options(${lib} PRIVATE -Wno-error=maybe-uninitialized) -target_compile_options(${lib} PRIVATE -Wno-error=stringop-overread) diff --git a/src/http.cpp b/src/http.cpp deleted file mode 100644 index e38c673..0000000 --- a/src/http.cpp +++ /dev/null @@ -1,97 +0,0 @@ -#include -#include -#include - -#include "main.h" - -#ifndef MIN -#define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#endif - -esp_err_t oai_http_event_handler(esp_http_client_event_t *evt) { - static int output_len; - switch (evt->event_id) { - case HTTP_EVENT_REDIRECT: - ESP_LOGD(LOG_TAG, "HTTP_EVENT_REDIRECT"); - esp_http_client_set_header(evt->client, "From", "user@example.com"); - esp_http_client_set_header(evt->client, "Accept", "text/html"); - esp_http_client_set_redirection(evt->client); - break; - case HTTP_EVENT_ERROR: - ESP_LOGD(LOG_TAG, "HTTP_EVENT_ERROR"); - break; - case HTTP_EVENT_ON_CONNECTED: - ESP_LOGD(LOG_TAG, "HTTP_EVENT_ON_CONNECTED"); - break; - case HTTP_EVENT_HEADER_SENT: - ESP_LOGD(LOG_TAG, "HTTP_EVENT_HEADER_SENT"); - break; - case HTTP_EVENT_ON_HEADER: - ESP_LOGD(LOG_TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", - evt->header_key, evt->header_value); - break; - case HTTP_EVENT_ON_DATA: { - ESP_LOGD(LOG_TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len); - if (esp_http_client_is_chunked_response(evt->client)) { - ESP_LOGE(LOG_TAG, "Chunked HTTP response not supported"); -#ifndef LINUX_BUILD - esp_restart(); -#endif - } - - if (output_len == 0 && evt->user_data) { - memset(evt->user_data, 0, MAX_HTTP_OUTPUT_BUFFER); - } - - // If user_data buffer is configured, copy the response into the buffer - int copy_len = 0; - if (evt->user_data) { - // The last byte in evt->user_data is kept for the NULL character in - // case of out-of-bound access. - copy_len = MIN(evt->data_len, (MAX_HTTP_OUTPUT_BUFFER - output_len)); - if (copy_len) { - memcpy(((char *)evt->user_data) + output_len, evt->data, copy_len); - } - } - output_len += copy_len; - - break; - } - case HTTP_EVENT_ON_FINISH: - ESP_LOGD(LOG_TAG, "HTTP_EVENT_ON_FINISH"); - output_len = 0; - break; - case HTTP_EVENT_DISCONNECTED: - ESP_LOGI(LOG_TAG, "HTTP_EVENT_DISCONNECTED"); - output_len = 0; - break; - } - return ESP_OK; -} - -void oai_http_request(char *offer, char *answer) { - esp_http_client_config_t config; - memset(&config, 0, sizeof(esp_http_client_config_t)); - - config.url = OPENAI_REALTIMEAPI; - config.event_handler = oai_http_event_handler; - config.user_data = answer; - - snprintf(answer, MAX_HTTP_OUTPUT_BUFFER, "Bearer %s", OPENAI_API_KEY); - - esp_http_client_handle_t client = esp_http_client_init(&config); - esp_http_client_set_method(client, HTTP_METHOD_POST); - esp_http_client_set_header(client, "Content-Type", "application/sdp"); - esp_http_client_set_header(client, "Authorization", answer); - esp_http_client_set_post_field(client, offer, strlen(offer)); - - esp_err_t err = esp_http_client_perform(client); - if (err != ESP_OK || esp_http_client_get_status_code(client) != 201) { - ESP_LOGE(LOG_TAG, "Error perform http request %s", esp_err_to_name(err)); -#ifndef LINUX_BUILD - esp_restart(); -#endif - } - - esp_http_client_cleanup(client); -} diff --git a/src/idf_component.yml b/src/idf_component.yml deleted file mode 100644 index a494de4..0000000 --- a/src/idf_component.yml +++ /dev/null @@ -1,3 +0,0 @@ -dependencies: - idf: - version: ">=4.1.0" diff --git a/src/main.cpp b/src/main.cpp deleted file mode 100644 index bd84e2d..0000000 --- a/src/main.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "main.h" - -#include -#include -#include - -#ifndef LINUX_BUILD -#include "nvs_flash.h" - -extern "C" void app_main(void) { - 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_ERROR_CHECK(esp_event_loop_create_default()); - peer_init(); - oai_init_audio_capture(); - oai_init_audio_decoder(); - oai_wifi(); - oai_webrtc(); -} -#else -int main(void) { - ESP_ERROR_CHECK(esp_event_loop_create_default()); - peer_init(); - oai_webrtc(); -} -#endif diff --git a/src/main.h b/src/main.h deleted file mode 100644 index 198ef5c..0000000 --- a/src/main.h +++ /dev/null @@ -1,13 +0,0 @@ -#include - -#define LOG_TAG "realtimeapi-sdk" -#define MAX_HTTP_OUTPUT_BUFFER 2048 - -void oai_wifi(void); -void oai_init_audio_capture(void); -void oai_init_audio_decoder(void); -void oai_init_audio_encoder(); -void oai_send_audio(PeerConnection *peer_connection); -void oai_audio_decode(uint8_t *data, size_t size); -void oai_webrtc(); -void oai_http_request(char *offer, char *answer); diff --git a/src/media.cpp b/src/media.cpp deleted file mode 100644 index 3ca44c0..0000000 --- a/src/media.cpp +++ /dev/null @@ -1,144 +0,0 @@ -#include -#include - -#include "main.h" - -#define OPUS_OUT_BUFFER_SIZE 1276 // 1276 bytes is recommended by opus_encode -#define SAMPLE_RATE 8000 -#define BUFFER_SAMPLES 320 - -#define MCLK_PIN 0 -#define DAC_BCLK_PIN 15 -#define DAC_LRCLK_PIN 16 -#define DAC_DATA_PIN 17 -#define ADC_BCLK_PIN 38 -#define ADC_LRCLK_PIN 39 -#define ADC_DATA_PIN 40 - -#define OPUS_ENCODER_BITRATE 30000 -#define OPUS_ENCODER_COMPLEXITY 0 - -void oai_init_audio_capture() { - i2s_config_t i2s_config_out = { - .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX), - .sample_rate = SAMPLE_RATE, - .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, - .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, - .communication_format = I2S_COMM_FORMAT_I2S_MSB, - .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, - .dma_buf_count = 8, - .dma_buf_len = BUFFER_SAMPLES, - .use_apll = 1, - .tx_desc_auto_clear = true, - }; - if (i2s_driver_install(I2S_NUM_0, &i2s_config_out, 0, NULL) != ESP_OK) { - printf("Failed to configure I2S driver for audio output"); - return; - } - - i2s_pin_config_t pin_config_out = { - .mck_io_num = MCLK_PIN, - .bck_io_num = DAC_BCLK_PIN, - .ws_io_num = DAC_LRCLK_PIN, - .data_out_num = DAC_DATA_PIN, - .data_in_num = I2S_PIN_NO_CHANGE, - }; - if (i2s_set_pin(I2S_NUM_0, &pin_config_out) != ESP_OK) { - printf("Failed to set I2S pins for audio output"); - return; - } - i2s_zero_dma_buffer(I2S_NUM_0); - - i2s_config_t i2s_config_in = { - .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX), - .sample_rate = SAMPLE_RATE, - .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, - .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT, - .communication_format = I2S_COMM_FORMAT_I2S_MSB, - .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, - .dma_buf_count = 8, - .dma_buf_len = BUFFER_SAMPLES, - .use_apll = 1, - }; - if (i2s_driver_install(I2S_NUM_1, &i2s_config_in, 0, NULL) != ESP_OK) { - printf("Failed to configure I2S driver for audio input"); - return; - } - - i2s_pin_config_t pin_config_in = { - .mck_io_num = MCLK_PIN, - .bck_io_num = ADC_BCLK_PIN, - .ws_io_num = ADC_LRCLK_PIN, - .data_out_num = I2S_PIN_NO_CHANGE, - .data_in_num = ADC_DATA_PIN, - }; - if (i2s_set_pin(I2S_NUM_1, &pin_config_in) != ESP_OK) { - printf("Failed to set I2S pins for audio input"); - return; - } -} - -opus_int16 *output_buffer = NULL; -OpusDecoder *opus_decoder = NULL; - -void oai_init_audio_decoder() { - int decoder_error = 0; - opus_decoder = opus_decoder_create(SAMPLE_RATE, 2, &decoder_error); - if (decoder_error != OPUS_OK) { - printf("Failed to create OPUS decoder"); - return; - } - - output_buffer = (opus_int16 *)malloc(BUFFER_SAMPLES * sizeof(opus_int16)); -} - -void oai_audio_decode(uint8_t *data, size_t size) { - int decoded_size = - opus_decode(opus_decoder, data, size, output_buffer, BUFFER_SAMPLES, 0); - - if (decoded_size > 0) { - size_t bytes_written = 0; - i2s_write(I2S_NUM_0, output_buffer, BUFFER_SAMPLES * sizeof(opus_int16), - &bytes_written, portMAX_DELAY); - } -} - -OpusEncoder *opus_encoder = NULL; -opus_int16 *encoder_input_buffer = NULL; -uint8_t *encoder_output_buffer = NULL; - -void oai_init_audio_encoder() { - int encoder_error; - opus_encoder = opus_encoder_create(SAMPLE_RATE, 1, OPUS_APPLICATION_VOIP, - &encoder_error); - if (encoder_error != OPUS_OK) { - printf("Failed to create OPUS encoder"); - return; - } - - if (opus_encoder_init(opus_encoder, SAMPLE_RATE, 1, OPUS_APPLICATION_VOIP) != - OPUS_OK) { - printf("Failed to initialize OPUS encoder"); - return; - } - - opus_encoder_ctl(opus_encoder, OPUS_SET_BITRATE(OPUS_ENCODER_BITRATE)); - opus_encoder_ctl(opus_encoder, OPUS_SET_COMPLEXITY(OPUS_ENCODER_COMPLEXITY)); - opus_encoder_ctl(opus_encoder, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE)); - encoder_input_buffer = (opus_int16 *)malloc(BUFFER_SAMPLES); - encoder_output_buffer = (uint8_t *)malloc(OPUS_OUT_BUFFER_SIZE); -} - -void oai_send_audio(PeerConnection *peer_connection) { - size_t bytes_read = 0; - - i2s_read(I2S_NUM_1, encoder_input_buffer, BUFFER_SAMPLES, &bytes_read, - portMAX_DELAY); - - auto encoded_size = - opus_encode(opus_encoder, encoder_input_buffer, BUFFER_SAMPLES / 2, - encoder_output_buffer, OPUS_OUT_BUFFER_SIZE); - - peer_connection_send_audio(peer_connection, encoder_output_buffer, - encoded_size); -} diff --git a/src/webrtc.cpp b/src/webrtc.cpp deleted file mode 100644 index bc1782d..0000000 --- a/src/webrtc.cpp +++ /dev/null @@ -1,113 +0,0 @@ -#ifndef LINUX_BUILD -#include -#include -#endif - -#include -#include -#include - -#include "main.h" - -#define TICK_INTERVAL 15 -#define GREETING \ - "{\"type\": \"response.create\", \"response\": {\"modalities\": " \ - "[\"audio\", \"text\"], \"instructions\": \"Say 'How can I help?.'\"}}" - -PeerConnection *peer_connection = NULL; - -#ifndef LINUX_BUILD -StaticTask_t task_buffer; -void oai_send_audio_task(void *user_data) { - oai_init_audio_encoder(); - - while (1) { - oai_send_audio(peer_connection); - vTaskDelay(pdMS_TO_TICKS(TICK_INTERVAL)); - } -} -#endif - -static void oai_ondatachannel_onmessage_task(char *msg, size_t len, - void *userdata, uint16_t sid) { -#ifdef LOG_DATACHANNEL_MESSAGES - ESP_LOGI(LOG_TAG, "DataChannel Message: %s", msg); -#endif -} - -static void oai_ondatachannel_onopen_task(void *userdata) { - if (peer_connection_create_datachannel(peer_connection, DATA_CHANNEL_RELIABLE, - 0, 0, (char *)"oai-events", - (char *)"") != -1) { - ESP_LOGI(LOG_TAG, "DataChannel created"); - peer_connection_datachannel_send(peer_connection, (char *)GREETING, - strlen(GREETING)); - } else { - ESP_LOGE(LOG_TAG, "Failed to create DataChannel"); - } -} - -static void oai_onconnectionstatechange_task(PeerConnectionState state, - void *user_data) { - ESP_LOGI(LOG_TAG, "PeerConnectionState: %s", - peer_connection_state_to_string(state)); - - if (state == PEER_CONNECTION_DISCONNECTED || - state == PEER_CONNECTION_CLOSED) { -#ifndef LINUX_BUILD - esp_restart(); -#endif - } else if (state == PEER_CONNECTION_CONNECTED) { -#ifndef LINUX_BUILD - StackType_t *stack_memory = (StackType_t *)heap_caps_malloc( - 20000 * sizeof(StackType_t), MALLOC_CAP_SPIRAM); - xTaskCreateStaticPinnedToCore(oai_send_audio_task, "audio_publisher", 20000, - NULL, 7, stack_memory, &task_buffer, 0); -#endif - } -} - -static void oai_on_icecandidate_task(char *description, void *user_data) { - char local_buffer[MAX_HTTP_OUTPUT_BUFFER + 1] = {0}; - oai_http_request(description, local_buffer); - peer_connection_set_remote_description(peer_connection, local_buffer); -} - -void oai_webrtc() { - PeerConfiguration peer_connection_config = { - .ice_servers = {}, - .audio_codec = CODEC_OPUS, - .video_codec = CODEC_NONE, - .datachannel = DATA_CHANNEL_STRING, - .onaudiotrack = [](uint8_t *data, size_t size, void *userdata) -> void { -#ifndef LINUX_BUILD - oai_audio_decode(data, size); -#endif - }, - .onvideotrack = NULL, - .on_request_keyframe = NULL, - .user_data = NULL, - }; - - peer_connection = peer_connection_create(&peer_connection_config); - if (peer_connection == NULL) { - ESP_LOGE(LOG_TAG, "Failed to create peer connection"); -#ifndef LINUX_BUILD - esp_restart(); -#endif - } - - peer_connection_oniceconnectionstatechange(peer_connection, - oai_onconnectionstatechange_task); - peer_connection_onicecandidate(peer_connection, oai_on_icecandidate_task); - peer_connection_ondatachannel(peer_connection, - oai_ondatachannel_onmessage_task, - oai_ondatachannel_onopen_task, NULL); - - peer_connection_create_offer(peer_connection); - - while (1) { - peer_connection_loop(peer_connection); - vTaskDelay(pdMS_TO_TICKS(TICK_INTERVAL)); - } -} diff --git a/src/wifi.cpp b/src/wifi.cpp deleted file mode 100644 index 6e56ee4..0000000 --- a/src/wifi.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include "main.h" - -static bool g_wifi_connected = false; - -static void oai_event_handler(void *arg, esp_event_base_t event_base, - int32_t event_id, void *event_data) { - static int s_retry_num = 0; - if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { - if (s_retry_num < 5) { - esp_wifi_connect(); - s_retry_num++; - ESP_LOGI(LOG_TAG, "retry to connect to the AP"); - } - ESP_LOGI(LOG_TAG, "connect to the AP fail"); - } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { - ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; - ESP_LOGI(LOG_TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip)); - g_wifi_connected = true; - } -} - -void oai_wifi(void) { - ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, - &oai_event_handler, NULL)); - ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, - &oai_event_handler, NULL)); - - ESP_ERROR_CHECK(esp_netif_init()); - esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta(); - assert(sta_netif); - - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - ESP_ERROR_CHECK(esp_wifi_init(&cfg)); - ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); - ESP_ERROR_CHECK(esp_wifi_start()); - - ESP_LOGI(LOG_TAG, "Connecting to WiFi SSID: %s", WIFI_SSID); - wifi_config_t wifi_config; - memset(&wifi_config, 0, sizeof(wifi_config)); - strncpy((char *)wifi_config.sta.ssid, (char *)WIFI_SSID, - sizeof(wifi_config.sta.ssid)); - strncpy((char *)wifi_config.sta.password, (char *)WIFI_PASSWORD, - sizeof(wifi_config.sta.password)); - - ESP_ERROR_CHECK(esp_wifi_set_config( - static_cast(ESP_IF_WIFI_STA), &wifi_config)); - ESP_ERROR_CHECK(esp_wifi_connect()); - - // block until we get an IP address - while (!g_wifi_connected) { - vTaskDelay(pdMS_TO_TICKS(200)); - } -}