diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 3f3955f9..b569e336 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -61,6 +61,8 @@ jobs:
platformio pkg install -e S3release
- name: Check pre-commit hooks
uses: pre-commit/action@v3.0.1
+ - name: Refresh GitHub TLS certificate
+ run: python cert_updater.py
- name: Build firmware
run: platformio run -e release
- name: Build filesystem
diff --git a/.github/workflows/native-tests.yml b/.github/workflows/native-tests.yml
new file mode 100644
index 00000000..f4a7c9cf
--- /dev/null
+++ b/.github/workflows/native-tests.yml
@@ -0,0 +1,32 @@
+name: native-tests
+
+on:
+ pull_request:
+ paths-ignore:
+ - "Hardware/**"
+ push:
+ branches:
+ - develop
+ paths-ignore:
+ - "Hardware/**"
+
+jobs:
+ native-tests:
+ strategy:
+ fail-fast: false
+ matrix:
+ os:
+ - ubuntu-latest
+ - macos-latest
+ - windows-latest
+ runs-on: ${{ matrix.os }}
+
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v5
+ with:
+ python-version: "3.11"
+ - name: Install PlatformIO
+ run: python -m pip install platformio
+ - name: Run native tests
+ run: platformio test -e native
diff --git a/AGENTS.md b/AGENTS.md
index 4e57f8b2..20cda5ff 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -37,23 +37,29 @@ PlatformIO is the expected entry point.
- Static analysis: `pio check -e debug`
- Pre-commit checks: `pre-commit run --all-files`
+`pio` is installed at `/Users/anthonydoud/.platformio/penv/bin/pio` when it is not on `PATH`; use that absolute command rather than treating a missing shell alias as an unavailable test environment. Native tests are expected to run locally.
+
S3 firmware and filesystem builds use `S3firmware.bin` and `S3littlefs.bin` as their native PlatformIO output/upload names. They also create `S3partitions.bin` and `S3bootloader.bin` copies for releases; the generic partition and bootloader intermediates remain because PlatformIO's flash uploader depends on those names.
The GitHub release archive includes firmware, merged factory, LittleFS, partition-table, and bootloader binaries for both classic ESP32 and ESP32-S3 targets.
GitHub Actions exports `SS2K_FIRMWARE_VERSION` from the date-based release tag before invoking PlatformIO. `git_tag_macro.py` requires that override in Actions so published firmware never receives a `git describe` commit suffix; local builds retain branch/commit version details.
+The release workflow runs `cert_updater.py` once before firmware builds. Local PlatformIO builds use the checked-in `include/cert.h` and do not perform network-dependent certificate updates.
Filesystem builds stage deterministic gzip copies of every HTML/CSS source file under the environment build directory. They also refresh the checked-in `.gz` companions and `list.json` in `data/` or `data_s3/`, which are consumed by repository-based automatic OTA updates.
-Codex environment constraint:
-
-- Do not run PlatformIO firmware or filesystem builds from the Codex environment. The Windows Xtensa toolchain can hang and leave orphaned compiler processes here. Make the requested changes, run non-build checks where useful, and clearly leave PlatformIO build validation for the user to run manually.
-
Important timing/network notes:
-- First PlatformIO builds/tests may download ESP32 platforms and toolchains. They can take 15-45 minutes for firmware builds and 5-15 minutes for native tests.
+- PlatformIO firmware/filesystem builds and USB flashing are supported from the Codex environment. Local builds normally finish in under five minutes.
+- A directly attached ESP32-S3 exposes its USB CDC serial port as `/dev/cu.usbmodem*` (currently `/dev/cu.usbmodem21101`). A debug build (`S3debug`, with `__DEBUG__` and `SERIAL_CUSTOM_CHARACTERISTIC`) can be observed directly with `/Users/anthonydoud/.platformio/penv/bin/pio device monitor -p /dev/cu.usbmodem21101`; confirm the present port with `ls /dev/cu.usb*` first. Serial monitoring is read-only, while uploading a debug build is an explicit device mutation and should only be done when the task authorizes it.
+- When testing attached hardware, do not switch the development machine's WiFi connection to the SmartSpin2k access point: that network has no internet access, while builds and tooling may require internet service. Communicate with the device over USB unless the user explicitly directs otherwise. Preserve the device's stored WiFi/LittleFS/NVS settings by preferring application-partition-only flashing (S3 app offset `0x60000`).
+- First PlatformIO builds/tests may download missing ESP32 platforms and toolchains and therefore take longer than normal.
- In restricted environments, PlatformIO can fail on network downloads. If that happens, report it rather than trying to fake validation.
- The firmware itself cannot be fully run without ESP32 hardware, BLE devices, and a stepper driver.
-Native tests cover sensor parsing, BLE device-name stability logic, power-buffer behavior, and power-table lookup/fill/save flows. When changing:
+Native tests cover sensor parsing, BLE device-name stability logic, firmware-update protocol handling, and power-table/ERG replay flows. When changing:
+
+- Native Arduino types/timing come from the repository-owned `lib/ArduinoCompat`; the suite does not use ArduinoFake. Keep this shim and test filesystem setup portable across Apple Clang/POSIX and Windows native toolchains.
+- Multi-byte protocol fields use `lib/SS2K/include/ByteUtils.h`, which wraps the platform `os/endian.h` implementation and adds explicit signed helpers such as `get_le16s()`/`put_le32s()`. ArduinoCompat supplies `os/endian.h` for native tests; do not add another endian implementation.
+- `test/data/active_ride_log.txt` is the single real-world fixture for power-table and ERG tests. Each test replays it independently through `test/test_data_helpers.h`; generated tables and audit reports belong under ignored `test/output/`.
- `src/Power_Table.cpp` or `src/PowerTable_Helpers.cpp`, run `pio test -e native`.
- `lib/SS2K/src/sensors/*`, run the native tests for sensor parsing.
@@ -91,7 +97,7 @@ Boot sequence:
3. Start stepper serial and optional aux serial for Peloton.
4. Mount LittleFS.
5. Load and re-save `userConfig`.
-6. Start WiFi and run firmware update check.
+6. Complete WiFi station connection or AP fallback, synchronize the clock when online, and repair missing web files before starting BLE.
7. Configure GPIO pins.
8. Initialize LED state; commanded-reboot quiet mode uses RTC memory so true power cycles still show startup blink behavior.
9. Configure TMC/FastAccelStepper via `SS2K::setupTMCStepperDriver()`.
@@ -239,9 +245,10 @@ Key functions:
- `SpinBLEClient::start()`: creates BLE client task and configures scanning.
- `ScanCallbacks::onResult()`: filters supported devices, updates `foundDevices`, sets slots to connect when user config matches.
+- Scan results for current config apps are streamed one device at a time on custom-characteristic ID `0x32`, with begin/device/end records and per-peer MTU fragmentation. The legacy `foundDevices` JSON remains capped at 480 bytes for older apps; do not make it unbounded again.
- `SpinBLEClient::connectToServer()`: creates fresh BLE client, connects, sets slot state, removes duplicates.
- `subscribeToAllNotifications()`: subscribes to notify/indicate characteristics for supported services.
-- `SpinBLEClient::postConnect()`: completes service subscriptions, reads FTMS resistance range, starts FTMS training where needed, drains notification queues.
+- `SpinBLEClient::postConnect()`: completes service subscriptions, reads FTMS resistance range, starts FTMS training where needed, drains notification queues. Notification setup discovers only the characteristics the firmware consumes so large remote GATT tables do not exhaust the classic ESP32 heap. HID is the exception because remotes can expose multiple Report characteristics with the same UUID.
- `SpinBLEClient::checkBLEReconnect()`: sets `doScan` when configured devices are missing.
- `SpinBLEClient::adevName2UniqueName()`: stable names for saved device preferences. Public/static random addresses get address suffix; private random addresses prefer manufacturer-data suffix or base name.
@@ -288,6 +295,7 @@ Primary files: `src/BLE_Server.cpp`, `src/BLE_Fitness_Machine_Service.cpp`, serv
The primary BLE advertisement carries the current WiFi IPv4 address in versioned SmartSpin2k manufacturer data.
The device name and 128-bit SmartSpin2k service UUID are kept in the scan response to stay within the legacy advertisement size limit.
+IP changes rebuild the complete advertisement and scan-response payloads before advertising restarts; NimBLE's manufacturer-data setter appends fields and must not be used alone to replace the previous IP.
Zwift/OpenBikeControl services exist but are currently commented out in regular BLE advertising/setup; DirCon and the source files still matter.
@@ -385,6 +393,7 @@ Stepper safety:
- Homed devices clamp to `rtConfig->minStep/maxStep`.
- Unhomed devices use provisional defaults unless power-table/resistance updates refine limits.
+- FastAccelStepper pulse generation is initialized independently of TMC UART detection, so the firmware remains safe when the physical driver is absent. Runtime stepper-setting methods must still tolerate null driver/stepper pointers in case peripheral allocation fails.
- Do not bypass `moveStepper()` target clamping for ordinary control paths.
## ERG Mode
@@ -421,6 +430,8 @@ Primary files: `include/ERG_Mode.h`, `src/ERG_Mode.cpp`.
`_inSetpointState()`:
- Uses proportional-only control with `ERGSensitivity`.
+- Keeps the original strict `lookupSlope()` for table validation. ERG uses `lookupErgSlope()`, which may use a near-edge measured segment only when the cadence-bounding rows agree and each segment has endpoint headroom; it never extrapolates beyond measured data.
+- Blends a trusted ERG table gain 50/50 with the watt-scheduled fallback gain and bounds raw table gain to 0.5-1.25x fallback before blending. This deliberately favors stable convergence over aggressive corrections. Fallback log lines include the rejected-slope reason.
- Scales gain by watt error size.
- Caps movement by stepper speed and `ERG_MODE_DELAY`.
@@ -440,8 +451,7 @@ Data structures:
- `PowerBuffer`: fixed `POWER_SAMPLES` sample buffer used before committing a table entry.
- `TableEntry`: compact stored table cell: `int16_t targetPosition`, `int8_t readings`.
- `PTData`: `POWERTABLE_CAD_SIZE` x `POWERTABLE_WATT_SIZE` table.
-- `ResistanceModel`: regression model predicting position from watts/cadence and inverse watts from position/cadence.
-- `PTHelpers`: indexing, lookup, cleaning, interpolation, monotonic enforcement, and model fitting.
+- `PTHelpers`: indexing, measured-point interpolation/extrapolation, cleaning, and monotonic enforcement.
Table dimensions/constants are in `include/settings.h`:
@@ -456,9 +466,9 @@ Flow:
1. `PowerTable::processPowerValue()` accepts sane cadence/watts and stable position samples.
2. A full `PowerBuffer` is averaged in `PowerTable::newEntry()`.
3. `PTHelpers::calculateIndex()` maps watts/cadence to table indexes.
-4. `PTHelpers::enterData()` averages the cell, rejects monotonic violations, fills gaps, runs PAVA-style monotonic correction, fits `ResistanceModel`, and fills inferred cells.
-5. `lookup()` uses the model to predict a target position and multiplies by `TABLE_DIVISOR`.
-6. `lookupWatts()` inverses the model for `pTab4Pwr`.
+4. `PTHelpers::enterData()` averages the cell, rejects monotonic violations, and runs PAVA-style monotonic correction across measured entries.
+5. `lookup()` locally interpolates or extrapolates measured watt/position pairs, using equal-torque cadence scaling, and returns a full-scale position.
+6. `lookupWatts()` numerically inverts the cadence-blended forward `lookup()` surface, preserves exact measured anchors/plateau midpoints, and applies a monotonic cadence envelope for `pTab4Pwr`. Estimated power is bounded to the FTMS 4000 W maximum.
Persistence:
@@ -470,8 +480,7 @@ Persistence:
Caution:
-- `ResistanceModel::fit()` uses only entries with `readings >= 2`.
-- Inferred cells can improve lookup smoothness but should not be treated as real measurements.
+- Runtime lookup uses only entries with `readings >= 2`; legacy inferred cells with `readings == 1` are ignored.
- Power-table and homing logic are tightly linked now. Loading/saving without homing is intentionally blocked.
## DirCon
@@ -489,6 +498,8 @@ DirCon exposes BLE-like services over TCP:
- BLE server updates call `DirConManager::notifyCharacteristic()` so TCP clients receive corresponding updates.
DirCon uses static buffers and fixed client/subscription arrays. Be cautious with dynamic allocation and payload sizes.
+Outbound DirCon frames use bounded per-client queues and are drained with non-blocking socket sends only from `DirConManager::update()`. Keep responses and notifications on that path so slow or vanished TCP peers cannot block firmware tasks or interleave frames.
+DirCon notification broadcasts check for an active subscribed recipient before encoding, avoiding unnecessary dynamic allocations during BLE scans.
## HTTP/Web UI
@@ -497,9 +508,12 @@ Primary files: `src/HTTP_Server_Basic.cpp`, `include/HTTP_Server_Basic.h`, `data
Responsibilities:
- Start/stop WiFi (`startWifi()`, `stopWifi()`).
+- WiFi startup is deliberately linear: wait for the configured station up to the connection timeout, fall back to AP mode if needed, and only then continue firmware initialization.
- Serve LittleFS web assets and built-in OTA pages.
-- Firmware update flow through `HTTP_Server::FirmwareUpdate()`.
-- Automatic filesystem updates treat remote `list.json` as an allowlist, preserve config/power-table/recovery metadata, and store the installed filesystem release version in NVS.
+- Before BLE starts, boot checks the local `list.json`. If every listed asset exists, no TLS connection is created. `HTTP_Server::syncWebServerFiles()` is repair-only and runs synchronously when the local manifest is missing/invalid or a listed asset is absent and station internet is available.
+- Browser-uploaded firmware uses the low-level ESP-IDF OTA API, and filesystem images stream directly to the LittleFS partition with sector-at-a-time erases. Neither path uses Arduino `Update` or its 4 KiB heap allocation on memory-constrained classic ESP32 builds. Filesystem uploads must exactly match the partition size; arbitrary file uploads are rejected.
+- Web filesystem repair fetches the remote `list.json`, downloads only missing assets, and installs the fetched manifest only after repair succeeds. It never performs boot-time version upgrades or prunes existing files.
+- Downloads use bounded HTTP/TLS timeouts and temporary files so partial assets are never served. Repair completes before BLE allocation/scanning, avoiding their peak internal-RAM loads overlapping on classic ESP32.
- Settings JSON/API behavior through `settingsProcessor()`.
- Periodic web client update through `webClientUpdate()`.
- BLE scanner page support.
diff --git a/CustomCharacteristic.md b/CustomCharacteristic.md
index 20ee7342..99f6eab8 100644
--- a/CustomCharacteristic.md
+++ b/CustomCharacteristic.md
@@ -79,7 +79,7 @@ From BLE_common.h
|BLE_simulateWatts |0x0E |bool | |
|BLE_simulateCad |0x0F |bool | |
|BLE_FTMSMode |0x10 |bool | |
-|BLE_autoUpdate |0x11 |bool |updates on (01) or off (00) |
+|BLE_autoUpdate |0x11 |bool |Deprecated compatibility field; always off |
|BLE_ssid |0x12 | |Not Implemented |
|BLE_password |0x13 | |Not Implemented |
|BLE_foundDevices |0x14 | |Not Implemented |
diff --git a/cert_updater.py b/cert_updater.py
index aab1be7a..7da3c859 100644
--- a/cert_updater.py
+++ b/cert_updater.py
@@ -1,14 +1,10 @@
#!/usr/bin/env python3
"""
This script fetches the current CA certificate for raw.githubusercontent.com
-and updates the cert.h file with it during compilation.
+and updates cert.h before release firmware is compiled in GitHub Actions.
-Similar to git_tag_macro.py, this script is run as part of the build process
-to ensure the certificate is always up to date.
-
-This script can be run in two ways:
-1. Directly: python cert_updater.py
-2. As a PlatformIO pre-script: extra_scripts = pre:cert_updater.py
+Local PlatformIO builds intentionally do not run this script. It can also be
+run manually with: python cert_updater.py
"""
import os
@@ -383,10 +379,7 @@ def log_to_stderr(message):
def main():
"""
- Main function when run directly or by PlatformIO build flags
-
- When called by PlatformIO in the build_flags section (like !python cert_updater.py),
- it needs to print a valid build flag to stdout without any other output.
+ Update the checked-in certificate for a subsequent firmware build.
"""
# Save the original print function
original_print = print
@@ -425,9 +418,6 @@ def safe_print(*args, **kwargs):
# Reset print back to original
setattr(builtins, 'print', original_print)
- # Print only the build flag to stdout - this will be picked up by PlatformIO
- print("-DCERT_UPDATER_VERSION=1")
-
return 0
except Exception as e:
log_to_stderr(f"ERROR in cert_updater.py: {e}")
@@ -435,9 +425,7 @@ def safe_print(*args, **kwargs):
# Reset print back to original
setattr(builtins, 'print', original_print)
- # Print only the build flag to stdout
- print("-DCERT_UPDATER_ERROR=1")
return 1
if __name__ == "__main__":
- main()
\ No newline at end of file
+ sys.exit(main())
diff --git a/data/settings.html b/data/settings.html
index fea8c153..482b1b2d 100644
--- a/data/settings.html
+++ b/data/settings.html
@@ -145,13 +145,6 @@
Reset to Defaults?
tooltipText: 'Make stepper silent at expense of torque',
defaultValue: false
},
- autoUpdate: {
- type: 'toggle',
- title: 'Automatic Updates',
- tooltip: 'OTA firmware',
- tooltipText: 'Check for new firmware on boot?',
- defaultValue: false
- },
stepperDir: {
type: 'toggle',
title: 'Stepper Motor Direction',
diff --git a/data/settings.html.gz b/data/settings.html.gz
index 627bf40f..b62a002b 100644
Binary files a/data/settings.html.gz and b/data/settings.html.gz differ
diff --git a/data_s3/settings.html b/data_s3/settings.html
index fea8c153..482b1b2d 100644
--- a/data_s3/settings.html
+++ b/data_s3/settings.html
@@ -145,13 +145,6 @@ Reset to Defaults?
tooltipText: 'Make stepper silent at expense of torque',
defaultValue: false
},
- autoUpdate: {
- type: 'toggle',
- title: 'Automatic Updates',
- tooltip: 'OTA firmware',
- tooltipText: 'Check for new firmware on boot?',
- defaultValue: false
- },
stepperDir: {
type: 'toggle',
title: 'Stepper Motor Direction',
diff --git a/data_s3/settings.html.gz b/data_s3/settings.html.gz
index 627bf40f..b62a002b 100644
Binary files a/data_s3/settings.html.gz and b/data_s3/settings.html.gz differ
diff --git a/include/BLE_Common.h b/include/BLE_Common.h
index cc1f1f6c..8fab9bd9 100644
--- a/include/BLE_Common.h
+++ b/include/BLE_Common.h
@@ -59,13 +59,6 @@ using BLEServices::SUPPORTED_SERVICES;
#define FMTS_SERVER_LOG_TAG "FTMS_SERVER"
#define CUSTOM_CHAR_LOG_TAG "Custom_C"
-// macros to convert different types of bytes into int The naming here sucks and
-// should be fixed.
-#define bytes_to_s16(MSB, LSB) (((signed int)((signed char)MSB))) << 8 | (((signed char)LSB))
-#define bytes_to_u16(MSB, LSB) (((signed int)((signed char)MSB))) << 8 | (((unsigned char)LSB))
-#define bytes_to_int(MSB, LSB) ((static_cast((unsigned char)MSB))) << 8 | (((unsigned char)LSB))
-// Potentially, something like this is a better way of doing this ^^
-
// Setup
void setupBLE();
extern TaskHandle_t BLEClientTask;
@@ -115,6 +108,7 @@ extern SpinBLEServer spinBLEServer;
// extern BLE_Wattbike_Service wattbikeService;
void startBLEServer();
+void refreshBLEAdvertisementIp();
void logCharacteristic(char* buffer, const size_t bufferCapacity, const byte* data, const size_t dataLength, const NimBLEUUID serviceUUID, const NimBLEUUID charUUID,
const char* format, ...);
void calculateInstPwrFromHR();
diff --git a/include/BLE_Custom_Characteristic.h b/include/BLE_Custom_Characteristic.h
index 1673c46d..52bb4df3 100644
--- a/include/BLE_Custom_Characteristic.h
+++ b/include/BLE_Custom_Characteristic.h
@@ -9,63 +9,7 @@
#include
#include "BLE_Common.h"
-
-// Codes
-const uint8_t cc_read = 0x01; // value to request read operation
-const uint8_t cc_write = 0x02; // Value to request write operation
-const uint8_t cc_error = 0xff; // value server error/unable
-const uint8_t cc_success = 0x80; // value for success
-
-// custom characteristic codes
-const uint8_t BLE_firmwareUpdateURL = 0x01; // URL used to update firmware
-const uint8_t BLE_incline = 0x02; // target incline like
-const uint8_t BLE_simulatedWatts = 0x03; // current watts
-const uint8_t BLE_simulatedHr = 0x04; // current hr
-const uint8_t BLE_simulatedCad = 0x05; // current cad
-const uint8_t BLE_simulatedSpeed = 0x06; // current speed
-const uint8_t BLE_deviceName = 0x07; // Name of SmartSpin2k (Changes BLE name and URL for local access)
-const uint8_t BLE_shiftStep = 0x08; // Amount of stepper steps for a shift
-const uint8_t BLE_stepperPower = 0x09; // Stepper power in ma. Capped at 2000
-const uint8_t BLE_stealthChop = 0x0A; // Stepper StealthChop (Makes it quieter with a little torque sacrificed)
-const uint8_t BLE_inclineMultiplier = 0x0B; // Incline * this = steps to move. 3.0 is a good starting value for most bikes.
-const uint8_t BLE_powerCorrectionFactor = 0x0C; // Correction factor for FTMS and CPS connected devices. .1-2.0
-const uint8_t BLE_simulateHr = 0x0D; // If set to 1, override connected HR and use simulated above.
-const uint8_t BLE_simulateWatts = 0x0E; // Are we sending watts
-const uint8_t BLE_simulateCad = 0x0F; // Are we sending cad
-const uint8_t BLE_FTMSMode = 0x10; // get or set FTMS mode using values such as FitnessMachineControlPointProcedure::SetTargetPower
-const uint8_t BLE_autoUpdate = 0x11; // Attempt to update firmware on reboot?
-const uint8_t BLE_ssid = 0x12; // WiFi SSID. If it's not a network in range, fallback to AP mode made with devicename and "password"
-const uint8_t BLE_password = 0x13; // WiFi Password for known network. Fallback to "password" if network above not in range
-const uint8_t BLE_foundDevices = 0x14; // BLE fitness devices in range
-const uint8_t BLE_connectedPowerMeter = 0x15; // Desired Power Meter
-const uint8_t BLE_connectedHeartMonitor = 0x16; // Desired HR Monitor
-const uint8_t BLE_shifterPosition = 0x17; // Current Shifter Position
-const uint8_t BLE_saveToLittleFS = 0x18; // Write to make settings Permanent
-const uint8_t BLE_targetPosition = 0x19; // The target position of the stepper motor
-const uint8_t BLE_externalControl = 0x1A; // Stop all internal control of stepper motor - let external device control motor
-const uint8_t BLE_syncMode = 0x1B; // Stop stepper motor and set the current position to the target position
-const uint8_t BLE_reboot = 0x1C; // Reboots the SmartSpin2k
-const uint8_t BLE_resetToDefaults = 0x1D; // Resets the SmartSpin2k to defaults - Settings only, not the filesystem
-const uint8_t BLE_stepperSpeed = 0x1E; // Stepper motor speed. Use cautiously. numbers above 3500 increase crashiness
-const uint8_t BLE_ERGSensitivity = 0x1F; // Aggressiveness of the control loop for ERG mode
-const uint8_t BLE_shiftDir = 0x20; // Flip flops the hardwired shifter direction
-const uint8_t BLE_minBrakeWatts = 0x21; // Minimum watts @ 90rpm . Used to set software end stop so motor doesn't crash and lose steps.
-const uint8_t BLE_maxBrakeWatts = 0x22; // "" Maximum watts for the other direction
-const uint8_t BLE_restartBLE = 0x23; // Closes all connections to the BLE client - used to connect new BLE devices the user selects.
-const uint8_t BLE_scanBLE = 0x24; // Scan for new BLE devices
-const uint8_t BLE_firmwareVer = 0x25; // String of the current firmware version
-const uint8_t BLE_resetPowerTable = 0x26; // Delete all power table information.
-const uint8_t BLE_powerTableData = 0x27; // sets or requests power table data
-const uint8_t BLE_simulatedTargetWatts = 0x28; // current target watts
-const uint8_t BLE_simulateTargetWatts = 0x29; // are we sending target watts
-const uint8_t BLE_hMin = 0x2A; // Minimum homing value
-const uint8_t BLE_hMax = 0x2B; // Maximum homing value
-const uint8_t BLE_homingSensitivity = 0x2C; // Homing sensitivity value
-const uint8_t BLE_pTab4Pwr = 0x2D; // Use power values for power table
-const uint8_t BLE_UDPLogging = 0x2E; // Enable or disable UDP logging
-const uint8_t BLE_hardwareVersion = 0x2F; // Read-only string identifying the detected hardware revision
-const uint8_t BLE_BLELogging = 0x30; // Enable or disable BLE logging
-const uint8_t BLE_allSettings = 0x31; // Read all user settings as a chunked JSON snapshot
+#include "CustomCharacteristicProtocol.h"
class BLE_ss2kCustomCharacteristic {
public:
@@ -75,6 +19,9 @@ class BLE_ss2kCustomCharacteristic {
static void process(std::string rxValue, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE, uint16_t mtu = 23, bool indicateResponse = true);
// Custom Characteristic value that needs to be notified
static void notify(char _item, int tableRow = -1);
+ static void beginScanResults();
+ static void notifyScanResult(const String& name, const NimBLEUUID& serviceUuid);
+ static void endScanResults();
// Notify any changed value in userConfig
static void parseNemit();
diff --git a/include/BLE_Definitions.h b/include/BLE_Definitions.h
index 21d3e482..1ab3d040 100644
--- a/include/BLE_Definitions.h
+++ b/include/BLE_Definitions.h
@@ -8,6 +8,8 @@
#pragma once
#include
+#include
+#include "ByteUtils.h"
// The .xml file is wrong, make sure to reference the actual FTMS .pdf
struct FitnessMachineIndoorBikeDataFlags {
@@ -73,7 +75,7 @@ struct FitnessMachineControlPointResultCode {
// https://www.bluetooth.com/specifications/specs/fitness-machine-service-1-0/
// Table 4.3: Definition of the bits of the Fitness Machine Features field
struct FitnessMachineFeatureFlags {
- enum Types : uint {
+ enum Types : uint32_t {
AverageSpeedSupported = 1U << 0,
CadenceSupported = 1U << 1,
TotalDistanceSupported = 1U << 2,
@@ -97,7 +99,7 @@ struct FitnessMachineFeatureFlags {
// https://www.bluetooth.com/specifications/specs/fitness-machine-service-1-0/
// Table 4.4: Definition of the bits of the Target Setting Features field
struct FitnessMachineTargetFlags {
- enum Types : uint {
+ enum Types : uint32_t {
SpeedTargetSettingSupported = 1U << 0,
InclinationTargetSettingSupported = 1U << 1,
ResistanceTargetSettingSupported = 1U << 2,
@@ -282,32 +284,28 @@ class CyclingPowerMeasurement {
flagBits |= flags.accumulatedEnergyPresent ? (1U << 11) : 0;
flagBits |= flags.offsetCompensationIndicator ? (1U << 12) : 0;
- data[offset++] = static_cast(flagBits & 0xFF);
- data[offset++] = static_cast((flagBits >> 8) & 0xFF);
+ put_le16(&data[offset], flagBits);
+ offset += 2;
// Add Instantaneous Power
- data[offset++] = static_cast(instantaneousPower & 0xFF);
- data[offset++] = static_cast((instantaneousPower >> 8) & 0xFF);
+ put_le16s(&data[offset], instantaneousPower);
+ offset += 2;
// Conditional fields based on flags
if (flags.wheelRevolutionDataPresent) {
// Add wheel revolution data if present
- data[offset++] = static_cast(cumulativeWheelRevolutions & 0xFF);
- data[offset++] = static_cast((cumulativeWheelRevolutions >> 8) & 0xFF);
- data[offset++] = static_cast((cumulativeWheelRevolutions >> 16) & 0xFF);
- data[offset++] = static_cast((cumulativeWheelRevolutions >> 24) & 0xFF);
-
- data[offset++] = static_cast(lastWheelEventTime & 0xFF);
- data[offset++] = static_cast((lastWheelEventTime >> 8) & 0xFF);
+ put_le32(&data[offset], cumulativeWheelRevolutions);
+ offset += 4;
+ put_le16(&data[offset], lastWheelEventTime);
+ offset += 2;
}
// Conditional fields based on flags
if (flags.crankRevolutionDataPresent) {
// Add crank revolution data if present
- data[offset++] = static_cast(cumulativeCrankRevolutions & 0xFF);
- data[offset++] = static_cast((cumulativeCrankRevolutions >> 8) & 0xFF);
-
- data[offset++] = static_cast(lastCrankEventTime & 0xFF);
- data[offset++] = static_cast((lastCrankEventTime >> 8) & 0xFF);
+ put_le16(&data[offset], cumulativeCrankRevolutions);
+ offset += 2;
+ put_le16(&data[offset], lastCrankEventTime);
+ offset += 2;
}
return offset;
@@ -363,22 +361,18 @@ class CscMeasurement {
// Conditional fields based on flags
if (flags.wheelRevolutionDataPresent) {
// Add wheel revolution data if present
- for (int i = 0; i < 4; ++i) {
- data[offset++] = static_cast((cumulativeWheelRevolutions >> (i * 8)) & 0xFF);
- }
- for (int i = 0; i < 2; ++i) {
- data[offset++] = static_cast((lastWheelEventTime >> (i * 8)) & 0xFF);
- }
+ put_le32(&data[offset], cumulativeWheelRevolutions);
+ offset += 4;
+ put_le16(&data[offset], lastWheelEventTime);
+ offset += 2;
}
if (flags.crankRevolutionDataPresent) {
// Add crank revolution data if present
- for (int i = 0; i < 2; ++i) {
- data[offset++] = static_cast((cumulativeCrankRevolutions >> (i * 8)) & 0xFF);
- }
- for (int i = 0; i < 2; ++i) {
- data[offset++] = static_cast((lastCrankEventTime >> (i * 8)) & 0xFF);
- }
+ put_le16(&data[offset], cumulativeCrankRevolutions);
+ offset += 2;
+ put_le16(&data[offset], lastCrankEventTime);
+ offset += 2;
}
return offset;
diff --git a/include/BLE_Zwift_Service.h b/include/BLE_Zwift_Service.h
index 5cd28f0f..7d5603be 100644
--- a/include/BLE_Zwift_Service.h
+++ b/include/BLE_Zwift_Service.h
@@ -70,17 +70,6 @@ class BLE_Zwift_Service {
// Encode a button mask into a protobuf varint message and send as notification
void sendButtonNotification(ZwiftProtocol::RideButtonMask buttonMask);
- // Encode uint32 as protobuf varint, returns number of bytes written
- static size_t encodeVarint32(uint32_t value, uint8_t *buffer);
-
- // Encode uint64 as ULEB128 varint, returns number of bytes written
- static size_t encodeUleb128(uint64_t value, uint8_t *buffer);
- // Returns the number of bytes a ULEB128-encoded value would occupy
- static size_t encodeUleb128Len(uint64_t value);
-
- // Decode ULEB128 varint from buffer, returns number of bytes consumed
- static size_t decodeUleb128(const uint8_t *buf, size_t bufLen, uint64_t *result);
-
// Send the "all buttons released" state
void sendAllButtonsReleased();
diff --git a/include/DirConManager.h b/include/DirConManager.h
index 6123992a..9999e8cd 100644
--- a/include/DirConManager.h
+++ b/include/DirConManager.h
@@ -23,6 +23,11 @@
#define DIRCON_SEND_BUFFER_SIZE 256
#define DIRCON_MAX_CHARACTERISTICS 20 // maximum number of characteristics to track for subscriptions
#define DIRCON_MAX_SERVICES 10 // maximum number of services that can register with DirCon
+#define DIRCON_OUTBOUND_QUEUE_DEPTH 8 // bounded per-client queue; notifications are dropped when full
+#define DIRCON_SEND_STALL_TIMEOUT_MS 5000 // disconnect a client whose queued data cannot make progress
+#define DIRCON_TCP_KEEPIDLE_SECONDS 5
+#define DIRCON_TCP_KEEPINTVL_SECONDS 2
+#define DIRCON_TCP_KEEPCNT 3
// Result struct populated by service write handler callbacks
struct DirConWriteResult {
@@ -70,6 +75,7 @@ class DirConManager {
static bool started;
static String statusMessage;
static WiFiClient dirConClients[DIRCON_MAX_CLIENTS];
+ static bool clientActive[DIRCON_MAX_CLIENTS];
static WiFiServer* tcpServer;
static void setupMDNS();
static void updateStatusMessage();
@@ -78,9 +84,12 @@ class DirConManager {
// TCP connection handling
static void checkForNewClients();
static void handleClientData();
+ static void drainOutboundQueues();
+ static void closeClient(size_t clientIndex, const char* reason);
+ static bool queueOutboundFrame(size_t clientIndex, const uint8_t* data, size_t length, bool notification, const NimBLEUUID* requiredSubscription = nullptr);
+ static void configureClientSocket(WiFiClient& client);
static uint8_t receiveBuffer[DIRCON_MAX_CLIENTS][DIRCON_RECEIVE_BUFFER_SIZE];
static size_t receiveBufferLength[DIRCON_MAX_CLIENTS];
- static uint8_t sendBuffer[DIRCON_SEND_BUFFER_SIZE];
// Message handling
static bool processDirConMessage(DirConMessage* message, size_t clientIndex);
diff --git a/include/DirConUUIDCodec.h b/include/DirConUUIDCodec.h
new file mode 100644
index 00000000..780a080a
--- /dev/null
+++ b/include/DirConUUIDCodec.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2020 Anthony Doud & Joel Baranick
+ * All rights reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+#pragma once
+
+#include
+
+#ifndef PLATFORMIO_ENV_NATIVE
+#include
+#endif
+
+namespace DirConUUIDCodec {
+
+inline void appendValueBytes(const uint8_t* uuidBytes, std::vector& message) {
+ for (size_t i = 16; i > 0; i--) {
+ message.push_back(uuidBytes[i - 1]);
+ }
+}
+
+#ifndef PLATFORMIO_ENV_NATIVE
+inline void appendBytes(NimBLEUUID uuid, std::vector& message) {
+ uuid.to128();
+ appendValueBytes(uuid.getValue(), message);
+}
+
+inline NimBLEUUID fromBytes(const uint8_t* data) {
+ NimBLEUUID uuid(data, 16);
+ uuid.reverseByteOrder();
+ return uuid;
+}
+#endif
+
+} // namespace DirConUUIDCodec
diff --git a/include/ERG_Mode.h b/include/ERG_Mode.h
index 11952c60..7cedc350 100644
--- a/include/ERG_Mode.h
+++ b/include/ERG_Mode.h
@@ -14,6 +14,8 @@
#define ERG_MODE_LOG_TAG "ERG_Mode"
#define ERG_MODE_DELAY 700
+constexpr int ERG_MODE_LOG_INTERVAL_MS = 2000;
+
struct Mode {
static const int MAINTAIN = 0;
static const int DECREASING = 1;
diff --git a/include/ERG_Mode_Utils.h b/include/ERG_Mode_Utils.h
new file mode 100644
index 00000000..230bceb8
--- /dev/null
+++ b/include/ERG_Mode_Utils.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 Anthony Doud & Joel Baranick
+ * All rights reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+#pragma once
+
+#include
+#include
+
+namespace ErgControl {
+
+constexpr int LOW_GAIN_WATTS = 120;
+constexpr int HIGH_GAIN_WATTS = 400;
+constexpr int MIN_SCHEDULE_WATTS = 30;
+constexpr double TABLE_GAIN_MIN_FALLBACK_RATIO = 0.5;
+constexpr double TABLE_GAIN_MAX_FALLBACK_RATIO = 1.25;
+constexpr double TABLE_GAIN_BLEND = 0.5;
+constexpr double GAIN_MIN_SENSITIVITY_RATIO = 0.25;
+constexpr double GAIN_MAX_SENSITIVITY_RATIO = 4.0;
+constexpr double SLOPE_CONTROL_DIVISOR = 10.0;
+
+inline double sanitizeSensitivity(double sensitivity) {
+ return std::isfinite(sensitivity) && sensitivity > 0.0 ? sensitivity : 1.0;
+}
+
+inline double fallbackGain(double sensitivity, int operatingWatts) {
+ sensitivity = sanitizeSensitivity(sensitivity);
+ if (operatingWatts < LOW_GAIN_WATTS) {
+ return sensitivity * LOW_GAIN_WATTS / std::max(operatingWatts, MIN_SCHEDULE_WATTS);
+ }
+ if (operatingWatts > HIGH_GAIN_WATTS) {
+ return sensitivity * HIGH_GAIN_WATTS / operatingWatts;
+ }
+ return sensitivity;
+}
+
+inline double boundedTableGain(double localGain, double fallback) {
+ if (!std::isfinite(localGain) || localGain <= 0.0) return fallback;
+ const double minimumGain = fallback * TABLE_GAIN_MIN_FALLBACK_RATIO;
+ const double maximumGain = fallback * TABLE_GAIN_MAX_FALLBACK_RATIO;
+ return std::max(minimumGain, std::min(localGain, maximumGain));
+}
+
+inline double blendedTableGain(double localGain, double fallback) {
+ const double bounded = boundedTableGain(localGain, fallback);
+ return fallback + (bounded - fallback) * TABLE_GAIN_BLEND;
+}
+
+inline double errorScheduledGain(double gain, int error, bool maintaining) {
+ const int absoluteError = std::abs(error);
+ if (absoluteError < 10 || !maintaining) return gain * 0.25;
+ if (absoluteError < 50) return gain * 0.75;
+ if (absoluteError > 100) return gain * 1.25;
+ return gain;
+}
+
+inline double clampGain(double gain, double sensitivity) {
+ sensitivity = sanitizeSensitivity(sensitivity);
+ const double minimumGain = sensitivity * GAIN_MIN_SENSITIVITY_RATIO;
+ const double maximumGain = sensitivity * GAIN_MAX_SENSITIVITY_RATIO;
+ return std::max(minimumGain, std::min(gain, maximumGain));
+}
+
+} // namespace ErgControl
diff --git a/include/HTTP_Server_Basic.h b/include/HTTP_Server_Basic.h
index 4fb684a7..c946423e 100644
--- a/include/HTTP_Server_Basic.h
+++ b/include/HTTP_Server_Basic.h
@@ -23,7 +23,7 @@ class HTTP_Server {
static void handleIndexFile();
static void settingsProcessor();
static void handleHrSlider();
- static void FirmwareUpdate();
+ static bool syncWebServerFiles();
static void webClientUpdate();
diff --git a/include/PowerTable_Helpers.h b/include/PowerTable_Helpers.h
index 0c743534..145175ec 100644
--- a/include/PowerTable_Helpers.h
+++ b/include/PowerTable_Helpers.h
@@ -13,6 +13,28 @@
#define RETURN_ERROR INT32_MIN
+class PowerTableSlopeStatus {
+ public:
+ enum Value : uint8_t {
+ Trusted,
+ InvalidRequest,
+ InsufficientRows,
+ MissingLocalSupport,
+ InconsistentRows,
+ };
+
+ static const char* name(Value status) {
+ switch (status) {
+ case Trusted: return "trusted";
+ case InvalidRequest: return "invalid request";
+ case InsufficientRows: return "one supporting row";
+ case MissingLocalSupport: return "missing local segment";
+ case InconsistentRows: return "inconsistent rows";
+ }
+ return "unknown";
+ }
+};
+
class PowerEntry {
public:
int watts;
@@ -73,42 +95,20 @@ class PTData {
TableRow tableRow[POWERTABLE_CAD_SIZE];
};
-class ResistanceModel {
+class PTHelpers {
private:
- // Coefficients (Normalizing makes these fit in float/double safely)
- // Model: Z = b0 + b1*x + b2*y + b3*x^2 + b4*y^2 + b5*x*y
- double b[6] = {0};
- bool isQuadratic = false;
- bool isValid = false;
-
- // Normalization bounds (to keep math stable)
- double minW = 0, maxW = 1;
- double minR = 0, maxR = 1;
-
- // Helper: Normalize a value to 0.0 - 1.0 range
- double normW(double w) { return (w - minW) / (maxW - minW); }
- double normR(double r) { return (r - minR) / (maxR - minR); }
- bool solveMatrix(double A[6][6], double B[6], int n);
-
- public:
- void fit(const PTData& data);
- int16_t predict(double watts, double rpm);
- int predictWatts(int32_t resistance, float cadence);
- bool getIsValid() { return isValid; }
- bool getIsQuadratic() { return isQuadratic; }
-};
+ void clean(PTData& ptData);
+ bool enforceMonotonicAcrossCadence(PTData& ptData);
+ bool enforceMonotonicAcrossPower(PTData& ptData);
+ int32_t invertForwardSurface(int cad, int32_t targetPosition, PTData& ptData);
-class PTHelpers {
public:
- ResistanceModel resistanceModel;
int32_t lookup(int watts, int cad, PTData& ptData);
- int lookupWatts(int cad, int32_t targetPosition, PTData& ptData);
+ bool lookupSlope(int watts, int cad, double& stepsPerWatt, PTData& ptData, PowerTableSlopeStatus::Value* status = nullptr);
+ bool lookupErgSlope(int watts, int cad, double& stepsPerWatt, PTData& ptData, PowerTableSlopeStatus::Value* status = nullptr);
+ int32_t lookupWatts(int cad, int32_t targetPosition, PTData& ptData);
int getTotalReadings(PTData& ptData);
ptIndex calculateIndex(int watts, int cad);
+ bool cadenceIsWithinTable(int cad);
void enterData(PTData& ptData, ptIndex index, int pos);
- void clean(PTData& ptData);
- void fill(PTData& ptData);
- void fillGaps(PTData& ptData);
- bool fillAllWattColumns(PTData& ptData);
- bool fillAllCadenceLines(PTData& ptData);
};
diff --git a/include/Power_Table.h b/include/Power_Table.h
index c497a8b2..274d4a1c 100644
--- a/include/Power_Table.h
+++ b/include/Power_Table.h
@@ -33,6 +33,15 @@ class PowerTable {
// returns target position for given cadence and watts. Returns RETURN_ERROR if not found.
int32_t lookup(int watts, int cad) { return this->ptHelpers.lookup(watts, cad, this->ptData); }
+ // Returns a local steps-per-watt slope only when two nearby cadence rows
+ // provide consistent measured segments around the requested watts.
+ bool lookupSlope(int watts, int cad, double& stepsPerWatt, PowerTableSlopeStatus::Value* status = nullptr) {
+ return this->ptHelpers.lookupSlope(watts, cad, stepsPerWatt, this->ptData, status);
+ }
+ bool lookupErgSlope(int watts, int cad, double& stepsPerWatt, PowerTableSlopeStatus::Value* status = nullptr) {
+ return this->ptHelpers.lookupErgSlope(watts, cad, stepsPerWatt, this->ptData, status);
+ }
+
// returns watts for given cadence and target position. Returns RETURN_ERROR if not found.
int32_t lookupWatts(int cad, int32_t targetPosition) { return this->ptHelpers.lookupWatts(cad, targetPosition, this->ptData); }
@@ -52,4 +61,4 @@ class PowerTable {
unsigned long lastSaveTime = millis();
};
-extern PowerTable* powerTable;
\ No newline at end of file
+extern PowerTable* powerTable;
diff --git a/include/SmartSpin_parameters.h b/include/SmartSpin_parameters.h
index bb58bc8f..9402171f 100644
--- a/include/SmartSpin_parameters.h
+++ b/include/SmartSpin_parameters.h
@@ -7,11 +7,7 @@
#pragma once
-#ifndef PLATFORMIO_ENV_NATIVE
#include
-#else
-#include
-#endif
#include "settings.h"
@@ -130,7 +126,7 @@ class userParameters {
float inclineMultiplier;
float powerCorrectionFactor;
float ERGSensitivity;
- bool autoUpdate;
+ bool autoUpdate; // Deprecated compatibility field; boot-time firmware OTA is disabled.
int stepperPower;
int maxWatts;
int minWatts;
@@ -175,7 +171,10 @@ class userParameters {
float getERGSensitivity() { return ERGSensitivity; }
void setERGSensitivity(float ergS) { ERGSensitivity = ergS; }
- void setAutoUpdate(bool atd) { autoUpdate = atd; }
+ void setAutoUpdate(bool atd) {
+ (void)atd;
+ autoUpdate = false;
+ }
bool getAutoUpdate() { return autoUpdate; }
void setSsid(String sid) { ssid = sid; }
diff --git a/include/Zwift_Protocol_Messages.h b/include/Zwift_Protocol_Messages.h
index 38f6c9bb..fff0e965 100644
--- a/include/Zwift_Protocol_Messages.h
+++ b/include/Zwift_Protocol_Messages.h
@@ -8,13 +8,50 @@
#pragma once
#include
+#include
#include
namespace ZwiftProtocol {
+inline size_t encodeUleb128(uint64_t value, uint8_t* buffer) {
+ size_t length = 0;
+ do {
+ uint8_t byte = static_cast(value & 0x7FU);
+ value >>= 7U;
+ if (value != 0) byte |= 0x80U;
+ buffer[length++] = byte;
+ } while (value != 0);
+ return length;
+}
+
+inline size_t decodeUleb128(const uint8_t* buffer, size_t bufferLength, uint64_t* value) {
+ if (buffer == nullptr || value == nullptr) return 0;
+ *value = 0;
+ for (size_t i = 0, shift = 0; i < bufferLength && shift < 64; ++i, shift += 7) {
+ const uint8_t byte = buffer[i];
+ *value |= static_cast(byte & 0x7FU) << shift;
+ if ((byte & 0x80U) == 0) return i + 1;
+ }
+ return 0;
+}
+
+inline size_t uleb128Length(uint64_t value) {
+ size_t length = 1;
+ while ((value >>= 7U) != 0) ++length;
+ return length;
+}
+
+inline uint64_t encodeZigZag64(int64_t value) {
+ return (static_cast(value) << 1U) ^ static_cast(-(value < 0));
+}
+
+inline int64_t decodeZigZag64(uint64_t value) {
+ return static_cast((value >> 1U) ^ static_cast(-static_cast(value & 1U)));
+}
+
template
-constexpr auto toUnderlying(Enum value) noexcept -> std::underlying_type_t {
- return static_cast>(value);
+constexpr auto toUnderlying(Enum value) noexcept -> typename std::underlying_type::type {
+ return static_cast::type>(value);
}
enum class CommandCode : uint8_t {
diff --git a/include/boards.h b/include/boards.h
index eee640c2..c125e01a 100644
--- a/include/boards.h
+++ b/include/boards.h
@@ -7,11 +7,7 @@
#pragma once
-#ifndef UNIT_TEST
#include
-#else
-#include
-#endif
class Board {
public:
diff --git a/include/cert.h b/include/cert.h
index 53645c33..bb812d6a 100644
--- a/include/cert.h
+++ b/include/cert.h
@@ -7,7 +7,7 @@
/*
* Automatically updated CA certificate for raw.githubusercontent.com
- * Last updated: 2026-08-23 08:57:32
+ * Last updated: 2026-08-27 02:06:05
*/
#pragma once
@@ -41,4 +41,4 @@ const char* rootCACertificate =
"YVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPAmRGunUHBcnWEvgJBQl9n\n"
"JEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57demyPxgcYxn/eR44/KJ4EBs+lVDR3veyJ\n"
"m+kXQ99b21/+jh5Xos1AnX5iItreGCc=\n"
- "-----END CERTIFICATE-----\n";
\ No newline at end of file
+ "-----END CERTIFICATE-----\n";
diff --git a/include/settings.h b/include/settings.h
index eeceb522..0b3692dc 100644
--- a/include/settings.h
+++ b/include/settings.h
@@ -7,9 +7,6 @@
#pragma once
-// Update firmware on boot?
-#define AUTO_FIRMWARE_UPDATE true
-
// Default Bluetooth WiFi and MDNS Name
const char* const DEVICE_NAME = "SmartSpin2k";
@@ -189,6 +186,12 @@ constexpr const char* ANY = "any";
// The client will be disconnected.
#define BLE_CLIENT_DISCONNECT_TIMEOUT 5000
+// Interval for periodic main-task maintenance.
+#define MAIN_MAINTENANCE_INTERVAL_MS 6007
+
+// Interval for rider status logging used by ride analysis and power-table tests.
+#define RIDER_STATUS_LOG_INTERVAL_MS 1000
+
#ifndef DEBUG_LOG_BUFFER_SIZE
#define DEBUG_LOG_BUFFER_SIZE 600
#endif
diff --git a/lib/ArduinoCompat/include/Arduino.h b/lib/ArduinoCompat/include/Arduino.h
new file mode 100644
index 00000000..b4e98e40
--- /dev/null
+++ b/lib/ArduinoCompat/include/Arduino.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2020 Anthony Doud & Joel Baranick
+ * All rights reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+#pragma once
+
+#include
+#include
+#include
+
+using String = std::string;
+
+inline unsigned long millis() {
+ using namespace std::chrono;
+ return static_cast(duration_cast(steady_clock::now().time_since_epoch()).count());
+}
diff --git a/lib/SS2K/include/ByteUtils.h b/lib/SS2K/include/ByteUtils.h
new file mode 100644
index 00000000..16f3cecd
--- /dev/null
+++ b/lib/SS2K/include/ByteUtils.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2020 Anthony Doud & Joel Baranick
+ * All rights reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+#pragma once
+
+#include
+#include
+
+// The underlying endian API deliberately exposes unsigned bit patterns. These
+// wrappers make two's-complement signed protocol fields explicit at call sites.
+inline void put_le16s(void* buffer, int16_t value) { put_le16(buffer, static_cast(value)); }
+inline void put_le32s(void* buffer, int32_t value) { put_le32(buffer, static_cast(value)); }
+inline int16_t get_le16s(const void* buffer) { return static_cast(get_le16(buffer)); }
+inline int32_t get_le32s(const void* buffer) { return static_cast(get_le32(buffer)); }
+
diff --git a/lib/SS2K/include/CustomCharacteristicProtocol.h b/lib/SS2K/include/CustomCharacteristicProtocol.h
new file mode 100644
index 00000000..9da218b9
--- /dev/null
+++ b/lib/SS2K/include/CustomCharacteristicProtocol.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2020 Anthony Doud & Joel Baranick
+ * All rights reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+#pragma once
+
+#include
+
+// SmartSpin2k custom-characteristic operations and result codes.
+static const uint8_t cc_read = 0x01;
+static const uint8_t cc_write = 0x02;
+static const uint8_t cc_success = 0x80;
+static const uint8_t cc_error = 0xFF;
+
+// Custom-characteristic variable identifiers. Keep these contiguous so tests can
+// verify that every protocol value has an explicitly documented wire format.
+static const uint8_t BLE_firmwareUpdateURL = 0x01;
+static const uint8_t BLE_incline = 0x02;
+static const uint8_t BLE_simulatedWatts = 0x03;
+static const uint8_t BLE_simulatedHr = 0x04;
+static const uint8_t BLE_simulatedCad = 0x05;
+static const uint8_t BLE_simulatedSpeed = 0x06;
+static const uint8_t BLE_deviceName = 0x07;
+static const uint8_t BLE_shiftStep = 0x08;
+static const uint8_t BLE_stepperPower = 0x09;
+static const uint8_t BLE_stealthChop = 0x0A;
+static const uint8_t BLE_inclineMultiplier = 0x0B;
+static const uint8_t BLE_powerCorrectionFactor = 0x0C;
+static const uint8_t BLE_simulateHr = 0x0D;
+static const uint8_t BLE_simulateWatts = 0x0E;
+static const uint8_t BLE_simulateCad = 0x0F;
+static const uint8_t BLE_FTMSMode = 0x10;
+static const uint8_t BLE_autoUpdate = 0x11;
+static const uint8_t BLE_ssid = 0x12;
+static const uint8_t BLE_password = 0x13;
+static const uint8_t BLE_foundDevices = 0x14;
+static const uint8_t BLE_connectedPowerMeter = 0x15;
+static const uint8_t BLE_connectedHeartMonitor = 0x16;
+static const uint8_t BLE_shifterPosition = 0x17;
+static const uint8_t BLE_saveToLittleFS = 0x18;
+static const uint8_t BLE_targetPosition = 0x19;
+static const uint8_t BLE_externalControl = 0x1A;
+static const uint8_t BLE_syncMode = 0x1B;
+static const uint8_t BLE_reboot = 0x1C;
+static const uint8_t BLE_resetToDefaults = 0x1D;
+static const uint8_t BLE_stepperSpeed = 0x1E;
+static const uint8_t BLE_ERGSensitivity = 0x1F;
+static const uint8_t BLE_shiftDir = 0x20;
+static const uint8_t BLE_minBrakeWatts = 0x21;
+static const uint8_t BLE_maxBrakeWatts = 0x22;
+static const uint8_t BLE_restartBLE = 0x23;
+static const uint8_t BLE_scanBLE = 0x24;
+static const uint8_t BLE_firmwareVer = 0x25;
+static const uint8_t BLE_resetPowerTable = 0x26;
+static const uint8_t BLE_powerTableData = 0x27;
+static const uint8_t BLE_simulatedTargetWatts = 0x28;
+static const uint8_t BLE_simulateTargetWatts = 0x29;
+static const uint8_t BLE_hMin = 0x2A;
+static const uint8_t BLE_hMax = 0x2B;
+static const uint8_t BLE_homingSensitivity = 0x2C;
+static const uint8_t BLE_pTab4Pwr = 0x2D;
+static const uint8_t BLE_UDPLogging = 0x2E;
+static const uint8_t BLE_hardwareVersion = 0x2F;
+static const uint8_t BLE_BLELogging = 0x30;
+static const uint8_t BLE_allSettings = 0x31;
+static const uint8_t BLE_scanResults = 0x32;
+
+enum CustomCharacteristicValueFormat : uint8_t {
+ CustomAction,
+ CustomBoolean,
+ CustomUnsigned16,
+ CustomSigned16,
+ CustomSigned32,
+ CustomString,
+ CustomPowerTableRow,
+ CustomSettingsSnapshot,
+ CustomScanResultStream,
+ CustomBooleanWriteStringRead,
+ CustomUnknown
+};
+
+inline CustomCharacteristicValueFormat customCharacteristicValueFormat(uint8_t id) {
+ switch (id) {
+ case BLE_firmwareUpdateURL:
+ case BLE_deviceName:
+ case BLE_ssid:
+ case BLE_password:
+ case BLE_foundDevices:
+ case BLE_connectedPowerMeter:
+ case BLE_connectedHeartMonitor:
+ case BLE_firmwareVer:
+ case BLE_hardwareVersion: return CustomString;
+
+ case BLE_incline:
+ case BLE_inclineMultiplier:
+ case BLE_shifterPosition: return CustomSigned16;
+
+ case BLE_simulatedWatts:
+ case BLE_simulatedHr:
+ case BLE_simulatedCad:
+ case BLE_simulatedSpeed:
+ case BLE_shiftStep:
+ case BLE_stepperPower:
+ case BLE_powerCorrectionFactor:
+ case BLE_FTMSMode:
+ case BLE_stepperSpeed:
+ case BLE_ERGSensitivity:
+ case BLE_minBrakeWatts:
+ case BLE_maxBrakeWatts:
+ case BLE_simulatedTargetWatts:
+ case BLE_homingSensitivity: return CustomUnsigned16;
+
+ case BLE_stealthChop:
+ case BLE_simulateHr:
+ case BLE_simulateWatts:
+ case BLE_simulateCad:
+ case BLE_autoUpdate:
+ case BLE_externalControl:
+ case BLE_syncMode:
+ case BLE_shiftDir:
+ case BLE_simulateTargetWatts:
+ case BLE_pTab4Pwr:
+ case BLE_UDPLogging: return CustomBoolean;
+
+ case BLE_targetPosition:
+ case BLE_hMin:
+ case BLE_hMax: return CustomSigned32;
+
+ case BLE_saveToLittleFS:
+ case BLE_reboot:
+ case BLE_resetToDefaults:
+ case BLE_restartBLE:
+ case BLE_scanBLE:
+ case BLE_resetPowerTable: return CustomAction;
+
+ case BLE_powerTableData: return CustomPowerTableRow;
+ case BLE_BLELogging: return CustomBooleanWriteStringRead;
+ case BLE_allSettings: return CustomSettingsSnapshot;
+ case BLE_scanResults: return CustomScanResultStream;
+ default: return CustomUnknown;
+ }
+}
diff --git a/lib/SS2K/include/ScanResultProtocol.h b/lib/SS2K/include/ScanResultProtocol.h
new file mode 100644
index 00000000..b08f494c
--- /dev/null
+++ b/lib/SS2K/include/ScanResultProtocol.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 Anthony Doud & Joel Baranick
+ * All rights reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+
+#include "CustomCharacteristicProtocol.h"
+
+namespace ScanResultProtocol {
+
+constexpr uint8_t VERSION = 1;
+constexpr size_t HEADER_LENGTH = 10;
+
+enum class Event : uint8_t {
+ Begin = 0,
+ Device = 1,
+ End = 2,
+};
+
+// A device body is deliberately compact and independent of JSON. UUID strings
+// are at most 36 bytes and advertised BLE names are short, so the body normally
+// fits in one negotiated ATT packet. It can still be fragmented for the 23-byte
+// minimum MTU.
+inline std::vector makeDeviceBody(const std::string& uuid, const std::string& name) {
+ const size_t uuidLength = std::min(uuid.length(), UINT8_MAX);
+ std::vector body;
+ body.reserve(1 + uuidLength + name.length());
+ body.push_back(static_cast(uuidLength));
+ body.insert(body.end(), uuid.begin(), uuid.begin() + uuidLength);
+ body.insert(body.end(), name.begin(), name.end());
+ return body;
+}
+
+inline std::vector makePacket(Event event, uint16_t scanId, uint16_t sequence, uint8_t fragment, uint8_t fragmentCount,
+ const uint8_t* payload = nullptr, size_t payloadLength = 0) {
+ std::vector packet(HEADER_LENGTH + payloadLength);
+ packet[0] = cc_success;
+ packet[1] = BLE_scanResults;
+ packet[2] = VERSION;
+ packet[3] = static_cast(event);
+ packet[4] = static_cast(scanId & 0xff);
+ packet[5] = static_cast(scanId >> 8);
+ packet[6] = static_cast(sequence & 0xff);
+ packet[7] = static_cast(sequence >> 8);
+ packet[8] = fragment;
+ packet[9] = fragmentCount;
+ if (payloadLength != 0 && payload != nullptr) {
+ std::copy_n(payload, payloadLength, packet.begin() + HEADER_LENGTH);
+ }
+ return packet;
+}
+
+} // namespace ScanResultProtocol
diff --git a/lib/SS2K/src/sensors/ChronoData.cpp b/lib/SS2K/src/sensors/ChronoData.cpp
index 0ccbf10f..65e1b76a 100644
--- a/lib/SS2K/src/sensors/ChronoData.cpp
+++ b/lib/SS2K/src/sensors/ChronoData.cpp
@@ -5,7 +5,7 @@
* SPDX-License-Identifier: GPL-2.0-only
*/
-#include "endian.h"
+#include "ByteUtils.h"
#include "sensors/ChronoData.h"
bool ChronoData::hasHeartRate() { return false; }
diff --git a/lib/SS2K/src/sensors/CscSensorData.cpp b/lib/SS2K/src/sensors/CscSensorData.cpp
index 67a6cf05..137af873 100644
--- a/lib/SS2K/src/sensors/CscSensorData.cpp
+++ b/lib/SS2K/src/sensors/CscSensorData.cpp
@@ -7,7 +7,7 @@
#include
#include "Data.h"
-#include "endian.h"
+#include "ByteUtils.h"
#include "sensors/CscSensorData.h"
// Replacement for Arduino's millis() using standard C++
@@ -110,4 +110,4 @@ void CscSensorData::decode(uint8_t *data, size_t length) {
lastCrankRevolutions = crankRevolutions;
lastCrankEventTime = crankEventTime;
}
-}
\ No newline at end of file
+}
diff --git a/lib/SS2K/src/sensors/CyclePowerData.cpp b/lib/SS2K/src/sensors/CyclePowerData.cpp
index 3ccff280..cd710e97 100644
--- a/lib/SS2K/src/sensors/CyclePowerData.cpp
+++ b/lib/SS2K/src/sensors/CyclePowerData.cpp
@@ -7,7 +7,7 @@
#include
#include "Data.h"
-#include "endian.h"
+#include "ByteUtils.h"
#include "sensors/CyclePowerData.h"
#ifdef PLATFORMIO_ENV_NATIVE
@@ -122,7 +122,7 @@ void CyclePowerData::decode(uint8_t *data, size_t length) {
// This casting behavior makes sure the roll over works correctly. Unit tests confirm
const float crankChange = (uint16_t)((this->crankRev - this->lastCrankRev) * 1024);
const float timeElapsed = (uint16_t)(this->crankEventTime - this->lastCrankEventTime);
- float cadence = std::round((crankChange / timeElapsed) * 60); // cadence in RPM
+ float cadence = (crankChange / timeElapsed) * 60.0f; // cadence in RPM
if (cadence > 1) {
if (cadence > 200) { // Human is unlikely producing 200+ cadence
// Cadence Error: Could happen if cadence measurements were missed
diff --git a/lib/SS2K/src/sensors/FitnessMachineIndoorBikeData.cpp b/lib/SS2K/src/sensors/FitnessMachineIndoorBikeData.cpp
index b3879b75..90db016b 100644
--- a/lib/SS2K/src/sensors/FitnessMachineIndoorBikeData.cpp
+++ b/lib/SS2K/src/sensors/FitnessMachineIndoorBikeData.cpp
@@ -6,7 +6,7 @@
*/
#include "Data.h"
-#include "endian.h"
+#include "ByteUtils.h"
#include "sensors/FitnessMachineIndoorBikeData.h"
// See:
@@ -81,7 +81,7 @@ void FitnessMachineIndoorBikeData::decode(uint8_t *data, size_t length) {
}
dataIndex += byteSize;
value = convert(value, byteSize, signedFlags[typeIndex]);
- double_t result = double_t(static_cast(std::round((value * resolutions[typeIndex] * 10) + 0.5)) / 10.0);
+ double_t result = std::round(value * resolutions[typeIndex] * 10.0) / 10.0;
values[typeIndex] = result;
continue;
}
diff --git a/lib/SS2K/src/sensors/FlywheelData.cpp b/lib/SS2K/src/sensors/FlywheelData.cpp
index 2fcc02b8..20c3a7f2 100644
--- a/lib/SS2K/src/sensors/FlywheelData.cpp
+++ b/lib/SS2K/src/sensors/FlywheelData.cpp
@@ -5,7 +5,7 @@
* SPDX-License-Identifier: GPL-2.0-only
*/
-#include "endian.h"
+#include "ByteUtils.h"
#include "sensors/FlywheelData.h"
bool FlywheelData::hasHeartRate() { return false; }
diff --git a/lib/SS2K/src/sensors/PelotonData.cpp b/lib/SS2K/src/sensors/PelotonData.cpp
index 70baeabe..b8e721e7 100644
--- a/lib/SS2K/src/sensors/PelotonData.cpp
+++ b/lib/SS2K/src/sensors/PelotonData.cpp
@@ -5,7 +5,7 @@
* SPDX-License-Identifier: GPL-2.0-only
*/
-#include "endian.h"
+#include "ByteUtils.h"
#include "sensors/PelotonData.h"
#include "Constants.h"
@@ -75,4 +75,4 @@ void PelotonData::decode(uint8_t *data, size_t length) {
// power = INT_MIN;
// hasData = false;
}
-}
\ No newline at end of file
+}
diff --git a/lib/SS2K/src/sensors/endian.c b/lib/SS2K/src/sensors/endian.c
deleted file mode 100644
index 6dd91995..00000000
--- a/lib/SS2K/src/sensors/endian.c
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * Copyright (C) 2020 Anthony Doud & Joel Baranick
- * All rights reserved
- *
- * SPDX-License-Identifier: GPL-2.0-only
- */
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-#include "endian.h"
-
-void
-put_le16(void *buf, uint16_t x)
-{
- uint8_t *u8ptr;
-
- u8ptr = buf;
- u8ptr[0] = (uint8_t)x;
- u8ptr[1] = (uint8_t)(x >> 8);
-}
-
-void
-put_le32(void *buf, uint32_t x)
-{
- uint8_t *u8ptr;
-
- u8ptr = buf;
- u8ptr[0] = (uint8_t)x;
- u8ptr[1] = (uint8_t)(x >> 8);
- u8ptr[2] = (uint8_t)(x >> 16);
- u8ptr[3] = (uint8_t)(x >> 24);
-}
-
-void
-put_le64(void *buf, uint64_t x)
-{
- uint8_t *u8ptr;
-
- u8ptr = buf;
- u8ptr[0] = (uint8_t)x;
- u8ptr[1] = (uint8_t)(x >> 8);
- u8ptr[2] = (uint8_t)(x >> 16);
- u8ptr[3] = (uint8_t)(x >> 24);
- u8ptr[4] = (uint8_t)(x >> 32);
- u8ptr[5] = (uint8_t)(x >> 40);
- u8ptr[6] = (uint8_t)(x >> 48);
- u8ptr[7] = (uint8_t)(x >> 56);
-}
-
-uint16_t
-get_le16(const void *buf)
-{
- const uint8_t *u8ptr;
- uint16_t x;
-
- u8ptr = buf;
- x = u8ptr[0];
- x |= (uint16_t)u8ptr[1] << 8;
-
- return x;
-}
-
-uint32_t
-get_le32(const void *buf)
-{
- const uint8_t *u8ptr;
- uint32_t x;
-
- u8ptr = buf;
- x = u8ptr[0];
- x |= (uint32_t)u8ptr[1] << 8;
- x |= (uint32_t)u8ptr[2] << 16;
- x |= (uint32_t)u8ptr[3] << 24;
-
- return x;
-}
-
-uint64_t
-get_le64(const void *buf)
-{
- const uint8_t *u8ptr;
- uint64_t x;
-
- u8ptr = buf;
- x = u8ptr[0];
- x |= (uint64_t)u8ptr[1] << 8;
- x |= (uint64_t)u8ptr[2] << 16;
- x |= (uint64_t)u8ptr[3] << 24;
- x |= (uint64_t)u8ptr[4] << 32;
- x |= (uint64_t)u8ptr[5] << 40;
- x |= (uint64_t)u8ptr[6] << 48;
- x |= (uint64_t)u8ptr[7] << 56;
-
- return x;
-}
-
-void
-put_be16(void *buf, uint16_t x)
-{
- uint8_t *u8ptr;
-
- u8ptr = buf;
- u8ptr[0] = (uint8_t)(x >> 8);
- u8ptr[1] = (uint8_t)x;
-}
-
-void
-put_be32(void *buf, uint32_t x)
-{
- uint8_t *u8ptr;
-
- u8ptr = buf;
- u8ptr[0] = (uint8_t)(x >> 24);
- u8ptr[1] = (uint8_t)(x >> 16);
- u8ptr[2] = (uint8_t)(x >> 8);
- u8ptr[3] = (uint8_t)x;
-}
-
-void
-put_be64(void *buf, uint64_t x)
-{
- uint8_t *u8ptr;
-
- u8ptr = buf;
- u8ptr[0] = (uint8_t)(x >> 56);
- u8ptr[1] = (uint8_t)(x >> 48);
- u8ptr[2] = (uint8_t)(x >> 40);
- u8ptr[3] = (uint8_t)(x >> 32);
- u8ptr[4] = (uint8_t)(x >> 24);
- u8ptr[5] = (uint8_t)(x >> 16);
- u8ptr[6] = (uint8_t)(x >> 8);
- u8ptr[7] = (uint8_t)x;
-}
-
-uint16_t
-get_be16(const void *buf)
-{
- const uint8_t *u8ptr;
- uint16_t x;
-
- u8ptr = buf;
- x = (uint16_t)u8ptr[0] << 8;
- x |= u8ptr[1];
-
- return x;
-}
-
-uint32_t
-get_be32(const void *buf)
-{
- const uint8_t *u8ptr;
- uint32_t x;
-
- u8ptr = buf;
- x = (uint32_t)u8ptr[0] << 24;
- x |= (uint32_t)u8ptr[1] << 16;
- x |= (uint32_t)u8ptr[2] << 8;
- x |= u8ptr[3];
-
- return x;
-}
-
-uint64_t
-get_be64(const void *buf)
-{
- const uint8_t *u8ptr;
- uint64_t x;
-
- u8ptr = buf;
- x = (uint64_t)u8ptr[0] << 56;
- x |= (uint64_t)u8ptr[1] << 48;
- x |= (uint64_t)u8ptr[2] << 40;
- x |= (uint64_t)u8ptr[3] << 32;
- x |= (uint64_t)u8ptr[4] << 24;
- x |= (uint64_t)u8ptr[5] << 16;
- x |= (uint64_t)u8ptr[6] << 8;
- x |= u8ptr[7];
-
- return x;
-}
-void
-swap_in_place(void *buf, int len)
-{
- uint8_t *u8ptr;
- uint8_t tmp;
- int i;
- int j;
-
- u8ptr = buf;
-
- for (i = 0, j = len - 1; i < j; i++, j--) {
- tmp = u8ptr[i];
-
- u8ptr[i] = u8ptr[j];
- u8ptr[j] = tmp;
- }
-}
-
-/* swap octets */
-void
-swap_buf(uint8_t *dst, const uint8_t *src, int len)
-{
- int i;
-
- for (i = 0; i < len; i++) {
- dst[len - 1 - i] = src[i];
- }
-}
diff --git a/lib/SS2K/src/sensors/endian.h b/lib/SS2K/src/sensors/endian.h
deleted file mode 100644
index c44ab69f..00000000
--- a/lib/SS2K/src/sensors/endian.h
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * Copyright (C) 2020 Anthony Doud & Joel Baranick
- * All rights reserved
- *
- * SPDX-License-Identifier: GPL-2.0-only
- */
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-#ifndef H_ENDIAN_
-#define H_ENDIAN_
-
-#include
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* Internal helpers */
-#ifndef os_bswap_64
-#define os_bswap_64(x) ((uint64_t) \
- ((((x) & 0xff00000000000000ull) >> 56) | \
- (((x) & 0x00ff000000000000ull) >> 40) | \
- (((x) & 0x0000ff0000000000ull) >> 24) | \
- (((x) & 0x000000ff00000000ull) >> 8) | \
- (((x) & 0x00000000ff000000ull) << 8) | \
- (((x) & 0x0000000000ff0000ull) << 24) | \
- (((x) & 0x000000000000ff00ull) << 40) | \
- (((x) & 0x00000000000000ffull) << 56)))
-#endif
-
-#ifndef os_bswap_32
-#define os_bswap_32(x) ((uint32_t) \
- ((((x) & 0xff000000) >> 24) | \
- (((x) & 0x00ff0000) >> 8) | \
- (((x) & 0x0000ff00) << 8) | \
- (((x) & 0x000000ff) << 24)))
-#endif
-
-#ifndef os_bswap_16
-#define os_bswap_16(x) ((uint16_t) \
- ((((x) & 0xff00) >> 8) | \
- (((x) & 0x00ff) << 8)))
-#endif
-
-#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
-
-#ifndef ntohll
-#define ntohll(x) ((uint64_t)(x))
-#endif
-
-#ifndef htonll
-#define htonll(x) ((uint64_t)(x))
-#endif
-
-#ifndef ntohl
-#define ntohl(x) ((uint32_t)(x))
-#endif
-
-#ifndef htonl
-#define htonl(x) ((uint32_t)(x))
-#endif
-
-#ifndef ntohs
-#define ntohs(x) ((uint16_t)(x))
-#endif
-
-#ifndef htons
-#define htons(x) ((uint16_t)(x))
-#endif
-
-#ifndef htobe16
-#define htobe16(x) ((uint16_t)(x))
-#endif
-
-#ifndef htole16
-#define htole16(x) os_bswap_16 (x)
-#endif
-
-#ifndef be16toh
-#define be16toh(x) ((uint16_t)(x))
-#endif
-
-#ifndef le16toh
-#define le16toh(x) os_bswap_16 (x)
-#endif
-
-#ifndef htobe32
-#define htobe32(x) ((uint32_t)(x))
-#endif
-
-#ifndef htole32
-#define htole32(x) os_bswap_32 (x)
-#endif
-
-#ifndef be32toh
-#define be32toh(x) ((uint32_t)(x))
-#endif
-
-#ifndef le32toh
-#define le32toh(x) os_bswap_32 (x)
-#endif
-
-#ifndef htobe64
-#define htobe64(x) ((uint64_t)(x))
-#endif
-
-#ifndef htole64
-#define htole64(x) os_bswap_64 (x)
-#endif
-
-#ifndef be64toh
-#define be64toh(x) ((uint64_t)(x))
-#endif
-
-#ifndef le64toh
-#define le64toh(x) os_bswap_64 (x)
-#endif
-
-#else
-
-#ifndef ntohll
-#define ntohll(x) os_bswap_64(x)
-#endif
-
-#ifndef htonll
-#define htonll ntohll
-#endif
-
-#ifndef ntohl
-#define ntohl(x) os_bswap_32(x)
-#endif
-
-#ifndef htonl
-#define htonl ntohl
-#endif
-
-#ifndef htons
-#define htons(x) os_bswap_16(x)
-#endif
-
-#ifndef ntohs
-#define ntohs htons
-#endif
-
-#ifndef htobe16
-#define htobe16(x) os_bswap_16(x)
-#endif
-
-#ifndef htole16
-#define htole16(x) ((uint16_t)(x))
-#endif
-
-#ifndef be16toh
-#define be16toh(x) os_bswap_16(x)
-#endif
-
-#ifndef le16toh
-#define le16toh(x) ((uint16_t)(x))
-#endif
-
-#ifndef htobe32
-#define htobe32(x) os_bswap_32(x)
-#endif
-
-#ifndef htole32
-#define htole32(x) ((uint32_t)(x))
-#endif
-
-#ifndef be32toh
-#define be32toh(x) os_bswap_32(x)
-#endif
-
-#ifndef le32toh
-#define le32toh(x) ((uint32_t)(x))
-#endif
-
-#ifndef htobe64
-#define htobe64(x) os_bswap_64(x)
-#endif
-
-#ifndef htole64
-#define htole64(x) ((uint64_t)(x))
-#endif
-
-#ifndef be64toh
-#define be64toh(x) os_bswap_64(x)
-#endif
-
-#ifndef le64toh
-#define le64toh(x) ((uint64_t)(x))
-#endif
-
-#endif
-
-void put_le16(void *buf, uint16_t x);
-void put_le24(void *buf, uint32_t x);
-void put_le32(void *buf, uint32_t x);
-void put_le64(void *buf, uint64_t x);
-uint16_t get_le16(const void *buf);
-uint32_t get_le24(const void *buf);
-uint32_t get_le32(const void *buf);
-uint64_t get_le64(const void *buf);
-void put_be16(void *buf, uint16_t x);
-void put_be24(void *buf, uint32_t x);
-void put_be32(void *buf, uint32_t x);
-void put_be64(void *buf, uint64_t x);
-uint16_t get_be16(const void *buf);
-uint32_t get_be24(const void *buf);
-uint32_t get_be32(const void *buf);
-uint64_t get_be64(const void *buf);
-void swap_in_place(void *buf, int len);
-void swap_buf(uint8_t *dst, const uint8_t *src, int len);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/platformio.ini b/platformio.ini
index 4ace3a0c..35c83759 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -50,7 +50,6 @@ custom_component_remove =
build_flags =
!python3 git_tag_macro.py
!python3 build_date_macro.py
- !python3 cert_updater.py
;-DCONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y
;-D CONFIG_SPI_SLAVE_ISR_IN_IRAM=n
;-D CONFIG_NEWLIB_NANO_FORMAT=y
@@ -131,6 +130,7 @@ build_flags =
${common.build_flags}
${board_esp32s3.build_flags}
-D__DEBUG__=1
+ -D SERIAL_CUSTOM_CHARACTERISTIC
debug_tool = esp-builtin
debug_init_break = tbreak setup
check_tool = cppcheck
@@ -146,9 +146,9 @@ check_skip_packages = true
[env:native]
platform = native
lib_deps =
- ArduinoFake
lib/ArduinoCompat
lib/SS2K
+ throwtheswitch/Unity @ ^2.6.1
build_flags =
-Wunused
-Wunused-function
diff --git a/sdkconfig.S3debug b/sdkconfig.S3debug
new file mode 100644
index 00000000..d3cbfcfa
--- /dev/null
+++ b/sdkconfig.S3debug
@@ -0,0 +1,5201 @@
+#
+# Automatically generated file. DO NOT EDIT.
+# Espressif IoT Development Framework (ESP-IDF) 5.5.5 Project Configuration
+#
+# default:
+CONFIG_SOC_ADC_SUPPORTED=y
+# default:
+CONFIG_SOC_UART_SUPPORTED=y
+# default:
+CONFIG_SOC_PCNT_SUPPORTED=y
+# default:
+CONFIG_SOC_PHY_SUPPORTED=y
+# default:
+CONFIG_SOC_WIFI_SUPPORTED=y
+# default:
+CONFIG_SOC_TWAI_SUPPORTED=y
+# default:
+CONFIG_SOC_GDMA_SUPPORTED=y
+# default:
+CONFIG_SOC_UHCI_SUPPORTED=y
+# default:
+CONFIG_SOC_AHB_GDMA_SUPPORTED=y
+# default:
+CONFIG_SOC_GPTIMER_SUPPORTED=y
+# default:
+CONFIG_SOC_LCDCAM_SUPPORTED=y
+# default:
+CONFIG_SOC_LCDCAM_CAM_SUPPORTED=y
+# default:
+CONFIG_SOC_LCDCAM_I80_LCD_SUPPORTED=y
+# default:
+CONFIG_SOC_LCDCAM_RGB_LCD_SUPPORTED=y
+# default:
+CONFIG_SOC_MCPWM_SUPPORTED=y
+# default:
+CONFIG_SOC_DEDICATED_GPIO_SUPPORTED=y
+# default:
+CONFIG_SOC_CACHE_SUPPORT_WRAP=y
+# default:
+CONFIG_SOC_ULP_SUPPORTED=y
+# default:
+CONFIG_SOC_ULP_FSM_SUPPORTED=y
+# default:
+CONFIG_SOC_RISCV_COPROC_SUPPORTED=y
+# default:
+CONFIG_SOC_BT_SUPPORTED=y
+# default:
+CONFIG_SOC_USB_OTG_SUPPORTED=y
+# default:
+CONFIG_SOC_USB_SERIAL_JTAG_SUPPORTED=y
+# default:
+CONFIG_SOC_CCOMP_TIMER_SUPPORTED=y
+# default:
+CONFIG_SOC_ASYNC_MEMCPY_SUPPORTED=y
+# default:
+CONFIG_SOC_SUPPORTS_SECURE_DL_MODE=y
+# default:
+CONFIG_SOC_EFUSE_KEY_PURPOSE_FIELD=y
+# default:
+CONFIG_SOC_EFUSE_SUPPORTED=y
+# default:
+CONFIG_SOC_SDMMC_HOST_SUPPORTED=y
+# default:
+CONFIG_SOC_RTC_FAST_MEM_SUPPORTED=y
+# default:
+CONFIG_SOC_RTC_SLOW_MEM_SUPPORTED=y
+# default:
+CONFIG_SOC_RTC_MEM_SUPPORTED=y
+# default:
+CONFIG_SOC_PSRAM_DMA_CAPABLE=y
+# default:
+CONFIG_SOC_XT_WDT_SUPPORTED=y
+# default:
+CONFIG_SOC_I2S_SUPPORTED=y
+# default:
+CONFIG_SOC_RMT_SUPPORTED=y
+# default:
+CONFIG_SOC_SDM_SUPPORTED=y
+# default:
+CONFIG_SOC_GPSPI_SUPPORTED=y
+# default:
+CONFIG_SOC_LEDC_SUPPORTED=y
+# default:
+CONFIG_SOC_I2C_SUPPORTED=y
+# default:
+CONFIG_SOC_SYSTIMER_SUPPORTED=y
+# default:
+CONFIG_SOC_SUPPORT_COEXISTENCE=y
+# default:
+CONFIG_SOC_TEMP_SENSOR_SUPPORTED=y
+# default:
+CONFIG_SOC_AES_SUPPORTED=y
+# default:
+CONFIG_SOC_MPI_SUPPORTED=y
+# default:
+CONFIG_SOC_SHA_SUPPORTED=y
+# default:
+CONFIG_SOC_HMAC_SUPPORTED=y
+# default:
+CONFIG_SOC_DIG_SIGN_SUPPORTED=y
+# default:
+CONFIG_SOC_FLASH_ENC_SUPPORTED=y
+# default:
+CONFIG_SOC_SECURE_BOOT_SUPPORTED=y
+# default:
+CONFIG_SOC_MEMPROT_SUPPORTED=y
+# default:
+CONFIG_SOC_TOUCH_SENSOR_SUPPORTED=y
+# default:
+CONFIG_SOC_BOD_SUPPORTED=y
+# default:
+CONFIG_SOC_CLK_TREE_SUPPORTED=y
+# default:
+CONFIG_SOC_MPU_SUPPORTED=y
+# default:
+CONFIG_SOC_WDT_SUPPORTED=y
+# default:
+CONFIG_SOC_SPI_FLASH_SUPPORTED=y
+# default:
+CONFIG_SOC_RNG_SUPPORTED=y
+# default:
+CONFIG_SOC_LIGHT_SLEEP_SUPPORTED=y
+# default:
+CONFIG_SOC_DEEP_SLEEP_SUPPORTED=y
+# default:
+CONFIG_SOC_LP_PERIPH_SHARE_INTERRUPT=y
+# default:
+CONFIG_SOC_PM_SUPPORTED=y
+# default:
+CONFIG_SOC_SIMD_INSTRUCTION_SUPPORTED=y
+# default:
+CONFIG_SOC_XTAL_SUPPORT_40M=y
+# default:
+CONFIG_SOC_APPCPU_HAS_CLOCK_GATING_BUG=y
+# default:
+CONFIG_SOC_ADC_RTC_CTRL_SUPPORTED=y
+# default:
+CONFIG_SOC_ADC_DIG_CTRL_SUPPORTED=y
+# default:
+CONFIG_SOC_ADC_ARBITER_SUPPORTED=y
+# default:
+CONFIG_SOC_ADC_DIG_IIR_FILTER_SUPPORTED=y
+# default:
+CONFIG_SOC_ADC_MONITOR_SUPPORTED=y
+# default:
+CONFIG_SOC_ADC_DMA_SUPPORTED=y
+# default:
+CONFIG_SOC_ADC_PERIPH_NUM=2
+# default:
+CONFIG_SOC_ADC_MAX_CHANNEL_NUM=10
+# default:
+CONFIG_SOC_ADC_ATTEN_NUM=4
+# default:
+CONFIG_SOC_ADC_DIGI_CONTROLLER_NUM=2
+# default:
+CONFIG_SOC_ADC_PATT_LEN_MAX=24
+# default:
+CONFIG_SOC_ADC_DIGI_MIN_BITWIDTH=12
+# default:
+CONFIG_SOC_ADC_DIGI_MAX_BITWIDTH=12
+# default:
+CONFIG_SOC_ADC_DIGI_RESULT_BYTES=4
+# default:
+CONFIG_SOC_ADC_DIGI_DATA_BYTES_PER_CONV=4
+# default:
+CONFIG_SOC_ADC_DIGI_IIR_FILTER_NUM=2
+# default:
+CONFIG_SOC_ADC_DIGI_MONITOR_NUM=2
+# default:
+CONFIG_SOC_ADC_SAMPLE_FREQ_THRES_HIGH=83333
+# default:
+CONFIG_SOC_ADC_SAMPLE_FREQ_THRES_LOW=611
+# default:
+CONFIG_SOC_ADC_RTC_MIN_BITWIDTH=12
+# default:
+CONFIG_SOC_ADC_RTC_MAX_BITWIDTH=12
+# default:
+CONFIG_SOC_ADC_CALIBRATION_V1_SUPPORTED=y
+# default:
+CONFIG_SOC_ADC_SELF_HW_CALI_SUPPORTED=y
+# default:
+CONFIG_SOC_ADC_SHARED_POWER=y
+# default:
+CONFIG_SOC_APB_BACKUP_DMA=y
+# default:
+CONFIG_SOC_BROWNOUT_RESET_SUPPORTED=y
+# default:
+CONFIG_SOC_CACHE_WRITEBACK_SUPPORTED=y
+# default:
+CONFIG_SOC_CACHE_FREEZE_SUPPORTED=y
+# default:
+CONFIG_SOC_CACHE_ACS_INVALID_STATE_ON_PANIC=y
+# default:
+CONFIG_SOC_CPU_CORES_NUM=2
+# default:
+CONFIG_SOC_CPU_INTR_NUM=32
+# default:
+CONFIG_SOC_CPU_HAS_FPU=y
+# default:
+CONFIG_SOC_HP_CPU_HAS_MULTIPLE_CORES=y
+# default:
+CONFIG_SOC_CPU_BREAKPOINTS_NUM=2
+# default:
+CONFIG_SOC_CPU_WATCHPOINTS_NUM=2
+# default:
+CONFIG_SOC_CPU_WATCHPOINT_MAX_REGION_SIZE=0x40
+# default:
+CONFIG_SOC_SIMD_PREFERRED_DATA_ALIGNMENT=16
+# default:
+CONFIG_SOC_DS_SIGNATURE_MAX_BIT_LEN=4096
+# default:
+CONFIG_SOC_DS_KEY_PARAM_MD_IV_LENGTH=16
+# default:
+CONFIG_SOC_DS_KEY_CHECK_MAX_WAIT_US=1100
+# default:
+CONFIG_SOC_AHB_GDMA_VERSION=1
+# default:
+CONFIG_SOC_GDMA_NUM_GROUPS_MAX=1
+# default:
+CONFIG_SOC_GDMA_PAIRS_PER_GROUP=5
+# default:
+CONFIG_SOC_GDMA_PAIRS_PER_GROUP_MAX=5
+# default:
+CONFIG_SOC_AHB_GDMA_SUPPORT_PSRAM=y
+# default:
+CONFIG_SOC_GPIO_PORT=1
+# default:
+CONFIG_SOC_GPIO_PIN_COUNT=49
+# default:
+CONFIG_SOC_GPIO_SUPPORT_PIN_GLITCH_FILTER=y
+# default:
+CONFIG_SOC_GPIO_FILTER_CLK_SUPPORT_APB=y
+# default:
+CONFIG_SOC_GPIO_SUPPORT_RTC_INDEPENDENT=y
+# default:
+CONFIG_SOC_GPIO_SUPPORT_FORCE_HOLD=y
+# default:
+CONFIG_SOC_GPIO_VALID_GPIO_MASK=0x1FFFFFFFFFFFF
+# default:
+CONFIG_SOC_GPIO_IN_RANGE_MAX=48
+# default:
+CONFIG_SOC_GPIO_OUT_RANGE_MAX=48
+# default:
+CONFIG_SOC_GPIO_VALID_DIGITAL_IO_PAD_MASK=0x0001FFFFFC000000
+# default:
+CONFIG_SOC_GPIO_CLOCKOUT_BY_IO_MUX=y
+# default:
+CONFIG_SOC_GPIO_CLOCKOUT_CHANNEL_NUM=3
+# default:
+CONFIG_SOC_DEDIC_GPIO_OUT_CHANNELS_NUM=8
+# default:
+CONFIG_SOC_DEDIC_GPIO_IN_CHANNELS_NUM=8
+# default:
+CONFIG_SOC_DEDIC_GPIO_OUT_AUTO_ENABLE=y
+# default:
+CONFIG_SOC_I2C_NUM=2
+# default:
+CONFIG_SOC_HP_I2C_NUM=2
+# default:
+CONFIG_SOC_I2C_FIFO_LEN=32
+# default:
+CONFIG_SOC_I2C_CMD_REG_NUM=8
+# default:
+CONFIG_SOC_I2C_SUPPORT_SLAVE=y
+# default:
+CONFIG_SOC_I2C_SUPPORT_HW_CLR_BUS=y
+# default:
+CONFIG_SOC_I2C_SUPPORT_XTAL=y
+# default:
+CONFIG_SOC_I2C_SUPPORT_RTC=y
+# default:
+CONFIG_SOC_I2C_SUPPORT_10BIT_ADDR=y
+# default:
+CONFIG_SOC_I2C_SLAVE_SUPPORT_BROADCAST=y
+# default:
+CONFIG_SOC_I2C_SLAVE_SUPPORT_I2CRAM_ACCESS=y
+# default:
+CONFIG_SOC_I2C_SLAVE_CAN_GET_STRETCH_CAUSE=y
+# default:
+CONFIG_SOC_I2S_NUM=2
+# default:
+CONFIG_SOC_I2S_HW_VERSION_2=y
+# default:
+CONFIG_SOC_I2S_SUPPORTS_XTAL=y
+# default:
+CONFIG_SOC_I2S_SUPPORTS_PLL_F160M=y
+# default:
+CONFIG_SOC_I2S_SUPPORTS_PCM=y
+# default:
+CONFIG_SOC_I2S_SUPPORTS_PDM=y
+# default:
+CONFIG_SOC_I2S_SUPPORTS_PDM_TX=y
+# default:
+CONFIG_SOC_I2S_SUPPORTS_PCM2PDM=y
+# default:
+CONFIG_SOC_I2S_SUPPORTS_PDM_RX=y
+# default:
+CONFIG_SOC_I2S_SUPPORTS_PDM2PCM=y
+# default:
+CONFIG_SOC_I2S_PDM_MAX_TX_LINES=2
+# default:
+CONFIG_SOC_I2S_PDM_MAX_RX_LINES=4
+# default:
+CONFIG_SOC_I2S_SUPPORTS_TDM=y
+# default:
+CONFIG_SOC_LEDC_SUPPORT_APB_CLOCK=y
+# default:
+CONFIG_SOC_LEDC_SUPPORT_XTAL_CLOCK=y
+# default:
+CONFIG_SOC_LEDC_TIMER_NUM=4
+# default:
+CONFIG_SOC_LEDC_CHANNEL_NUM=8
+# default:
+CONFIG_SOC_LEDC_TIMER_BIT_WIDTH=14
+# default:
+CONFIG_SOC_LEDC_SUPPORT_FADE_STOP=y
+# default:
+CONFIG_SOC_MCPWM_GROUPS=2
+# default:
+CONFIG_SOC_MCPWM_TIMERS_PER_GROUP=3
+# default:
+CONFIG_SOC_MCPWM_OPERATORS_PER_GROUP=3
+# default:
+CONFIG_SOC_MCPWM_COMPARATORS_PER_OPERATOR=2
+# default:
+CONFIG_SOC_MCPWM_GENERATORS_PER_OPERATOR=2
+# default:
+CONFIG_SOC_MCPWM_TRIGGERS_PER_OPERATOR=2
+# default:
+CONFIG_SOC_MCPWM_GPIO_FAULTS_PER_GROUP=3
+# default:
+CONFIG_SOC_MCPWM_CAPTURE_TIMERS_PER_GROUP=y
+# default:
+CONFIG_SOC_MCPWM_CAPTURE_CHANNELS_PER_TIMER=3
+# default:
+CONFIG_SOC_MCPWM_GPIO_SYNCHROS_PER_GROUP=3
+# default:
+CONFIG_SOC_MCPWM_SWSYNC_CAN_PROPAGATE=y
+# default:
+CONFIG_SOC_MMU_LINEAR_ADDRESS_REGION_NUM=1
+# default:
+CONFIG_SOC_MMU_PERIPH_NUM=1
+# default:
+CONFIG_SOC_MPU_MIN_REGION_SIZE=0x20000000
+# default:
+CONFIG_SOC_MPU_REGIONS_MAX_NUM=8
+# default:
+CONFIG_SOC_PCNT_GROUPS=1
+# default:
+CONFIG_SOC_PCNT_UNITS_PER_GROUP=4
+# default:
+CONFIG_SOC_PCNT_CHANNELS_PER_UNIT=2
+# default:
+CONFIG_SOC_PCNT_THRES_POINT_PER_UNIT=2
+# default:
+CONFIG_SOC_RMT_GROUPS=1
+# default:
+CONFIG_SOC_RMT_TX_CANDIDATES_PER_GROUP=4
+# default:
+CONFIG_SOC_RMT_RX_CANDIDATES_PER_GROUP=4
+# default:
+CONFIG_SOC_RMT_CHANNELS_PER_GROUP=8
+# default:
+CONFIG_SOC_RMT_MEM_WORDS_PER_CHANNEL=48
+# default:
+CONFIG_SOC_RMT_SUPPORT_RX_PINGPONG=y
+# default:
+CONFIG_SOC_RMT_SUPPORT_RX_DEMODULATION=y
+# default:
+CONFIG_SOC_RMT_SUPPORT_ASYNC_STOP=y
+# default:
+CONFIG_SOC_RMT_SUPPORT_TX_LOOP_COUNT=y
+# default:
+CONFIG_SOC_RMT_SUPPORT_TX_LOOP_AUTO_STOP=y
+# default:
+CONFIG_SOC_RMT_SUPPORT_TX_SYNCHRO=y
+# default:
+CONFIG_SOC_RMT_SUPPORT_TX_CARRIER_DATA_ONLY=y
+# default:
+CONFIG_SOC_RMT_SUPPORT_XTAL=y
+# default:
+CONFIG_SOC_RMT_SUPPORT_RC_FAST=y
+# default:
+CONFIG_SOC_RMT_SUPPORT_APB=y
+# default:
+CONFIG_SOC_RMT_SUPPORT_DMA=y
+# default:
+CONFIG_SOC_LCD_I80_SUPPORTED=y
+# default:
+CONFIG_SOC_LCD_RGB_SUPPORTED=y
+# default:
+CONFIG_SOC_LCD_I80_BUSES=1
+# default:
+CONFIG_SOC_LCD_RGB_PANELS=1
+# default:
+CONFIG_SOC_LCD_I80_BUS_WIDTH=16
+# default:
+CONFIG_SOC_LCD_RGB_DATA_WIDTH=16
+# default:
+CONFIG_SOC_LCD_SUPPORT_RGB_YUV_CONV=y
+# default:
+CONFIG_SOC_LCDCAM_I80_NUM_BUSES=1
+# default:
+CONFIG_SOC_LCDCAM_I80_BUS_WIDTH=16
+# default:
+CONFIG_SOC_LCDCAM_RGB_NUM_PANELS=1
+# default:
+CONFIG_SOC_LCDCAM_RGB_DATA_WIDTH=16
+# default:
+CONFIG_SOC_RTC_CNTL_CPU_PD_DMA_BUS_WIDTH=128
+# default:
+CONFIG_SOC_RTC_CNTL_CPU_PD_REG_FILE_NUM=549
+# default:
+CONFIG_SOC_RTC_CNTL_TAGMEM_PD_DMA_BUS_WIDTH=128
+# default:
+CONFIG_SOC_RTC_CNTL_NEEDS_ATOMIC_ACCESS=y
+# default:
+CONFIG_SOC_RTCIO_PIN_COUNT=22
+# default:
+CONFIG_SOC_RTCIO_INPUT_OUTPUT_SUPPORTED=y
+# default:
+CONFIG_SOC_RTCIO_HOLD_SUPPORTED=y
+# default:
+CONFIG_SOC_RTCIO_WAKE_SUPPORTED=y
+# default:
+CONFIG_SOC_LP_IO_CLOCK_IS_INDEPENDENT=y
+# default:
+CONFIG_SOC_SDM_GROUPS=1
+# default:
+CONFIG_SOC_SDM_CHANNELS_PER_GROUP=8
+# default:
+CONFIG_SOC_SDM_CLK_SUPPORT_APB=y
+# default:
+CONFIG_SOC_SPI_PERIPH_NUM=3
+# default:
+CONFIG_SOC_SPI_MAX_CS_NUM=6
+# default:
+CONFIG_SOC_SPI_MAXIMUM_BUFFER_SIZE=64
+# default:
+CONFIG_SOC_SPI_SUPPORT_DDRCLK=y
+# default:
+CONFIG_SOC_SPI_SLAVE_SUPPORT_SEG_TRANS=y
+# default:
+CONFIG_SOC_SPI_SUPPORT_CD_SIG=y
+# default:
+CONFIG_SOC_SPI_SUPPORT_CONTINUOUS_TRANS=y
+# default:
+CONFIG_SOC_SPI_SUPPORT_SLAVE_HD_VER2=y
+# default:
+CONFIG_SOC_SPI_SUPPORT_CLK_APB=y
+# default:
+CONFIG_SOC_SPI_SUPPORT_CLK_XTAL=y
+# default:
+CONFIG_SOC_SPI_PERIPH_SUPPORT_CONTROL_DUMMY_OUT=y
+# default:
+CONFIG_SOC_MEMSPI_IS_INDEPENDENT=y
+# default:
+CONFIG_SOC_SPI_MAX_PRE_DIVIDER=16
+# default:
+CONFIG_SOC_SPI_SUPPORT_OCT=y
+# default:
+CONFIG_SOC_SPI_SCT_SUPPORTED=y
+# default:
+CONFIG_SOC_SPI_SCT_REG_NUM=14
+# default:
+CONFIG_SOC_SPI_SCT_BUFFER_NUM_MAX=y
+# default:
+CONFIG_SOC_SPI_SCT_CONF_BITLEN_MAX=0x3FFFA
+# default:
+CONFIG_SOC_SPIRAM_SUPPORTED=y
+# default:
+CONFIG_SOC_SPIRAM_XIP_SUPPORTED=y
+# default:
+CONFIG_SOC_SYSTIMER_COUNTER_NUM=2
+# default:
+CONFIG_SOC_SYSTIMER_ALARM_NUM=3
+# default:
+CONFIG_SOC_SYSTIMER_BIT_WIDTH_LO=32
+# default:
+CONFIG_SOC_SYSTIMER_BIT_WIDTH_HI=20
+# default:
+CONFIG_SOC_SYSTIMER_FIXED_DIVIDER=y
+# default:
+CONFIG_SOC_SYSTIMER_INT_LEVEL=y
+# default:
+CONFIG_SOC_SYSTIMER_ALARM_MISS_COMPENSATE=y
+# default:
+CONFIG_SOC_TIMER_GROUPS=2
+# default:
+CONFIG_SOC_TIMER_GROUP_TIMERS_PER_GROUP=2
+# default:
+CONFIG_SOC_TIMER_GROUP_COUNTER_BIT_WIDTH=54
+# default:
+CONFIG_SOC_TIMER_GROUP_SUPPORT_XTAL=y
+# default:
+CONFIG_SOC_TIMER_GROUP_SUPPORT_APB=y
+# default:
+CONFIG_SOC_TIMER_GROUP_TOTAL_TIMERS=4
+# default:
+CONFIG_SOC_LP_TIMER_BIT_WIDTH_LO=32
+# default:
+CONFIG_SOC_LP_TIMER_BIT_WIDTH_HI=16
+# default:
+CONFIG_SOC_TOUCH_SENSOR_VERSION=2
+# default:
+CONFIG_SOC_TOUCH_SENSOR_NUM=15
+# default:
+CONFIG_SOC_TOUCH_MIN_CHAN_ID=1
+# default:
+CONFIG_SOC_TOUCH_MAX_CHAN_ID=14
+# default:
+CONFIG_SOC_TOUCH_SUPPORT_BENCHMARK=y
+# default:
+CONFIG_SOC_TOUCH_SUPPORT_SLEEP_WAKEUP=y
+# default:
+CONFIG_SOC_TOUCH_SUPPORT_WATERPROOF=y
+# default:
+CONFIG_SOC_TOUCH_SUPPORT_PROX_SENSING=y
+# default:
+CONFIG_SOC_TOUCH_SUPPORT_DENOISE_CHAN=y
+# default:
+CONFIG_SOC_TOUCH_PROXIMITY_CHANNEL_NUM=3
+# default:
+CONFIG_SOC_TOUCH_PROXIMITY_MEAS_DONE_SUPPORTED=y
+# default:
+CONFIG_SOC_TOUCH_SAMPLE_CFG_NUM=1
+# default:
+CONFIG_SOC_TWAI_CONTROLLER_NUM=1
+# default:
+CONFIG_SOC_TWAI_MASK_FILTER_NUM=1
+# default:
+CONFIG_SOC_TWAI_CLK_SUPPORT_APB=y
+# default:
+CONFIG_SOC_TWAI_BRP_MIN=2
+# default:
+CONFIG_SOC_TWAI_BRP_MAX=16384
+# default:
+CONFIG_SOC_TWAI_SUPPORTS_RX_STATUS=y
+# default:
+CONFIG_SOC_UART_NUM=3
+# default:
+CONFIG_SOC_UART_HP_NUM=3
+# default:
+CONFIG_SOC_UART_FIFO_LEN=128
+# default:
+CONFIG_SOC_UART_BITRATE_MAX=5000000
+# default:
+CONFIG_SOC_UART_SUPPORT_FSM_TX_WAIT_SEND=y
+# default:
+CONFIG_SOC_UART_SUPPORT_WAKEUP_INT=y
+# default:
+CONFIG_SOC_UART_SUPPORT_APB_CLK=y
+# default:
+CONFIG_SOC_UART_SUPPORT_RTC_CLK=y
+# default:
+CONFIG_SOC_UART_SUPPORT_XTAL_CLK=y
+# default:
+CONFIG_SOC_UART_WAKEUP_SUPPORT_ACTIVE_THRESH_MODE=y
+# default:
+CONFIG_SOC_UHCI_NUM=1
+# default:
+CONFIG_SOC_USB_OTG_PERIPH_NUM=1
+# default:
+CONFIG_SOC_USB_FSLS_PHY_NUM=1
+# default:
+CONFIG_SOC_SHA_DMA_MAX_BUFFER_SIZE=3968
+# default:
+CONFIG_SOC_SHA_SUPPORT_DMA=y
+# default:
+CONFIG_SOC_SHA_SUPPORT_RESUME=y
+# default:
+CONFIG_SOC_SHA_GDMA=y
+# default:
+CONFIG_SOC_SHA_SUPPORT_SHA1=y
+# default:
+CONFIG_SOC_SHA_SUPPORT_SHA224=y
+# default:
+CONFIG_SOC_SHA_SUPPORT_SHA256=y
+# default:
+CONFIG_SOC_SHA_SUPPORT_SHA384=y
+# default:
+CONFIG_SOC_SHA_SUPPORT_SHA512=y
+# default:
+CONFIG_SOC_SHA_SUPPORT_SHA512_224=y
+# default:
+CONFIG_SOC_SHA_SUPPORT_SHA512_256=y
+# default:
+CONFIG_SOC_SHA_SUPPORT_SHA512_T=y
+# default:
+CONFIG_SOC_MPI_MEM_BLOCKS_NUM=4
+# default:
+CONFIG_SOC_MPI_OPERATIONS_NUM=3
+# default:
+CONFIG_SOC_RSA_MAX_BIT_LEN=4096
+# default:
+CONFIG_SOC_AES_SUPPORT_DMA=y
+# default:
+CONFIG_SOC_AES_GDMA=y
+# default:
+CONFIG_SOC_AES_SUPPORT_AES_128=y
+# default:
+CONFIG_SOC_AES_SUPPORT_AES_256=y
+# default:
+CONFIG_SOC_PM_SUPPORT_EXT0_WAKEUP=y
+# default:
+CONFIG_SOC_PM_SUPPORT_EXT1_WAKEUP=y
+# default:
+CONFIG_SOC_PM_SUPPORT_EXT_WAKEUP=y
+# default:
+CONFIG_SOC_PM_SUPPORT_WIFI_WAKEUP=y
+# default:
+CONFIG_SOC_PM_SUPPORT_BT_WAKEUP=y
+# default:
+CONFIG_SOC_PM_SUPPORT_TOUCH_SENSOR_WAKEUP=y
+# default:
+CONFIG_SOC_PM_SUPPORT_CPU_PD=y
+# default:
+CONFIG_SOC_PM_SUPPORT_TAGMEM_PD=y
+# default:
+CONFIG_SOC_PM_SUPPORT_RTC_PERIPH_PD=y
+# default:
+CONFIG_SOC_PM_SUPPORT_RC_FAST_PD=y
+# default:
+CONFIG_SOC_PM_SUPPORT_VDDSDIO_PD=y
+# default:
+CONFIG_SOC_PM_SUPPORT_MAC_BB_PD=y
+# default:
+CONFIG_SOC_PM_SUPPORT_MODEM_PD=y
+# default:
+CONFIG_SOC_CONFIGURABLE_VDDSDIO_SUPPORTED=y
+# default:
+CONFIG_SOC_PM_SUPPORT_DEEPSLEEP_CHECK_STUB_ONLY=y
+# default:
+CONFIG_SOC_PM_CPU_RETENTION_BY_RTCCNTL=y
+# default:
+CONFIG_SOC_PM_MODEM_RETENTION_BY_BACKUPDMA=y
+# default:
+CONFIG_SOC_PM_MODEM_PD_BY_SW=y
+# default:
+CONFIG_SOC_CLK_RC_FAST_D256_SUPPORTED=y
+# default:
+CONFIG_SOC_RTC_SLOW_CLK_SUPPORT_RC_FAST_D256=y
+# default:
+CONFIG_SOC_CLK_RC_FAST_SUPPORT_CALIBRATION=y
+# default:
+CONFIG_SOC_CLK_XTAL32K_SUPPORTED=y
+# default:
+CONFIG_SOC_CLK_LP_FAST_SUPPORT_XTAL_D2=y
+# default:
+CONFIG_SOC_EFUSE_DIS_DOWNLOAD_ICACHE=y
+# default:
+CONFIG_SOC_EFUSE_DIS_DOWNLOAD_DCACHE=y
+# default:
+CONFIG_SOC_EFUSE_HARD_DIS_JTAG=y
+# default:
+CONFIG_SOC_EFUSE_DIS_USB_JTAG=y
+# default:
+CONFIG_SOC_EFUSE_SOFT_DIS_JTAG=y
+# default:
+CONFIG_SOC_EFUSE_DIS_DIRECT_BOOT=y
+# default:
+CONFIG_SOC_EFUSE_DIS_ICACHE=y
+# default:
+CONFIG_SOC_EFUSE_BLOCK9_KEY_PURPOSE_QUIRK=y
+# default:
+CONFIG_SOC_SECURE_BOOT_V2_RSA=y
+# default:
+CONFIG_SOC_EFUSE_SECURE_BOOT_KEY_DIGESTS=3
+# default:
+CONFIG_SOC_EFUSE_REVOKE_BOOT_KEY_DIGESTS=y
+# default:
+CONFIG_SOC_SUPPORT_SECURE_BOOT_REVOKE_KEY=y
+# default:
+CONFIG_SOC_FLASH_ENCRYPTED_XTS_AES_BLOCK_MAX=64
+# default:
+CONFIG_SOC_FLASH_ENCRYPTION_XTS_AES=y
+# default:
+CONFIG_SOC_FLASH_ENCRYPTION_XTS_AES_OPTIONS=y
+# default:
+CONFIG_SOC_FLASH_ENCRYPTION_XTS_AES_128=y
+# default:
+CONFIG_SOC_FLASH_ENCRYPTION_XTS_AES_256=y
+# default:
+CONFIG_SOC_MEMPROT_CPU_PREFETCH_PAD_SIZE=16
+# default:
+CONFIG_SOC_MEMPROT_MEM_ALIGN_SIZE=256
+# default:
+CONFIG_SOC_PHY_DIG_REGS_MEM_SIZE=21
+# default:
+CONFIG_SOC_MAC_BB_PD_MEM_SIZE=192
+# default:
+CONFIG_SOC_WIFI_LIGHT_SLEEP_CLK_WIDTH=12
+# default:
+CONFIG_SOC_SPI_MEM_SUPPORT_AUTO_WAIT_IDLE=y
+# default:
+CONFIG_SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND=y
+# default:
+CONFIG_SOC_SPI_MEM_SUPPORT_AUTO_RESUME=y
+# default:
+CONFIG_SOC_SPI_MEM_SUPPORT_SW_SUSPEND=y
+# default:
+CONFIG_SOC_SPI_MEM_SUPPORT_FLASH_OPI_MODE=y
+# default:
+CONFIG_SOC_SPI_MEM_SUPPORT_TIMING_TUNING=y
+# default:
+CONFIG_SOC_SPI_MEM_SUPPORT_CONFIG_GPIO_BY_EFUSE=y
+# default:
+CONFIG_SOC_SPI_MEM_SUPPORT_WRAP=y
+# default:
+CONFIG_SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY=y
+# default:
+CONFIG_SOC_MEMSPI_CORE_CLK_SHARED_WITH_PSRAM=y
+# default:
+CONFIG_SOC_SPI_MEM_SUPPORT_CACHE_32BIT_ADDR_MAP=y
+# default:
+CONFIG_SOC_SPI_MEM_FLASH_SUPPORT_HPM=y
+# default:
+CONFIG_SOC_MEMSPI_ENCRYPTION_ALIGNMENT=16
+# default:
+CONFIG_SOC_COEX_HW_PTI=y
+# default:
+CONFIG_SOC_EXTERNAL_COEX_LEADER_TX_LINE=y
+# default:
+CONFIG_SOC_SDMMC_USE_GPIO_MATRIX=y
+# default:
+CONFIG_SOC_SDMMC_NUM_SLOTS=2
+# default:
+CONFIG_SOC_SDMMC_SUPPORT_XTAL_CLOCK=y
+# default:
+CONFIG_SOC_SDMMC_DELAY_PHASE_NUM=4
+# default:
+CONFIG_SOC_TEMPERATURE_SENSOR_SUPPORT_FAST_RC=y
+# default:
+CONFIG_SOC_WIFI_HW_TSF=y
+# default:
+CONFIG_SOC_WIFI_FTM_SUPPORT=y
+# default:
+CONFIG_SOC_WIFI_GCMP_SUPPORT=y
+# default:
+CONFIG_SOC_WIFI_WAPI_SUPPORT=y
+# default:
+CONFIG_SOC_WIFI_TXOP_SUPPORT=y
+# default:
+CONFIG_SOC_WIFI_CSI_SUPPORT=y
+# default:
+CONFIG_SOC_WIFI_MESH_SUPPORT=y
+# default:
+CONFIG_SOC_WIFI_SUPPORT_VARIABLE_BEACON_WINDOW=y
+# default:
+CONFIG_SOC_WIFI_PHY_NEEDS_USB_WORKAROUND=y
+# default:
+CONFIG_SOC_BLE_SUPPORTED=y
+# default:
+CONFIG_SOC_BLE_MESH_SUPPORTED=y
+# default:
+CONFIG_SOC_BLE_50_SUPPORTED=y
+# default:
+CONFIG_SOC_BLE_DEVICE_PRIVACY_SUPPORTED=y
+# default:
+CONFIG_SOC_BLUFI_SUPPORTED=y
+# default:
+CONFIG_SOC_ULP_HAS_ADC=y
+# default:
+CONFIG_SOC_PHY_COMBO_MODULE=y
+# default:
+CONFIG_SOC_LCDCAM_CAM_SUPPORT_RGB_YUV_CONV=y
+# default:
+CONFIG_SOC_LCDCAM_CAM_PERIPH_NUM=1
+# default:
+CONFIG_SOC_LCDCAM_CAM_DATA_WIDTH_MAX=16
+# default:
+CONFIG_IDF_CMAKE=y
+# default:
+CONFIG_IDF_TOOLCHAIN="gcc"
+# default:
+CONFIG_IDF_TOOLCHAIN_GCC=y
+# default:
+CONFIG_IDF_TARGET_ARCH_XTENSA=y
+# default:
+CONFIG_IDF_TARGET_ARCH="xtensa"
+# default:
+CONFIG_IDF_TARGET="esp32s3"
+# default:
+CONFIG_IDF_INIT_VERSION="5.5.5"
+# default:
+CONFIG_IDF_TARGET_ESP32S3=y
+# default:
+CONFIG_IDF_FIRMWARE_CHIP_ID=0x0009
+
+#
+# Build type
+#
+# default:
+CONFIG_APP_BUILD_TYPE_APP_2NDBOOT=y
+# default:
+# CONFIG_APP_BUILD_TYPE_RAM is not set
+# default:
+CONFIG_APP_BUILD_GENERATE_BINARIES=y
+# default:
+CONFIG_APP_BUILD_BOOTLOADER=y
+# default:
+CONFIG_APP_BUILD_USE_FLASH_SECTIONS=y
+# default:
+# CONFIG_APP_REPRODUCIBLE_BUILD is not set
+# default:
+# CONFIG_APP_NO_BLOBS is not set
+# end of Build type
+
+#
+# Bootloader config
+#
+
+#
+# Bootloader manager
+#
+# default:
+CONFIG_BOOTLOADER_COMPILE_TIME_DATE=y
+# default:
+CONFIG_BOOTLOADER_PROJECT_VER=1
+# end of Bootloader manager
+
+#
+# Application Rollback
+#
+# default:
+# CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE is not set
+# end of Application Rollback
+
+#
+# Recovery Bootloader and Rollback
+#
+# end of Recovery Bootloader and Rollback
+
+# default:
+CONFIG_BOOTLOADER_OFFSET_IN_FLASH=0x0
+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
+#
+# default:
+CONFIG_BOOTLOADER_LOG_VERSION_1=y
+# default:
+CONFIG_BOOTLOADER_LOG_VERSION=1
+# default:
+# CONFIG_BOOTLOADER_LOG_LEVEL_NONE is not set
+# default:
+# CONFIG_BOOTLOADER_LOG_LEVEL_ERROR is not set
+# default:
+# CONFIG_BOOTLOADER_LOG_LEVEL_WARN is not set
+# default:
+CONFIG_BOOTLOADER_LOG_LEVEL_INFO=y
+# default:
+# CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG is not set
+# default:
+# CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE is not set
+# default:
+CONFIG_BOOTLOADER_LOG_LEVEL=3
+
+#
+# Format
+#
+# default:
+# CONFIG_BOOTLOADER_LOG_COLORS is not set
+# default:
+CONFIG_BOOTLOADER_LOG_TIMESTAMP_SOURCE_CPU_TICKS=y
+# end of Format
+
+#
+# Settings
+#
+# default:
+CONFIG_BOOTLOADER_LOG_MODE_TEXT_EN=y
+# default:
+CONFIG_BOOTLOADER_LOG_MODE_TEXT=y
+# end of Settings
+# end of Log
+
+#
+# Serial Flash Configurations
+#
+# default:
+# CONFIG_BOOTLOADER_FLASH_DC_AWARE is not set
+# default:
+CONFIG_BOOTLOADER_FLASH_XMC_SUPPORT=y
+# end of Serial Flash Configurations
+
+# default:
+CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_9V=y
+# default:
+# CONFIG_BOOTLOADER_FACTORY_RESET is not set
+# default:
+# CONFIG_BOOTLOADER_APP_TEST is not set
+# default:
+CONFIG_BOOTLOADER_REGION_PROTECTION_ENABLE=y
+# default:
+CONFIG_BOOTLOADER_WDT_ENABLE=y
+# default:
+# CONFIG_BOOTLOADER_WDT_DISABLE_IN_USER_CODE is not set
+# default:
+CONFIG_BOOTLOADER_WDT_TIME_MS=9000
+# default:
+# CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP is not set
+# default:
+# CONFIG_BOOTLOADER_SKIP_VALIDATE_ON_POWER_ON is not set
+# default:
+# CONFIG_BOOTLOADER_SKIP_VALIDATE_ALWAYS is not set
+# default:
+CONFIG_BOOTLOADER_RESERVE_RTC_SIZE=0
+# default:
+# CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC is not set
+# end of Bootloader config
+
+#
+# Security features
+#
+# default:
+CONFIG_SECURE_BOOT_V2_RSA_SUPPORTED=y
+# default:
+CONFIG_SECURE_BOOT_V2_PREFERRED=y
+# default:
+# CONFIG_SECURE_SIGNED_APPS_NO_SECURE_BOOT is not set
+# default:
+# CONFIG_SECURE_BOOT is not set
+# default:
+# CONFIG_SECURE_FLASH_ENC_ENABLED is not set
+# default:
+CONFIG_SECURE_ROM_DL_MODE_ENABLED=y
+# end of Security features
+
+#
+# Application manager
+#
+# default:
+CONFIG_APP_COMPILE_TIME_DATE=y
+# default:
+# CONFIG_APP_EXCLUDE_PROJECT_VER_VAR is not set
+# default:
+# CONFIG_APP_EXCLUDE_PROJECT_NAME_VAR is not set
+# default:
+# CONFIG_APP_PROJECT_VER_FROM_CONFIG is not set
+# default:
+CONFIG_APP_RETRIEVE_LEN_ELF_SHA=9
+# end of Application manager
+
+# default:
+CONFIG_ESP_ROM_HAS_CRC_LE=y
+# default:
+CONFIG_ESP_ROM_HAS_CRC_BE=y
+# default:
+CONFIG_ESP_ROM_HAS_MZ_CRC32=y
+# default:
+CONFIG_ESP_ROM_HAS_JPEG_DECODE=y
+# default:
+CONFIG_ESP_ROM_UART_CLK_IS_XTAL=y
+# default:
+CONFIG_ESP_ROM_HAS_RETARGETABLE_LOCKING=y
+# default:
+CONFIG_ESP_ROM_USB_OTG_NUM=3
+# default:
+CONFIG_ESP_ROM_USB_SERIAL_DEVICE_NUM=4
+# default:
+CONFIG_ESP_ROM_HAS_ERASE_0_REGION_BUG=y
+# default:
+CONFIG_ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV=y
+# default:
+CONFIG_ESP_ROM_GET_CLK_FREQ=y
+# default:
+CONFIG_ESP_ROM_HAS_HAL_WDT=y
+# default:
+CONFIG_ESP_ROM_NEEDS_SWSETUP_WORKAROUND=y
+# default:
+CONFIG_ESP_ROM_HAS_LAYOUT_TABLE=y
+# default:
+CONFIG_ESP_ROM_HAS_SPI_FLASH=y
+# default:
+CONFIG_ESP_ROM_HAS_SPI_FLASH_MMAP=y
+# default:
+CONFIG_ESP_ROM_HAS_ETS_PRINTF_BUG=y
+# default:
+CONFIG_ESP_ROM_HAS_NEWLIB=y
+# default:
+CONFIG_ESP_ROM_HAS_NEWLIB_NANO_FORMAT=y
+# default:
+CONFIG_ESP_ROM_HAS_NEWLIB_32BIT_TIME=y
+# default:
+CONFIG_ESP_ROM_NEEDS_SET_CACHE_MMU_SIZE=y
+# default:
+CONFIG_ESP_ROM_RAM_APP_NEEDS_MMU_INIT=y
+# default:
+CONFIG_ESP_ROM_HAS_FLASH_COUNT_PAGES_BUG=y
+# default:
+CONFIG_ESP_ROM_HAS_CACHE_SUSPEND_WAITI_BUG=y
+# default:
+CONFIG_ESP_ROM_HAS_CACHE_WRITEBACK_BUG=y
+# default:
+CONFIG_ESP_ROM_HAS_SW_FLOAT=y
+# default:
+CONFIG_ESP_ROM_HAS_VERSION=y
+# default:
+CONFIG_ESP_ROM_SUPPORT_DEEP_SLEEP_WAKEUP_STUB=y
+# default:
+CONFIG_ESP_ROM_HAS_OUTPUT_PUTC_FUNC=y
+# default:
+CONFIG_ESP_ROM_CONSOLE_OUTPUT_SECONDARY=y
+
+#
+# Boot ROM Behavior
+#
+# default:
+CONFIG_BOOT_ROM_LOG_ALWAYS_ON=y
+# default:
+# CONFIG_BOOT_ROM_LOG_ALWAYS_OFF is not set
+# default:
+# CONFIG_BOOT_ROM_LOG_ON_GPIO_HIGH is not set
+# default:
+# CONFIG_BOOT_ROM_LOG_ON_GPIO_LOW is not set
+# end of Boot ROM Behavior
+
+#
+# Serial flasher config
+#
+# default:
+# CONFIG_ESPTOOLPY_NO_STUB is not set
+# default:
+# CONFIG_ESPTOOLPY_OCT_FLASH is not set
+# default:
+CONFIG_ESPTOOLPY_FLASH_MODE_AUTO_DETECT=y
+# default:
+# CONFIG_ESPTOOLPY_FLASHMODE_QIO is not set
+# default:
+# CONFIG_ESPTOOLPY_FLASHMODE_QOUT is not set
+# default:
+CONFIG_ESPTOOLPY_FLASHMODE_DIO=y
+# default:
+# CONFIG_ESPTOOLPY_FLASHMODE_DOUT is not set
+# default:
+CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_STR=y
+# default:
+CONFIG_ESPTOOLPY_FLASHMODE="dio"
+# default:
+# CONFIG_ESPTOOLPY_FLASHFREQ_120M is not set
+# default:
+CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
+# default:
+# CONFIG_ESPTOOLPY_FLASHFREQ_40M is not set
+# default:
+# CONFIG_ESPTOOLPY_FLASHFREQ_20M is not set
+# default:
+CONFIG_ESPTOOLPY_FLASHFREQ="80m"
+# CONFIG_ESPTOOLPY_FLASHSIZE_1MB is not set
+# CONFIG_ESPTOOLPY_FLASHSIZE_2MB is not set
+# CONFIG_ESPTOOLPY_FLASHSIZE_4MB is not set
+# CONFIG_ESPTOOLPY_FLASHSIZE_8MB is not set
+CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y
+# CONFIG_ESPTOOLPY_FLASHSIZE_32MB is not set
+# CONFIG_ESPTOOLPY_FLASHSIZE_64MB is not set
+# CONFIG_ESPTOOLPY_FLASHSIZE_128MB is not set
+# default:
+CONFIG_ESPTOOLPY_FLASHSIZE="16MB"
+# default:
+# CONFIG_ESPTOOLPY_HEADER_FLASHSIZE_UPDATE is not set
+# default:
+CONFIG_ESPTOOLPY_BEFORE_RESET=y
+# default:
+# CONFIG_ESPTOOLPY_BEFORE_NORESET is not set
+# default:
+CONFIG_ESPTOOLPY_BEFORE="default_reset"
+# default:
+CONFIG_ESPTOOLPY_AFTER_RESET=y
+# default:
+# CONFIG_ESPTOOLPY_AFTER_NORESET is not set
+# default:
+CONFIG_ESPTOOLPY_AFTER="hard_reset"
+# default:
+CONFIG_ESPTOOLPY_MONITOR_BAUD=115200
+# end of Serial flasher config
+
+#
+# Partition Table
+#
+# default:
+CONFIG_PARTITION_TABLE_SINGLE_APP=y
+# default:
+# CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE is not set
+# default:
+# CONFIG_PARTITION_TABLE_TWO_OTA is not set
+# default:
+# CONFIG_PARTITION_TABLE_TWO_OTA_LARGE is not set
+# default:
+# CONFIG_PARTITION_TABLE_CUSTOM is not set
+# default:
+CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
+# default:
+CONFIG_PARTITION_TABLE_FILENAME="partitions_singleapp.csv"
+# default:
+CONFIG_PARTITION_TABLE_OFFSET=0x8000
+# default:
+CONFIG_PARTITION_TABLE_MD5=y
+# end of Partition Table
+
+#
+# Arduino Configuration
+#
+# default:
+CONFIG_ARDUINO_VARIANT="esp32s3"
+# default:
+CONFIG_ENABLE_ARDUINO_DEPENDS=y
+# CONFIG_AUTOSTART_ARDUINO is not set
+# default:
+# CONFIG_ARDUINO_RUN_CORE0 is not set
+# default:
+CONFIG_ARDUINO_RUN_CORE1=y
+# default:
+# CONFIG_ARDUINO_RUN_NO_AFFINITY is not set
+# default:
+CONFIG_ARDUINO_RUNNING_CORE=1
+# default:
+CONFIG_ARDUINO_LOOP_STACK_SIZE=8192
+# default:
+# CONFIG_ARDUINO_EVENT_RUN_CORE0 is not set
+# default:
+CONFIG_ARDUINO_EVENT_RUN_CORE1=y
+# default:
+# CONFIG_ARDUINO_EVENT_RUN_NO_AFFINITY is not set
+# default:
+CONFIG_ARDUINO_EVENT_RUNNING_CORE=1
+# default:
+# CONFIG_ARDUINO_SERIAL_EVENT_RUN_CORE0 is not set
+# default:
+# CONFIG_ARDUINO_SERIAL_EVENT_RUN_CORE1 is not set
+# default:
+CONFIG_ARDUINO_SERIAL_EVENT_RUN_NO_AFFINITY=y
+# default:
+CONFIG_ARDUINO_SERIAL_EVENT_TASK_RUNNING_CORE=-1
+# default:
+CONFIG_ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE=2048
+# default:
+CONFIG_ARDUINO_SERIAL_EVENT_TASK_PRIORITY=24
+# default:
+CONFIG_ARDUINO_UDP_RUN_CORE0=y
+# default:
+# CONFIG_ARDUINO_UDP_RUN_CORE1 is not set
+# default:
+# CONFIG_ARDUINO_UDP_RUN_NO_AFFINITY is not set
+# default:
+CONFIG_ARDUINO_UDP_RUNNING_CORE=0
+# default:
+CONFIG_ARDUINO_UDP_TASK_PRIORITY=3
+# default:
+CONFIG_ARDUINO_UDP_TASK_STACK_SIZE=4096
+# default:
+# CONFIG_ARDUINO_ISR_IRAM is not set
+# default:
+# CONFIG_DISABLE_HAL_LOCKS is not set
+
+#
+# Debug Log Configuration
+#
+# default:
+# CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL_NONE is not set
+# default:
+CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL_ERROR=y
+# default:
+# CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL_WARN is not set
+# default:
+# CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL_INFO is not set
+# default:
+# CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL_DEBUG is not set
+# default:
+# CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL_VERBOSE is not set
+# default:
+CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL=1
+# default:
+# CONFIG_ARDUHAL_LOG_COLORS is not set
+# default:
+# CONFIG_ARDUHAL_ESP_LOG is not set
+# end of Debug Log Configuration
+
+# default:
+CONFIG_ARDUHAL_PARTITION_SCHEME_DEFAULT=y
+# default:
+# CONFIG_ARDUHAL_PARTITION_SCHEME_MINIMAL is not set
+# default:
+# CONFIG_ARDUHAL_PARTITION_SCHEME_NO_OTA is not set
+# default:
+# CONFIG_ARDUHAL_PARTITION_SCHEME_HUGE_APP is not set
+# default:
+# CONFIG_ARDUHAL_PARTITION_SCHEME_MIN_SPIFFS is not set
+# default:
+CONFIG_ARDUHAL_PARTITION_SCHEME="default"
+# default:
+# CONFIG_ARDUINO_SELECTIVE_COMPILATION is not set
+# end of Arduino Configuration
+
+#
+# ESP Speech Recognition
+#
+# default:
+CONFIG_MODEL_IN_FLASH=y
+# default:
+# CONFIG_MODEL_IN_SDCARD is not set
+# default:
+CONFIG_AFE_INTERFACE_V1=y
+# default:
+CONFIG_SR_NSN_WEBRTC=y
+# default:
+# CONFIG_SR_NSN_NSNET2 is not set
+# default:
+CONFIG_SR_VADN_WEBRTC=y
+# default:
+# CONFIG_SR_VADN_VADNET1_MEDIUM is not set
+
+#
+# Load Multiple Wake Words (WakeNet9s)
+#
+# default:
+# CONFIG_SR_WN_WN9S_HILEXIN is not set
+# default:
+# CONFIG_SR_WN_WN9S_HIESP is not set
+# default:
+# CONFIG_SR_WN_WN9S_NIHAOXIAOZHI is not set
+# default:
+# CONFIG_SR_WN_WN9S_HIJASON is not set
+# end of Load Multiple Wake Words (WakeNet9s)
+
+#
+# Load Multiple Wake Words (WakeNet9 or WakeNet10)
+#
+# default:
+# CONFIG_SR_WN_WN10_HILEXIN is not set
+# default:
+# CONFIG_SR_WN_WN10_XIAOAITONGXUE is not set
+# default:
+# CONFIG_SR_WN_WN10_NIHAOXIAOZHI is not set
+# default:
+# CONFIG_SR_WN_WN9_HILEXIN is not set
+# default:
+# CONFIG_SR_WN_WN9_HIESP is not set
+# default:
+# CONFIG_SR_WN_WN9L_JA_KONNICHIHAESP_TTS3 is not set
+# default:
+# CONFIG_SR_WN_WN9L_FR_BONJOURESP_TTS3 is not set
+# default:
+# CONFIG_SR_WN_WN9_NIHAOMIAOBAN_TTS2 is not set
+# default:
+# CONFIG_SR_WN_WN9_XIAOAITONGXUE is not set
+# default:
+# CONFIG_SR_WN_WN9L_XIAOAITONGXUE is not set
+# default:
+# CONFIG_SR_WN_WN9_NIHAOXIAOZHI_TTS is not set
+# default:
+# CONFIG_SR_WN_WN9L_NIHAOXIAOZHI_TTS3 is not set
+# default:
+# CONFIG_SR_WN_WN9_ALEXA is not set
+# default:
+# CONFIG_SR_WN_WN9_JARVIS_TTS is not set
+# default:
+# CONFIG_SR_WN_WN9_COMPUTER_TTS is not set
+# default:
+# CONFIG_SR_WN_WN9_HEYWILLOW_TTS is not set
+# default:
+# CONFIG_SR_WN_WN9_HIMFIVE is not set
+# default:
+# CONFIG_SR_WN_WN9_SOPHIA_TTS is not set
+# default:
+# CONFIG_SR_WN_WN9_HEYWANDA_TTS is not set
+# default:
+# CONFIG_SR_WN_WN9_HIJOLLY_TTS2 is not set
+# default:
+# CONFIG_SR_WN_WN9_HIFAIRY_TTS2 is not set
+# default:
+# CONFIG_SR_WN_WN9_HEYPRINTER_TTS is not set
+# default:
+# CONFIG_SR_WN_WN9_MYCROFT_TTS is not set
+# default:
+# CONFIG_SR_WN_WN9_HIJOY_TTS is not set
+# default:
+# CONFIG_SR_WN_WN9_HIJASON_TTS2 is not set
+# default:
+# CONFIG_SR_WN_WN9_ASTROLABE_TTS is not set
+# default:
+# CONFIG_SR_WN_WN9_HEYILY_TTS2 is not set
+# default:
+# CONFIG_SR_WN_WN9_BLUECHIP_TTS2 is not set
+# default:
+# CONFIG_SR_WN_WN9_HIANDY_TTS2 is not set
+# default:
+# CONFIG_SR_WN_WN9_HEYIVY_TTS2 is not set
+# default:
+# CONFIG_SR_WN_WN9L_HISTACKCHAN_TTS3 is not set
+# default:
+# CONFIG_SR_WN_WN9_HEYKIRA_TTS3 is not set
+# default:
+# CONFIG_SR_WN_WN9L_HEYGIGI_TTS3 is not set
+# default:
+# CONFIG_SR_WN_WN9_XIAOLONGXIAOLONG_TTS is not set
+# default:
+# CONFIG_SR_WN_WN9_HIMIAOMIAO_TTS is not set
+# default:
+# CONFIG_SR_WN_WN9_MIAOMIAOTONGXUE_TTS is not set
+# default:
+# CONFIG_SR_WN_WN9_NIHAOXIAOXIN_TTS is not set
+# default:
+# CONFIG_SR_WN_WN9_XIAOMEITONGXUE_TTS is not set
+# default:
+# CONFIG_SR_WN_WN9_HILILI_TTS is not set
+# default:
+# CONFIG_SR_WN_WN9_HITELLY_TTS is not set
+# default:
+# CONFIG_SR_WN_WN9_XIAOBINXIAOBIN_TTS is not set
+# default:
+# CONFIG_SR_WN_WN9_HAIXIAOWU_TTS is not set
+# default:
+# CONFIG_SR_WN_WN9_XIAOYAXIAOYA_TTS2 is not set
+# default:
+# CONFIG_SR_WN_WN9_LINAIBAN_TTS2 is not set
+# default:
+# CONFIG_SR_WN_WN9_XIAOSUROU_TTS2 is not set
+# default:
+# CONFIG_SR_WN_WN9_XIAOYUTONGXUE_TTS2 is not set
+# default:
+# CONFIG_SR_WN_WN9_XIAOMINGTONGXUE_TTS2 is not set
+# default:
+# CONFIG_SR_WN_WN9_XIAOKANGTONGXUE_TTS2 is not set
+# default:
+# CONFIG_SR_WN_WN9_XIAOJIANXIAOJIAN_TTS2 is not set
+# default:
+# CONFIG_SR_WN_WN9_XIAOTEXIAOTE_TTS2 is not set
+# default:
+# CONFIG_SR_WN_WN9_NIHAOXIAOYI_TTS2 is not set
+# default:
+# CONFIG_SR_WN_WN9_NIHAOBAIYING_TTS2 is not set
+# default:
+# CONFIG_SR_WN_WN9_NIHAODONGDONG_TTS2 is not set
+# default:
+# CONFIG_SR_WN_WN9_HIWALLE_TTS2 is not set
+# default:
+# CONFIG_SR_WN_WN9_XIAOLUXIAOLU_TTS2 is not set
+# default:
+# CONFIG_SR_WN_WN9_NIHAOXIAOAN_TTS2 is not set
+# default:
+# CONFIG_SR_WN_WN9_NI3HAO3XIAO3MAI4_TTS2 is not set
+# default:
+# CONFIG_SR_WN_WN9_NI3HAO3XIAO3RUI4_TTS3 is not set
+# default:
+# CONFIG_SR_WN_WN9_HAI1XIAO3OU1_TTS3 is not set
+# default:
+# CONFIG_SR_WN_WN9_XIAO3JIA1XIAO3JIA1_TTS3 is not set
+# default:
+# CONFIG_SR_WN_WN9_XIAO3FENG1XIAO3FENG1_TTS3 is not set
+# default:
+# CONFIG_SR_WN_WN9_HAI1XIAO3XIANG4_TTS3 is not set
+# default:
+# CONFIG_SR_WN_WN9L_NI3HAO3XING1BAO3_TTS3 is not set
+# end of Load Multiple Wake Words (WakeNet9 or WakeNet10)
+
+# default:
+CONFIG_SR_MN_CN_NONE=y
+# default:
+# CONFIG_SR_MN_CN_MULTINET5_RECOGNITION_QUANT8 is not set
+# default:
+# CONFIG_SR_MN_CN_MULTINET6_QUANT is not set
+# default:
+# CONFIG_SR_MN_CN_MULTINET6_AC_QUANT is not set
+# default:
+# CONFIG_SR_MN_CN_MULTINET7_QUANT is not set
+# default:
+# CONFIG_SR_MN_CN_MULTINET7_AC_QUANT is not set
+# default:
+CONFIG_SR_MN_EN_NONE=y
+# default:
+# CONFIG_SR_MN_EN_MULTINET5_SINGLE_RECOGNITION_QUANT8 is not set
+# default:
+# CONFIG_SR_MN_EN_MULTINET6_QUANT is not set
+# default:
+# CONFIG_SR_MN_EN_MULTINET7_QUANT is not set
+# default:
+CONFIG_ESP_SR_EXTERNAL_MODEL_PATH=""
+# end of ESP Speech Recognition
+
+#
+# Compiler options
+#
+# default:
+CONFIG_COMPILER_OPTIMIZATION_DEBUG=y
+# default:
+# CONFIG_COMPILER_OPTIMIZATION_SIZE is not set
+# default:
+# CONFIG_COMPILER_OPTIMIZATION_PERF is not set
+# default:
+# CONFIG_COMPILER_OPTIMIZATION_NONE is not set
+# default:
+CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y
+# default:
+# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT is not set
+# default:
+# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE is not set
+# default:
+CONFIG_COMPILER_ASSERT_NDEBUG_EVALUATE=y
+# default:
+CONFIG_COMPILER_FLOAT_LIB_FROM_GCCLIB=y
+# default:
+CONFIG_COMPILER_OPTIMIZATION_ASSERTION_LEVEL=2
+# default:
+# CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT is not set
+# default:
+CONFIG_COMPILER_HIDE_PATHS_MACROS=y
+# default:
+# CONFIG_COMPILER_CXX_EXCEPTIONS is not set
+# default:
+# CONFIG_COMPILER_CXX_RTTI is not set
+# default:
+CONFIG_COMPILER_STACK_CHECK_MODE_NONE=y
+# default:
+# CONFIG_COMPILER_STACK_CHECK_MODE_NORM is not set
+# default:
+# CONFIG_COMPILER_STACK_CHECK_MODE_STRONG is not set
+# default:
+# CONFIG_COMPILER_STACK_CHECK_MODE_ALL is not set
+# default:
+# CONFIG_COMPILER_NO_MERGE_CONSTANTS is not set
+# default:
+# CONFIG_COMPILER_WARN_WRITE_STRINGS is not set
+# default:
+CONFIG_COMPILER_DISABLE_DEFAULT_ERRORS=y
+# default:
+# CONFIG_COMPILER_DISABLE_GCC12_WARNINGS is not set
+# default:
+# CONFIG_COMPILER_DISABLE_GCC13_WARNINGS is not set
+# default:
+# CONFIG_COMPILER_DISABLE_GCC14_WARNINGS is not set
+# default:
+# CONFIG_COMPILER_DUMP_RTL_FILES is not set
+# default:
+CONFIG_COMPILER_RT_LIB_GCCLIB=y
+# default:
+CONFIG_COMPILER_RT_LIB_NAME="gcc"
+# default:
+CONFIG_COMPILER_ORPHAN_SECTIONS_WARNING=y
+# default:
+# CONFIG_COMPILER_ORPHAN_SECTIONS_PLACE is not set
+# default:
+# CONFIG_COMPILER_STATIC_ANALYZER is not set
+# end of Compiler options
+
+#
+# Component config
+#
+
+#
+# Application Level Tracing
+#
+# default:
+# CONFIG_APPTRACE_DEST_JTAG is not set
+# default:
+CONFIG_APPTRACE_DEST_NONE=y
+# default:
+# CONFIG_APPTRACE_DEST_UART1 is not set
+# default:
+# CONFIG_APPTRACE_DEST_UART2 is not set
+# default:
+# CONFIG_APPTRACE_DEST_USB_CDC is not set
+# default:
+CONFIG_APPTRACE_DEST_UART_NONE=y
+# default:
+CONFIG_APPTRACE_UART_TASK_PRIO=1
+# default:
+CONFIG_APPTRACE_LOCK_ENABLE=y
+# end of Application Level Tracing
+
+#
+# Bluetooth
+#
+CONFIG_BT_ENABLED=y
+# CONFIG_BT_BLUEDROID_ENABLED is not set
+CONFIG_BT_NIMBLE_ENABLED=y
+# CONFIG_BT_CONTROLLER_ONLY is not set
+# default:
+CONFIG_BT_CONTROLLER_ENABLED=y
+# default:
+# CONFIG_BT_CONTROLLER_DISABLED is not set
+
+#
+# Common Options
+#
+# default:
+CONFIG_BT_ALARM_MAX_NUM=50
+# default:
+CONFIG_BT_BLE_HOST_ALLOW_SUB_SPEC_MIN_CONN_INT=y
+# default:
+CONFIG_BT_SMP_CRYPTO_STACK_TINYCRYPT=y
+# default:
+# CONFIG_BT_SMP_CRYPTO_STACK_MBEDTLS is not set
+
+#
+# BT Logs
+#
+
+#
+# Log Sources
+#
+# end of Log Sources
+
+# default:
+# CONFIG_BT_LOG_CRITICAL_ONLY is not set
+# default:
+# CONFIG_BLE_LOG_ENABLED is not set
+
+#
+# Legacy SPI Log Output (Deprecated - use BT Log Async Output instead)
+#
+# default:
+# CONFIG_BT_BLE_LOG_SPI_OUT_ENABLED is not set
+# end of Legacy SPI Log Output (Deprecated - use BT Log Async Output instead)
+# end of BT Logs
+
+# default:
+# CONFIG_BT_LE_USED_MEM_STATISTICS_ENABLED is not set
+# end of Common Options
+
+#
+# NimBLE Options
+#
+
+#
+# General
+#
+# default:
+CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_INTERNAL=y
+# default:
+# CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_EXTERNAL is not set
+# default:
+# CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_DEFAULT is not set
+# default:
+CONFIG_BT_NIMBLE_PINNED_TO_CORE=0
+# default:
+CONFIG_BT_NIMBLE_PINNED_TO_CORE_0=y
+# default:
+# CONFIG_BT_NIMBLE_PINNED_TO_CORE_1 is not set
+# default:
+CONFIG_BT_NIMBLE_HOST_TASK_STACK_SIZE=4096
+# default:
+CONFIG_BT_NIMBLE_LEGACY_VHCI_ENABLE=y
+# end of General
+
+#
+# Roles and Profiles
+#
+CONFIG_BT_NIMBLE_ROLE_CENTRAL=y
+CONFIG_BT_NIMBLE_ROLE_PERIPHERAL=y
+CONFIG_BT_NIMBLE_ROLE_BROADCASTER=y
+CONFIG_BT_NIMBLE_ROLE_OBSERVER=y
+# default:
+CONFIG_BT_NIMBLE_GATT_CLIENT=y
+# default:
+CONFIG_BT_NIMBLE_GATT_SERVER=y
+# end of Roles and Profiles
+
+#
+# Security (SMP)
+#
+# default:
+CONFIG_BT_NIMBLE_SECURITY_ENABLE=y
+# default:
+CONFIG_BT_NIMBLE_SM_LEGACY=y
+# default:
+CONFIG_BT_NIMBLE_SM_SC=y
+# default:
+# CONFIG_BT_NIMBLE_SM_SC_DEBUG_KEYS is not set
+# default:
+CONFIG_BT_NIMBLE_LL_CFG_FEAT_LE_ENCRYPTION=y
+# default:
+CONFIG_BT_NIMBLE_SM_LVL=0
+# default:
+CONFIG_BT_NIMBLE_SM_SC_ONLY=0
+# default:
+# CONFIG_BT_NIMBLE_SMP_ID_RESET is not set
+# default:
+# CONFIG_BT_NIMBLE_NVS_PERSIST is not set
+# default:
+CONFIG_BT_NIMBLE_MAX_BONDS=3
+# default:
+# CONFIG_BT_NIMBLE_HANDLE_REPEAT_PAIRING_DELETION is not set
+# end of Security (SMP)
+
+#
+# GAP
+#
+# default:
+CONFIG_BT_NIMBLE_RPA_TIMEOUT=900
+# default:
+CONFIG_BT_NIMBLE_WHITELIST_SIZE=12
+# default:
+CONFIG_BT_NIMBLE_ENABLE_CONN_REATTEMPT=y
+# default:
+CONFIG_BT_NIMBLE_MAX_CONN_REATTEMPT=3
+# default:
+CONFIG_BT_NIMBLE_HS_PVCY=y
+# default:
+# CONFIG_BT_NIMBLE_HOST_ALLOW_CONNECT_WITH_SCAN is not set
+# default:
+# CONFIG_BT_NIMBLE_HOST_QUEUE_CONG_CHECK is not set
+CONFIG_BT_NIMBLE_MAX_CONNECTIONS=7
+# default:
+CONFIG_BT_NIMBLE_MAX_CCCDS=8
+# default:
+# CONFIG_BT_NIMBLE_CRYPTO_STACK_MBEDTLS is not set
+# default:
+CONFIG_BT_NIMBLE_HS_STOP_TIMEOUT_MS=2000
+# default:
+CONFIG_BT_NIMBLE_USE_ESP_TIMER=y
+# default:
+# CONFIG_BT_NIMBLE_HS_FLOW_CTRL is not set
+# end of GAP
+
+#
+# GATT / ATT
+#
+# default:
+CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU=256
+# default:
+CONFIG_BT_NIMBLE_ATT_MAX_PREP_ENTRIES=64
+# default:
+CONFIG_BT_NIMBLE_GATT_MAX_PROCS=4
+# default:
+# CONFIG_BT_NIMBLE_BLE_GATT_BLOB_TRANSFER is not set
+# default:
+# CONFIG_BT_NIMBLE_GATT_CACHING is not set
+# default:
+# CONFIG_BT_NIMBLE_INCL_SVC_DISCOVERY is not set
+# end of GATT / ATT
+
+#
+# L2CAP
+#
+# default:
+CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM=0
+# end of L2CAP
+
+#
+# Memory Settings
+#
+# default:
+CONFIG_BT_NIMBLE_MSYS_1_BLOCK_COUNT=12
+# default:
+CONFIG_BT_NIMBLE_MSYS_1_BLOCK_SIZE=256
+# default:
+CONFIG_BT_NIMBLE_MSYS_2_BLOCK_COUNT=24
+# default:
+CONFIG_BT_NIMBLE_MSYS_2_BLOCK_SIZE=320
+# default:
+CONFIG_BT_NIMBLE_TRANSPORT_ACL_FROM_LL_COUNT=24
+# default:
+CONFIG_BT_NIMBLE_TRANSPORT_ACL_SIZE=255
+# default:
+CONFIG_BT_NIMBLE_TRANSPORT_EVT_SIZE=70
+# default:
+CONFIG_BT_NIMBLE_TRANSPORT_EVT_COUNT=30
+# default:
+CONFIG_BT_NIMBLE_TRANSPORT_EVT_DISCARD_COUNT=8
+# default:
+CONFIG_BT_NIMBLE_L2CAP_COC_SDU_BUFF_COUNT=1
+# default:
+# CONFIG_BT_NIMBLE_MEMPOOL_RUNTIME_ALLOC is not set
+# end of Memory Settings
+
+#
+# BLE 5.x Features
+#
+# default:
+CONFIG_BT_NIMBLE_50_FEATURE_SUPPORT=y
+# default:
+CONFIG_BT_NIMBLE_LL_CFG_FEAT_LE_2M_PHY=y
+# default:
+CONFIG_BT_NIMBLE_LL_CFG_FEAT_LE_CODED_PHY=y
+# default:
+# CONFIG_BT_NIMBLE_EXT_ADV is not set
+# default:
+# CONFIG_BT_NIMBLE_EXT_SCAN is not set
+# default:
+# CONFIG_BT_NIMBLE_ISO is not set
+# end of BLE 5.x Features
+
+#
+# Services
+#
+# default:
+CONFIG_BT_NIMBLE_PROX_SERVICE=y
+# default:
+CONFIG_BT_NIMBLE_ANS_SERVICE=y
+# default:
+CONFIG_BT_NIMBLE_CTS_SERVICE=y
+# default:
+CONFIG_BT_NIMBLE_HTP_SERVICE=y
+# default:
+CONFIG_BT_NIMBLE_IPSS_SERVICE=y
+# default:
+CONFIG_BT_NIMBLE_TPS_SERVICE=y
+# default:
+CONFIG_BT_NIMBLE_IAS_SERVICE=y
+# default:
+CONFIG_BT_NIMBLE_LLS_SERVICE=y
+# default:
+CONFIG_BT_NIMBLE_SPS_SERVICE=y
+# default:
+CONFIG_BT_NIMBLE_HR_SERVICE=y
+# default:
+# CONFIG_BT_NIMBLE_HID_SERVICE is not set
+# default:
+CONFIG_BT_NIMBLE_BAS_SERVICE=y
+# default:
+# CONFIG_BT_NIMBLE_SVC_BAS_BATTERY_LEVEL_NOTIFY is not set
+# default:
+CONFIG_BT_NIMBLE_DIS_SERVICE=y
+# default:
+# CONFIG_BT_NIMBLE_SVC_DIS_MANUFACTURER_NAME is not set
+# default:
+# CONFIG_BT_NIMBLE_SVC_DIS_SERIAL_NUMBER is not set
+# default:
+# CONFIG_BT_NIMBLE_SVC_DIS_HARDWARE_REVISION is not set
+# default:
+# CONFIG_BT_NIMBLE_SVC_DIS_FIRMWARE_REVISION is not set
+# default:
+# CONFIG_BT_NIMBLE_SVC_DIS_SOFTWARE_REVISION is not set
+# default:
+# CONFIG_BT_NIMBLE_SVC_DIS_SYSTEM_ID is not set
+# default:
+# CONFIG_BT_NIMBLE_SVC_DIS_PNP_ID is not set
+# default:
+# CONFIG_BT_NIMBLE_SVC_DIS_INCLUDED is not set
+# default:
+CONFIG_BT_NIMBLE_GAP_SERVICE=y
+# default:
+CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME="nimble"
+# default:
+CONFIG_BT_NIMBLE_GAP_DEVICE_NAME_MAX_LEN=31
+# default:
+CONFIG_BT_NIMBLE_SVC_GAP_APPEARANCE=0
+# default:
+CONFIG_BT_NIMBLE_SVC_GAP_NAME_WRITE_PERM=0
+# default:
+CONFIG_BT_NIMBLE_SVC_GAP_NAME_WRITE_PERM_ENC=0
+# default:
+CONFIG_BT_NIMBLE_SVC_GAP_NAME_WRITE_PERM_AUTHEN=0
+# default:
+CONFIG_BT_NIMBLE_SVC_GAP_NAME_WRITE_PERM_AUTHOR=0
+# default:
+# CONFIG_BT_NIMBLE_SVC_GAP_GATT_SECURITY_LEVEL is not set
+# default:
+# CONFIG_BT_NIMBLE_SVC_GAP_RPA_ONLY is not set
+# default:
+CONFIG_BT_NIMBLE_SVC_GAP_CAR_CHAR_NOT_SUPP=y
+# default:
+# CONFIG_BT_NIMBLE_SVC_GAP_CAR_NOT_SUPP is not set
+# default:
+# CONFIG_BT_NIMBLE_SVC_GAP_CAR_SUPP is not set
+# default:
+CONFIG_BT_NIMBLE_SVC_GAP_CENT_ADDR_RESOLUTION=-1
+
+#
+# GAP Appearance write permissions
+#
+# default:
+# CONFIG_BT_NIMBLE_SVC_GAP_APPEAR_WRITE is not set
+# default:
+CONFIG_BT_NIMBLE_SVC_GAP_APPEAR_WRITE_PERM=0
+# default:
+CONFIG_BT_NIMBLE_SVC_GAP_APPEAR_WRITE_PERM_ENC=0
+# default:
+CONFIG_BT_NIMBLE_SVC_GAP_APPEAR_WRITE_PERM_ATHN=0
+# default:
+CONFIG_BT_NIMBLE_SVC_GAP_APPEAR_WRITE_PERM_ATHR=0
+# end of GAP Appearance write permissions
+
+#
+# GAP device name write permissions
+#
+# default:
+# CONFIG_BT_NIMBLE_SVC_GAP_NAME_WRITE is not set
+# end of GAP device name write permissions
+
+#
+# Peripheral Preferred Connection Parameters (PPCP) settings
+#
+# default:
+CONFIG_BT_NIMBLE_SVC_GAP_PPCP_MAX_CONN_INTERVAL=0
+# default:
+CONFIG_BT_NIMBLE_SVC_GAP_PPCP_MIN_CONN_INTERVAL=0
+# default:
+CONFIG_BT_NIMBLE_SVC_GAP_PPCP_SLAVE_LATENCY=0
+# default:
+CONFIG_BT_NIMBLE_SVC_GAP_PPCP_SUPERVISION_TMO=0
+# end of Peripheral Preferred Connection Parameters (PPCP) settings
+# end of Services
+
+#
+# Extra Features
+#
+# default:
+# CONFIG_BT_NIMBLE_DYNAMIC_SERVICE is not set
+# default:
+# CONFIG_BT_NIMBLE_BLUFI_ENABLE is not set
+# default:
+# CONFIG_BT_NIMBLE_ENC_ADV_DATA is not set
+# default:
+# CONFIG_BT_NIMBLE_ADV_UUID_CONCAT is not set
+# default:
+# CONFIG_BT_NIMBLE_GATTC_PROC_PREEMPTION_PROTECT is not set
+# default:
+# CONFIG_BT_NIMBLE_GATTC_AUTO_PAIR is not set
+# default:
+CONFIG_BT_NIMBLE_EATT_CHAN_NUM=0
+# default:
+# CONFIG_BT_NIMBLE_SUBRATE is not set
+# default:
+# CONFIG_BT_NIMBLE_STATIC_PASSKEY is not set
+# default:
+CONFIG_BT_NIMBLE_DTM_MODE_TEST=y
+# default:
+CONFIG_BT_NIMBLE_MEM_OPTIMIZATION=y
+# default:
+CONFIG_BT_NIMBLE_STATIC_TO_DYNAMIC=y
+# default:
+CONFIG_BT_NIMBLE_SM_SIGN_CNT=y
+# default:
+CONFIG_BT_NIMBLE_CPFD_CAFD=y
+# default:
+CONFIG_BT_NIMBLE_RECONFIG_MTU=y
+# default:
+# CONFIG_BT_NIMBLE_LOW_SPEED_MODE is not set
+# end of Extra Features
+
+#
+# NimBLE Mesh
+#
+# default:
+# CONFIG_BT_NIMBLE_MESH is not set
+# end of NimBLE Mesh
+
+#
+# Host-controller Transport
+#
+# default:
+CONFIG_UART_HW_FLOWCTRL_DISABLE=y
+# default:
+# CONFIG_UART_HW_FLOWCTRL_CTS_RTS is not set
+# default:
+CONFIG_BT_NIMBLE_HCI_UART_FLOW_CTRL=0
+# default:
+CONFIG_BT_NIMBLE_HCI_UART_RTS_PIN=19
+# default:
+CONFIG_BT_NIMBLE_HCI_UART_CTS_PIN=23
+# end of Host-controller Transport
+
+#
+# Debugging/Testing
+#
+# default:
+# CONFIG_BT_NIMBLE_MEM_DEBUG is not set
+# default:
+# CONFIG_BT_NIMBLE_LOG_LEVEL_NONE is not set
+# default:
+# CONFIG_BT_NIMBLE_LOG_LEVEL_ERROR is not set
+# default:
+# CONFIG_BT_NIMBLE_LOG_LEVEL_WARNING is not set
+# default:
+CONFIG_BT_NIMBLE_LOG_LEVEL_INFO=y
+# default:
+# CONFIG_BT_NIMBLE_LOG_LEVEL_DEBUG is not set
+# default:
+CONFIG_BT_NIMBLE_LOG_LEVEL=1
+# default:
+CONFIG_BT_NIMBLE_PRINT_ERR_NAME=y
+# default:
+# CONFIG_BT_NIMBLE_DEBUG is not set
+# default:
+# CONFIG_BT_NIMBLE_TEST_THROUGHPUT_TEST is not set
+# end of Debugging/Testing
+
+#
+# Vendor / Optimization
+#
+# default:
+# CONFIG_BT_NIMBLE_VS_SUPPORT is not set
+# default:
+# CONFIG_BT_NIMBLE_HIGH_DUTY_ADV_ITVL is not set
+# end of Vendor / Optimization
+
+#
+# Helper Utils
+#
+# default:
+CONFIG_BT_NIMBLE_CHK_HOST_STATUS=y
+# default:
+CONFIG_BT_NIMBLE_UTIL_API=y
+# default:
+CONFIG_BT_NIMBLE_EXTRA_ADV_FIELDS=y
+# end of Helper Utils
+# end of NimBLE Options
+
+#
+# Controller Options
+#
+# default:
+CONFIG_BT_CTRL_MODE_EFF=1
+# default:
+CONFIG_BT_CTRL_CHECK_CONFIG_EFF=1
+# default:
+CONFIG_BT_CTRL_BLE_MAX_ACT=6
+# default:
+CONFIG_BT_CTRL_BLE_MAX_ACT_EFF=6
+# default:
+CONFIG_BT_CTRL_BLE_STATIC_ACL_TX_BUF_NB=0
+# default:
+CONFIG_BT_CTRL_PINNED_TO_CORE_0=y
+# default:
+# CONFIG_BT_CTRL_PINNED_TO_CORE_1 is not set
+# default:
+CONFIG_BT_CTRL_PINNED_TO_CORE=0
+# default:
+CONFIG_BT_CTRL_HCI_MODE_VHCI=y
+# default:
+# CONFIG_BT_CTRL_HCI_MODE_UART_H4 is not set
+# default:
+CONFIG_BT_CTRL_HCI_TL=1
+# default:
+CONFIG_BT_CTRL_ADV_DUP_FILT_MAX=30
+# default:
+CONFIG_BT_BLE_CCA_MODE_NONE=y
+# default:
+# CONFIG_BT_BLE_CCA_MODE_HW is not set
+# default:
+# CONFIG_BT_BLE_CCA_MODE_SW is not set
+# default:
+CONFIG_BT_BLE_CCA_MODE=0
+# default:
+CONFIG_BT_CTRL_HW_CCA_VAL=75
+# default:
+CONFIG_BT_CTRL_HW_CCA_EFF=0
+# default:
+CONFIG_BT_CTRL_CE_LENGTH_TYPE_ORIG=y
+# default:
+# CONFIG_BT_CTRL_CE_LENGTH_TYPE_CE is not set
+# default:
+# CONFIG_BT_CTRL_CE_LENGTH_TYPE_SD is not set
+# default:
+CONFIG_BT_CTRL_CE_LENGTH_TYPE_EFF=0
+# default:
+CONFIG_BT_CTRL_TX_ANTENNA_INDEX_0=y
+# default:
+# CONFIG_BT_CTRL_TX_ANTENNA_INDEX_1 is not set
+# default:
+CONFIG_BT_CTRL_TX_ANTENNA_INDEX_EFF=0
+# default:
+CONFIG_BT_CTRL_RX_ANTENNA_INDEX_0=y
+# default:
+# CONFIG_BT_CTRL_RX_ANTENNA_INDEX_1 is not set
+# default:
+CONFIG_BT_CTRL_RX_ANTENNA_INDEX_EFF=0
+# default:
+# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N24 is not set
+# default:
+# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N21 is not set
+# default:
+# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N18 is not set
+# default:
+# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N15 is not set
+# default:
+# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N12 is not set
+# default:
+# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N9 is not set
+# default:
+# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N6 is not set
+# default:
+# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N3 is not set
+# default:
+# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N0 is not set
+# default:
+# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P3 is not set
+# default:
+# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P6 is not set
+# default:
+CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P9=y
+# default:
+# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P12 is not set
+# default:
+# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P15 is not set
+# default:
+# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P18 is not set
+# default:
+# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P20 is not set
+# default:
+CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_EFF=11
+# default:
+CONFIG_BT_CTRL_BLE_ADV_REPORT_FLOW_CTRL_SUPP=y
+# default:
+CONFIG_BT_CTRL_BLE_ADV_REPORT_FLOW_CTRL_NUM=100
+# default:
+CONFIG_BT_CTRL_BLE_ADV_REPORT_DISCARD_THRSHOLD=20
+# default:
+CONFIG_BT_CTRL_BLE_SCAN_DUPL=y
+# default:
+CONFIG_BT_CTRL_SCAN_DUPL_TYPE_DEVICE=y
+# default:
+# CONFIG_BT_CTRL_SCAN_DUPL_TYPE_DATA is not set
+# default:
+# CONFIG_BT_CTRL_SCAN_DUPL_TYPE_DATA_DEVICE is not set
+# default:
+CONFIG_BT_CTRL_SCAN_DUPL_TYPE=0
+# default:
+CONFIG_BT_CTRL_SCAN_DUPL_CACHE_SIZE=100
+# default:
+CONFIG_BT_CTRL_DUPL_SCAN_CACHE_REFRESH_PERIOD=0
+# default:
+# CONFIG_BT_CTRL_BLE_MESH_SCAN_DUPL_EN is not set
+# default:
+# CONFIG_BT_CTRL_COEX_PHY_CODED_TX_RX_TLIM_EN is not set
+# default:
+CONFIG_BT_CTRL_COEX_PHY_CODED_TX_RX_TLIM_DIS=y
+# default:
+CONFIG_BT_CTRL_COEX_PHY_CODED_TX_RX_TLIM_EFF=0
+
+#
+# MODEM SLEEP Options
+#
+# default:
+# CONFIG_BT_CTRL_MODEM_SLEEP is not set
+# end of MODEM SLEEP Options
+
+# default:
+CONFIG_BT_CTRL_SLEEP_MODE_EFF=0
+# default:
+CONFIG_BT_CTRL_SLEEP_CLOCK_EFF=0
+# default:
+CONFIG_BT_CTRL_HCI_TL_EFF=1
+# default:
+# CONFIG_BT_CTRL_AGC_RECORRECT_EN is not set
+# default:
+# CONFIG_BT_CTRL_SCAN_BACKOFF_UPPERLIMITMAX is not set
+# default:
+# CONFIG_BT_BLE_ADV_DATA_LENGTH_ZERO_AUX is not set
+# default:
+CONFIG_BT_CTRL_CHAN_ASS_EN=y
+# default:
+CONFIG_BT_CTRL_LE_PING_EN=y
+
+#
+# BLE disconnects when Instant Passed (0x28) occurs
+#
+# default:
+# CONFIG_BT_CTRL_BLE_LLCP_CONN_UPDATE is not set
+# default:
+# CONFIG_BT_CTRL_BLE_LLCP_CHAN_MAP_UPDATE is not set
+# default:
+# CONFIG_BT_CTRL_BLE_LLCP_PHY_UPDATE is not set
+# end of BLE disconnects when Instant Passed (0x28) occurs
+
+# default:
+# CONFIG_BT_CTRL_RUN_IN_FLASH_ONLY is not set
+# default:
+CONFIG_BT_CTRL_DTM_ENABLE=y
+# default:
+CONFIG_BT_CTRL_BLE_MASTER=y
+# default:
+# CONFIG_BT_CTRL_BLE_TEST is not set
+# default:
+CONFIG_BT_CTRL_BLE_SCAN=y
+# default:
+CONFIG_BT_CTRL_BLE_SECURITY_ENABLE=y
+# default:
+CONFIG_BT_CTRL_BLE_ADV=y
+# default:
+# CONFIG_BT_CTRL_CHECK_CONNECT_IND_ACCESS_ADDRESS is not set
+# default:
+CONFIG_BT_CTRL_BLE_MIN_CONN_INTERVAL_ENABLE=y
+
+#
+# Controller debug log Options (Experimental)
+#
+# default:
+# CONFIG_BT_CTRL_LE_LOG_EN is not set
+# end of Controller debug log Options (Experimental)
+# end of Controller Options
+
+# default:
+# CONFIG_BT_HCI_LOG_DEBUG_EN is not set
+# end of Bluetooth
+
+# default:
+# CONFIG_BLE_MESH is not set
+
+#
+# Console Library
+#
+# default:
+# CONFIG_CONSOLE_SORTED_HELP is not set
+# end of Console Library
+
+#
+# Driver Configurations
+#
+
+#
+# Legacy TWAI Driver Configurations
+#
+# default:
+# CONFIG_TWAI_SKIP_LEGACY_CONFLICT_CHECK is not set
+# default:
+CONFIG_TWAI_ERRATA_FIX_LISTEN_ONLY_DOM=y
+# end of Legacy TWAI Driver Configurations
+
+#
+# Legacy ADC Driver Configuration
+#
+# default:
+# CONFIG_ADC_SUPPRESS_DEPRECATE_WARN is not set
+# default:
+# CONFIG_ADC_SKIP_LEGACY_CONFLICT_CHECK is not set
+
+#
+# Legacy ADC Calibration Configuration
+#
+# default:
+# CONFIG_ADC_CALI_SUPPRESS_DEPRECATE_WARN is not set
+# end of Legacy ADC Calibration Configuration
+# end of Legacy ADC Driver Configuration
+
+#
+# Legacy MCPWM Driver Configurations
+#
+# default:
+# CONFIG_MCPWM_SUPPRESS_DEPRECATE_WARN is not set
+# default:
+# CONFIG_MCPWM_SKIP_LEGACY_CONFLICT_CHECK is not set
+# end of Legacy MCPWM Driver Configurations
+
+#
+# Legacy Timer Group Driver Configurations
+#
+# default:
+# CONFIG_GPTIMER_SUPPRESS_DEPRECATE_WARN is not set
+# default:
+# CONFIG_GPTIMER_SKIP_LEGACY_CONFLICT_CHECK is not set
+# end of Legacy Timer Group Driver Configurations
+
+#
+# Legacy RMT Driver Configurations
+#
+# default:
+# CONFIG_RMT_SUPPRESS_DEPRECATE_WARN is not set
+# default:
+# CONFIG_RMT_SKIP_LEGACY_CONFLICT_CHECK is not set
+# end of Legacy RMT Driver Configurations
+
+#
+# Legacy I2S Driver Configurations
+#
+# default:
+# CONFIG_I2S_SUPPRESS_DEPRECATE_WARN is not set
+# default:
+# CONFIG_I2S_SKIP_LEGACY_CONFLICT_CHECK is not set
+# end of Legacy I2S Driver Configurations
+
+#
+# Legacy I2C Driver Configurations
+#
+# default:
+# CONFIG_I2C_SKIP_LEGACY_CONFLICT_CHECK is not set
+# end of Legacy I2C Driver Configurations
+
+#
+# Legacy PCNT Driver Configurations
+#
+# default:
+# CONFIG_PCNT_SUPPRESS_DEPRECATE_WARN is not set
+# default:
+# CONFIG_PCNT_SKIP_LEGACY_CONFLICT_CHECK is not set
+# end of Legacy PCNT Driver Configurations
+
+#
+# Legacy SDM Driver Configurations
+#
+# default:
+# CONFIG_SDM_SUPPRESS_DEPRECATE_WARN is not set
+# default:
+# CONFIG_SDM_SKIP_LEGACY_CONFLICT_CHECK is not set
+# end of Legacy SDM Driver Configurations
+
+#
+# Legacy Temperature Sensor Driver Configurations
+#
+# default:
+# CONFIG_TEMP_SENSOR_SUPPRESS_DEPRECATE_WARN is not set
+# default:
+# CONFIG_TEMP_SENSOR_SKIP_LEGACY_CONFLICT_CHECK is not set
+# end of Legacy Temperature Sensor Driver Configurations
+
+#
+# Legacy Touch Sensor Driver Configurations
+#
+# default:
+# CONFIG_TOUCH_SUPPRESS_DEPRECATE_WARN is not set
+# default:
+# CONFIG_TOUCH_SKIP_LEGACY_CONFLICT_CHECK is not set
+# end of Legacy Touch Sensor Driver Configurations
+# end of Driver Configurations
+
+#
+# eFuse Bit Manager
+#
+# default:
+# CONFIG_EFUSE_CUSTOM_TABLE is not set
+# default:
+# CONFIG_EFUSE_VIRTUAL is not set
+# default:
+CONFIG_EFUSE_MAX_BLK_LEN=256
+# end of eFuse Bit Manager
+
+#
+# ESP-TLS
+#
+# default:
+CONFIG_ESP_TLS_USING_MBEDTLS=y
+# default:
+# CONFIG_ESP_TLS_USE_SECURE_ELEMENT is not set
+# default:
+CONFIG_ESP_TLS_USE_DS_PERIPHERAL=y
+# default:
+# CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS is not set
+# default:
+# CONFIG_ESP_TLS_SERVER_SESSION_TICKETS is not set
+# default:
+# CONFIG_ESP_TLS_SERVER_CERT_SELECT_HOOK is not set
+# default:
+# CONFIG_ESP_TLS_SERVER_MIN_AUTH_MODE_OPTIONAL is not set
+# default:
+# CONFIG_ESP_TLS_PSK_VERIFICATION is not set
+# default:
+# CONFIG_ESP_TLS_INSECURE is not set
+# default:
+CONFIG_ESP_TLS_DYN_BUF_STRATEGY_SUPPORTED=y
+# end of ESP-TLS
+
+#
+# ADC and ADC Calibration
+#
+# default:
+# CONFIG_ADC_ONESHOT_CTRL_FUNC_IN_IRAM is not set
+# default:
+# CONFIG_ADC_CONTINUOUS_ISR_IRAM_SAFE is not set
+# default:
+# CONFIG_ADC_CONTINUOUS_FORCE_USE_ADC2_ON_C3_S3 is not set
+# default:
+# CONFIG_ADC_ENABLE_DEBUG_LOG is not set
+# end of ADC and ADC Calibration
+
+#
+# Wireless Coexistence
+#
+# default:
+CONFIG_ESP_COEX_ENABLED=y
+# default:
+CONFIG_ESP_COEX_SW_COEXIST_ENABLE=y
+# default:
+# CONFIG_ESP_COEX_POWER_MANAGEMENT is not set
+# default:
+# CONFIG_ESP_COEX_GPIO_DEBUG is not set
+# end of Wireless Coexistence
+
+#
+# Common ESP-related
+#
+# default:
+CONFIG_ESP_ERR_TO_NAME_LOOKUP=y
+# end of Common ESP-related
+
+#
+# ESP-Driver:Camera Controller Configurations
+#
+# default:
+# CONFIG_CAM_CTLR_DVP_CAM_ISR_CACHE_SAFE is not set
+# end of ESP-Driver:Camera Controller Configurations
+
+#
+# ESP-Driver:GPIO Configurations
+#
+# default:
+# CONFIG_GPIO_CTRL_FUNC_IN_IRAM is not set
+# end of ESP-Driver:GPIO Configurations
+
+#
+# ESP-Driver:GPTimer Configurations
+#
+# default:
+CONFIG_GPTIMER_ISR_HANDLER_IN_IRAM=y
+# default:
+# CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM is not set
+# default:
+# CONFIG_GPTIMER_ISR_CACHE_SAFE is not set
+# default:
+CONFIG_GPTIMER_OBJ_CACHE_SAFE=y
+# default:
+# CONFIG_GPTIMER_ENABLE_DEBUG_LOG is not set
+# end of ESP-Driver:GPTimer Configurations
+
+#
+# ESP-Driver:I2C Configurations
+#
+# default:
+# CONFIG_I2C_ISR_IRAM_SAFE is not set
+# default:
+# CONFIG_I2C_ENABLE_DEBUG_LOG is not set
+# default:
+# CONFIG_I2C_ENABLE_SLAVE_DRIVER_VERSION_2 is not set
+# default:
+CONFIG_I2C_MASTER_ISR_HANDLER_IN_IRAM=y
+# end of ESP-Driver:I2C Configurations
+
+#
+# ESP-Driver:I2S Configurations
+#
+# default:
+# CONFIG_I2S_ISR_IRAM_SAFE is not set
+# default:
+# CONFIG_I2S_ENABLE_DEBUG_LOG is not set
+# end of ESP-Driver:I2S Configurations
+
+#
+# ESP-Driver:LEDC Configurations
+#
+# default:
+# CONFIG_LEDC_CTRL_FUNC_IN_IRAM is not set
+# end of ESP-Driver:LEDC Configurations
+
+#
+# ESP-Driver:MCPWM Configurations
+#
+# default:
+CONFIG_MCPWM_ISR_HANDLER_IN_IRAM=y
+# default:
+# CONFIG_MCPWM_ISR_CACHE_SAFE is not set
+# default:
+# CONFIG_MCPWM_CTRL_FUNC_IN_IRAM is not set
+# default:
+CONFIG_MCPWM_OBJ_CACHE_SAFE=y
+# default:
+# CONFIG_MCPWM_ENABLE_DEBUG_LOG is not set
+# end of ESP-Driver:MCPWM Configurations
+
+#
+# ESP-Driver:PCNT Configurations
+#
+# default:
+# CONFIG_PCNT_CTRL_FUNC_IN_IRAM is not set
+# default:
+# CONFIG_PCNT_ISR_IRAM_SAFE is not set
+# default:
+# CONFIG_PCNT_ENABLE_DEBUG_LOG is not set
+# end of ESP-Driver:PCNT Configurations
+
+#
+# ESP-Driver:RMT Configurations
+#
+# default:
+CONFIG_RMT_ENCODER_FUNC_IN_IRAM=y
+# default:
+CONFIG_RMT_TX_ISR_HANDLER_IN_IRAM=y
+# default:
+CONFIG_RMT_RX_ISR_HANDLER_IN_IRAM=y
+# default:
+# CONFIG_RMT_RECV_FUNC_IN_IRAM is not set
+# default:
+# CONFIG_RMT_TX_ISR_CACHE_SAFE is not set
+# default:
+# CONFIG_RMT_RX_ISR_CACHE_SAFE is not set
+# default:
+CONFIG_RMT_OBJ_CACHE_SAFE=y
+# default:
+# CONFIG_RMT_ENABLE_DEBUG_LOG is not set
+# default:
+# CONFIG_RMT_ISR_IRAM_SAFE is not set
+# end of ESP-Driver:RMT Configurations
+
+#
+# ESP-Driver:Sigma Delta Modulator Configurations
+#
+# default:
+# CONFIG_SDM_CTRL_FUNC_IN_IRAM is not set
+# default:
+# CONFIG_SDM_ENABLE_DEBUG_LOG is not set
+# end of ESP-Driver:Sigma Delta Modulator Configurations
+
+#
+# ESP-Driver:SPI Configurations
+#
+# default:
+# CONFIG_SPI_MASTER_IN_IRAM is not set
+# default:
+CONFIG_SPI_MASTER_ISR_IN_IRAM=y
+# default:
+# CONFIG_SPI_SLAVE_IN_IRAM is not set
+# default:
+CONFIG_SPI_SLAVE_ISR_IN_IRAM=y
+# end of ESP-Driver:SPI Configurations
+
+#
+# ESP-Driver:Touch Sensor Configurations
+#
+# default:
+# CONFIG_TOUCH_CTRL_FUNC_IN_IRAM is not set
+# default:
+# CONFIG_TOUCH_ISR_IRAM_SAFE is not set
+# default:
+# CONFIG_TOUCH_ENABLE_DEBUG_LOG is not set
+# default:
+# CONFIG_TOUCH_SKIP_FSM_CHECK is not set
+# end of ESP-Driver:Touch Sensor Configurations
+
+#
+# ESP-Driver:Temperature Sensor Configurations
+#
+# default:
+# CONFIG_TEMP_SENSOR_ENABLE_DEBUG_LOG is not set
+# end of ESP-Driver:Temperature Sensor Configurations
+
+#
+# ESP-Driver:TWAI Configurations
+#
+# default:
+# CONFIG_TWAI_ISR_IN_IRAM is not set
+# default:
+# CONFIG_TWAI_IO_FUNC_IN_IRAM is not set
+# default:
+# CONFIG_TWAI_ISR_CACHE_SAFE is not set
+# default:
+# CONFIG_TWAI_ENABLE_DEBUG_LOG is not set
+# end of ESP-Driver:TWAI Configurations
+
+#
+# ESP-Driver:UART Configurations
+#
+# default:
+# CONFIG_UART_ISR_IN_IRAM is not set
+# end of ESP-Driver:UART Configurations
+
+#
+# ESP-Driver:UHCI Configurations
+#
+# default:
+# CONFIG_UHCI_ISR_HANDLER_IN_IRAM is not set
+# default:
+# CONFIG_UHCI_ISR_CACHE_SAFE is not set
+# default:
+# CONFIG_UHCI_ENABLE_DEBUG_LOG is not set
+# end of ESP-Driver:UHCI Configurations
+
+#
+# ESP-Driver:USB Serial/JTAG Configuration
+#
+# default:
+CONFIG_USJ_ENABLE_USB_SERIAL_JTAG=y
+# end of ESP-Driver:USB Serial/JTAG Configuration
+
+#
+# Ethernet
+#
+# default:
+CONFIG_ETH_ENABLED=y
+# default:
+CONFIG_ETH_USE_SPI_ETHERNET=y
+# default:
+# CONFIG_ETH_SPI_ETHERNET_DM9051 is not set
+# default:
+# CONFIG_ETH_SPI_ETHERNET_W5500 is not set
+# default:
+# CONFIG_ETH_SPI_ETHERNET_KSZ8851SNL is not set
+# default:
+# CONFIG_ETH_USE_OPENETH is not set
+# default:
+# CONFIG_ETH_TRANSMIT_MUTEX is not set
+# end of Ethernet
+
+#
+# Event Loop Library
+#
+# default:
+# CONFIG_ESP_EVENT_LOOP_PROFILING is not set
+# default:
+CONFIG_ESP_EVENT_POST_FROM_ISR=y
+# default:
+CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR=y
+# end of Event Loop Library
+
+#
+# GDB Stub
+#
+# default:
+CONFIG_ESP_GDBSTUB_ENABLED=y
+# default:
+# CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME is not set
+# default:
+CONFIG_ESP_GDBSTUB_SUPPORT_TASKS=y
+# default:
+CONFIG_ESP_GDBSTUB_MAX_TASKS=32
+# end of GDB Stub
+
+#
+# ESP HID
+#
+# default:
+CONFIG_ESPHID_TASK_SIZE_BT=2048
+# default:
+CONFIG_ESPHID_TASK_SIZE_BLE=4096
+# end of ESP HID
+
+#
+# ESP HTTP client
+#
+# default:
+CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=y
+# default:
+# CONFIG_ESP_HTTP_CLIENT_ENABLE_BASIC_AUTH is not set
+# default:
+# CONFIG_ESP_HTTP_CLIENT_ENABLE_DIGEST_AUTH is not set
+# default:
+# CONFIG_ESP_HTTP_CLIENT_ENABLE_CUSTOM_TRANSPORT is not set
+# default:
+CONFIG_ESP_HTTP_CLIENT_EVENT_POST_TIMEOUT=2000
+# end of ESP HTTP client
+
+#
+# HTTP Server
+#
+# default:
+CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024
+# default:
+CONFIG_HTTPD_MAX_URI_LEN=512
+# default:
+CONFIG_HTTPD_ERR_RESP_NO_DELAY=y
+# default:
+CONFIG_HTTPD_PURGE_BUF_LEN=32
+# default:
+# CONFIG_HTTPD_LOG_PURGE_DATA is not set
+# default:
+# CONFIG_HTTPD_WS_SUPPORT is not set
+# default:
+# CONFIG_HTTPD_QUEUE_WORK_BLOCKING is not set
+# default:
+CONFIG_HTTPD_SERVER_EVENT_POST_TIMEOUT=2000
+# end of HTTP Server
+
+#
+# ESP HTTPS OTA
+#
+# default:
+# CONFIG_ESP_HTTPS_OTA_DECRYPT_CB is not set
+# default:
+# CONFIG_ESP_HTTPS_OTA_ALLOW_HTTP is not set
+# default:
+CONFIG_ESP_HTTPS_OTA_EVENT_POST_TIMEOUT=2000
+# end of ESP HTTPS OTA
+
+#
+# ESP HTTPS server
+#
+# default:
+# CONFIG_ESP_HTTPS_SERVER_ENABLE is not set
+# default:
+CONFIG_ESP_HTTPS_SERVER_EVENT_POST_TIMEOUT=2000
+# default:
+# CONFIG_ESP_HTTPS_SERVER_CERT_SELECT_HOOK is not set
+# end of ESP HTTPS server
+
+#
+# Hardware Settings
+#
+# default:
+CONFIG_ESP_HW_SUPPORT_FUNC_IN_IRAM=y
+
+#
+# Chip revision
+#
+# default:
+CONFIG_ESP32S3_REV_MIN_0=y
+# default:
+# CONFIG_ESP32S3_REV_MIN_1 is not set
+# default:
+# CONFIG_ESP32S3_REV_MIN_2 is not set
+# default:
+CONFIG_ESP32S3_REV_MIN_FULL=0
+# default:
+CONFIG_ESP_REV_MIN_FULL=0
+
+#
+# Maximum Supported ESP32-S3 Revision (Rev v0.99)
+#
+# default:
+CONFIG_ESP32S3_REV_MAX_FULL=99
+# default:
+CONFIG_ESP_REV_MAX_FULL=99
+# default:
+CONFIG_ESP_EFUSE_BLOCK_REV_MIN_FULL=0
+# default:
+CONFIG_ESP_EFUSE_BLOCK_REV_MAX_FULL=199
+
+#
+# Maximum Supported ESP32-S3 eFuse Block Revision (eFuse Block Rev v1.99)
+#
+# end of Chip revision
+
+#
+# MAC Config
+#
+# default:
+CONFIG_ESP_MAC_ADDR_UNIVERSE_WIFI_STA=y
+# default:
+CONFIG_ESP_MAC_ADDR_UNIVERSE_WIFI_AP=y
+# default:
+CONFIG_ESP_MAC_ADDR_UNIVERSE_BT=y
+# default:
+CONFIG_ESP_MAC_ADDR_UNIVERSE_ETH=y
+# default:
+CONFIG_ESP_MAC_UNIVERSAL_MAC_ADDRESSES_FOUR=y
+# default:
+CONFIG_ESP_MAC_UNIVERSAL_MAC_ADDRESSES=4
+# default:
+# CONFIG_ESP32S3_UNIVERSAL_MAC_ADDRESSES_TWO is not set
+# default:
+CONFIG_ESP32S3_UNIVERSAL_MAC_ADDRESSES_FOUR=y
+# default:
+CONFIG_ESP32S3_UNIVERSAL_MAC_ADDRESSES=4
+# default:
+# CONFIG_ESP_MAC_USE_CUSTOM_MAC_AS_BASE_MAC is not set
+# end of MAC Config
+
+#
+# Sleep Config
+#
+# default:
+CONFIG_ESP_SLEEP_FLASH_LEAKAGE_WORKAROUND=y
+# default:
+CONFIG_ESP_SLEEP_PSRAM_LEAKAGE_WORKAROUND=y
+# default:
+CONFIG_ESP_SLEEP_MSPI_NEED_ALL_IO_PU=y
+# default:
+CONFIG_ESP_SLEEP_RTC_BUS_ISO_WORKAROUND=y
+# default:
+CONFIG_ESP_SLEEP_GPIO_RESET_WORKAROUND=y
+# default:
+CONFIG_ESP_SLEEP_WAIT_FLASH_READY_EXTRA_DELAY=2000
+# default:
+# CONFIG_ESP_SLEEP_CACHE_SAFE_ASSERTION is not set
+# default:
+# CONFIG_ESP_SLEEP_DEBUG is not set
+# default:
+CONFIG_ESP_SLEEP_GPIO_ENABLE_INTERNAL_RESISTORS=y
+# end of Sleep Config
+
+#
+# RTC Clock Config
+#
+# default:
+CONFIG_RTC_CLK_SRC_INT_RC=y
+# default:
+# CONFIG_RTC_CLK_SRC_EXT_CRYS is not set
+# default:
+# CONFIG_RTC_CLK_SRC_EXT_OSC is not set
+# default:
+# CONFIG_RTC_CLK_SRC_INT_8MD256 is not set
+# default:
+CONFIG_RTC_CLK_CAL_CYCLES=1024
+# default:
+CONFIG_RTC_CLK_FUNC_IN_IRAM=y
+# default:
+CONFIG_RTC_TIME_FUNC_IN_IRAM=y
+# end of RTC Clock Config
+
+#
+# Peripheral Control
+#
+# default:
+CONFIG_ESP_PERIPH_CTRL_FUNC_IN_IRAM=y
+# default:
+CONFIG_ESP_REGI2C_CTRL_FUNC_IN_IRAM=y
+# end of Peripheral Control
+
+#
+# GDMA Configurations
+#
+# default:
+CONFIG_GDMA_CTRL_FUNC_IN_IRAM=y
+# default:
+CONFIG_GDMA_ISR_HANDLER_IN_IRAM=y
+# default:
+CONFIG_GDMA_OBJ_DRAM_SAFE=y
+# default:
+# CONFIG_GDMA_ENABLE_DEBUG_LOG is not set
+# default:
+# CONFIG_GDMA_ISR_IRAM_SAFE is not set
+# end of GDMA Configurations
+
+#
+# Main XTAL Config
+#
+# default:
+CONFIG_XTAL_FREQ_40=y
+# default:
+CONFIG_XTAL_FREQ=40
+# end of Main XTAL Config
+
+#
+# Power Supplier
+#
+
+#
+# Brownout Detector
+#
+# default:
+CONFIG_ESP_BROWNOUT_DET=y
+# default:
+CONFIG_ESP_BROWNOUT_DET_LVL_SEL_7=y
+# default:
+# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_6 is not set
+# default:
+# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_5 is not set
+# default:
+# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_4 is not set
+# default:
+# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_3 is not set
+# default:
+# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_2 is not set
+# default:
+# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_1 is not set
+# default:
+CONFIG_ESP_BROWNOUT_DET_LVL=7
+# default:
+CONFIG_ESP_BROWNOUT_USE_INTR=y
+# end of Brownout Detector
+# end of Power Supplier
+
+# default:
+CONFIG_ESP_SPI_BUS_LOCK_ISR_FUNCS_IN_IRAM=y
+# default:
+CONFIG_ESP_INTR_IN_IRAM=y
+# end of Hardware Settings
+
+#
+# ESP-Driver:LCD Controller Configurations
+#
+# default:
+# CONFIG_LCD_RGB_ISR_IRAM_SAFE is not set
+# default:
+# CONFIG_LCD_RGB_RESTART_IN_VSYNC is not set
+# default:
+# CONFIG_LCD_ENABLE_DEBUG_LOG is not set
+# end of ESP-Driver:LCD Controller Configurations
+
+#
+# ESP-MM: Memory Management Configurations
+#
+# default:
+# CONFIG_ESP_MM_CACHE_MSYNC_C2M_CHUNKED_OPS is not set
+# end of ESP-MM: Memory Management Configurations
+
+#
+# ESP NETIF Adapter
+#
+# default:
+CONFIG_ESP_NETIF_IP_LOST_TIMER_INTERVAL=120
+# default:
+# CONFIG_ESP_NETIF_PROVIDE_CUSTOM_IMPLEMENTATION is not set
+# default:
+CONFIG_ESP_NETIF_TCPIP_LWIP=y
+# default:
+# CONFIG_ESP_NETIF_LOOPBACK is not set
+# default:
+CONFIG_ESP_NETIF_USES_TCPIP_WITH_BSD_API=y
+# default:
+CONFIG_ESP_NETIF_REPORT_DATA_TRAFFIC=y
+# default:
+# CONFIG_ESP_NETIF_RECEIVE_REPORT_ERRORS is not set
+# default:
+# CONFIG_ESP_NETIF_L2_TAP is not set
+# default:
+# CONFIG_ESP_NETIF_BRIDGE_EN is not set
+# default:
+# CONFIG_ESP_NETIF_SET_DNS_PER_DEFAULT_NETIF is not set
+# end of ESP NETIF Adapter
+
+#
+# Partition API Configuration
+#
+# end of Partition API Configuration
+
+#
+# PHY
+#
+# default:
+CONFIG_ESP_PHY_ENABLED=y
+# default:
+CONFIG_ESP_PHY_CALIBRATION_AND_DATA_STORAGE=y
+# default:
+# CONFIG_ESP_PHY_INIT_DATA_IN_PARTITION is not set
+# default:
+CONFIG_ESP_PHY_MAX_WIFI_TX_POWER=20
+# default:
+CONFIG_ESP_PHY_MAX_TX_POWER=20
+# default:
+# CONFIG_ESP_PHY_REDUCE_TX_POWER is not set
+# default:
+CONFIG_ESP_PHY_ENABLE_USB=y
+# default:
+# CONFIG_ESP_PHY_ENABLE_CERT_TEST is not set
+# default:
+CONFIG_ESP_PHY_RF_CAL_PARTIAL=y
+# default:
+# CONFIG_ESP_PHY_RF_CAL_NONE is not set
+# default:
+# CONFIG_ESP_PHY_RF_CAL_FULL is not set
+# default:
+CONFIG_ESP_PHY_CALIBRATION_MODE=0
+# default:
+CONFIG_ESP_PHY_PLL_TRACK_PERIOD_MS=1000
+# default:
+# CONFIG_ESP_PHY_PLL_TRACK_DEBUG is not set
+# default:
+# CONFIG_ESP_PHY_RECORD_USED_TIME is not set
+# default:
+CONFIG_ESP_PHY_IRAM_OPT=y
+# default:
+# CONFIG_ESP_PHY_DEBUG is not set
+# default:
+CONFIG_ESP_PHY_ENABLE_VERSION_PRINT=y
+# end of PHY
+
+#
+# Power Management
+#
+# default:
+# CONFIG_PM_SLEEP_FUNC_IN_IRAM is not set
+# default:
+# CONFIG_PM_ENABLE is not set
+# default:
+# CONFIG_PM_SLP_IRAM_OPT is not set
+# default:
+CONFIG_PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP=y
+# default:
+CONFIG_PM_RESTORE_CACHE_TAGMEM_AFTER_LIGHT_SLEEP=y
+# default:
+CONFIG_PM_SLP_SPIRAM_HALFSLEEP_ENABLED=y
+# default:
+CONFIG_PM_SLP_SPIRAM_HALFSLEEP_EXIT_WAIT_DELAY=150
+# end of Power Management
+
+#
+# ESP PSRAM
+#
+CONFIG_SPIRAM=y
+
+#
+# SPI RAM config
+#
+CONFIG_SPIRAM_MODE_QUAD=y
+# CONFIG_SPIRAM_MODE_OCT is not set
+CONFIG_SPIRAM_TYPE_AUTO=y
+# CONFIG_SPIRAM_TYPE_ESPPSRAM16 is not set
+# CONFIG_SPIRAM_TYPE_ESPPSRAM32 is not set
+# CONFIG_SPIRAM_TYPE_ESPPSRAM64 is not set
+# default:
+CONFIG_SPIRAM_CLK_IO=30
+# default:
+CONFIG_SPIRAM_CS_IO=26
+# default:
+# CONFIG_SPIRAM_XIP_FROM_PSRAM is not set
+# default:
+# CONFIG_SPIRAM_FETCH_INSTRUCTIONS is not set
+# default:
+# CONFIG_SPIRAM_RODATA is not set
+# CONFIG_SPIRAM_SPEED_120M is not set
+# CONFIG_SPIRAM_SPEED_80M is not set
+CONFIG_SPIRAM_SPEED_40M=y
+# default:
+CONFIG_SPIRAM_SPEED=40
+# default:
+CONFIG_SPIRAM_BOOT_HW_INIT=y
+CONFIG_SPIRAM_BOOT_INIT=y
+# default:
+CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION=y
+# default:
+# CONFIG_SPIRAM_IGNORE_NOTFOUND is not set
+# CONFIG_SPIRAM_USE_MEMMAP is not set
+# CONFIG_SPIRAM_USE_CAPS_ALLOC is not set
+CONFIG_SPIRAM_USE_MALLOC=y
+# default:
+CONFIG_SPIRAM_MEMTEST=y
+CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=16384
+# default:
+# CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP is not set
+CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=32768
+# default:
+# CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY is not set
+# default:
+# CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY is not set
+# end of SPI RAM config
+# end of ESP PSRAM
+
+#
+# ESP Ringbuf
+#
+# default:
+# CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH is not set
+# end of ESP Ringbuf
+
+#
+# ESP-ROM
+#
+# default:
+CONFIG_ESP_ROM_PRINT_IN_IRAM=y
+# end of ESP-ROM
+
+#
+# ESP Security Specific
+#
+# end of ESP Security Specific
+
+#
+# ESP System Settings
+#
+# default:
+# CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_80 is not set
+# default:
+CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_160=y
+# default:
+# CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240 is not set
+# default:
+CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=160
+
+#
+# Cache config
+#
+# default:
+CONFIG_ESP32S3_INSTRUCTION_CACHE_16KB=y
+# default:
+# CONFIG_ESP32S3_INSTRUCTION_CACHE_32KB is not set
+# default:
+CONFIG_ESP32S3_INSTRUCTION_CACHE_SIZE=0x4000
+# default:
+# CONFIG_ESP32S3_INSTRUCTION_CACHE_4WAYS is not set
+# default:
+CONFIG_ESP32S3_INSTRUCTION_CACHE_8WAYS=y
+# default:
+CONFIG_ESP32S3_ICACHE_ASSOCIATED_WAYS=8
+# default:
+# CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_16B is not set
+# default:
+CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_32B=y
+# default:
+CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_SIZE=32
+# default:
+# CONFIG_ESP32S3_DATA_CACHE_16KB is not set
+# default:
+CONFIG_ESP32S3_DATA_CACHE_32KB=y
+# default:
+# CONFIG_ESP32S3_DATA_CACHE_64KB is not set
+# default:
+CONFIG_ESP32S3_DATA_CACHE_SIZE=0x8000
+# default:
+# CONFIG_ESP32S3_DATA_CACHE_4WAYS is not set
+# default:
+CONFIG_ESP32S3_DATA_CACHE_8WAYS=y
+# default:
+CONFIG_ESP32S3_DCACHE_ASSOCIATED_WAYS=8
+# default:
+# CONFIG_ESP32S3_DATA_CACHE_LINE_16B is not set
+# default:
+CONFIG_ESP32S3_DATA_CACHE_LINE_32B=y
+# default:
+# CONFIG_ESP32S3_DATA_CACHE_LINE_64B is not set
+# default:
+CONFIG_ESP32S3_DATA_CACHE_LINE_SIZE=32
+# end of Cache config
+
+#
+# Memory
+#
+# default:
+# CONFIG_ESP32S3_RTCDATA_IN_FAST_MEM is not set
+# default:
+# CONFIG_ESP32S3_USE_FIXED_STATIC_RAM_SIZE is not set
+# end of Memory
+
+#
+# Trace memory
+#
+# default:
+# CONFIG_ESP32S3_TRAX is not set
+# default:
+CONFIG_ESP32S3_TRACEMEM_RESERVE_DRAM=0x0
+# end of Trace memory
+
+# default:
+CONFIG_ESP_SYSTEM_IN_IRAM=y
+# default:
+# CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT is not set
+# default:
+CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT=y
+# default:
+# CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT is not set
+# default:
+# CONFIG_ESP_SYSTEM_PANIC_GDBSTUB is not set
+# default:
+CONFIG_ESP_SYSTEM_PANIC_REBOOT_DELAY_SECONDS=0
+# default:
+CONFIG_ESP_SYSTEM_RTC_FAST_MEM_AS_HEAP_DEPCHECK=y
+# default:
+CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP=y
+
+#
+# Memory protection
+#
+# default:
+CONFIG_ESP_SYSTEM_MEMPROT_FEATURE=y
+# default:
+CONFIG_ESP_SYSTEM_MEMPROT_FEATURE_LOCK=y
+# end of Memory protection
+
+# default:
+CONFIG_ESP_SYSTEM_EVENT_QUEUE_SIZE=32
+# default:
+CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=2304
+# default:
+CONFIG_ESP_MAIN_TASK_STACK_SIZE=3584
+# default:
+CONFIG_ESP_MAIN_TASK_AFFINITY_CPU0=y
+# default:
+# CONFIG_ESP_MAIN_TASK_AFFINITY_CPU1 is not set
+# default:
+# CONFIG_ESP_MAIN_TASK_AFFINITY_NO_AFFINITY is not set
+# default:
+CONFIG_ESP_MAIN_TASK_AFFINITY=0x0
+# default:
+CONFIG_ESP_MINIMAL_SHARED_STACK_SIZE=2048
+# default:
+CONFIG_ESP_CONSOLE_UART_DEFAULT=y
+# default:
+# CONFIG_ESP_CONSOLE_USB_CDC is not set
+# default:
+# CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG is not set
+# default:
+# CONFIG_ESP_CONSOLE_UART_CUSTOM is not set
+# default:
+# CONFIG_ESP_CONSOLE_NONE is not set
+# default:
+# CONFIG_ESP_CONSOLE_SECONDARY_NONE is not set
+# default:
+CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG=y
+# default:
+CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED=y
+# default:
+CONFIG_ESP_CONSOLE_UART=y
+# default:
+CONFIG_ESP_CONSOLE_UART_NUM=0
+# default:
+CONFIG_ESP_CONSOLE_ROM_SERIAL_PORT_NUM=0
+# default:
+CONFIG_ESP_CONSOLE_UART_BAUDRATE=115200
+# default:
+CONFIG_ESP_INT_WDT=y
+# default:
+CONFIG_ESP_INT_WDT_TIMEOUT_MS=800
+# default:
+CONFIG_ESP_INT_WDT_CHECK_CPU1=y
+# default:
+CONFIG_ESP_TASK_WDT_EN=y
+# default:
+CONFIG_ESP_TASK_WDT_INIT=y
+# default:
+# CONFIG_ESP_TASK_WDT_PANIC is not set
+# default:
+CONFIG_ESP_TASK_WDT_TIMEOUT_S=5
+# default:
+CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0=y
+# default:
+CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1=y
+# default:
+# CONFIG_ESP_PANIC_HANDLER_IRAM is not set
+# default:
+# CONFIG_ESP_DEBUG_STUBS_ENABLE is not set
+# default:
+CONFIG_ESP_DEBUG_OCDAWARE=y
+# default:
+CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_4=y
+# default:
+CONFIG_ESP_SYSTEM_BBPLL_RECALIB=y
+# end of ESP System Settings
+
+#
+# IPC (Inter-Processor Call)
+#
+# default:
+CONFIG_ESP_IPC_ENABLE=y
+# default:
+CONFIG_ESP_IPC_TASK_STACK_SIZE=1280
+# default:
+CONFIG_ESP_IPC_USES_CALLERS_PRIORITY=y
+# default:
+CONFIG_ESP_IPC_ISR_ENABLE=y
+# end of IPC (Inter-Processor Call)
+
+#
+# ESP Timer (High Resolution Timer)
+#
+# default:
+CONFIG_ESP_TIMER_IN_IRAM=y
+# default:
+# CONFIG_ESP_TIMER_PROFILING is not set
+# default:
+CONFIG_ESP_TIME_FUNCS_USE_RTC_TIMER=y
+# default:
+CONFIG_ESP_TIME_FUNCS_USE_ESP_TIMER=y
+# default:
+CONFIG_ESP_TIMER_TASK_STACK_SIZE=3584
+# default:
+CONFIG_ESP_TIMER_INTERRUPT_LEVEL=1
+# default:
+# CONFIG_ESP_TIMER_SHOW_EXPERIMENTAL is not set
+# default:
+CONFIG_ESP_TIMER_TASK_AFFINITY=0x0
+# default:
+CONFIG_ESP_TIMER_TASK_AFFINITY_CPU0=y
+# default:
+CONFIG_ESP_TIMER_ISR_AFFINITY_CPU0=y
+# default:
+# CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD is not set
+# default:
+CONFIG_ESP_TIMER_IMPL_SYSTIMER=y
+# end of ESP Timer (High Resolution Timer)
+
+#
+# Wi-Fi
+#
+# default:
+CONFIG_ESP_WIFI_ENABLED=y
+# default:
+CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=10
+# default:
+CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=32
+# default:
+# CONFIG_ESP_WIFI_STATIC_TX_BUFFER is not set
+# default:
+CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER=y
+# default:
+CONFIG_ESP_WIFI_TX_BUFFER_TYPE=1
+# default:
+CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER_NUM=32
+# default:
+CONFIG_ESP_WIFI_STATIC_RX_MGMT_BUFFER=y
+# default:
+# CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUFFER is not set
+# default:
+CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUF=0
+# default:
+CONFIG_ESP_WIFI_RX_MGMT_BUF_NUM_DEF=5
+# default:
+# CONFIG_ESP_WIFI_CSI_ENABLED is not set
+# default:
+CONFIG_ESP_WIFI_AMPDU_TX_ENABLED=y
+# default:
+CONFIG_ESP_WIFI_TX_BA_WIN=6
+# default:
+CONFIG_ESP_WIFI_AMPDU_RX_ENABLED=y
+# default:
+CONFIG_ESP_WIFI_RX_BA_WIN=6
+# default:
+CONFIG_ESP_WIFI_NVS_ENABLED=y
+# default:
+CONFIG_ESP_WIFI_TASK_PINNED_TO_CORE_0=y
+# default:
+# CONFIG_ESP_WIFI_TASK_PINNED_TO_CORE_1 is not set
+# default:
+CONFIG_ESP_WIFI_SOFTAP_BEACON_MAX_LEN=752
+# default:
+CONFIG_ESP_WIFI_MGMT_SBUF_NUM=32
+# default:
+CONFIG_ESP_WIFI_IRAM_OPT=y
+# default:
+# CONFIG_ESP_WIFI_EXTRA_IRAM_OPT is not set
+# default:
+CONFIG_ESP_WIFI_RX_IRAM_OPT=y
+# default:
+CONFIG_ESP_WIFI_ENABLE_WPA3_SAE=y
+# default:
+CONFIG_ESP_WIFI_ENABLE_SAE_PK=y
+# default:
+CONFIG_ESP_WIFI_ENABLE_SAE_H2E=y
+# default:
+CONFIG_ESP_WIFI_SOFTAP_SAE_SUPPORT=y
+# default:
+CONFIG_ESP_WIFI_ENABLE_WPA3_OWE_STA=y
+# default:
+# CONFIG_ESP_WIFI_SLP_IRAM_OPT is not set
+# default:
+CONFIG_ESP_WIFI_SLP_DEFAULT_MIN_ACTIVE_TIME=50
+# default:
+# CONFIG_ESP_WIFI_BSS_MAX_IDLE_SUPPORT is not set
+# default:
+CONFIG_ESP_WIFI_SLP_DEFAULT_MAX_ACTIVE_TIME=10
+# default:
+CONFIG_ESP_WIFI_SLP_DEFAULT_WAIT_BROADCAST_DATA_TIME=15
+# default:
+# CONFIG_ESP_WIFI_FTM_ENABLE is not set
+# default:
+CONFIG_ESP_WIFI_STA_DISCONNECTED_PM_ENABLE=y
+# default:
+# CONFIG_ESP_WIFI_GCMP_SUPPORT is not set
+# default:
+CONFIG_ESP_WIFI_GMAC_SUPPORT=y
+# default:
+CONFIG_ESP_WIFI_SOFTAP_SUPPORT=y
+# default:
+# CONFIG_ESP_WIFI_SLP_BEACON_LOST_OPT is not set
+# default:
+CONFIG_ESP_WIFI_ESPNOW_MAX_ENCRYPT_NUM=7
+# default:
+CONFIG_ESP_WIFI_MBEDTLS_CRYPTO=y
+# default:
+CONFIG_ESP_WIFI_MBEDTLS_TLS_CLIENT=y
+# default:
+# CONFIG_ESP_WIFI_WAPI_PSK is not set
+# default:
+# CONFIG_ESP_WIFI_SUITE_B_192 is not set
+# default:
+# CONFIG_ESP_WIFI_11KV_SUPPORT is not set
+# default:
+# CONFIG_ESP_WIFI_MBO_SUPPORT is not set
+# default:
+# CONFIG_ESP_WIFI_DPP_SUPPORT is not set
+# default:
+# CONFIG_ESP_WIFI_P256_ACCEL is not set
+# default:
+# CONFIG_ESP_WIFI_11R_SUPPORT is not set
+# default:
+# CONFIG_ESP_WIFI_WPS_SOFTAP_REGISTRAR is not set
+
+#
+# WPS Configuration Options
+#
+# default:
+# CONFIG_ESP_WIFI_WPS_STRICT is not set
+# default:
+# CONFIG_ESP_WIFI_WPS_PASSPHRASE is not set
+# default:
+# CONFIG_ESP_WIFI_WPS_RECONNECT_ON_FAIL is not set
+# end of WPS Configuration Options
+
+# default:
+# CONFIG_ESP_WIFI_DEBUG_PRINT is not set
+# default:
+CONFIG_ESP_WIFI_ENTERPRISE_SUPPORT=y
+# default:
+# CONFIG_ESP_WIFI_ENT_FREE_DYNAMIC_BUFFER is not set
+# default:
+# CONFIG_ESP_WIFI_PASSIVE_HIDDEN_AP_SUPPORT is not set
+# end of Wi-Fi
+
+#
+# Core dump
+#
+# default:
+# CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH is not set
+# default:
+# CONFIG_ESP_COREDUMP_ENABLE_TO_UART is not set
+# default:
+CONFIG_ESP_COREDUMP_ENABLE_TO_NONE=y
+# end of Core dump
+
+#
+# FAT Filesystem support
+#
+# default:
+CONFIG_FATFS_VOLUME_COUNT=2
+# default:
+CONFIG_FATFS_LFN_NONE=y
+# default:
+# CONFIG_FATFS_LFN_HEAP is not set
+# default:
+# CONFIG_FATFS_LFN_STACK is not set
+# default:
+# CONFIG_FATFS_SECTOR_512 is not set
+# default:
+CONFIG_FATFS_SECTOR_4096=y
+# default:
+# CONFIG_FATFS_CODEPAGE_DYNAMIC is not set
+# default:
+CONFIG_FATFS_CODEPAGE_437=y
+# default:
+# CONFIG_FATFS_CODEPAGE_720 is not set
+# default:
+# CONFIG_FATFS_CODEPAGE_737 is not set
+# default:
+# CONFIG_FATFS_CODEPAGE_771 is not set
+# default:
+# CONFIG_FATFS_CODEPAGE_775 is not set
+# default:
+# CONFIG_FATFS_CODEPAGE_850 is not set
+# default:
+# CONFIG_FATFS_CODEPAGE_852 is not set
+# default:
+# CONFIG_FATFS_CODEPAGE_855 is not set
+# default:
+# CONFIG_FATFS_CODEPAGE_857 is not set
+# default:
+# CONFIG_FATFS_CODEPAGE_860 is not set
+# default:
+# CONFIG_FATFS_CODEPAGE_861 is not set
+# default:
+# CONFIG_FATFS_CODEPAGE_862 is not set
+# default:
+# CONFIG_FATFS_CODEPAGE_863 is not set
+# default:
+# CONFIG_FATFS_CODEPAGE_864 is not set
+# default:
+# CONFIG_FATFS_CODEPAGE_865 is not set
+# default:
+# CONFIG_FATFS_CODEPAGE_866 is not set
+# default:
+# CONFIG_FATFS_CODEPAGE_869 is not set
+# default:
+# CONFIG_FATFS_CODEPAGE_932 is not set
+# default:
+# CONFIG_FATFS_CODEPAGE_936 is not set
+# default:
+# CONFIG_FATFS_CODEPAGE_949 is not set
+# default:
+# CONFIG_FATFS_CODEPAGE_950 is not set
+# default:
+CONFIG_FATFS_CODEPAGE=437
+# default:
+CONFIG_FATFS_FS_LOCK=0
+# default:
+CONFIG_FATFS_TIMEOUT_MS=10000
+# default:
+CONFIG_FATFS_PER_FILE_CACHE=y
+# default:
+CONFIG_FATFS_ALLOC_PREFER_EXTRAM=y
+# default:
+# CONFIG_FATFS_USE_FASTSEEK is not set
+# default:
+CONFIG_FATFS_USE_STRFUNC_NONE=y
+# default:
+# CONFIG_FATFS_USE_STRFUNC_WITHOUT_CRLF_CONV is not set
+# default:
+# CONFIG_FATFS_USE_STRFUNC_WITH_CRLF_CONV is not set
+# default:
+CONFIG_FATFS_VFS_FSTAT_BLKSIZE=0
+# default:
+# CONFIG_FATFS_IMMEDIATE_FSYNC is not set
+# default:
+# CONFIG_FATFS_USE_LABEL is not set
+# default:
+CONFIG_FATFS_LINK_LOCK=y
+# default:
+# CONFIG_FATFS_USE_DYN_BUFFERS is not set
+
+#
+# File system free space calculation behavior
+#
+# default:
+CONFIG_FATFS_DONT_TRUST_FREE_CLUSTER_CNT=0
+# default:
+CONFIG_FATFS_DONT_TRUST_LAST_ALLOC=0
+# end of File system free space calculation behavior
+# end of FAT Filesystem support
+
+#
+# FreeRTOS
+#
+
+#
+# Kernel
+#
+# default:
+# CONFIG_FREERTOS_SMP is not set
+# default:
+# CONFIG_FREERTOS_UNICORE is not set
+CONFIG_FREERTOS_HZ=1000
+# default:
+# CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE is not set
+# default:
+# CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL is not set
+# default:
+CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY=y
+# default:
+CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=1
+# default:
+CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=1536
+# default:
+# CONFIG_FREERTOS_USE_IDLE_HOOK is not set
+# default:
+# CONFIG_FREERTOS_USE_TICK_HOOK is not set
+# default:
+CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16
+# default:
+# CONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITY is not set
+# default:
+CONFIG_FREERTOS_USE_TIMERS=y
+# default:
+CONFIG_FREERTOS_TIMER_SERVICE_TASK_NAME="Tmr Svc"
+# default:
+# CONFIG_FREERTOS_TIMER_TASK_AFFINITY_CPU0 is not set
+# default:
+# CONFIG_FREERTOS_TIMER_TASK_AFFINITY_CPU1 is not set
+# default:
+CONFIG_FREERTOS_TIMER_TASK_NO_AFFINITY=y
+# default:
+CONFIG_FREERTOS_TIMER_SERVICE_TASK_CORE_AFFINITY=0x7FFFFFFF
+# default:
+CONFIG_FREERTOS_TIMER_TASK_PRIORITY=1
+# default:
+CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=2048
+# default:
+CONFIG_FREERTOS_TIMER_QUEUE_LENGTH=10
+# default:
+CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0
+# default:
+CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=1
+# default:
+# CONFIG_FREERTOS_USE_TRACE_FACILITY is not set
+# default:
+# CONFIG_FREERTOS_USE_LIST_DATA_INTEGRITY_CHECK_BYTES is not set
+# default:
+# CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS is not set
+# default:
+# CONFIG_FREERTOS_USE_APPLICATION_TASK_TAG is not set
+# end of Kernel
+
+#
+# Port
+#
+# default:
+CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=y
+# default:
+# CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK is not set
+# default:
+CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS=y
+# default:
+# CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK is not set
+# default:
+# CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP is not set
+# default:
+CONFIG_FREERTOS_CHECK_MUTEX_GIVEN_BY_OWNER=y
+# default:
+CONFIG_FREERTOS_ISR_STACKSIZE=1536
+# default:
+CONFIG_FREERTOS_INTERRUPT_BACKTRACE=y
+# default:
+# CONFIG_FREERTOS_FPU_IN_ISR is not set
+# default:
+CONFIG_FREERTOS_TICK_SUPPORT_SYSTIMER=y
+# default:
+CONFIG_FREERTOS_CORETIMER_SYSTIMER_LVL1=y
+# default:
+# CONFIG_FREERTOS_CORETIMER_SYSTIMER_LVL3 is not set
+# default:
+CONFIG_FREERTOS_SYSTICK_USES_SYSTIMER=y
+# default:
+# CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH is not set
+# default:
+# CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE is not set
+# end of Port
+
+#
+# Extra
+#
+CONFIG_FREERTOS_TASK_CREATE_ALLOW_EXT_MEM=y
+# end of Extra
+
+# default:
+CONFIG_FREERTOS_PORT=y
+# default:
+CONFIG_FREERTOS_NO_AFFINITY=0x7FFFFFFF
+# default:
+CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y
+# default:
+CONFIG_FREERTOS_DEBUG_OCDAWARE=y
+# default:
+CONFIG_FREERTOS_ENABLE_TASK_SNAPSHOT=y
+# default:
+CONFIG_FREERTOS_PLACE_SNAPSHOT_FUNS_INTO_FLASH=y
+# default:
+CONFIG_FREERTOS_NUMBER_OF_CORES=2
+# default:
+CONFIG_FREERTOS_IN_IRAM=y
+# end of FreeRTOS
+
+#
+# Hardware Abstraction Layer (HAL) and Low Level (LL)
+#
+# default:
+CONFIG_HAL_ASSERTION_EQUALS_SYSTEM=y
+# default:
+# CONFIG_HAL_ASSERTION_DISABLE is not set
+# default:
+# CONFIG_HAL_ASSERTION_SILENT is not set
+# default:
+# CONFIG_HAL_ASSERTION_ENABLE is not set
+# default:
+CONFIG_HAL_DEFAULT_ASSERTION_LEVEL=2
+# default:
+CONFIG_HAL_WDT_USE_ROM_IMPL=y
+# end of Hardware Abstraction Layer (HAL) and Low Level (LL)
+
+#
+# Heap memory debugging
+#
+# default:
+CONFIG_HEAP_POISONING_DISABLED=y
+# default:
+# CONFIG_HEAP_POISONING_LIGHT is not set
+# default:
+# CONFIG_HEAP_POISONING_COMPREHENSIVE is not set
+# default:
+CONFIG_HEAP_TRACING_OFF=y
+# default:
+# CONFIG_HEAP_TRACING_STANDALONE is not set
+# default:
+# CONFIG_HEAP_TRACING_TOHOST is not set
+# default:
+# CONFIG_HEAP_USE_HOOKS is not set
+# default:
+# CONFIG_HEAP_TASK_TRACKING is not set
+# default:
+# CONFIG_HEAP_ABORT_WHEN_ALLOCATION_FAILS is not set
+# default:
+# CONFIG_HEAP_PLACE_FUNCTION_INTO_FLASH is not set
+# end of Heap memory debugging
+
+#
+# Log
+#
+# default:
+CONFIG_LOG_VERSION_1=y
+# default:
+# CONFIG_LOG_VERSION_2 is not set
+# default:
+CONFIG_LOG_VERSION=1
+
+#
+# Log Level
+#
+# default:
+# CONFIG_LOG_DEFAULT_LEVEL_NONE is not set
+# default:
+# CONFIG_LOG_DEFAULT_LEVEL_ERROR is not set
+# default:
+# CONFIG_LOG_DEFAULT_LEVEL_WARN is not set
+# default:
+CONFIG_LOG_DEFAULT_LEVEL_INFO=y
+# default:
+# CONFIG_LOG_DEFAULT_LEVEL_DEBUG is not set
+# default:
+# CONFIG_LOG_DEFAULT_LEVEL_VERBOSE is not set
+# default:
+CONFIG_LOG_DEFAULT_LEVEL=3
+# default:
+CONFIG_LOG_MAXIMUM_EQUALS_DEFAULT=y
+# default:
+# CONFIG_LOG_MAXIMUM_LEVEL_DEBUG is not set
+# default:
+# CONFIG_LOG_MAXIMUM_LEVEL_VERBOSE is not set
+# default:
+CONFIG_LOG_MAXIMUM_LEVEL=3
+
+#
+# Level Settings
+#
+# default:
+# CONFIG_LOG_MASTER_LEVEL is not set
+# default:
+CONFIG_LOG_DYNAMIC_LEVEL_CONTROL=y
+# default:
+# CONFIG_LOG_TAG_LEVEL_IMPL_NONE is not set
+# default:
+# CONFIG_LOG_TAG_LEVEL_IMPL_LINKED_LIST is not set
+# default:
+CONFIG_LOG_TAG_LEVEL_IMPL_CACHE_AND_LINKED_LIST=y
+# default:
+# CONFIG_LOG_TAG_LEVEL_CACHE_ARRAY is not set
+# default:
+CONFIG_LOG_TAG_LEVEL_CACHE_BINARY_MIN_HEAP=y
+# default:
+CONFIG_LOG_TAG_LEVEL_IMPL_CACHE_SIZE=31
+# end of Level Settings
+# end of Log Level
+
+#
+# Format
+#
+# default:
+# CONFIG_LOG_COLORS is not set
+# default:
+CONFIG_LOG_TIMESTAMP_SOURCE_RTOS=y
+# default:
+# CONFIG_LOG_TIMESTAMP_SOURCE_SYSTEM is not set
+# end of Format
+
+#
+# Settings
+#
+# default:
+CONFIG_LOG_MODE_TEXT_EN=y
+# default:
+CONFIG_LOG_MODE_TEXT=y
+# end of Settings
+
+# default:
+CONFIG_LOG_IN_IRAM=y
+# end of Log
+
+#
+# LWIP
+#
+# default:
+CONFIG_LWIP_ENABLE=y
+# default:
+CONFIG_LWIP_LOCAL_HOSTNAME="espressif"
+# default:
+CONFIG_LWIP_TCPIP_TASK_PRIO=18
+# default:
+# CONFIG_LWIP_TCPIP_CORE_LOCKING is not set
+# default:
+# CONFIG_LWIP_CHECK_THREAD_SAFETY is not set
+# default:
+CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES=y
+# default:
+# CONFIG_LWIP_L2_TO_L3_COPY is not set
+# default:
+# CONFIG_LWIP_IRAM_OPTIMIZATION is not set
+# default:
+# CONFIG_LWIP_EXTRA_IRAM_OPTIMIZATION is not set
+# default:
+CONFIG_LWIP_TIMERS_ONDEMAND=y
+# default:
+CONFIG_LWIP_ND6=y
+# default:
+# CONFIG_LWIP_FORCE_ROUTER_FORWARDING is not set
+# default:
+CONFIG_LWIP_MAX_SOCKETS=10
+# default:
+# CONFIG_LWIP_USE_ONLY_LWIP_SELECT is not set
+# default:
+# CONFIG_LWIP_SO_LINGER is not set
+# default:
+CONFIG_LWIP_SO_REUSE=y
+# default:
+CONFIG_LWIP_SO_REUSE_RXTOALL=y
+# default:
+CONFIG_LWIP_SO_RCVBUF=y
+# default:
+# CONFIG_LWIP_NETBUF_RECVINFO is not set
+# default:
+CONFIG_LWIP_IP_DEFAULT_TTL=64
+# default:
+CONFIG_LWIP_IP4_FRAG=y
+# default:
+CONFIG_LWIP_IP6_FRAG=y
+# default:
+# CONFIG_LWIP_IP4_REASSEMBLY is not set
+# default:
+# CONFIG_LWIP_IP6_REASSEMBLY is not set
+# default:
+CONFIG_LWIP_IP_REASS_MAX_PBUFS=10
+# default:
+# CONFIG_LWIP_IP_FORWARD is not set
+# default:
+# CONFIG_LWIP_STATS is not set
+# default:
+CONFIG_LWIP_ESP_GRATUITOUS_ARP=y
+# default:
+CONFIG_LWIP_GARP_TMR_INTERVAL=60
+# default:
+CONFIG_LWIP_ESP_MLDV6_REPORT=y
+# default:
+CONFIG_LWIP_MLDV6_TMR_INTERVAL=40
+# default:
+CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=32
+# default:
+CONFIG_LWIP_DHCP_DOES_ARP_CHECK=y
+# default:
+# CONFIG_LWIP_DHCP_DOES_ACD_CHECK is not set
+# default:
+# CONFIG_LWIP_DHCP_DOES_NOT_CHECK_OFFERED_IP is not set
+# default:
+# CONFIG_LWIP_DHCP_DISABLE_CLIENT_ID is not set
+# default:
+CONFIG_LWIP_DHCP_DISABLE_VENDOR_CLASS_ID=y
+# default:
+# CONFIG_LWIP_DHCP_RESTORE_LAST_IP is not set
+# default:
+CONFIG_LWIP_DHCP_OPTIONS_LEN=69
+# default:
+CONFIG_LWIP_NUM_NETIF_CLIENT_DATA=0
+# default:
+CONFIG_LWIP_DHCP_COARSE_TIMER_SECS=1
+
+#
+# DHCP server
+#
+# default:
+CONFIG_LWIP_DHCPS=y
+# default:
+CONFIG_LWIP_DHCPS_LEASE_UNIT=60
+# default:
+CONFIG_LWIP_DHCPS_MAX_STATION_NUM=8
+# default:
+CONFIG_LWIP_DHCPS_STATIC_ENTRIES=y
+# default:
+CONFIG_LWIP_DHCPS_ADD_DNS=y
+# end of DHCP server
+
+# default:
+# CONFIG_LWIP_AUTOIP is not set
+# default:
+CONFIG_LWIP_IPV4=y
+# default:
+CONFIG_LWIP_IPV6=y
+# default:
+# CONFIG_LWIP_IPV6_AUTOCONFIG is not set
+# default:
+CONFIG_LWIP_IPV6_NUM_ADDRESSES=3
+# default:
+# CONFIG_LWIP_IPV6_FORWARD is not set
+# default:
+# CONFIG_LWIP_NETIF_STATUS_CALLBACK is not set
+# default:
+CONFIG_LWIP_NETIF_LOOPBACK=y
+# default:
+CONFIG_LWIP_LOOPBACK_MAX_PBUFS=8
+
+#
+# TCP
+#
+# default:
+CONFIG_LWIP_MAX_ACTIVE_TCP=16
+# default:
+CONFIG_LWIP_MAX_LISTENING_TCP=16
+# default:
+CONFIG_LWIP_TCP_HIGH_SPEED_RETRANSMISSION=y
+# default:
+CONFIG_LWIP_TCP_MAXRTX=12
+# default:
+CONFIG_LWIP_TCP_SYNMAXRTX=12
+# default:
+CONFIG_LWIP_TCP_MSS=1440
+# default:
+CONFIG_LWIP_TCP_TMR_INTERVAL=250
+# default:
+CONFIG_LWIP_TCP_MSL=60000
+# default:
+CONFIG_LWIP_TCP_FIN_WAIT_TIMEOUT=20000
+# default:
+CONFIG_LWIP_TCP_SND_BUF_DEFAULT=5760
+# default:
+CONFIG_LWIP_TCP_WND_DEFAULT=5760
+# default:
+CONFIG_LWIP_TCP_RECVMBOX_SIZE=6
+# default:
+CONFIG_LWIP_TCP_ACCEPTMBOX_SIZE=6
+# default:
+CONFIG_LWIP_TCP_QUEUE_OOSEQ=y
+# default:
+CONFIG_LWIP_TCP_OOSEQ_TIMEOUT=6
+# default:
+CONFIG_LWIP_TCP_OOSEQ_MAX_PBUFS=4
+# default:
+# CONFIG_LWIP_TCP_SACK_OUT is not set
+# default:
+CONFIG_LWIP_TCP_OVERSIZE_MSS=y
+# default:
+# CONFIG_LWIP_TCP_OVERSIZE_QUARTER_MSS is not set
+# default:
+# CONFIG_LWIP_TCP_OVERSIZE_DISABLE is not set
+# default:
+CONFIG_LWIP_TCP_RTO_TIME=1500
+# end of TCP
+
+#
+# UDP
+#
+# default:
+CONFIG_LWIP_MAX_UDP_PCBS=16
+# default:
+CONFIG_LWIP_UDP_RECVMBOX_SIZE=6
+# end of UDP
+
+#
+# Checksums
+#
+# default:
+# CONFIG_LWIP_CHECKSUM_CHECK_IP is not set
+# default:
+# CONFIG_LWIP_CHECKSUM_CHECK_UDP is not set
+# default:
+CONFIG_LWIP_CHECKSUM_CHECK_ICMP=y
+# end of Checksums
+
+# default:
+CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=3072
+# default:
+CONFIG_LWIP_TCPIP_TASK_AFFINITY_NO_AFFINITY=y
+# default:
+# CONFIG_LWIP_TCPIP_TASK_AFFINITY_CPU0 is not set
+# default:
+# CONFIG_LWIP_TCPIP_TASK_AFFINITY_CPU1 is not set
+# default:
+CONFIG_LWIP_TCPIP_TASK_AFFINITY=0x7FFFFFFF
+# default:
+CONFIG_LWIP_IPV6_MEMP_NUM_ND6_QUEUE=3
+# default:
+CONFIG_LWIP_IPV6_ND6_NUM_NEIGHBORS=5
+# default:
+CONFIG_LWIP_IPV6_ND6_NUM_PREFIXES=5
+# default:
+CONFIG_LWIP_IPV6_ND6_NUM_ROUTERS=3
+# default:
+CONFIG_LWIP_IPV6_ND6_NUM_DESTINATIONS=10
+# default:
+# CONFIG_LWIP_IPV6_ND6_ROUTE_INFO_OPTION_SUPPORT is not set
+# default:
+CONFIG_LWIP_PPP_SUPPORT=y
+# default:
+CONFIG_LWIP_PPP_ENABLE_IPV4=y
+# default:
+CONFIG_LWIP_PPP_ENABLE_IPV6=y
+# default:
+# CONFIG_LWIP_PPP_NOTIFY_PHASE_SUPPORT is not set
+# default:
+# CONFIG_LWIP_PPP_PAP_SUPPORT is not set
+# default:
+# CONFIG_LWIP_PPP_CHAP_SUPPORT is not set
+# default:
+# CONFIG_LWIP_PPP_MSCHAP_SUPPORT is not set
+# default:
+# CONFIG_LWIP_PPP_MPPE_SUPPORT is not set
+# default:
+# CONFIG_LWIP_PPP_SERVER_SUPPORT is not set
+# default:
+CONFIG_LWIP_PPP_VJ_HEADER_COMPRESSION=y
+# default:
+# CONFIG_LWIP_ENABLE_LCP_ECHO is not set
+# default:
+# CONFIG_LWIP_PPP_DEBUG_ON is not set
+# default:
+# CONFIG_LWIP_USE_EXTERNAL_MBEDTLS is not set
+# default:
+# CONFIG_LWIP_SLIP_SUPPORT is not set
+
+#
+# ICMP
+#
+# default:
+CONFIG_LWIP_ICMP=y
+# default:
+# CONFIG_LWIP_MULTICAST_PING is not set
+# default:
+# CONFIG_LWIP_BROADCAST_PING is not set
+# end of ICMP
+
+#
+# LWIP RAW API
+#
+# default:
+CONFIG_LWIP_MAX_RAW_PCBS=16
+# end of LWIP RAW API
+
+#
+# SNTP
+#
+# default:
+CONFIG_LWIP_SNTP_MAX_SERVERS=1
+# default:
+# CONFIG_LWIP_DHCP_GET_NTP_SRV is not set
+# default:
+CONFIG_LWIP_SNTP_UPDATE_DELAY=3600000
+# default:
+CONFIG_LWIP_SNTP_STARTUP_DELAY=y
+# default:
+CONFIG_LWIP_SNTP_MAXIMUM_STARTUP_DELAY=5000
+# end of SNTP
+
+#
+# DNS
+#
+# default:
+CONFIG_LWIP_DNS_MAX_HOST_IP=1
+# default:
+CONFIG_LWIP_DNS_MAX_SERVERS=3
+# default:
+# CONFIG_LWIP_FALLBACK_DNS_SERVER_SUPPORT is not set
+# default:
+# CONFIG_LWIP_DNS_SETSERVER_WITH_NETIF is not set
+# default:
+# CONFIG_LWIP_USE_ESP_GETADDRINFO is not set
+# end of DNS
+
+# default:
+CONFIG_LWIP_BRIDGEIF_MAX_PORTS=7
+# default:
+CONFIG_LWIP_ESP_LWIP_ASSERT=y
+
+#
+# Hooks
+#
+# default:
+# CONFIG_LWIP_HOOK_TCP_ISN_NONE is not set
+# default:
+CONFIG_LWIP_HOOK_TCP_ISN_DEFAULT=y
+# default:
+# CONFIG_LWIP_HOOK_TCP_ISN_CUSTOM is not set
+# default:
+CONFIG_LWIP_HOOK_IP6_ROUTE_NONE=y
+# default:
+# CONFIG_LWIP_HOOK_IP6_ROUTE_DEFAULT is not set
+# default:
+# CONFIG_LWIP_HOOK_IP6_ROUTE_CUSTOM is not set
+# default:
+CONFIG_LWIP_HOOK_ND6_GET_GW_NONE=y
+# default:
+# CONFIG_LWIP_HOOK_ND6_GET_GW_DEFAULT is not set
+# default:
+# CONFIG_LWIP_HOOK_ND6_GET_GW_CUSTOM is not set
+# default:
+CONFIG_LWIP_HOOK_IP6_SELECT_SRC_ADDR_NONE=y
+# default:
+# CONFIG_LWIP_HOOK_IP6_SELECT_SRC_ADDR_DEFAULT is not set
+# default:
+# CONFIG_LWIP_HOOK_IP6_SELECT_SRC_ADDR_CUSTOM is not set
+# default:
+CONFIG_LWIP_HOOK_DHCP_EXTRA_OPTION_NONE=y
+# default:
+# CONFIG_LWIP_HOOK_DHCP_EXTRA_OPTION_DEFAULT is not set
+# default:
+# CONFIG_LWIP_HOOK_DHCP_EXTRA_OPTION_CUSTOM is not set
+# default:
+CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_NONE=y
+# default:
+# CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_DEFAULT is not set
+# default:
+# CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_CUSTOM is not set
+# default:
+CONFIG_LWIP_HOOK_DNS_EXT_RESOLVE_NONE=y
+# default:
+# CONFIG_LWIP_HOOK_DNS_EXT_RESOLVE_CUSTOM is not set
+# default:
+# CONFIG_LWIP_HOOK_IP6_INPUT_NONE is not set
+# default:
+CONFIG_LWIP_HOOK_IP6_INPUT_DEFAULT=y
+# default:
+# CONFIG_LWIP_HOOK_IP6_INPUT_CUSTOM is not set
+# end of Hooks
+
+# default:
+# CONFIG_LWIP_DEBUG is not set
+# end of LWIP
+
+#
+# mbedTLS
+#
+# default:
+CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
+# default:
+# CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC is not set
+# default:
+# CONFIG_MBEDTLS_DEFAULT_MEM_ALLOC is not set
+# default:
+# CONFIG_MBEDTLS_CUSTOM_MEM_ALLOC is not set
+# default:
+CONFIG_MBEDTLS_ASYMMETRIC_CONTENT_LEN=y
+# default:
+CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=16384
+# default:
+CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096
+# default:
+# CONFIG_MBEDTLS_DYNAMIC_BUFFER is not set
+# default:
+# CONFIG_MBEDTLS_DEBUG is not set
+
+#
+# mbedTLS v3.x related
+#
+# default:
+# CONFIG_MBEDTLS_SSL_PROTO_TLS1_3 is not set
+# default:
+# CONFIG_MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH is not set
+# default:
+# CONFIG_MBEDTLS_X509_TRUSTED_CERT_CALLBACK is not set
+# default:
+# CONFIG_MBEDTLS_SSL_CONTEXT_SERIALIZATION is not set
+# default:
+CONFIG_MBEDTLS_SSL_KEEP_PEER_CERTIFICATE=y
+# default:
+# CONFIG_MBEDTLS_SSL_KEYING_MATERIAL_EXPORT is not set
+# default:
+CONFIG_MBEDTLS_PKCS7_C=y
+# end of mbedTLS v3.x related
+
+#
+# Certificate Bundle
+#
+# default:
+CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=y
+# default:
+CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=y
+# default:
+# CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN is not set
+# default:
+# CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_NONE is not set
+# default:
+# CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE is not set
+# default:
+# CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEPRECATED_LIST is not set
+# default:
+CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_MAX_CERTS=200
+# end of Certificate Bundle
+
+# default:
+# CONFIG_MBEDTLS_ECP_RESTARTABLE is not set
+# default:
+CONFIG_MBEDTLS_CMAC_C=y
+# default:
+CONFIG_MBEDTLS_HARDWARE_AES=y
+# default:
+CONFIG_MBEDTLS_AES_USE_INTERRUPT=y
+# default:
+CONFIG_MBEDTLS_AES_INTERRUPT_LEVEL=0
+# default:
+CONFIG_MBEDTLS_GCM_SUPPORT_NON_AES_CIPHER=y
+# default:
+CONFIG_MBEDTLS_HARDWARE_MPI=y
+# default:
+# CONFIG_MBEDTLS_LARGE_KEY_SOFTWARE_MPI is not set
+# default:
+CONFIG_MBEDTLS_MPI_USE_INTERRUPT=y
+# default:
+CONFIG_MBEDTLS_MPI_INTERRUPT_LEVEL=0
+# default:
+CONFIG_MBEDTLS_HARDWARE_SHA=y
+# default:
+CONFIG_MBEDTLS_ROM_MD5=y
+# default:
+# CONFIG_MBEDTLS_ATCA_HW_ECDSA_SIGN is not set
+# default:
+# CONFIG_MBEDTLS_ATCA_HW_ECDSA_VERIFY is not set
+# default:
+CONFIG_MBEDTLS_HAVE_TIME=y
+# default:
+# CONFIG_MBEDTLS_PLATFORM_TIME_ALT is not set
+# default:
+# CONFIG_MBEDTLS_HAVE_TIME_DATE is not set
+# default:
+CONFIG_MBEDTLS_ECDSA_DETERMINISTIC=y
+# default:
+CONFIG_MBEDTLS_SHA1_C=y
+# default:
+CONFIG_MBEDTLS_SHA512_C=y
+# default:
+# CONFIG_MBEDTLS_SHA3_C is not set
+# default:
+CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT=y
+# default:
+# CONFIG_MBEDTLS_TLS_SERVER_ONLY is not set
+# default:
+# CONFIG_MBEDTLS_TLS_CLIENT_ONLY is not set
+# default:
+# CONFIG_MBEDTLS_TLS_DISABLED is not set
+# default:
+CONFIG_MBEDTLS_TLS_SERVER=y
+# default:
+CONFIG_MBEDTLS_TLS_CLIENT=y
+# default:
+CONFIG_MBEDTLS_TLS_ENABLED=y
+
+#
+# TLS Key Exchange Methods
+#
+CONFIG_MBEDTLS_PSK_MODES=y
+CONFIG_MBEDTLS_KEY_EXCHANGE_PSK=y
+# default:
+CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_PSK=y
+# default:
+CONFIG_MBEDTLS_KEY_EXCHANGE_RSA_PSK=y
+# default:
+CONFIG_MBEDTLS_KEY_EXCHANGE_RSA=y
+# default:
+CONFIG_MBEDTLS_KEY_EXCHANGE_ELLIPTIC_CURVE=y
+# default:
+CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_RSA=y
+# default:
+CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA=y
+# default:
+CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA=y
+# default:
+CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_RSA=y
+# end of TLS Key Exchange Methods
+
+# default:
+CONFIG_MBEDTLS_SSL_RENEGOTIATION=y
+# default:
+CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y
+# default:
+# CONFIG_MBEDTLS_SSL_PROTO_GMTSSL1_1 is not set
+# default:
+# CONFIG_MBEDTLS_SSL_PROTO_DTLS is not set
+# default:
+CONFIG_MBEDTLS_SSL_ALPN=y
+# default:
+CONFIG_MBEDTLS_CLIENT_SSL_SESSION_TICKETS=y
+# default:
+CONFIG_MBEDTLS_SERVER_SSL_SESSION_TICKETS=y
+
+#
+# Symmetric Ciphers
+#
+# default:
+CONFIG_MBEDTLS_AES_C=y
+# default:
+# CONFIG_MBEDTLS_CAMELLIA_C is not set
+# default:
+# CONFIG_MBEDTLS_DES_C is not set
+# default:
+# CONFIG_MBEDTLS_BLOWFISH_C is not set
+# default:
+# CONFIG_MBEDTLS_XTEA_C is not set
+# default:
+CONFIG_MBEDTLS_CCM_C=y
+# default:
+CONFIG_MBEDTLS_GCM_C=y
+# default:
+# CONFIG_MBEDTLS_NIST_KW_C is not set
+# end of Symmetric Ciphers
+
+# default:
+# CONFIG_MBEDTLS_RIPEMD160_C is not set
+
+#
+# Certificates
+#
+# default:
+CONFIG_MBEDTLS_PEM_PARSE_C=y
+# default:
+CONFIG_MBEDTLS_PEM_WRITE_C=y
+# default:
+CONFIG_MBEDTLS_X509_CRL_PARSE_C=y
+# default:
+CONFIG_MBEDTLS_X509_CSR_PARSE_C=y
+# end of Certificates
+
+# default:
+CONFIG_MBEDTLS_ECP_C=y
+# default:
+CONFIG_MBEDTLS_PK_PARSE_EC_EXTENDED=y
+# default:
+CONFIG_MBEDTLS_PK_PARSE_EC_COMPRESSED=y
+# default:
+# CONFIG_MBEDTLS_DHM_C is not set
+# default:
+CONFIG_MBEDTLS_ECDH_C=y
+# default:
+CONFIG_MBEDTLS_ECDSA_C=y
+# default:
+# CONFIG_MBEDTLS_ECJPAKE_C is not set
+# default:
+CONFIG_MBEDTLS_ECP_DP_SECP192R1_ENABLED=y
+# default:
+CONFIG_MBEDTLS_ECP_DP_SECP224R1_ENABLED=y
+# default:
+CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED=y
+# default:
+CONFIG_MBEDTLS_ECP_DP_SECP384R1_ENABLED=y
+# default:
+CONFIG_MBEDTLS_ECP_DP_SECP521R1_ENABLED=y
+# default:
+CONFIG_MBEDTLS_ECP_DP_SECP192K1_ENABLED=y
+# default:
+CONFIG_MBEDTLS_ECP_DP_SECP224K1_ENABLED=y
+# default:
+CONFIG_MBEDTLS_ECP_DP_SECP256K1_ENABLED=y
+# default:
+CONFIG_MBEDTLS_ECP_DP_BP256R1_ENABLED=y
+# default:
+CONFIG_MBEDTLS_ECP_DP_BP384R1_ENABLED=y
+# default:
+CONFIG_MBEDTLS_ECP_DP_BP512R1_ENABLED=y
+# default:
+CONFIG_MBEDTLS_ECP_DP_CURVE25519_ENABLED=y
+# default:
+CONFIG_MBEDTLS_ECP_NIST_OPTIM=y
+# default:
+# CONFIG_MBEDTLS_ECP_FIXED_POINT_OPTIM is not set
+# default:
+# CONFIG_MBEDTLS_POLY1305_C is not set
+# default:
+# CONFIG_MBEDTLS_CHACHA20_C is not set
+# default:
+# CONFIG_MBEDTLS_HKDF_C is not set
+# default:
+# CONFIG_MBEDTLS_THREADING_C is not set
+# default:
+CONFIG_MBEDTLS_ERROR_STRINGS=y
+# default:
+CONFIG_MBEDTLS_FS_IO=y
+# default:
+# CONFIG_MBEDTLS_ALLOW_WEAK_CERTIFICATE_VERIFICATION is not set
+# end of mbedTLS
+
+#
+# ESP-MQTT Configurations
+#
+# default:
+CONFIG_MQTT_PROTOCOL_311=y
+# default:
+# CONFIG_MQTT_PROTOCOL_5 is not set
+# default:
+CONFIG_MQTT_TRANSPORT_SSL=y
+# default:
+CONFIG_MQTT_TRANSPORT_WEBSOCKET=y
+# default:
+CONFIG_MQTT_TRANSPORT_WEBSOCKET_SECURE=y
+# default:
+# CONFIG_MQTT_MSG_ID_INCREMENTAL is not set
+# default:
+# CONFIG_MQTT_SKIP_PUBLISH_IF_DISCONNECTED is not set
+# default:
+# CONFIG_MQTT_REPORT_DELETED_MESSAGES is not set
+# default:
+# CONFIG_MQTT_USE_CUSTOM_CONFIG is not set
+# default:
+# CONFIG_MQTT_TASK_CORE_SELECTION_ENABLED is not set
+# default:
+# CONFIG_MQTT_CUSTOM_OUTBOX is not set
+# end of ESP-MQTT Configurations
+
+#
+# LibC
+#
+# default:
+CONFIG_LIBC_NEWLIB=y
+# default:
+# CONFIG_LIBC_PICOLIBC is not set
+# default:
+CONFIG_LIBC_MISC_IN_IRAM=y
+# default:
+CONFIG_LIBC_LOCKS_PLACE_IN_IRAM=y
+# default:
+CONFIG_LIBC_STDOUT_LINE_ENDING_CRLF=y
+# default:
+# CONFIG_LIBC_STDOUT_LINE_ENDING_LF is not set
+# default:
+# CONFIG_LIBC_STDOUT_LINE_ENDING_CR is not set
+# default:
+# CONFIG_LIBC_STDIN_LINE_ENDING_CRLF is not set
+# default:
+# CONFIG_LIBC_STDIN_LINE_ENDING_LF is not set
+# default:
+CONFIG_LIBC_STDIN_LINE_ENDING_CR=y
+# default:
+# CONFIG_LIBC_NEWLIB_NANO_FORMAT is not set
+# default:
+CONFIG_LIBC_TIME_SYSCALL_USE_RTC_HRT=y
+# default:
+# CONFIG_LIBC_TIME_SYSCALL_USE_RTC is not set
+# default:
+# CONFIG_LIBC_TIME_SYSCALL_USE_HRT is not set
+# default:
+# CONFIG_LIBC_TIME_SYSCALL_USE_NONE is not set
+# default:
+CONFIG_LIBC_ASSERT_BUFFER_SIZE=200
+# end of LibC
+
+# default:
+CONFIG_STDATOMIC_S32C1I_SPIRAM_WORKAROUND=y
+
+#
+# NVS
+#
+# default:
+# CONFIG_NVS_ENCRYPTION is not set
+# default:
+# CONFIG_NVS_ASSERT_ERROR_CHECK is not set
+# default:
+# CONFIG_NVS_LEGACY_DUP_KEYS_COMPATIBILITY is not set
+# default:
+# CONFIG_NVS_ALLOCATE_CACHE_IN_SPIRAM is not set
+# end of NVS
+
+#
+# OpenThread
+#
+# default:
+# CONFIG_OPENTHREAD_ENABLED is not set
+
+#
+# OpenThread Spinel
+#
+# default:
+# CONFIG_OPENTHREAD_SPINEL_ONLY is not set
+# end of OpenThread Spinel
+
+# default:
+# CONFIG_OPENTHREAD_DEBUG is not set
+# end of OpenThread
+
+#
+# Protocomm
+#
+# default:
+CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_0=y
+# default:
+CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_1=y
+# default:
+CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_2=y
+# default:
+CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_PATCH_VERSION=y
+# end of Protocomm
+
+#
+# PThreads
+#
+# default:
+CONFIG_PTHREAD_TASK_PRIO_DEFAULT=5
+# default:
+CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072
+# default:
+CONFIG_PTHREAD_STACK_MIN=768
+# default:
+CONFIG_PTHREAD_DEFAULT_CORE_NO_AFFINITY=y
+# default:
+# CONFIG_PTHREAD_DEFAULT_CORE_0 is not set
+# default:
+# CONFIG_PTHREAD_DEFAULT_CORE_1 is not set
+# default:
+CONFIG_PTHREAD_TASK_CORE_DEFAULT=-1
+# default:
+CONFIG_PTHREAD_TASK_NAME_DEFAULT="pthread"
+# end of PThreads
+
+#
+# MMU Config
+#
+# default:
+CONFIG_MMU_PAGE_SIZE_64KB=y
+# default:
+CONFIG_MMU_PAGE_MODE="64KB"
+# default:
+CONFIG_MMU_PAGE_SIZE=0x10000
+# end of MMU Config
+
+#
+# Main Flash configuration
+#
+
+#
+# SPI Flash behavior when brownout
+#
+# default:
+CONFIG_SPI_FLASH_BROWNOUT_RESET_XMC=y
+# default:
+CONFIG_SPI_FLASH_BROWNOUT_RESET=y
+# end of SPI Flash behavior when brownout
+
+#
+# Optional and Experimental Features (READ DOCS FIRST)
+#
+
+#
+# Features here require specific hardware (READ DOCS FIRST!)
+#
+# default:
+# CONFIG_SPI_FLASH_HPM_ENA is not set
+# default:
+CONFIG_SPI_FLASH_HPM_AUTO=y
+# default:
+# CONFIG_SPI_FLASH_HPM_DIS is not set
+# default:
+CONFIG_SPI_FLASH_HPM_ON=y
+# default:
+CONFIG_SPI_FLASH_HPM_DC_AUTO=y
+# default:
+# CONFIG_SPI_FLASH_HPM_DC_DISABLE is not set
+# default:
+# CONFIG_SPI_FLASH_AUTO_SUSPEND is not set
+# default:
+CONFIG_SPI_FLASH_SUSPEND_TSUS_VAL_US=50
+# default:
+# CONFIG_SPI_FLASH_FORCE_ENABLE_XMC_C_SUSPEND is not set
+# default:
+# CONFIG_SPI_FLASH_FORCE_ENABLE_C6_H2_SUSPEND is not set
+# default:
+CONFIG_SPI_FLASH_PLACE_FUNCTIONS_IN_IRAM=y
+# end of Optional and Experimental Features (READ DOCS FIRST)
+# end of Main Flash configuration
+
+#
+# SPI Flash driver
+#
+# default:
+# CONFIG_SPI_FLASH_VERIFY_WRITE is not set
+# default:
+# CONFIG_SPI_FLASH_ENABLE_COUNTERS is not set
+# default:
+CONFIG_SPI_FLASH_ROM_DRIVER_PATCH=y
+# default:
+# CONFIG_SPI_FLASH_ROM_IMPL is not set
+# default:
+CONFIG_SPI_FLASH_DANGEROUS_WRITE_ABORTS=y
+# default:
+# CONFIG_SPI_FLASH_DANGEROUS_WRITE_FAILS is not set
+# default:
+# CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED is not set
+# default:
+# CONFIG_SPI_FLASH_BYPASS_BLOCK_ERASE is not set
+# default:
+CONFIG_SPI_FLASH_YIELD_DURING_ERASE=y
+# default:
+CONFIG_SPI_FLASH_ERASE_YIELD_DURATION_MS=20
+# default:
+CONFIG_SPI_FLASH_ERASE_YIELD_TICKS=1
+# default:
+CONFIG_SPI_FLASH_WRITE_CHUNK_SIZE=8192
+# default:
+# CONFIG_SPI_FLASH_SIZE_OVERRIDE is not set
+# default:
+# CONFIG_SPI_FLASH_CHECK_ERASE_TIMEOUT_DISABLED is not set
+# default:
+# CONFIG_SPI_FLASH_OVERRIDE_CHIP_DRIVER_LIST is not set
+
+#
+# Auto-detect flash chips
+#
+# default:
+CONFIG_SPI_FLASH_VENDOR_XMC_SUPPORT_ENABLED=y
+# default:
+CONFIG_SPI_FLASH_VENDOR_GD_SUPPORT_ENABLED=y
+# default:
+CONFIG_SPI_FLASH_VENDOR_ISSI_SUPPORT_ENABLED=y
+# default:
+CONFIG_SPI_FLASH_VENDOR_MXIC_SUPPORT_ENABLED=y
+# default:
+CONFIG_SPI_FLASH_VENDOR_WINBOND_SUPPORT_ENABLED=y
+# default:
+CONFIG_SPI_FLASH_VENDOR_BOYA_SUPPORT_ENABLED=y
+# default:
+CONFIG_SPI_FLASH_VENDOR_TH_SUPPORT_ENABLED=y
+# default:
+CONFIG_SPI_FLASH_SUPPORT_ISSI_CHIP=y
+# default:
+CONFIG_SPI_FLASH_SUPPORT_MXIC_CHIP=y
+# default:
+CONFIG_SPI_FLASH_SUPPORT_GD_CHIP=y
+# default:
+CONFIG_SPI_FLASH_SUPPORT_WINBOND_CHIP=y
+# default:
+CONFIG_SPI_FLASH_SUPPORT_BOYA_CHIP=y
+# default:
+CONFIG_SPI_FLASH_SUPPORT_TH_CHIP=y
+# default:
+CONFIG_SPI_FLASH_SUPPORT_MXIC_OPI_CHIP=y
+# end of Auto-detect flash chips
+
+# default:
+CONFIG_SPI_FLASH_ENABLE_ENCRYPTED_READ_WRITE=y
+# end of SPI Flash driver
+
+#
+# SPIFFS Configuration
+#
+# default:
+CONFIG_SPIFFS_MAX_PARTITIONS=3
+
+#
+# SPIFFS Cache Configuration
+#
+# default:
+CONFIG_SPIFFS_CACHE=y
+# default:
+CONFIG_SPIFFS_CACHE_WR=y
+# default:
+# CONFIG_SPIFFS_CACHE_STATS is not set
+# end of SPIFFS Cache Configuration
+
+# default:
+CONFIG_SPIFFS_PAGE_CHECK=y
+# default:
+CONFIG_SPIFFS_GC_MAX_RUNS=10
+# default:
+# CONFIG_SPIFFS_GC_STATS is not set
+# default:
+CONFIG_SPIFFS_PAGE_SIZE=256
+# default:
+CONFIG_SPIFFS_OBJ_NAME_LEN=32
+# default:
+# CONFIG_SPIFFS_FOLLOW_SYMLINKS is not set
+# default:
+CONFIG_SPIFFS_USE_MAGIC=y
+# default:
+CONFIG_SPIFFS_USE_MAGIC_LENGTH=y
+# default:
+CONFIG_SPIFFS_META_LENGTH=4
+# default:
+CONFIG_SPIFFS_USE_MTIME=y
+
+#
+# Debug Configuration
+#
+# default:
+# CONFIG_SPIFFS_DBG is not set
+# default:
+# CONFIG_SPIFFS_API_DBG is not set
+# default:
+# CONFIG_SPIFFS_GC_DBG is not set
+# default:
+# CONFIG_SPIFFS_CACHE_DBG is not set
+# default:
+# CONFIG_SPIFFS_CHECK_DBG is not set
+# default:
+# CONFIG_SPIFFS_TEST_VISUALISATION is not set
+# end of Debug Configuration
+# end of SPIFFS Configuration
+
+#
+# TCP Transport
+#
+
+#
+# Websocket
+#
+# default:
+CONFIG_WS_TRANSPORT=y
+# default:
+CONFIG_WS_BUFFER_SIZE=1024
+# default:
+# CONFIG_WS_DYNAMIC_BUFFER is not set
+# end of Websocket
+# end of TCP Transport
+
+#
+# Ultra Low Power (ULP) Co-processor
+#
+# default:
+# CONFIG_ULP_COPROC_ENABLED is not set
+
+#
+# ULP Debugging Options
+#
+# end of ULP Debugging Options
+# end of Ultra Low Power (ULP) Co-processor
+
+#
+# Unity unit testing library
+#
+# default:
+CONFIG_UNITY_ENABLE_FLOAT=y
+# default:
+CONFIG_UNITY_ENABLE_DOUBLE=y
+# default:
+# CONFIG_UNITY_ENABLE_64BIT is not set
+# default:
+# CONFIG_UNITY_ENABLE_COLOR is not set
+# default:
+CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
+# default:
+# CONFIG_UNITY_ENABLE_FIXTURE is not set
+# default:
+# CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL is not set
+# default:
+# CONFIG_UNITY_TEST_ORDER_BY_FILE_PATH_AND_LINE is not set
+# end of Unity unit testing library
+
+#
+# USB-OTG
+#
+# default:
+CONFIG_USB_HOST_CONTROL_TRANSFER_MAX_SIZE=256
+# default:
+CONFIG_USB_HOST_HW_BUFFER_BIAS_BALANCED=y
+# default:
+# CONFIG_USB_HOST_HW_BUFFER_BIAS_IN is not set
+# default:
+# CONFIG_USB_HOST_HW_BUFFER_BIAS_PERIODIC_OUT is not set
+
+#
+# Hub Driver Configuration
+#
+
+#
+# Root Port configuration
+#
+# default:
+CONFIG_USB_HOST_DEBOUNCE_DELAY_MS=250
+# default:
+CONFIG_USB_HOST_RESET_HOLD_MS=30
+# default:
+CONFIG_USB_HOST_RESET_RECOVERY_MS=30
+# default:
+CONFIG_USB_HOST_SET_ADDR_RECOVERY_MS=10
+# end of Root Port configuration
+
+# default:
+# CONFIG_USB_HOST_HUBS_SUPPORTED is not set
+# end of Hub Driver Configuration
+
+# default:
+# CONFIG_USB_HOST_ENABLE_ENUM_FILTER_CALLBACK is not set
+# default:
+CONFIG_USB_OTG_SUPPORTED=y
+# end of USB-OTG
+
+#
+# Virtual file system
+#
+# default:
+CONFIG_VFS_SUPPORT_IO=y
+# default:
+CONFIG_VFS_SUPPORT_DIR=y
+# default:
+CONFIG_VFS_SUPPORT_SELECT=y
+# default:
+CONFIG_VFS_SUPPRESS_SELECT_DEBUG_OUTPUT=y
+# default:
+# CONFIG_VFS_SELECT_IN_RAM is not set
+# default:
+CONFIG_VFS_SUPPORT_TERMIOS=y
+# default:
+CONFIG_VFS_MAX_COUNT=8
+
+#
+# Host File System I/O (Semihosting)
+#
+# default:
+CONFIG_VFS_SEMIHOSTFS_MAX_MOUNT_POINTS=1
+# end of Host File System I/O (Semihosting)
+
+# default:
+CONFIG_VFS_INITIALIZE_DEV_NULL=y
+# end of Virtual file system
+
+#
+# Wear Levelling
+#
+# default:
+# CONFIG_WL_SECTOR_SIZE_512 is not set
+# default:
+CONFIG_WL_SECTOR_SIZE_4096=y
+# default:
+CONFIG_WL_SECTOR_SIZE=4096
+# end of Wear Levelling
+
+#
+# Wi-Fi Provisioning Manager
+#
+# default:
+CONFIG_WIFI_PROV_SCAN_MAX_ENTRIES=16
+# default:
+CONFIG_WIFI_PROV_AUTOSTOP_TIMEOUT=30
+# default:
+# CONFIG_WIFI_PROV_BLE_BONDING is not set
+# default:
+CONFIG_WIFI_PROV_BLE_SEC_CONN=y
+# default:
+# CONFIG_WIFI_PROV_BLE_FORCE_ENCRYPTION is not set
+# default:
+# CONFIG_WIFI_PROV_BLE_NOTIFY is not set
+# default:
+# CONFIG_WIFI_PROV_KEEP_BLE_ON_AFTER_PROV is not set
+# default:
+CONFIG_WIFI_PROV_STA_ALL_CHANNEL_SCAN=y
+# default:
+# CONFIG_WIFI_PROV_STA_FAST_SCAN is not set
+# end of Wi-Fi Provisioning Manager
+
+#
+# cJSON
+#
+# default:
+CONFIG_CJSON_NESTING_LIMIT=1000
+# default:
+CONFIG_CJSON_CIRCULAR_LIMIT=10000
+# end of cJSON
+
+#
+# ESP-DL Configuration
+#
+
+#
+# Vision/Image: Pixel Convert Support
+#
+# default:
+CONFIG_PIX_CVT_RGB565_TO_RGB565_SUPPORT=y
+# default:
+CONFIG_PIX_CVT_RGB565_TO_RGB888_SUPPORT=y
+# default:
+CONFIG_PIX_CVT_RGB565_TO_GRAY_SUPPORT=y
+# default:
+CONFIG_PIX_CVT_RGB565_TO_HSV_SUPPORT=y
+# default:
+CONFIG_PIX_CVT_RGB888_TO_RGB888_SUPPORT=y
+# default:
+CONFIG_PIX_CVT_RGB888_TO_RGB565_SUPPORT=y
+# default:
+CONFIG_PIX_CVT_RGB888_TO_GRAY_SUPPORT=y
+# default:
+CONFIG_PIX_CVT_RGB888_TO_HSV_SUPPORT=y
+# default:
+CONFIG_PIX_CVT_GRAY_TO_GRAY_SUPPORT=y
+# default:
+CONFIG_PIX_CVT_HSV_TO_HSV_MASK_SUPPORT=y
+# default:
+CONFIG_PIX_CVT_YUV_TO_RGB888_SUPPORT=y
+# default:
+CONFIG_PIX_CVT_YUV_TO_RGB565_SUPPORT=y
+# default:
+CONFIG_PIX_CVT_YUV_TO_GRAY_SUPPORT=y
+# default:
+CONFIG_PIX_CVT_YUV_TO_HSV_SUPPORT=y
+# default:
+CONFIG_PIX_CVT_YUV_TO_YUV_SUPPORT=y
+# end of Vision/Image: Pixel Convert Support
+# end of ESP-DL Configuration
+
+#
+# DSP Library
+#
+# default:
+CONFIG_DSP_OPTIMIZATIONS_SUPPORTED=y
+# default:
+# CONFIG_DSP_ANSI is not set
+# default:
+CONFIG_DSP_OPTIMIZED=y
+# default:
+CONFIG_DSP_OPTIMIZATION=1
+# default:
+# CONFIG_DSP_MAX_FFT_SIZE_512 is not set
+# default:
+# CONFIG_DSP_MAX_FFT_SIZE_1024 is not set
+# default:
+# CONFIG_DSP_MAX_FFT_SIZE_2048 is not set
+# default:
+CONFIG_DSP_MAX_FFT_SIZE_4096=y
+# default:
+# CONFIG_DSP_MAX_FFT_SIZE_8192 is not set
+# default:
+# CONFIG_DSP_MAX_FFT_SIZE_16384 is not set
+# default:
+# CONFIG_DSP_MAX_FFT_SIZE_32768 is not set
+# default:
+CONFIG_DSP_MAX_FFT_SIZE=4096
+# end of DSP Library
+
+#
+# Zigbee
+#
+# default:
+# CONFIG_ZB_ENABLED is not set
+# end of Zigbee
+
+#
+# esp-modem
+#
+# default:
+CONFIG_ESP_MODEM_CMUX_DEFRAGMENT_PAYLOAD=y
+# default:
+# CONFIG_ESP_MODEM_USE_INFLATABLE_BUFFER_IF_NEEDED is not set
+# default:
+CONFIG_ESP_MODEM_CMUX_DELAY_AFTER_DLCI_SETUP=0
+# default:
+CONFIG_ESP_MODEM_CMUX_DELAY_AFTER_NETIF_DISCONNECT=0
+# default:
+# CONFIG_ESP_MODEM_CMUX_USE_SHORT_PAYLOADS_ONLY is not set
+# default:
+# CONFIG_ESP_MODEM_ADD_CUSTOM_MODULE is not set
+# default:
+CONFIG_ESP_MODEM_C_API_STR_MAX=128
+# default:
+# CONFIG_ESP_MODEM_URC_HANDLER is not set
+# default:
+# CONFIG_ESP_MODEM_PPP_ESCAPE_BEFORE_EXIT is not set
+# default:
+# CONFIG_ESP_MODEM_ADD_DEBUG_LOGS is not set
+# default:
+# CONFIG_ESP_MODEM_ENABLE_DEVELOPMENT_MODE is not set
+# default:
+CONFIG_ESP_MODEM_USE_PPP_MODE=y
+# end of esp-modem
+
+#
+# mDNS
+#
+# default:
+CONFIG_MDNS_MAX_INTERFACES=3
+# default:
+CONFIG_MDNS_MAX_SERVICES=10
+# default:
+CONFIG_MDNS_TASK_PRIORITY=1
+# default:
+CONFIG_MDNS_ACTION_QUEUE_LEN=16
+# default:
+CONFIG_MDNS_TASK_STACK_SIZE=4096
+# default:
+# CONFIG_MDNS_TASK_AFFINITY_NO_AFFINITY is not set
+# default:
+CONFIG_MDNS_TASK_AFFINITY_CPU0=y
+# default:
+# CONFIG_MDNS_TASK_AFFINITY_CPU1 is not set
+# default:
+CONFIG_MDNS_TASK_AFFINITY=0x0
+
+#
+# MDNS Memory Configuration
+#
+# default:
+# CONFIG_MDNS_TASK_CREATE_FROM_SPIRAM is not set
+# default:
+CONFIG_MDNS_TASK_CREATE_FROM_INTERNAL=y
+# default:
+# CONFIG_MDNS_MEMORY_ALLOC_SPIRAM is not set
+# default:
+CONFIG_MDNS_MEMORY_ALLOC_INTERNAL=y
+# default:
+# CONFIG_MDNS_MEMORY_CUSTOM_IMPL is not set
+# end of MDNS Memory Configuration
+
+# default:
+CONFIG_MDNS_SERVICE_ADD_TIMEOUT_MS=2000
+# default:
+CONFIG_MDNS_TIMER_PERIOD_MS=100
+# default:
+# CONFIG_MDNS_NETWORKING_SOCKET is not set
+# default:
+# CONFIG_MDNS_SKIP_SUPPRESSING_OWN_QUERIES is not set
+# default:
+# CONFIG_MDNS_ENABLE_DEBUG_PRINTS is not set
+# default:
+CONFIG_MDNS_ENABLE_CONSOLE_CLI=y
+# default:
+# CONFIG_MDNS_RESPOND_REVERSE_QUERIES is not set
+# default:
+CONFIG_MDNS_MULTIPLE_INSTANCE=y
+
+#
+# MDNS Predefined interfaces
+#
+# default:
+CONFIG_MDNS_PREDEF_NETIF_STA=y
+# default:
+CONFIG_MDNS_PREDEF_NETIF_AP=y
+# default:
+CONFIG_MDNS_PREDEF_NETIF_ETH=y
+# end of MDNS Predefined interfaces
+# end of mDNS
+
+#
+# Network Provisioning Manager
+#
+# default:
+CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI=y
+# default:
+CONFIG_NETWORK_PROV_SCAN_MAX_ENTRIES=16
+# default:
+CONFIG_NETWORK_PROV_AUTOSTOP_TIMEOUT=30
+# default:
+# CONFIG_NETWORK_PROV_BLE_BONDING is not set
+# default:
+CONFIG_NETWORK_PROV_BLE_SEC_CONN=y
+# default:
+# CONFIG_NETWORK_PROV_BLE_FORCE_ENCRYPTION is not set
+# default:
+# CONFIG_NETWORK_PROV_KEEP_BLE_ON_AFTER_PROV is not set
+# default:
+CONFIG_NETWORK_PROV_WIFI_STA_ALL_CHANNEL_SCAN=y
+# default:
+# CONFIG_NETWORK_PROV_WIFI_STA_FAST_SCAN is not set
+# end of Network Provisioning Manager
+
+#
+# LittleFS
+#
+# default:
+# CONFIG_LITTLEFS_SDMMC_SUPPORT is not set
+# default:
+CONFIG_LITTLEFS_MAX_PARTITIONS=3
+# default:
+CONFIG_LITTLEFS_PAGE_SIZE=256
+# default:
+CONFIG_LITTLEFS_OBJ_NAME_LEN=64
+# default:
+CONFIG_LITTLEFS_READ_SIZE=128
+# default:
+CONFIG_LITTLEFS_WRITE_SIZE=128
+# default:
+CONFIG_LITTLEFS_LOOKAHEAD_SIZE=128
+# default:
+CONFIG_LITTLEFS_CACHE_SIZE=512
+# default:
+CONFIG_LITTLEFS_BLOCK_CYCLES=512
+# default:
+CONFIG_LITTLEFS_USE_MTIME=y
+# default:
+# CONFIG_LITTLEFS_USE_ONLY_HASH is not set
+# default:
+# CONFIG_LITTLEFS_HUMAN_READABLE is not set
+# default:
+CONFIG_LITTLEFS_MTIME_USE_SECONDS=y
+# default:
+# CONFIG_LITTLEFS_MTIME_USE_NONCE is not set
+# default:
+# CONFIG_LITTLEFS_SPIFFS_COMPAT is not set
+# default:
+# CONFIG_LITTLEFS_FLUSH_FILE_EVERY_WRITE is not set
+# default:
+# CONFIG_LITTLEFS_FCNTL_GET_PATH is not set
+# default:
+# CONFIG_LITTLEFS_MULTIVERSION is not set
+# default:
+# CONFIG_LITTLEFS_MALLOC_STRATEGY_DISABLE is not set
+# default:
+CONFIG_LITTLEFS_MALLOC_STRATEGY_DEFAULT=y
+# default:
+# CONFIG_LITTLEFS_MALLOC_STRATEGY_INTERNAL is not set
+# default:
+# CONFIG_LITTLEFS_MALLOC_STRATEGY_SPIRAM is not set
+# default:
+# CONFIG_LITTLEFS_MALLOC_STRATEGY_SPIRAM_PREFER is not set
+# default:
+CONFIG_LITTLEFS_ASSERTS=y
+# default:
+# CONFIG_LITTLEFS_MMAP_PARTITION is not set
+# default:
+# CONFIG_LITTLEFS_WDT_RESET is not set
+# end of LittleFS
+# end of Component config
+
+# default:
+# CONFIG_IDF_EXPERIMENTAL_FEATURES is not set
+
+# Deprecated options for backward compatibility
+# CONFIG_APP_BUILD_TYPE_ELF_RAM is not set
+# CONFIG_NO_BLOBS is not set
+# CONFIG_APP_ROLLBACK_ENABLE is not set
+# CONFIG_LOG_BOOTLOADER_LEVEL_NONE is not set
+# CONFIG_LOG_BOOTLOADER_LEVEL_ERROR is not set
+# CONFIG_LOG_BOOTLOADER_LEVEL_WARN is not set
+CONFIG_LOG_BOOTLOADER_LEVEL_INFO=y
+# CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG is not set
+# CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE is not set
+CONFIG_LOG_BOOTLOADER_LEVEL=3
+# CONFIG_FLASH_ENCRYPTION_ENABLED is not set
+# CONFIG_FLASHMODE_QIO is not set
+# CONFIG_FLASHMODE_QOUT is not set
+CONFIG_FLASHMODE_DIO=y
+# CONFIG_FLASHMODE_DOUT is not set
+CONFIG_MONITOR_BAUD=115200
+CONFIG_OPTIMIZATION_LEVEL_DEBUG=y
+CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG=y
+CONFIG_COMPILER_OPTIMIZATION_DEFAULT=y
+# CONFIG_OPTIMIZATION_LEVEL_RELEASE is not set
+# CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE is not set
+CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y
+# CONFIG_OPTIMIZATION_ASSERTIONS_SILENT is not set
+# CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED is not set
+CONFIG_OPTIMIZATION_ASSERTION_LEVEL=2
+# CONFIG_CXX_EXCEPTIONS is not set
+CONFIG_STACK_CHECK_NONE=y
+# CONFIG_STACK_CHECK_NORM is not set
+# CONFIG_STACK_CHECK_STRONG is not set
+# CONFIG_STACK_CHECK_ALL is not set
+# CONFIG_WARN_WRITE_STRINGS is not set
+# CONFIG_ESP32_APPTRACE_DEST_TRAX is not set
+CONFIG_ESP32_APPTRACE_DEST_NONE=y
+CONFIG_ESP32_APPTRACE_LOCK_ENABLE=y
+# CONFIG_BLUEDROID_ENABLED is not set
+CONFIG_NIMBLE_ENABLED=y
+CONFIG_NIMBLE_MEM_ALLOC_MODE_INTERNAL=y
+# CONFIG_NIMBLE_MEM_ALLOC_MODE_EXTERNAL is not set
+# CONFIG_NIMBLE_MEM_ALLOC_MODE_DEFAULT is not set
+CONFIG_NIMBLE_PINNED_TO_CORE=0
+CONFIG_NIMBLE_PINNED_TO_CORE_0=y
+# CONFIG_NIMBLE_PINNED_TO_CORE_1 is not set
+CONFIG_NIMBLE_TASK_STACK_SIZE=4096
+CONFIG_BT_NIMBLE_TASK_STACK_SIZE=4096
+CONFIG_NIMBLE_ROLE_CENTRAL=y
+CONFIG_NIMBLE_ROLE_PERIPHERAL=y
+CONFIG_NIMBLE_ROLE_BROADCASTER=y
+CONFIG_NIMBLE_ROLE_OBSERVER=y
+CONFIG_NIMBLE_SM_LEGACY=y
+CONFIG_NIMBLE_SM_SC=y
+# CONFIG_NIMBLE_SM_SC_DEBUG_KEYS is not set
+CONFIG_BT_NIMBLE_SM_SC_LVL=0
+# CONFIG_NIMBLE_NVS_PERSIST is not set
+CONFIG_NIMBLE_MAX_BONDS=3
+CONFIG_NIMBLE_RPA_TIMEOUT=900
+CONFIG_NIMBLE_MAX_CONNECTIONS=7
+CONFIG_NIMBLE_MAX_CCCDS=8
+# CONFIG_NIMBLE_CRYPTO_STACK_MBEDTLS is not set
+# CONFIG_NIMBLE_HS_FLOW_CTRL is not set
+CONFIG_NIMBLE_ATT_PREFERRED_MTU=256
+CONFIG_NIMBLE_L2CAP_COC_MAX_NUM=0
+CONFIG_BT_NIMBLE_MSYS1_BLOCK_COUNT=12
+CONFIG_BT_NIMBLE_ACL_BUF_COUNT=24
+CONFIG_BT_NIMBLE_ACL_BUF_SIZE=255
+CONFIG_BT_NIMBLE_HCI_EVT_BUF_SIZE=70
+CONFIG_BT_NIMBLE_HCI_EVT_HI_BUF_COUNT=30
+CONFIG_BT_NIMBLE_HCI_EVT_LO_BUF_COUNT=8
+CONFIG_NIMBLE_SVC_GAP_DEVICE_NAME="nimble"
+CONFIG_NIMBLE_GAP_DEVICE_NAME_MAX_LEN=31
+CONFIG_NIMBLE_SVC_GAP_APPEARANCE=0
+# CONFIG_NIMBLE_MESH is not set
+# CONFIG_NIMBLE_DEBUG is not set
+# CONFIG_BT_NIMBLE_COEX_PHY_CODED_TX_RX_TLIM_EN is not set
+CONFIG_BT_NIMBLE_COEX_PHY_CODED_TX_RX_TLIM_DIS=y
+CONFIG_SW_COEXIST_ENABLE=y
+CONFIG_ESP32_WIFI_SW_COEXIST_ENABLE=y
+CONFIG_ESP_WIFI_SW_COEXIST_ENABLE=y
+# CONFIG_CAM_CTLR_DVP_CAM_ISR_IRAM_SAFE is not set
+# CONFIG_GPTIMER_ISR_IRAM_SAFE is not set
+# CONFIG_MCPWM_ISR_IRAM_SAFE is not set
+# CONFIG_EVENT_LOOP_PROFILING is not set
+CONFIG_POST_EVENTS_FROM_ISR=y
+CONFIG_POST_EVENTS_FROM_IRAM_ISR=y
+CONFIG_GDBSTUB_SUPPORT_TASKS=y
+CONFIG_GDBSTUB_MAX_TASKS=32
+# CONFIG_OTA_ALLOW_HTTP is not set
+CONFIG_ESP32S3_DEEP_SLEEP_WAKEUP_DELAY=2000
+CONFIG_ESP_SLEEP_DEEP_SLEEP_WAKEUP_DELAY=2000
+CONFIG_ESP32S3_RTC_CLK_SRC_INT_RC=y
+# CONFIG_ESP32S3_RTC_CLK_SRC_EXT_CRYS is not set
+# CONFIG_ESP32S3_RTC_CLK_SRC_EXT_OSC is not set
+# CONFIG_ESP32S3_RTC_CLK_SRC_INT_8MD256 is not set
+CONFIG_ESP32S3_RTC_CLK_CAL_CYCLES=1024
+CONFIG_PERIPH_CTRL_FUNC_IN_IRAM=y
+CONFIG_BROWNOUT_DET=y
+CONFIG_ESP32S3_BROWNOUT_DET=y
+CONFIG_BROWNOUT_DET_LVL_SEL_7=y
+CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_7=y
+# CONFIG_BROWNOUT_DET_LVL_SEL_6 is not set
+# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_6 is not set
+# CONFIG_BROWNOUT_DET_LVL_SEL_5 is not set
+# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_5 is not set
+# CONFIG_BROWNOUT_DET_LVL_SEL_4 is not set
+# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_4 is not set
+# CONFIG_BROWNOUT_DET_LVL_SEL_3 is not set
+# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_3 is not set
+# CONFIG_BROWNOUT_DET_LVL_SEL_2 is not set
+# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_2 is not set
+# CONFIG_BROWNOUT_DET_LVL_SEL_1 is not set
+# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_1 is not set
+CONFIG_BROWNOUT_DET_LVL=7
+CONFIG_ESP32S3_BROWNOUT_DET_LVL=7
+CONFIG_ESP_SYSTEM_BROWNOUT_INTR=y
+CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE=y
+# CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION is not set
+CONFIG_ESP32_PHY_MAX_WIFI_TX_POWER=20
+CONFIG_ESP32_PHY_MAX_TX_POWER=20
+# CONFIG_REDUCE_PHY_TX_POWER is not set
+# CONFIG_ESP32_REDUCE_PHY_TX_POWER is not set
+CONFIG_ESP_SYSTEM_PM_POWER_DOWN_CPU=y
+CONFIG_PM_POWER_DOWN_TAGMEM_IN_LIGHT_SLEEP=y
+CONFIG_ESP32S3_SPIRAM_SUPPORT=y
+CONFIG_DEFAULT_PSRAM_CLK_IO=30
+CONFIG_DEFAULT_PSRAM_CS_IO=26
+# CONFIG_ESP32S3_DEFAULT_CPU_FREQ_80 is not set
+CONFIG_ESP32S3_DEFAULT_CPU_FREQ_160=y
+# CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240 is not set
+CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ=160
+CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32
+CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2304
+CONFIG_MAIN_TASK_STACK_SIZE=3584
+CONFIG_CONSOLE_UART_DEFAULT=y
+# CONFIG_CONSOLE_UART_CUSTOM is not set
+# CONFIG_CONSOLE_UART_NONE is not set
+# CONFIG_ESP_CONSOLE_UART_NONE is not set
+CONFIG_CONSOLE_UART=y
+CONFIG_CONSOLE_UART_NUM=0
+CONFIG_CONSOLE_UART_BAUDRATE=115200
+CONFIG_INT_WDT=y
+CONFIG_INT_WDT_TIMEOUT_MS=800
+CONFIG_INT_WDT_CHECK_CPU1=y
+CONFIG_TASK_WDT=y
+CONFIG_ESP_TASK_WDT=y
+# CONFIG_TASK_WDT_PANIC is not set
+CONFIG_TASK_WDT_TIMEOUT_S=5
+CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0=y
+CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1=y
+# CONFIG_ESP32_DEBUG_STUBS_ENABLE is not set
+CONFIG_ESP32S3_DEBUG_OCDAWARE=y
+CONFIG_IPC_TASK_STACK_SIZE=1280
+CONFIG_TIMER_TASK_STACK_SIZE=3584
+CONFIG_ESP32_WIFI_ENABLED=y
+CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM=10
+CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=32
+# CONFIG_ESP32_WIFI_STATIC_TX_BUFFER is not set
+CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER=y
+CONFIG_ESP32_WIFI_TX_BUFFER_TYPE=1
+CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM=32
+# CONFIG_ESP32_WIFI_CSI_ENABLED is not set
+CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED=y
+CONFIG_ESP32_WIFI_TX_BA_WIN=6
+CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y
+CONFIG_ESP32_WIFI_RX_BA_WIN=6
+CONFIG_ESP32_WIFI_NVS_ENABLED=y
+CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_0=y
+# CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_1 is not set
+CONFIG_ESP32_WIFI_SOFTAP_BEACON_MAX_LEN=752
+CONFIG_ESP32_WIFI_MGMT_SBUF_NUM=32
+CONFIG_ESP32_WIFI_IRAM_OPT=y
+CONFIG_ESP32_WIFI_RX_IRAM_OPT=y
+CONFIG_ESP32_WIFI_ENABLE_WPA3_SAE=y
+CONFIG_ESP32_WIFI_ENABLE_WPA3_OWE_STA=y
+CONFIG_WPA_MBEDTLS_CRYPTO=y
+CONFIG_WPA_MBEDTLS_TLS_CLIENT=y
+# CONFIG_WPA_WAPI_PSK is not set
+# CONFIG_WPA_SUITE_B_192 is not set
+# CONFIG_WPA_11KV_SUPPORT is not set
+# CONFIG_WPA_MBO_SUPPORT is not set
+# CONFIG_WPA_DPP_SUPPORT is not set
+# CONFIG_WPA_11R_SUPPORT is not set
+# CONFIG_WPA_WPS_SOFTAP_REGISTRAR is not set
+# CONFIG_WPA_WPS_STRICT is not set
+# CONFIG_WPA_DEBUG_PRINT is not set
+# CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH is not set
+# CONFIG_ESP32_ENABLE_COREDUMP_TO_UART is not set
+CONFIG_ESP32_ENABLE_COREDUMP_TO_NONE=y
+CONFIG_TIMER_TASK_PRIORITY=1
+CONFIG_TIMER_TASK_STACK_DEPTH=2048
+CONFIG_TIMER_QUEUE_LENGTH=10
+# CONFIG_ENABLE_STATIC_TASK_CLEAN_UP_HOOK is not set
+CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY=y
+# CONFIG_HAL_ASSERTION_SILIENT is not set
+# CONFIG_L2_TO_L3_COPY is not set
+CONFIG_ESP_GRATUITOUS_ARP=y
+CONFIG_GARP_TMR_INTERVAL=60
+CONFIG_TCPIP_RECVMBOX_SIZE=32
+CONFIG_TCP_MAXRTX=12
+CONFIG_TCP_SYNMAXRTX=12
+CONFIG_TCP_MSS=1440
+CONFIG_TCP_MSL=60000
+CONFIG_TCP_SND_BUF_DEFAULT=5760
+CONFIG_TCP_WND_DEFAULT=5760
+CONFIG_TCP_RECVMBOX_SIZE=6
+CONFIG_TCP_QUEUE_OOSEQ=y
+CONFIG_TCP_OVERSIZE_MSS=y
+# CONFIG_TCP_OVERSIZE_QUARTER_MSS is not set
+# CONFIG_TCP_OVERSIZE_DISABLE is not set
+CONFIG_UDP_RECVMBOX_SIZE=6
+CONFIG_TCPIP_TASK_STACK_SIZE=3072
+CONFIG_TCPIP_TASK_AFFINITY_NO_AFFINITY=y
+# CONFIG_TCPIP_TASK_AFFINITY_CPU0 is not set
+# CONFIG_TCPIP_TASK_AFFINITY_CPU1 is not set
+CONFIG_TCPIP_TASK_AFFINITY=0x7FFFFFFF
+CONFIG_PPP_SUPPORT=y
+# CONFIG_PPP_NOTIFY_PHASE_SUPPORT is not set
+# CONFIG_PPP_PAP_SUPPORT is not set
+# CONFIG_PPP_CHAP_SUPPORT is not set
+# CONFIG_PPP_MSCHAP_SUPPORT is not set
+# CONFIG_PPP_MPPE_SUPPORT is not set
+# CONFIG_PPP_DEBUG_ON is not set
+CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF=y
+# CONFIG_NEWLIB_STDOUT_LINE_ENDING_LF is not set
+# CONFIG_NEWLIB_STDOUT_LINE_ENDING_CR is not set
+# CONFIG_NEWLIB_STDIN_LINE_ENDING_CRLF is not set
+# CONFIG_NEWLIB_STDIN_LINE_ENDING_LF is not set
+CONFIG_NEWLIB_STDIN_LINE_ENDING_CR=y
+# CONFIG_NEWLIB_NANO_FORMAT is not set
+CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC_HRT=y
+CONFIG_ESP32S3_TIME_SYSCALL_USE_RTC_SYSTIMER=y
+CONFIG_ESP32S3_TIME_SYSCALL_USE_RTC_FRC1=y
+# CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC is not set
+# CONFIG_ESP32S3_TIME_SYSCALL_USE_RTC is not set
+# CONFIG_NEWLIB_TIME_SYSCALL_USE_HRT is not set
+# CONFIG_ESP32S3_TIME_SYSCALL_USE_SYSTIMER is not set
+# CONFIG_ESP32S3_TIME_SYSCALL_USE_FRC1 is not set
+# CONFIG_NEWLIB_TIME_SYSCALL_USE_NONE is not set
+# CONFIG_ESP32S3_TIME_SYSCALL_USE_NONE is not set
+CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT=5
+CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072
+CONFIG_ESP32_PTHREAD_STACK_MIN=768
+CONFIG_ESP32_DEFAULT_PTHREAD_CORE_NO_AFFINITY=y
+# CONFIG_ESP32_DEFAULT_PTHREAD_CORE_0 is not set
+# CONFIG_ESP32_DEFAULT_PTHREAD_CORE_1 is not set
+CONFIG_ESP32_PTHREAD_TASK_CORE_DEFAULT=-1
+CONFIG_ESP32_PTHREAD_TASK_NAME_DEFAULT="pthread"
+CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS=y
+# CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS is not set
+# CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED is not set
+CONFIG_SUPPRESS_SELECT_DEBUG_OUTPUT=y
+CONFIG_SUPPORT_TERMIOS=y
+CONFIG_SEMIHOSTFS_MAX_MOUNT_POINTS=1
+# End of deprecated options
diff --git a/sdkconfig.release b/sdkconfig.release
index d974d52a..a326c285 100644
--- a/sdkconfig.release
+++ b/sdkconfig.release
@@ -2913,6 +2913,7 @@ CONFIG_WIFI_PROV_STA_ALL_CHANNEL_SCAN=y
#
# Zigbee
#
+# default:
# CONFIG_ZB_ENABLED is not set
# end of Zigbee
@@ -2948,13 +2949,21 @@ CONFIG_ESP_MODEM_USE_PPP_MODE=y
#
# mDNS
#
+# default:
CONFIG_MDNS_MAX_INTERFACES=3
+# default:
CONFIG_MDNS_MAX_SERVICES=10
+# default:
CONFIG_MDNS_TASK_PRIORITY=1
+# default:
CONFIG_MDNS_ACTION_QUEUE_LEN=16
+# default:
CONFIG_MDNS_TASK_STACK_SIZE=4096
+# default:
# CONFIG_MDNS_TASK_AFFINITY_NO_AFFINITY is not set
+# default:
CONFIG_MDNS_TASK_AFFINITY_CPU0=y
+# default:
# CONFIG_MDNS_TASK_AFFINITY_CPU1 is not set
# default:
CONFIG_MDNS_TASK_AFFINITY=0x0
@@ -2962,24 +2971,37 @@ CONFIG_MDNS_TASK_AFFINITY=0x0
#
# MDNS Memory Configuration
#
+# default:
CONFIG_MDNS_TASK_CREATE_FROM_INTERNAL=y
+# default:
CONFIG_MDNS_MEMORY_ALLOC_INTERNAL=y
+# default:
# CONFIG_MDNS_MEMORY_CUSTOM_IMPL is not set
# end of MDNS Memory Configuration
+# default:
CONFIG_MDNS_SERVICE_ADD_TIMEOUT_MS=2000
+# default:
CONFIG_MDNS_TIMER_PERIOD_MS=100
+# default:
# CONFIG_MDNS_NETWORKING_SOCKET is not set
+# default:
# CONFIG_MDNS_SKIP_SUPPRESSING_OWN_QUERIES is not set
+# default:
# CONFIG_MDNS_ENABLE_DEBUG_PRINTS is not set
+# default:
CONFIG_MDNS_ENABLE_CONSOLE_CLI=y
+# default:
# CONFIG_MDNS_RESPOND_REVERSE_QUERIES is not set
+# default:
CONFIG_MDNS_MULTIPLE_INSTANCE=y
#
# MDNS Predefined interfaces
#
+# default:
CONFIG_MDNS_PREDEF_NETIF_STA=y
+# default:
CONFIG_MDNS_PREDEF_NETIF_AP=y
# end of MDNS Predefined interfaces
# end of mDNS
@@ -2987,44 +3009,78 @@ CONFIG_MDNS_PREDEF_NETIF_AP=y
#
# Network Provisioning Manager
#
+# default:
CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI=y
+# default:
CONFIG_NETWORK_PROV_SCAN_MAX_ENTRIES=16
+# default:
CONFIG_NETWORK_PROV_AUTOSTOP_TIMEOUT=30
+# default:
# CONFIG_NETWORK_PROV_BLE_BONDING is not set
+# default:
CONFIG_NETWORK_PROV_BLE_SEC_CONN=y
+# default:
# CONFIG_NETWORK_PROV_BLE_FORCE_ENCRYPTION is not set
+# default:
# CONFIG_NETWORK_PROV_KEEP_BLE_ON_AFTER_PROV is not set
+# default:
CONFIG_NETWORK_PROV_WIFI_STA_ALL_CHANNEL_SCAN=y
+# default:
# CONFIG_NETWORK_PROV_WIFI_STA_FAST_SCAN is not set
# end of Network Provisioning Manager
#
# LittleFS
#
+# default:
# CONFIG_LITTLEFS_SDMMC_SUPPORT is not set
+# default:
CONFIG_LITTLEFS_MAX_PARTITIONS=3
+# default:
CONFIG_LITTLEFS_PAGE_SIZE=256
+# default:
CONFIG_LITTLEFS_OBJ_NAME_LEN=64
+# default:
CONFIG_LITTLEFS_READ_SIZE=128
+# default:
CONFIG_LITTLEFS_WRITE_SIZE=128
+# default:
CONFIG_LITTLEFS_LOOKAHEAD_SIZE=128
+# default:
CONFIG_LITTLEFS_CACHE_SIZE=512
+# default:
CONFIG_LITTLEFS_BLOCK_CYCLES=512
+# default:
CONFIG_LITTLEFS_USE_MTIME=y
+# default:
# CONFIG_LITTLEFS_USE_ONLY_HASH is not set
+# default:
# CONFIG_LITTLEFS_HUMAN_READABLE is not set
+# default:
CONFIG_LITTLEFS_MTIME_USE_SECONDS=y
+# default:
# CONFIG_LITTLEFS_MTIME_USE_NONCE is not set
+# default:
# CONFIG_LITTLEFS_SPIFFS_COMPAT is not set
+# default:
# CONFIG_LITTLEFS_FLUSH_FILE_EVERY_WRITE is not set
+# default:
# CONFIG_LITTLEFS_FCNTL_GET_PATH is not set
+# default:
# CONFIG_LITTLEFS_MULTIVERSION is not set
+# default:
# CONFIG_LITTLEFS_MALLOC_STRATEGY_DISABLE is not set
+# default:
CONFIG_LITTLEFS_MALLOC_STRATEGY_DEFAULT=y
+# default:
# CONFIG_LITTLEFS_MALLOC_STRATEGY_INTERNAL is not set
+# default:
# CONFIG_LITTLEFS_MALLOC_STRATEGY_SPIRAM_PREFER is not set
+# default:
CONFIG_LITTLEFS_ASSERTS=y
+# default:
# CONFIG_LITTLEFS_MMAP_PARTITION is not set
+# default:
# CONFIG_LITTLEFS_WDT_RESET is not set
# end of LittleFS
# end of Component config
diff --git a/src/BLE_Client.cpp b/src/BLE_Client.cpp
index 2cb382b5..b5baf696 100644
--- a/src/BLE_Client.cpp
+++ b/src/BLE_Client.cpp
@@ -10,9 +10,11 @@
#include "BLE_Device_Identity.h"
#include "BLE_Fitness_Machine_Service.h"
#include "SS2KLog.h"
+#include "ByteUtils.h"
#include
#include
+#include
#include
#include
@@ -23,6 +25,13 @@ SpinBLEClient spinBLEClient;
static MyClientCallback myClientCallback;
static ScanCallbacks myScanCallbacks;
+namespace {
+// The legacy characteristic is a single ATT value and can never carry an
+// unbounded scan. Keep a small snapshot for old config-app releases while the
+// versioned scan-result stream carries every device to current releases.
+constexpr size_t LEGACY_FOUND_DEVICES_MAX_LENGTH = 480;
+}
+
void SpinBLEClient::start() {
// Create the task for the BLE Client loop
xTaskCreatePinnedToCore(bleClientTask, /* Task function. */
@@ -71,14 +80,39 @@ static void notifyCB(NimBLERemoteCharacteristic* pBLERemoteCharacteristic, uint8
}
}
+static bool subscribeToCharacteristic(NimBLEClient* pClient, NimBLERemoteService* pSvc, const BLEServiceInfo& service, const NimBLEUUID& characteristicUUID) {
+ if (!pClient->isConnected()) {
+ return false;
+ }
+
+ // Ask NimBLE for only the characteristic we consume. Discovering every characteristic
+ // caches a heap object for each one and can exhaust the classic ESP32 heap on large GATT tables.
+ NimBLERemoteCharacteristic* pChr = pSvc->getCharacteristic(characteristicUUID);
+ if (!pChr) {
+ SS2K_LOG(BLE_CLIENT_LOG_TAG, "Characteristic %s not found in %s", characteristicUUID.toString().c_str(), service.name.c_str());
+ return false;
+ }
+
+ SS2K_LOG(BLE_CLIENT_LOG_TAG, "Found %s, %s", service.serviceUUID.toString().c_str(), pChr->getUUID().toString().c_str());
+ if (!pChr->canNotify() && !pChr->canIndicate()) {
+ SS2K_LOG(BLE_CLIENT_LOG_TAG, "%s %s does not support notifications or indications", service.name.c_str(), pChr->getUUID().toString().c_str());
+ return false;
+ }
+
+ const bool wantNotify = pChr->canNotify();
+ if (!pChr->subscribe(wantNotify, notifyCB)) {
+ SS2K_LOG(BLE_CLIENT_LOG_TAG, "Failed to subscribe to %s %s (handle %d)", service.name.c_str(), pChr->getUUID().toString().c_str(), pChr->getHandle());
+ return false;
+ }
+
+ SS2K_LOG(BLE_CLIENT_LOG_TAG, "Subscribed to %s %s handle: %d", service.name.c_str(), pChr->getUUID().toString().c_str(), pChr->getHandle());
+ return true;
+}
+
/**
- * @brief Subscribes to all notifications for supported BLE services on the given client.
- *
- * This function iterates through all supported BLE services and subscribes to notifications
- * for each characteristic that supports notifications or indications.
+ * @brief Subscribes to the notification characteristics consumed by SmartSpin2k.
*
- * @param pClient Pointer to the NimBLEClient object representing the BLE client.
- * The client must be connected for the function to proceed.
+ * @param pClient Pointer to the connected NimBLE client.
*/
bool subscribeToAllNotifications(NimBLEClient* pClient) {
bool isSubscribed = false;
@@ -95,34 +129,20 @@ bool subscribeToAllNotifications(NimBLEClient* pClient) {
NimBLERemoteService* pSvc = pClient->getService(service.serviceUUID);
if (pSvc) {
SS2K_LOG(BLE_CLIENT_LOG_TAG, "Found %s", service.name.c_str());
- auto chars = pSvc->getCharacteristics(true);
- for (auto* pChr : chars) {
- if (!pChr) {
- continue;
- }
- // Defensive: Ensure characteristic still belongs to this service (avoid stale pointer after service refresh)
- if (pChr->getRemoteService() != pSvc) {
- SS2K_LOGW(BLE_CLIENT_LOG_TAG, "Characteristic parent mismatch while subscribing %s", pChr->getUUID().toString().c_str());
- continue;
- }
- if (!pClient->isConnected()) {
- SS2K_LOGW(BLE_CLIENT_LOG_TAG, "Stopping subscription: client disconnected before subscribing to %s", pChr->getUUID().toString().c_str());
- break;
- }
- SS2K_LOG(BLE_CLIENT_LOG_TAG, "Found %s, %s", service.serviceUUID.toString().c_str(), pChr->getUUID().toString().c_str());
- if (pChr->canNotify() || pChr->canIndicate()) {
- bool wantNotify = pChr->canNotify();
- // Wrap subscribe in a try/catch-equivalent pattern: NimBLE doesn't throw, but we guard return status
- if (wantNotify ? pChr->subscribe(true, notifyCB) : pChr->subscribe(false, notifyCB)) {
- SS2K_LOG(BLE_CLIENT_LOG_TAG, "Subscribed to %s %s handle: %d", service.name.c_str(), pChr->getUUID().toString().c_str(), pChr->getHandle());
- isSubscribed = true;
- } else {
- // Additional debug: check CCCD descriptor presence which subscribe() internally uses
- NimBLERemoteDescriptor* cccd = pChr->getDescriptor(NimBLEUUID((uint16_t)0x2902));
- SS2K_LOG(BLE_CLIENT_LOG_TAG, "Failed to subscribe to %s %s (handle %d, hasCCCD=%s)", service.name.c_str(), pChr->getUUID().toString().c_str(), pChr->getHandle(),
- cccd ? "Y" : "N");
- }
- }
+
+ // HID remotes may expose several Report characteristics with the same UUID. They
+ // are subscribed during connectBLE_HID(), where full discovery is intentional.
+ if (service.serviceUUID == HID_SERVICE_UUID) {
+ isSubscribed = true;
+ continue;
+ }
+
+ isSubscribed |= subscribeToCharacteristic(pClient, pSvc, service, service.characteristicUUID);
+
+ // FTMS control-point indications acknowledge commands sent later in postConnect().
+ // Status and training-status notifications are not consumed by this firmware.
+ if (service.serviceUUID == FITNESSMACHINESERVICE_UUID) {
+ isSubscribed |= subscribeToCharacteristic(pClient, pSvc, service, FITNESSMACHINECONTROLPOINT_UUID);
}
}
}
@@ -407,6 +427,9 @@ void ScanCallbacks::onResult(const NimBLEAdvertisedDevice* advertisedDevice) {
}
SS2K_LOG(BLE_CLIENT_LOG_TAG, "Found device %s with %s", aDevName.c_str(), servicesStr.c_str());
if (serviceInfo) {
+ const NimBLEUUID& primaryServiceUUID = advertisedDevice->isAdvertisingService(FITNESSMACHINESERVICE_UUID) ? FITNESSMACHINESERVICE_UUID : serviceInfo->serviceUUID;
+ BLE_ss2kCustomCharacteristic::notifyScanResult(aDevName, primaryServiceUUID);
+
// Add device to foundDevices list
JsonDocument devices;
const char* foundDevicesJson = userConfig->getFoundDevices();
@@ -434,11 +457,12 @@ void ScanCallbacks::onResult(const NimBLEAdvertisedDevice* advertisedDevice) {
}
String output;
serializeJson(devices, output);
- userConfig->setFoundDevices(output);
+ if (output.length() <= LEGACY_FOUND_DEVICES_MAX_LENGTH) {
+ userConfig->setFoundDevices(output);
+ }
}
SS2K_LOG(BLE_CLIENT_LOG_TAG, "Supported Device: %s with service %s", aDevName.c_str(), serviceInfo->name.c_str());
- const NimBLEUUID& primaryServiceUUID = serviceInfo->serviceUUID;
// check to see if we're already connected to this device
for (size_t i = 0; i < NUM_BLE_DEVICES; i++) {
if (spinBLEClient.myBLEDevices[i].advertisedDevice != nullptr) {
@@ -536,8 +560,6 @@ void ScanCallbacks::onResult(const NimBLEAdvertisedDevice* advertisedDevice) {
* @param duration The duration in seconds for which the scan should run
*/
void SpinBLEClient::scanProcess(int duration) {
- this->doScan = false; // Confirming we did the scan
-
NimBLEScan* pBLEScan = NimBLEDevice::getScan();
static bool waitForOnScanEndToComplete = false;
@@ -550,12 +572,21 @@ void SpinBLEClient::scanProcess(int duration) {
return;
}
+ this->doScan = false; // Confirming we started the requested scan.
+
SS2K_LOG(BLE_CLIENT_LOG_TAG, "Scanning for BLE servers and putting them into a list...");
- pBLEScan->start(duration, false, true);
+ userConfig->setFoundDevices("{}");
+ BLE_ss2kCustomCharacteristic::beginScanResults();
+ if (!pBLEScan->start(duration, false, true)) {
+ BLE_ss2kCustomCharacteristic::endScanResults();
+ this->doScan = true;
+ SS2K_LOGE(BLE_CLIENT_LOG_TAG, "Unable to start BLE scan");
+ }
}
void ScanCallbacks::onScanEnd(const NimBLEScanResults& results, int reason) {
SS2K_LOG(BLE_CLIENT_LOG_TAG, "Scan Ended");
+ BLE_ss2kCustomCharacteristic::endScanResults();
}
// remove the last connected BLE Power Meter
@@ -620,8 +651,7 @@ void SpinBLEClient::FTMSControlPointWrite(const uint8_t* pData, int length) {
char logBuf[kLogBufCapacity];
if (modData[0] == FitnessMachineControlPointProcedure::SetIndoorBikeSimulationParameters) { // use virtual Shifting
int incline = ss2k->getTargetPosition() / userConfig->getInclineMultiplier();
- modData[3] = (uint8_t)(incline & 0xff);
- modData[4] = (uint8_t)(incline >> 8);
+ put_le16s(&modData[3], static_cast(incline));
writeCharacteristic->writeValue(modData, length);
logBufLength = ss2k_log_hex_to_buffer(modData, length, logBuf, 0, kLogBufCapacity);
logBufLength += snprintf(logBuf + logBufLength, kLogBufCapacity - logBufLength, "-> Shifted Sim Data: %d", rtConfig->getShifterPosition());
@@ -718,9 +748,9 @@ void SpinBLEClient::postConnect() {
auto rr = resistanceRangeCharacteristic->readValue();
if (rr.size() >= 6) {
const uint8_t* b = reinterpret_cast(rr.data());
- int16_t minRaw = static_cast(b[0] | (static_cast(b[1]) << 8));
- int16_t maxRaw = static_cast(b[2] | (static_cast(b[3]) << 8));
- uint16_t incRaw = static_cast(b[4] | (static_cast(b[5]) << 8));
+ int16_t minRaw = get_le16s(&b[0]);
+ int16_t maxRaw = get_le16s(&b[2]);
+ uint16_t incRaw = get_le16(&b[4]);
float incF = static_cast(incRaw) / 10.0f; // FTMS resolution 0.1, convert to actual increment value
float minF = (static_cast(minRaw) / 10.0f) / incF; // Convert FTMS 0.1 units to normalized scale
@@ -846,8 +876,8 @@ void SpinBLEClient::connectBLE_HID(NimBLEClient* pClient) {
// One real device reports 2 with the same UUID but
// different handles. Using getCharacteristic() results
// in subscribing to only one.
- std::vector charVector = pSvc->getCharacteristics(true);
- for (auto& it : charVector) {
+ const std::vector& charVector = pSvc->getCharacteristics(true);
+ for (auto* it : charVector) {
if (it->getUUID() == NimBLEUUID(HID_REPORT_DATA_UUID)) {
Serial.println(it->toString().c_str());
if (it->canNotify()) {
diff --git a/src/BLE_Custom_Characteristic.cpp b/src/BLE_Custom_Characteristic.cpp
index 205adf77..eab6b4b5 100644
--- a/src/BLE_Custom_Characteristic.cpp
+++ b/src/BLE_Custom_Characteristic.cpp
@@ -90,6 +90,8 @@ This characteristic allows for reading and writing various user configuration pa
#include
#include "BleAppender.h"
#include "DirConManager.h"
+#include "ByteUtils.h"
+#include "ScanResultProtocol.h"
#include
#include
@@ -114,11 +116,55 @@ struct SettingsSnapshotTransfer {
SettingsSnapshotTransfer settingsSnapshot;
SettingsSnapshotTransfer dirConSettingsSnapshot;
+uint16_t scanResultId = 0;
+uint16_t scanResultSequence = 0;
void resetSettingsSnapshot() {
settingsSnapshot = SettingsSnapshotTransfer();
}
+void sendScanResultPayload(ScanResultProtocol::Event event, uint16_t sequence, const std::vector& payload = {}) {
+ NimBLEServer* server = NimBLEDevice::getServer();
+ if (server == nullptr) return;
+ NimBLEService* service = server->getServiceByUUID(SMARTSPIN2K_SERVICE_UUID);
+ if (service == nullptr) return;
+ NimBLECharacteristic* characteristic = service->getCharacteristic(SMARTSPIN2K_CHARACTERISTIC_UUID);
+ if (characteristic == nullptr) return;
+
+ // Fragment independently for every BLE peer because negotiated MTUs can
+ // differ. This keeps even the 23-byte minimum MTU valid without retaining a
+ // scan-wide JSON document in firmware memory.
+ for (const uint16_t connHandle : server->getPeerDevices()) {
+ const uint16_t mtu = server->getPeerMTU(connHandle);
+ const size_t attPayload = mtu > 3 ? mtu - 3 : 20;
+ const size_t fragmentMax = attPayload > ScanResultProtocol::HEADER_LENGTH ? attPayload - ScanResultProtocol::HEADER_LENGTH : 1;
+ const size_t fragmentCountSize = payload.empty() ? 1 : (payload.size() + fragmentMax - 1) / fragmentMax;
+ if (fragmentCountSize > UINT8_MAX) {
+ SS2K_LOGE(CUSTOM_CHAR_LOG_TAG, "Scan result %u requires too many fragments", static_cast(sequence));
+ continue;
+ }
+
+ const uint8_t fragmentCount = static_cast(fragmentCountSize);
+ for (uint8_t fragment = 0; fragment < fragmentCount; ++fragment) {
+ const size_t offset = fragment * fragmentMax;
+ const size_t length = payload.empty() ? 0 : std::min(fragmentMax, payload.size() - offset);
+ std::vector packet = ScanResultProtocol::makePacket(event, scanResultId, sequence, fragment, fragmentCount,
+ length == 0 ? nullptr : payload.data() + offset, length);
+ if (!characteristic->notify(packet.data(), packet.size(), connHandle)) {
+ SS2K_LOGW(CUSTOM_CHAR_LOG_TAG, "Failed to send scan result %u fragment %u/%u", static_cast(sequence),
+ static_cast(fragment + 1), static_cast(fragmentCount));
+ break;
+ }
+ }
+ }
+
+ // DirCon is a framed TCP transport and does not need ATT fragmentation.
+ if (payload.size() <= UINT8_MAX) {
+ std::vector packet = ScanResultProtocol::makePacket(event, scanResultId, sequence, 0, 1, payload.data(), payload.size());
+ DirConManager::notifyCharacteristic(service->getUUID(), characteristic->getUUID(), packet.data(), packet.size());
+ }
+}
+
std::vector makeSettingsSnapshotChunk(SettingsSnapshotTransfer& snapshot) {
size_t remaining = snapshot.json.length() - snapshot.offset;
size_t payloadSize = std::min(snapshot.payloadLength, remaining);
@@ -126,10 +172,8 @@ std::vector makeSettingsSnapshotChunk(SettingsSnapshotTransfer& snapsho
packet[0] = cc_success;
packet[1] = BLE_allSettings;
packet[2] = SETTINGS_SNAPSHOT_VERSION;
- packet[3] = static_cast(snapshot.chunk & 0xff);
- packet[4] = static_cast(snapshot.chunk >> 8);
- packet[5] = static_cast(snapshot.chunkCount & 0xff);
- packet[6] = static_cast(snapshot.chunkCount >> 8);
+ put_le16(&packet[3], snapshot.chunk);
+ put_le16(&packet[5], snapshot.chunkCount);
std::copy_n(snapshot.json.data() + snapshot.offset, payloadSize, packet.begin() + SETTINGS_SNAPSHOT_HEADER_LENGTH);
snapshot.offset += payloadSize;
@@ -315,6 +359,22 @@ void BLE_ss2kCustomCharacteristic::notify(char _item, int tableRow) {
DirConManager::notifyCharacteristic(service->getUUID(), characteristic->getUUID(), value.data(), value.size());
}
+void BLE_ss2kCustomCharacteristic::beginScanResults() {
+ ++scanResultId;
+ if (scanResultId == 0) ++scanResultId;
+ scanResultSequence = 0;
+ sendScanResultPayload(ScanResultProtocol::Event::Begin, 0);
+}
+
+void BLE_ss2kCustomCharacteristic::notifyScanResult(const String& name, const NimBLEUUID& serviceUuid) {
+ const std::string uuid = serviceUuid.toString();
+ const std::vector body = ScanResultProtocol::makeDeviceBody(uuid, std::string(name.c_str(), name.length()));
+ sendScanResultPayload(ScanResultProtocol::Event::Device, scanResultSequence, body);
+ ++scanResultSequence;
+}
+
+void BLE_ss2kCustomCharacteristic::endScanResults() { sendScanResultPayload(ScanResultProtocol::Event::End, scanResultSequence); }
+
void BLE_ss2kCustomCharacteristic::process(std::string rxValue, uint16_t connHandle, uint16_t mtu, bool indicateResponse) {
// Find the Characteristic
if (rxValue.length() < 2 || NimBLEDevice::getServer()->getServiceByUUID(SMARTSPIN2K_SERVICE_UUID) == nullptr) {
@@ -354,6 +414,27 @@ void BLE_ss2kCustomCharacteristic::process(std::string rxValue, uint16_t connHan
returnValue[i] = rxValue[i];
}
+ size_t requiredLength = 2;
+ if (rxValue[0] == cc_write) {
+ switch (customCharacteristicValueFormat(static_cast(rxValue[1]))) {
+ case CustomUnsigned16:
+ case CustomSigned16: requiredLength = 4; break;
+ case CustomSigned32: requiredLength = 6; break;
+ case CustomBoolean:
+ case CustomBooleanWriteStringRead: requiredLength = 3; break;
+ case CustomPowerTableRow: requiredLength = 3 + (2 * POWERTABLE_WATT_SIZE); break;
+ default: break;
+ }
+ } else if (rxValue[0] == cc_read && static_cast(rxValue[1]) == BLE_powerTableData) {
+ requiredLength = 3;
+ }
+
+ if (rxValue.length() < requiredLength) {
+ pCharacteristic->setValue(returnValue.data(), 2);
+ if (indicateResponse) pCharacteristic->indicate(connHandle);
+ return;
+ }
+
switch (rxValue[1]) {
case BLE_firmwareUpdateURL: // 0x01
LOG_BUF_APPEND("<-Firmware Update URL");
@@ -374,13 +455,12 @@ void BLE_ss2kCustomCharacteristic::process(std::string rxValue, uint16_t connHan
if (rxValue[0] == cc_read) {
returnValue[0] = cc_success;
int inc = rtConfig->getTargetIncline() * 10;
- returnValue[2] = (uint8_t)(inc & 0xff);
- returnValue[3] = (uint8_t)(inc >> 8);
+ put_le16s(&returnValue[2], static_cast(inc));
returnLength += 2;
}
if (rxValue[0] == cc_write) {
returnValue[0] = cc_success;
- rtConfig->setTargetIncline(bytes_to_u16(rxValue[3], rxValue[2]) / 10);
+ rtConfig->setTargetIncline(get_le16s(&pData[2]) / 10.0f);
LOG_BUF_APPEND("(%f)", rtConfig->getTargetIncline());
}
} break;
@@ -389,13 +469,12 @@ void BLE_ss2kCustomCharacteristic::process(std::string rxValue, uint16_t connHan
LOG_BUF_APPEND("<-simulatedWatts");
if (rxValue[0] == cc_read) {
returnValue[0] = cc_success;
- returnValue[2] = (uint8_t)(rtConfig->watts.getValue() & 0xff);
- returnValue[3] = (uint8_t)(rtConfig->watts.getValue() >> 8);
+ put_le16(&returnValue[2], static_cast(rtConfig->watts.getValue()));
returnLength += 2;
}
if (rxValue[0] == cc_write) {
returnValue[0] = cc_success;
- rtConfig->watts.setValue(bytes_to_u16(rxValue[3], rxValue[2]));
+ rtConfig->watts.setValue(get_le16(&pData[2]));
LOG_BUF_APPEND("(%d)", rtConfig->watts.getValue());
}
break;
@@ -404,13 +483,12 @@ void BLE_ss2kCustomCharacteristic::process(std::string rxValue, uint16_t connHan
LOG_BUF_APPEND("<-simulatedHr");
if (rxValue[0] == cc_read) {
returnValue[0] = cc_success;
- returnValue[2] = (uint8_t)(rtConfig->hr.getValue() & 0xff);
- returnValue[3] = (uint8_t)(rtConfig->hr.getValue() >> 8);
+ put_le16(&returnValue[2], static_cast(rtConfig->hr.getValue()));
returnLength += 2;
}
if (rxValue[0] == cc_write) {
returnValue[0] = cc_success;
- rtConfig->hr.setValue(bytes_to_u16(rxValue[3], rxValue[2]));
+ rtConfig->hr.setValue(get_le16(&pData[2]));
LOG_BUF_APPEND("(%d)", rtConfig->hr.getValue());
}
break;
@@ -419,13 +497,12 @@ void BLE_ss2kCustomCharacteristic::process(std::string rxValue, uint16_t connHan
LOG_BUF_APPEND("<-simulatedCad");
if (rxValue[0] == cc_read) {
returnValue[0] = cc_success;
- returnValue[2] = (uint8_t)(rtConfig->cad.getValue() & 0xff);
- returnValue[3] = (uint8_t)(rtConfig->cad.getValue() >> 8);
+ put_le16(&returnValue[2], static_cast(rtConfig->cad.getValue()));
returnLength += 2;
}
if (rxValue[0] == cc_write) {
returnValue[0] = cc_success;
- rtConfig->cad.setValue(bytes_to_u16(rxValue[3], rxValue[2]));
+ rtConfig->cad.setValue(get_le16(&pData[2]));
LOG_BUF_APPEND("(%d)", rtConfig->cad.getValue());
}
break;
@@ -435,13 +512,12 @@ void BLE_ss2kCustomCharacteristic::process(std::string rxValue, uint16_t connHan
int spd = rtConfig->getSimulatedSpeed() * 10;
if (rxValue[0] == cc_read) {
returnValue[0] = cc_success;
- returnValue[2] = (uint8_t)(spd & 0xff);
- returnValue[3] = (uint8_t)(spd >> 8);
+ put_le16(&returnValue[2], static_cast(spd));
returnLength += 2;
}
if (rxValue[0] == cc_write) {
returnValue[0] = cc_success;
- rtConfig->setSimulatedSpeed(bytes_to_u16(rxValue[3], rxValue[2]) / 10);
+ rtConfig->setSimulatedSpeed(get_le16(&pData[2]) / 10.0f);
LOG_BUF_APPEND("(%d)", rtConfig->getSimulatedSpeed());
}
} break;
@@ -464,13 +540,12 @@ void BLE_ss2kCustomCharacteristic::process(std::string rxValue, uint16_t connHan
LOG_BUF_APPEND("<-shiftStep");
if (rxValue[0] == cc_read) {
returnValue[0] = cc_success;
- returnValue[2] = (uint8_t)(userConfig->getShiftStep() & 0xff);
- returnValue[3] = (uint8_t)(userConfig->getShiftStep() >> 8);
+ put_le16(&returnValue[2], static_cast(userConfig->getShiftStep()));
returnLength += 2;
}
if (rxValue[0] == cc_write) {
returnValue[0] = cc_success;
- userConfig->setShiftStep(bytes_to_u16(rxValue[3], rxValue[2]));
+ userConfig->setShiftStep(get_le16(&pData[2]));
LOG_BUF_APPEND("(%d)", userConfig->getShiftStep());
}
break;
@@ -479,13 +554,12 @@ void BLE_ss2kCustomCharacteristic::process(std::string rxValue, uint16_t connHan
LOG_BUF_APPEND("<-stepperPower");
if (rxValue[0] == cc_read) {
returnValue[0] = cc_success;
- returnValue[2] = (uint8_t)(userConfig->getStepperPower() & 0xff);
- returnValue[3] = (uint8_t)(userConfig->getStepperPower() >> 8);
+ put_le16(&returnValue[2], static_cast(userConfig->getStepperPower()));
returnLength += 2;
}
if (rxValue[0] == cc_write) {
returnValue[0] = cc_success;
- userConfig->setStepperPower(bytes_to_u16(rxValue[3], rxValue[2]));
+ userConfig->setStepperPower(get_le16(&pData[2]));
ss2k->updateStepperPower();
LOG_BUF_APPEND("(%d)", userConfig->getStepperPower());
}
@@ -511,13 +585,12 @@ void BLE_ss2kCustomCharacteristic::process(std::string rxValue, uint16_t connHan
int inc = userConfig->getInclineMultiplier() * 10;
if (rxValue[0] == cc_read) {
returnValue[0] = cc_success;
- returnValue[2] = (uint8_t)(inc & 0xff);
- returnValue[3] = (uint8_t)(inc >> 8);
+ put_le16s(&returnValue[2], static_cast(inc));
returnLength += 2;
}
if (rxValue[0] == cc_write) {
returnValue[0] = cc_success;
- userConfig->setInclineMultiplier((bytes_to_u16(rxValue[3], rxValue[2])) / 10.0);
+ userConfig->setInclineMultiplier(get_le16s(&pData[2]) / 10.0f);
LOG_BUF_APPEND("(%f)", userConfig->getInclineMultiplier());
}
} break;
@@ -527,13 +600,12 @@ void BLE_ss2kCustomCharacteristic::process(std::string rxValue, uint16_t connHan
int pcf = userConfig->getPowerCorrectionFactor() * 10;
if (rxValue[0] == cc_read) {
returnValue[0] = cc_success;
- returnValue[2] = (uint8_t)(pcf & 0xff);
- returnValue[3] = (uint8_t)(pcf >> 8);
+ put_le16(&returnValue[2], static_cast(pcf));
returnLength += 2;
}
if (rxValue[0] == cc_write) {
returnValue[0] = cc_success;
- userConfig->setPowerCorrectionFactor((bytes_to_u16(rxValue[3], rxValue[2])) / 10.0);
+ userConfig->setPowerCorrectionFactor(get_le16(&pData[2]) / 10.0f);
LOG_BUF_APPEND("(%f)", userConfig->getPowerCorrectionFactor());
}
} break;
@@ -584,13 +656,12 @@ void BLE_ss2kCustomCharacteristic::process(std::string rxValue, uint16_t connHan
LOG_BUF_APPEND("<-FTMSMode");
if (rxValue[0] == cc_read) {
returnValue[0] = cc_success;
- returnValue[2] = (uint8_t)(rtConfig->getFTMSMode() & 0xff);
- returnValue[3] = (uint8_t)(rtConfig->getFTMSMode() >> 8);
+ put_le16(&returnValue[2], rtConfig->getFTMSMode());
returnLength += 2;
}
if (rxValue[0] == cc_write) {
returnValue[0] = cc_success;
- rtConfig->setFTMSMode(bytes_to_u16(rxValue[3], rxValue[2]));
+ rtConfig->setFTMSMode(get_le16(&pData[2]));
LOG_BUF_APPEND("(%hhu)", rtConfig->getFTMSMode());
}
break;
@@ -683,13 +754,12 @@ void BLE_ss2kCustomCharacteristic::process(std::string rxValue, uint16_t connHan
LOG_BUF_APPEND("<-shifterPosition");
if (rxValue[0] == cc_read) {
returnValue[0] = cc_success;
- returnValue[2] = (uint8_t)(rtConfig->getShifterPosition() & 0xff);
- returnValue[3] = (uint8_t)(rtConfig->getShifterPosition() >> 8);
+ put_le16s(&returnValue[2], static_cast(rtConfig->getShifterPosition()));
returnLength += 2;
}
if (rxValue[0] == cc_write) {
returnValue[0] = cc_success;
- rtConfig->setShifterPosition(bytes_to_u16(rxValue[3], rxValue[2]));
+ rtConfig->setShifterPosition(get_le16s(&pData[2]));
LOG_BUF_APPEND("(%d)", rtConfig->getShifterPosition());
#ifdef CUSTOM_CHAR_DEBUG
SS2K_LOG(CUSTOM_CHAR_LOG_TAG, "%s", logBuf);
@@ -711,15 +781,12 @@ void BLE_ss2kCustomCharacteristic::process(std::string rxValue, uint16_t connHan
LOG_BUF_APPEND("<-targetPosition");
if (rxValue[0] == cc_read) {
returnValue[0] = cc_success;
- returnValue[2] = (uint8_t)(ss2k->getTargetPosition() & 0xff);
- returnValue[3] = (uint8_t)(ss2k->getTargetPosition() >> 8);
- returnValue[4] = (uint8_t)(ss2k->getTargetPosition() >> 16);
- returnValue[5] = (uint8_t)(ss2k->getTargetPosition() >> 24);
+ put_le32s(&returnValue[2], ss2k->getTargetPosition());
returnLength += 4;
}
if (rxValue[0] == cc_write) {
returnValue[0] = cc_success;
- ss2k->setTargetPosition(int32_t((uint8_t)(rxValue[2]) << 0 | (uint8_t)(rxValue[3]) << 8 | (uint8_t)(rxValue[4]) << 16 | (uint8_t)(rxValue[5]) << 24));
+ ss2k->setTargetPosition(get_le32s(&pData[2]));
LOG_BUF_APPEND(" (%f)", ss2k->getTargetPosition());
}
break;
@@ -772,13 +839,12 @@ void BLE_ss2kCustomCharacteristic::process(std::string rxValue, uint16_t connHan
LOG_BUF_APPEND("<-stepperSpeed");
if (rxValue[0] == cc_read) {
returnValue[0] = cc_success;
- returnValue[2] = (uint8_t)(userConfig->getStepperSpeed() & 0xff);
- returnValue[3] = (uint8_t)(userConfig->getStepperSpeed() >> 8);
+ put_le16(&returnValue[2], static_cast(userConfig->getStepperSpeed()));
returnLength += 2;
}
if (rxValue[0] == cc_write) {
returnValue[0] = cc_success;
- userConfig->setStepperSpeed(bytes_to_u16(rxValue[3], rxValue[2]));
+ userConfig->setStepperSpeed(get_le16(&pData[2]));
LOG_BUF_APPEND("(%d)", userConfig->getStepperSpeed());
ss2k->updateStepperSpeed();
#ifdef CUSTOM_CHAR_DEBUG
@@ -792,13 +858,12 @@ void BLE_ss2kCustomCharacteristic::process(std::string rxValue, uint16_t connHan
int pcf = userConfig->getERGSensitivity() * 10;
if (rxValue[0] == cc_read) {
returnValue[0] = cc_success;
- returnValue[2] = (uint8_t)(pcf & 0xff);
- returnValue[3] = (uint8_t)(pcf >> 8);
+ put_le16(&returnValue[2], static_cast(pcf));
returnLength += 2;
}
if (rxValue[0] == cc_write) {
returnValue[0] = cc_success;
- userConfig->setERGSensitivity((bytes_to_u16(rxValue[3], rxValue[2])) / 10.0);
+ userConfig->setERGSensitivity(get_le16(&pData[2]) / 10.0f);
LOG_BUF_APPEND("(%f)", userConfig->getERGSensitivity());
}
} break;
@@ -821,13 +886,12 @@ void BLE_ss2kCustomCharacteristic::process(std::string rxValue, uint16_t connHan
LOG_BUF_APPEND("<-MinWatts");
if (rxValue[0] == cc_read) {
returnValue[0] = cc_success;
- returnValue[2] = (uint8_t)(userConfig->getMinWatts() & 0xff);
- returnValue[3] = (uint8_t)(userConfig->getMinWatts() >> 8);
+ put_le16(&returnValue[2], static_cast(userConfig->getMinWatts()));
returnLength += 2;
}
if (rxValue[0] == cc_write) {
returnValue[0] = cc_success;
- userConfig->setMinWatts(bytes_to_u16(rxValue[3], rxValue[2]));
+ userConfig->setMinWatts(get_le16(&pData[2]));
LOG_BUF_APPEND("(%d)", userConfig->getMinWatts());
#ifdef CUSTOM_CHAR_DEBUG
SS2K_LOG(CUSTOM_CHAR_LOG_TAG, "%s", logBuf);
@@ -838,13 +902,12 @@ void BLE_ss2kCustomCharacteristic::process(std::string rxValue, uint16_t connHan
LOG_BUF_APPEND("<-MaxWatts");
if (rxValue[0] == cc_read) {
returnValue[0] = cc_success;
- returnValue[2] = (uint8_t)(userConfig->getMaxWatts() & 0xff);
- returnValue[3] = (uint8_t)(userConfig->getMaxWatts() >> 8);
+ put_le16(&returnValue[2], static_cast(userConfig->getMaxWatts()));
returnLength += 2;
}
if (rxValue[0] == cc_write) {
returnValue[0] = cc_success;
- userConfig->setMaxWatts(bytes_to_u16(rxValue[3], rxValue[2]));
+ userConfig->setMaxWatts(get_le16(&pData[2]));
LOG_BUF_APPEND("(%d)", userConfig->getMaxWatts());
#ifdef CUSTOM_CHAR_DEBUG
SS2K_LOG(CUSTOM_CHAR_LOG_TAG, "%s", logBuf);
@@ -883,22 +946,21 @@ void BLE_ss2kCustomCharacteristic::process(std::string rxValue, uint16_t connHan
LOG_BUF_APPEND("<-Power Tab Data");
if (rxValue[0] == cc_read) {
int row = 6; // 90rpm
- if (rxValue[2] >= 0 || rxValue[2] < POWERTABLE_CAD_SIZE) {
+ if (rxValue[2] >= 0 && rxValue[2] < POWERTABLE_CAD_SIZE) {
row = rxValue[2];
}
returnString += (uint8_t)row;
for (int i = 0; i < POWERTABLE_WATT_SIZE; i++) {
- returnString += (uint8_t)(powerTable->ptData.tableRow[row].tableEntry[i].targetPosition & 0xff);
- returnString += (uint8_t)(powerTable->ptData.tableRow[row].tableEntry[i].targetPosition >> 8);
- // Serial.printf("%02x%02x ", (uint8_t)(powerTable->ptData.tableRow[row].tableEntry[i].targetPosition & 0xff),
- // (uint8_t)(powerTable->ptData.tableRow[row].tableEntry[i].targetPosition >> 8));
+ uint8_t encodedPosition[2];
+ put_le16s(encodedPosition, powerTable->ptData.tableRow[row].tableEntry[i].targetPosition);
+ returnString.append(reinterpret_cast(encodedPosition), sizeof(encodedPosition));
}
}
if (rxValue[0] == cc_write) {
returnValue[0] = cc_success;
if (rxValue[2] >= 0 && rxValue[2] < POWERTABLE_CAD_SIZE) {
for (int i = 0; i < POWERTABLE_WATT_SIZE; i++) {
- powerTable->ptData.tableRow[rxValue[2]].tableEntry[i].targetPosition = (int16_t((uint8_t)(rxValue[i * 2 + 3]) << 0 | (uint8_t)(rxValue[i * 2 + 4]) << 8));
+ powerTable->ptData.tableRow[rxValue[2]].tableEntry[i].targetPosition = get_le16s(&pData[i * 2 + 3]);
// Ensure each entry has a valid reading count to be considered during loading
if (powerTable->ptData.tableRow[rxValue[2]].tableEntry[i].targetPosition != INT16_MIN) {
powerTable->ptData.tableRow[rxValue[2]].tableEntry[i].readings = MINIMUM_RELIABLE_POSITIONS + 1;
@@ -919,13 +981,12 @@ void BLE_ss2kCustomCharacteristic::process(std::string rxValue, uint16_t connHan
LOG_BUF_APPEND("<-targetWatts");
if (rxValue[0] == cc_read) {
returnValue[0] = cc_success;
- returnValue[2] = (uint8_t)(rtConfig->watts.getTarget() & 0xff);
- returnValue[3] = (uint8_t)(rtConfig->watts.getTarget() >> 8);
+ put_le16(&returnValue[2], static_cast(rtConfig->watts.getTarget()));
returnLength += 2;
}
if (rxValue[0] == cc_write) {
returnValue[0] = cc_success;
- rtConfig->watts.setValue(bytes_to_u16(rxValue[3], rxValue[2]));
+ rtConfig->watts.setTarget(get_le16(&pData[2]));
LOG_BUF_APPEND("(%d)", rtConfig->watts.getTarget());
}
break;
@@ -946,15 +1007,12 @@ void BLE_ss2kCustomCharacteristic::process(std::string rxValue, uint16_t connHan
LOG_BUF_APPEND("<-hMin");
if (rxValue[0] == cc_read) {
returnValue[0] = cc_success;
- returnValue[2] = (uint8_t)(userConfig->getHMin() & 0xff);
- returnValue[3] = (uint8_t)(userConfig->getHMin() >> 8);
- returnValue[4] = (uint8_t)(userConfig->getHMin() >> 16);
- returnValue[5] = (uint8_t)(userConfig->getHMin() >> 24);
+ put_le32s(&returnValue[2], userConfig->getHMin());
returnLength += 4;
}
if (rxValue[0] == cc_write) {
returnValue[0] = cc_success;
- int32_t hMin = int32_t((uint8_t)(rxValue[2]) << 0 | (uint8_t)(rxValue[3]) << 8 | (uint8_t)(rxValue[4]) << 16 | (uint8_t)(rxValue[5]) << 24);
+ int32_t hMin = get_le32s(&pData[2]);
userConfig->setHMin(hMin);
rtConfig->setMinStep(hMin);
LOG_BUF_APPEND(" (%d)", hMin);
@@ -965,15 +1023,12 @@ void BLE_ss2kCustomCharacteristic::process(std::string rxValue, uint16_t connHan
LOG_BUF_APPEND("<-hMax");
if (rxValue[0] == cc_read) {
returnValue[0] = cc_success;
- returnValue[2] = (uint8_t)(userConfig->getHMax() & 0xff);
- returnValue[3] = (uint8_t)(userConfig->getHMax() >> 8);
- returnValue[4] = (uint8_t)(userConfig->getHMax() >> 16);
- returnValue[5] = (uint8_t)(userConfig->getHMax() >> 24);
+ put_le32s(&returnValue[2], userConfig->getHMax());
returnLength += 4;
}
if (rxValue[0] == cc_write) {
returnValue[0] = cc_success;
- int32_t hMax = int32_t((uint8_t)(rxValue[2]) << 0 | (uint8_t)(rxValue[3]) << 8 | (uint8_t)(rxValue[4]) << 16 | (uint8_t)(rxValue[5]) << 24);
+ int32_t hMax = get_le32s(&pData[2]);
Serial.printf("hMax: %d\n <--------------------------------------------", hMax);
userConfig->setHMax(hMax);
rtConfig->setMaxStep(hMax);
@@ -985,13 +1040,12 @@ void BLE_ss2kCustomCharacteristic::process(std::string rxValue, uint16_t connHan
LOG_BUF_APPEND("<-homingSensitivity");
if (rxValue[0] == cc_read) {
returnValue[0] = cc_success;
- returnValue[2] = (uint8_t)(userConfig->getHomingSensitivity() & 0xff);
- returnValue[3] = (uint8_t)(userConfig->getHomingSensitivity() >> 8);
+ put_le16(&returnValue[2], static_cast(userConfig->getHomingSensitivity()));
returnLength += 2;
}
if (rxValue[0] == cc_write) {
returnValue[0] = cc_success;
- userConfig->setHomingSensitivity(bytes_to_u16(rxValue[3], rxValue[2]));
+ userConfig->setHomingSensitivity(get_le16(&pData[2]));
LOG_BUF_APPEND("(%d)", userConfig->getHomingSensitivity());
}
break;
@@ -1076,12 +1130,6 @@ void BLE_ss2kCustomCharacteristic::parseNemit() {
static userParameters _oldParams;
static RuntimeParameters _oldRTParams;
- if (userConfig->getAutoUpdate() != _oldParams.getAutoUpdate()) {
- _oldParams.setAutoUpdate(userConfig->getAutoUpdate());
- BLE_ss2kCustomCharacteristic::notify(BLE_autoUpdate);
- return; // only do one at a time because immediate update isn't super important for these values
- }
-
if (strcmp(userConfig->getFirmwareUpdateURL(), _oldParams.getFirmwareUpdateURL()) != 0) {
_oldParams.setFirmwareUpdateURL(userConfig->getFirmwareUpdateURL());
BLE_ss2kCustomCharacteristic::notify(BLE_firmwareUpdateURL);
diff --git a/src/BLE_Cycling_Power_Service.cpp b/src/BLE_Cycling_Power_Service.cpp
index 7da01ea8..4c0a7a12 100644
--- a/src/BLE_Cycling_Power_Service.cpp
+++ b/src/BLE_Cycling_Power_Service.cpp
@@ -6,6 +6,7 @@
*/
#include "BLE_Cycling_Power_Service.h"
#include "DirConManager.h"
+#include "ByteUtils.h"
#include
BLE_Cycling_Power_Service::BLE_Cycling_Power_Service() : pPowerMonitor(nullptr), cyclingPowerFeatureCharacteristic(nullptr), sensorLocationCharacteristic(nullptr) {}
@@ -19,10 +20,7 @@ void BLE_Cycling_Power_Service::setupService(NimBLEServer *pServer, MyCharacteri
CyclingPowerFeatureFlags::Types cpFeatureFlags = CyclingPowerFeatureFlags::WheelRevolutionDataSupported | CyclingPowerFeatureFlags::CrankRevolutionDataSupported;
- cpFeature[0] = static_cast(cpFeatureFlags & 0xFF);
- cpFeature[1] = static_cast((cpFeatureFlags >> 8) & 0xFF);
- cpFeature[2] = static_cast((cpFeatureFlags >> 16) & 0xFF);
- cpFeature[3] = static_cast((cpFeatureFlags >> 24) & 0xFF);
+ put_le32(cpFeature, static_cast(cpFeatureFlags));
cyclingPowerFeatureCharacteristic->setValue(cpFeature, sizeof(cpFeature));
sensorLocationCharacteristic->setValue(cpsLocation, sizeof(cpsLocation));
diff --git a/src/BLE_Cycling_Speed_Cadence.cpp b/src/BLE_Cycling_Speed_Cadence.cpp
index da2a2ce0..f2168ed2 100644
--- a/src/BLE_Cycling_Speed_Cadence.cpp
+++ b/src/BLE_Cycling_Speed_Cadence.cpp
@@ -6,6 +6,7 @@
*/
#include "BLE_Cycling_Speed_Cadence.h"
#include "DirConManager.h"
+#include "ByteUtils.h"
#include
BLE_Cycling_Speed_Cadence::BLE_Cycling_Speed_Cadence() : pCyclingSpeedCadenceService(nullptr), cscMeasurement(nullptr), cscFeature(nullptr) {}
@@ -18,8 +19,7 @@ void BLE_Cycling_Speed_Cadence::setupService(NimBLEServer *pServer, MyCharacteri
CyclingSpeedCadenceFeatureFlags::Types cscFeatureFlags =
CyclingSpeedCadenceFeatureFlags::WheelRevolutionDataSupported | CyclingSpeedCadenceFeatureFlags::CrankRevolutionDataSupported;
- cscFeatureBytes[0] = static_cast(cscFeatureFlags & 0xFF);
- cscFeatureBytes[1] = static_cast((cscFeatureFlags >> 8) & 0xFF);
+ put_le16(cscFeatureBytes, static_cast(cscFeatureFlags));
cscFeature->setValue(cscFeatureBytes, sizeof(cscFeatureBytes));
cscMeasurement->setCallbacks(chrCallbacks);
diff --git a/src/BLE_Fitness_Machine_Service.cpp b/src/BLE_Fitness_Machine_Service.cpp
index f5610394..8776e090 100644
--- a/src/BLE_Fitness_Machine_Service.cpp
+++ b/src/BLE_Fitness_Machine_Service.cpp
@@ -7,6 +7,7 @@
#include "BLE_Fitness_Machine_Service.h"
#include "DirConManager.h"
#include "Main.h"
+#include "ByteUtils.h"
#include
#include
@@ -186,8 +187,8 @@ void BLE_Fitness_Machine_Service::processFTMSWrite() {
case FitnessMachineControlPointProcedure::SetTargetInclination: {
rtConfig->setFTMSMode((uint8_t)rxValue[0]);
returnValue[2] = FitnessMachineControlPointResultCode::Success;
- int16_t rawInclineTenthsPercent = (int16_t)((rxValue[2] << 8) | rxValue[1]); // signed 0.1% units
- port = static_cast(rawInclineTenthsPercent) * 10; // convert to 0.01% units
+ int16_t rawInclineTenthsPercent = get_le16s(&pData[1]); // signed 0.1% units
+ port = static_cast(rawInclineTenthsPercent) * 10; // convert to 0.01% units
rtConfig->setTargetIncline(port);
logBufLength += snprintf(logBuf + logBufLength,
kLogBufCapacity - logBufLength,
@@ -199,7 +200,7 @@ void BLE_Fitness_Machine_Service::processFTMSWrite() {
case FitnessMachineControlPointProcedure::SetTargetResistanceLevel: {
rtConfig->setFTMSMode((uint8_t)rxValue[0]);
- int16_t requestedResistance = (int16_t)((rxValue[2] << 8) | rxValue[1]);
+ int16_t requestedResistance = get_le16s(&pData[1]);
if (requestedResistance >= rtConfig->getMinResistance() && requestedResistance <= rtConfig->getMaxResistance()) {
rtConfig->resistance.setTarget(requestedResistance);
@@ -246,14 +247,15 @@ void BLE_Fitness_Machine_Service::processFTMSWrite() {
rtConfig->setFTMSMode((uint8_t)rxValue[0]);
if (spinBLEClient.connectedPM || rtConfig->watts.getSimulate() || spinBLEClient.connectedCD) {
returnValue[2] = FitnessMachineControlPointResultCode::Success; // 0x01;
- rtConfig->watts.setTarget(bytes_to_u16(rxValue[2], rxValue[1]));
+ rtConfig->watts.setTarget(get_le16s(&pData[1]));
logBufLength += snprintf(logBuf + logBufLength, kLogBufCapacity - logBufLength, "-> ERG Mode Target: %d Current: %d Incline: %2f", rtConfig->watts.getTarget(),
rtConfig->watts.getValue(), rtConfig->getTargetIncline() / 100);
ftmsStatus = {FitnessMachineStatus::TargetPowerChanged, (uint8_t)rxValue[1], (uint8_t)rxValue[2]};
ftmsTrainingStatus[1] = FitnessMachineTrainingStatus::WattControl; // 0x0C;
// Adjust set point for powerCorrectionFactor and send to FTMS server (if connected)
int adjustedTarget = rtConfig->watts.getTarget() / userConfig->getPowerCorrectionFactor();
- const uint8_t translated[] = {FitnessMachineControlPointProcedure::SetTargetPower, (uint8_t)(adjustedTarget % 256), (uint8_t)(adjustedTarget / 256)};
+ uint8_t translated[] = {FitnessMachineControlPointProcedure::SetTargetPower, 0, 0};
+ put_le16s(&translated[1], static_cast(adjustedTarget));
spinBLEClient.FTMSControlPointWrite(translated, 3);
} else {
returnValue[2] = FitnessMachineControlPointResultCode::OpCodeNotSupported; // 0x02; no power meter connected, so no ERG
@@ -286,13 +288,10 @@ void BLE_Fitness_Machine_Service::processFTMSWrite() {
case FitnessMachineControlPointProcedure::SetIndoorBikeSimulationParameters: { // sim mode
rtConfig->setFTMSMode((uint8_t)rxValue[0]);
returnValue[2] = FitnessMachineControlPointResultCode::Success; // 0x01;
- signed char buf[2];
- // int16_t windSpeed = (rxValue[2] << 8) + rxValue[1];
- buf[0] = rxValue[3]; // (Least significant byte)
- buf[1] = rxValue[4]; // (Most significant byte)
+ // int16_t windSpeed = get_le16s(&pData[1]);
// int8_t rollingResistance = rxValue[5];
// int8_t windResistance = rxValue[6];
- port = bytes_to_u16(buf[1], buf[0]);
+ port = get_le16s(&pData[3]);
rtConfig->setTargetIncline(port);
logBufLength += snprintf(logBuf + logBufLength, kLogBufCapacity - logBufLength, "-> Sim Mode Incline %2f", rtConfig->getTargetIncline() / 100);
ftmsStatus = {FitnessMachineStatus::IndoorBikeSimulationParametersChanged,
@@ -330,7 +329,7 @@ void BLE_Fitness_Machine_Service::processFTMSWrite() {
case FitnessMachineControlPointProcedure::SetTargetedCadence: {
rtConfig->setFTMSMode((uint8_t)rxValue[0]);
returnValue[2] = FitnessMachineControlPointResultCode::Success; // 0x01;
- int targetCadence = bytes_to_u16(rxValue[2], rxValue[1]);
+ int targetCadence = get_le16(&pData[1]);
// rtConfig->setTargetCadence(targetCadence);
logBufLength += snprintf(logBuf + logBufLength, kLogBufCapacity - logBufLength, "-> Target Cadence: %d ", targetCadence);
ftmsStatus = {FitnessMachineStatus::TargetedCadenceChanged, (uint8_t)rxValue[1], (uint8_t)rxValue[2]};
diff --git a/src/BLE_Server.cpp b/src/BLE_Server.cpp
index 90eebaf4..c9810c27 100644
--- a/src/BLE_Server.cpp
+++ b/src/BLE_Server.cpp
@@ -62,7 +62,7 @@ std::string bleAdvertisementName(const char* deviceName) {
return name;
}
-void addIpAddressToAdvertisement(NimBLEAdvertising* advertising) {
+bool addIpAddressToAdvertisement(NimBLEAdvertising* advertising) {
IPAddress ipAddress = WiFi.status() == WL_CONNECTED ? WiFi.localIP() : WiFi.softAPIP();
const uint8_t manufacturerData[] = {
0xff, 0xff, // Reserved Bluetooth SIG company identifier for development/testing.
@@ -73,10 +73,46 @@ void addIpAddressToAdvertisement(NimBLEAdvertising* advertising) {
if (advertising->setManufacturerData(manufacturerData, sizeof(manufacturerData))) {
SS2K_LOG(BLE_SERVER_LOG_TAG, "Advertising WiFi IP address %s", ipAddress.toString().c_str());
+ return true;
} else {
SS2K_LOGW(BLE_SERVER_LOG_TAG, "Unable to fit WiFi IP address in BLE advertisement data");
+ return false;
}
}
+
+bool configureBLEAdvertisement(NimBLEAdvertising* advertising) {
+ // NimBLEAdvertising::setManufacturerData() appends rather than replaces an
+ // existing field. Rebuild both payloads so an IP refresh cannot retain the
+ // original 0.0.0.0 manufacturer data or overflow the 31-byte advertisement.
+ advertising->clearData();
+ advertising->enableScanResponse(true);
+ advertising->addServiceUUID(CYCLINGPOWERSERVICE_UUID);
+ advertising->addServiceUUID(CSCSERVICE_UUID);
+ // Garmin watches won't connect if HR is advertised.
+ // advertising->addServiceUUID(HEARTSERVICE_UUID);
+ // Most apps look for FTMS to recognize the device as a smart trainer.
+ advertising->addServiceUUID(FITNESSMACHINESERVICE_UUID);
+ advertising->setAppearance(0x0484); // Cycling Power Sensor.
+ if (!addIpAddressToAdvertisement(advertising)) return false;
+
+ NimBLEAdvertisementData scanResponseData;
+ const std::string advertisedName = bleAdvertisementName(userConfig->getDeviceName());
+ if (advertisedName.size() < std::strlen(userConfig->getDeviceName())) {
+ scanResponseData.setShortName(advertisedName);
+ SS2K_LOGW(BLE_SERVER_LOG_TAG, "BLE device name shortened to '%s' to fit scan response", advertisedName.c_str());
+ } else {
+ scanResponseData.setName(advertisedName);
+ }
+ scanResponseData.setCompleteServices(SMARTSPIN2K_SERVICE_UUID);
+ if (!advertising->setScanResponseData(scanResponseData)) {
+ SS2K_LOGE(BLE_SERVER_LOG_TAG, "Unable to configure BLE scan response data");
+ return false;
+ }
+
+ advertising->setMaxInterval(250);
+ advertising->setMinInterval(160);
+ return true;
+}
} // namespace
void BLERequestMtuExchange(uint16_t connectionHandle) {
@@ -96,9 +132,6 @@ void startBLEServer() {
// start services
BLEAdvertising* pAdvertising = BLEDevice::getAdvertising();
- pAdvertising->enableScanResponse(true);
- NimBLEAdvertisementData oScanResponseData;
- std::vector oServiceUUIDs;
cyclingSpeedCadenceService.setupService(spinBLEServer.pServer, &chrCallbacks);
cyclingPowerService.setupService(spinBLEServer.pServer, &chrCallbacks);
heartService.setupService(spinBLEServer.pServer, &chrCallbacks);
@@ -107,29 +140,12 @@ void startBLEServer() {
deviceInformationService.setupService(spinBLEServer.pServer);
// zwiftService.setupService(spinBLEServer.pServer);
// openBikeControlService.setupService(spinBLEServer.pServer);
- pAdvertising->addServiceUUID(CYCLINGPOWERSERVICE_UUID);
- pAdvertising->addServiceUUID(CSCSERVICE_UUID);
- // Garmin watches won't connect if HR is advertised.
- // pAdvertising->addServiceUUID(HEARTSERVICE_UUID);
- // Most apps look for the fitness machine service UUID to recognize the device as a smart trainer.
- pAdvertising->addServiceUUID(FITNESSMACHINESERVICE_UUID);
// uncoment to enable as controller. Zwift won't pair as ct and controller at the same time.
// pAdvertising->addServiceUUID(ZWIFT_RIDE_CUSTOM_SERVICE_UUID);
// pAdvertising->addServiceUUID(OPENBIKECONTROL_SERVICE_UUID);
- // Garmin devices need this in the primary advertisement to recognize the device as a cycling power sensor.
- pAdvertising->setAppearance(0x0484); // Cycling Power Sensor, per https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile/
- // Keep the name and 128-bit SmartSpin2k UUID in the scan response. The primary
- // advertisement uses the space previously occupied by the duplicate name for the IP address.
- addIpAddressToAdvertisement(pAdvertising);
- const std::string advertisedName = bleAdvertisementName(userConfig->getDeviceName());
- if (advertisedName.size() < std::strlen(userConfig->getDeviceName())) {
- oScanResponseData.setShortName(advertisedName);
- SS2K_LOGW(BLE_SERVER_LOG_TAG, "BLE device name shortened to '%s' to fit scan response", advertisedName.c_str());
- } else {
- oScanResponseData.setName(advertisedName);
+ if (!configureBLEAdvertisement(pAdvertising)) {
+ SS2K_LOGE(BLE_SERVER_LOG_TAG, "Unable to configure BLE advertisement data");
}
- oScanResponseData.setCompleteServices(SMARTSPIN2K_SERVICE_UUID);
- pAdvertising->setScanResponseData(oScanResponseData);
// wattbikeService.setupService(spinBLEServer.pServer); // No callback needed
// sb20Service.begin();
@@ -137,13 +153,31 @@ void startBLEServer() {
// const std::string fitnessData = {0b00000001, 0b00100000, 0b00000000};
// pAdvertising->setServiceData(FITNESSMACHINESERVICE_UUID, fitnessData);
- pAdvertising->setMaxInterval(250);
- pAdvertising->setMinInterval(160);
pAdvertising->start();
SS2K_LOG(BLE_SERVER_LOG_TAG, "Bluetooth Characteristics defined!");
}
+void refreshBLEAdvertisementIp() {
+ if (!NimBLEDevice::isInitialized()) {
+ SS2K_LOGW(BLE_SERVER_LOG_TAG, "BLE is not initialized; IP advertisement refresh deferred");
+ return;
+ }
+
+ NimBLEAdvertising* advertising = NimBLEDevice::getAdvertising();
+ const bool wasAdvertising = advertising->isAdvertising();
+ if (wasAdvertising && !advertising->stop()) {
+ SS2K_LOGW(BLE_SERVER_LOG_TAG, "Unable to stop BLE advertising for IP address refresh");
+ return;
+ }
+ if (!configureBLEAdvertisement(advertising)) {
+ SS2K_LOGE(BLE_SERVER_LOG_TAG, "Unable to rebuild BLE advertisement with current IP address");
+ }
+ if (wasAdvertising && !advertising->start()) {
+ SS2K_LOGE(BLE_SERVER_LOG_TAG, "Unable to restart BLE advertising after IP address refresh");
+ }
+}
+
void SpinBLEServer::update() {
// Wheel and crank is used in multiple characteristics. Update first.
spinBLEServer.updateWheelAndCrankRev();
@@ -160,23 +194,35 @@ void SpinBLEServer::update() {
}
double SpinBLEServer::calculateSpeed() {
- // Constants for the formula: adjusted for calibration
- const double dragCoefficient = 1.95;
- const double frontalArea = 0.9; // m^2
- const double airDensity = 1.225; // kg/m^3
- const double rollingResistance = 0.004;
- const double combinedConstant = 0.5 * airDensity * dragCoefficient * frontalArea + rollingResistance;
- double power = rtConfig->watts.getValue(); // Power in watts
- double speedInMetersPerSecond = std::cbrt(power / combinedConstant); // Speed in m/s
-
- // Convert speed from m/s to km/h
- double speedKmH = speedInMetersPerSecond * 3.6;
-
- // Apply a calibration factor based on empirical data to adjust the speed into a realistic range
- double calibrationFactor = 1; // This is an example value; adjust based on calibration
- speedKmH *= calibrationFactor;
-
- return speedKmH;
+ // Estimate flat-road speed from P = 0.5 * rho * CdA * v^3 + Crr * m * g * v.
+ // These conservative defaults model an upright rider plus bike. This is only
+ // used when no real or simulated speed source is available.
+ constexpr double airDensity = 1.225; // kg/m^3
+ constexpr double dragArea = 0.50; // CdA, m^2
+ constexpr double rollingResistance = 0.004;
+ constexpr double totalMass = 90.0; // rider and bike, kg
+ constexpr double gravity = 9.80665; // m/s^2
+ constexpr double aerodynamicCoefficient = 0.5 * airDensity * dragArea;
+ constexpr double rollingCoefficient = rollingResistance * totalMass * gravity;
+
+ const double power = rtConfig->watts.getValue();
+ if (power <= 0.0) return 0.0;
+
+ // Solve the monotonic equation with a bounded binary search. 30 m/s is well
+ // above any expected indoor-bike estimate and keeps the calculation stable.
+ double low = 0.0;
+ double high = 30.0;
+ for (int i = 0; i < 24; ++i) {
+ const double speed = (low + high) / 2.0;
+ const double requiredPower = aerodynamicCoefficient * speed * speed * speed + rollingCoefficient * speed;
+ if (requiredPower < power) {
+ low = speed;
+ } else {
+ high = speed;
+ }
+ }
+
+ return ((low + high) / 2.0) * 3.6; // m/s to km/h
}
void SpinBLEServer::updateWheelAndCrankRev() {
diff --git a/src/BLE_Zwift_Service.cpp b/src/BLE_Zwift_Service.cpp
index 676e5deb..e1a72f6f 100644
--- a/src/BLE_Zwift_Service.cpp
+++ b/src/BLE_Zwift_Service.cpp
@@ -12,10 +12,14 @@
#include
using ZwiftProtocol::CommandCode;
+using ZwiftProtocol::decodeUleb128;
+using ZwiftProtocol::decodeZigZag64;
+using ZwiftProtocol::encodeUleb128;
using ZwiftProtocol::makeTag;
using ZwiftProtocol::RideButtonMask;
using ZwiftProtocol::toUnderlying;
using ZwiftProtocol::WireType;
+using ZwiftProtocol::uleb128Length;
namespace {
constexpr uint8_t kRideAnalogButtonsPayloadSize = 0x18;
@@ -340,7 +344,7 @@ void BLE_Zwift_Service::sendGeneralInfoSyncTx() {
cpos += nameLen;
size_t contentLen = cpos;
- size_t subContentLen = 1 + encodeUleb128Len(contentLen) + contentLen;
+ size_t subContentLen = 1 + uleb128Length(contentLen) + contentLen;
uint8_t resp[60];
size_t pos = 0;
@@ -428,7 +432,7 @@ void BLE_Zwift_Service::sendButtonNotification(RideButtonMask buttonMask) {
uint8_t buf[33];
buf[0] = toUnderlying(CommandCode::RideKeyPadStatus);
buf[1] = kRideKeypadButtonMapTag;
- size_t varintLen = encodeVarint32(buttonMap, &buf[2]);
+ size_t varintLen = encodeUleb128(buttonMap, &buf[2]);
size_t pos = 2 + varintLen;
memcpy(&buf[pos], analogData, sizeof(analogData));
pos += sizeof(analogData);
@@ -438,16 +442,6 @@ void BLE_Zwift_Service::sendButtonNotification(RideButtonMask buttonMask) {
DirConManager::notifyCharacteristic(NimBLEUUID(ZWIFT_RIDE_CUSTOM_SERVICE_UUID), asyncCharacteristic->getUUID(), buf, pos);
}
-size_t BLE_Zwift_Service::encodeVarint32(uint32_t value, uint8_t* buffer) {
- size_t i = 0;
- while (value > 0x7F) {
- buffer[i++] = static_cast((value & 0x7F) | 0x80);
- value >>= 7;
- }
- buffer[i++] = static_cast(value);
- return i;
-}
-
void BLE_Zwift_Service::sendAllButtonsReleased() {
asyncCharacteristic->setValue(ALL_RELEASED, ALL_RELEASED_LEN);
asyncCharacteristic->notify();
@@ -561,7 +555,7 @@ void BLE_Zwift_Service::handleZwiftCommand(const uint8_t* data, size_t length) {
pos += decoded;
if (fieldTag == makeTag(ZwiftProtocol::SimulationParam::Field::InclineX100, WireType::Varint)) {
- int64_t grade = static_cast((fieldValue >> 1) ^ -(fieldValue & 1));
+ int64_t grade = decodeZigZag64(fieldValue);
SS2K_LOG(getLogTag(), "SIM grade: %.2f%%", grade / 100.0);
rtConfig->setFTMSMode(FitnessMachineControlPointProcedure::SetIndoorBikeSimulationParameters);
rtConfig->setTargetIncline(static_cast(grade));
@@ -658,41 +652,6 @@ void BLE_Zwift_Service::applyGearRatio() {
SS2K_LOG(getLogTag(), "Gear %d -> shifter position %d", closestIndex, newShifterPos);
}
-size_t BLE_Zwift_Service::decodeUleb128(const uint8_t* buf, size_t bufLen, uint64_t* result) {
- *result = 0;
- size_t i = 0;
- unsigned shift = 0;
- while (i < bufLen) {
- uint8_t byte = buf[i];
- *result |= static_cast(byte & 0x7F) << shift;
- i++;
- if ((byte & 0x80) == 0) break;
- shift += 7;
- if (shift >= 64) break; // overflow protection
- }
- return i;
-}
-
-size_t BLE_Zwift_Service::encodeUleb128(uint64_t value, uint8_t* buffer) {
- size_t i = 0;
- do {
- uint8_t byte = static_cast(value & 0x7F);
- value >>= 7;
- if (value) byte |= 0x80;
- buffer[i++] = byte;
- } while (value);
- return i;
-}
-
-size_t BLE_Zwift_Service::encodeUleb128Len(uint64_t value) {
- size_t len = 0;
- do {
- value >>= 7;
- len++;
- } while (value);
- return len;
-}
-
// ---- Callbacks ----
void ZwiftSyncRxCallbacks::onWrite(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo) {
diff --git a/src/DirConManager.cpp b/src/DirConManager.cpp
index 90313cf4..b5d4b10f 100644
--- a/src/DirConManager.cpp
+++ b/src/DirConManager.cpp
@@ -9,6 +9,8 @@
#include "SS2KLog.h"
#include
#include
+#include
+#include
#define DIRCON_LOG_TAG "DirConManager"
@@ -16,10 +18,10 @@
bool DirConManager::started = false;
String DirConManager::statusMessage = "";
WiFiClient DirConManager::dirConClients[DIRCON_MAX_CLIENTS];
+bool DirConManager::clientActive[DIRCON_MAX_CLIENTS] = {false};
WiFiServer* DirConManager::tcpServer = nullptr;
uint8_t DirConManager::receiveBuffer[DIRCON_MAX_CLIENTS][DIRCON_RECEIVE_BUFFER_SIZE];
size_t DirConManager::receiveBufferLength[DIRCON_MAX_CLIENTS] = {0};
-uint8_t DirConManager::sendBuffer[DIRCON_SEND_BUFFER_SIZE];
uint8_t DirConManager::lastSequenceNumber[DIRCON_MAX_CLIENTS] = {0};
Subscription DirConManager::clientSubscriptions[DIRCON_MAX_CLIENTS][DIRCON_MAX_CHARACTERISTICS];
DirConManager::ServiceRegistration DirConManager::registeredServices[DIRCON_MAX_SERVICES] = {};
@@ -31,6 +33,31 @@ static size_t uuidListLength = 0;
namespace {
+struct DirConOutboundFrame {
+ size_t length = 0;
+ size_t offset = 0;
+ bool notification = false;
+ uint8_t data[DIRCON_SEND_BUFFER_SIZE];
+};
+
+struct DirConOutboundQueue {
+ DirConOutboundFrame frames[DIRCON_OUTBOUND_QUEUE_DEPTH];
+ size_t count = 0;
+ unsigned long lastProgressMs = 0;
+ uint32_t droppedNotifications = 0;
+ bool closeRequested = false;
+};
+
+DirConOutboundQueue s_outboundQueues[DIRCON_MAX_CLIENTS];
+SemaphoreHandle_t s_outboundMutex = xSemaphoreCreateMutex();
+
+void resetOutboundQueue(size_t clientIndex) {
+ s_outboundQueues[clientIndex].count = 0;
+ s_outboundQueues[clientIndex].lastProgressMs = millis();
+ s_outboundQueues[clientIndex].droppedNotifications = 0;
+ s_outboundQueues[clientIndex].closeRequested = false;
+}
+
const char* dirConMessageName(uint8_t identifier) {
switch (identifier) {
case DIRCON_MSGID_DISCOVER_SERVICES:
@@ -107,13 +134,22 @@ bool DirConManager::start() {
}
// Initialize buffers
+ if (s_outboundMutex == nullptr) {
+ SS2K_LOG(DIRCON_LOG_TAG, "Failed to create outbound queue mutex");
+ return false;
+ }
+
+ xSemaphoreTake(s_outboundMutex, portMAX_DELAY);
for (int i = 0; i < DIRCON_MAX_CLIENTS; i++) {
+ clientActive[i] = false;
receiveBufferLength[i] = 0;
lastSequenceNumber[i] = 0;
+ resetOutboundQueue(i);
for (int j = 0; j < DIRCON_MAX_CHARACTERISTICS; j++) {
clientSubscriptions[i][j].active = false;
}
}
+ xSemaphoreGive(s_outboundMutex);
// Setup MDNS service
setupMDNS();
@@ -142,6 +178,9 @@ bool DirConManager::start() {
void DirConManager::stop() {
if (started) {
+ // Prevent other tasks from queuing more notifications while clients close.
+ started = false;
+
// Stop TCP server and disconnect clients
if (tcpServer != nullptr) {
tcpServer->close();
@@ -149,13 +188,18 @@ void DirConManager::stop() {
tcpServer = nullptr;
}
+ xSemaphoreTake(s_outboundMutex, portMAX_DELAY);
for (int i = 0; i < DIRCON_MAX_CLIENTS; i++) {
- if (dirConClients[i].connected()) {
- dirConClients[i].stop();
+ dirConClients[i].stop();
+ clientActive[i] = false;
+ receiveBufferLength[i] = 0;
+ resetOutboundQueue(i);
+ for (int j = 0; j < DIRCON_MAX_CHARACTERISTICS; j++) {
+ clientSubscriptions[i][j].active = false;
}
}
+ xSemaphoreGive(s_outboundMutex);
- started = false;
updateStatusMessage();
SS2K_LOG(DIRCON_LOG_TAG, "%s", statusMessage.c_str());
}
@@ -178,13 +222,16 @@ void DirConManager::update() {
// Handle data from connected clients
handleClientData();
+
+ // Socket writes happen only here and are explicitly non-blocking.
+ drainOutboundQueues();
}
// returns the number of connected clients
int DirConManager::connectedClients() {
int connectedClients = 0;
for (int i = 0; i < DIRCON_MAX_CLIENTS; i++) {
- if (dirConClients[i].connected()) {
+ if (clientActive[i]) {
connectedClients++;
}
}
@@ -202,7 +249,7 @@ void DirConManager::updateStatusMessage() {
void DirConManager::setupMDNS() {
// Static buffers for strings to avoid repeated allocations
static char macAddress[18]; // MAC format: 11:22:33:44:55:66\0
- static char serialNumber[12]; // SS2K-112233445566\0
+ static char serialNumber[13]; // Twelve decimal digits plus null terminator
// Get device MAC address using existing buffer
strcpy(macAddress, WiFi.macAddress().c_str());
@@ -213,8 +260,11 @@ void DirConManager::setupMDNS() {
}
}
- // Create a unique serial number (using MAC address), and remove the dashes and change the letters to decimal numbers.
- snprintf(serialNumber, sizeof(serialNumber), "%02X%02X%02X%02X%02X%02X", macAddress[0], macAddress[1], macAddress[3], macAddress[4], macAddress[6], macAddress[7]);
+ // MyWhoosh expects the DirCon serial number to contain decimal digits only.
+ // Preserve the established encoding from the original MyWhoosh compatibility
+ // fix: encode the six characters in the first three MAC octets as ASCII hex.
+ snprintf(serialNumber, sizeof(serialNumber), "%02X%02X%02X%02X%02X%02X", macAddress[0], macAddress[1], macAddress[3], macAddress[4], macAddress[6],
+ macAddress[7]);
// Add DirCon service to MDNS
SS2K_LOG(DIRCON_LOG_TAG, "Adding DirCon MDNS service: %s.%s on port %d", DIRCON_MDNS_SERVICE_NAME, DIRCON_MDNS_SERVICE_PROTOCOL, DIRCON_TCP_PORT);
@@ -311,8 +361,11 @@ void DirConManager::checkForNewClients() {
bool clientAccepted = false;
int clientIndex = -1;
+ configureClientSocket(newClient);
+
+ xSemaphoreTake(s_outboundMutex, portMAX_DELAY);
for (int i = 0; i < DIRCON_MAX_CLIENTS; i++) {
- if (!dirConClients[i].connected()) {
+ if (!clientActive[i]) {
dirConClients[i] = newClient;
clientAccepted = true;
clientIndex = i;
@@ -320,13 +373,16 @@ void DirConManager::checkForNewClients() {
// Clear receive buffer and subscription state
receiveBufferLength[i] = 0;
lastSequenceNumber[i] = 0;
+ resetOutboundQueue(i);
for (int j = 0; j < DIRCON_MAX_CHARACTERISTICS; j++) {
clientSubscriptions[i][j].active = false;
}
+ clientActive[i] = true;
break;
}
}
+ xSemaphoreGive(s_outboundMutex);
if (clientAccepted) {
String clientIP = dirConClients[clientIndex].remoteIP().toString();
@@ -338,19 +394,47 @@ void DirConManager::checkForNewClients() {
}
}
+void DirConManager::configureClientSocket(WiFiClient& client) {
+ int keepAliveIdle = DIRCON_TCP_KEEPIDLE_SECONDS;
+ int keepAliveInterval = DIRCON_TCP_KEEPINTVL_SECONDS;
+ int keepAliveCount = DIRCON_TCP_KEEPCNT;
+
+ if (client.setSocketOption(IPPROTO_TCP, TCP_KEEPIDLE, &keepAliveIdle, sizeof(keepAliveIdle)) < 0 ||
+ client.setSocketOption(IPPROTO_TCP, TCP_KEEPINTVL, &keepAliveInterval, sizeof(keepAliveInterval)) < 0 ||
+ client.setSocketOption(IPPROTO_TCP, TCP_KEEPCNT, &keepAliveCount, sizeof(keepAliveCount)) < 0) {
+ SS2K_LOG(DIRCON_LOG_TAG, "Warning: failed to configure DirCon TCP keepalive");
+ }
+}
+
+void DirConManager::closeClient(size_t clientIndex, const char* reason) {
+ if (clientIndex >= DIRCON_MAX_CLIENTS) {
+ return;
+ }
+
+ String clientIP = dirConClients[clientIndex].remoteIP().toString();
+
+ xSemaphoreTake(s_outboundMutex, portMAX_DELAY);
+ dirConClients[clientIndex].stop();
+ clientActive[clientIndex] = false;
+ receiveBufferLength[clientIndex] = 0;
+ resetOutboundQueue(clientIndex);
+ for (int j = 0; j < DIRCON_MAX_CHARACTERISTICS; j++) {
+ clientSubscriptions[clientIndex][j].active = false;
+ }
+ xSemaphoreGive(s_outboundMutex);
+
+ SS2K_LOG(DIRCON_LOG_TAG, "DirCon client %s disconnected: %s", clientIP.c_str(), reason);
+ updateStatusMessage();
+}
+
void DirConManager::handleClientData() {
for (int i = 0; i < DIRCON_MAX_CLIENTS; i++) {
- if (!dirConClients[i].connected()) {
+ if (!clientActive[i]) {
continue;
}
- // Check if client disconnected
if (!dirConClients[i].connected()) {
- String clientIP = dirConClients[i].remoteIP().toString();
- SS2K_LOG(DIRCON_LOG_TAG, "DirCon client %s disconnected", clientIP.c_str());
- dirConClients[i].stop();
- removeAllSubscriptions(i);
- updateStatusMessage();
+ closeClient(i, "socket no longer connected");
continue;
}
@@ -398,6 +482,127 @@ void DirConManager::handleClientData() {
}
}
+bool DirConManager::queueOutboundFrame(size_t clientIndex, const uint8_t* data, size_t length, bool notification, const NimBLEUUID* requiredSubscription) {
+ if (clientIndex >= DIRCON_MAX_CLIENTS || data == nullptr || length == 0) {
+ return false;
+ }
+ if (xSemaphoreTake(s_outboundMutex, pdMS_TO_TICKS(10)) != pdTRUE) {
+ SS2K_LOG(DIRCON_LOG_TAG, "Failed to acquire outbound queue mutex, dropping %s", notification ? "notification" : "response");
+ return false;
+ }
+
+ bool queued = false;
+ DirConOutboundQueue& outbound = s_outboundQueues[clientIndex];
+ bool subscriptionMatches = requiredSubscription == nullptr;
+ if (!subscriptionMatches) {
+ for (int i = 0; i < DIRCON_MAX_CHARACTERISTICS; i++) {
+ if (clientSubscriptions[clientIndex][i].active && clientSubscriptions[clientIndex][i].uuid == *requiredSubscription) {
+ subscriptionMatches = true;
+ break;
+ }
+ }
+ }
+
+ if (clientActive[clientIndex] && subscriptionMatches) {
+ if (length > DIRCON_SEND_BUFFER_SIZE) {
+ SS2K_LOG(DIRCON_LOG_TAG, "Cannot queue %u-byte frame for client %u; maximum is %u", static_cast(length), static_cast(clientIndex),
+ DIRCON_SEND_BUFFER_SIZE);
+ xSemaphoreGive(s_outboundMutex);
+ return false;
+ }
+
+ if (outbound.count >= DIRCON_OUTBOUND_QUEUE_DEPTH && !notification) {
+ // Preserve command responses by evicting the newest notification that has not begun sending.
+ for (size_t i = outbound.count; i > 0; i--) {
+ size_t candidate = i - 1;
+ if (outbound.frames[candidate].notification && outbound.frames[candidate].offset == 0) {
+ for (size_t j = candidate; j + 1 < outbound.count; j++) {
+ outbound.frames[j] = outbound.frames[j + 1];
+ }
+ outbound.count--;
+ outbound.droppedNotifications++;
+ break;
+ }
+ }
+ }
+
+ if (outbound.count < DIRCON_OUTBOUND_QUEUE_DEPTH) {
+ if (outbound.count == 0) {
+ outbound.lastProgressMs = millis();
+ }
+ DirConOutboundFrame& frame = outbound.frames[outbound.count++];
+ memcpy(frame.data, data, length);
+ frame.length = length;
+ frame.offset = 0;
+ frame.notification = notification;
+ queued = true;
+ } else if (notification) {
+ outbound.droppedNotifications++;
+ uint32_t dropped = outbound.droppedNotifications;
+ if (dropped == 1 || (dropped & (dropped - 1)) == 0) {
+ SS2K_LOG(DIRCON_LOG_TAG, "Client %u outbound queue full; dropped %u notification(s)", static_cast(clientIndex), static_cast(dropped));
+ }
+ } else {
+ outbound.closeRequested = true;
+ SS2K_LOG(DIRCON_LOG_TAG, "Client %u outbound queue full with responses; scheduling disconnect", static_cast(clientIndex));
+ }
+ }
+
+ xSemaphoreGive(s_outboundMutex);
+ return queued;
+}
+
+void DirConManager::drainOutboundQueues() {
+ for (size_t clientIndex = 0; clientIndex < DIRCON_MAX_CLIENTS; clientIndex++) {
+ bool shouldClose = false;
+ const char* closeReason = "outbound send failed";
+
+ xSemaphoreTake(s_outboundMutex, portMAX_DELAY);
+ DirConOutboundQueue& outbound = s_outboundQueues[clientIndex];
+ if (!clientActive[clientIndex]) {
+ xSemaphoreGive(s_outboundMutex);
+ continue;
+ }
+
+ if (outbound.closeRequested) {
+ shouldClose = true;
+ closeReason = "outbound queue exhausted";
+ } else if (outbound.count > 0) {
+ DirConOutboundFrame& frame = outbound.frames[0];
+ int socketFd = dirConClients[clientIndex].fd();
+ ssize_t sent = 0;
+
+ if (socketFd < 0) {
+ shouldClose = true;
+ closeReason = "invalid socket during send";
+ } else if ((sent = ::send(socketFd, frame.data + frame.offset, frame.length - frame.offset, MSG_DONTWAIT)) > 0) {
+ frame.offset += static_cast(sent);
+ outbound.lastProgressMs = millis();
+ if (frame.offset == frame.length) {
+ for (size_t i = 0; i + 1 < outbound.count; i++) {
+ outbound.frames[i] = outbound.frames[i + 1];
+ }
+ outbound.count--;
+ }
+ } else if (sent == 0) {
+ shouldClose = true;
+ closeReason = "peer closed during send";
+ } else if (errno != EAGAIN && errno != EWOULDBLOCK && errno != ENOMEM) {
+ SS2K_LOG(DIRCON_LOG_TAG, "Non-blocking send failed for client %u: errno %d", static_cast(clientIndex), errno);
+ shouldClose = true;
+ } else if (millis() - outbound.lastProgressMs >= DIRCON_SEND_STALL_TIMEOUT_MS) {
+ shouldClose = true;
+ closeReason = "outbound send stalled";
+ }
+ }
+ xSemaphoreGive(s_outboundMutex);
+
+ if (shouldClose) {
+ closeClient(clientIndex, closeReason);
+ }
+ }
+}
+
bool DirConManager::processDirConMessage(DirConMessage* message, size_t clientIndex) {
if (!message->Request) {
// We only process requests, not responses
@@ -572,7 +777,7 @@ void DirConManager::sendErrorResponse(uint8_t messageId, uint8_t sequenceNumber,
}
void DirConManager::sendResponse(DirConMessage* message, size_t clientIndex) {
- if (clientIndex >= DIRCON_MAX_CLIENTS || !dirConClients[clientIndex].connected()) {
+ if (clientIndex >= DIRCON_MAX_CLIENTS || !clientActive[clientIndex]) {
SS2K_LOG(DIRCON_LOG_TAG, "Cannot send response - client %d is not connected", clientIndex);
return;
}
@@ -602,14 +807,16 @@ void DirConManager::sendResponse(DirConMessage* message, size_t clientIndex) {
static_cast(message->SequenceNumber), uuid.c_str(), result, static_cast(message->AdditionalData.size()),
message->AdditionalData.empty() ? "" : ":", payloadPreview);
}
- dirConClients[clientIndex].write(encodedMessage->data(), encodedMessage->size());
+ if (!queueOutboundFrame(clientIndex, encodedMessage->data(), encodedMessage->size(), false)) {
+ SS2K_LOG(DIRCON_LOG_TAG, "Failed to queue response for client %u", static_cast(clientIndex));
+ }
} else {
SS2K_LOG(DIRCON_LOG_TAG, "Error: No encoded message to send");
}
}
void DirConManager::notifyCharacteristic(const NimBLEUUID& serviceUuid, const NimBLEUUID& characteristicUuid, const uint8_t* data, size_t length, bool onlySubscribers) {
- if (!started || !connectedClients()) {
+ if (!started) {
return;
}
@@ -620,9 +827,28 @@ void DirConManager::notifyCharacteristic(const NimBLEUUID& serviceUuid, const Ni
broadcastNotification(characteristicUuid, data, length, onlySubscribers);
}
-static SemaphoreHandle_t s_notifyMutex = xSemaphoreCreateMutex();
-
void DirConManager::broadcastNotification(const NimBLEUUID& characteristicUuid, const uint8_t* data, size_t length, bool onlySubscribers) {
+ // Most characteristic updates have no DirCon recipient. Check before building
+ // the encoded frame so BLE scan callbacks do not compete with needless vector
+ // allocations on memory-constrained classic ESP32 devices.
+ bool hasRecipient = false;
+ xSemaphoreTake(s_outboundMutex, portMAX_DELAY);
+ for (size_t clientIndex = 0; clientIndex < DIRCON_MAX_CLIENTS && !hasRecipient; clientIndex++) {
+ if (!clientActive[clientIndex]) continue;
+ if (!onlySubscribers) {
+ hasRecipient = true;
+ break;
+ }
+ for (size_t subscriptionIndex = 0; subscriptionIndex < DIRCON_MAX_CHARACTERISTICS; subscriptionIndex++) {
+ if (clientSubscriptions[clientIndex][subscriptionIndex].active && clientSubscriptions[clientIndex][subscriptionIndex].uuid == characteristicUuid) {
+ hasRecipient = true;
+ break;
+ }
+ }
+ }
+ xSemaphoreGive(s_outboundMutex);
+ if (!hasRecipient) return;
+
DirConMessage notification; // stack-allocated, safe per-call
notification.Request = false;
@@ -640,23 +866,14 @@ void DirConManager::broadcastNotification(const NimBLEUUID& characteristicUuid,
return;
}
- // Grab mutex before touching the TCP client
- if (xSemaphoreTake(s_notifyMutex, pdMS_TO_TICKS(10)) != pdTRUE) {
- SS2K_LOG(DIRCON_LOG_TAG, "broadcastNotification: failed to acquire mutex, dropping notification");
- return;
- }
-
for (int i = 0; i < DIRCON_MAX_CLIENTS; i++) {
- if (!dirConClients[i].connected() || (onlySubscribers && !hasSubscription(i, characteristicUuid))) {
- continue;
- }
#ifdef DEBUG_DIRCON_MESSAGES
- DirConMessage::printVectorBytesToSerial(*encodedMessage, false);
+ if (!onlySubscribers || hasSubscription(i, characteristicUuid)) {
+ DirConMessage::printVectorBytesToSerial(*encodedMessage, false);
+ }
#endif
- dirConClients[i].write(encodedMessage->data(), encodedMessage->size());
+ queueOutboundFrame(i, encodedMessage->data(), encodedMessage->size(), true, onlySubscribers ? &characteristicUuid : nullptr);
}
-
- xSemaphoreGive(s_notifyMutex);
}
std::vector DirConManager::getCharacteristics(const NimBLEUUID& serviceUuid) {
@@ -713,45 +930,69 @@ uint8_t DirConManager::getDirConProperties(uint32_t characteristicProperties) {
}
void DirConManager::addSubscription(size_t clientIndex, const NimBLEUUID& characteristicUuid) {
+ if (clientIndex >= DIRCON_MAX_CLIENTS) {
+ return;
+ }
+ xSemaphoreTake(s_outboundMutex, portMAX_DELAY);
for (int j = 0; j < DIRCON_MAX_CHARACTERISTICS; j++) {
if (clientSubscriptions[clientIndex][j].active &&
clientSubscriptions[clientIndex][j].uuid == characteristicUuid) {
+ xSemaphoreGive(s_outboundMutex);
return; // already subscribed
}
if (!clientSubscriptions[clientIndex][j].active) {
clientSubscriptions[clientIndex][j].uuid = characteristicUuid;
clientSubscriptions[clientIndex][j].active = true;
+ xSemaphoreGive(s_outboundMutex);
SS2K_LOG(DIRCON_LOG_TAG, "Client %d subscribed to characteristic %s", clientIndex, characteristicUuid.toString().c_str());
return;
}
}
+ xSemaphoreGive(s_outboundMutex);
SS2K_LOG(DIRCON_LOG_TAG, "Warning: no free subscription slot for client %d", clientIndex);
}
void DirConManager::removeSubscription(size_t clientIndex, const NimBLEUUID& characteristicUuid) {
+ if (clientIndex >= DIRCON_MAX_CLIENTS) {
+ return;
+ }
+ xSemaphoreTake(s_outboundMutex, portMAX_DELAY);
for (int j = 0; j < DIRCON_MAX_CHARACTERISTICS; j++) {
if (clientSubscriptions[clientIndex][j].active &&
clientSubscriptions[clientIndex][j].uuid == characteristicUuid) {
clientSubscriptions[clientIndex][j].active = false;
+ xSemaphoreGive(s_outboundMutex);
SS2K_LOG(DIRCON_LOG_TAG, "Client %d unsubscribed from characteristic %s", clientIndex, characteristicUuid.toString().c_str());
return;
}
}
+ xSemaphoreGive(s_outboundMutex);
}
void DirConManager::removeAllSubscriptions(size_t clientIndex) {
+ if (clientIndex >= DIRCON_MAX_CLIENTS) {
+ return;
+ }
+ xSemaphoreTake(s_outboundMutex, portMAX_DELAY);
for (int j = 0; j < DIRCON_MAX_CHARACTERISTICS; j++) {
clientSubscriptions[clientIndex][j].active = false;
}
+ xSemaphoreGive(s_outboundMutex);
SS2K_LOG(DIRCON_LOG_TAG, "Removed all subscriptions for client %d", clientIndex);
}
bool DirConManager::hasSubscription(size_t clientIndex, const NimBLEUUID& characteristicUuid) {
+ if (clientIndex >= DIRCON_MAX_CLIENTS) {
+ return false;
+ }
+ xSemaphoreTake(s_outboundMutex, portMAX_DELAY);
for (int j = 0; j < DIRCON_MAX_CHARACTERISTICS; j++) {
if (clientSubscriptions[clientIndex][j].active &&
clientSubscriptions[clientIndex][j].uuid == characteristicUuid) {
+ xSemaphoreGive(s_outboundMutex);
return true;
}
}
+ xSemaphoreGive(s_outboundMutex);
return false;
}
diff --git a/src/DirConMessage.cpp b/src/DirConMessage.cpp
index 1da1bdf0..7f3ccb07 100644
--- a/src/DirConMessage.cpp
+++ b/src/DirConMessage.cpp
@@ -5,6 +5,8 @@
* SPDX-License-Identifier: GPL-2.0-only
*/
#include "DirConMessage.h"
+#include "ByteUtils.h"
+#include "DirConUUIDCodec.h"
#include "SS2KLog.h"
#include "settings.h"
#include "BLE_Common.h"
@@ -13,6 +15,16 @@
static constexpr size_t kDirConHexLogMaxBytes = 256;
+namespace {
+
+void appendBigEndian16(std::vector& output, uint16_t value) {
+ uint8_t bytes[2];
+ put_be16(bytes, value);
+ output.insert(output.end(), bytes, bytes + sizeof(bytes));
+}
+
+} // namespace
+
// Helper functions to print raw bytes to serial monitor
#ifdef DEBUG_DIRCON_MESSAGES
void printRawBytesToSerial(const uint8_t* data, size_t length, bool isIncoming) {
@@ -39,30 +51,6 @@ void DirConMessage::printVectorBytesToSerial(const std::vector& data, b
}
#endif
-// Helper functions for UUID conversion - matching the expected DirCon protocol format
-void uuidToBytes(NimBLEUUID& uuid, std::vector& message) {
- // Log the UUID being processed for debugging
- //SS2K_LOG(DIRCON_LOG_TAG, "Processing UUID: %s", uuid.toString().c_str());
-
- uint8_t *uuidBytes = (uint8_t*)uuid.to128().getBase();
-
- // Add the bytes to the message
- for(size_t i = 16; i > 0; i--) {
- message.push_back(uuidBytes[i]);
- }
-}
-
-NimBLEUUID bytesToUuid(uint8_t* data, size_t offset) {
- uint8_t* ptr = data + offset;
-
- NimBLEUUID uUidOut(ptr, 16);
- NimBLEUUID reversed = uUidOut.reverseByteOrder();
-
- //SS2K_LOG(DIRCON_LOG_TAG, "Derived UUID: %s", reversed.toString().c_str());
-
- return reversed;
-}
-
DirConMessage::DirConMessage() {}
std::vector* DirConMessage::encode(uint8_t sequenceNumber) {
@@ -92,20 +80,17 @@ std::vector* DirConMessage::encode(uint8_t sequenceNumber) {
// Handle error responses
if (!this->Request && this->ResponseCode != DIRCON_RESPCODE_SUCCESS_REQUEST) {
this->Length = 0;
- this->encodedMessage.push_back((uint8_t)(this->Length >> 8));
- this->encodedMessage.push_back((uint8_t)(this->Length));
+ appendBigEndian16(this->encodedMessage, this->Length);
}
// Handle discover services request/response
else if (this->Identifier == DIRCON_MSGID_DISCOVER_SERVICES) {
if (this->Request) {
this->Length = 0;
- this->encodedMessage.push_back((uint8_t)(this->Length >> 8));
- this->encodedMessage.push_back((uint8_t)(this->Length));
+ appendBigEndian16(this->encodedMessage, this->Length);
} else {
// Calculate length - each UUID is 16 bytes
this->Length = this->AdditionalUUIDs.size() * 16;
- this->encodedMessage.push_back((uint8_t)(this->Length >> 8));
- this->encodedMessage.push_back((uint8_t)(this->Length));
+ appendBigEndian16(this->encodedMessage, this->Length);
// Debug log to show number of UUIDs being added
SS2K_LOG(DIRCON_LOG_TAG, "Adding %d service UUIDs to discovery response", this->AdditionalUUIDs.size());
@@ -116,20 +101,19 @@ std::vector* DirConMessage::encode(uint8_t sequenceNumber) {
SS2K_LOG(DIRCON_LOG_TAG, "Adding service %d UUID: %s", counter, uuidToAdd.toString().c_str());
// Add the UUID bytes to the message
- uuidToBytes(uuidToAdd, this->encodedMessage);
+ DirConUUIDCodec::appendBytes(uuidToAdd, this->encodedMessage);
}
}
}
// Handle discover characteristics response
else if (this->Identifier == DIRCON_MSGID_DISCOVER_CHARACTERISTICS && !this->Request) {
this->Length = 16 + this->AdditionalUUIDs.size() * 17;
- this->encodedMessage.push_back((uint8_t)(this->Length >> 8));
- this->encodedMessage.push_back((uint8_t)(this->Length));
- uuidToBytes(this->UUID, this->encodedMessage);
+ appendBigEndian16(this->encodedMessage, this->Length);
+ DirConUUIDCodec::appendBytes(this->UUID, this->encodedMessage);
size_t dataIndex = 0;
for (size_t counter = 0; counter < this->AdditionalUUIDs.size(); counter++) {
- uuidToBytes(this->AdditionalUUIDs[counter], this->encodedMessage);
+ DirConUUIDCodec::appendBytes(this->AdditionalUUIDs[counter], this->encodedMessage);
this->encodedMessage.push_back(this->AdditionalData[dataIndex]);
dataIndex++;
}
@@ -138,17 +122,15 @@ std::vector* DirConMessage::encode(uint8_t sequenceNumber) {
else if (((this->Identifier == DIRCON_MSGID_READ_CHARACTERISTIC || this->Identifier == DIRCON_MSGID_DISCOVER_CHARACTERISTICS) && this->Request) ||
(this->Identifier == DIRCON_MSGID_ENABLE_CHARACTERISTIC_NOTIFICATIONS && !this->Request)) {
this->Length = 16;
- this->encodedMessage.push_back((uint8_t)(this->Length >> 8));
- this->encodedMessage.push_back((uint8_t)(this->Length));
- uuidToBytes(this->UUID, this->encodedMessage);
+ appendBigEndian16(this->encodedMessage, this->Length);
+ DirConUUIDCodec::appendBytes(this->UUID, this->encodedMessage);
}
// Handle write characteristic, unsolicited notification, read response, or notification enabling
else if (this->Identifier == DIRCON_MSGID_WRITE_CHARACTERISTIC || this->Identifier == DIRCON_MSGID_UNSOLICITED_CHARACTERISTIC_NOTIFICATION ||
(this->Identifier == DIRCON_MSGID_READ_CHARACTERISTIC && !this->Request) || (this->Identifier == DIRCON_MSGID_ENABLE_CHARACTERISTIC_NOTIFICATIONS && this->Request)) {
this->Length = 16 + this->AdditionalData.size();
- this->encodedMessage.push_back((uint8_t)(this->Length >> 8));
- this->encodedMessage.push_back((uint8_t)(this->Length));
- uuidToBytes(this->UUID, this->encodedMessage);
+ appendBigEndian16(this->encodedMessage, this->Length);
+ DirConUUIDCodec::appendBytes(this->UUID, this->encodedMessage);
for (size_t counter = 0; counter < this->AdditionalData.size(); counter++) {
this->encodedMessage.push_back(this->AdditionalData[counter]);
@@ -174,7 +156,7 @@ size_t DirConMessage::parse(uint8_t* data, size_t len, uint8_t sequenceNumber) {
this->Identifier = data[1];
this->SequenceNumber = data[2];
this->ResponseCode = data[3];
- this->Length = (data[4] << 8) | data[5];
+ this->Length = get_be16(&data[4]);
this->Request = false;
this->UUID = NimBLEUUID();
this->AdditionalData.clear();
@@ -202,7 +184,7 @@ size_t DirConMessage::parse(uint8_t* data, size_t len, uint8_t sequenceNumber) {
size_t index = 0;
while (this->Length >= index + 16) {
// Parse UUID with consistent byte order
- NimBLEUUID uuid = bytesToUuid(data + DIRCON_MESSAGE_HEADER_LENGTH, index);
+ NimBLEUUID uuid = DirConUUIDCodec::fromBytes(data + DIRCON_MESSAGE_HEADER_LENGTH + index);
this->AdditionalUUIDs.push_back(uuid);
index += 16;
parsedBytes += 16;
@@ -217,7 +199,7 @@ size_t DirConMessage::parse(uint8_t* data, size_t len, uint8_t sequenceNumber) {
case DIRCON_MSGID_DISCOVER_CHARACTERISTICS:
if (this->Length >= 16) {
// Parse the UUID in the same reversed byte format
- this->UUID = bytesToUuid(data + DIRCON_MESSAGE_HEADER_LENGTH, 0);
+ this->UUID = DirConUUIDCodec::fromBytes(data + DIRCON_MESSAGE_HEADER_LENGTH);
parsedBytes += 16;
if (this->Length == 16) {
this->Request = this->isRequest(sequenceNumber);
@@ -247,7 +229,7 @@ size_t DirConMessage::parse(uint8_t* data, size_t len, uint8_t sequenceNumber) {
case DIRCON_MSGID_READ_CHARACTERISTIC:
if (this->Length >= 16) {
// Update READ_CHARACTERISTIC to use consistent byte ordering
- this->UUID = bytesToUuid(data + DIRCON_MESSAGE_HEADER_LENGTH, 0);
+ this->UUID = DirConUUIDCodec::fromBytes(data + DIRCON_MESSAGE_HEADER_LENGTH);
parsedBytes += 16;
if (this->Length == 16) {
this->Request = this->isRequest(sequenceNumber);
@@ -268,7 +250,7 @@ size_t DirConMessage::parse(uint8_t* data, size_t len, uint8_t sequenceNumber) {
case DIRCON_MSGID_WRITE_CHARACTERISTIC:
if (this->Length > 16) {
// Update WRITE_CHARACTERISTIC UUID parsing
- this->UUID = bytesToUuid(data + DIRCON_MESSAGE_HEADER_LENGTH, 0);
+ this->UUID = DirConUUIDCodec::fromBytes(data + DIRCON_MESSAGE_HEADER_LENGTH);
parsedBytes += 16;
this->Request = this->isRequest(sequenceNumber);
this->AdditionalData.clear();
@@ -286,7 +268,7 @@ size_t DirConMessage::parse(uint8_t* data, size_t len, uint8_t sequenceNumber) {
case DIRCON_MSGID_ENABLE_CHARACTERISTIC_NOTIFICATIONS:
if (this->Length >= 16) {
// Parse UUID
- this->UUID = bytesToUuid(data + DIRCON_MESSAGE_HEADER_LENGTH, 0);
+ this->UUID = DirConUUIDCodec::fromBytes(data + DIRCON_MESSAGE_HEADER_LENGTH);
parsedBytes += 16;
// Payload (if any) follows the UUID; typical CCCD is 1-2 bytes
@@ -312,7 +294,7 @@ size_t DirConMessage::parse(uint8_t* data, size_t len, uint8_t sequenceNumber) {
case DIRCON_MSGID_UNSOLICITED_CHARACTERISTIC_NOTIFICATION:
if (this->Length > 16) {
// Update UNSOLICITED_CHARACTERISTIC_NOTIFICATION UUID parsing
- this->UUID = bytesToUuid(data + DIRCON_MESSAGE_HEADER_LENGTH, 0);
+ this->UUID = DirConUUIDCodec::fromBytes(data + DIRCON_MESSAGE_HEADER_LENGTH);
parsedBytes += 16;
this->AdditionalData.clear();
for (size_t dataIndex = 0; dataIndex < (this->Length - 16); dataIndex++) {
diff --git a/src/ERG_Mode.cpp b/src/ERG_Mode.cpp
index d0676f20..134cbeff 100644
--- a/src/ERG_Mode.cpp
+++ b/src/ERG_Mode.cpp
@@ -6,6 +6,7 @@
*/
#include "ERG_Mode.h"
+#include "ERG_Mode_Utils.h"
#include "SS2KLog.h"
#include "Main.h"
#include "BLE_Custom_Characteristic.h"
@@ -22,50 +23,20 @@ static unsigned long ergTimer = millis() + ERG_MODE_DELAY;
static bool isDelayed = false;
namespace {
-constexpr int ERG_SLOPE_SAMPLE_WATTS = POWERTABLE_WATT_INCREMENT;
-constexpr int ERG_LOW_GAIN_WATTS = 120;
-constexpr int ERG_HIGH_GAIN_WATTS = 400;
-constexpr int ERG_MIN_SCHEDULE_WATTS = 30;
-constexpr double ERG_SLOPE_CONTROL_DIVISOR = 10.0;
-constexpr double ERG_GAIN_MIN_DIVISOR = 4.0;
-constexpr double ERG_GAIN_MAX_MULTIPLIER = 4.0;
-constexpr int ERG_LOG_INTERVAL_MS = 2000;
-
-double fallbackErgGain(double sensitivity, int operatingWatts) {
- if (operatingWatts < ERG_LOW_GAIN_WATTS) {
- return sensitivity * ERG_LOW_GAIN_WATTS / std::max(operatingWatts, ERG_MIN_SCHEDULE_WATTS);
- }
- if (operatingWatts > ERG_HIGH_GAIN_WATTS) {
- return sensitivity * ERG_HIGH_GAIN_WATTS / operatingWatts;
- }
- return sensitivity;
-}
-
-double scheduledErgGain(double sensitivity, int operatingWatts, int cadence, bool& usedPowerTable) {
+double scheduledErgGain(double sensitivity, int operatingWatts, int cadence, bool& usedPowerTable, PowerTableSlopeStatus::Value& slopeStatus) {
usedPowerTable = false;
- const int lowerWatts = std::max(0, operatingWatts - ERG_SLOPE_SAMPLE_WATTS);
- const int upperWatts = operatingWatts + ERG_SLOPE_SAMPLE_WATTS;
- const int32_t lowerPosition = powerTable->lookup(lowerWatts, cadence);
- const int32_t upperPosition = powerTable->lookup(upperWatts, cadence);
-
- double gain = fallbackErgGain(sensitivity, operatingWatts);
- // Sparse linear fits are useful for lookup, but not stable enough to schedule ERG gain from their slope.
- if (powerTable->ptHelpers.resistanceModel.getIsValid() && powerTable->ptHelpers.resistanceModel.getIsQuadratic() && lowerPosition != RETURN_ERROR &&
- upperPosition != RETURN_ERROR && upperPosition > lowerPosition) {
- const double localStepsPerWatt = static_cast(upperPosition - lowerPosition) / static_cast(upperWatts - lowerWatts);
- gain = localStepsPerWatt * sensitivity / ERG_SLOPE_CONTROL_DIVISOR;
- usedPowerTable = true;
+ sensitivity = ErgControl::sanitizeSensitivity(sensitivity);
+ const double fallback = ErgControl::fallbackGain(sensitivity, operatingWatts);
+ double localStepsPerWatt;
+ if (powerTable->lookupErgSlope(operatingWatts, cadence, localStepsPerWatt, &slopeStatus)) {
+ const double gain = localStepsPerWatt * sensitivity / ErgControl::SLOPE_CONTROL_DIVISOR;
+ usedPowerTable = true;
+ return ErgControl::blendedTableGain(gain, fallback);
}
-
- return gain;
+ return fallback;
}
-double clampErgGain(double gain, double sensitivity) {
- const double minimumGain = sensitivity / ERG_GAIN_MIN_DIVISOR;
- const double maximumGain = sensitivity * ERG_GAIN_MAX_MULTIPLIER;
- return std::max(minimumGain, std::min(gain, maximumGain));
-}
} // namespace
void ErgMode::runERG() {
@@ -186,7 +157,15 @@ void ErgMode::runERG() {
// Lookup watts using the Power Table.
if (powerTable->_hasBeenLoadedThisSession) {
// Instead of directly outputting this, we should smooth the output by averaging it with the last value.
- _smoothPWR = ((previousPower + powerTable->lookupWatts(rtConfig->cad.getValue(), ss2k->getCurrentPosition())) / 2);
+ const int tablePower = powerTable->lookupWatts(rtConfig->cad.getValue(), ss2k->getCurrentPosition());
+ // A zero lookup only occurs with zero cadence or an invalid table, so
+ // do not carry a previous power value after the rider has stopped.
+ if (tablePower > 0) {
+ _smoothPWR = (previousPower + tablePower) / 2;
+ } else {
+ _smoothPWR = 0;
+ previousPower = 0;
+ }
} else {
// only run _manageSaveState every 5 seconds
static unsigned long int saveStateTimer = millis();
@@ -196,9 +175,6 @@ void ErgMode::runERG() {
saveStateTimer = millis();
}
}
- // So the user knows pTab4PWR is enabled, provide some cadence feedback even if the value returned by the table is 0.
- int minimumPower = rtConfig->cad.getValue() / 2; // 50% of the cadence value
- _smoothPWR = _smoothPWR < minimumPower ? round((minimumPower + previousPower) / 2.0f) : _smoothPWR;
rtConfig->watts.setValue(_smoothPWR);
previousPower = (rtConfig->watts.getValue() + previousPower) / 2;
}
@@ -307,23 +283,18 @@ int32_t ErgMode::_inSetpointState() {
// Scale the proportional gain to the local power-table slope. This compensates for the eddy-current brake producing fewer watts per step at low resistance
// and more watts per step at high resistance. ERG sensitivity controls how much of the predicted correction is applied and bounds bad model slopes.
- const double sensitivity = userConfig->getERGSensitivity();
- bool usedPowerTable = false;
- double Kp = scheduledErgGain(sensitivity, target, rtConfig->cad.getValue(), usedPowerTable);
-
- // modifying gains based on error
- if (abs(error) < 10 || (mode != Mode::MAINTAIN)) {
- Kp = Kp * 0.25; // decrease further for tiny errors
- } else if (abs(error) < 50) {
- Kp = Kp * 0.75; // Moderate for medium errors
- } else if (abs(error) > 100) {
- Kp = Kp * 1.25; // Aggressive for large errors
- }
+ const double configuredSensitivity = userConfig->getERGSensitivity();
+ bool usedPowerTable = false;
+ PowerTableSlopeStatus::Value slopeStatus = PowerTableSlopeStatus::InvalidRequest;
+ double Kp = scheduledErgGain(configuredSensitivity, target, rtConfig->cad.getValue(), usedPowerTable, slopeStatus);
+ const double controlSensitivity = ErgControl::sanitizeSensitivity(configuredSensitivity);
+
+ Kp = ErgControl::errorScheduledGain(Kp, error, mode == Mode::MAINTAIN);
if (watts < userConfig->getMinWatts()) {
- Kp = Kp * sensitivity; // Increase gain at very low watts to prevent Zwift from timing out on an initial interval.
+ Kp = Kp * controlSensitivity; // Increase gain at very low watts to prevent Zwift from timing out on an initial interval.
}
- Kp = clampErgGain(Kp, sensitivity);
+ Kp = ErgControl::clampGain(Kp, controlSensitivity);
mode = Mode::MAINTAIN;
@@ -344,12 +315,12 @@ int32_t ErgMode::_inSetpointState() {
// Calculate new incline
float newIncline = ss2k->getCurrentPosition() + PID_output;
- // log output every five seconds
+ // Log output at the configured ERG interval.
static unsigned long lastTime = 0;
- if (millis() - lastTime > ERG_LOG_INTERVAL_MS) {
+ if (millis() - lastTime > ERG_MODE_LOG_INTERVAL_MS) {
lastTime = millis();
- SS2K_LOG(ERG_MODE_LOG_TAG, "%dw, Target %dw, Kp: %.3f (%s), PID Output: %f, Moving to: %f", rtConfig->watts.getValue(), rtConfig->watts.getTarget(), Kp,
- usedPowerTable ? "table" : "fallback", PID_output, newIncline);
+ SS2K_LOG(ERG_MODE_LOG_TAG, "%dw, Target %dw, Kp: %.3f (%s%s%s), PID Output: %f, Moving to: %f", rtConfig->watts.getValue(), rtConfig->watts.getTarget(), Kp,
+ usedPowerTable ? "table" : "fallback", usedPowerTable ? "" : ": ", usedPowerTable ? "" : PowerTableSlopeStatus::name(slopeStatus), PID_output, newIncline);
}
return newIncline;
diff --git a/src/HTTP_Server_Basic.cpp b/src/HTTP_Server_Basic.cpp
index 01d8a2d1..e45a8219 100644
--- a/src/HTTP_Server_Basic.cpp
+++ b/src/HTTP_Server_Basic.cpp
@@ -6,7 +6,6 @@
*/
#include "Main.h"
-#include "Version_Converter.h"
#include "Builtin_Pages.h"
#include "HTTP_Server_Basic.h"
#include "cert.h"
@@ -15,18 +14,17 @@
#include "FirmwareImageValidation.h"
#include
#include
-#include
#include
#include
#include
#include
-#include
#include
#include
#include
#include
-
-File fsUploadFile;
+#include
+#include
+#include
IPAddress myIP;
@@ -63,61 +61,202 @@ void writeStoredBuildVersion(const String& version) {
namespace {
const char* OTA_REC_NS = "ota_recover"; // namespace
const char* OTA_REC_KEY = "ver"; // key storing last recovered firmware version
-const char* OTA_FS_VER_KEY = "fs_ver"; // key storing the installed web-filesystem version
bool otaUploadRejected = false;
bool otaFilesystemUpload = false;
bool otaFirmwareUpdateBegun = false;
+esp_ota_handle_t otaFirmwareHandle = 0;
+const esp_partition_t* otaFirmwarePartition = nullptr;
+const esp_partition_t* otaFilesystemPartition = nullptr;
+size_t otaFilesystemBytesWritten = 0;
+size_t otaFilesystemBytesErased = 0;
+bool otaFilesystemUnmounted = false;
uint8_t otaFirmwareHeader[sizeof(esp_image_header_t)];
size_t otaFirmwareHeaderLength = 0;
String otaUploadError;
+constexpr unsigned long NTP_SYNC_TIMEOUT_MS = 10000;
+constexpr time_t VALID_CLOCK_EPOCH = 1609459200; // 2021-01-01 UTC
+constexpr char LITTLEFS_PARTITION_LABEL[] = "spiffs";
+
+bool networkServicesStarted = false;
+
+void abortFirmwareUpload() {
+ if (otaFirmwareUpdateBegun) {
+ esp_ota_abort(otaFirmwareHandle);
+ }
+ otaFirmwareHandle = 0;
+ otaFirmwarePartition = nullptr;
+ otaFirmwareUpdateBegun = false;
+}
+
void resetFirmwareUploadValidation() {
+ abortFirmwareUpload();
otaFirmwareUpdateBegun = false;
otaFirmwareHeaderLength = 0;
otaUploadError = "";
}
-bool beginHttpRequest(HTTPClient& request, NetworkClient& client, const String& url) {
- if (request.begin(client, url)) return true;
- SS2K_LOGE(HTTP_SERVER_LOG_TAG, "Unable to initialize HTTP request for %s", url.c_str());
+void resetFilesystemUpload() {
+ otaFilesystemPartition = nullptr;
+ otaFilesystemBytesWritten = 0;
+ otaFilesystemBytesErased = 0;
+}
+
+bool remountFilesystem() {
+ if (!otaFilesystemUnmounted) return true;
+
+ otaFilesystemUnmounted = false;
+ if (LittleFS.begin(false)) return true;
+
+ SS2K_LOGE(HTTP_SERVER_LOG_TAG, "Failed to remount LittleFS after filesystem upload");
return false;
}
-int beginHttpGet(HTTPClient& request, NetworkClient& client, const String& url) {
- if (!beginHttpRequest(request, client, url)) return HTTPC_ERROR_CONNECTION_REFUSED;
- return request.GET();
+void rejectFirmwareUpload(const char* message, esp_err_t error = ESP_OK) {
+ otaUploadRejected = true;
+ otaUploadError = message;
+ abortFirmwareUpload();
+ ss2k->isUpdating = false;
+ if (error == ESP_OK) {
+ SS2K_LOGE(HTTP_SERVER_LOG_TAG, "%s", message);
+ } else {
+ SS2K_LOGE(HTTP_SERVER_LOG_TAG, "%s: %s (%d)", message, esp_err_to_name(error), error);
+ }
}
-bool validateRemoteFirmwareHeader(HTTPClient& request, NetworkClient& client, const String& firmwareUrl) {
- if (!beginHttpRequest(request, client, firmwareUrl)) return false;
- // Some custom update servers may ignore Range and return the entire image.
- // Close this connection after reading the header so unread image bytes cannot
- // contaminate the subsequent HTTPUpdate request on the shared client.
- request.setReuse(false);
- request.addHeader("Range", String("bytes=0-") + (sizeof(esp_image_header_t) - 1));
-
- const int httpCode = request.GET();
- if (httpCode != HTTP_CODE_OK && httpCode != HTTP_CODE_PARTIAL_CONTENT) {
- SS2K_LOGE(HTTP_SERVER_LOG_TAG, "Firmware header request failed for %s: HTTP %d", firmwareUrl.c_str(), httpCode);
- request.end();
+bool beginFirmwareUpload() {
+ otaFirmwarePartition = esp_ota_get_next_update_partition(nullptr);
+ if (otaFirmwarePartition == nullptr) {
+ rejectFirmwareUpload("No OTA update partition is available.");
+ return false;
+ }
+
+ // WebServer does not expose the file size until UPLOAD_FILE_END. Incremental
+ // erase avoids both a full-partition erase here and Arduino Update's 4 KiB
+ // heap buffer, which can exhaust fragmented RAM on the classic ESP32.
+ const esp_err_t result = esp_ota_begin(otaFirmwarePartition, OTA_WITH_SEQUENTIAL_WRITES, &otaFirmwareHandle);
+ if (result != ESP_OK) {
+ rejectFirmwareUpload("Unable to start firmware update.", result);
return false;
}
- uint8_t headerBytes[sizeof(esp_image_header_t)];
- NetworkClient* stream = request.getStreamPtr();
- const size_t bytesRead = stream->readBytes(headerBytes, sizeof(headerBytes));
- request.end();
+ otaFirmwareUpdateBegun = true;
+ return true;
+}
+
+bool writeFirmwareUpload(const uint8_t* data, size_t length) {
+ const esp_err_t result = esp_ota_write(otaFirmwareHandle, data, length);
+ if (result == ESP_OK) return true;
+
+ rejectFirmwareUpload("Firmware upload write failed.", result);
+ return false;
+}
- const FirmwareImageHeaderValidation validation = validateFirmwareImageHeader(headerBytes, bytesRead);
- if (validation.result != FirmwareImageHeaderResult::Valid) {
- SS2K_LOGE(HTTP_SERVER_LOG_TAG, "Rejected remote firmware image: %s (expected chip 0x%04x, image chip 0x%04x)",
- firmwareImageHeaderResultName(validation.result), CONFIG_IDF_FIRMWARE_CHIP_ID, static_cast(validation.imageChipId));
+bool finishFirmwareUpload() {
+ const esp_ota_handle_t completedHandle = otaFirmwareHandle;
+ otaFirmwareHandle = 0;
+ otaFirmwareUpdateBegun = false;
+
+ esp_err_t result = esp_ota_end(completedHandle);
+ if (result != ESP_OK) {
+ otaFirmwarePartition = nullptr;
+ rejectFirmwareUpload("Firmware image validation failed.", result);
+ return false;
+ }
+
+ result = esp_ota_set_boot_partition(otaFirmwarePartition);
+ otaFirmwarePartition = nullptr;
+ if (result != ESP_OK) {
+ rejectFirmwareUpload("Unable to select the uploaded firmware for boot.", result);
+ return false;
+ }
+
+ return true;
+}
+
+void rejectFilesystemUpload(const char* message, esp_err_t error = ESP_OK) {
+ otaUploadRejected = true;
+ otaUploadError = message;
+ resetFilesystemUpload();
+ ss2k->isUpdating = false;
+ if (error == ESP_OK) {
+ SS2K_LOGE(HTTP_SERVER_LOG_TAG, "%s", message);
+ } else {
+ SS2K_LOGE(HTTP_SERVER_LOG_TAG, "%s: %s (%d)", message, esp_err_to_name(error), error);
+ }
+}
+
+bool beginFilesystemUpload() {
+ otaFilesystemPartition =
+ esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, LITTLEFS_PARTITION_LABEL);
+ if (otaFilesystemPartition == nullptr) {
+ rejectFilesystemUpload("No LittleFS partition is available.");
return false;
}
+ // The filesystem must not remain mounted while its backing partition is
+ // erased and rewritten. Configuration remains available in userConfig and
+ // is restored to the uploaded image after it is mounted.
+ LittleFS.end();
+ otaFilesystemUnmounted = true;
+ ss2k->isUpdating = true;
return true;
}
+bool writeFilesystemUpload(const uint8_t* data, size_t length) {
+ if (otaFilesystemPartition == nullptr || otaUploadRejected) return false;
+ if (length > otaFilesystemPartition->size - otaFilesystemBytesWritten) {
+ rejectFilesystemUpload("Filesystem image is larger than the LittleFS partition.");
+ return false;
+ }
+
+ const size_t writeEnd = otaFilesystemBytesWritten + length;
+ const size_t eraseSize = otaFilesystemPartition->erase_size;
+ const size_t eraseEnd = ((writeEnd + eraseSize - 1) / eraseSize) * eraseSize;
+ if (eraseEnd > otaFilesystemBytesErased) {
+ const esp_err_t result =
+ esp_partition_erase_range(otaFilesystemPartition, otaFilesystemBytesErased, eraseEnd - otaFilesystemBytesErased);
+ if (result != ESP_OK) {
+ rejectFilesystemUpload("Unable to erase the LittleFS partition.", result);
+ return false;
+ }
+ otaFilesystemBytesErased = eraseEnd;
+ }
+
+ const esp_err_t result = esp_partition_write(otaFilesystemPartition, otaFilesystemBytesWritten, data, length);
+ if (result != ESP_OK) {
+ rejectFilesystemUpload("Filesystem upload write failed.", result);
+ return false;
+ }
+
+ otaFilesystemBytesWritten = writeEnd;
+ return true;
+}
+
+bool finishFilesystemUpload(size_t uploadedSize) {
+ if (otaFilesystemPartition == nullptr || otaUploadRejected) return false;
+ if (uploadedSize != otaFilesystemBytesWritten || uploadedSize != otaFilesystemPartition->size) {
+ rejectFilesystemUpload("Filesystem image size does not match the LittleFS partition.");
+ return false;
+ }
+
+ resetFilesystemUpload();
+ return true;
+}
+
+bool beginHttpRequest(HTTPClient& request, NetworkClient& client, const String& url) {
+ request.setConnectTimeout(3000);
+ request.setTimeout(5000);
+ if (request.begin(client, url)) return true;
+ SS2K_LOGE(HTTP_SERVER_LOG_TAG, "Unable to initialize HTTP request for %s", url.c_str());
+ return false;
+}
+
+int beginHttpGet(HTTPClient& request, NetworkClient& client, const String& url) {
+ if (!beginHttpRequest(request, client, url)) return HTTPC_ERROR_CONNECTION_REFUSED;
+ return request.GET();
+}
+
bool filesystemIndexExists() {
return LittleFS.exists("/index.html.gz") || LittleFS.exists("/index.html");
}
@@ -134,22 +273,6 @@ String contentTypeForPath(String path) {
return "application/octet-stream";
}
-String getNVSFilesystemVersion() {
- Preferences p;
- if (!p.begin(OTA_REC_NS, true)) return "";
- String version = p.getString(OTA_FS_VER_KEY, "");
- p.end();
- return version;
-}
-
-bool setNVSFilesystemVersion(const String& version) {
- Preferences p;
- if (!p.begin(OTA_REC_NS, false)) return false;
- size_t length = p.putString(OTA_FS_VER_KEY, version);
- p.end();
- return length > 0;
-}
-
bool manifestContains(const JsonArray& files, const String& filename) {
for (JsonVariantConst entry : files) {
String manifestName = "/" + entry.as();
@@ -158,41 +281,22 @@ bool manifestContains(const JsonArray& files, const String& filename) {
return false;
}
-bool pruneFilesystem(const JsonArray& files) {
- bool success = true;
- while (true) {
- File root = LittleFS.open("/");
- if (!root || !root.isDirectory()) {
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "Failed to inspect filesystem before update");
- return false;
- }
+bool filesystemNeedsRepair() {
+ File manifest = LittleFS.open(DATA_FILELIST, FILE_READ);
+ if (!manifest) return true;
- String staleName;
- File entry = root.openNextFile();
- while (entry) {
- String filename = entry.name();
- if (!filename.startsWith("/")) filename = "/" + filename;
- bool preserved = filename == configFILENAME || filename == POWER_TABLE_FILENAME || filename == BUILD_VERSION_FILENAME;
- if (!preserved && !manifestContains(files, filename)) {
- staleName = filename;
- entry.close();
- break;
- }
- entry = root.openNextFile();
- }
- entry.close();
- root.close();
+ JsonDocument doc;
+ DeserializationError error = deserializeJson(doc, manifest);
+ manifest.close();
+ if (error) return true;
- if (staleName.isEmpty()) break;
- if (LittleFS.remove(staleName)) {
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "Removed stale filesystem file: %s", staleName.c_str());
- } else {
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "Failed to remove stale filesystem file: %s", staleName.c_str());
- success = false;
- break;
- }
+ JsonArray files = doc.as();
+ if (files.isNull() || (!manifestContains(files, "/index.html.gz") && !manifestContains(files, "/index.html"))) return true;
+
+ for (JsonVariantConst entry : files) {
+ if (!LittleFS.exists("/" + entry.as())) return true;
}
- return success;
+ return false;
}
String getNVSRecoveryVersion() {
@@ -234,13 +338,44 @@ void _APSetup() {
// WiFi.setHostname(userConfig->getDeviceName());
WiFi.softAPsetHostname(userConfig->getDeviceName());
WiFi.enableAP(true);
- delay(500); // Micro controller requires some time to reset the mode
+ delay(500);
+}
+
+namespace {
+void stopNetworkServices() {
+ dnsServer.stop();
+ DirConManager::stop();
+ if (networkServicesStarted) {
+ MDNS.end();
+ networkServicesStarted = false;
+ }
}
+void startNetworkServices() {
+ if (networkServicesStarted) return;
+
+ if (!MDNS.begin(userConfig->getDeviceName())) {
+ SS2K_LOGE(HTTP_SERVER_LOG_TAG, "Error setting up MDNS responder");
+ } else {
+ MDNS.addService("http", "_tcp", 80);
+ MDNS.addServiceTxt("http", "_tcp", "lf", "0");
+ networkServicesStarted = true;
+ }
+}
+
+void logNetworkReady(const char* mode) {
+ SS2K_LOG(HTTP_SERVER_LOG_TAG, "%s ready, IP address: %s", mode, myIP.toString().c_str());
+ SS2K_LOG(HTTP_SERVER_LOG_TAG, "Open http://%s.local/", userConfig->getDeviceName());
+}
+} // namespace
+
// ********************************WIFI Setup*************************
void startWifi() {
- int i = 0;
-
+ int connectionAttempts = 0;
+
+ stopNetworkServices();
+ httpServer.internetConnection = false;
+
// Check build version for OTA WiFi issue handling
String storedVersion = readStoredBuildVersion();
String currentVersion = FIRMWARE_VERSION;
@@ -279,88 +414,72 @@ void startWifi() {
}
}
- // Trying Station mode first:
+ // Complete WiFi setup before starting web-file repair, BLE, HTTP, or DirCon.
if (strcmp(userConfig->getSsid(), DEVICE_NAME) != 0) {
SS2K_LOG(HTTP_SERVER_LOG_TAG, "Connecting to: %s", userConfig->getSsid());
_staSetup();
- while (WiFi.status() != WL_CONNECTED) {
+ while (WiFi.status() != WL_CONNECTED && connectionAttempts < WIFI_CONNECT_TIMEOUT) {
+ delay(1000);
+ connectionAttempts++;
+ SS2K_LOG(HTTP_SERVER_LOG_TAG, "Waiting for WiFi connection (%d/%d)", connectionAttempts, WIFI_CONNECT_TIMEOUT);
+ }
+
+ if (WiFi.status() != WL_CONNECTED) {
+ SS2K_LOGW(HTTP_SERVER_LOG_TAG, "WiFi connection timed out; switching to AP mode");
+ WiFi.disconnect(true, false);
+ WiFi.setAutoReconnect(false);
+ WiFi.mode(WIFI_MODE_NULL);
delay(1000);
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "Waiting for connection to be established...");
- i++;
- if (i > WIFI_CONNECT_TIMEOUT) {
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "Couldn't Connect. Switching to AP mode");
- WiFi.disconnect(true, true);
- WiFi.setAutoReconnect(false);
- WiFi.mode(WIFI_MODE_NULL);
- delay(1000);
- break;
- }
}
}
- // Did we connect in STA mode?
if (WiFi.status() == WL_CONNECTED) {
- myIP = WiFi.localIP();
+ myIP = WiFi.localIP();
httpServer.internetConnection = true;
- }
-
- // Couldn't connect to existing network, Create SoftAP
- if (WiFi.status() != WL_CONNECTED) {
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "Starting AP Mode");
+ logNetworkReady("WiFi station");
+ } else {
_APSetup();
- if (strcmp(userConfig->getSsid(), DEVICE_NAME) == 0) {
- // If default SSID is still in use, let the user select a new password.
- // Else fall back to the default password.
- WiFi.softAP(userConfig->getDeviceName(), userConfig->getPassword());
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "Using Stored Password");
- } else {
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "Using Default Password");
- WiFi.softAP(userConfig->getDeviceName(), DEFAULT_PASSWORD);
+ const bool usingStoredPassword = strcmp(userConfig->getSsid(), DEVICE_NAME) == 0;
+ const char* password = usingStoredPassword ? userConfig->getPassword() : DEFAULT_PASSWORD;
+ SS2K_LOG(HTTP_SERVER_LOG_TAG, "Starting AP mode with %s password", usingStoredPassword ? "stored" : "default");
+ if (!WiFi.softAP(userConfig->getDeviceName(), password)) {
+ SS2K_LOGE(HTTP_SERVER_LOG_TAG, "Unable to start WiFi access point");
}
+
delay(50);
myIP = WiFi.softAPIP();
- /* Setup the DNS server redirecting all the domains to the apIP */
dnsServer.setErrorReplyCode(DNSReplyCode::NoError);
dnsServer.start(DNS_PORT, "*", myIP);
+ logNetworkReady("Access point");
}
- if (!MDNS.begin(userConfig->getDeviceName())) {
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "Error setting up MDNS responder!");
- }
-
- MDNS.addService("http", "_tcp", 80);
- MDNS.addServiceTxt("http", "_tcp", "lf", "0");
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "Connected to %s IP address: %s", userConfig->getSsid(), myIP.toString().c_str());
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "Open http://%s.local/", userConfig->getDeviceName());
-
- // Initialize DirCon MDNS service
- if (DirConManager::start()) {
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "DirCon service started successfully");
- } else {
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "Error starting DirCon service");
- }
-
+ startNetworkServices();
WiFi.setTxPower(WIFI_POWER_19_5dBm);
- if (WiFi.getMode() == WIFI_STA) {
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "Syncing clock...");
- configTime(0, 0, "pool.ntp.org"); // get UTC time via NTP
- time_t now = time(nullptr);
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "Waiting for clock sync");
- while (now < 10) { // wait 10 seconds
- SS2K_LOG(".", ".");
+ if (httpServer.internetConnection) {
+ SS2K_LOG(HTTP_SERVER_LOG_TAG, "Synchronizing clock before web filesystem repair");
+ configTime(0, 0, "pool.ntp.org");
+ const unsigned long syncStarted = millis();
+ time_t now = time(nullptr);
+ while (now < VALID_CLOCK_EPOCH && millis() - syncStarted < NTP_SYNC_TIMEOUT_MS) {
delay(100);
now = time(nullptr);
}
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "Clock synced to: %.f", difftime(now, (time_t)0));
+
+ if (now < VALID_CLOCK_EPOCH) {
+ SS2K_LOGW(HTTP_SERVER_LOG_TAG, "Clock synchronization timed out");
+ } else {
+ SS2K_LOG(HTTP_SERVER_LOG_TAG, "Clock synchronized");
+ }
}
}
void stopWifi() {
SS2K_LOG(HTTP_SERVER_LOG_TAG, "Closing connection to: %s", userConfig->getSsid());
- // Stop DirCon service before disconnecting WiFi
- DirConManager::stop();
- WiFi.disconnect();
+ stopNetworkServices();
+ WiFi.setAutoReconnect(false);
+ WiFi.disconnect(true, false);
+ WiFi.mode(WIFI_MODE_NULL);
}
void HTTP_Server::start() {
@@ -539,38 +658,29 @@ void HTTP_Server::start() {
[]() {
server.sendHeader("Connection", "close");
if (otaUploadRejected) {
+ if (otaFilesystemUpload) remountFilesystem();
server.send(400, "text/plain", otaUploadError.isEmpty() ? String("Wrong image filename. Expected ") + FW_BINFILE + " or " + FS_BINFILE + "."
: otaUploadError);
return;
}
- // Check if the Update process reported an error and send the final status.
- if (Update.hasError()) {
- // You can get more specific error information if you want
- // size_t len = Update.getErrorString(error_string_buffer, 128);
- // server.send(500, "text/plain", error_string_buffer);
- server.send(500, "text/plain", "FAIL");
- } else {
- if (otaFilesystemUpload) {
- // Update replaced the mounted partition underneath LittleFS. Remount
- // the new image before restoring the in-memory user configuration.
- LittleFS.end();
- if (LittleFS.begin(false)) {
- userConfig->saveToLittleFS();
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "Settings saved to the uploaded LittleFS image");
- } else {
- SS2K_LOGE(HTTP_SERVER_LOG_TAG, "Failed to remount LittleFS before saving settings");
- }
- }
- server.send(200, "text/plain", "OK");
- if (otaFilesystemUpload) {
- // The filesystem was replaced underneath the running web server. Reboot
- // directly after allowing the response to reach the browser.
- delay(500);
- ESP.restart();
- } else {
- // Firmware uploads can use the normal cooperative reboot path.
- ss2k->rebootFlag = true;
+ if (otaFilesystemUpload) {
+ if (!remountFilesystem()) {
+ ss2k->isUpdating = false;
+ server.send(500, "text/plain", "Uploaded filesystem could not be mounted.");
+ return;
}
+ userConfig->saveToLittleFS();
+ SS2K_LOG(HTTP_SERVER_LOG_TAG, "Settings saved to the uploaded LittleFS image");
+ }
+ server.send(200, "text/plain", "OK");
+ if (otaFilesystemUpload) {
+ // Reboot directly because active HTTP handlers and assets came from the
+ // filesystem that was just replaced.
+ delay(500);
+ ESP.restart();
+ } else {
+ // Firmware uploads can use the normal cooperative reboot path.
+ ss2k->rebootFlag = true;
}
},
// This is the onUpload callback. It handles the file data as it arrives.
@@ -581,6 +691,7 @@ void HTTP_Server::start() {
otaUploadRejected = false;
otaFilesystemUpload = upload.filename == FS_BINFILE;
resetFirmwareUploadValidation();
+ resetFilesystemUpload();
}
if (upload.filename == FW_BINFILE) {
if (upload.status == UPLOAD_FILE_START) {
@@ -606,99 +717,51 @@ void HTTP_Server::start() {
return;
}
- if (!Update.begin(UPDATE_SIZE_UNKNOWN, U_FLASH)) {
- otaUploadRejected = true;
- otaUploadError = "Unable to start firmware update.";
- ss2k->isUpdating = false;
- Update.printError(Serial);
- return;
- }
- otaFirmwareUpdateBegun = true;
+ if (!beginFirmwareUpload()) return;
- if (Update.write(otaFirmwareHeader, sizeof(otaFirmwareHeader)) != sizeof(otaFirmwareHeader)) {
- otaUploadRejected = true;
- otaUploadError = "Failed to write firmware image header.";
- Update.printError(Serial);
- return;
- }
+ if (!writeFirmwareUpload(otaFirmwareHeader, sizeof(otaFirmwareHeader))) return;
}
}
if (!otaUploadRejected && otaFirmwareUpdateBegun && chunkOffset < upload.currentSize &&
- Update.write(upload.buf + chunkOffset, upload.currentSize - chunkOffset) != upload.currentSize - chunkOffset) {
- otaUploadRejected = true;
- otaUploadError = "Firmware upload write failed.";
- Update.printError(Serial);
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "Upload Write Failed.");
+ !writeFirmwareUpload(upload.buf + chunkOffset, upload.currentSize - chunkOffset)) {
+ return;
}
} else if (upload.status == UPLOAD_FILE_END) {
- // Finalize the update. The true parameter tells it to flash the remaining buffer.
// DO NOT send a response here.
if (otaUploadRejected) {
- if (otaFirmwareUpdateBegun) Update.abort();
+ abortFirmwareUpload();
ss2k->isUpdating = false;
} else if (!otaFirmwareUpdateBegun) {
otaUploadRejected = true;
if (otaUploadError.isEmpty()) otaUploadError = "Firmware image header is incomplete.";
ss2k->isUpdating = false;
- } else if (Update.end(true)) {
+ } else if (finishFirmwareUpload()) {
SS2K_LOG(HTTP_SERVER_LOG_TAG, "Firmware Upload Finished Successfully.");
- } else {
- otaUploadRejected = true;
- otaUploadError = "Firmware image validation failed.";
- Update.printError(Serial);
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "Unknown OTA issue on end.");
}
// The reboot will be triggered in the onComplete handler after the response.
+ } else if (upload.status == UPLOAD_FILE_ABORTED) {
+ rejectFirmwareUpload("Firmware upload was aborted.");
}
} else if (upload.filename == FS_BINFILE) {
if (upload.status == UPLOAD_FILE_START) {
SS2K_LOG(HTTP_SERVER_LOG_TAG, "Update Start: %s", upload.filename.c_str());
- if (!Update.begin(UPDATE_SIZE_UNKNOWN, U_SPIFFS)) {
- Update.printError(Serial);
- }
+ beginFilesystemUpload();
} else if (upload.status == UPLOAD_FILE_WRITE) {
- if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
- Update.printError(Serial);
- }
+ writeFilesystemUpload(upload.buf, upload.currentSize);
} else if (upload.status == UPLOAD_FILE_END) {
- // Finalize the update.
// DO NOT send a response here.
- if (Update.end(true)) {
+ if (finishFilesystemUpload(upload.totalSize)) {
SS2K_LOG(HTTP_SERVER_LOG_TAG, "Littlefs Upload Finished Successfully.");
- } else {
- Update.printError(Serial);
}
+ } else if (upload.status == UPLOAD_FILE_ABORTED) {
+ rejectFilesystemUpload("Filesystem upload was aborted.");
+ remountFilesystem();
}
- } else if (upload.filename.endsWith(".bin")) {
- if (upload.status == UPLOAD_FILE_START) {
- otaUploadRejected = true;
- otaUploadError = String("Wrong image filename. Expected ") + FW_BINFILE + " or " + FS_BINFILE + ".";
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "Rejected image %s; expected %s or %s", upload.filename.c_str(), FW_BINFILE, FS_BINFILE);
- }
- } else { // Handles other file uploads to LittleFS
- if (upload.status == UPLOAD_FILE_START) {
- String filename = upload.filename;
- if (!filename.startsWith("/")) {
- filename = "/" + filename;
- }
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "handleFileUpload Name: %s", filename.c_str());
- fsUploadFile = LittleFS.open(filename, "w");
- filename = String();
- } else if (upload.status == UPLOAD_FILE_WRITE) {
- if (fsUploadFile) {
- fsUploadFile.write(upload.buf, upload.currentSize);
- }
- } else if (upload.status == UPLOAD_FILE_END) {
- if (fsUploadFile) {
- fsUploadFile.close();
- }
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "handleFileUpload Size: %zu", upload.totalSize);
- // For non-firmware files, it's okay to send a response here,
- // but for consistency, it's better to let the onComplete handler do it.
- // For this example, we assume the main onComplete handler is for firmware.
- // A more robust solution would check which type of file was uploaded.
- }
+ } else if (upload.status == UPLOAD_FILE_START) {
+ otaUploadRejected = true;
+ otaUploadError = String("Wrong upload filename. Expected ") + FW_BINFILE + " or " + FS_BINFILE + ".";
+ SS2K_LOG(HTTP_SERVER_LOG_TAG, "Rejected upload %s; expected %s or %s", upload.filename.c_str(), FW_BINFILE, FS_BINFILE);
}
});
@@ -716,11 +779,11 @@ void HTTP_Server::webClientUpdate() {
_webClientTimer = millis();
static unsigned long mDnsTimer = millis(); // NOLINT: There is no overload in String for uint64_t
server.handleClient();
- // if (WiFi.getMode() != WIFI_MODE_STA) {
- // dnsServer.processNextRequest();
- // }
+ if (WiFi.getMode() != WIFI_MODE_STA) {
+ dnsServer.processNextRequest();
+ }
// Keep MDNS alive
- if ((millis() - mDnsTimer) > 30000) {
+ if (networkServicesStarted && (millis() - mDnsTimer) > 30000) {
MDNS.addServiceTxt("http", "_tcp", "lf", String(mDnsTimer));
mDnsTimer = millis();
}
@@ -817,13 +880,6 @@ void HTTP_Server::settingsProcessor() {
userConfig->setERGSensitivity(ERGSensitivity);
}
}
- // checkboxes don't report off, so need to check using another parameter
- // that's always present on that page
- if (!server.arg("autoUpdate").isEmpty()) {
- userConfig->setAutoUpdate(true);
- } else if (wasSettingsUpdate) {
- userConfig->setAutoUpdate(false);
- }
if (!server.arg("stepperDir").isEmpty()) {
userConfig->setStepperDir(true);
} else if (wasSettingsUpdate) {
@@ -938,152 +994,128 @@ void HTTP_Server::stop() {
// github fingerprint
// 70:94:DE:DD:E6:C4:69:48:3A:92:70:A1:48:56:78:2D:18:64:E0:B7
-void HTTP_Server::FirmwareUpdate() {
+bool HTTP_Server::syncWebServerFiles() {
+ if (!filesystemNeedsRepair()) {
+ SS2K_LOG(HTTP_SERVER_LOG_TAG, "Web filesystem is complete; repair skipped");
+ return true;
+ }
+
+ if (!httpServer.internetConnection || WiFi.status() != WL_CONNECTED) {
+ SS2K_LOGW(HTTP_SERVER_LOG_TAG, "Web filesystem has missing files, but station internet is unavailable");
+ return false;
+ }
+
// HTTPClient keeps a non-owning pointer to the network client. Declare the
// network client first so HTTPClient is destroyed before its transport.
WiFiClientSecure localClient;
HTTPClient http;
localClient.setCACert(rootCACertificate);
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "Checking for newer firmware:");
- int httpCode = beginHttpGet(http, localClient, userConfig->getFirmwareUpdateURL() + String(FW_VERSIONFILE));
- String payload;
- if (httpCode == HTTP_CODE_OK) { // if version received
- payload = http.getString(); // save received version
- payload.trim();
- SS2K_LOG(HTTP_SERVER_LOG_TAG, " - Server version: %s", payload.c_str());
+ localClient.setHandshakeTimeout(20);
+ SS2K_LOG(HTTP_SERVER_LOG_TAG, "Repairing missing web filesystem files");
+
+ int httpCode = beginHttpGet(http, localClient, DATA_UPDATEURL DATA_FILELIST);
+ JsonDocument doc;
+ if (httpCode == HTTP_CODE_OK) {
+ DeserializationError error = deserializeJson(doc, http.getStream());
+ if (error) {
+ SS2K_LOG(HTTP_SERVER_LOG_TAG, "Failed to read file list");
+ http.end();
+ return false;
+ }
httpServer.internetConnection = true;
} else {
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "error downloading %s %d", FW_VERSIONFILE, httpCode);
+ SS2K_LOG(HTTP_SERVER_LOG_TAG, "error downloading %s %d (%s)", DATA_FILELIST, httpCode, HTTPClient::errorToString(httpCode).c_str());
httpServer.internetConnection = false;
}
-
http.end();
- if (httpCode == HTTP_CODE_OK) { // if version received
- const String serverVersion = payload;
- const String filesystemVersion = getNVSFilesystemVersion();
- bool updateAnyway = false;
- if (!filesystemIndexExists()) {
- // force firmware update if index.html is missing
- // updateAnyway = true;
- SS2K_LOG(HTTP_SERVER_LOG_TAG, " -index.html not found.");
- }
- Version availableVer(payload.c_str());
- Version currentVer(FIRMWARE_VERSION);
- // Development builds append branch/commit data to the release they follow.
- // Treat that suffix as newer when the numeric date components are equal.
- bool firmwareIsAhead = currentVer > availableVer || (currentVer == availableVer && serverVersion != FIRMWARE_VERSION);
- bool filesystemUpgradeAvailable = !firmwareIsAhead &&
- (filesystemVersion.isEmpty() || availableVer > Version(filesystemVersion.c_str()));
- bool filesystemNeedsUpdate = !filesystemIndexExists() || filesystemUpgradeAvailable;
- SS2K_LOG(HTTP_SERVER_LOG_TAG, " - Filesystem version: %s%s", filesystemVersion.isEmpty() ? "not recorded" : filesystemVersion.c_str(),
- filesystemNeedsUpdate ? " (update required)" : "");
- if (firmwareIsAhead && filesystemIndexExists()) {
- SS2K_LOG(HTTP_SERVER_LOG_TAG, " - Firmware is ahead of the server; not installing older release files");
- }
+ if (httpCode != HTTP_CODE_OK) return false;
- if (filesystemNeedsUpdate) {
- //////////////// Update LittleFS//////////////
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "Updating FileSystem");
- httpCode = beginHttpGet(http, localClient, DATA_UPDATEURL DATA_FILELIST);
- JsonDocument doc;
- if (httpCode == HTTP_CODE_OK) { // if version received
- DeserializationError error = deserializeJson(doc, http.getStream());
- if (error) {
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "Failed to read file list");
- http.end(); // Make sure to end HTTP before returning
- return;
- }
- httpServer.internetConnection = true;
- } else {
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "error downloading %s %d", DATA_FILELIST, httpCode);
- httpServer.internetConnection = false;
- }
- // End HTTP connection after file list download
- http.end();
- if (httpCode != HTTP_CODE_OK) return;
+ JsonArray files = doc.as();
+ if (files.isNull() || (!manifestContains(files, "/index.html.gz") && !manifestContains(files, "/index.html"))) {
+ SS2K_LOG(HTTP_SERVER_LOG_TAG, "Filesystem file list is invalid or has no index page");
+ return false;
+ }
- JsonArray files = doc.as();
- if (files.isNull() || (!manifestContains(files, "/index.html.gz") && !manifestContains(files, "/index.html"))) {
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "Filesystem file list is invalid or has no index page");
- return;
- }
- bool filesystemUpdateSucceeded = pruneFilesystem(files);
- // iterate through file list and download files individually
- for (JsonVariant v : files) {
- String fileName = "/" + v.as();
- httpCode = beginHttpGet(http, localClient, DATA_UPDATEURL + fileName);
- if (httpCode == HTTP_CODE_OK) {
- LittleFS.remove(fileName);
- File file = LittleFS.open(fileName, FILE_WRITE, true);
- if (!file) {
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "Failed to create file, %s", fileName.c_str());
- filesystemUpdateSucceeded = false;
- http.end(); // End HTTP before returning
- return;
- }
- int bytesWritten = http.writeToStream(&file);
- file.close();
- if (bytesWritten < 0) {
- LittleFS.remove(fileName);
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "Error writing %s (%d)", fileName.c_str(), bytesWritten);
- httpServer.internetConnection = false;
- filesystemUpdateSucceeded = false;
- } else {
- if (fileName.endsWith(".gz")) {
- String uncompressedName = fileName.substring(0, fileName.length() - 3);
- LittleFS.remove(uncompressedName);
- }
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "Created: %s (%d bytes)", fileName.c_str(), bytesWritten);
- httpServer.internetConnection = true;
- }
- } else {
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "Error downloading %s %d", fileName.c_str(), httpCode);
- httpServer.internetConnection = false;
- filesystemUpdateSucceeded = false;
- }
- // End HTTP connection after each file download
+ bool filesystemUpdateSucceeded = true;
+ bool downloadedFile = false;
+ for (JsonVariantConst v : files) {
+ String fileName = "/" + v.as();
+ if (LittleFS.exists(fileName)) continue;
+ if (WiFi.status() != WL_CONNECTED) {
+ SS2K_LOGW(HTTP_SERVER_LOG_TAG, "Web filesystem repair stopped because WiFi disconnected");
+ filesystemUpdateSucceeded = false;
+ break;
+ }
+
+ downloadedFile = true;
+ SS2K_LOG(HTTP_SERVER_LOG_TAG, "Downloading web file: %s", fileName.c_str());
+ httpCode = beginHttpGet(http, localClient, DATA_UPDATEURL + fileName);
+ if (httpCode == HTTP_CODE_OK) {
+ const String temporaryFileName = fileName + ".download";
+ LittleFS.remove(temporaryFileName);
+ File file = LittleFS.open(temporaryFileName, FILE_WRITE, true);
+ if (!file) {
+ SS2K_LOG(HTTP_SERVER_LOG_TAG, "Failed to create temporary file for %s", fileName.c_str());
+ filesystemUpdateSucceeded = false;
http.end();
+ continue;
}
-
- if (filesystemUpdateSucceeded && filesystemIndexExists()) {
- if (setNVSFilesystemVersion(serverVersion)) {
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "Filesystem updated to version %s", serverVersion.c_str());
- } else {
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "Failed to save filesystem version %s", serverVersion.c_str());
- }
+ int bytesWritten = http.writeToStream(&file);
+ file.close();
+ if (bytesWritten < 0) {
+ LittleFS.remove(temporaryFileName);
+ SS2K_LOG(HTTP_SERVER_LOG_TAG, "Error writing %s (%d)", fileName.c_str(), bytesWritten);
+ httpServer.internetConnection = false;
+ filesystemUpdateSucceeded = false;
+ } else if (!LittleFS.rename(temporaryFileName, fileName)) {
+ LittleFS.remove(temporaryFileName);
+ SS2K_LOGE(HTTP_SERVER_LOG_TAG, "Unable to install downloaded file %s", fileName.c_str());
+ filesystemUpdateSucceeded = false;
} else {
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "Filesystem update incomplete; version was not changed");
+ if (fileName.endsWith(".gz")) {
+ String uncompressedName = fileName.substring(0, fileName.length() - 3);
+ LittleFS.remove(uncompressedName);
+ }
+ SS2K_LOG(HTTP_SERVER_LOG_TAG, "Created: %s (%d bytes)", fileName.c_str(), bytesWritten);
+ httpServer.internetConnection = true;
}
+ } else {
+ SS2K_LOG(HTTP_SERVER_LOG_TAG, "Error downloading %s %d (%s)", fileName.c_str(), httpCode, HTTPClient::errorToString(httpCode).c_str());
+ httpServer.internetConnection = false;
+ filesystemUpdateSucceeded = false;
}
+ http.end();
+ }
- //////// Update Firmware /////////
- if (((availableVer > currentVer) || updateAnyway) && (userConfig->getAutoUpdate())) {
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "New firmware detected!");
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "Upgrading from %s to %s", FIRMWARE_VERSION, serverVersion.c_str());
- const String firmwareUrl = userConfig->getFirmwareUpdateURL() + String(FW_BINFILE);
- if (!validateRemoteFirmwareHeader(http, localClient, firmwareUrl)) {
- SS2K_LOGE(HTTP_SERVER_LOG_TAG, "Firmware update cancelled before flashing because image validation failed");
- return;
- }
- t_httpUpdate_return ret = httpUpdate.update(localClient, firmwareUrl);
- switch (ret) {
- case HTTP_UPDATE_FAILED:
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "HTTP_UPDATE_FAILED Error %d : %s", httpUpdate.getLastError(), httpUpdate.getLastErrorString().c_str());
- break;
-
- case HTTP_UPDATE_NO_UPDATES:
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "HTTP_UPDATE_NO_UPDATES");
- break;
-
- case HTTP_UPDATE_OK:
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "HTTP_UPDATE_OK");
- break;
- }
- } else { // don't update
- SS2K_LOG(HTTP_SERVER_LOG_TAG, " - Current Version: %s", FIRMWARE_VERSION);
- }
- } else {
- SS2K_LOG(HTTP_SERVER_LOG_TAG, "Could not connect to Github. httpCode: %d", httpCode);
+ if (!filesystemUpdateSucceeded || !filesystemIndexExists()) {
+ SS2K_LOGW(HTTP_SERVER_LOG_TAG, "Web filesystem repair incomplete");
+ return false;
}
- // localClient will be automatically destroyed when the function exits
+
+ // Install the fetched manifest only after every missing asset is present.
+ // This keeps the next boot's local completeness check independent of TLS.
+ const String temporaryManifest = String(DATA_FILELIST) + ".download";
+ LittleFS.remove(temporaryManifest);
+ File manifest = LittleFS.open(temporaryManifest, FILE_WRITE, true);
+ if (!manifest) {
+ SS2K_LOGE(HTTP_SERVER_LOG_TAG, "Unable to create temporary web filesystem manifest");
+ return false;
+ }
+ const size_t manifestBytes = serializeJson(doc, manifest);
+ manifest.close();
+ if (manifestBytes == 0) {
+ LittleFS.remove(temporaryManifest);
+ SS2K_LOGE(HTTP_SERVER_LOG_TAG, "Unable to write web filesystem manifest");
+ return false;
+ }
+
+ LittleFS.remove(DATA_FILELIST);
+ if (!LittleFS.rename(temporaryManifest, DATA_FILELIST)) {
+ LittleFS.remove(temporaryManifest);
+ SS2K_LOGE(HTTP_SERVER_LOG_TAG, "Unable to install web filesystem manifest");
+ return false;
+ }
+
+ SS2K_LOG(HTTP_SERVER_LOG_TAG, downloadedFile ? "Missing web files restored" : "Web filesystem manifest restored");
+ return true;
}
diff --git a/src/Main.cpp b/src/Main.cpp
index 700a35ad..1efee5c9 100644
--- a/src/Main.cpp
+++ b/src/Main.cpp
@@ -9,6 +9,8 @@
#include "Stepper.h"
#include "SS2KLog.h"
#include
+#include
+#include
#include
#include
#include "ERG_Mode.h"
@@ -46,6 +48,82 @@ RuntimeParameters* rtConfig = new RuntimeParameters;
namespace {
RTC_NOINIT_ATTR bool inhibitLedOnBoot = false;
+#ifdef SERIAL_CUSTOM_CHARACTERISTIC
+constexpr size_t SERIAL_CUSTOM_CHARACTERISTIC_MAX_REQUEST = 128;
+
+// Debug-only USB serial protocol:
+// cc 01 1e read BLE_stepperSpeed
+// cc 02 1e b8 0b 00 00 write BLE_stepperSpeed (3000, little-endian)
+// Each complete command is newline-delimited and replies as `cc_response` followed
+// by the characteristic's raw response bytes in hexadecimal.
+void processSerialCustomCharacteristic() {
+ static char line[SERIAL_CUSTOM_CHARACTERISTIC_MAX_REQUEST * 3 + 1];
+ static size_t lineLength = 0;
+
+ while (Serial.available()) {
+ const int received = Serial.read();
+ if (received < 0) return;
+
+ if (received == '\r') continue;
+ if (received == '\n') {
+ line[lineLength] = '\0';
+
+ if (lineLength >= 2 && line[0] == 'c' && line[1] == 'c') {
+ uint8_t request[SERIAL_CUSTOM_CHARACTERISTIC_MAX_REQUEST];
+ size_t requestLength = 0;
+ char* cursor = line + 2;
+ bool valid = true;
+
+ while (*cursor != '\0') {
+ while (*cursor == ' ' || *cursor == '\t') cursor++;
+ if (*cursor == '\0') break;
+ if (requestLength == sizeof(request) || !isxdigit(static_cast(cursor[0])) || !isxdigit(static_cast(cursor[1]))) {
+ valid = false;
+ break;
+ }
+
+ char byteText[] = {cursor[0], cursor[1], '\0'};
+ request[requestLength++] = static_cast(strtoul(byteText, nullptr, 16));
+ cursor += 2;
+ if (*cursor != '\0' && *cursor != ' ' && *cursor != '\t') {
+ valid = false;
+ break;
+ }
+ }
+
+ if (valid && requestLength >= 2 && request[1] != BLE_allSettings) {
+ BLE_ss2kCustomCharacteristic::process(std::string(reinterpret_cast(request), requestLength), BLE_HS_CONN_HANDLE_NONE, 23, false);
+ NimBLEService* service = NimBLEDevice::getServer()->getServiceByUUID(SMARTSPIN2K_SERVICE_UUID);
+ NimBLECharacteristic* characteristic = service == nullptr ? nullptr : service->getCharacteristic(SMARTSPIN2K_CHARACTERISTIC_UUID);
+ if (characteristic != nullptr) {
+ NimBLEAttValue response = characteristic->getValue();
+ Serial.print("cc_response");
+ for (size_t i = 0; i < response.size(); ++i) {
+ Serial.printf(" %02x", response[i]);
+ }
+ Serial.println();
+ } else {
+ Serial.println("cc_error unavailable");
+ }
+ } else {
+ Serial.println("cc_error invalid_request");
+ }
+ }
+
+ lineLength = 0;
+ continue;
+ }
+
+ if (lineLength < sizeof(line) - 1) {
+ line[lineLength++] = static_cast(received);
+ } else {
+ lineLength = 0;
+ Serial.println("cc_error request_too_long");
+ }
+ }
+}
+#endif
+
// RTC memory survives commanded ESP.restart() calls but not power loss. A set
// flag means the previous reboot was intentional, so consume it and keep the
// LED quiet until activity turns it back on.
@@ -161,11 +239,10 @@ void SS2K::finishSetup() {
}
#endif
- // Check for firmware update. It's important that this stays before BLE &
- // HTTP setup because otherwise they use too much traffic and the device
- // fails to update which really sucks when it corrupts your settings.
+ // Finish WiFi and web filesystem repair before BLE and network services add
+ // their runtime memory and traffic load.
startWifi();
- httpServer.FirmwareUpdate();
+ httpServer.syncWebServerFiles();
pinMode(currentBoard.shiftUpPin, INPUT_PULLUP); // Push-Button with input Pullup
pinMode(currentBoard.shiftDownPin, INPUT_PULLUP); // Push-Button with input Pullup
@@ -196,12 +273,11 @@ void SS2K::finishSetup() {
ss2k->startTasks();
httpServer.start();
- // Start DirCon TCP server for direct control over the bike trainer
SS2K_LOG(MAIN_LOG_TAG, "Starting DirCon TCP service");
if (DirConManager::start()) {
SS2K_LOG(MAIN_LOG_TAG, "DirCon TCP service started successfully");
} else {
- SS2K_LOG(MAIN_LOG_TAG, "Failed to start DirCon TCP service");
+ SS2K_LOGE(MAIN_LOG_TAG, "Failed to start DirCon TCP service");
}
#ifdef TEST_PTAB4PWR
@@ -225,13 +301,18 @@ void loop() { // Delete this task so we can make one that's more memory efficie
void SS2K::maintenanceLoop(void* pvParameters) {
finishSetup();
- static unsigned long intervalTimer2 = millis();
- static unsigned long rebootTimer = millis();
+ static unsigned long maintenanceTimer = millis();
+ static unsigned long riderStatusLogTimer = millis();
+ static unsigned long rebootTimer = millis();
while (true) {
delay(10);
BLEFirmwareUpdateLoop();
+#ifdef SERIAL_CUSTOM_CHARACTERISTIC
+ processSerialCustomCharacteristic();
+#endif
+
// be quiet while updating via BLE
if (!ss2k->isUpdating) {
static unsigned long bleTimer = millis();
@@ -275,7 +356,7 @@ void SS2K::maintenanceLoop(void* pvParameters) {
// If we're in ERG mode, modify shift commands to inc/dec the target watts instead.
// If we have a resistance bike attached, slow down when we're close to the limits.
- if (ss2k->pelotonIsConnected && !rtConfig->getHomed() && !spinBLEServer.spinDownFlag) {
+ if (stepper && ss2k->pelotonIsConnected && !rtConfig->getHomed() && !spinBLEServer.spinDownFlag) {
int speed = userConfig->getStepperSpeed();
float resistance = rtConfig->resistance.getValue();
float maxResistance = rtConfig->getMaxResistance();
@@ -335,8 +416,8 @@ void SS2K::maintenanceLoop(void* pvParameters) {
userConfig->saveToLittleFS();
}
- // Things to do every 6 seconds
- if ((millis() - intervalTimer2) > 6007) {
+ // Periodic maintenance.
+ if ((millis() - maintenanceTimer) > MAIN_MAINTENANCE_INTERVAL_MS) {
// Reboot after half an hour without meaningful pedaling. Also treat unchanged values as inactive because disconnected servers can leave stale readings behind.
constexpr int inactivityThreshold = 10;
constexpr unsigned long inactivityRebootDelay = 1800000;
@@ -376,12 +457,17 @@ void SS2K::maintenanceLoop(void* pvParameters) {
SS2K_LOG(MAIN_LOG_TAG, "Best Block: %d", heap_caps_get_largest_free_block(MALLOC_CAP_8BIT));
}
#endif // DEBUG_STACK
- // Log userParameters
- SS2K_LOG(MAIN_LOG_TAG, "PM Con %d, CAD con %d, HRM Con %d, W %d, Cad %d, HR %d, Gear %d, Res %d, Current Pos %d, Target Pos %d", spinBLEClient.connectedPM,
- spinBLEClient.connectedCD, spinBLEClient.connectedHRM, rtConfig->watts.getValue(), rtConfig->cad.getValue(), rtConfig->hr.getValue(), rtConfig->getShifterPosition(),
- rtConfig->resistance.getValue(), ss2k->getCurrentPosition(), ss2k->getTargetPosition());
+ // Connection state changes infrequently, so keep it out of the one-second rider status record.
+ SS2K_LOG(MAIN_LOG_TAG, "DEV PM=%d CAD=%d HRM=%d", spinBLEClient.connectedPM, spinBLEClient.connectedCD, spinBLEClient.connectedHRM);
+ maintenanceTimer = millis();
+ }
- intervalTimer2 = millis();
+ if ((millis() - riderStatusLogTimer) >= RIDER_STATUS_LOG_INTERVAL_MS) {
+ // Log rider status for diagnostics and ride analysis.
+ SS2K_LOG(MAIN_LOG_TAG, "W=%d C=%d H=%d G=%d R=%d P=%d->%d", rtConfig->watts.getValue(), rtConfig->cad.getValue(), rtConfig->hr.getValue(),
+ rtConfig->getShifterPosition(), rtConfig->resistance.getValue(), ss2k->getCurrentPosition(), ss2k->getTargetPosition());
+
+ riderStatusLogTimer = millis();
}
}
}
@@ -534,7 +620,11 @@ void SS2K::restartWifi() {
stopWifi();
delay(100);
startWifi();
+ refreshBLEAdvertisementIp();
httpServer.start();
+ if (!DirConManager::start()) {
+ SS2K_LOGE(MAIN_LOG_TAG, "Failed to restart DirCon TCP service");
+ }
}
void SS2K::handleShiftButtons() {
@@ -617,6 +707,13 @@ void SS2K::txSerial() { // Serial.printf(" Before TX ");
txCheck = 1;
}
pelotonIsConnected = false;
+ // Peloton serial shares the global power/cadence connection flags with BLE sensors.
+ // Only release them when no specific BLE power meter is configured, matching the
+ // Peloton coexistence checks in collectAndSet().
+ if (strcmp(userConfig->getConnectedPowerMeter(), NONE) == 0 || strcmp(userConfig->getConnectedPowerMeter(), ANY) == 0) {
+ spinBLEClient.connectedPM = false;
+ spinBLEClient.connectedCD = false;
+ }
rtConfig->setMinResistance(-DEFAULT_RESISTANCE_RANGE);
rtConfig->setMaxResistance(DEFAULT_RESISTANCE_RANGE);
txCheck++;
diff --git a/src/PowerTable_Helpers.cpp b/src/PowerTable_Helpers.cpp
index 3ba57c85..8c85f8ff 100644
--- a/src/PowerTable_Helpers.cpp
+++ b/src/PowerTable_Helpers.cpp
@@ -7,287 +7,187 @@
#include "PowerTable_Helpers.h"
-// if building PLATFORMIO_ENV_NATIVE environment define SS2K_LOG as Serial.printf(), else include the SS2KLog.h.
-#ifdef PLATFORMIO_ENV_NATIVE
-#include
-#include
#include
-#include
-#include
-#include
-#include