diff --git a/Documentation/overview.rst b/Documentation/overview.rst
index 159d7450b8df682f4c2939ed9af5f4709a9198e8..26e8baa295b63dd80c98b4550a5421c73aaf458a 100644
--- a/Documentation/overview.rst
+++ b/Documentation/overview.rst
@@ -29,8 +29,6 @@ number of tasks that will have been keeping card10 running.  These are:
 +--------------------+-------------------------------+----------+-------------------------------------------+
 | `vInterruptsTask`_ | ``interrupts_task`` (static)  | +2       | Interrupt dispatcher worker               |
 +--------------------+-------------------------------+----------+-------------------------------------------+
-| `vLedTask`_        | -/-                           | +1       | LED Animations                            |
-+--------------------+-------------------------------+----------+-------------------------------------------+
 | `vMAX30001Task`_   | ``max30001_task_id`` (static) | +1       | `MAX30001`_ ECG driver                    |
 +--------------------+-------------------------------+----------+-------------------------------------------+
 | `vBhi160Task`_     | ``bhi160_task_id`` (static)   | +1       | `BHI160`_ sensor fusion driver            |
diff --git a/epicardium/main.c b/epicardium/main.c
index 44c26255aac1848c2d8b1268b12402e28bb6d105..3f0f291fef2158ae869ee6d077e24376877300db 100644
--- a/epicardium/main.c
+++ b/epicardium/main.c
@@ -172,17 +172,6 @@ int main(void)
 		}
 	}
 
