ソースを参照

Powersave features

xnecas 2 週間 前
親
コミット
f843607add

+ 2 - 0
.clangd

@@ -0,0 +1,2 @@
+CompileFlags:
+    Remove: [-f*, -m*]

+ 7 - 1
.vscode/settings.json

@@ -27,5 +27,11 @@
   "idf.customExtraVars": {
     "IDF_TARGET": "esp32s3"
   },
-  "idf.flashType": "UART"
+  "idf.flashType": "UART",
+  "clangd.path": "C:\\Espressif\\tools\\esp-clang\\esp-19.1.2_20250312\\esp-clang\\bin\\clangd.exe",
+  "clangd.arguments": [
+    "--background-index",
+    "--query-driver=**",
+    "--compile-commands-dir=c:\\EspProjects\\bat-remote\\build"
+  ]
 }

+ 85 - 0
main/src/backup.txt

@@ -0,0 +1,85 @@
+#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();
+}

+ 12 - 0
main/src/config/all_config.h

@@ -0,0 +1,12 @@
+#ifndef _ALL_CONFIG_H_
+#define _ALL_CONFIG_H_
+
+#include "states_config.h"
+#include "colors_config.h"
+#include "timing_config.h"
+#include "layout_config.h"
+#include "hw_config.h"
+#include "icons/all_icons.h"
+
+
+#endif // _ALL_CONFIG_H_

+ 12 - 0
main/src/config/colors_config.h

@@ -0,0 +1,12 @@
+#ifndef _COLORS_CONFIG_H_
+#define _COLORS_CONFIG_H_
+
+#define COL_ICON_FG           TFT_WHITE
+
+#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
+
+
+#endif // _COLORS_H_

+ 14 - 0
main/src/config/hw_config.h

@@ -0,0 +1,14 @@
+#ifndef _HW_CONFIG_H_
+#define _HW_CONFIG_H_
+
+#define SERIAL_BAUD 115200
+
+#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
+
+#endif // _HW_CONFIG_H_

+ 7 - 0
main/src/config/layout_config.h

@@ -0,0 +1,7 @@
+#ifndef _LAYOUT_CONFIG_H_
+#define _LAYOUT_CONFIG_H_
+
+#define LAY_ICON_YOFFSET  -15
+#define LAY_DESC_YOFFSET  +60
+
+#endif // _LAYOUT_CONFIG_H_

+ 8 - 5
main/src/config/states_config.h

@@ -3,11 +3,14 @@
 
 enum StateID
 {
-    NOTHING = -1,
-    LOCK = 0,
-    TARGET,
-    START,
-    STOP,
+    STATE_NULL = -1,
+    STATE_LOCK = 0,
+    STATE_UNLOCK,
+    STATE_TARGET,
+    STATE_START,
+    STATE_RUN,
+    STATE_STOP,
+    STATE_CANCEL,
     _STATES_COUNT
 };
 

+ 11 - 0
main/src/config/timing_config.h

@@ -0,0 +1,11 @@
+#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
+
+#endif // _TIMING_CONFIG_H_

+ 18 - 0
main/src/data_structures/phase_cmd.h

@@ -0,0 +1,18 @@
+#ifndef _PHASE_CMD_H_
+#define _PHASE_CMD_H_
+
+#include <cinttypes>
+
+union PhaseCmd
+{
+    struct
+    {
+        uint8_t sensor_addr;
+        uint8_t measure_num;
+        uint8_t phase_num; 
+    };
+
+    uint8_t data[3];
+};
+
+#endif // _PHASE_CMD_H_

+ 17 - 0
main/src/data_structures/state_tx.h

@@ -0,0 +1,17 @@
+#ifndef _STATE_DATA_H_
+#define _STATE_DATA_H_
+
+#include <cinttypes>
+
+enum StateID;
+
+struct StateTx
+{
+    StateTx(StateID id) : id{id}, wait{-1} {}
+    StateTx(StateID id, int64_t wait) : id{id}, wait{wait} {}
+
+    StateID id;
+    int64_t wait;
+};
+
+#endif // _STATE_DATA_H_

