code enhancements
- add MQTT - add ESP32-C6 - fix simulation Signed-off-by: Peter Siegmund <developer@mars3142.org>
This commit is contained in:
+1
-1
@@ -1,2 +1,2 @@
|
||||
release:
|
||||
idf.py -B build-release -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.release" fullclean build
|
||||
idf.py -B build-release -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.release" -DIDF_TARGET=esp32c6 fullclean build size
|
||||
|
||||
@@ -15,5 +15,5 @@ idf_component_register(SRCS
|
||||
simulator
|
||||
persistence-manager
|
||||
message-manager
|
||||
simulator
|
||||
my_mqtt_client
|
||||
)
|
||||
|
||||
@@ -2,4 +2,4 @@ dependencies:
|
||||
idf:
|
||||
version: '>=5.0.0'
|
||||
espressif/mdns:
|
||||
version: '*'
|
||||
version: ^1.10.1
|
||||
|
||||
@@ -67,7 +67,7 @@ esp_err_t api_capabilities_get_handler(httpd_req_t *req)
|
||||
// Thread only available for esp32c6 or esp32h2
|
||||
bool thread = false;
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32H2)
|
||||
thread = true;
|
||||
thread = false;
|
||||
#endif
|
||||
cJSON *json = cJSON_CreateObject();
|
||||
cJSON_AddBoolToObject(json, "thread", thread);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <esp_http_server.h>
|
||||
#include <esp_log.h>
|
||||
#include <mdns.h>
|
||||
#include <sdkconfig.h>
|
||||
#include <string.h>
|
||||
|
||||
static const char *TAG = "api_server";
|
||||
@@ -55,7 +56,7 @@ static esp_err_t start_webserver(void)
|
||||
config.server_port = s_config.port;
|
||||
config.lru_purge_enable = true;
|
||||
config.max_uri_handlers = 32;
|
||||
config.max_open_sockets = 7;
|
||||
config.max_open_sockets = (CONFIG_LWIP_MAX_SOCKETS - 3);
|
||||
config.uri_match_fn = httpd_uri_match_wildcard;
|
||||
|
||||
ESP_LOGI(TAG, "Starting HTTP server on port %d", config.server_port);
|
||||
@@ -175,6 +176,7 @@ esp_err_t api_server_ws_broadcast_status(bool on, const char *mode, const char *
|
||||
"{\"type\":\"status\",\"on\":%s,\"mode\":\"%s\",\"schema\":\"%s\","
|
||||
"\"color\":{\"r\":%d,\"g\":%d,\"b\":%d}}",
|
||||
on ? "true" : "false", mode, schema, r, g, b);
|
||||
|
||||
return api_server_ws_broadcast(buffer);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
#include "api_server.h"
|
||||
#include "common.h"
|
||||
|
||||
#include "message_manager.h"
|
||||
#include "my_mqtt_client.h"
|
||||
#include <esp_http_server.h>
|
||||
#include <esp_log.h>
|
||||
#include <message_manager.h>
|
||||
#include <string.h>
|
||||
|
||||
static const char *TAG = "websocket_handler";
|
||||
@@ -267,6 +268,8 @@ esp_err_t websocket_broadcast(httpd_handle_t server, const char *message)
|
||||
}
|
||||
}
|
||||
|
||||
mqtt_publish(message);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
## Required IDF version
|
||||
idf:
|
||||
version: '>=4.1.0'
|
||||
|
||||
espressif/ble_conn_mgr: '^0.1.3'
|
||||
version: '>=5.0.0'
|
||||
espressif/ble_conn_mgr: '^0.1.6'
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
dependencies:
|
||||
espressif/led_strip: '~3.0.1'
|
||||
idf:
|
||||
version: '>=5.0.0'
|
||||
espressif/led_strip: '~3.0.3'
|
||||
|
||||
@@ -103,15 +103,6 @@ static void message_manager_task(void *param)
|
||||
message_listeners[i](&msg);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t mac[6];
|
||||
esp_read_mac(mac, ESP_MAC_WIFI_STA);
|
||||
const esp_app_desc_t *app_desc = esp_app_get_description();
|
||||
char topic[60];
|
||||
snprintf(topic, sizeof(topic), "device/%s/%02x%02x", app_desc->project_name, mac[4], mac[5]);
|
||||
|
||||
char *data = "{\"key\":\"value\"}";
|
||||
mqtt_client_publish(topic, data, strlen(data), 0, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +1,5 @@
|
||||
dependencies:
|
||||
idf:
|
||||
version: '>=5.0.0'
|
||||
espressif/mqtt: ^1.0.0
|
||||
espressif/cjson: "*"
|
||||
|
||||
@@ -3,11 +3,13 @@
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
void mqtt_client_start(void);
|
||||
void mqtt_client_publish(const char *topic, const char *data, size_t len, int qos, bool retain);
|
||||
void mqtt_client_start(void);
|
||||
void mqtt_publish(const char *message);
|
||||
void mqtt_client_publish(const char *topic, const char *data, size_t len, int qos, bool retain);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -8,6 +8,12 @@
|
||||
#include "mqtt_client.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#include <cJSON.h>
|
||||
#include <esp_timer.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#define DEVICE_TOPIC_MAX_LEN 60
|
||||
|
||||
static const char *TAG = "mqtt_client";
|
||||
static esp_mqtt_client_handle_t client = NULL;
|
||||
|
||||
@@ -108,12 +114,123 @@ void mqtt_client_start(void)
|
||||
}
|
||||
}
|
||||
|
||||
void get_device_topic(char *topic, size_t topic_len)
|
||||
{
|
||||
uint8_t mac[6];
|
||||
esp_read_mac(mac, ESP_MAC_WIFI_STA);
|
||||
const esp_app_desc_t *app_desc = esp_app_get_description();
|
||||
snprintf(topic, topic_len, "device/%s/%02x%02x", app_desc->project_name, mac[4], mac[5]);
|
||||
}
|
||||
|
||||
void mqtt_publish(const char *message)
|
||||
{
|
||||
// Uptime in ms
|
||||
int64_t uptime_ms = esp_timer_get_time() / 1000;
|
||||
|
||||
// UTC time as ISO8601
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
struct tm tm_utc;
|
||||
gmtime_r(&tv.tv_sec, &tm_utc);
|
||||
char timestamp[32];
|
||||
strftime(timestamp, sizeof(timestamp), "%Y-%m-%dT%H:%M:%SZ", &tm_utc);
|
||||
|
||||
// Firmware version
|
||||
const esp_app_desc_t *app_desc = esp_app_get_description();
|
||||
const char *firmware = app_desc->version;
|
||||
|
||||
// Reset reason
|
||||
esp_reset_reason_t reset_reason = esp_reset_reason();
|
||||
const char *reset_reason_str = "UNKNOWN";
|
||||
switch (reset_reason)
|
||||
{
|
||||
case ESP_RST_POWERON:
|
||||
reset_reason_str = "POWERON";
|
||||
break;
|
||||
case ESP_RST_EXT:
|
||||
reset_reason_str = "EXT";
|
||||
break;
|
||||
case ESP_RST_SW:
|
||||
reset_reason_str = "SW";
|
||||
break;
|
||||
case ESP_RST_PANIC:
|
||||
reset_reason_str = "PANIC";
|
||||
break;
|
||||
case ESP_RST_INT_WDT:
|
||||
reset_reason_str = "INT_WDT";
|
||||
break;
|
||||
case ESP_RST_TASK_WDT:
|
||||
reset_reason_str = "TASK_WDT";
|
||||
break;
|
||||
case ESP_RST_WDT:
|
||||
reset_reason_str = "WDT";
|
||||
break;
|
||||
case ESP_RST_DEEPSLEEP:
|
||||
reset_reason_str = "DEEPSLEEP";
|
||||
break;
|
||||
case ESP_RST_BROWNOUT:
|
||||
reset_reason_str = "BROWNOUT";
|
||||
break;
|
||||
case ESP_RST_SDIO:
|
||||
reset_reason_str = "SDIO";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Create JSON object
|
||||
uint8_t mac[6];
|
||||
esp_read_mac(mac, ESP_MAC_WIFI_STA);
|
||||
cJSON *root = cJSON_CreateObject();
|
||||
char mac_str[18];
|
||||
snprintf(mac_str, sizeof(mac_str), "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
cJSON_AddStringToObject(root, "device_id", mac_str);
|
||||
cJSON_AddNumberToObject(root, "uptime", uptime_ms);
|
||||
cJSON_AddStringToObject(root, "timestamp", timestamp);
|
||||
cJSON_AddStringToObject(root, "firmware", firmware);
|
||||
cJSON_AddStringToObject(root, "reset_reason", reset_reason_str);
|
||||
|
||||
// Insert message as JSON object if possible
|
||||
char topic_with_type[128];
|
||||
strncpy(topic_with_type, "", sizeof(topic_with_type));
|
||||
topic_with_type[sizeof(topic_with_type) - 1] = '\0';
|
||||
|
||||
cJSON *msg_obj = cJSON_Parse(message);
|
||||
if (msg_obj)
|
||||
{
|
||||
cJSON *type_item = cJSON_DetachItemFromObject(msg_obj, "type");
|
||||
if (type_item && cJSON_IsString(type_item))
|
||||
{
|
||||
// Extend topic
|
||||
strncat(topic_with_type, type_item->valuestring, sizeof(topic_with_type) - strlen(topic_with_type) - 1);
|
||||
}
|
||||
cJSON_AddItemToObject(root, "message", msg_obj);
|
||||
cJSON_Delete(type_item); // Free memory
|
||||
}
|
||||
else
|
||||
{
|
||||
cJSON_AddStringToObject(root, "message", message);
|
||||
}
|
||||
|
||||
// Publish JSON via MQTT
|
||||
char *json_str = cJSON_PrintUnformatted(root);
|
||||
mqtt_client_publish(topic_with_type, json_str, strlen(json_str), 0, true);
|
||||
cJSON_Delete(root);
|
||||
free(json_str);
|
||||
}
|
||||
|
||||
void mqtt_client_publish(const char *topic, const char *data, size_t len, int qos, bool retain)
|
||||
{
|
||||
if (client)
|
||||
{
|
||||
int msg_id = esp_mqtt_client_publish(client, topic, data, len, qos, retain);
|
||||
ESP_LOGI(TAG, "Publish: topic=%s, msg_id=%d, qos=%d, retain=%d, len=%d", topic, msg_id, qos, retain, (int)len);
|
||||
char base_topic[DEVICE_TOPIC_MAX_LEN];
|
||||
get_device_topic(base_topic, sizeof(base_topic));
|
||||
char full_topic[DEVICE_TOPIC_MAX_LEN + 64];
|
||||
snprintf(full_topic, sizeof(full_topic), "%s/%s", base_topic, topic);
|
||||
|
||||
int msg_id = esp_mqtt_client_publish(client, full_topic, data, len, qos, retain);
|
||||
ESP_LOGV(TAG, "Publish: topic=%s, msg_id=%d, qos=%d, retain=%d, len=%d", full_topic, msg_id, qos, retain,
|
||||
(int)len);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "led_strip_ws2812.h"
|
||||
#include "message_manager.h"
|
||||
#include "persistence_manager.h"
|
||||
#include "simulator.h"
|
||||
#include "storage.h"
|
||||
#include <esp_heap_caps.h>
|
||||
#include <esp_log.h>
|
||||
@@ -15,28 +16,7 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static const char *TAG = "simulator";
|
||||
static char *time = NULL;
|
||||
|
||||
static char *time_to_string(int hhmm)
|
||||
{
|
||||
static char buffer[20];
|
||||
snprintf(buffer, sizeof(buffer), "%02d:%02d", hhmm / 100, hhmm % 100);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static TaskHandle_t simulation_task_handle = NULL;
|
||||
static SemaphoreHandle_t simulation_mutex = NULL;
|
||||
|
||||
static void ensure_mutex_initialized(void)
|
||||
{
|
||||
if (simulation_mutex == NULL)
|
||||
{
|
||||
simulation_mutex = xSemaphoreCreateMutex();
|
||||
}
|
||||
}
|
||||
|
||||
// The struct is extended with a 'next' pointer to form a linked list.
|
||||
// Type definitions
|
||||
typedef struct light_item_node_t
|
||||
{
|
||||
char time[5];
|
||||
@@ -46,23 +26,42 @@ typedef struct light_item_node_t
|
||||
struct light_item_node_t *next;
|
||||
} light_item_node_t;
|
||||
|
||||
// Global pointers for the head and tail of the list.
|
||||
static light_item_node_t *head = NULL;
|
||||
static light_item_node_t *tail = NULL;
|
||||
|
||||
// Interpolation mode selection
|
||||
typedef enum
|
||||
{
|
||||
INTERPOLATION_RGB,
|
||||
INTERPOLATION_HSV
|
||||
} interpolation_mode_t;
|
||||
|
||||
// You can change this to test different interpolation methods
|
||||
// Constants and global variables
|
||||
static const char *TAG = "simulator";
|
||||
static char *time = NULL;
|
||||
static TaskHandle_t simulation_task_handle = NULL;
|
||||
static SemaphoreHandle_t simulation_mutex = NULL;
|
||||
static light_item_node_t *head = NULL;
|
||||
static const interpolation_mode_t interpolation_mode = INTERPOLATION_RGB;
|
||||
|
||||
char *get_time(void)
|
||||
// Helper function: converts hhmm format to minutes of the day
|
||||
static int hhmm_to_minutes(const char time[5])
|
||||
{
|
||||
return time;
|
||||
int t = atoi(time);
|
||||
return (t / 100) * 60 + (t % 100);
|
||||
}
|
||||
|
||||
// Helper function: converts int hhmm to string
|
||||
static char *time_to_string(int hhmm)
|
||||
{
|
||||
static char buffer[20];
|
||||
snprintf(buffer, sizeof(buffer), "%02d:%02d", hhmm / 100, hhmm % 100);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// Helper function: ensures mutex is initialized
|
||||
static void ensure_mutex_initialized(void)
|
||||
{
|
||||
if (simulation_mutex == NULL)
|
||||
{
|
||||
simulation_mutex = xSemaphoreCreateMutex();
|
||||
}
|
||||
}
|
||||
|
||||
// Main interpolation function that selects the appropriate method
|
||||
@@ -78,10 +77,11 @@ static rgb_t interpolate_color(rgb_t start, rgb_t end, float factor)
|
||||
}
|
||||
}
|
||||
|
||||
// Linked list management
|
||||
esp_err_t add_light_item(const char time[5], uint8_t red, uint8_t green, uint8_t blue, uint8_t white,
|
||||
uint8_t brightness, uint8_t saturation)
|
||||
{
|
||||
// Allocate memory for a new node in PSRAM.
|
||||
// Allocate memory for new node in PSRAM.
|
||||
light_item_node_t *new_node = (light_item_node_t *)heap_caps_malloc(sizeof(light_item_node_t), MALLOC_CAP_DEFAULT);
|
||||
if (new_node == NULL)
|
||||
{
|
||||
@@ -106,18 +106,22 @@ esp_err_t add_light_item(const char time[5], uint8_t red, uint8_t green, uint8_t
|
||||
new_node->blue = (uint8_t)(color.blue * brightness_factor);
|
||||
new_node->next = NULL;
|
||||
|
||||
// Append the new node to the end of the list.
|
||||
if (head == NULL)
|
||||
// Insert sorted: find the correct position
|
||||
if (head == NULL || hhmm_to_minutes(new_node->time) < hhmm_to_minutes(head->time))
|
||||
{
|
||||
// If the list is empty, the new node becomes both head and tail.
|
||||
// New head
|
||||
new_node->next = head;
|
||||
head = new_node;
|
||||
tail = new_node;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise, append the new node to the end and update tail.
|
||||
tail->next = new_node;
|
||||
tail = new_node;
|
||||
light_item_node_t *prev = head;
|
||||
while (prev->next != NULL && hhmm_to_minutes(prev->next->time) < hhmm_to_minutes(new_node->time))
|
||||
{
|
||||
prev = prev->next;
|
||||
}
|
||||
new_node->next = prev->next;
|
||||
prev->next = new_node;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
@@ -136,7 +140,6 @@ void cleanup_light_items(void)
|
||||
}
|
||||
|
||||
head = NULL;
|
||||
tail = NULL;
|
||||
ESP_LOGI(TAG, "Cleaned up all light items.");
|
||||
}
|
||||
|
||||
@@ -153,6 +156,8 @@ static void initialize_light_items(void)
|
||||
load_file(filename);
|
||||
persistence_manager_deinit(&persistence);
|
||||
|
||||
// The list is now sorted because add_light_item inserts sorted
|
||||
|
||||
if (head == NULL)
|
||||
{
|
||||
ESP_LOGW(TAG, "Light schedule is empty. Simulation will not run.");
|
||||
@@ -199,43 +204,7 @@ static light_item_node_t *find_best_light_item_for_time(int hhmm)
|
||||
return best_item;
|
||||
}
|
||||
|
||||
static light_item_node_t *find_next_light_item_for_time(int hhmm)
|
||||
{
|
||||
light_item_node_t *current = head;
|
||||
light_item_node_t *next_item = NULL;
|
||||
int next_time = 9999; // Initialize with a value larger than any possible time
|
||||
|
||||
// First pass: find the soonest time after hhmm
|
||||
while (current != NULL)
|
||||
{
|
||||
int current_time = atoi(current->time);
|
||||
if (current_time > hhmm && current_time < next_time)
|
||||
{
|
||||
next_time = current_time;
|
||||
next_item = current;
|
||||
}
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
// If no item is found for the rest of the day, wrap around to the beginning of the next day
|
||||
if (next_item == NULL)
|
||||
{
|
||||
current = head;
|
||||
next_time = 9999;
|
||||
while (current != NULL)
|
||||
{
|
||||
int current_time = atoi(current->time);
|
||||
if (current_time < next_time)
|
||||
{
|
||||
next_time = current_time;
|
||||
next_item = current;
|
||||
}
|
||||
current = current->next;
|
||||
}
|
||||
}
|
||||
return next_item;
|
||||
}
|
||||
|
||||
// Messaging
|
||||
static void send_simulation_message(const char *time, rgb_t color)
|
||||
{
|
||||
message_t msg = {};
|
||||
@@ -247,6 +216,12 @@ static void send_simulation_message(const char *time, rgb_t color)
|
||||
message_manager_post(&msg, pdMS_TO_TICKS(100));
|
||||
}
|
||||
|
||||
// Public API
|
||||
char *get_time(void)
|
||||
{
|
||||
return time;
|
||||
}
|
||||
|
||||
void start_simulate_day(void)
|
||||
{
|
||||
initialize_light_items();
|
||||
@@ -308,49 +283,55 @@ void simulate_cycle(void *args)
|
||||
time = time_to_string(hhmm);
|
||||
|
||||
light_item_node_t *current_item = find_best_light_item_for_time(hhmm);
|
||||
light_item_node_t *next_item = find_next_light_item_for_time(hhmm);
|
||||
light_item_node_t *next_item = NULL;
|
||||
|
||||
if (current_item != NULL)
|
||||
{
|
||||
rgb_t color = {0, 0, 0};
|
||||
|
||||
// Use head as fallback if next_item is NULL
|
||||
next_item = next_item ? next_item : head;
|
||||
// Cyclic interpolation: if current_item is the tail element, set next_item to head
|
||||
if (current_item->next == NULL && head != NULL)
|
||||
{
|
||||
next_item = head;
|
||||
}
|
||||
else
|
||||
{
|
||||
next_item = current_item->next;
|
||||
}
|
||||
|
||||
if (next_item != NULL)
|
||||
{
|
||||
int current_item_time_min = (atoi(current_item->time) / 100) * 60 + (atoi(current_item->time) % 100);
|
||||
int next_item_time_min = (atoi(next_item->time) / 100) * 60 + (atoi(next_item->time) % 100);
|
||||
int current_minutes = hhmm_to_minutes(current_item->time);
|
||||
int next_minutes = hhmm_to_minutes(next_item->time);
|
||||
|
||||
if (next_item_time_min < current_item_time_min)
|
||||
// Cyclic transition: if next_minutes < current_minutes, add day length
|
||||
if (next_minutes < current_minutes)
|
||||
{
|
||||
next_item_time_min += total_minutes_in_day;
|
||||
next_minutes += total_minutes_in_day;
|
||||
}
|
||||
|
||||
int minutes_since_current_item_start = current_minute_of_day - current_item_time_min;
|
||||
if (minutes_since_current_item_start < 0)
|
||||
int minutes_since_current = current_minute_of_day - current_minutes;
|
||||
if (minutes_since_current < 0)
|
||||
{
|
||||
minutes_since_current_item_start += total_minutes_in_day;
|
||||
minutes_since_current += total_minutes_in_day;
|
||||
}
|
||||
|
||||
int interval_duration = next_item_time_min - current_item_time_min;
|
||||
if (interval_duration == 0)
|
||||
int interval = next_minutes - current_minutes;
|
||||
if (interval == 0)
|
||||
{
|
||||
interval_duration = 1;
|
||||
interval = 1;
|
||||
}
|
||||
|
||||
float interpolation_factor = (float)minutes_since_current_item_start / (float)interval_duration;
|
||||
float factor = (float)minutes_since_current / (float)interval;
|
||||
|
||||
// Prepare colors for interpolation
|
||||
rgb_t start_rgb = {.red = current_item->red, .green = current_item->green, .blue = current_item->blue};
|
||||
rgb_t end_rgb = {.red = next_item->red, .green = next_item->green, .blue = next_item->blue};
|
||||
|
||||
// Use the interpolation function
|
||||
color = interpolate_color(start_rgb, end_rgb, interpolation_factor);
|
||||
color = interpolate_color(start_rgb, end_rgb, factor);
|
||||
led_strip_update(LED_STATE_SIMULATION, color);
|
||||
}
|
||||
else
|
||||
{
|
||||
// No next_item and no head, use only current
|
||||
color = (rgb_t){.red = current_item->red, .green = current_item->green, .blue = current_item->blue};
|
||||
led_strip_update(LED_STATE_SIMULATION, color);
|
||||
}
|
||||
@@ -402,7 +383,7 @@ void stop_simulation_task(void)
|
||||
simulation_task_handle = NULL;
|
||||
xSemaphoreGive(simulation_mutex);
|
||||
|
||||
// Prüfe ob der Task noch existiert bevor er gelöscht wird
|
||||
// Check if the task still exists before deleting it
|
||||
eTaskState state = eTaskGetState(handle_to_delete);
|
||||
if (state != eDeleted && state != eInvalid)
|
||||
{
|
||||
|
||||
@@ -3,6 +3,7 @@ idf_component_register(SRCS
|
||||
src/app_task.cpp
|
||||
src/button_handling.c
|
||||
src/i2c_checker.c
|
||||
src/u8g2_mqtt.cpp
|
||||
src/hal/u8g2_esp32_hal.c
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_REQUIRES
|
||||
|
||||
@@ -3,5 +3,5 @@ dependencies:
|
||||
git: https://github.com/olikraus/u8g2.git
|
||||
# u8g2_hal:
|
||||
# git: https://github.com/mkfrey/u8g2-hal-esp-idf.git
|
||||
espressif/button: ^4.1.4
|
||||
espressif/esp_insights: ^1.2.7
|
||||
espressif/button: ^4.1.6
|
||||
espressif/esp_insights: ^1.3.2
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
extern QueueHandle_t display_mqtt_queue;
|
||||
|
||||
void u8g2_mqtt_task(void *pvParameters);
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "my_mqtt_client.h"
|
||||
#include "persistence_manager.h"
|
||||
#include "simulator.h"
|
||||
#include "u8g2_mqtt.h"
|
||||
#include "ui/ClockScreenSaver.h"
|
||||
#include "ui/ScreenSaver.h"
|
||||
#include "ui/SplashScreen.h"
|
||||
@@ -33,6 +34,7 @@ u8g2_t u8g2;
|
||||
uint8_t last_value = 0;
|
||||
menu_options_t options;
|
||||
uint8_t received_signal;
|
||||
uint64_t last_mqtt_sync = 0;
|
||||
|
||||
std::shared_ptr<Widget> m_widget;
|
||||
std::vector<std::shared_ptr<Widget>> m_history;
|
||||
@@ -148,27 +150,27 @@ static void handle_button(uint8_t button)
|
||||
{
|
||||
switch (button)
|
||||
{
|
||||
case 1:
|
||||
case CONFIG_BUTTON_UP:
|
||||
m_widget->OnButtonClicked(ButtonType::UP);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
case CONFIG_BUTTON_LEFT:
|
||||
m_widget->OnButtonClicked(ButtonType::LEFT);
|
||||
break;
|
||||
|
||||
case 5:
|
||||
case CONFIG_BUTTON_RIGHT:
|
||||
m_widget->OnButtonClicked(ButtonType::RIGHT);
|
||||
break;
|
||||
|
||||
case 6:
|
||||
case CONFIG_BUTTON_DOWN:
|
||||
m_widget->OnButtonClicked(ButtonType::DOWN);
|
||||
break;
|
||||
|
||||
case 16:
|
||||
case CONFIG_BUTTON_BACK:
|
||||
m_widget->OnButtonClicked(ButtonType::BACK);
|
||||
break;
|
||||
|
||||
case 18:
|
||||
case CONFIG_BUTTON_SELECT:
|
||||
m_widget->OnButtonClicked(ButtonType::SELECT);
|
||||
break;
|
||||
|
||||
@@ -259,6 +261,10 @@ void app_task(void *args)
|
||||
|
||||
start_simulation();
|
||||
|
||||
display_mqtt_queue = xQueueCreate(1, 1024);
|
||||
|
||||
xTaskCreatePinnedToCore(u8g2_mqtt_task, "mqtt_disp", 4096, nullptr, 5, nullptr, tskNO_AFFINITY);
|
||||
|
||||
auto oldTime = esp_timer_get_time();
|
||||
|
||||
while (true)
|
||||
@@ -279,6 +285,15 @@ void app_task(void *args)
|
||||
m_inactivityTracker->update(deltaMs);
|
||||
}
|
||||
|
||||
// MQTT
|
||||
auto now = esp_timer_get_time();
|
||||
if (now - last_mqtt_sync > 1000000)
|
||||
{
|
||||
uint8_t *u8g2_buf = u8g2_GetBufferPtr(&u8g2);
|
||||
xQueueOverwrite(display_mqtt_queue, u8g2_buf);
|
||||
last_mqtt_sync = now;
|
||||
}
|
||||
|
||||
u8g2_SendBuffer(&u8g2);
|
||||
|
||||
if (xQueueReceive(buttonQueue, &received_signal, pdMS_TO_TICKS(10)) == pdTRUE)
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "persistence_manager.h"
|
||||
#include "wifi_manager.h"
|
||||
#include <ble_manager.h>
|
||||
#include <driver/gpio.h>
|
||||
#include <esp_event.h>
|
||||
#include <esp_insights.h>
|
||||
#include <esp_log.h>
|
||||
@@ -13,9 +14,31 @@
|
||||
#include <nvs_flash.h>
|
||||
#include <sdkconfig.h>
|
||||
|
||||
#define WIFI_ENABLE GPIO_NUM_3
|
||||
#define WIFI_ANT_CONFIG GPIO_NUM_14
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32C6)
|
||||
// GPIO für WiFi Enable konfigurieren
|
||||
gpio_config_t io_conf = {.pin_bit_mask = (1ULL << WIFI_ENABLE),
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pull_up_en = GPIO_PULLUP_DISABLE,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.intr_type = GPIO_INTR_DISABLE};
|
||||
gpio_config(&io_conf);
|
||||
gpio_set_level(WIFI_ENABLE, 0); // LOW
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
// GPIO for WiFi antenna configuration
|
||||
io_conf.pin_bit_mask = (1ULL << WIFI_ANT_CONFIG);
|
||||
gpio_config(&io_conf);
|
||||
gpio_set_level(WIFI_ANT_CONFIG, 1); // HIGH
|
||||
#endif
|
||||
|
||||
// Initialize NVS
|
||||
esp_err_t err = nvs_flash_init();
|
||||
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND)
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
#include "u8g2_mqtt.h"
|
||||
|
||||
#include "esp_timer.h"
|
||||
#include <my_mqtt_client.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <u8g2.h>
|
||||
|
||||
#define BUFFER_SIZE (128 * 64 / 8)
|
||||
QueueHandle_t display_mqtt_queue = nullptr;
|
||||
|
||||
void u8g2_mqtt_task(void *pvParameters)
|
||||
{
|
||||
static uint8_t current_buffer[BUFFER_SIZE];
|
||||
static uint8_t previous_buffer[BUFFER_SIZE] = {0};
|
||||
static uint8_t mqtt_payload[BUFFER_SIZE * 2 + 1];
|
||||
|
||||
uint64_t last_keyframe_time = 0;
|
||||
const uint64_t KEYFRAME_INTERVAL_US = 5000000; // 5 seconds in microseconds
|
||||
|
||||
while (true)
|
||||
{
|
||||
// Blocks without CPU load until app_task provides a frame
|
||||
if (xQueueReceive(display_mqtt_queue, current_buffer, portMAX_DELAY) == pdTRUE)
|
||||
{
|
||||
int payload_size = 0;
|
||||
uint64_t current_time = esp_timer_get_time();
|
||||
|
||||
// Time-based I-frame decision (or on initial start)
|
||||
bool is_keyframe = (current_time - last_keyframe_time >= KEYFRAME_INTERVAL_US) || (last_keyframe_time == 0);
|
||||
|
||||
if (is_keyframe)
|
||||
{
|
||||
mqtt_payload[payload_size++] = 0x01; // Header: I-frame
|
||||
last_keyframe_time = current_time;
|
||||
|
||||
for (int i = 0; i < BUFFER_SIZE;)
|
||||
{
|
||||
uint8_t count = 1;
|
||||
while (i + count < BUFFER_SIZE && current_buffer[i] == current_buffer[i + count] && count < 255)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
mqtt_payload[payload_size++] = count;
|
||||
mqtt_payload[payload_size++] = current_buffer[i];
|
||||
i += count;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mqtt_payload[payload_size++] = 0x00; // Header: P-frame (Diff)
|
||||
uint8_t xor_buffer[BUFFER_SIZE];
|
||||
|
||||
for (int i = 0; i < BUFFER_SIZE; i++)
|
||||
{
|
||||
xor_buffer[i] = current_buffer[i] ^ previous_buffer[i];
|
||||
}
|
||||
|
||||
for (int i = 0; i < BUFFER_SIZE;)
|
||||
{
|
||||
uint8_t count = 1;
|
||||
while (i + count < BUFFER_SIZE && xor_buffer[i] == xor_buffer[i + count] && count < 255)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
mqtt_payload[payload_size++] = count;
|
||||
mqtt_payload[payload_size++] = xor_buffer[i];
|
||||
i += count;
|
||||
}
|
||||
}
|
||||
|
||||
// --- MQTT SEND ---
|
||||
mqtt_client_publish("stream", (char *)mqtt_payload, payload_size, 0, false);
|
||||
|
||||
memcpy(previous_buffer, current_buffer, BUFFER_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,17 +4,24 @@ CONFIG_IDF_TARGET="esp32c6"
|
||||
#
|
||||
# Display Settings
|
||||
#
|
||||
CONFIG_DISPLAY_SDA_PIN=9
|
||||
CONFIG_DISPLAY_SCL_PIN=8
|
||||
CONFIG_DISPLAY_SDA_PIN=22
|
||||
CONFIG_DISPLAY_SCL_PIN=23
|
||||
# end of Display Settings
|
||||
|
||||
#
|
||||
# Button Configuration
|
||||
#
|
||||
CONFIG_BUTTON_UP=7
|
||||
CONFIG_BUTTON_DOWN=4
|
||||
CONFIG_BUTTON_LEFT=6
|
||||
CONFIG_BUTTON_RIGHT=5
|
||||
CONFIG_BUTTON_SELECT=19
|
||||
CONFIG_BUTTON_BACK=20
|
||||
CONFIG_BUTTON_UP=18
|
||||
CONFIG_BUTTON_DOWN=17
|
||||
CONFIG_BUTTON_LEFT=20
|
||||
CONFIG_BUTTON_RIGHT=19
|
||||
CONFIG_BUTTON_SELECT=1
|
||||
CONFIG_BUTTON_BACK=2
|
||||
# end of Button Configuration
|
||||
|
||||
CONFIG_WLED_DIN_PIN=21
|
||||
CONFIG_STATUS_WLED_PIN=16
|
||||
|
||||
CONFIG_API_SERVER_HOSTNAME="system-control"
|
||||
|
||||
CONFIG_LWIP_MAX_SOCKETS=20
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
# default ESP target
|
||||
CONFIG_IDF_TARGET="esp32s3"
|
||||
|
||||
CONFIG_API_SERVER_HOSTNAME="system-client"
|
||||
|
||||
@@ -3,4 +3,6 @@ CONFIG_APP_REPRODUCIBLE_BUILD=y
|
||||
|
||||
# Compiler options
|
||||
CONFIG_COMPILER_OPTIMIZATION_PERF=y
|
||||
# CONFIG_COMPILER_OPTIMIZATION_SIZE=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE=y
|
||||
CONFIG_DEBUG_INFO=n
|
||||
|
||||
@@ -46,8 +46,7 @@
|
||||
</div>
|
||||
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-primary" id="connect-btn" onclick="saveWifi()" data-i18n="captive.connect"
|
||||
disabled>
|
||||
<button class="btn btn-primary" id="connect-btn" onclick="saveWifi()" data-i18n="captive.connect">
|
||||
💾 Verbinden
|
||||
</button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user