-	/* LEDs */
-	if (xTaskCreate(
-		    vLedTask,
-		    (const char *)"LED",
-		    configMINIMAL_STACK_SIZE,
-		    NULL,
-		    tskIDLE_PRIORITY + 1,
-		    NULL) != pdPASS) {
-		panic("Failed to create %s task!", "LED");
-	}
-
 	/* Lifecycle */
 	if (xTaskCreate(
 		    vLifecycleTask,
diff --git a/epicardium/modules/personal_state.c b/epicardium/modules/personal_state.c
index 285cb872dd37315bf1bb5fd227e6cab3bd968f74..a2a1730cba4fd4a6b45c1463bad8b7ad8bdfb7f4 100644
--- a/epicardium/modules/personal_state.c
+++ b/epicardium/modules/personal_state.c
@@ -2,20 +2,36 @@
 #include "leds.h"
 #include "modules.h"
 
+#include "os/work_queue.h"
+
+#include "FreeRTOS.h"
+#include "timers.h"
+
 #include <math.h>
 
-uint8_t _personal_state_enabled   = 0;
-uint8_t personal_state            = STATE_NONE;
-uint8_t personal_state_persistent = 0;
+static uint8_t _personal_state_enabled   = 0;
+static uint8_t personal_state            = STATE_NONE;
+static uint8_t personal_state_persistent = 0;
+
+static int led_animation_ticks = 0;
+static int led_animation_state = 0;
 
-int led_animation_ticks = 0;
-int led_animation_state = 0;
+static TimerHandle_t led_timer;
+static StaticTimer_t led_timer_buffer;
+static void worktick(void *data);
+
+static const int led_animation_rate = 1000 / 25; /* 25Hz -> 40ms*/
 
 int personal_state_enabled()
 {
 	return _personal_state_enabled;
 }
 
+static void tick(TimerHandle_t xTimer)
+{
+	workqueue_schedule(worktick, NULL);
+}
+
 int epic_personal_state_set(uint8_t state, bool persistent)
 {
 	if (state > STATE_CAMP)
@@ -30,7 +46,29 @@ int epic_personal_state_set(uint8_t state, bool persistent)
 	_personal_state_enabled   = (state != STATE_NONE);
 	personal_state_persistent = persistent;
 
-	if (was_enabled && !_personal_state_enabled) {
+	if (!was_enabled && _personal_state_enabled) {
+		// Activate
+		if (!led_timer) {
+			led_timer = xTimerCreateStatic(
+				"personal state",
+				led_animation_rate / portTICK_PERIOD_MS,
+				pdTRUE,
+				NULL,
+				tick,
+				&led_timer_buffer
+			);
+			// since &poll_timer_buffer is not NULL, xTimerCreateStatic should allways succeed, so
+			// we don't need to check for poll_timer being NULL.
+		}
+
+		if (xTimerIsTimerActive(led_timer) == pdFALSE) {
+			xTimerStart(led_timer, 0);
+		}
+
+	} else if (was_enabled && !_personal_state_enabled) {
+		// Deactivate
+		xTimerStop(led_timer, 0);
+		// TODO: we might need a lock here to avoid a race condition
 		leds_prep(PERSONAL_STATE_LED, 0, 0, 0);
 		epic_leds_update();
 	}
@@ -48,80 +86,68 @@ int epic_personal_state_is_persistent()
 	return personal_state_persistent;
 }
 
-void vLedTask(void *pvParameters)
+static void worktick(void *data)
 {
-	const int led_animation_rate = 1000 / 25; /* 25Hz -> 40ms*/
-	while (1) {
-		if (_personal_state_enabled) {
-			led_animation_ticks++;
-			if (personal_state == STATE_NO_CONTACT) {
-				leds_prep(PERSONAL_STATE_LED, 255, 0, 0);
-			} else if (personal_state == STATE_CHAOS) {
-				if (led_animation_state == 0) {
-					leds_prep(
-						PERSONAL_STATE_LED, 0, 0, 255
-					);
-					if (led_animation_ticks >
-					    (200 / led_animation_rate)) {
-						led_animation_ticks = 0;
-						led_animation_state = 1;
-					}
-				} else if (led_animation_state == 1) {
-					leds_prep(PERSONAL_STATE_LED, 0, 0, 0);
-					if (led_animation_ticks >
-					    (300 / led_animation_rate)) {
-						led_animation_ticks = 0;
-						led_animation_state = 2;
-					}
-				} else if (led_animation_state == 2) {
-					leds_prep(
-						PERSONAL_STATE_LED, 0, 0, 255
-					);
-					if (led_animation_ticks >
-					    (1000 / led_animation_rate)) {
-						led_animation_ticks = 0;
-						led_animation_state = 3;
-					}
-				} else if (led_animation_state == 3) {
-					leds_prep(PERSONAL_STATE_LED, 0, 0, 0);
-					if (led_animation_ticks >
-					    (300 / led_animation_rate)) {
-						led_animation_ticks = 0;
-						led_animation_state = 0;
-					}
+	if (_personal_state_enabled) {
+		led_animation_ticks++;
+		if (personal_state == STATE_NO_CONTACT) {
+			leds_prep(PERSONAL_STATE_LED, 255, 0, 0);
+		} else if (personal_state == STATE_CHAOS) {
+			if (led_animation_state == 0) {
+				leds_prep(PERSONAL_STATE_LED, 0, 0, 255);
+				if (led_animation_ticks >
+				    (200 / led_animation_rate)) {
+					led_animation_ticks = 0;
+					led_animation_state = 1;
+				}
+			} else if (led_animation_state == 1) {
+				leds_prep(PERSONAL_STATE_LED, 0, 0, 0);
+				if (led_animation_ticks >
+				    (300 / led_animation_rate)) {
+					led_animation_ticks = 0;
+					led_animation_state = 2;
 				}
-			} else if (personal_state == STATE_COMMUNICATION) {
-				if (led_animation_state == 0) {
-					leds_prep(
-						PERSONAL_STATE_LED, 255, 255, 0
-					);
-					if (led_animation_ticks >
-					    (1000 / led_animation_rate)) {
-						led_animation_ticks = 0;
-						led_animation_state = 1;
-					}
-				} else if (led_animation_state == 1) {
-					leds_prep(PERSONAL_STATE_LED, 0, 0, 0);
-					if (led_animation_ticks >
-					    (300 / led_animation_rate)) {
-						led_animation_ticks = 0;
-						led_animation_state = 0;
-					}
+			} else if (led_animation_state == 2) {
+				leds_prep(PERSONAL_STATE_LED, 0, 0, 255);
+				if (led_animation_ticks >
+				    (1000 / led_animation_rate)) {
+					led_animation_ticks = 0;
+					led_animation_state = 3;
+				}
+			} else if (led_animation_state == 3) {
+				leds_prep(PERSONAL_STATE_LED, 0, 0, 0);
+				if (led_animation_ticks >
+				    (300 / led_animation_rate)) {
+					led_animation_ticks = 0;
+					led_animation_state = 0;
 				}
-			} else if (personal_state == STATE_CAMP) {
-				leds_prep_hsv(
-					PERSONAL_STATE_LED,
-					120.0f,
-					1.0f,
-					fabs(sin(
-						led_animation_ticks /
-						(float)(1000 /
-							led_animation_rate))));
 			}
-
-			epic_leds_update();
+		} else if (personal_state == STATE_COMMUNICATION) {
+			if (led_animation_state == 0) {
+				leds_prep(PERSONAL_STATE_LED, 255, 255, 0);
+				if (led_animation_ticks >
+				    (1000 / led_animation_rate)) {
+					led_animation_ticks = 0;
+					led_animation_state = 1;
+				}
+			} else if (led_animation_state == 1) {
+				leds_prep(PERSONAL_STATE_LED, 0, 0, 0);
+				if (led_animation_ticks >
+				    (300 / led_animation_rate)) {
+					led_animation_ticks = 0;
+					led_animation_state = 0;
+				}
+			}
+		} else if (personal_state == STATE_CAMP) {
+			leds_prep_hsv(
+				PERSONAL_STATE_LED,
+				120.0f,
+				1.0f,
+				fabs(sin(
+					led_animation_ticks /
+					(float)(1000 / led_animation_rate))));
 		}
 
-		vTaskDelay(led_animation_rate / portTICK_PERIOD_MS);
+		epic_leds_update();
 	}
 }