diff --git a/cmake/boardcore.cmake b/cmake/boardcore.cmake
index 49c7e5016601ccf8571c727c9ad078a644ae2e14..3c6806988b9fcf80a1a9bce82c4536b4ae40033f 100644
--- a/cmake/boardcore.cmake
+++ b/cmake/boardcore.cmake
@@ -97,6 +97,7 @@ foreach(OPT_BOARD ${BOARDS})
         # Utils
         ${SBS_BASE}/src/shared/utils/AeroUtils/AeroUtils.cpp
         ${SBS_BASE}/src/shared/utils/ButtonHandler/ButtonHandler.cpp
+        ${SBS_BASE}/src/shared/utils/PinObserver/PinObserver.cpp
         ${SBS_BASE}/src/shared/utils/SkyQuaternion/SkyQuaternion.cpp
         ${SBS_BASE}/src/shared/utils/Stats/Stats.cpp
         ${SBS_BASE}/src/shared/utils/TestUtils/TestHelper.cpp
diff --git a/src/shared/utils/ButtonHandler/ButtonHandler.cpp b/src/shared/utils/ButtonHandler/ButtonHandler.cpp
index 56a0e5df40a5f5e5b2d12d4c3c80e2f0bfb00774..89c20e726514ba2acc90a9f5ea5d1d2266c3365f 100644
--- a/src/shared/utils/ButtonHandler/ButtonHandler.cpp
+++ b/src/shared/utils/ButtonHandler/ButtonHandler.cpp
@@ -44,6 +44,11 @@ bool ButtonHandler::registerButtonCallback(miosix::GpioPin pin,
     return false;
 }
 
+bool ButtonHandler::unregisterButtonCallback(miosix::GpioPin pin)
+{
+    return callbacks.erase(pin) != 0;
+}
+
 bool ButtonHandler::start() { return scheduler.start(); }
 
 void ButtonHandler::stop() { scheduler.stop(); }
@@ -63,12 +68,12 @@ void ButtonHandler::periodicButtonValueCheck(miosix::GpioPin pin)
     // Retrieve the pin information
     const ButtonCallback &callback = std::get<0>(callbacks[pin]);
     bool &wasPressed               = std::get<1>(callbacks[pin]);
-    int &pressedTicks              = std::get<2>(callbacks[pin]);
+    unsigned int &pressedTicks     = std::get<2>(callbacks[pin]);
 
     // Read the current button status
     // Note: The button is assumed to be pressed if the pin value is low
     // (pulldown)
-    bool isNowPressed = !pin.value();
+    const bool isNowPressed = !pin.value();
 
     if (isNowPressed)
     {
diff --git a/src/shared/utils/ButtonHandler/ButtonHandler.h b/src/shared/utils/ButtonHandler/ButtonHandler.h
index 2ff97a9beb9e4acb7e6e98c45ee88550fa3deddf..8ae5b7d583f10ed726f93e12709bb88279d8e0c3 100644
--- a/src/shared/utils/ButtonHandler/ButtonHandler.h
+++ b/src/shared/utils/ButtonHandler/ButtonHandler.h
@@ -23,8 +23,8 @@
 #pragma once
 
 #include <Singleton.h>
-#include <miosix.h>
 #include <scheduler/TaskScheduler.h>
+#include <utils/GpioPinCompare.h>
 
 #include <map>
 
@@ -33,24 +33,10 @@ namespace Boardcore
 
 enum class ButtonEvent
 {
-    PRESSED,         // Called as soon as the button is pressed
-    SHORT_PRESS,     // Called as soon as the button is released
-    LONG_PRESS,      // Called as soon as the button is released
-    VERY_LONG_PRESS  // Called as soon as the button is released
-};
-
-/**
- * @brief Comparison operator between GpioPins used for std::map.
- */
-struct GpioPinCompare
-{
-    bool operator()(const miosix::GpioPin& lhs,
-                    const miosix::GpioPin& rhs) const
-    {
-        if (lhs.getPort() == rhs.getPort())
-            return lhs.getNumber() < rhs.getNumber();
-        return lhs.getPort() < rhs.getPort();
-    }
+    PRESSED,         ///< The button is pressed.
+    SHORT_PRESS,     ///< The button is released before LONG_PRESS_TICKS.
+    LONG_PRESS,      ///< The button is released before  VERY_LONG_PRESS_TICKS.
+    VERY_LONG_PRESS  ///< The button is released after VERY_LONG_PRESS_TICKS.
 };
 
 /**
@@ -122,17 +108,12 @@ private:
     /**
      * @brief Map of all the callbacks registered in the ButtonHandler.
      *
-     * The key is the GpioPin for which the callback is registered. To used
-     * GpioPin as a map key, the GpioPinCompare operator was defined as
-     * explained here:
-     * https://stackoverflow.com/questions/1102392/how-can-i-use-stdmaps-with-user-defined-types-as-key
-     *
      * The type stored is a tuple containing:
      * - The button callback function;
      * - Whether or not the button was pressed in the last check iteration;
      * - The relative tick of the last pin value change.
      */
-    std::map<miosix::GpioPin, std::tuple<ButtonCallback, bool, int>,
+    std::map<miosix::GpioPin, std::tuple<ButtonCallback, bool, unsigned int>,
              GpioPinCompare>
         callbacks;
 };
