Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,28 @@ config WIFI_WATCHDOG_CHECK_INTERVAL_SECONDS
help
Interval in seconds between WiFi watchdog checks.

choice HEARTBEAT_LED_TYPE
prompt "Select heartbeat LED variant"
default HEARTBEAT_LED_STRIP if $(dt_alias_enabled,led-strip)
default HEARTBEAT_LED_GPIO if $(dt_alias_enabled,heartbeat-led)

config HEARTBEAT_LED_STRIP
bool "WS2812 RGB LED strip"
depends on LED_STRIP
help
Enable heartbeat LED using a WS2812 RGB LED strip.
GREEN when WiFi is connected, RED otherwise.
Blinks at a 1-second interval.

config HEARTBEAT_LED_GPIO
bool "GPIO LED"
depends on GPIO
help
Enable heartbeat LED using a simple GPIO-driven LED.
Blinks at a 1-second interval unconditionally.

endchoice

endmenu

config MEMFAULT_BUILTIN_DEVICE_INFO_SOFTWARE_VERSION
Expand Down
38 changes: 38 additions & 0 deletions boards/esp32c3_devkitm.overlay
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,48 @@
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/dt-bindings/led/led.h>

&wifi {
status = "okay";
};

&coretemp {
status = "okay";
};

/ {
aliases {
led-strip = &led_strip;
};
};

/* WS2812 RGB LED on GPIO8 via SPI2 */
&spi2 {
/* Workaround to support WS2812 driver */
line-idle-low;

led_strip: ws2812@0 {
compatible = "worldsemi,ws2812-spi";

/* SPI */
reg = <0>;
spi-max-frequency = <6400000>;

/* WS2812 */
chain-length = <1>;
spi-cpha;
spi-one-frame = <0xf0>;
spi-zero-frame = <0xc0>;
color-mapping = <LED_COLOR_ID_GREEN LED_COLOR_ID_RED LED_COLOR_ID_BLUE>;
};
};

&pinctrl {
spim2_default: spim2_default {
group2 {
/* WS2812 data on GPIO8 */
pinmux = <SPIM2_MOSI_GPIO8>;
};
};
};
3 changes: 3 additions & 0 deletions boards/esp32c6_devkitc_esp32c6_hpcore.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Enable DMA for heartbeat LED
CONFIG_DMA=y
CONFIG_LED_STRIP=y
35 changes: 35 additions & 0 deletions boards/esp32c6_devkitc_esp32c6_hpcore.overlay
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,45 @@
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/dt-bindings/led/led.h>

&wifi {
status = "okay";
};

&coretemp {
status = "okay";
};

/ {
aliases {
led-strip = &led_strip;
};
};

/* WS2812 RGB LED on GPIO8 via I2S */
&i2s_default {
group1 {
pinmux = <I2S_O_SD_GPIO8>;
};
};

i2s_led: &i2s {
status = "okay";

dmas = <&dma 3>;
dma-names = "tx";

led_strip: ws2812@0 {
compatible = "worldsemi,ws2812-i2s";

reg = <0>;
chain-length = <1>;
color-mapping = <LED_COLOR_ID_GREEN LED_COLOR_ID_RED LED_COLOR_ID_BLUE>;
reset-delay = <500>;
};
};

&dma {
status = "okay";
};
4 changes: 4 additions & 0 deletions boards/esp32s3_devkitc_esp32s3_procpu.conf
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Enable external SPI RAM
CONFIG_SPI=y
CONFIG_ESP_SPIRAM=y

# Enable DMA for heartbeat LED
CONFIG_DMA=y
CONFIG_LED_STRIP=y
35 changes: 35 additions & 0 deletions boards/esp32s3_devkitc_esp32s3_procpu.overlay
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/dt-bindings/led/led.h>

&wifi {
status = "okay";
};
Expand All @@ -16,3 +18,36 @@
&psram0 {
size = <DT_SIZE_M(2)>;
};

/ {
aliases {
led-strip = &led_strip;
};
};

/* WS2812 RGB LED on GPIO38 via I2S0 */
&i2s0_default {
group1 {
pinmux = <I2S0_O_SD_GPIO38>;
};
};

i2s_led: &i2s0 {
status = "okay";

dmas = <&dma 3>;
dma-names = "tx";

led_strip: ws2812@0 {
compatible = "worldsemi,ws2812-i2s";

reg = <0>;
chain-length = <1>;
color-mapping = <LED_COLOR_ID_GREEN LED_COLOR_ID_RED LED_COLOR_ID_BLUE>;
reset-delay = <500>;
};
};

&dma {
status = "okay";
};
3 changes: 3 additions & 0 deletions boards/esp32s3_devkitm_esp32s3_procpu.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Enable DMA + LED_STRIP for heartbeat LED
CONFIG_DMA=y
CONFIG_LED_STRIP=y
38 changes: 38 additions & 0 deletions boards/esp32s3_devkitm_esp32s3_procpu.overlay
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,48 @@
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/dt-bindings/led/led.h>

&wifi {
status = "okay";
};

&coretemp {
status = "okay";
};

/ {
aliases {
led-strip = &led_strip;
};
};

/* WS2812 RGB LED on GPIO48 via SPI3 */
&spi3 {
/* Workaround to support WS2812 driver */
line-idle-low;

led_strip: ws2812@0 {
compatible = "worldsemi,ws2812-spi";

/* SPI */
reg = <0>;
spi-max-frequency = <6400000>;

/* WS2812 */
chain-length = <1>;
spi-cpha;
spi-one-frame = <0xf0>;
spi-zero-frame = <0xc0>;
color-mapping = <LED_COLOR_ID_GREEN LED_COLOR_ID_RED LED_COLOR_ID_BLUE>;
};
};

