Memory issues resolved

This commit is contained in:
Brent Perteet
2025-08-20 15:46:17 +00:00
parent c4ca91bf84
commit 25f875b3b2
9 changed files with 736 additions and 35 deletions

View File

@@ -1,11 +1,11 @@
{ {
"C_Cpp.intelliSenseEngine": "default", "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": [ "idf.openOcdConfigs": [
"board/esp32-wrover-kit-3.3v.cfg" "board/esp32-wrover-kit-3.3v.cfg"
], ],
"idf.portWin": "COM4", "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", "idf.flashType": "UART",
"files.associations": { "files.associations": {
"esp_system.h": "c", "esp_system.h": "c",

13
.vscode/tasks.json vendored Normal file
View 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
View 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.

View File

@@ -24,6 +24,9 @@
#include "esp_gap_bt_api.h" #include "esp_gap_bt_api.h"
#include "esp_a2dp_api.h" #include "esp_a2dp_api.h"
#include "esp_avrc_api.h" #include "esp_avrc_api.h"
#include "nvs_flash.h"
#include "nvs.h"
#include "esp_timer.h"
/* log tags */ /* log tags */
#define BT_AV_TAG "BT_AV" #define BT_AV_TAG "BT_AV"
@@ -37,6 +40,12 @@
#define APP_RC_CT_TL_GET_CAPS (0) #define APP_RC_CT_TL_GET_CAPS (0)
#define APP_RC_CT_TL_RN_VOLUME_CHANGE (1) #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 { enum {
BT_APP_STACK_UP_EVT = 0x0000, /* event for stack up */ BT_APP_STACK_UP_EVT = 0x0000, /* event for stack up */
BT_APP_HEART_BEAT_EVT = 0xff00, /* event for heart beat */ 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 */ /* utils for transfer BLuetooth Deveice Address into string form */
static char *bda2str(esp_bd_addr_t bda, char *str, size_t size); 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 */ /* 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_unconnected_hdlr(uint16_t event, void *param);
static void bt_app_av_state_connecting_hdlr(uint16_t event, void *param); static void bt_app_av_state_connecting_hdlr(uint16_t event, void *param);
@@ -120,7 +148,469 @@ static int s_intv_cnt = 0; /* count of heart
static int s_connecting_intv = 0; /* count of heart beat intervals for connecting */ static int s_connecting_intv = 0; /* count of heart beat intervals for connecting */
static uint32_t s_pkt_cnt = 0; /* count of packets */ 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 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 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 * STATIC FUNCTION DEFINITIONS
@@ -212,14 +702,20 @@ static void filter_inquiry_scan_result(esp_bt_gap_cb_param_t *param)
/* search for target device in its Extended Inqury Response */ /* search for target device in its Extended Inqury Response */
if (eir) { if (eir) {
get_name_from_eir(eir, s_peer_bdname, NULL); get_name_from_eir(eir, s_peer_bdname, NULL);
//if (strcmp((char *)s_peer_bdname, TARGET_DEVICE_NAME) == 0)
{ // Save discovered audio device to NVS (but don't connect to it)
ESP_LOGI(BT_AV_TAG, "Found a target device, address %s, name %s", bda_str, s_peer_bdname); nvs_add_discovered_device(param->disc_res.bda, (char *)s_peer_bdname);
s_a2d_state = APP_AV_STATE_DISCOVERED;
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); memcpy(s_peer_bda, param->disc_res.bda, ESP_BD_ADDR_LEN);
ESP_LOGI(BT_AV_TAG, "Cancel device discovery ..."); ESP_LOGI(BT_AV_TAG, "Cancel device discovery ...");
esp_bt_gap_cancel_discovery(); esp_bt_gap_cancel_discovery();
}
} }
} }
@@ -238,7 +734,7 @@ static void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *pa
if (param->disc_st_chg.state == ESP_BT_GAP_DISCOVERY_STOPPED) { if (param->disc_st_chg.state == ESP_BT_GAP_DISCOVERY_STOPPED) {
if (s_a2d_state == APP_AV_STATE_DISCOVERED) { if (s_a2d_state == APP_AV_STATE_DISCOVERED) {
s_a2d_state = APP_AV_STATE_CONNECTING; s_a2d_state = APP_AV_STATE_CONNECTING;
ESP_LOGI(BT_AV_TAG, "Device discovery stopped."); ESP_LOGI(BT_AV_TAG, "Device discovery stopped.");
ESP_LOGI(BT_AV_TAG, "a2dp connecting to peer: %s", s_peer_bdname); ESP_LOGI(BT_AV_TAG, "a2dp connecting to peer: %s", s_peer_bdname);
/* connect source to peer device specified by Bluetooth Device Address */ /* connect source to peer device specified by Bluetooth Device Address */
esp_a2d_source_connect(s_peer_bda); esp_a2d_source_connect(s_peer_bda);
@@ -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_set_scan_mode(ESP_BT_NON_CONNECTABLE, ESP_BT_NON_DISCOVERABLE);
esp_bt_gap_get_device_name(); esp_bt_gap_get_device_name();
ESP_LOGI(BT_AV_TAG, "Starting device discovery..."); // Print list of saved devices from NVS
s_a2d_state = APP_AV_STATE_DISCOVERING; nvs_debug_print_known_devices();
esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, 10, 0);
// 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 */ /* create and start heart beat timer */
do { 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) 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); bt_app_work_dispatch(bt_app_av_sm_hdlr, event, param, sizeof(esp_a2d_cb_param_t), NULL);
@@ -585,7 +1092,11 @@ void audio_producer_task(void *pvParameters) {
/* generate some random noise to simulate source audio */ /* generate some random noise to simulate source audio */
static int32_t bt_app_a2d_data_cb(uint8_t *data, int32_t len) static int32_t bt_app_a2d_data_cb(uint8_t *data, int32_t len)
{ {
size_t bytes_read = xStreamBufferReceive(audio_stream_buf, data, len, 0); 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) { if (bytes_read < len) {
memset(data + bytes_read, 0, len - bytes_read); // fill silence memset(data + bytes_read, 0, len - bytes_read); // fill silence
@@ -697,12 +1208,14 @@ static void bt_app_av_state_unconnected_hdlr(uint16_t event, void *param)
case ESP_A2D_MEDIA_CTRL_ACK_EVT: case ESP_A2D_MEDIA_CTRL_ACK_EVT:
break; break;
case BT_APP_HEART_BEAT_EVT: { case BT_APP_HEART_BEAT_EVT: {
uint8_t *bda = s_peer_bda; // Try to connect to known devices, or start discovery if none available
ESP_LOGI(BT_AV_TAG, "a2dp connecting to peer: %02x:%02x:%02x:%02x:%02x:%02x", esp_err_t connect_ret = nvs_try_connect_all_known_devices();
bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); if (connect_ret != ESP_OK) {
esp_a2d_source_connect(s_peer_bda); // No known devices, start discovery
s_a2d_state = APP_AV_STATE_CONNECTING; ESP_LOGI(BT_AV_TAG, "No known devices available, starting discovery...");
s_connecting_intv = 0; s_a2d_state = APP_AV_STATE_DISCOVERING;
esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, 10, 0);
}
break; break;
} }
case ESP_A2D_REPORT_SNK_DELAY_VALUE_EVT: { case ESP_A2D_REPORT_SNK_DELAY_VALUE_EVT: {
@@ -729,8 +1242,17 @@ static void bt_app_av_state_connecting_hdlr(uint16_t event, void *param)
ESP_LOGI(BT_AV_TAG, "a2dp connected"); ESP_LOGI(BT_AV_TAG, "a2dp connected");
s_a2d_state = APP_AV_STATE_CONNECTED; s_a2d_state = APP_AV_STATE_CONNECTED;
s_media_state = APP_AV_MEDIA_STATE_IDLE; 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) { } else if (a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_DISCONNECTED) {
s_a2d_state = APP_AV_STATE_UNCONNECTED; 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; break;
} }
@@ -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. * when connecting lasts more than 2 heart beat intervals.
*/ */
if (++s_connecting_intv >= 2) { if (++s_connecting_intv >= 2) {
s_a2d_state = APP_AV_STATE_UNCONNECTED; 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; s_connecting_intv = 0;
} }
break; break;

View File

@@ -58,7 +58,7 @@ typedef struct
lv_obj_t *obj; lv_obj_t *obj;
} menu_context_t; } menu_context_t;
static void gui_task(void); static void gui_task(void *pvParameters);
static void createBubble(lv_obj_t * scr); static void createBubble(lv_obj_t * scr);
static void build_scrollable_menu(void); static void build_scrollable_menu(void);
static void currentFocusIndex(menu_context_t *ctx); 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()); system_subscribe(xTaskGetCurrentTaskHandle());
// Grab queue handle // Grab queue handle
@@ -528,6 +529,7 @@ static void gui_task(void)
//lv_obj_add_flag(_menu, LV_OBJ_FLAG_HIDDEN); //lv_obj_add_flag(_menu, LV_OBJ_FLAG_HIDDEN);
UNLOCK(); UNLOCK();
ESP_LOGI(TAG, "Start GUI Task...");
while (1) while (1)
{ {

View File

@@ -53,7 +53,7 @@
#if LV_USE_STDLIB_MALLOC == LV_STDLIB_BUILTIN #if LV_USE_STDLIB_MALLOC == LV_STDLIB_BUILTIN
/*Size of the memory available for `lv_malloc()` in bytes (>= 2kB)*/ /*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*/ /*Size of the memory expand for `lv_malloc()` in bytes*/
#define LV_MEM_POOL_EXPAND_SIZE 0 #define LV_MEM_POOL_EXPAND_SIZE 0

View File

@@ -13,6 +13,7 @@
#include "esp_system.h" #include "esp_system.h"
#include "esp_log.h" #include "esp_log.h"
#include "esp_heap_caps.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "freertos/timers.h" #include "freertos/timers.h"
@@ -30,10 +31,20 @@
static const char *TAG = "main";
/********************************* /*********************************
* STATIC FUNCTION DECLARATIONS * 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 { typedef struct {
float alpha; // smoothing factor, 0<alpha<1 float alpha; // smoothing factor, 0<alpha<1
float prev_output; // y[n-1] float prev_output; // y[n-1]
@@ -88,7 +99,6 @@ static inline float LPF_Update(LowPassFilter *f, float input) {
/********************************* /*********************************
* STATIC VARIABLE DEFINITIONS * STATIC VARIABLE DEFINITIONS
********************************/ ********************************/
static const char *TAG = "main";
/********************************* /*********************************
* STATIC FUNCTION DEFINITIONS * STATIC FUNCTION DEFINITIONS
@@ -247,6 +257,7 @@ static void imu_task(void *pvParameters) {
void app_main(void) void app_main(void)
{ {
print_heap_info("STARTUP");
/* initialize NVS — it is used to store PHY calibration data */ /* initialize NVS — it is used to store PHY calibration data */
esp_err_t ret = nvs_flash_init(); esp_err_t ret = nvs_flash_init();
@@ -255,27 +266,27 @@ void app_main(void)
ret = nvs_flash_init(); ret = nvs_flash_init();
} }
ESP_ERROR_CHECK(ret); ESP_ERROR_CHECK(ret);
print_heap_info("POST_NVS");
init_gpio(); init_gpio();
print_heap_info("POST_GPIO");
system_init(); system_init();
print_heap_info("POST_SYSTEM");
// Initialize IMU // Initialize IMU
ESP_ERROR_CHECK(lsm6dsv_init(22, 21)); // SCL = IO14, SDA = IO15 ESP_ERROR_CHECK(lsm6dsv_init(22, 21)); // SCL = IO14, SDA = IO15
print_heap_info("POST_IMU");
// Create IMU task // 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(); bt_app_init();
print_heap_info("POST_BLUETOOTH");
gui_start(); gui_start();
print_heap_info("POST_GUI");
gpio_set_level(PIN_NUM_LED_2, 1); gpio_set_level(PIN_NUM_LED_2, 1);

View File

@@ -11,7 +11,7 @@ void system_init(void)
_systemState.zeroAngle = 0.0f; _systemState.zeroAngle = 0.0f;
_systemState.primaryAxis = PRIMARY_AXIS; _systemState.primaryAxis = PRIMARY_AXIS;
EventGroupHandle_t evt = xEventGroupCreate(); _systemEvent = xEventGroupCreate();
_eventManager.count = 0; _eventManager.count = 0;
_eventManager.mutex = xSemaphoreCreateMutex(); _eventManager.mutex = xSemaphoreCreateMutex();

View File

@@ -2455,7 +2455,7 @@ CONFIG_LVGL_VERSION_PATCH=2
# #
# Examples # Examples
# #
CONFIG_LV_BUILD_EXAMPLES=y # CONFIG_LV_BUILD_EXAMPLES is not set
# end of Examples # end of Examples
# #