Compare commits

...

17 Commits

Author SHA1 Message Date
Brent Perteet
31e0e3a148 add label scrolling in Bluetooth menu 2025-11-19 17:42:22 -06:00
Brent Perteet
a272a15bcf Improve calibration and volume menu UX
- Add calibration submenu with three options: Cancel (default), Calibrate, and Clear
- Cancel returns to main menu to prevent accidental calibration
- Calibrate runs system_setZeroAngle() and displays "Calibrated" confirmation
- Clear runs system_clearZeroAngle() and displays "Cleared" confirmation
- Use LVGL timer for non-blocking 1-second confirmation messages
- Center confirmation messages both horizontally and vertically
- Use larger font (montserrat_16) for better readability
- Remove "Back" button from volume control page
- Center volume control elements vertically on screen
- Improve overall menu navigation and visual consistency

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-16 21:53:32 -06:00
Brent Perteet
8a1966ea90 Add charge status monitoring and LED control
- Add LED_0 pin definition and charge status GPIO pin
- Implement charge status monitoring in main loop
- Add LED visual indicators for charging state
- Configure LVGL Montserrat 10 font and set as default
- Reset GPIO pins to clear SPI configurations
- Update COM port to COM14 in VSCode settings

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-16 15:55:59 -06:00
Brent Perteet
b8a3a09e9f Fix Bluetooth pairing, menu crashes, and improve UX
Major fixes and improvements to Bluetooth device management and menu navigation:

**Bluetooth Device Pairing**
- Fixed discovered devices not being saved as paired after connection
- Only save devices to NVS when successfully connected (not just discovered)
- Auto-pair discovered devices on successful A2DP connection
- Update device list to show paired status immediately after connection

**Critical Bug Fixes**
- Fixed dangling pointer crash in NVS request/response mechanism
  - Was sending pointer to stack variable, now sends result value directly
  - Prevents crash when connecting to Bluetooth devices
- Fixed use-after-free crash when clicking menu items after dynamic updates
  - Menu context now properly synchronized after adding/removing items
  - Prevents InstructionFetchError crashes in menu navigation
- Fixed memory exhaustion by reducing MAX_BT_DEVICES from 20 to 8
  - Prevents heap allocation failures when populating device list

**Menu & UX Improvements**
- "Clear Paired" button now properly disconnects active connections
- "Clear Paired" button always visible when paired devices exist
- GUI updates immediately after clearing paired devices
- Paired devices marked with asterisk prefix (* Device Name)
- Removed redundant "(paired)" suffix text
- Long device names scroll smoothly when selected (3-second animation)
- Refresh button preserved during menu updates to prevent crashes
- Menu focus state properly maintained across all dynamic updates

**Technical Details**
- bt_add_discovered_device() no longer saves to NVS
- Added currentFocusIndex() calls after all menu modifications
- Improved clear_bt_device_list() to avoid deleting active buttons
- Added bt_disconnect_current_device() for clean disconnections
- Fixed NVS notification mechanism to avoid stack variable pointers

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-14 11:39:58 -06:00
Brent Perteet
115105c032 Fix menu navigation issues with Bluetooth and initial focus
- Initialize menu context when showing menu to properly track focused item
- Fix double-activation bug that caused immediate exit from Bluetooth menu
- Add separate refresh_bt_device_list() to avoid infinite discovery loop
- Delete old BT page before creating new one to prevent memory leaks
- Add "Clear Paired" option to Bluetooth menu
- Improve Bluetooth menu refresh behavior to show "Scanning..." message

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-13 19:55:32 -06:00
Brent Perteet
19e8ca30c3 everything working and tested on separate computer 2025-10-27 18:27:55 -05:00
Brent Perteet
de7041c1f5 EXE working but getting logging error 2025-10-25 11:52:11 -05:00
Brent Perteet
fe69dc6f19 flash works when not in EXE 2025-10-25 11:29:53 -05:00
Brent Perteet
bca2f6ea9c Added logo to flasher 2025-10-14 09:50:35 -05:00
Brent Perteet
5a893a034c update flasher 2025-10-13 20:51:52 -05:00
2513a9e7fb adding web flasher tool 2025-10-13 18:54:50 -05:00
Brent Perteet
04d2c71d01 adding flasher 2025-10-12 13:40:43 -05:00
Brent Perteet
a89fdc6843 Update GUI styling and font configuration
- Fix bt_app.h syntax error (remove stray "1")
- Improve menu item alignment and styling
- Enable additional LVGL fonts (Montserrat 8, Unscii 16)
- Clean up volume page layout, remove instruction labels
- Update default font to Montserrat 8

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-27 18:23:40 +00:00
Brent Perteet
439c6ef22d Enhance Bluetooth functionality and GUI integration
- Add Bluetooth device list management with discovery and pairing support
- Implement volume control with AVRC integration
- Add system event handling for GUI-to-Bluetooth communication
- Enhance menu system with device selection and volume control pages
- Improve memory management by making menu creation lazy
- Add proper event subscription between GUI and Bluetooth modules
- Update filter coefficient for improved audio clicking behavior

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-25 16:46:16 +00:00
Brent Perteet
25f875b3b2 Memory issues resolved 2025-08-20 15:46:17 +00:00
Brent Perteet
c4ca91bf84 added tweaks to filter 2025-08-08 17:31:27 -05:00
Brent Perteet
fb9fd84bf1 Fix readme 2025-07-22 14:11:25 -05:00
52 changed files with 5584 additions and 5530 deletions

View File

@@ -0,0 +1,32 @@
{
"permissions": {
"allow": [
"Bash(idf.py build:*)",
"Bash(grep:*)",
"Bash(git add:*)",
"Bash(git commit:*)",
"Bash(pip install:*)",
"Bash(build_from_spec.bat)",
"mcp__ide__getDiagnostics",
"Bash(source:*)",
"Bash(chmod:*)",
"Bash(python3:*)",
"Bash(pyinstaller:*)",
"Bash(open:*)",
"Bash(dos2unix:*)",
"Bash(./build_macos.sh:*)",
"Bash(brew install:*)",
"Bash(python:*)",
"Bash(./dist/ESP32_Flasher:*)",
"Bash(pkill:*)",
"Bash(curl -s http://127.0.0.1:5000/)",
"Bash(./build_web_macos.sh:*)",
"Bash(./dist/ESP32_Flasher.app/Contents/MacOS/ESP32_Flasher:*)",
"Bash(where:*)",
"Bash(idf.py size-components:*)",
"Bash(find:*)",
"Bash(git log:*)"
],
"deny": []
}
}

17
.gitignore vendored
View File

@@ -1,6 +1,22 @@
.pytest_cache/
__pycache__/
# macOS
.DS_Store
# Python virtual environments
venv/
env/
ENV/
.venv/
# PyInstaller build artifacts
dist/
build/
*.spec
*.pyc
*.pyo
# esp-idf built binaries
build/
build_*_*/
@@ -21,3 +37,4 @@ dependencies.lock
managed_components
components
output

39
.vscode/c_cpp_properties.json vendored Normal file
View File

@@ -0,0 +1,39 @@
{
"configurations": [
{
"name": "Win32",
"includePath": [
"${workspaceFolder}/**",
"${env:USERPROFILE}/esp/v5.4.1/esp-idf/components/**",
"${env:USERPROFILE}/.espressif/tools/xtensa-esp-elf/**",
"${workspaceFolder}/build/config",
"${workspaceFolder}/managed_components/**"
],
"defines": [
"ESP_PLATFORM"
],
"compilerPath": "${env:USERPROFILE}/.espressif/tools/xtensa-esp-elf/esp-13.2.0_20241113/xtensa-esp-elf/bin/xtensa-esp32-elf-gcc.exe",
"cStandard": "c11",
"cppStandard": "c++20",
"intelliSenseMode": "gcc-x64"
},
{
"name": "Mac",
"includePath": [
"${workspaceFolder}/**",
"/Users/brent/esp/v5.5.1/esp-idf/components/**",
"/Users/brent/.espressif/tools/xtensa-esp-elf/**",
"${workspaceFolder}/build/config",
"${workspaceFolder}/managed_components/**"
],
"defines": [
"ESP_PLATFORM"
],
"compilerPath": "/Users/brent/.espressif/tools/xtensa-esp-elf/esp-13.2.0_20240530/xtensa-esp-elf/bin/xtensa-esp32-elf-gcc",
"cStandard": "c11",
"cppStandard": "c++20",
"intelliSenseMode": "gcc-x64"
}
],
"version": 4
}

50
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,50 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Build ESP32 Firmware (Windows)",
"type": "process",
"request": "launch",
"command": "idf.py",
"args": ["build"],
"cwd": "${workspaceFolder}",
"windows": {
"command": "idf.py"
},
"problemMatcher": ["$gcc"]
},
{
"name": "Build ESP32 Firmware (macOS)",
"type": "process",
"request": "launch",
"command": "bash",
"args": ["-lc", "source ${IDF_PATH:-/opt/esp/idf}/export.sh && idf.py build"],
"cwd": "${workspaceFolder}",
"osx": {
"command": "bash"
},
"problemMatcher": ["$gcc"]
},
{
"name": "Build Windows Flasher",
"type": "process",
"request": "launch",
"command": "${workspaceFolder}/flash_tool/build_from_spec.bat",
"cwd": "${workspaceFolder}/flash_tool",
"windows": {
"command": "${workspaceFolder}/flash_tool/build_from_spec.bat"
}
},
{
"name": "Build macOS Flasher",
"type": "process",
"request": "launch",
"command": "${workspaceFolder}/flash_tool/build_macos.sh",
"cwd": "${workspaceFolder}/flash_tool",
"osx": {
"command": "bash",
"args": ["${workspaceFolder}/flash_tool/build_macos.sh"]
}
}
]
}

15
.vscode/settings.json vendored
View File

@@ -1,11 +1,11 @@
{
"C_Cpp.intelliSenseEngine": "default",
"idf.espIdfPathWin": "C:\\Users\\brent.RPX\\esp\\v5.3.1\\esp-idf",
"idf.espIdfPathWin": "C:\\Users\\Brent.Perteet\\esp\\v5.4.1\\esp-idf",
"idf.openOcdConfigs": [
"board/esp32-wrover-kit-3.3v.cfg"
],
"idf.portWin": "COM4",
"idf.toolsPathWin": "C:\\Users\\brent.RPX\\.espressif\\tools",
"idf.portWin": "COM14",
"idf.toolsPathWin": "C:\\Users\\Brent.Perteet\\.espressif",
"idf.flashType": "UART",
"files.associations": {
"esp_system.h": "c",
@@ -35,7 +35,12 @@
"semphr.h": "c",
"lv_spinbox.h": "c",
"lv_slider.h": "c",
"lv_menu.h": "c"
"lv_menu.h": "c",
"stdint.h": "c",
"random": "c"
},
"git.ignoreLimitWarning": true
"git.ignoreLimitWarning": true,
"idf.pythonInstallPath": "C:\\Users\\Brent.Perteet\\.espressif\\tools\\idf-python\\3.11.2\\python.exe",
"idf.espIdfPath": "/Users/brent/esp/v5.5.1/esp-idf",
"idf.toolsPath": "/Users/brent/.espressif"
}

37
.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,37 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "adapter",
"type": "shell",
"command": "idf.py",
"args": ["build"],
"windows": {
"command": "cmd.exe",
"args": ["/c", "${env:USERPROFILE}\\esp\\v5.4.1\\esp-idf\\export.bat", "&&", "idf.py", "build"]
},
"osx": {
"command": "bash",
"args": ["-lc", "source ${IDF_PATH:-/opt/esp/idf}/export.sh && idf.py build"]
},
"linux": {
"command": "bash",
"args": ["-lc", "source ${IDF_PATH:-/opt/esp/idf}/export.sh && idf.py build"]
},
"group": { "kind": "build", "isDefault": true },
"problemMatcher": ["${config:idf.cmakeCompilerArgs}"],
"presentation": {
"reveal": "always",
"panel": "dedicated"
}
},
{
"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": "build",
"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

@@ -1,6 +1,21 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
configure_file(${CMAKE_SOURCE_DIR}/settings.json ${CMAKE_BINARY_DIR}/settings.json COPYONLY)
execute_process(
COMMAND ${CMAKE_COMMAND} -E echo "Parsing settings.json..."
)
execute_process(
COMMAND ${CMAKE_COMMAND} -E env python ${CMAKE_SOURCE_DIR}/prepare_config.py
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
)
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(level-shot)
include("${CMAKE_SOURCE_DIR}/project_settings.cmake")
project(${PROJECT_NAME})

View File

@@ -1,4 +1,3 @@
<<<<<<< HEAD
## 🐳 ESP-IDF Dockerized Development Environment (Windows)
This project includes a fully containerized [ESP-IDF](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/index.html) development environment using **Docker** and **Visual Studio Code**.
@@ -210,95 +209,3 @@ Features:
* If `idf.py` is not found, make sure the container terminal sources `export.sh`
=======
| Supported Targets | ESP32 |
| ----------------- | ----- |
A2DP-SOURCE EXAMPLE
========================
Example of A2DP audio source role
This is the example of using Advanced Audio Distribution Profile (A2DP) APIs to transmit audio stream. Application can take advantage of this example to implement portable audio players or microphones to transmit audio stream to A2DP sink devices.
## How to use this example
### Hardware Required
This example is able to run on any commonly available ESP32 development board, and is supposed to connect to [A2DP sink example](../a2dp_sink) in ESP-IDF.
### Configure the project
```
idf.py menuconfig
```
* Enable Classic Bluetooth and A2DP under Component config --> Bluetooth --> Bluedroid Enable
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output.
```
idf.py -p PORT flash monitor
```
(Replace PORT with the name of the serial port to use.)
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example Output
For the first step, this example performs device discovery to search for a target device (A2DP sink) whose device name is "ESP_SPEAKER" and whose "Rendering" bit of its Service Class field is set in its Class of Device (COD). If a candidate target is found, the local device will initiate connection with it.
After connection with A2DP sink is established, the example performs the following running loop 1-2-3-4-1:
1. audio transmission starts and lasts for a while
2. audio transmission stops
3. disconnect with target device
4. reconnect to target device
The example implements an event loop triggered by a periodic "heart beat" timer and events from Bluetooth protocol stack callback functions.
After the local device discovers the target device and initiates connection, there will be logging message like this:
```
I (4090) BT_AV: Found a target device, address xx:xx:xx:xx:xx:xx, name ESP_SPEAKER
I (4090) BT_AV: Cancel device discovery ...
I (4100) BT_AV: Device discovery stopped.
I (4100) BT_AV: a2dp connecting to peer: ESP_SPEAKER
```
If connection is set up successfully, there will be such message:
```
I (5100) BT_AV: a2dp connected
```
Starting of audio transmission has the following notification message:
```
I (10880) BT_AV: a2dp media ready checking ...
...
I (10880) BT_AV: a2dp media ready, starting ...
...
I (11400) BT_AV: a2dp media start successfully.
```
Stop of audio transmission, and disconnection with remote device generate the following notification message:
```
I (110880) BT_AV: a2dp media stopping...
...
I (110920) BT_AV: a2dp media stopped successfully, disconnecting...
...
I (111040) BT_AV: a2dp disconnected
```
## Troubleshooting
* For current stage, the supported audio codec in ESP32 A2DP is SBC. SBC audio stream is encoded from PCM data normally formatted as 44.1kHz sampling rate, two-channel 16-bit sample data.
* The raw PCM media stream in the example is generated by a sequence of random number, so the sound played on the sink side will be piercing noise.
* As a usage limitation, ESP32 A2DP source can support at most one connection with remote A2DP sink devices. Also, A2DP source cannot be used together with A2DP sink at the same time, but can be used with other profiles such as SPP and HFP.
* Usually, the `ACL_CONN_NUM` is set to `2` but not `1` as one is used for a2dp connection and the other is used for avrcp connection.
>>>>>>> 4feb4c0a98bddb1f2a172ea4b195ce31ba18d442

51
bt_test/bt_test.ino Normal file
View File

@@ -0,0 +1,51 @@
#include <BluetoothSerial.h>
#include "esp_bt.h"
#include "esp_bt_main.h"
#include "esp_bt_device.h"
BluetoothSerial SerialBT;
void setBluetoothTxPower(esp_power_level_t powerLevel) {
esp_err_t err = esp_bredr_tx_power_set(powerLevel, powerLevel);
if (err != ESP_OK) {
Serial.printf("Failed to set TX Power: %s\n", esp_err_to_name(err));
} else {
Serial.printf("TX Power set to level %d\n", powerLevel);
}
}
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("Initializing Bluetooth...");
if (!btStart()) {
Serial.println("Failed to start BT controller");
return;
}
// Disable and re-enable the classic BT stack to safely set TX power
esp_bluedroid_disable();
esp_bluedroid_deinit();
// Set Classic BT TX power (e.g., ESP_PWR_LVL_P9 = highest)
//setBluetoothTxPower(ESP_PWR_LVL_P9);
setBluetoothTxPower(ESP_PWR_LVL_N12);
esp_bluedroid_init();
esp_bluedroid_enable();
if (!SerialBT.begin("ESP32-TXPowerTest")) {
Serial.println("Bluetooth Serial failed to start");
return;
}
Serial.println("Bluetooth initialized and ready!");
}
void loop() {
SerialBT.println("Hello from ESP32 with boosted TX power!");
delay(1);
}

141
docker.md Normal file
View File

