From fa6a19d1aae8f69456e11b593d9c2162b18a954f Mon Sep 17 00:00:00 2001 From: localhorst Date: Mon, 5 Jan 2026 22:18:58 +0100 Subject: [PATCH] basic led control with local btn --- ARCHITECTURE.md | 390 ------------------------------ README.md | 55 +---- functions.ino | 212 ---------------- main/CMakeLists.txt | 6 +- main/animation.c | 2 +- main/control.c | 493 ++------------------------------------ main/control.h | 43 +--- main/localbtn.c | 85 +++++++ main/localbtn.h | 36 +++ main/main.c | 12 +- nfc01.ino | 85 ------- tools/dev_https_server.py | 120 ---------- tools/requirements.txt | 5 - webapp/app/app.js | 322 ------------------------- webapp/css/style.css | 313 ------------------------ webapp/data/favicon.ico | 0 webapp/index.html | 115 --------- 17 files changed, 156 insertions(+), 2138 deletions(-) delete mode 100644 ARCHITECTURE.md delete mode 100644 functions.ino create mode 100644 main/localbtn.c create mode 100644 main/localbtn.h delete mode 100644 nfc01.ino delete mode 100644 tools/dev_https_server.py delete mode 100644 tools/requirements.txt delete mode 100644 webapp/app/app.js delete mode 100644 webapp/css/style.css delete mode 100644 webapp/data/favicon.ico delete mode 100644 webapp/index.html diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md deleted file mode 100644 index 8a4bce1..0000000 --- a/ARCHITECTURE.md +++ /dev/null @@ -1,390 +0,0 @@ -# Architecture Documentation - -## System Overview - -The ESP32 LED Controller is a real-time embedded system designed for model aircraft LED control with the following key components: - -``` -┌─────────────────────────────────────────────────────────┐ -│ Application Layer │ -│ ┌──────────┐ ┌───────────┐ ┌──────────────────────┐ │ -│ │ main.c │ │ Web-BLE │ │ Animation Patterns │ │ -│ └────┬─────┘ └─────┬─────┘ └──────────┬───────────┘ │ -└───────┼──────────────┼────────────────────┼─────────────┘ - │ │ │ -┌───────┴──────────────┴────────────────────┴─────────────┐ -│ Control Layer │ -│ ┌──────────────────────────────────────────────────┐ │ -│ │ control.c - System Orchestration │ │ -│ │ - BLE GATT Server │ │ -│ │ - NVS Configuration Storage │ │ -│ │ - OTA Firmware Update │ │ -│ │ - Subsystem Initialization │ │ -│ └──────────────────────────────────────────────────┘ │ -└──────────────────────────────────────────────────────────┘ - │ │ │ -┌───────┼──────────────┼────────────────────┼─────────────┐ -│ │ │ │ │ -│ ┌────▼────┐ ┌────▼─────┐ ┌────▼─────────┐ │ -│ │ LED │ │ RC Signal│ │ Animation │ │ -│ │ Driver │ │ Reader │ │ Engine │ │ -│ └─────────┘ └──────────┘ └──────────────┘ │ -│ │ -│ Hardware Layer │ -└──────────────────────────────────────────────────────────┘ - │ │ │ -┌───────┼──────────────┼────────────────────┼─────────────┐ -│ ┌────▼────┐ ┌────▼─────┐ │ -│ │ RMT │ │ GPIO │ │ -│ │ (WS2812)│ │ (PWM) │ │ -│ └─────────┘ └──────────┘ │ -│ │ -│ ESP-IDF HAL & Drivers │ -└──────────────────────────────────────────────────────────┘ -``` - -## Module Details - -### 1. main.c - Application Entry Point -**Responsibilities:** -- System initialization orchestration -- Animation task creation and management -- System health monitoring -- Error handling and recovery - -**Key Functions:** -- `app_main()`: Entry point, initializes control system -- `animation_task()`: FreeRTOS task for 60 FPS animation updates - -**Task Priority:** Animation task runs at priority 5 (above default) - ---- - -### 2. control.c/h - System Control & Orchestration -**Responsibilities:** -- BLE GATT server implementation -- Configuration management (NVS) -- OTA firmware update handling -- Subsystem initialization and coordination - -**BLE Service Structure:** -``` -Service UUID: 0x00FF -├── Config Characteristic (0xFF01) -│ ├── Read: Get current configuration -│ └── Write: Update and persist configuration -├── Mode Characteristic (0xFF02) -│ ├── Read: Get current animation mode -│ └── Write: Set animation mode -├── PWM Emulation (0xFF03) -│ └── Write: Trigger mode change -└── OTA Characteristic (0xFF04) - └── Write: Stream firmware binary -``` - -**Configuration Storage:** -```c -typedef struct { - int8_t led_pin_strip_a; // -1 = disabled - int8_t led_pin_strip_b; // -1 = disabled - int8_t pwm_pin; // -1 = disabled - ble_timeout_t ble_timeout; // 0, 60, 300 seconds - uint32_t magic; // 0xDEADBEEF validation -} controller_config_t; -``` - -**NVS Namespace:** `led_ctrl` -**NVS Key:** `config` - -**BLE Timeout Logic:** -- Timer starts at boot -- Pauses when device connects -- Resumes when device disconnects -- Disables BLE advertising on timeout - -**OTA Update Flow:** -1. Client writes firmware data in 512-byte chunks -2. ESP32 writes to inactive OTA partition -3. Last chunk (< 512 bytes) triggers completion -4. Validates partition, sets boot partition -5. Resets configuration and restarts - ---- - -### 3. led.c/h - WS2812B LED Driver -**Technology:** ESP32 RMT (Remote Control) peripheral - -**Why RMT?** -- Hardware timing generation (no CPU overhead) -- Precise WS2812B timing requirements: - - T0H: 350ns ±150ns - - T0L: 900ns ±150ns - - T1H: 900ns ±150ns - - T1L: 350ns ±150ns - - Reset: >280µs - -**Implementation:** -``` -RMT Channel → Custom Encoder → WS2812B Strip - ↓ ↓ ↓ - 80 MHz GRB Encoding Serial Data -``` - -**Color Management:** -- Internal RGB buffer for each strip -- Thread-safe access via mutex -- Hardware converts RGB → GRB for WS2812B -- Support for HSV → RGB conversion - -**Key Functions:** -- `led_init()`: Configure RMT channels -- `led_set_pixel_a/b()`: Set individual LED -- `led_fill_a/b()`: Set all LEDs same color -- `led_show()`: Update physical LEDs -- `led_fade_to_black()`: Fade effect for trails - -**Memory Usage:** -- Strip A buffer: num_leds * 3 bytes (RGB) -- Strip B buffer: num_leds * 3 bytes (RGB) -- Default: 44 LEDs * 2 strips * 3 = 264 bytes - ---- - -### 4. rcsignal.c/h - PWM Signal Reader -**Technology:** GPIO interrupt + software edge detection - -**PWM Signal Specification:** -- Standard RC PWM: 1000-2000µs pulse width -- Detection threshold: 1500µs -- Timeout: 100ms (signal loss detection) - -**Mode Change Logic:** -``` -PWM < 1500µs → Set "pull_detected" flag - ↓ -PWM > 1500µs AND pull_detected → Mode++ - ↓ -pull_detected = false -``` - -**Implementation:** -- ISR captures rising/falling edges -- Calculates pulse width in microseconds -- Monitor task (10ms interval) detects mode changes -- Callback notifies animation system - -**Thread Safety:** -- Volatile variables for ISR communication -- Monitor task runs at priority 5 - ---- - -### 5. animation.c/h - Animation Engine -**Update Rate:** 60 FPS (16.67ms per frame) - -**Global State:** -- `global_hue`: Slow color cycling (updates every 3 frames) -- `frame_counter`: Frame synchronization -- `current_mode`: Active animation pattern - -**Animation Techniques:** - -#### Fade Effects -```c -// Smooth trails for chase animations -led_fade_to_black(amount); -// Each LED: color = (color * (255 - amount)) / 255 -``` - -#### Beat Synchronization -```c -// Sine wave based on BPM and time -beatsin16(bpm, min_val, max_val); -// Returns position oscillating between min and max -``` - -#### HSV Color Space -- Hue: 0-255 (color wheel) -- Saturation: 0-255 (color intensity) -- Value: 0-255 (brightness) -- Automatic RGB conversion - -**Animation Modes Breakdown:** - -1. **Static Colors** (Black, Red, Blue, Green, White) - - Single `led_fill()` call - - No per-frame updates needed - -2. **Rainbow** - - HSV hue gradient across strip - - Hue offset per LED: `global_hue + (i * 7)` - - Global hue increments for animation - -3. **Rainbow Glitter** - - Base rainbow + random white sparkles - - 80/255 chance per frame - - Random LED position - -4. **Confetti** - - Fade to black (10/255 per frame) - - Random position + random hue - - Creates "fireworks" effect - -5. **Sinelon** - - Sweeping dot using sine wave - - Position: `beatsin16(13 BPM)` - - 20/255 fade creates trails - -6. **BPM** - - Color palette based on party colors - - Beat: `beatsin8(33 BPM)` - - Brightness modulation per LED - -7. **Navigation** - - Fixed positions for aviation lights - - Red: LEDs 0-2 (left) - - Green: Last 3 LEDs (right) - - White blink: 30 Hz (half frame rate) - -8. **Chase (Red)** - - Red dot with ±2 LED trail - - Position: `beatsin16(40 BPM)` - - No fade (instant clear) - -9. **Chase RGB** - - Same as Chase but HSV color cycling - - Hue: `global_hue` - -10. **Random** - - Random LED, random color each frame - - Rare full clear event - ---- - -## Data Flow - -### Configuration Update Flow -``` -Web Browser → BLE Write → control.c → NVS Save → Restart (if pins changed) -``` - -### Animation Update Flow -``` -animation_task (60Hz) → animation_update() → LED buffer → led_show() → RMT → LEDs -``` - -### PWM Mode Change Flow -``` -RC Signal → GPIO ISR → rcsignal.c → Callback → control.c → animation.c -``` - -### OTA Update Flow -``` -Web Browser → BLE Write (chunks) → control.c → esp_ota → Flash → Restart -``` - -## Thread Safety - -### Mutexes Used -1. **led_mutex**: Protects LED buffer access - - Used by: animation_update(), led_show() - - Type: FreeRTOS mutex - -### ISR Safety -- **rcsignal.c**: Volatile variables for ISR communication -- **Minimal ISR work**: Only timestamp and edge detection -- **Deferred processing**: Monitor task handles logic - -### Task Priorities -``` -Priority 5: animation_task, rcsignal_monitor_task -Priority 1: BLE stack tasks (default) -Priority 0: Idle task -``` - -## Memory Management - -### Static Allocation -- Configuration structure: 11 bytes (NVS) -- Animation state: ~100 bytes (global variables) - -### Dynamic Allocation -- LED buffers: `num_leds * 3 * 2` bytes (both strips) -- RMT encoder: ~200 bytes per strip -- BLE stack: ~30KB (ESP-IDF managed) - -### Flash Usage -- Code: ~500KB (with BLE stack) -- OTA partitions: 2x 1MB (dual-boot) -- NVS: 24KB -- Factory: 1MB - -### Heap Usage Estimate -- Total: ~50KB during operation -- Available: ~250KB on ESP32 - -## Power Optimization - -### Active Mode -- CPU: 240 MHz (animation processing) -- BLE: Active (advertising/connected) -- Power: ~180mA (ESP32 only) - -### BLE Disabled Mode -- CPU: 240 MHz (animation only) -- BLE: Disabled after timeout -- Power: ~100mA (ESP32 only) - -### LED Power -- Per LED: ~60mA at full white -- 44 LEDs full white: ~2.6A -- Typical animation: ~500mA average - -## ESP32 vs ESP32-C3 Differences - -### ESP32 (Xtensa) -- Dual-core: FreeRTOS symmetric multiprocessing -- BLE + Classic Bluetooth controller -- More GPIO pins available -- Recommended for complex projects - -### ESP32-C3 (RISC-V) -- Single-core: Simpler task management -- BLE only (no Classic Bluetooth) -- Fewer GPIO pins -- Lower cost option - -### Compatibility -- Same codebase works on both -- Pin numbers differ (check datasheet) -- RMT peripheral identical -- BLE functionality identical - -## Performance Characteristics - -### Latency -- **PWM detection**: <10ms -- **BLE command**: <100ms -- **Mode change**: <20ms (next frame) -- **LED update**: 16.67ms (60 FPS locked) - -### Throughput -- **LED data**: ~13.44 Mbps theoretical (RMT) -- **BLE**: ~1 Mbps (limited by MTU) -- **OTA**: ~40 KB/s (BLE transfer) - -### Timing Precision -- **Animation frame**: ±0.5ms jitter -- **WS2812B timing**: ±50ns (RMT hardware) -- **PWM measurement**: ±1µs (ISR timing) - ---- - - -## Future Enhancement Ideas TODO - -1. **Dynamic LED Count**: Auto-detect number of LEDs -2. **Multi-Strip Sync**: Synchronized patterns -3. **Pattern Editor**: Visual animation designer -4. **Scheduler**: Time-based mode changes - diff --git a/README.md b/README.md index fa6b7dd..40f94e9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # ESP32 LED Controller for Model Aircraft -Professional LED controller firmware for ESP32 with Web-BLE configuration interface. Designed for model aircraft with WS2812B LED strips. +Professional LED controller firmware for ESP32. Designed for model aircraft with WS2812B LED strips. ## Features @@ -26,28 +26,17 @@ Professional LED controller firmware for ESP32 with Web-BLE configuration interf 13. **Chase (RGB)** - RGB cycling chase effect 14. **Random** - Random LED colors -### Web-BLE Configuration -- **Pin Configuration**: Set GPIO pins for LED strips and PWM input -- **BLE Auto-Off**: Configure timeout (Never/1min/5min) -- **Manual Control**: Select animation modes from web interface -- **PWM Emulation**: Test mode switching without RC signal -- **OTA Firmware Update**: Upload new firmware via Bluetooth - ## Project Structure ``` led-controller-firmware/ ├── main/ │ ├── main.c # Application entry point -│ ├── control.c/h # BLE, NVS, initialization +│ ├── control.c/h # NVS, initialization │ ├── led.c/h # WS2812B control (RMT driver) │ ├── rcsignal.c/h # PWM signal reading +│ ├── localbtn.c/h # Local btn reading │ └── animation.c/h # LED animation patterns -├── webapp/ -│ ├── index.html # Web-BLE interface -│ ├── app/app.js # BLE communication logic -│ ├── css/style.css # UI styling -│ └── data/favicon.ico ├── CMakeLists.txt ├── sdkconfig.defaults └── partitions.csv # OTA-enabled partition table @@ -107,42 +96,6 @@ GND -> Common Ground - 1500µs threshold for mode switching - Rising edge >1500µs after <1500µs triggers next mode -## Web-BLE Configuration - -### Access the Interface - -1. Open `webapp/index.html` in Chrome, Edge, or Opera (Web Bluetooth required) -2. Click "Connect via BLE" -3. Select "LED-Controller" from device list -4. Configure settings and control LEDs - -### Configuration Options - -#### Pin Setup -- **LED Strip A GPIO**: -1 to disable, 0-48 for GPIO pin -- **LED Strip B GPIO**: -1 to disable, 0-48 for GPIO pin -- **PWM Input GPIO**: -1 to disable, 0-48 for GPIO pin - -#### BLE Timeout -- **Never**: BLE stays on until manually disabled -- **1 Minute**: Auto-disable after 1 min of boot (unless connected) -- **5 Minutes**: Auto-disable after 5 min of boot (unless connected) - -#### Firmware Update -1. Build firmware: `idf.py build` -2. Find binary: `build/led_controller.bin` -3. Upload via Web-BLE interface -4. Device restarts with new firmware (settings reset) - -## Default Configuration - -On first boot or after reset: -- All pins: **Not configured** (-1) -- BLE timeout: **Never** -- Animation mode: **Black** (off) - -Configure via Web-BLE before use. - ## Development ### Adding New Animations @@ -154,7 +107,7 @@ Configure via Web-BLE before use. ### Modifying LED Count -Edit `DEFAULT_NUM_LEDS_A` and `DEFAULT_NUM_LEDS_B` in `control.c`. TODO +Edit `DEFAULT_NUM_LEDS_A` and `DEFAULT_NUM_LEDS_B` in `control.c`. TODO: ### Testing diff --git a/functions.ino b/functions.ino deleted file mode 100644 index f74f1eb..0000000 --- a/functions.ino +++ /dev/null @@ -1,212 +0,0 @@ -boolean getRC01() { - rc01Val = pulseIn(rc01, HIGH); - //Serial.println(rc01Val); - if (rc01Val > 1500) { - //Serial.println("RC1 ON"); - return true; - } else { - //Serial.println("RC1 OFF"); - return false; - } -} - -boolean getRC02() { - rc02Val = pulseIn(rc02, HIGH); - // Serial.println(rc02Val); - if (rc02Val < 1500) { - pullRC = true; - } - if (rc02Val > 1500 && pullRC) { - //Serial.println("RC2 ON"); - pullRC = false; - return true; - } else { - //Serial.println("RC2 OFF"); - return false; - } -} - -void serialPrintModus(int modus) { - switch (modus) { - case 0: - Serial.println("Black"); - break; - case 1: - Serial.println("Red"); - break; - case 2: - Serial.println("Blue"); - break; - case 3: - Serial.println("Green"); - break; - case 4: - Serial.println("White"); - break; - case 5: - Serial.println("Rainbow"); - break; - case 6: - Serial.println("RainbowWithGlitter"); - break; - case 7: - Serial.println("Confetti"); - break; - case 8: - Serial.println("Sinelon"); - break; - case 9: - Serial.println("BPM"); - break; - case 10: - Serial.println("Navigation"); - break; - case 11: - Serial.println("Chase"); - break; - case 12: - Serial.println("ChaseRGB"); - break; - case 13: - Serial.println("Random"); - break; - } -} - -void rainbow() -{ - Serial.println("Rainbow"); - // FastLED's built-in rainbow generator - fill_rainbow( leds, NUM_LEDS, gHue, 7); -} - -void rainbowWithGlitter() -{ - // built-in FastLED rainbow, plus some random sparkly glitter - rainbow(); - addGlitter(255); -} - -void addGlitter( fract8 chanceOfGlitter) -{ - if ( random8() < chanceOfGlitter) { - leds[ random16(NUM_LEDS) ] += CRGB::White; - } -} - -void confetti() -{ - // random colored speckles that blink in and fade smoothly - fadeToBlackBy( leds, NUM_LEDS, 10); - int pos = random16(NUM_LEDS); - leds[pos] += CHSV( gHue + random8(64), 200, 255); -} - -void sinelon() -{ - // a colored dot sweeping back and forth, with fading trails - fadeToBlackBy( leds, NUM_LEDS, 20); - int pos = beatsin16(13, 0, NUM_LEDS); - leds[pos] += CHSV( gHue, 255, 192); -} - -void bpm() -{ - // colored stripes pulsing at a defined Beats-Per-Minute (BPM) - uint8_t BeatsPerMinute = 33; - CRGBPalette16 palette = PartyColors_p; - uint8_t beat = beatsin8( BeatsPerMinute, 64, 255); - for ( int i = 0; i < NUM_LEDS; i++) { //9948 - leds[i] = ColorFromPalette(palette, gHue + (i * 2), beat - gHue + (i * 10)); - } -} - -void blackMode() { - fill_solid(leds, NUM_LEDS, CRGB::Black); // Just to be sure, let's really make it BLACK. -} - -void redMode() { - fill_solid(leds, NUM_LEDS, CRGB::Red); -} - -void blueMode() { - fill_solid(leds, NUM_LEDS, CRGB::Blue); -} - -void greenMode() { - fill_solid(leds, NUM_LEDS, CRGB::Green); -} - -void whiteMode() { - fill_solid(leds, NUM_LEDS, CRGB::White); -} - -void navigation() { - FastLED.clear(); - leds[0] = CRGB::Red; - leds[1] = CRGB::Red; - leds[2] = CRGB::Red; - leds[41] = CRGB::Green; - leds[42] = CRGB::Green; - leds[43] = CRGB::Green; - - leds[5] = CRGB::White; - leds[6] = CRGB::White; - leds[37] = CRGB::White; - leds[38] = CRGB::White; - - FastLED.delay(100); - - leds[5] = CRGB::Black; - leds[6] = CRGB::Black; - leds[37] = CRGB::Black; - leds[38] = CRGB::Black; -} - -void chase() { - FastLED.clear(); - // a colored dot sweeping back and forth, with fading trails - //fadeToBlackBy( leds, NUM_LEDS, 20); - int pos = beatsin16(40, 0, NUM_LEDS); - leds[pos] = CRGB::Red; - if (pos < 41) { - leds[pos + 1] = CRGB::Red; - leds[pos + 2] = CRGB::Red; - } - if (pos > 1) { - leds[pos - 1] = CRGB::Red; - leds[pos - 2] = CRGB::Red; - } - -} - -void chaseRGB() { - FastLED.clear(); - // a colored dot sweeping back and forth, with fading trails - //fadeToBlackBy( leds, NUM_LEDS, 20); - int pos = beatsin16(40, 0, NUM_LEDS); - leds[pos] += CHSV( gHue, 255, 192); - if (pos < 41) { - leds[pos + 1] += CHSV( gHue, 255, 192); - leds[pos + 2] += CHSV( gHue, 255, 192); - } - if (pos > 1) { - leds[pos - 1] += CHSV( gHue, 255, 192); - leds[pos - 2] += CHSV( gHue, 255, 192); - } - -} - -void randomMode(){ - randomVal = random(0,45); - - if(randomVal == 44){ - if(random(5,11) == 9){ - FastLED.clear(); - } - }else{ - leds[randomVal] = random(0, 16777216); - } - -} - diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index a68f73b..e428543 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -5,10 +5,6 @@ idf_component_register( "led.c" "rcsignal.c" "animation.c" + "localbtn.c" INCLUDE_DIRS "." - EMBED_FILES - "../webapp/index.html" - "../webapp/app/app.js" - "../webapp/css/style.css" - "../webapp/data/favicon.ico" ) diff --git a/main/animation.c b/main/animation.c index 6200539..f637113 100644 --- a/main/animation.c +++ b/main/animation.c @@ -190,7 +190,7 @@ static void anim_bpm(void) // PartyColors palette simulation const uint8_t palette_colors[] = { 170, 240, 90, 150, 210, 30, 180, 0, - 210, 270, 150, 240, 330, 60, 300, 120}; + 210, 255, 150, 240, 255, 60, 255, 120}; for (uint16_t i = 0; i < num_leds_a; i++) { diff --git a/main/control.c b/main/control.c index 824c39e..f1450b3 100644 --- a/main/control.c +++ b/main/control.c @@ -1,26 +1,21 @@ /** * @file control.c - * @brief Control module implementation with BLE, NVS, and OTA + * @brief Control module implementation */ #include "control.h" #include "led.h" #include "rcsignal.h" +#include "localbtn.h" #include "animation.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" #include "esp_log.h" #include "esp_system.h" #include "nvs_flash.h" #include "nvs.h" #include "esp_timer.h" -#include "esp_ota_ops.h" -#include "esp_http_server.h" - -#include "esp_bt.h" -#include "esp_gap_ble_api.h" -#include "esp_gatts_api.h" -#include "esp_bt_main.h" -#include "esp_gatt_common_api.h" #include @@ -28,66 +23,19 @@ static const char *TAG = "CONTROL"; #define NVS_NAMESPACE "led_ctrl" #define CONFIG_MAGIC 0xDEADBEEF -#define DEFAULT_NUM_LEDS_A 44 -#define DEFAULT_NUM_LEDS_B 44 - -// BLE Configuration -#define GATTS_SERVICE_UUID 0x00FF -#define GATTS_CHAR_UUID_CONFIG 0xFF01 -#define GATTS_CHAR_UUID_MODE 0xFF02 -#define GATTS_CHAR_UUID_PWM 0xFF03 -#define GATTS_CHAR_UUID_OTA 0xFF04 -#define GATTS_NUM_HANDLE_TEST 8 - -#define DEVICE_NAME "LED-Controller" -#define ADV_CONFIG_FLAG (1 << 0) -#define SCAN_RSP_CONFIG_FLAG (1 << 1) +#define DEFAULT_NUM_LEDS_A 7 +#define DEFAULT_NUM_LEDS_B 7 // Global state static controller_config_t current_config = { .led_pin_strip_a = -1, .led_pin_strip_b = -1, .pwm_pin = -1, - .ble_timeout = BLE_TIMEOUT_NEVER, .magic = CONFIG_MAGIC}; -static bool ble_enabled = true; static uint8_t current_animation_mode = 0; -static esp_timer_handle_t ble_timeout_timer = NULL; -static bool ble_connected = false; - -// OTA state -static const esp_partition_t *update_partition = NULL; -static esp_ota_handle_t update_handle = 0; -static size_t ota_bytes_written = 0; - -// BLE variables -static uint8_t adv_config_done = 0; -static uint16_t gatts_if_global = ESP_GATT_IF_NONE; -static uint16_t conn_id_global = 0; -static uint16_t service_handle = 0; - -// BLE advertising parameters -static esp_ble_adv_params_t adv_params = { - .adv_int_min = 0x20, - .adv_int_max = 0x40, - .adv_type = ADV_TYPE_IND, - .own_addr_type = BLE_ADDR_TYPE_PUBLIC, - .channel_map = ADV_CHNL_ALL, - .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, -}; - -// Characteristic handles -static struct -{ - uint16_t config_handle; - uint16_t mode_handle; - uint16_t pwm_handle; - uint16_t ota_handle; -} char_handles = {0}; // Forward declarations -static void ble_timeout_callback(void *arg); static void on_mode_change(uint8_t new_mode); // NVS Functions @@ -115,7 +63,6 @@ static esp_err_t load_config_from_nvs(void) ESP_LOGI(TAG, " Strip A: GPIO%d", current_config.led_pin_strip_a); ESP_LOGI(TAG, " Strip B: GPIO%d", current_config.led_pin_strip_b); ESP_LOGI(TAG, " PWM Pin: GPIO%d", current_config.pwm_pin); - ESP_LOGI(TAG, " BLE Timeout: %d", current_config.ble_timeout); return ESP_OK; } @@ -152,10 +99,9 @@ static esp_err_t save_config_to_nvs(void) esp_err_t control_reset_config(void) { - current_config.led_pin_strip_a = -1; - current_config.led_pin_strip_b = -1; - current_config.pwm_pin = -1; - current_config.ble_timeout = BLE_TIMEOUT_NEVER; + current_config.led_pin_strip_a = 12; + current_config.led_pin_strip_b = 14; + current_config.pwm_pin = 13; current_config.magic = CONFIG_MAGIC; return save_config_to_nvs(); @@ -191,59 +137,6 @@ esp_err_t control_update_config(const controller_config_t *config) return err; } -// BLE timeout handling -static void ble_timeout_callback(void *arg) -{ - if (!ble_connected) - { - ESP_LOGI(TAG, "BLE timeout reached, disabling BLE"); - control_disable_ble(); - } -} - -static void start_ble_timeout(void) -{ - if (current_config.ble_timeout == BLE_TIMEOUT_NEVER) - { - return; - } - - if (ble_timeout_timer == NULL) - { - esp_timer_create_args_t timer_args = { - .callback = ble_timeout_callback, - .name = "ble_timeout"}; - esp_timer_create(&timer_args, &ble_timeout_timer); - } - - esp_timer_stop(ble_timeout_timer); - esp_timer_start_once(ble_timeout_timer, (uint64_t)current_config.ble_timeout * 1000000ULL); - ESP_LOGI(TAG, "BLE timeout started: %d seconds", current_config.ble_timeout); -} - -void control_disable_ble(void) -{ - if (!ble_enabled) - return; - - ble_enabled = false; - - if (ble_timeout_timer) - { - esp_timer_stop(ble_timeout_timer); - } - - // Stop BLE advertising - esp_ble_gap_stop_advertising(); - - ESP_LOGI(TAG, "BLE disabled"); -} - -bool control_is_ble_enabled(void) -{ - return ble_enabled; -} - // Animation mode change callback static void on_mode_change(uint8_t new_mode) { @@ -270,361 +163,6 @@ void control_emulate_pwm_pulse(void) rcsignal_trigger_mode_change(); } -// Embedded web files (will be linked) -extern const uint8_t index_html_start[] asm("_binary_index_html_start"); -extern const uint8_t index_html_end[] asm("_binary_index_html_end"); -extern const uint8_t app_js_start[] asm("_binary_app_js_start"); -extern const uint8_t app_js_end[] asm("_binary_app_js_end"); -extern const uint8_t style_css_start[] asm("_binary_style_css_start"); -extern const uint8_t style_css_end[] asm("_binary_style_css_end"); -extern const uint8_t favicon_ico_start[] asm("_binary_favicon_ico_start"); -extern const uint8_t favicon_ico_end[] asm("_binary_favicon_ico_end"); - -// BLE GAP event handler -static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) -{ - ESP_LOGI(TAG, "gap_event_handler() event: %i\n", event); - switch (event) - { - case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: - adv_config_done &= (~ADV_CONFIG_FLAG); - if (adv_config_done == 0) - { - esp_ble_gap_start_advertising(&adv_params); - } - break; - - case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT: - adv_config_done &= (~SCAN_RSP_CONFIG_FLAG); - if (adv_config_done == 0) - { - esp_ble_gap_start_advertising(&adv_params); - } - break; - - case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: - if (param->adv_start_cmpl.status == ESP_BT_STATUS_SUCCESS) - { - ESP_LOGI(TAG, "BLE advertising started"); - } - break; - - default: - break; - } -} - -// BLE GATTS event handler -static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) -{ - switch (event) - { - case ESP_GATTS_REG_EVT: - ESP_LOGI(TAG, "GATTS register, status %d, app_id %d", param->reg.status, param->reg.app_id); - gatts_if_global = gatts_if; - - // Set device name - esp_ble_gap_set_device_name(DEVICE_NAME); - - // Config advertising data - esp_ble_adv_data_t adv_data = { - .set_scan_rsp = false, - .include_name = true, - .include_txpower = true, - .min_interval = 0x0006, - .max_interval = 0x0010, - .appearance = 0x00, - .manufacturer_len = 0, - .p_manufacturer_data = NULL, - .service_data_len = 0, - .p_service_data = NULL, - .service_uuid_len = sizeof(uint16_t), - .p_service_uuid = (uint8_t[]){GATTS_SERVICE_UUID & 0xFF, (GATTS_SERVICE_UUID >> 8) & 0xFF}, - .flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT), - }; - esp_ble_gap_config_adv_data(&adv_data); - adv_config_done |= ADV_CONFIG_FLAG; - - // Create service - esp_gatt_srvc_id_t service_id = { - .is_primary = true, - .id.inst_id = 0, - .id.uuid.len = ESP_UUID_LEN_16, - .id.uuid.uuid.uuid16 = GATTS_SERVICE_UUID, - }; - esp_ble_gatts_create_service(gatts_if, &service_id, GATTS_NUM_HANDLE_TEST); - break; - - case ESP_GATTS_CREATE_EVT: - ESP_LOGI(TAG, "CREATE_SERVICE_EVT, status %d, service_handle %d", param->create.status, param->create.service_handle); - service_handle = param->create.service_handle; - - esp_ble_gatts_start_service(service_handle); - - // Add characteristics - esp_bt_uuid_t char_uuid; - char_uuid.len = ESP_UUID_LEN_16; - - // Config characteristic - char_uuid.uuid.uuid16 = GATTS_CHAR_UUID_CONFIG; - esp_ble_gatts_add_char(service_handle, &char_uuid, - ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, - ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_WRITE, - NULL, NULL); - - // Mode characteristic - char_uuid.uuid.uuid16 = GATTS_CHAR_UUID_MODE; - esp_ble_gatts_add_char(service_handle, &char_uuid, - ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, - ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_WRITE | ESP_GATT_CHAR_PROP_BIT_NOTIFY, - NULL, NULL); - - // PWM emulation characteristic - char_uuid.uuid.uuid16 = GATTS_CHAR_UUID_PWM; - esp_ble_gatts_add_char(service_handle, &char_uuid, - ESP_GATT_PERM_WRITE, - ESP_GATT_CHAR_PROP_BIT_WRITE, - NULL, NULL); - - // OTA characteristic - char_uuid.uuid.uuid16 = GATTS_CHAR_UUID_OTA; - esp_ble_gatts_add_char(service_handle, &char_uuid, - ESP_GATT_PERM_WRITE, - ESP_GATT_CHAR_PROP_BIT_WRITE, - NULL, NULL); - break; - - case ESP_GATTS_ADD_CHAR_EVT: - ESP_LOGI(TAG, "ADD_CHAR_EVT, status %d, char_handle %d", param->add_char.status, param->add_char.attr_handle); - - // Store handles - if (param->add_char.char_uuid.uuid.uuid16 == GATTS_CHAR_UUID_CONFIG) - { - char_handles.config_handle = param->add_char.attr_handle; - } - else if (param->add_char.char_uuid.uuid.uuid16 == GATTS_CHAR_UUID_MODE) - { - char_handles.mode_handle = param->add_char.attr_handle; - } - else if (param->add_char.char_uuid.uuid.uuid16 == GATTS_CHAR_UUID_PWM) - { - char_handles.pwm_handle = param->add_char.attr_handle; - } - else if (param->add_char.char_uuid.uuid.uuid16 == GATTS_CHAR_UUID_OTA) - { - char_handles.ota_handle = param->add_char.attr_handle; - } - break; - - case ESP_GATTS_CONNECT_EVT: - ESP_LOGI(TAG, "BLE device connected"); - conn_id_global = param->connect.conn_id; - ble_connected = true; - - // Stop timeout timer when connected - if (ble_timeout_timer) - { - esp_timer_stop(ble_timeout_timer); - } - break; - - case ESP_GATTS_DISCONNECT_EVT: - ESP_LOGI(TAG, "BLE device disconnected"); - ble_connected = false; - - // Restart advertising and timeout - if (ble_enabled) - { - esp_ble_gap_start_advertising(&adv_params); - start_ble_timeout(); - } - break; - - case ESP_GATTS_READ_EVT: - ESP_LOGI(TAG, "GATTS_READ_EVT, handle %d", param->read.handle); - - esp_gatt_rsp_t rsp; - memset(&rsp, 0, sizeof(esp_gatt_rsp_t)); - rsp.attr_value.handle = param->read.handle; - - if (param->read.handle == char_handles.config_handle) - { - rsp.attr_value.len = sizeof(controller_config_t); - memcpy(rsp.attr_value.value, ¤t_config, sizeof(controller_config_t)); - } - else if (param->read.handle == char_handles.mode_handle) - { - rsp.attr_value.len = 1; - rsp.attr_value.value[0] = current_animation_mode; - } - - esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id, - ESP_GATT_OK, &rsp); - break; - - case ESP_GATTS_WRITE_EVT: - ESP_LOGI(TAG, "GATTS_WRITE_EVT, handle %d, len %d", param->write.handle, param->write.len); - - if (param->write.handle == char_handles.config_handle) - { - // Update configuration - if (param->write.len == sizeof(controller_config_t)) - { - controller_config_t new_config; - memcpy(&new_config, param->write.value, sizeof(controller_config_t)); - control_update_config(&new_config); - } - } - else if (param->write.handle == char_handles.mode_handle) - { - // Set animation mode - if (param->write.len == 1) - { - control_set_animation_mode(param->write.value[0]); - } - } - else if (param->write.handle == char_handles.pwm_handle) - { - // Emulate PWM pulse - control_emulate_pwm_pulse(); - } - else if (param->write.handle == char_handles.ota_handle) - { - // Handle OTA data - if (ota_bytes_written == 0) - { - // First packet - start OTA - ESP_LOGI(TAG, "Starting OTA update..."); - update_partition = esp_ota_get_next_update_partition(NULL); - if (update_partition == NULL) - { - ESP_LOGE(TAG, "No OTA partition found"); - break; - } - - esp_err_t err = esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle); - if (err != ESP_OK) - { - ESP_LOGE(TAG, "OTA begin failed: %s", esp_err_to_name(err)); - break; - } - } - - // Write OTA data - esp_err_t err = esp_ota_write(update_handle, param->write.value, param->write.len); - if (err != ESP_OK) - { - ESP_LOGE(TAG, "OTA write failed: %s", esp_err_to_name(err)); - esp_ota_abort(update_handle); - ota_bytes_written = 0; - break; - } - - ota_bytes_written += param->write.len; - ESP_LOGI(TAG, "OTA progress: %d bytes", ota_bytes_written); - - // Check if this is the last packet (indicated by packet size < MTU) - if (param->write.len < 512) - { - ESP_LOGI(TAG, "OTA complete, total bytes: %d", ota_bytes_written); - - err = esp_ota_end(update_handle); - if (err != ESP_OK) - { - ESP_LOGE(TAG, "OTA end failed: %s", esp_err_to_name(err)); - break; - } - - err = esp_ota_set_boot_partition(update_partition); - if (err != ESP_OK) - { - ESP_LOGE(TAG, "OTA set boot partition failed: %s", esp_err_to_name(err)); - break; - } - - // Reset configuration - control_reset_config(); - - ESP_LOGI(TAG, "OTA successful, restarting..."); - vTaskDelay(pdMS_TO_TICKS(1000)); - esp_restart(); - } - } - - if (!param->write.is_prep) - { - esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, - ESP_GATT_OK, NULL); - } - break; - - default: - break; - } -} - -// Initialize BLE -static esp_err_t init_ble(void) -{ - if (!ble_enabled) - { - ESP_LOGI(TAG, "BLE disabled by configuration"); - return ESP_OK; - } - - esp_err_t ret; - - // Initialize BT controller - esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); - ret = esp_bt_controller_init(&bt_cfg); - if (ret) - { - ESP_LOGE(TAG, "BT controller init failed: %s", esp_err_to_name(ret)); - return ret; - } - - ret = esp_bt_controller_enable(ESP_BT_MODE_BLE); - if (ret) - { - ESP_LOGE(TAG, "BT controller enable failed: %s", esp_err_to_name(ret)); - return ret; - } - - ret = esp_bluedroid_init(); - if (ret) - { - ESP_LOGE(TAG, "Bluedroid init failed: %s", esp_err_to_name(ret)); - return ret; - } - - ret = esp_bluedroid_enable(); - if (ret) - { - ESP_LOGE(TAG, "Bluedroid enable failed: %s", esp_err_to_name(ret)); - return ret; - } - - // Register callbacks - esp_ble_gatts_register_callback(gatts_event_handler); - esp_ble_gap_register_callback(gap_event_handler); - esp_ble_gatts_app_register(0); - - // Set MTU - esp_ble_gatt_set_local_mtu(517); - - // Start timeout timer - start_ble_timeout(); - - esp_ble_gatts_app_register(0); - - vTaskDelay(pdMS_TO_TICKS(100)); - esp_ble_gap_start_advertising(&adv_params); - - ESP_LOGI(TAG, "BLE initialized"); - - return ESP_OK; -} - // Main initialization esp_err_t control_init(void) { @@ -669,17 +207,18 @@ esp_err_t control_init(void) return ret; } - // Register mode change callback - rcsignal_register_callback(on_mode_change); - - // Initialize BLE - ret = init_ble(); + // Initialize local BTN + ret = localbtn_init(); if (ret != ESP_OK) { - ESP_LOGE(TAG, "BLE init failed: %s", esp_err_to_name(ret)); + ESP_LOGE(TAG, "Local BTN init failed: %s", esp_err_to_name(ret)); return ret; } + // Register mode change callback + rcsignal_register_callback(on_mode_change); + localbtn_register_callback(on_mode_change); + ESP_LOGI(TAG, "Control system initialized successfully"); return ESP_OK; diff --git a/main/control.h b/main/control.h index cc15cdf..bb4da93 100644 --- a/main/control.h +++ b/main/control.h @@ -1,6 +1,6 @@ /** * @file control.h - * @brief Control module for LED controller - handles initialization of LEDs, PWM, and Bluetooth + * @brief Control module for LED controller - handles initialization of LEDs, PWM */ #ifndef CONTROL_H @@ -10,24 +10,15 @@ #include #include -/** - * @brief BLE auto-off timeout options - */ -typedef enum { - BLE_TIMEOUT_NEVER = 0, - BLE_TIMEOUT_1MIN = 60, - BLE_TIMEOUT_5MIN = 300 -} ble_timeout_t; - /** * @brief Configuration structure stored in NVS */ -typedef struct { - int8_t led_pin_strip_a; // GPIO pin for LED strip A (-1 = not configured) - int8_t led_pin_strip_b; // GPIO pin for LED strip B (-1 = not configured) - int8_t pwm_pin; // GPIO pin for PWM input (-1 = not configured) - ble_timeout_t ble_timeout; // BLE auto-off timeout - uint32_t magic; // Magic number to validate config (0xDEADBEEF) +typedef struct +{ + int8_t led_pin_strip_a; // GPIO pin for LED strip A (-1 = not configured) + int8_t led_pin_strip_b; // GPIO pin for LED strip B (-1 = not configured) + int8_t pwm_pin; // GPIO pin for PWM input (-1 = not configured) + uint32_t magic; // Magic number to validate config (0xDEADBEEF) } controller_config_t; /** @@ -41,14 +32,14 @@ esp_err_t control_init(void); * @brief Get current configuration * @return Pointer to current configuration (read-only) */ -const controller_config_t* control_get_config(void); +const controller_config_t *control_get_config(void); /** * @brief Update configuration and save to NVS * @param config New configuration * @return ESP_OK on success */ -esp_err_t control_update_config(const controller_config_t* config); +esp_err_t control_update_config(const controller_config_t *config); /** * @brief Reset configuration to defaults @@ -56,22 +47,6 @@ esp_err_t control_update_config(const controller_config_t* config); */ esp_err_t control_reset_config(void); -/** - * @brief Get BLE enabled status - * @return true if BLE is enabled - */ -bool control_is_ble_enabled(void); - -/** - * @brief Manually disable BLE - */ -void control_disable_ble(void); - -/** - * @brief Emulate PWM pulse (for web button) - */ -void control_emulate_pwm_pulse(void); - /** * @brief Set animation mode manually * @param mode Animation mode (0-13) diff --git a/main/localbtn.c b/main/localbtn.c new file mode 100644 index 0000000..d0e0335 --- /dev/null +++ b/main/localbtn.c @@ -0,0 +1,85 @@ +/** + * @file localbtn.c + * @brief Local GPIO0 BTN reading implementation using edge capture + */ + +#include "localbtn.h" +#include "driver/gpio.h" +#include "esp_timer.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "soc/gpio_num.h" +#include + +static const char *TAG = "LOCALBTN"; +uint8_t current_mode; +localbtn_mode_change_callback_t callback; + +#define BOOT_BTN GPIO_NUM_0 +#define MAX_MODES 14 // TODO: get from control + +bool boot_button_pressed(void) +{ + return gpio_get_level(BOOT_BTN) == 0; // active LOW +} + +static void monitor_task(void *arg) +{ + bool lastState = false; + while (1) + { + vTaskDelay(pdMS_TO_TICKS(10)); + + bool currentState = boot_button_pressed(); + if ((currentState) && (lastState != currentState)) + { + printf("BOOT button pressed\n"); + + current_mode = (current_mode + 1) % MAX_MODES; + + ESP_LOGI(TAG, "Mode changed to: %d ", current_mode); + + if (callback) + { + callback(current_mode); + } + } + + lastState = currentState; + } +} + +esp_err_t localbtn_init() +{ + + gpio_config_t io_conf = { + .pin_bit_mask = 1ULL << BOOT_BTN, + .mode = GPIO_MODE_INPUT, + .pull_up_en = GPIO_PULLUP_ENABLE, // safe even if external pull-up exists + .pull_down_en = GPIO_PULLDOWN_DISABLE, + .intr_type = GPIO_INTR_DISABLE}; + ESP_ERROR_CHECK(gpio_config(&io_conf)); + + // Create monitor task + BaseType_t ret = xTaskCreate(monitor_task, "localbtn_monitor", 2048, NULL, 5, NULL); + if (ret != pdPASS) + { + return ESP_FAIL; + } + + // TODO: rcsignal.initialized = true; + ESP_LOGI(TAG, "local btn initialized on GPIO%d", BOOT_BTN); + + return ESP_OK; +} + +void localbtn_deinit(void) +{ + // TODO: +} + +void localbtn_register_callback(localbtn_mode_change_callback_t cb) +{ + callback = cb; +} diff --git a/main/localbtn.h b/main/localbtn.h new file mode 100644 index 0000000..c5227da --- /dev/null +++ b/main/localbtn.h @@ -0,0 +1,36 @@ +/** + * @file localbtn.h + * @brief Local GPIO0 BTN reading implementation using edge capture + */ + +#ifndef LOCALBTN_H +#define LOCALBTN_H + +#include +#include +#include "esp_err.h" + +/** + * @brief Callback function type for mode changes + * @param new_mode New animation mode (0-13) + */ +typedef void (*localbtn_mode_change_callback_t)(uint8_t new_mode); + +/** + * @brief Initialize local btn reading + * @return ESP_OK on success + */ +esp_err_t localbtn_init(); + +/** + * @brief Deinitialize local btn reading + */ +void localbtn_deinit(void); + +/** + * @brief Register callback for mode changes + * @param callback Callback function + */ +void localbtn_register_callback(localbtn_mode_change_callback_t cb); + +#endif // LOCALBTN_H diff --git a/main/main.c b/main/main.c index 0229b06..a546c85 100644 --- a/main/main.c +++ b/main/main.c @@ -44,7 +44,7 @@ void app_main(void) ESP_LOGI(TAG, " ESP32 LED Controller for Model Aircraft"); ESP_LOGI(TAG, "=============================================="); - // Initialize control system (LEDs, PWM, BLE) + // Initialize control system (LEDs, PWM) esp_err_t ret = control_init(); if (ret != ESP_OK) { @@ -56,6 +56,8 @@ void app_main(void) } } + control_reset_config(); + // Create animation update task BaseType_t task_ret = xTaskCreate( animation_task, @@ -76,8 +78,6 @@ void app_main(void) } ESP_LOGI(TAG, "System initialized successfully"); - ESP_LOGI(TAG, "BLE Device Name: LED-Controller"); - ESP_LOGI(TAG, "Connect via Web-BLE to configure"); // Main loop - just monitor system status while (1) @@ -85,10 +85,6 @@ void app_main(void) vTaskDelay(pdMS_TO_TICKS(5000)); // Periodic status logging - //const controller_config_t *config = control_get_config(); - ESP_LOGI(TAG, "Status - Mode: %d, BLE: %s, PWM Active: %s", - control_get_animation_mode(), - control_is_ble_enabled() ? "ON" : "OFF", - "N/A"); // Could add rcsignal_is_active() here + ESP_LOGI(TAG, "Status - Mode: %d", control_get_animation_mode()); } } diff --git a/nfc01.ino b/nfc01.ino deleted file mode 100644 index baf10c9..0000000 --- a/nfc01.ino +++ /dev/null @@ -1,85 +0,0 @@ -#include "FastLED.h" - -int rc01 = 9; -int rc02 = 10; -int led_spotlight = 2; -int rc01Val = 0; -int rc02Val = 0; -int modus = 0; -int modusMax = 13; -int red = 0; -int green = 0; -int blue = 0; -int randomVal = 0; - -#define DATA_PIN 3 -#define LED_TYPE WS2812B -#define COLOR_ORDER GRB -#define NUM_LEDS 44 -CRGB leds[NUM_LEDS]; -#define BRIGHTNESS 255 -#define FRAMES_PER_SECOND 60 -#define ARRAY_SIZE(A) (sizeof(A) / sizeof((A)[0])) - -boolean pullRC = true; - -void setup() { - //Serial.begin(9600); - Serial.println("_-_-_- Night Fly Controller V01 _-_-_-"); - pinMode(rc01, INPUT); - pinMode(rc02, INPUT); - pinMode(led_spotlight, OUTPUT); - pinMode(LED_BUILTIN, OUTPUT); - digitalWrite(LED_BUILTIN, HIGH); - - delay(3000); // 3 second delay for recovery - - // tell FastLED about the LED strip configuration - FastLED.addLeds(leds, NUM_LEDS).setCorrection(TypicalLEDStrip); - // set master brightness control - FastLED.setBrightness(BRIGHTNESS); -} - - -// List of patterns to cycle through. Each is defined as a separate function below. -typedef void (*SimplePatternList[])(); -SimplePatternList gPatterns = { blackMode, redMode, blueMode, greenMode, whiteMode, rainbow, rainbowWithGlitter, confetti, sinelon, bpm, navigation, chase, chaseRGB, randomMode }; -uint8_t gHue = 0; // rotating "base color" used by many of the patterns - -void loop() { - - if (getRC01()) { - digitalWrite(led_spotlight, HIGH); - - } else { - digitalWrite(led_spotlight, LOW); - } - -setModus(); - -} - -void setModus(){ - if (getRC02()) { - modus = modus + 1; - if (modus > modusMax) { - modus = 1; - } - } - Serial.println(modus); - serialPrintModus(modus); - - gPatterns[modus](); - - // send the 'leds' array out to the actual LED strip - FastLED.show(); - // insert a delay to keep the framerate modest - FastLED.delay(1000 / FRAMES_PER_SECOND); - - // do some periodic updates - EVERY_N_MILLISECONDS( 20 ) { - gHue++; // slowly cycle the "base color" through the rainbow - } - -} - diff --git a/tools/dev_https_server.py b/tools/dev_https_server.py deleted file mode 100644 index 2ac67bc..0000000 --- a/tools/dev_https_server.py +++ /dev/null @@ -1,120 +0,0 @@ -#!/usr/bin/env python3 -""" -Ad-hoc Flask HTTPS Server for LED Controller Webapp -Serves the webapp over HTTPS (required for Web Bluetooth API) -""" - -import os -import sys -from flask import Flask, send_from_directory, send_file -from pathlib import Path - -# Configuration -HOST = '0.0.0.0' # Listen on all interfaces -PORT = 5000 # HTTPS port -DEBUG = True - -# Get webapp directory (one level up from tools/) -SCRIPT_DIR = Path(__file__).parent -WEBAPP_DIR = SCRIPT_DIR.parent / 'webapp' - -# Verify webapp directory exists -if not WEBAPP_DIR.exists(): - print(f"❌ ERROR: Webapp directory not found at {WEBAPP_DIR}") - print(f" Please run this script from the base repository directory") - sys.exit(1) - -# Create Flask app -app = Flask(__name__) - -@app.route('/') -def index(): - """Serve index.html""" - return send_file(WEBAPP_DIR / 'index.html') - -@app.route('/app/') -def serve_app(filename): - """Serve files from app/ directory""" - return send_from_directory(WEBAPP_DIR / 'app', filename) - -@app.route('/css/') -def serve_css(filename): - """Serve files from css/ directory""" - return send_from_directory(WEBAPP_DIR / 'css', filename) - -@app.route('/data/') -def serve_data(filename): - """Serve files from data/ directory""" - return send_from_directory(WEBAPP_DIR / 'data', filename) - -@app.route('/favicon.ico') -def favicon(): - """Serve favicon""" - return send_file(WEBAPP_DIR / 'data' / 'favicon.ico') - -def print_banner(): - """Print startup banner with instructions""" - print("=" * 70) - print(" 🚀 LED Controller HTTPS Development Server") - print("=" * 70) - print() - print("📱 Web Bluetooth requires HTTPS!") - print(" This server provides a self-signed certificate for development.") - print() - print("🌐 Access the webapp at:") - print(f" https://localhost:{PORT}") - print(f" https://127.0.0.1:{PORT}") - print(f" https://:{PORT}") - print() - print("⚠️ Browser Security Warning:") - print(" You'll see a 'Not Secure' warning - this is normal!") - print(" Click 'Advanced' → 'Proceed to localhost' (or similar)") - print() - print("🔧 To stop the server: Press Ctrl+C") - print("=" * 70) - print() - -def get_local_ip(): - """Get local IP address for convenience""" - import socket - try: - s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - s.connect(("8.8.8.8", 80)) - ip = s.getsockname()[0] - s.close() - return ip - except: - return "unknown" - -if __name__ == '__main__': - print_banner() - - # Print local IP for convenience - local_ip = get_local_ip() - if local_ip != "unknown": - print(f"💡 Your local IP: {local_ip}") - print(f" Access from phone: https://{local_ip}:{PORT}") - print() - - print("🔄 Starting HTTPS server...") - print() - - try: - # Run with ad-hoc SSL context (self-signed certificate) - # Flask will automatically generate a certificate - app.run( - host=HOST, - port=PORT, - debug=DEBUG, - ssl_context='adhoc' # Auto-generate self-signed cert - ) - except OSError as e: - if "Address already in use" in str(e): - print(f"❌ ERROR: Port {PORT} is already in use!") - print(f" Try a different port or stop the other service.") - sys.exit(1) - else: - raise - except KeyboardInterrupt: - print("\n\n👋 Server stopped by user") - sys.exit(0) diff --git a/tools/requirements.txt b/tools/requirements.txt deleted file mode 100644 index 8779ea6..0000000 --- a/tools/requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -# Flask HTTPS Development Server Requirements -# Install with: pip install -r requirements.txt - -Flask>=2.3.0 -pyOpenSSL>=23.0.0 # Required for adhoc SSL certificate generation diff --git a/webapp/app/app.js b/webapp/app/app.js deleted file mode 100644 index e2fb368..0000000 --- a/webapp/app/app.js +++ /dev/null @@ -1,322 +0,0 @@ -/** - * LED Controller Web-BLE Application - * Communicates with ESP32 LED Controller via Bluetooth Low Energy - */ - -// BLE Service and Characteristic UUIDs -const SERVICE_UUID = 0x00FF; -const CHAR_CONFIG_UUID = 0xFF01; -const CHAR_MODE_UUID = 0xFF02; -const CHAR_PWM_UUID = 0xFF03; -const CHAR_OTA_UUID = 0xFF04; - -// Animation mode names -const MODE_NAMES = [ - 'Black', 'Red', 'Blue', 'Green', 'White', - 'Rainbow', 'Rainbow Glitter', 'Confetti', 'Sinelon', - 'BPM Pulse', 'Navigation', 'Chase (Red)', 'Chase (RGB)', 'Random' -]; - -class LEDController { - constructor() { - this.device = null; - this.server = null; - this.service = null; - this.characteristics = { - config: null, - mode: null, - pwm: null, - ota: null - }; - this.connected = false; - - this.initUI(); - } - - initUI() { - // Connect button - document.getElementById('connect-btn').addEventListener('click', () => this.connect()); - - // Save configuration button - document.getElementById('save-config-btn').addEventListener('click', () => this.saveConfig()); - - // PWM emulation button - document.getElementById('pwm-emulate-btn').addEventListener('click', () => this.emulatePWM()); - - // Mode selection buttons - document.querySelectorAll('.btn-mode').forEach(btn => { - btn.addEventListener('click', (e) => { - const mode = parseInt(e.target.dataset.mode); - this.setMode(mode); - }); - }); - - // Firmware file selection - document.getElementById('firmware-file').addEventListener('change', (e) => { - const uploadBtn = document.getElementById('ota-upload-btn'); - uploadBtn.disabled = !e.target.files.length; - }); - - // OTA upload button - document.getElementById('ota-upload-btn').addEventListener('click', () => this.uploadFirmware()); - } - - async connect() { - try { - console.log('Requesting Bluetooth Device...'); - - this.device = await navigator.bluetooth.requestDevice({ - filters: [{ name: 'LED-Controller' }], - optionalServices: [SERVICE_UUID] - }); - - console.log('Connecting to GATT Server...'); - this.server = await this.device.gatt.connect(); - - console.log('Getting Service...'); - this.service = await this.server.getPrimaryService(SERVICE_UUID); - - console.log('Getting Characteristics...'); - this.characteristics.config = await this.service.getCharacteristic(CHAR_CONFIG_UUID); - this.characteristics.mode = await this.service.getCharacteristic(CHAR_MODE_UUID); - this.characteristics.pwm = await this.service.getCharacteristic(CHAR_PWM_UUID); - this.characteristics.ota = await this.service.getCharacteristic(CHAR_OTA_UUID); - - this.connected = true; - this.updateConnectionStatus(true); - - // Load current configuration - await this.loadConfig(); - await this.loadCurrentMode(); - - // Show control sections - document.getElementById('config-section').classList.remove('hidden'); - document.getElementById('control-section').classList.remove('hidden'); - document.getElementById('ota-section').classList.remove('hidden'); - - console.log('Connected successfully!'); - - // Handle disconnection - this.device.addEventListener('gattserverdisconnected', () => { - this.onDisconnected(); - }); - - } catch (error) { - console.error('Connection failed:', error); - alert('Failed to connect: ' + error.message); - } - } - - async loadConfig() { - try { - const value = await this.characteristics.config.readValue(); - const config = this.parseConfig(value); - - document.getElementById('led-pin-a').value = config.ledPinA; - document.getElementById('led-pin-b').value = config.ledPinB; - document.getElementById('pwm-pin').value = config.pwmPin; - document.getElementById('ble-timeout').value = config.bleTimeout; - - console.log('Configuration loaded:', config); - } catch (error) { - console.error('Failed to load config:', error); - } - } - - async loadCurrentMode() { - try { - const value = await this.characteristics.mode.readValue(); - const mode = value.getUint8(0); - this.updateCurrentMode(mode); - } catch (error) { - console.error('Failed to load current mode:', error); - } - } - - parseConfig(dataView) { - return { - ledPinA: dataView.getInt8(0), - ledPinB: dataView.getInt8(1), - pwmPin: dataView.getInt8(2), - bleTimeout: dataView.getUint32(3, true), - magic: dataView.getUint32(7, true) - }; - } - - createConfigBuffer(config) { - const buffer = new ArrayBuffer(11); - const view = new DataView(buffer); - - view.setInt8(0, config.ledPinA); - view.setInt8(1, config.ledPinB); - view.setInt8(2, config.pwmPin); - view.setUint32(3, config.bleTimeout, true); - view.setUint32(7, 0xDEADBEEF, true); // Magic number - - return buffer; - } - - async saveConfig() { - try { - const config = { - ledPinA: parseInt(document.getElementById('led-pin-a').value), - ledPinB: parseInt(document.getElementById('led-pin-b').value), - pwmPin: parseInt(document.getElementById('pwm-pin').value), - bleTimeout: parseInt(document.getElementById('ble-timeout').value) - }; - - const buffer = this.createConfigBuffer(config); - await this.characteristics.config.writeValue(buffer); - - alert('Configuration saved! Device will restart if pins changed.'); - console.log('Configuration saved:', config); - } catch (error) { - console.error('Failed to save config:', error); - alert('Failed to save configuration: ' + error.message); - } - } - - async setMode(mode) { - try { - const buffer = new Uint8Array([mode]); - await this.characteristics.mode.writeValue(buffer); - this.updateCurrentMode(mode); - console.log('Mode set to:', MODE_NAMES[mode]); - } catch (error) { - console.error('Failed to set mode:', error); - } - } - - async emulatePWM() { - try { - const buffer = new Uint8Array([1]); - await this.characteristics.pwm.writeValue(buffer); - console.log('PWM pulse emulated'); - - // Read back the new mode - setTimeout(() => this.loadCurrentMode(), 100); - } catch (error) { - console.error('Failed to emulate PWM:', error); - } - } - - async uploadFirmware() { - const fileInput = document.getElementById('firmware-file'); - const file = fileInput.files[0]; - - if (!file) { - alert('Please select a firmware file'); - return; - } - - if (!confirm('This will update the firmware and reset all settings. Continue?')) { - return; - } - - try { - const arrayBuffer = await file.arrayBuffer(); - const data = new Uint8Array(arrayBuffer); - - const progressBar = document.getElementById('ota-progress-bar'); - const progressText = document.getElementById('ota-progress-text'); - const progressContainer = document.getElementById('ota-progress'); - - progressContainer.classList.remove('hidden'); - - const chunkSize = 512; - const totalChunks = Math.ceil(data.length / chunkSize); - - console.log(`Starting OTA upload: ${data.length} bytes, ${totalChunks} chunks`); - - for (let i = 0; i < totalChunks; i++) { - const start = i * chunkSize; - const end = Math.min(start + chunkSize, data.length); - const chunk = data.slice(start, end); - - await this.characteristics.ota.writeValue(chunk); - - const progress = Math.round((i + 1) / totalChunks * 100); - progressBar.style.width = progress + '%'; - progressText.textContent = progress + '%'; - - console.log(`Upload progress: ${progress}%`); - - // Small delay to prevent overwhelming the device - await new Promise(resolve => setTimeout(resolve, 20)); - } - - alert('Firmware uploaded successfully! Device will restart.'); - console.log('OTA upload complete'); - - } catch (error) { - console.error('OTA upload failed:', error); - alert('Firmware upload failed: ' + error.message); - } - } - - updateCurrentMode(mode) { - const modeName = MODE_NAMES[mode] || 'Unknown'; - document.getElementById('current-mode').textContent = modeName; - } - - updateConnectionStatus(connected) { - const indicator = document.getElementById('status-indicator'); - const statusText = document.getElementById('connection-status'); - const connectBtn = document.getElementById('connect-btn'); - - if (connected) { - indicator.classList.remove('disconnected'); - indicator.classList.add('connected'); - statusText.textContent = 'Connected'; - connectBtn.textContent = 'Disconnect'; - connectBtn.onclick = () => this.disconnect(); - } else { - indicator.classList.remove('connected'); - indicator.classList.add('disconnected'); - statusText.textContent = 'Disconnected'; - connectBtn.textContent = 'Connect via BLE'; - connectBtn.onclick = () => this.connect(); - } - } - - disconnect() { - if (this.device && this.device.gatt.connected) { - this.device.gatt.disconnect(); - } - this.onDisconnected(); - } - - onDisconnected() { - console.log('Disconnected from device'); - this.connected = false; - this.device = null; - this.server = null; - this.service = null; - this.characteristics = { - config: null, - mode: null, - pwm: null, - ota: null - }; - - this.updateConnectionStatus(false); - - // Hide control sections - document.getElementById('config-section').classList.add('hidden'); - document.getElementById('control-section').classList.add('hidden'); - document.getElementById('ota-section').classList.add('hidden'); - } -} - -// Initialize app when DOM is loaded -document.addEventListener('DOMContentLoaded', () => { - // Check if Web Bluetooth is supported - if (!navigator.bluetooth) { - alert('Web Bluetooth is not supported in this browser. Please use Chrome, Edge, or Opera.'); - document.getElementById('connect-btn').disabled = true; - return; - } - - console.log('LED Controller Web-BLE Interface loaded'); - new LEDController(); -}); diff --git a/webapp/css/style.css b/webapp/css/style.css deleted file mode 100644 index e3142e7..0000000 --- a/webapp/css/style.css +++ /dev/null @@ -1,313 +0,0 @@ -/* LED Controller Web-BLE Interface Styles */ - -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -:root { - --primary-color: #2196F3; - --success-color: #4CAF50; - --warning-color: #FF9800; - --danger-color: #F44336; - --dark-bg: #1a1a2e; - --card-bg: #16213e; - --text-primary: #ffffff; - --text-secondary: #b0b0b0; - --border-color: #0f4c75; - --hover-bg: #1e3a5f; -} - -body { - font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; - background: linear-gradient(135deg, var(--dark-bg) 0%, #0f3460 100%); - color: var(--text-primary); - min-height: 100vh; - padding: 20px; -} - -.container { - max-width: 800px; - margin: 0 auto; -} - -header { - text-align: center; - margin-bottom: 30px; -} - -header h1 { - font-size: 2.5em; - margin-bottom: 10px; - text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5); -} - -.subtitle { - color: var(--text-secondary); - font-size: 1.1em; -} - -.card { - background: var(--card-bg); - border-radius: 12px; - padding: 25px; - margin-bottom: 20px; - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3); - border: 1px solid var(--border-color); -} - -.card h2 { - margin-bottom: 20px; - color: var(--primary-color); - font-size: 1.5em; -} - -.hidden { - display: none !important; -} - -/* Connection Status */ -.connection-status { - display: flex; - align-items: center; - gap: 10px; - margin-bottom: 15px; - font-size: 1.1em; -} - -.status-indicator { - width: 16px; - height: 16px; - border-radius: 50%; - animation: pulse 2s infinite; -} - -.status-indicator.disconnected { - background: var(--danger-color); -} - -.status-indicator.connected { - background: var(--success-color); -} - -@keyframes pulse { - 0%, 100% { opacity: 1; } - 50% { opacity: 0.5; } -} - -/* Form Elements */ -.form-group { - margin-bottom: 20px; -} - -.form-group label { - display: block; - margin-bottom: 8px; - font-weight: 500; - color: var(--text-primary); -} - -.form-group input[type="number"], -.form-group select { - width: 100%; - padding: 12px; - background: var(--dark-bg); - border: 1px solid var(--border-color); - border-radius: 6px; - color: var(--text-primary); - font-size: 1em; -} - -.form-group input[type="number"]:focus, -.form-group select:focus { - outline: none; - border-color: var(--primary-color); -} - -.form-group small { - display: block; - margin-top: 5px; - color: var(--text-secondary); - font-size: 0.9em; -} - -.form-group input[type="file"] { - width: 100%; - padding: 10px; - background: var(--dark-bg); - border: 2px dashed var(--border-color); - border-radius: 6px; - color: var(--text-primary); - cursor: pointer; -} - -/* Buttons */ -.btn { - padding: 12px 24px; - border: none; - border-radius: 6px; - font-size: 1em; - font-weight: 600; - cursor: pointer; - transition: all 0.3s ease; - width: 100%; -} - -.btn:hover { - transform: translateY(-2px); - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); -} - -.btn:active { - transform: translateY(0); -} - -.btn:disabled { - opacity: 0.5; - cursor: not-allowed; -} - -.btn-primary { - background: var(--primary-color); - color: white; -} - -.btn-success { - background: var(--success-color); - color: white; -} - -.btn-warning { - background: var(--warning-color); - color: white; -} - -.btn-secondary { - background: var(--border-color); - color: white; -} - -.btn-mode { - background: var(--hover-bg); - color: var(--text-primary); - border: 1px solid var(--border-color); -} - -.btn-mode:hover { - background: var(--primary-color); -} - -/* Button Grid */ -.button-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); - gap: 10px; - margin-bottom: 20px; -} - -/* Current Mode Display */ -.current-mode { - background: var(--dark-bg); - padding: 15px; - border-radius: 6px; - margin-bottom: 20px; - text-align: center; - font-size: 1.1em; - border: 1px solid var(--border-color); -} - -.current-mode span { - color: var(--primary-color); - font-weight: bold; -} - -/* Progress Bar */ -.progress-container { - width: 100%; - height: 40px; - background: var(--dark-bg); - border-radius: 20px; - overflow: hidden; - margin: 20px 0; - position: relative; - border: 1px solid var(--border-color); -} - -.progress-bar { - height: 100%; - background: linear-gradient(90deg, var(--primary-color), var(--success-color)); - transition: width 0.3s ease; - width: 0%; -} - -.progress-text { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - font-weight: bold; - text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5); -} - -/* Warning Box */ -.warning { - background: rgba(255, 152, 0, 0.1); - border: 1px solid var(--warning-color); - border-radius: 6px; - padding: 15px; - margin-bottom: 20px; - color: var(--warning-color); - font-weight: 500; -} - -/* Divider */ -.divider { - height: 1px; - background: var(--border-color); - margin: 20px 0; -} - -/* Footer */ -footer { - text-align: center; - margin-top: 40px; - padding: 20px; - color: var(--text-secondary); -} - -footer small { - font-size: 0.9em; -} - -/* Responsive Design */ -@media (max-width: 600px) { - header h1 { - font-size: 2em; - } - - .button-grid { - grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); - } - - .btn { - font-size: 0.9em; - padding: 10px 16px; - } -} - -/* Animations */ -@keyframes fadeIn { - from { - opacity: 0; - transform: translateY(20px); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -.card { - animation: fadeIn 0.5s ease; -} diff --git a/webapp/data/favicon.ico b/webapp/data/favicon.ico deleted file mode 100644 index e69de29..0000000 diff --git a/webapp/index.html b/webapp/index.html deleted file mode 100644 index 1edec70..0000000 --- a/webapp/index.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - - LED Controller Config - - - - -
-
-

✈️ LED Controller

-

Model Aircraft LED Configuration via Web-BLE

-
- -
-

🔗 Connection

-
- - Disconnected -
- -
- - - - - - - -
-

ESP32 LED Controller © 2026

-

Supports ESP32 DevKitC & ESP32-C3 MINI

-
-
- - - -