diff --git a/src/shared/utils/GpioPinCompare.h b/src/shared/utils/GpioPinCompare.h
new file mode 100644
index 0000000000000000000000000000000000000000..428c32c0b97ce46eba3ddb52ee4887efd4ab10e9
--- /dev/null
+++ b/src/shared/utils/GpioPinCompare.h
@@ -0,0 +1,47 @@
+/* Copyright (c) 2022 Skyward Experimental Rocketry
+ * Authors: Alberto Nidasio
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#pragma once
+
+#include <miosix.h>
+
+namespace Boardcore
+{
+
+/**
+ * @brief Comparison operator between GpioPins used for std::map.
+ *
+ * This function was implemented to use GpioPin as a map key. Check here for
+ * more explanation:
+ * https://stackoverflow.com/questions/1102392/how-can-i-use-stdmaps-with-user-defined-types-as-key
+ */
+struct GpioPinCompare
+{
+    bool operator()(const miosix::GpioPin& lhs,
+                    const miosix::GpioPin& rhs) const
+    {
+        if (lhs.getPort() == rhs.getPort())
+            return lhs.getNumber() < rhs.getNumber();
+        return lhs.getPort() < rhs.getPort();
+    }
+};
+}  // namespace Boardcore
diff --git a/src/shared/utils/PinObserver.h b/src/shared/utils/PinObserver.h
deleted file mode 100644
index a09573f5453f043219bde522004011a3f2a671b7..0000000000000000000000000000000000000000
--- a/src/shared/utils/PinObserver.h
+++ /dev/null
@@ -1,228 +0,0 @@
-/* Copyright (c) 2018 Skyward Experimental Rocketry
- * Author: Luca Erbetta
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#pragma once
-
-#include <diagnostic/StackLogger.h>
-#include <miosix.h>
-
-#include <functional>
-#include <map>
-#include <utility>
-
-#include "ActiveObject.h"
-
-using miosix::FastMutex;
-using miosix::GpioPin;
-using miosix::Lock;
-using miosix::Thread;
-using miosix::Unlock;
-
-using std::function;
-using std::map;
-using std::pair;
-
-namespace Boardcore
-{
-
-/**
- * Class used to call a callback after a pin performs a specific transition
- * (RISING or FALLING edge) and stays in the new state for a specific amount of
- * time. Useful if you want to monitor pin transitions but you want to avoid
- * spurious state changes.
- *
- * A callback to monitor each state change no matter the thresold or the
- * transition is also available, in order to be able to observe the current
- * state of the pin
- */
-class PinObserver : public ActiveObject
-{
-public:
-    /**
-     * @brief Pin transition
-     * Actual enumaration value represents the stat of the pin after the
-     * corresponding transition has occured.
-     */
-    enum class Transition : int
-    {
-        FALLING_EDGE = 0,
-        RISING_EDGE  = 1
-    };
-
-    using OnStateChangeCallback =
-        function<void(unsigned int, unsigned char, int)>;
-
-    using OnTransitionCallback = function<void(unsigned int, unsigned char)>;
-
-    /**
-     * @brief Construct a new Pin Observer object
-     *
-     * @param pollIntervalMs Pin transition polling interval
-     */
-    PinObserver(unsigned int pollIntervalMs = 20) : pollInterval(pollIntervalMs)
-    {
-    }
-
-    /**
-     * Observe a pin for a specific transition, and optionally for every
-     * single state change.
-     *
-     * The @param transitionCb function is called only if the two following
-     * conditions are verified:
-     * 1.  The transition specified in the @param transition is detected
-     * 2.  The pin stays in the new state for at least detection_threshols
-     * samples.
-     *
-     * The @param onstatechangeCb function [optional] is called at each state
-     * change, both rising and falling edge, regardless of the @param
-     * detectionThreshold
-     *
-     * @param p GPIOA_BASE, GPIOB_BASE ...
-     * @param n Which pin (0 to 15)
-     * @param transition What transition to detect (RISING or FALLING edge)
-     * @param transitionCb Function to call when the transition is detected and
-     * the pin stays in the new configuration for at least @param
-     * detectionThreshold samples
-     * @param detectionThreshold How many times the pin should be observed in
-     * the post-transition state to trigger the actual transition callback.
-     * @param onstatechangeCb Function to be called at each pin state change,
-     * no matter the threshold or the transition
-     */
-    void observePin(unsigned int p, unsigned char n, Transition transition,
-                    OnTransitionCallback transitionCb,
-                    unsigned int detectionThreshold       = 1,
-                    OnStateChangeCallback onstatechangeCb = nullptr)
-    {
-        Lock<FastMutex> lock(mtxMap);
-        observedPins.insert(
-            std::make_pair(pair<unsigned int, unsigned char>({p, n}),
-                           ObserverData{GpioPin{p, n}, transition, transitionCb,
-                                        onstatechangeCb, detectionThreshold}));
-    }
-
-    /**
-     * @brief Stop monitoring the specified pin
-     *
-     * @param p GPIOA_BASE, GPIOB_BASE ...
-     * @param n Which pin (0 to 15)
-     */
-    void removeObservedPin(unsigned int p, unsigned char n)
-    {
-        Lock<FastMutex> lock(mtxMap);
-        observedPins.erase({p, n});
-    }
-
-protected:
-    void run()
-    {
-        while (true)
-        {
-            {
-                Lock<FastMutex> lock(mtxMap);
-
-                for (auto it = observedPins.begin(); it != observedPins.end();
-                     it++)
-                {
-                    pair<int, int> key    = it->first;
-                    ObserverData& pinData = it->second;
-
-                    int oldState = pinData.state;
-                    int newState = pinData.pin.value();
-
-                    // Save current state in the struct
-                    pinData.state = newState;
-
-                    // Are we in a post-transition state?
-                    if (pinData.state == static_cast<int>(pinData.transition))
-                    {
-                        ++pinData.detectedCount;
-                    }
-                    else
-                    {
-                        pinData.detectedCount = 0;
-                    }
-
-                    // Pre-calcualate conditions in order to unlock the mutex
-                    // only one time
-
-                    bool stateChange = pinData.onstatechangeCallback &&
-                                       oldState != pinData.state;
-                    bool pinTriggered =
-                        pinData.detectedCount == pinData.detectionThreshold;
-
-                    {
-                        Unlock<FastMutex> unlock(lock);
-
-                        if (stateChange)
-                        {
-                            pinData.onstatechangeCallback(key.first, key.second,
-                                                          newState);
-                        }
-
-                        if (pinTriggered)
-                        {
-                            pinData.transitionCallback(key.first, key.second);
-                        }
-                    }
-                }
-            }
-
-            StackLogger::getInstance().updateStack(THID_PIN_OBS);
-
-            Thread::sleep(pollInterval);
-        }
-    }
-
-private:
-    struct ObserverData
-    {
-        GpioPin pin;
-        Transition transition;
-        OnTransitionCallback transitionCallback;
-        OnStateChangeCallback onstatechangeCallback;
-        unsigned int detectionThreshold;
-        unsigned int detectedCount;
-        int state;  // 1 if HIGH, 0 if LOW
-
-        ObserverData(GpioPin pin, Transition transition,
-                     OnTransitionCallback transitionCallback,
-                     OnStateChangeCallback onstatechangeCallback,
-                     unsigned int detectionThreshold)
-            : pin(pin), transition(transition),
-              transitionCallback(transitionCallback),
-              onstatechangeCallback(onstatechangeCallback),
-              detectionThreshold(detectionThreshold),
-              // Set to this value to avoid detection if the pin is already in
-              // the ending state of the "trigger" transition
-              detectedCount(detectionThreshold + 1), state(0)
-        {
-        }
-    };
-
-    map<pair<unsigned int, unsigned char>, ObserverData> observedPins;
-    FastMutex mtxMap;
-
-    unsigned int pollInterval;
-    bool stopped = true;
-};
-
-}  // namespace Boardcore
diff --git a/src/shared/utils/PinObserver/PinObserver.cpp b/src/shared/utils/PinObserver/PinObserver.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..14430cda3df132bbfdcbf58565715f3b88067834
--- /dev/null
+++ b/src/shared/utils/PinObserver/PinObserver.cpp
@@ -0,0 +1,97 @@
+/* Copyright (c) 2018-2022 Skyward Experimental Rocketry
+ * Author: Luca Erbetta, Alberto Nidasio
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "PinObserver.h"
+
+#include <functional>
+
+namespace Boardcore
+{
+
+bool PinObserver::registerPinCallback(miosix::GpioPin pin, PinCallback callback,
+                                      unsigned int detectionThreshold)
+{
+    // Try to insert the callback
+    auto result =
+        callbacks.insert({pin, {callback, detectionThreshold, pin.value(), 0}});
+
+    // Check if the insertion took place
+    if (result.second)
+    {
+        return scheduler.addTask(
+            std::bind(&PinObserver::periodicPinValueCheck, this, pin),
+            SAMPLE_PERIOD, TaskScheduler::Policy::SKIP);
+    }
+
+    return false;
+}
+
+bool PinObserver::unregisterPinCallback(miosix::GpioPin pin)
+{
+    return callbacks.erase(pin) != 0;
+}
+
+bool PinObserver::start() { return scheduler.start(); }
+
+void PinObserver::stop() { scheduler.stop(); }
+
+PinObserver::PinObserver() { scheduler.start(); }
+
+void PinObserver::periodicPinValueCheck(miosix::GpioPin pin)
+{
+    // Make sure the pin informations are still present
+    if (callbacks.find(pin) == callbacks.end())
+        return;
+
+    // Retrieve the pin information
+    const PinCallback &callback           = std::get<0>(callbacks[pin]);
+    const unsigned int detectionThreshold = std::get<1>(callbacks[pin]);
+    bool &previousState                   = std::get<2>(callbacks[pin]);
+    unsigned int &detectedCount           = std::get<3>(callbacks[pin]);
+
+    // Read the current pin status
+    const bool newState = pin.value();
+
+    // Are we in a transition?
+    if (previousState != newState)
+    {
+        detectedCount = 0;  // Yes, reset the counter
+    }
+    else
+    {
+        detectedCount++;  // No, continue to increment
+
+        // If the count reaches the threshold, then trigger the event
+        if (detectedCount > detectionThreshold)
+        {
+            if (newState)
+                callback(PinTransition::RISING_EDGE);
+            else
+                callback(PinTransition::FALLING_EDGE);
+        }
+    }
+
+    // Save the current pin status
+    previousState = newState;
+}
+
+}  // namespace Boardcore
diff --git a/src/shared/utils/PinObserver/PinObserver.h b/src/shared/utils/PinObserver/PinObserver.h
new file mode 100644
index 0000000000000000000000000000000000000000..07090622a702af94f2dcbc04fdd1e3fe05065f62
--- /dev/null
+++ b/src/shared/utils/PinObserver/PinObserver.h
@@ -0,0 +1,133 @@
+/* Copyright (c) 2018-2022 Skyward Experimental Rocketry
+ * Author: Luca Erbetta, Alberto Nidasio
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#pragma once
+
+#include <Singleton.h>
+#include <scheduler/TaskScheduler.h>
+#include <utils/GpioPinCompare.h>
+
+#include <map>
+
+namespace Boardcore
+{
+
+/**
+ * @brief Pin transition.
+ */
+enum class PinTransition
+{
+    FALLING_EDGE,  ///< The pin goes from high to low.
+    RISING_EDGE    ///< The pin goes from low to high.
+};
+
+/**
+ * Class used to call a callback after a pin performs a specific transition
+ * (RISING or FALLING edge) and stays in the new state for a specific amount of
+ * time. Useful if you want to monitor pin transitions but you want to avoid
+ * spurious state changes.
+ *
+ * A callback to monitor each state change no matter the threshold or the
+ * transition is also available, in order to be able to observe the current
+ * state of the pin.
+ */
+class PinObserver : public Singleton<PinObserver>
+{
+    friend Singleton<PinObserver>;
+
+    static constexpr uint32_t SAMPLE_PERIOD = 100;  // 10Hz
+
+public:
+    using PinCallback = std::function<void(PinTransition)>;
+
+    /**
+     * Observe a pin for a specific transition, and optionally for every
+     * single state change.
+     *
+     * @param pin Pin to listen to.
+     * @param callback Function to call on button events.
+     * @param detectionThreshold How many times the pin should be observed in
+     * the post-transition state to trigger the actual transition callback,
+     * defaults to 1.
+     * @return False if another callback was already registered for the pin.
+     */
+    bool registerPinCallback(miosix::GpioPin pin, PinCallback callback,
+                             unsigned int detectionThreshold = 1);
+
+    /**
+     * @brief Unregisters the callback associated with the specified pin, if
+     * any.
+     *
+     * @param pin Pin whose callback function is to be removed.
+     * @return True if a callback was present and removed for the given pin.
+     */
+    bool unregisterPinCallback(miosix::GpioPin pin);
+
+    /**
+     * @brief Starts the PinObserver's task scheduler.
+     *
+     * Note that the scheduler is started as soon as the PinObserver is first
+     * used.
+     *
+     * @return Whether the task scheduler was started or not.
+     */
+    bool start();
+
+    /**
+     * @brief Stops the PinObserver's task scheduler.
+     */
+    void stop();
+
+private:
+    /**
+     * @brief Construct a new PinObserver object.
+     *
+     * @param pollInterval Pin transition polling interval, defaults to 20 [ms].
+     */
+    PinObserver();
+
+    /**
+     * @brief This function is added to the scheduler for every pin registered
+     * in the PinObserver.
+     *
+     * @param pin Pin whose value need to be checked.
+     */
+    void periodicPinValueCheck(miosix::GpioPin pin);
+
+    TaskScheduler scheduler;
+
+    /**
+     * @brief Map of all the callbacks registered in the PinObserver.
+
+     * The type stored is a tuple containing:
+     * - The button callback function;
+     * - Detection threshold: number of periods to trigger an event
+     * - The last pin status;
+     * - Number of periods the pin values stayed the same;
+     */
+    std::map<miosix::GpioPin,
+             std::tuple<PinCallback, unsigned int, bool, unsigned int>,
+             GpioPinCompare>
+        callbacks;
+};
+
+}  // namespace Boardcore
diff --git a/src/tests/test-pinobserver.cpp b/src/tests/test-pinobserver.cpp
index 1fe87708a4f27d22bf72f4d84455860331c34b0e..9856200ef36e72da7b604ae2d496a830ee3dc912 100644
--- a/src/tests/test-pinobserver.cpp
+++ b/src/tests/test-pinobserver.cpp
@@ -21,10 +21,14 @@
  */
 
 #include <miosix.h>
