#include #include #include #include #include #include #include #include #include #include #include #include #include #include "LED_Segments.h" // Constants #define LED_STRIP_RMT_RES_HZ (10 * 1000 * 1000) #define TOPIC_BUFFER_SIZE 50 // Global variables LED_Segments segments[CONFIG_LED_SEGMENT_COUNT]; esp_mqtt_client_handle_t mqtt_client; led_strip_handle_t led_internal; led_strip_handle_t led_segments; uint64_t last_time_blink = 0; uint64_t last_time_pulse = 0; uint64_t last_time_walk_fill = 0; int refresh_pulse = CONFIG_PATTERN_TIME / 2 / 255; int refresh_blink = CONFIG_PATTERN_TIME / CONFIG_BLINK_COUNT; int refresh_walk_fill = CONFIG_PATTERN_TIME / CONFIG_SEGMENT_COUNT; int value_blink = 0; int value_pulse = 0; int value_walk_fill = 0; int direction_pulse = 0; // Function prototypes esp_err_t init_mqtt(void); esp_err_t handle_mqtt_cmd(char *topic, uint8_t *payload, int length); // helper functions uint8_t limit(uint8_t value) { return (LIMIT * value) / 255; } // Event handlers static void network_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { ESP_ERROR_CHECK(led_strip_set_pixel(debug_led, 0, LIMIT, LIMIT, 0)); ESP_ERROR_CHECK(led_strip_refresh(debug_led)); esp_wifi_connect(); } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { ESP_ERROR_CHECK(led_strip_set_pixel(debug_led, 0, LIMIT, LIMIT, 0)); ESP_ERROR_CHECK(led_strip_refresh(debug_led)); esp_wifi_connect(); } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data; ESP_LOGI("network", "got ip:" IPSTR, IP2STR(&event->ip_info.ip)); ESP_ERROR_CHECK(init_mqtt()); ESP_LOGI("main", "init_mqtt done"); ESP_ERROR_CHECK(led_strip_set_pixel(debug_led, 0, 0, LIMIT, 0)); ESP_ERROR_CHECK(led_strip_refresh(debug_led)); vTaskDelay(pdMS_TO_TICKS(1000)); ESP_ERROR_CHECK(led_strip_set_pixel(debug_led, 0, 0, 0, 0)); ESP_ERROR_CHECK(led_strip_refresh(debug_led)); } } /* * @brief Event handler registered to receive MQTT events * * This function is called by the MQTT client event loop. * * @param handler_args user data registered to the event. * @param base Event base for the handler(always MQTT Base in this example). * @param event_id The id for the received event. * @param event_data The data for the event, esp_mqtt_event_handle_t. */ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) { ESP_LOGD("mqtt", "Event dispatched from event loop base=%s, event_id=%" PRIu32, base, event_id); esp_mqtt_event_handle_t event = event_data; esp_mqtt_client_handle_t client = event->client; int msg_id; switch ((esp_mqtt_event_id_t) event_id) { case MQTT_EVENT_CONNECTED: ESP_LOGI("mqtt", "MQTT_EVENT_CONNECTED"); char id[6] = "00000"; sprintf(id, "%05d", CONFIG_FABLIGHT_ID); // Register Reader msg_id = esp_mqtt_client_publish(client, "fablight", id, 0, 0, 0); ESP_LOGI("mqtt", "sent publish successful, msg_id=%d", msg_id); char topic_recv[TOPIC_BUFFER_SIZE]; snprintf(topic_recv, TOPIC_BUFFER_SIZE, "fablight/%05d/+/static", CONFIG_FABLIGHT_ID); msg_id = esp_mqtt_client_subscribe(client, topic_recv, 0); ESP_LOGI("mqtt", "sent subscribe successful, msg_id=%d", msg_id); snprintf(topic_recv, TOPIC_BUFFER_SIZE, "fablight/%05d/+/pulse", CONFIG_FABLIGHT_ID); msg_id = esp_mqtt_client_subscribe(client, topic_recv, 0); ESP_LOGI("mqtt", "sent subscribe successful, msg_id=%d", msg_id); snprintf(topic_recv, TOPIC_BUFFER_SIZE, "fablight/%05d/+/blink", CONFIG_FABLIGHT_ID); msg_id = esp_mqtt_client_subscribe(client, topic_recv, 0); ESP_LOGI("mqtt", "sent subscribe successful, msg_id=%d", msg_id); snprintf(topic_recv, TOPIC_BUFFER_SIZE, "fablight/%05d/+/walk", CONFIG_FABLIGHT_ID); msg_id = esp_mqtt_client_subscribe(client, topic_recv, 0); ESP_LOGI("mqtt", "sent subscribe successful, msg_id=%d", msg_id); snprintf(topic_recv, TOPIC_BUFFER_SIZE, "fablight/%05d/+/fill", CONFIG_FABLIGHT_ID); msg_id = esp_mqtt_client_subscribe(client, topic_recv, 0); ESP_LOGI("mqtt", "sent subscribe successful, msg_id=%d", msg_id); snprintf(topic_recv, TOPIC_BUFFER_SIZE, "fablight/%05d/buzzer", CONFIG_FABLIGHT_ID); msg_id = esp_mqtt_client_subscribe(client, topic_recv, 0); ESP_LOGI("mqtt", "sent subscribe successful, msg_id=%d", msg_id); snprintf(topic_recv, TOPIC_BUFFER_SIZE, "fablight/%05d/sync", CONFIG_FABLIGHT_ID); msg_id = esp_mqtt_client_subscribe(client, topic_recv, 0); ESP_LOGI("mqtt", "sent subscribe successful, msg_id=%d", msg_id); snprintf(topic_recv, TOPIC_BUFFER_SIZE, "fablight/sync"); msg_id = esp_mqtt_client_subscribe(client, topic_recv, 0); ESP_LOGI("mqtt", "sent subscribe successful, msg_id=%d", msg_id); snprintf(topic_recv, TOPIC_BUFFER_SIZE, "fablight/%05d/clear", CONFIG_FABLIGHT_ID); msg_id = esp_mqtt_client_subscribe(client, topic_recv, 0); ESP_LOGI("mqtt", "sent subscribe successful, msg_id=%d", msg_id); break; case MQTT_EVENT_DISCONNECTED: ESP_LOGI("mqtt", "MQTT_EVENT_DISCONNECTED"); break; case MQTT_EVENT_SUBSCRIBED: ESP_LOGI("mqtt", "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id); break; case MQTT_EVENT_UNSUBSCRIBED: ESP_LOGI("mqtt", "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id); break; case MQTT_EVENT_PUBLISHED: ESP_LOGI("mqtt", "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id); break; case MQTT_EVENT_DATA: ESP_LOGI("mqtt", "MQTT_EVENT_DATA"); printf("TOPIC=%.*s\r\n", event->topic_len, event->topic); printf("DATA=%.*s\r\n", event->data_len, event->data); char* topic = strndup(event->topic, event->topic_len); esp_err_t ret = handle_mqtt_cmd(topic, (uint8_t *) event->data, event->data_len); free(topic); if (ret != ESP_OK) { ESP_LOGE("mqtt", "Error handling MQTT command: %s", esp_err_to_name(ret)); } break; case MQTT_EVENT_ERROR: ESP_LOGE("mqtt", "MQTT_EVENT_ERROR"); if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) { ESP_LOGE("mqtt", "Last error code reported from esp-tls: 0x%x", event->error_handle->esp_tls_last_esp_err); ESP_LOGE("mqtt", "Last tls stack error number: 0x%x", event->error_handle->esp_tls_stack_err); ESP_LOGE("mqtt", "Last captured errno : %d (%s)", event->error_handle->esp_transport_sock_errno, strerror(event->error_handle->esp_transport_sock_errno)); } else if (event->error_handle->error_type == MQTT_ERROR_TYPE_CONNECTION_REFUSED) { ESP_LOGE("mqtt", "Connection refused error: 0x%x", event->error_handle->connect_return_code); } else { ESP_LOGW("mqtt", "Unknown error type: 0x%x", event->error_handle->error_type); } break; default: ESP_LOGI("mqtt", "Other event id:%d", event->event_id); break; } } // Initialization Functions esp_err_t init_led_strip() { // LED strip general initialization, according to your led board design led_strip_config_t debug_led_config = { .strip_gpio_num = CONFIG_PIN_DEBUG_LED, // The GPIO that connected to the LED strip's data line .max_leds = 1, // The number of LEDs in the strip, .led_pixel_format = LED_PIXEL_FORMAT_GRB, // Pixel format of your LED strip .led_model = LED_MODEL_WS2812, // LED strip model .flags.invert_out = false, // whether to invert the output signal }; // LED strip backend configuration: RMT led_strip_rmt_config_t debug_led_rmt_config = { .clk_src = RMT_CLK_SRC_DEFAULT, // different clock source can lead to different power consumption .resolution_hz = LED_STRIP_RMT_RES_HZ, // RMT counter clock frequency .flags.with_dma = false, // DMA feature is available on ESP target like ESP32-S3 }; ESP_ERROR_CHECK(led_strip_new_rmt_device(&debug_led_config, &debug_led_rmt_config, &debug_led)); ESP_LOGI("led", "Created LED strip object with RMT backend for debug LED"); // LED strip general initialization, according to your led board design led_strip_config_t strip_config = { .strip_gpio_num = CONFIG_PIN_SEGMENT_DATA, // The GPIO that connected to the LED strip's data line .max_leds = CONFIG_SEGMENT_COUNT * CONFIG_SEGMENT_SIZE, // The number of LEDs in the strip, .led_pixel_format = LED_PIXEL_FORMAT_GRB, // Pixel format of your LED strip .led_model = LED_MODEL_WS2812, // LED strip model .flags.invert_out = false, // whether to invert the output signal }; // LED strip backend configuration: RMT led_strip_rmt_config_t rmt_config = { .clk_src = RMT_CLK_SRC_DEFAULT, // different clock source can lead to different power consumption .resolution_hz = LED_STRIP_RMT_RES_HZ, // RMT counter clock frequency .flags.with_dma = true, // DMA feature is available on ESP target like ESP32-S3 }; ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip)); ESP_LOGI("led", "Created LED strip object with RMT backend for Segment LEDs"); return ESP_OK; } esp_err_t init_gpio(void) { gpio_config_t io_conf = {}; //disable interrupt io_conf.intr_type = GPIO_INTR_DISABLE; //set as output mode io_conf.mode = GPIO_MODE_OUTPUT; //bit mask of the pins that you want to set,e.g.GPIO18/19 io_conf.pin_bit_mask = (1ULL << CONFIG_PIN_BUZZER); //disable pull-down mode io_conf.pull_down_en = 0; //disable pull-up mode io_conf.pull_up_en = 0; //configure GPIO with the given settings esp_err_t ret = gpio_config(&io_conf); ESP_ERROR_CHECK(ret); return ret; } esp_err_t init_eventloop() { esp_err_t ret = esp_event_loop_create_default(); ESP_ERROR_CHECK(ret); return ret; } esp_err_t init_wifi() { esp_err_t ret; ret = esp_netif_init(); ESP_ERROR_CHECK(ret); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ret = esp_wifi_init(&cfg); ESP_ERROR_CHECK(ret); ret = esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &network_event_handler, NULL, NULL); ESP_ERROR_CHECK(ret); ret = esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &network_event_handler, NULL, NULL); ESP_ERROR_CHECK(ret); // Initialize default station as network interface instance (esp-netif) esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta(); assert(sta_netif); // Initialize and start WiFi wifi_config_t wifi_config = { .sta = { .ssid = CONFIG_WIFI_SSID, .password = CONFIG_WIFI_PASSWORD, .scan_method = CONFIG_WIFI_FAST_SCAN ? WIFI_FAST_SCAN : WIFI_ALL_CHANNEL_SCAN, .sort_method = WIFI_CONNECT_AP_BY_SIGNAL, .threshold.rssi = -127, .threshold.authmode = WIFI_AUTH_WPA2_PSK, }, }; ret = esp_wifi_set_mode(WIFI_MODE_STA); ESP_ERROR_CHECK(ret); ret = esp_wifi_set_config(WIFI_IF_STA, &wifi_config); ESP_ERROR_CHECK(ret); ret = esp_wifi_start(); ESP_ERROR_CHECK(ret); return ret; } esp_err_t init_nvs() { // Initialize NVS esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_ERROR_CHECK(nvs_flash_erase()); ret = nvs_flash_init(); } ESP_ERROR_CHECK(ret); return ret; } esp_err_t init_mqtt() { esp_err_t ret; const esp_mqtt_client_config_t mqtt_cfg = { .broker = { .address.uri = CONFIG_BROKER_URI, .verification.crt_bundle_attach = esp_crt_bundle_attach } }; esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg); /* The last argument may be used to pass data to the event handler, in this example mqtt_event_handler */ ret = esp_mqtt_client_register_event(client, MQTT_EVENT_ANY, mqtt_event_handler, NULL); ESP_ERROR_CHECK(ret); ret = esp_mqtt_client_start(client); ESP_ERROR_CHECK(ret); mqtt_client = client; return ret; } // Buisness Logic esp_err_t handle_mqtt_cmd(char *topic, uint8_t *payload, int length) { int topic_len = (int) strlen(topic); int mode_static_len = (int) strlen("/static"); int mode_pulse_len = (int) strlen("/pulse"); int mode_blink_len = (int) strlen("/blink"); int mode_fill_len = (int) strlen("/fill"); int mode_walk_len = (int) strlen("/walk"); int buzzer_len = (int) strlen("/buzzer"); int sync_len = (int) strlen("/sync"); int clear_len = (int) strlen("/clear"); if (!strcmp(topic + topic_len - mode_static_len, "/static")) { if (length != 3) { return ESP_FAIL; } char str_id[6] = {0}; strncpy(str_id, topic + topic_len - mode_static_len - 5, 5); int id = strtol(str_id, NULL, 10); if (id >= CONFIG_SEGMENT_COUNT) { return ESP_FAIL; } for (int i = 0; i < CONFIG_SEGMENT_SIZE; i++) { ESP_ERROR_CHECK( led_strip_set_pixel(led_strip, id * CONFIG_SEGMENT_SIZE + i, limit(payload[0]), limit(payload[1]), limit(payload[2]))); } ESP_ERROR_CHECK(led_strip_refresh(led_strip)); led_color[id][0] = MODE_STATIC; led_color[id][1] = limit(payload[0]); led_color[id][2] = limit(payload[1]); led_color[id][3] = limit(payload[2]); ESP_LOGI("mqtt_handler", "set segment %d to static color %d %d %d", id, led_color[id][1], led_color[id][2], led_color[id][3]); } if (!strcmp(topic + topic_len - mode_pulse_len, "/pulse")) { if (length != 3) { return ESP_FAIL; } char str_id[6] = {0}; strncpy(str_id, topic + topic_len - mode_pulse_len - 5, 5); int id = strtol(str_id, NULL, 10); if (id >= CONFIG_SEGMENT_COUNT) { return ESP_FAIL; } led_color[id][0] = MODE_PULSE; led_color[id][1] = limit(payload[0]); led_color[id][2] = limit(payload[1]); led_color[id][3] = limit(payload[2]); ESP_LOGI("mqtt_handler", "set segment %d to pulse color %d %d %d", id, led_color[id][1], led_color[id][2], led_color[id][3]); } if (!strcmp(topic + topic_len - mode_blink_len, "/blink")) { if (length != 3) { return ESP_FAIL; } char str_id[6] = {0}; strncpy(str_id, topic + topic_len - mode_blink_len - 5, 5); int id = strtol(str_id, NULL, 10); if (id >= CONFIG_SEGMENT_COUNT) { return ESP_FAIL; } led_color[id][0] = MODE_BLINK; led_color[id][1] = limit(payload[0]); led_color[id][2] = limit(payload[1]); led_color[id][3] = limit(payload[2]); ESP_LOGI("mqtt_handler", "set segment %d to blink color %d %d %d", id, led_color[id][1], led_color[id][2], led_color[id][3]); } if (!strcmp(topic + topic_len - mode_fill_len, "/fill")) { if (length != 3) { return ESP_FAIL; } char str_id[6] = {0}; strncpy(str_id, topic + topic_len - mode_fill_len - 5, 5); int id = strtol(str_id, NULL, 10); if (id >= CONFIG_SEGMENT_COUNT) { return ESP_FAIL; } led_color[id][0] = MODE_FILL; led_color[id][1] = limit(payload[0]); led_color[id][2] = limit(payload[1]); led_color[id][3] = limit(payload[2]); ESP_LOGI("mqtt_handler", "set segment %d to fill color %d %d %d", id, led_color[id][1], led_color[id][2], led_color[id][3]); } if (!strcmp(topic + topic_len - mode_walk_len, "/walk")) { if (length != 3) { return ESP_FAIL; } char str_id[6] = {0}; strncpy(str_id, topic + topic_len - mode_walk_len - 5, 5); int id = strtol(str_id, NULL, 10); if (id >= CONFIG_SEGMENT_COUNT) { return ESP_FAIL; } led_color[id][0] = MODE_WALK; led_color[id][1] = limit(payload[0]); led_color[id][2] = limit(payload[1]); led_color[id][3] = limit(payload[2]); ESP_LOGI("mqtt_handler", "set segment %d to walk color %d %d %d", id, led_color[id][1], led_color[id][2], led_color[id][3]); } if (!strcmp(topic + topic_len - buzzer_len, "/buzzer")) { if (length != 1) { return ESP_FAIL; } if (payload[0] == 0) { gpio_set_level(CONFIG_PIN_BUZZER, 0); } else { gpio_set_level(CONFIG_PIN_BUZZER, 1); } ESP_LOGI("mqtt_handler", "set buzzer to %d", payload[0]); } if (!strcmp(topic + topic_len - sync_len, "/sync")) { uint64_t time = esp_timer_get_time(); last_time_blink = time; last_time_pulse = time; last_time_walk_fill = time; value_blink = 0; value_pulse = 0; value_walk_fill = 0; direction_pulse = 0; ESP_LOGI("mqtt_handler", "sync"); } if (!strcmp(topic + topic_len - clear_len, "/clear")) { ESP_ERROR_CHECK(led_strip_clear(led_strip)); // ESP_ERROR_CHECK(led_strip_refresh(led_strip)); for (int i = 0; i < CONFIG_SEGMENT_COUNT; i++) { led_color[i][0] = MODE_OFF; led_color[i][1] = 0; led_color[i][2] = 0; led_color[i][3] = 0; } ESP_LOGI("mqtt_handler", "clear"); } ESP_LOGI("mqtt_handler", "topic: %s", topic); return ESP_OK; } _Noreturn void app_main(void) { init_led_strip(); ESP_ERROR_CHECK(led_strip_set_pixel(debug_led, 0, LIMIT, 0, 0)); ESP_ERROR_CHECK(led_strip_refresh(debug_led)); init_gpio(); init_eventloop(); init_nvs(); init_wifi(); while (1) { if (esp_timer_get_time() > last_time_blink + refresh_blink) { last_time_blink = esp_timer_get_time(); value_blink = value_blink == 0 ? 1 : 0; for (int i = 0; i < CONFIG_SEGMENT_COUNT; i++) { if (led_color[i][0] == MODE_BLINK) { for (int e = 0; e < CONFIG_SEGMENT_SIZE; e++) { ESP_ERROR_CHECK(led_strip_set_pixel(led_strip, i * CONFIG_SEGMENT_SIZE + e, led_color[i][1] * value_blink, led_color[i][2] * value_blink, led_color[i][3] * value_blink)); } } } ESP_ERROR_CHECK(led_strip_refresh(led_strip)); } // PULSE if (esp_timer_get_time() > last_time_pulse + refresh_pulse) { last_time_pulse = esp_timer_get_time(); if (direction_pulse == 0) { value_pulse--; if (value_pulse <= 0) { direction_pulse = 1; } } else { value_pulse++; if (value_pulse >= 255) { direction_pulse = 0; } } for (int i = 0; i < CONFIG_SEGMENT_COUNT; i++) { if (led_color[i][0] == MODE_PULSE) { for (int e = 0; e < CONFIG_SEGMENT_SIZE; e++) { ESP_ERROR_CHECK(led_strip_set_pixel(led_strip, i * CONFIG_SEGMENT_SIZE + e, (value_pulse * led_color[i][1]) / 255, (value_pulse * led_color[i][2]) / 255, (value_pulse * led_color[i][3]) / 255)); } } } ESP_ERROR_CHECK(led_strip_refresh(led_strip)); } // WALK or FILL if (esp_timer_get_time() > last_time_walk_fill + refresh_walk_fill) { last_time_walk_fill = esp_timer_get_time(); if (value_walk_fill >= CONFIG_SEGMENT_COUNT) { value_walk_fill = 0; for (int i = 0; i < CONFIG_SEGMENT_COUNT; i++) { if (led_color[i][0] == MODE_FILL) { for (int e = 0; e < CONFIG_SEGMENT_SIZE; e++) { ESP_ERROR_CHECK(led_strip_set_pixel(led_strip, i * CONFIG_SEGMENT_SIZE + e, 0, 0, 0)); } } } } else { value_walk_fill++; if (value_walk_fill == CONFIG_SEGMENT_COUNT) { for (int i = 0; i < CONFIG_SEGMENT_COUNT; i++) { if (led_color[i][0] == MODE_FILL) { for (int e = 0; e < CONFIG_SEGMENT_SIZE; e++) { ESP_ERROR_CHECK(led_strip_set_pixel(led_strip, i * CONFIG_SEGMENT_SIZE + e, 0, 0, 0)); } } } } } if (led_color[value_walk_fill][0] == MODE_FILL || led_color[value_walk_fill][0] == MODE_WALK) { for (int e = 0; e < CONFIG_SEGMENT_SIZE; e++) { ESP_ERROR_CHECK(led_strip_set_pixel(led_strip, value_walk_fill * CONFIG_SEGMENT_SIZE + e, led_color[value_walk_fill][1], led_color[value_walk_fill][2], led_color[value_walk_fill][3])); } } if (value_walk_fill > 0 && led_color[value_walk_fill - 1][0] == MODE_WALK) { for (int e = 0; e < CONFIG_SEGMENT_SIZE; e++) { ESP_ERROR_CHECK(led_strip_set_pixel(led_strip, value_walk_fill - 1 * CONFIG_SEGMENT_SIZE + e, 0, 0, 0)); } } ESP_ERROR_CHECK(led_strip_refresh(led_strip)); } } }