Răsfoiți Sursa

Redactored and documented

xnecas 2 săptămâni în urmă
părinte
comite
c1d0db18cf

+ 31 - 43
README.md

@@ -1,53 +1,41 @@
-| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 | Linux |
-| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- | ----- |
+# Bat-Sense Operator Controller
 
-# Hello World Example
+Firmvér pre prenosné ovládacie zariadenie založené na platforme **M5Stack**, slúžiace ako diaľkový ESP-NOW operátorský ovládač pre meracie zariadenie **Bat-Sense**.
 
-Starts a FreeRTOS task to print "Hello World".
+---
 
-(See the README.md file in the upper level 'examples' directory for more information about examples.)
+## 📋 Prehľad projektu
 
-## How to use example
+Tento projekt implementuje robustnú, stavovým automatom riadenú firmvérovú architektúru určenú pre nízkoenergetické (low-power) embedded zariadenia. Slúži ako konzola na správu celého životného cyklu merania, pričom s podriadeným zariadením komunikuje bezdrôtovo pomocou vlastného protokolu **Nano Bus (Nbus)** cez vrstvu **EspNowSerial**.
 
-Follow detailed instructions provided specifically for this example.
+---
 
-Select the instructions depending on Espressif chip installed on your development board:
+## 🛠️ Hlavné vlastnosti
 
-- [ESP32 Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/stable/get-started/index.html)
-- [ESP32-S2 Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/get-started/index.html)
+* **Stavový automat (State Machine):** Čistá architektúra riadená plánovačom `StateRunner`, ktorá spravuje 6 na sebe nadväzujúcich stavov:
+  * `STATE_LOCK` – Uzamknutý / neaktívny stav
+  * `STATE_TARGET` – Yameranie cieľa
+  * `STATE_START` – Inicializačná štartovacia sekvencia
+  * `STATE_RUN` – Aktívny beh merania s vizuálnou animáciou
+  * `STATE_STOP` – Ukončenie meracieho cyklu
+  * `STATE_CANCEL` – Prerušenie / zrušenie operácie
+* **ESP-NOW Komunikácia:** Rýchly bezdrôtový prenos príkazov a paketov v reálnom čase pomocou vrstvy `NbusBroadcastComm` s overovaním integrity pomocou CRC8.
+* **Optimalizovaná správa napájania:** Periodický monitoring batérie pomocou FreeRTOS časovačov a podpora prechodu do úsporného režimu.
+* **Kompaktná konfigurácia:** Všetky konštanty (farby, fonty, hardvérové piny, časovanie, Nbus parametre) sú zjednotené v jednom centrálnom súbore `all_config.h` pre rýchlejšiu kompiláciu a lepšiu udržiavateľnosť.
+* **Doxygen dokumentácia:** Zdrojový kód je plne zdokumentovaný štandardizovanými Doxygen komentármi.
 
+---
 
-## Example folder contents
+## 📂 Architektúra projektového repozitára
 