@@ -0,0 +1,141 @@
Perfect! Below is the updated `README.md` section with **complete instructions** for setting up the Dockerized ESP-IDF environment on **Windows**, **including installation of the official Espressif VS Code extension**.
You can paste this into your repos `README.md`. Its ready to go and designed to guide teammates or collaborators through cloning, launching, and building the project with confidence.
---
## 🐳 ESP-IDF Dockerized Development Environment (Windows)
This project includes a fully containerized [ESP-IDF](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/index.html) development environment using **Docker** and **Visual Studio Code**.
Follow these instructions to set up, build, and flash the firmware inside a VS Code **Dev Container** on **Windows**.
---
### ✅ Prerequisites
Ensure the following are installed on your system:
1. [**Docker Desktop for Windows**](https://www.docker.com/products/docker-desktop)
* Enable **WSL 2 backend** during installation.
2. [**Visual Studio Code**](https://code.visualstudio.com/)
3. [**Dev Containers Extension**](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)
* In VS Code: `Extensions → Search "Dev Containers" → Install`
4. [**Espressif IDF Extension** for VS Code](https://marketplace.visualstudio.com/items?itemName=espressif.esp-idf-extension)
* In VS Code: `Extensions → Search "ESP-IDF" → Install`
> 💡 **Optional (recommended):** Install [WSL 2 with Ubuntu](https://learn.microsoft.com/en-us/windows/wsl/install) for improved Linux compatibility inside Docker.
---
### 🚀 Getting Started
#### 1. Clone this repository
```bash
git clone https://github.com/your-username/your-esp-idf-project.git
cd your-esp-idf-project
```
---
#### 2. Open in Visual Studio Code
From the project root:
```bash
code .
```
> Ensure you're opening the folder that contains `.devcontainer/`.
---
#### 3. Reopen in Dev Container
In VS Code:
* Press `F1` or `Ctrl+Shift+P`
* Run: **Dev Containers: Reopen in Container**
VS Code will:
* Build the Docker image (based on the provided `Dockerfile`)
* Set up the ESP-IDF environment
* Install extensions automatically
---
#### 4. Verify Environment
Once setup is complete:
* A terminal should launch inside the container
* Run:
```bash
idf.py --version
```
to confirm ESP-IDF is active and available.
---
#### 5. Build the Firmware
Inside the containers terminal:
```bash
idf.py build
```
You should see standard ESP-IDF build output and a `.bin` firmware file in the `build/` directory.
---
### 🔌 Flashing the ESP32 (Optional)
If you want to flash from inside the container:
1. Connect your ESP32 via USB.
2. Identify the COM port on Windows (e.g., `COM3`).
3. Pass the USB device into the container (this may require configuration).
4. Then run:
```bash
idf.py -p COM3 flash monitor
```
> ⚠️ **Note:** Docker Desktop for Windows doesnt always expose serial ports to containers directly. You can build firmware inside the container and flash it from your host as a fallback.
---
### 🧰 Notes
* The ESP-IDF version is pinned in the Dockerfile (e.g., `espressif/idf:v5.2.1`)
* The container automatically runs `source $IDF_PATH/export.sh` to prepare the environment.
* VS Code extensions (`.devcontainer.json`) include:
* `ms-vscode.cpptools`
* `ms-vscode.cmake-tools`
* `espressif.esp-idf-extension`
---
### 🛠 Troubleshooting
* If `idf.py` is not found, make sure the container terminal sources `export.sh`
* If USB flashing fails, flash from host or use WSL serial forwarding tools like `socat` or `usbip`
---
Would you like this `README.md` version to be packaged with:
* A **starter repo structure**
* A prebuilt `.devcontainer/` directory
* A sample `Dockerfile`, `main.c`, and `CMakeLists.txt`?
If yes, I can bundle that up for you — just say the word.

4
flash_gui.bat Normal file
View File

@@ -0,0 +1,4 @@
@echo off
echo Starting ESP32 Firmware Flash GUI...
python flash_tool/gui_flasher.py
pause

View File

@@ -0,0 +1,9 @@
{
"permissions": {
"allow": [
"Bash(python:*)"
],
"deny": [],
"ask": []
}
}

110
flash_tool/BUILD_NOTES.md Normal file
View File

@@ -0,0 +1,110 @@
# Build Notes
## Build Status
**macOS Build: SUCCESS**
- Built on: macOS 15.6 (arm64)
- Python Version: 3.9.6 (Xcode system Python with tkinter support)
- Output: `dist/ESP32_Flasher.app`
- Size: ~7.0 MB
- Tested: ✅ Application launches and runs successfully
## Prerequisites
### macOS
**Important:** Homebrew's Python 3.13 does NOT include tkinter support by default.
**Solution:** Install python-tk package:
```bash
brew install python-tk@3.13
```
However, the build system will automatically use the system Python (3.9.6 from Xcode) which has tkinter built-in. This is the recommended approach.
## Known Issues
### ~~macOS tkinter Warning~~ - RESOLVED
**Previous Issue:** Homebrew Python 3.13 doesn't include tkinter
**Solution:** Build system now uses Xcode's Python 3.9.6 which has native tkinter support
**Status:** ✅ Fixed - no warnings, app works perfectly
### Virtual Environment Required (macOS)
Due to PEP 668 (externally-managed environments), macOS requires using a virtual environment for pip installations. The build scripts automatically handle this.
## Build Commands
### Quick Build (Any Platform)
```bash
python3 build.py
```
### macOS Specific
```bash
./build_macos.sh
```
or
```bash
source venv/bin/activate
pyinstaller ESP32_Flasher_macOS.spec
```
### Windows Specific
```batch
build_from_spec.bat
```
## Distribution
### macOS
- Distribute the entire `ESP32_Flasher.app` folder (it's a bundle)
- Users may need to run: `xattr -cr ESP32_Flasher.app` if macOS blocks it
- Consider creating a DMG for easier distribution
### Windows
- Distribute the single `ESP32_Flasher.exe` file
- No installation required
- May trigger Windows SmartScreen (normal for unsigned apps)
## File Structure
```
flash_tool/
├── dist/ # Build output
│ ├── ESP32_Flasher.app # macOS bundle
│ └── ESP32_Flasher # Standalone binary
├── build/ # Build artifacts
├── venv/ # Python virtual environment (macOS)
├── gui_flasher.py # Source code
├── ESP32_Flasher_macOS.spec # macOS build config
├── ESP32_Flasher_Windows.spec # Windows build config
├── build.py # Universal build script
├── build_macos.sh # macOS build script
└── build_from_spec.bat # Windows build script
```
## Next Steps
1. **Testing**: Test the app with actual ESP32 hardware and firmware
2. **Icons**: Add custom icons (.icns for macOS, .ico for Windows)
3. **Code Signing**: Sign the executables for production distribution
4. **DMG Creation**: Package macOS app in a DMG for easier distribution
5. **Windows Installer**: Consider creating an NSIS or WiX installer
## Troubleshooting
### "App is damaged" on macOS
```bash
xattr -cr dist/ESP32_Flasher.app
```
### Port permission issues on Linux
```bash
sudo usermod -a -G dialout $USER
```
(Logout/login required)
### Build fails on macOS
Make sure virtual environment is active:
```bash
source venv/bin/activate
```

View File

@@ -0,0 +1,43 @@
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(
['gui_flasher.py'],
pathex=[],
binaries=[],
datas=[('requirements.txt', '.')],
hiddenimports=['serial.tools.list_ports'],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='ESP32_Flasher',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True, # Set to False for windowed mode
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)

View File

@@ -0,0 +1,45 @@
# -*- mode: python ; coding: utf-8 -*-
# PyInstaller spec file for Windows executable
block_cipher = None
a = Analysis(
['gui_flasher.py'],
pathex=[],
binaries=[],
datas=[('requirements.txt', '.')],
hiddenimports=['serial.tools.list_ports'],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='ESP32_Flasher',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=False, # Set to False for windowed mode (no console)
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
icon=None, # Add path to .ico file if you have one
)

View File

@@ -0,0 +1,56 @@
# -*- mode: python ; coding: utf-8 -*-
# PyInstaller spec file for macOS executable
block_cipher = None
a = Analysis(
['gui_flasher.py'],
pathex=[],
binaries=[],
datas=[('requirements.txt', '.')],
hiddenimports=['serial.tools.list_ports'],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='ESP32_Flasher',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=False, # macOS app bundle (no console window)
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
# Create macOS app bundle
app = BUNDLE(
exe,
name='ESP32_Flasher.app',
icon=None, # Add path to .icns file if you have one
bundle_identifier='com.soundshot.esp32flasher',
info_plist={
'NSPrincipalClass': 'NSApplication',
'NSHighResolutionCapable': 'True',
},
)

View File

@@ -0,0 +1,81 @@
# -*- mode: python ; coding: utf-8 -*-
# PyInstaller spec file for Windows executable (Web-based UI)
block_cipher = None
import os
import sys
# Find esptool installation dynamically
try:
import esptool
esptool_dir = os.path.dirname(esptool.__file__)
print(f"Found esptool at: {esptool_dir}")
except ImportError:
print("Warning: esptool not found, stub files will not be included")
esptool_dir = None
# Build datas list - include all esptool data files
datas_list = [
('templates', 'templates'),
('static', 'static'),
]
# Add esptool targets directory (includes stub_flasher JSON files)
if esptool_dir and os.path.exists(os.path.join(esptool_dir, 'targets')):
targets_dir = os.path.join(esptool_dir, 'targets')
datas_list.append((targets_dir, 'esptool/targets'))
print(f"Including esptool targets from: {targets_dir}")
a = Analysis(
['web_flasher.py'],
pathex=[],
binaries=[],
datas=datas_list,
hiddenimports=[
'serial.tools.list_ports',
'flask',
'jinja2',
'werkzeug',
'flask_socketio',
'socketio',
'engineio.async_drivers.threading',
'esptool',
'esptool.cmds',
'esptool.loader',
'esptool.util',
],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='ESP32_Flasher',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=False, # No console window
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
icon=None, # Add path to .ico file if you have one
)

View File

@@ -0,0 +1,69 @@
# -*- mode: python ; coding: utf-8 -*-
# PyInstaller spec file for macOS executable (Web-based UI)
block_cipher = None
a = Analysis(
['web_flasher.py'],
pathex=[],
binaries=[],
datas=[
('templates', 'templates'),
('static', 'static'),
],
hiddenimports=[
'serial.tools.list_ports',
'flask',
'jinja2',
'werkzeug',
'flask_socketio',
'socketio',
'engineio.async_drivers.threading',
],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='ESP32_Flasher',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=False, # macOS app bundle (no console window)
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
# Create macOS app bundle
app = BUNDLE(
exe,
name='ESP32_Flasher.app',
icon=None, # Add path to .icns file if you have one
bundle_identifier='com.soundshot.esp32flasher',
info_plist={
'NSPrincipalClass': 'NSApplication',
'NSHighResolutionCapable': 'True',
'CFBundleShortVersionString': '1.0.0',
'CFBundleDisplayName': 'ESP32 Flasher',
},
)

148
flash_tool/README.md Normal file
View File

@@ -0,0 +1,148 @@
# ESP32 Firmware Flasher GUI
A cross-platform GUI tool for flashing ESP32 firmware packages.
## Features
- Simple, user-friendly graphical interface
- Cross-platform support (Windows, macOS, Linux)
- Automatic serial port detection
- Firmware package validation (.zip files)
- Real-time flashing progress output
- Configurable flash parameters
## Building Executables
### Universal Build (Recommended)
The easiest way to build for your current platform:
```bash
python build.py
```
This script will:
1. Detect your platform automatically
2. Install required dependencies
3. Build the appropriate executable
### Platform-Specific Builds
#### macOS
```bash
./build_macos.sh
```
This creates an application bundle at `dist/ESP32_Flasher.app`
To run:
```bash
open dist/ESP32_Flasher.app
```
#### Windows
```batch
build_from_spec.bat
```
This creates an executable at `dist\ESP32_Flasher.exe`
#### Linux
```bash
python build.py
```
Creates an executable at `dist/ESP32_Flasher`
## Running Without Building
You can run the GUI directly with Python:
```bash
python gui_flasher.py
```
### Requirements
```bash
pip install -r requirements.txt
```
## Firmware Package Format
The flasher expects a `.zip` file containing these files:
- `bootloader.bin` - ESP32 bootloader
- `partition-table.bin` - Partition table
- `ota_data_initial.bin` - OTA data
- `soundshot.bin` - Main application firmware
## Usage
1. Launch the ESP32_Flasher application
2. Select your ESP32's serial port (or click Refresh)
3. Browse and select your firmware `.zip` package
4. (Optional) Adjust flash settings if needed
5. Click "Flash Firmware"
6. Wait for the process to complete
## Flash Settings
Default settings work for most ESP32 boards:
- **Chip**: esp32
- **Baud Rate**: 460800 (faster) or 115200 (more reliable)
- **Flash Mode**: dio
- **Flash Freq**: 40m
- **Flash Size**: 2MB (adjust based on your board)
## Troubleshooting
### Port Not Detected
- Ensure ESP32 is connected via USB
- Install CH340/CP2102 drivers if needed (Windows/macOS)
- On Linux, add user to `dialout` group: `sudo usermod -a -G dialout $USER`
### Flash Failed
- Try lower baud rate (115200)
- Press and hold BOOT button during flash
- Check USB cable quality
- Verify firmware package integrity
### macOS: "App is damaged"
Run this command to allow the app:
```bash
xattr -cr dist/ESP32_Flasher.app
```
## Development
### Project Structure
```
flash_tool/
├── gui_flasher.py # Main GUI application
├── ESP32_Flasher_macOS.spec # PyInstaller spec for macOS
├── ESP32_Flasher_Windows.spec # PyInstaller spec for Windows
├── build.py # Universal build script
├── build_macos.sh # macOS build script
├── build_from_spec.bat # Windows build script
├── requirements.txt # Python dependencies
└── README.md # This file
```
### Dependencies
- **tkinter**: GUI framework (included with Python)
- **pyserial**: Serial port communication
- **esptool**: ESP32 flashing utility
- **pyinstaller**: Executable builder
## License
[Your license here]
## Support
For issues and questions, please contact [your contact info].

BIN
flash_tool/SoundShot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

171
flash_tool/build.py Executable file
View File

@@ -0,0 +1,171 @@
#!/usr/bin/env python3
"""
Universal build script for ESP32 Flasher GUI
Automatically detects platform and builds the appropriate executable
"""
import sys
import subprocess
import platform
import os
def get_platform():
"""Detect the current platform"""
system = platform.system()
if system == "Darwin":
return "macos"
elif system == "Windows":
return "windows"
elif system == "Linux":
return "linux"
else:
return "unknown"
def setup_venv():
"""Create and setup virtual environment if on macOS"""
if platform.system() == "Darwin":
if not os.path.exists("venv"):
print("Creating virtual environment (required on macOS)...")
try:
subprocess.run([sys.executable, "-m", "venv", "venv"], check=True)
print("✓ Virtual environment created")
except subprocess.CalledProcessError as e:
print(f"✗ Failed to create virtual environment: {e}")
return False
# Use venv Python for the rest of the script
venv_python = os.path.join("venv", "bin", "python3")
if os.path.exists(venv_python):
return venv_python
return sys.executable
def install_dependencies(python_exe=None):
"""Install required Python packages"""
if python_exe is None:
python_exe = sys.executable
print("Installing dependencies...")
packages = ["pyinstaller", "esptool", "pyserial"]
try:
subprocess.run([python_exe, "-m", "pip", "install"] + packages, check=True)
print("✓ Dependencies installed successfully")
return True
except subprocess.CalledProcessError as e:
print(f"✗ Failed to install dependencies: {e}")
return False
def build_macos():
"""Build macOS application bundle"""
print("\n=== Building for macOS ===")
spec_file = "ESP32_Flasher_macOS.spec"
if not os.path.exists(spec_file):
print(f"✗ Error: {spec_file} not found")
return False
try:
subprocess.run(["pyinstaller", spec_file], check=True)
if os.path.exists("dist/ESP32_Flasher.app"):
print("\n✓ Build complete!")
print(f"\nApplication created at: dist/ESP32_Flasher.app")
print("\nTo run the application:")
print(" open dist/ESP32_Flasher.app")
return True
else:
print("\n✗ Build failed - application not found in dist folder")
return False
except subprocess.CalledProcessError as e:
print(f"\n✗ Build failed: {e}")
return False
def build_windows():
"""Build Windows executable"""
print("\n=== Building for Windows ===")
spec_file = "ESP32_Flasher_Windows.spec"
if not os.path.exists(spec_file):
print(f"✗ Error: {spec_file} not found")
return False
try:
subprocess.run(["pyinstaller", spec_file], check=True)
if os.path.exists("dist/ESP32_Flasher.exe"):
print("\n✓ Build complete!")
print(f"\nExecutable created at: dist\\ESP32_Flasher.exe")
print("\nYou can now distribute ESP32_Flasher.exe to users.")
return True
else:
print("\n✗ Build failed - executable not found in dist folder")
return False
except subprocess.CalledProcessError as e:
print(f"\n✗ Build failed: {e}")
return False
def build_linux():
"""Build Linux executable"""
print("\n=== Building for Linux ===")
print("Note: Linux users typically prefer to run Python scripts directly.")
print("Creating standalone executable anyway...")
# Use macOS spec as template for Linux (both use ELF format)
spec_file = "ESP32_Flasher_macOS.spec"
if not os.path.exists(spec_file):
print(f"✗ Error: {spec_file} not found")
return False
try:
subprocess.run(["pyinstaller", spec_file], check=True)
if os.path.exists("dist/ESP32_Flasher"):
print("\n✓ Build complete!")
print(f"\nExecutable created at: dist/ESP32_Flasher")
print("\nTo run:")
print(" ./dist/ESP32_Flasher")
return True
else:
print("\n✗ Build failed - executable not found in dist folder")
return False
except subprocess.CalledProcessError as e:
print(f"\n✗ Build failed: {e}")
return False
def main():
"""Main build function"""
print("=== ESP32 Flasher - Universal Build Script ===\n")
# Detect platform
current_platform = get_platform()
print(f"Detected platform: {current_platform}")
if current_platform == "unknown":
print("✗ Unsupported platform")
return 1
# Setup virtual environment if needed (macOS)
python_exe = setup_venv()
# Install dependencies
if not install_dependencies(python_exe):
return 1
# Build for detected platform
success = False
if current_platform == "macos":
success = build_macos()
elif current_platform == "windows":
success = build_windows()
elif current_platform == "linux":
success = build_linux()
return 0 if success else 1
if __name__ == "__main__":
sys.exit(main())

7
flash_tool/build_exe.bat Normal file
View File

@@ -0,0 +1,7 @@
@echo off
echo Building ESP32 Flasher Executable...
cd /d "%~dp0"
python build_executable.py
echo.
echo Build complete! Check the 'dist' folder for ESP32_Flasher.exe
pause

View File

@@ -0,0 +1,49 @@
#!/usr/bin/env python3
"""
Build script to create standalone executable for ESP32 Flash GUI
"""
import subprocess
import sys
import os
from pathlib import Path
def install_requirements():
"""Install required packages"""
print("Installing requirements...")
subprocess.check_call([sys.executable, "-m", "pip", "install", "-r", "requirements.txt"])
def build_executable():
"""Build standalone executable using PyInstaller"""
print("Building standalone executable...")
# PyInstaller command
cmd = [
sys.executable, "-m", "PyInstaller",
"--onefile", # Single executable file
"--windowed", # No console window (remove if you want console)
"--name", "ESP32_Flasher",
"--icon", "icon.ico" if Path("icon.ico").exists() else None,
"--add-data", "requirements.txt;.", # Include requirements file
"gui_flasher.py"
]
# Remove None values
cmd = [arg for arg in cmd if arg is not None]
subprocess.check_call(cmd)
print("\nExecutable built successfully!")
print("Find it in the 'dist' folder as 'ESP32_Flasher.exe'")
def main():
os.chdir(Path(__file__).parent)
try:
install_requirements()
build_executable()
except subprocess.CalledProcessError as e:
print(f"Error: {e}")
sys.exit(1)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,24 @@
@echo off
echo === ESP32 Flasher - Windows Build Script ===
echo.
echo Installing dependencies...
pip install pyinstaller esptool pyserial
echo.
echo Building Windows executable...
pyinstaller ESP32_Flasher_Windows.spec
echo.
if exist "dist\ESP32_Flasher.exe" (
echo Build complete!
echo.
echo Executable created at: dist\ESP32_Flasher.exe
echo.
echo You can now distribute ESP32_Flasher.exe to users.
) else (
echo Build failed - executable not found in dist folder
exit /b 1
)
pause

45
flash_tool/build_macos.sh Executable file
View File

@@ -0,0 +1,45 @@
#!/bin/bash
# Build script for macOS executable
echo "=== ESP32 Flasher - macOS Build Script ==="
echo ""
# Check if running on macOS
if [[ "$OSTYPE" != "darwin"* ]]; then
echo "❌ Error: This script is for macOS only."
echo " Use build_from_spec.bat on Windows."
exit 1
fi
# Create virtual environment if it doesn't exist
if [ ! -d "venv" ]; then
echo "Creating virtual environment..."
python3 -m venv venv
fi
echo "Activating virtual environment..."
source venv/bin/activate
echo "Installing dependencies..."
pip install pyinstaller esptool pyserial
echo ""
echo "Building macOS application bundle..."
pyinstaller ESP32_Flasher_macOS.spec
echo ""
if [ -d "dist/ESP32_Flasher.app" ]; then
echo "✓ Build complete!"
echo ""
echo "Application created at: dist/ESP32_Flasher.app"
echo ""
echo "To run the application:"
echo " open dist/ESP32_Flasher.app"
echo ""
echo "To distribute, you can:"
echo " 1. Zip the .app bundle"
echo " 2. Create a DMG installer (requires additional tools)"
else
echo "❌ Build failed - application not found in dist folder"
exit 1
fi

43
flash_tool/build_web_macos.sh Executable file
View File

@@ -0,0 +1,43 @@
#!/bin/bash
# Build script for macOS executable (Web-based UI)
echo "=== ESP32 Flasher (Web UI) - macOS Build Script ==="
echo ""
# Check if running on macOS
if [[ "$OSTYPE" != "darwin"* ]]; then
echo "❌ Error: This script is for macOS only."
echo " Use build_web_windows.bat on Windows."
exit 1
fi
# Create virtual environment if it doesn't exist
if [ ! -d "venv" ]; then
echo "Creating virtual environment..."
python3 -m venv venv
fi
echo "Activating virtual environment..."
source venv/bin/activate
echo "Installing dependencies..."
pip install -r requirements.txt
echo ""
echo "Building macOS application bundle..."
pyinstaller ESP32_WebFlasher_macOS.spec
echo ""
if [ -d "dist/ESP32_Flasher.app" ]; then
echo "✓ Build complete!"
echo ""
echo "Application created at: dist/ESP32_Flasher.app"
echo ""
echo "To run the application:"
echo " open dist/ESP32_Flasher.app"
echo ""
echo "The app will open in your default web browser!"
else
echo "❌ Build failed - application not found in dist folder"
exit 1
fi

View File

@@ -0,0 +1,32 @@
@echo off
echo === ESP32 Flasher (Web UI) - Windows Build Script ===
echo.
REM Check if running on Windows
if not "%OS%"=="Windows_NT" (
echo Error: This script is for Windows only.
echo Use build_web_macos.sh on macOS.
exit /b 1
)
echo Installing dependencies...
pip install -r requirements.txt
echo.
echo Building Windows executable...
pyinstaller ESP32_WebFlasher_Windows.spec
echo.
if exist "dist\ESP32_Flasher.exe" (
echo Build complete!
echo.
echo Executable created at: dist\ESP32_Flasher.exe
echo.
echo The app will open in your default web browser!
echo You can now distribute ESP32_Flasher.exe to users.
) else (
echo Build failed - executable not found in dist folder
exit /b 1
)
pause

View File

@@ -0,0 +1,14 @@
@echo off
echo Installing auto-py-to-exe...
pip install auto-py-to-exe esptool pyserial
echo.
echo Starting auto-py-to-exe GUI...
echo.
echo Configure the following settings:
echo - Script Location: gui_flasher.py
echo - Onefile: One File
echo - Console Window: Console Based (or Window Based if you prefer no console)
echo - Additional Files: Add requirements.txt
echo.
auto-py-to-exe

View File

@@ -27,8 +27,10 @@ def main():
# Define binaries and offsets
bootloader = root / "build" / "bootloader" / "bootloader.bin"
firmware = root / "build" / f"{project}.bin"
ota_intitial = root / "build" / "ota_data_initial.bin"
partition = root / "build" / "partition_table" / "partition-table.bin"
# Verify binaries exist
for f in [bootloader, firmware, partition]:
if not f.exists():
@@ -47,8 +49,9 @@ def main():
"--flash-mode", flash_mode, # ✅ hyphenated flags
"--flash-freq", flash_freq,
"--flash-size", flash_size,
"0x0", str(bootloader),
"0x10000", str(firmware),
"0x1000", str(bootloader),
"0x20000", str(firmware),
"0x11000", str(ota_intitial),
"0x8000", str(partition)
]

337
flash_tool/gui_flasher.py Normal file
View File

@@ -0,0 +1,337 @@
#!/usr/bin/env python3
"""
ESP32 Firmware Flash GUI Tool
Simple GUI interface for flashing firmware packages to ESP32 devices
"""
import tkinter as tk
from tkinter import ttk, filedialog, messagebox, scrolledtext
import subprocess
import sys
import threading
import zipfile
import tempfile
import os
from pathlib import Path
import serial.tools.list_ports
class ESP32FlasherGUI:
def __init__(self, root):
self.root = root
self.root.title("ESP32 Firmware Flasher")
self.root.geometry("600x500")
# Configure colors for dark mode compatibility
self.setup_colors()
# Variables
self.port_var = tk.StringVar()
self.firmware_path_var = tk.StringVar()
self.temp_dir = None
self.setup_ui()
self.refresh_ports()
def setup_colors(self):
"""Configure colors that work in both light and dark mode"""
# Try to detect dark mode on macOS
try:
import subprocess
result = subprocess.run(
['defaults', 'read', '-g', 'AppleInterfaceStyle'],
capture_output=True,
text=True
)
is_dark_mode = (result.returncode == 0 and 'Dark' in result.stdout)
except:
is_dark_mode = False
if is_dark_mode:
# Dark mode colors
self.bg_color = '#2b2b2b'
self.fg_color = '#ffffff'
self.text_bg = '#1e1e1e'
self.text_fg = '#d4d4d4'
self.button_bg = '#3c3c3c'
else:
# Light mode colors
self.bg_color = '#f0f0f0'
self.fg_color = '#000000'
self.text_bg = '#ffffff'
self.text_fg = '#000000'
self.button_bg = '#e0e0e0'
# Configure root window
self.root.configure(bg=self.bg_color)
def setup_ui(self):
# Main frame
main_frame = tk.Frame(self.root, bg=self.bg_color, padx=10, pady=10)
main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
# Configure grid weights
self.root.columnconfigure(0, weight=1)
self.root.rowconfigure(0, weight=1)
main_frame.columnconfigure(1, weight=1)
# Port selection
tk.Label(main_frame, text="Serial Port:", bg=self.bg_color, fg=self.fg_color).grid(
row=0, column=0, sticky=tk.W, pady=5)
port_frame = tk.Frame(main_frame, bg=self.bg_color)
port_frame.grid(row=0, column=1, sticky=(tk.W, tk.E), pady=5)
port_frame.columnconfigure(0, weight=1)
self.port_combo = ttk.Combobox(port_frame, textvariable=self.port_var, state="readonly")
self.port_combo.grid(row=0, column=0, sticky=(tk.W, tk.E), padx=(0, 5))
tk.Button(port_frame, text="Refresh", command=self.refresh_ports,
bg=self.button_bg, fg=self.fg_color, relief=tk.RAISED).grid(row=0, column=1)
# Firmware package selection
tk.Label(main_frame, text="Firmware Package:", bg=self.bg_color, fg=self.fg_color).grid(
row=1, column=0, sticky=tk.W, pady=5)
firmware_frame = tk.Frame(main_frame, bg=self.bg_color)
firmware_frame.grid(row=1, column=1, sticky=(tk.W, tk.E), pady=5)
firmware_frame.columnconfigure(0, weight=1)
self.firmware_entry = tk.Entry(firmware_frame, textvariable=self.firmware_path_var,
state="readonly", bg=self.text_bg, fg=self.text_fg,
relief=tk.SUNKEN, borderwidth=2)
self.firmware_entry.grid(row=0, column=0, sticky=(tk.W, tk.E), padx=(0, 5))
tk.Button(firmware_frame, text="Browse", command=self.browse_firmware,
bg=self.button_bg, fg=self.fg_color, relief=tk.RAISED).grid(row=0, column=1)
# Flash settings frame
settings_frame = tk.LabelFrame(main_frame, text="Flash Settings",
bg=self.bg_color, fg=self.fg_color,
relief=tk.GROOVE, borderwidth=2, padx=5, pady=5)
settings_frame.grid(row=2, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=10)
settings_frame.columnconfigure(1, weight=1)
# Flash settings
tk.Label(settings_frame, text="Chip:", bg=self.bg_color, fg=self.fg_color).grid(
row=0, column=0, sticky=tk.W, pady=2, padx=(0, 10))
self.chip_var = tk.StringVar(value="esp32")
tk.Entry(settings_frame, textvariable=self.chip_var, width=15,
bg=self.text_bg, fg=self.text_fg, relief=tk.SUNKEN).grid(row=0, column=1, sticky=tk.W, pady=2)
tk.Label(settings_frame, text="Baud Rate:", bg=self.bg_color, fg=self.fg_color).grid(
row=1, column=0, sticky=tk.W, pady=2, padx=(0, 10))
self.baud_var = tk.StringVar(value="460800")
tk.Entry(settings_frame, textvariable=self.baud_var, width=15,
bg=self.text_bg, fg=self.text_fg, relief=tk.SUNKEN).grid(row=1, column=1, sticky=tk.W, pady=2)
tk.Label(settings_frame, text="Flash Mode:", bg=self.bg_color, fg=self.fg_color).grid(
row=2, column=0, sticky=tk.W, pady=2, padx=(0, 10))
self.flash_mode_var = tk.StringVar(value="dio")
tk.Entry(settings_frame, textvariable=self.flash_mode_var, width=15,
bg=self.text_bg, fg=self.text_fg, relief=tk.SUNKEN).grid(row=2, column=1, sticky=tk.W, pady=2)
tk.Label(settings_frame, text="Flash Freq:", bg=self.bg_color, fg=self.fg_color).grid(
row=3, column=0, sticky=tk.W, pady=2, padx=(0, 10))
self.flash_freq_var = tk.StringVar(value="40m")
tk.Entry(settings_frame, textvariable=self.flash_freq_var, width=15,
bg=self.text_bg, fg=self.text_fg, relief=tk.SUNKEN).grid(row=3, column=1, sticky=tk.W, pady=2)
tk.Label(settings_frame, text="Flash Size:", bg=self.bg_color, fg=self.fg_color).grid(
row=4, column=0, sticky=tk.W, pady=2, padx=(0, 10))
self.flash_size_var = tk.StringVar(value="2MB")
tk.Entry(settings_frame, textvariable=self.flash_size_var, width=15,
bg=self.text_bg, fg=self.text_fg, relief=tk.SUNKEN).grid(row=4, column=1, sticky=tk.W, pady=2)
# Flash button
self.flash_button = tk.Button(main_frame, text="Flash Firmware", command=self.flash_firmware,
bg=self.button_bg, fg=self.fg_color,
relief=tk.RAISED, padx=20, pady=5,
font=('TkDefaultFont', 10, 'bold'))
self.flash_button.grid(row=3, column=0, columnspan=2, pady=10)
# Progress bar
self.progress = ttk.Progressbar(main_frame, mode='indeterminate')
self.progress.grid(row=4, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=5)
# Log output
tk.Label(main_frame, text="Output:", bg=self.bg_color, fg=self.fg_color).grid(
row=5, column=0, sticky=tk.W, pady=(10, 0))
self.log_text = scrolledtext.ScrolledText(
main_frame,
height=15,
width=70,
bg=self.text_bg,
fg=self.text_fg,
insertbackground=self.text_fg, # Cursor color
relief=tk.SUNKEN,
borderwidth=2
)
self.log_text.grid(row=6, column=0, columnspan=2, sticky=(tk.W, tk.E, tk.N, tk.S), pady=5)
main_frame.rowconfigure(6, weight=1)
def refresh_ports(self):
"""Refresh the list of available serial ports"""
ports = [port.device for port in serial.tools.list_ports.comports()]
self.port_combo['values'] = ports
if ports and not self.port_var.get():
self.port_var.set(ports[0])
def browse_firmware(self):
"""Browse for firmware package (.zip file)"""
filename = filedialog.askopenfilename(
title="Select Firmware Package",
filetypes=[("ZIP files", "*.zip"), ("All files", "*.*")]
)
if filename:
self.firmware_path_var.set(filename)
self.validate_firmware_package(filename)
def validate_firmware_package(self, zip_path):
"""Validate that the ZIP contains required firmware files"""
required_files = [
"bootloader.bin",
"soundshot.bin",
"ota_data_initial.bin",
"partition-table.bin"
]
try:
with zipfile.ZipFile(zip_path, 'r') as zip_file:
zip_contents = zip_file.namelist()
missing_files = []
for required_file in required_files:
if required_file not in zip_contents:
missing_files.append(required_file)
if missing_files:
messagebox.showwarning(
"Invalid Package",
f"Missing required files:\n{', '.join(missing_files)}"
)
return False
else:
self.log_message(f"✓ Valid firmware package: {Path(zip_path).name}")
return True
except zipfile.BadZipFile:
messagebox.showerror("Error", "Invalid ZIP file")
return False
def log_message(self, message):
"""Add message to log output"""
self.log_text.insert(tk.END, message + "\n")
self.log_text.see(tk.END)
self.root.update()
def extract_firmware_package(self, zip_path):
"""Extract firmware package to temporary directory"""
if self.temp_dir:
# Clean up previous temp dir
import shutil
shutil.rmtree(self.temp_dir, ignore_errors=True)
self.temp_dir = tempfile.mkdtemp(prefix="esp32_flash_")
with zipfile.ZipFile(zip_path, 'r') as zip_file:
zip_file.extractall(self.temp_dir)
return self.temp_dir
def flash_firmware(self):
"""Flash the firmware to ESP32"""
# Validate inputs
if not self.port_var.get():
messagebox.showerror("Error", "Please select a serial port")
return
if not self.firmware_path_var.get():
messagebox.showerror("Error", "Please select a firmware package")
return
# Disable flash button and start progress
self.flash_button.config(state="disabled")
self.progress.start()
self.log_text.delete(1.0, tk.END)
# Run flash in separate thread to prevent UI freezing
thread = threading.Thread(target=self._flash_worker)
thread.daemon = True
thread.start()
def _flash_worker(self):
"""Worker thread for flashing firmware"""
try:
# Extract firmware package
self.log_message("Extracting firmware package...")
temp_dir = self.extract_firmware_package(self.firmware_path_var.get())
# Define file paths
bootloader = os.path.join(temp_dir, "bootloader.bin")
firmware = os.path.join(temp_dir, "soundshot.bin")
ota_initial = os.path.join(temp_dir, "ota_data_initial.bin")
partition = os.path.join(temp_dir, "partition-table.bin")
# Build esptool command
cmd = [
sys.executable, "-m", "esptool",
"--chip", self.chip_var.get(),
"--port", self.port_var.get(),
"--baud", self.baud_var.get(),
"--before", "default_reset",
"--after", "hard_reset",
"write-flash",
"--flash-mode", self.flash_mode_var.get(),
"--flash-freq", self.flash_freq_var.get(),
"--flash-size", self.flash_size_var.get(),
"0x1000", bootloader,
"0x20000", firmware,
"0x11000", ota_initial,
"0x8000", partition
]
self.log_message(f"Running: {' '.join(cmd)}\n")
# Run esptool
process = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
bufsize=1,
universal_newlines=True
)
# Stream output
for line in process.stdout:
self.root.after(0, self.log_message, line.rstrip())
process.wait()
if process.returncode == 0:
self.root.after(0, self.log_message, "\n✓ Flash completed successfully!")
self.root.after(0, messagebox.showinfo, "Success", "Firmware flashed successfully!")
else:
self.root.after(0, self.log_message, f"\n✗ Flash failed with return code {process.returncode}")
self.root.after(0, messagebox.showerror, "Error", "Flash operation failed!")
except Exception as e:
self.root.after(0, self.log_message, f"\n✗ Error: {str(e)}")
self.root.after(0, messagebox.showerror, "Error", f"Flash operation failed: {str(e)}")
finally:
# Re-enable UI
self.root.after(0, self.progress.stop)
self.root.after(0, lambda: self.flash_button.config(state="normal"))
# Clean up temp directory
if self.temp_dir:
import shutil
shutil.rmtree(self.temp_dir, ignore_errors=True)
self.temp_dir = None
def main():
root = tk.Tk()
app = ESP32FlasherGUI(root)
root.mainloop()
if __name__ == "__main__":
main()

View File

@@ -3,11 +3,7 @@ import serial
import sys
import time
from pathlib import Path
# import colorama
# colorama.just_fix_windows_console()
print("\x1b[0;32mThis should be green\x1b[0m")
print("\x1b[0;31mThis should be red\x1b[0m")
def main():
# Load config
@@ -27,6 +23,10 @@ def main():
try:
with serial.Serial(port, baud, timeout=0.5) as ser:
# 🔧 Clear DTR and RTS lines (common for ESP32 reset handling)
ser.dtr = False
ser.rts = False
print("[Serial Monitor] Press Ctrl+C to exit.\n")
while True:
if ser.in_waiting:

View File

@@ -0,0 +1,6 @@
esptool>=4.0
pyserial>=3.5
pyinstaller>=5.0
flask>=2.3.0
flask-socketio>=5.3.0
python-socketio>=5.11.0

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

View File

@@ -0,0 +1,570 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SoundShot Firmware Flasher</title>
<script src="https://cdn.socket.io/4.5.4/socket.io.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
background: linear-gradient(135deg, #d94125 0%, #a33318 100%);
min-height: 100vh;
padding: 20px;
display: flex;
justify-content: center;
align-items: center;
}
.container {
background: white;
border-radius: 12px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
max-width: 700px;
width: 100%;
padding: 40px;
}
.header {
text-align: center;
margin-bottom: 30px;
background: linear-gradient(135deg, #2c2c2c 0%, #1a1a1a 100%);
padding: 40px 20px 30px 20px;
margin: -40px -40px 30px -40px;
border-radius: 12px 12px 0 0;
}
.logo {
max-width: 450px;
width: 100%;
height: auto;
margin-bottom: 10px;
}
h1 {
color: #c04026;
margin-bottom: 10px;
font-size: 28px;
}
.subtitle {
color: #d0d0d0;
font-size: 14px;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 8px;
color: #333;
font-weight: 500;
font-size: 14px;
}
input[type="text"],
select {
width: 100%;
padding: 12px;
border: 2px solid #e0e0e0;
border-radius: 8px;
font-size: 14px;
transition: border-color 0.3s;
}
input[type="text"]:focus,
select:focus {
outline: none;
border-color: #c04026;
}
.file-input-wrapper {
position: relative;
overflow: hidden;
display: inline-block;
width: 100%;
}
.file-input-wrapper input[type="file"] {
position: absolute;
left: -9999px;
}
.file-input-label {
display: block;
padding: 12px;
border: 2px dashed #e0e0e0;
border-radius: 8px;
text-align: center;
cursor: pointer;
transition: all 0.3s;
color: #666;
}
.file-input-label:hover {
border-color: #c04026;
background: #fef6f4;
}
.file-selected {
border-color: #c04026;
border-style: solid;
background: #fef6f4;
color: #c04026;
}
.btn {
width: 100%;
padding: 14px;
border: none;
border-radius: 8px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s;
}
.btn-primary {
background: linear-gradient(135deg, #d94125 0%, #a33318 100%);
color: white;
}
.btn-primary:hover:not(:disabled) {
transform: translateY(-2px);
box-shadow: 0 10px 20px rgba(217, 65, 37, 0.3);
}
.btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.output-section {
margin-top: 30px;
}
.output-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.output-toggle {
background: #f0f0f0;
border: none;
border-radius: 6px;
padding: 8px 16px;
cursor: pointer;
font-size: 13px;
color: #333;
transition: background 0.3s;
}
.output-toggle:hover {
background: #e0e0e0;
}
.output-box {
background: #1e1e1e;
color: #d4d4d4;
padding: 20px;
border-radius: 8px;
font-family: 'Courier New', monospace;
font-size: 13px;
max-height: 400px;
overflow-y: auto;
line-height: 1.6;
white-space: pre-wrap;
word-wrap: break-word;
display: none;
}
.output-box.expanded {
display: block;
}
.output-box:empty::before {
content: 'Output will appear here...';
color: #666;
}
.progress-bar {
width: 100%;
height: 8px;
background: #e0e0e0;
border-radius: 4px;
overflow: hidden;
margin: 20px 0;
display: none;
}
.progress-bar.active {
display: block;
}
.progress-bar-fill {
height: 100%;
background: linear-gradient(90deg, #d94125, #a33318);
width: 0%;
transition: width 0.3s ease-out;
}
.progress-bar-fill.indeterminate {
animation: progress 2s ease-in-out infinite;
}
@keyframes progress {
0% { width: 0%; }
50% { width: 100%; }
100% { width: 0%; }
}
.progress-text {
text-align: center;
font-size: 13px;
color: #c04026;
font-weight: 600;
margin-top: 5px;
display: none;
}
.progress-text.active {
display: block;
}
.status-message {
padding: 12px;
border-radius: 8px;
margin: 15px 0;
font-weight: 500;
display: none;
}
.status-message.success {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
display: block;
}
.status-message.error {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
display: block;
}
.refresh-btn {
padding: 8px 16px;
background: #f0f0f0;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
color: #333;
margin-left: 10px;
transition: background 0.3s;
}
.refresh-btn:hover {
background: #e0e0e0;
}
.port-section {
display: flex;
align-items: flex-end;
gap: 10px;
}
.port-section .form-group {
flex: 1;
margin-bottom: 0;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<img src="/static/SoundShot.png" alt="SoundShot" class="logo">
<p class="subtitle">Firmware Flasher</p>
</div>
<form id="flashForm" onsubmit="return false;">
<div class="port-section">
<div class="form-group">
<label for="port">Serial Port</label>
<select id="port" name="port" required>
<option value="">Select a port...</option>
</select>
</div>
<button type="button" class="refresh-btn" onclick="refreshPorts()">🔄 Refresh</button>
</div>
<div class="form-group">
<label for="firmware">Firmware Package (.zip)</label>
<div class="file-input-wrapper">
<input type="file" id="firmware" name="firmware" accept=".zip" required>
<label for="firmware" class="file-input-label" id="fileLabel">
📦 Click to select firmware package
</label>
</div>
</div>
<!-- Hidden fields with default values -->
<input type="hidden" id="chip" name="chip" value="esp32">
<input type="hidden" id="baud" name="baud" value="460800">
<input type="hidden" id="flash_mode" name="flash_mode" value="dio">
<input type="hidden" id="flash_freq" name="flash_freq" value="40m">
<input type="hidden" id="flash_size" name="flash_size" value="2MB">
<button type="submit" class="btn btn-primary" id="flashBtn">
⚡ Flash Firmware
</button>
<div class="progress-bar" id="progressBar">
<div class="progress-bar-fill" id="progressBarFill"></div>
</div>
<div class="progress-text" id="progressText">0%</div>
<div class="status-message" id="statusMessage"></div>
</form>
<div class="output-section">
<div class="output-header">
<label>Output Log</label>
<button type="button" class="output-toggle" id="outputToggle" onclick="toggleOutput()">▼ Show Details</button>
</div>
<div class="output-box" id="output"></div>
</div>
</div>
<script>
let statusCheckInterval = null;
let socket = null;
let heartbeatInterval = null;
// Toggle output visibility
function toggleOutput() {
const output = document.getElementById('output');
const toggle = document.getElementById('outputToggle');
if (output.classList.contains('expanded')) {
output.classList.remove('expanded');
toggle.textContent = '▼ Show Details';
} else {
output.classList.add('expanded');
toggle.textContent = '▲ Hide Details';
}
}
// Initialize Socket.IO connection
function initWebSocket() {
socket = io();
socket.on('connect', () => {
console.log('WebSocket connected');
// Start sending heartbeats every 2 seconds
heartbeatInterval = setInterval(() => {
socket.emit('heartbeat');
}, 2000);
});
socket.on('disconnect', () => {
console.log('WebSocket disconnected');
if (heartbeatInterval) {
clearInterval(heartbeatInterval);
heartbeatInterval = null;
}
});
socket.on('heartbeat_ack', (data) => {
// Heartbeat acknowledged
});
}
// Load ports on page load
document.addEventListener('DOMContentLoaded', () => {
initWebSocket();
refreshPorts();
});
// Clean up on page unload
window.addEventListener('beforeunload', () => {
if (socket) {
socket.disconnect();
}
});
// Handle file selection
document.getElementById('firmware').addEventListener('change', function(e) {
const fileLabel = document.getElementById('fileLabel');
if (e.target.files.length > 0) {
fileLabel.textContent = '✓ ' + e.target.files[0].name;
fileLabel.classList.add('file-selected');
} else {
fileLabel.textContent = '📦 Click to select firmware package';
fileLabel.classList.remove('file-selected');
}
});
async function refreshPorts() {
const portSelect = document.getElementById('port');
portSelect.innerHTML = '<option value="">Loading ports...</option>';
try {
const response = await fetch('/api/ports');
const data = await response.json();
if (data.success) {
portSelect.innerHTML = '<option value="">Select a port...</option>';
data.ports.forEach(port => {
const option = document.createElement('option');
option.value = port.device;
option.textContent = `${port.device} - ${port.description}`;
portSelect.appendChild(option);
});
} else {
portSelect.innerHTML = '<option value="">Error loading ports</option>';
}
} catch (error) {
portSelect.innerHTML = '<option value="">Error loading ports</option>';
console.error('Error:', error);
}
}
document.getElementById('flashForm').addEventListener('submit', async (e) => {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
const flashBtn = document.getElementById('flashBtn');
const progressBar = document.getElementById('progressBar');
const progressBarFill = document.getElementById('progressBarFill');
const progressText = document.getElementById('progressText');
const output = document.getElementById('output');
const outputToggle = document.getElementById('outputToggle');
const statusMessage = document.getElementById('statusMessage');
// Disable form
flashBtn.disabled = true;
flashBtn.textContent = '⚡ Flashing...';
progressBar.classList.add('active');
progressBarFill.classList.add('indeterminate');
progressBarFill.style.width = '0%';
progressText.classList.add('active');
progressText.textContent = '0%';
output.textContent = '';
output.classList.remove('expanded');
outputToggle.textContent = '▼ Show Details';
statusMessage.style.display = 'none';
statusMessage.className = 'status-message';
// Create FormData
const formData = new FormData(e.target);
try {
const response = await fetch('/api/flash', {
method: 'POST',
body: formData
});
const data = await response.json();
if (data.success) {
// Start checking status
statusCheckInterval = setInterval(checkStatus, 500);
} else {
showError(data.error);
resetForm();
}
} catch (error) {
showError('Failed to start flash: ' + error.message);
resetForm();
}
});
async function checkStatus() {
try {
const response = await fetch('/api/status');
const data = await response.json();
// Update output
const output = document.getElementById('output');
output.textContent = data.output.join('\n');
output.scrollTop = output.scrollHeight;
// Update progress bar
const progressBarFill = document.getElementById('progressBarFill');
const progressText = document.getElementById('progressText');
if (data.progress !== undefined && data.progress > 0) {
// Switch from indeterminate to determinate
progressBarFill.classList.remove('indeterminate');
progressBarFill.style.width = data.progress + '%';
progressText.textContent = data.progress.toFixed(1) + '%';
}
// Check if complete
if (!data.running && statusCheckInterval) {
clearInterval(statusCheckInterval);
statusCheckInterval = null;
const statusMessage = document.getElementById('statusMessage');
if (data.success === true) {
progressBarFill.style.width = '100%';
progressText.textContent = '100%';
statusMessage.textContent = '✓ Flash completed successfully!';
statusMessage.className = 'status-message success';
statusMessage.style.display = 'block';
} else if (data.success === false) {
statusMessage.textContent = '✗ Flash operation failed. Check the output log for details.';
statusMessage.className = 'status-message error';
statusMessage.style.display = 'block';
}
resetForm();
}
} catch (error) {
console.error('Error checking status:', error);
clearInterval(statusCheckInterval);
statusCheckInterval = null;
resetForm();
}
}
function showError(message) {
const statusMessage = document.getElementById('statusMessage');
statusMessage.textContent = '✗ ' + message;
statusMessage.className = 'status-message error';
statusMessage.style.display = 'block';
}
function resetForm() {
const flashBtn = document.getElementById('flashBtn');
const progressBar = document.getElementById('progressBar');
const progressBarFill = document.getElementById('progressBarFill');
const progressText = document.getElementById('progressText');
flashBtn.disabled = false;
flashBtn.textContent = '⚡ Flash Firmware';
// Delay hiding progress to let user see completion
setTimeout(() => {
progressBar.classList.remove('active');
progressText.classList.remove('active');
progressBarFill.classList.remove('indeterminate');
progressBarFill.style.width = '0%';
}, 2000);
}
</script>
</body>
</html>

343
flash_tool/web_flasher.py Normal file
View File

@@ -0,0 +1,343 @@
#!/usr/bin/env python3
"""
ESP32 Firmware Flash Web Server
Web-based GUI interface for flashing firmware packages to ESP32 devices
"""
from flask import Flask, render_template, request, jsonify, send_from_directory
from flask_socketio import SocketIO, emit
import subprocess
import sys
import threading
import zipfile
import tempfile
import os
import shutil
from pathlib import Path
import serial.tools.list_ports
import webbrowser
import socket
import time
import signal
app = Flask(__name__)
app.config['MAX_CONTENT_LENGTH'] = 100 * 1024 * 1024 # 100MB max file size
app.config['SECRET_KEY'] = 'esp32-flasher-secret-key'
socketio = SocketIO(app, cors_allowed_origins="*")
# Global state
flash_status = {
'running': False,
'output': [],
'success': None,
'progress': 0
}
# Client connection tracking
client_connected = False
last_heartbeat = time.time()
shutdown_timer = None
def get_free_port():
"""Find a free port to run the server on"""
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind(('', 0))
s.listen(1)
port = s.getsockname()[1]
return port
@app.route('/')
def index():
"""Serve the main page"""
return render_template('index.html')
@app.route('/static/<path:filename>')
def serve_static(filename):
"""Serve static files"""
static_dir = os.path.join(os.path.dirname(__file__), 'static')
return send_from_directory(static_dir, filename)
@app.route('/api/ports')
def get_ports():
"""Get list of available serial ports"""
try:
ports = [{'device': port.device, 'description': port.description}
for port in serial.tools.list_ports.comports()]
return jsonify({'success': True, 'ports': ports})
except Exception as e:
return jsonify({'success': False, 'error': str(e)})
@app.route('/api/flash', methods=['POST'])
def flash_firmware():
"""Flash firmware to ESP32"""
global flash_status
if flash_status['running']:
return jsonify({'success': False, 'error': 'Flash operation already in progress'})
# Get form data
port = request.form.get('port')
chip = request.form.get('chip', 'esp32')
baud = request.form.get('baud', '460800')
flash_mode = request.form.get('flash_mode', 'dio')
flash_freq = request.form.get('flash_freq', '40m')
flash_size = request.form.get('flash_size', '2MB')
# Get uploaded file
if 'firmware' not in request.files:
return jsonify({'success': False, 'error': 'No firmware file uploaded'})
firmware_file = request.files['firmware']
if firmware_file.filename == '':
return jsonify({'success': False, 'error': 'No firmware file selected'})
# Save and extract firmware
try:
# Create temp directory
temp_dir = tempfile.mkdtemp(prefix="esp32_flash_")
zip_path = os.path.join(temp_dir, 'firmware.zip')
firmware_file.save(zip_path)
# Extract firmware
with zipfile.ZipFile(zip_path, 'r') as zip_file:
zip_file.extractall(temp_dir)
# Start flash in background thread
flash_status = {'running': True, 'output': [], 'success': None, 'progress': 0}
thread = threading.Thread(
target=flash_worker,
args=(temp_dir, port, chip, baud, flash_mode, flash_freq, flash_size)
)
thread.daemon = True
thread.start()
return jsonify({'success': True, 'message': 'Flash started'})
except Exception as e:
return jsonify({'success': False, 'error': str(e)})
@app.route('/api/status')
def get_status():
"""Get current flash status"""
return jsonify(flash_status)
@socketio.on('connect')
def handle_connect():
"""Handle client connection"""
global client_connected, last_heartbeat, shutdown_timer
client_connected = True
last_heartbeat = time.time()
# Cancel any pending shutdown
if shutdown_timer:
shutdown_timer.cancel()
shutdown_timer = None
print('Client connected')
@socketio.on('disconnect')
def handle_disconnect():
"""Handle client disconnection"""
global client_connected, shutdown_timer
client_connected = False
print('Client disconnected - scheduling shutdown in 1 second...')
# Schedule shutdown after 5 seconds
shutdown_timer = threading.Timer(1.0, shutdown_server)
shutdown_timer.daemon = True
shutdown_timer.start()
@socketio.on('heartbeat')
def handle_heartbeat():
"""Handle heartbeat from client"""
global last_heartbeat
last_heartbeat = time.time()
emit('heartbeat_ack', {'timestamp': last_heartbeat})
def shutdown_server():
"""Gracefully shutdown the server"""
print('\nNo clients connected. Shutting down server...')
# Give a moment for any pending operations
time.sleep(1)
# Shutdown Flask
try:
func = request.environ.get('werkzeug.server.shutdown')
if func is None:
# Alternative shutdown method
os.kill(os.getpid(), signal.SIGTERM)
else:
func()
except:
# Force exit as last resort
os._exit(0)
def flash_worker(temp_dir, port, chip, baud, flash_mode, flash_freq, flash_size):
"""Background worker for flashing firmware"""
global flash_status
import re
import io
from contextlib import redirect_stdout, redirect_stderr
try:
# Define file paths
bootloader = os.path.join(temp_dir, "bootloader.bin")
firmware = os.path.join(temp_dir, "soundshot.bin")
ota_initial = os.path.join(temp_dir, "ota_data_initial.bin")
partition = os.path.join(temp_dir, "partition-table.bin")
# Verify files exist
required_files = [bootloader, firmware, ota_initial, partition]
for file_path in required_files:
if not os.path.exists(file_path):
flash_status['output'].append(f"ERROR: Missing file {os.path.basename(file_path)}")
flash_status['success'] = False
flash_status['running'] = False
return
# Build esptool command arguments
esptool_args = [
"--chip", chip,
"--port", port,
"--baud", baud,
"--before", "default_reset",
"--after", "hard_reset",
"write-flash",
"--flash-mode", flash_mode,
"--flash-freq", flash_freq,
"--flash-size", flash_size,
"0x1000", bootloader,
"0x20000", firmware,
"0x11000", ota_initial,
"0x8000", partition
]
flash_status['output'].append(f"Running esptool with args: {' '.join(esptool_args)}")
flash_status['output'].append("")
# Import and run esptool directly (works in both script and EXE)
import esptool
# Fix esptool data path for PyInstaller
if getattr(sys, 'frozen', False):
# Running as compiled executable
bundle_dir = sys._MEIPASS
esptool_targets = os.path.join(bundle_dir, 'esptool', 'targets')
if os.path.exists(esptool_targets):
# Override esptool's resource path
esptool.RESOURCES_DIR = bundle_dir
# Capture stdout/stderr
output_buffer = io.StringIO()
# Backup original sys.argv and replace with our arguments
original_argv = sys.argv
sys.argv = ['esptool.py'] + esptool_args
# Run esptool and capture output
return_code = 0
# Regex to parse progress lines
# Example: "Writing at 0x0011325c... (85 %)"
# or "Writing at 0x0011325c [========================> ] 85.3% 622592/730050 bytes..."
progress_pattern = re.compile(r'(\d+(?:\.\d+)?)\s*%')
# Save original stdout/stderr
original_stdout = sys.stdout
original_stderr = sys.stderr
try:
# Custom output class to capture and stream esptool output
class OutputCapture:
def __init__(self):
self.buffer = []
def write(self, text):
if text and text.strip():
lines = text.rstrip().split('\n')
for line in lines:
flash_status['output'].append(line)
self.buffer.append(line)
# Try to extract progress percentage
match = progress_pattern.search(line)
if match:
try:
percent = float(match.group(1))
flash_status['progress'] = percent
except ValueError:
pass
return len(text)
def flush(self):
pass
capture = OutputCapture()
# Redirect stdout/stderr to our capture object
with redirect_stdout(capture), redirect_stderr(capture):
esptool.main()
except SystemExit as e:
return_code = e.code if e.code is not None else 0
finally:
# Restore original sys.argv and stdout/stderr
sys.argv = original_argv
sys.stdout = original_stdout
sys.stderr = original_stderr
if return_code == 0:
flash_status['output'].append("")
flash_status['output'].append("✓ Flash completed successfully!")
flash_status['success'] = True
flash_status['progress'] = 100
else:
flash_status['output'].append("")
flash_status['output'].append(f"✗ Flash failed with return code {return_code}")
flash_status['success'] = False
except Exception as e:
flash_status['output'].append(f"✗ Error: {str(e)}")
flash_status['success'] = False
finally:
flash_status['running'] = False
# Clean up temp directory
try:
shutil.rmtree(temp_dir, ignore_errors=True)
except:
pass
def open_browser(port):
"""Open browser to the application"""
url = f'http://127.0.0.1:{port}'
webbrowser.open(url)
def main():
"""Run the web server"""
import logging
# Disable Flask request logging to avoid colorama issues with stdout redirection
log = logging.getLogger('werkzeug')
log.setLevel(logging.ERROR)
port = get_free_port()
print(f"Starting ESP32 Flasher on port {port}...")
print(f"Open your browser to: http://127.0.0.1:{port}")
print(f"Server will automatically shut down when browser is closed.\n")
# Open browser after short delay
timer = threading.Timer(1.5, open_browser, args=[port])
timer.daemon = True
timer.start()
# Run Flask server with SocketIO
try:
socketio.run(app, host='127.0.0.1', port=port, debug=False, allow_unsafe_werkzeug=True, log_output=False)
except KeyboardInterrupt:
print('\nShutting down...')
except SystemExit:
print('Server stopped.')
if __name__ == "__main__":
main()

View File

@@ -1,19 +1,4 @@
<<<<<<< HEAD
idf_component_register(SRCS "system.c.LOCAL.c" "bt_app.c" "system.c" "bubble.c" "keypad.c" "main.c"
"gui.c"
"lsm6dsv.c"
INCLUDE_DIRS "."
REQUIRES "driver"
"esp_lcd"
"lvgl"
"esp_lvgl_port"
"esp_timer"
"nvs_flash"
"bt")
=======
idf_component_register(SRCS "bt_app.c" "system.c" "bubble.c" "keypad.c" "main.c"
"gui.c"
"lsm6dsv.c"
INCLUDE_DIRS "."
@@ -24,5 +9,4 @@ idf_component_register(SRCS "bt_app.c" "system.c" "bubble.c" "keypad.c" "main.c"
"esp_timer"
"nvs_flash"
"bt")
>>>>>>> 4feb4c0a98bddb1f2a172ea4b195ce31ba18d442

File diff suppressed because it is too large Load Diff

View File

@@ -10,6 +10,7 @@
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include "esp_bt_defs.h"
/* log tag */
#define BT_APP_CORE_TAG "BT_APP_CORE"
@@ -67,4 +68,47 @@ void bt_app_task_shut_down(void);
void bt_app_init(void);
/* Bluetooth device management for GUI */
#define MAX_BT_DEVICES 8 // Reduced from 20 to save memory
#define MAX_BT_NAME_LEN 32
typedef struct {
esp_bd_addr_t bda;
char name[MAX_BT_NAME_LEN];
bool is_paired;
int rssi;
} bt_device_info_t;
typedef struct {
bt_device_info_t devices[MAX_BT_DEVICES];
int count;
bool discovery_active;
} bt_device_list_t;
/* Get current device list for GUI display */
bt_device_list_t* bt_get_device_list(void);
/* Start device discovery */
bool bt_start_discovery(void);
/* Stop device discovery */
bool bt_stop_discovery(void);
/* Connect to specific device by index in device list */
bool bt_connect_device(int device_index);
/* Clear discovered devices (keep paired devices) */
void bt_clear_discovered_devices(void);
/* Clear all devices from the device list (paired and discovered) */
void bt_clear_all_devices(void);
/* Disconnect from currently connected device */
void bt_disconnect_current_device(void);
/* Volume control functions */
void bt_volume_up(void);
void bt_volume_down(void);
int bt_get_current_volume(void);
#endif /* __BT_APP_CORE_H__ */

View File

@@ -1,13 +1,14 @@
<<<<<<< HEAD
#ifndef GPIO_H
#define GPIO_H
// Define keypad buttons
#define PIN_NUM_BUTTON_0 36
#define PIN_NUM_BUTTON_1 39
#define PIN_NUM_LED_1 32
#define PIN_NUM_LED_2 33
#define PIN_NUM_LED_0 32
#define PIN_NUM_LED_1 33
#define PIN_NUM_LED_2 25
#define PIN_NUM_nON 26
#define PIN_NUM_CHARGE_STATUS 2
// Define GPIO pins
@@ -27,34 +28,4 @@
#define PIN_NUM_BK_LIGHT 5 // Backlight control pin, -1 if not used
#endif
=======
#ifndef GPIO_H
#define GPIO_H
// Define keypad buttons
#define PIN_NUM_BUTTON_0 36
#define PIN_NUM_BUTTON_1 39
#define PIN_NUM_LED_1 32
#define PIN_NUM_LED_2 33
#define PIN_NUM_nON 26
// Define GPIO pins
#ifdef DEVKIT
#define PIN_NUM_MOSI 23 // SDA pin for LCD
#define PIN_NUM_CLK 18 // SCL pin for LCD
#define PIN_NUM_CS 2 // CS pin
#define PIN_NUM_DC 12 // Data/Command pin (RS)
#define PIN_NUM_RST 13 // Reset pin
#define PIN_NUM_BK_LIGHT -1 // Backlight control pin, -1 if not used
#else
#define PIN_NUM_MOSI 23 // SDA pin for LCD
#define PIN_NUM_CLK 18 // SCL pin for LCD
#define PIN_NUM_CS 12 // CS pin
#define PIN_NUM_DC 15 // Data/Command pin (RS)
#define PIN_NUM_RST 13 // Reset pin
#define PIN_NUM_BK_LIGHT 5 // Backlight control pin, -1 if not used
#endif
>>>>>>> 4feb4c0a98bddb1f2a172ea4b195ce31ba18d442
#endif

1749
main/gui.c

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,3 @@
<<<<<<< HEAD
#ifndef _KEYPAD_H
#define _KEYPAD_H
@@ -21,28 +20,4 @@ typedef enum
void keypad_start(void);
QueueHandle_t keypad_getQueue(void);
=======
#ifndef _KEYPAD_H
#define _KEYPAD_H
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
typedef enum
{
KEY0 = (1 << 0),
KEY1 = (1 << 1),
} keycode_t;
#define KEY_SHORT_PRESS 0
#define KEY_LONG_PRESS 4
#define KEY_UP KEY0
#define KEY_DOWN KEY1
void keypad_start(void);
QueueHandle_t keypad_getQueue(void);
>>>>>>> 4feb4c0a98bddb1f2a172ea4b195ce31ba18d442
#endif

View File

@@ -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
@@ -481,7 +481,7 @@
/*Montserrat fonts with ASCII range and some symbols using bpp = 4
*https://fonts.google.com/specimen/Montserrat*/
#define LV_FONT_MONTSERRAT_8 0
#define LV_FONT_MONTSERRAT_8 1
#define LV_FONT_MONTSERRAT_10 0
#define LV_FONT_MONTSERRAT_12 0
#define LV_FONT_MONTSERRAT_14 1
@@ -511,7 +511,7 @@
/*Pixel perfect monospace fonts*/
#define LV_FONT_UNSCII_8 0
#define LV_FONT_UNSCII_16 0
#define LV_FONT_UNSCII_16 1
/*Optionally declare custom fonts here.
*You can use these fonts as default font too and they will be available globally.
@@ -519,7 +519,7 @@
#define LV_FONT_CUSTOM_DECLARE
/*Always set a default font*/
#define LV_FONT_DEFAULT &lv_font_montserrat_14
#define LV_FONT_DEFAULT &lv_font_unscii_16
/*Enable handling large font and/or fonts with a lot of characters.
*The limit depends on the font size, font face and bpp.

View File

@@ -1,4 +1,3 @@
<<<<<<< HEAD
/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
*
@@ -14,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"
@@ -31,15 +31,50 @@
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]
} LowPassFilter;
#define ALPHA_MIN 0.2f
#define ALPHA_MAX 0.4f
// Clamp function to bound value between min and max
double clamp(double value, double min, double max) {
if (value < min) return min;
if (value > max) return max;
return value;
}
// Compute alpha using linear ramp
float compute_alpha_linear(double x, float alpha_min, float alpha_max, float threshold) {
float abs_x = fabs(x);
float t = clamp(abs_x / threshold, 0.0, 1.0); // Normalize |x| to [0,1]
return alpha_min + (alpha_max - alpha_min) * t;
}
// Compute sigmoid-based dynamic alpha
float compute_alpha_sigmoid(float x, float alpha_min, float alpha_max, float k, float midpoint) {
float abs_x = fabs(x);
float exponent = -k * (abs_x - midpoint);
float sigmoid = 1.0 / (1.0 + exp(exponent));
return alpha_min + (alpha_max - alpha_min) * sigmoid;
}
// Initialize the filter. alpha = smoothing factor.
// e.g. alpha = dt/(RC+dt) if you derive from cutoff freq & sample time.
// init_output can be 0 or your first sample to avoid startup glitch.
@@ -51,7 +86,11 @@ static inline void LPF_Init(LowPassFilter *f, float alpha, float init_output) {
// Run one input sample through the filter.
// Returns y[n] = alpha * x[n] + (1-alpha) * y[n-1].
static inline float LPF_Update(LowPassFilter *f, float input) {
float out = f->alpha * input + (1.0f - f->alpha) * f->prev_output;
//float alpha = compute_alpha_sigmoid(input, ALPHA_MIN, ALPHA_MAX, 1.0f, 0.0f);
float alpha = compute_alpha_linear(input, ALPHA_MIN, ALPHA_MAX, 2.0f);
float out = alpha * input + (1.0f - alpha) * f->prev_output;
//float out = f->alpha * input + (1.0f - f->alpha) * f->prev_output;
f->prev_output = out;
return out;
}
@@ -60,26 +99,52 @@ static inline float LPF_Update(LowPassFilter *f, float input) {
/*********************************
* STATIC VARIABLE DEFINITIONS
********************************/
static const char *TAG = "main";
/*********************************
* STATIC FUNCTION DEFINITIONS
********************************/
// Helper function to set LED with verification
static void set_led_level(gpio_num_t pin, uint32_t level)
{
esp_err_t err = gpio_set_level(pin, level);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to set GPIO %d to %lu: %s", pin, level, esp_err_to_name(err));
}
}
static void init_gpio(void)
{
gpio_config_t io_conf;
// Reset GPIO peripheral to clear any SPI/peripheral configurations
gpio_reset_pin(PIN_NUM_LED_0);
gpio_reset_pin(PIN_NUM_LED_1);
gpio_reset_pin(PIN_NUM_LED_2);
// Configure output GPIO
io_conf.pin_bit_mask = (1ULL << PIN_NUM_LED_1) | (1ULL << PIN_NUM_LED_2);
io_conf.pin_bit_mask = (1ULL << PIN_NUM_LED_0) | (1ULL << PIN_NUM_LED_1) | (1ULL << PIN_NUM_LED_2);
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
gpio_config(&io_conf);
// Set initial state
gpio_set_level(PIN_NUM_LED_0, 0);
gpio_set_level(PIN_NUM_LED_1, 0);
gpio_set_level(PIN_NUM_LED_2, 0);
ESP_LOGI(TAG, "LED pins configured: %d, %d, %d", PIN_NUM_LED_0, PIN_NUM_LED_1, PIN_NUM_LED_2);
// Configure charge status input GPIO
io_conf.pin_bit_mask = (1ULL << PIN_NUM_CHARGE_STATUS);
io_conf.mode = GPIO_MODE_INPUT;
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
gpio_config(&io_conf);
#if 1
@@ -219,6 +284,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();
@@ -227,27 +293,32 @@ 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
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 = NULL;
xTaskCreate(imu_task, "imu_task", 2048, NULL, 5, &h);
print_heap_info("POST_IMU_TASK");
bt_app_init();
print_heap_info("POST_BLUETOOTH");
gpio_set_level(PIN_NUM_LED_0, 1);
gpio_set_level(PIN_NUM_LED_1, 1);
gpio_set_level(PIN_NUM_LED_2, 1);
gui_start();
print_heap_info("POST_GUI");
gpio_set_level(PIN_NUM_LED_2, 1);
@@ -286,300 +357,25 @@ void app_main(void)
#else
while (1)
{
vTaskDelay(pdMS_TO_TICKS(1000));
}
#endif
}
=======
/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
// Read charge status and update system
bool is_charging = gpio_get_level(PIN_NUM_CHARGE_STATUS);
system_setChargeStatus(is_charging);
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <inttypes.h>
#include "math.h"
#include "esp_system.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/timers.h"
#include "driver/gpio.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "bt_app.h"
#include "lsm6dsv.h"
#include "gui.h"
#include "gpio.h"
#include "keypad.h"
#include "system.h"
/*********************************
* STATIC FUNCTION DECLARATIONS
********************************/
typedef struct {
float alpha; // smoothing factor, 0<alpha<1
float prev_output; // y[n-1]
} LowPassFilter;
// Initialize the filter. alpha = smoothing factor.
// e.g. alpha = dt/(RC+dt) if you derive from cutoff freq & sample time.
// init_output can be 0 or your first sample to avoid startup glitch.
static inline void LPF_Init(LowPassFilter *f, float alpha, float init_output) {
f->alpha = alpha;
f->prev_output = init_output;
}
// Run one input sample through the filter.
// Returns y[n] = alpha * x[n] + (1-alpha) * y[n-1].
static inline float LPF_Update(LowPassFilter *f, float input) {
float out = f->alpha * input + (1.0f - f->alpha) * f->prev_output;
f->prev_output = out;
return out;
}
/*********************************
* STATIC VARIABLE DEFINITIONS
********************************/
static const char *TAG = "main";
/*********************************
* STATIC FUNCTION DEFINITIONS
********************************/
static void init_gpio(void)
if (is_charging)
{
gpio_config_t io_conf;
// Configure output GPIO
io_conf.pin_bit_mask = (1ULL << PIN_NUM_LED_1) | (1ULL << PIN_NUM_LED_2);
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
gpio_config(&io_conf);
#if 1
// Configure output power signal
gpio_set_level(PIN_NUM_nON, 1);
io_conf.pin_bit_mask = (1ULL << PIN_NUM_nON);
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
gpio_config(&io_conf);
#endif
// Configure LCD Backlight GPIO
io_conf.pin_bit_mask = (1ULL << PIN_NUM_BK_LIGHT);
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
gpio_config(&io_conf);
}
// IMU task to read sensor data
static void imu_task(void *pvParameters) {
lsm6dsv_data_t imu_data;
char data_str[128];
float roll, pitch, yaw;
lsm6dsv_fifo_t fifo;
ImuData_t imu;
LowPassFilter lpf;
LPF_Init(&lpf, FILTER_COEFF, 0);
while (1) {
// uint8_t samples = lsm6dsv_fifo_ready();
//ESP_LOGI(TAG, "Samples: %d", samples);
// while (samples)
// {
// lsm6dsv_fifo_read(&fifo);
// // snprintf(data_str, sizeof(data_str),
// // "tag: %d, count: %d, (%d, %d, %d)",
// // fifo.tag, fifo.count, x, y, z);
// // ESP_LOGI(TAG, "%s", data_str);
// samples--;
// }
// lsm6dsv_getAttitude(&fifo, &roll, &pitch, &yaw);
// snprintf(data_str, sizeof(data_str),
// "r: %0.3f, p: %0.3f, y: %0.3f",
// roll, pitch, yaw);
// ESP_LOGI(TAG, "%s", data_str);
float xz;
float yz;
float xy;
#if 1
if (lsm6dsv_read_data(&imu_data) == ESP_OK)
{
// snprintf(data_str, sizeof(data_str),
// "Acc: %.2f, %.2f, %.2f; "
// "Gyro: %.2f, %.2f, %.2f (%d)\n",
// imu_data.acc_x, imu_data.acc_y, imu_data.acc_z,
// imu_data.gyro_x, imu_data.gyro_y, imu_data.gyro_z, lsm6dsv_fifo_ready());
#if 1
imu.raw[ANGLE_XZ] = atan2f((float)imu_data.acc_z, (float)imu_data.acc_x) * 180 / M_PI;
imu.raw[ANGLE_YZ] = atan2f((float)imu_data.acc_z, (float)imu_data.acc_y) * 180 / M_PI;
imu.raw[ANGLE_XY] = atan2f((float)imu_data.acc_x, (float)imu_data.acc_y) * 180 / M_PI;
float angle;
angle = system_getAngle();
if (angle > MAX_INDICATION_ANGLE)
{
angle = MAX_INDICATION_ANGLE;
gpio_set_level(PIN_NUM_LED_0, 0);
gpio_set_level(PIN_NUM_LED_1, 1);
gpio_set_level(PIN_NUM_LED_2, 0);
}
else
if (angle < -MAX_INDICATION_ANGLE)
{
angle = -MAX_INDICATION_ANGLE;
gpio_set_level(PIN_NUM_LED_0, 1);
gpio_set_level(PIN_NUM_LED_1, 0);
gpio_set_level(PIN_NUM_LED_2, 0);
}
// low pass filter the angle measurement
imu.angle = LPF_Update(&lpf, angle);
//imu.filtered[ANGLE_XY] = LPF_Update(&lpf, imu.raw[ANGLE_XY]);
system_setImuData(imu);
// snprintf(data_str, sizeof(data_str),
// "(%.2f, %.2f, %.2f)", xy, yz, xy);
#endif
#if 0
snprintf(data_str, sizeof(data_str),
"(%.1f, %.1f, %.1f) (%.2f, %.2f, %.2f)",
imu.raw[ANGLE_XZ], imu.raw[ANGLE_YZ], imu.raw[ANGLE_XY],
(float)imu_data.acc_x, (float)imu_data.acc_y, (float)imu_data.acc_z);
ESP_LOGI(TAG, "%s", data_str);
#endif
// Update label text in LVGL task
//lv_label_set_text(imu_label, data_str);
}
#endif
vTaskDelay(pdMS_TO_TICKS(100)); // Update every 100ms
}
}
/*********************************
* MAIN ENTRY POINT
********************************/
void app_main(void)
{
/* initialize NVS — it is used to store PHY calibration data */
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
init_gpio();
system_init();
// Initialize IMU
ESP_ERROR_CHECK(lsm6dsv_init(22, 21)); // SCL = IO14, SDA = IO15
// Create IMU task
TaskHandle_t h = xTaskCreate(imu_task, "imu_task", 4096, NULL, 5, NULL);
bt_app_init();
gui_start();
gpio_set_level(PIN_NUM_LED_2, 1);
#if 0
keypad_start();
QueueHandle_t q = keypad_getQueue();
uint32_t ev = 0;
//gpio_set_level(PIN_NUM_LED_1, 1);
gpio_set_level(PIN_NUM_LED_2, 1);
// Main loop - LVGL task will run automatically
while (1) {
if (xQueueReceive(q, &ev, pdMS_TO_TICKS(10)) == pdTRUE)
{
switch (ev)
{
case (KEY_UP << KEY_LONG_PRESS):
{
system_setZeroAngle();
break;
}
case (KEY_DOWN << KEY_LONG_PRESS):
{
system_clearZeroAngle();
break;
}
}
}
}
#else
while (1)
{
vTaskDelay(pdMS_TO_TICKS(1000));
system_processNvsRequests();
vTaskDelay(pdMS_TO_TICKS(10));
}
#endif
}
>>>>>>> 4feb4c0a98bddb1f2a172ea4b195ce31ba18d442

View File

@@ -1,21 +1,35 @@
<<<<<<< HEAD
#include "system.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "nvs.h"
#include "esp_timer.h"
#include <string.h>
static SystemState_t _systemState;
static EventGroupHandle_t _systemEvent;
static EventManager_t _eventManager;
static QueueHandle_t _nvsRequestQueue;
static const char* NVS_NAMESPACE = "bt_devices";
static const char* NVS_KEY_COUNT = "count";
static esp_err_t nvs_load_devices_internal(paired_device_t *devices, size_t *count);
static esp_err_t nvs_save_devices_internal(const paired_device_t *devices, size_t count);
void system_init(void)
{
_systemState.zeroAngle = 0.0f;
_systemState.primaryAxis = PRIMARY_AXIS;
_systemState.pairedDeviceCount = 0;
_systemState.isCharging = false;
EventGroupHandle_t evt = xEventGroupCreate();
_systemEvent = xEventGroupCreate();
_eventManager.count = 0;
_eventManager.mutex = xSemaphoreCreateMutex();
system_initNvsService();
}
int system_getPrimaryAxis(void)
@@ -44,6 +58,22 @@ float system_getAngle(void)
return angle;
}
void system_setChargeStatus(bool charging)
{
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
_systemState.isCharging = charging;
xSemaphoreGive(_eventManager.mutex);
}
bool system_getChargeStatus(void)
{
bool charging;
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
charging = _systemState.isCharging;
xSemaphoreGive(_eventManager.mutex);
return charging;
}
void system_setZeroAngle(void)
{
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
@@ -167,174 +197,319 @@ void system_notifyAll(uint32_t eventBits) {
}
xSemaphoreGive(em->mutex);
}
=======
#include "system.h"
#include "esp_log.h"
static SystemState_t _systemState;
static EventGroupHandle_t _systemEvent;
static EventManager_t _eventManager;
void system_init(void)
{
_systemState.zeroAngle = 0.0f;
_systemState.primaryAxis = PRIMARY_AXIS;
EventGroupHandle_t evt = xEventGroupCreate();
_eventManager.count = 0;
_eventManager.mutex = xSemaphoreCreateMutex();
}
int system_getPrimaryAxis(void)
{
int axis;
void system_requestBtRefresh(void) {
ESP_LOGI("system", "BT Refresh requested");
system_notifyAll(EM_EVENT_BT_REFRESH);
}
void system_requestBtConnect(int device_index) {
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
axis = _systemState.primaryAxis;
_systemState.btDeviceIndex = device_index;
xSemaphoreGive(_eventManager.mutex);
return axis;
ESP_LOGI("system", "BT Connect requested for device %d", device_index);
system_notifyAll(EM_EVENT_BT_CONNECT);
}
float system_getAngle(void)
{
float angle;
int system_getBtDeviceIndex(void) {
int index;
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
angle = _systemState.imu.raw[_systemState.primaryAxis] - _systemState.zeroAngle;
index = _systemState.btDeviceIndex;
xSemaphoreGive(_eventManager.mutex);
return angle;
return index;
}
void system_setZeroAngle(void)
{
void system_requestVolumeUp(void) {
ESP_LOGI("system", "Volume Up requested");
system_notifyAll(EM_EVENT_VOLUME_UP);
}
void system_requestVolumeDown(void) {
ESP_LOGI("system", "Volume Down requested");
system_notifyAll(EM_EVENT_VOLUME_DOWN);
}
void system_initNvsService(void) {
_nvsRequestQueue = xQueueCreate(10, sizeof(nvs_request_t));
if (_nvsRequestQueue == NULL) {
ESP_LOGE("system", "Failed to create NVS request queue");
}
memset(_systemState.pairedDevices, 0, sizeof(_systemState.pairedDevices));
_systemState.pairedDeviceCount = 0;
paired_device_t devices[MAX_PAIRED_DEVICES];
size_t count = 0;
if (nvs_load_devices_internal(devices, &count) == ESP_OK) {
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
_systemState.zeroAngle = _systemState.imu.raw[_systemState.primaryAxis];
ESP_LOGI("system", "Zero: %.1f", _systemState.zeroAngle);
memcpy(_systemState.pairedDevices, devices, count * sizeof(paired_device_t));
_systemState.pairedDeviceCount = count;
xSemaphoreGive(_eventManager.mutex);
ESP_LOGI("system", "Loaded %d paired devices from NVS", count);
}
}
void system_clearZeroAngle(void)
{
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
static esp_err_t nvs_load_devices_internal(paired_device_t *devices, size_t *count) {
nvs_handle_t nvs_handle;
esp_err_t ret;
_systemState.zeroAngle = 0.0f;
ESP_LOGI("system", "Zero: %.1f", _systemState.zeroAngle);
xSemaphoreGive(_eventManager.mutex);
ret = nvs_open(NVS_NAMESPACE, NVS_READONLY, &nvs_handle);
if (ret != ESP_OK) {
ESP_LOGE("system", "Failed to open NVS namespace: %s", esp_err_to_name(ret));
*count = 0;
return ret;
}
float system_getZeroAngle(void)
{
float angle;
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
angle = _systemState.zeroAngle;
xSemaphoreGive(_eventManager.mutex);
return angle;
size_t device_count = 0;
size_t required_size = sizeof(size_t);
ret = nvs_get_blob(nvs_handle, NVS_KEY_COUNT, &device_count, &required_size);
if (ret != ESP_OK) {
nvs_close(nvs_handle);
*count = 0;
return ESP_OK;
}
void system_setImuData(ImuData_t imu)
{
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
device_count = (device_count > MAX_PAIRED_DEVICES) ? MAX_PAIRED_DEVICES : device_count;
_systemState.imu = imu;
xSemaphoreGive(_eventManager.mutex);
//ESP_LOGI("g", "New IMU Data: %.2f", xy);
system_notifyAll(EM_EVENT_NEW_DATA);
}
ImuData_t system_getImuData(void)
{
ImuData_t imu;
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
imu = _systemState.imu;
xSemaphoreGive(_eventManager.mutex);
return imu;
}
void system_waitForEvent(void)
{
}
SystemState_t *system_getState(void)
{
return &_systemState;
}
BaseType_t system_subscribe(TaskHandle_t task) {
EventManager_t *em = &_eventManager;
ESP_LOGI("g", "Subscribe: %p", task);
if (xSemaphoreTake(em->mutex, portMAX_DELAY) == pdFALSE) {
return pdFAIL;
}
if (em->count < EM_MAX_SUBSCRIBERS) {
// avoid duplicates?
ESP_LOGI("g", "PASS");
em->subscribers[em->count++] = task;
xSemaphoreGive(em->mutex);
return pdPASS;
}
xSemaphoreGive(em->mutex);
return pdFAIL; // full
}
BaseType_t system_unsubscribe(TaskHandle_t task) {
EventManager_t *em = &_eventManager;
BaseType_t removed = pdFAIL;
if (xSemaphoreTake(em->mutex, portMAX_DELAY) == pdTRUE) {
for (size_t i = 0; i < em->count; ++i) {
if (em->subscribers[i] == task) {
// shift down
for (size_t j = i; j + 1 < em->count; ++j)
em->subscribers[j] = em->subscribers[j+1];
em->count--;
removed = pdPASS;
for (size_t i = 0; i < device_count; i++) {
char key[16];
snprintf(key, sizeof(key), "device_%zu", i);
required_size = sizeof(paired_device_t);
ret = nvs_get_blob(nvs_handle, key, &devices[i], &required_size);
if (ret != ESP_OK) {
ESP_LOGW("system", "Failed to load device %zu", i);
device_count = i;
break;
}
}
xSemaphoreGive(em->mutex);
}
return removed;
nvs_close(nvs_handle);
*count = device_count;
return ESP_OK;
}
void system_notifyAll(uint32_t eventBits) {
EventManager_t *em = &_eventManager;
if (xSemaphoreTake(em->mutex, portMAX_DELAY) == pdTRUE) {
for (size_t i = 0; i < em->count; ++i) {
// set the bits in each task's notification value
//ESP_LOGI("g", "Notify: %p", em->subscribers[i]);
xTaskNotify(em->subscribers[i],
eventBits,
eSetBits);
static esp_err_t nvs_save_devices_internal(const paired_device_t *devices, size_t count) {
nvs_handle_t nvs_handle;
esp_err_t ret;
ret = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs_handle);
if (ret != ESP_OK) {
ESP_LOGE("system", "Failed to open NVS namespace for write: %s", esp_err_to_name(ret));
return ret;
}
xSemaphoreGive(em->mutex);
ret = nvs_set_blob(nvs_handle, NVS_KEY_COUNT, &count, sizeof(size_t));
if (ret != ESP_OK) {
nvs_close(nvs_handle);
return ret;
}
>>>>>>> 4feb4c0a98bddb1f2a172ea4b195ce31ba18d442
for (size_t i = 0; i < count; i++) {
char key[16];
snprintf(key, sizeof(key), "device_%zu", i);
ret = nvs_set_blob(nvs_handle, key, &devices[i], sizeof(paired_device_t));
if (ret != ESP_OK) {
nvs_close(nvs_handle);
return ret;
}
}
ret = nvs_commit(nvs_handle);
nvs_close(nvs_handle);
return ret;
}
void system_processNvsRequests(void) {
nvs_request_t request;
while (xQueueReceive(_nvsRequestQueue, &request, 0) == pdTRUE) {
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
switch (request.operation) {
case NVS_OP_SAVE_DEVICE:
request.result = nvs_save_devices_internal(_systemState.pairedDevices, _systemState.pairedDeviceCount);
break;
case NVS_OP_LOAD_DEVICES:
request.result = nvs_load_devices_internal(_systemState.pairedDevices, &_systemState.pairedDeviceCount);
break;
case NVS_OP_REMOVE_DEVICE: {
size_t removed_index = SIZE_MAX;
for (size_t i = 0; i < _systemState.pairedDeviceCount; i++) {
if (memcmp(_systemState.pairedDevices[i].bda, request.bda, ESP_BD_ADDR_LEN) == 0) {
removed_index = i;
break;
}
}
if (removed_index != SIZE_MAX) {
for (size_t i = removed_index; i < _systemState.pairedDeviceCount - 1; i++) {
_systemState.pairedDevices[i] = _systemState.pairedDevices[i + 1];
}
_systemState.pairedDeviceCount--;
request.result = nvs_save_devices_internal(_systemState.pairedDevices, _systemState.pairedDeviceCount);
} else {
request.result = ESP_ERR_NOT_FOUND;
}
break;
}
case NVS_OP_UPDATE_TIMESTAMP: {
for (size_t i = 0; i < _systemState.pairedDeviceCount; i++) {
if (memcmp(_systemState.pairedDevices[i].bda, request.bda, ESP_BD_ADDR_LEN) == 0) {
_systemState.pairedDevices[i].last_connected = (uint32_t)(esp_timer_get_time() / 1000000);
request.result = nvs_save_devices_internal(_systemState.pairedDevices, _systemState.pairedDeviceCount);
break;
}
}
break;
}
default:
request.result = ESP_ERR_INVALID_ARG;
break;
}
xSemaphoreGive(_eventManager.mutex);
// Send the result directly as the notification value (not a pointer)
if (request.requestor) {
xTaskNotify(request.requestor, (uint32_t)request.result, eSetValueWithOverwrite);
}
}
}
esp_err_t system_savePairedDevice(const paired_device_t *device) {
if (!device) return ESP_ERR_INVALID_ARG;
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
size_t existing_index = SIZE_MAX;
for (size_t i = 0; i < _systemState.pairedDeviceCount; i++) {
if (memcmp(_systemState.pairedDevices[i].bda, device->bda, ESP_BD_ADDR_LEN) == 0) {
existing_index = i;
break;
}
}
if (existing_index != SIZE_MAX) {
_systemState.pairedDevices[existing_index] = *device;
} else if (_systemState.pairedDeviceCount < MAX_PAIRED_DEVICES) {
_systemState.pairedDevices[_systemState.pairedDeviceCount++] = *device;
} else {
xSemaphoreGive(_eventManager.mutex);
return ESP_ERR_NO_MEM;
}
xSemaphoreGive(_eventManager.mutex);
nvs_request_t request = {
.operation = NVS_OP_SAVE_DEVICE,
.device = *device,
.requestor = xTaskGetCurrentTaskHandle()
};
if (xQueueSend(_nvsRequestQueue, &request, pdMS_TO_TICKS(NVS_TIMEOUT_MS)) == pdTRUE) {
uint32_t notification;
if (xTaskNotifyWait(0, UINT32_MAX, &notification, pdMS_TO_TICKS(NVS_TIMEOUT_MS)) == pdTRUE) {
// notification contains the result directly (not a pointer)
return (esp_err_t)notification;
}
}
return ESP_ERR_TIMEOUT;
}
esp_err_t system_loadPairedDevices(paired_device_t *devices, size_t *count) {
if (!devices || !count) return ESP_ERR_INVALID_ARG;
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
size_t copy_count = (*count < _systemState.pairedDeviceCount) ? *count : _systemState.pairedDeviceCount;
memcpy(devices, _systemState.pairedDevices, copy_count * sizeof(paired_device_t));
*count = copy_count;
xSemaphoreGive(_eventManager.mutex);
return ESP_OK;
}
esp_err_t system_removePairedDevice(esp_bd_addr_t bda) {
nvs_request_t request = {
.operation = NVS_OP_REMOVE_DEVICE,
.requestor = xTaskGetCurrentTaskHandle()
};
memcpy(request.bda, bda, ESP_BD_ADDR_LEN);
if (xQueueSend(_nvsRequestQueue, &request, pdMS_TO_TICKS(NVS_TIMEOUT_MS)) == pdTRUE) {
uint32_t notification;
if (xTaskNotifyWait(0, UINT32_MAX, &notification, pdMS_TO_TICKS(NVS_TIMEOUT_MS)) == pdTRUE) {
// notification contains the result directly (not a pointer)
return (esp_err_t)notification;
}
}
return ESP_ERR_TIMEOUT;
}
bool system_isDeviceKnown(esp_bd_addr_t bda) {
bool known = false;
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
for (size_t i = 0; i < _systemState.pairedDeviceCount; i++) {
if (memcmp(_systemState.pairedDevices[i].bda, bda, ESP_BD_ADDR_LEN) == 0) {
known = true;
break;
}
}
xSemaphoreGive(_eventManager.mutex);
return known;
}
esp_err_t system_getKnownDeviceCount(size_t *count) {
if (!count) return ESP_ERR_INVALID_ARG;
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
*count = _systemState.pairedDeviceCount;
xSemaphoreGive(_eventManager.mutex);
return ESP_OK;
}
esp_err_t system_updateConnectionTimestamp(esp_bd_addr_t bda) {
nvs_request_t request = {
.operation = NVS_OP_UPDATE_TIMESTAMP,
.requestor = xTaskGetCurrentTaskHandle()
};
memcpy(request.bda, bda, ESP_BD_ADDR_LEN);
if (xQueueSend(_nvsRequestQueue, &request, pdMS_TO_TICKS(NVS_TIMEOUT_MS)) == pdTRUE) {
uint32_t notification;
if (xTaskNotifyWait(0, UINT32_MAX, &notification, pdMS_TO_TICKS(NVS_TIMEOUT_MS)) == pdTRUE) {
// notification contains the result directly (not a pointer)
return (esp_err_t)notification;
}
}
return ESP_ERR_TIMEOUT;
}
const paired_device_t* system_getPairedDevices(size_t *count) {
if (!count) return NULL;
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
*count = _systemState.pairedDeviceCount;
xSemaphoreGive(_eventManager.mutex);
return (const paired_device_t*)_systemState.pairedDevices;
}
esp_err_t system_clearAllPairedDevices(void) {
// Directly clear in-memory state and save to NVS
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
_systemState.pairedDeviceCount = 0;
esp_err_t ret = nvs_save_devices_internal(_systemState.pairedDevices, 0);
xSemaphoreGive(_eventManager.mutex);
if (ret == ESP_OK) {
ESP_LOGI("system", "Cleared all paired devices from NVS");
} else {
ESP_LOGE("system", "Failed to clear paired devices: %s", esp_err_to_name(ret));
}
return ret;
}

View File

@@ -1,13 +1,25 @@
<<<<<<< HEAD
#ifndef SYSTEM_H
#define SYSTEM_H
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"
#include "esp_bt_defs.h"
#define DISABLE_GUI
#define MAX_PAIRED_DEVICES 10
#define DEVICE_NAME_MAX_LEN 32
// Forward declaration of paired_device_t
typedef struct {
char name[DEVICE_NAME_MAX_LEN];
esp_bd_addr_t bda;
uint32_t last_connected;
bool is_connected;
} paired_device_t;
enum
{
ANGLE_XY = 0,
@@ -33,16 +45,32 @@ typedef struct SystemState_s
float zeroAngle;
int primaryAxis;
// BT event data
int btDeviceIndex;
// NVS cached data
paired_device_t pairedDevices[MAX_PAIRED_DEVICES];
size_t pairedDeviceCount;
// Charge status
bool isCharging;
} SystemState_t;
#define MAX_INDICATION_ANGLE 5.0f
#define FILTER_COEFF 0.2f // 0 to 1 Smaller number is heavier filter
#define MAX_INDICATION_ANGLE 7.5f
#define FILTER_COEFF 0.4f // 0 to 1 Smaller number is heavier filter
#define PRIMARY_AXIS ANGLE_YZ
#define RIFLE_AXIS Y
#define EM_MAX_SUBSCRIBERS 8 // tweak as needed
#define EM_EVENT_NEW_DATA (1UL<<0)
#define EM_EVENT_ERROR (1UL<<1)
#define EM_EVENT_BT_REFRESH (1UL<<2)
#define EM_EVENT_BT_CONNECT (1UL<<3)
#define EM_EVENT_VOLUME_UP (1UL<<4)
#define EM_EVENT_VOLUME_DOWN (1UL<<5)
#define EM_EVENT_BT_DISCOVERY_COMPLETE (1UL<<6)
// …add more event bit-masks here…
typedef struct {
@@ -59,6 +87,9 @@ ImuData_t system_getImuData(void);
int system_getPrimaryAxis(void);
float system_getAngle(void);
void system_setChargeStatus(bool charging);
bool system_getChargeStatus(void);
void system_setZeroAngle(void);
void system_clearZeroAngle(void);
float system_getZeroAngle(void);
@@ -72,81 +103,46 @@ BaseType_t system_unsubscribe(TaskHandle_t task);
// Notify all subscribers of one or more event bits
void system_notifyAll(uint32_t eventBits);
// BT-specific event functions
void system_requestBtRefresh(void);
void system_requestBtConnect(int device_index);
int system_getBtDeviceIndex(void);
=======
#ifndef SYSTEM_H
#define SYSTEM_H
// Volume control functions
void system_requestVolumeUp(void);
void system_requestVolumeDown(void);
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#define DISABLE_GUI
enum
{
ANGLE_XY = 0,
ANGLE_XZ,
ANGLE_YZ,
};
typedef struct
{
float raw[3];
float filtered[3];
float angle;
} ImuData_t;
typedef struct SystemState_s
{
ImuData_t imu;
EventGroupHandle_t event;
float zeroAngle;
int primaryAxis;
} SystemState_t;
#define MAX_INDICATION_ANGLE 5.0f
#define FILTER_COEFF 0.2f // 0 to 1 Smaller number is heavier filter
#define PRIMARY_AXIS ANGLE_YZ
#define EM_MAX_SUBSCRIBERS 8 // tweak as needed
#define EM_EVENT_NEW_DATA (1UL<<0)
#define EM_EVENT_ERROR (1UL<<1)
// …add more event bit-masks here…
// NVS Service
typedef enum {
NVS_OP_SAVE_DEVICE,
NVS_OP_LOAD_DEVICES,
NVS_OP_REMOVE_DEVICE,
NVS_OP_IS_DEVICE_KNOWN,
NVS_OP_GET_DEVICE_COUNT,
NVS_OP_UPDATE_TIMESTAMP
} nvs_operation_type_t;
typedef struct {
TaskHandle_t subscribers[EM_MAX_SUBSCRIBERS];
size_t count;
SemaphoreHandle_t mutex;
} EventManager_t;
nvs_operation_type_t operation;
paired_device_t device;
esp_bd_addr_t bda;
TaskHandle_t requestor;
esp_err_t result;
bool response_ready;
} nvs_request_t;
void system_init(void);
void system_setImuData(ImuData_t imu);
ImuData_t system_getImuData(void);
int system_getPrimaryAxis(void);
float system_getAngle(void);
void system_setZeroAngle(void);
void system_clearZeroAngle(void);
float system_getZeroAngle(void);
// Subscribe (register) current task to receive events
BaseType_t system_subscribe(TaskHandle_t task);
// Unsubscribe if you ever need
BaseType_t system_unsubscribe(TaskHandle_t task);
// Notify all subscribers of one or more event bits
void system_notifyAll(uint32_t eventBits);
void system_initNvsService(void);
void system_processNvsRequests(void);
esp_err_t system_savePairedDevice(const paired_device_t *device);
esp_err_t system_loadPairedDevices(paired_device_t *devices, size_t *count);
esp_err_t system_removePairedDevice(esp_bd_addr_t bda);
bool system_isDeviceKnown(esp_bd_addr_t bda);
esp_err_t system_getKnownDeviceCount(size_t *count);
esp_err_t system_updateConnectionTimestamp(esp_bd_addr_t bda);
const paired_device_t* system_getPairedDevices(size_t *count);
esp_err_t system_clearAllPairedDevices(void);
>>>>>>> 4feb4c0a98bddb1f2a172ea4b195ce31ba18d442
#define NVS_TIMEOUT_MS 5000
#endif

View File

@@ -7,4 +7,4 @@ with open("project_settings.cmake", "w") as out:
out.write(f'set(PROJECT_NAME "{cfg["project_name"]}")\n')
out.write(f'set(CHIP "{cfg["chip"]}")\n')
out.write(f'set(PORT "{cfg["port"]}")\n')
out.write(f'set(BAUD "{cfg["baud"]}")\n')
out.write(f'set(BAUD "{cfg["flash_baud"]}")\n')

4
project_settings.cmake Normal file
View File

@@ -0,0 +1,4 @@
set(PROJECT_NAME "soundshot")
set(CHIP "esp32")
set(PORT "COM14")
set(BAUD "460800")

210
sdkconfig
View File

@@ -1,6 +1,6 @@
#
# Automatically generated file. DO NOT EDIT.
# Espressif IoT Development Framework (ESP-IDF) 5.3.1 Project Configuration
# Espressif IoT Development Framework (ESP-IDF) 5.4.1 Project Configuration
#
CONFIG_SOC_BROWNOUT_RESET_SUPPORTED="Not determined"
CONFIG_SOC_TWAI_BRP_DIV_SUPPORTED="Not determined"
@@ -91,12 +91,14 @@ CONFIG_SOC_GPIO_OUT_RANGE_MAX=33
CONFIG_SOC_GPIO_VALID_DIGITAL_IO_PAD_MASK=0xEF0FEA
CONFIG_SOC_GPIO_CLOCKOUT_BY_IO_MUX=y
CONFIG_SOC_GPIO_CLOCKOUT_CHANNEL_NUM=3
CONFIG_SOC_GPIO_SUPPORT_HOLD_IO_IN_DSLP=y
CONFIG_SOC_I2C_NUM=2
CONFIG_SOC_HP_I2C_NUM=2
CONFIG_SOC_I2C_FIFO_LEN=32
CONFIG_SOC_I2C_CMD_REG_NUM=16
CONFIG_SOC_I2C_SUPPORT_SLAVE=y
CONFIG_SOC_I2C_SUPPORT_APB=y
CONFIG_SOC_I2C_SUPPORT_10BIT_ADDR=y
CONFIG_SOC_I2C_STOP_INDEPENDENT=y
CONFIG_SOC_I2S_NUM=2
CONFIG_SOC_I2S_HW_VERSION_1=y
@@ -111,6 +113,7 @@ CONFIG_SOC_I2S_SUPPORTS_ADC_DAC=y
CONFIG_SOC_I2S_SUPPORTS_ADC=y
CONFIG_SOC_I2S_SUPPORTS_DAC=y
CONFIG_SOC_I2S_SUPPORTS_LCD_CAMERA=y
CONFIG_SOC_I2S_MAX_DATA_WIDTH=24
CONFIG_SOC_I2S_TRANS_SIZE_ALIGN_WORD=y
CONFIG_SOC_I2S_LCD_I80_VARIANT=y
CONFIG_SOC_LCD_I80_SUPPORTED=y
@@ -120,6 +123,7 @@ CONFIG_SOC_LEDC_HAS_TIMER_SPECIFIC_MUX=y
CONFIG_SOC_LEDC_SUPPORT_APB_CLOCK=y
CONFIG_SOC_LEDC_SUPPORT_REF_TICK=y
CONFIG_SOC_LEDC_SUPPORT_HS_MODE=y
CONFIG_SOC_LEDC_TIMER_NUM=4
CONFIG_SOC_LEDC_CHANNEL_NUM=8
CONFIG_SOC_LEDC_TIMER_BIT_WIDTH=20
CONFIG_SOC_MCPWM_GROUPS=2
@@ -172,6 +176,8 @@ CONFIG_SOC_TIMER_GROUP_TIMERS_PER_GROUP=2
CONFIG_SOC_TIMER_GROUP_COUNTER_BIT_WIDTH=64
CONFIG_SOC_TIMER_GROUP_TOTAL_TIMERS=4
CONFIG_SOC_TIMER_GROUP_SUPPORT_APB=y
CONFIG_SOC_LP_TIMER_BIT_WIDTH_LO=32
CONFIG_SOC_LP_TIMER_BIT_WIDTH_HI=16
CONFIG_SOC_TOUCH_SENSOR_VERSION=1
CONFIG_SOC_TOUCH_SENSOR_NUM=10
CONFIG_SOC_TOUCH_SAMPLE_CFG_NUM=1
@@ -214,6 +220,7 @@ CONFIG_SOC_PM_SUPPORT_RC_FAST_PD=y
CONFIG_SOC_PM_SUPPORT_VDDSDIO_PD=y
CONFIG_SOC_PM_SUPPORT_MODEM_PD=y
CONFIG_SOC_CONFIGURABLE_VDDSDIO_SUPPORTED=y
CONFIG_SOC_PM_MODEM_PD_BY_SW=y
CONFIG_SOC_CLK_APLL_SUPPORTED=y
CONFIG_SOC_CLK_RC_FAST_D256_SUPPORTED=y
CONFIG_SOC_RTC_SLOW_CLK_SUPPORT_RC_FAST_D256=y
@@ -236,10 +243,11 @@ CONFIG_SOC_PHY_COMBO_MODULE=y
CONFIG_SOC_EMAC_RMII_CLK_OUT_INTERNAL_LOOPBACK=y
CONFIG_IDF_CMAKE=y
CONFIG_IDF_TOOLCHAIN="gcc"
CONFIG_IDF_TOOLCHAIN_GCC=y
CONFIG_IDF_TARGET_ARCH_XTENSA=y
CONFIG_IDF_TARGET_ARCH="xtensa"
CONFIG_IDF_TARGET="esp32"
CONFIG_IDF_INIT_VERSION="5.3.1"
CONFIG_IDF_INIT_VERSION="5.4.1"
CONFIG_IDF_TARGET_ESP32=y
CONFIG_IDF_FIRMWARE_CHIP_ID=0x0000
@@ -273,6 +281,10 @@ CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
# CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_DEBUG is not set
# CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_PERF is not set
# CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_NONE is not set
#
# Log
#
# CONFIG_BOOTLOADER_LOG_LEVEL_NONE is not set
# CONFIG_BOOTLOADER_LOG_LEVEL_ERROR is not set
# CONFIG_BOOTLOADER_LOG_LEVEL_WARN is not set
@@ -281,6 +293,14 @@ CONFIG_BOOTLOADER_LOG_LEVEL_INFO=y
# CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE is not set
CONFIG_BOOTLOADER_LOG_LEVEL=3
#
# Format
#
# CONFIG_BOOTLOADER_LOG_COLORS is not set
CONFIG_BOOTLOADER_LOG_TIMESTAMP_SOURCE_CPU_TICKS=y
# end of Format
# end of Log
#
# Serial Flash Configurations
#
@@ -336,6 +356,7 @@ CONFIG_ESP_ROM_HAS_SW_FLOAT=y
CONFIG_ESP_ROM_USB_OTG_NUM=-1
CONFIG_ESP_ROM_USB_SERIAL_DEVICE_NUM=-1
CONFIG_ESP_ROM_SUPPORT_DEEP_SLEEP_WAKEUP_STUB=y
CONFIG_ESP_ROM_HAS_OUTPUT_PUTC_FUNC=y
#
# Serial flasher config
@@ -377,6 +398,7 @@ CONFIG_ESPTOOLPY_MONITOR_BAUD=115200
# CONFIG_PARTITION_TABLE_SINGLE_APP is not set
# CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE is not set
# CONFIG_PARTITION_TABLE_TWO_OTA is not set
# CONFIG_PARTITION_TABLE_TWO_OTA_LARGE is not set
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
@@ -400,6 +422,7 @@ CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y
# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT is not set
# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE is not set
# CONFIG_COMPILER_ASSERT_NDEBUG_EVALUATE is not set
CONFIG_COMPILER_FLOAT_LIB_FROM_GCCLIB=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTION_LEVEL=2
# CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT is not set
@@ -410,14 +433,18 @@ CONFIG_COMPILER_STACK_CHECK_MODE_NONE=y
# CONFIG_COMPILER_STACK_CHECK_MODE_NORM is not set
# CONFIG_COMPILER_STACK_CHECK_MODE_STRONG is not set
# CONFIG_COMPILER_STACK_CHECK_MODE_ALL is not set
# CONFIG_COMPILER_NO_MERGE_CONSTANTS is not set
# CONFIG_COMPILER_WARN_WRITE_STRINGS is not set
# CONFIG_COMPILER_DISABLE_DEFAULT_ERRORS is not set
# CONFIG_COMPILER_DISABLE_GCC12_WARNINGS is not set
# CONFIG_COMPILER_DISABLE_GCC13_WARNINGS is not set
# CONFIG_COMPILER_DISABLE_GCC14_WARNINGS is not set
# CONFIG_COMPILER_DUMP_RTL_FILES is not set
CONFIG_COMPILER_RT_LIB_GCCLIB=y
CONFIG_COMPILER_RT_LIB_NAME="gcc"
# CONFIG_COMPILER_ORPHAN_SECTIONS_WARNING is not set
CONFIG_COMPILER_ORPHAN_SECTIONS_PLACE=y
# CONFIG_COMPILER_STATIC_ANALYZER is not set
# end of Compiler options
#
@@ -449,7 +476,7 @@ CONFIG_BT_CONTROLLER_ENABLED=y
#
# Bluedroid Options
#
CONFIG_BT_BTC_TASK_STACK_SIZE=3072
CONFIG_BT_BTC_TASK_STACK_SIZE=8192
CONFIG_BT_BLUEDROID_PINNED_TO_CORE_0=y
# CONFIG_BT_BLUEDROID_PINNED_TO_CORE_1 is not set
CONFIG_BT_BLUEDROID_PINNED_TO_CORE=0
@@ -461,10 +488,20 @@ CONFIG_BT_ENC_KEY_SIZE_CTRL_VSC=y
# CONFIG_BT_ENC_KEY_SIZE_CTRL_NONE is not set
# CONFIG_BT_CLASSIC_BQB_ENABLED is not set
CONFIG_BT_A2DP_ENABLE=y
CONFIG_BT_AVRCP_ENABLED=y
#
# AVRCP Features
#
CONFIG_BT_AVRCP_CT_COVER_ART_ENABLED=y
# end of AVRCP Features
# CONFIG_BT_SPP_ENABLED is not set
# CONFIG_BT_L2CAP_ENABLED is not set
# CONFIG_BT_SDP_COMMON_ENABLED is not set
# CONFIG_BT_HFP_ENABLE is not set
# CONFIG_BT_HID_ENABLED is not set
CONFIG_BT_GOEPC_ENABLED=y
CONFIG_BT_BLE_ENABLED=y
CONFIG_BT_GATTS_ENABLE=y
# CONFIG_BT_GATTS_PPCP_CHAR_GAP is not set
@@ -485,6 +522,7 @@ CONFIG_BT_GATTC_CONNECT_RETRY_COUNT=3
CONFIG_BT_BLE_SMP_ENABLE=y
# CONFIG_BT_SMP_SLAVE_CON_PARAMS_UPD_ENABLE is not set
# CONFIG_BT_BLE_SMP_ID_RESET_ENABLE is not set
CONFIG_BT_BLE_SMP_BOND_NVS_FLASH=y
# CONFIG_BT_STACK_NO_LOG is not set
#
@@ -672,8 +710,8 @@ CONFIG_BT_BLE_ESTAB_LINK_CONN_TOUT=30
CONFIG_BT_MAX_DEVICE_NAME_LEN=32
# CONFIG_BT_BLE_RPA_SUPPORTED is not set
CONFIG_BT_BLE_RPA_TIMEOUT=900
# CONFIG_BT_BLE_42_FEATURES_SUPPORTED is not set
# CONFIG_BT_BLE_HIGH_DUTY_ADV_INTERVAL is not set
# CONFIG_BT_ABORT_WHEN_ALLOCATION_FAILS is not set
# end of Bluedroid Options
#
@@ -682,6 +720,7 @@ CONFIG_BT_BLE_RPA_TIMEOUT=900
# CONFIG_BTDM_CTRL_MODE_BLE_ONLY is not set
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=y
# CONFIG_BTDM_CTRL_MODE_BTDM is not set
CONFIG_BTDM_CTRL_BR_EDR_MIN_ENC_KEY_SZ_DFT=7
CONFIG_BTDM_CTRL_BR_EDR_MAX_ACL_CONN=2
CONFIG_BTDM_CTRL_BR_EDR_MAX_SYNC_CONN=0
# CONFIG_BTDM_CTRL_BR_EDR_SCO_DATA_PATH_HCI is not set
@@ -692,11 +731,16 @@ CONFIG_BTDM_CTRL_PCM_ROLE_MASTER=y
# CONFIG_BTDM_CTRL_PCM_ROLE_SLAVE is not set
CONFIG_BTDM_CTRL_PCM_POLAR_FALLING_EDGE=y
# CONFIG_BTDM_CTRL_PCM_POLAR_RISING_EDGE is not set
CONFIG_BTDM_CTRL_PCM_FSYNCSHP_STEREO_MODE=y
# CONFIG_BTDM_CTRL_PCM_FSYNCSHP_MONO_MODE_LF is not set
# CONFIG_BTDM_CTRL_PCM_FSYNCSHP_MONO_MODE_FF is not set
CONFIG_BTDM_CTRL_PCM_ROLE_EFF=0
CONFIG_BTDM_CTRL_PCM_POLAR_EFF=0
CONFIG_BTDM_CTRL_PCM_FSYNCSHP_EFF=0
CONFIG_BTDM_CTRL_LEGACY_AUTH_VENDOR_EVT=y
CONFIG_BTDM_CTRL_LEGACY_AUTH_VENDOR_EVT_EFF=y
CONFIG_BTDM_CTRL_BLE_MAX_CONN_EFF=0
CONFIG_BTDM_CTRL_BR_EDR_MIN_ENC_KEY_SZ_DFT_EFF=7
CONFIG_BTDM_CTRL_BR_EDR_MAX_ACL_CONN_EFF=2
CONFIG_BTDM_CTRL_BR_EDR_MAX_SYNC_CONN_EFF=0
CONFIG_BTDM_CTRL_PINNED_TO_CORE_0=y
@@ -716,6 +760,14 @@ CONFIG_BTDM_CTRL_LPCLK_SEL_MAIN_XTAL=y
CONFIG_BTDM_BLE_SLEEP_CLOCK_ACCURACY_INDEX_EFF=1
# CONFIG_BTDM_CTRL_SCAN_BACKOFF_UPPERLIMITMAX is not set
# CONFIG_BTDM_CTRL_CHECK_CONNECT_IND_ACCESS_ADDRESS is not set
#
# BLE disconnects when Instant Passed (0x28) occurs
#
# end of BLE disconnects when Instant Passed (0x28) occurs
# CONFIG_BTDM_CTRL_CONTROLLER_DEBUG_MODE_1 is not set
CONFIG_BTDM_RESERVE_DRAM=0xdb5c
CONFIG_BTDM_CTRL_HLI=y
# end of Controller Options
@@ -724,6 +776,7 @@ CONFIG_BTDM_CTRL_HLI=y
# Common Options
#
CONFIG_BT_ALARM_MAX_NUM=50
# CONFIG_BT_BLE_LOG_SPI_OUT_ENABLED is not set
# end of Common Options
# CONFIG_BT_HCI_LOG_DEBUG_EN is not set
@@ -757,6 +810,7 @@ CONFIG_TWAI_ERRATA_FIX_LISTEN_ONLY_DOM=y
#
CONFIG_ADC_DISABLE_DAC=y
# CONFIG_ADC_SUPPRESS_DEPRECATE_WARN is not set
# CONFIG_ADC_SKIP_LEGACY_CONFLICT_CHECK is not set
#
# Legacy ADC Calibration Configuration
@@ -772,42 +826,49 @@ CONFIG_ADC_CAL_LUT_ENABLE=y
# Legacy DAC Driver Configurations
#
# CONFIG_DAC_SUPPRESS_DEPRECATE_WARN is not set
# CONFIG_DAC_SKIP_LEGACY_CONFLICT_CHECK is not set
# end of Legacy DAC Driver Configurations
#
# Legacy MCPWM Driver Configurations
#
# CONFIG_MCPWM_SUPPRESS_DEPRECATE_WARN is not set
# CONFIG_MCPWM_SKIP_LEGACY_CONFLICT_CHECK is not set
# end of Legacy MCPWM Driver Configurations
#
# Legacy Timer Group Driver Configurations
#
# CONFIG_GPTIMER_SUPPRESS_DEPRECATE_WARN is not set
# CONFIG_GPTIMER_SKIP_LEGACY_CONFLICT_CHECK is not set
# end of Legacy Timer Group Driver Configurations
#
# Legacy RMT Driver Configurations
#
# CONFIG_RMT_SUPPRESS_DEPRECATE_WARN is not set
# CONFIG_RMT_SKIP_LEGACY_CONFLICT_CHECK is not set
# end of Legacy RMT Driver Configurations
#
# Legacy I2S Driver Configurations
#
# CONFIG_I2S_SUPPRESS_DEPRECATE_WARN is not set
# CONFIG_I2S_SKIP_LEGACY_CONFLICT_CHECK is not set
# end of Legacy I2S Driver Configurations
#
# Legacy PCNT Driver Configurations
#
# CONFIG_PCNT_SUPPRESS_DEPRECATE_WARN is not set
# CONFIG_PCNT_SKIP_LEGACY_CONFLICT_CHECK is not set
# end of Legacy PCNT Driver Configurations
#
# Legacy SDM Driver Configurations
#
# CONFIG_SDM_SUPPRESS_DEPRECATE_WARN is not set
# CONFIG_SDM_SKIP_LEGACY_CONFLICT_CHECK is not set
# end of Legacy SDM Driver Configurations
# end of Driver Configurations
@@ -859,6 +920,7 @@ CONFIG_ADC_DISABLE_DAC_OUTPUT=y
CONFIG_ESP_COEX_ENABLED=y
CONFIG_ESP_COEX_SW_COEXIST_ENABLE=y
# CONFIG_ESP_COEX_POWER_MANAGEMENT is not set
# CONFIG_ESP_COEX_GPIO_DEBUG is not set
# end of Wireless Coexistence
#
@@ -897,6 +959,7 @@ CONFIG_GPTIMER_ISR_HANDLER_IN_IRAM=y
#
# CONFIG_I2C_ISR_IRAM_SAFE is not set
# CONFIG_I2C_ENABLE_DEBUG_LOG is not set
# CONFIG_I2C_ENABLE_SLAVE_DRIVER_VERSION_2 is not set
# end of ESP-Driver:I2C Configurations
#
@@ -1004,6 +1067,13 @@ CONFIG_ESP_GDBSTUB_SUPPORT_TASKS=y
CONFIG_ESP_GDBSTUB_MAX_TASKS=32
# end of GDB Stub
#
# ESP HID
#
CONFIG_ESPHID_TASK_SIZE_BT=2048
CONFIG_ESPHID_TASK_SIZE_BLE=4096
# end of ESP HID
#
# ESP HTTP client
#
@@ -1011,6 +1081,7 @@ CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=y
# CONFIG_ESP_HTTP_CLIENT_ENABLE_BASIC_AUTH is not set
# CONFIG_ESP_HTTP_CLIENT_ENABLE_DIGEST_AUTH is not set
# CONFIG_ESP_HTTP_CLIENT_ENABLE_CUSTOM_TRANSPORT is not set
CONFIG_ESP_HTTP_CLIENT_EVENT_POST_TIMEOUT=2000
# end of ESP HTTP client
#
@@ -1023,6 +1094,7 @@ CONFIG_HTTPD_PURGE_BUF_LEN=32
# CONFIG_HTTPD_LOG_PURGE_DATA is not set
# CONFIG_HTTPD_WS_SUPPORT is not set
# CONFIG_HTTPD_QUEUE_WORK_BLOCKING is not set
CONFIG_HTTPD_SERVER_EVENT_POST_TIMEOUT=2000
# end of HTTP Server
#
@@ -1030,12 +1102,14 @@ CONFIG_HTTPD_PURGE_BUF_LEN=32
#
# CONFIG_ESP_HTTPS_OTA_DECRYPT_CB is not set
# CONFIG_ESP_HTTPS_OTA_ALLOW_HTTP is not set
CONFIG_ESP_HTTPS_OTA_EVENT_POST_TIMEOUT=2000
# end of ESP HTTPS OTA
#
# ESP HTTPS server
#
# CONFIG_ESP_HTTPS_SERVER_ENABLE is not set
CONFIG_ESP_HTTPS_SERVER_EVENT_POST_TIMEOUT=2000
# end of ESP HTTPS server
#
@@ -1060,6 +1134,12 @@ CONFIG_ESP_REV_MIN_FULL=0
#
CONFIG_ESP32_REV_MAX_FULL=399
CONFIG_ESP_REV_MAX_FULL=399
CONFIG_ESP_EFUSE_BLOCK_REV_MIN_FULL=0
CONFIG_ESP_EFUSE_BLOCK_REV_MAX_FULL=99
#
# Maximum Supported ESP32 eFuse Block Revision (eFuse Block Rev v0.99)
#
# end of Chip revision
#
@@ -1112,6 +1192,7 @@ CONFIG_PERIPH_CTRL_FUNC_IN_IRAM=y
# Main XTAL Config
#
# CONFIG_XTAL_FREQ_26 is not set
# CONFIG_XTAL_FREQ_32 is not set
CONFIG_XTAL_FREQ_40=y
# CONFIG_XTAL_FREQ_AUTO is not set
CONFIG_XTAL_FREQ=40
@@ -1121,31 +1202,29 @@ CONFIG_ESP_SPI_BUS_LOCK_ISR_FUNCS_IN_IRAM=y
# end of Hardware Settings
#
# LCD and Touch Panel
# ESP-Driver:LCD Controller Configurations
#
#
# LCD Touch Drivers are maintained in the IDF Component Registry
#
#
# LCD Peripheral Configuration
#
CONFIG_LCD_PANEL_IO_FORMAT_BUF_SIZE=32
# CONFIG_LCD_ENABLE_DEBUG_LOG is not set
# end of LCD Peripheral Configuration
# end of LCD and Touch Panel
# end of ESP-Driver:LCD Controller Configurations
#
# ESP-MM: Memory Management Configurations
#
# end of ESP-MM: Memory Management Configurations
#
# ESP NETIF Adapter
#
CONFIG_ESP_NETIF_IP_LOST_TIMER_INTERVAL=120
# CONFIG_ESP_NETIF_PROVIDE_CUSTOM_IMPLEMENTATION is not set
CONFIG_ESP_NETIF_TCPIP_LWIP=y
# CONFIG_ESP_NETIF_LOOPBACK is not set
CONFIG_ESP_NETIF_USES_TCPIP_WITH_BSD_API=y
CONFIG_ESP_NETIF_REPORT_DATA_TRAFFIC=y
# CONFIG_ESP_NETIF_RECEIVE_REPORT_ERRORS is not set
# CONFIG_ESP_NETIF_L2_TAP is not set
# CONFIG_ESP_NETIF_BRIDGE_EN is not set
# CONFIG_ESP_NETIF_SET_DNS_PER_DEFAULT_NETIF is not set
# end of ESP NETIF Adapter
#
@@ -1162,17 +1241,20 @@ CONFIG_ESP_PHY_CALIBRATION_AND_DATA_STORAGE=y
CONFIG_ESP_PHY_MAX_WIFI_TX_POWER=20
CONFIG_ESP_PHY_MAX_TX_POWER=20
# CONFIG_ESP_PHY_REDUCE_TX_POWER is not set
# CONFIG_ESP_PHY_ENABLE_CERT_TEST is not set
CONFIG_ESP_PHY_RF_CAL_PARTIAL=y
# CONFIG_ESP_PHY_RF_CAL_NONE is not set
# CONFIG_ESP_PHY_RF_CAL_FULL is not set
CONFIG_ESP_PHY_CALIBRATION_MODE=0
# CONFIG_ESP_PHY_PLL_TRACK_DEBUG is not set
# CONFIG_ESP_PHY_RECORD_USED_TIME is not set
# end of PHY
#
# Power Management
#
# CONFIG_PM_ENABLE is not set
CONFIG_PM_SLP_IRAM_OPT=y
# end of Power Management
#
@@ -1187,6 +1269,11 @@ CONFIG_ESP_PHY_CALIBRATION_MODE=0
# CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH is not set
# end of ESP Ringbuf
#
# ESP Security Specific
#
# end of ESP Security Specific
#
# ESP System Settings
#
@@ -1404,6 +1491,9 @@ CONFIG_FATFS_FS_LOCK=0
CONFIG_FATFS_TIMEOUT_MS=10000
CONFIG_FATFS_PER_FILE_CACHE=y
# CONFIG_FATFS_USE_FASTSEEK is not set
CONFIG_FATFS_USE_STRFUNC_NONE=y
# CONFIG_FATFS_USE_STRFUNC_WITHOUT_CRLF_CONV is not set
# CONFIG_FATFS_USE_STRFUNC_WITH_CRLF_CONV is not set
CONFIG_FATFS_VFS_FSTAT_BLKSIZE=0
# CONFIG_FATFS_IMMEDIATE_FSYNC is not set
# CONFIG_FATFS_USE_LABEL is not set
@@ -1429,6 +1519,7 @@ CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=1536
# CONFIG_FREERTOS_USE_TICK_HOOK is not set
CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16
# CONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITY is not set
CONFIG_FREERTOS_USE_TIMERS=y
CONFIG_FREERTOS_TIMER_SERVICE_TASK_NAME="Tmr Svc"
# CONFIG_FREERTOS_TIMER_TASK_AFFINITY_CPU0 is not set
# CONFIG_FREERTOS_TIMER_TASK_AFFINITY_CPU1 is not set
@@ -1464,6 +1555,11 @@ CONFIG_FREERTOS_SYSTICK_USES_CCOUNT=y
# CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE is not set
# end of Port
#
# Extra
#
# end of Extra
CONFIG_FREERTOS_PORT=y
CONFIG_FREERTOS_NO_AFFINITY=0x7FFFFFFF
CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y
@@ -1501,7 +1597,11 @@ CONFIG_HEAP_ABORT_WHEN_ALLOCATION_FAILS=y
# end of Heap memory debugging
#
# Log output
# Log
#
#
# Log Level
#
# CONFIG_LOG_DEFAULT_LEVEL_NONE is not set
# CONFIG_LOG_DEFAULT_LEVEL_ERROR is not set
@@ -1514,11 +1614,29 @@ CONFIG_LOG_MAXIMUM_EQUALS_DEFAULT=y
# CONFIG_LOG_MAXIMUM_LEVEL_DEBUG is not set
# CONFIG_LOG_MAXIMUM_LEVEL_VERBOSE is not set
CONFIG_LOG_MAXIMUM_LEVEL=3
#
# Level Settings
#
# CONFIG_LOG_MASTER_LEVEL is not set
CONFIG_LOG_DYNAMIC_LEVEL_CONTROL=y
# CONFIG_LOG_TAG_LEVEL_IMPL_NONE is not set
# CONFIG_LOG_TAG_LEVEL_IMPL_LINKED_LIST is not set
CONFIG_LOG_TAG_LEVEL_IMPL_CACHE_AND_LINKED_LIST=y
# CONFIG_LOG_TAG_LEVEL_CACHE_ARRAY is not set
CONFIG_LOG_TAG_LEVEL_CACHE_BINARY_MIN_HEAP=y
CONFIG_LOG_TAG_LEVEL_IMPL_CACHE_SIZE=31
# end of Level Settings
# end of Log Level
#
# Format
#
CONFIG_LOG_COLORS=y
CONFIG_LOG_TIMESTAMP_SOURCE_RTOS=y
# CONFIG_LOG_TIMESTAMP_SOURCE_SYSTEM is not set
# end of Log output
# end of Format
# end of Log
#
# LWIP
@@ -1557,6 +1675,8 @@ CONFIG_LWIP_ESP_MLDV6_REPORT=y
CONFIG_LWIP_MLDV6_TMR_INTERVAL=40
CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=32
CONFIG_LWIP_DHCP_DOES_ARP_CHECK=y
# CONFIG_LWIP_DHCP_DOES_ACD_CHECK is not set
# CONFIG_LWIP_DHCP_DOES_NOT_CHECK_OFFERED_IP is not set
# CONFIG_LWIP_DHCP_DISABLE_CLIENT_ID is not set
CONFIG_LWIP_DHCP_DISABLE_VENDOR_CLASS_ID=y
# CONFIG_LWIP_DHCP_RESTORE_LAST_IP is not set
@@ -1571,6 +1691,7 @@ CONFIG_LWIP_DHCPS=y
CONFIG_LWIP_DHCPS_LEASE_UNIT=60
CONFIG_LWIP_DHCPS_MAX_STATION_NUM=8
CONFIG_LWIP_DHCPS_STATIC_ENTRIES=y
CONFIG_LWIP_DHCPS_ADD_DNS=y
# end of DHCP server
# CONFIG_LWIP_AUTOIP is not set
@@ -1629,9 +1750,12 @@ CONFIG_LWIP_TCPIP_TASK_AFFINITY_NO_AFFINITY=y
# CONFIG_LWIP_TCPIP_TASK_AFFINITY_CPU0 is not set
# CONFIG_LWIP_TCPIP_TASK_AFFINITY_CPU1 is not set
CONFIG_LWIP_TCPIP_TASK_AFFINITY=0x7FFFFFFF
# CONFIG_LWIP_PPP_SUPPORT is not set
CONFIG_LWIP_IPV6_MEMP_NUM_ND6_QUEUE=3
CONFIG_LWIP_IPV6_ND6_NUM_NEIGHBORS=5
CONFIG_LWIP_IPV6_ND6_NUM_PREFIXES=5
CONFIG_LWIP_IPV6_ND6_NUM_ROUTERS=3
CONFIG_LWIP_IPV6_ND6_NUM_DESTINATIONS=10
# CONFIG_LWIP_PPP_SUPPORT is not set
# CONFIG_LWIP_SLIP_SUPPORT is not set
#
@@ -1661,8 +1785,10 @@ CONFIG_LWIP_SNTP_MAXIMUM_STARTUP_DELAY=5000
#
# DNS
#
CONFIG_LWIP_DNS_MAX_HOST_IP=1
CONFIG_LWIP_DNS_MAX_SERVERS=3
# CONFIG_LWIP_FALLBACK_DNS_SERVER_SUPPORT is not set
# CONFIG_LWIP_DNS_SETSERVER_WITH_NETIF is not set
# end of DNS
CONFIG_LWIP_BRIDGEIF_MAX_PORTS=7
@@ -1686,6 +1812,8 @@ CONFIG_LWIP_HOOK_IP6_SELECT_SRC_ADDR_NONE=y
CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_NONE=y
# CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_DEFAULT is not set
# CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_CUSTOM is not set
CONFIG_LWIP_HOOK_DNS_EXT_RESOLVE_NONE=y
# CONFIG_LWIP_HOOK_DNS_EXT_RESOLVE_CUSTOM is not set
CONFIG_LWIP_HOOK_IP6_INPUT_NONE=y
# CONFIG_LWIP_HOOK_IP6_INPUT_DEFAULT is not set
# CONFIG_LWIP_HOOK_IP6_INPUT_CUSTOM is not set
@@ -1744,6 +1872,7 @@ CONFIG_MBEDTLS_HAVE_TIME=y
# CONFIG_MBEDTLS_HAVE_TIME_DATE is not set
CONFIG_MBEDTLS_ECDSA_DETERMINISTIC=y
CONFIG_MBEDTLS_SHA512_C=y
CONFIG_MBEDTLS_SHA3_C=y
CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT=y
# CONFIG_MBEDTLS_TLS_SERVER_ONLY is not set
# CONFIG_MBEDTLS_TLS_CLIENT_ONLY is not set
@@ -1797,6 +1926,8 @@ CONFIG_MBEDTLS_X509_CSR_PARSE_C=y
# end of Certificates
CONFIG_MBEDTLS_ECP_C=y
CONFIG_MBEDTLS_PK_PARSE_EC_EXTENDED=y
CONFIG_MBEDTLS_PK_PARSE_EC_COMPRESSED=y
# CONFIG_MBEDTLS_DHM_C is not set
CONFIG_MBEDTLS_ECDH_C=y
CONFIG_MBEDTLS_ECDSA_C=y
@@ -1820,6 +1951,7 @@ CONFIG_MBEDTLS_ECP_FIXED_POINT_OPTIM=y
# CONFIG_MBEDTLS_HKDF_C is not set
# CONFIG_MBEDTLS_THREADING_C is not set
CONFIG_MBEDTLS_ERROR_STRINGS=y
CONFIG_MBEDTLS_FS_IO=y
# end of mbedTLS
#
@@ -1867,25 +1999,10 @@ CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC_HRT=y
# CONFIG_OPENTHREAD_ENABLED is not set
#
# Thread Operational Dataset
# OpenThread Spinel
#
CONFIG_OPENTHREAD_NETWORK_NAME="OpenThread-ESP"
CONFIG_OPENTHREAD_MESH_LOCAL_PREFIX="fd00:db8:a0:0::/64"
CONFIG_OPENTHREAD_NETWORK_CHANNEL=15
CONFIG_OPENTHREAD_NETWORK_PANID=0x1234
CONFIG_OPENTHREAD_NETWORK_EXTPANID="dead00beef00cafe"
CONFIG_OPENTHREAD_NETWORK_MASTERKEY="00112233445566778899aabbccddeeff"
CONFIG_OPENTHREAD_NETWORK_PSKC="104810e2315100afd6bc9215a6bfac53"
# end of Thread Operational Dataset
CONFIG_OPENTHREAD_XTAL_ACCURACY=130
# CONFIG_OPENTHREAD_SPINEL_ONLY is not set
# CONFIG_OPENTHREAD_RX_ON_WHEN_IDLE is not set
#
# Thread Address Query Config
#
# end of Thread Address Query Config
# end of OpenThread Spinel
# end of OpenThread
#
@@ -1894,6 +2011,7 @@ CONFIG_OPENTHREAD_XTAL_ACCURACY=130
CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_0=y
CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_1=y
CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_2=y
CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_PATCH_VERSION=y
# end of Protocomm
#
@@ -1936,6 +2054,7 @@ CONFIG_SPI_FLASH_BROWNOUT_RESET=y
# Features here require specific hardware (READ DOCS FIRST!)
#
CONFIG_SPI_FLASH_SUSPEND_TSUS_VAL_US=50
# CONFIG_SPI_FLASH_FORCE_ENABLE_XMC_C_SUSPEND is not set
# end of Optional and Experimental Features (READ DOCS FIRST)
# end of Main Flash configuration
@@ -2065,6 +2184,8 @@ CONFIG_VFS_MAX_COUNT=8
#
CONFIG_VFS_SEMIHOSTFS_MAX_MOUNT_POINTS=1
# end of Host File System I/O (Semihosting)
CONFIG_VFS_INITIALIZE_DEV_NULL=y
# end of Virtual file system
#
@@ -2082,6 +2203,7 @@ CONFIG_WIFI_PROV_SCAN_MAX_ENTRIES=16
CONFIG_WIFI_PROV_AUTOSTOP_TIMEOUT=30
# CONFIG_WIFI_PROV_BLE_BONDING is not set
# CONFIG_WIFI_PROV_BLE_FORCE_ENCRYPTION is not set
# CONFIG_WIFI_PROV_BLE_NOTIFY is not set
# CONFIG_WIFI_PROV_KEEP_BLE_ON_AFTER_PROV is not set
CONFIG_WIFI_PROV_STA_ALL_CHANNEL_SCAN=y
# CONFIG_WIFI_PROV_STA_FAST_SCAN is not set
@@ -2246,13 +2368,13 @@ CONFIG_LV_ATTRIBUTE_MEM_ALIGN_SIZE=1
#
# Enable built-in fonts
#
# CONFIG_LV_FONT_MONTSERRAT_8 is not set
# CONFIG_LV_FONT_MONTSERRAT_10 is not set
# CONFIG_LV_FONT_MONTSERRAT_12 is not set
CONFIG_LV_FONT_MONTSERRAT_8=y
CONFIG_LV_FONT_MONTSERRAT_10=y
CONFIG_LV_FONT_MONTSERRAT_12=y
CONFIG_LV_FONT_MONTSERRAT_14=y
# CONFIG_LV_FONT_MONTSERRAT_16 is not set
# CONFIG_LV_FONT_MONTSERRAT_18 is not set
CONFIG_LV_FONT_MONTSERRAT_20=y
# CONFIG_LV_FONT_MONTSERRAT_20 is not set
# CONFIG_LV_FONT_MONTSERRAT_22 is not set
# CONFIG_LV_FONT_MONTSERRAT_24 is not set
# CONFIG_LV_FONT_MONTSERRAT_26 is not set
@@ -2272,7 +2394,7 @@ CONFIG_LV_FONT_MONTSERRAT_20=y
# CONFIG_LV_FONT_SIMSUN_14_CJK is not set
# CONFIG_LV_FONT_SIMSUN_16_CJK is not set
CONFIG_LV_FONT_UNSCII_8=y
# CONFIG_LV_FONT_UNSCII_16 is not set
CONFIG_LV_FONT_UNSCII_16=y
# end of Enable built-in fonts
# CONFIG_LV_FONT_DEFAULT_MONTSERRAT_8 is not set
@@ -2455,7 +2577,7 @@ CONFIG_LVGL_VERSION_PATCH=2
#
# Examples
#
CONFIG_LV_BUILD_EXAMPLES=y
# CONFIG_LV_BUILD_EXAMPLES is not set
# end of Examples
#
@@ -2515,7 +2637,7 @@ CONFIG_ESP32_APPTRACE_DEST_NONE=y
CONFIG_ESP32_APPTRACE_LOCK_ENABLE=y
CONFIG_BLUEDROID_ENABLED=y
# CONFIG_NIMBLE_ENABLED is not set
CONFIG_BTC_TASK_STACK_SIZE=3072
CONFIG_BTC_TASK_STACK_SIZE=8192
CONFIG_BLUEDROID_PINNED_TO_CORE_0=y
# CONFIG_BLUEDROID_PINNED_TO_CORE_1 is not set
CONFIG_BLUEDROID_PINNED_TO_CORE=0
@@ -2811,8 +2933,6 @@ CONFIG_ESP32_WIFI_DYNAMIC_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_AMPDU_RX_ENABLED=y
CONFIG_ESP32_WIFI_RX_BA_WIN=6
CONFIG_ESP32_WIFI_RX_BA_WIN=6
CONFIG_ESP32_WIFI_NVS_ENABLED=y
CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_0=y

View File

@@ -1,4 +1,3 @@
<<<<<<< HEAD
# Override some defaults so BT stack is enabled and
# Classic BT is enabled
CONFIG_BT_ENABLED=y
@@ -8,14 +7,3 @@ CONFIG_BTDM_CTRL_MODE_BTDM=n
CONFIG_BT_BLUEDROID_ENABLED=y
CONFIG_BT_CLASSIC_ENABLED=y
CONFIG_BT_A2DP_ENABLE=y
=======
# Override some defaults so BT stack is enabled and
# Classic BT is enabled
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=n
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=y
CONFIG_BTDM_CTRL_MODE_BTDM=n
CONFIG_BT_BLUEDROID_ENABLED=y
CONFIG_BT_CLASSIC_ENABLED=y
CONFIG_BT_A2DP_ENABLE=y
>>>>>>> 4feb4c0a98bddb1f2a172ea4b195ce31ba18d442

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,11 @@
{
"project_name": "soundshot",
"chip": "esp32h2",
"port": "COM8",
"chip": "esp32",
"port": "COM14",
"monitor_baud": 115200,
"flash_baud": 460800,
"flash_mode": "dio",
"flash_freq": "48m",
"flash_freq": "40m",
"flash_size": "2MB",
"before": "default-reset",
"after": "hard-reset"