From e5c75fd70e4a6b61e2bf956348e8caa7c292c567 Mon Sep 17 00:00:00 2001 From: "RZ_MINIX\\rober" Date: Mon, 21 Jul 2025 08:46:13 -0700 Subject: [PATCH] Added support for 4 LED and progress of OTA in LEDS --- main/App.cpp | 40 +++++++++++-- main/CommandProcessor.cpp | 2 +- main/Led.cpp | 47 ++++++++++++++- main/Led.h | 5 ++ main/MqttService.cpp | 18 +++--- main/Ota.cpp | 67 ++++++++++++++++++--- main/Ota.h | 5 +- main/SensorData.h | 4 ++ main/sensors/SensorService.cpp | 105 ++++++++++++++++++++++++++++----- main/sensors/SensorService.h | 1 + make.sh | 2 +- 11 files changed, 255 insertions(+), 41 deletions(-) diff --git a/main/App.cpp b/main/App.cpp index da18c78..3f0861a 100644 --- a/main/App.cpp +++ b/main/App.cpp @@ -85,11 +85,11 @@ void App::init() if(getLocalTime(&timeinfo)) { - ESP_LOGI(TAG, "ok"); + ESP_LOGI(TAG, "pool.ntp.org ok"); break; } else - ESP_LOGE(TAG, "Failed"); + ESP_LOGE(TAG, "pool.ntp.org Failed"); } needs_provision = false; @@ -106,6 +106,22 @@ void App::init() if(wifi_status == Wifi::WIFI_STATUS::CONNECTED) { ESP_LOGI(TAG, "Connected via fallback network"); + ESP_LOGI(TAG, "Getting local time..."); + struct tm timeinfo; + + int tries = 5; + while (tries--) + { + configTime(0, 0, "pool.ntp.org"); // needed? + + if(getLocalTime(&timeinfo)) + { + ESP_LOGI(TAG, "pool.ntp.org ok"); + break; + } + else + ESP_LOGE(TAG, "pool.ntp.org Failed"); + } needs_provision = false; } else @@ -126,6 +142,22 @@ void App::init() if(wifi_status == Wifi::WIFI_STATUS::CONNECTED) { ESP_LOGI(TAG, "Connected to fallback network"); + ESP_LOGI(TAG, "Getting local time..."); + struct tm timeinfo; + + int tries = 5; + while (tries--) + { + configTime(0, 0, "pool.ntp.org"); // needed? + + if(getLocalTime(&timeinfo)) + { + ESP_LOGI(TAG, "pool.ntp.org ok"); + break; + } + else + ESP_LOGE(TAG, "pool.ntp.org Failed"); + } needs_provision = false; } else @@ -141,10 +173,10 @@ void App::init() provision.start(); } - //otaCheck(); + otaCheck(); // m_led->setPulse(255, 0, 255); - m_led->setColor(0, 0, 0); + m_led->allOff(); m_mqtt_service = new MqttService(*this); m_mqtt_service->start(); diff --git a/main/CommandProcessor.cpp b/main/CommandProcessor.cpp index 64fec78..3331c51 100644 --- a/main/CommandProcessor.cpp +++ b/main/CommandProcessor.cpp @@ -189,7 +189,7 @@ bool CommandProcessor::cmdReportAllSettings(Parser & p, char * buffer) { int64_t up_time = esp_timer_get_time(); uint8_t wifi_mac[8]; - + ESP_LOGI(TAG, "cmdReportAllSettings:"); //added mac address esp_read_mac(wifi_mac, ESP_MAC_WIFI_STA); m_rw.reset(); diff --git a/main/Led.cpp b/main/Led.cpp index 3731c8a..d30169b 100644 --- a/main/Led.cpp +++ b/main/Led.cpp @@ -53,6 +53,39 @@ void Led::steady(uint8_t r, uint8_t g, uint8_t b, uint8_t d) } } +void Led::setLedColor(uint8_t led_index, uint8_t r, uint8_t g, uint8_t b) +{ + if (led_index < m_strip->numPixels()) { + m_strip->setPixelColor(led_index, scale(r), scale(g), scale(b)); + m_strip->show(); + } +} + +void Led::suspendTask() +{ + if (m_task != nullptr) { + vTaskSuspend(m_task); + ESP_LOGI("LED", "LED task suspended for OTA"); + } +} + +void Led::resumeTask() +{ + if (m_task != nullptr) { + vTaskResume(m_task); + ESP_LOGI("LED", "LED task resumed"); + } +} + +void Led::setDirectColor(uint8_t led_index, uint8_t r, uint8_t g, uint8_t b) +{ + // Direct LED control without going through the task queue + if (led_index < m_strip->numPixels()) { + m_strip->setPixelColor(led_index, scale(r), scale(g), scale(b)); + m_strip->show(); + } +} + void Led::run() { struct LED_QUEUE msg; @@ -99,10 +132,12 @@ Led::Led(uint32_t pin) : m_queue(5) { ESP_LOGW("led", "Starting led..."); - m_strip = new Adafruit_NeoPixel(1, pin, NEO_GRB + NEO_KHZ800); + m_strip = new Adafruit_NeoPixel(4, pin, NEO_GRB + NEO_KHZ800); m_strip->begin(); - m_strip->setPixelColor(0, 0, 0, 0); + for(int i = 0; i < 4; i++) { + m_strip->setPixelColor(i, 0, 0, 0); + } m_strip->show(); m_brightness = SETTINGS.led.brightness; @@ -139,6 +174,14 @@ void Led::setPulse(uint8_t r, uint8_t g, uint8_t b) m_queue.send(&msg, 1000); } +void Led::allOff() +{ + for(int i = 0; i < 4; i++) { + m_strip->setPixelColor(i, 0, 0, 0); + } + m_strip->show(); +} + void Led::setBrightness(uint8_t brightness) { m_brightness = brightness; diff --git a/main/Led.h b/main/Led.h index 93f6b4a..53dcac6 100644 --- a/main/Led.h +++ b/main/Led.h @@ -48,6 +48,11 @@ public: void setBrightness(uint8_t brightness); void setColor(uint8_t r, uint8_t g, uint8_t b); void setPulse(uint8_t r, uint8_t g, uint8_t b); + void setLedColor(uint8_t led_index, uint8_t r, uint8_t g, uint8_t b); + void suspendTask(); + void resumeTask(); + void allOff(); + void setDirectColor(uint8_t led_index, uint8_t r, uint8_t g, uint8_t b); }; #endif /* __LED_H__ */ \ No newline at end of file diff --git a/main/MqttService.cpp b/main/MqttService.cpp index 7f199f5..1aa721e 100644 --- a/main/MqttService.cpp +++ b/main/MqttService.cpp @@ -24,9 +24,9 @@ void MqttService::callback(char* topic, uint8_t * payload, uint32_t length) ESP_LOGW(TAG, "%s", (char*)payload); char * buffer = m_app_if.getCommandProcessor()->process((char *)payload, strlen((char *)payload)); // reply to - ESP_LOGI(TAG, "%s", buffer); + m_mqtt_client->publish("/well_hub", buffer, strlen(buffer)); - + ESP_LOGW(TAG, "Published to /well_hub: %s", buffer); m_app_if.getCommandProcessor()->give(); } @@ -55,26 +55,30 @@ void MqttService::task() while (!m_mqtt_client->connected()) { sprintf(top, "wh_%02X%02X%02X%02X%02X%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + + if(strcmp(SETTINGS.mqtt.device_id, "0") == 0) { + strcpy(SETTINGS.mqtt.device_id, top); + } ESP_LOGI(TAG, "connecting to mqtt broker, dev id '%s'...", SETTINGS.mqtt.device_id); //if (m_mqtt_client->connect(top, SETTINGS.mqtt.device_id, NULL)) - if (m_mqtt_client->connect(top, "well_user", "We3l1_best!")) + if (m_mqtt_client->connect(SETTINGS.mqtt.device_id, "well_user", "We3l1_best!")) { try_connect_count = 0; ESP_LOGI(TAG, "connected"); sprintf(top, "/%02X%02X%02X%02X%02X%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); - ESP_LOGI(TAG, "Subscribing to %s", top); + ESP_LOGW(TAG, "Subscribing to %s", top); if(m_mqtt_client->subscribe(top)) - ESP_LOGI(TAG, "subscribed"); + ESP_LOGW(TAG, "subscribed"); else ESP_LOGE(TAG, "subscribe failed"); sprintf(top, "/%08x", SETTINGS.device.group_id); - ESP_LOGI(TAG, "Subscribing to %s", top); + ESP_LOGW(TAG, "Subscribing to %s", top); if(m_mqtt_client->subscribe(top)) - ESP_LOGI(TAG, "subscribed"); + ESP_LOGW(TAG, "subscribed"); else ESP_LOGE(TAG, "subscribe failed"); } diff --git a/main/Ota.cpp b/main/Ota.cpp index 325d789..e28408c 100644 --- a/main/Ota.cpp +++ b/main/Ota.cpp @@ -22,7 +22,7 @@ #include -#define OTA_URL "http://wellnua.com/FW/wellhub.enc.bin" +//#define OTA_URL "http://wellnua.com/FW/wellhub.enc.bin" extern const uint8_t server_cert[] asm("_binary_bigfoot_inc_pem_start"); static const char *TAG = "OTA"; @@ -34,6 +34,48 @@ Ota::Ota(AppIF & app) : m_app(app) } +/// @brief Display download progress on 4 LEDs using binary representation +/// @param progress_percent Progress from 0-100 +void Ota::displayProgress(int progress_percent) +{ + // Clamp progress to 0-100 range + if (progress_percent < 0) progress_percent = 0; + if (progress_percent > 100) progress_percent = 100; + + // Color definitions (R, G, B) + struct Color { + uint8_t r, g, b; + }; + + const Color colors[4] = { + {255, 0, 0}, // 00 = Red + {0, 255, 0}, // 01 = Green + {0, 0, 255}, // 10 = Blue + {255, 255, 0} // 11 = Yellow + }; + + // Convert progress to 8-bit binary (we only need 0-100, but using 8 bits for full range) + uint8_t binary_progress = (uint8_t)progress_percent; + + // Extract 2-bit values for each LED (D, C, B, A from left to right) + // LED positions: D=3, C=2, B=1, A=0 + uint8_t led_values[4]; + led_values[0] = (binary_progress >> 0) & 0x03; // LED A (rightmost) - bits 1:0 + led_values[1] = (binary_progress >> 2) & 0x03; // LED B - bits 3:2 + led_values[2] = (binary_progress >> 4) & 0x03; // LED C - bits 5:4 + led_values[3] = (binary_progress >> 6) & 0x03; // LED D (leftmost) - bits 7:6 + + // Set each LED color based on its 2-bit value + for (int i = 0; i < 4; i++) { + const Color& color = colors[led_values[i]]; + + // Assuming you have access to LED strip with 4 LEDs + // You may need to modify this based on your actual LED setup + m_app.getLed()->setDirectColor(i, color.r, color.g, color.b); + } +} + + void Ota::taskFatalError() { ESP_LOGE(TAG, "Exiting task due to fatal error..."); @@ -51,13 +93,14 @@ void Ota::start() /* update handle : set by esp_ota_begin(), must be freed via esp_ota_end() */ esp_ota_handle_t update_handle = 0; const esp_partition_t *update_partition = NULL; + String otaUrl = "http://wellnua.com/FW/group/" + String(SETTINGS.device.group_id) + "/wellhub.enc.bin"; - ESP_LOGW(TAG, "Starting OTA check '%s'...", OTA_URL); + + ESP_LOGW(TAG, "Starting OTA check '%s'...", otaUrl.c_str()); HTTPClient client; - //client.begin(OTA_URL, (const char *)server_cert); - client.begin(OTA_URL); + client.begin(otaUrl); int http_code = client.GET(); if(http_code != HTTP_CODE_OK) @@ -104,10 +147,12 @@ void Ota::start() if (data_read != FW_HEADER) { ESP_LOGE(TAG, "Error: SSL data read error"); + client.end(); return; } esp_aes_context m_dec_ctx; + uint8_t m_dec_iv[16]; memset(m_dec_iv, 0, sizeof(m_dec_iv)); @@ -124,6 +169,7 @@ void Ota::start() return; } + int left = remote_len-FW_HEADER; // encryption header while (1) @@ -138,6 +184,7 @@ void Ota::start() if (data_read < 0) { ESP_LOGE(TAG, "Error: SSL data read error"); + client.end(); return; } else if (data_read > 0) @@ -190,8 +237,7 @@ void Ota::start() invalid_app_info.version); ESP_LOGW(TAG, "The firmware has been rolled back to the previous version."); - client.end(); - + client.end(); return; } } @@ -204,8 +250,9 @@ void Ota::start() return; } - m_app.getLed()->setPulse(255, 255, 0); - + //m_app.getLed()->setPulse(255, 255, 0); + m_app.getLed()->suspendTask(); + displayProgress(0); image_header_was_checked = true; ESP_LOGI(TAG, "Starting..."); @@ -221,6 +268,8 @@ void Ota::start() else { ESP_LOGE(TAG, "received package does not fit len"); + client.end(); + m_app.getLed()->resumeTask(); return; } } @@ -241,6 +290,8 @@ void Ota::start() { last_reported = progress; ESP_LOGI(TAG, "%d%%", last_reported); + // Display progress on LEDs + displayProgress(last_reported); delay(1); } } diff --git a/main/Ota.h b/main/Ota.h index 5be9c8a..d5a21a5 100644 --- a/main/Ota.h +++ b/main/Ota.h @@ -19,11 +19,10 @@ class Ota { public: void start(); - + void displayProgress(int progress_percent); private: char otaWriteData[BUFFSIZE + 1] = { 0 }; - AppIF & m_app; - + AppIF & m_app; private: void taskFatalError(); diff --git a/main/SensorData.h b/main/SensorData.h index 01c3c98..c355316 100644 --- a/main/SensorData.h +++ b/main/SensorData.h @@ -80,6 +80,10 @@ struct MESSAGE_NOTIFY_LIGHT // 21 bytes #define MQTT_MESSAGE_NOTIFY_LIGHT(msgaddr_) \ createHeader(&(msgaddr_)->header); \ (msgaddr_)->id = MESSAGE_TYPE_NOTIFY_LIGHT; + +#define MQTT_MESSAGE_NOTIFY_PRESSURE(msgaddr_) \ + createHeader(&(msgaddr_)->header); \ + (msgaddr_)->id = MESSAGE_TYPE_NOTIFY_PRESSURE; #define MESSAGE_TYPE_NOTIFY_PRESSURE 0x01 diff --git a/main/sensors/SensorService.cpp b/main/sensors/SensorService.cpp index 0aa6db3..10a0ed0 100644 --- a/main/sensors/SensorService.cpp +++ b/main/sensors/SensorService.cpp @@ -67,23 +67,95 @@ void SensorService::start() pinMode(LIGHT_SENSOR_PIN, INPUT); } -void::SensorService::processPressure(float pressure) + // Kalman filter variables + static double kp_q = 0.5; // process noise + static double kp_r = 32; // sensor noise + static double kp_p = 1023; // estimation error + static double kp_x = 0; // initial value + + // Pressure monitoring variables + static uint64_t pressure_period_started = 0; + static float pressure_filtered_min = 0; + static float pressure_filtered_max = 0; + static const uint64_t pressure_window_ms = 1000; // 1000ms = 1 second + +double getFilteredValue(double m) { - static float filtered = 0; - static uint64_t previous = esp_timer_get_time(); - if(filtered == 0 && pressure != 0) - filtered = pressure; - - uint64_t now = esp_timer_get_time(); - -// ESP_LOGI(TAG, "delta T: %d", (uint32_t)((now-previous)/1000)); - - filtered = 0.95 * filtered + 0.05 * pressure; - previous = now; -// ESP_LOGI(TAG, "%0.3f \t%0.3f", pressure, filtered); + if(kp_x == 0) kp_x=m; + double k_k; + kp_p = kp_p + kp_q; + k_k = kp_p / (kp_p + kp_r); + kp_x = kp_x + k_k * (m - kp_x); + kp_p = (1 - k_k) * kp_p; + return kp_x; } + + +void::SensorService::processPressure(float pressure) +{ + //static uint64_t previous = esp_timer_get_time(); + + // Get filtered pressure value using Kalman filter + double filtered = getFilteredValue((double)pressure); + double PRESSURE_THRESHOLD = 14.0; + uint64_t now = esp_timer_get_time(); + uint64_t now_ms = now / 1000; // Convert to milliseconds + + // Initialize pressure monitoring period + if (pressure_period_started == 0) { + pressure_period_started = now_ms; + pressure_filtered_min = filtered; + pressure_filtered_max = filtered; + } + else { + // Track min and max during the window + if (filtered > pressure_filtered_max) { + pressure_filtered_max = filtered; + //ESP_LOGE(TAG, "pressure_filtered_max: %0.3f", filtered); + } + + if (filtered < pressure_filtered_min) { + pressure_filtered_min = filtered; + //ESP_LOGE(TAG, "pressure_filtered_min: %0.3f", filtered); + } + + // Check if window period has elapsed + if (now_ms > (pressure_period_started + pressure_window_ms)) { + double change = 1000*abs(pressure_filtered_max - pressure_filtered_min); + ESP_LOGE(TAG, "@P: %0.3f", change); + + // Check for significant pressure change (door detection) + if (change > PRESSURE_THRESHOLD) { // Threshold of 1 + //ESP_LOGE(TAG, "Door detected - pressure change: %0.3f", change); + + // Send MQTT notification + struct MESSAGE_NOTIFY_PRESSURE msg; + MQTT_MESSAGE_NOTIFY_PRESSURE(&msg); + msg.value = change; // or you could send the change value + m_app_if.getBuffer()->putBlock((uint8_t*)&msg, sizeof(msg)); + //ESP_LOGE(TAG, "Door detected - pressure change: %0.3f, time: %u.%06u", change, msg.header.sec, msg.header.usec); + // Convert epoch time to human-readable format + time_t timestamp = msg.header.sec; + struct tm *timeinfo = localtime(×tamp); + + char time_str[64]; + strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", timeinfo); + ESP_LOGE(TAG, "Door detected - pressure change: %0.3f, time: %s.%06u", change, time_str, msg.header.usec); + } + + // Reset for next window + pressure_period_started = now_ms; + pressure_filtered_min = filtered; + pressure_filtered_max = filtered; + } + } + + //previous = now; + // Optional: store current filtered value for other uses + //m_pressure_value = filtered; +} void SensorService::postBme68xData(float pressure, float temp) { uint8_t msg_buffer[sizeof(struct MESSAGE_SENSORS_BLOCK) + 10*sizeof(struct GAS_DATA)]; @@ -132,12 +204,15 @@ void SensorService::processLight(int light_value) { if(last_light_val >= 0) { - if(abs(min_light_val - last_light_val) > 4096*5/100) + double change = abs(min_light_val - last_light_val); + ESP_LOGE(TAG, "@L: %0.3f", change); + + if(change > 4096*2/100.0) { ESP_LOGI(TAG, "light tripped"); struct MESSAGE_NOTIFY_LIGHT msg; MQTT_MESSAGE_NOTIFY_LIGHT(&msg); - msg.value = min_light_val; + msg.value = abs(min_light_val - last_light_val); m_app_if.getBuffer()->putBlock((uint8_t*)&msg, sizeof(msg)); } } diff --git a/main/sensors/SensorService.h b/main/sensors/SensorService.h index a7f55cf..ec0532d 100644 --- a/main/sensors/SensorService.h +++ b/main/sensors/SensorService.h @@ -34,6 +34,7 @@ protected: uint16_t m_light_value = 0; public: + SensorService(AppIF & app_if); void start(); }; diff --git a/make.sh b/make.sh index b74c7dc..75ca976 100644 --- a/make.sh +++ b/make.sh @@ -1,7 +1,7 @@ #!/bin/bash # first device connected - +idf.py clean #comment during development idf.py -DROLE=SENDER build ret_val=$?