#include "driver/gpio.h" #include "driver/rmt_tx.h" #include "esp_log.h" #include "esp_now.h" #include "esp_system.h" #include "esp_timer.h" #include "esp_wifi.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "led_strip_encoder.h" #include "nvs_flash.h" #include #include #include #define LED_STRIP_GPIO_NUM 8 #define LED_STRIP_LED_NUMBERS 8 #define LED_STRIP_RMT_RES_HZ \ (10 * 1000 * 1000) // 10MHz resolution, 1 tick = 0.1us #define BUTTON_GPIO_NUM 7 #define ESPNOW_WIFI_CHANNEL 6 static const char *TAG = "DinnerRecv"; static const uint8_t dinner_send_mac[6] = {0x08, 0x3A, 0xF2, 0x3E, 0x6D, 0x34}; // Global variables static bool dinner_alert = false; static uint64_t dinner_start_time = 0; static uint64_t last_blink_time = 0; static bool led_state = false; static uint8_t breath = 0; static uint64_t last_breath_time = 0; static bool button_pressed = false; static uint64_t last_button_time = 0; // RMT and LED strip handles static rmt_channel_handle_t led_chan = NULL; static rmt_encoder_handle_t led_encoder = NULL; typedef struct { uint8_t r; uint8_t g; uint8_t b; } led_color_t; static led_color_t led_strip_buf[LED_STRIP_LED_NUMBERS]; static void led_strip_set_pixel(int index, uint8_t red, uint8_t green, uint8_t blue) { if (index < LED_STRIP_LED_NUMBERS) { led_strip_buf[index].r = red; led_strip_buf[index].g = green; led_strip_buf[index].b = blue; } } static void led_strip_clear(void) { for (int i = 0; i < LED_STRIP_LED_NUMBERS; i++) { led_strip_set_pixel(i, 0, 0, 0); } } static void led_strip_show(void) { rmt_transmit_config_t tx_config = { .loop_count = 0, // no transfer loop }; rmt_transmit(led_chan, led_encoder, led_strip_buf, sizeof(led_strip_buf), &tx_config); rmt_tx_wait_all_done(led_chan, portMAX_DELAY); } static uint64_t get_time_ms(void) { return esp_timer_get_time() / 1000; } static void on_data_received(const esp_now_recv_info_t *mac_addr, const uint8_t *data, int data_len) { char message[data_len + 1]; memcpy(message, data, data_len); message[data_len] = '\0'; ESP_LOGI(TAG, "Received message: %s", message); if (strcmp(message, "dinner") == 0) { ESP_LOGI(TAG, "Dinner alert received!"); dinner_alert = true; dinner_start_time = get_time_ms(); last_blink_time = get_time_ms(); led_state = false; } } static void blink_all_leds(void) { if (get_time_ms() - last_blink_time >= 500) { last_blink_time = get_time_ms(); led_state = !led_state; if (led_state) { for (int i = 0; i < LED_STRIP_LED_NUMBERS; i++) { led_strip_set_pixel(i, 0, 0, 255); // G R B } } else { led_strip_clear(); } led_strip_show(); } } static void breathe(void) { uint8_t val = breath > 128 ? 256 - breath : breath; for (int i = 0; i < LED_STRIP_LED_NUMBERS; i++) { led_strip_set_pixel(i, val, val, val); } led_strip_show(); } static void dinner_animation(void) { if (get_time_ms() - dinner_start_time > 20 * 1000) { dinner_alert = false; ESP_LOGI(TAG, "Dinner alert timeout - returning to normal mode"); led_strip_clear(); led_strip_show(); } else { blink_all_leds(); } } static void breath_animation(void) { uint64_t time = get_time_ms(); if (time % 25 == 0 && time != last_breath_time) { breath++; last_breath_time = time; breathe(); } } static esp_err_t init_wifi(void) { 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); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&cfg)); ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); ESP_ERROR_CHECK(esp_wifi_start()); ESP_ERROR_CHECK( esp_wifi_set_channel(ESPNOW_WIFI_CHANNEL, WIFI_SECOND_CHAN_NONE)); return ESP_OK; } static esp_err_t send_dinner_ack(void) { const char *message = "dinner-ack"; esp_err_t ret = esp_now_send(dinner_send_mac, (const uint8_t *)message, strlen(message)); if (ret != ESP_OK) { ESP_LOGE(TAG, "Failed to send dinner-ack: %s", esp_err_to_name(ret)); return ret; } ESP_LOGI(TAG, "Sent dinner-ack message"); return ESP_OK; } static esp_err_t init_espnow(void) { esp_err_t ret = esp_now_init(); if (ret != ESP_OK) { ESP_LOGE(TAG, "Error initializing ESP-NOW: %s", esp_err_to_name(ret)); return ret; } ESP_ERROR_CHECK(esp_now_register_recv_cb(on_data_received)); ESP_LOGI(TAG, "ESP-NOW initialized and ready to receive messages"); ret = esp_now_add_peer(&(esp_now_peer_info_t){ .peer_addr = {dinner_send_mac[0], dinner_send_mac[1], dinner_send_mac[2], dinner_send_mac[3], dinner_send_mac[4], dinner_send_mac[5]}, .channel = ESPNOW_WIFI_CHANNEL, .encrypt = false, }); if (ret != ESP_OK && ret != ESP_ERR_ESPNOW_EXIST) { ESP_LOGE(TAG, "Failed to add peer: %s", esp_err_to_name(ret)); return ret; } ESP_LOGI(TAG, "ESP-NOW sender registered"); return ESP_OK; } static esp_err_t init_led_strip(void) { ESP_LOGI(TAG, "Create RMT TX channel"); rmt_tx_channel_config_t tx_chan_config = { .clk_src = RMT_CLK_SRC_DEFAULT, .gpio_num = LED_STRIP_GPIO_NUM, .mem_block_symbols = 64, .resolution_hz = LED_STRIP_RMT_RES_HZ, .trans_queue_depth = 4, }; ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_chan_config, &led_chan)); ESP_LOGI(TAG, "Install led strip encoder"); led_strip_encoder_config_t encoder_config = { .resolution = LED_STRIP_RMT_RES_HZ, }; ESP_ERROR_CHECK(rmt_new_led_strip_encoder(&encoder_config, &led_encoder)); ESP_LOGI(TAG, "Enable RMT TX channel"); ESP_ERROR_CHECK(rmt_enable(led_chan)); // Clear all LEDs initially led_strip_clear(); led_strip_show(); return ESP_OK; } static esp_err_t init_button(void) { gpio_config_t io_conf = { .pin_bit_mask = (1ULL << BUTTON_GPIO_NUM), .mode = GPIO_MODE_INPUT, .pull_up_en = GPIO_PULLUP_ENABLE, .pull_down_en = GPIO_PULLDOWN_DISABLE, .intr_type = GPIO_INTR_DISABLE, }; ESP_ERROR_CHECK(gpio_config(&io_conf)); ESP_LOGI(TAG, "Button initialized on GPIO %d", BUTTON_GPIO_NUM); return ESP_OK; } static void check_button(void) { int button_level = gpio_get_level(BUTTON_GPIO_NUM); uint64_t current_time = get_time_ms(); if (button_level == 0 && !button_pressed && (current_time - last_button_time > 200)) { button_pressed = true; last_button_time = current_time; ESP_LOGI(TAG, "Button pressed!"); if (dinner_alert) { dinner_alert = false; ESP_LOGI(TAG, "Dinner alert dismissed by button press"); led_strip_clear(); led_strip_show(); send_dinner_ack(); } } else if (button_level == 1) { button_pressed = false; } } void app_main(void) { ESP_LOGI(TAG, "Dinner RECV"); ESP_ERROR_CHECK(init_led_strip()); ESP_ERROR_CHECK(init_button()); ESP_ERROR_CHECK(init_wifi()); ESP_ERROR_CHECK(init_espnow()); // Print WiFi parameters uint8_t mac[6]; esp_wifi_get_mac(WIFI_IF_STA, mac); ESP_LOGI(TAG, "Wi-Fi parameters:"); ESP_LOGI(TAG, " Mode: STA"); ESP_LOGI(TAG, " MAC Address: %02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); ESP_LOGI(TAG, " Channel: %d", ESPNOW_WIFI_CHANNEL); // Main loop while (1) { check_button(); if (dinner_alert) { dinner_animation(); } else { breath_animation(); } vTaskDelay(pdMS_TO_TICKS(10)); // prevent watchdog timeout } }