&pinctrl {
spim3_default: spim3_default {
group2 {
/* WS2812 data on GPIO48 */
pinmux = <SPIM3_MOSI_GPIO48>;
};
};
};
1 change: 1 addition & 0 deletions boards/esp_wrover_kit_procpu.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CONFIG_HEARTBEAT_LED_GPIO=y
6 changes: 6 additions & 0 deletions boards/esp_wrover_kit_procpu.overlay
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
&wifi {
status = "okay";
};

/ {
aliases {
heartbeat-led = &red_led;
};
};
130 changes: 130 additions & 0 deletions src/heartbeat_led.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
//! @file
//!
//! @brief Heartbeat LED module.
//!
//! Blinks an LED at a 1-second interval to indicate the device is running.
//!
//! On boards with a WS2812 RGB LED (CONFIG_HEARTBEAT_LED_STRIP):
//! - GREEN when WiFi is connected
//! - RED when WiFi is not connected
//!
//! On boards with a simple GPIO LED (CONFIG_HEARTBEAT_LED_GPIO):
//! - Blinks unconditionally at a 1-second interval

#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>

LOG_MODULE_REGISTER(heartbeat_led, LOG_LEVEL_DBG);

// Blink half-period: 500ms on + 500ms off = 1 second total
#define HEARTBEAT_BLINK_HALF_PERIOD_MS 500

#define HEARTBEAT_LED_STACK_SIZE 1024
#define HEARTBEAT_LED_PRIORITY 7

#if defined(CONFIG_HEARTBEAT_LED_STRIP)

#include <zephyr/drivers/led_strip.h>
#include <zephyr/net/net_mgmt.h>
#include <zephyr/net/wifi_mgmt.h>

static const struct device* s_led_strip = DEVICE_DT_GET(DT_ALIAS(led_strip));

static bool s_wifi_connected = false;
static struct net_mgmt_event_callback s_wifi_led_mgmt_cb;

// WiFi event handler to track connection state
static void prv_wifi_event_handler(struct net_mgmt_event_callback* cb,
uint64_t mgmt_event, struct net_if* iface) {
ARG_UNUSED(iface);
if (mgmt_event == NET_EVENT_WIFI_CONNECT_RESULT) {
const struct wifi_status* status = (const struct wifi_status*)cb->info;
s_wifi_connected = (status->status == 0);
LOG_DBG("Heartbeat LED: WiFi %s",
s_wifi_connected ? "connected" : "failed");
} else if (mgmt_event == NET_EVENT_WIFI_DISCONNECT_RESULT) {
s_wifi_connected = false;
LOG_DBG("Heartbeat LED: WiFi disconnected");
}
}

static void prv_heartbeat_led_thread(void* arg1, void* arg2, void* arg3) {
ARG_UNUSED(arg1);
ARG_UNUSED(arg2);
ARG_UNUSED(arg3);

net_mgmt_init_event_callback(
&s_wifi_led_mgmt_cb, prv_wifi_event_handler,
NET_EVENT_WIFI_CONNECT_RESULT | NET_EVENT_WIFI_DISCONNECT_RESULT);
net_mgmt_add_event_callback(&s_wifi_led_mgmt_cb);

if (!device_is_ready(s_led_strip)) {
LOG_ERR("LED strip device not ready");
return;
}

bool led_on = false;
while (1) {
led_on = !led_on;

struct led_rgb pixel;
if (led_on) {
if (s_wifi_connected) {
// Green: WiFi connected
pixel = (struct led_rgb){.r = 0, .g = 32, .b = 0};
} else {
// Red: WiFi not connected
pixel = (struct led_rgb){.r = 32, .g = 0, .b = 0};
}
} else {
// Off
pixel = (struct led_rgb){.r = 0, .g = 0, .b = 0};
}

int rc = led_strip_update_rgb(s_led_strip, &pixel, 1);
if (rc != 0) {
LOG_ERR("LED strip update failed: %d", rc);
}

k_sleep(K_MSEC(HEARTBEAT_BLINK_HALF_PERIOD_MS));
}
}

#elif defined(CONFIG_HEARTBEAT_LED_GPIO)

#include <zephyr/drivers/gpio.h>

static const struct gpio_dt_spec s_led =
GPIO_DT_SPEC_GET(DT_ALIAS(heartbeat_led), gpios);

static void prv_heartbeat_led_thread(void* arg1, void* arg2, void* arg3) {
ARG_UNUSED(arg1);
ARG_UNUSED(arg2);
ARG_UNUSED(arg3);

if (!gpio_is_ready_dt(&s_led)) {
LOG_ERR("Heartbeat LED GPIO not ready");
return;
}

int rc = gpio_pin_configure_dt(&s_led, GPIO_OUTPUT_INACTIVE);
if (rc != 0) {
LOG_ERR("Failed to configure heartbeat LED GPIO: %d", rc);
return;
}

while (1) {
gpio_pin_toggle_dt(&s_led);
k_sleep(K_MSEC(HEARTBEAT_BLINK_HALF_PERIOD_MS));
}
}

#endif /* CONFIG_HEARTBEAT_LED_STRIP / CONFIG_HEARTBEAT_LED_GPIO */

#if defined(CONFIG_HEARTBEAT_LED_STRIP) || defined(CONFIG_HEARTBEAT_LED_GPIO)

K_THREAD_DEFINE(heartbeat_led_thread, HEARTBEAT_LED_STACK_SIZE,
prv_heartbeat_led_thread, NULL, NULL, NULL,
HEARTBEAT_LED_PRIORITY, 0, 0);

#endif /* CONFIG_HEARTBEAT_LED_STRIP || CONFIG_HEARTBEAT_LED_GPIO */