-#include <utils/PinObserver.h>
+#include <utils/PinObserver/PinObserver.h>
+
+#include <functional>
 
 using namespace Boardcore;
 using namespace miosix;
+using namespace std;
+using namespace std::placeholders;
 
 static constexpr unsigned int POLL_INTERVAL = 20;
 
@@ -35,60 +39,38 @@ static constexpr unsigned char PIN1_PIN = 5;
 static constexpr unsigned int PIN2_PORT = GPIOE_BASE;
 static constexpr unsigned char PIN2_PIN = 6;
 
-void onStateChange(unsigned int p, unsigned char n, int state)
+void onTransition(GpioPin pin, PinTransition transition)
 {
-    if (p == BTN_PORT && n == BTN_PIN)
-    {
-        printf("BTN state change: %d\n", state);
-    }
-    if (p == PIN1_PORT && n == PIN1_PIN)
-    {
-        printf("PIN1 state change: %d\n", state);
-    }
-    if (p == PIN2_PORT && n == PIN2_PIN)
-    {
-        printf("PIN2 state change: %d\n", state);
-    }
-}
+    if (pin.getPort() == BTN_PORT && pin.getNumber() == BTN_PIN)
+        printf("BTN transition: ");
+    if (pin.getPort() == PIN1_PORT && pin.getNumber() == PIN1_PIN)
+        printf("PIN1 transition: ");
+    if (pin.getPort() == PIN2_PORT && pin.getNumber() == PIN2_PIN)
+        printf("PIN2 transition: ");
 
