diff --git a/src/shared/events/Event.h b/src/shared/events/Event.h
index c38ec3ef83a50bbbe86428bbb8f7d9e89a363427..62a2ab6a86907ce8b5d1d1f97ae2b0bc29e0a542 100644
--- a/src/shared/events/Event.h
+++ b/src/shared/events/Event.h
@@ -31,11 +31,12 @@ typedef uint8_t Event;
 
 enum BasicEvent : Event
 {
-    EV_ENTRY        = 0,
-    EV_EXIT         = 1,
-    EV_EMPTY        = 2,
-    EV_INIT         = 3,
-    EV_FIRST_CUSTOM = 4
+    EV_ENTRY          = 0,
+    EV_EXIT           = 1,
+    EV_EMPTY          = 2,
+    EV_INIT           = 3,
+    EV_ASYNC_CONTINUE = 4,  // Async continuation support
+    EV_FIRST_CUSTOM   = 5
 };
 
 /**
diff --git a/src/shared/events/FSM.h b/src/shared/events/FSM.h
index f4ed570643450d16616eafcd84c575b02b6de859..35a3626beabe70990d4d6978b1e096c6a55ddc28 100644
--- a/src/shared/events/FSM.h
+++ b/src/shared/events/FSM.h
@@ -56,6 +56,11 @@ protected:
 private:
     void (T::*state)(const Event&);
     Event specialEvent;
+
+protected:
+    // Async continuation support
+    uint16_t asyncDelayedEventId;
+    uint32_t asyncContinuation = 0;
 };
 
 template <class T>
@@ -93,4 +98,89 @@ void FSM<T>::handleEvent(const Event& e)
     (static_cast<T*>(this)->*state)(e);
 }
 
+/**
+ * @brief Asynchronous continuation support.
+ *
+ * The following macros provide support for asynchronous continuation inside a
+ * single state handler. This allows to write code that will be executed
+ * asynchronously just like synchronous code, without the need to split the
+ * logic into multiple states or switch-case branches.
+ *
+ * This enables stateful algorithms to be written in a more linear and readable
+ * way.
+ *
+ * @example
+ *  void MyFSM::state_myState(const Event& event) {
+ *      ASYNC_BEGIN(TOPIC_MY_FSM);
+ *      ...some operation...
+ *      ASYNC_WAIT_FOR(1000);
+ *
+ *      // Code executed after 1 second
+ *      ...some other operation...
+ *      ASYNC_WAIT_FOR(2000);
+ *
+ *      // Code executed after 2 seconds
+ *     ...some other operation...
+ *      ASYNC_END();
+ *  }
+ */
+
+/**
+ * @brief Begins an asynchronous continuation context.
+ *
+ * This macro defines the beginning of a region of code that will be executed
+ * asynchronously. When a wait point is reached, the FSM will return from the
+ * state handler and resume from the waiting point at a later time.
+ *
+ * @note This macro must be followed by an ASYNC_END() macro.
+ * @note Local variables are not preserved between wait points, so they must be
+ * declared as class members.
+ * @note All macros assume that the name of the event parameter is "event".
+ *
+ * @param _topic Topic to use for the async events. Must be unique for each FSM.
+ */
+#define ASYNC_BEGIN(_topic)                                            \
+    constexpr auto _ASYNC_TOPIC = _topic;                              \
+    /* Reset async continuation if entering the state */               \
+    if (event == EV_ENTRY)                                             \
+        asyncContinuation = 0;                                         \
+    /* Remove any pending async event if exiting the state */          \
+    if (event == EV_EXIT)                                              \
+        EventBroker::getInstance().removeDelayed(asyncDelayedEventId); \
+                                                                       \
+    if (event == EV_ASYNC_CONTINUE || event == EV_ENTRY)               \
+    {                                                                  \
+        switch (asyncContinuation)                                     \
+        {                                                              \
+            case 0:
+
+/**
+ * @brief Ends an asynchronous continuation context.
+ *
+ * This macro defines the end of an asynchronous continuation context.
+ */
+#define ASYNC_END() \
+    } /* switch */  \
+    return;         \
+    } /* if */
+
+/**
+ * @brief Waits for the specified amount of time before continuing execution.
+ *
+ * This macro defines a wait point in an asynchronous continuation context.
+ * When this point is reached, the FSM will post a delayed EV_ASYNC_CONTINUE
+ * event of the specified time and return from the state handler. When the
+ * delayed event is finally received, the FSM will resume execution from the
+ * waiting point.
+ */
+#define ASYNC_WAIT_FOR(t)                                                 \
+    case __LINE__:                                                        \
+        if (asyncContinuation != __LINE__)                                \
+        {                                                                 \
+            asyncContinuation   = __LINE__;                               \
+            asyncDelayedEventId = EventBroker::getInstance().postDelayed( \
+                EV_ASYNC_CONTINUE, _ASYNC_TOPIC, t);                      \
+            return;                                                       \
+        }
+
 }  // namespace Boardcore