Compare commits
20 Commits
4697b369db
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| d427859804 | |||
| 40bea065a7 | |||
|
|
3bce9e772c | ||
|
|
31e0e3a148 | ||
|
|
a272a15bcf | ||
|
|
8a1966ea90 | ||
|
|
b8a3a09e9f | ||
|
|
115105c032 | ||
|
|
19e8ca30c3 | ||
|
|
de7041c1f5 | ||
|
|
fe69dc6f19 | ||
|
|
bca2f6ea9c | ||
|
|
5a893a034c | ||
| 2513a9e7fb | |||
|
|
04d2c71d01 | ||
|
|
a89fdc6843 | ||
|
|
439c6ef22d | ||
|
|
25f875b3b2 | ||
|
|
c4ca91bf84 | ||
|
|
fb9fd84bf1 |
32
.claude/settings.local.json
Normal file
32
.claude/settings.local.json
Normal 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
17
.gitignore
vendored
@@ -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
39
.vscode/c_cpp_properties.json
vendored
Normal 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
50
.vscode/launch.json
vendored
Normal 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"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
17
.vscode/settings.json
vendored
17
.vscode/settings.json
vendored
@@ -1,11 +1,12 @@
|
||||
{
|
||||
"C_Cpp.intelliSenseEngine": "default",
|
||||
"idf.espIdfPathWin": "C:\\Users\\brent.RPX\\esp\\v5.3.1\\esp-idf",
|
||||
"idf.projectName": "soundshot",
|
||||
"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": "COM3",
|
||||
"idf.toolsPathWin": "C:\\Users\\Brent.Perteet\\.espressif",
|
||||
"idf.flashType": "UART",
|
||||
"files.associations": {
|
||||
"esp_system.h": "c",
|
||||
@@ -35,7 +36,13 @@
|
||||
"semphr.h": "c",
|
||||
"lv_spinbox.h": "c",
|
||||
"lv_slider.h": "c",
|
||||
"lv_menu.h": "c"
|
||||
"lv_menu.h": "c",
|
||||
"stdint.h": "c",
|
||||
"random": "c",
|
||||
"string.h": "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
37
.vscode/tasks.json
vendored
Normal 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
147
CLAUDE.md
Normal file
@@ -0,0 +1,147 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
SoundShot is an ESP32-based embedded system that appears to be an IMU-based angle measurement device with Bluetooth connectivity and GUI interface. The project uses ESP-IDF (Espressif IoT Development Framework) and can be developed using either Docker containers or native tooling.
|
||||
|
||||
## Build System & Development Environment
|
||||
|
||||
### Docker Development (Recommended)
|
||||
- **Setup**: Run `setup-env.bat` to create Python virtual environment and install dependencies
|
||||
- **Container**: Uses ESP-IDF Docker containers with VS Code Dev Containers extension
|
||||
- **Build**: `idf.py build` (inside container)
|
||||
|
||||
### Native Development
|
||||
- **Build**: `idf.py build` (requires ESP-IDF setup)
|
||||
- **Configuration**: Uses `settings.json` for project parameters
|
||||
- **CMake**: Project uses ESP-IDF CMake build system with custom configuration generator
|
||||
|
||||
### Essential Commands
|
||||
|
||||
```bash
|
||||
# Environment setup (Windows only)
|
||||
setup-env.bat
|
||||
|
||||
# Build firmware
|
||||
idf.py build
|
||||
|
||||
# Flash device (using custom Python tool)
|
||||
flash.bat
|
||||
python flash_tool/flash.py
|
||||
|
||||
# Monitor serial output
|
||||
monitor.bat
|
||||
python flash_tool/monitor.py
|
||||
|
||||
# Combined flash and monitor
|
||||
flashmon.bat
|
||||
|
||||
# Clean build
|
||||
idf.py fullclean
|
||||
```
|
||||
|
||||
## Project Architecture
|
||||
|
||||
### Core Components
|
||||
|
||||
**Main Application (`main/`)**
|
||||
- `main.c` - Entry point, IMU processing, task coordination
|
||||
- `system.c/h` - System state management, event handling, calibration
|
||||
- `gui.c/h` - LVGL-based user interface (3 modes: main, menu, bubble)
|
||||
- `bt_app.c/h` - Bluetooth connectivity and communication
|
||||
- `lsm6dsv.c/h` - LSM6DSV IMU sensor driver
|
||||
- `keypad.c/h` - Physical button input handling
|
||||
- `bubble.c/h` - Bubble level visualization
|
||||
|
||||
**Key Features**
|
||||
- Real-time angle measurement with configurable filtering
|
||||
- Bluetooth connectivity for data transmission
|
||||
- LVGL-based GUI with multiple display modes
|
||||
- Zero-angle calibration system
|
||||
- Low-pass filtering with adaptive alpha coefficients
|
||||
|
||||
### Hardware Configuration
|
||||
|
||||
**GPIO Pins** (`gpio.h`)
|
||||
- LEDs, LCD backlight, power control
|
||||
- I2C for IMU communication (SCL=22, SDA=21)
|
||||
- Serial communication and button inputs
|
||||
|
||||
**IMU Processing**
|
||||
- LSM6DSV 6-axis IMU sensor
|
||||
- Angle computation from accelerometer data (XZ, YZ, XY planes)
|
||||
- Configurable filtering with `FILTER_COEFF` and `MAX_INDICATION_ANGLE`
|
||||
- Primary axis selection via `PRIMARY_AXIS` define
|
||||
|
||||
## Configuration Management
|
||||
|
||||
### settings.json Structure
|
||||
```json
|
||||
{
|
||||
"project_name": "soundshot",
|
||||
"chip": "esp32",
|
||||
"port": "COM3",
|
||||
"monitor_baud": 115200,
|
||||
"flash_baud": 460800,
|
||||
"flash_mode": "dio",
|
||||
"flash_freq": "40m",
|
||||
"flash_size": "2MB"
|
||||
}
|
||||
```
|
||||
|
||||
**Configuration Flow**
|
||||
1. `settings.json` → `prepare_config.py` → `project_settings.cmake`
|
||||
2. CMake includes generated settings during build
|
||||
3. Flash tools read `settings.json` for esptool parameters
|
||||
|
||||
### Build Dependencies
|
||||
|
||||
**ESP-IDF Components**
|
||||
- `driver` - GPIO, I2C, peripherals
|
||||
- `esp_lcd` - Display drivers
|
||||
- `lvgl` + `esp_lvgl_port` - GUI framework
|
||||
- `esp_timer` - High resolution timers
|
||||
- `nvs_flash` - Non-volatile storage
|
||||
- `bt` - Bluetooth stack
|
||||
|
||||
**Managed Components**
|
||||
- LVGL 9.x with ESP32 port integration
|
||||
- Located in `managed_components/`
|
||||
|
||||
## Development Guidelines
|
||||
|
||||
### Code Organization
|
||||
- Hardware abstraction in dedicated modules (`lsm6dsv.c`, `keypad.c`)
|
||||
- System state centralized in `system.c` with event-based communication
|
||||
- GUI logic separated from business logic
|
||||
- Bluetooth handled as separate subsystem
|
||||
|
||||
### Key Constants & Tuning Parameters
|
||||
- `MAX_INDICATION_ANGLE` (5.0°) - Angle measurement limit
|
||||
- `FILTER_COEFF` (0.4) - Low-pass filter strength
|
||||
- `PRIMARY_AXIS` - Main measurement axis selection
|
||||
- Filter alpha computed dynamically based on input magnitude
|
||||
|
||||
### Testing & Debugging
|
||||
- Serial monitoring at 115200 baud via `monitor.bat`
|
||||
- Debug prints use ESP-IDF logging (`ESP_LOGI`, `ESP_LOGW`, etc.)
|
||||
- IMU data output can be enabled via conditional compilation blocks
|
||||
|
||||
### Flash Memory Layout
|
||||
- Bootloader: 0x1000
|
||||
- Partition table: 0x8000
|
||||
- OTA data: 0x11000
|
||||
- Application: 0x20000
|
||||
|
||||
## Common Development Patterns
|
||||
|
||||
When modifying this codebase:
|
||||
1. IMU changes: Update filtering parameters in `main.c` and constants in `system.h`
|
||||
2. GUI changes: Modify LVGL code in `gui.c`, add new modes to `GuiMode_t` enum
|
||||
3. Bluetooth changes: Update message handlers in `bt_app.c`
|
||||
4. Hardware changes: Update pin definitions in `gpio.h` and initialization in `main.c`
|
||||
5. Build changes: Modify component dependencies in `main/CMakeLists.txt`
|
||||
|
||||
Always test flashing and monitoring tools after hardware configuration changes.
|
||||
@@ -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})
|
||||
93
README.md
93
README.md
@@ -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
51
bt_test/bt_test.ino
Normal 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
141
docker.md
Normal 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 repo’s `README.md`. It’s 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 container’s 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 doesn’t 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
4
flash_gui.bat
Normal file
@@ -0,0 +1,4 @@
|
||||
@echo off
|
||||
echo Starting ESP32 Firmware Flash GUI...
|
||||
python flash_tool/gui_flasher.py
|
||||
pause
|
||||
9
flash_tool/.claude/settings.local.json
Normal file
9
flash_tool/.claude/settings.local.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(python:*)"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
}
|
||||
}
|
||||
110
flash_tool/BUILD_NOTES.md
Normal file
110
flash_tool/BUILD_NOTES.md
Normal 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
|
||||
```
|
||||
43
flash_tool/ESP32_Flasher.spec
Normal file
43
flash_tool/ESP32_Flasher.spec
Normal 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,
|
||||
)
|
||||
45
flash_tool/ESP32_Flasher_Windows.spec
Normal file
45
flash_tool/ESP32_Flasher_Windows.spec
Normal 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
|
||||
)
|
||||
56
flash_tool/ESP32_Flasher_macOS.spec
Normal file
56
flash_tool/ESP32_Flasher_macOS.spec
Normal 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',
|
||||
},
|
||||
)
|
||||
81
flash_tool/ESP32_WebFlasher_Windows.spec
Normal file
81
flash_tool/ESP32_WebFlasher_Windows.spec
Normal 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
|
||||
)
|
||||
69
flash_tool/ESP32_WebFlasher_macOS.spec
Normal file
69
flash_tool/ESP32_WebFlasher_macOS.spec
Normal 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
148
flash_tool/README.md
Normal 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
BIN
flash_tool/SoundShot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 105 KiB |
171
flash_tool/build.py
Executable file
171
flash_tool/build.py
Executable 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
7
flash_tool/build_exe.bat
Normal 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
|
||||
49
flash_tool/build_executable.py
Normal file
49
flash_tool/build_executable.py
Normal 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()
|
||||
24
flash_tool/build_from_spec.bat
Normal file
24
flash_tool/build_from_spec.bat
Normal 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
45
flash_tool/build_macos.sh
Executable 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
43
flash_tool/build_web_macos.sh
Executable 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
|
||||
32
flash_tool/build_web_windows.bat
Normal file
32
flash_tool/build_web_windows.bat
Normal 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
|
||||
14
flash_tool/build_with_auto_py_to_exe.bat
Normal file
14
flash_tool/build_with_auto_py_to_exe.bat
Normal 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
|
||||
@@ -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
337
flash_tool/gui_flasher.py
Normal 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()
|
||||
@@ -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:
|
||||
|
||||
6
flash_tool/requirements.txt
Normal file
6
flash_tool/requirements.txt
Normal 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
|
||||
BIN
flash_tool/static/SoundShot.png
Normal file
BIN
flash_tool/static/SoundShot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 105 KiB |
570
flash_tool/templates/index.html
Normal file
570
flash_tool/templates/index.html
Normal 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
343
flash_tool/web_flasher.py
Normal 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()
|
||||
@@ -1,6 +1,4 @@
|
||||
<<<<<<< HEAD
|
||||
idf_component_register(SRCS "system.c.LOCAL.c" "bt_app.c" "system.c" "bubble.c" "keypad.c" "main.c"
|
||||
|
||||
idf_component_register(SRCS "battery.c" "bt_app.c" "system.c" "bubble.c" "keypad.c" "main.c"
|
||||
"gui.c"
|
||||
"lsm6dsv.c"
|
||||
INCLUDE_DIRS "."
|
||||
@@ -10,19 +8,6 @@ idf_component_register(SRCS "system.c.LOCAL.c" "bt_app.c" "system.c" "bubble.c"
|
||||
"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 "."
|
||||
REQUIRES "driver"
|
||||
"esp_lcd"
|
||||
"lvgl"
|
||||
"esp_lvgl_port"
|
||||
"esp_timer"
|
||||
"nvs_flash"
|
||||
"bt")
|
||||
>>>>>>> 4feb4c0a98bddb1f2a172ea4b195ce31ba18d442
|
||||
"bt"
|
||||
"esp_adc")
|
||||
|
||||
|
||||
271
main/battery.c
Normal file
271
main/battery.c
Normal file
@@ -0,0 +1,271 @@
|
||||
#include "battery.h"
|
||||
#include "system.h"
|
||||
#include "gpio.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_adc/adc_oneshot.h"
|
||||
#include "esp_adc/adc_cali.h"
|
||||
#include "esp_adc/adc_cali_scheme.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/gpio.h"
|
||||
|
||||
static const char *TAG = "battery";
|
||||
|
||||
static adc_oneshot_unit_handle_t adc1_handle = NULL;
|
||||
static adc_cali_handle_t adc1_cali_handle = NULL;
|
||||
static bool adc_calibration_enabled = false;
|
||||
|
||||
// ADC Calibration initialization
|
||||
static bool adc_calibration_init(adc_unit_t unit, adc_channel_t channel, adc_atten_t atten, adc_cali_handle_t *out_handle)
|
||||
{
|
||||
adc_cali_handle_t handle = NULL;
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
bool calibrated = false;
|
||||
|
||||
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
|
||||
if (!calibrated) {
|
||||
ESP_LOGI(TAG, "Calibration scheme: Curve Fitting");
|
||||
adc_cali_curve_fitting_config_t cali_config = {
|
||||
.unit_id = unit,
|
||||
.chan = channel,
|
||||
.atten = atten,
|
||||
.bitwidth = BATTERY_ADC_WIDTH,
|
||||
};
|
||||
ret = adc_cali_create_scheme_curve_fitting(&cali_config, &handle);
|
||||
if (ret == ESP_OK) {
|
||||
calibrated = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
|
||||
if (!calibrated) {
|
||||
ESP_LOGI(TAG, "Calibration scheme: Line Fitting");
|
||||
adc_cali_line_fitting_config_t cali_config = {
|
||||
.unit_id = unit,
|
||||
.atten = atten,
|
||||
.bitwidth = BATTERY_ADC_WIDTH,
|
||||
};
|
||||
ret = adc_cali_create_scheme_line_fitting(&cali_config, &handle);
|
||||
if (ret == ESP_OK) {
|
||||
calibrated = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
*out_handle = handle;
|
||||
if (ret == ESP_OK) {
|
||||
ESP_LOGI(TAG, "ADC calibration successful");
|
||||
} else {
|
||||
ESP_LOGW(TAG, "ADC calibration failed: %s", esp_err_to_name(ret));
|
||||
}
|
||||
return calibrated;
|
||||
}
|
||||
|
||||
esp_err_t battery_init(void)
|
||||
{
|
||||
esp_err_t ret;
|
||||
|
||||
// Configure ADC1 oneshot mode
|
||||
adc_oneshot_unit_init_cfg_t init_config = {
|
||||
.unit_id = ADC_UNIT_1,
|
||||
};
|
||||
ret = adc_oneshot_new_unit(&init_config, &adc1_handle);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to initialize ADC unit: %s", esp_err_to_name(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Configure ADC channel
|
||||
adc_oneshot_chan_cfg_t config = {
|
||||
.bitwidth = BATTERY_ADC_WIDTH,
|
||||
.atten = BATTERY_ADC_ATTEN,
|
||||
};
|
||||
ret = adc_oneshot_config_channel(adc1_handle, BATTERY_ADC_CHANNEL, &config);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to configure ADC channel: %s", esp_err_to_name(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Initialize calibration
|
||||
adc_calibration_enabled = adc_calibration_init(ADC_UNIT_1, BATTERY_ADC_CHANNEL,
|
||||
BATTERY_ADC_ATTEN, &adc1_cali_handle);
|
||||
|
||||
ESP_LOGI(TAG, "Battery monitoring initialized on GPIO34 (ADC1_CH6)");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
int battery_read_raw(void)
|
||||
{
|
||||
int adc_raw = 0;
|
||||
esp_err_t ret = adc_oneshot_read(adc1_handle, BATTERY_ADC_CHANNEL, &adc_raw);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to read ADC: %s", esp_err_to_name(ret));
|
||||
return -1;
|
||||
}
|
||||
return adc_raw;
|
||||
}
|
||||
|
||||
int battery_read_voltage_mv(void)
|
||||
{
|
||||
int voltage_mv = 0;
|
||||
int adc_sum = 0;
|
||||
int valid_samples = 0;
|
||||
|
||||
// Take multiple samples and average
|
||||
for (int i = 0; i < BATTERY_SAMPLES; i++) {
|
||||
int adc_raw = battery_read_raw();
|
||||
if (adc_raw >= 0) {
|
||||
adc_sum += adc_raw;
|
||||
valid_samples++;
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(1)); // Small delay between samples
|
||||
}
|
||||
|
||||
if (valid_samples == 0) {
|
||||
ESP_LOGE(TAG, "No valid ADC samples");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int adc_avg = adc_sum / valid_samples;
|
||||
|
||||
// Convert to voltage using calibration if available
|
||||
if (adc_calibration_enabled) {
|
||||
esp_err_t ret = adc_cali_raw_to_voltage(adc1_cali_handle, adc_avg, &voltage_mv);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGW(TAG, "Calibration conversion failed, using raw calculation");
|
||||
adc_calibration_enabled = false; // Disable for future reads
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to manual calculation if calibration not available
|
||||
if (!adc_calibration_enabled) {
|
||||
// Simple linear conversion for 12-bit ADC with 12dB attenuation
|
||||
// Approximate range: 0-3300mV for 0-4095 raw values
|
||||
voltage_mv = (adc_avg * 3300) / 4095;
|
||||
}
|
||||
|
||||
// Apply voltage divider ratio to get actual battery voltage
|
||||
voltage_mv = (int)(voltage_mv * BATTERY_VOLTAGE_DIVIDER_RATIO);
|
||||
|
||||
return voltage_mv;
|
||||
}
|
||||
|
||||
int battery_get_percentage(void)
|
||||
{
|
||||
int voltage_mv = battery_read_voltage_mv();
|
||||
|
||||
if (voltage_mv < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Clamp to min/max range
|
||||
if (voltage_mv >= BATTERY_VOLTAGE_MAX) {
|
||||
return 100;
|
||||
}
|
||||
if (voltage_mv <= BATTERY_VOLTAGE_MIN) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Linear interpolation between min and max
|
||||
int percentage = ((voltage_mv - BATTERY_VOLTAGE_MIN) * 100) /
|
||||
(BATTERY_VOLTAGE_MAX - BATTERY_VOLTAGE_MIN);
|
||||
|
||||
return percentage;
|
||||
}
|
||||
|
||||
// Battery monitoring task
|
||||
static void battery_monitoring_task(void *pvParameters)
|
||||
{
|
||||
ESP_LOGI(TAG, "Battery monitoring task started");
|
||||
bool led_toggle = false;
|
||||
int loop_count = 0;
|
||||
int voltage_mv = 0;
|
||||
bool is_charging = false;
|
||||
|
||||
while (1)
|
||||
{
|
||||
// Read voltage every 20 iterations (5000ms)
|
||||
if (loop_count % 20 == 0) {
|
||||
voltage_mv = battery_read_voltage_mv();
|
||||
int percentage = battery_get_percentage();
|
||||
|
||||
if (voltage_mv >= 0 && percentage >= 0) {
|
||||
// Update system state with battery info
|
||||
system_setBatteryVoltage(voltage_mv);
|
||||
system_setBatteryPercentage(percentage);
|
||||
|
||||
ESP_LOGI(TAG, "Battery: %d mV (%d%%)", voltage_mv, percentage);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Failed to read battery voltage");
|
||||
}
|
||||
|
||||
// Read charge status and update system
|
||||
is_charging = !gpio_get_level(PIN_NUM_CHARGE_STATUS);
|
||||
system_setChargeStatus(is_charging);
|
||||
}
|
||||
|
||||
// LED control based on charging status and voltage (runs every 250ms)
|
||||
if (is_charging)
|
||||
{
|
||||
// Charging: Red until voltage > 4150mV, then Green
|
||||
if (voltage_mv > 4150) {
|
||||
gpio_set_level(PIN_LED_GREEN, 1); // Green ON
|
||||
gpio_set_level(PIN_LED_RED, 0); // Red OFF
|
||||
} else {
|
||||
gpio_set_level(PIN_LED_GREEN, 0); // Green OFF
|
||||
gpio_set_level(PIN_LED_RED, 1); // Red ON
|
||||
}
|
||||
gpio_set_level(PIN_NUM_LED_2, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not charging: voltage-based indication
|
||||
if (voltage_mv > 3900)
|
||||
{
|
||||
// Good voltage: Green steady
|
||||
gpio_set_level(PIN_LED_GREEN, 1); // Green ON
|
||||
gpio_set_level(PIN_LED_RED, 0); // Red OFF
|
||||
}
|
||||
else
|
||||
if (voltage_mv > 3700)
|
||||
{
|
||||
// Warning voltage: Green flashing at 2Hz
|
||||
gpio_set_level(PIN_LED_GREEN, led_toggle ? 0 : 1);
|
||||
gpio_set_level(PIN_LED_RED, 0); // Red OFF
|
||||
led_toggle = !led_toggle;
|
||||
}
|
||||
else
|
||||
if (voltage_mv > 3600)
|
||||
{
|
||||
// Low voltage: Red steady
|
||||
gpio_set_level(PIN_LED_GREEN, 0); // Green OFF
|
||||
gpio_set_level(PIN_LED_RED, 1); // Red ON
|
||||
}
|
||||
else
|
||||
if (voltage_mv > 3500)
|
||||
{
|
||||
// Warning voltage: Red flashing at 2Hz
|
||||
gpio_set_level(PIN_LED_RED, led_toggle ? 0 : 1);
|
||||
gpio_set_level(PIN_LED_GREEN, 0); // Green OFF
|
||||
led_toggle = !led_toggle;
|
||||
}
|
||||
else
|
||||
{
|
||||
// power off
|
||||
gpio_set_level(PIN_NUM_nON, 0);
|
||||
}
|
||||
|
||||
gpio_set_level(PIN_NUM_LED_2, 1);
|
||||
}
|
||||
|
||||
loop_count++;
|
||||
vTaskDelay(pdMS_TO_TICKS(250));
|
||||
}
|
||||
}
|
||||
|
||||
void battery_start_monitoring_task(void)
|
||||
{
|
||||
xTaskCreate(battery_monitoring_task, "battery_task", 3072, NULL, 5, NULL);
|
||||
ESP_LOGI(TAG, "Battery monitoring task created");
|
||||
}
|
||||
56
main/battery.h
Normal file
56
main/battery.h
Normal file
@@ -0,0 +1,56 @@
|
||||
#ifndef BATTERY_H
|
||||
#define BATTERY_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "esp_err.h"
|
||||
|
||||
// Battery monitoring configuration
|
||||
#define BATTERY_ADC_CHANNEL ADC_CHANNEL_6 // GPIO34 (ADC1_CH6)
|
||||
#define BATTERY_ADC_ATTEN ADC_ATTEN_DB_12 // Full range ~3.9V (0-3300mV with attenuation)
|
||||
#define BATTERY_ADC_WIDTH ADC_BITWIDTH_12 // 12-bit resolution (0-4095)
|
||||
|
||||
// Battery voltage calculation constants
|
||||
// Adjust these based on your voltage divider circuit
|
||||
#define BATTERY_VOLTAGE_DIVIDER_RATIO 2.0f // Example: R1=R2, adjust for your circuit
|
||||
#define BATTERY_SAMPLES 16 // Number of samples to average
|
||||
|
||||
// Battery percentage thresholds (in millivolts at battery)
|
||||
#define BATTERY_VOLTAGE_MAX 4200 // Fully charged Li-Ion
|
||||
#define BATTERY_VOLTAGE_MIN 3000 // Empty Li-Ion (cutoff)
|
||||
|
||||
/**
|
||||
* @brief Initialize battery monitoring ADC
|
||||
*
|
||||
* @return esp_err_t ESP_OK on success
|
||||
*/
|
||||
esp_err_t battery_init(void);
|
||||
|
||||
/**
|
||||
* @brief Read raw ADC value from battery pin
|
||||
*
|
||||
* @return int Raw ADC value (0-4095)
|
||||
*/
|
||||
int battery_read_raw(void);
|
||||
|
||||
/**
|
||||
* @brief Read battery voltage in millivolts (averaged)
|
||||
*
|
||||
* @return int Battery voltage in mV
|
||||
*/
|
||||
int battery_read_voltage_mv(void);
|
||||
|
||||
/**
|
||||
* @brief Get battery percentage (0-100)
|
||||
*
|
||||
* @return int Battery percentage
|
||||
*/
|
||||
int battery_get_percentage(void);
|
||||
|
||||
/**
|
||||
* @brief Start battery monitoring task
|
||||
*
|
||||
* This task periodically reads battery voltage and updates system state
|
||||
*/
|
||||
void battery_start_monitoring_task(void);
|
||||
|
||||
#endif // BATTERY_H
|
||||
1966
main/bt_app.c
1966
main/bt_app.c
File diff suppressed because it is too large
Load Diff
@@ -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__ */
|
||||
|
||||
41
main/gpio.h
41
main/gpio.h
@@ -1,13 +1,18 @@
|
||||
<<<<<<< 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 PIN_LED_RED PIN_NUM_LED_0
|
||||
#define PIN_LED_GREEN PIN_NUM_LED_1
|
||||
|
||||
|
||||
|
||||
// Define GPIO pins
|
||||
@@ -27,34 +32,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
|
||||
1979
main/gui.c
1979
main/gui.c
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
@@ -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.
|
||||
|
||||
402
main/main.c
402
main/main.c
@@ -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"
|
||||
@@ -28,18 +28,54 @@
|
||||
#include "gpio.h"
|
||||
#include "keypad.h"
|
||||
#include "system.h"
|
||||
#include "battery.h"
|
||||
|
||||
|
||||
|
||||
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 +87,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 +100,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 +285,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,30 +294,41 @@ 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");
|
||||
|
||||
// Initialize battery monitoring
|
||||
ESP_ERROR_CHECK(battery_init());
|
||||
battery_start_monitoring_task();
|
||||
print_heap_info("POST_BATTERY");
|
||||
|
||||
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);
|
||||
|
||||
|
||||
battery_start_monitoring_task();
|
||||
#if 0
|
||||
|
||||
keypad_start();
|
||||
@@ -286,300 +364,10 @@ void app_main(void)
|
||||
#else
|
||||
while (1)
|
||||
{
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
|
||||
|
||||
system_processNvsRequests();
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
=======
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
else
|
||||
if (angle < -MAX_INDICATION_ANGLE)
|
||||
{
|
||||
angle = -MAX_INDICATION_ANGLE;
|
||||
}
|
||||
|
||||
// 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));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
>>>>>>> 4feb4c0a98bddb1f2a172ea4b195ce31ba18d442
|
||||
|
||||
667
main/system.c
667
main/system.c
@@ -1,21 +1,49 @@
|
||||
<<<<<<< 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 const char* NVS_NAMESPACE_SETTINGS = "settings";
|
||||
static const char* NVS_KEY_VOLUME = "volume";
|
||||
static const char* NVS_KEY_SWAP_LR = "swap_lr";
|
||||
|
||||
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;
|
||||
_systemState.swapLR = false;
|
||||
_systemState.volume = 50; // Default volume
|
||||
_systemState.batteryVoltage_mv = 0;
|
||||
_systemState.batteryPercentage = 0;
|
||||
|
||||
EventGroupHandle_t evt = xEventGroupCreate();
|
||||
_systemEvent = xEventGroupCreate();
|
||||
|
||||
_eventManager.count = 0;
|
||||
_eventManager.mutex = xSemaphoreCreateMutex();
|
||||
|
||||
system_initNvsService();
|
||||
|
||||
// Load saved volume from NVS
|
||||
system_loadVolume();
|
||||
|
||||
// Load saved swap L/R setting from NVS
|
||||
system_loadSwapLR();
|
||||
}
|
||||
|
||||
int system_getPrimaryAxis(void)
|
||||
@@ -44,6 +72,82 @@ 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_setBatteryVoltage(int voltage_mv)
|
||||
{
|
||||
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
|
||||
_systemState.batteryVoltage_mv = voltage_mv;
|
||||
xSemaphoreGive(_eventManager.mutex);
|
||||
}
|
||||
|
||||
int system_getBatteryVoltage(void)
|
||||
{
|
||||
int voltage;
|
||||
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
|
||||
voltage = _systemState.batteryVoltage_mv;
|
||||
xSemaphoreGive(_eventManager.mutex);
|
||||
return voltage;
|
||||
}
|
||||
|
||||
void system_setBatteryPercentage(int percentage)
|
||||
{
|
||||
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
|
||||
_systemState.batteryPercentage = percentage;
|
||||
xSemaphoreGive(_eventManager.mutex);
|
||||
}
|
||||
|
||||
int system_getBatteryPercentage(void)
|
||||
{
|
||||
int percentage;
|
||||
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
|
||||
percentage = _systemState.batteryPercentage;
|
||||
xSemaphoreGive(_eventManager.mutex);
|
||||
return percentage;
|
||||
}
|
||||
|
||||
void system_setSwapLR(bool swap)
|
||||
{
|
||||
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
|
||||
_systemState.swapLR = swap;
|
||||
xSemaphoreGive(_eventManager.mutex);
|
||||
ESP_LOGI("system", "Swap L/R: %s", swap ? "ON" : "OFF");
|
||||
}
|
||||
|
||||
bool system_getSwapLR(void)
|
||||
{
|
||||
bool swap;
|
||||
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
|
||||
swap = _systemState.swapLR;
|
||||
xSemaphoreGive(_eventManager.mutex);
|
||||
return swap;
|
||||
}
|
||||
|
||||
void system_toggleSwapLR(void)
|
||||
{
|
||||
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
|
||||
_systemState.swapLR = !_systemState.swapLR;
|
||||
ESP_LOGI("system", "Swap L/R toggled: %s", _systemState.swapLR ? "ON" : "OFF");
|
||||
xSemaphoreGive(_eventManager.mutex);
|
||||
|
||||
// Save to NVS
|
||||
system_saveSwapLR();
|
||||
}
|
||||
|
||||
void system_setZeroAngle(void)
|
||||
{
|
||||
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
|
||||
@@ -167,174 +271,459 @@ 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);
|
||||
index = _systemState.btDeviceIndex;
|
||||
xSemaphoreGive(_eventManager.mutex);
|
||||
return index;
|
||||
}
|
||||
|
||||
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_setVolume(int volume) {
|
||||
if (volume < 0) volume = 0;
|
||||
if (volume > 100) volume = 100;
|
||||
|
||||
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
|
||||
|
||||
|
||||
angle = _systemState.imu.raw[_systemState.primaryAxis] - _systemState.zeroAngle;
|
||||
|
||||
|
||||
_systemState.volume = volume;
|
||||
xSemaphoreGive(_eventManager.mutex);
|
||||
|
||||
return angle;
|
||||
ESP_LOGI("system", "Volume set to %d", volume);
|
||||
}
|
||||
|
||||
void system_setZeroAngle(void)
|
||||
{
|
||||
int system_getVolume(void) {
|
||||
int volume;
|
||||
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
|
||||
|
||||
|
||||
_systemState.zeroAngle = _systemState.imu.raw[_systemState.primaryAxis];
|
||||
|
||||
ESP_LOGI("system", "Zero: %.1f", _systemState.zeroAngle);
|
||||
|
||||
volume = _systemState.volume;
|
||||
xSemaphoreGive(_eventManager.mutex);
|
||||
return volume;
|
||||
}
|
||||
|
||||
void system_clearZeroAngle(void)
|
||||
{
|
||||
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
|
||||
esp_err_t system_saveVolume(void) {
|
||||
nvs_handle_t nvs_handle;
|
||||
esp_err_t ret;
|
||||
|
||||
_systemState.zeroAngle = 0.0f;
|
||||
int volume = system_getVolume();
|
||||
|
||||
ESP_LOGI("system", "Zero: %.1f", _systemState.zeroAngle);
|
||||
|
||||
xSemaphoreGive(_eventManager.mutex);
|
||||
}
|
||||
|
||||
float system_getZeroAngle(void)
|
||||
{
|
||||
float angle;
|
||||
|
||||
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
|
||||
|
||||
angle = _systemState.zeroAngle;
|
||||
|
||||
xSemaphoreGive(_eventManager.mutex);
|
||||
|
||||
return angle;
|
||||
}
|
||||
|
||||
void system_setImuData(ImuData_t imu)
|
||||
{
|
||||
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
|
||||
|
||||
_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;
|
||||
ret = nvs_open(NVS_NAMESPACE_SETTINGS, NVS_READWRITE, &nvs_handle);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE("system", "Failed to open NVS namespace for volume write: %s", esp_err_to_name(ret));
|
||||
return ret;
|
||||
}
|
||||
if (em->count < EM_MAX_SUBSCRIBERS) {
|
||||
// avoid duplicates?
|
||||
ESP_LOGI("g", "PASS");
|
||||
em->subscribers[em->count++] = task;
|
||||
xSemaphoreGive(em->mutex);
|
||||
return pdPASS;
|
||||
|
||||
ret = nvs_set_i32(nvs_handle, NVS_KEY_VOLUME, volume);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE("system", "Failed to write volume to NVS: %s", esp_err_to_name(ret));
|
||||
nvs_close(nvs_handle);
|
||||
return ret;
|
||||
}
|
||||
xSemaphoreGive(em->mutex);
|
||||
return pdFAIL; // full
|
||||
|
||||
ret = nvs_commit(nvs_handle);
|
||||
nvs_close(nvs_handle);
|
||||
|
||||
if (ret == ESP_OK) {
|
||||
ESP_LOGI("system", "Volume %d saved to NVS", volume);
|
||||
} else {
|
||||
ESP_LOGE("system", "Failed to commit volume to NVS: %s", esp_err_to_name(ret));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
BaseType_t system_unsubscribe(TaskHandle_t task) {
|
||||
esp_err_t system_loadVolume(void) {
|
||||
nvs_handle_t nvs_handle;
|
||||
esp_err_t ret;
|
||||
int32_t volume = 50; // Default value
|
||||
|
||||
EventManager_t *em = &_eventManager;
|
||||
ret = nvs_open(NVS_NAMESPACE_SETTINGS, NVS_READONLY, &nvs_handle);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGI("system", "No saved volume found, using default: %ld", volume);
|
||||
system_setVolume(volume);
|
||||
return ESP_OK; // Not an error, just means no saved value
|
||||
}
|
||||
|
||||
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;
|
||||
ret = nvs_get_i32(nvs_handle, NVS_KEY_VOLUME, &volume);
|
||||
nvs_close(nvs_handle);
|
||||
|
||||
if (ret == ESP_OK) {
|
||||
ESP_LOGI("system", "Loaded volume from NVS: %ld", volume);
|
||||
system_setVolume(volume);
|
||||
} else if (ret == ESP_ERR_NVS_NOT_FOUND) {
|
||||
ESP_LOGI("system", "No saved volume found, using default: %ld", volume);
|
||||
system_setVolume(volume);
|
||||
ret = ESP_OK; // Not an error
|
||||
} else {
|
||||
ESP_LOGE("system", "Failed to read volume from NVS: %s", esp_err_to_name(ret));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t system_saveSwapLR(void) {
|
||||
nvs_handle_t nvs_handle;
|
||||
esp_err_t ret;
|
||||
|
||||
bool swapLR = system_getSwapLR();
|
||||
|
||||
ret = nvs_open(NVS_NAMESPACE_SETTINGS, NVS_READWRITE, &nvs_handle);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE("system", "Failed to open NVS namespace for swap L/R write: %s", esp_err_to_name(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = nvs_set_u8(nvs_handle, NVS_KEY_SWAP_LR, swapLR ? 1 : 0);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE("system", "Failed to write swap L/R to NVS: %s", esp_err_to_name(ret));
|
||||
nvs_close(nvs_handle);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = nvs_commit(nvs_handle);
|
||||
nvs_close(nvs_handle);
|
||||
|
||||
if (ret == ESP_OK) {
|
||||
ESP_LOGI("system", "Swap L/R %s saved to NVS", swapLR ? "ON" : "OFF");
|
||||
} else {
|
||||
ESP_LOGE("system", "Failed to commit swap L/R to NVS: %s", esp_err_to_name(ret));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t system_loadSwapLR(void) {
|
||||
nvs_handle_t nvs_handle;
|
||||
esp_err_t ret;
|
||||
uint8_t swapLR_u8 = 0; // Default value (OFF)
|
||||
|
||||
ret = nvs_open(NVS_NAMESPACE_SETTINGS, NVS_READONLY, &nvs_handle);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGI("system", "No saved swap L/R found, using default: OFF");
|
||||
system_setSwapLR(false);
|
||||
return ESP_OK; // Not an error, just means no saved value
|
||||
}
|
||||
|
||||
ret = nvs_get_u8(nvs_handle, NVS_KEY_SWAP_LR, &swapLR_u8);
|
||||
nvs_close(nvs_handle);
|
||||
|
||||
if (ret == ESP_OK) {
|
||||
bool swapLR = (swapLR_u8 != 0);
|
||||
ESP_LOGI("system", "Loaded swap L/R from NVS: %s", swapLR ? "ON" : "OFF");
|
||||
system_setSwapLR(swapLR);
|
||||
} else if (ret == ESP_ERR_NVS_NOT_FOUND) {
|
||||
ESP_LOGI("system", "No saved swap L/R found, using default: OFF");
|
||||
system_setSwapLR(false);
|
||||
ret = ESP_OK; // Not an error
|
||||
} else {
|
||||
ESP_LOGE("system", "Failed to read swap L/R from NVS: %s", esp_err_to_name(ret));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t nvs_load_devices_internal(paired_device_t *devices, size_t *count) {
|
||||
nvs_handle_t nvs_handle;
|
||||
esp_err_t ret;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
device_count = (device_count > MAX_PAIRED_DEVICES) ? MAX_PAIRED_DEVICES : device_count;
|
||||
|
||||
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, ¬ification, 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, ¬ification, 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, ¬ification, 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;
|
||||
}
|
||||
167
main/system.h
167
main/system.h
@@ -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,42 @@ 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;
|
||||
|
||||
// Swap L/R audio channels
|
||||
bool swapLR;
|
||||
|
||||
// Volume setting (0-100)
|
||||
int volume;
|
||||
|
||||
// Battery monitoring
|
||||
int batteryVoltage_mv;
|
||||
int batteryPercentage;
|
||||
|
||||
} 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 +97,20 @@ ImuData_t system_getImuData(void);
|
||||
int system_getPrimaryAxis(void);
|
||||
float system_getAngle(void);
|
||||
|
||||
void system_setChargeStatus(bool charging);
|
||||
bool system_getChargeStatus(void);
|
||||
|
||||
void system_setBatteryVoltage(int voltage_mv);
|
||||
int system_getBatteryVoltage(void);
|
||||
void system_setBatteryPercentage(int percentage);
|
||||
int system_getBatteryPercentage(void);
|
||||
|
||||
void system_setSwapLR(bool swap);
|
||||
bool system_getSwapLR(void);
|
||||
void system_toggleSwapLR(void);
|
||||
esp_err_t system_saveSwapLR(void);
|
||||
esp_err_t system_loadSwapLR(void);
|
||||
|
||||
void system_setZeroAngle(void);
|
||||
void system_clearZeroAngle(void);
|
||||
float system_getZeroAngle(void);
|
||||
@@ -72,81 +124,50 @@ 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);
|
||||
void system_setVolume(int volume);
|
||||
int system_getVolume(void);
|
||||
esp_err_t system_saveVolume(void);
|
||||
esp_err_t system_loadVolume(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
|
||||
@@ -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
4
project_settings.cmake
Normal file
@@ -0,0 +1,4 @@
|
||||
set(PROJECT_NAME "soundshot")
|
||||
set(CHIP "esp32")
|
||||
set(PORT "COM14")
|
||||
set(BAUD "460800")
|
||||
210
sdkconfig
210
sdkconfig
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
3109
sdkconfig.old
3109
sdkconfig.old
File diff suppressed because it is too large
Load Diff
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user