Initial Commit

This commit is contained in:
Sean DuBois
2024-11-14 15:54:24 -05:00
commit c0a0e2f97f
23 changed files with 683 additions and 0 deletions

9
.clang-format Normal file
View File

@@ -0,0 +1,9 @@
BasedOnStyle: Google
IndentWidth: 2
ColumnLimit: 80
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
BreakBeforeBraces: Attach
DerivePointerAlignment: false
PointerAlignment: Right

28
.github/workflows/build.yaml vendored Normal file
View File

@@ -0,0 +1,28 @@
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

View File

@@ -0,0 +1,13 @@
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'

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
build
sdkconfig
sdkconfig.old
managed_components

12
.gitmodules vendored Normal file
View File

@@ -0,0 +1,12 @@
[submodule "components/srtp"]
path = components/srtp
url = https://git@github.com/sepfy/esp_ports
[submodule "deps/libpeer"]
path = deps/libpeer
url = https://github.com/sean-der/libpeer
[submodule "components/esp-libopus"]
path = components/esp-libopus
url = https://github.com/XasWorks/esp-libopus.git
[submodule "components/esp-protocols"]
path = components/esp-protocols
url = https://github.com/espressif/esp-protocols.git

35
CMakeLists.txt Normal file
View File

@@ -0,0 +1,35 @@
cmake_minimum_required(VERSION 3.19)
# Audio Sending is implemented, but not performant enough yet
add_compile_definitions(SEND_AUDIO=0)
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()
add_compile_definitions(OPENAI_API_KEY="$ENV{OPENAI_API_KEY}")
add_compile_definitions(OPENAI_REALTIMEAPI="https://api.openai.com/v1/realtime?model=gpt-4o-realtime-preview-2024-10-01")
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)

48
README.md Normal file
View File

@@ -0,0 +1,48 @@
# Open RealtimeAPI Embedded SDK
# Table of Contents
- [Docs](#docs)
- [Installation](#installation)
- [Usage](#usage)
## Platform/Device Support
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.
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
`protoc` must be in your path with `protobufc` installed.
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

View File

@@ -0,0 +1,24 @@
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 building of usrsctp
file(READ ${CMAKE_CURRENT_SOURCE_DIR}/../../deps/libpeer/src/config.h INPUT_CONTENT)
string(REPLACE "#define HAVE_USRSCTP" "" MODIFIED_CONTENT ${INPUT_CONTENT})
file(WRITE ${CMAKE_CURRENT_SOURCE_DIR}/../../deps/libpeer/src/config.h ${MODIFIED_CONTENT})
# 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")
endif()
add_definitions("-DHTTP_DO_NOT_USE_CUSTOM_CONFIG -DMQTT_DO_NOT_USE_CUSTOM_CONFIG -DDISABLE_PEER_SIGNALING=true")

1
components/srtp Submodule

Submodule components/srtp added at f39a4a2c70

10
dependencies.lock Normal file
View File

@@ -0,0 +1,10 @@
dependencies:
idf:
source:
type: idf
version: 5.5.0
direct_dependencies:
- idf
manifest_hash: 655e4ae2c4a00dc0e9b6d66aa2a909e40e81c57604a11f1553343408aeddfb41
target: esp32s3
version: 2.0.0

1
deps/libpeer vendored Submodule

Submodule deps/libpeer added at 988ca19368

6
partitions.csv Normal file
View File

@@ -0,0 +1,6 @@
# 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,
1 # ESP-IDF Partition Table
2 # Name, Type, SubType, Offset, Size, Flags
3 nvs, data, nvs, 0x9000, 0x6000,
4 phy_init, data, phy, 0xf000, 0x1000,
5 factory, app, factory, 0x10000, 0x180000,

31
sdkconfig.defaults Normal file
View File

@@ -0,0 +1,31 @@
# 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=8192
# 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

22
src/CMakeLists.txt Normal file
View File

@@ -0,0 +1,22 @@
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)

97
src/http.cpp Normal file
View File

@@ -0,0 +1,97 @@
#include <esp_http_client.h>
#include <esp_log.h>
#include <string.h>
#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);
}

3
src/idf_component.yml Normal file
View File

@@ -0,0 +1,3 @@
dependencies:
idf:
version: ">=4.1.0"

32
src/main.cpp Normal file
View File

@@ -0,0 +1,32 @@
#include "main.h"
#include <esp_event.h>
#include <esp_log.h>
#include <peer.h>
#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

13
src/main.h Normal file
View File

@@ -0,0 +1,13 @@
#include <peer.h>
#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);

144
src/media.cpp Normal file
View File

@@ -0,0 +1,144 @@
#include <driver/i2s.h>
#include <opus.h>
#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);
}

87
src/webrtc.cpp Normal file
View File

@@ -0,0 +1,87 @@
#ifndef LINUX_BUILD
#include <driver/i2s.h>
#include <opus.h>
#endif
#include <esp_event.h>
#include <esp_log.h>
#include <string.h>
#include "main.h"
#define TICK_INTERVAL 15
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_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_NONE,
.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_create_offer(peer_connection);
while (1) {
peer_connection_loop(peer_connection);
vTaskDelay(pdMS_TO_TICKS(TICK_INTERVAL));
}
}

61
src/wifi.cpp Normal file
View File

@@ -0,0 +1,61 @@
#include <assert.h>
#include <esp_event.h>
#include <esp_log.h>
#include <esp_wifi.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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<wifi_interface_t>(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));
}
}