+ 74 - 0
main/src/device/device.cpp

@@ -0,0 +1,74 @@
+#include "device.h"
+
+void Device::battery_timer_cb(TimerHandle_t pxTimer)
+{
+    Device::_batt_refresh = true;
+}
+
+
+bool Device::get_battery_refresh()
+{
+    return Device::_batt_refresh;
+}
+
+void Device::set_battery_refresh(bool value)
+{
+    Device::_batt_refresh = value;
+}
+
+void Device::begin_esp_now_serial()
+{
+    EnSerial.begin(EN_SERIAL_CHANNEL, EN_SERIAL_BROADCAST);
+    EspMacAddress rx_address = EN_SERIAL_ADDRESS;    
+    EnSerial.add_peer(rx_address);
+    _wifi_active = true;
+}
+
+void Device::end_esp_now_serial()
+{
+    EnSerial.end();
+    _wifi_active = false;
+}
+
+
+bool Device::hardware_init()
+{
+    // init Serial
+    Serial.begin(SERIAL_BAUD);
+    
+    // init M5 base class
+    this->M5Board.begin();
+
+    // init esp-now serial
+    this->begin_esp_now_serial();
+
+    // init drawer
+    Drawer.begin();
+
+    BattTimer = xTimerCreate("BatteryTimer", pdMS_TO_TICKS(TIM_BATTERY_REFRESH), pdTRUE, NULL, battery_timer_cb);
+
+    if (BattTimer != NULL)
+        return xTimerStart(BattTimer, 0) == pdPASS;
+
+    return false;
+}
+
+void Device::enter_low_power()
+{
+    if (_wifi_active)
+        end_esp_now_serial();
+
+    setCpuFrequencyMhz(10);
+}
+
+void Device::enter_high_power(bool wifi)
+{
+    setCpuFrequencyMhz(160);
+    
+    if (wifi)
+        begin_esp_now_serial();
+}
+
+
+// global instance
+Device Dev;

+ 42 - 0
main/src/device/device.h

@@ -0,0 +1,42 @@
+#ifndef _DEVICE_H_
+#define _DEVICE_H_
+
+
+#include "M5Unified.h"
+#include "drawer/drawer.h"
+#include "EspNowSerial/EspNowSerial.h"
+
+#include "config/all_config.h"
+
+class Device
+{
+public:
+    m5::M5Unified &M5Board{M5};
+    M5Drawer Drawer{M5.Display};
+    EspNowSerial EnSerial;
+    TimerHandle_t BattTimer;
+
+    bool hardware_init();
+    bool get_battery_refresh();
+    void set_battery_refresh(bool value);
+
+    void enter_low_power();
+    void enter_high_power(bool wifi=false);
+
+private:
+    void begin_esp_now_serial();
+    void end_esp_now_serial();
+
+    bool _wifi_active{false};
+
+
+    static inline void battery_timer_cb(TimerHandle_t pxTimer);
+    static inline bool _batt_refresh{false};
+};
+
+// global instance
+extern Device Dev;
+
+
+
+#endif // _DEVICE_H_

+ 81 - 0
main/src/drawer/drawer.cpp