-The project **hello_world** contains one source file in C language [hello_world_main.c](main/hello_world_main.c). The file is located in folder [main](main).
-
-ESP-IDF projects are built using CMake. The project build configuration is contained in `CMakeLists.txt` files that provide set of directives and instructions describing the project's source files and targets (executable, library, or both).
-
-Below is short explanation of remaining files in the project folder.
-
-```
-├── CMakeLists.txt
-├── pytest_hello_world.py      Python script used for automated testing
-├── main
-│   ├── CMakeLists.txt
-│   └── hello_world_main.c
-└── README.md                  This is the file you are currently reading
-```
-
-For more information on structure and contents of ESP-IDF projects, please refer to Section [Build System](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/build-system.html) of the ESP-IDF Programming Guide.
-
-## Troubleshooting
-
-* Program upload failure
-
-    * Hardware connection is not correct: run `idf.py -p PORT monitor`, and reboot your board to see if there are any output logs.
-    * The baud rate for downloading is too high: lower your baud rate in the `menuconfig` menu, and try again.
-
-## Technical support and feedback
-
-Please use the following feedback channels:
-
-* For technical queries, go to the [esp32.com](https://esp32.com/) forum
-* For a feature request or bug report, create a [GitHub issue](https://github.com/espressif/esp-idf/issues)
-
-We will get back to you as soon as possible.
+```text
+├── all_config.h             # Centrálna konfigurácia (hardvér, farby, časovanie, Nbus)
+├── main.cpp                 # Hlavný vstupný bod firmvéru (Arduino setup/loop)
+├── device/                  # Správa hardvéru, displeja a ESP-NOW
+├── drawer/                  # UI renderovanie, kreslenie ikon, textov a batérie
+├── nbus/                    # Nano Bus protokol a ESP-NOW broadcast komunikácia
+├── states/                  # Logika stavového automatu
+│   ├── state_runner.h       # Plánovač a riadenie prechodov medzi stavmi
+│   ├── state_interface.h    # Abstraktné rozhranie pre stavy (IState)
+│   └── state_impl.h         # Implementácie jednotlivých stavov (Lock, Target, Start, Run, Stop, Cancel)
+└── data_structures/         # Dátové štruktúry (PhaseCmd, StateTx, IconStruct)

+ 0 - 85
main/src/backup.txt

@@ -1,85 +0,0 @@
-#include "Arduino.h"
-#include "states/state_impl/state_impl.h"
-#include "drawer/drawer.h"
-#include "EspNowSerial/EspNowSerial.h"
-#include "nbus/nbus_comm.h"
-
-
-
-NbusComm NBC(Dev.EnSerial);
-
-StateLock   state_lock;
-StateUnlock state_unlock;
-StateTarget state_target;
-StateStart  state_start;
-StateRun    state_run;
-StateStop   state_stop;
-StateCancel state_cancel;
-
-IState *states[_STATES_COUNT] = {
-  &state_lock,
-  &state_unlock,
-  &state_target,
-  &state_start,
-  &state_run,
-  &state_stop,
-  &state_cancel
-}; 
-
-StateTx curr_state(STATE_LOCK);
-StateTx next_state(STATE_LOCK);
-int64_t time0;
-
-void setup()
-{
-
-  Dev.hardware_init();
-
-  Dev.Drawer.set_text_color(TFT_WHITE, TFT_BLACK);
-  Dev.Drawer.draw_battery(Dev.M5Board.Power.getBatteryLevel());
-
-  time0 = Dev.M5Board.millis();
-  
-  states[curr_state.id]->setup();
-  Dev.Drawer.refresh();
-
-  Dev.M5Board.delay(1000);
-  uint8_t aa[] = {6, 1, 3};
-  NBC.cmd_set_data(aa, 3); 
-
-
-
-}
-
-void loop()
-{
-  // 1. Spracovanie stavov
-  next_state = states[curr_state.id]->loop(Dev.M5Board.millis() - time0);
-
-  // pause transition if needed
-  if (next_state.wait >= 0)
-  {
-    Dev.Drawer.refresh();
-    
-    if (next_state.wait > 0)
-      Dev.M5Board.delay(next_state.wait);
-    
-    curr_state.wait = -1;
-  }
-  
-  if (next_state.id != curr_state.id)
-  {
-    time0 = millis();
-    curr_state = next_state;
-    states[curr_state.id]->setup();
-    Dev.Drawer.refresh();
-  }
-
-  if (Dev.get_battery_refresh())
-  {
-    Dev.set_battery_refresh(false);
-    Dev.Drawer.draw_battery(Dev.M5Board.Power.getBatteryLevel());
-  }
-
-  Dev.M5Board.update();
-}

+ 1 - 1
main/src/config/all_config.h

@@ -3,11 +3,11 @@
 
 #include "states_config.h"
 #include "colors_config.h"
+#include  "fonts_config.h"
 #include "timing_config.h"
 #include "layout_config.h"
 #include "hw_config.h"
 #include "nbus_config.h"
 #include "icons/all_icons.h"
 
-
 #endif // _ALL_CONFIG_H_

+ 8 - 8
main/src/config/colors_config.h

@@ -1,14 +1,14 @@
 #ifndef _COLORS_CONFIG_H_
 #define _COLORS_CONFIG_H_
 
-#define COL_BG                TFT_BLACK
-#define COL_TEXT_FG           TFT_WHITE
+#define COL_BG               TFT_BLACK       ///< Default application background color
+#define COL_TEXT_FG          TFT_WHITE       ///< Default foreground text color
 
-#define COL_ICON_FG           TFT_WHITE
+#define COL_ICON_FG          TFT_WHITE       ///< Default icon foreground color
 
-#define COL_ICON_BG_INACTIVE  TFT_GREY
-#define COL_ICON_BG_ACTIVE    TFT_DARKGREEN
-#define COL_ICON_BG_CANCEL    TFT_RED
-#define COL_ICON_BG_REC       TFT_RED
+#define COL_ICON_BG_INACTIVE TFT_GREY        ///< Icon background color when in an inactive state
+#define COL_ICON_BG_ACTIVE   TFT_DARKGREEN   ///< Icon background color when active
+#define COL_ICON_BG_CANCEL   TFT_RED         ///< Icon background color for cancel or abort actions
+#define COL_ICON_BG_REC      TFT_RED         ///< Icon background color during recording or active operation
 
-#endif // _COLORS_H_
+#endif // _COLORS_H_

+ 8 - 0
main/src/config/fonts_config.h

@@ -0,0 +1,8 @@
+#ifndef _FONTS_CONFIG_H_
+#define _FONTS_CONFIG_H_
+
+#define FNT_DESCRIPTION Font4   ///< Font used for rendering description texts
+#define FNT_COUNTER     Font4   ///< Font used for rendering measurement numbers and counters
+#define FNT_BATTERY     Font4   ///< Font used for rendering battery indicators and percentage values
+
+#endif // _FONTS_CONFIG_H_

+ 6 - 10
main/src/config/hw_config.h

@@ -1,18 +1,14 @@
 #ifndef _HW_CONFIG_H_
 #define _HW_CONFIG_H_
 
-#define SERIAL_BAUD 115200
+#define SERIAL_BAUD 115200                                          ///< Default baud rate for serial communication
 
-#define CORE_LOW_POWER_FREQUENCY_MHZ    10
+#define CORE_LOW_POWER_FREQUENCY_MHZ 10                             ///< CPU clock frequency in low-power mode (MHz)
 
-#define DISP_BRIGHTNESS 16
+#define DISP_BRIGHTNESS 16                                          ///< Default display backlight brightness level
 
-#define EN_SERIAL_CHANNEL 11
-#define EN_SERIAL_BROADCAST false
-#define EN_SERIAL_ADDRESS {0x9c, 0x9e, 0x6e, 0xe0, 0x87, 0xf4}
-
-#define WAKE_BUTTON_A 11
-#define WAKE_BUTTON_B 12
-#define LCD_BACKLIGHT_PIN 38
+#define EN_SERIAL_CHANNEL 11                                        ///< ESP-NOW communication channel
+#define EN_SERIAL_BROADCAST false                                   ///< Flag to enable or disable ESP-NOW broadcast mode
+#define EN_SERIAL_ADDRESS {0x9c, 0x9e, 0x6e, 0xe0, 0x87, 0xf4}      ///< Default ESP-NOW peer MAC address
 
 #endif // _HW_CONFIG_H_

+ 5 - 2
main/src/config/layout_config.h

@@ -1,7 +1,10 @@
 #ifndef _LAYOUT_CONFIG_H_
 #define _LAYOUT_CONFIG_H_
 
-#define LAY_ICON_YOFFSET  -15
-#define LAY_DESC_YOFFSET  +60
+#define LAY_TEXT_BUFFER     16       ///< Size of the internal text formatting buffer (in bytes)
+
+#define LAY_MENU_YOFFET     +5       ///< Vertical offset for menu positioning (pixels)
+#define LAY_ICON_YOFFSET    -15      ///< Vertical offset for icon drawing (pixels)
+#define LAY_DESC_YOFFSET    +60      ///< Vertical offset for description text drawing (pixels)
 
 #endif // _LAYOUT_CONFIG_H_

+ 7 - 7
main/src/config/nbus_config.h

@@ -1,12 +1,12 @@
 #ifndef _NBUS_CONFIG_H_
 #define _NBUS_CONFIG_H_
 
-#define NBUS_MEAS_ID            6
-#define NBUS_PHASE_TARGET       1
-#define NBUS_PHASE_INTERLUDE    2
-#define NBUS_PHASE_START        3
-#define NBUS_PHASE_MOVEMENT     4
-#define NBUS_PHASE_STOP         5
-#define NBUS_PHASE_ACK          6
+#define NBUS_MEAS_ID            6       ///< address of sensor "measure_data"
+#define NBUS_PHASE_TARGET       1       ///< Phase identifier for target positioning
+#define NBUS_PHASE_INTERLUDE    2       ///< Phase identifier for interlude
+#define NBUS_PHASE_START        3       ///< Phase identifier for operation start
+#define NBUS_PHASE_MOVEMENT     4       ///< Phase identifier for movement execution
+#define NBUS_PHASE_STOP         5       ///< Phase identifier for operation stop
+#define NBUS_PHASE_ACK          6       ///< Phase identifier for acknowledgment response
 
 #endif // _NBUS_CONFIG_H_

+ 10 - 7
main/src/config/states_config.h

@@ -1,15 +1,18 @@
 #ifndef _STATES_CONFIG_H_
 #define _STATES_CONFIG_H_
 
+/**
+ * @brief Enumeration of all application state identifiers.
+ */
 enum StateID
 {
-    STATE_LOCK = 0,
-    STATE_TARGET,
-    STATE_START,
-    STATE_RUN,
-    STATE_STOP,
-    STATE_CANCEL,
-    _STATES_COUNT
+    STATE_LOCK = 0,     ///< Locked or idle state
+    STATE_TARGET,       ///< Target positioning state
+    STATE_START,        ///< Initialization or start sequence state
+    STATE_RUN,          ///< Active measurement execution state
+    STATE_STOP,         ///< Completion of a measurement cycle
+    STATE_CANCEL,       ///< Cancellation or abort state
+    _STATES_COUNT       ///< Total number of states (must be last, do not modify)
 };
 
 #endif // _STATES_CONFIG_H_

+ 5 - 6
main/src/config/timing_config.h

@@ -1,11 +1,10 @@
 #ifndef _TIMING_CONFIG_H_
 #define _TIMING_CONFIG_H_
 
-#define TIM_INFO_SCREEN         1000
-#define TIM_LONG_PRESS          1500
-#define TIM_RUN_BLINK           1000
-#define TIM_RUN_REFRESH            0
-#define TIM_POWER_OFF           5 * 60 * 1000 // 5 min 
-#define TIM_BATTERY_REFRESH     60 * 1000     // 1 min
+#define TIM_INFO_SCREEN         1000            ///< Duration to display informational screens (milliseconds)
+#define TIM_LONG_PRESS          1500            ///< Threshold duration to recognize a long button press (milliseconds)
+#define TIM_RUN_BLINK           1000            ///< Interval for UI blinking animation during active execution (milliseconds)
+#define TIM_POWER_OFF           5 * 60 * 1000   // 5 min ///< Inactivity duration before automatic power off (milliseconds)
+#define TIM_BATTERY_REFRESH     60 * 1000       // 1 min ///< Interval for periodic battery status checks and screen updates (milliseconds)
 
 #endif // _TIMING_CONFIG_H_

+ 6 - 3
main/src/data_structures/icon_struct.h

@@ -3,11 +3,14 @@
 
 #include <cinttypes>
 
+/**
+ * @brief Structure representing a graphical icon with dimensions and raw pixel data.
+ */
 struct IconStruct
 {
-    const int16_t width;
-    const int16_t height;
-    const uint8_t *data;
+    const int16_t width;   ///< Width of the icon in pixels
+    const int16_t height;  ///< Height of the icon in pixels
+    const uint8_t *data;   ///< Pointer to the raw image/pixel data array
 };
 
 #endif // _ICON_STRUCT_H_

+ 9 - 5
main/src/data_structures/phase_cmd.h

@@ -3,17 +3,21 @@
 
 #include <cinttypes>
 
+/**
+ * @brief Union representing a command packet for sending measurement phase data to a slave device.
+ * 
+ * Allows accessing the data either as individual named fields or as a raw byte array.
+ */
 union PhaseCmd
 {
     struct
     {
-        uint8_t sensor_addr;
-        uint8_t measure_num;
-        uint8_t phase_num; 
+        uint8_t sensor_addr;   ///< Address of the target sensor
+        uint8_t measure_num;   ///< Measurement number or index
+        uint8_t phase_num;     ///< Phase number of the measurement
     };
 
-    uint8_t data[3];
+    uint8_t data[3];           ///< Raw byte array representation of the command (size: 3 bytes)
 };
 
-
 #endif // _PHASE_CMD_H_

+ 18 - 13
main/src/data_structures/state_tx.h

@@ -1,25 +1,30 @@
-#ifndef _STATE_DATA_H_
-#define _STATE_DATA_H_
+#ifndef _STATE_TX_H_
+#define _STATE_TX_H_
 
 #include <cinttypes>
 
-enum StateID;       ///< Forward-declared type
+enum StateID;           ///< Forward-declared identifier type for application states
 
+/**
+ * @brief Transition modes defining how the state machine behaves after completing a state.
+ */
 enum StateTxMode
 {
-    NO_OP,
-    REFRESH,
-    WAIT,
-    REFRESH_WAIT,
-    ERROR
+    NO_OP,             ///< No Operation
+    REFRESH,           ///< Refresh after end()
+    WAIT,              ///< Wait between states
+    REFRESH_WAIT,      ///< Refresh then wait
+    ERROR              ///< Immediately cancel
 };
 
+/**
+ * @brief Structure containing metadata for state transitions and timing.
+ */
 struct StateTx
 {
-    StateID id;
-    StateTxMode mode{NO_OP};
-    uint64_t wait{0};
+    StateID id;                 ///< Target state identifier
+    StateTxMode mode{NO_OP};    ///< Transition mode (defaults to NO_OP)
+    uint64_t wait{0};           ///< Wait duration in milliseconds or ticks (defaults to 0)
 };
 
-
-#endif // _STATE_DATA_H_
+#endif // _STATE_TX_H_

+ 12 - 12
main/src/device/device.cpp

@@ -1,11 +1,10 @@
 #include "device.h"
 
-void Device::battery_timer_cb(TimerHandle_t pxTimer)
+void Device::_battery_timer_cb(TimerHandle_t pxTimer)
 {
     Device::_batt_refresh = true;
 }
 
-
 bool Device::get_battery_refresh()
 {
     return Device::_batt_refresh;
@@ -16,19 +15,18 @@ void Device::set_battery_refresh(bool value)
     Device::_batt_refresh = value;
 }
 
-void Device::begin_esp_now_serial()
+void Device::_begin_esp_now_serial()
 {
     EnSerial.begin(EN_SERIAL_CHANNEL, EN_SERIAL_BROADCAST);
     _wifi_active = true;
 }
 
-void Device::end_esp_now_serial()
+void Device::_end_esp_now_serial()
 {
     EnSerial.end();
     _wifi_active = false;
 }
 
-
 bool Device::hardware_init()
 {
     // init Serial
@@ -36,17 +34,20 @@ bool Device::hardware_init()
     
     // init M5 base class
     M5Board.begin();
+    
+    // adjust disp brightness
+    M5Board.Display.setBrightness(DISP_BRIGHTNESS);
 
-    // add esp-now serial peer
-    EnSerial.begin(EN_SERIAL_CHANNEL, EN_SERIAL_BROADCAST);
+    // init esp-now serial
+    _begin_esp_now_serial();
     EspMacAddress rx_address = EN_SERIAL_ADDRESS;    
     EnSerial.add_peer(rx_address);
-    EnSerial.end();
 
     // init drawer
     Drawer.begin();
 
-    BattTimer = xTimerCreate("BatteryTimer", pdMS_TO_TICKS(TIM_BATTERY_REFRESH), pdTRUE, NULL, battery_timer_cb);
+    // init battery timer
+    BattTimer = xTimerCreate("BatteryTimer", pdMS_TO_TICKS(TIM_BATTERY_REFRESH), pdTRUE, NULL, _battery_timer_cb);
 
     if (BattTimer != NULL)
         return xTimerStart(BattTimer, 0) == pdPASS;
@@ -57,7 +58,7 @@ bool Device::hardware_init()
 void Device::enter_low_power()
 {
     if (_wifi_active)
-        end_esp_now_serial();
+        _end_esp_now_serial();
 
     setCpuFrequencyMhz(CORE_LOW_POWER_FREQUENCY_MHZ);
 }
@@ -67,9 +68,8 @@ void Device::enter_high_power(bool wifi)
     setCpuFrequencyMhz(CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ);
     
     if (wifi)
-        begin_esp_now_serial();
+        _begin_esp_now_serial();
 }
 
-
 // global instance
 Device Dev;

+ 48 - 14
main/src/device/device.h

@@ -1,42 +1,76 @@
 #ifndef _DEVICE_H_
 #define _DEVICE_H_
 
-
 #include "M5Unified.h"
 #include "drawer/drawer.h"
 #include "EspNowSerial/EspNowSerial.h"
-
 #include "config/all_config.h"
 
+/**
+ * @brief Main system device controller managing hardware initialization, power states, and peripherals.
+ */
 class Device
 {
 public:
-    m5::M5Unified &M5Board{M5};
-    M5Drawer Drawer{M5.Display};
-    EspNowSerial EnSerial;
-    TimerHandle_t BattTimer;
+    m5::M5Unified &M5Board{M5};           ///< Reference to the M5Unified board instance
+    M5Drawer Drawer{M5.Display};          ///< Drawer instance for managing display rendering
+    EspNowSerial EnSerial;                ///< ESP-NOW serial communication interface instance
+    TimerHandle_t BattTimer;              ///< FreeRTOS timer handle for periodic battery level checks
 
+    /**
+     * @brief Initialize all underlying hardware components, peripherals, and timers.
+     * @return true If initialization was successful.
+     * @return false If initialization failed.
+     */
     bool hardware_init();
+
+    /**
+     * @brief Get the status of the battery refresh flag.
+     * @return true If a battery refresh is requested.
+     * @return false If no refresh is pending.
+     */
     bool get_battery_refresh();
+
+    /**
+     * @brief Set the status of the battery refresh flag.
+     * @param value The boolean value to set.
+     */
     void set_battery_refresh(bool value);
 
+    /**
+     * @brief Transition the device into a low-power mode (power saving).
+     */
     void enter_low_power();
-    void enter_high_power(bool wifi=false);
+
+    /**
+     * @brief Transition the device into high-power performance mode.
+     * @param wifi Optional flag to enable Wi-Fi (defaults to false).
+     */
+    void enter_high_power(bool wifi = false);
 
 private:
-    void begin_esp_now_serial();
-    void end_esp_now_serial();
+    /**
+     * @brief Initialize the ESP-NOW serial communication protocol.
+     */
+    void _begin_esp_now_serial();
+
+    /**
+     * @brief Terminate or shut down the ESP-NOW serial communication protocol.
+     */
+    void _end_esp_now_serial();
 
-    bool _wifi_active{false};
+    bool _wifi_active{false};             ///< Flag indicating whether the Wi-Fi interface is currently active
 
+    /**
+     * @brief FreeRTOS timer callback triggered periodically for battery level updates.
+     * @param pxTimer Handle of the timer that expired.
+     */
+    static inline void _battery_timer_cb(TimerHandle_t pxTimer);
 
-    static inline void battery_timer_cb(TimerHandle_t pxTimer);
-    static inline bool _batt_refresh{false};
+    static inline bool _batt_refresh{false}; ///< Internal static flag tracking battery refresh state
 };
 
 // global instance
 extern Device Dev;
 
-
-
 #endif // _DEVICE_H_

+ 23 - 20
main/src/drawer/drawer.cpp

@@ -8,23 +8,21 @@ M5Drawer::M5Drawer(M5GFX &display)
 
 void M5Drawer::begin()
 {
+    // calculate center position
     _center_x = _display.width() / 2;
     _center_y = _display.height() / 2;
 
+    // init sprite
     _sprite.createSprite(_display.width(), _display.height());
     _sprite.clear();
+
+    // config colors
+    _sprite.setTextColor(COL_TEXT_FG, COL_BG);
 }
 
 void M5Drawer::refresh()
 {    
     _sprite.pushSprite(0, 0);
-
-}
-
-void M5Drawer::set_text_color(int32_t fg_color, int32_t bg_color)
-{
-    _color_fg_text = fg_color;
-    _color_bg_text = bg_color;
 }
 
 void M5Drawer::draw_icon(const IconStruct &icon, int32_t fg_color, int32_t bg_color, int16_t y_offset)
@@ -41,19 +39,21 @@ void M5Drawer::draw_icon(const IconStruct &icon, int32_t fg_color, int32_t bg_co
     _sprite.drawXBitmap(x0, y0, icon.data, icon.width, icon.height, fg_color);
 }
 
-
 void M5Drawer::draw_description(const char* line1, const char* line2, int16_t y_offset)
 {
-    _sprite.setFont(&Font4);
+    _sprite.setFont(&FNT_DESCRIPTION);
 
+    // erase old text
     int16_t text_max_height = _sprite.fontHeight() * 2;
-    _sprite.fillRect(0, _center_y + y_offset, _display.width(), text_max_height, _color_bg_text);
+    _sprite.fillRect(0, _center_y + y_offset, _display.width(), text_max_height, COL_BG);
 
+    // calculate position
     int16_t x0 = _center_x;
     int16_t y0 = _center_y + y_offset;
 
+    // draw new text
     _sprite.setTextDatum(top_center);
-    _sprite.setTextColor(_color_fg_text, _color_bg_text);
+    _sprite.setTextColor(COL_TEXT_FG, COL_BG);
 
     _sprite.drawString(line1, x0, y0);
     _sprite.drawString(line2, x0, y0 + _sprite.fontHeight());
@@ -61,27 +61,30 @@ void M5Drawer::draw_description(const char* line1, const char* line2, int16_t y_
 
 void M5Drawer::draw_battery(int32_t level)
 {
-    _sprite.setFont(&Font4);
+    // config test
+    _sprite.setFont(&FNT_BATTERY);
     _sprite.setTextDatum(top_right);
 
-    _sprite.setTextColor(COL_BG, COL_BG);
-     _sprite.drawString("000%", _display.width() - 1, 0);
-
+    // erase old text
+   _sprite.setTextPadding(_sprite.textWidth("000%"));
 
+    // draw new text
     _sprite.setTextColor(COL_TEXT_FG, COL_BG);
     snprintf(_string_buff, sizeof(_string_buff), "%d%%", (int16_t)level);
-    _sprite.drawString(_string_buff, _display.width() - 1, 0);
+    _sprite.drawString(_string_buff, _display.width() - 1, LAY_MENU_YOFFET);
 }
 
 void M5Drawer::draw_measure_number(int16_t number)
 {
-    _sprite.setFont(&Font4);
+    // config text
+    _sprite.setFont(&FNT_COUNTER);
     _sprite.setTextDatum(top_left);
 
-    _sprite.setTextColor(COL_BG, COL_BG);
-    _sprite.drawString("M000", 0, 0);
+    // erase old text
+    _sprite.setTextPadding(_sprite.textWidth("M000"));
 
+    // draw new text
     _sprite.setTextColor(COL_TEXT_FG, COL_BG);
     snprintf(_string_buff, sizeof(_string_buff), "M%d", number);
-    _sprite.drawString(_string_buff, 0, 0);
+    _sprite.drawString(_string_buff, 0, LAY_MENU_YOFFET);
 }

+ 46 - 11
main/src/drawer/drawer.h

@@ -5,32 +5,67 @@
 #include "data_structures/icon_struct.h"
 #include "all_config.h"
 
+/**
+ * @brief Handles drawing operations, UI rendering, and graphics management for the M5Stack display.
+ */
 class M5Drawer
 {
+
 public:
+
+    /**
+     * @brief Construct a new M5Drawer object.
+     * @param display Reference to the M5GFX display instance.
+     */
     M5Drawer(M5GFX &display);
+
+    /**
+     * @brief Initialize the drawer, display, and internal sprite buffers.
+     */
     void begin();
-    void refresh();
 
-    void set_text_color(int32_t fg_color, int32_t bg_color);
+    /**
+     * @brief Refresh the display and push updates to the screen.
+     */
+    void refresh();
 
+    /**
+     * @brief Draw an icon on the screen.
+     * @param icon Reference to the icon structure data.
+     * @param fg_color Foreground color for the icon.
+     * @param bg_color Background color for the icon.
+     * @param y_offset Vertical position offset.
+     */
     void draw_icon(const IconStruct &icon, int32_t fg_color, int32_t bg_color, int16_t y_offset);
+
+    /**
+     * @brief Draw a multi-line description text.
+     * @param line1 Pointer to the first line string.
+     * @param line2 Pointer to the second line string.
+     * @param y_offset Vertical position offset.
+     */
     void draw_description(const char* line1, const char* line2, int16_t y_offset);
+
+    /**
+     * @brief Draw the battery level indicator.
+     * @param level Battery level percentage or status value.
+     */
     void draw_battery(int32_t level);
+
+    /**
+     * @brief Draw a measurement number on the screen.
+     * @param number The measurement value to display.
+     */
     void draw_measure_number(int16_t number);
 
 private:
-    M5GFX   &_display;
-    M5Canvas   _sprite;
 
-    int16_t _center_x;
-    int16_t _center_y;
-    char _string_buff[16];
+    M5GFX    &_display;                 ///< Reference to the main M5GFX display driver
+    M5Canvas   _sprite;                 ///< Internal canvas buffer for flicker-free drawing
 
-    int32_t _color_fg_text;
-    int32_t _color_bg_text;
+    int16_t _center_x;                  ///< Screen center X coordinate
+    int16_t _center_y;                  ///< Screen center Y coordinate
+    char _string_buff[LAY_TEXT_BUFFER]; ///< Internal buffer for string formatting
 };
 
-
-
 #endif // _DRAWER_H_

+ 20 - 129
main/src/main.cpp

@@ -1,143 +1,34 @@
 #include "Arduino.h"
-#include "states/state_impl/state_impl.h"
-#include "drawer/drawer.h"
-#include "EspNowSerial/EspNowSerial.h"
-#include "nbus/nbus_comm.h"
+#include "states/state_runner/state_runner.h"
 
+// State variables
+StateLock   StateLockInst;
+StateTarget StateTargetInst;
+StateStart  StateStartInst;
+StateRun    StateRunInst;
+StateStop   StateStopInst;
+StateCancel StateCancelInst;
 
-
-NbusComm NBC(Dev.EnSerial);
-
-StateLock   state_lock;
-StateTarget state_target;
-StateStart  state_start;
-StateRun    state_run;
-StateStop   state_stop;
-StateCancel state_cancel;
-
-IState *states[_STATES_COUNT] = {
-  &state_lock,
-  &state_target,
-  &state_start,
-  &state_run,
-  &state_stop,
-  &state_cancel
-}; 
-
-StateTx curr_state(STATE_LOCK);
-StateTx next_state(STATE_LOCK);
-int64_t time0;
+// App scheduler
+StateRunner Runner;
 
 void setup()
 {
-
+  // init hardware
   Dev.hardware_init();
-  Dev.M5Board.Display.setBrightness(DISP_BRIGHTNESS);
-
-  Dev.Drawer.set_text_color(TFT_WHITE, TFT_BLACK);
-  Dev.Drawer.draw_battery(Dev.M5Board.Power.getBatteryLevel());
 
-  time0 = Dev.M5Board.millis();
+  // init scheduler
+  Runner.add_state(StateLockInst);
+  Runner.add_state(StateTargetInst);
+  Runner.add_state(StateStartInst);
+  Runner.add_state(StateRunInst);
+  Runner.add_state(StateStopInst);
+  Runner.add_state(StateCancelInst);
   
-  states[curr_state.id]->begin();
-  Dev.Drawer.refresh();
-  Dev.enter_low_power();
-}
-
-void _update_loop()
-{
-  curr_state = next_state;
-  Dev.M5Board.update();
-}
-
-void _handle_same_state()
-{
-  if (next_state.mode != 0)
-  Serial.println((int)next_state.mode);
-
-  if (next_state.mode == REFRESH || next_state.mode == REFRESH_WAIT)
-  {
-    // upadte screen
-    Dev.enter_high_power(false);
-    Dev.Drawer.refresh();
-    Serial.println("here");
-    Dev.enter_low_power();
-  }
-
-  if (next_state.mode == WAIT || next_state.mode == REFRESH_WAIT)
-  {
-    Dev.M5Board.delay(next_state.wait);
-  }
-}
-
-void _handle_next_state()
-{
-  // if error occured
-  if (next_state.mode == ERROR)
-  {
-    Dev.enter_high_power(false);              // do not turn on wifi in error, do not call end()
-  }
-  else
-  {
-    Dev.enter_high_power(true);               // turn on wifi 
-    states[curr_state.id]->end();             // properly end previous state
-  }
-
-  // refresh display if needed
-  if (next_state.mode == REFRESH || next_state.mode == REFRESH_WAIT)
-  {
-    Dev.Drawer.refresh();
-  }
-
-  // wait between states if needed
-  if (next_state.mode == WAIT || next_state.mode == REFRESH_WAIT)
-  {    
-    Dev.enter_low_power();                    // go to low power mode
-    Dev.M5Board.delay(next_state.wait);       // during wait
-    Dev.enter_high_power(true);
-  }
-
-  // init new state
-  states[next_state.id]->begin();
-  Dev.Drawer.refresh();
-
-  // go to low power
-  Dev.enter_low_power();
-  // set new time
-  time0 = Dev.M5Board.millis();
-}
-
-void _update_battery()
-{
-  // redraw battery
-  Dev.set_battery_refresh(false);
-  Dev.Drawer.draw_battery(Dev.M5Board.Power.getBatteryLevel());
-
-  // upadte screen
-  Dev.enter_high_power(false);
-  Dev.Drawer.refresh();
-  Dev.enter_low_power();
+  Runner.begin();
 }
 
 void loop()
 {
-  // proces logic on loop
-  next_state = states[curr_state.id]->loop(Dev.M5Board.millis() - time0);
-  if (next_state.mode != 0)
-  {
-    Serial.printf("state %d -> %d", next_state.id, next_state.mode);
-  }
-  
-  // if no state change
-  if (next_state.id == curr_state.id)
-    _handle_same_state();
-  else
-    _handle_next_state();
-
-  // update battery
-  if (Dev.get_battery_refresh())
-    _update_battery();
-
-  // update current variables
-  _update_loop();
+  Runner.loop();
 }

+ 10 - 11
main/src/nbus/nbus_comm.cpp → main/src/nbus/nbus_broadcast_comm.cpp

@@ -1,6 +1,6 @@
-#include "nbus_comm.h"
+#include "nbus_broadcast_comm.h"
 
-uint8_t const NbusComm::_crc8x_table[] = {
+uint8_t const NbusBroadcastComm::_crc8x_table[] = {
     0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, 0x70, 0x77, 0x7E,
     0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB,
     0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8,
@@ -16,7 +16,7 @@ uint8_t const NbusComm::_crc8x_table[] = {
     0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC,
     0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3};
 
-uint8_t NbusComm::_crc8x_fast(void const *mem, uint16_t len)
+uint8_t NbusBroadcastComm::_crc8x_fast(void const *mem, uint16_t len)
 {
     uint8_t crc = CRC8_INIT_VALUE;
     uint8_t const *data = (uint8_t *)mem;
@@ -28,31 +28,30 @@ uint8_t NbusComm::_crc8x_fast(void const *mem, uint16_t len)
     return crc;
 }
 
-NbusComm::NbusComm(Stream &slave_interface) :_slave_interface{slave_interface}
+NbusBroadcastComm::NbusBroadcastComm(Stream &slave_interface) :_slave_interface{slave_interface}
 {
 
 }
 
-
-void NbusComm::cmd_set_start()
+void NbusBroadcastComm::cmd_set_start()
 {
    _init_packet(CMD_SET_START, nullptr, 0);
    _slave_interface.write(_packet, _size);
 }
 
-void NbusComm::cmd_set_stop()
+void NbusBroadcastComm::cmd_set_stop()
 {
     _init_packet(CMD_SET_STOP, nullptr, 0);
     _slave_interface.write(_packet, _size);
 }
 
-void NbusComm::cmd_set_data(uint8_t *data, int16_t count)
+void NbusBroadcastComm::cmd_set_data(uint8_t *data, int16_t count)
 {
     _init_packet(CMD_SET_DATA, data, count);
     _slave_interface.write(_packet, _size);
 }
 
-void  NbusComm::_init_packet(uint8_t fc, uint8_t *data, int16_t count)
+void  NbusBroadcastComm::_init_packet(uint8_t fc, uint8_t *data, int16_t count)
 {   
     if (count > NBUS_COMM_PACKET_SIZE - NBUS_META)
         return;
@@ -66,7 +65,7 @@ void  NbusComm::_init_packet(uint8_t fc, uint8_t *data, int16_t count)
     _add_crc();
 }
 
-void NbusComm::_add_data(uint8_t *data, int16_t count)
+void NbusBroadcastComm::_add_data(uint8_t *data, int16_t count)
 {
     if (data == nullptr || count == 0)
         return;
@@ -75,7 +74,7 @@ void NbusComm::_add_data(uint8_t *data, int16_t count)
         _packet[_size++] = data[i];
 }
 
-void NbusComm::_add_crc()
+void NbusBroadcastComm::_add_crc()
 {
     _packet[_size] = _crc8x_fast(_packet + 1, _size - 1);
     _size++;

+ 93 - 0
main/src/nbus/nbus_broadcast_comm.h

@@ -0,0 +1,93 @@
+#ifndef _NBUS_BROADCAST_COMM_H_
+#define _NBUS_BROADCAST_COMM_H_
+
+#include <Stream.h>
+
+#define CRC8_INIT_VALUE 0x0        ///< Initial value for CRC8 calculation
+#define CRC8_TABLE_SIZE 256        ///< Size of the CRC8 lookup table
+#define NBUS_COMM_PACKET_SIZE 16   ///< Fixed size of an Nbus broadcast packet in bytes
+
+#define NBUS_PACKET_META 4         ///< Number of metadata without length byte
+#define NBUS_META        5         ///< Metadata size identifier
+#define NBUS_LEN         0         ///< Packet length index
+#define NBUS_MA          1         ///< Module address index
+#define NBUS_SA          2         ///< Sensor address index
+#define NBUS_FC          3         ///< Function code index
+#define NBUS_DT          4         ///< Data payload start index
+
+/**
+ * @brief Command codes for Nano Bus broadcast communication.
+ */
+enum NbusCmd
+{
+    CMD_SET_START = 0x23,          ///< Command to start an automatic transmission
+    CMD_SET_STOP  = 0x22,          ///< Command to stop an automatic transmission
+    CMD_SET_DATA  = 0x2B           ///< Command to set data
+};
+
+/**
+ * @brief Handler for managing Nano Bus broadcast communication over a stream interface.
+ */
+class NbusBroadcastComm
+{
+public:
+    /**
+     * @brief Construct a new NbusBroadcastComm object.
+     * @param slave_interface Reference to the communication stream (e.g., HardwareSerial, SoftwareSerial).
+     */
+    NbusBroadcastComm(Stream &slave_interface);
+
+    /**
+     * @brief Send a broadcast command to start operations.
+     */
+    void cmd_set_start();
+
+    /**
+     * @brief Send a broadcast command to stop operations.
+     */
+    void cmd_set_stop();
+
+    /**
+     * @brief Send a broadcast set data command containing a payload.
+     * @param data Pointer to the data buffer to transmit.
+     * @param count Number of bytes to transmit from the buffer.
+     */
+    void cmd_set_data(uint8_t *data, int16_t count);
+
+private:
+    Stream &_slave_interface;      ///< Reference to the underlying communication stream interface
+
+    /**
+     * @brief Initialize a new communication packet with a function code and optional data.
+     * @param fc Function code for the packet.
+     * @param data Pointer to the payload data.
+     * @param count Number of bytes in the data payload.
+     */
+    void _init_packet(uint8_t fc, uint8_t *data, int16_t count);
+
+    /**
+     * @brief Append data payload to the current packet.
+     * @param data Pointer to the data to add.
+     * @param count Number of bytes to add.
+     */
+    void _add_data(uint8_t *data, int16_t count);
+
+    /**
+     * @brief Calculate and append the CRC8 checksum to the packet.
+     */
+    void _add_crc();
+
+    int32_t _size{0};                                       ///< Current size of the packet in bytes
+    uint8_t _packet[NBUS_COMM_PACKET_SIZE];                 ///< Internal packet buffer
+    static uint8_t const _crc8x_table[CRC8_TABLE_SIZE];     ///< Precalculated CRC8 lookup table
+
+    /**
+     * @brief Compute the fast CRC8 checksum for a memory block using a lookup table.
+     * @param mem Pointer to the memory block.
+     * @param len Length of the memory block in bytes.
+     * @return uint8_t Calculated CRC8 checksum value.
+     */
+    static uint8_t _crc8x_fast(void const *mem, uint16_t len);
+};
+
+#endif // _NBUS_BROADCAST_COMM_H_

+ 0 - 54
main/src/nbus/nbus_comm.h

@@ -1,54 +0,0 @@
-#ifndef _NBUS_COMM_H_
-#define _NBUS_COMM_H_
-
-#include <Stream.h>
-
-#define CRC8_INIT_VALUE 0x0
-#define CRC8_TABLE_SIZE 256
-#define NBUS_COMM_PACKET_SIZE 16
-
-
-#define NBUS_PACKET_META 4
-#define NBUS_META 5
-#define NBUS_LEN 0
-#define NBUS_MA  1
-#define NBUS_SA  2
-#define NBUS_FC  3
-#define NBUS_DT  4
-
-
-enum NbusCmd
-{
-    CMD_SET_START = 0x23,
-    CMD_SET_STOP  = 0x22,
-    CMD_SET_DATA  = 0x2B
-};
-
-class NbusComm
-{
-public:
-    NbusComm(Stream &slave_interface);
-
-    void cmd_set_start();
-    void cmd_set_stop();
-    void cmd_set_data(uint8_t *data, int16_t count);
-
-
-private:
-    Stream &_slave_interface;
-    
-    void _init_packet(uint8_t fc, uint8_t *data, int16_t count);
-    void _add_data(uint8_t *data, int16_t count);
-    void _add_crc();
-
-
-    int32_t _size{0};
-    uint8_t _packet[NBUS_COMM_PACKET_SIZE];
-    static uint8_t const _crc8x_table[CRC8_TABLE_SIZE];
-
-
-    static uint8_t _crc8x_fast(void const *mem, uint16_t len);
-
-};
-
-#endif // _NBUS_COMM_H_

+ 59 - 6
main/src/states/state_impl/state_impl.cpp

@@ -11,7 +11,14 @@ void GenericState::send_start()
 
 void GenericState::send_phase(int8_t phase)
 {
-    GenericState::_nbus_comm.cmd_set_data(PhaseCmd{NBUS_MEAS_ID, (uint8_t)GenericState::_measure_num, (uint8_t)phase}.data, sizeof(PhaseCmd));
+    GenericState::_nbus_comm.cmd_set_data(
+        PhaseCmd{
+            NBUS_MEAS_ID, 
+            (uint8_t)GenericState::_measure_num, 
+            (uint8_t)phase
+        }.data, 
+        sizeof(PhaseCmd)
+    );
 }
 
 void GenericState::send_stop()
@@ -24,8 +31,14 @@ void GenericState::send_stop()
 //  STATE LOCK
 //////////////////////////////////////////////////////////////////////////////////////////////////
 
+StateID StateLock::whoami()
+{
+    return STATE_LOCK;
+}
+
 void StateLock::begin()
 {
+    // send nbus command
     GenericState::send_stop();
     
     // draw screen
@@ -46,7 +59,7 @@ StateTx StateLock::loop(int64_t duration)
     }
 
     // released button -> go to target
-    else if (Dev.M5Board.BtnA.wasReleased())
+    else if (Dev.M5Board.BtnA.wasReleasedAfterHold())
        return {STATE_TARGET};
 
     // timer -> power off    
@@ -66,6 +79,11 @@ void StateLock::end()
 //  STATE TARGET
 //////////////////////////////////////////////////////////////////////////////////////////////////
 
+StateID StateTarget::whoami()
+{
+    return STATE_TARGET;
+}
+
 void StateTarget::begin()
 {
     // draw screen
@@ -89,9 +107,11 @@ StateTx StateTarget::loop(int64_t duration)
 
 void StateTarget::end()
 {
+    // send nbus command
     GenericState::send_start();
     GenericState::send_phase(NBUS_PHASE_TARGET);
 
+    // draw screen
     Dev.Drawer.draw_icon(TARGET_ICON, COL_ICON_FG, COL_ICON_BG_ACTIVE, LAY_ICON_YOFFSET);
 }
 
@@ -100,6 +120,11 @@ void StateTarget::end()
 //  STATE START
 //////////////////////////////////////////////////////////////////////////////////////////////////
 
+StateID StateStart::whoami()
+{
+    return STATE_START;
+}
+
 void StateStart::begin()
 {
     // set measuring phase
@@ -125,8 +150,10 @@ StateTx StateStart::loop(int64_t duration)
 
 void StateStart::end()
 {
+    // send nbus command
     GenericState::send_phase(NBUS_PHASE_START);
 
+    // draw screen
     Dev.Drawer.draw_icon(GO_ICON, COL_ICON_FG, COL_ICON_BG_ACTIVE, LAY_ICON_YOFFSET);
 }
 
@@ -134,8 +161,17 @@ void StateStart::end()
 //  STATE RUN
 //////////////////////////////////////////////////////////////////////////////////////////////////
 
+StateID StateRun::whoami()
+{
+    return STATE_RUN;
+}
+
 void StateRun::begin()
 {
+    // reset state variables
+    _last_blink_time = 0;
+    _icon_state = false;
+
     // draw screen
     Dev.Drawer.draw_icon(REC_ICON, COL_ICON_FG, COL_ICON_BG_INACTIVE, LAY_ICON_YOFFSET);
     Dev.Drawer.draw_description("Meranie", "pohybu", LAY_DESC_YOFFSET);
@@ -150,6 +186,7 @@ StateTx StateRun::loop(int64_t duration)
     // if need to refresh
     if (duration - _last_blink_time >= TIM_RUN_BLINK)
     {
+        // change blinking state
         _icon_state ^= 1;
         _last_blink_time = duration;
 
@@ -175,8 +212,7 @@ StateTx StateRun::loop(int64_t duration)
 
 void StateRun::end()
 {
-    _last_blink_time = 0;
-    _icon_state = false;
+    // send nbus command
     GenericState::send_phase(NBUS_PHASE_MOVEMENT);
 }
 
@@ -184,10 +220,14 @@ void StateRun::end()
 //  STATE STOP
 //////////////////////////////////////////////////////////////////////////////////////////////////
 
-
+StateID StateStop::whoami()
+{
+    return STATE_STOP;
+}
 
 void StateStop::begin()
 {
+    // send nbus command
     GenericState::send_phase(NBUS_PHASE_ACK);
 
     // draw screen
@@ -197,6 +237,7 @@ void StateStop::begin()
 
 StateTx StateStop::loop(int64_t duration)
 {
+    // timer -> lock 
     if (duration > TIM_INFO_SCREEN)
         return {STATE_LOCK, REFRESH_WAIT, TIM_INFO_SCREEN};
 
@@ -205,6 +246,7 @@ StateTx StateStop::loop(int64_t duration)
 
 void StateStop::end()
 {
+    // draw screen
     Dev.Drawer.draw_icon(OK_ICON, COL_ICON_FG, COL_ICON_BG_ACTIVE, LAY_ICON_YOFFSET);
 }
 
@@ -213,8 +255,14 @@ void StateStop::end()
 //  STATE CANCEL
 //////////////////////////////////////////////////////////////////////////////////////////////////
 
+StateID StateCancel::whoami()
+{
+    return STATE_CANCEL;
+}
+
 void StateCancel::begin()
 {
+    // draw screen
     Dev.Drawer.draw_measure_number(--GenericState::_measure_num);
     Dev.Drawer.draw_icon(CANCEL_ICON, COL_ICON_FG, COL_ICON_BG_CANCEL, LAY_ICON_YOFFSET);
     Dev.Drawer.draw_description("Meranie", "zrusene", LAY_DESC_YOFFSET);
@@ -222,7 +270,12 @@ void StateCancel::begin()
 
 StateTx StateCancel::loop(int64_t duration)
 {
-    if (duration > TIM_INFO_SCREEN && Dev.M5Board.BtnA.isReleased())
+    // timer -> delay sceen
+    if (duration < TIM_INFO_SCREEN)
+        return {STATE_CANCEL, WAIT, TIM_INFO_SCREEN};
+    
+    // button released -> lock    
+    if (Dev.M5Board.BtnA.wasReleasedAfterHold())
         return {STATE_LOCK};
     
     return {STATE_CANCEL};

+ 44 - 13
main/src/states/state_impl/state_impl.h

@@ -3,79 +3,110 @@
 
 #include "device/device.h"
 #include "states/state_interface.h"
-#include "nbus/nbus_comm.h"
+#include "nbus/nbus_broadcast_comm.h"
 #include "data_structures/phase_cmd.h"
 
 #include <atomic>
 
-
+/**
+ * @brief Base class for application states, providing shared communication and measurement utilities.
+ */
 class GenericState: public IState
 {
 protected:
+    /**
+     * @brief Send a start broadcast command via Nano Bus.
+     */
     static void send_start();
+
+    /**
+     * @brief Send a measurement phase command via Nano Bus.
+     * @param phase The phase number to transmit.
+     */
     static void send_phase(int8_t phase);
+
+    /**
+     * @brief Send a stop broadcast command via Nano Bus.
+     */
     static void send_stop();
 
-    inline static std::atomic<int32_t> _measure_num{1};
-    inline static NbusComm _nbus_comm{Dev.EnSerial};
+    inline static std::atomic<int32_t> _measure_num{1};         ///< Shared atomic counter tracking the current measurement number
+    inline static NbusBroadcastComm _nbus_comm{Dev.EnSerial};   ///< Shared Nano Bus communication handler instance
 };
 
-
+/**
+ * @brief Represents the locked or idle state of the application.
+ */
 class StateLock : public GenericState
 {
 public:
+    StateID whoami() override;   
     void begin() override;
     StateTx loop(int64_t duration) override;
     void end() override;
 };
 
-
+/**
+ * @brief Represents the positioning state.
+ */
 class StateTarget : public GenericState
 {
 public:
+    StateID whoami() override;
     void begin() override;
     StateTx loop(int64_t duration) override;
     void end() override;
 };
 
-
+/**
+ * @brief Represents the start sequence state (e.g. movement recording).
+ */
 class StateStart : public GenericState
 {
 public:
+    StateID whoami() override;
     void begin() override;
     StateTx loop(int64_t duration) override;
     void end() override;
 };
 
-
+/**
+ * @brief Represents the active measurement execution state.
+ */
 class StateRun : public GenericState
 {
 public:
+    StateID whoami() override;
     void begin() override;
     StateTx loop(int64_t duration) override;
     void end() override;
 private:
-    int64_t _last_blink_time = 0;
-    bool _icon_state = false;
+    int64_t _last_blink_time = 0; ///< Timestamp of the last UI blink update
+    bool _icon_state = false;     ///< Current visibility state of the blinking icon
 };
 
+/**
+ * @brief Represents the completion of a measurement cycle.
+ */
 class StateStop : public GenericState
 {
 public:
+    StateID whoami() override;
     void begin() override;
     StateTx loop(int64_t duration) override;
     void end() override;
 };
 
-
+/**
+ * @brief Represents the abort state.
+ */
 class StateCancel : public GenericState
 {
 public:
+    StateID whoami() override;
     void begin() override;
     StateTx loop(int64_t duration) override;
     void end() override;
 };
 
-
-
 #endif // _STATE_IMPL_H_

+ 22 - 2
main/src/states/state_interface.h

@@ -3,14 +3,34 @@
 
 #include "data_structures/state_tx.h"
 
-
+/**
+ * @brief Abstract interface class defining the lifecycle and behavior for application states.
+ */
 class IState
 {
 public:
+    /**
+     * @brief Get the unique identifier of the current state.
+     * @return StateID The identifier of this state.
+     */
+    virtual StateID whoami() = 0;    
+
+    /**
+     * @brief Enter the state.
+     */
     virtual void begin() = 0;
+
+    /**
+     * @brief Execute the main logic loop for the state.
+     * @param duration Time duration (in milliseconds or ticks) since the beginning of state.
+     * @return StateTx Metadata regarding state transitions, modes, and wait times.
+     */
     virtual StateTx loop(int64_t duration) = 0; 
+
+    /**
+     * @brief Clean up resources and exit the state.
+     */
     virtual void end() = 0;
 };
 
-
 #endif // _STATE_INTERFACE_H_

+ 107 - 0
main/src/states/state_runner/state_runner.cpp

@@ -0,0 +1,107 @@
+#include "state_runner.h"
+
+bool StateRunner::add_state(IState &state)
+{
+    uint8_t idx = state.whoami();
+
+    if (idx > _STATES_COUNT)
+        return false;
+    
+    _states[idx] = &state;
+    return true;
+}
+
+void StateRunner::begin()
+{
+    // device starts in high power
+    _states[_curr_state.id]->begin();
+    Dev.Drawer.draw_battery(Dev.M5Board.Power.getBatteryLevel());
+    Dev.Drawer.refresh();
+    Dev.enter_low_power();
+}
+
+ void StateRunner::loop()
+ {
+    // process logic on loop
+    _next_state = _states[_curr_state.id]->loop(Dev.M5Board.millis() - _time0);
+
+    // process transition between states
+    if (_next_state.id == _curr_state.id)
+        _move_to_same_state();
+    else
+        _move_to_next_state();
+
+    // update battery if needed
+    if (Dev.get_battery_refresh())
+        _update_battery();
+
+    // update  loop variables
+    _curr_state = _next_state;
+    Dev.M5Board.update();
+ }
+
+void StateRunner::_move_to_same_state()
+{
+  // refresh if needed
+  if (_next_state.mode == REFRESH || _next_state.mode == REFRESH_WAIT)
+  {
+    Dev.enter_high_power(false);
+    Dev.Drawer.refresh();
+    Dev.enter_low_power();
+  }
+
+  // delay if needed
+  if (_next_state.mode == WAIT || _next_state.mode == REFRESH_WAIT)
+  {
+    Dev.M5Board.delay(_next_state.wait);
+  }
+}
+
+void StateRunner::_move_to_next_state()
+{
+  // if error occured
+  if (_next_state.mode == ERROR)
+  {
+    Dev.enter_high_power(false);              // do not turn on wifi in error, do not call end()
+  }
+  else
+  {
+    Dev.enter_high_power(true);               // turn on wifi 
+    _states[_curr_state.id]->end();           // properly end previous state
+  }
+
+  // refresh display if needed
+  if (_next_state.mode == REFRESH || _next_state.mode == REFRESH_WAIT)
+  {
+    Dev.Drawer.refresh();
+  }
+
+  // wait between states if needed
+  if (_next_state.mode == WAIT || _next_state.mode == REFRESH_WAIT)
+  {    
+    Dev.enter_low_power();                    // go to low power mode
+    Dev.M5Board.delay(_next_state.wait);      // during wait
+    Dev.enter_high_power(true);
+  }
+
+  // init new state
+  _states[_next_state.id]->begin();
+  Dev.Drawer.refresh();
+
+  // go to low power
+  Dev.enter_low_power();
+  // set new time
+  _time0 = Dev.M5Board.millis();
+}
+
+void StateRunner::_update_battery()
+{
+  // redraw battery
+  Dev.set_battery_refresh(false);
+  Dev.Drawer.draw_battery(Dev.M5Board.Power.getBatteryLevel());
+
+  // upadte screen
+  Dev.enter_high_power(false);
+  Dev.Drawer.refresh();
+  Dev.enter_low_power();
+}

+ 53 - 0
main/src/states/state_runner/state_runner.h

@@ -0,0 +1,53 @@
+#ifndef _STATE_RUNNER_H_
+#define _STATE_RUNNER_H_
+
+#include "states/state_impl/state_impl.h"
+
+/**
+ * @brief Application-dependent state machine scheduler and runner managing state lifecycles and transitions.
+ */
+class StateRunner
+{
+public:
+    /**
+     * @brief Initialize the state runner and register initial states.
+     */
+    void begin();
+
+    /**
+     * @brief Add a state instance to the scheduler's state registry.
+     * @param state Reference to the state object implementing IState.
+     * @return true If the state was added successfully.
+     * @return false If registration failed.
+     * @note Must be called before begin().
+     */
+    bool add_state(IState &state);
+
+    /**
+     * @brief Execute the primary scheduler loop, handling state execution, timing, and transitions.
+     */
+    void loop();
+
+private:
+    /**
+     * @brief Handle continuation within the same state.
+     */
+    void _move_to_same_state();
+
+    /**
+     * @brief Transition execution from the current state to the next state.
+     */
+    void _move_to_next_state();
+
+    /**
+     * @brief Update battery status or trigger periodic battery checks.
+     */
+    void _update_battery();
+
+    IState *_states[_STATES_COUNT]{nullptr};    ///< Array of pointers to registered state instances
+    StateTx _curr_state{STATE_LOCK};            ///< Metadata describing the currently active state
+    StateTx _next_state{STATE_LOCK};            ///< Metadata describing the upcoming state
+    uint64_t _time0{0};                         ///< Timestamp tracking the start of the current state loop or duration
+};
+
+#endif // _STATE_RUNNER_H_