Added support for 4 LED and progress of OTA in LEDS

This commit is contained in:
RZ_MINIX\rober 2025-07-21 08:46:13 -07:00
parent 92cf2e6f09
commit e5c75fd70e
11 changed files with 255 additions and 41 deletions

View File

@ -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();

View File

@ -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();

View File

@ -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;

View File

@ -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__ */

View File

@ -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));
m_mqtt_client->publish("/well_hub", buffer, strlen(buffer));
ESP_LOGW(TAG, "Published to /well_hub: %s", buffer);
m_app_if.getCommandProcessor()->give();
}
@ -56,25 +56,29 @@ void MqttService::task()
{
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");
}

View File

@ -22,7 +22,7 @@
#include <HTTPClient.h>
#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)
@ -191,7 +238,6 @@ void Ota::start()
ESP_LOGW(TAG, "The firmware has been rolled back to the previous version.");
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);
}
}

View File

@ -19,11 +19,10 @@ class Ota
{
public:
void start();
void displayProgress(int progress_percent);
private:
char otaWriteData[BUFFSIZE + 1] = { 0 };
AppIF & m_app;
private:
void taskFatalError();

View File

@ -81,6 +81,10 @@ struct MESSAGE_NOTIFY_LIGHT // 21 bytes
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
#define MESSAGE_TYPE_NOTIFY_TEMPERATURE 0x02

View File

@ -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(&timestamp);
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));
}
}

View File

@ -34,6 +34,7 @@ protected:
uint16_t m_light_value = 0;
public:
SensorService(AppIF & app_if);
void start();
};

View File

@ -1,7 +1,7 @@
#!/bin/bash
# first device connected
idf.py clean #comment during development
idf.py -DROLE=SENDER build
ret_val=$?