@@ -0,0 +1,81 @@
+#include "drawer.h"
+
+M5Drawer::M5Drawer(M5GFX &display)
+: _display{display}, _sprite{&display}
+{
+
+}
+
+void M5Drawer::begin()
+{
+    _center_x = _display.width() / 2;
+    _center_y = _display.height() / 2;
+
+    _sprite.createSprite(_display.width(), _display.height());
+    _sprite.clear();
+}
+
+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)
+{
+    // calculate position
+    int16_t half_max_dim = max(icon.width, icon.height) / 2;
+    int16_t x0 = _center_x - half_max_dim;
+    int16_t y0 = _center_y - half_max_dim + y_offset;
+
+    // draw background circle
+    _sprite.fillCircle(_center_x, _center_y + y_offset, half_max_dim, bg_color);
+
+    // draw bitmap on top
+    _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);
+
+    int16_t text_max_height = _sprite.fontHeight() * 2;
+    _sprite.fillRect(0, _center_y + y_offset, _display.width(), text_max_height, _color_bg_text);
+
+    int16_t half_max_width = max(_sprite.textWidth(line1), _sprite.textWidth(line2)) / 2;
+    int16_t x0 = _center_x;// - half_max_width;
+    int16_t y0 = _center_y + y_offset;
+
+    _sprite.setTextDatum(top_center);
+    _sprite.setTextColor(_color_fg_text, _color_bg_text);
+
+    _sprite.drawString(line1, x0, y0);
+    _sprite.drawString(line2, x0, y0 + _sprite.fontHeight());
+}
+
+void M5Drawer::draw_battery(int32_t level)
+{
+    _sprite.setFont(&Font4);
+
+    snprintf(_string_buff, sizeof(_string_buff), "%d%%", (int16_t)level);
+    
+    _sprite.setTextDatum(top_right);
+    _sprite.drawString(_string_buff, _display.width() - 1, 0);
+}
+
+void M5Drawer::draw_measure_number(int16_t number)
+{
+    _sprite.setFont(&Font4);
+
+    snprintf(_string_buff, sizeof(_string_buff), "M%d", number);
+    
+    _sprite.setTextDatum(top_left);
+    _sprite.drawString(_string_buff, 0, 0);
+}

+ 35 - 0
main/src/drawer/drawer.h

@@ -0,0 +1,35 @@
+#ifndef _DRAWER_H_
+#define _DRAWER_H_
+
+#include <M5GFX.h>
+#include "data_structures/icon_struct.h"
+
+class M5Drawer
+{
+public:
+    M5Drawer(M5GFX &display);
+    void begin();
+    void refresh();
+
+    void set_text_color(int32_t fg_color, int32_t bg_color);
+
+    void draw_icon(const IconStruct &icon, int32_t fg_color, int32_t bg_color, int16_t y_offset);
+    void draw_description(const char* line1, const char* line2, int16_t y_offset);
+    void draw_battery(int32_t level);
+    void draw_measure_number(int16_t number);
+
+private:
+    M5GFX   &_display;
+    M5Canvas   _sprite;
+
+    int16_t _center_x;
+    int16_t _center_y;
+    char _string_buff[16];
+
+    int32_t _color_fg_text;
+    int32_t _color_bg_text;
+};
+
+
+
+#endif // _DRAWER_H_

+ 1 - 0
main/src/icons/all_icons.h

@@ -5,6 +5,7 @@
 #include "go_icon.h"
 #include "lock_icon.h"
 #include "ok_icon.h"
+#include "rec_icon.h"
 #include "target_icon.h"
 #include "unlock_icon.h"
 

+ 109 - 0
main/src/icons/rec_icon.h

@@ -0,0 +1,109 @@
+#ifndef _REC_ICON_H_
+#define _REC_ICON_H_
+
+#include "data_structures/icon_struct.h"
+
+#define ICON_REC_WIDTH  95
+#define ICON_REC_HEIGHT 95
+
+static const uint8_t ICON_REC_DATA[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0xc0, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0xff, 0x1f, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0xff, 0x1f, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0xc0, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static const IconStruct REC_ICON = {ICON_REC_WIDTH, ICON_REC_HEIGHT, ICON_REC_DATA};
+
+#endif // _REC_ICON_H_

+ 72 - 17
main/src/main.cpp

@@ -1,41 +1,96 @@
 #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;
+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_target
+  &state_unlock,
+  &state_target,
+  &state_start,
+  &state_run,
+  &state_stop,
+  &state_cancel
 }; 
 
