SeedStudioESP32S3 compatible only

This commit is contained in:
koenieeee
2026-02-03 23:17:49 +01:00
parent 61eab9b43d
commit 58ddc768f3
13 changed files with 4821 additions and 94 deletions
+6 -1
View File
@@ -4,7 +4,12 @@ cmake_minimum_required(VERSION 3.16.0)
set(EXTRA_COMPONENT_DIRS src)
# Use custom partition table with SPIFFS
set(PARTITION_CSV_PATH "${CMAKE_SOURCE_DIR}/partitions.csv")
# Use partitions-s3.csv for ESP32-S3 builds
if(IDF_TARGET STREQUAL "esp32s3")
set(PARTITION_CSV_PATH "${CMAKE_SOURCE_DIR}/partitions-s3.csv")
else()
set(PARTITION_CSV_PATH "${CMAKE_SOURCE_DIR}/partitions.csv")
endif()
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(esp32-wand-gateway)
+17 -5
View File
@@ -37,7 +37,12 @@ if [ -f "model.tflite" ]; then
echo ""
fi
# Set target to ESP32-S3
# Clean previous build to ensure fresh configuration
echo "Cleaning previous build configuration..."
#rm -rf build/
#rm -f sdkconfig sdkconfig.old
# Set target to ESP32-S3 (this will copy sdkconfig.esp32s3 to build/sdkconfig)
echo "Setting target to ESP32-S3..."
idf.py set-target esp32s3
@@ -49,7 +54,7 @@ export IDF_EXTRA_PARTITION_SUBTYPES=""
# Build the project
echo ""
echo "Building firmware..."
idf.py -D PARTITION_TABLE_FILENAME=partitions-s3.csv build
idf.py build
# Check if build succeeded
if [ $? -ne 0 ]; then
@@ -69,12 +74,19 @@ echo ""
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo ""
echo "Flashing firmware and SPIFFS partition..."
echo "(Using slower speed for VMware USB compatibility)"
echo ""
echo "Put device in bootloader mode:"
echo " 1. Hold BOOT button (tiny button near USB)"
echo " 2. Press and release RESET button"
echo " 3. Release BOOT button"
echo ""
read -p "Press Enter when ready to flash..."
echo "Waiting 3 seconds for device to enumerate..."
sleep 3
# Flash everything: bootloader, partition table, app, and SPIFFS
# Use --no-stub and slower baud for VMware USB passthrough compatibility
idf.py -p "$PORT" -b 115200 flash --no-stub
idf.py -p "$PORT" -b 115200 flash
# Flash SPIFFS partition with model if it exists
if [ -f "data/model.tflite" ]; then
+1
View File
@@ -9,6 +9,7 @@
#include "nimble/nimble_port_freertos.h"
#include "services/gap/ble_svc_gap.h"
#include "services/gatt/ble_svc_gatt.h"
#include "esp_bt.h"
#include "spell_detector.h"
#include "wand_commands.h"
#include "wand_protocol.h"
+2 -2
View File
@@ -1,8 +1,8 @@
#ifndef CONFIG_H
#define CONFIG_H
// USB HID Support - enabled for ESP32-S3 composite HID + CDC (logs)
#define USE_USB_HID_DEVICE 1
// USB HID Support - disabled to reduce interference with BLE/WiFi
#define USE_USB_HID_DEVICE 0
// Wand BLE UUIDs
#define WAND_SERVICE_UUID "57420001-587e-48a0-974c-544d6163c577"
+1
View File
@@ -48,6 +48,7 @@ struct IMUSample;
// Button state flags
#define BUTTON_ALL_PRESSED 0x0F
#define BUTTON_MIN_FOR_TRACKING 3 // Minimum buttons pressed to start tracking (3 out of 4)
// Macro system opcodes
#define MACRO_CONTROL 0x68
+19 -46
View File
@@ -928,17 +928,11 @@ CONFIG_ESPTOOLPY_MONITOR_BAUD=115200
#
# Partition Table
#
# default:
# CONFIG_PARTITION_TABLE_SINGLE_APP is not set
# default:
# CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE is not set
# default:
# CONFIG_PARTITION_TABLE_TWO_OTA is not set
# default:
# CONFIG_PARTITION_TABLE_TWO_OTA_LARGE is not set
# default:
CONFIG_PARTITION_TABLE_CUSTOM=y
# default:
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-s3.csv"
# default:
CONFIG_PARTITION_TABLE_FILENAME="partitions-s3.csv"
@@ -1428,15 +1422,11 @@ CONFIG_BT_NIMBLE_EXTRA_ADV_FIELDS=y
#
# default:
CONFIG_BT_CTRL_MODE_EFF=1
CONFIG_BT_CTRL_BLE_MAX_ACT=4
# default:
CONFIG_BT_CTRL_BLE_MAX_ACT=6
# default:
CONFIG_BT_CTRL_BLE_MAX_ACT_EFF=6
# default:
CONFIG_BT_CTRL_BLE_STATIC_ACL_TX_BUF_NB=0
# default:
CONFIG_BT_CTRL_BLE_MAX_ACT_EFF=4
CONFIG_BT_CTRL_BLE_STATIC_ACL_TX_BUF_NB=6
CONFIG_BT_CTRL_PINNED_TO_CORE_0=y
# default:
# CONFIG_BT_CTRL_PINNED_TO_CORE_1 is not set
# default:
CONFIG_BT_CTRL_PINNED_TO_CORE=0
@@ -1728,7 +1718,6 @@ CONFIG_ESP_TLS_DYN_BUF_STRATEGY_SUPPORTED=y
#
# default:
CONFIG_ESP_COEX_ENABLED=y
# default:
CONFIG_ESP_COEX_SW_COEXIST_ENABLE=y
# default:
# CONFIG_ESP_COEX_POWER_MANAGEMENT is not set
@@ -2331,7 +2320,6 @@ CONFIG_ESP_NETIF_RECEIVE_REPORT_ERRORS=y
#
# default:
CONFIG_ESP_PHY_ENABLED=y
# default:
CONFIG_ESP_PHY_CALIBRATION_AND_DATA_STORAGE=y
# default:
# CONFIG_ESP_PHY_INIT_DATA_IN_PARTITION is not set
@@ -2339,20 +2327,16 @@ CONFIG_ESP_PHY_CALIBRATION_AND_DATA_STORAGE=y
CONFIG_ESP_PHY_MAX_WIFI_TX_POWER=20
# default:
CONFIG_ESP_PHY_MAX_TX_POWER=20
# default:
# CONFIG_ESP_PHY_REDUCE_TX_POWER is not set
# default:
CONFIG_ESP_PHY_ENABLE_USB=y
# default:
# CONFIG_ESP_PHY_ENABLE_CERT_TEST is not set
# default:
CONFIG_ESP_PHY_RF_CAL_PARTIAL=y
# default:
# CONFIG_ESP_PHY_RF_CAL_PARTIAL is not set
# CONFIG_ESP_PHY_RF_CAL_NONE is not set
CONFIG_ESP_PHY_RF_CAL_FULL=y
# default:
# CONFIG_ESP_PHY_RF_CAL_FULL is not set
# default:
CONFIG_ESP_PHY_CALIBRATION_MODE=0
CONFIG_ESP_PHY_CALIBRATION_MODE=2
# default:
CONFIG_ESP_PHY_PLL_TRACK_PERIOD_MS=1000
# default:
@@ -2370,7 +2354,6 @@ CONFIG_ESP_PHY_IRAM_OPT=y
#
# default:
# CONFIG_PM_SLEEP_FUNC_IN_IRAM is not set
# default:
# CONFIG_PM_ENABLE is not set
# default:
# CONFIG_PM_SLP_IRAM_OPT is not set
@@ -2390,11 +2373,9 @@ CONFIG_SPIRAM=y
#
# SPI RAM config
#
CONFIG_SPIRAM_MODE_QUAD=y
# CONFIG_SPIRAM_MODE_OCT is not set
# CONFIG_SPIRAM_MODE_QUAD is not set
CONFIG_SPIRAM_MODE_OCT=y
CONFIG_SPIRAM_TYPE_AUTO=y
# CONFIG_SPIRAM_TYPE_ESPPSRAM16 is not set
# CONFIG_SPIRAM_TYPE_ESPPSRAM32 is not set
# CONFIG_SPIRAM_TYPE_ESPPSRAM64 is not set
# default:
CONFIG_SPIRAM_CLK_IO=30
@@ -2406,12 +2387,13 @@ CONFIG_SPIRAM_CS_IO=26
# CONFIG_SPIRAM_FETCH_INSTRUCTIONS is not set
# default:
# CONFIG_SPIRAM_RODATA is not set
# CONFIG_SPIRAM_SPEED_120M is not set
CONFIG_SPIRAM_SPEED_80M=y
# CONFIG_SPIRAM_SPEED_40M is not set
# default:
CONFIG_SPIRAM_SPEED=80
# default:
# CONFIG_SPIRAM_ECC_ENABLE is not set
# default:
CONFIG_SPIRAM_BOOT_HW_INIT=y
CONFIG_SPIRAM_BOOT_INIT=y
# default:
@@ -2487,14 +2469,11 @@ CONFIG_ESP_CONSOLE_UART_BAUDRATE=115200
#
# ESP System Settings
#
# default:
# CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_80 is not set
# CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_160 is not set
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
# default:
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_160=y
# default:
# CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240 is not set
# default:
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=160
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=240
#
# Cache config
@@ -2691,9 +2670,7 @@ CONFIG_ESP_TRACE_TRANSPORT_NAME="none"
#
# default:
CONFIG_ESP_WIFI_ENABLED=y
# default:
CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=10
# default:
CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=32
# default:
CONFIG_ESP_WIFI_STATIC_TX_BUFFER=y
@@ -2713,14 +2690,12 @@ CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUF=0
CONFIG_ESP_WIFI_RX_MGMT_BUF_NUM_DEF=5
# default:
# CONFIG_ESP_WIFI_CSI_ENABLED is not set
# default:
CONFIG_ESP_WIFI_AMPDU_TX_ENABLED=y
# default:
CONFIG_ESP_WIFI_TX_BA_WIN=6
# default:
CONFIG_ESP_WIFI_AMPDU_RX_ENABLED=y
# default:
CONFIG_ESP_WIFI_RX_BA_WIN=6
CONFIG_ESP_WIFI_RX_BA_WIN=16
# default:
# CONFIG_ESP_WIFI_AMSDU_TX_ENABLED is not set
# default:
@@ -2733,11 +2708,9 @@ CONFIG_ESP_WIFI_TASK_PINNED_TO_CORE_0=y
CONFIG_ESP_WIFI_SOFTAP_BEACON_MAX_LEN=752
# default:
CONFIG_ESP_WIFI_MGMT_SBUF_NUM=32
# default:
CONFIG_ESP_WIFI_IRAM_OPT=y
# default:
# CONFIG_ESP_WIFI_EXTRA_IRAM_OPT is not set
# default:
CONFIG_ESP_WIFI_RX_IRAM_OPT=y
# default:
CONFIG_ESP_WIFI_ENABLE_WPA3_SAE=y
@@ -3323,7 +3296,7 @@ CONFIG_LWIP_TCP_QUEUE_OOSEQ=y
# default:
CONFIG_LWIP_TCP_OOSEQ_TIMEOUT=6
# default:
CONFIG_LWIP_TCP_OOSEQ_MAX_PBUFS=4
CONFIG_LWIP_TCP_OOSEQ_MAX_PBUFS=0
# default:
# CONFIG_LWIP_TCP_SACK_OUT is not set
# default:
@@ -4482,9 +4455,9 @@ CONFIG_CONSOLE_UART=y
CONFIG_CONSOLE_UART_NUM=0
CONFIG_CONSOLE_UART_BAUDRATE=115200
# CONFIG_ESP32S3_DEFAULT_CPU_FREQ_80 is not set
CONFIG_ESP32S3_DEFAULT_CPU_FREQ_160=y
# CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240 is not set
CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ=160
# CONFIG_ESP32S3_DEFAULT_CPU_FREQ_160 is not set
CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240=y
CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ=240
CONFIG_ESP_SYSTEM_PMP_IDRAM_SPLIT=y
CONFIG_ESP_SYSTEM_MEMPROT_FEATURE_VIA_TEE=y
CONFIG_ESP_SYSTEM_MEMPROT_FEATURE=y
@@ -4517,7 +4490,7 @@ CONFIG_ESP32_WIFI_CACHE_TX_BUFFER_NUM=32
CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED=y
CONFIG_ESP32_WIFI_TX_BA_WIN=6
CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y
CONFIG_ESP32_WIFI_RX_BA_WIN=6
CONFIG_ESP32_WIFI_RX_BA_WIN=16
# CONFIG_ESP32_WIFI_AMSDU_TX_ENABLED is not set
CONFIG_ESP32_WIFI_NVS_ENABLED=y
CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_0=y
+65
View File
@@ -0,0 +1,65 @@
# ESP32-S3 Specific Configuration
# This file is automatically merged when target is set to esp32s3
# Flash Configuration (8MB Flash)
CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
CONFIG_ESPTOOLPY_FLASHSIZE="8MB"
# PSRAM Configuration for Seeeduino XIAO ESP32S3 (8MB OCTAL PSRAM)
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_OCT=y
CONFIG_SPIRAM_TYPE_AUTO=y
CONFIG_SPIRAM_SPEED_80M=y
CONFIG_SPIRAM_BOOT_INIT=y
CONFIG_SPIRAM_USE_MALLOC=y
CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=16384
CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y
# Partition Table
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-s3.csv"
# External Antenna Configuration for Seeeduino XIAO ESP32S3
# With external antenna, use full RF calibration for best performance
CONFIG_ESP_PHY_RF_CAL_FULL=y
CONFIG_ESP_PHY_RF_CAL_PARTIAL=n
CONFIG_ESP_PHY_CALIBRATION_AND_DATA_STORAGE=y
# Bluetooth/WiFi Coexistence Settings
CONFIG_ESP_COEX_SW_COEXIST_ENABLE=y
CONFIG_ESP_WIFI_SW_COEXIST_PREFERENCE_BALANCE=y
CONFIG_ESP_COEX_EXTERNAL_COEXIST_ENABLE=n
# BLE Settings optimized for external antenna
CONFIG_BT_BLE_ENABLED=y
CONFIG_BT_NIMBLE_ENABLED=y
CONFIG_BT_NIMBLE_MAX_CONNECTIONS=1
CONFIG_BTDM_CTRL_BR_EDR_MAX_ACL_CONN_EFF=0
CONFIG_BTDM_CTRL_BLE_MAX_CONN_EFF=1
CONFIG_BT_CTRL_BLE_MAX_ACT=4
CONFIG_BT_CTRL_BLE_STATIC_ACL_TX_BUF_NB=6
CONFIG_BT_CTRL_BLE_MAX_CONN=1
CONFIG_BT_CTRL_PINNED_TO_CORE_0=y
# WiFi Settings optimized for BLE coexistence (balanced, not aggressive)
CONFIG_ESP_WIFI_IRAM_OPT=y
CONFIG_ESP_WIFI_RX_IRAM_OPT=y
CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=6
CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=16
CONFIG_ESP_WIFI_TX_BUFFER_TYPE=1
CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER_NUM=16
CONFIG_ESP_PHY_REDUCE_TX_POWER=n
CONFIG_ESP_PHY_MAX_TX_POWER=17
CONFIG_ESP_WIFI_AMPDU_TX_ENABLED=n
CONFIG_ESP_WIFI_AMPDU_RX_ENABLED=n
CONFIG_ESP_WIFI_NVS_ENABLED=y
# Prioritize BLE over WiFi for wand stability
CONFIG_ESP_WIFI_SW_COEXIST_PREFERENCE_BT=y
# CPU and Power settings
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=240
# Power Management - keep radio powered for stability
CONFIG_PM_ENABLE=n
+2 -1
View File
@@ -2379,7 +2379,8 @@ CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU=y
# ESP PSRAM
#
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_QUAD=y
# CONFIG_SPIRAM_MODE_QUAD is not set
CONFIG_SPIRAM_MODE_OCT=y
CONFIG_SPIRAM_TYPE_AUTO=y
CONFIG_SPIRAM_SPEED_80M=y
CONFIG_SPIRAM_BOOT_INIT=y
+4580
View File
File diff suppressed because it is too large Load Diff
+35 -14
View File
@@ -15,7 +15,9 @@
#include "services/gatt/ble_svc_gatt.h"
static const char *TAG = "ble_client";
#if USE_USB_HID_DEVICE
extern USBHIDManager usbHID;
#endif
// Global for scan callback
static WebServer *g_web_server = nullptr;
@@ -647,6 +649,12 @@ bool WandBLEClient::begin(const unsigned char *model_data, size_t model_size)
return false;
}
// Boost BLE TX power for Seeeduino XIAO's weaker PCB antenna
ESP_LOGI(TAG, "Setting BLE TX power to maximum for better range...");
esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_DEFAULT, ESP_PWR_LVL_P9);
esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_ADV, ESP_PWR_LVL_P9);
esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_SCAN, ESP_PWR_LVL_P9);
nimble_port_freertos_init(ble_host_task);
return true;
}
@@ -683,17 +691,17 @@ bool WandBLEClient::connect(const char *address)
peer_addr = addr;
// Configure connection parameters for better stability with WiFi coexistence
// These params prioritize connection stability over low latency
// Configure connection parameters - use standard values that NimBLE accepts
// Values must be in valid ranges per Bluetooth spec
struct ble_gap_conn_params conn_params;
conn_params.scan_itvl = 0x0010; // 10ms scan interval
conn_params.scan_window = 0x0010; // 10ms scan window
conn_params.itvl_min = 0x0018; // 30ms min connection interval (24 * 1.25ms)
conn_params.itvl_max = 0x0028; // 50ms max connection interval (40 * 1.25ms)
conn_params.scan_itvl = 0x0010; // 10ms (16 * 0.625ms) - standard
conn_params.scan_window = 0x0010; // 10ms (16 * 0.625ms) - must be <= scan_itvl
conn_params.itvl_min = 0x0018; // 30ms (24 * 1.25ms) - standard range
conn_params.itvl_max = 0x0028; // 50ms (40 * 1.25ms) - standard range
conn_params.latency = 0; // No slave latency
conn_params.supervision_timeout = 0x0C80; // 32 seconds (3200 * 10ms)
conn_params.min_ce_len = 0x0010; // 10ms
conn_params.max_ce_len = 0x0300; // 480ms
conn_params.min_ce_len = 0x0010; // 10ms (16 * 0.625ms)
conn_params.max_ce_len = 0x0300; // 480ms (768 * 0.625ms)
ESP_LOGI(TAG, "Attempting connection to %s (random address type) with 32s supervision timeout", address);
rc = ble_gap_connect(BLE_OWN_ADDR_PUBLIC, &addr, 30000, &conn_params,
@@ -824,12 +832,17 @@ void WandBLEClient::processButtonPacket(const uint8_t *data, size_t length)
return;
}
// Count pressed buttons for easier tracking
uint8_t buttonsPressed = __builtin_popcount(buttonState & 0x0F);
bool enoughButtonsPressed = (buttonsPressed >= BUTTON_MIN_FOR_TRACKING);
bool wasEnoughPressed = (__builtin_popcount(lastButtonState & 0x0F) >= BUTTON_MIN_FOR_TRACKING);
if (buttonState != lastButtonState)
{
bool b1 = buttonState & 0x01, b2 = buttonState & 0x02,
b3 = buttonState & 0x04, b4 = buttonState & 0x08;
ESP_LOGI(TAG, "🔘 Buttons: [1]=%s [2]=%s [3]=%s [4]=%s",
b1 ? "" : "", b2 ? "" : "", b3 ? "" : "", b4 ? "" : "");
ESP_LOGI(TAG, "🔘 Buttons: [1]=%s [2]=%s [3]=%s [4]=%s (%d/4 pressed)",
b1 ? "" : "", b2 ? "" : "", b3 ? "" : "", b4 ? "" : "", buttonsPressed);
// Broadcast button state to web GUI
if (webServer)
@@ -838,8 +851,8 @@ void WandBLEClient::processButtonPacket(const uint8_t *data, size_t length)
}
}
// All buttons pressed - start tracking
if (buttonState == BUTTON_ALL_PRESSED && lastButtonState != BUTTON_ALL_PRESSED)
// 3+ buttons pressed - start tracking (was 4 buttons required)
if (enoughButtonsPressed && !wasEnoughPressed)
{
// Log heap status before starting tracking
ESP_LOGI(TAG, "Free heap before tracking: %lu bytes", esp_get_free_heap_size());
@@ -849,10 +862,12 @@ void WandBLEClient::processButtonPacket(const uint8_t *data, size_t length)
if (!ahrsTracker.isTracking())
{
ahrsTracker.startTracking();
ESP_LOGI(TAG, "Started spell tracking");
ESP_LOGI(TAG, "Started spell tracking (%d buttons pressed)", buttonsPressed);
// Disable mouse movement during spell tracking
#if USE_USB_HID_DEVICE
usbHID.setInSpellMode(true);
#endif
// Notify web visualizer (don't broadcast position[0], wait for position[1])
if (webServer)
@@ -862,7 +877,7 @@ void WandBLEClient::processButtonPacket(const uint8_t *data, size_t length)
}
}
// Buttons released - detect spell
else if (buttonState != BUTTON_ALL_PRESSED && lastButtonState == BUTTON_ALL_PRESSED)
else if (!enoughButtonsPressed && wasEnoughPressed)
{
// Clear wand LEDs after spell tracking
wandCommands.clearAllLEDs();
@@ -883,7 +898,9 @@ void WandBLEClient::processButtonPacket(const uint8_t *data, size_t length)
spellCallback(spell_name, spellDetector.getConfidence());
// Send mapped keyboard key for detected spell
#if USE_USB_HID_DEVICE
usbHID.sendSpellKeyboardForSpell(spell_name);
#endif
}
else if (!spell_name)
{
@@ -902,7 +919,9 @@ void WandBLEClient::processButtonPacket(const uint8_t *data, size_t length)
}
// Re-enable mouse movement after spell tracking
#if USE_USB_HID_DEVICE
usbHID.setInSpellMode(false);
#endif
// Notify web visualizer
if (webServer)
@@ -974,7 +993,9 @@ void WandBLEClient::updateAHRS(const IMUSample &sample)
// Rate limit mouse updates to ~60 Hz (every 4th point)
if (new_count == 2 || ++mouse_counter >= 4)
{
#if USE_USB_HID_DEVICE
usbHID.updateMouseFromGesture(accum_dx, accum_dy);
#endif
accum_dx = 0.0f;
accum_dy = 0.0f;
mouse_counter = 0;
+51 -5
View File
@@ -13,6 +13,7 @@
#include "esp_netif.h"
#include "lwip/ip4_addr.h"
#include "nvs_flash.h"
#include "driver/gpio.h"
#include "ble_client.h"
#include "config.h"
#include "usb_hid.h"
@@ -21,6 +22,12 @@
static const char *TAG = "main";
// Seeeduino XIAO ESP32S3 antenna switch GPIO
// Some XIAO ESP32S3 use GPIO14, others GPIO3
// Try GPIO14 first (most common for XIAO Sense), change to GPIO3 if needed
#define ANTENNA_SWITCH_GPIO GPIO_NUM_14
#define USE_EXTERNAL_ANTENNA 1 // Set to 1 for external antenna, 0 for internal
// MAC address formatting macros (if not defined by esp_wifi)
#ifndef MACSTR
#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
@@ -250,8 +257,35 @@ void onIMUData(float ax, float ay, float az, float gx, float gy, float gz)
extern "C" void app_main()
{
vTaskDelay(100 / portTICK_PERIOD_MS); // Small delay to ensure logging is ready
ESP_LOGI(TAG, "app_main() starting...");
// Wait 3 seconds for serial monitor to connect and catch all startup logs
vTaskDelay(3000 / portTICK_PERIOD_MS);
ESP_LOGI(TAG, "");
ESP_LOGI(TAG, "================================================");
ESP_LOGI(TAG, " ESP32-S3 Magic Wand Gateway Starting...");
ESP_LOGI(TAG, " Seeeduino XIAO ESP32S3");
ESP_LOGI(TAG, "================================================");
ESP_LOGI(TAG, "");
// Configure antenna switch for Seeeduino XIAO ESP32S3
ESP_LOGI(TAG, "Configuring RF antenna...");
gpio_config_t io_conf = {};
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pin_bit_mask = (1ULL << ANTENNA_SWITCH_GPIO);
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
gpio_config(&io_conf);
#if USE_EXTERNAL_ANTENNA
gpio_set_level(ANTENNA_SWITCH_GPIO, 1); // HIGH = external U.FL antenna
ESP_LOGI(TAG, "✓ Using EXTERNAL antenna (U.FL connector on GPIO3)");
ESP_LOGI(TAG, " Make sure antenna is properly attached!");
#else
gpio_set_level(ANTENNA_SWITCH_GPIO, 0); // LOW = internal PCB antenna
ESP_LOGI(TAG, "✓ Using INTERNAL PCB antenna");
#endif
ESP_LOGI(TAG, "");
// Check PSRAM status early
ESP_LOGI(TAG, "");
@@ -639,25 +673,37 @@ extern "C" void app_main()
const uint32_t BATTERY_CHECK_INTERVAL = 100; // Check every 10 seconds (100 * 100ms)
uint32_t keepalive_counter = 0;
const uint32_t KEEPALIVE_INTERVAL = 30; // Send keep-alive every 3 seconds (30 * 100ms)
uint32_t reconnect_attempts = 0;
const uint32_t MAX_RECONNECT_ATTEMPTS = 3; // Try 3 times then pause
while (1)
{
// Check connection status (only try to reconnect if we have a valid configured MAC)
if (!wandClient.isConnected() && (mac_from_nvs || strcmp(WAND_MAC_ADDRESS, "C2:BD:5D:3C:67:4E") != 0))
{
ESP_LOGW(TAG, "Connection lost, attempting reconnect...");
vTaskDelay(2000 / portTICK_PERIOD_MS);
// After 3 failed attempts, wait much longer to give WiFi priority
if (reconnect_attempts >= MAX_RECONNECT_ATTEMPTS)
{
ESP_LOGW(TAG, "Connection lost after %d attempts. Pausing reconnects for 5 minutes to prioritize WiFi...", MAX_RECONNECT_ATTEMPTS);
vTaskDelay(300000 / portTICK_PERIOD_MS); // Wait 5 minutes
reconnect_attempts = 0; // Reset counter
}
ESP_LOGW(TAG, "Connection lost, attempting reconnect... (attempt %d/%d)", reconnect_attempts + 1, MAX_RECONNECT_ATTEMPTS);
vTaskDelay(30000 / portTICK_PERIOD_MS); // Wait 30 seconds before reconnect attempt
// Attempt to connect
wandClient.connect(wand_mac);
reconnect_attempts++;
// Wait for connection to establish
ESP_LOGI(TAG, "Waiting for connection...");
vTaskDelay(5000 / portTICK_PERIOD_MS);
vTaskDelay(10000 / portTICK_PERIOD_MS); // Wait 10 seconds for connection
// Check if connection succeeded
if (wandClient.isConnected())
{
reconnect_attempts = 0; // Reset on successful connection
ESP_LOGI(TAG, "Reconnected! Waiting for service discovery...");
vTaskDelay(5000 / portTICK_PERIOD_MS);
+20 -20
View File
@@ -51,15 +51,15 @@ size_t SpellEffects::buildEffect(const char *spell_name, uint8_t *buffer, size_t
// Build effect based on spell name
if (strcmp(spell_name, "Lumos") == 0)
{
// Buzz 150ms + White LED 2s
len += addBuzz(buffer, len, 150);
// Buzz 50ms + White LED 2s (reduced vibration)
len += addBuzz(buffer, len, 50);
len += addLEDTransition(buffer, len, (uint8_t)LedGroup::TIP,
255, 255, 255, 2000);
}
else if (strcmp(spell_name, "Nox") == 0)
{
// Buzz 100ms + Purple flash + Clear
len += addBuzz(buffer, len, 100);
// Buzz 30ms + Purple flash + Clear (reduced vibration)
len += addBuzz(buffer, len, 30);
len += addLEDTransition(buffer, len, (uint8_t)LedGroup::TIP,
51, 0, 51, 200);
len += addDelay(buffer, len, 100);
@@ -67,57 +67,57 @@ size_t SpellEffects::buildEffect(const char *spell_name, uint8_t *buffer, size_t
}
else if (strcmp(spell_name, "Verdimillious") == 0 || strcmp(spell_name, "Reducto") == 0)
{
// Green spell effect
len += addBuzz(buffer, len, 200);
// Green spell effect (reduced vibration)
len += addBuzz(buffer, len, 50);
len += addLEDTransition(buffer, len, (uint8_t)LedGroup::TIP,
0, 255, 0, 200);
}
else if (strcmp(spell_name, "Incendio") == 0 || strcmp(spell_name, "Flagrate") == 0)
{
// Fire spell effect (orange)
len += addBuzz(buffer, len, 150);
// Fire spell effect (orange, reduced vibration)
len += addBuzz(buffer, len, 50);
len += addLEDTransition(buffer, len, (uint8_t)LedGroup::TIP,
255, 102, 0, 400);
}
else if (strcmp(spell_name, "Expelliarmus") == 0)
{
// Red disarming spell
len += addBuzz(buffer, len, 200);
// Red disarming spell (reduced vibration)
len += addBuzz(buffer, len, 50);
len += addLEDTransition(buffer, len, (uint8_t)LedGroup::TIP,
255, 0, 0, 300);
}
else if (strcmp(spell_name, "Stupefy") == 0)
{
// Red stunning spell with longer buzz
len += addBuzz(buffer, len, 250);
// Red stunning spell (reduced vibration)
len += addBuzz(buffer, len, 60);
len += addLEDTransition(buffer, len, (uint8_t)LedGroup::TIP,
200, 0, 0, 400);
}
else if (strcmp(spell_name, "Protego") == 0)
{
// Blue shield spell
len += addBuzz(buffer, len, 150);
// Blue shield spell (reduced vibration)
len += addBuzz(buffer, len, 50);
len += addLEDTransition(buffer, len, (uint8_t)LedGroup::TIP,
0, 100, 255, 500);
}
else if (strcmp(spell_name, "Wingardium Leviosa") == 0)
{
// Light blue levitation spell
len += addBuzz(buffer, len, 100);
// Light blue levitation spell (reduced vibration)
len += addBuzz(buffer, len, 40);
len += addLEDTransition(buffer, len, (uint8_t)LedGroup::TIP,
100, 200, 255, 600);
}
else if (strcmp(spell_name, "Accio") == 0)
{
// Cyan summoning spell
len += addBuzz(buffer, len, 120);
// Cyan summoning spell (reduced vibration)
len += addBuzz(buffer, len, 40);
len += addLEDTransition(buffer, len, (uint8_t)LedGroup::TIP,
0, 255, 255, 300);
}
else
{
// Default effect for unknown spells (blue flash)
len += addBuzz(buffer, len, 100);
// Default effect for unknown spells (blue flash, reduced vibration)
len += addBuzz(buffer, len, 40);
len += addLEDTransition(buffer, len, (uint8_t)LedGroup::TIP,
0, 100, 255, 200);
}
+22
View File
@@ -8,7 +8,9 @@
#include <string.h>
// Forward declaration from main.cpp
#if USE_USB_HID_DEVICE
extern USBHIDManager usbHID;
#endif
static const char *TAG = "web_server";
@@ -1979,6 +1981,7 @@ esp_err_t WebServer::settings_get_handler(httpd_req_t *req)
ESP_LOGI(TAG, "settings_get_handler called!");
// Return mouse sensitivity and all 73 spell keycodes
#if USE_USB_HID_DEVICE
char buffer[2048];
int offset = 0;
@@ -1997,6 +2000,10 @@ esp_err_t WebServer::settings_get_handler(httpd_req_t *req)
httpd_resp_set_type(req, "application/json");
httpd_resp_sendstr(req, buffer);
#else
httpd_resp_set_type(req, "application/json");
httpd_resp_sendstr(req, "{\"status\":\"disabled\",\"message\":\"USB HID not enabled\"}");
#endif
return ESP_OK;
}
@@ -2025,6 +2032,7 @@ esp_err_t WebServer::settings_save_handler(httpd_req_t *req)
ESP_LOGI(TAG, "Received settings: %s", buffer);
// Parse JSON - expect format: {"mouse_sensitivity": 1.5, "spells": [0, 58, 0, ...]}
#if USE_USB_HID_DEVICE
float mouse_sens = 1.0f;
char *mouse_ptr = strstr(buffer, "\"mouse_sensitivity\"");
if (mouse_ptr)
@@ -2072,8 +2080,10 @@ esp_err_t WebServer::settings_save_handler(httpd_req_t *req)
}
}
}
#endif
// Save to NVS
#if USE_USB_HID_DEVICE
if (usbHID.saveSettings())
{
httpd_resp_set_type(req, "application/json");
@@ -2088,12 +2098,19 @@ esp_err_t WebServer::settings_save_handler(httpd_req_t *req)
free(buffer);
return ESP_FAIL;
}
#else
httpd_resp_set_type(req, "application/json");
httpd_resp_sendstr(req, "{\"status\":\"disabled\",\"message\":\"USB HID not enabled\"}");
free(buffer);
return ESP_OK;
#endif
}
esp_err_t WebServer::settings_reset_handler(httpd_req_t *req)
{
ESP_LOGI(TAG, "settings_reset_handler called!");
#if USE_USB_HID_DEVICE
if (usbHID.resetSettings())
{
httpd_resp_set_type(req, "application/json");
@@ -2106,4 +2123,9 @@ esp_err_t WebServer::settings_reset_handler(httpd_req_t *req)
httpd_resp_sendstr(req, "{\"status\":\"error\",\"message\":\"Failed to reset settings\"}");
return ESP_FAIL;
}
#else
httpd_resp_set_type(req, "application/json");
httpd_resp_sendstr(req, "{\"status\":\"disabled\",\"message\":\"USB HID not enabled\"}");
return ESP_OK;
#endif
}