-void onTransition(unsigned int p, unsigned char n)
-{
-    if (p == BTN_PORT && n == BTN_PIN)
-    {
-        printf("BTN transition.\n");
-    }
-    if (p == PIN1_PORT && n == PIN1_PIN)
-    {
-        printf("PIN1 transition.\n");
-    }
-    if (p == PIN2_PORT && n == PIN2_PIN)
-    {
-        printf("PIN2 transition.\n");
-    }
+    if (transition == PinTransition::FALLING_EDGE)
+        printf("FALLING_EDGE\n");
+    else
+        printf("RISING_EDGE\n");
 }
 
 int main()
 {
-    GpioPin(BTN_PORT, BTN_PIN).mode(Mode::INPUT);
-    GpioPin(PIN1_PORT, PIN1_PIN).mode(Mode::INPUT_PULL_DOWN);
-    GpioPin(PIN2_PORT, PIN2_PIN).mode(Mode::INPUT_PULL_UP);
-
-    PinObserver observer;
-
-    observer.observePin(BTN_PORT, BTN_PIN, PinObserver::Transition::RISING_EDGE,
-                        &onTransition, 1, onStateChange);
+    auto btn  = GpioPin(BTN_PORT, BTN_PIN);
+    auto pin1 = GpioPin(PIN1_PORT, PIN1_PIN);
+    auto pin2 = GpioPin(PIN2_PORT, PIN2_PIN);
 
-    observer.observePin(PIN1_PORT, PIN1_PIN,
-                        PinObserver::Transition::FALLING_EDGE, &onTransition,
-                        1000 / POLL_INTERVAL, onStateChange);
+    btn.mode(Mode::INPUT);
+    pin1.mode(Mode::INPUT_PULL_DOWN);
+    pin2.mode(Mode::INPUT_PULL_UP);
 
-    observer.observePin(PIN2_PORT, PIN2_PIN,
-                        PinObserver::Transition::RISING_EDGE, &onTransition,
-                        1000 / POLL_INTERVAL, onStateChange);
+    PinObserver::getInstance().registerPinCallback(
+        btn, std::bind(onTransition, btn, _1), 10);
+    PinObserver::getInstance().registerPinCallback(
+        pin1, std::bind(onTransition, pin1, _1), 10);
+    PinObserver::getInstance().registerPinCallback(
+        pin2, std::bind(onTransition, pin2, _1), 10);
 
-    observer.start();
-    for (;;)
-    {
+    while (true)
         Thread::sleep(10000);
-    }
 }