-StateID curr_state_id = LOCK;
-StateID next_state_id = LOCK;
-
+StateTx curr_state(STATE_LOCK);
+StateTx next_state(STATE_LOCK);
+int64_t time0;
 
 void setup()
 {
-  M5.begin();
-  Serial.begin(115200);
-  states[curr_state_id]->setup();
-}
 
+  Dev.hardware_init();
+  Dev.M5Board.Display.setBrightness(16);
 
+  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()
 {
-  next_state_id = states[curr_state_id]->loop();
 
-  if (next_state_id != curr_state_id)
+
+  // 1. Spracovanie stavov (teraz už má čerstvé dáta z tlačidiel)
+  next_state = states[curr_state.id]->loop(Dev.M5Board.millis() - time0);
+
+  // pause transition if needed
+  if (next_state.wait >= 0)
   {
-    curr_state_id = next_state_id;
-    states[curr_state_id]->setup();
-  
+    Dev.enter_high_power(false);
+    Dev.Drawer.refresh();
+    Dev.enter_low_power();
+    
+    if (next_state.wait > 0)
+      Dev.M5Board.delay(next_state.wait);
+  }
+
+  // 2. Prechod do nového stavu, ak nastala zmena
+  if (next_state.id != curr_state.id)
+  {
+    time0 = millis();
+    curr_state = next_state;
+    Dev.enter_high_power(true);
+    Serial.println(getCpuFrequencyMhz());
+    states[curr_state.id]->setup();
+    Dev.Drawer.refresh();
+    Dev.enter_low_power();
+    Serial.println(getCpuFrequencyMhz());
+
   }
 
-  M5.update();
+  // 3. Obnovenie batérie, ak je vyžiadané
+  if (Dev.get_battery_refresh())
+  {
+    Dev.set_battery_refresh(false);
+    Dev.Drawer.draw_battery(Dev.M5Board.Power.getBatteryLevel());
+  }
 
- 
+    // 0. AKTUALIZÁCIA HARDVÉRU MUSÍ BYŤ PRVÁ (hlavne po prebudení zo spánku!)
+  Dev.M5Board.update();
 }

+ 82 - 0
main/src/nbus/nbus_comm.cpp

@@ -0,0 +1,82 @@
+#include "nbus_comm.h"
+
+uint8_t const NbusComm::_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,
+    0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6,
+    0xE3, 0xE4, 0xED, 0xEA, 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D,
+    0x9A, 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, 0x57, 0x50,
+    0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, 0x89, 0x8E, 0x87, 0x80, 0x95,
+    0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC,
+    0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F,
+    0x58, 0x4D, 0x4A, 0x43, 0x44, 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A,
+    0x33, 0x34, 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, 0x3E,
+    0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, 0xAE, 0xA9, 0xA0, 0xA7,
+    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 crc = CRC8_INIT_VALUE;
+    uint8_t const *data = (uint8_t *)mem;
+    if (data == NULL)
+        return 0xff;
+    crc &= 0xff;
+    while (len--)
+        crc = _crc8x_table[crc ^ *data++];
+    return crc;
+}
+
+NbusComm::NbusComm(Stream &slave_interface) :_slave_interface{slave_interface}
+{
+
+}
+
+
+void NbusComm::cmd_set_start()
+{
+   _init_packet(CMD_SET_START, nullptr, 0);
+   _slave_interface.write(_packet, _size);
+}
+
+void NbusComm::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)
+{
+    _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)
+{   
+    if (count > NBUS_COMM_PACKET_SIZE - NBUS_META)
+        return;
+
+    _size = 0;
+    _packet[_size++] = NBUS_PACKET_META + count;
+    _packet[_size++]  = 0;
+    _packet[_size++]  = 0;
+    _packet[_size++]  = fc;
+    _add_data(data, count);
+    _add_crc();
+}
+
+void NbusComm::_add_data(uint8_t *data, int16_t count)
+{
+    if (data == nullptr || count == 0)
+        return;
+
+    for (int16_t i = 0; i < count; i++)
+        _packet[_size++] = data[i];
+}
+
+void NbusComm::_add_crc()
+{
+    _packet[_size] = _crc8x_fast(_packet + 1, _size - 1);
+    _size++;
+}

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

