Memory issues resolved
This commit is contained in:
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"C_Cpp.intelliSenseEngine": "default",
|
||||
"idf.espIdfPathWin": "C:\\Users\\brent.RPX\\esp\\v5.3.1\\esp-idf",
|
||||
"idf.espIdfPathWin": "C:\\esp\\frameworks\\esp-idf-v5.3.3",
|
||||
"idf.openOcdConfigs": [
|
||||
"board/esp32-wrover-kit-3.3v.cfg"
|
||||
],
|
||||
"idf.portWin": "COM4",
|
||||
"idf.toolsPathWin": "C:\\Users\\brent.RPX\\.espressif\\tools",
|
||||
"idf.toolsPathWin": "C:\\esp\\frameworks\\esp-idf-v5.3.3\\tools\\tools",
|
||||
"idf.flashType": "UART",
|
||||
"files.associations": {
|
||||
"esp_system.h": "c",
|
||||
|
||||
13
.vscode/tasks.json
vendored
Normal file
13
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "ESP-IDF: Build (RAM) + Sync to Host",
|
||||
"type": "shell",
|
||||
"command": "bash -lc 'source ${IDF_PATH:-/opt/esp/idf}/export.sh && idf.py build && rsync -a --delete \"$PWD/build/\" /host_build/'",
|
||||
"group": { "kind": "build", "isDefault": true },
|
||||
"problemMatcher": "$gcc"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
147
CLAUDE.md
Normal file
147
CLAUDE.md
Normal file
@@ -0,0 +1,147 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
SoundShot is an ESP32-based embedded system that appears to be an IMU-based angle measurement device with Bluetooth connectivity and GUI interface. The project uses ESP-IDF (Espressif IoT Development Framework) and can be developed using either Docker containers or native tooling.
|
||||
|
||||
## Build System & Development Environment
|
||||
|
||||
### Docker Development (Recommended)
|
||||
- **Setup**: Run `setup-env.bat` to create Python virtual environment and install dependencies
|
||||
- **Container**: Uses ESP-IDF Docker containers with VS Code Dev Containers extension
|
||||
- **Build**: `idf.py build` (inside container)
|
||||
|
||||
### Native Development
|
||||
- **Build**: `idf.py build` (requires ESP-IDF setup)
|
||||
- **Configuration**: Uses `settings.json` for project parameters
|
||||
- **CMake**: Project uses ESP-IDF CMake build system with custom configuration generator
|
||||
|
||||
### Essential Commands
|
||||
|
||||
```bash
|
||||
# Environment setup (Windows only)
|
||||
setup-env.bat
|
||||
|
||||
# Build firmware
|
||||
idf.py build
|
||||
|
||||
# Flash device (using custom Python tool)
|
||||
flash.bat
|
||||
python flash_tool/flash.py
|
||||
|
||||
# Monitor serial output
|
||||
monitor.bat
|
||||
python flash_tool/monitor.py
|
||||
|
||||
# Combined flash and monitor
|
||||
flashmon.bat
|
||||
|
||||
# Clean build
|
||||
idf.py fullclean
|
||||
```
|
||||
|
||||
## Project Architecture
|
||||
|
||||
### Core Components
|
||||
|
||||
**Main Application (`main/`)**
|
||||
- `main.c` - Entry point, IMU processing, task coordination
|
||||
- `system.c/h` - System state management, event handling, calibration
|
||||
- `gui.c/h` - LVGL-based user interface (3 modes: main, menu, bubble)
|
||||
- `bt_app.c/h` - Bluetooth connectivity and communication
|
||||
- `lsm6dsv.c/h` - LSM6DSV IMU sensor driver
|
||||
- `keypad.c/h` - Physical button input handling
|
||||
- `bubble.c/h` - Bubble level visualization
|
||||
|
||||
**Key Features**
|
||||
- Real-time angle measurement with configurable filtering
|
||||
- Bluetooth connectivity for data transmission
|
||||
- LVGL-based GUI with multiple display modes
|
||||
- Zero-angle calibration system
|
||||
- Low-pass filtering with adaptive alpha coefficients
|
||||
|
||||
### Hardware Configuration
|
||||
|
||||
**GPIO Pins** (`gpio.h`)
|
||||
- LEDs, LCD backlight, power control
|
||||
- I2C for IMU communication (SCL=22, SDA=21)
|
||||
- Serial communication and button inputs
|
||||
|
||||
**IMU Processing**
|
||||
- LSM6DSV 6-axis IMU sensor
|
||||
- Angle computation from accelerometer data (XZ, YZ, XY planes)
|
||||
- Configurable filtering with `FILTER_COEFF` and `MAX_INDICATION_ANGLE`
|
||||
- Primary axis selection via `PRIMARY_AXIS` define
|
||||
|
||||
## Configuration Management
|
||||
|
||||
### settings.json Structure
|
||||
```json
|
||||
{
|
||||
"project_name": "soundshot",
|
||||
"chip": "esp32",
|
||||
"port": "COM3",
|
||||
"monitor_baud": 115200,
|
||||
"flash_baud": 460800,
|
||||
"flash_mode": "dio",
|
||||
"flash_freq": "40m",
|
||||
"flash_size": "2MB"
|
||||
}
|
||||
```
|
||||
|
||||
**Configuration Flow**
|
||||
1. `settings.json` → `prepare_config.py` → `project_settings.cmake`
|
||||
2. CMake includes generated settings during build
|
||||
3. Flash tools read `settings.json` for esptool parameters
|
||||
|
||||
### Build Dependencies
|
||||
|
||||
**ESP-IDF Components**
|
||||
- `driver` - GPIO, I2C, peripherals
|
||||
- `esp_lcd` - Display drivers
|
||||
- `lvgl` + `esp_lvgl_port` - GUI framework
|
||||
- `esp_timer` - High resolution timers
|
||||
- `nvs_flash` - Non-volatile storage
|
||||
- `bt` - Bluetooth stack
|
||||
|
||||
**Managed Components**
|
||||
- LVGL 9.x with ESP32 port integration
|
||||
- Located in `managed_components/`
|
||||
|
||||
## Development Guidelines
|
||||
|
||||
### Code Organization
|
||||
- Hardware abstraction in dedicated modules (`lsm6dsv.c`, `keypad.c`)
|
||||
- System state centralized in `system.c` with event-based communication
|
||||
- GUI logic separated from business logic
|
||||
- Bluetooth handled as separate subsystem
|
||||
|
||||
### Key Constants & Tuning Parameters
|
||||
- `MAX_INDICATION_ANGLE` (5.0°) - Angle measurement limit
|
||||
- `FILTER_COEFF` (0.4) - Low-pass filter strength
|
||||
- `PRIMARY_AXIS` - Main measurement axis selection
|
||||
- Filter alpha computed dynamically based on input magnitude
|
||||
|
||||
### Testing & Debugging
|
||||
- Serial monitoring at 115200 baud via `monitor.bat`
|
||||
- Debug prints use ESP-IDF logging (`ESP_LOGI`, `ESP_LOGW`, etc.)
|
||||
- IMU data output can be enabled via conditional compilation blocks
|
||||
|
||||
### Flash Memory Layout
|
||||
- Bootloader: 0x1000
|
||||
- Partition table: 0x8000
|
||||
- OTA data: 0x11000
|
||||
- Application: 0x20000
|
||||
|
||||
## Common Development Patterns
|
||||
|
||||
When modifying this codebase:
|
||||
1. IMU changes: Update filtering parameters in `main.c` and constants in `system.h`
|
||||
2. GUI changes: Modify LVGL code in `gui.c`, add new modes to `GuiMode_t` enum
|
||||
3. Bluetooth changes: Update message handlers in `bt_app.c`
|
||||
4. Hardware changes: Update pin definitions in `gpio.h` and initialization in `main.c`
|
||||
5. Build changes: Modify component dependencies in `main/CMakeLists.txt`
|
||||
|
||||
Always test flashing and monitoring tools after hardware configuration changes.
|
||||
550
main/bt_app.c
550
main/bt_app.c
@@ -24,6 +24,9 @@
|
||||
#include "esp_gap_bt_api.h"
|
||||
#include "esp_a2dp_api.h"
|
||||
#include "esp_avrc_api.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "nvs.h"
|
||||
#include "esp_timer.h"
|
||||
|
||||
/* log tags */
|
||||
#define BT_AV_TAG "BT_AV"
|
||||
@@ -37,6 +40,12 @@
|
||||
#define APP_RC_CT_TL_GET_CAPS (0)
|
||||
#define APP_RC_CT_TL_RN_VOLUME_CHANGE (1)
|
||||
|
||||
/* NVS storage constants */
|
||||
#define NVS_NAMESPACE "bt_devices"
|
||||
#define NVS_KEY_PREFIX "device_"
|
||||
#define NVS_KEY_COUNT "dev_count"
|
||||
#define MAX_PAIRED_DEVICES 10
|
||||
|
||||
enum {
|
||||
BT_APP_STACK_UP_EVT = 0x0000, /* event for stack up */
|
||||
BT_APP_HEART_BEAT_EVT = 0xff00, /* event for heart beat */
|
||||
@@ -99,6 +108,25 @@ static void bt_app_av_sm_hdlr(uint16_t event, void *param);
|
||||
/* utils for transfer BLuetooth Deveice Address into string form */
|
||||
static char *bda2str(esp_bd_addr_t bda, char *str, size_t size);
|
||||
|
||||
/* NVS storage functions for paired devices */
|
||||
typedef struct {
|
||||
esp_bd_addr_t bda;
|
||||
char name[ESP_BT_GAP_MAX_BDNAME_LEN + 1];
|
||||
uint32_t last_connected;
|
||||
} paired_device_t;
|
||||
|
||||
static esp_err_t nvs_save_paired_device(const paired_device_t *device);
|
||||
static esp_err_t nvs_load_paired_devices(paired_device_t *devices, size_t *count);
|
||||
static esp_err_t nvs_remove_paired_device(esp_bd_addr_t bda);
|
||||
static bool nvs_is_device_known(esp_bd_addr_t bda);
|
||||
static esp_err_t nvs_get_known_device_count(size_t *count);
|
||||
static esp_err_t nvs_try_connect_known_devices(void);
|
||||
static void nvs_debug_print_known_devices(void);
|
||||
static esp_err_t nvs_add_discovered_device(esp_bd_addr_t bda, const char *name);
|
||||
static esp_err_t nvs_update_connection_timestamp(esp_bd_addr_t bda);
|
||||
static esp_err_t nvs_try_connect_all_known_devices(void);
|
||||
static esp_err_t nvs_try_next_known_device(void);
|
||||
|
||||
/* A2DP application state machine handler for each state */
|
||||
static void bt_app_av_state_unconnected_hdlr(uint16_t event, void *param);
|
||||
static void bt_app_av_state_connecting_hdlr(uint16_t event, void *param);
|
||||
@@ -121,6 +149,468 @@ static int s_connecting_intv = 0; /* count of heart
|
||||
static uint32_t s_pkt_cnt = 0; /* count of packets */
|
||||
static esp_avrc_rn_evt_cap_mask_t s_avrc_peer_rn_cap; /* AVRC target notification event capability bit mask */
|
||||
static TimerHandle_t s_tmr;
|
||||
static int s_current_device_index = -1; /* index of device currently being attempted */
|
||||
static paired_device_t s_known_devices[MAX_PAIRED_DEVICES]; /* cached list of known devices */
|
||||
static size_t s_known_device_count = 0; /* count of cached known devices */
|
||||
|
||||
/*********************************
|
||||
* NVS STORAGE FUNCTION DEFINITIONS
|
||||
********************************/
|
||||
|
||||
static esp_err_t nvs_save_paired_device(const paired_device_t *device)
|
||||
{
|
||||
nvs_handle_t nvs_handle;
|
||||
esp_err_t ret;
|
||||
size_t count = 0;
|
||||
char key[32];
|
||||
|
||||
if (!device) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
ret = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs_handle);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(BT_AV_TAG, "Error opening NVS handle: %s", esp_err_to_name(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Get current device count
|
||||
size_t required_size = sizeof(size_t);
|
||||
nvs_get_blob(nvs_handle, NVS_KEY_COUNT, &count, &required_size);
|
||||
|
||||
// Check if device already exists
|
||||
paired_device_t existing_devices[MAX_PAIRED_DEVICES];
|
||||
size_t existing_count = MAX_PAIRED_DEVICES;
|
||||
nvs_load_paired_devices(existing_devices, &existing_count);
|
||||
|
||||
int device_index = -1;
|
||||
for (int i = 0; i < existing_count; i++) {
|
||||
if (memcmp(existing_devices[i].bda, device->bda, ESP_BD_ADDR_LEN) == 0) {
|
||||
device_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If device not found and we have space, add it
|
||||
if (device_index == -1) {
|
||||
if (count >= MAX_PAIRED_DEVICES) {
|
||||
ESP_LOGW(BT_AV_TAG, "Maximum paired devices reached");
|
||||
nvs_close(nvs_handle);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
device_index = count;
|
||||
count++;
|
||||
}
|
||||
|
||||
// Save device data
|
||||
snprintf(key, sizeof(key), "%s%d", NVS_KEY_PREFIX, device_index);
|
||||
ret = nvs_set_blob(nvs_handle, key, device, sizeof(paired_device_t));
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(BT_AV_TAG, "Error saving device: %s", esp_err_to_name(ret));
|
||||
nvs_close(nvs_handle);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Save device count
|
||||
ret = nvs_set_blob(nvs_handle, NVS_KEY_COUNT, &count, sizeof(size_t));
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(BT_AV_TAG, "Error saving device count: %s", esp_err_to_name(ret));
|
||||
nvs_close(nvs_handle);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = nvs_commit(nvs_handle);
|
||||
nvs_close(nvs_handle);
|
||||
|
||||
char bda_str[18];
|
||||
ESP_LOGI(BT_AV_TAG, "Saved paired device: %s (%s)",
|
||||
device->name, bda2str(device->bda, bda_str, sizeof(bda_str)));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t nvs_load_paired_devices(paired_device_t *devices, size_t *count)
|
||||
{
|
||||
nvs_handle_t nvs_handle;
|
||||
esp_err_t ret;
|
||||
size_t device_count = 0;
|
||||
char key[32];
|
||||
|
||||
if (!devices || !count || *count == 0) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
ret = nvs_open(NVS_NAMESPACE, NVS_READONLY, &nvs_handle);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGD(BT_AV_TAG, "NVS namespace not found (first run): %s", esp_err_to_name(ret));
|
||||
*count = 0;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Get device count
|
||||
size_t required_size = sizeof(size_t);
|
||||
ret = nvs_get_blob(nvs_handle, NVS_KEY_COUNT, &device_count, &required_size);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGD(BT_AV_TAG, "No paired devices found");
|
||||
nvs_close(nvs_handle);
|
||||
*count = 0;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Load each device
|
||||
size_t loaded = 0;
|
||||
for (int i = 0; i < device_count && loaded < *count; i++) {
|
||||
snprintf(key, sizeof(key), "%s%d", NVS_KEY_PREFIX, i);
|
||||
required_size = sizeof(paired_device_t);
|
||||
ret = nvs_get_blob(nvs_handle, key, &devices[loaded], &required_size);
|
||||
if (ret == ESP_OK) {
|
||||
loaded++;
|
||||
}
|
||||
}
|
||||
|
||||
nvs_close(nvs_handle);
|
||||
*count = loaded;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static bool nvs_is_device_known(esp_bd_addr_t bda)
|
||||
{
|
||||
paired_device_t devices[MAX_PAIRED_DEVICES];
|
||||
size_t count = MAX_PAIRED_DEVICES;
|
||||
|
||||
if (nvs_load_paired_devices(devices, &count) != ESP_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (memcmp(devices[i].bda, bda, ESP_BD_ADDR_LEN) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static esp_err_t nvs_try_connect_known_devices(void)
|
||||
{
|
||||
paired_device_t devices[MAX_PAIRED_DEVICES];
|
||||
size_t count = MAX_PAIRED_DEVICES;
|
||||
|
||||
esp_err_t ret = nvs_load_paired_devices(devices, &count);
|
||||
if (ret != ESP_OK || count == 0) {
|
||||
ESP_LOGI(BT_AV_TAG, "No known devices to connect to");
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
// Filter out devices that have never been connected (last_connected == 0)
|
||||
// and sort by last_connected timestamp (most recent first)
|
||||
paired_device_t connected_devices[MAX_PAIRED_DEVICES];
|
||||
size_t connected_count = 0;
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (devices[i].last_connected > 0) {
|
||||
connected_devices[connected_count++] = devices[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (connected_count == 0) {
|
||||
ESP_LOGI(BT_AV_TAG, "No previously connected devices to reconnect to");
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
// Sort connected devices by last_connected timestamp (most recent first)
|
||||
for (int i = 0; i < connected_count - 1; i++) {
|
||||
for (int j = i + 1; j < connected_count; j++) {
|
||||
if (connected_devices[i].last_connected < connected_devices[j].last_connected) {
|
||||
paired_device_t temp = connected_devices[i];
|
||||
connected_devices[i] = connected_devices[j];
|
||||
connected_devices[j] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try to connect to the most recently connected device
|
||||
char bda_str[18];
|
||||
ESP_LOGI(BT_AV_TAG, "Attempting to connect to previously connected device: %s (%s)",
|
||||
connected_devices[0].name, bda2str(connected_devices[0].bda, bda_str, sizeof(bda_str)));
|
||||
|
||||
memcpy(s_peer_bda, connected_devices[0].bda, ESP_BD_ADDR_LEN);
|
||||
strcpy((char*)s_peer_bdname, connected_devices[0].name);
|
||||
|
||||
ret = esp_a2d_source_connect(s_peer_bda);
|
||||
if (ret == ESP_OK) {
|
||||
s_a2d_state = APP_AV_STATE_CONNECTING;
|
||||
s_connecting_intv = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void nvs_debug_print_known_devices(void)
|
||||
{
|
||||
paired_device_t devices[MAX_PAIRED_DEVICES];
|
||||
size_t count = MAX_PAIRED_DEVICES;
|
||||
|
||||
esp_err_t ret = nvs_load_paired_devices(devices, &count);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(BT_AV_TAG, "Failed to load devices for debug: %s", esp_err_to_name(ret));
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGI(BT_AV_TAG, "=== Known Paired Devices (%d) ===", count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
char bda_str[18];
|
||||
ESP_LOGI(BT_AV_TAG, "%d: %s (%s) last_connected: %lu",
|
||||
i + 1, devices[i].name,
|
||||
bda2str(devices[i].bda, bda_str, sizeof(bda_str)),
|
||||
devices[i].last_connected);
|
||||
}
|
||||
ESP_LOGI(BT_AV_TAG, "=== End Device List ===");
|
||||
}
|
||||
|
||||
static esp_err_t nvs_remove_paired_device(esp_bd_addr_t bda)
|
||||
{
|
||||
paired_device_t devices[MAX_PAIRED_DEVICES];
|
||||
size_t count = MAX_PAIRED_DEVICES;
|
||||
|
||||
esp_err_t ret = nvs_load_paired_devices(devices, &count);
|
||||
if (ret != ESP_OK || count == 0) {
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
// Find device to remove
|
||||
int found_index = -1;
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (memcmp(devices[i].bda, bda, ESP_BD_ADDR_LEN) == 0) {
|
||||
found_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found_index == -1) {
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
// Open NVS for writing
|
||||
nvs_handle_t nvs_handle;
|
||||
ret = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs_handle);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Shift remaining devices
|
||||
for (int i = found_index; i < count - 1; i++) {
|
||||
devices[i] = devices[i + 1];
|
||||
}
|
||||
count--;
|
||||
|
||||
// Save updated devices
|
||||
char key[32];
|
||||
for (int i = 0; i < count; i++) {
|
||||
snprintf(key, sizeof(key), "%s%d", NVS_KEY_PREFIX, i);
|
||||
ret = nvs_set_blob(nvs_handle, key, &devices[i], sizeof(paired_device_t));
|
||||
if (ret != ESP_OK) {
|
||||
nvs_close(nvs_handle);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the last device slot
|
||||
snprintf(key, sizeof(key), "%s%d", NVS_KEY_PREFIX, (int)count);
|
||||
nvs_erase_key(nvs_handle, key);
|
||||
|
||||
// Update count
|
||||
ret = nvs_set_blob(nvs_handle, NVS_KEY_COUNT, &count, sizeof(size_t));
|
||||
if (ret == ESP_OK) {
|
||||
ret = nvs_commit(nvs_handle);
|
||||
}
|
||||
|
||||
nvs_close(nvs_handle);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t nvs_get_known_device_count(size_t *count)
|
||||
{
|
||||
if (!count) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nvs_handle_t nvs_handle;
|
||||
esp_err_t ret = nvs_open(NVS_NAMESPACE, NVS_READONLY, &nvs_handle);
|
||||
if (ret != ESP_OK) {
|
||||
*count = 0;
|
||||
return ESP_OK; // No devices is not an error
|
||||
}
|
||||
|
||||
size_t required_size = sizeof(size_t);
|
||||
ret = nvs_get_blob(nvs_handle, NVS_KEY_COUNT, count, &required_size);
|
||||
if (ret != ESP_OK) {
|
||||
*count = 0;
|
||||
ret = ESP_OK; // No devices is not an error
|
||||
}
|
||||
|
||||
nvs_close(nvs_handle);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t nvs_add_discovered_device(esp_bd_addr_t bda, const char *name)
|
||||
{
|
||||
if (!bda || !name) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// Check if device is already known
|
||||
if (nvs_is_device_known(bda)) {
|
||||
char bda_str[18];
|
||||
ESP_LOGD(BT_AV_TAG, "Device %s (%s) already known, skipping",
|
||||
name, bda2str(bda, bda_str, sizeof(bda_str)));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Create paired_device_t structure for discovered device
|
||||
paired_device_t device;
|
||||
memcpy(device.bda, bda, ESP_BD_ADDR_LEN);
|
||||
strncpy(device.name, name, ESP_BT_GAP_MAX_BDNAME_LEN);
|
||||
device.name[ESP_BT_GAP_MAX_BDNAME_LEN] = '\0';
|
||||
device.last_connected = 0; // Never connected, set to 0
|
||||
|
||||
// Save to NVS
|
||||
esp_err_t ret = nvs_save_paired_device(&device);
|
||||
if (ret == ESP_OK) {
|
||||
char bda_str[18];
|
||||
ESP_LOGI(BT_AV_TAG, "Added discovered device to NVS: %s (%s)",
|
||||
device.name, bda2str(device.bda, bda_str, sizeof(bda_str)));
|
||||
} else {
|
||||
ESP_LOGE(BT_AV_TAG, "Failed to save discovered device: %s", esp_err_to_name(ret));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t nvs_update_connection_timestamp(esp_bd_addr_t bda)
|
||||
{
|
||||
if (!bda) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
paired_device_t devices[MAX_PAIRED_DEVICES];
|
||||
size_t count = MAX_PAIRED_DEVICES;
|
||||
|
||||
esp_err_t ret = nvs_load_paired_devices(devices, &count);
|
||||
if (ret != ESP_OK || count == 0) {
|
||||
ESP_LOGW(BT_AV_TAG, "No devices found to update timestamp");
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
// Find the device and update its timestamp
|
||||
int device_index = -1;
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (memcmp(devices[i].bda, bda, ESP_BD_ADDR_LEN) == 0) {
|
||||
device_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (device_index == -1) {
|
||||
ESP_LOGW(BT_AV_TAG, "Device not found in NVS to update timestamp");
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
// Update timestamp to current time (using ESP timer)
|
||||
devices[device_index].last_connected = (uint32_t)(esp_timer_get_time() / 1000000); // Convert to seconds
|
||||
|
||||
// Save updated device
|
||||
ret = nvs_save_paired_device(&devices[device_index]);
|
||||
if (ret == ESP_OK) {
|
||||
char bda_str[18];
|
||||
ESP_LOGI(BT_AV_TAG, "Updated connection timestamp for device: %s (%s)",
|
||||
devices[device_index].name, bda2str(devices[device_index].bda, bda_str, sizeof(bda_str)));
|
||||
} else {
|
||||
ESP_LOGE(BT_AV_TAG, "Failed to update connection timestamp: %s", esp_err_to_name(ret));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t nvs_try_connect_all_known_devices(void)
|
||||
{
|
||||
// Load all known devices into cache
|
||||
s_known_device_count = MAX_PAIRED_DEVICES;
|
||||
esp_err_t ret = nvs_load_paired_devices(s_known_devices, &s_known_device_count);
|
||||
if (ret != ESP_OK || s_known_device_count == 0) {
|
||||
ESP_LOGI(BT_AV_TAG, "No known devices to connect to");
|
||||
s_current_device_index = -1;
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
// Sort devices by last_connected timestamp (most recent first)
|
||||
// This prioritizes recently connected devices
|
||||
for (int i = 0; i < s_known_device_count - 1; i++) {
|
||||
for (int j = i + 1; j < s_known_device_count; j++) {
|
||||
if (s_known_devices[i].last_connected < s_known_devices[j].last_connected) {
|
||||
paired_device_t temp = s_known_devices[i];
|
||||
s_known_devices[i] = s_known_devices[j];
|
||||
s_known_devices[j] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start with the first (most recently connected) device
|
||||
s_current_device_index = 0;
|
||||
|
||||
char bda_str[18];
|
||||
ESP_LOGI(BT_AV_TAG, "Attempting to connect to device %d/%d: %s (%s)",
|
||||
s_current_device_index + 1, (int)s_known_device_count,
|
||||
s_known_devices[s_current_device_index].name,
|
||||
bda2str(s_known_devices[s_current_device_index].bda, bda_str, sizeof(bda_str)));
|
||||
|
||||
memcpy(s_peer_bda, s_known_devices[s_current_device_index].bda, ESP_BD_ADDR_LEN);
|
||||
strcpy((char*)s_peer_bdname, s_known_devices[s_current_device_index].name);
|
||||
|
||||
ret = esp_a2d_source_connect(s_peer_bda);
|
||||
if (ret == ESP_OK) {
|
||||
s_a2d_state = APP_AV_STATE_CONNECTING;
|
||||
s_connecting_intv = 0;
|
||||
} else {
|
||||
ESP_LOGE(BT_AV_TAG, "Failed to initiate connection: %s", esp_err_to_name(ret));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t nvs_try_next_known_device(void)
|
||||
{
|
||||
if (s_current_device_index < 0 || s_known_device_count == 0) {
|
||||
ESP_LOGI(BT_AV_TAG, "No more devices to try");
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
// Move to next device
|
||||
s_current_device_index++;
|
||||
if (s_current_device_index >= s_known_device_count) {
|
||||
ESP_LOGI(BT_AV_TAG, "Exhausted all known devices, starting discovery...");
|
||||
s_current_device_index = -1;
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
char bda_str[18];
|
||||
ESP_LOGI(BT_AV_TAG, "Attempting to connect to next device %d/%d: %s (%s)",
|
||||
s_current_device_index + 1, (int)s_known_device_count,
|
||||
s_known_devices[s_current_device_index].name,
|
||||
bda2str(s_known_devices[s_current_device_index].bda, bda_str, sizeof(bda_str)));
|
||||
|
||||
memcpy(s_peer_bda, s_known_devices[s_current_device_index].bda, ESP_BD_ADDR_LEN);
|
||||
strcpy((char*)s_peer_bdname, s_known_devices[s_current_device_index].name);
|
||||
|
||||
esp_err_t ret = esp_a2d_source_connect(s_peer_bda);
|
||||
if (ret == ESP_OK) {
|
||||
s_a2d_state = APP_AV_STATE_CONNECTING;
|
||||
s_connecting_intv = 0;
|
||||
} else {
|
||||
ESP_LOGE(BT_AV_TAG, "Failed to initiate connection: %s", esp_err_to_name(ret));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*********************************
|
||||
* STATIC FUNCTION DEFINITIONS
|
||||
@@ -212,16 +702,22 @@ static void filter_inquiry_scan_result(esp_bt_gap_cb_param_t *param)
|
||||
/* search for target device in its Extended Inqury Response */
|
||||
if (eir) {
|
||||
get_name_from_eir(eir, s_peer_bdname, NULL);
|
||||
//if (strcmp((char *)s_peer_bdname, TARGET_DEVICE_NAME) == 0)
|
||||
{
|
||||
ESP_LOGI(BT_AV_TAG, "Found a target device, address %s, name %s", bda_str, s_peer_bdname);
|
||||
|
||||
// Save discovered audio device to NVS (but don't connect to it)
|
||||
nvs_add_discovered_device(param->disc_res.bda, (char *)s_peer_bdname);
|
||||
|
||||
ESP_LOGI(BT_AV_TAG, "Found audio device, address %s, name %s (saved to NVS, not connecting)",
|
||||
bda_str, s_peer_bdname);
|
||||
|
||||
// Don't automatically connect to discovered devices - just save them to NVS
|
||||
// The old code would set s_a2d_state = APP_AV_STATE_DISCOVERED and connect
|
||||
// Now we just continue discovery to find more devices
|
||||
s_a2d_state = APP_AV_STATE_DISCOVERED;
|
||||
memcpy(s_peer_bda, param->disc_res.bda, ESP_BD_ADDR_LEN);
|
||||
ESP_LOGI(BT_AV_TAG, "Cancel device discovery ...");
|
||||
esp_bt_gap_cancel_discovery();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param)
|
||||
{
|
||||
@@ -344,9 +840,19 @@ static void bt_av_hdl_stack_evt(uint16_t event, void *p_param)
|
||||
esp_bt_gap_set_scan_mode(ESP_BT_NON_CONNECTABLE, ESP_BT_NON_DISCOVERABLE);
|
||||
esp_bt_gap_get_device_name();
|
||||
|
||||
ESP_LOGI(BT_AV_TAG, "Starting device discovery...");
|
||||
// Print list of saved devices from NVS
|
||||
nvs_debug_print_known_devices();
|
||||
|
||||
// Try to connect to all known devices sequentially
|
||||
esp_err_t connect_ret = nvs_try_connect_all_known_devices();
|
||||
if (connect_ret != ESP_OK) {
|
||||
// No known devices found, start discovery to find new devices
|
||||
ESP_LOGI(BT_AV_TAG, "No known devices found, starting discovery...");
|
||||
s_a2d_state = APP_AV_STATE_DISCOVERING;
|
||||
esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, 10, 0);
|
||||
} else {
|
||||
ESP_LOGI(BT_AV_TAG, "Attempting to connect to known devices...");
|
||||
}
|
||||
|
||||
/* create and start heart beat timer */
|
||||
do {
|
||||
@@ -365,6 +871,7 @@ static void bt_av_hdl_stack_evt(uint16_t event, void *p_param)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param)
|
||||
{
|
||||
bt_app_work_dispatch(bt_app_av_sm_hdlr, event, param, sizeof(esp_a2d_cb_param_t), NULL);
|
||||
@@ -585,6 +1092,10 @@ void audio_producer_task(void *pvParameters) {
|
||||
/* generate some random noise to simulate source audio */
|
||||
static int32_t bt_app_a2d_data_cb(uint8_t *data, int32_t len)
|
||||
{
|
||||
if (data == NULL || len <= 0 || audio_stream_buf == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t bytes_read = xStreamBufferReceive(audio_stream_buf, data, len, 0);
|
||||
|
||||
if (bytes_read < len) {
|
||||
@@ -697,12 +1208,14 @@ static void bt_app_av_state_unconnected_hdlr(uint16_t event, void *param)
|
||||
case ESP_A2D_MEDIA_CTRL_ACK_EVT:
|
||||
break;
|
||||
case BT_APP_HEART_BEAT_EVT: {
|
||||
uint8_t *bda = s_peer_bda;
|
||||
ESP_LOGI(BT_AV_TAG, "a2dp connecting to peer: %02x:%02x:%02x:%02x:%02x:%02x",
|
||||
bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]);
|
||||
esp_a2d_source_connect(s_peer_bda);
|
||||
s_a2d_state = APP_AV_STATE_CONNECTING;
|
||||
s_connecting_intv = 0;
|
||||
// Try to connect to known devices, or start discovery if none available
|
||||
esp_err_t connect_ret = nvs_try_connect_all_known_devices();
|
||||
if (connect_ret != ESP_OK) {
|
||||
// No known devices, start discovery
|
||||
ESP_LOGI(BT_AV_TAG, "No known devices available, starting discovery...");
|
||||
s_a2d_state = APP_AV_STATE_DISCOVERING;
|
||||
esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, 10, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ESP_A2D_REPORT_SNK_DELAY_VALUE_EVT: {
|
||||
@@ -729,9 +1242,18 @@ static void bt_app_av_state_connecting_hdlr(uint16_t event, void *param)
|
||||
ESP_LOGI(BT_AV_TAG, "a2dp connected");
|
||||
s_a2d_state = APP_AV_STATE_CONNECTED;
|
||||
s_media_state = APP_AV_MEDIA_STATE_IDLE;
|
||||
|
||||
// Update connection timestamp for this device
|
||||
nvs_update_connection_timestamp(s_peer_bda);
|
||||
} else if (a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_DISCONNECTED) {
|
||||
ESP_LOGI(BT_AV_TAG, "Connection failed, trying next device...");
|
||||
// Try next known device before giving up
|
||||
esp_err_t next_ret = nvs_try_next_known_device();
|
||||
if (next_ret != ESP_OK) {
|
||||
// No more devices to try, go to unconnected state
|
||||
s_a2d_state = APP_AV_STATE_UNCONNECTED;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ESP_A2D_AUDIO_STATE_EVT:
|
||||
@@ -744,7 +1266,13 @@ static void bt_app_av_state_connecting_hdlr(uint16_t event, void *param)
|
||||
* when connecting lasts more than 2 heart beat intervals.
|
||||
*/
|
||||
if (++s_connecting_intv >= 2) {
|
||||
ESP_LOGI(BT_AV_TAG, "Connection timeout, trying next device...");
|
||||
// Try next known device before giving up
|
||||
esp_err_t next_ret = nvs_try_next_known_device();
|
||||
if (next_ret != ESP_OK) {
|
||||
// No more devices to try, go to unconnected state
|
||||
s_a2d_state = APP_AV_STATE_UNCONNECTED;
|
||||
}
|
||||
s_connecting_intv = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -58,7 +58,7 @@ typedef struct
|
||||
lv_obj_t *obj;
|
||||
} menu_context_t;
|
||||
|
||||
static void gui_task(void);
|
||||
static void gui_task(void *pvParameters);
|
||||
static void createBubble(lv_obj_t * scr);
|
||||
static void build_scrollable_menu(void);
|
||||
static void currentFocusIndex(menu_context_t *ctx);
|
||||
@@ -513,8 +513,9 @@ static void build_scrollable_menu(void) {
|
||||
}
|
||||
|
||||
|
||||
static void gui_task(void)
|
||||
static void gui_task(void *pvParameters)
|
||||
{
|
||||
(void)pvParameters; // Unused parameter
|
||||
system_subscribe(xTaskGetCurrentTaskHandle());
|
||||
|
||||
// Grab queue handle
|
||||
@@ -528,6 +529,7 @@ static void gui_task(void)
|
||||
//lv_obj_add_flag(_menu, LV_OBJ_FLAG_HIDDEN);
|
||||
UNLOCK();
|
||||
|
||||
ESP_LOGI(TAG, "Start GUI Task...");
|
||||
while (1)
|
||||
{
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
|
||||
#if LV_USE_STDLIB_MALLOC == LV_STDLIB_BUILTIN
|
||||
/*Size of the memory available for `lv_malloc()` in bytes (>= 2kB)*/
|
||||
#define LV_MEM_SIZE (64 * 1024U) /*[bytes]*/
|
||||
#define LV_MEM_SIZE (32 * 1024U) /*[bytes]*/
|
||||
|
||||
/*Size of the memory expand for `lv_malloc()` in bytes*/
|
||||
#define LV_MEM_POOL_EXPAND_SIZE 0
|
||||
|
||||
29
main/main.c
29
main/main.c
@@ -13,6 +13,7 @@
|
||||
|
||||
#include "esp_system.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/timers.h"
|
||||
@@ -30,10 +31,20 @@
|
||||
|
||||
|
||||
|
||||
static const char *TAG = "main";
|
||||
|
||||
/*********************************
|
||||
* STATIC FUNCTION DECLARATIONS
|
||||
********************************/
|
||||
|
||||
static void print_heap_info(const char* tag) {
|
||||
size_t free_heap = esp_get_free_heap_size();
|
||||
size_t min_free_heap = esp_get_minimum_free_heap_size();
|
||||
size_t largest_block = heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT);
|
||||
|
||||
ESP_LOGI(TAG, "[%s] Free heap: %zu bytes, Min free: %zu bytes, Largest block: %zu bytes",
|
||||
tag, free_heap, min_free_heap, largest_block);
|
||||
}
|
||||
typedef struct {
|
||||
float alpha; // smoothing factor, 0<alpha<1
|
||||
float prev_output; // y[n-1]
|
||||
@@ -88,7 +99,6 @@ static inline float LPF_Update(LowPassFilter *f, float input) {
|
||||
/*********************************
|
||||
* STATIC VARIABLE DEFINITIONS
|
||||
********************************/
|
||||
static const char *TAG = "main";
|
||||
|
||||
/*********************************
|
||||
* STATIC FUNCTION DEFINITIONS
|
||||
@@ -247,6 +257,7 @@ static void imu_task(void *pvParameters) {
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
print_heap_info("STARTUP");
|
||||
|
||||
/* initialize NVS — it is used to store PHY calibration data */
|
||||
esp_err_t ret = nvs_flash_init();
|
||||
@@ -255,27 +266,27 @@ void app_main(void)
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK(ret);
|
||||
print_heap_info("POST_NVS");
|
||||
|
||||
init_gpio();
|
||||
print_heap_info("POST_GPIO");
|
||||
|
||||
system_init();
|
||||
|
||||
|
||||
print_heap_info("POST_SYSTEM");
|
||||
|
||||
// Initialize IMU
|
||||
ESP_ERROR_CHECK(lsm6dsv_init(22, 21)); // SCL = IO14, SDA = IO15
|
||||
|
||||
|
||||
print_heap_info("POST_IMU");
|
||||
|
||||
// Create IMU task
|
||||
TaskHandle_t h = xTaskCreate(imu_task, "imu_task", 4096, NULL, 5, NULL);
|
||||
|
||||
|
||||
TaskHandle_t h = xTaskCreate(imu_task, "imu_task", 2048, NULL, 5, NULL);
|
||||
print_heap_info("POST_IMU_TASK");
|
||||
|
||||
bt_app_init();
|
||||
print_heap_info("POST_BLUETOOTH");
|
||||
|
||||
gui_start();
|
||||
|
||||
print_heap_info("POST_GUI");
|
||||
gpio_set_level(PIN_NUM_LED_2, 1);
|
||||
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ void system_init(void)
|
||||
_systemState.zeroAngle = 0.0f;
|
||||
_systemState.primaryAxis = PRIMARY_AXIS;
|
||||
|
||||
EventGroupHandle_t evt = xEventGroupCreate();
|
||||
_systemEvent = xEventGroupCreate();
|
||||
|
||||
_eventManager.count = 0;
|
||||
_eventManager.mutex = xSemaphoreCreateMutex();
|
||||
|
||||
Reference in New Issue
Block a user