@@ -0,0 +1,54 @@
+#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_

+ 152 - 51
main/src/states/state_impl/state_impl.cpp

@@ -1,92 +1,193 @@
 #include "state_impl.h"
 
-void draw_icon(const IconStruct &icon)
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+//  STATE LOCK
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+void StateLock::setup()
 {
-    // calculate position
-    int16_t max_dim = icon.width >= icon.height ? icon.width : icon.height;
-    int16_t sx = (M5.Display.width()) / 2;
-    int16_t sy = (M5.Display.height()) / 2 - 20;
-    
-    int16_t x0 = sx - max_dim / 2;
-    int16_t y0 = sy - max_dim / 2;
+    // draw screen
+    Dev.Drawer.draw_icon(LOCK_ICON, COL_ICON_FG, COL_ICON_BG_INACTIVE, LAY_ICON_YOFFSET);
+    Dev.Drawer.draw_description("Odomknite", "podrzanim", LAY_DESC_YOFFSET);
+    Dev.Drawer.draw_measure_number(GenericState::measure_num++);
+}
 
-    // draw background circle
-    M5.Display.fillCircle(sx, sy, max_dim / 2, TFT_RED);
+StateTx StateLock::loop(int64_t duration)
+{
+    // long press -> unlock
+    if (Dev.M5Board.BtnB.pressedFor(TIM_LONG_PRESS))
+        return {STATE_UNLOCK};
 
-    // draw bitmap on top
-    M5.Display.drawXBitmap(x0, y0, icon.data, icon.width, icon.height, TFT_WHITE);
+    // timer -> power off    
+    if (duration > TIM_POWER_OFF)
+        Dev.M5Board.Power.powerOff();
 
+    return {STATE_LOCK};
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+//  STATE UNLOCK
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+void StateUnlock::setup()
+{
+    // draw screen
+    Dev.Drawer.draw_icon(UNLOCK_ICON, COL_ICON_FG, COL_ICON_BG_ACTIVE, LAY_ICON_YOFFSET);
+    Dev.Drawer.draw_description("Odomknute", "", LAY_DESC_YOFFSET);
 }
 
-void draw_desc(const char* line1, const char* line2)
+StateTx StateUnlock::loop(int64_t duration)
 {
-M5.Display.setFont(&Font4);
+    // wait until button is released
+    if (Dev.M5Board.BtnA.isReleased())
+       return {STATE_TARGET, TIM_INFO_SCREEN};
+
+    return {STATE_UNLOCK};
+}
 
-int16_t max_width = max(M5.Display.textWidth(line1), M5.Display.textWidth(line2));
-int16_t sx = (M5.Display.width() - max_width) / 2;
-int16_t sy = (M5.Display.height() / 2) + 60;
 
-M5.Display.setTextDatum(top_left);
+//////////////////////////////////////////////////////////////////////////////////////////////////
+//  STATE TARGET
+//////////////////////////////////////////////////////////////////////////////////////////////////
 
-M5.Display.drawString(line1, sx, sy);
-M5.Display.drawString(line2, sx, sy + M5.Display.fontHeight());
+void StateTarget::setup()
+{
+    // draw screen
+    Dev.Drawer.draw_icon(TARGET_ICON, COL_ICON_FG, COL_ICON_BG_INACTIVE, LAY_ICON_YOFFSET);
+    Dev.Drawer.draw_description("Zamerajte", "poziciu", LAY_DESC_YOFFSET);
 }
 
-void draw_battery()
+StateTx StateTarget::loop(int64_t duration)
 {
-    M5.Display.setFont(&Font4);
+    // long press -> cancel
+    if (Dev.M5Board.BtnA.pressedFor(TIM_LONG_PRESS))
+        return {STATE_CANCEL};
 
     
-    int16_t sx = M5.Display.width() - 1;
-    int16_t sy = 0;
-char buf[16];
-snprintf(buf, sizeof(buf), "%ld%%", M5.Power.getBatteryLevel());
-M5.Display.setTextDatum(top_right);
-M5.Display.drawString(buf, M5.Display.width() - 1, 0);
-
+    // short press -> start
+    if (Dev.M5Board.BtnA.wasReleased())
+    {
+        Dev.Drawer.draw_icon(TARGET_ICON, COL_ICON_FG, COL_ICON_BG_ACTIVE, LAY_ICON_YOFFSET);
+        return {STATE_START, TIM_INFO_SCREEN};
+    }
 
+    return {STATE_TARGET};
 }
 
-void draw_measurenum(int8_t m)
-{
-    M5.Display.setFont(&Font4);
-    char buf[16];
-snprintf(buf, sizeof(buf), "M%d", m);
-M5.Display.setTextDatum(top_left);
-M5.Display.drawString(buf, 0, 0);
 
+//////////////////////////////////////////////////////////////////////////////////////////////////
+//  STATE START
+//////////////////////////////////////////////////////////////////////////////////////////////////
 
+void StateStart::setup()
+{
+    // draw screen
+    Dev.Drawer.draw_icon(GO_ICON, COL_ICON_FG, COL_ICON_BG_INACTIVE, LAY_ICON_YOFFSET);
+    Dev.Drawer.draw_description("Spustite", "meranie", LAY_DESC_YOFFSET);
 }
 
-void StateLock::setup()
+StateTx StateStart::loop(int64_t duration)
 {
-    draw_icon(LOCK_ICON);
-    draw_desc("Odomknite", "podrzanim");
-    draw_battery();
-    draw_measurenum(7);
+    // long press -> cancel
+    if (Dev.M5Board.BtnA.pressedFor(TIM_LONG_PRESS))
+        return {STATE_CANCEL};
+
+    // short press -> run
+    if (Dev.M5Board.BtnA.wasReleased())
+    {
+        Dev.Drawer.draw_icon(GO_ICON, COL_ICON_FG, COL_ICON_BG_ACTIVE, LAY_ICON_YOFFSET);
+        return {STATE_RUN, TIM_INFO_SCREEN};
+    }
+    
+    return {STATE_START};
 }
 
+//////////////////////////////////////////////////////////////////////////////////////////////////
+//  STATE RUN
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+void StateRun::setup()
+{
+    // 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);
+}
 
-StateID StateLock::loop()
+StateTx StateRun::loop(int64_t duration)
 {
-    if (M5.BtnB.pressedFor(2000))
+    // long press -> cancel
+    if (Dev.M5Board.BtnA.pressedFor(TIM_LONG_PRESS))
+        return {STATE_CANCEL};
+
+    // if need to refresh
+    if (duration - _last_blink_time >= TIM_RUN_BLINK)
     {
-        draw_icon(UNLOCK_ICON);
-        M5.delay(1000);
-        return TARGET;
+        _icon_state ^= 1;
+        _last_blink_time = duration;
+
+        if (_icon_state)
+        {
+            Dev.Drawer.draw_icon(REC_ICON, COL_ICON_FG, COL_ICON_BG_REC, LAY_ICON_YOFFSET);
+            return {STATE_RUN, TIM_RUN_REFRESH};
+        }
+        else
+        {
+            Dev.Drawer.draw_icon(REC_ICON, COL_ICON_FG, COL_ICON_BG_INACTIVE, LAY_ICON_YOFFSET);
+            return {STATE_RUN, TIM_RUN_REFRESH};
+        }
     }
 
-    return LOCK;
+    // short press -> stop
+    if (Dev.M5Board.BtnA.wasReleased())
+    {
+        _last_blink_time = 0;
+        _icon_state = false;
+        return {STATE_STOP};
+    }
+
+    return {STATE_RUN};
 }
 
-void StateTarget::setup()
+//////////////////////////////////////////////////////////////////////////////////////////////////
+//  STATE STOP
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+void StateStop::setup()
 {
-    draw_icon(TARGET_ICON);
+    // draw screen
+    Dev.Drawer.draw_icon(OK_ICON, COL_ICON_FG, COL_ICON_BG_INACTIVE, LAY_ICON_YOFFSET);
+    Dev.Drawer.draw_description("Meranie", "dokoncene", LAY_DESC_YOFFSET);
 }
 
+StateTx StateStop::loop(int64_t duration)
+{
+    if (duration > TIM_INFO_SCREEN)
+    {
+        Dev.Drawer.draw_icon(OK_ICON, COL_ICON_FG, COL_ICON_BG_ACTIVE, LAY_ICON_YOFFSET);
+        return {STATE_LOCK, TIM_INFO_SCREEN};
+    }
+
+    return {STATE_STOP};
+}
 
-StateID StateTarget::loop()
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+//  STATE CANCEL
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+void StateCancel::setup()
 {
+    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);
+}
 
-    return TARGET;
+StateTx StateCancel::loop(int64_t duration)
+{
+    if (duration > TIM_INFO_SCREEN && Dev.M5Board.BtnA.isReleased())
+        return {STATE_LOCK};
+    
+    return {STATE_CANCEL};
 }

+ 57 - 7
main/src/states/state_impl/state_impl.h

@@ -1,26 +1,76 @@
 #ifndef _STATE_IMPL_H_
 #define _STATE_IMPL_H_
 
-#include "config/states_config.h"
+#include "device/device.h"
 #include "states/state_interface.h"
 
-#include "icons/all_icons.h"
+#include <atomic>
 
-#include "M5Unified.h"
 
-class StateLock : public IState
+class GenericState: public IState
+{
+public:
+    inline static std::atomic<int32_t> measure_num{1};
+};
+
+
+class StateLock : public GenericState
+{
+public:
+    void setup() override;
+    StateTx loop(int64_t duration) override;
+};
+
+
+class StateUnlock : public GenericState
+{
+public:
+    void setup() override;
+    StateTx loop(int64_t duration) override;
+};
+
+
+class StateTarget : public GenericState
+{
+public:
+    void setup() override;
+    StateTx loop(int64_t duration) override;
+};
+
+
+class StateStart : public GenericState
+{
+public:
+    void setup() override;
+    StateTx loop(int64_t duration) override;
+};
+
+
+class StateRun : public GenericState
 {
 public:
     void setup() override;
-    StateID loop() override;
+    StateTx loop(int64_t duration) override;
+private:
+    int64_t _last_blink_time = 0;
+    bool _icon_state = false;
 };
 
-class StateTarget : public IState
+class StateStop : public GenericState
 {
 public:
     void setup() override;
-    StateID loop() override;
+    StateTx loop(int64_t duration) override;
 };
 
 
+class StateCancel : public GenericState
+{
+public:
+    void setup() override;
+    StateTx loop(int64_t duration) override;
+};
+
+
+
 #endif // _STATE_IMPL_H_

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

@@ -1,14 +1,14 @@
 #ifndef _STATE_INTERFACE_H_
 #define _STATE_INTERFACE_H_
 
-enum StateID;
+#include "data_structures/state_tx.h"
 
 
 class IState
 {
 public:
     virtual void setup() = 0;
-    virtual StateID loop() = 0; 
+    virtual StateTx loop(int64_t duration) = 0; 
 };