Browse Source

Первый

foxtime 2 months ago
commit
9156c51c59
100 changed files with 7623 additions and 0 deletions
  1. BIN
      .pio/build/pico/.sconsign311.dblite
  2. BIN
      .pio/build/pico/FrameworkArduino/Interrupts.cpp.o
  3. BIN
      .pio/build/pico/FrameworkArduino/Serial.cpp.o
  4. BIN
      .pio/build/pico/FrameworkArduino/Tone.cpp.o
  5. BIN
      .pio/build/pico/FrameworkArduino/USB/PluggableUSBDevice.cpp.o
  6. BIN
      .pio/build/pico/FrameworkArduino/USB/USBCDC.cpp.o
  7. BIN
      .pio/build/pico/FrameworkArduino/USB/USBSerial.cpp.o
  8. BIN
      .pio/build/pico/FrameworkArduino/WMath.cpp.o
  9. BIN
      .pio/build/pico/FrameworkArduino/abi.cpp.o
  10. BIN
      .pio/build/pico/FrameworkArduino/api/CanMsg.cpp.o
  11. BIN
      .pio/build/pico/FrameworkArduino/api/CanMsgRingbuffer.cpp.o
  12. BIN
      .pio/build/pico/FrameworkArduino/api/Common.cpp.o
  13. BIN
      .pio/build/pico/FrameworkArduino/api/IPAddress.cpp.o
  14. BIN
      .pio/build/pico/FrameworkArduino/api/PluggableUSB.cpp.o
  15. BIN
      .pio/build/pico/FrameworkArduino/api/Print.cpp.o
  16. BIN
      .pio/build/pico/FrameworkArduino/api/Stream.cpp.o
  17. BIN
      .pio/build/pico/FrameworkArduino/api/String.cpp.o
  18. BIN
      .pio/build/pico/FrameworkArduino/arm_hal_random.c.o
  19. BIN
      .pio/build/pico/FrameworkArduino/as_mbed_library/variant.cpp.o
  20. BIN
      .pio/build/pico/FrameworkArduino/itoa.c.o
  21. BIN
      .pio/build/pico/FrameworkArduino/main.cpp.o
  22. BIN
      .pio/build/pico/FrameworkArduino/mbed/platform/cxxsupport/mstd_mutex.cpp.o
  23. BIN
      .pio/build/pico/FrameworkArduino/pinToIndex.cpp.o
  24. BIN
      .pio/build/pico/FrameworkArduino/random_seed.cpp.o
  25. BIN
      .pio/build/pico/FrameworkArduino/timer.cpp.o
  26. BIN
      .pio/build/pico/FrameworkArduino/wiring.cpp.o
  27. BIN
      .pio/build/pico/FrameworkArduino/wiring_analog.cpp.o
  28. BIN
      .pio/build/pico/FrameworkArduino/wiring_digital.cpp.o
  29. BIN
      .pio/build/pico/FrameworkArduino/wiring_pulse.cpp.o
  30. BIN
      .pio/build/pico/FrameworkArduino/wiring_shift.cpp.o
  31. BIN
      .pio/build/pico/FrameworkArduinoVariant/double_tap_usb_boot.cpp.o
  32. BIN
      .pio/build/pico/FrameworkArduinoVariant/variant.cpp.o
  33. 160 0
      .pio/build/pico/cpp.linker_script.ld
  34. BIN
      .pio/build/pico/firmware.elf
  35. BIN
      .pio/build/pico/firmware.elf.uf2
  36. BIN
      .pio/build/pico/firmware.uf2
  37. 0 0
      .pio/build/pico/idedata.json
  38. BIN
      .pio/build/pico/lib245/GyverIO/utils/shift.cpp.o
  39. BIN
      .pio/build/pico/lib245/libGyverIO.a
  40. BIN
      .pio/build/pico/lib4eb/EncButton/core/io.cpp.o
  41. BIN
      .pio/build/pico/lib4eb/EncButton/core/self.cpp.o
  42. BIN
      .pio/build/pico/lib4eb/libEncButton.a
  43. BIN
      .pio/build/pico/lib6a1/MyLD2410/MyLD2410.cpp.o
  44. BIN
      .pio/build/pico/lib6a1/libMyLD2410.a
  45. BIN
      .pio/build/pico/libFrameworkArduino.a
  46. BIN
      .pio/build/pico/libFrameworkArduinoVariant.a
  47. BIN
      .pio/build/pico/src/main.cpp.o
  48. 1 0
      .pio/build/project.checksum
  49. 2 0
      .pio/libdeps/pico/EncButton/.gitattributes
  50. 20 0
      .pio/libdeps/pico/EncButton/.github/workflows/tg-send.yml
  51. 1 0
      .pio/libdeps/pico/EncButton/.piopm
  52. 21 0
      .pio/libdeps/pico/EncButton/LICENSE
  53. 1867 0
      .pio/libdeps/pico/EncButton/README.md
  54. 1776 0
      .pio/libdeps/pico/EncButton/README_EN.md
  55. 73 0
      .pio/libdeps/pico/EncButton/examples/callback/callback.ino
  56. 75 0
      .pio/libdeps/pico/EncButton/examples/callback2/callback2.ino
  57. 114 0
      .pio/libdeps/pico/EncButton/examples/demo/demo.ino
  58. 27 0
      .pio/libdeps/pico/EncButton/examples/double/double.ino
  59. 78 0
      .pio/libdeps/pico/EncButton/examples/doubleCallback/doubleCallback.ino
  60. 39 0
      .pio/libdeps/pico/EncButton/examples/isr/isr.ino
  61. 48 0
      .pio/libdeps/pico/EncButton/examples/one_button_3_var/one_button_3_var.ino
  62. 47 0
      .pio/libdeps/pico/EncButton/examples/one_enc_3_var/one_enc_3_var.ino
  63. 34 0
      .pio/libdeps/pico/EncButton/examples/virtual_buttons/virtual_AnalogKey/virtual_AnalogKey.ino
  64. 49 0
      .pio/libdeps/pico/EncButton/examples/virtual_buttons/virtual_SimpleKeypad/virtual_SimpleKeypad.ino
  65. 47 0
      .pio/libdeps/pico/EncButton/examples/virtual_buttons/virtual_SimpleKeypad_array/virtual_SimpleKeypad_array.ino
  66. 127 0
      .pio/libdeps/pico/EncButton/keywords.txt
  67. 10 0
      .pio/libdeps/pico/EncButton/library.properties
  68. 10 0
      .pio/libdeps/pico/EncButton/src/EncButton.h
  69. 70 0
      .pio/libdeps/pico/EncButton/src/core/Button.h
  70. 110 0
      .pio/libdeps/pico/EncButton/src/core/EncButton.h
  71. 89 0
      .pio/libdeps/pico/EncButton/src/core/Encoder.h
  72. 29 0
      .pio/libdeps/pico/EncButton/src/core/MultiButton.h
  73. 577 0
      .pio/libdeps/pico/EncButton/src/core/VirtButton.h
  74. 187 0
      .pio/libdeps/pico/EncButton/src/core/VirtEncButton.h
  75. 181 0
      .pio/libdeps/pico/EncButton/src/core/VirtEncoder.h
  76. 31 0
      .pio/libdeps/pico/EncButton/src/core/flags.h
  77. 13 0
      .pio/libdeps/pico/EncButton/src/core/io.cpp
  78. 7 0
      .pio/libdeps/pico/EncButton/src/core/io.h
  79. 3 0
      .pio/libdeps/pico/EncButton/src/core/self.cpp
  80. 3 0
      .pio/libdeps/pico/EncButton/src/core/self.h
  81. 2 0
      .pio/libdeps/pico/GyverIO/.gitattributes
  82. 20 0
      .pio/libdeps/pico/GyverIO/.github/workflows/tg-send.yml
  83. 1 0
      .pio/libdeps/pico/GyverIO/.piopm
  84. 21 0
      .pio/libdeps/pico/GyverIO/LICENSE
  85. 205 0
      .pio/libdeps/pico/GyverIO/README.md
  86. 187 0
      .pio/libdeps/pico/GyverIO/README_EN.md
  87. 219 0
      .pio/libdeps/pico/GyverIO/examples/test/test.ino
  88. 48 0
      .pio/libdeps/pico/GyverIO/keywords.txt
  89. 9 0
      .pio/libdeps/pico/GyverIO/library.properties
  90. 6 0
      .pio/libdeps/pico/GyverIO/src/GyverIO.h
  91. 82 0
      .pio/libdeps/pico/GyverIO/src/GyverIO_SPI.h
  92. 29 0
      .pio/libdeps/pico/GyverIO/src/gio/gio.h
  93. 43 0
      .pio/libdeps/pico/GyverIO/src/gio/gio_arduino.h
  94. 356 0
      .pio/libdeps/pico/GyverIO/src/gio/gio_avr.h
  95. 7 0
      .pio/libdeps/pico/GyverIO/src/gio/gio_defs.h
  96. 112 0
      .pio/libdeps/pico/GyverIO/src/gio/gio_esp32.h
  97. 89 0
      .pio/libdeps/pico/GyverIO/src/gio/gio_esp8266.h
  98. 79 0
      .pio/libdeps/pico/GyverIO/src/utils/PinIO.h
  99. 36 0
      .pio/libdeps/pico/GyverIO/src/utils/PinT.h
  100. 146 0
      .pio/libdeps/pico/GyverIO/src/utils/shift.cpp

BIN
.pio/build/pico/.sconsign311.dblite


BIN
.pio/build/pico/FrameworkArduino/Interrupts.cpp.o


BIN
.pio/build/pico/FrameworkArduino/Serial.cpp.o


BIN
.pio/build/pico/FrameworkArduino/Tone.cpp.o


BIN
.pio/build/pico/FrameworkArduino/USB/PluggableUSBDevice.cpp.o


BIN
.pio/build/pico/FrameworkArduino/USB/USBCDC.cpp.o


BIN
.pio/build/pico/FrameworkArduino/USB/USBSerial.cpp.o


BIN
.pio/build/pico/FrameworkArduino/WMath.cpp.o


BIN
.pio/build/pico/FrameworkArduino/abi.cpp.o


BIN
.pio/build/pico/FrameworkArduino/api/CanMsg.cpp.o


BIN
.pio/build/pico/FrameworkArduino/api/CanMsgRingbuffer.cpp.o


BIN
.pio/build/pico/FrameworkArduino/api/Common.cpp.o


BIN
.pio/build/pico/FrameworkArduino/api/IPAddress.cpp.o


BIN
.pio/build/pico/FrameworkArduino/api/PluggableUSB.cpp.o


BIN
.pio/build/pico/FrameworkArduino/api/Print.cpp.o


BIN
.pio/build/pico/FrameworkArduino/api/Stream.cpp.o


BIN
.pio/build/pico/FrameworkArduino/api/String.cpp.o


BIN
.pio/build/pico/FrameworkArduino/arm_hal_random.c.o


BIN
.pio/build/pico/FrameworkArduino/as_mbed_library/variant.cpp.o


BIN
.pio/build/pico/FrameworkArduino/itoa.c.o


BIN
.pio/build/pico/FrameworkArduino/main.cpp.o


BIN
.pio/build/pico/FrameworkArduino/mbed/platform/cxxsupport/mstd_mutex.cpp.o


BIN
.pio/build/pico/FrameworkArduino/pinToIndex.cpp.o


BIN
.pio/build/pico/FrameworkArduino/random_seed.cpp.o


BIN
.pio/build/pico/FrameworkArduino/timer.cpp.o


BIN
.pio/build/pico/FrameworkArduino/wiring.cpp.o


BIN
.pio/build/pico/FrameworkArduino/wiring_analog.cpp.o


BIN
.pio/build/pico/FrameworkArduino/wiring_digital.cpp.o


BIN
.pio/build/pico/FrameworkArduino/wiring_pulse.cpp.o


BIN
.pio/build/pico/FrameworkArduino/wiring_shift.cpp.o


BIN
.pio/build/pico/FrameworkArduinoVariant/double_tap_usb_boot.cpp.o


BIN
.pio/build/pico/FrameworkArduinoVariant/variant.cpp.o


+ 160 - 0
.pio/build/pico/cpp.linker_script.ld

@@ -0,0 +1,160 @@
+MEMORY
+{
+    FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 2048k
+    RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k
+    SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k
+    SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k
+}
+ENTRY(_entry_point)
+SECTIONS
+{
+    .flash_begin : {
+        __flash_binary_start = .;
+    } > FLASH
+    .second_stage_ota : {
+         KEEP (*(.second_stage_ota))
+     } > FLASH
+    .boot2 : {
+        __boot2_start__ = .;
+        KEEP (*(.boot2))
+        __boot2_end__ = .;
+    } > FLASH
+    ASSERT(__boot2_end__ - __boot2_start__ == 256,
+        "ERROR: Pico second stage bootloader must be 256 bytes in size")
+    .text : {
+        __logical_binary_start = .;
+        KEEP (*(.vectors))
+        KEEP (*(.binary_info_header))
+        __binary_info_header_end = .;
+        KEEP (*(.reset))
+        KEEP (*(.init))
+        *(.fini)
+        *crtbegin.o(.ctors)
+        *crtbegin?.o(.ctors)
+        *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
+        *(SORT(.ctors.*))
+        *(.ctors)
+        *crtbegin.o(.dtors)
+        *crtbegin?.o(.dtors)
+        *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
+        *(SORT(.dtors.*))
+        *(.dtors)
+        *(.eh_frame*)
+        . = ALIGN(4);
+    } > FLASH
+    .rodata : {
+        . = ALIGN(4);
+        *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*)))
+        . = ALIGN(4);
+    } > FLASH
+    .ARM.extab :
+    {
+        *(.ARM.extab* .gnu.linkonce.armextab.*)
+    } > FLASH
+    __exidx_start = .;
+    .ARM.exidx :
+    {
+        *(.ARM.exidx* .gnu.linkonce.armexidx.*)
+    } > FLASH
+    __exidx_end = .;
+    . = ALIGN(4);
+    __binary_info_start = .;
+    .binary_info :
+    {
+        KEEP(*(.binary_info.keep.*))
+        *(.binary_info.*)
+    } > FLASH
+    __binary_info_end = .;
+    . = ALIGN(4);
+    __etext = .;
+   .ram_vector_table (COPY): {
+        *(.ram_vector_table)
+    } > RAM
+    .data : {
+        __data_start__ = .;
+        *(vtable)
+        *(.time_critical*)
+        . = ALIGN(4);
+        *(.data*)
+        . = ALIGN(4);
+        *(.after_data.*)
+        . = ALIGN(4);
+        PROVIDE_HIDDEN (__mutex_array_start = .);
+        KEEP(*(SORT(.mutex_array.*)))
+        KEEP(*(.mutex_array))
+        PROVIDE_HIDDEN (__mutex_array_end = .);
+        . = ALIGN(4);
+        PROVIDE_HIDDEN (__preinit_array_start = .);
+        KEEP(*(SORT(.preinit_array.*)))
+        KEEP(*(.preinit_array))
+        PROVIDE_HIDDEN (__preinit_array_end = .);
+        . = ALIGN(4);
+        PROVIDE_HIDDEN (__init_array_start = .);
+        KEEP(*(SORT(.init_array.*)))
+        KEEP(*(.init_array))
+        PROVIDE_HIDDEN (__init_array_end = .);
+        . = ALIGN(4);
+        PROVIDE_HIDDEN (__fini_array_start = .);
+        *(SORT(.fini_array.*))
+        *(.fini_array)
+        PROVIDE_HIDDEN (__fini_array_end = .);
+        *(.jcr)
+        . = ALIGN(4);
+        __data_end__ = .;
+    } > RAM AT> FLASH
+    .uninitialized_data (COPY): {
+        . = ALIGN(4);
+        __uninitialized_data_start__ = .;
+        *(.uninitialized_data*)
+        __uninitialized_data_end__ = .;
+    } > RAM
+    .scratch_x : {
+        __scratch_x_start__ = .;
+        *(.scratch_x.*)
+        . = ALIGN(4);
+        __scratch_x_end__ = .;
+    } > SCRATCH_X AT > FLASH
+    __scratch_x_source__ = LOADADDR(.scratch_x);
+    .scratch_y : {
+        __scratch_y_start__ = .;
+        *(.scratch_y.*)
+        . = ALIGN(4);
+        __scratch_y_end__ = .;
+    } > SCRATCH_Y AT > FLASH
+    __scratch_y_source__ = LOADADDR(.scratch_y);
+    .bss : {
+        . = ALIGN(4);
+        __bss_start__ = .;
+        *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*)))
+        *(COMMON)
+        . = ALIGN(4);
+        __bss_end__ = .;
+    } > RAM
+    .heap (COPY):
+    {
+        __end__ = .;
+        PROVIDE(end = .);
+        *(.heap*)
+        . = ORIGIN(RAM) + LENGTH(RAM) - 0x400;
+        __HeapLimit = .;
+    } > RAM
+    .stack1_dummy (COPY):
+    {
+        *(.stack1*)
+    } > SCRATCH_X
+    .stack_dummy (COPY):
+    {
+        *(.stack*)
+    } > RAM
+    .flash_end : {
+        __flash_binary_end = .;
+    } > FLASH
+    __StackTop = ORIGIN(RAM) + LENGTH(RAM);
+    __StackLimit = __StackTop - 0x400;
+    __StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X);
+    __StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy);
+    __StackBottom = __StackTop - SIZEOF(.stack_dummy);
+    PROVIDE(__stack = __StackTop);
+    ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed")
+    ASSERT( __binary_info_header_end - __logical_binary_start <= 256, "Binary info must be in first 256 bytes of the binary")
+}

BIN
.pio/build/pico/firmware.elf


BIN
.pio/build/pico/firmware.elf.uf2


BIN
.pio/build/pico/firmware.uf2


File diff suppressed because it is too large
+ 0 - 0
.pio/build/pico/idedata.json


BIN
.pio/build/pico/lib245/GyverIO/utils/shift.cpp.o


BIN
.pio/build/pico/lib245/libGyverIO.a


BIN
.pio/build/pico/lib4eb/EncButton/core/io.cpp.o


BIN
.pio/build/pico/lib4eb/EncButton/core/self.cpp.o


BIN
.pio/build/pico/lib4eb/libEncButton.a


BIN
.pio/build/pico/lib6a1/MyLD2410/MyLD2410.cpp.o


BIN
.pio/build/pico/lib6a1/libMyLD2410.a


BIN
.pio/build/pico/libFrameworkArduino.a


BIN
.pio/build/pico/libFrameworkArduinoVariant.a


BIN
.pio/build/pico/src/main.cpp.o


+ 1 - 0
.pio/build/project.checksum

@@ -0,0 +1 @@
+be5d6171dbde8cf5ff2f6ca5ed4e400a5f9b98b0

+ 2 - 0
.pio/libdeps/pico/EncButton/.gitattributes

@@ -0,0 +1,2 @@
+# Auto detect text files and perform LF normalization
+* text=auto

+ 20 - 0
.pio/libdeps/pico/EncButton/.github/workflows/tg-send.yml

@@ -0,0 +1,20 @@
+
+name: Telegram Message
+on:
+  release:
+    types: [published]
+jobs:
+  build:
+    name: Send Message
+    runs-on: ubuntu-latest
+    steps:
+      - name: send telegram message on push
+        uses: appleboy/telegram-action@master
+        with:
+          to: ${{ secrets.TELEGRAM_TO }}
+          token: ${{ secrets.TELEGRAM_TOKEN }}
+          disable_web_page_preview: true
+          message: |
+            ${{ github.event.repository.name }} v${{ github.event.release.tag_name }}
+            ${{ github.event.release.body }}
+            https://github.com/${{ github.repository }}

+ 1 - 0
.pio/libdeps/pico/EncButton/.piopm

@@ -0,0 +1 @@
+{"type": "library", "name": "EncButton", "version": "3.7.2", "spec": {"owner": "gyverlibs", "id": 12289, "name": "EncButton", "requirements": null, "uri": null}}

+ 21 - 0
.pio/libdeps/pico/EncButton/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 AlexGyver
+
+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.

+ 1867 - 0
.pio/libdeps/pico/EncButton/README.md

@@ -0,0 +1,1867 @@
+[![latest](https://img.shields.io/github/v/release/GyverLibs/EncButton.svg?color=brightgreen)](https://github.com/GyverLibs/EncButton/releases/latest/download/EncButton.zip)
+[![PIO](https://badges.registry.platformio.org/packages/gyverlibs/library/EncButton.svg)](https://registry.platformio.org/libraries/gyverlibs/EncButton)
+[![Foo](https://img.shields.io/badge/Website-AlexGyver.ru-blue.svg?style=flat-square)](https://alexgyver.ru/)
+[![Foo](https://img.shields.io/badge/%E2%82%BD%24%E2%82%AC%20%D0%9F%D0%BE%D0%B4%D0%B4%D0%B5%D1%80%D0%B6%D0%B0%D1%82%D1%8C-%D0%B0%D0%B2%D1%82%D0%BE%D1%80%D0%B0-orange.svg?style=flat-square)](https://alexgyver.ru/support_alex/)
+[![Foo](https://img.shields.io/badge/README-ENGLISH-blueviolet.svg?style=flat-square)](https://github-com.translate.goog/GyverLibs/EncButton?_x_tr_sl=ru&_x_tr_tl=en)  
+
+[![Foo](https://img.shields.io/badge/ПОДПИСАТЬСЯ-НА%20ОБНОВЛЕНИЯ-brightgreen.svg?style=social&logo=telegram&color=blue)](https://t.me/GyverLibs)
+
+# EncButton
+
+| ⚠️⚠️⚠️<br>**Новая версия v3 несовместима с предыдущими, смотри [документацию](#docs), [примеры](#example) и краткий [гайд по миграции](#migrate) с v2 на v3!**<br>⚠️⚠️⚠️ |
+| ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+
+Лёгкая и очень функциональная библиотека для энкодера с кнопкой, энкодера или кнопки с Arduino
+- Кнопка
+    - Обработка событий: нажатие, отпускание, клик, счётчик кликов, удержание, импульсное удержание, время удержания + предварительные клики для всех режимов
+    - Программное подавление дребезга
+    - Поддержка обработки двух одновременно нажимаемых кнопок как третьей кнопки
+- Энкодер
+    - Обработка событий: обычный поворот, нажатый поворот, быстрый поворот
+    - Поддержка четырёх типов инкрементальных энкодеров
+    - Высокоточный алгоритм определения позиции
+    - Буферизация в прерывании
+- Простое и понятное использование
+- Огромное количество возможностей и их комбинаций для разных сценариев использования даже одной кнопки
+- Виртуальный режим (например для работы с расширителем пинов)
+- Оптимизирована для работы в прерывании
+- Максимально быстрое чтение пинов для AVR, esp8266, esp32 (используется GyverIO)
+- Быстрые асинхронные алгоритмы опроса действий с кнопки и энкодера
+- Жёсткая оптимизация и небольшой вес во Flash и SRAM памяти: 5 байт SRAM (на экземпляр) и ~350 байт Flash на обработку кнопки
+
+Примеры сценариев использования:
+- Несколько кликов - включение режима (по кол-ву кликов)
+- Несколько кликов + короткое удержание - ещё вариант включения режима (по кол-ву кликов)
+- Несколько кликов + удержание - постепенное изменение значения выбранной переменной (по кол-ву кликов)
+- Несколько кликов выбирают переменную, энкодер её изменяет
+- Изменение шага изменения переменной при вращении энкодера - например уменьшение при зажатой кнопке и увеличение при быстром вращении
+- Навигация по меню при вращении энкодера, изменение переменной при вращении зажатого энкодера
+- Полноценная навигация по меню при использовании двух кнопок (одновременное удержание для перехода на следующий уровень, одновременное нажатие для возврата на предыдущий)
+- И так далее
+
+### Совместимость
+Совместима со всеми Arduino платформами (используются Arduino-функции)
+
+## Содержание
+- [Установка](#install)
+- [Информация](#info)
+- [Документация](#docs)
+  - [Настройки компиляции](#config)
+  - [Полное описание классов](#class)
+  - [Обработка и опрос](#tick)
+  - [Предварительные клики](#preclicks)
+  - [Прямое чтение кнопки](#btnread)
+  - [Погружение в цикл](#loop)
+  - [Timeout](#timeout)
+  - [Busy](#busy)
+  - [Получение события](#actions)
+  - [Оптимизация](#optimise)
+  - [Коллбэки](#callback)
+  - [Одновременное нажатие](#double)
+  - [Прерывания](#isr)
+  - [Массив кнопок/энкодеров](#array)
+  - [Кастомные функции](#custom)
+  - [Опрос по таймеру](#timer)
+  - [Мини примеры, сценарии](#examples-mini)
+- [Миграция с v2](#migrate)
+- [Примеры](#example)
+- [Версии](#versions)
+- [Баги и обратная связь](#feedback)
+
+<a id="install"></a>
+## Установка
+- Для работы требуется библиотека [GyverIO](https://github.com/GyverLibs/GyverIO)
+- Библиотеку можно найти по названию **EncButton** и установить через менеджер библиотек в:
+    - Arduino IDE
+    - Arduino IDE v2
+    - PlatformIO
+- [Скачать библиотеку](https://github.com/GyverLibs/EncButton/archive/refs/heads/main.zip) .zip архивом для ручной установки:
+    - Распаковать и положить в *C:\Program Files (x86)\Arduino\libraries* (Windows x64)
+    - Распаковать и положить в *C:\Program Files\Arduino\libraries* (Windows x32)
+    - Распаковать и положить в *Документы/Arduino/libraries/*
+    - (Arduino IDE) автоматическая установка из .zip: *Скетч/Подключить библиотеку/Добавить .ZIP библиотеку…* и указать скачанный архив
+- Читай более подробную инструкцию по установке библиотек [здесь](https://alexgyver.ru/arduino-first/#%D0%A3%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BA%D0%B0_%D0%B1%D0%B8%D0%B1%D0%BB%D0%B8%D0%BE%D1%82%D0%B5%D0%BA)
+### Обновление
+- Рекомендую всегда обновлять библиотеку: в новых версиях исправляются ошибки и баги, а также проводится оптимизация и добавляются новые фичи
+- Через менеджер библиотек IDE: найти библиотеку как при установке и нажать "Обновить"
+- Вручную: **удалить папку со старой версией**, а затем положить на её место новую. "Замену" делать нельзя: иногда в новых версиях удаляются файлы, которые останутся при замене и могут привести к ошибкам!
+
+<a id="info"></a>
+
+## Информация
+### Энкодер
+#### Тип энкодера
+Библиотека поддерживает все 4 типа *инкрементальных* энкодеров, тип можно настроить при помощи `setEncType(тип)`:
+- `EB_STEP4_LOW` - активный низкий сигнал (подтяжка к VCC). Полный период (4 фазы) за один щелчок. *Установлен по умолчанию*
+- `EB_STEP4_HIGH` - активный высокий сигнал (подтяжка к GND). Полный период (4 фазы) за один щелчок
+- `EB_STEP2` - половина периода (2 фазы) за один щелчок
+- `EB_STEP1` - четверть периода (1 фаза) за один щелчок, а также энкодеры без фиксации  
+
+![diagram](/doc/enc_type.png)  
+
+#### Рекомендации
+Для работы по сценарию "энкодер с кнопкой" рекомендую вот такие ([ссылка](https://ali.ski/cmPI2), [ссылка](https://ali.ski/sZbTK)) круглые китайские модули с распаянными цепями антидребезга (имеют тип `EB_STEP4_LOW` по классификации выше):  
+![scheme](/doc/encAli.png)  
+
+Самостоятельно обвязать энкодер можно по следующей схеме (RC фильтры на каналы энкодера + подтяжка всех пинов к VCC):  
+![scheme](/doc/enc_scheme.png)  
+
+> Примечание: по умолчанию в библиотеке пины энкодера настроены на `INPUT` с расчётом на внешнюю подтяжку. Если у вас энкодер без подтяжки - можно использовать внутреннюю `INPUT_PULLUP`, указав это при инициализации энкодера (см. документацию ниже).
+
+### Кнопка
+#### Уровень кнопки
+Кнопка может быть подключена к микроконтроллеру двумя способами и давать при нажатии высокий или низкий сигнал. В библиотеке предусмотрена настройка `setBtnLevel(уровень)`, где уровень - активный сигнал кнопки:
+- `HIGH` - кнопка подключает VCC. Установлен по умолчанию в `Virt`-библиотеках
+- `LOW` - кнопка подключает GND. Установлен по умолчанию в основных библиотеках  
+
+![scheme](/doc/btn_scheme.png)  
+
+#### Подтяжка пина
+В схемах с микроконтроллерами чаще всего используется подключение кнопки к GND с подтяжкой пина к VCC. Подтяжка может быть внешней (режим пина нужно поставить `INPUT`) или внутренней (режим пина `INPUT_PULLUP`). В "реальных" проектах рекомендуется внешняя подтяжка, т.к. она менее подвержена помехам - у внутренней слишком высокое сопротивление.
+
+<a id="docs"></a>
+
+## Документация
+
+<a id="config"></a>
+
+### Дефайны настроек
+Объявлять до подключения библиотеки
+
+```cpp
+
+// отключить поддержку pressFor/holdFor/stepFor и счётчик степов (экономит 2 байта оперативки)
+#define EB_NO_FOR
+
+// отключить обработчик событий attach (экономит 2 байта оперативки)
+#define EB_NO_CALLBACK
+
+// отключить счётчик энкодера [VirtEncoder, Encoder, EncButton] (экономит 4 байта оперативки)
+#define EB_NO_COUNTER
+
+// отключить буферизацию энкодера (экономит 2 байта оперативки)
+#define EB_NO_BUFFER
+
+/*
+  Настройка таймаутов для всех классов
+  - Заменяет таймауты константами, изменить их из программы (SetXxxTimeout()) будет нельзя
+  - Настройка влияет на все объявленные в программе кнопки/энкодеры
+  - Экономит 1 байт оперативки на объект за каждый таймаут
+  - Показаны значения по умолчанию в мс
+  - Значения не ограничены 4000мс, как при установке из программы (SetXxxTimeout())
+*/
+#define EB_DEB_TIME 50      // таймаут гашения дребезга кнопки (кнопка)
+#define EB_CLICK_TIME 500   // таймаут ожидания кликов (кнопка)
+#define EB_HOLD_TIME 600    // таймаут удержания (кнопка)
+#define EB_STEP_TIME 200    // таймаут импульсного удержания (кнопка)
+#define EB_FAST_TIME 30     // таймаут быстрого поворота (энкодер)
+#define EB_TOUT_TIME 1000   // таймаут действия (кнопка и энкодер)
+```
+
+<a id="class"></a>
+
+### Классы
+Как работать с документацией: EncButton начиная с версии 3.0 представляет собой несколько библиотек (классов) для различных сценариев использования, они друг друга наследуют для расширения функциональности. Таким образом библиотека представляет собой "луковицу", каждый слой которой имеет доступ к функциям нижних слоёв:
+- Базовые классы:
+  - `VirtButton` - базовый класс виртуальной кнопки, обеспечивает все возможности кнопки
+  - `VirtEncoder` - базовый класс виртуального энкодера, определяет факт и направление вращения энкодера
+  - `VirtEncButton` - базовый класс виртуального энкодера с кнопкой, обеспечивает опрос энкодера с учётом кнопки, *наследует VirtButton и VirtEncoder*
+- Основные классы:
+  - `Button`, `ButtonT` - класс кнопки, *наследует VirtButton*
+  - `Encoder`, `EncoderT` - класс энкодера, *наследует VirtEncoder*
+  - `EncButton`, `EncButtonT` - класс энкодера с кнопкой, *наследует VirtEncButton, VirtButton, VirtEncoder*
+
+Таким образом для изучения всех доступных функций конкретной библиотеки нужно смотреть не только её, но и то что она наследует. Например для обработки кнопки при помощи `Button` нужно открыть ниже описание `Button` и `VirtButton`.
+
+> *Виртуальный* - без указания пина микроконтроллера, работает напрямую с переданным значением, например для опроса кнопок-энкодеров через расширители пинов и сдвиговые регистры.
+
+> `T`-версии библиотек требуют указания пинов константами (цифрами). Номера пинов будут храниться в памяти программы, это ускоряет работу и делает код легче на 1 байт за каждый пин.
+
+> Примечание: `#include <EncButton.h>` подключает все инструменты библиотеки!
+
+<details>
+<summary>Таблица функций кнопки</summary>
+
+|                   | VirtButton | VirtEncButton | Button | EncButton |
+| ----------------- | :--------: | :-----------: | :----: | :-------: |
+| read              |            |               |   ✔    |           |
+| readBtn           |            |               |        |     ✔     |
+| tickRaw           |     ✔      |       ✔       |   ✔    |     ✔     |
+| setHoldTimeout    |     ✔      |       ✔       |   ✔    |     ✔     |
+| setStepTimeout    |     ✔      |       ✔       |   ✔    |     ✔     |
+| setClickTimeout   |     ✔      |       ✔       |   ✔    |     ✔     |
+| setDebTimeout     |     ✔      |       ✔       |   ✔    |     ✔     |
+| setTimeout        |     ✔      |       ✔       |   ✔    |     ✔     |
+| setBtnLevel       |     ✔      |       ✔       |   ✔    |     ✔     |
+| pressISR          |     ✔      |       ✔       |   ✔    |     ✔     |
+| reset             |     ✔      |       ✔       |   ✔    |     ✔     |
+| clear             |     ✔      |       ✔       |   ✔    |     ✔     |
+| skipEvents        |     ✔      |       ✔       |   ✔    |     ✔     |
+| attach            |     ✔      |       ✔       |   ✔    |     ✔     |
+| detach            |     ✔      |       ✔       |   ✔    |     ✔     |
+| press             |     ✔      |       ✔       |   ✔    |     ✔     |
+| release           |     ✔      |       ✔       |   ✔    |     ✔     |
+| click             |     ✔      |       ✔       |   ✔    |     ✔     |
+| pressing          |     ✔      |       ✔       |   ✔    |     ✔     |
+| hold              |     ✔      |       ✔       |   ✔    |     ✔     |
+| holding           |     ✔      |       ✔       |   ✔    |     ✔     |
+| step              |     ✔      |       ✔       |   ✔    |     ✔     |
+| hasClicks         |     ✔      |       ✔       |   ✔    |     ✔     |
+| getClicks         |     ✔      |       ✔       |   ✔    |     ✔     |
+| getSteps          |     ✔      |       ✔       |   ✔    |     ✔     |
+| releaseHold       |     ✔      |       ✔       |   ✔    |     ✔     |
+| releaseStep       |     ✔      |       ✔       |   ✔    |     ✔     |
+| releaseHoldStep   |     ✔      |       ✔       |   ✔    |     ✔     |
+| waiting           |     ✔      |       ✔       |   ✔    |     ✔     |
+| busy              |     ✔      |       ✔       |   ✔    |     ✔     |
+| action            |     ✔      |       ✔       |   ✔    |     ✔     |
+| getAction         |     ✔      |       ✔       |   ✔    |     ✔     |
+| timeout           |     ✔      |       ✔       |   ✔    |     ✔     |
+| pressFor          |     ✔      |       ✔       |   ✔    |     ✔     |
+| holdFor           |     ✔      |       ✔       |   ✔    |     ✔     |
+| stepFor           |     ✔      |       ✔       |   ✔    |     ✔     |
+</details>
+
+<details>
+<summary>Таблица функций энкодера</summary>
+
+|                | VirtEncoder | Encoder | VirtEncButton | EncButton |
+| -------------- | :---------: | :-----: | :-----------: | :-------: |
+| readEnc        |             |         |               |     ✔     |
+| initEnc        |      ✔      |    ✔    |       ✔       |     ✔     |
+| setEncReverse  |      ✔      |    ✔    |       ✔       |     ✔     |
+| setEncType     |      ✔      |    ✔    |       ✔       |     ✔     |
+| setEncISR      |      ✔      |    ✔    |       ✔       |     ✔     |
+| clear          |      ✔      |    ✔    |       ✔       |     ✔     |
+| turn           |      ✔      |    ✔    |       ✔       |     ✔     |
+| dir            |      ✔      |    ✔    |       ✔       |     ✔     |
+| tickRaw        |      ✔      |    ✔    |       ✔       |     ✔     |
+| pollEnc        |      ✔      |    ✔    |       ✔       |     ✔     |
+| counter        |      ✔      |    ✔    |       ✔       |     ✔     |
+| setFastTimeout |             |         |       ✔       |     ✔     |
+| turnH          |             |         |       ✔       |     ✔     |
+| fast           |             |         |       ✔       |     ✔     |
+| right          |             |         |       ✔       |     ✔     |
+| left           |             |         |       ✔       |     ✔     |
+| rightH         |             |         |       ✔       |     ✔     |
+| leftH          |             |         |       ✔       |     ✔     |
+| action         |             |         |       ✔       |     ✔     |
+| getAction      |             |         |       ✔       |     ✔     |
+| timeout        |             |         |       ✔       |     ✔     |
+| attach         |             |         |       ✔       |     ✔     |
+| detach         |             |         |       ✔       |     ✔     |
+</details>
+
+<details>
+<summary>VirtButton</summary>
+
+```cpp
+// ================ НАСТРОЙКИ ================
+// установить таймаут удержания, умолч. 600 (макс. 4000 мс)
+void setHoldTimeout(uint16_t tout);
+
+// установить таймаут импульсного удержания, умолч. 200 (макс. 4000 мс)
+void setStepTimeout(uint16_t tout);
+
+// установить таймаут ожидания кликов, умолч. 500 (макс. 4000 мс)
+void setClickTimeout(uint16_t tout);
+
+// установить таймаут антидребезга, умолч. 50 (макс. 255 мс)
+void setDebTimeout(uint8_t tout);
+
+// установить время таймаута, умолч. 1000 (макс. 4000 мс)
+void setTimeout(const uint16_t tout);
+
+// установить уровень кнопки (HIGH - кнопка замыкает VCC, LOW - замыкает GND)
+// умолч. HIGH, то есть true - кнопка нажата
+void setBtnLevel(bool level);
+
+// подключить функцию-обработчик событий
+void attach(void (*handler)());
+
+// отключить функцию-обработчик событий
+void detach();
+
+// ================== СБРОС ==================
+// сбросить системные флаги (принудительно закончить обработку)
+void reset();
+
+// принудительно сбросить флаги событий
+void clear(bool resetTout = false);
+
+// игнорировать все события до отпускания кнопки
+void skipEvents();
+
+// ================ ОБРАБОТКА ================
+// обработка кнопки значением
+bool tick(bool s);
+
+// обработка виртуальной кнопки как одновременное нажатие двух других кнопок
+bool tick(VirtButton& b0, VirtButton& b1);
+
+// кнопка нажата в прерывании кнопки
+void pressISR();
+
+// обработка кнопки без сброса событий и вызова коллбэка
+bool tickRaw(bool s);
+
+// ================== ОПРОС ==================
+// кнопка нажата [событие]
+bool press();
+bool press(uint8_t clicks);
+
+// кнопка отпущена (в любом случае) [событие]
+bool release();
+bool release(uint8_t clicks);
+
+// клик по кнопке (отпущена без удержания) [событие]
+bool click();
+bool click(uint8_t clicks);
+
+// кнопка зажата (между press() и release()) [состояние]
+bool pressing();
+bool pressing(uint8_t clicks);
+
+// кнопка была удержана (больше таймаута) [событие]
+bool hold();
+bool hold(uint8_t clicks);
+
+// кнопка удерживается (больше таймаута) [состояние]
+bool holding();
+bool holding(uint8_t clicks);
+
+// импульсное удержание [событие]
+bool step();
+bool step(uint8_t clicks);
+
+// зафиксировано несколько кликов [событие]
+bool hasClicks();
+bool hasClicks(uint8_t clicks);
+
+// кнопка отпущена после удержания [событие]
+bool releaseHold();
+bool releaseHold(uint8_t clicks);
+
+// кнопка отпущена после импульсного удержания [событие]
+bool releaseStep();
+bool releaseStep(uint8_t clicks);
+
+// кнопка отпущена после удержания или импульсного удержания [событие]
+bool releaseHoldStep();
+bool releaseHoldStep(uint8_t clicks);
+
+// получить количество кликов
+uint8_t getClicks();
+
+// получить количество степов
+uint16_t getSteps();
+
+// кнопка ожидает повторных кликов (между click() и hasClicks()) [состояние]
+bool waiting();
+
+// идёт обработка (между первым нажатием и после ожидания кликов) [состояние]
+bool busy();
+
+// было действие с кнопки, вернёт код события [событие]
+uint16_t action();
+EBAction getAction();
+
+// ================== ВРЕМЯ ==================
+// после взаимодействия с кнопкой (или энкодером EncButton) время setTimeout, мс [событие]
+bool timeout();
+
+// после взаимодействия с кнопкой (или энкодером EncButton) время setTimeout, мс [состояние]
+bool timeoutState();
+
+// время, которое кнопка удерживается (с начала нажатия), мс
+uint16_t pressFor();
+
+// кнопка удерживается дольше чем (с начала нажатия), мс [состояние]
+bool pressFor(uint16_t ms);
+
+// время, которое кнопка удерживается (с начала удержания), мс
+uint16_t holdFor();
+
+// кнопка удерживается дольше чем (с начала удержания), мс [состояние]
+bool holdFor(uint16_t ms);
+
+// время, которое кнопка удерживается (с начала степа), мс
+uint16_t stepFor();
+
+// кнопка удерживается дольше чем (с начала степа), мс [состояние]
+bool stepFor(uint16_t ms);
+```
+</details>
+<details>
+<summary>VirtEncoder</summary>
+
+```cpp
+// ==================== НАСТРОЙКИ ====================
+// инвертировать направление энкодера (умолч. 0)
+void setEncReverse(bool rev);
+
+// установить тип энкодера (EB_STEP4_LOW, EB_STEP4_HIGH, EB_STEP2, EB_STEP1)
+void setEncType(uint8_t type);
+
+// использовать обработку энкодера в прерывании
+void setEncISR(bool use);
+
+// инициализация энкодера
+void initEnc(bool e0, bool e1);
+
+// инициализация энкодера совмещённым значением
+void initEnc(int8_t v);
+
+// сбросить флаги событий
+void clear();
+
+// ====================== ОПРОС ======================
+// был поворот [событие]
+bool turn();
+
+// направление энкодера (1 или -1) [состояние]
+int8_t dir();
+
+// счётчик
+int32_t counter;
+
+// ==================== ОБРАБОТКА ====================
+// опросить энкодер в прерывании. Вернёт 1 или -1 при вращении, 0 при остановке
+int8_t tickISR(bool e0, bool e1);
+int8_t tickISR(int8_t state);
+
+// опросить энкодер. Вернёт 1 или -1 при вращении, 0 при остановке
+int8_t tick(bool e0, bool e1);
+int8_t tick(int8_t state);
+int8_t tick();  // сама обработка в прерывании
+
+// опросить энкодер без сброса события поворота. Вернёт 1 или -1 при вращении, 0 при остановке
+int8_t tickRaw(bool e0, bool e1);
+int8_t tickRaw(int8_t state);
+int8_t tickRaw();  // сама обработка в прерывании
+
+// опросить энкодер без установки флагов на поворот (быстрее). Вернёт 1 или -1 при вращении, 0 при остановке
+int8_t pollEnc(bool e0, bool e1);
+int8_t pollEnc(int8_t state);
+```
+</details>
+<details>
+<summary>VirtEncButton</summary>
+
+- Доступны функции из `VirtButton`
+- Доступны функции из `VirtEncoder`
+
+```cpp
+// ================== НАСТРОЙКИ ==================
+// установить таймаут быстрого поворота, мс
+void setFastTimeout(uint8_t tout);
+
+// сбросить флаги энкодера и кнопки
+void clear(bool resetTout = false);
+
+// ==================== ОПРОС ====================
+// ЛЮБОЙ поворот энкодера [событие]
+bool turn();
+
+// нажатый поворот энкодера [событие]
+bool turnH();
+
+// быстрый поворот энкодера [состояние]
+bool fast();
+
+// ненажатый поворот направо [событие]
+bool right();
+
+// ненажатый поворот налево [событие]
+bool left();
+
+// нажатый поворот направо [событие]
+bool rightH();
+
+// нажатый поворот налево [событие]
+bool leftH();
+
+// было действие с кнопки или энкодера, вернёт код события [событие]
+uint16_t action();
+EBAction getAction();
+
+// ==================== ОБРАБОТКА ====================
+// обработка в прерывании (только энкодер). Вернёт 0 в покое, 1 или -1 при повороте
+int8_t tickISR(bool e0, bool e1);
+int8_t tickISR(int8_t e01);
+
+// обработка энкодера и кнопки
+bool tick(bool e0, bool e1, bool btn);
+bool tick(int8_t e01, bool btn);
+bool tick(bool btn);  // энкодер в прерывании
+
+// обработка энкодера и кнопки без сброса флагов и вызова коллбэка
+bool tickRaw(bool e0, bool e1, bool btn);
+bool tickRaw(int8_t e01, bool btn);
+bool tickRaw(bool btn);  // энкодер в прерывании
+```
+</details>
+<details>
+<summary>Button</summary>
+
+- Доступны функции из `VirtButton`
+- Режим кнопки по умолчанию - `LOW`
+
+```cpp
+Button;
+Button(uint8_t pin);                // с указанием пина
+Button(uint8_t npin, uint8_t mode); // + режим работы (умолч. INPUT_PULLUP)
+Button(uint8_t npin, uint8_t mode, uint8_t btnLevel); // + уровень кнопки (умолч. LOW)
+```
+```cpp
+// указать пин и его режим работы
+void init(uint8_t npin, uint8_t mode);
+
+// прочитать текущее значение кнопки (без дебаунса) с учётом setBtnLevel
+bool read();
+
+// функция обработки, вызывать в loop
+bool tick();
+
+// обработка кнопки без сброса событий и вызова коллбэка
+bool tickRaw();
+```
+</details>
+<details>
+<summary>ButtonT</summary>
+
+- Доступны функции из `VirtButton`
+- Режим кнопки по умолчанию - `LOW`
+
+```cpp
+ButtonT<uint8_t pin>;                 // с указанием пина
+ButtonT<uint8_t pin> (uint8_t mode);  // + режим работы (умолч. INPUT_PULLUP)
+ButtonT<uint8_t pin> (uint8_t mode, uint8_t btnLevel); // + уровень кнопки (умолч. LOW)
+```
+```cpp
+// указать режим работы
+void init(uint8_t mode);
+
+// прочитать текущее значение кнопки (без дебаунса) с учётом setBtnLevel
+bool read();
+
+// функция обработки, вызывать в loop
+bool tick();
+```
+</details>
+<details>
+<summary>Encoder</summary>
+
+- Доступны функции из `VirtEncoder`
+
+```cpp
+Encoder;
+Encoder(uint8_t encA, uint8_t encB);                // с указанием пинов
+Encoder(uint8_t encA, uint8_t encB, uint8_t mode);  // + режим работы (умолч. INPUT)
+```
+```cpp
+// указать пины и их режим работы
+void init(uint8_t encA, uint8_t encB, uint8_t mode);
+
+// функция обработки для вызова в прерывании энкодера
+int8_t tickISR();
+
+// функция обработки для вызова в loop
+int8_t tick();
+```
+</details>
+<details>
+<summary>EncoderT</summary>
+
+- Доступны функции из `VirtEncoder`
+
+```cpp
+EncoderT<uint8_t encA, uint8_t encB>;                 // с указанием пинов
+EncoderT<uint8_t encA, uint8_t encB> (uint8_t mode);  // + режим работы (умолч. INPUT)
+```
+```cpp
+// указать режим работы пинов
+void init(uint8_t mode);
+
+// функция обработки для вызова в прерывании энкодера
+int8_t tickISR();
+
+// функция обработки для вызова в loop
+int8_t tick();
+```
+</details>
+<details>
+<summary>EncButton</summary>
+
+- Доступны функции из `VirtButton`
+- Доступны функции из `VirtEncoder`
+- Доступны функции из `VirtEncButton`
+
+```cpp
+EncButton;
+
+// настроить пины (энк, энк, кнопка)
+EncButton(uint8_t encA, uint8_t encB, uint8_t btn);
+
+// настроить пины (энк, энк, кнопка, pinmode энк, pinmode кнопка, уровень кнопки)
+EncButton(uint8_t encA, uint8_t encB, uint8_t btn, uint8_t modeEnc = INPUT, uint8_t modeBtn = INPUT_PULLUP, uint8_t btnLevel = LOW);
+```
+```cpp
+// настроить пины (энк, энк, кнопка, pinmode энк, pinmode кнопка, уровень кнопки)
+void init(uint8_t encA, uint8_t encB, uint8_t btn, uint8_t modeEnc = INPUT, uint8_t modeBtn = INPUT_PULLUP, uint8_t btnLevel = LOW);
+
+// функция обработки для вызова в прерывании энкодера
+int8_t tickISR();
+
+// функция обработки, вызывать в loop
+bool tick();
+
+// прочитать значение кнопки с учётом setBtnLevel
+bool readBtn();
+
+// прочитать значение энкодера
+int8_t readEnc();
+```
+</details>
+<details>
+<summary>EncButtonT</summary>
+
+- Доступны функции из `VirtButton`
+- Доступны функции из `VirtEncoder`
+- Доступны функции из `VirtEncButton`
+
+```cpp
+// с указанием пинов
+EncButtonT<uint8_t encA, uint8_t encB, uint8_t btn>;
+
+// + режим работы пинов, уровень кнопки
+EncButtonT<uint8_t encA, uint8_t encB, uint8_t btn> (uint8_t modeEnc = INPUT, uint8_t modeBtn = INPUT_PULLUP, uint8_t btnLevel = LOW);
+```
+```cpp
+// настроить режим работы пинов, уровень кнопки
+void init(uint8_t modeEnc = INPUT, uint8_t modeBtn = INPUT_PULLUP, uint8_t btnLevel = LOW);
+
+// функция обработки для вызова в прерывании энкодера
+int8_t tickISR();
+
+// функция обработки, вызывать в loop
+bool tick();
+
+// прочитать значение кнопки
+bool readBtn();
+
+// прочитать значение энкодера
+int8_t readEnc();
+```
+</details>
+
+<a id="tick"></a>
+
+### Обработка и опрос
+Во всех библиотеках есть общая **функция обработки** (тикер `tick`), которая получает текущий сигнал с кнопки и энкодера
+- Эту функцию нужно однократно вызывать в основном цикле программы (для виртуальных - с передачей значения)
+- Функция возвращает `true` при наступлении события (для энкодера - `1` или `-1` при повороте, `0` при его отсутствии. Таким образом поворот в любую сторону расценивается как `true`)
+- Есть отдельные функции для вызова в прерывании, они имеют суффикс `ISR`, см. документацию ниже
+
+Библиотека обрабатывает сигнал внутри этой функции, результат можно получить из **функций опроса** событий. Они бывают двух типов:
+- `[событие]` - функция вернёт `true` однократно при наступлении события. Сбросится после следующего вызова функции обработки (например клик, поворот энкодера). За исключением события `timeout`
+- `[состояние]` - функция возвращает `true`, пока активно это состояние (например кнопка удерживается)
+
+Для простоты восприятия функцию обработки нужно размещать в начале цикла, а опросы делать ниже:
+```cpp
+void loop() {
+  btn.tick();   // опрос
+
+  if (btn.click()) Serial.println("click"); // однократно выведет при клике
+  if (btn.click()) Serial.println("click"); // тот же клик!
+}
+```
+> В отличие от предыдущих версий библиотеки, функции опроса сбрасываются не внутри себя, а *внутри функции обработки*. Таким образом в примере выше при клике по кнопке в порт дважды выведется сообщение `click()`. Это позволяет использовать функции опроса по несколько раз за текущую итерацию цикла для создания сложной логики работы программы.
+
+#### Несколько функций обработки
+По очевидным причинам нельзя вызывать функцию обработки больше одного раза за цикл - каждый следующий вызов сбросит события от предыдущего и код будет работать некорректно. Вот так - нельзя:
+```cpp
+// так нельзя
+void loop() {
+  btn.tick();
+  if (btn.click()) ...
+
+  // ....
+
+  btn.tick();
+  if (btn.hold()) ...
+}
+```
+
+Если очень нужно попасть в глухой цикл и опрашивать там кнопку, то вот так - можно:
+```cpp
+// так можно
+void loop() {
+  btn.tick();
+  if (btn.click()) ...
+
+  while (true) {
+    btn.tick();
+    if (btn.hold()) ...
+    if (btn.click()) break;
+  }
+}
+```
+
+Если библиотека используется с подключенным обработчиком событий `attach()` (см. ниже), то можно вызывать `tick()` где угодно и сколько угодно раз, события будут обработаны в обработчике:
+```cpp
+// так можно
+void cb() {
+  switch (btn.action()) {
+    // ...
+  }
+}
+
+void setup() {
+  btn.attach(cb);
+}
+
+void loop() {
+  btn.tick();
+  // ...
+  btn.tick();
+  // ...
+  btn.tick();
+}
+```
+
+#### "Загруженная" программа
+Библиотека EncButton - **асинхронная**: она не ждёт, пока закончится обработка кнопки, а позволяет программе выполняться дальше. Это означает, что для корректной работы библиотеки основной цикл программы должен выполняться как можно быстрее и не содержать задержек и других "глухих" циклов внутри себя. Для обеспечения правильной обработки кнопки не рекомендуется иметь в основном цикле задержки длительностью более 50-100 мс. Несколько советов:
+- Новичкам: изучить цикл уроков [как написать скетч](https://alexgyver.ru/lessons/how-to-sketch/)
+  - Писать асинхронный код в `loop()`
+  - Любую синхронную конструкцию на `delay()` можно сделать асинхронной при помощи `millis()`
+  - Если в программе *каждая* итерация главного цикла выполняется дольше 50-100мс - в большинстве случаев программа написана неправильно, за исключением каких-то особых случаев
+- Подключить кнопку на аппаратное прерывание (см. ниже)
+- Избегать выполнения "тяжёлых" участков кода, пока идёт обработка кнопки, например поместив их в условие `if (!button.busy()) { тяжёлый код }`
+- Если оптимизировать основной цикл невозможно - вызывать тикер в другом "потоке" и использовать функцию-обработчик:
+  - В прерывании таймера с периодом ~50мс или чаще
+  - На другом ядре (например ESP32)
+  - В другом таске FreeRTOS
+  - Внутри `yield()` (внутри `delay()`)
+
+#### Раздельная обработка
+> Имеет смысл только при ручном опросе событий! При подключенной функции-обработчике достаточно вызывать обычный `tick()` между тяжёлыми участками программы
+
+Также в загруженной программе можно разделить обработку и сброс событий: вместо `tick()` использовать `tickRaw()` между тяжёлыми участками кода и ручной сброс `clear()`. Порядок следующий:
+- Опросить действия (click, press, turn...)
+- Вызвать `clear()`
+- Вызывать `tickRaw()` между тяжёлыми участками кода
+
+```cpp
+void loop() {
+  if (btn.click()) ...
+  if (btn.press()) ...
+  if (btn.step()) ...
+
+  btn.clear();
+
+  // ...
+  btn.tickRaw();
+  // ...
+  btn.tickRaw();
+  // ...
+  btn.tickRaw();
+  // ...
+}
+```
+Это позволит опрашивать кнопку/энкодер в не очень хорошо написанной программе, где основной цикл завален тяжёлым кодом. Внутри `tickRaw()` накапливаются события, которые раз в цикл разбираются, а затем вручную сбрасываются.
+
+> В этом сценарии буферизация энкодера в прерывании не работает и не обрабатываются все события `releaseXxx`
+
+#### Обработка внутри delay
+Если сложно избавиться от `delay()` внутри главного цикла программы, то на некоторых платформах можно поместить свой код внутри него. Таким образом можно получить даже обработку энкодера в цикле с дилеями без использования прерываний:
+```cpp
+// вставка кода в delay
+void yield() {
+  eb.tickRaw();
+}
+
+void loop() {
+  if (eb.click()) ...
+  if (btn.turn()) ...
+
+  eb.clear();
+
+  // ...
+  delay(10);
+  // ...
+  delay(50);
+  // ...
+}
+```
+
+> В этом сценарии буферизация энкодера в прерывании не работает и не обрабатываются все события `releaseXxx`
+
+#### Обработка кнопки
+Библиотека обрабатывает кнопку следующим образом:
+- Нажатие с программным подавлением дребезга (удержание дольше таймаута deb), результат - событие `press`, состояния `pressing` и `busy`
+- Удержание дольше таймаута удержания hold - событие `hold`, состояние `holding`
+- Удержание дольше таймаута удержания hold + таймаута степ - импульсное событие `step`, срабатывает с периодом step пока кнопка удерживается
+- Отпускание кнопки, результат - событие `release`, снятие состояний `pressing` и `holding`
+  - Отпускание до таймаута удержания - событие `click`
+  - Отпускание после удержания - событие `releaseHold`
+  - Отпускание после импульсного удержания - событие `releaseStep`
+  - События `releaseHold` и `releaseStep` взаимоисключающие, если кнопка была удержана до `step` - `releaseHold` уже не сработает
+- Ожидание нового клика в течение таймаута click, состояние `waiting`
+- Если нового клика нет - снятие состоятия `busy`, обработка закончена
+  - Если кнопка снова нажата - обработка нового клика
+  - Счётчик кликов `getClicks()` сбрасывается после событий `releaseHold`/`releaseStep`, которые проверяют предварительные клики. В общем обработчике `action()` это события `EB_REL_HOLD_C` или `EB_REL_STEP_C`
+  - Количество сделанных кликов нужно проверять по событию `hasClicks`, а также можно опросить внутри почти всех событий кнопки, которые идут до `releaseXxx`
+- Если ожидается `timeout` - событие timeout периодом из `setTimeout`
+- Обработка кнопки в прерывании сообщает библиотеке о факте нажатия, вся остальная обработка выполняется штатно в `tick()`
+
+> Отличие `click(n)` от `hasClicks(n)`: `click(n)` вернёт `true` в любом случае при совпадении количества кликов, даже если будет сделано больше кликов. `hasClicks(n)` вернёт `true` только в том случае, если было сделано ровно указанное количество кликов и больше кликов не было!
+
+> Лучше один раз увидеть, чем сто раз прочитать. Запусти пример demo и понажимай на кнопку, или попробуй [онлайн-симуляцию в Wokwi](https://wokwi.com/projects/373591584298469377)
+
+##### Click
+![click](/doc/click.gif)  
+
+##### Hold
+![hold](/doc/hold.gif)  
+
+##### Step
+![step](/doc/step.gif)  
+
+Онлайн-симуляция доступна [здесь](https://wokwi.com/projects/373591584298469377)
+
+#### Обработка энкодера
+- "Быстрым" поворотом считается поворот, совершённый менее чем за настроенный таймаут от предыдущего поворота
+- Обработанные в прерывании повороты становятся активными (вызывают события) после вызова `tick()`
+- Доступ к счётчику энкодера `counter` - это публичная переменная класса, можно делать с ней всё что угодно:
+```cpp
+Serial.println(eb.counter); // читать
+eb.counter += 1234;         // менять
+eb.counter = 0;             // обнулять
+```
+
+#### Обработка энкодера с кнопкой
+- Поворот энкодера при зажатой кнопке снимает и блокирует все последующие события и клики, за исключением события `release`. Состояния нажатой кнопки не изменяются
+- Поворот энкодера также влияет на системный таймаут (функция `timeout()`) - сработает через указанное время после поворота энкодера
+- Счётчик кликов доступен при нажатом повороте: несколько кликов, зажатие кнопки, поворот
+
+<a id="preclicks"></a>
+
+### Предварительные клики
+Библиотека считает количество кликов по кнопке и некоторые функции опроса могут отдельно обрабатываться с *предварительными кликами*. Например 3 клика, затем удержание. Это очень сильно расширяет возможности одной кнопки. Есть два варианта работы с такими событиями:
+```cpp
+  // 1
+  if (btn.hold()) {
+    if (btn.getClicks() == 2) Serial.println("hold 2 clicks");
+  }
+
+  // 2
+  if (btn.hold(2)) Serial.println("hold 2 clicks");
+```
+
+В первом варианте можно получить количество кликов для дальнейшей обработки вручную, а во втором - библиотека сделает это сама, если количество кликов для действия заранее известно.
+
+<a id="btnread"></a>
+
+### Прямое чтение кнопки
+В некоторых сценариях бывает нужно получить состояние кнопки "здесь и сейчас", например определить удерживается ли кнопка сразу после запуска микроконтроллера (старта программы). Функцию `tick()` нужно вызывать постоянно в цикле, чтобы шла обработка кнопки с гашением дребезга контактов и прочими расчётами, поэтому конструкция следующего вида **работать не будет**:
+```cpp
+void setup() {
+  btn.tick();
+  if (btn.press()) Serial.println("Кнопка нажата при старте");
+}
+```
+
+Для таких сценариев помогут следующие функции, возвращают `true` если кнопка нажата:
+- `read()` для библиотек Button и ButtonT
+- `readBtn()` для библиотек EncButton и EncButtonT
+
+> Опрос кнопки выполняется с учётом настроенного ранее уровня кнопки (setBtnLevel)! Вручную дополнительно инвертировать логику не нужно:
+
+```cpp
+void setup() {
+  // btn.setBtnLevel(LOW); // можно настроить уровень
+
+  if (btn.read()) Serial.println("Кнопка нажата при старте");
+}
+```
+
+<a id="loop"></a>
+
+### Погружение в цикл
+Допустим нужно обработать кнопку синхронно и с гашением дребезга. Например если кнопка зажата при старте микроконтроллера - получить её удержание или даже импульсное удержание внутри блока `setup`, то есть до начала выполнения основной программы. Можно воспользоваться состоянием `busy` и опрашивать кнопку из цикла:
+```cpp
+void setup() {
+  Serial.begin(115200);
+
+  btn.tick();
+  while (btn.busy()) {
+    btn.tick();
+    if (btn.hold()) Serial.println("hold");
+    if (btn.step()) Serial.println("step");
+  }
+
+  Serial.println("program start");
+}
+```
+Как это работает: первый тик опрашивает кнопку, если кнопка нажата - сразу же активируется состояние busy и система попадает в цикл `while`. Внутри него продолжаем тикать и получать события с кнопки. Когда кнопка будет отпущена и сработают все события - флаг busy опустится и программа автоматически покинет цикл. Можно переписать эту конструкцию на цикл с постусловием, более красиво:
+```cpp
+do {
+  btn.tick();
+  if (btn.hold()) Serial.println("hold");
+  if (btn.step()) Serial.println("step");
+} while (btn.busy());
+```
+
+<a id="timeout"></a>
+
+### Timeout
+В связанных с кнопкой классах (Button, EncButton) есть функция `timeout()` - она однократно вернёт `true`, если после окончания действий с кнопкой/энкодером прошло указанное в `setTimeout` время. Это можно использовать для сохранения параметров после ввода, например:
+```cpp
+void setup() {
+  //eb.setTimeout(1500);  // умолч. 1000
+}
+void loop() {
+  eb.tick();
+
+  // ...
+
+  if (eb.timeout()) {
+    // после взаимодействия с энкодером прошло 1000 мс
+    // EEPROM.put(0, settings);
+  }
+}
+```
+
+В текущей версии обработка таймаута реализована не очень красиво ради экономии места: системный флаг таймаута активен всё время (`action` будет возвращать событие таймаута), сбрасывается в следующих случаях:
+
+- Вызов `timeout()` - данный метод однократно вернёт `true`, последующие вызовы будут возвращать `false` до нового действия
+- Автоматически сбросится после вызова обработчика, если он подключен
+- При вызове `clear(true)` - с флагом очистки таймаута
+- При вызове `reset()`
+
+Если нужно пробросить опрос таймаута через несколько вызовов - можно использовать `timeoutState()`, но последний вызов должен быть `timeout()`.
+
+<a id="busy"></a>
+
+### Busy
+Функция `busy()` возвращает `true`, пока идёт обработка кнопки, т.е. пока система ожидает действий и выхода таймаутов. Это можно использовать для оптимизации кода, например избегать каких то долгих и тяжёлых частей программы на время обработки кнопки:
+```cpp
+void loop() {
+  eb.tick();
+
+  // ...
+
+  if (!eb.busy()) {
+    // потенциально долгий и тяжёлый код
+  }
+}
+```
+
+<a id="actions"></a>
+
+### Получение события
+Доступно во всех классах **с кнопкой**:
+- `VirtButton`
+- `Button`
+- `VirtEncButton`
+- `EncButton`
+
+Функция `action()` при наступлении события возвращает код события (отличный от нуля, что само по себе является индикацией наличия события):
+- `EB_PRESS` - нажатие на кнопку
+- `EB_HOLD` - кнопка удержана
+- `EB_STEP` - импульсное удержание
+- `EB_RELEASE` - кнопка отпущена
+- `EB_CLICK` - одиночный клик
+- `EB_CLICKS` - сигнал о нескольких кликах
+- `EB_TURN` - поворот энкодера
+- `EB_REL_HOLD` - кнопка отпущена после удержания
+- `EB_REL_HOLD_C` - кнопка отпущена после удержания с предв. кликами
+- `EB_REL_STEP` - кнопка отпущена после степа
+- `EB_REL_STEP_C` - кнопка отпущена после степа с предв. кликами
+- `EB_TIMEOUT` - прошёл таймаут после нажатия кнопки или поворота энкодера
+
+Полученный код события можно обработать через `switch`:
+```cpp
+switch (eb.action()) {
+  case EB_PRESS:
+    // ...
+    break;
+  case EB_HOLD:
+    // ...
+    break;
+  // ...
+}
+```
+
+Есть аналогичная функция `getAction()`, вернёт то же самое, но с более читаемыми константами (удобно с автодополнением):
+
+- `EBAction::Press`
+- `EBAction::Hold`
+- `EBAction::Step`
+- `EBAction::Release`
+- `EBAction::Click`
+- `EBAction::Clicks`
+- `EBAction::Turn`
+- `EBAction::ReleaseHold`
+- `EBAction::ReleaseHoldClicks`
+- `EBAction::ReleaseStep`
+- `EBAction::ReleaseStepClicks`
+- `EBAction::Timeout`
+
+Полученный код события можно обработать через `switch`:
+```cpp
+switch (eb.getAction()) {
+  case EBAction::Press:
+    // ...
+    break;
+  case EBAction::Hold:
+    // ...
+    break;
+  // ...
+}
+```
+
+> Результат функций `action()`/`getAction()` сбрасывается после следующего вызова `tick()`, то есть доступен на всей текущей итерации основного цикла
+
+<a id="optimise"></a>
+
+### Оптимизация
+#### Вес библиотеки
+Для максимального уменьшения веса библиотеки (в частности в оперативной памяти) нужно задавать тайматуы константами через define (экономия 1 байт за таймаут), отключить обработчик событий, счётчики-буферы и использовать T-класс (экономия 1 байт за пин):
+```cpp
+#define EB_NO_FOR
+#define EB_NO_CALLBACK
+#define EB_NO_COUNTER
+#define EB_NO_BUFFER
+#define EB_DEB_TIME 50     // таймаут гашения дребезга кнопки (кнопка)
+#define EB_CLICK_TIME 500  // таймаут ожидания кликов (кнопка)
+#define EB_HOLD_TIME 600   // таймаут удержания (кнопка)
+#define EB_STEP_TIME 200   // таймаут импульсного удержания (кнопка)
+#define EB_FAST_TIME 30    // таймаут быстрого поворота (энкодер)
+#define EB_TOUT_TIME 1000  // таймаут действия (кнопка и энкодер)
+#include <EncButton.h>
+EncButtonT<2, 3, 4> eb;
+```
+В таком случае энкодер с кнопкой займёт в SRAM всего 8 байт, а просто кнопка - 5.
+
+#### Скорость выполнения
+Чтобы сократить время на проверку системных флагов событий (незначительно, но приятно) можно поместить все опросы в условие по `tick()`, так как `tick()` возвращает `true` только при наступлении **события**:
+```cpp
+void loop() {
+  if (eb.tick()) {
+    if (eb.turn()) ...;
+    if (eb.click()) ...;
+  }
+}
+```
+
+Также опрос событий при помощи функции `action()` выполняется быстрее, чем ручной опрос отдельных функций событий, поэтому максимально эффективно библиотека будет работать вот в таком формате:
+```cpp
+void loop() {
+  if (eb.tick()) {
+    switch (eb.action()) {
+      case EB_PRESS:
+        // ...
+        break;
+      case EB_HOLD:
+        // ...
+        break;
+      // ...
+    }
+  }
+}
+```
+
+Для опроса **состояний** кнопки `pressing()`, `holding()`, `waiting()` можно поместить их вовнутрь условия по `busy()`, чтобы не опрашивать состояния пока их гарантированно нет:
+```cpp
+if (btn.busy()) {
+  if (btn.pressing())...
+  if (btn.holding())...
+  if (btn.waiting())...
+}
+```
+
+<a id="callback"></a>
+
+### Коллбэки
+Можно подключить внешнюю функцию-обрбаотчик события, она будет вызвана при наступлении любого события. Данная возможность работает во всех классах **с кнопкой**:
+- `VirtButton`
+- `Button`
+- `VirtEncButton`
+- `EncButton`
+
+> Внутри коллбэка можно получить указатель на текущий объект (который вызвал коллбэк) из переменной `void* EB_self`
+
+```cpp
+EncButton eb(2, 3, 4);
+
+void callback() {
+  switch (eb.action()) {
+    case EB_PRESS:
+      // ...
+      break;
+    case EB_HOLD:
+      // ...
+      break;
+    // ...
+  }
+
+  // здесь EB_self указатель на eb
+}
+
+void setup() {
+  eb.attach(callback);
+}
+
+void loop() {
+  eb.tick();
+}
+```
+
+<a id="double"></a>
+
+### Одновременное нажатие
+Библиотека нативно поддерживает работу с двумя одновременно нажатыми кнопками как с третьей кнопкой. Для этого нужно:
+1. Cоздать специальную кнопку `MultiButton`
+2. Передать виртуальной кнопке в обработку свои кнопки (это могут быть объекты классов `VirtButton`, `Button`, `EncButton` + их `T`-версии). **Мульти-кнопка сама опросит обе кнопки!**
+3. Опрашивать события или слушать обработчик
+
+```cpp
+Button b0(4);
+Button b1(5);
+MultiButton b2;  // 1
+
+void loop() {
+  b2.tick(b0, b1);  // 2
+
+  // 3
+  if (b0.click()) Serial.println("b0 click");
+  if (b1.click()) Serial.println("b1 click");
+  if (b2.click()) Serial.println("b0+b1 click");
+}
+```
+
+Библиотека сама "сбросит" лишние события с реальных кнопок, если они были нажаты вместе, за исключением события `press`. Таким образом получается полноценная третья кнопка из двух других с удобным опросом.
+
+<a id="isr"></a>
+
+### Прерывания
+#### Энкодер
+Для обработки энкодера в загруженной программе нужно:
+- Подключить оба его пина на аппаратные прерывания по `CHANGE`
+- Установить `setEncISR(true)`
+- Вызывать в обработчике специальный тикер для прерывания
+- Основной тикер также нужно вызывать в `loop` для корреткной работы - события генерируются в основном тикере:
+```cpp
+// пример для ATmega328 и EncButton
+EncButton eb(2, 3, 4);
+
+/*
+// esp8266/esp32
+IRAM_ATTR void isr() {
+  eb.tickISR();
+}
+*/
+
+void isr() {
+  eb.tickISR();
+}
+void setup() {
+  attachInterrupt(0, isr, CHANGE);
+  attachInterrupt(1, isr, CHANGE);
+  eb.setEncISR(true);
+}
+void loop() {
+  eb.tick();
+}
+```
+
+Примечание: использование работы в прерывании позволяет корректно обрабатывать позицию энкодера и не пропустить новый поворот. Событие с поворотом, полученное из прерывания, станет доступно *после* вызова `tick` в основном цикле программы, что позволяет не нарушать последовательность работы основного цикла:
+- Буферизация отключена: событие `turn` активируется только один раз, независимо от количества щелчков энкодера, совершённых между двумя вызовами `tick` (щелчки обработаны в прерывании)
+- Буферизация включена: событие `turn` будет вызвано столько раз, сколько реально было щелчков энкодера, это позволяет вообще не пропускать повороты и не нагружать систему в прерывании. **Размер буфера - 5 необработанных щелчков энкодера**
+
+Примечания:
+- Функция `setEncISR` работает только в не виртуальных классах. Если он включен - основной тикер `tick` просто не опрашивает пины энкодера, что экономит процессорное время. Обработка происходит только в прерывании
+- Счётчик энкодера всегда имеет актуальное значение и может опережать буферизированные повороты в программе с большими задержками в основном цикле!
+- На разных платформах прерывания могут работать по разному (например на ESPxx - нужно добавить функции аттрибут `IRAM_ATTR`, см. документацию на свою платформу!)
+- Обработчик, подключенный в `attach()`, будет вызван из `tick()`, то есть *не из прерывания*!
+
+#### Виртуальные классы
+В виртуальных есть тикер, в который не нужно передавать состояние энкодера, если он обрабатывается в прерывании, это позволяет не опрашивать пины в холостую. Например:
+
+```cpp
+VirtEncoder e;
+
+void isr() {
+    e.tickISR(digitalRead(2), digitalRead(3));
+}
+void setup() {
+    attachInterrupt(0, isr, CHANGE);
+    attachInterrupt(1, isr, CHANGE);
+
+    e.setEncISR(1);
+}
+void loop() {
+    e.tick();   // не передаём состояния пинов
+}
+```
+
+#### Кнопка
+Для обработки кнопки в прерывании нужно:
+- Подключить прерывание на **нажатие** кнопки с учётом её физического подключения и уровня:
+  - Если кнопка замыкает `LOW` - прерывание `FALLING`
+  - Если кнопка замыкает `HIGH` - прерывание `RISING`
+- Вызывать `pressISR()` в обработчике прерывания
+
+```cpp
+Button b(2);
+
+/*
+// esp8266/esp32
+IRAM_ATTR void isr() {
+  b.pressISR();
+}
+*/
+
+void isr() {
+  b.pressISR();
+}
+void setup() {
+  attachInterrupt(0, isr, FALLING);
+}
+void loop() {
+  b.tick();
+}
+```
+
+Примечание: кнопка обрабатывается в основном `tick()`, а функция `pressISR()` всего лишь сообщает библиотеке, что кнопка была нажата вне `tick()`. Это позволяет не пропустить нажатие кнопки, пока программа была занята чем-то другим.
+
+<a id="array"></a>
+
+### Массив кнопок/энкодеров
+Создать массив можно только из нешаблонных классов (без буквы `T`), потому что номера пинов придётся указать уже в рантайме далее в программе. Например:
+```cpp
+Button btns[5];
+EncButton ebs[3];
+
+void setup() {
+  btns[0].init(2);  // указать пин
+  btns[1].init(5);
+  btns[2].init(10);
+  // ...
+
+  ebs[0].init(11, 12, 13, INPUT);
+  ebs[1].init(14, 15, 16);
+  // ...
+}
+void loop() {
+  for (int i = 0; i < 5; i++) btns[i].tick();
+  for (int i = 0; i < 3; i++) ebs[i].tick();
+
+  if (btns[2].click()) Serial.println("btn2 click");
+  // ...
+}
+```
+
+<a id="custom"></a>
+
+### Кастомные функции
+Библиотека поддерживает задание своих функций для чтения пина и получения времени без редактирования файлов библиотеки. Для этого нужно реализовать соответствующую функцию в своём .cpp или .ino файле:
+- `bool EB_read(uint8_t pin)` - для своей функции чтения пина
+- `void EB_mode(uint8_t pin, uint8_t mode)` - для своего аналога pinMode
+- `uint32_t EB_uptime()` - для своего аналога millis()
+
+Пример:
+
+```cpp
+#include <EncButton.h>
+
+bool EB_read(uint8_t pin) {
+    return digitalRead(pin);
+}
+
+void EB_mode(uint8_t pin, uint8_t mode) {
+    pinMode(pin, mode);
+}
+
+uint32_t EB_uptime() {
+    return millis();
+}
+```
+
+<a id="timer"></a>
+
+### Опрос по таймеру
+Иногда может понадобиться вызывать `tick()` не на каждой итерации, а по таймеру. Например для виртуальной кнопки с расширителя пинов, когда чтение расширителя пинов - долгая операция, и вызывать её часто не имеет смысла. Вот так делать нельзя, события будут активны в течение периода таймера!
+```cpp
+void loop() {
+  // таймер на 50 мс
+  static uint32_t tmr;
+  if (millis() - tmr >= 50) {
+    tmr = millis();
+    btn.tick(readSomePin());
+  }
+
+  // будет активно в течение 50 мс!!!
+  if (btn.click()) foo();
+}
+```
+
+В данной ситуации нужно поступить так: тикать по таймеру, там же обрабатывать события и сбрасывать флаги в конце:
+```cpp
+void loop() {
+  // таймер на 50 мс
+  static uint32_t tmr;
+  if (millis() - tmr >= 50) {
+    tmr = millis();
+    // тик
+    btn.tick(readSomePin());
+
+    // разбор событий
+    if (btn.click()) foo();
+
+    // сброс флагов
+    btn.clear();
+  }
+}
+```
+
+Либо можно подключить обработчик и вызывать `clear()` в конце функции:
+```cpp
+void callback() {
+  switch (btn.action()) {
+    // ...
+  }
+
+  // сброс флагов
+  btn.clear();
+}
+
+void loop() {
+  // таймер на 50 мс
+  static uint32_t tmr;
+  if (millis() - tmr >= 50) {
+    tmr = millis();
+    btn.tick(readSomePin());
+  }
+}
+```
+
+В случае с вызовом по таймеру антидребезг будет частично обеспечиваться самим таймером и в библиотеке его можно отключить (поставить период 0).
+
+Для корректной работы таймаутов, состояний и счётчика кликов нужен другой подход: буферизировать прочитанные по таймеру состояния и передавать их в тик в основном цикле. Например так:
+```cpp
+bool readbuf = 0;  // буфер пина
+
+void loop() {
+  // таймер на 50 мс
+  static uint32_t tmr;
+  if (millis() - tmr >= 50) {
+    tmr = millis();
+    readbuf = readSomePin();  // чтение в буфер
+  }
+
+  // тик из буфера
+  btn.tick(readbuf);
+
+  if (btn.click()) foo();
+}
+```
+
+### Пропуск событий
+EncButton позволяет кнопке работать в паре с энкодером для корректного отслеживания *нажатых поворотов* - при нажатом повороте события с кнопки будут пропущены, т.е. не обработается удержание и клик. Допустим кнопок несколько: они могут выполнять действия как сами по себе, так и в паре с энкодером (кнопка зажата и крутится энкодер, в программе меняется выбранное кнопкой значение). Чтобы при удержании кнопка не генерировала события (удержание, степ, клики...) можно включить пропуск событий. Он будет действовать **до отпускания кнопки**:
+
+```cpp
+if (btn.pressing() && enc.turn()) {
+  btn.skipEvents();  // зафиксирован поворот. Пропускаем события
+  // нажатый поворот
+}
+
+if (btn.click()) {
+  // просто клик
+}
+```
+
+<a id="examples-mini"></a>
+
+### Мини примеры, сценарии
+```cpp
+// меняем значения переменных
+
+// поворот энкодера
+if (enc.turn()) {
+  // меняем с шагом 5
+  var += 5 * enc.dir();
+
+  // меняем с шагом 1 при обычном повороте, 10 при быстром
+  var += enc.fast() ? 10 : 1;
+
+  // меняем с шагом 1 при обычном повороте, 10 при нажатом
+  var += enc.pressing() ? 10 : 1;
+
+  // меняем одну переменную при повороте, другую - при нажатом повороте
+  if (enc.pressing()) var0++;
+  else var1++;
+
+  // если кнопка нажата - доступны предварительные клики
+  // Выбираем переменную для изменения по предв. кликам
+  if (enc.pressing()) {
+    switch (enc.getClicks()) {
+      case 1: var0 += enc.dir();
+        break;
+      case 2: var1 += enc.dir();
+        break;
+      case 3: var2 += enc.dir();
+        break;
+    }
+  }
+}
+
+// импульсное удержание на каждом шаге инкрементирует переменную
+if (btn.step()) var++;
+
+// смена направления изменения переменной после отпускания из step
+if (btn.step()) var += dir;
+if (btn.releaseStep()) dir = -dir;
+
+// изменение выбранной переменной при помощи step
+if (btn.step(1)) var1++;  // клик-удержание
+if (btn.step(2)) var2++;  // клик-клик-удержание
+if (btn.step(3)) var3++;  // клик-клик-клик-удержание
+
+// если держать step больше 2 секунд - инкремент +5, пока меньше - +1
+if (btn.step()) {
+  if (btn.stepFor(2000)) var += 5;
+  else var += 1;
+}
+
+// включение режима по количеству кликов
+if (btn.hasClicks()) mode = btn.getClicks();
+
+// включение режима по нескольким кликам и удержанию
+if (btn.hold(1)) mode = 1;  // клик-удержание
+if (btn.hold(2)) mode = 2;  // клик-клик-удержание
+if (btn.hold(3)) mode = 3;  // клик-клик-клик-удержание
+
+// или так
+if (btn.hold()) mode = btn.getClicks();
+
+// кнопка отпущена, смотрим сколько её удерживали
+if (btn.release()) {
+  // от 1 до 2 секунд
+  if (btn.pressFor() > 1000 && btn.pressFor() <= 2000) mode = 1;
+  // от 2 до 3 секунд
+  else if (btn.pressFor() > 2000 && btn.pressFor() <= 3000) mode = 2;
+}
+```
+
+<a id="migrate"></a>
+
+## Гайд по миграции с v2 на v3
+### Инициализация
+```cpp
+// ВИРТУАЛЬНЫЕ
+VirtEncButton eb; // энкодер с кнопкой
+VirtButton b;     // кнопка
+VirtEncoder e;    // энкодер
+
+// РЕАЛЬНЫЕ
+// энкодер с кнопкой
+EncButton eb(enc0, enc1, btn);                    // пины энкодера и кнопки
+EncButton eb(enc0, enc1, btn, modeEnc);           // + режим пинов энкодера (умолч. INPUT)
+EncButton eb(enc0, enc1, btn, modeEnc, modeBtn);  // + режим пина кнопки (умолч. INPUT_PULLUP)
+EncButton eb(enc0, enc1, btn, modeEnc, modeBtn, btnLevel);  // + уровень кнопки (умолч. LOW)
+// шаблонный
+EncButton<enc0, enc1, btn> eb;                    // пины энкодера и кнопки
+EncButton<enc0, enc1, btn> eb(modeEnc);           // + режим пинов энкодера (умолч. INPUT)
+EncButton<enc0, enc1, btn> eb(modeEnc, modeBtn);  // + режим пина кнопки (умолч. INPUT_PULLUP)
+EncButton<enc0, enc1, btn> eb(modeEnc, modeBtn, btnLevel);  // + уровень кнопки (умолч. LOW)
+
+// кнопка
+Button b(pin);                  // пин
+Button b(pin, mode);            // + режим пина кнопки (умолч. INPUT_PULLUP)
+Button b(pin, mode, btnLevel);  // + уровень кнопки (умолч. LOW)
+// шаблонный
+ButtonT<pin> b;                 // пин
+ButtonT<pin> b(mode);           // + режим пина кнопки (умолч. INPUT_PULLUP)
+ButtonT<pin> b(mode, btnLevel); // + уровень кнопки (умолч. LOW)
+
+// энкодер
+Encoder e(enc0, enc1);          // пины энкодера
+Encoder e(enc0, enc1, mode);    // + режим пинов энкодера (умолч. INPUT)
+// шаблонный
+EncoderT<enc0, enc1> e;         // пины энкодера
+EncoderT<enc0, enc1> e(mode);   // + режим пинов энкодера (умолч. INPUT)
+```
+
+### Функции
+| v2          | v3           |
+| ----------- | ------------ |
+| `held()`    | `hold()`     |
+| `hold()`    | `holding()`  |
+| `state()`   | `pressing()` |
+| `setPins()` | `init()`     |
+
+- Изменился порядок указания пинов (см. доку выше)
+- `clearFlags()` заменена на `clear()` (сбросить флаги событий) и `reset()` (сбросить системные флаги обработки, закончить обработку)
+
+### Логика работы
+В v3 функции опроса событий (click, turn...) не сбрасываются сразу после своего вызова - они сбрасываются при следующем вызове `tick()`, таким образом сохраняют своё значение во всех последующих вызовах на текущей итерации главного цикла программы. **Поэтому `tick()` нужно вызывать только 1 раз за цикл, иначе будут пропуски действий!** Читай об этом выше.
+
+<a id="example"></a>
+## Примеры
+Остальные примеры смотри в **examples**!
+<details>
+<summary>Полное демо EncButton</summary>
+
+```cpp
+// #define EB_NO_FOR           // отключить поддержку pressFor/holdFor/stepFor и счётчик степов (экономит 2 байта оперативки)
+// #define EB_NO_CALLBACK      // отключить обработчик событий attach (экономит 2 байта оперативки)
+// #define EB_NO_COUNTER       // отключить счётчик энкодера (экономит 4 байта оперативки)
+// #define EB_NO_BUFFER        // отключить буферизацию энкодера (экономит 1 байт оперативки)
+
+// #define EB_DEB_TIME 50      // таймаут гашения дребезга кнопки (кнопка)
+// #define EB_CLICK_TIME 500   // таймаут ожидания кликов (кнопка)
+// #define EB_HOLD_TIME 600    // таймаут удержания (кнопка)
+// #define EB_STEP_TIME 200    // таймаут импульсного удержания (кнопка)
+// #define EB_FAST_TIME 30     // таймаут быстрого поворота (энкодер)
+// #define EB_TOUT_TIME 1000   // таймаут действия (кнопка и энкодер)
+
+#include <EncButton.h>
+EncButton eb(2, 3, 4);
+//EncButton eb(2, 3, 4, INPUT); // + режим пинов энкодера
+//EncButton eb(2, 3, 4, INPUT, INPUT_PULLUP); // + режим пинов кнопки
+//EncButton eb(2, 3, 4, INPUT, INPUT_PULLUP, LOW);  // + уровень кнопки
+
+void setup() {
+    Serial.begin(115200);
+
+    // показаны значения по умолчанию
+    eb.setBtnLevel(LOW);
+    eb.setClickTimeout(500);
+    eb.setDebTimeout(50);
+    eb.setHoldTimeout(600);
+    eb.setStepTimeout(200);
+
+    eb.setEncReverse(0);
+    eb.setEncType(EB_STEP4_LOW);
+    eb.setFastTimeout(30);
+
+    // сбросить счётчик энкодера
+    eb.counter = 0;
+}
+
+void loop() {
+    eb.tick();
+
+    // обработка поворота общая
+    if (eb.turn()) {
+        Serial.print("turn: dir ");
+        Serial.print(eb.dir());
+        Serial.print(", fast ");
+        Serial.print(eb.fast());
+        Serial.print(", hold ");
+        Serial.print(eb.pressing());
+        Serial.print(", counter ");
+        Serial.print(eb.counter);
+        Serial.print(", clicks ");
+        Serial.println(eb.getClicks());
+    }
+
+    // обработка поворота раздельная
+    if (eb.left()) Serial.println("left");
+    if (eb.right()) Serial.println("right");
+    if (eb.leftH()) Serial.println("leftH");
+    if (eb.rightH()) Serial.println("rightH");
+
+    // кнопка
+    if (eb.press()) Serial.println("press");
+    if (eb.click()) Serial.println("click");
+
+    if (eb.release()) {
+      Serial.println("release");
+
+      Serial.print("clicks: ");
+      Serial.print(eb.getClicks());
+      Serial.print(", steps: ");
+      Serial.print(eb.getSteps());
+      Serial.print(", press for: ");
+      Serial.print(eb.pressFor());
+      Serial.print(", hold for: ");
+      Serial.print(eb.holdFor());
+      Serial.print(", step for: ");
+      Serial.println(eb.stepFor());
+    }
+
+    // состояния
+    // Serial.println(eb.pressing());
+    // Serial.println(eb.holding());
+    // Serial.println(eb.busy());
+    // Serial.println(eb.waiting());
+
+    // таймаут
+    if (eb.timeout()) Serial.println("timeout!");
+
+    // удержание
+    if (eb.hold()) Serial.println("hold");
+    if (eb.hold(3)) Serial.println("hold 3");
+
+    // импульсное удержание
+    if (eb.step()) Serial.println("step");
+    if (eb.step(3)) Serial.println("step 3");
+
+    // отпущена после импульсного удержания
+    if (eb.releaseStep()) Serial.println("release step");
+    if (eb.releaseStep(3)) Serial.println("release step 3");
+
+    // отпущена после удержания
+    if (eb.releaseHold()) Serial.println("release hold");
+    if (eb.releaseHold(2)) Serial.println("release hold 2");
+
+    // проверка на количество кликов
+    if (eb.hasClicks(3)) Serial.println("has 3 clicks");
+
+    // вывести количество кликов
+    if (eb.hasClicks()) {
+        Serial.print("has clicks: ");
+        Serial.println(eb.getClicks());
+    }
+}
+```
+</details>
+<details>
+<summary>Подключение обработчика</summary>
+
+```cpp
+#include <EncButton.h>
+EncButton eb(2, 3, 4);
+
+void callback() {
+    Serial.print("callback: ");
+    switch (eb.action()) {
+        case EB_PRESS:
+            Serial.println("press");
+            break;
+        case EB_HOLD:
+            Serial.println("hold");
+            break;
+        case EB_STEP:
+            Serial.println("step");
+            break;
+        case EB_RELEASE:
+            Serial.println("release");
+            break;
+        case EB_CLICK:
+            Serial.println("click");
+            break;
+        case EB_CLICKS:
+            Serial.print("clicks ");
+            Serial.println(eb.getClicks());
+            break;
+        case EB_TURN:
+            Serial.print("turn ");
+            Serial.print(eb.dir());
+            Serial.print(" ");
+            Serial.print(eb.fast());
+            Serial.print(" ");
+            Serial.println(eb.pressing());
+            break;
+        case EB_REL_HOLD:
+            Serial.println("release hold");
+            break;
+        case EB_REL_HOLD_C:
+            Serial.print("release hold clicks ");
+            Serial.println(eb.getClicks());
+            break;
+        case EB_REL_STEP:
+            Serial.println("release step");
+            break;
+        case EB_REL_STEP_C:
+            Serial.print("release step clicks ");
+            Serial.println(eb.getClicks());
+            break;
+    }
+}
+
+void setup() {
+    Serial.begin(115200);
+    eb.attach(callback);
+}
+
+void loop() {
+    eb.tick();
+}
+```
+</details>
+<details>
+<summary>Все типы кнопок</summary>
+
+```cpp
+#include <EncButton.h>
+
+Button btn(4);
+ButtonT<5> btnt;
+VirtButton btnv;
+
+void setup() {
+    Serial.begin(115200);
+}
+
+void loop() {
+    // Button
+    btn.tick();
+    if (btn.click()) Serial.println("btn click");
+
+    // ButtonT
+    btnt.tick();
+    if (btnt.click()) Serial.println("btnt click");
+
+    // VirtButton
+    btnv.tick(!digitalRead(4));  // передать логическое значение
+    if (btnv.click()) Serial.println("btnv click");
+}
+```
+</details>
+<details>
+<summary>Все типы энкодеров</summary>
+
+```cpp
+#include <EncButton.h>
+
+Encoder enc(2, 3);
+EncoderT<5, 6> enct;
+VirtEncoder encv;
+
+void setup() {
+    Serial.begin(115200);
+}
+
+void loop() {
+    // опрос одинаковый для всех, 3 способа:
+
+    // 1
+    // tick вернёт 1 или -1, значит это шаг
+    if (enc.tick()) Serial.println(enc.counter);
+
+    // 2
+    // можно опросить через turn()
+    enct.tick();
+    if (enct.turn()) Serial.println(enct.dir());
+
+    // 3
+    // можно не использовать опросные функции, а получить направление напрямую
+    int8_t v = encv.tick(digitalRead(2), digitalRead(3));
+    if (v) Serial.println(v);  // выведет 1 или -1
+}
+```
+</details>
+
+<a id="versions"></a>
+## Версии
+<details>
+<summary>Старые</summary>
+
+- v1.1 - пуллап отдельныи методом
+- v1.2 - можно передать конструктору параметр INPUT_PULLUP / INPUT(умолч)
+- v1.3 - виртуальное зажатие кнопки энкодера вынесено в отдельную функцию + мелкие улучшения
+- v1.4 - обработка нажатия и отпускания кнопки
+- v1.5 - добавлен виртуальный режим
+- v1.6 - оптимизация работы в прерывании
+- v1.6.1 - подтяжка по умолчанию INPUT_PULLUP
+- v1.7 - большая оптимизация памяти, переделан FastIO
+- v1.8 - индивидуальная настройка таймаута удержания кнопки (была общая на всех)
+- v1.8.1 - убран FastIO
+- v1.9 - добавлена отдельная отработка нажатого поворота и запрос направления
+- v1.10 - улучшил обработку released, облегчил вес в режиме callback и исправил баги
+- v1.11 - ещё больше всякой оптимизации + настройка уровня кнопки
+- v1.11.1 - совместимость Digispark
+- v1.12 - добавил более точный алгоритм энкодера EB_BETTER_ENC
+- v1.13 - добавлен экспериментальный EncButton2
+- v1.14 - добавлена releaseStep(). Отпускание кнопки внесено в дебаунс
+- v1.15 - добавлен setPins() для EncButton2
+- v1.16 - добавлен режим EB_HALFSTEP_ENC для полушаговых энкодеров
+- v1.17 - добавлен step с предварительными кликами
+- v1.18 - не считаем клики после активации step. hold() и held() тоже могут принимать предварительные клики. Переделан и улучшен дебаунс
+- v1.18.1 - исправлена ошибка в releaseStep() (не возвращала результат)
+- v1.18.2 - fix compiler warnings
+- v1.19 - оптимизация скорости, уменьшен вес в sram
+- v1.19.1 - ещё чутка увеличена производительность
+- v1.19.2 - ещё немного увеличена производительность, спасибо XRay3D
+- v1.19.3 - сделал высокий уровень кнопки по умолчанию в виртуальном режиме
+- v1.19.4 - фикс EncButton2
+- v1.20 - исправлена критическая ошибка в EncButton2
+- v1.21 - EB_HALFSTEP_ENC теперь работает для обычного режима
+- v1.22 - улучшен EB_HALFSTEP_ENC для обычного режима
+- v1.23 - getDir() заменил на dir()
+- v2.0 
+    - Алгоритм EB_BETTER_ENC оптимизирован и установлен по умолчанию, дефайн EB_BETTER_ENC упразднён
+    - Добавлен setEncType() для настройки типа энкодера из программы, дефайн EB_HALFSTEP_ENC упразднён
+    - Добавлен setEncReverse() для смены направления энкодера из программы
+    - Добавлен setStepTimeout() для установки периода импульсного удержания, дефайн EB_STEP упразднён
+    - Мелкие улучшения и оптимизация
+</details>
+
+- v3.0
+  - Библиотека переписана с нуля, с предыдущими версиями несовместима!
+    - Полностью другая инициализация объекта
+    - Переименованы: hold()->holding(), held()->hold()
+  - Оптимизация Flash памяти: библиотека весит меньше, в некоторых сценариях - на несколько килобайт
+  - Оптимизация скорости выполнения кода, в том числе в прерывании
+  - На несколько байт меньше оперативной памяти, несколько уровней оптимизации на выбор
+  - Более простое, понятное и удобное использование
+  - Более читаемый исходный код
+  - Разбитие на классы для использования в разных сценариях
+  - Новые функции, возможности и обработчики для кнопки и энкодера
+  - Буферизация энкодера в прерывании
+  - Нативная обработка двух одновременно нажимаемых кнопок как третьей кнопки
+  - Поддержка 4-х типов энкодеров
+  - Переписана документация
+  - EncButton теперь заменяет GyverLibs/VirtualButton (архивирована)
+- v3.1
+  - Расширена инициализация кнопки
+  - Убраны holdEncButton() и toggleEncButton()
+  - Добавлен turnH()
+  - Оптимизированы прерывания энкодера, добавлена setEncISR()
+  - Буферизация направления и быстрого поворота
+  - Сильно оптимизирована скорость работы action() (общий обработчик)
+  - Добавлено подключение внешней функции-обработчика событий
+  - Добавлена обработка кнопки в прерывании - pressISR()
+- v3.2
+  - Добавлены функции tickRaw() и clear() для всех классов. Позволяет проводить раздельную обработку (см. доку)
+  - Улучшена обработка кнопки с использованием прерываний
+- v3.3
+  - Добавлены функции получения времени удержания pressFor(), holdFor(), stepFor() (отключаемые)
+  - Добавлен счётчик степов getSteps() (отключаемый)
+- v3.4
+  - Доступ к счётчику кликов во время нажатого поворота
+  - Добавлена функция detach()
+- v3.5 
+  - Добавлена зависимость GyverIO (ускорен опрос пинов)
+  - Добавлена возможность задать свои функции аптайма и чтения пина
+- v3.5.2 
+  - Оптимизация
+  - Упрощена замена кастомных функций
+  - Исправлена ошибка компиляции при использовании библиотеки в нескольких .cpp файлах
+- v3.5.3
+  - Добавлено количество кликов в опрос press/release/click/pressing
+- v3.5.5 - коллбэк на базе std::function для ESP
+- v3.5.8 - добавлен метод releaseHoldStep()
+- v3.5.11 - добавлен метод skipEvents() для игнорирования событий кнопки в сложных сценариях использования
+- v3.6.0 
+  - Добавлен класс MultiButton для корректного опроса нескольких кнопок с вызовом обработчика
+  - Добавлено подключение обработчика с передачей указателя на объект
+
+<a id="feedback"></a>
+## Баги и обратная связь
+При нахождении багов создавайте **Issue**, а лучше сразу пишите на почту [alex@alexgyver.ru](mailto:alex@alexgyver.ru)  
+Библиотека открыта для доработки и ваших **Pull Request**'ов!
+
+При сообщении о багах или некорректной работе библиотеки нужно обязательно указывать:
+- Версия библиотеки
+- Какой используется МК
+- Версия SDK (для ESP)
+- Версия Arduino IDE
+- Корректно ли работают ли встроенные примеры, в которых используются функции и конструкции, приводящие к багу в вашем коде
+- Какой код загружался, какая работа от него ожидалась и как он работает в реальности
+- В идеале приложить минимальный код, в котором наблюдается баг. Не полотно из тысячи строк, а минимальный код

+ 1776 - 0
.pio/libdeps/pico/EncButton/README_EN.md

@@ -0,0 +1,1776 @@
+This is an automatic translation, may be incorrect in some places. See sources and examples!
+
+# ENCBUTTON
+
+|⚠️⚠️⚠️ <br> ** The new version of V3 is incompatible with the previous ones, see [documentation] (#docs), [examples] (# Example) and brief [migration guide] (#migrate) from v2 to v3! ** <*** <BR> ⚠️⚠️⚠️ |
+| ----------------------------------------------------------------------------------------------------------------------------------------------------- |
+
+A light and very functional library for an encoder with a button, encoder or buttons with Arduino
+- Button
+    - processing of events: pressing, releasing, click, click counter, retention, impulse retention, deduction time + preliminary clicks for all modes
+    - Program suppression of rubbish
+    - support for processing two simultaneously pressed buttons as a third button
+- Encoder
+    - Processing of events: a normal turn, pressed turn, fast turn
+    - Support of four types of incidental encoders
+    - high -precision algorithm for determining the position
+    - buffer in interruption
+- simple and understandable use
+- a huge number of opportunities and their combinations for different scenarios for using even one button
+- virtual regime (for example, for working with a pain expander)
+- optimized to work in interruption
+- The fastest reading of pins for AVR, ESP8266, ESP32 (used by gyverio)
+- Fast asynchronous algorithms of survey of actions from the button and encoder
+- rigid optimization and low weight in Flash and SRAM memory: 5 bytes SRAM (on an instance) and ~ 350 bytes Flash to process the button
+
+Examples of use scenarios:
+- Several clicks - inclusion of the regime (according to the number of clicks)
+- Several clicks + short retention - another option for turning on the mode (according to the number of clicks)
+- several clicks + holding - a gradual change in the value of the selected variable (on the number of clicks)
+- Several clicks choose a variable, encoder changes it
+- changing the step of changes in the variable during the rotation of the encoder - for example, a decrease with a closed button and an increase with rapid rotation
+- navigation by menu during the rotation of the encoder, a change in the variable during the rotation of a clamped encoder
+- full -fledged navigation by the menu when using two buttons (simultaneous retention to go to the next level, simultaneous pressing for return to the previous one)
+- And so on
+
+## compatibility
+Compatible with all arduino platforms (used arduino functions)
+
+## Content
+- [installation] (# Install)
+- [Information] (# Info)
+- [documentation] (#docs)
+  - [compilation settings] (#config)
+  - [full description of classes] (#class)
+  - [processing and survey] (#tick)
+  - [preliminary clicks] (# PRECLICS)
+  - [direct reading of the button] (#btnread)
+  - [immersion in the cycle] (#loop)
+  - [Timeout] (# Timeout)
+  - [Busy] (# Busy)
+  - [receipt of an event]
+  - [Optimization] (# Optimise)
+  - [Collback] (#callback)
+  - [Simultaneous pressing] (# Double)
+  - [interruption] (# ISR)
+  - [array of buttons/encoders] (# Array)
+  - [custom functions] (# Custom)
+  - [Timer survey] (# Timer)
+  - [Mini examples, scenarios] (# Examples-Mini)
+- [migration with v2] (#migrate)
+- [Examples] (# Example)
+- [versions] (#varsions)
+- [bugs and feedback] (#fedback)
+
+<a id="install"> </a>
+## Installation
+- For work, a library is required [gyverio] (https://github.com/gyverlibs/gyverio)
+-Library can be found by the name ** encbutton ** and installed through the library manager in:
+    - Arduino ide
+    - Arduino ide v2
+    - Platformio
+- [download the library] (https://github.com/gyverlibs/encbuton/archive/refs/heads/main.zip) .Zip archive for manual installation:
+    - unpack and put in * C: \ Program Files (X86) \ Arduino \ Libraries * (Windows X64)
+    - unpack and put in * C: \ Program Files \ Arduino \ Libraries * (Windows X32)
+    - unpack and put in *documents/arduino/libraries/ *
+    - (Arduino id) Automatic installation from. Zip: * sketch/connect the library/add .Zip library ... * and specify downloaded archive
+- Read more detailed instructions for installing libraries [here] (https://alexgyver.ru/arduino-first/#%D0%A3%D1%81%D1%82%D0%B0%BD%D0%BE%BE%BE%BED0%B2%D0%BA%D0%B0_%D0%B1%D0%B8%D0%B1%D0%BB%D0%B8%D0%BE%D1%82%D0%B5%D0%BA)
+### Update
+- I recommend always updating the library: errors and bugs are corrected in the new versions, as well as optimization and new features are added
+- through the IDE library manager: find the library how to install and click "update"
+- Manually: ** remove the folder with the old version **, and then put a new one in its place.“Replacement” cannot be done: sometimes in new versions, files that remain when replacing are deleted and can lead to errors!
+
+<a id="info"> </a>
+
+## Information
+## encoder
+#### type of encoder
+The library supports all 4 types of * incidental * encoders, the type can be configured using `setenctype (type)`:
+- `eb_step4_Low` - active low signal (tightening to VCC).Full period (4 phases) per click.*Set by default*
+- `eb_step4_high` - an active high signal (tightening to GND).Full period (4 phases) for one click
+- `eb_step2` - half of the period (2 phases) per click
+- `eb_step1` - a quarter of the period (1 phase) per click, as well as encoders without fixation
+
+! [Diagram] (/doc/enc_type.png)
+
+#### Recommendations
+To work according to the Encoder with the button, I recommend these ([link] (https://ali.ski/cmpi2), [link] (https://ali.ski/szbtk)) Round Chinese modules with broken anti -ship chains(have the type `eb_step4_low` according to the classification above):
+! [Scheme] (/doc/encali.png)
+
+You can tie an encoder yourself according to the following scheme (RC filters to the encoder channels + tightening of all pens to VCC):
+! [Scheme] (/doc/enc_scheme.png)
+
+> Note: by default in the library of Pino Encoder, you are configured by `Input` with the calculation of an external luster.If you have an encoder without lifting, you can use the internal `Input_pullup`, indicating this in the initialization of the encoder (see the documentation below).
+
+### Button
+### The level of the button
+The button can be connected to the microcontroller in two ways and give a high or low signal when pressed.The library provides for setting up `setbtnlevel (level)`, where the level is an active button signal:
+- `High` - the button connects the VCC.Installed by default in `virt`-bibliotexes
+- `Low` - the button connects GND.Set by default in the main libraries
+
+! [Scheme] (/doc/btn_scheme.png)
+
+#### Pin
+In diagrams with microcontrollers, the connection of the GND button with a PIN suspension to VCC is most often used.The tightening can be external (Pin mode must be put `Input`) or internal (PIN mode` Input_pullup`).In "real" projects, an external lifelong is recommended, becauseIt is less susceptible to interference - the internal has too high resistance.
+
+<a id="docs"> </a>
+
+## Documentation
+
+<a id="config"> </a>
+
+### Defaine settings
+Be up to the library
+
+`` `CPP
+
+// Disable PressFor/Holdfor/StepFor support and Stepov counter (saves 2 bytes of RAM)
+#define eb_no_for
+
+// Disable the event processor attach (saves 2 bytes of RAM)
+#define eb_no_callback
+
+// Disable the encoder counter [Virtencoder, Encoder, Encbutton] (saves 4 bytes of RAM)
+#define eb_no_counter
+
+// Disconnect the buffer of the encoder (saves 2 bytes of RAM)
+#define eb_no_buffer
+
+/*
+  Setting up timeouts for all classes
+  - replaces the timauts constants, changeCranberries from the program (setxxxtimeout ()) will not be
+  - Setting affects all buttons announced in the program/Encoders
+  - saves 1 bytes of RAM for an object for each timeout
+  - shows the default values in MS
+  - values are not limited to 4000MS, as when installing from the program (SetXXXTimeout ())
+*/
+#define eb_deb_time 50 // Timesout to extinguish the trim button (button)
+#define eb_click_time 500 // Click Stayout (button)
+#define eb_hold_time 600 // Maintenance Times (button)
+#define eb_step_time 200 // Impulse retention Timesout (button)
+#define EB_FAST_TIME 30 // TIMAUT RAM (ENCODER)
+`` `
+
+<a id="class"> </a>
+
+### classes
+How to work with the documentation: Encbutton starting with version 3.0 is several libraries (classes) for various use scenarios, they inherit each other to expand functionality.Thus, the library is a “onion”, each layer of which has access to the functions of the lower layers:
+- Basic classes:
+  - `virtbutton` - the base class of the virtual button, provides all the possibilities of the buttons
+  - `virtencoder` - the base class of the virtual encoder, determines the fact and direction of the rotation of the enkoder
+  - `virtencbutton` - the base class of a virtual encoder with a button, provides an encoder poll taking into account the button, *inherits Virtbututton and Virtencoder *
+- Main classes:
+  - `Button`,` buttont` - button class, *inherits virtbutton *
+  - `Encoder`,` Encodert` - Encoder class, *inherits virtencoder *
+  - `ENCBUTTON`,` ENCBUTTONT` - ENCODER class with a button, *inherits VirtenCbutton, Virtbutton, Virtencoder *
+
+Thus, to study all the available functions of a particular library, you need to watch not only it, but also what it inherits.For example, to process the button using `Button`, you need to open below the description of` button` and `virtbutton`.
+
+> * Virtual * - without specifying a PIN of the microcontroller, works directly with the transmitted value, for example, for a survey of the enemies buttons through pain extensors and shift registers.
+
+> `T'- version of libraries require indicating Pino constants (numbers).Pino numbers will be stored in the memory of the program, this accelerates the work and makes the code easier by 1 byte for each pin.
+
+> Note: `# Include <encbutton.h>` connects all the library tools!
+
+<details>
+<summary> Table of Functions of the button </ Summary>
+
+||Virtbutton |Virtencbutton |Button |Encbutton |
+| ---------------- |: -------------------------------- |: ----------------: |:--------: |: ---------: |
+|Read |||✔ ||
+|Readbtn ||||✔ |
+|TickRaW |✔ |✔ |✔ |✔ |
+|SetHoldtimeout |✔ |✔ |✔ |✔ |
+|Setsteptimeout |✔ |✔ |✔ |✔ |
+|SetClicktimeout |✔ |✔ |✔ |✔ |
+|Setdebtimeout |✔ |✔ |✔ |✔ |
+|Setbtnlevel |✔ |✔ |✔ |✔ |
+|Pressisr |✔ |✔ |✔ |✔ |
+|Reset |✔ |✔ |✔ |✔ |
+|Clear |✔ |✔ |✔ |✔ |
+|Attach |✔ |✔ |✔ |✔ |
+|Detach |✔ |✔ |✔ |✔ |
+|Press |✔ |✔ |✔ |✔ |
+|Release |✔ |✔ |✔ |✔ |
+|Click |✔ |✔ |✔ |✔ |
+|Pressing |✔ |✔ |✔ |✔ |
+|Hold |✔ |✔ |✔ |✔ |
+|Holding |✔ |✔ |✔ |✔ |
+|STEP |✔ |✔ |✔ |✔ |
+|Hasclicks |✔ |✔ |✔ |✔ |
+|GetClicks |✔ |✔ |✔ |✔ |
+|Getsteps |✔ |✔ |✔ |✔ |
+|Releasehold |✔ |✔ |✔ |✔ |
+|ReleaseStep |✔ |✔ |✔ |✔ |
+|Waiting |✔ |✔ |✔ |✔ |
+|Busy |✔ |✔ |✔ |✔ |
+|Action |✔ |✔ |✔ |✔ |
+|Timeout |✔ |✔ |✔ |✔ |
+|Pressfor |✔ |✔ |✔ |✔ |
+|Holdfor |✔ |✔ |✔ |✔ |
+|STEPFOR |✔ |✔ |✔ |✔ |
+</details>
+
+<details>
+<summary> Encoder functions table </ Summary>
+
+||Virtencoder |Encoder |Virtencbutton |Encbutton |
+| -------------- |: ----------------------------- |: -------: |: -----------------------------: |: ---------: |
+|Readenc ||||✔ |
+|Initenc |✔ |✔ |✔ |✔ |
+|Setencreverse |✔ |✔ |✔ |✔ |
+|Setenctype |✔ |✔ |✔ |✔ |
+|Setencisr |✔ |✔ |✔ |✔ |
+|Clear |✔ |✔ |✔ |✔ |
+|Turn |✔ |✔ |✔ |✔ |
+|Dir |✔ |✔ |✔ |✔ |
+|TickRaW |✔ |✔ |✔ |✔ |
+|Pollenc |✔ |✔ |✔ |✔ |
+|Counter |✔ |✔ |✔ |✔ |
+|SetFasttimeout |||✔ |✔ |
+|Turnh |||✔ |✔ |
+|Fast |||✔ |✔ |
+|Right |||✔ |✔ |
+|Left |||✔ |✔ |
+|Righth |||✔ |✔ |
+|Lefth |||✔ |✔ |
+|Action |||✔ |✔ |
+|Timeout |||✔ |✔ |
+|Attach |||✔ |✔ |
+|Detach |||✔ |✔ |
+</details>
+
+<details>
+<summary> virtbutton </summary>
+
+`` `CPP
+// ==ward
+// Set the deduction timeout, silence.600 (max. 4000 ms)
+VOID SetHoldtimeout (Uint16_T Tout);
+
+// Install the timout of impulse retention, silence.200 (max. 4000 ms)
+VOID Setsteptimeout (Uint16_T Tout);
+
+// Install the expectations of clicks, silence.500 (max. 4000 ms)
+VOID setClicktimeout (Uint16_T Tout);
+
+// Install the Timout of the Anti -Direct, silence.50 (Max. 255 ms)
+VOID Setdebtimeout (Uint8_t Tout);
+
+// set the level of the button (HIGH - button closes VCC, Low - closes GND)
+// silent.High, that is, True - the button is pressed
+VOID setbtnlevel (Bool LEVEL);
+
+// Connect the function-processor of events (type VOID F ())
+VOID attach (VOID (*handler) ());
+
+// Disconnect the event-handle function
+VOID Detach ();
+
+// ==============ward
+// throw off system flags (forcibly finish processing)
+VOID Reset ();
+
+// forcibly drop the flags of events
+Void Clear ();
+
+// ====ward
+// button processing with value
+Bool Tick (Bool S);
+
+// Processing of the virtual button as simultaneous pressing two other buttons
+Bool Tick (Virtbutton & B0, Virtbutton & B1);
+
+// button pressed in the interruption of the button
+VOID Pressisr ();
+
+// Processing the button without reseting events and calling collobes
+Bool Tickrad (Bool S);
+
+// ================== Poll ================================ward
+// Pressure button [event]
+Bool Press ();
+Bool Press (Uint8_T Clicks);
+
+// button released (in any case) [evente]
+Bool Release ();
+Bool Release (Uint8_T Clicks);
+
+// click on the button (released without holding) [event]
+Bool click ();
+Bool Click (Uint8_T Clicks);
+
+// Squeezed button (between Press () and Release ()) [condition]
+Bool Pressing ();
+Bool Pressing (Uint8_T Clicks);
+
+// The button was withheld (more timeout) [event]
+Bool Hold ();
+Bool Hold (Uint8_T Clicks);
+
+// The button is held (more timeout) [condition]
+Bool Holding ();
+Bool Holding (Uint8_T Clicks);
+
+// Impulse retention [event]
+Bool Step ();
+Bool Step (Uint8_T Clicks);
+
+// Several clicks were recorded [event]
+Bool HasClicks ();
+Bool HasClicks (Uint8_T Clicks);
+
+// button released after holding [Event]
+Bool ReleaseHold ();
+Bool ReleaseHold (Uint8_T Clicks);
+
+// Button is released after impulse retention [Event]
+Bool ReleaseStep ();
+Bool ReleaseStep (Uint8_T Clicks);
+
+// get the number of clicks
+uint8_t getclicks ();
+
+// Get the number of steps
+uint16_t getsteps ();
+
+// button awaits repeated clicks (between Click () and HasClicks ()) [condition]
+Bool Waiting ();
+
+// processing (between the first press and after waiting for clicks) [condition]
+Bool Busy ();
+
+// there was an action from the button, the event code [event] will return
+Uint16_T Action ();
+
+// ==========================================================================
+// After interacting with the button (or enkoder Encbutton), the specified time has passed, ms [event]
+Bool Timeout (Uint16_T MS);
+
+// The time that the button is held (from the beginning of the press), ms
+uint16_t Pressfor ();
+
+// The button is held longer than (from the beginning of pressing), ms [condition]
+Bool Pressfor (Uint16_T MS);
+
+// The time that the button is held (from the beginning of retention), ms
+uint16_t holdfor ();
+
+// The button is held longer than (from the beginning of retention), ms [condition]
+Bool Holdfor (Uint16_T MS);
+
+// The time that the button is held (from the beginning of the step), ms
+uint16_t stepfor ();
+
+// The button is held longer than (from the beginning of the step), ms [condition]
+Bool StepFor (Uint16_T MS);
+`` `
+</details>
+<details>
+<summary> virtencoder </summary>
+
+`` `CPP
+// ===============================================
+// Invert the direction of the encoder (silence 0)
+VOID Setencreverse (Bool Rev);
+
+// Install the type of encoder (eb_step4_low, eb_step4_high, eb_step2, eb_step1)
+VOID Setenctype (Uint8_T Type);
+
+// use encoder processing in interruption
+VOID Setencisr (Bool Use);
+
+// Initialization of the encoder
+VOID Initenc (Bool E0, Bool E1);
+
+// Encoder initialization with a combined value
+VOID Initenc (int8_t v);
+
+// throw off the flags of events
+Void Clear ();
+
+// ===================== Support ==========================
+// there was a turn [event]
+Bool Turn ();
+
+// Direction of the encoder (1 or -1) [condition]
+int8_t die ();
+
+// counter
+Int32_T Counter;
+
+// ====ward
+// Interrogate the encoder in interruption.Will return 1 or -1 during rotation, 0 when stopping
+Int8_t Tickisr (Bool E0, Bool E1);
+Int8_T Tickisr (int8_t state);
+
+// Introduce the Encoder.Will return 1 or -1 during rotation, 0 when stopping
+Int8_T Tick (Bool E0, Bool E1);
+int8_t tick (int8_t state);
+int8_t tick ();// The processing itself in interruption
+
+// Introduce the Encoder without resetting the turning event.Will return 1 or -1 during rotation, 0 when stopping
+Int8_T TickRaW (Bool E0, Bool E1);
+Int8_T TickRaW (int8_t state);
+Int8_T TickRaW ();// The processing itself in interruption
+
+// Introduce the encoder without installing flags on a turn (faster).Will return 1 or -1 during rotation, 0 when stopping
+Int8_t Pollenc (Bool E0, Bool E1);
+Int8_t Pollenc (Int8_T State);
+`` `
+</details>
+<details>
+<summary> virtencbutton </summary>
+
+- Available functions from `virtbutton`
+- Available functions from `virtencoder`
+
+`` `CPP
+// ============ward
+// Install a timeout of fast turning, ms
+VOID setfasttimeout (Uint8_t Tout);
+
+// throw the flags of encoder and buttons
+Void Clear ();
+
+// =================== Poll =================================
+// Any turn of the encoder [event]
+Bool Turn ();
+
+// pressed turn of enkoder [event]
+Bool Turnh ();
+
+// Fast turning of the encoder [condition]
+Bool Fast ();
+
+// Open Turn to the right [event]
+Bool Right ();
+
+// Non -pressed turnfrom left [event]
+Bool Left ();
+
+// pressed turn to the right [event]
+Bool Righth ();
+
+// pressed turn to the left [event]
+Bool Lefth ();
+
+// there was an action from a button or encoder, will return the event code [event]
+Uint16_T Action ();
+
+// ====ward
+// processing in interruption (only encoder).Will return 0 at rest, 1 or -1 when turning
+Int8_t Tickisr (Bool E0, Bool E1);
+Int8_T Tickisr (int8_t e01);
+
+// processing of encoder and buttons
+Bool Tick (Bool E0, Bool E1, Bool BTN);
+Bool Tick (Int8_t E01, Bool BTN);
+Bool Tick (Bool BTN);// Encoder in interruption
+
+// Processing the encoder and buttons without discharge flags and calling collobes
+Bool Tickrad (Bool E0, Bool E1, Bool BTN);
+Bool Tickrade (Int8_t E01, Bool BTN);
+Bool Tickrade (Bool BTN);// Encoder in interruption
+`` `
+</details>
+<details>
+<summary> Button </summary>
+
+- Available functions from `virtbutton`
+- default buttons mode - `Low`
+
+`` `CPP
+Button;
+Button (uint8_t pin);// indicating Pin
+Button (uint8_t npin, uint8_t mode);// + mode of operation (silence input_pullup)
+Button (uint8_t npin, uint8_t mode, uint8_t btnlevel);// + button level (silence)
+`` `
+`` `CPP
+// indicate the pin and its operating mode
+VOID Init (uint8_t npin, uint8_t mode);
+
+// Read the current value of the button (without debate) taking into account Setbtnlevel
+Bool Read ();
+
+// processing function, call in loop
+Bool Tick ();
+
+// Processing the button without reseting events and calling collobes
+Bool Tickrade ();
+`` `
+</details>
+<details>
+<summary> buttont </summary>
+
+- Available functions from `virtbutton`
+- default buttons mode - `Low`
+
+`` `CPP
+Buttont <uint8_t pin>;// indicating Pin
+Buttont <uint8_t pin> (uint8_t mode);// + mode of operation (silence input_pullup)
+Buttont <uint8_t pin> (uint8_t mode, uint8_t btnlevel);// + button level (silence)
+`` `
+`` `CPP
+// specify the operating mode
+VOID Init (Uint8_t Mode);
+
+// Read the current value of the button (without debate) taking into account Setbtnlevel
+Bool Read ();
+
+// processing function, call in loop
+Bool Tick ();
+`` `
+</details>
+<details>
+<summary> encoder </summary>
+
+- Available functions from `virtencoder`
+
+`` `CPP
+Encoder;
+Encoder (Uint8_t Enca, Uint8_T ENCB);// indicating Pinov
+Encoder (Uint8_t Enca, Uint8_t Encb, Uint8_t Mode);// + mode of operation (silence. Input)
+`` `
+`` `CPP
+// Indicate pins and their operating mode
+VOID Init (Uint8_t Enca, Uint8_t Encb, Uint8_t Mode);
+
+// Function of processing for calling in an interruption of encoder
+int8_t tickisr ();
+
+// Function of processing for calling in LOOP
+int8_t tick ();
+`` `
+</details>
+<details>
+<summary> encodert </summary>
+
+- Available functions from `virtencoder`
+
+`` `CPP
+Encodert <uint8_t enca, uint8_t encb>;// indicating Pinov
+Encodert <uint8_t enca, uint8_t encb> (Uint8_t Mode);// + mode of operation (silence. Input)
+`` `
+`` `CPP
+// specify the mode of operation of Pinov
+VOID Init (Uint8_t Mode);
+
+// Function of processing for calling in an interruption of encoder
+int8_t tickisr ();
+
+// Function of processing for calling in LOOP
+int8_t tick ();
+`` `
+</details>
+<details>
+<summary> encbutton </summary>
+
+- Available functions from `virtbutton`
+- Available functions from `virtencoder`
+- Available functions from `virtencbutton`
+
+`` `CPP
+ENCBUTTON;
+
+// Set the Pins (ENK, ENK, button)
+ENCBUTTON (UINT8_T ENCA, UINT8_T ENCB, UINT8_T BTN);
+
+// Reference Pins (ENK, ENK, button, Pinmode ENK, Pinmode button, button level)
+ENCBUTTON (UINT8_T ENCA, UINT8_T ENCB, UINT8_T BTN, UINT8_T MODEENC = Input, Uint8_t Modebtn = Input_Pullup, Uint8_T BTNLEVEL = LOW);
+`` `
+`` `CPP
+// Reference Pins (ENK, ENK, button, Pinmode ENK, Pinmode button, button level)
+VOID Init (Uint8_t Enca, Uint8_t Encb, Uint8_t BTN, UINT8_T MODEENC = Input, Uint8_t Modebtn = Input_Pullup, Uint8_T BTNLEVEL = LOW);
+
+// Function of processing for calling in an interruption of encoder
+int8_t tickisr ();
+
+// processing function, call in loop
+Bool Tick ();
+
+// Read the value of the button taking into account Setbtnlevel
+Bool Readbtn ();
+
+// Read the value of the encoder
+Int8_T Readenc ();
+`` `
+</details>
+<details>
+<summary> encbuttont </summary>
+
+- Available functions from `virtbutton`
+- Available functions from `viRencoder`
+- Available functions from `virtencbutton`
+
+`` `CPP
+// indicating Pinov
+ENCBUTTONT <uint8_T ENCA, UINT8_T ENCB, UINT8_T BTN>;
+
+// + Pino operation mode, button level
+ENCBUTTONT <uINT8_T ENCA, UINT8_T ENCB, UINT8_T BTN> (Uint8_t Modeenc = Input, Uint8_t Modebtn = Input_Pullup, Uint8_T BTNlevel = Low);
+`` `
+`` `CPP
+// Configure Pino operation mode, button level
+VOID Init (Uint8_t Modeenc = Input, Uint8_t Modebtn = Input_pullup, Uint8_t Btnlevel = Low);
+
+// Function of processing for calling in an interruption of encoder
+int8_t tickisr ();
+
+// processing function, call in loop
+Bool Tick ();
+
+// Read the button value
+Bool Readbtn ();
+
+// Read the value of the encoder
+Int8_T Readenc ();
+`` `
+</details>
+
+<a id="tick"> </a>
+
+### Processing and Poll
+All libraries have a general ** function of processing ** (ticker `tick`), which receives the current signal from the button and encoder
+- this function must be caused once in the main cycle of the program (for virtual - with the transmission of the meaning)
+- the function returns `true` when the event occurs (for encoder -` 1` or `-1` when turning,` 0` in its absence. Thus, the turn in any direction is regarded as `true`)
+- There are separate functions for calling in interruption, they have a suffix `isr`, see documentation below
+
+The library processes the signal inside this function, the result can be obtained from ** functions of the survey ** events.They are of two types:
+- `[event]` - the function will return `true` once upon the event of an event.It will be reset after the next call call (for example, click, turning enncoder)
+- `[condition]` - the function returns `true`, while this condition is actively (for example, the button is held)
+
+For simplicity of perception, the processing function must be placed at the beginning of the cycle, and polls do below:
+`` `CPP
+VOID loop () {
+  btn.tick ();// survey
+
+  if (btn.click ()) serial.println ("click");// will display once when clicking
+  if (btn.click ()) serial.println ("click");// The same click!
+}
+`` `
+> Unlike previous versions of the library, the survey functions are not reset inside themselves, but *inside the processing function *.Thus, in the example above, when clicking on the button in the port, the message `click ()` is displayed twice.This allows you to use the survey functions several times for the current iteration of the cycle to create a complex logic of the program.
+
+#### several functions of processing
+For obvious reasons, it is impossible to cause the processing function more than once per cycle - each next call will drop events from the previous one and the code will work incorrectly.So - you can’t:
+`` `CPP
+// you can not do it this way
+VOID loop () {
+  btn.tick ();
+  if (btn.click ()) ...
+
+  // ....
+
+  btn.tick ();
+  if (btn.hold ()) ...
+}
+`` `
+
+If you really need to get into a deaf cycle and interrogate the button there, then it can: you can:
+`` `CPP
+// so it is possible
+VOID loop () {
+  btn.tick ();
+  if (btn.click ()) ...
+
+  While (True) {
+    btn.tick ();
+    if (btn.hold ()) ...
+    if (btn.click ()) Break;
+  }
+}
+`` `
+
+If the library is used with an connected event handler `Attach ()` (see below), then you can call `tick ()` anywhere and as many times as you like, the events will be processed in the handler:
+`` `CPP
+// so it is possible
+VOID CB () {
+  switch (btn.action ()) {
+    // ...
+  }
+}
+
+VOID setup () {
+  btn.attach (CB);
+}
+
+VOID loop () {
+  btn.tick ();
+  // ...
+  btn.tick ();
+  // ...
+  btn.tick ();
+}
+`` `
+
+### "loaded" program
+The Encbutton - ** asynchronous ** library: it does not wait until the button is completed, and allows the program to be executed further.This means that for the correct operation of the library, the main cycle of the program should be performed as quickly as possible and not contain delays and other "deaf" cycles within itself.To ensure proper processing of the button, it is not recommended to have a main delay cycle lasting more than 50-100 ms.A few tips:
+-beginners: to study the lesson cycle [how to write a sketch] (https://alexgyver.ru/lessns/how-to-sketch/)
+  - write asynchronous code in `loop ()`
+  - Any synchronous structure on `delay ()` can be made asynchronous using `millis ()` `
+  - if in the program * each * Iteration gThe cranberries of the bay cycle are performed longer than 50-100ms-in most cases the program is written incorrectly, with the exception of some special cases
+- connect the button to the hardware interruption (see below)
+- avoid the execution of "heavy" sections of the code while the button is processing, for example, by placing them in the condition `If (! Button.busy ()) {heavy code}`} `
+- If it is impossible to optimize the main cycle - call the ticker in another "stream" and use the processor:
+  - in interruption of a timer with a period of ~ 50ms or more often
+  - on another core (for example, ESP32)
+  - In another Task Freertos
+  - inside `yield ()` (inside `delay ()`)
+
+#### Separate processing
+> It makes sense only with a manual survey of events!With a connected processing function, it is enough to call the usual `tick ()` between the heavy sections of the program
+
+Also, in a loaded program, you can divide the processing and reset of events: instead of `tick ()` use `tickRAW ()` between heavy sections of the code and manual reset `Clear ()`.The order is as follows:
+- Surrender actions (Click, Press, Turn ...)
+- Call `Clear ()`
+- call `tickRaW ()` between heavy sections of code
+
+`` `CPP
+VOID loop () {
+  if (btn.click ()) ...
+  if (btn.press ()) ...
+  if (btn.step ()) ...
+
+  btn.clear ();
+
+  // ...
+  BTN.TickRAW ();
+  // ...
+  BTN.TickRAW ();
+  // ...
+  BTN.TickRAW ();
+  // ...
+}
+`` `
+This will allow to interview the button/encoder in a not very well written program, where the main cycle is littered with heavy code.Inside the `Tickrade ()` Events accumulate that are dismantled once in the cycle, and then manually reset.
+
+> In this scenario, the Encoder's buffering in the interruption does not work and all events are not processed `Releasexxx`
+
+#### Processing inside Delay
+If it is difficult to get rid of the `delay ()` inside the main cycle of the program, then on some platforms you can place your code inside it.Thus, you can even get encoder processing in a cycle with deals without using interruptions:
+`` `CPP
+// Code insertion in Delay
+VOID yield () {
+  EB.TickRAW ();
+}
+
+VOID loop () {
+  if.click ()) ...
+  if (btn.turn ()) ...
+
+  eb.clear ();
+
+  // ...
+  Delay (10);
+  // ...
+  DELAY (50);
+  // ...
+}
+`` `
+
+> In this scenario, the Encoder's buffering in the interruption does not work and all events are not processed `Releasexxx`
+
+#### button processing
+The library processes the button as follows:
+- Pressing with software suppression of rubbish (holding longer than the Deb time), the result is the event `Press`, the state of` Pressing` and `Busy`
+- retention longer than the Hold Hold time - the event `hold`, the state` holding`
+- holding longer than the Hold + Timeshot Timeshu Taimout - a pulse event `step`, triggers with the STEP period while the button holds
+- release of the button, the result - the event `Release`, the removal of the states` Pressing` and `Holding`
+  - release to the deduction time - event `click`
+  - release after holding - event `Releasehold`
+  - release after impulse deduction - event `ReleaseStep`
+  - Events `Releasehold` and` ReleaseStep` mutually exclusive, if the button was withheld `step` -` Releasehold` will no longer work
+- Waiting for a new click during the Click timeout, the state `Waiting`
+- If there is no new click - the removal of the state of `Busy`, the processing is completed
+  - If the button is pressed again - processing a new click
+  - The Clicks Clicks `getClicks ()` is discarded after the events `Releasehold`/` Releastep`, which check the preliminary clicks.In the general processor `Action ()` Events `EB_REL_Hold_C` or` EB_REL_STEP_C`
+  - The number of clicks made must be checked by the `Hasclicks` event, and you can also interview almost all the events of the buttons that go to` Releasexxx`
+- If `Timeout` is expected - Timeout event with the specified period from the current moment
+- processing the button in the interruption informs the library about the fact of pressing, the rest of the processing is performed regularly in `Tick ()` `
+
+> The difference is `Click (n)` `Hasclicks (n)`: `Click (n)` will return `true` in any case when the number of clicks coincides, even if more clicks are made.`HasClicks (n)` will return `true` only inCranberry, if the exactly indicated number of clicks was made and there were no more clicks!
+
+> It is better to see once than read a hundred times.Launch an example of Demo and go on the button, or try [online symulation in Wokwi] (https://wokwi.com/projects/373591584298469377)
+
+##### Click
+! [click] (/doc/click.gif)
+
+##### Hold
+! [Hold] (/doc/hold.gif)
+
+##### STEP
+! [STEP] (/DOC/STEP.GIF)
+
+Online symulation is available [here] (https://wokwi.com/projects/373591584298469377)
+
+#### Encoder Processing
+- "Fast" turn is considered a turn committed less than tuned timaut from the previous turn
+- the turns processed in interruption become active (cause events) after calling `tick ()`
+- Access to the encoder’s counter `Counter` is a public variable of the class, you can do anything with it:
+`` `CPP
+Serial.println (eb.counter);// read
+Eb.counter += 1234;// change
+eb.counter = 0;// Knock
+`` `
+
+#### encoder processing with a button
+- The turning of the encoder with a clamped button removes and blocks all subsequent events and clicks, with the exception of the event `redase`.The states of the pressed button do not change
+- The turning of the encoder also affects the system timout (the `timeout ()` function) will work after the indicated time after turning the enkoder
+- the Klikov counter is available when pressed: several clicks, click of a button, turn
+
+<a id="preclicks"> </a>
+
+### Preliminary clicks
+The library considers the number of clicks by the button and some survey functions can separately be processed with *preliminary clicks *.For example, 3 clicks, then retention.This greatly expands the capabilities of one button.There are two options for working with such events:
+`` `CPP
+  // 1
+  if (btn.hold ()) {
+    if (btn.getclics () == 2) serial.println ("Hold 2 Clicks");
+  }
+
+  // 2
+  if (btn.hold (2)) serial.println ("Hold 2 Clicks");
+`` `
+
+In the first version, you can get the number of clicks for further processing manually, and in the second - the library will do this itself if the number of clicks for action is known in advance.
+
+<a id="btnread"> </a>
+
+## direct reading button
+In some scenarios, you need to get the state of the "here and now" button, for example, determine whether the button is held immediately after starting the microcontroller (program start).The function `tick ()` must be called constantly in the cycle so that the button is processing with the extinguishing of the ratio of contacts and other calculations, so the design of the next type ** will not work **:
+`` `CPP
+VOID setup () {
+  btn.tick ();
+  if (btn.press ()) serial.println ("button pressed at start");
+}
+`` `
+
+The following functions will help for such scenarios, return `true` if the button is pressed:
+- `read ()` for libraries button and buttont
+- `Readbtn ()` for library libraries and encbuttont
+
+> The button survey is performed taking into account the previously configured button level (Setbtnlevel)!It is not necessary to manually invert the logic:
+
+`` `CPP
+VOID setup () {
+  // btn.setbtnlevel (LOW);// you can configure the level
+
+  if (btn.read ()) serial.println ("button pressed at start");
+}
+`` `
+
+<a id="loop"> </a>
+
+### immersion in the cycle
+Suppose you need to process the button synchronously and with the extinguishing of the rattles.For example, if the button is clamped at the start of the microcontroller, get its retention or even the pulse retention inside the `setup` unit, that is, before the start of the main program.You can use the state of `Busy` and interview the button from the cycle:
+`` `CPP
+VOID setup () {
+  Serial.Begin (115200);
+
+  btn.tick ();
+  While (btn.busy ()) {
+    btn.tick ();
+    if (btn.hold ()) serial.println ("Hold");
+    if (btn.step ()) serial.println ("step");
+  }
+
+  Serial.println ("Program Start");
+}
+`` `
+How it works: the first tick interrogates the button, if the button is pressed - the state of the Busy is immediately activated and the system enters the `While` cycle.Inside it, we continue to tick and get events from the button.When the button is released and all events will work - the Busy flag will drop and the program will automatically leave the cycle.You can rewrite this design to the cycle with a postcryption, more beautiful:
+`` `CPP
+do {
+  B.tn.tick ();
+  if (btn.hold ()) serial.println ("Hold");
+  if (btn.step ()) serial.println ("step");
+} While (btn.busy ());
+`` `
+
+<a id="timeout"> </a>
+
+## Timeout
+In the classes associated with the button (Button, Encbutton) there is a function `Timeout (Time)` - it will once return `true` if the indicated time has passed after the action with the button/encoder.This can be used to preserve parameters after entering, for example:
+`` `CPP
+VOID loop () {
+  eb.tick ();
+
+  // ...
+
+  if.timeout ()) {
+    // after interaction with encoder 1 second passed
+    // eeprom.put (0, settings);
+  }
+}
+`` `
+
+<a id="busy"> </a>
+
+### Busy
+The `Busy () function` Returns `True` while the button processing is underway, i.e.So far, the system awaits actions and the release of timeouts.This can be used to optimize the code, for example, avoid some long and heavy parts of the program during the button processing:
+`` `CPP
+VOID loop () {
+  eb.tick ();
+
+  // ...
+
+  if (! eb.busy ()) {
+    // Potentially long and difficult code
+  }
+}
+`` `
+
+<a id="Actions"> </a>
+
+### Obtaining an event
+Available in all classes ** with the ** button:
+- `virtbutton`
+- `Button`
+- `virtencbutton`
+- `encbutton`
+
+The function `Action ()` when the event occurs, the event is returned (different from scratch, which in itself is an indication of the existence of an event):
+- `eb_press` - click on the button
+- `eb_hold` - the button is kept
+- `eb_step` - impulse retention
+- `eb_release` - the button is released
+- `eb_click` - Single click
+- `eb_clicks` - A few click signal
+- `eb_turn` - turn of the encoder
+- `eb_rel_hold` - the button is released after holding
+- `EB_REL_HOLD_C` - the button is released after holding off the premises.clicks
+- `EB_REL_STEP` - the button is released after the step
+- `EB_REL_STEP_C` - the button is released after the step with a border.clicks
+
+> The result of the function `Action ()` is reset after the next call `tick ()`, that is, is available on the entire current iteration of the main cycle
+
+The obtained event code can be processed through `switch`:
+`` `CPP
+switch (eb.action ()) {
+  Case eb_press:
+    // ...
+    Break;
+  Case eb_hold:
+    // ...
+    Break;
+  // ...
+}
+`` `
+
+<a id="OPTIMISE"> </a>
+
+## Optimization
+#### Library weight
+To maximally reduce the weight of the library (in particular in RAM), you need to set the Timatui constants through Define (saving 1 bytes per timaut), turn off the events processor, counters-buffers and use the T-class (saving 1 byte per pin):
+`` `CPP
+#define eb_no_for
+#define eb_no_callback
+#define eb_no_counter
+#define eb_no_buffer
+#define eb_deb_time 50 // Timesout to extinguish the trim button (button)
+#define eb_click_time 500 // Click Stayout (button)
+#define eb_hold_time 600 // Maintenance Times (button)
+#define eb_step_time 200 // Impulse retention Timesout (button)
+#define EB_FAST_TIME 30 // TIMAUT RAM (ENCODER)
+#include <encbutton.h>
+ENCBUTTONT <2, 3, 4> EB;
+`` `
+In this case, an encoder with a button will take only 8 bytes in SRAM, and just a button - 5.
+
+#### Fulfillment speed
+To reduce the time for checking the system flags of events (insignificantly, but pleasant), you can place all the polls in the condition by `tick ()`, since `tick ()` returns `true` only when ** events **: the events **:
+`` `CPP
+VOID loop () {
+  if (eb.tick ()) {
+    if.turn ()) ...;
+    if.click ()) ...;
+  }
+}
+`` `
+
+Also, a survey of events using the `Action () function is performed faster than a manual survey of individual functions of events, so the library will work in this format as efficiently as possible:
+`` `CPP
+VOID loop () {
+  if (eb.tick ()) {
+    switch (eb.action ()) {
+      Case eb_press:
+        // ...
+        Break;
+      Case eb_hold:
+        // ...
+        Break;
+      // ...
+    }
+  }
+}
+`` `
+
+For polling ** states ** buttons `Pressing ()`, `Holding ()`, `WATING ()` You can place them inside the conditions of `BUSY ()` so as not to interview them until they are guaranteed:
+`` `CPP
+if (btn.busy ()) {
+  if (btn.pressing ()) ...
+  if (btn.holding ()) ...
+  if (btn.waiting ()) ...
+}
+`` `
+
+<a id="callback"> </a>
+
+### Collback
+You can connect the external function-shacklerCranberry, it will be caused when any event occurs.This opportunity works in all classes ** with the ** button:
+- `virtbutton`
+- `Button`
+- `virtencbutton`
+- `encbutton`
+
+`` `CPP
+ENCBUTTON EB (2, 3, 4);
+
+Void callback () {
+  switch (eb.action ()) {
+    Case eb_press:
+      // ...
+      Break;
+    Case eb_hold:
+      // ...
+      Break;
+    // ...
+  }
+}
+
+VOID setup () {
+  eb.attach (callback);
+}
+
+VOID loop () {
+  eb.tick ();
+}
+`` `
+
+<a id="duble"> </a>
+
+### Simultaneous pressing
+The library supports work with two simultaneously pressed buttons as with the third button.For this you need:
+1. To make a virtual button `virtbutton`
+2. Call the processing of real buttons
+3. Pass these buttons to the virtual button to process (these can be objects of classes `virtbutton`,` button`, `encbutton` + their` t`- version)
+4. Next to interrogate events
+
+`` `CPP
+Button b0 (4);
+Button b1 (5);
+Virtbutton B2;// 1
+
+VOID loop () {
+  b0.tick ();// 2
+  b1.tick ();// 2
+  B2.Tick (B0, B1);// 3
+
+  // 4
+  if (b0.click ()) serial.println ("b0 click");
+  if (b1.click ()) serial.println ("b1 click");
+  if (b2.click ()) serial.println ("b0+b1 click");
+}
+`` `
+
+The library itself will “drop” unnecessary events from real buttons if they were pressed together, with the exception of the event `Press`.Thus, a full -fledged third button of two others with a convenient survey is obtained.
+
+<a id="isr"> </a>
+
+## interrupt
+### encoder
+For processing an encoder in a loaded program you need:
+- Connect both of his pins to hardware interruptions by `Change`
+- install `setencisr (true)`
+- call a special ticker for interruption in the handler
+- The main ticker also needs to be called in `loop` for corrething work - events are generated in the main ticker:
+`` `CPP
+// Example for Atmega328 and Encbutton
+ENCBUTTON EB (2, 3, 4);
+
+/*
+// ESP8266/ESP32
+IRAM_ATTR VOID ISR () {
+  eb.tickisr ();
+}
+*/
+
+VOID isr () {
+  eb.tickisr ();
+}
+VOID setup () {
+  Attachinterrupt (0, Isr, Change);
+  Attachinterrupt (1, ISR, Change);
+  eb.setencisr (true);
+}
+VOID loop () {
+  eb.tick ();
+}
+`` `
+
+Note: The use of work in the interruption allows you to correctly process the encoder position and not miss a new turn.An event with a turn obtained from an interruption will become available * after * call `Tick` in the main cycle of the program, which allows not to violate the sequence of the main cycle:
+- Buferization is disabled: the `turn` event is activated only once, regardless of the number of clicks of the encoder made between the two challenges of` tick` (clicks are processed in interruption)
+- The buffering is included: the `turn` event will be caused as many times as there were really clicks of the encoder, this allows you to not miss the turns and not load the system in the interruption.** Boofer size - 5 unprocessed clicks of encoder **
+
+Notes:
+- The `setencisr` function works only in non - virtual classes.If it is turned on, the main ticker `tick` simply does not interview Encoder's pins, which saves processor time.Processing occurs only in interruption
+- The encoder counter is always relevant and can be ahead of buffering turns in the program with large delays in the main cycle!
+- on different interrupt platforms, they can work differently (for example, on ESPXX - you need to add the functions of the atrica `` IRAM_ATTR`, see documentation on your platform!)
+- a processor connected to `Attach ()` will be called from `Tick ()`, that is, *not from interruption *!
+
+### virtual classes
+In the virtual ones there is a ticker in which it is not necessary to transmit the state of the encoder, if it is processed in an interruption, this allows you to not interview pins in idle.For example:
+
+`` `CPP
+Virtencoder e;
+
+VOID isr () {
+    E.tickisr (DigitalRead (2), DigitalRead (3));
+}
+VOID setup () {
+    Attachinterrupt (0, Isr, Change);
+    Attachinterrupt (1, ISR, Change);
+
+    E.Setencisr (1);
+}
+VOID loop () {
+    E.tick ();// Do not transmit the states of Pinov
+}
+`` `
+
+#### Button
+To process the button in the interruption, you need:
+- Connect an interruption on ** press ** buttons taking into account its physical connection and level:
+  - If the button is deputy`Low` - Interruption` Falling`
+  - if the button closes `high` - interruption` rising`
+- call `Pressisr ()` in the interruption processor
+
+`` `CPP
+Button b (2);
+
+/*
+// ESP8266/ESP32
+IRAM_ATTR VOID ISR () {
+  B.Pressisr ();
+}
+*/
+
+VOID isr () {
+  B.Pressisr ();
+}
+VOID setup () {
+  Attachinterrupt (0, ISR, Falling);
+}
+VOID loop () {
+  B.tick ();
+}
+`` `
+
+Note: the button is processed mainly `tick ()`, and the function `Pressisr ()` just informs the library that the button was pressed outside `Tick ()`.This allows you not to miss the pressing of the button until the program was busy with something else.
+
+<a id="array"> </a>
+
+### Array of buttons/Encoder
+You can create an array only from non -step classes (without the letter `t`), because Pinov numbers will have to be indicated already in the radio further in the program.For example:
+`` `CPP
+Button btns [5];
+ENCBUTTON EBS [3];
+
+VOID setup () {
+  btns [0] .init (2);// Indicate the pin
+  btns [1] .init (5);
+  btns [2] .init (10);
+  // ...
+
+  EBS [0] .init (11, 12, 13, Input);
+  EBS [1] .init (14, 15, 16);
+  // ...
+}
+VOID loop () {
+  for (int i = 0; i <5; i ++) btns [i] .Tick ();
+  for (int i = 0; i <3; i ++) EBS [i] .Tick ();
+
+  if (btns [2] .Click ()) serial.println ("BTN2 click");
+  // ...
+}
+`` `
+
+<a id="cubom"> </a>
+
+### Caste functions
+The library supports the task of its functions for reading PIN and getting time without editing library files.To do this, you need to implement the corresponding function in your .cpp or.
+- `bool eb_read (uint8_t pin)` - for its pine reading function
+- `void eb_mode (uint8_t pin, uint8_t mode)` - for your analogue Pinmode
+- `uint32_t eb_uptime ()` - for your analogue millis ()
+
+Example:
+
+`` `CPP
+#include <encbutton.h>
+
+Bool eb_read (uint8_t pin) {
+    Return DigitalRead (PIN);
+}
+
+VOID eb_mode (uint8_t pin, uint8_t mode) {
+    Pinmode (PIN, Mode);
+}
+
+uint32_t eb_uptime () {
+    Return Millis ();
+}
+`` `
+
+<a id="timer"> </a>
+
+### Survey by timer
+Sometimes it may be necessary to call `tick ()` not on every iteration, but by the timer.For example, for a virtual button from the Pino Expand, when reading the Pino Expand is a long operation, and it often does not make sense to call it.You can’t do this, events will be active during the timer!
+`` `CPP
+VOID loop () {
+  // Timer for 50 ms
+  Static uint32_t tmr;
+  if (millis () - tmr> = 50) {
+    TMR = Millis ();
+    btn.tick (Readsomepin ());
+  }
+
+  // will be actively within 50 ms !!!
+  if (btn.click ()) foo ();
+}
+`` `
+
+In this situation, you need to do this: tick along the timer, process events there and drop flags at the end:
+`` `CPP
+VOID loop () {
+  // Timer for 50 ms
+  Static uint32_t tmr;
+  if (millis () - tmr> = 50) {
+    TMR = Millis ();
+    // TIK
+    btn.tick (Readsomepin ());
+
+    // analysis of events
+    if (btn.click ()) foo ();
+
+    // Reset of the flags
+    btn.clear ();
+  }
+}
+`` `
+
+Or you can connect the handler and call `clear ()` at the end of the function:
+`` `CPP
+Void callback () {
+  switch (btn.action ()) {
+    // ...
+  }
+
+  // Reset of the flags
+  btn.clear ();
+}
+
+VOID loop () {
+  // Timer for 50 ms
+  Static uint32_t tmr;
+  if (millis () - tmr> = 50) {
+    TMR = Millis ();
+    btn.tick (Readsomepin ());
+  }
+}
+`` `
+
+In the case of calling the timer, the anti -departments will be partially provided by the timer itself and in the library it can be turned off (set the period 0).
+
+For the correct operation of timeouts, conditions and a click counter, you need another approach: buffering the states read according to the timer and transfer them to the TIC in the main cycle.For example:
+`` `CPP
+Bool Readbuf = 0;// buffer Pina
+
+VOID loop () {
+  // Timer for 50 ms
+  Static uint32_t tmr;
+  if (millis () - tmr> = 50) {
+    TMR = Millis ();
+    Readbuf = Readsomepin ();// Reading in the buffer
+  }
+
+  // tick from the buffer
+  BTN.Tick (Readbuf);
+
+  if (btn.click ()) foo ();
+}
+`` `
+
+<a id="EXAMPles-mini"> </a>
+
+### Mini examples, scripts
+`` `CPP
+// Change the values of the variables
+
+// Turn of the encoder
+if (enc.turn ()) {
+  // Change in step 5
+  var += 5 * enc.dir ();
+
+  // Change in step 1 with a normal turn, 10 with fast
+  Var += ENC.FAST ()?10: 1;
+
+  // Change in step 1 with a normal turn, 10 with pressed
+  vAR += ENC.Pressing ()?10: 1;
+
+  // Change one variable when turning, the other - with a pressed turn
+  if (enc.pressing ()) Var0 ++;
+  Else Var1 ++;
+
+  // If the button is pressed - preliminary clicks are available
+  // Choose a variable for changes in the premises.Clicks
+  if (enc.pressing ()) {
+    Switch (enc.getClicks ()) {
+      CASE 1: VAR0 += ENC.DIR ();
+        Break;
+      CASE 2: VAR1 += ENC.DIR ();
+        Break;
+      CASE 3: VAR2 += ENC.DIR ();
+        Break;
+    }
+  }
+}
+
+// Impulse retention at every step is increasing the variable
+if (btn.step ()) var ++;
+
+// Change the direction of change in the variable after letting go from STEP
+if (btn.step ()) var += dir;
+if (btn.releastep ()) die = -dir;
+
+// Change the selected variable using STEP
+if (btn.step (1)) Var1 ++;// Click-holding
+if (btn.step (2)) Var2 ++;// Click-Click-holding
+if (btn.step (3)) var3 ++;// Click-Click-Click-hold
+
+// if you keep the STEP for more than 2 seconds - an incremental +5, so far less - +1
+if (btn.step ()) {
+  if (btn.stepfor (2000)) var += 5;
+  Else Var += 1;
+}
+
+// inclusion of the mode by the number of clicks
+if (btn.hasclicks ()) mode = btn.getclicks ();
+
+// inclusion of the mode by several clicks and retention
+if (btn.hold (1)) mode = 1;// Click-holding
+if (btn.hold (2)) mode = 2;// Click-Click-holding
+if (btn.hold (3)) mode = 3;// Click-Click-Click-hold
+
+// or so
+if (btn.hold ()) mode = btn.getclicks ();
+
+// Button is released, look how much it was held
+if (btn.release ()) {
+  // from 1 to 2 seconds
+  if (btn.pressfor ()> 1000 && btn.pressfor () <= 2000) mod = 1;
+  // from 2 to 3 seconds
+  Else if (BTN.PressFor ()> 2000 && BTN.PressFor () <= 3000) Mode = 2;
+}
+`` `
+
+<a id="migrate"> </a>
+
+## guide for migration from v2 to v3
+## H initialization
+`` `CPP
+// virtual
+Virtencbutton eb;// Encoder with button
+Virtbutton b;// button
+Virtencoder e;// Encoder
+
+// Real
+// Encoder with button
+ENCBUTTON EB (ENC0, ENC1, BTN);// Encoder Pins and buttons
+ENCBUTTON EB (ENC0, ENC1, BTN, MODEENC);// + Pino Pino Encoder Pin (silence. Input)
+ENCBUTTON EB (ENC0, ENC1, BTN, MODEENC, MODEBTN);// + Pin mode buttons (silent. Input_pullup)
+ENCBUTTON EB (ENC0, ENC1, BTN, MODEENC, MODEBTN, BTNlevel);// + button level (silence)
+// template
+ENCBUTTON <ENC0, ENC1, BTN> EB;// Encoder Pins and buttons
+ENCBUTTON <ENC0, ENC1, BTN> EB (Modeenc);// + Pino Pino Encoder Pin (silence. Input)
+ENCBUTTON <ENC0, ENC1, BTN> EB (Modeenc, Modebtn);// + Pin mode buttons (silent. Input_pullup)
+ENCBUTTON <ENC0, ENC1, BTN> EB (Modeenc, Modebtn, Btnlevel);// + button level (silence)
+
+// button
+Button b (pin);// PIN
+Button b (PIN, Mode);// + Pin mode buttons (silent. Input_pullup)
+Button B (PIN, Mode, Btnlevel);// + button level (silence)
+// template
+Buttont <pin> b;// PIN
+Buttont <pin> b (mode);// + Pin mode buttons (silent. Input_pullup)
+Buttont <pin> b (mode, btnlevel);// + button level (silence)
+
+// Encoder
+ENCODER E (ENC0, ENC1);// Pines of Encoder
+ENCODER E (ENC0, ENC1, Mode);// + Pino Pino Encoder Pin (silence. Input)
+// template
+Encodert <enc0, enc1> e;// Pines of Encoder
+Encodert <Enc0, Enc1> E (Mode);// + Pino Pino Encoder Pin (silence. Input)
+`` `
+
+### functions
+|v2 |v3 |
+| ------------- | -------------------------------------
+|`HELD ()` |`Hold ()` |
+|`Hold ()` |`Holding ()` |
+|`state ()` |`Pressing ()` |
+|`setpins ()` |`Init ()` |
+
+- The procedure for indicating Pinov has changed (see DEMPLE above)
+- `Clearflags ()` replaced by `Clear ()` (drop the flags of events) and `reset ()` (drop systemic flags of processing, finish processing)
+
+### Logic of Work
+In the V3, the functions of an event survey (Click, Turn ...) are not discarded immediately after their call - they are discarded at the next call `Tick ()`, thus retain their meaning in all subsequent challenges on the current iteration of the main cycle of the program.** Therefore, `tick ()` needs to be called only 1 time per cycle, otherwise there will be missions of actions! ** Read about thisabove.
+
+<a id="EXAMPLE"> </a>
+## Examples
+The rest of the examples look at ** Examples **!
+<details>
+<summary> Full demo encbutton </summary>
+
+`` `CPP
+// #define eb_no_for // Disable Pressfor/Holdfor/StepFor support and Stepov counter (saves 2 bytes of RAM)
+// #define eb_no_callback // Disable the event processor Attach (saves 2 bytes of RAM)
+// #define eb_no_counter // Disable the enkoder counter (saves 4 bytes of RAM)
+// #define EB_NO_BUFFER // Disable the buffer of the encoder (saves 1 byte of RAM)
+
+// #define eb_deb_time 50 // Timesout to darebells button (button)
+// #define eb_click_time 500 // Clicks standstatics (button)
+// #define eb_hold_time 600 // Maintenance Times (button)
+// #define eb_step_time 200 // pulse retention rate (button)
+// #define EB_FAST_TIME 30 // Quick turn Timesout (Encoder)
+
+#include <encbutton.h>
+ENCBUTTON EB (2, 3, 4);
+// ENCBUTTON EB (2, 3, 4, Input);// + Pino Pino mode
+// ENCBUTTON EB (2, 3, 4, Input, Input_pullup);// + button pins mode
+// ENCBUTTON EB (2, 3, 4, Input, Input_pullup, Low);// + button level
+
+VOID setup () {
+    Serial.Begin (115200);
+
+    // shows the default values
+    eb.setbtnlevel (Low);
+    EB.SetClicktimeout (500);
+    eb.Setdebtimeout (50);
+    Eb.SetHoldtimeout (600);
+    eb.setsteptimeout (200);
+
+    eb.setencreverse (0);
+    eb.setenctype (eb_step4_low);
+    eb.setfasttimeout (30);
+
+    // throw the Encoder counter
+    eb.counter = 0;
+}
+
+VOID loop () {
+    eb.tick ();
+
+    // General rotation processing
+    if.turn ()) {
+        Serial.print ("Turn: Dir");
+        Serial.print (eb.dir ());
+        Serial.print (", fast");
+        Serial.print (eb.fast ());
+        Serial.print (", Hold");
+        Serial.print (eb.pressing ());
+        Serial.print (", Counter");
+        Serial.print (eb.counter);
+        Serial.print (", clicks");
+        Serial.println (eb.getClicks ());
+    }
+
+    // Turning rotation processing
+    if.left ()) serial.println ("Left");
+    if.right ()) serial.println ("right");
+    if.left ()) serial.println ("Lefth");
+    if.righth ()) serial.println ("righth");
+
+    // button
+    if.press ()) serial.println ("Press");
+    if.click ()) serial.println ("click");
+
+    if.release ()) {
+      Serial.println ("Release");
+
+      Serial.print ("Clicks:");
+      Serial.print (eb.getClicks ());
+      Serial.print (", stps:");
+      Serial.print (eb.getsteps ());
+      Serial.print (", Press for:");
+      Serial.print (eb.pressfor ());
+      Serial.print (", Hold for:");
+      Serial.print (eb.holdfor ());
+      Serial.print (", step for:");
+      Serial.println (eb.stepfor ());
+    }
+
+    // States
+    // serial.println (eb.pressing ());
+    // serial.println (eb.holding ());
+    // serial.println (eb.busy ());
+    // serial.println (eb.waiting ());
+
+    // Timesout
+    if (eb.timeout ()) serial.println ("Timeout!");
+
+    // Holding
+    if.hold ()) serial.println ("Hold");
+    if.hold (3)) serial.println ("Hold 3");
+
+    // Impulse retention
+    if.step ()) serial.println ("step");
+    if.step (3)) serial.println ("STEP 3");
+
+    // released after impulse deduction
+    if (eb.releastep ()) serial.println ("Release Step");
+    if (eb.releastep (3)) serial.println ("Release Step 3");
+
+    // released after holding
+    if.releasehold ()) serial.println ("Release Hold");
+    if (eb.releasehold (2)) serial.println ("Release Hold 2");
+
+    // Check for the number of clicks
+    if.hasclicks (3)) Serial.println ("Has 3 Clicks");
+
+    // Bring the number of clicks
+    if.hasclicks ()) {
+        Serial.print ("Has Clicks:");
+        Serial.println (eb.getClicks ());
+    }
+}
+`` `
+</details>
+<details>
+<summary> connection of the handler </summary>
+
+`` `CPP
+#include <encbutton.h>
+ENCBUTTON EB (2, 3, 4);
+
+Void callback () {
+    Serial.print ("callback:");
+    switch (eb.action ()) {
+        Case eb_press:
+            Serial.println ("Press");
+            Break;
+        Case eb_hold:serial.println ("Hold");
+            Break;
+        Case eb_step:
+            Serial.println ("STEP");
+            Break;
+        Case eb_release:
+            Serial.println ("Release");
+            Break;
+        Case eb_click:
+            Serial.println ("click");
+            Break;
+        Case eb_clicks:
+            Serial.print ("Clicks");
+            Serial.println (eb.getClicks ());
+            Break;
+        Case eb_turn:
+            Serial.print ("turn");
+            Serial.print (eb.dir ());
+            Serial.print ("");
+            Serial.print (eb.fast ());
+            Serial.print ("");
+            Serial.println (eb.pressing ());
+            Break;
+        Case eb_rel_hold:
+            Serial.println ("Release Hold");
+            Break;
+        CASE EB_REL_HOLD_C:
+            Serial.print ("Release Hold Clicks");
+            Serial.println (eb.getClicks ());
+            Break;
+        CASE EB_REL_STEP:
+            Serial.println ("Release Step");
+            Break;
+        CASE EB_REL_STEP_C:
+            Serial.print ("Release Step Clicks");
+            Serial.println (eb.getClicks ());
+            Break;
+    }
+}
+
+VOID setup () {
+    Serial.Begin (115200);
+    eb.attach (callback);
+}
+
+VOID loop () {
+    eb.tick ();
+}
+`` `
+</details>
+<details>
+<summary> All types of buttons </summary>
+
+`` `CPP
+#include <encbutton.h>
+
+Button BTN (4);
+Buttont <5> btnt;
+Virtbutton BTNV;
+
+VOID setup () {
+    Serial.Begin (115200);
+}
+
+VOID loop () {
+    // Button
+    btn.tick ();
+    if (btn.click ()) serial.println ("btn click");
+
+    // Buttont
+    btnt.tick ();
+    if (btnt.click ()) serial.println ("BTNT CLICK");
+
+    // virtbutton
+    btnv.tick (! DigitalRead (4));// transmit logical value
+    if (btnv.click ()) serial.println ("btnv click");
+}
+`` `
+</details>
+<details>
+<summary> All types of encoder </summary>
+
+`` `CPP
+#include <encbutton.h>
+
+ENCODER ENC (2, 3);
+ENCODERT <5, 6> ENCT;
+Virtencoder encv;
+
+VOID setup () {
+    Serial.Begin (115200);
+}
+
+VOID loop () {
+    // The survey is the same for everyone, 3 ways:
+
+    // 1
+    // Tick will return 1 or -1, then this is a step
+    if (enc.tick ()) serial.println (enc.counter);
+
+    // 2
+    // can be interviewed through turn ()
+    enct.tick ();
+    if (enct.turn ()) serial.println (enct.dir ());
+
+    // 3
+    // you can not use survey functions, but get the direction directly
+    int8_t v = encv.tick (DigitalRead (2), DigitalRead (3));
+    if (v) serial.println (v);// Derive 1 or -1
+}
+`` `
+</details>
+
+<a id="versions"> </a>
+## versions
+<details>
+<summary> Old </ Summary>
+
+- V1.1 - Pullap separately by the method
+- V1.2 - You can transfer the parameter input_pullup / input (silent) to the designer
+- V1.3 - Virtual clamping of the encoder button is made into a separate function + minor improvements
+- V1.4 - Processing of pressing and releasing the button
+- v1.5 - added virtual mode
+- V1.6 - Optimization of work in interruption
+- V1.6.1 - Saching by default Input_pullup
+- V1.7 - a large memory optimization, remade Fastio
+- V1.8 - Individual tuning of the TIMUUT Maintenance of the button (was common at all)
+- v1.8.1 - removed Fastio
+- v1.9 - added a separate development of a pressed turn and a request for direction
+- V1.10 - improved ReleASDE processing, eased the weight in callback and corrected the bugs
+- V1.11 - even more than any optimization + setting button level
+- V1.11.1 - Digispark compatibility
+- V1.12 - added a more accurate algorithm of enkoder Eb_better_enc
+- V1.13 - Added experimental ENCBUTTON2
+- V1.14 - added ReleaseStep ().The release of the button is included in the debate
+- v1.15 - added Setpins () for Encbutton2
+- V1.16 - added EB_HALFSTEP_Enc mode for hemisphere encoders
+- v1.17 - added STEP with preliminary clicks
+- V1.18 - We do not consider clicks after the activation of STEP.Hold () and Held () can also take preliminary clicks.Redistributed and improved debate
+- V1.18.1 - Fixed error in ReleaseStep () (did not return the result)
+- V1.18.2 - Fix Compiler Warnings
+- V1.19 - speed optimization, reduced weight in SRAM
+- v1.19.1 - still a bit increased performance
+- v1.19.2 - not yetCranberries increased a lot of performance, thanks xray3d
+- v1.19.3 - made a high level of the default button in virtual mode
+- V1.19.4 - Fix Encbutton2
+- V1.20 - Critical error is fixed in Encbutton2
+- V1.21 - EB_HALFSTEP_ENC now works for a normal mode
+- V1.22 - Improved EB_HALFSTEP_Enc for a normal mode
+- V1.23 - Getdir () replaced with DIR ()
+- V2.0
+    - The eb_better_enc algorithm is optimized and set by default, the define eb_better_enc is abolished
+    - added setenctype () to configure the type of encoder from the program, the define EB_HALFSTEP_ENC is abolished
+    - added Setencreverse () to change the direction of the encoder from the program
+    - added setteptimeout () to set the period of impulse deduction, the define EB_STEP is abolished
+    - Small improvements and optimization
+</details>
+
+- V3.0
+  - The library is rewritten from scratch, with previous versions is incompatible!
+    - completely different initialization of the object
+    -renamed: Hold ()-> Holding (), HELD ()-> HOLD ()
+  - Optimization of Flash memory: the library weighs less, in some scenarios - by several kilobytes
+  - optimization of the speed of code execution, including in interruption
+  - several bytes less than RAM, several optimization levels to choose from
+  - a simpler, understandable and convenient use
+  - more readable source code
+  - Breaking into classes for use in different scenarios
+  - new functions, capabilities and handlers for the button and encoder
+  - Encoder's buffer in interruption
+  - native processing of two simultaneously pressed buttons as a third button
+  - support of 4 types of encoder
+  - The documentation is rewritten
+  - Encbutton is now replacing Gyverlibs/Virtualbutton (archived)
+- V3.1
+  - The initialization of the button is expanded
+  - removed Holdencbutton () and Toggleencbutton ()
+  - added Turnh ()
+  - Optimized the interruptions of encoder, added Setencisr ()
+  - Buerization of the direction and quick turn
+  - strongly optimized the speed of Action () (general processor)
+  - Added connection of the external function-processor of events
+  - Added button processing in interruption - Pressisr ()
+- V3.2
+  - Added the functions of TickRaW () and Clear () for all classes.Allows for separate processing (see document)
+  - improved processing button using interruptions
+- V3.3
+  - Added functions of receiving PressFor (), HoldFor (), StepFor () (disconnected)
+  - Added meter of the steps Getsteps () (disconnected)
+- V3.4
+  - access to the click counter during a pressed turn
+  - Added function Detach ()
+- V3.5
+  - added dependence of Gyverio (accelerated Pino survey)
+  - added the opportunity to set your pharmacy and pine reading functions
+- V3.5.2
+  - Optimization
+  - Simplified replacement of custom functions
+  - Fixed a compilation error when using a library in several .cpp files
+- V3.5.3
+  - Added the number of clicks to the Press/Release/Click/Pressing poll
+- V3.5.5 - Collback based on the STD :: Function for ESP
+    
+<a id="feedback"> </a>
+## bugs and feedback
+Create ** Issue ** when you find the bugs, and better immediately write to the mail [alex@alexgyver.ru] (mailto: alex@alexgyver.ru)
+The library is open for refinement and your ** pull Request ** 'ow!
+
+When reporting about bugs or incorrect work of the library, it is necessary to indicate:
+- The version of the library
+- What is MK used
+- SDK version (for ESP)
+- version of Arduino ide
+- whether the built -in examples work correctly, in which the functions and designs are used, leading to a bug in your code
+- what code has been loaded, what work was expected from it and how it works in reality
+- Ideally, attach the minimum code in which the bug is observed.Not a canvas of a thousand lines, but a minimum code

+ 73 - 0
.pio/libdeps/pico/EncButton/examples/callback/callback.ino

@@ -0,0 +1,73 @@
+// опрос событий через функцию-обработчик
+
+#include <Arduino.h>
+#include <EncButton.h>
+
+EncButton eb(2, 3, 4);
+
+void cb() {
+    // здесь EB_self - указатель на сам объект
+
+    Serial.print("callback: ");
+    switch (eb.action()) {
+        case EB_PRESS:
+            Serial.println("press");
+            break;
+        case EB_HOLD:
+            Serial.println("hold");
+            break;
+        case EB_STEP:
+            Serial.println("step");
+            break;
+        case EB_RELEASE:
+            Serial.print("release. steps: ");
+            Serial.print(eb.getSteps());
+            Serial.print(", press for: ");
+            Serial.print(eb.pressFor());
+            Serial.print(", hold for: ");
+            Serial.print(eb.holdFor());
+            Serial.print(", step for: ");
+            Serial.println(eb.stepFor());
+            break;
+        case EB_CLICK:
+            Serial.println("click");
+            break;
+        case EB_CLICKS:
+            Serial.print("clicks ");
+            Serial.println(eb.getClicks());
+            break;
+        case EB_TURN:
+            Serial.print("turn ");
+            Serial.print(eb.dir());
+            Serial.print(" ");
+            Serial.print(eb.fast());
+            Serial.print(" ");
+            Serial.println(eb.pressing());
+            break;
+        case EB_REL_HOLD:
+            Serial.println("release hold");
+            break;
+        case EB_REL_HOLD_C:
+            Serial.print("release hold clicks ");
+            Serial.println(eb.getClicks());
+            break;
+        case EB_REL_STEP:
+            Serial.println("release step");
+            break;
+        case EB_REL_STEP_C:
+            Serial.print("release step clicks ");
+            Serial.println(eb.getClicks());
+            break;
+        default:
+            Serial.println();
+    }
+}
+
+void setup() {
+    Serial.begin(115200);
+    eb.attach(cb);
+}
+
+void loop() {
+    eb.tick();
+}

+ 75 - 0
.pio/libdeps/pico/EncButton/examples/callback2/callback2.ino

@@ -0,0 +1,75 @@
+// опрос событий через функцию-обработчик
+
+#include <Arduino.h>
+#include <EncButton.h>
+
+EncButton eb(2, 3, 4);
+
+void cb() {
+    // здесь EB_self - указатель на сам объект
+
+    Serial.print("callback: ");
+    switch (eb.getAction()) {
+        case EBAction::Press:
+            Serial.println("press");
+            break;
+        case EBAction::Hold:
+            Serial.println("hold");
+            break;
+        case EBAction::Step:
+            Serial.println("step");
+            break;
+        case EBAction::Release:
+            Serial.print("release. steps: ");
+            Serial.print(eb.getSteps());
+            Serial.print(", press for: ");
+            Serial.print(eb.pressFor());
+            Serial.print(", hold for: ");
+            Serial.print(eb.holdFor());
+            Serial.print(", step for: ");
+            Serial.println(eb.stepFor());
+            break;
+        case EBAction::Click:
+            Serial.println("click");
+            break;
+        case EBAction::Clicks:
+            Serial.print("clicks ");
+            Serial.println(eb.getClicks());
+            break;
+        case EBAction::Turn:
+            Serial.print("turn ");
+            Serial.print(eb.dir());
+            Serial.print(" ");
+            Serial.print(eb.fast());
+            Serial.print(" ");
+            Serial.println(eb.pressing());
+            break;
+        case EBAction::ReleaseHold:
+            Serial.println("release hold");
+            break;
+        case EBAction::ReleaseHoldClicks:
+            Serial.print("release hold clicks ");
+            Serial.println(eb.getClicks());
+            break;
+        case EBAction::ReleaseStep:
+            Serial.println("release step");
+            break;
+        case EBAction::ReleaseStepClicks:
+            Serial.print("release step clicks ");
+            Serial.println(eb.getClicks());
+            break;
+        case EBAction::Timeout:
+            Serial.println("timeout");
+            break;
+        default: break;
+    }
+}
+
+void setup() {
+    Serial.begin(115200);
+    eb.attach(cb);
+}
+
+void loop() {
+    eb.tick();
+}

+ 114 - 0
.pio/libdeps/pico/EncButton/examples/demo/demo.ino

@@ -0,0 +1,114 @@
+// полное демо
+#include <Arduino.h>
+// #define EB_NO_FOR           // отключить поддержку pressFor/holdFor/stepFor и счётчик степов (экономит 2 байта оперативки)
+// #define EB_NO_CALLBACK      // отключить обработчик событий attach (экономит 2 байта оперативки)
+// #define EB_NO_COUNTER       // отключить счётчик энкодера (экономит 4 байта оперативки)
+// #define EB_NO_BUFFER        // отключить буферизацию энкодера (экономит 1 байт оперативки)
+
+// #define EB_DEB_TIME 50      // таймаут гашения дребезга кнопки (кнопка)
+// #define EB_CLICK_TIME 500   // таймаут ожидания кликов (кнопка)
+// #define EB_HOLD_TIME 600    // таймаут удержания (кнопка)
+// #define EB_STEP_TIME 200    // таймаут импульсного удержания (кнопка)
+// #define EB_FAST_TIME 30     // таймаут быстрого поворота (энкодер)
+// #define EB_TOUT_TIME 1000   // таймаут действия (кнопка и энкодер)
+
+#include <EncButton.h>
+EncButton eb(2, 3, 4);
+// EncButton eb(2, 3, 4, INPUT); // + режим пинов энкодера
+// EncButton eb(2, 3, 4, INPUT, INPUT_PULLUP); // + режим пинов кнопки
+
+void setup() {
+    Serial.begin(115200);
+
+    // показаны значения по умолчанию
+    eb.setBtnLevel(LOW);
+    eb.setClickTimeout(500);
+    eb.setDebTimeout(50);
+    eb.setHoldTimeout(600);
+    eb.setStepTimeout(200);
+    eb.setTimeout(1000);
+
+    eb.setEncReverse(0);
+    eb.setEncType(EB_STEP4_LOW);
+    eb.setFastTimeout(30);
+
+    // сбросить счётчик энкодера
+    eb.counter = 0;
+}
+
+void loop() {
+    eb.tick();
+
+    // обработка поворота общая
+    if (eb.turn()) {
+        Serial.print("turn: dir ");
+        Serial.print(eb.dir());
+        Serial.print(", fast ");
+        Serial.print(eb.fast());
+        Serial.print(", hold ");
+        Serial.print(eb.pressing());
+        Serial.print(", counter ");
+        Serial.print(eb.counter);
+        Serial.print(", clicks ");
+        Serial.println(eb.getClicks());
+    }
+
+    // обработка поворота раздельная
+    if (eb.left()) Serial.println("left");
+    if (eb.right()) Serial.println("right");
+    if (eb.leftH()) Serial.println("leftH");
+    if (eb.rightH()) Serial.println("rightH");
+
+    // кнопка
+    if (eb.press()) Serial.println("press");
+    if (eb.click()) Serial.println("click");
+
+    if (eb.release()) {
+      Serial.println("release");
+
+      Serial.print("clicks: ");
+      Serial.print(eb.getClicks());
+      Serial.print(", steps: ");
+      Serial.print(eb.getSteps());
+      Serial.print(", press for: ");
+      Serial.print(eb.pressFor());
+      Serial.print(", hold for: ");
+      Serial.print(eb.holdFor());
+      Serial.print(", step for: ");
+      Serial.println(eb.stepFor());
+    }
+
+    // состояния
+    // Serial.println(eb.pressing());
+    // Serial.println(eb.holding());
+    // Serial.println(eb.busy());
+    // Serial.println(eb.waiting());
+
+    // таймаут
+    if (eb.timeout()) Serial.println("timeout!");
+
+    // удержание
+    if (eb.hold()) Serial.println("hold");
+    if (eb.hold(3)) Serial.println("hold 3");
+
+    // импульсное удержание
+    if (eb.step()) Serial.println("step");
+    if (eb.step(3)) Serial.println("step 3");
+
+    // отпущена после импульсного удержания
+    if (eb.releaseStep()) Serial.println("release step");
+    if (eb.releaseStep(3)) Serial.println("release step 3");
+
+    // отпущена после удержания
+    if (eb.releaseHold()) Serial.println("release hold");
+    if (eb.releaseHold(2)) Serial.println("release hold 2");
+
+    // проверка на количество кликов
+    if (eb.hasClicks(3)) Serial.println("has 3 clicks");
+
+    // вывести количество кликов
+    if (eb.hasClicks()) {
+        Serial.print("has clicks: ");
+        Serial.println(eb.getClicks());
+    }
+}

+ 27 - 0
.pio/libdeps/pico/EncButton/examples/double/double.ino

@@ -0,0 +1,27 @@
+// опрос одновременного нажатия двух кнопок как нажатия третьей кнопки (виртуальной)
+// библиотека сама сбросит события с первых двух кнопок, если они нажаты вместе
+
+#include <Arduino.h>
+#include <EncButton.h>
+
+Button b0(4);
+Button b1(5);
+VirtButton b2;  // виртуальная
+
+void setup() {
+    Serial.begin(115200);
+}
+
+void loop() {
+    b0.tick();
+    b1.tick();
+
+    // обработка одновременного нажатия двух кнопок
+    b2.tick(b0, b1);
+
+    if (b0.click()) Serial.println("b0 click");
+    if (b1.click()) Serial.println("b1 click");
+
+    if (b2.click()) Serial.println("b0+b1 click");
+    if (b2.step()) Serial.println("b0+b1 step");
+}

+ 78 - 0
.pio/libdeps/pico/EncButton/examples/doubleCallback/doubleCallback.ino

@@ -0,0 +1,78 @@
+// опрос одновременного нажатия двух кнопок как нажатия третьей кнопки
+// с корректным вызовом обработчиков
+
+#include <Arduino.h>
+#include <EncButton.h>
+
+Button b0(4);
+Button b1(5);
+MultiButton b12;  // виртуальная
+
+void decode(uint16_t action) {
+    switch (action) {
+        case EB_PRESS:
+            Serial.println("press");
+            break;
+        case EB_STEP:
+            Serial.println("step");
+            break;
+        case EB_RELEASE:
+            Serial.println("release");
+            break;
+        case EB_CLICK:
+            Serial.println("click");
+            break;
+        case EB_CLICKS:
+            Serial.println("clicks");
+            break;
+        case EB_REL_HOLD:
+            Serial.println("release hold");
+            break;
+        case EB_REL_HOLD_C:
+            Serial.println("release hold clicks ");
+            break;
+        case EB_REL_STEP:
+            Serial.println("release step");
+            break;
+        case EB_REL_STEP_C:
+            Serial.println("release step clicks ");
+            break;
+        case EB_TIMEOUT:
+            Serial.println("timeout");
+            break;
+    }
+}
+
+void setup() {
+    Serial.begin(115200);
+
+    // обработчики
+    b0.attach([]() {
+        uint16_t action = static_cast<VirtButton*>(EB_self)->action();
+        if (action != EB_HOLD) Serial.print("b0: ");
+        decode(action);
+    });
+
+    b1.attach([]() {
+        uint16_t action = static_cast<VirtButton*>(EB_self)->action();
+        if (action != EB_HOLD) Serial.print("b1: ");
+        decode(action);
+    });
+
+    b12.attach([]() {
+        uint16_t action = static_cast<VirtButton*>(EB_self)->action();
+        if (action != EB_HOLD) Serial.print("b0+b1: ");
+        decode(action);
+    });
+}
+
+void loop() {
+    // обработка одновременного нажатия двух кнопок
+    // обрабатываются все три кнопки
+    b12.tick(b0, b1);
+
+    // или вручную
+    if (b0.click()) Serial.println("b0 click");
+    if (b1.click()) Serial.println("b1 click");
+    if (b12.click()) Serial.println("b0+b1 click");
+}

+ 39 - 0
.pio/libdeps/pico/EncButton/examples/isr/isr.ino

@@ -0,0 +1,39 @@
+// энкодер и прерывания
+#include <Arduino.h>
+#include <EncButton.h>
+EncButton eb(2, 3, 4);
+
+/*
+// esp8266/esp32
+IRAM_ATTR void isr() {
+  eb.tickISR();
+}
+*/
+
+void isr() {
+  eb.tickISR();
+}
+
+void setup() {
+    Serial.begin(115200);
+    attachInterrupt(0, isr, CHANGE);
+    attachInterrupt(1, isr, CHANGE);
+    eb.setEncISR(true);
+}
+
+void loop() {
+    eb.tick();
+
+    if (eb.turn()) {
+        Serial.print("turn: dir ");
+        Serial.print(eb.dir());
+        Serial.print(", fast ");
+        Serial.print(eb.fast());
+        Serial.print(", hold ");
+        Serial.print(eb.pressing());
+        Serial.print(", counter ");
+        Serial.println(eb.counter);
+    }
+
+    delay(100);  // имитация загруженной программы
+}

+ 48 - 0
.pio/libdeps/pico/EncButton/examples/one_button_3_var/one_button_3_var.ino

@@ -0,0 +1,48 @@
+#include <Arduino.h>
+// используем одну КНОПКУ для удобного изменения трёх переменных
+// первая - один клик, затем удержание (нажал-отпустил-нажал-держим)
+// вторая - два клика, затем удержание
+// третья - три клика, затем удержание
+// смотри монитор порта
+
+#include <EncButton.h>
+Button btn(4);
+
+// переменные для изменения
+int val_a, val_b, val_c;
+
+// шаги изменения (signed)
+int8_t step_a = 1;
+int8_t step_b = 5;
+int8_t step_c = 10;
+
+void setup() {
+    Serial.begin(115200);
+}
+
+void loop() {
+    btn.tick();
+
+    // передаём количество предварительных кликов
+    if (btn.step(1)) {
+        val_a += step_a;
+        Serial.print("val_a: ");
+        Serial.println(val_a);
+    }
+    if (btn.step(2)) {
+        val_b += step_b;
+        Serial.print("val_b: ");
+        Serial.println(val_b);
+    }
+    if (btn.step(3)) {
+        val_c += step_c;
+        Serial.print("val_c: ");
+        Serial.println(val_c);
+    }
+
+    // разворачиваем шаг для изменения в обратную сторону
+    // передаём количество предварительных кликов
+    if (btn.releaseStep(1)) step_a = -step_a;
+    if (btn.releaseStep(2)) step_b = -step_b;
+    if (btn.releaseStep(3)) step_c = -step_c;
+}

+ 47 - 0
.pio/libdeps/pico/EncButton/examples/one_enc_3_var/one_enc_3_var.ino

@@ -0,0 +1,47 @@
+// управление тремя переменными при помощи энкодера:
+// - нащёлкай кнопкой нужную переменную (1, 2 или 3 клика)
+// - 1 переменная просто изменяется с постоянным шагом
+// - 2 переменная: шаг 1, при зажатой кнопке - шаг 5
+// - 3 переменная: шаг 1, при быстром вращении - шаг 5
+
+#include <Arduino.h>
+#include <EncButton.h>
+EncButton eb(2, 3, 4);
+
+int var1 = 0;
+int var2 = 0;
+int var3 = 0;
+uint8_t select = 1;  // выбранная переменная
+
+void setup() {
+    Serial.begin(115200);
+}
+
+void loop() {
+    eb.tick();
+
+    // выбор переменной для изменения
+    if (eb.hasClicks()) {
+        select = eb.getClicks();
+        Serial.println(String("Select: ") + select);
+    }
+
+    if (eb.turn()) {
+        // меняем переменную
+        switch (select) {
+            case 1:
+                // изменение с шагом 5
+                var1 += 5 * eb.dir();
+                break;
+            case 2:
+                // изменение с шагом 1, при зажатой кнопке шаг 5
+                var2 += (eb.pressing() ? 5 : 1) * eb.dir();
+                break;
+            case 3:
+                // изменение с шагом 1, при быстром вращении шаг 5
+                var3 += (eb.fast() ? 5 : 1) * eb.dir();
+                break;
+        }
+        Serial.println(String("vars ") + var1 + ',' + var2 + ',' + var3);
+    }
+}

+ 34 - 0
.pio/libdeps/pico/EncButton/examples/virtual_buttons/virtual_AnalogKey/virtual_AnalogKey.ino

@@ -0,0 +1,34 @@
+// пример работы в виртуальном режиме совместно с библиотекой AnalogKey
+// https://github.com/GyverLibs/AnalogKey
+
+#include <EncButton.h>
+VirtButton btn0;
+VirtButton btn1;
+
+#include <AnalogKey.h>
+// создаём массив значений сигналов с кнопок
+int16_t sigs[16] = {
+  1023, 927, 856, 783,
+  671,  632,  590,  560,
+  504,  480,  455,  440,
+  399,  319,  255,  230
+};
+
+// указываем пин, количество кнопок и массив значений
+AnalogKey<A0, 16, sigs> keys;
+
+void setup() {
+  Serial.begin(9600);
+}
+
+void loop() {
+  btn0.tick(keys.status(0));
+  btn1.tick(keys.status(1));
+
+  // забираем действия с кнопок
+  if (btn0.click()) Serial.println("click 0");
+  if (btn0.hold()) Serial.println("hold 0");
+
+  if (btn1.press()) Serial.println("press 1");
+  if (btn1.step()) Serial.println("step 1");
+}

+ 49 - 0
.pio/libdeps/pico/EncButton/examples/virtual_buttons/virtual_SimpleKeypad/virtual_SimpleKeypad.ino

@@ -0,0 +1,49 @@
+// пример работы в виртуальном режиме совместно с библиотекой SimpleKeypad
+// https://github.com/maximebohrer/SimpleKeypad
+
+#include <EncButton.h>
+VirtButton btn0;
+VirtButton btn1;
+
+// пины подключения (по порядку штекера)
+byte colPins[] = {7, 6, 5, 4};
+byte rowPins[] = {11, 10, 9, 8};
+
+// массив имён кнопок
+char keys[4][4] = {
+  {'1', '2', '3', 'A'},
+  {'4', '5', '6', 'B'},
+  {'7', '8', '9', 'C'},
+  {'*', '0', '#', 'D'}
+};
+
+#include <SimpleKeypad.h>
+SimpleKeypad pad((char*)keys, rowPins, colPins, 4, 4);
+
+void setup() {
+  Serial.begin(9600);
+  btn0.setDebTimeout(0);
+  btn1.setDebTimeout(0);
+}
+
+void loop() {
+  btn0.tick(0);
+  btn1.tick(0);
+  
+  // тикаем все кнопки, передавая сравнение с кодом кнопки в цикле
+  // делаем это по таймеру, чтобы не опрашивать клавиатуру постоянно
+  static uint32_t tmr;
+  if (millis() - tmr >= 10) {
+    tmr = millis();
+    char key = pad.scan();
+    btn0.tick(key == '1');
+    btn1.tick(key == '2');
+  }
+
+  // забираем действия с кнопок
+  if (btn0.click()) Serial.println("click 0");
+  if (btn0.hold()) Serial.println("hold 0");
+
+  if (btn1.press()) Serial.println("press 1");
+  if (btn1.step()) Serial.println("step 1");
+}

+ 47 - 0
.pio/libdeps/pico/EncButton/examples/virtual_buttons/virtual_SimpleKeypad_array/virtual_SimpleKeypad_array.ino

@@ -0,0 +1,47 @@
+// пример работы в виртуальном режиме совместно с библиотекой SimpleKeypad
+// https://github.com/maximebohrer/SimpleKeypad
+// передаём EncButton сразу всю клавиатуру через массивы и циклы
+
+#include <EncButton.h>
+VirtButton btn[16];
+
+// пины подключения (по порядку штекера)
+byte colPins[] = {7, 6, 5, 4};
+byte rowPins[] = {11, 10, 9, 8};
+
+// массив имён кнопок
+char keys[4][4] = {
+  {'1', '2', '3', 'A'},
+  {'4', '5', '6', 'B'},
+  {'7', '8', '9', 'C'},
+  {'*', '0', '#', 'D'}
+};
+
+#include <SimpleKeypad.h>
+SimpleKeypad pad((char*)keys, rowPins, colPins, 4, 4);
+
+void setup() {
+  Serial.begin(9600);
+  for (int i = 0; i < 16; i++) btn[i].setDebTimeout(0);
+}
+
+void loop() {
+  for (int i = 0; i < 16; i++) btn[i].tick(0);
+  
+  // массово тикаем все кнопки, передавая сравнение с кодом кнопки в цикле
+  // делаем это по таймеру, чтобы не опрашивать клавиатуру постоянно
+  static uint32_t tmr;
+  if (millis() - tmr >= 10) {
+    tmr = millis();
+    char key = pad.scan();
+    char* keysPtr = (char*)keys;  // указатель для удобства опроса
+    for (int i = 0; i < 16; i++) btn[i].tick(key == keysPtr[i]);
+  }
+
+  // забираем действия с кнопок
+  if (btn[0].click()) Serial.println("click 0");
+  if (btn[0].hold()) Serial.println("hold 0");
+
+  if (btn[1].press()) Serial.println("press 1");
+  if (btn[1].step()) Serial.println("step 1");
+}

+ 127 - 0
.pio/libdeps/pico/EncButton/keywords.txt

@@ -0,0 +1,127 @@
+#######################################
+# Syntax Coloring Map For EncButton
+#######################################
+
+#######################################
+# Datatypes (KEYWORD1)
+#######################################
+EncButton	KEYWORD1
+EncButtonT	KEYWORD1
+VirtEncButton	KEYWORD1
+Button	KEYWORD1
+ButtonT	KEYWORD1
+VirtEncoder	KEYWORD1
+VirtButton	KEYWORD1
+MultiButton	KEYWORD1
+
+EB_NO_COUNTER	KEYWORD1
+EB_NO_BUFFER	KEYWORD1
+EB_NO_CALLBACK	KEYWORD1
+EB_NO_FOR	KEYWORD1
+
+EB_DEB_TIME	KEYWORD1
+EB_CLICK_TIME	KEYWORD1
+EB_HOLD_TIME	KEYWORD1
+EB_STEP_TIME	KEYWORD1
+EB_FAST_TIME	KEYWORD1
+
+#######################################
+# Methods and Functions (KEYWORD2)
+#######################################
+
+EB_read	KEYWORD2
+EB_uptime	KEYWORD2
+EB_mode	KEYWORD2
+
+setHoldTimeout	KEYWORD2
+setStepTimeout	KEYWORD2
+setClickTimeout	KEYWORD2
+setDebTimeout	KEYWORD2
+setTimeout	KEYWORD2
+setBtnLevel	KEYWORD2
+reset	KEYWORD2
+clear	KEYWORD2
+press	KEYWORD2
+release	KEYWORD2
+click	KEYWORD2
+pressing	KEYWORD2
+hold	KEYWORD2
+holding	KEYWORD2
+step	KEYWORD2
+hasClicks	KEYWORD2
+getClicks	KEYWORD2
+getSteps	KEYWORD2
+releaseHold	KEYWORD2
+releaseStep	KEYWORD2
+releaseHoldStep	KEYWORD2
+timeout	KEYWORD2
+timeoutState	KEYWORD2
+waiting	KEYWORD2
+busy	KEYWORD2
+action	KEYWORD2
+getAction	KEYWORD2
+attach	KEYWORD2
+detach	KEYWORD2
+pressISR	KEYWORD2
+pressFor	KEYWORD2
+holdFor	KEYWORD2
+stepFor	KEYWORD2
+
+setEncReverse	KEYWORD2
+setEncType	KEYWORD2
+initEnc	KEYWORD2
+
+setFastTimeout	KEYWORD2
+setEncISR	KEYWORD2
+turn	KEYWORD2
+turnH	KEYWORD2
+right	KEYWORD2
+left	KEYWORD2
+rightH	KEYWORD2
+leftH	KEYWORD2
+encHolding	KEYWORD2
+dir	KEYWORD2
+fast	KEYWORD2
+
+pollEnc	KEYWORD2
+init	KEYWORD2
+tickRaw	KEYWORD2
+tickISR	KEYWORD2
+tick	KEYWORD2
+readBtn	KEYWORD2
+readEnc	KEYWORD2
+
+#######################################
+# Constants (LITERAL1)
+#######################################	
+EB_PRESS	LITERAL1
+EB_HOLD	LITERAL1
+EB_STEP	LITERAL1
+EB_RELEASE	LITERAL1
+EB_CLICK	LITERAL1
+EB_CLICKS	LITERAL1
+EB_TURN	LITERAL1
+EB_REL_HOLD	LITERAL1
+EB_REL_HOLD_C	LITERAL1
+EB_REL_STEP	LITERAL1
+EB_REL_STEP_C	LITERAL1
+EB_TIMEOUT	LITERAL1
+
+EB_STEP4_LOW	LITERAL1
+EB_STEP4_HIGH	LITERAL1
+EB_STEP2	LITERAL1
+EB_STEP1	LITERAL1
+
+None	LITERAL1
+Press	LITERAL1
+Hold	LITERAL1
+Step	LITERAL1
+Release	LITERAL1
+Click	LITERAL1
+Clicks	LITERAL1
+Turn	LITERAL1
+ReleaseHold	LITERAL1
+ReleaseHoldClicks	LITERAL1
+ReleaseStep	LITERAL1
+ReleaseStepClicks	LITERAL1
+Timeout	LITERAL1

+ 10 - 0
.pio/libdeps/pico/EncButton/library.properties

@@ -0,0 +1,10 @@
+name=EncButton
+version=3.7.2
+author=AlexGyver <alex@alexgyver.ru>
+maintainer=AlexGyver <alex@alexgyver.ru>
+sentence=Light and powerful library for button and encoder operation for Arduino
+paragraph=Debounce, click count, hold, step hold mode and many more. Maximum possibilities for button and encoder
+category=Sensors
+url=https://github.com/GyverLibs/EncButton
+architectures=*
+depends=GyverIO

+ 10 - 0
.pio/libdeps/pico/EncButton/src/EncButton.h

@@ -0,0 +1,10 @@
+#pragma once
+#include <Arduino.h>
+
+#include "core/Button.h"
+#include "core/EncButton.h"
+#include "core/Encoder.h"
+#include "core/MultiButton.h"
+#include "core/VirtButton.h"
+#include "core/VirtEncButton.h"
+#include "core/VirtEncoder.h"

+ 70 - 0
.pio/libdeps/pico/EncButton/src/core/Button.h

@@ -0,0 +1,70 @@
+#pragma once
+#include <Arduino.h>
+
+#include "VirtButton.h"
+#include "io.h"
+
+// ============= VAR PIN =============
+class Button : public VirtButton {
+   public:
+    Button(uint8_t npin = 0, uint8_t mode = INPUT_PULLUP, uint8_t btnLevel = LOW) {
+        init(npin, mode, btnLevel);
+    }
+
+    // указать пин и его режим работы
+    void init(uint8_t npin = 0, uint8_t mode = INPUT_PULLUP, uint8_t btnLevel = LOW) {
+        pin = npin;
+        EB_mode(pin, mode);
+        setBtnLevel(btnLevel);
+    }
+
+    // прочитать текущее значение кнопки (без дебаунса)
+    bool read() {
+        return EB_read(pin) ^ bf.read(EB_INV);
+    }
+
+    // функция обработки, вызывать в loop
+    bool tick() {
+        return VirtButton::tick(EB_read(pin));
+    }
+
+    // обработка кнопки без сброса событий и вызова коллбэка
+    bool tickRaw() {
+        return VirtButton::tickRaw(EB_read(pin));
+    }
+
+   private:
+    uint8_t pin;
+};
+
+// ============= TEMPLATE PIN =============
+template <uint8_t PIN>
+class ButtonT : public VirtButton {
+   public:
+    ButtonT(uint8_t mode = INPUT_PULLUP, uint8_t btnLevel = LOW) {
+        init(mode, btnLevel);
+    }
+
+    // указать режим работы пина
+    void init(uint8_t mode = INPUT_PULLUP, uint8_t btnLevel = LOW) {
+        EB_mode(PIN, mode);
+        setBtnLevel(btnLevel);
+    }
+
+    // прочитать текущее значение кнопки (без дебаунса)
+    bool read() {
+        return EB_read(PIN) ^ bf.read(EB_INV);
+    }
+
+    // функция обработки, вызывать в loop
+    bool tick() {
+        return VirtButton::tick(EB_read(PIN));
+    }
+
+    // обработка кнопки без сброса событий и вызова коллбэка
+    bool tickRaw() {
+        return VirtButton::tickRaw(EB_read(PIN));
+    }
+
+   private:
+};

+ 110 - 0
.pio/libdeps/pico/EncButton/src/core/EncButton.h

@@ -0,0 +1,110 @@
+#pragma once
+#include <Arduino.h>
+
+#include "VirtEncButton.h"
+#include "io.h"
+
+// ===================== CLASS =====================
+class EncButton : public VirtEncButton {
+   public:
+    // настроить пины (энк, энк, кнопка, pinmode энк, pinmode кнопка)
+    EncButton(uint8_t encA = 0, uint8_t encB = 0, uint8_t btn = 0, uint8_t modeEnc = INPUT, uint8_t modeBtn = INPUT_PULLUP, uint8_t btnLevel = LOW) {
+        init(encA, encB, btn, modeEnc, modeBtn, btnLevel);
+    }
+
+    // настроить пины (энк, энк, кнопка, pinmode энк, pinmode кнопка)
+    void init(uint8_t encA = 0, uint8_t encB = 0, uint8_t btn = 0, uint8_t modeEnc = INPUT, uint8_t modeBtn = INPUT_PULLUP, uint8_t btnLevel = LOW) {
+        e0 = encA;
+        e1 = encB;
+        b = btn;
+        EB_mode(e0, modeEnc);
+        EB_mode(e1, modeEnc);
+        EB_mode(b, modeBtn);
+        setBtnLevel(btnLevel);
+        initEnc(readEnc());
+    }
+
+    // ====================== TICK ======================
+    // функция обработки для вызова в прерывании энкодера
+    int8_t tickISR() {
+        return VirtEncButton::tickISR(readEnc());
+    }
+
+    // функция обработки, вызывать в loop
+    bool tick() {
+        if (ef.read(EB_EISR)) return VirtEncButton::tick(EB_read(b));
+        else return VirtEncButton::tick(readEnc(), EB_read(b));
+    }
+
+    // функция обработки без сброса событий
+    bool tickRaw() {
+        if (ef.read(EB_EISR)) return VirtEncButton::tickRaw(EB_read(b));
+        else return VirtEncButton::tickRaw(readEnc(), EB_read(b));
+    }
+
+    // ====================== READ ======================
+    // прочитать значение кнопки
+    bool readBtn() {
+        return EB_read(b) ^ bf.read(EB_INV);
+    }
+
+    // прочитать значение энкодера
+    int8_t readEnc() {
+        return EB_read(e0) | (EB_read(e1) << 1);
+    }
+
+    // ===================== PRIVATE =====================
+   private:
+    uint8_t e0, e1, b;
+};
+
+// ===================== T CLASS =====================
+template <uint8_t ENCA, uint8_t ENCB, uint8_t BTN>
+class EncButtonT : public VirtEncButton {
+   public:
+    // настроить пины (энк, энк, кнопка, pinmode энк, pinmode кнопка)
+    EncButtonT(uint8_t modeEnc = INPUT, uint8_t modeBtn = INPUT_PULLUP, uint8_t btnLevel = LOW) {
+        init(modeEnc, modeBtn, btnLevel);
+    }
+
+    // настроить пины (pinmode энк, pinmode кнопка)
+    void init(uint8_t modeEnc = INPUT, uint8_t modeBtn = INPUT_PULLUP, uint8_t btnLevel = LOW) {
+        EB_mode(ENCA, modeEnc);
+        EB_mode(ENCB, modeEnc);
+        EB_mode(BTN, modeBtn);
+        setBtnLevel(btnLevel);
+        initEnc(readEnc());
+    }
+
+    // ====================== TICK ======================
+    // функция обработки для вызова в прерывании энкодера
+    int8_t tickISR() {
+        return VirtEncButton::tickISR(readEnc());
+    }
+
+    // функция обработки, вызывать в loop
+    bool tick() {
+        if (ef.read(EB_EISR)) return VirtEncButton::tick(EB_read(BTN));
+        else return VirtEncButton::tick(readEnc(), EB_read(BTN));
+    }
+
+    // функция обработки без сброса событий
+    bool tickRaw() {
+        if (ef.read(EB_EISR)) return VirtEncButton::tickRaw(EB_read(BTN));
+        else return VirtEncButton::tickRaw(readEnc(), EB_read(BTN));
+    }
+
+    // ====================== READ ======================
+    // прочитать значение кнопки
+    bool readBtn() {
+        return EB_read(BTN) ^ bf.read(EB_INV);
+    }
+
+    // прочитать значение энкодера
+    int8_t readEnc() {
+        return EB_read(ENCA) | (EB_read(ENCB) << 1);
+    }
+
+    // ===================== PRIVATE =====================
+   private:
+};

+ 89 - 0
.pio/libdeps/pico/EncButton/src/core/Encoder.h

@@ -0,0 +1,89 @@
+#pragma once
+#include <Arduino.h>
+
+#include "VirtEncoder.h"
+#include "io.h"
+
+// ============= VAR PIN =============
+class Encoder : public VirtEncoder {
+   public:
+    // указать пины и их режим работы
+    Encoder(uint8_t encA = 0, uint8_t encB = 0, uint8_t mode = INPUT) {
+        init(encA, encB, mode);
+    }
+
+    // указать пины и их режим работы
+    void init(uint8_t encA = 0, uint8_t encB = 0, uint8_t mode = INPUT) {
+        e0 = encA;
+        e1 = encB;
+        EB_mode(e0, mode);
+        EB_mode(e1, mode);
+        initEnc(readEnc());
+    }
+
+    // функция обработки для вызова в прерывании энкодера
+    int8_t tickISR() {
+        return VirtEncoder::tickISR(readEnc());
+    }
+
+    // функция обработки, вызывать в loop
+    int8_t tick() {
+        if (ef.read(EB_EISR)) return VirtEncoder::tick();
+        else return VirtEncoder::tick(readEnc());
+    }
+
+    // обработка без сброса события поворота
+    int8_t tickRaw() {
+        if (ef.read(EB_EISR)) return VirtEncoder::tickRaw();
+        else return VirtEncoder::tickRaw(readEnc());
+    }
+
+   private:
+    uint8_t e0, e1;
+
+    // прочитать значение энкодера
+    int8_t readEnc() {
+        return EB_read(e0) | (EB_read(e1) << 1);
+    }
+};
+
+// ============= TEMPLATE PIN =============
+template <uint8_t ENCA, uint8_t ENCB>
+class EncoderT : public VirtEncoder {
+   public:
+    // указать режим работы пинов
+    EncoderT(uint8_t mode = INPUT) {
+        init(mode);
+    }
+
+    // указать режим работы пинов
+    void init(uint8_t mode = INPUT) {
+        EB_mode(ENCA, mode);
+        EB_mode(ENCB, mode);
+        initEnc(readEnc());
+    }
+
+    // функция обработки для вызова в прерывании энкодера
+    int8_t tickISR() {
+        return VirtEncoder::tickISR(readEnc());
+    }
+
+    // функция обработки, вызывать в loop
+    int8_t tick() {
+        if (ef.read(EB_EISR)) return VirtEncoder::tick();
+        else return VirtEncoder::tick(readEnc());
+    }
+
+    // обработка без сброса события поворота
+    int8_t tickRaw() {
+        if (ef.read(EB_EISR)) return VirtEncoder::tickRaw();
+        else return VirtEncoder::tickRaw(readEnc());
+    }
+
+    // прочитать значение энкодера
+    int8_t readEnc() {
+        return EB_read(ENCA) | (EB_read(ENCB) << 1);
+    }
+
+   private:
+};

+ 29 - 0
.pio/libdeps/pico/EncButton/src/core/MultiButton.h

@@ -0,0 +1,29 @@
+#pragma once
+#include <Arduino.h>
+
+#include "VirtButton.h"
+
+class MultiButton : public VirtButton {
+   public:
+    template <typename T0, typename T1>
+    bool tick(T0& b0, T1& b1) {
+        b0.clear();
+        b1.clear();
+        b0.tickRaw();
+        b1.tickRaw();
+
+        if (bf.read(EB_BOTH)) {
+            if (!b0.pressing() && !b1.pressing()) bf.clear(EB_BOTH);
+            if (!b0.pressing()) b0.reset();
+            if (!b1.pressing()) b1.reset();
+            b0.clear();
+            b1.clear();
+            return VirtButton::tick(true);
+        } else {
+            if (b0.pressing() && b1.pressing()) bf.set(EB_BOTH);
+            b0.call();
+            b1.call();
+            return VirtButton::tick(false);
+        }
+    }
+};

+ 577 - 0
.pio/libdeps/pico/EncButton/src/core/VirtButton.h

@@ -0,0 +1,577 @@
+#pragma once
+#include <Arduino.h>
+
+#include "flags.h"
+#include "io.h"
+#include "self.h"
+
+#ifndef __AVR__
+#include <functional>
+#endif
+
+// ===================== FLAGS ======================
+#define EB_PRESS (1 << 0)        // нажатие на кнопку
+#define EB_HOLD (1 << 1)         // кнопка удержана
+#define EB_STEP (1 << 2)         // импульсное удержание
+#define EB_RELEASE (1 << 3)      // кнопка отпущена
+#define EB_CLICK (1 << 4)        // одиночный клик
+#define EB_CLICKS (1 << 5)       // сигнал о нескольких кликах
+#define EB_TURN (1 << 6)         // поворот энкодера
+#define EB_REL_HOLD (1 << 7)     // кнопка отпущена после удержания
+#define EB_REL_HOLD_C (1 << 8)   // кнопка отпущена после удержания с предв. кликами
+#define EB_REL_STEP (1 << 9)     // кнопка отпущена после степа
+#define EB_REL_STEP_C (1 << 10)  // кнопка отпущена после степа с предв. кликами
+#define EB_TIMEOUT (1 << 11)     // прошёл таймаут после нажатия кнопки или поворота энкодера
+
+enum class EBAction {
+    None = 0,
+    Press = EB_PRESS,
+    Hold = EB_HOLD,
+    Step = EB_STEP,
+    Release = EB_RELEASE,
+    Click = EB_CLICK,
+    Clicks = EB_CLICKS,
+    Turn = EB_TURN,
+    ReleaseHold = EB_REL_HOLD,
+    ReleaseHoldClicks = EB_REL_HOLD_C,
+    ReleaseStep = EB_REL_STEP,
+    ReleaseStepClicks = EB_REL_STEP_C,
+    Timeout = EB_TIMEOUT,
+};
+
+// =================== TOUT BUILD ===================
+#define EB_SHIFT 4
+
+// таймаут антидребезга, мс
+#ifdef EB_DEB_TIME
+#define EB_DEB_T (EB_DEB_TIME)
+#else
+#endif
+
+// таймаут между клтками, мс
+#ifdef EB_CLICK_TIME
+#define EB_CLICK_T (EB_CLICK_TIME)
+#define EB_GET_CLICK_TIME() ((uint16_t)EB_CLICK_T)
+#else
+#define EB_GET_CLICK_TIME() (uint16_t)(EB_CLICK_T << EB_SHIFT)
+#endif
+
+// таймаут удержания, мс
+#ifdef EB_HOLD_TIME
+#define EB_HOLD_T (EB_HOLD_TIME)
+#define EB_GET_HOLD_TIME() ((uint16_t)EB_HOLD_T)
+#else
+#define EB_GET_HOLD_TIME() (uint16_t)(EB_HOLD_T << EB_SHIFT)
+#endif
+
+// период степа, мс
+#ifdef EB_STEP_TIME
+#define EB_STEP_T (EB_STEP_TIME)
+#define EB_GET_STEP_TIME() ((uint16_t)EB_STEP_T)
+#else
+#define EB_GET_STEP_TIME() (uint16_t)(EB_STEP_T << EB_SHIFT)
+#endif
+
+// время таймаута, мс
+#ifdef EB_TOUT_TIME
+#define EB_TOUT_T (EB_TOUT_TIME)
+#define EB_GET_TOUT_TIME() ((uint16_t)EB_TOUT_T)
+#else
+#define EB_GET_TOUT_TIME() (uint16_t)(EB_TOUT_T << EB_SHIFT)
+#endif
+
+// =================== PACK FLAGS ===================
+#define EB_CLKS_R (1 << 0)
+#define EB_PRS_R (1 << 1)
+#define EB_HLD_R (1 << 2)
+#define EB_STP_R (1 << 3)
+#define EB_REL_R (1 << 4)
+
+#define EB_PRS (1 << 5)
+#define EB_HLD (1 << 6)
+#define EB_STP (1 << 7)
+#define EB_REL (1 << 8)
+
+#define EB_BUSY (1 << 9)
+#define EB_DEB (1 << 10)
+#define EB_TOUT (1 << 11)
+#define EB_INV (1 << 12)
+#define EB_BOTH (1 << 13)
+#define EB_BISR (1 << 14)
+
+#define EB_EHLD (1 << 15)
+
+// базовый класс кнопки
+class VirtButton {
+#ifdef __AVR__
+    typedef void (*ActionHandler)();
+#else
+    typedef std::function<void()> ActionHandler;
+#endif
+
+   public:
+    // ====================== SET ======================
+    // установить таймаут удержания, умолч. 600 (макс. 4000 мс)
+    void setHoldTimeout(const uint16_t tout) {
+#ifndef EB_HOLD_TIME
+        EB_HOLD_T = tout >> EB_SHIFT;
+#endif
+    }
+
+    // установить таймаут импульсного удержания, умолч. 200 (макс. 4000 мс)
+    void setStepTimeout(const uint16_t tout) {
+#ifndef EB_STEP_TIME
+        EB_STEP_T = tout >> EB_SHIFT;
+#endif
+    }
+
+    // установить таймаут ожидания кликов, умолч. 500 (макс. 4000 мс)
+    void setClickTimeout(const uint16_t tout) {
+#ifndef EB_CLICK_TIME
+        EB_CLICK_T = tout >> EB_SHIFT;
+#endif
+    }
+
+    // установить таймаут антидребезга, умолч. 50 (макс. 255 мс)
+    void setDebTimeout(const uint8_t tout) {
+#ifndef EB_DEB_TIME
+        EB_DEB_T = tout;
+#endif
+    }
+
+    // установить время таймаута, умолч. 1000 (макс. 4000 мс)
+    void setTimeout(const uint16_t tout) {
+#ifndef EB_TOUT_TIME
+        EB_TOUT_T = tout >> EB_SHIFT;
+#endif
+    }
+
+    // установить уровень кнопки (HIGH - кнопка замыкает VCC, LOW - замыкает GND)
+    void setBtnLevel(const bool level) {
+        bf.write(EB_INV, !level);
+    }
+
+    // кнопка нажата в прерывании (не учитывает btnLevel!)
+    void pressISR() {
+        if (!bf.read(EB_DEB)) tmr = EB_uptime();
+        bf.set(EB_DEB | EB_BISR);
+    }
+
+    // сбросить системные флаги (принудительно закончить обработку)
+    void reset() {
+        clicks = 0;
+        bf.clear(~EB_INV);  // все кроме EB_INV
+    }
+
+    // принудительно сбросить флаги событий
+    void clear(bool resetTout = false) {
+        if (resetTout && bf.read(EB_TOUT)) bf.clear(EB_TOUT);
+        if (bf.read(EB_CLKS_R)) clicks = 0;
+        if (bf.read(EB_CLKS_R | EB_STP_R | EB_PRS_R | EB_HLD_R | EB_REL_R)) {
+            bf.clear(EB_CLKS_R | EB_STP_R | EB_PRS_R | EB_HLD_R | EB_REL_R);
+        }
+    }
+
+    // игнорировать все события до отпускания кнопки
+    void skipEvents() {
+        bf.set(EB_EHLD);
+    }
+
+    // подключить функцию-обработчик событий (вида void f())
+    void attach(ActionHandler handler) {
+#ifndef EB_NO_CALLBACK
+        cb = handler;
+#endif
+    }
+
+    // отключить функцию-обработчик событий
+    void detach() {
+#ifndef EB_NO_CALLBACK
+        cb = nullptr;
+#endif
+    }
+
+    // ====================== GET ======================
+    // кнопка нажата [событие]
+    bool press() {
+        return bf.read(EB_PRS_R);
+    }
+
+    // кнопка нажата с предварительными кликами [событие]
+    bool press(const uint8_t num) {
+        return (clicks == num) && press();
+    }
+
+    // кнопка отпущена (в любом случае) [событие]
+    bool release() {
+        return bf.eq(EB_REL_R | EB_REL, EB_REL_R | EB_REL);
+    }
+
+    // кнопка отпущена (в любом случае) с предварительными кликами [событие]
+    bool release(const uint8_t num) {
+        return (clicks == num) && release();
+    }
+
+    // клик по кнопке (отпущена без удержания) [событие]
+    bool click() {
+        return bf.eq(EB_REL_R | EB_REL | EB_HLD, EB_REL_R);
+    }
+
+    // клик по кнопке (отпущена без удержания) с предварительными кликами [событие]
+    bool click(const uint8_t num) {
+        return (clicks == num) && click();
+    }
+
+    // кнопка зажата (между press() и release()) [состояние]
+    bool pressing() {
+        return bf.read(EB_PRS);
+    }
+
+    // кнопка зажата (между press() и release()) с предварительными кликами [состояние]
+    bool pressing(const uint8_t num) {
+        return (clicks == num) && pressing();
+    }
+
+    // кнопка была удержана (больше таймаута) [событие]
+    bool hold() {
+        return bf.read(EB_HLD_R);
+    }
+
+    // кнопка была удержана (больше таймаута) с предварительными кликами [событие]
+    bool hold(const uint8_t num) {
+        return (clicks == num) && hold();
+    }
+
+    // кнопка удерживается (больше таймаута) [состояние]
+    bool holding() {
+        return bf.eq(EB_PRS | EB_HLD, EB_PRS | EB_HLD);
+    }
+
+    // кнопка удерживается (больше таймаута) с предварительными кликами [состояние]
+    bool holding(const uint8_t num) {
+        return (clicks == num) && holding();
+    }
+
+    // импульсное удержание [событие]
+    bool step() {
+        return bf.read(EB_STP_R);
+    }
+
+    // импульсное удержание с предварительными кликами [событие]
+    bool step(const uint8_t num) {
+        return (clicks == num) && step();
+    }
+
+    // зафиксировано несколько кликов [событие]
+    bool hasClicks() {
+        return bf.eq(EB_CLKS_R | EB_HLD, EB_CLKS_R);
+    }
+
+    // зафиксировано указанное количество кликов [событие]
+    bool hasClicks(const uint8_t num) {
+        return (clicks == num) && hasClicks();
+    }
+
+    // получить количество кликов
+    uint8_t getClicks() {
+        return clicks;
+    }
+
+    // получить количество степов
+    uint16_t getSteps() {
+#ifndef EB_NO_FOR
+        return ftmr ? ((stepFor() + EB_GET_STEP_TIME() - 1) / EB_GET_STEP_TIME()) : 0;  // (x + y - 1) / y
+#endif
+        return 0;
+    }
+
+    // кнопка отпущена после удержания [событие]
+    bool releaseHold() {
+        return bf.eq(EB_REL_R | EB_REL | EB_HLD | EB_STP, EB_REL_R | EB_HLD);
+    }
+
+    // кнопка отпущена после удержания с предварительными кликами [событие]
+    bool releaseHold(const uint8_t num) {
+        return clicks == num && bf.eq(EB_CLKS_R | EB_HLD | EB_STP, EB_CLKS_R | EB_HLD);
+    }
+
+    // кнопка отпущена после импульсного удержания [событие]
+    bool releaseStep() {
+        return bf.eq(EB_REL_R | EB_REL | EB_STP, EB_REL_R | EB_STP);
+    }
+
+    // кнопка отпущена после импульсного удержания с предварительными кликами [событие]
+    bool releaseStep(const uint8_t num) {
+        return clicks == num && bf.eq(EB_CLKS_R | EB_STP, EB_CLKS_R | EB_STP);
+    }
+
+    // кнопка отпущена после удержания или импульсного удержания [событие]
+    bool releaseHoldStep() {
+        return releaseHold() || releaseStep();
+    }
+
+    // кнопка отпущена после удержания или импульсного удержания с предварительными кликами [событие]
+    bool releaseHoldStep(const uint8_t num) {
+        return releaseHold(num) || releaseStep(num);
+    }
+
+    // кнопка ожидает повторных кликов [состояние]
+    bool waiting() {
+        return clicks && bf.eq(EB_PRS | EB_REL, 0);
+    }
+
+    // идёт обработка [состояние]
+    bool busy() {
+        return bf.read(EB_BUSY);
+    }
+
+    // было действие с кнопки, вернёт код события [событие]
+    uint16_t action() {
+        switch (bf.mask(0b111111111)) {
+            case (EB_PRS | EB_PRS_R): return EB_PRESS;
+            case (EB_PRS | EB_HLD | EB_HLD_R): return EB_HOLD;
+            case (EB_PRS | EB_HLD | EB_STP | EB_STP_R): return EB_STEP;
+            case (EB_REL | EB_REL_R):
+            case (EB_REL | EB_REL_R | EB_HLD):
+            case (EB_REL | EB_REL_R | EB_HLD | EB_STP):
+                return EB_RELEASE;
+            case (EB_REL_R): return EB_CLICK;
+            case (EB_CLKS_R): return EB_CLICKS;
+            case (EB_REL_R | EB_HLD): return EB_REL_HOLD;
+            case (EB_CLKS_R | EB_HLD): return EB_REL_HOLD_C;
+            case (EB_REL_R | EB_HLD | EB_STP): return EB_REL_STEP;
+            case (EB_CLKS_R | EB_HLD | EB_STP): return EB_REL_STEP_C;
+        }
+        if (timeoutState()) return EB_TIMEOUT;
+        return 0;
+    }
+
+    // было действие с кнопки, вернёт код события [событие]
+    EBAction getAction() {
+        return (EBAction)action();
+    }
+
+    // ====================== TIME ======================
+    // после взаимодействия с кнопкой (или энкодером EncButton) время setTimeout, мс [событие]
+    bool timeout() {
+        if (timeoutState()) {
+            bf.clear(EB_TOUT);
+            return 1;
+        }
+        return 0;
+    }
+
+    // после взаимодействия с кнопкой (или энкодером EncButton) время setTimeout, мс [состояние]
+    bool timeoutState() {
+        return bf.read(EB_TOUT) && (uint16_t)((uint16_t)EB_uptime() - tmr) >= EB_GET_TOUT_TIME();
+    }
+
+    // время, которое кнопка удерживается (с начала нажатия), мс
+    uint16_t pressFor() {
+#ifndef EB_NO_FOR
+        if (ftmr) return (uint16_t)EB_uptime() - ftmr;
+#endif
+        return 0;
+    }
+
+    // кнопка удерживается дольше чем (с начала нажатия), мс [состояние]
+    bool pressFor(const uint16_t ms) {
+        return pressFor() > ms;
+    }
+
+    // время, которое кнопка удерживается (с начала удержания), мс
+    uint16_t holdFor() {
+#ifndef EB_NO_FOR
+        if (bf.read(EB_HLD)) return pressFor() - EB_GET_HOLD_TIME();
+#endif
+        return 0;
+    }
+
+    // кнопка удерживается дольше чем (с начала удержания), мс [состояние]
+    bool holdFor(const uint16_t ms) {
+        return holdFor() > ms;
+    }
+
+    // время, которое кнопка удерживается (с начала степа), мс
+    uint16_t stepFor() {
+#ifndef EB_NO_FOR
+        if (bf.read(EB_STP)) return pressFor() - EB_GET_HOLD_TIME() * 2;
+#endif
+        return 0;
+    }
+
+    // кнопка удерживается дольше чем (с начала степа), мс [состояние]
+    bool stepFor(uint16_t ms) {
+        return stepFor() > ms;
+    }
+
+    // ====================== POLL ======================
+    // обработка виртуальной кнопки как одновременное нажатие двух других кнопок
+    bool tick(VirtButton& b0, VirtButton& b1) {
+        if (bf.read(EB_BOTH)) {
+            if (!b0.pressing() && !b1.pressing()) bf.clear(EB_BOTH);
+            if (!b0.pressing()) b0.reset();
+            if (!b1.pressing()) b1.reset();
+            b0.clear();
+            b1.clear();
+            return tick(1);
+        } else {
+            if (b0.pressing() && b1.pressing()) bf.set(EB_BOTH);
+            return tick(0);
+        }
+    }
+
+    // обработка кнопки значением
+    bool tick(bool s) {
+        clear();
+        s = pollBtn(s);
+#ifndef EB_NO_CALLBACK
+        if (s || timeoutState()) call();
+#endif
+        return s;
+    }
+
+    // обработка кнопки без сброса событий и вызова коллбэка
+    bool tickRaw(const bool s) {
+        return pollBtn(s);
+    }
+
+    // вызвать обработчик
+    void call(bool force = false) {  // todo force заменить на флаг
+#ifndef EB_NO_CALLBACK
+        if (cb && (force || action())) {
+            if (cb) {
+                EB_self = this;
+                cb();
+                EB_self = nullptr;
+                timeout();  // todo clear tout
+            }
+        }
+#endif
+    }
+
+    uint8_t clicks;
+
+    // deprecated
+    void setButtonLevel(bool level) __attribute__((deprecated)) {
+        bf.write(EB_INV, !level);
+    }
+    
+    // после взаимодействия с кнопкой (или энкодером EncButton) прошло указанное время, мс [событие]
+    bool timeout(const uint16_t tout) /*__attribute__((deprecated))*/ {
+        if (timeoutState(tout)) {
+            bf.clear(EB_TOUT);
+            return 1;
+        }
+        return 0;
+    }
+
+    // после взаимодействия с кнопкой (или энкодером EncButton) прошло указанное время, мс [состояние]
+    bool timeoutState(const uint16_t tout) /*__attribute__((deprecated))*/ {
+        return (bf.read(EB_TOUT) && (uint16_t)((uint16_t)EB_uptime() - tmr) > tout);
+    }
+
+    // ====================== PRIVATE ======================
+   protected:
+    uint16_t tmr = 0;
+    encb::Flags<uint16_t> bf;
+
+#ifndef EB_NO_CALLBACK
+    ActionHandler cb = nullptr;
+#endif
+
+   private:
+#ifndef EB_NO_FOR
+    uint16_t ftmr = 0;
+#endif
+#ifndef EB_DEB_TIME
+    uint8_t EB_DEB_T = 50;
+#endif
+#ifndef EB_CLICK_TIME
+    uint8_t EB_CLICK_T = (500 >> EB_SHIFT);
+#endif
+#ifndef EB_HOLD_TIME
+    uint8_t EB_HOLD_T = (600 >> EB_SHIFT);
+#endif
+#ifndef EB_STEP_TIME
+    uint8_t EB_STEP_T = (200 >> EB_SHIFT);
+#endif
+#ifndef EB_TOUT_TIME
+    uint8_t EB_TOUT_T = (1000 >> EB_SHIFT);
+#endif
+
+    bool pollBtn(bool s) {
+        if (bf.read(EB_BISR)) {
+            bf.clear(EB_BISR);
+            s = 1;
+        } else s ^= bf.read(EB_INV);
+
+        if (!bf.read(EB_BUSY)) {
+            if (s) bf.set(EB_BUSY);
+            else return 0;
+        }
+
+        uint16_t ms = EB_uptime();
+        uint16_t deb = ms - tmr;
+
+        if (s) {                                         // кнопка нажата
+            if (!bf.read(EB_PRS)) {                      // кнопка не была нажата ранее
+                if (!bf.read(EB_DEB) && EB_DEB_T) {      // дебаунс ещё не сработал
+                    bf.set(EB_DEB);                      // будем ждать дебаунс
+                    tmr = ms;                            // сброс таймаута
+                } else {                                 // первое нажатие
+                    if (deb >= EB_DEB_T || !EB_DEB_T) {  // ждём EB_DEB_TIME
+                        bf.set(EB_PRS | EB_PRS_R);       // флаг на нажатие
+#ifndef EB_NO_FOR
+                        ftmr = ms;
+#endif
+                        tmr = ms;  // сброс таймаута
+                    }
+                }
+            } else {  // кнопка уже была нажата
+                if (!bf.read(EB_EHLD)) {
+                    if (!bf.read(EB_HLD)) {               // удержание ещё не зафиксировано
+                        if (deb >= EB_GET_HOLD_TIME()) {  // ждём EB_HOLD_TIME - это удержание
+                            bf.set(EB_HLD_R | EB_HLD);    // флаг что было удержание
+                            tmr = ms;                     // сброс таймаута
+                        }
+                    } else {  // удержание зафиксировано
+                        if (deb >= (uint16_t)(bf.read(EB_STP) ? EB_GET_STEP_TIME() : EB_GET_HOLD_TIME())) {
+                            bf.set(EB_STP | EB_STP_R);  // флаг степ
+                            tmr = ms;                   // сброс таймаута
+                        }
+                    }
+                }
+            }
+        } else {                                       // кнопка не нажата
+            if (bf.read(EB_PRS)) {                     // но была нажата
+                if (deb >= EB_DEB_T) {                 // ждём EB_DEB_TIME
+                    if (!bf.read(EB_HLD)) clicks++;    // не удерживали - это клик
+                    if (bf.read(EB_EHLD)) clicks = 0;  //
+                    bf.set(EB_REL | EB_REL_R);         // флаг release
+                    bf.clear(EB_PRS);                  // кнопка отпущена
+                }
+            } else if (bf.read(EB_REL)) {
+                if (!bf.read(EB_EHLD)) {
+                    bf.set(EB_REL_R);  // флаг releaseHold / releaseStep
+                }
+                bf.clear(EB_REL | EB_EHLD);
+                tmr = ms;                                                                       // сброс таймаута
+            } else if (clicks) {                                                                // есть клики, ждём EB_CLICK_TIME
+                if (bf.read(EB_HLD | EB_STP) || deb >= EB_GET_CLICK_TIME()) bf.set(EB_CLKS_R);  // флаг clicks
+#ifndef EB_NO_FOR
+                else if (ftmr) ftmr = 0;
+#endif
+            } else if (bf.read(EB_BUSY)) {
+                bf.clear(EB_HLD | EB_STP | EB_BUSY);
+                bf.set(EB_TOUT);
+#ifndef EB_NO_FOR
+                ftmr = 0;
+#endif
+                tmr = ms;  // test!!
+            }
+            if (bf.read(EB_DEB)) bf.clear(EB_DEB);  // сброс ожидания нажатия (дебаунс)
+        }
+        return bf.read(EB_CLKS_R | EB_PRS_R | EB_HLD_R | EB_STP_R | EB_REL_R);
+    }
+};

+ 187 - 0
.pio/libdeps/pico/EncButton/src/core/VirtEncButton.h

@@ -0,0 +1,187 @@
+#pragma once
+#include <Arduino.h>
+
+#include "VirtButton.h"
+#include "VirtEncoder.h"
+#include "io.h"
+
+#ifdef EB_FAST_TIME
+#define EB_FAST_T (EB_FAST_TIME)
+#endif
+
+// базовый клас энкодера с кнопкой
+class VirtEncButton : public VirtButton, public VirtEncoder {
+   public:
+    // ====================== SET ======================
+    // установить таймаут быстрого поворота, мс
+    void setFastTimeout(const uint8_t tout) {
+#ifndef EB_FAST_TIME
+        EB_FAST_T = tout;
+#endif
+    }
+
+    // сбросить флаги энкодера и кнопки
+    void clear(bool resetTout = false) {
+        VirtButton::clear(resetTout);
+        VirtEncoder::clear();
+    }
+
+    // ====================== GET ======================
+    // нажатый поворот энкодера [событие]
+    bool turnH() {
+        return turn() && bf.read(EB_EHLD);
+    }
+
+    // быстрый поворот энкодера [состояние]
+    bool fast() {
+        return ef.read(EB_FAST);
+    }
+
+    // поворот направо [событие]
+    bool right() {
+        return ef.read(EB_DIR) && turn() && !bf.read(EB_EHLD);
+    }
+
+    // поворот налево [событие]
+    bool left() {
+        return !ef.read(EB_DIR) && turn() && !bf.read(EB_EHLD);
+    }
+
+    // нажатый поворот направо [событие]
+    bool rightH() {
+        return ef.read(EB_DIR) && turnH();
+    }
+
+    // нажатый поворот налево [событие]
+    bool leftH() {
+        return !ef.read(EB_DIR) && turnH();
+    }
+
+    // нажата кнопка энкодера. Аналог pressing() [состояние]
+    bool encHolding() {
+        return bf.read(EB_EHLD);
+    }
+
+    // было действие с кнопки или энкодера, вернёт код события [событие]
+    uint16_t action() {
+        if (turn()) return EB_TURN;
+        else return VirtButton::action();
+    }
+
+    // было действие с кнопки или энкодера, вернёт код события [событие]
+    EBAction getAction() {
+        return (EBAction)action();
+    }
+
+    // ====================== POLL ======================
+    // ISR
+    // обработка в прерывании (только энкодер). Вернёт 0 в покое, 1 или -1 при повороте
+    int8_t tickISR(const bool e0, const bool e1) {
+        return tickISR(e0 | (e1 << 1));
+    }
+
+    // обработка в прерывании (только энкодер). Вернёт 0 в покое, 1 или -1 при повороте
+    int8_t tickISR(int8_t state) {
+        state = VirtEncoder::pollEnc(state);
+        if (state) {
+#ifdef EB_NO_BUFFER
+            ef.set(EB_ISR_F);
+            ef.write(EB_DIR, state > 0);
+            ef.write(EB_FAST, checkFast());
+#else
+            for (uint8_t i = 0; i < 15; i += 3) {
+                if (!(ebuffer & (1 << i))) {
+                    ebuffer |= (1 << i);                         // turn
+                    if (state > 0) ebuffer |= (1 << (i + 1));    // dir
+                    if (checkFast()) ebuffer |= (1 << (i + 2));  // fast
+                    break;
+                }
+            }
+#endif
+        }
+        return state;
+    }
+
+    // TICK
+    // обработка энкодера и кнопки
+    bool tick(const bool e0, const bool e1, const bool btn) {
+        return tick(e0 | (e1 << 1), btn);
+    }
+
+    // обработка энкодера и кнопки. state = -1 для пропуска обработки энкодера
+    bool tick(const int8_t state, const bool btn) {
+        clear();
+        bool f = tickRaw(state, btn);
+#ifndef EB_NO_CALLBACK
+        if (f || timeoutState()) call(true);
+#endif
+        return f;
+    }
+
+    // обработка энкодера (в прерывании) и кнопки
+    bool tick(const bool btn) {
+        return tick(-1, btn);
+    }
+
+    // RAW
+    // обработка без сброса событий и вызова коллбэка
+    bool tickRaw(const bool e0, const bool e1, const bool btn) {
+        return tickRaw(e0 | (e1 << 1), btn);
+    }
+
+    // обработка без сброса событий и вызова коллбэка
+    bool tickRaw(int8_t state, bool btn) {
+        btn = VirtButton::tickRaw(btn);
+
+        bool encf = 0;
+#ifdef EB_NO_BUFFER
+        if (ef.read(EB_ISR_F)) {
+            ef.clear(EB_ISR_F);
+            encf = 1;
+        }
+#else
+        if (ebuffer) {
+            ef.write(EB_DIR, ebuffer & 0b10);
+            ef.write(EB_FAST, ebuffer & 0b100);
+            ebuffer >>= 3;
+            encf = 1;
+        }
+#endif
+        else if ((state >= 0) && (state = VirtEncoder::pollEnc(state))) {
+            ef.write(EB_DIR, state > 0);
+            ef.write(EB_FAST, checkFast());
+            encf = 1;
+        }
+        if (encf) {
+            if (bf.read(EB_PRS)) bf.set(EB_EHLD);  // зажать энкодер
+            else clicks = 0;
+            if (!bf.read(EB_TOUT)) bf.set(EB_TOUT);  // таймаут
+            ef.set(EB_ETRN_R);                       // флаг поворота
+        }
+        return encf | btn;
+    }
+
+    // обработка без сброса событий и вызова коллбэка (кнопка)
+    bool tickRaw(const bool btn) {
+        return tickRaw(-1, btn);
+    }
+
+    // ===================== PRIVATE =====================
+   protected:
+#ifndef EB_FAST_TIME
+    uint8_t EB_FAST_T = 30;
+#endif
+
+#ifndef EB_NO_BUFFER
+    uint16_t ebuffer = 0;
+#endif
+
+   private:
+    bool checkFast() {
+        uint16_t ms = EB_uptime();
+        bool f = 0;
+        if (ms - tmr < EB_FAST_T) f = 1;
+        tmr = ms;
+        return f;
+    }
+};

+ 181 - 0
.pio/libdeps/pico/EncButton/src/core/VirtEncoder.h

@@ -0,0 +1,181 @@
+#pragma once
+#include <Arduino.h>
+
+#include "flags.h"
+#include "io.h"
+
+// ===================== CONST ======================
+#define EB_STEP4_LOW 0
+#define EB_STEP4_HIGH 1
+#define EB_STEP2 2
+#define EB_STEP1 3
+
+// ===================== FLAGS ======================
+#define EB_TYPE (1 << 0)
+#define EB_REV (1 << 2)
+#define EB_FAST (1 << 3)
+#define EB_DIR (1 << 4)
+#define EB_ETRN_R (1 << 5)
+#define EB_ISR_F (1 << 6)
+#define EB_EISR (1 << 7)
+
+// базовый класс энкодера
+class VirtEncoder {
+   public:
+    VirtEncoder() {
+        prev = ecount = 0;
+    }
+    // ====================== SET ======================
+    // инвертировать направление энкодера
+    void setEncReverse(const bool rev) {
+        if (rev) ef.set(EB_REV);
+        else ef.clear(EB_REV);
+    }
+
+    // установить тип энкодера (EB_STEP4_LOW, EB_STEP4_HIGH, EB_STEP2, EB_STEP1)
+    void setEncType(const uint8_t type) {
+        ef.flags = (ef.flags & 0b11111100) | type;
+    }
+
+    // использовать обработку энкодера в прерывании
+    void setEncISR(const bool use) {
+        ef.write(EB_EISR, use);
+    }
+
+    // инициализация энкодера
+    void initEnc(const bool e0, const bool e1) {
+        initEnc(e0 | (e1 << 1));
+    }
+
+    // инициализация энкодера совмещённым значением
+    void initEnc(const int8_t v) {
+        prev = v;
+    }
+
+    // сбросить флаги событий
+    void clear() {
+        if (ef.read(EB_ETRN_R)) ef.clear(EB_ETRN_R);
+    }
+
+    // ====================== ОПРОС ======================
+    // был поворот [событие]
+    bool turn() {
+        return ef.read(EB_ETRN_R);
+    }
+
+    // направление энкодера (1 или -1) [состояние]
+    int8_t dir() {
+        return ef.read(EB_DIR) ? 1 : -1;
+    }
+
+    // ====================== POLL ======================
+    // ISR
+    // опросить энкодер в прерывании. Вернёт 1 или -1 при вращении, 0 при остановке
+    int8_t tickISR(const bool e0, const bool e1) {
+        return tickISR(e0 | (e1 << 1));
+    }
+
+    // опросить энкодер в прерывании. Вернёт 1 или -1 при вращении, 0 при остановке
+    int8_t tickISR(int8_t state) {
+        state = pollEnc(state);
+        if (state) {
+            ef.set(EB_ISR_F);
+            ef.write(EB_DIR, state > 0);
+        }
+        return state;
+    }
+
+    // TICK
+    // опросить энкодер. Вернёт 1 или -1 при вращении, 0 при остановке
+    int8_t tick(const bool e0, const bool e1) {
+        return tick(e0 | (e1 << 1));
+    }
+
+    // опросить энкодер. Вернёт 1 или -1 при вращении, 0 при остановке
+    int8_t tick(int8_t state) {
+        state = tickRaw(state);
+        if (state) return state;
+        clear();
+        return 0;
+    }
+
+    // опросить энкодер (сам опрос в прерывании)
+    int8_t tick() {
+        return tick(-1);
+    }
+
+    // RAW
+    // опросить энкодер без сброса события поворота
+    int8_t tickRaw(const bool e0, const bool e1) {
+        return tickRaw(e0 | (e1 << 1));
+    }
+
+    // опросить энкодер без сброса события поворота
+    int8_t tickRaw(int8_t state) {
+        if (ef.read(EB_ISR_F)) {
+            ef.clear(EB_ISR_F);
+            ef.set(EB_ETRN_R);
+            return dir();
+        }
+        if ((state >= 0) && (state = pollEnc(state))) {
+            ef.write(EB_DIR, state > 0);
+            ef.set(EB_ETRN_R);
+            return state;
+        }
+        return 0;
+    }
+
+    // опросить энкодер без сброса события поворота (сам опрос в прерывании)
+    int8_t tickRaw() {
+        return tickRaw(-1);
+    }
+
+    // POLL
+    // опросить энкодер без установки события поворота (быстрее). Вернёт 1 или -1 при вращении, 0 при остановке
+    int8_t pollEnc(const bool e0, const bool e1) {
+        return pollEnc(e0 | (e1 << 1));
+    }
+
+    // опросить энкодер без установки события поворота (быстрее). Вернёт 1 или -1 при вращении, 0 при остановке
+    int8_t pollEnc(int8_t state) {
+        if (prev != state) {
+            ecount += ((0x49941661 >> ((state | (prev << 2)) << 1)) & 0b11) - 1;  // faster
+            // switch (state | (prev << 2)) {
+            //     case 2: case 4: case 11: case 13: ecount++; break;
+            //     case 1: case 7: case 8: case 14: ecount--; break;
+            // }
+            prev = state;
+            if (!ecount) return 0;
+            switch (ef.mask(0b11)) {
+                case EB_STEP4_LOW:
+                    if (state != 0b11) return 0;  // skip 00, 01, 10
+                    break;
+                case EB_STEP4_HIGH:
+                    if (state) return 0;  // skip 01, 10, 11
+                    break;
+                case EB_STEP2:
+                    if (state == 0b10 || state == 0b01) return 0;  // skip 10 01
+                    break;
+            }
+            state = ((ecount > 0) ^ ef.read(EB_REV)) ? 1 : -1;
+            ecount = 0;
+#ifndef EB_NO_COUNTER
+            counter += state;
+#endif
+            return state;
+        }
+        return 0;
+    }
+
+#ifndef EB_NO_COUNTER
+    int32_t counter = 0;
+#endif
+
+    // ===================== PRIVATE =====================
+   protected:
+    encb::Flags<uint8_t> ef;
+
+   private:
+    int8_t prev : 4;
+    int8_t ecount : 4;
+};

+ 31 - 0
.pio/libdeps/pico/EncButton/src/core/flags.h

@@ -0,0 +1,31 @@
+#pragma once
+#include <Arduino.h>
+
+namespace encb {
+
+template <typename T>
+struct Flags {
+    T flags = 0;
+
+    inline T mask(const T x) __attribute__((always_inline)) {
+        return flags & x;
+    }
+    inline void set(const T x) __attribute__((always_inline)) {
+        flags |= x;
+    }
+    inline void clear(const T x) __attribute__((always_inline)) {
+        flags &= ~x;
+    }
+    inline bool read(const T x) __attribute__((always_inline)) {
+        return flags & x;
+    }
+    inline void write(const T x, const bool v) __attribute__((always_inline)) {
+        if (v) set(x);
+        else clear(x);
+    }
+    inline bool eq(const T x, const T y) __attribute__((always_inline)) {
+        return (flags & x) == y;
+    }
+};
+
+}  // namespace encb

+ 13 - 0
.pio/libdeps/pico/EncButton/src/core/io.cpp

@@ -0,0 +1,13 @@
+#include "io.h"
+
+bool __attribute__((weak)) EB_read(uint8_t pin) {
+    return gio::read(pin);
+}
+
+void __attribute__((weak)) EB_mode(uint8_t pin, uint8_t mode) {
+    gio::init(pin, mode);
+}
+
+uint32_t __attribute__((weak)) EB_uptime() {
+    return millis();
+}

+ 7 - 0
.pio/libdeps/pico/EncButton/src/core/io.h

@@ -0,0 +1,7 @@
+#pragma once
+#include <Arduino.h>
+#include <GyverIO.h>
+
+bool EB_read(uint8_t pin);
+void EB_mode(uint8_t pin, uint8_t mode);
+uint32_t EB_uptime();

+ 3 - 0
.pio/libdeps/pico/EncButton/src/core/self.cpp

@@ -0,0 +1,3 @@
+#include "self.h"
+
+void* EB_self = nullptr;

+ 3 - 0
.pio/libdeps/pico/EncButton/src/core/self.h

@@ -0,0 +1,3 @@
+#pragma once
+
+extern void* EB_self;

+ 2 - 0
.pio/libdeps/pico/GyverIO/.gitattributes

@@ -0,0 +1,2 @@
+# Auto detect text files and perform LF normalization
+* text=auto

+ 20 - 0
.pio/libdeps/pico/GyverIO/.github/workflows/tg-send.yml

@@ -0,0 +1,20 @@
+
+name: Telegram Message
+on:
+  release:
+    types: [published]
+jobs:
+  build:
+    name: Send Message
+    runs-on: ubuntu-latest
+    steps:
+      - name: send telegram message on push
+        uses: appleboy/telegram-action@master
+        with:
+          to: ${{ secrets.TELEGRAM_TO }}
+          token: ${{ secrets.TELEGRAM_TOKEN }}
+          disable_web_page_preview: true
+          message: |
+            ${{ github.event.repository.name }} v${{ github.event.release.tag_name }}
+            ${{ github.event.release.body }}
+            https://github.com/${{ github.repository }}

+ 1 - 0
.pio/libdeps/pico/GyverIO/.piopm

@@ -0,0 +1 @@
+{"type": "library", "name": "GyverIO", "version": "1.3.9", "spec": {"owner": "gyverlibs", "id": 16286, "name": "GyverIO", "requirements": null, "uri": null}}

+ 21 - 0
.pio/libdeps/pico/GyverIO/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2023 GyverLibs
+
+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.

+ 205 - 0
.pio/libdeps/pico/GyverIO/README.md

@@ -0,0 +1,205 @@
+[![latest](https://img.shields.io/github/v/release/GyverLibs/GyverIO.svg?color=brightgreen)](https://github.com/GyverLibs/GyverIO/releases/latest/download/GyverIO.zip)
+[![PIO](https://badges.registry.platformio.org/packages/gyverlibs/library/GyverIO.svg)](https://registry.platformio.org/libraries/gyverlibs/GyverIO)
+[![Foo](https://img.shields.io/badge/Website-AlexGyver.ru-blue.svg?style=flat-square)](https://alexgyver.ru/)
+[![Foo](https://img.shields.io/badge/%E2%82%BD%24%E2%82%AC%20%D0%9F%D0%BE%D0%B4%D0%B4%D0%B5%D1%80%D0%B6%D0%B0%D1%82%D1%8C-%D0%B0%D0%B2%D1%82%D0%BE%D1%80%D0%B0-orange.svg?style=flat-square)](https://alexgyver.ru/support_alex/)
+[![Foo](https://img.shields.io/badge/README-ENGLISH-blueviolet.svg?style=flat-square)](https://github-com.translate.goog/GyverLibs/GyverIO?_x_tr_sl=ru&_x_tr_tl=en)  
+
+[![Foo](https://img.shields.io/badge/ПОДПИСАТЬСЯ-НА%20ОБНОВЛЕНИЯ-brightgreen.svg?style=social&logo=telegram&color=blue)](https://t.me/GyverLibs)
+
+# GyverIO
+Быстрые функции для работы с пинами AVR (полный список смотри в gio_avr.h), ESP8266, ESP32
+- Ускорение в среднем в 20-30 раз, итоговое время для всех архитектур практически одинаковое
+- Классы для быстрого управления пином
+- Отдельная обработка случаев константных и неконстантных пинов для AVR
+- Быстрая реализация функций shiftIn/shiftOut
+- Универсальный класс hard SPI / soft SPI для использования в библиотеках
+
+## Скорость
+### Условия измерения
+|         | Версия | Частота |
+|---------|--------|---------|
+| AVR     | 1.8.19 | 16      |
+| ESP8266 | 3.1.2  | 80      |
+| ESP32   | 2.0.11 | 240     |
+| ESP32C3 | 2.0.11 | 80      |
+
+### GPIO (us)
+|              | write NC | write       | read NC   | read        | mode NC | mode        |
+|--------------|----------|-------------|-----------|-------------|---------|-------------|
+| AVR Ardu     | **5.3**  | 5.3         | **4.8**   | 4.8         | **3.3** | 3.3         |
+| AVR gio      | 1.6      | ***0.125*** | 1.75      | ***0.075*** | 1.8     | ***0.125*** |
+|              |          |             |           |             |         |             |
+| ESP8266 Ardu | **1.5**  | 1.5         | **0.54**  | 0.54        | **1.4** | 1.4         |
+| ESP8266 gio  | 0.29     | ***0.08***  | 0.5       | ***0.17***  | 1.29    | ***0.58***  |
+|              |          |             |           |             |         |             |
+| ESP32 Ardu   | **0.33** | 0.33        | **0.124** | 0.124       | **16**  | 16          |
+| ESP32 gio    | 0.04     | ***0.04***  | 0.085     | ***0.085*** | 0.126   | ***0.08***  |
+|              |          |             |           |             |         |             |
+| ESP32C3 Ardu | **0.91** | 0.91        | **0.25**  | 0.25        | **21**  | 21          |
+| ESP32C3 gio  | 0.05     | ***0.05***  | 0.4       | ***0.08***  | 0.49    | ***0.08***  |
+
+> *NC* - пины не константы
+
+> **Жирным** выделено худшее время (Arduino не константы), ***жирным курсивом*** - лучшее (gio константы)
+
+### Shift (MHz)
+|         | shiftOut | gio::shift |
+|---------|----------|------------|
+| AVR NC  | 0.06     | 0.66       |
+| AVR     | 0.06     | 1.3        |
+| ESP8266 | 0.2      | 1.1        |
+| ESP32   | 0.96     | 6          |
+| ESP32C3 | 0.35     | 2.6        |
+
+> *NC* - пины не константы
+
+### Совместимость
+Совместима со всеми Arduino платформами (используются Arduino-функции)
+- Для esp8266 и esp32 быстрый `pinMode()` (`mode()`) работает только на режимы `INPUT`/`OUTPUT`! В остальных режимах вызывается штатный `pinMode()`
+
+## Содержание
+- [Документация](#docs)
+- [Использование](#usage)
+- [Версии](#versions)
+- [Установка](#install)
+- [Баги и обратная связь](#feedback)
+
+<a id="docs"></a>
+## Документация
+### gio
+Быстые функции для работы с пинами
+
+```cpp
+int gio::read(int P);
+void gio::high(int P);
+void gio::low(int P)
+void gio::write(int P, int V);
+
+//
+void gio::toggle(int P);
+
+// режим пина. Для esp8266/esp32 только INPUT/OUTPUT!
+void gio::mode(int P, int V);
+
+// нужно вызывать для esp8266/esp32 при инициализации пина
+// иначе mode() не будет работать!
+void gio::init(int P);
+```
+> У esp8266/esp32 нужно обязательно вызвать `gio::init` перед использованием!
+
+### gio::shift
+Быстрый аналог shiftIn/shiftOut (отправка данных с клоком)
+
+```cpp
+// прочитать пакет. Вернёт true, если хотя бы один бит отличается
+bool gio::shift::read(uint8_t dat_pin, uint8_t clk_pin, uint8_t order, uint8_t* data, uint16_t len, uint8_t delay = 0);
+
+// прочитать байт
+uint8_t gio::shift::read_byte(uint8_t dat_pin, uint8_t clk_pin, uint8_t order, uint8_t delay = 0);
+
+// прочитать пакет + cs пин. Вернёт true, если хотя бы один бит отличается
+bool gio::shift::read_cs(uint8_t dat_pin, uint8_t clk_pin, uint8_t cs_pin, uint8_t order, uint8_t* data, uint16_t len, uint8_t delay = 0);
+
+// прочитать байт + cs пин
+uint8_t gio::shift::read_cs_byte(uint8_t dat_pin, uint8_t clk_pin, uint8_t cs_pin, uint8_t order, uint8_t delay = 0);
+
+// отправить пакет
+void gio::shift::send(uint8_t dat_pin, uint8_t clk_pin, uint8_t order, uint8_t* data, uint16_t len, uint8_t delay = 0);
+
+// отправить байт
+void gio::shift::send_byte(uint8_t dat_pin, uint8_t clk_pin, uint8_t order, uint8_t data, uint8_t delay = 0);
+
+// отправить пакет + cs пин
+void gio::shift::send_cs(uint8_t dat_pin, uint8_t clk_pin, uint8_t cs_pin, uint8_t order, uint8_t* data, uint16_t len, uint8_t delay = 0);
+
+// отправить байт + cs пин
+void gio::shift::send_cs_byte(uint8_t dat_pin, uint8_t clk_pin, uint8_t cs_pin, uint8_t order, uint8_t data, uint8_t delay = 0);
+```
+
+Параметр `order` может быть:
+- `LSBFIRST`/`LSB_NORMAL` - LSB, прямой порядок байтов
+- `MSBFIRST`/`MSB_NORMAL` - MSB, прямой порядок байтов
+- `LSB_REVERSE` - LSB, обратный порядок байтов
+- `MSB_REVERSE` - MSB, обратный порядок байтов
+
+#### Примечание
+- `delay` в микросекундах, позволяет уменьшить скорость передачи. Например `1` мкс ограничит скорость до ~1 МГц, 2 мкс до ~500 кГц
+- Пины нужно сконфигурировать как `OUTPUT` самостоятельно до отправки (при запуске программы например)
+
+### gio::SSPI
+Универсальный класс программно-аппаратного SPI с оптимизацией количества переменных для пинов
+
+```cpp
+SSPI<0, freq> spi;                  // аппаратный без пина CS
+SSPI<0, freq, cs> spi;              // аппаратный с пином CS в шаблоне
+SSPI<0, freq> spi(cs);              // аппаратный с пином CS в классе
+SSPI<1, freq, cs, dt, clk> spi;	    // программный с пинами в шаблоне
+SSPI<1, freq> spi(cs, dt, clk);	    // программный с пинами в классе
+```
+
+### Настройки компиляции
+```cpp
+#define GIO_USE_ARDUINO     // отключить быстрые функции (заменить на стандартные)
+#define GIO_NO_MASK         // отключить быстрый IO на основе маски для AVR (в классе PinIO и всех shift)
+```
+
+<a id="usage"></a>
+## Использование
+
+```cpp
+#include <GyverIO_SPI.h>
+
+gio::write(3, 1);   // включить пин 3
+
+// отправить данные по пинам 3 и 4
+uint8_t data[] = {34, 63, 231, 9};
+gio::shift::send(3, 4, MSBFIRST, data, 4);
+
+SSPI<0, f, cs> spi;
+spi.send(0x12);
+```
+
+<a id="versions"></a>
+## Версии
+- v1.0
+- v1.1 - в 3 раза ускорен AVR non-const, обновлены таблицы
+- v1.2 - исправлена ошибка!
+- v1.2.1 - небольшая оптимизация
+- v1.2.2 - добавлена инверсия в shift
+- v1.2.4 - исправлен баг в gio::shift::read для AVR NC
+- v1.2.5 - добавлен возврат true в gio::shift::read при изменении буфера
+- v1.3.0 - исправлена критическая ошибка AVR/mode/NC
+- v1.3.1 - добавлен дополнительный delay в shift для симметричности клока
+- v1.3.2 - SSPI вынесен в отдельный файл, чтобы не мешать компиляции на некоторых платформах
+- v1.3.4 - поддержка ESP32C6
+
+<a id="install"></a>
+## Установка
+- Библиотеку можно найти по названию **GyverIO** и установить через менеджер библиотек в:
+    - Arduino IDE
+    - Arduino IDE v2
+    - PlatformIO
+- [Скачать библиотеку](https://github.com/GyverLibs/GyverIO/archive/refs/heads/main.zip) .zip архивом для ручной установки:
+    - Распаковать и положить в *C:\Program Files (x86)\Arduino\libraries* (Windows x64)
+    - Распаковать и положить в *C:\Program Files\Arduino\libraries* (Windows x32)
+    - Распаковать и положить в *Документы/Arduino/libraries/*
+    - (Arduino IDE) автоматическая установка из .zip: *Скетч/Подключить библиотеку/Добавить .ZIP библиотеку…* и указать скачанный архив
+- Читай более подробную инструкцию по установке библиотек [здесь](https://alexgyver.ru/arduino-first/#%D0%A3%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BA%D0%B0_%D0%B1%D0%B8%D0%B1%D0%BB%D0%B8%D0%BE%D1%82%D0%B5%D0%BA)
+### Обновление
+- Рекомендую всегда обновлять библиотеку: в новых версиях исправляются ошибки и баги, а также проводится оптимизация и добавляются новые фичи
+- Через менеджер библиотек IDE: найти библиотеку как при установке и нажать "Обновить"
+- Вручную: **удалить папку со старой версией**, а затем положить на её место новую. "Замену" делать нельзя: иногда в новых версиях удаляются файлы, которые останутся при замене и могут привести к ошибкам!
+
+<a id="feedback"></a>
+## Баги и обратная связь
+При нахождении багов создавайте **Issue**, а лучше сразу пишите на почту [alex@alexgyver.ru](mailto:alex@alexgyver.ru)  
+Библиотека открыта для доработки и ваших **Pull Request**'ов!
+
+При сообщении о багах или некорректной работе библиотеки нужно обязательно указывать:
+- Версия библиотеки
+- Какой используется МК
+- Версия SDK (для ESP)
+- Версия Arduino IDE
+- Корректно ли работают ли встроенные примеры, в которых используются функции и конструкции, приводящие к багу в вашем коде
+- Какой код загружался, какая работа от него ожидалась и как он работает в реальности
+- В идеале приложить минимальный код, в котором наблюдается баг. Не полотно из тысячи строк, а минимальный код

+ 187 - 0
.pio/libdeps/pico/GyverIO/README_EN.md

@@ -0,0 +1,187 @@
+This is an automatic translation, may be incorrect in some places. See sources and examples!
+
+# Gyverio
+Fast functions for working with AVR Pin (a full list look at Gio_avr.h), ESP8266, ESP32
+- acceleration on average by 20-30 times, the final time for all architectures is almost the same
+- Classes for quick PIN control
+- Separate processing of cases of constant and uncontrolled pins for AVR
+- Fast implementation of Shiftin/Shiftout
+- Universal class Hard Spi / Soft SPI for use in libraries
+
+## Speed
+### measurement conditions
+||Version |Frequency |
+| --------- | -------- | -------- |
+|AVR |1.8.19 |16 |
+|ESP8266 |3.1.2 |80 |
+|ESP32 |2.0.11 |240 |
+|ESP32C3 |2.0.11 |80 |
+
+### GPIO (US)
+||Write NC |Write |Read NC |Read |Mode NC |Mode |
+| ------------- | ---------- | ----------------- | ------------------------------| ------------ | --------- | ------------
+|AVR Ardu |** 5.3 ** |5.3 |** 4.8 ** |4.8 |** 3.3 ** |3.3 |
+|AVR Gio |1.6 |*** 0.125 *** |1.75 |*** 0.075 *** |1.8 |*** 0.125 *** |
+||||||||
+|ESP8266 Ardu |** 1.5 ** |1.5 |** 0.54 ** |0.54 |** 1.4 ** |1.4 |
+|ESP8266 Gio |0.29 |*** 0.08 *** |0.5 |*** 0.17 *** |1.29 |*** 0.58 *** |
+||||||||
+|ESP32 Ardu |** 0.33 ** |0.33 |** 0.124 ** |0.124 |** 16 ** |16 |
+|ESP32 Gio |0.04 |*** 0.04 *** |0.085 |*** 0.085 *** |0.126 |*** 0.08 *** |
+||||||||
+|ESP32C3 Ardu |** 0.91 ** |0.91 |** 0.25 ** |0.25 |** 21 ** |21 |
+|ESP32C3 Gio |0.05 |*** 0.05 *** |0.4 |*** 0.08 *** |0.49 |*** 0.08 *** |
+
+> * Nc * - Pins are not constants
+
+> ** Fat ** highlighted the worst time (Arduino not constants), *** with a fat italics *** - the best (gio constants)
+
+### Shift (MHZ)
+||Shiftout |Gio :: Shift |
+| -------- | ---------- | -----------------------------------
+|AVR NC |0.06 |0.66 |
+|AVR |0.06 |1.3 |
+|ESP8266 |0.2 |1.1 |
+|ESP32 |0.96 |6 |
+|ESP32C3 |0.35 |2.6 |
+
+> * Nc * - Pins are not constants
+
+## compatibility
+Compatible with all arduino platforms (used arduino functions)
+- For ESP8266 and ESP32, fast `pinmode ()` (`mode ()`) works only on `Input`/` output`!In other modes, the standard `pinmode ()` is called
+
+## Content
+- [documentation] (#docs)
+- [use] (#usage)
+- [versions] (#varsions)
+- [installation] (# Install)
+- [bugs and feedback] (#fedback)
+
+<a id="docs"> </a>
+## Documentation
+## Gio
+Squeezing pins
+
+`` `CPP
+int Gio :: read (int p);
+Void Gio :: High (int p);
+Void Gio :: Low (int p)
+Void Gio :: Write (int p, int v);
+
+//
+Void Gio :: Toggle (int p);
+
+// Pin mode.For ESP8266/ESP32 only Input/Output!
+Void Gio :: Mode (int p, int v);
+
+// you need to call for ESP8266/ESP32 when initializing the PIN
+// Otherwise, mod () will not work!
+Void Gio :: Init (int p);
+`` `
+
+### Gio :: Shift
+Fast analogue Shiftin/Shiftout (sending data with a clock)
+
+`` `CPP
+// Read the package
+vOID GIO :: Shift :: Read (uint8_t data_pin, uint8_t clk_pin, uint8_t Order, uint8_t* Data, Uint16_t Len, Uint8_t Delay = 0);
+
+// Read the byte
+uint8_t Gio :: Shift :: read_byte (uint8_t dat_pin, uint8_t clak_pin, uint8_t Order, uint8_t demely = 0);
+
+// Read the package + cs pin
+VOID GIO :: Shift :: Read_cs (uint8_t data_pin, uint8_t clk_pin, uint8_t cs_pin, uint8_t Order, uint8_t* data, uint16_t Len, uint8_t Delay = 0);
+
+// read byte + cs pin
+Uint8_t Gio :: Shift :: Read_cs_byte (uint8_t data_pin, uint8_t clk_pin, uint8_t cs_pin, uint8_t order, uint8_t dlavay = 0);
+
+// Send a package
+VOID Gio :: Shift :: Send (uint8_t data_pin, uint8_t clk_pin, uint8_t Order, uint8_t* data, uint16_t len, uint8_t dlavay = 0);
+
+// Send byte
+VOID Gio :: Shift :: Send_byte (uint8_t data_pin, uint8_t clk_pin, uint8_t order, uint8_t data, uint8_t demelavy = 0);
+
+// Send package + cs pin
+VOID Gio :: Shift :: Send_cs (uint8_t data_pin, uint8_t clk_pin, uint8_t cs_pin, uint8_t Order, uint8_t* data, uint16_t Len, uint8_t demelavy = 0);
+
+// Send byte + cs pin
+VOID GIO :: Shift :: Send_cs_byte (uint8_t data_pin, uint8_t clk_pin, uint8_t cs_pin, uint8_t ORDER, UINT8_T DATA, UINT8_T DELAY = 0);
+`` `
+
+The `order` parameter can be:
+- `lsbfirst`/` lsb_normal` - lsb, direct bytes directly
+- `MSBFIRST`/` MSB_Normal` - MSB, direct bytes direct
+- `lsb_reverse` - lsb, reverse byte order
+- `MSB_Reverse` - MSB, reverse bytes
+
+#### Note
+- `Delay` in microseconds, allows you to reduce the speed of transmission.For example, `1` μs will limit speed up to ~ 1 MHz, 2 μs up to ~ 500 kHz
+- Pins should be configured as `output` yourself before sending (when starting a program for example)
+
+### Gio :: SSPI
+Universal class of software and hardware SPI with optimization of the number of variables for Pin
+
+`` `CPP
+SSPI <0, Freq> Spi;// hardware without PIN CS
+SSPI <0, Freq, CS> SPI;// Hardware with PIN CS in the template
+SSPI <0, Freq> Spi (CS);// Hardware with PIN CS in the classroom
+SSPI <1, Freq, CS, DT, CLK> Spi;// Program with pines in the template
+SSPI <1, FreQ> Spi (CS, DT, CLK);// Program with pins in class
+`` `
+
+### compilation settings
+`` `CPP
+#define gio_use_arduino // Disable quick functions (replace with standard)
+#define gio_no_mask // Disable a quick IO based on AVR mask (in the Pinio class and all Shift)
+`` `
+
+<a id="usage"> </a>
+## Usage
+
+`` `CPP
+Gio :: Write (3, 1);// Turn on PIN 3
+
+// Send data on pins 3 and 4
+uint8_t data [] = {34, 63, 231, 9};
+Gio :: Shift :: Send (3, 4, MSBFIRST, DATA, 4);
+`` `
+
+<a id="versions"> </a>
+## versions
+- V1.0
+- V1.1 - AVR Non -Const is 3 times accelerated, the tables are updated
+- V1.2 - Fixed a mistake!
+- V1.2.1 - Small optimization
+- v1.2.2 - added inversion to Shift
+
+<a id="install"> </a>
+## Installation
+- The library can be found by the name ** gyverio ** and installed through the library manager in:
+    - Arduino ide
+    - Arduino ide v2
+    - Platformio
+- [download the library] (https://github.com/gyverlibs/gyverio/archive/refs/heads/main.zip) .Zip archive for manual installation:
+    - unpack and put in * C: \ Program Files (X86) \ Arduino \ Libraries * (Windows X64)
+    - unpack and put in * C: \ Program Files \ Arduino \ Libraries * (Windows X32)
+    - unpack and put in *documents/arduino/libraries/ *
+    - (Arduino id) Automatic installation from. Zip: * sketch/connect the library/add .Zip library ... * and specify downloaded archive
+- Read more detailed instructions for installing libraries [here] (https://alexgyver.ru/arduino-first/#%D0%A3%D1%81%D1%82%D0%B0%BD%D0%BE%BE%BE%BED0%B2%D0%BA%D0%B0_%D0%B1%D0%B8%D0%B1%D0%BB%D0%B8%D0%BE%D1%82%D0%B5%D0%BA)
+### Update
+- I recommend always updating the library: errors and bugs are corrected in the new versions, as well as optimization and new features are added
+- through the IDE library manager: find the library how to install and click "update"
+- Manually: ** remove the folder with the old version **, and then put a new one in its place.“Replacement” cannot be done: sometimes files are deleted in new versions,Cranberries that remain when replacing and can lead to errors!
+
+<a id="feedback"> </a>
+## bugs and feedback
+Create ** Issue ** when you find the bugs, and better immediately write to the mail [alex@alexgyver.ru] (mailto: alex@alexgyver.ru)
+The library is open for refinement and your ** pull Request ** 'ow!
+
+When reporting about bugs or incorrect work of the library, it is necessary to indicate:
+- The version of the library
+- What is MK used
+- SDK version (for ESP)
+- version of Arduino ide
+- whether the built -in examples work correctly, in which the functions and designs are used, leading to a bug in your code
+- what code has been loaded, what work was expected from it and how it works in reality
+- Ideally, attach the minimum code in which the bug is observed.Not a canvas of a thousand lines, but a minimum code

+ 219 - 0
.pio/libdeps/pico/GyverIO/examples/test/test.ino

@@ -0,0 +1,219 @@
+#include <Arduino.h>
+
+#include "GyverIO.h"
+
+// gio::SSPI<false, 4000000> spi0;
+// gio::SSPI<false, 4000000, 9> spi1;
+// gio::SSPI<false, 4000000> spi2(9);
+// gio::SSPI<true, 4000000, 9, 11, 13> spi3;
+// gio::SSPI<true, 4000000> spi4(9, 11, 13);
+
+#define PIN_DAT 7
+#define PIN_CLK 6
+byte datNC, clkNC;
+
+void setup() {
+    pinMode(PIN_DAT, OUTPUT);
+    pinMode(PIN_CLK, OUTPUT);
+    // gio::shift::send_byte(PIN_DAT, PIN_CLK, MSBFIRST, 0b10100101);
+}
+
+void loop() {
+    datNC = digitalRead(5);
+    clkNC = digitalRead(5);
+    datNC = PIN_DAT;
+    clkNC = PIN_CLK;
+    digitalWrite(PIN_DAT, 1);
+    delayMicroseconds(50);
+    digitalWrite(PIN_DAT, 0);
+    delayMicroseconds(50);
+    noInterrupts();
+
+    shiftOut(datNC, clkNC, MSBFIRST, 0b10100101);
+    shiftOut(PIN_DAT, PIN_CLK, MSBFIRST, 0b10100101);
+    gio::shift::send_byte(datNC, clkNC, MSBFIRST, 0b10100101);
+    gio::shift::send_byte(PIN_DAT, PIN_CLK, MSBFIRST, 0b10100101);
+    delayMicroseconds(50);
+
+    // ardu write nc
+    digitalWrite(datNC, 1);
+    digitalWrite(datNC, 0);
+    digitalWrite(datNC, 1);
+    digitalWrite(datNC, 0);
+    digitalWrite(datNC, 1);
+    digitalWrite(datNC, 0);
+    digitalWrite(datNC, 1);
+    digitalWrite(datNC, 0);
+    digitalWrite(datNC, 1);
+    digitalWrite(datNC, 0);
+    delayMicroseconds(10);
+
+    // ardu write
+    digitalWrite(PIN_DAT, 1);
+    digitalWrite(PIN_DAT, 0);
+    digitalWrite(PIN_DAT, 1);
+    digitalWrite(PIN_DAT, 0);
+    digitalWrite(PIN_DAT, 1);
+    digitalWrite(PIN_DAT, 0);
+    digitalWrite(PIN_DAT, 1);
+    digitalWrite(PIN_DAT, 0);
+    digitalWrite(PIN_DAT, 1);
+    digitalWrite(PIN_DAT, 0);
+    delayMicroseconds(10);
+
+    // ardu read nc
+    gio::write(PIN_DAT, 1);
+    gio::write(PIN_DAT, 0);
+    gio::write(PIN_DAT, 1);
+    gio::write(PIN_DAT, 0);
+    gio::write(PIN_DAT, 1);
+
+    digitalRead(datNC);
+
+    gio::write(PIN_DAT, 0);
+    gio::write(PIN_DAT, 1);
+    gio::write(PIN_DAT, 0);
+    gio::write(PIN_DAT, 1);
+    gio::write(PIN_DAT, 0);
+    delayMicroseconds(10);
+
+    // ardu read
+    gio::write(PIN_DAT, 1);
+    gio::write(PIN_DAT, 0);
+    gio::write(PIN_DAT, 1);
+    gio::write(PIN_DAT, 0);
+    gio::write(PIN_DAT, 1);
+
+    digitalRead(PIN_DAT);
+
+    gio::write(PIN_DAT, 0);
+    gio::write(PIN_DAT, 1);
+    gio::write(PIN_DAT, 0);
+    gio::write(PIN_DAT, 1);
+    gio::write(PIN_DAT, 0);
+    delayMicroseconds(10);
+
+    // ardu mode nc
+    gio::write(PIN_DAT, 1);
+    gio::write(PIN_DAT, 0);
+    gio::write(PIN_DAT, 1);
+    gio::write(PIN_DAT, 0);
+    gio::write(PIN_DAT, 1);
+
+    pinMode(datNC, OUTPUT);
+
+    gio::write(PIN_DAT, 0);
+    gio::write(PIN_DAT, 1);
+    gio::write(PIN_DAT, 0);
+    gio::write(PIN_DAT, 1);
+    gio::write(PIN_DAT, 0);
+    delayMicroseconds(10);
+
+    // ardu mode
+    gio::write(PIN_DAT, 1);
+    gio::write(PIN_DAT, 0);
+    gio::write(PIN_DAT, 1);
+    gio::write(PIN_DAT, 0);
+    gio::write(PIN_DAT, 1);
+
+    pinMode(PIN_DAT, OUTPUT);
+
+    gio::write(PIN_DAT, 0);
+    gio::write(PIN_DAT, 1);
+    gio::write(PIN_DAT, 0);
+    gio::write(PIN_DAT, 1);
+    gio::write(PIN_DAT, 0);
+    delayMicroseconds(50);
+
+    // gio write nc
+    gio::write(datNC, 1);
+    gio::write(datNC, 0);
+    gio::write(datNC, 1);
+    gio::write(datNC, 0);
+    gio::write(datNC, 1);
+    gio::write(datNC, 0);
+    gio::write(datNC, 1);
+    gio::write(datNC, 0);
+    gio::write(datNC, 1);
+    gio::write(datNC, 0);
+    delayMicroseconds(10);
+
+    // gio write
+    gio::write(PIN_DAT, 1);
+    gio::write(PIN_DAT, 0);
+    gio::write(PIN_DAT, 1);
+    gio::write(PIN_DAT, 0);
+    gio::write(PIN_DAT, 1);
+    gio::write(PIN_DAT, 0);
+    gio::write(PIN_DAT, 1);
+    gio::write(PIN_DAT, 0);
+    gio::write(PIN_DAT, 1);
+    gio::write(PIN_DAT, 0);
+    delayMicroseconds(10);
+
+    // gio read nc
+    gio::write(PIN_DAT, 1);
+    gio::write(PIN_DAT, 0);
+    gio::write(PIN_DAT, 1);
+    gio::write(PIN_DAT, 0);
+    gio::write(PIN_DAT, 1);
+
+    gio::read(datNC);
+
+    gio::write(PIN_DAT, 0);
+    gio::write(PIN_DAT, 1);
+    gio::write(PIN_DAT, 0);
+    gio::write(PIN_DAT, 1);
+    gio::write(PIN_DAT, 0);
+    delayMicroseconds(10);
+
+    // gio read
+    gio::write(PIN_DAT, 1);
+    gio::write(PIN_DAT, 0);
+    gio::write(PIN_DAT, 1);
+    gio::write(PIN_DAT, 0);
+    gio::write(PIN_DAT, 1);
+
+    gio::read(PIN_DAT);
+
+    gio::write(PIN_DAT, 0);
+    gio::write(PIN_DAT, 1);
+    gio::write(PIN_DAT, 0);
+    gio::write(PIN_DAT, 1);
+    gio::write(PIN_DAT, 0);
+    delayMicroseconds(10);
+
+    // gio mode nc
+    gio::write(PIN_DAT, 1);
+    gio::write(PIN_DAT, 0);
+    gio::write(PIN_DAT, 1);
+    gio::write(PIN_DAT, 0);
+    gio::write(PIN_DAT, 1);
+
+    gio::mode(datNC, OUTPUT);
+
+    gio::write(PIN_DAT, 0);
+    gio::write(PIN_DAT, 1);
+    gio::write(PIN_DAT, 0);
+    gio::write(PIN_DAT, 1);
+    gio::write(PIN_DAT, 0);
+    delayMicroseconds(10);
+
+    // gio mode
+    gio::write(PIN_DAT, 1);
+    gio::write(PIN_DAT, 0);
+    gio::write(PIN_DAT, 1);
+    gio::write(PIN_DAT, 0);
+    gio::write(PIN_DAT, 1);
+
+    gio::mode(PIN_DAT, OUTPUT);
+
+    gio::write(PIN_DAT, 0);
+    gio::write(PIN_DAT, 1);
+    gio::write(PIN_DAT, 0);
+    gio::write(PIN_DAT, 1);
+    gio::write(PIN_DAT, 0);
+    delayMicroseconds(10);
+
+    interrupts();
+}

+ 48 - 0
.pio/libdeps/pico/GyverIO/keywords.txt

@@ -0,0 +1,48 @@
+#######################################
+# Syntax Coloring Map For GyverIO
+#######################################
+
+#######################################
+# Datatypes (KEYWORD1)
+#######################################
+
+GyverIO	KEYWORD1
+Pin	KEYWORD1
+PinIO	KEYWORD1
+PinT	KEYWORD1
+SSPI	KEYWORD1
+
+GIO_USE_ARDUINO	KEYWORD1
+GIO_NO_MASK	KEYWORD1
+
+#######################################
+# Methods and Functions (KEYWORD2)
+#######################################
+
+init	KEYWORD2
+toggle	KEYWORD2
+write	KEYWORD2
+high	KEYWORD2
+low	KEYWORD2
+read	KEYWORD2
+mode	KEYWORD2
+
+read	KEYWORD2
+read_byte	KEYWORD2
+read_cs	KEYWORD2
+read_cs_byte	KEYWORD2
+send	KEYWORD2
+send_byte	KEYWORD2
+send_cs	KEYWORD2
+send_cs_byte	KEYWORD2
+
+#######################################
+# Constants (LITERAL1)
+#######################################
+
+gio	LITERAL1
+shift	LITERAL1
+LSB_NORMAL	LITERAL1
+MSB_NORMAL	LITERAL1
+LSB_REVERSE	LITERAL1
+MSB_REVERSE	LITERAL1

+ 9 - 0
.pio/libdeps/pico/GyverIO/library.properties

@@ -0,0 +1,9 @@
+name=GyverIO
+version=1.3.9
+author=AlexGyver <alex@alexgyver.ru>
+maintainer=AlexGyver <alex@alexgyver.ru>
+sentence=Fast GPIO operation functions for AVR, ESP8266, ESP32
+paragraph=Fast GPIO operation functions for AVR, ESP8266, ESP32
+category=Signal Input/Output
+url=https://github.com/GyverLibs/GyverIO
+architectures=*

+ 6 - 0
.pio/libdeps/pico/GyverIO/src/GyverIO.h

@@ -0,0 +1,6 @@
+#pragma once
+
+#include "gio/gio.h"
+#include "utils/PinIO.h"
+#include "utils/PinT.h"
+#include "utils/shift.h"

+ 82 - 0
.pio/libdeps/pico/GyverIO/src/GyverIO_SPI.h

@@ -0,0 +1,82 @@
+#pragma once
+
+#include <Arduino.h>
+#include <SPI.h>
+
+#include "utils/shift.h"
+
+namespace gio {
+
+template <uint8_t _softT = 0, uint32_t _freqT = 4000000, int8_t _csT = -1, int8_t _datT = -1, int8_t _clkT = -1>
+class SSPI {
+   public:
+    SSPI(int8_t cs = -1, int8_t dat = -1, int8_t clk = -1) {
+        if (_softT) {
+            if (_datT >= 0) {
+                gio::init(_datT, OUTPUT);
+                gio::init(_clkT, OUTPUT);
+            } else {
+                _pins[1] = dat;
+                _pins[2] = clk;
+                gio::init(_pins[1], OUTPUT);
+                gio::init(_pins[2], OUTPUT);
+            }
+        }
+
+        if (_csT >= 0) {
+            gio::init(_csT, OUTPUT);
+            gio::high(_csT);
+        } else {
+            _pins[0] = cs;
+            if (cs >= 0) {
+                gio::init(cs, OUTPUT);
+                gio::high(cs);
+            }
+        }
+    }
+    void send(uint8_t data) {
+        _begin();
+        if (_softT) {
+            if (_datT >= 0) gio::shift::send_byte(_datT, _clkT, MSBFIRST, data, 1000000ul / _freqT);
+            else gio::shift::send_byte(_pins[1], _pins[2], MSBFIRST, data, 1000000ul / _freqT);
+        } else {
+            SPI.transfer(data);
+        }
+        _end();
+    }
+    void send(uint8_t* data, uint16_t len) {
+        _begin();
+        if (_softT) {
+            if (_datT >= 0) gio::shift::send(_datT, _clkT, MSBFIRST, data, len, 1000000ul / _freqT);
+            else gio::shift::send(_pins[1], _pins[2], MSBFIRST, data, len, 1000000ul / _freqT);
+        } else {
+            for (uint16_t i = 0; i < len; i++) SPI.transfer(data[i]);
+        }
+        _end();
+    }
+
+   private:
+    void _begin() {
+        if (_csT >= 0) gio::low(_csT);
+        else if (_pins[0] >= 0) gio::low(_pins[0]);
+
+        if (!_softT) SPI.beginTransaction(SPISettings(_freqT, MSBFIRST, SPI_MODE0));
+    }
+    void _end() {
+        if (!_softT) SPI.endTransaction();
+
+        if (_csT >= 0) gio::high(_csT);
+        else if (_pins[0] >= 0) gio::high(_pins[0]);
+    }
+
+    int8_t _pins[(_csT < 0) + ((_softT && _datT < 0) ? 2 : 0)];  // CS, DAT, CLK
+
+    /*
+    SSPI<0, f, cs> spi;             0
+    SSPI<0, f, -1> spi(?);          1
+    SSPI<1, f, cs, dat, clk> spi;   0
+    SSPI<1, f> spi(cs, dat, clk);   1+2
+    */
+};
+
+}  // namespace gio

+ 29 - 0
.pio/libdeps/pico/GyverIO/src/gio/gio.h

@@ -0,0 +1,29 @@
+#pragma once
+// быстрые функции пинов
+// #define GIO_USE_ARDUINO
+
+#ifdef GIO_USE_ARDUINO
+#include "gio_arduino.h"
+#else
+
+#if defined(__AVR__) || defined(ARDUINO_ARCH_AVR)
+#include "gio_avr.h"
+#elif defined(ESP8266) || defined(ARDUINO_ARCH_ESP8266)
+#include "gio_esp8266.h"
+#elif defined(ESP32) || defined(ARDUINO_ARCH_ESP32)
+#include "gio_esp32.h"
+#else
+#include "gio_arduino.h"
+#endif
+
+#endif  // GIO_USE_ARDUINO
+
+/*
+us (MHz)
+|         | digitalWrite | gio_write | gio_toggle | digitalRead | gio_read   | pinMode      | gio_mode   |
+|---------|--------------|-----------|------------|-------------|------------|--------------|------------|
+| AVR     | 1.47 (0.68)  | 0.06 (16) | 0.06 (16)  | 3.23 (0.31) | 0.13 (8)   | 3.33 (0.3)   | 0.13 (8)   |
+| ESP8266 | 1.56 (0.64)  | 0.08 (12) | 0.5 (2)    | 0.56 (1.8)  | 0.21 (4.8) | 1.43 (0.7)   | 0.67 (1.5) |
+| ESP32   | 0.33 (3)     | 0.04 (24) | 0.13 (8)   | 1.67 (0.6)  | 0.08 (12)  | 16.67 (0.06) | 0.08 (12)  |
+| ESP32C3 | 0.92 (1)     | 0.06 (16) | 0.19 (5.2) | 0.25 (4)    | 0.04 (24)  | 20 (0.05)    | 0.04 (24)  |
+*/

+ 43 - 0
.pio/libdeps/pico/GyverIO/src/gio/gio_arduino.h

@@ -0,0 +1,43 @@
+#pragma once
+#include <Arduino.h>
+
+#include "gio_defs.h"
+
+namespace gio {
+
+// mode
+_GIO_INLINE void mode(int P, int V) {
+    pinMode(P, V);
+}
+
+// read
+_GIO_INLINE int read(int P) {
+    return digitalRead(P);
+}
+
+// high
+_GIO_INLINE void high(int P) {
+    digitalWrite(P, 1);
+}
+
+// low
+_GIO_INLINE void low(int P) {
+    digitalWrite(P, 0);
+}
+
+// write
+_GIO_INLINE void write(int P, int V) {
+    digitalWrite(P, V);
+}
+
+// toggle
+_GIO_INLINE void toggle(int P) {
+    digitalWrite(P, !digitalRead(P));
+}
+
+// init
+_GIO_INLINE void init(int P, int V = INPUT) {
+    mode(P, V);
+}
+
+}

+ 356 - 0
.pio/libdeps/pico/GyverIO/src/gio/gio_avr.h

@@ -0,0 +1,356 @@
+/*
+    Optimized digital functions for AVR microcontrollers
+    by Watterott electronic (www.watterott.com)
+    based on https://code.google.com/p/digitalwritefast
+    based on https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/digitalWriteFast.h
+
+    License: BSD 3-Clause License (https://opensource.org/licenses/BSD-3-Clause)
+*/
+
+#pragma once
+#if defined(__AVR__) || defined(ARDUINO_ARCH_AVR)
+
+#include <Arduino.h>
+
+#include "gio_defs.h"
+
+// =====================================================================
+// ============================== PINOUT ===============================
+// =====================================================================
+
+// --- Arduino Mega and ATmega128x/256x based boards ---
+#if (defined(ARDUINO_AVR_MEGA) ||     \
+     defined(ARDUINO_AVR_MEGA1280) || \
+     defined(ARDUINO_AVR_MEGA2560) || \
+     defined(__AVR_ATmega1280__) ||   \
+     defined(__AVR_ATmega1281__) ||   \
+     defined(__AVR_ATmega2560__) ||   \
+     defined(__AVR_ATmega2561__))
+
+#define __avr_pin_to_port(P) \
+    (((P) >= 22 && (P) <= 29) ? &PORTA : ((((P) >= 10 && (P) <= 13) || ((P) >= 50 && (P) <= 53)) ? &PORTB : (((P) >= 30 && (P) <= 37) ? &PORTC : ((((P) >= 18 && (P) <= 21) || (P) == 38) ? &PORTD : ((((P) <= 3) || (P) == 5) ? &PORTE : (((P) >= 54 && (P) <= 61) ? &PORTF : ((((P) >= 39 && (P) <= 41) || (P) == 4) ? &PORTG : ((((P) >= 6 && (P) <= 9) || (P) == 16 || (P) == 17) ? &PORTH : (((P) == 14 || (P) == 15) ? &PORTJ : (((P) >= 62 && (P) <= 69) ? &PORTK : &PORTL))))))))))
+
+#define __avr_pin_to_ddr(P) \
+    (((P) >= 22 && (P) <= 29) ? &DDRA : ((((P) >= 10 && (P) <= 13) || ((P) >= 50 && (P) <= 53)) ? &DDRB : (((P) >= 30 && (P) <= 37) ? &DDRC : ((((P) >= 18 && (P) <= 21) || (P) == 38) ? &DDRD : ((((P) <= 3) || (P) == 5) ? &DDRE : (((P) >= 54 && (P) <= 61) ? &DDRF : ((((P) >= 39 && (P) <= 41) || (P) == 4) ? &DDRG : ((((P) >= 6 && (P) <= 9) || (P) == 16 || (P) == 17) ? &DDRH : (((P) == 14 || (P) == 15) ? &DDRJ : (((P) >= 62 && (P) <= 69) ? &DDRK : &DDRL))))))))))
+
+#define __avr_pin_to_pin(P) \
+    (((P) >= 22 && (P) <= 29) ? &PINA : ((((P) >= 10 && (P) <= 13) || ((P) >= 50 && (P) <= 53)) ? &PINB : (((P) >= 30 && (P) <= 37) ? &PINC : ((((P) >= 18 && (P) <= 21) || (P) == 38) ? &PIND : ((((P) <= 3) || (P) == 5) ? &PINE : (((P) >= 54 && (P) <= 61) ? &PINF : ((((P) >= 39 && (P) <= 41) || (P) == 4) ? &PING : ((((P) >= 6 && (P) <= 9) || (P) == 16 || (P) == 17) ? &PINH : (((P) == 14 || (P) == 15) ? &PINJ : (((P) >= 62 && (P) <= 69) ? &PINK : &PINL))))))))))
+
+#define __avr_pin_to_bit(P) \
+    (((P) >= 7 && (P) <= 9) ? (P)-3 : (((P) >= 10 && (P) <= 13) ? (P)-6 : (((P) >= 22 && (P) <= 29) ? (P)-22 : (((P) >= 30 && (P) <= 37) ? 37 - (P) : (((P) >= 39 && (P) <= 41) ? 41 - (P) : (((P) >= 42 && (P) <= 49) ? 49 - (P) : (((P) >= 50 && (P) <= 53) ? 53 - (P) : (((P) >= 54 && (P) <= 61) ? (P)-54 : (((P) >= 62 && (P) <= 69) ? (P)-62 : (((P) == 0 || (P) == 15 || (P) == 17 || (P) == 21) ? 0 : (((P) == 1 || (P) == 14 || (P) == 16 || (P) == 20) ? 1 : (((P) == 19) ? 2 : (((P) == 5 || (P) == 6 || (P) == 18) ? 3 : (((P) == 2) ? 4 : (((P) == 3 || (P) == 4) ? 5 : 7)))))))))))))))
+
+// --- Arduino MightyCore standard pinout ---
+#elif (defined(__AVR_ATmega1284P__) || \
+       defined(__AVR_ATmega1284__) ||  \
+       defined(__AVR_ATmega644P__) ||  \
+       defined(__AVR_ATmega644A__) ||  \
+       defined(__AVR_ATmega644__) ||   \
+       defined(__AVR_ATmega324PB__) || \
+       defined(__AVR_ATmega324PA__) || \
+       defined(__AVR_ATmega324P__) ||  \
+       defined(__AVR_ATmega324A__) ||  \
+       defined(__AVR_ATmega164P__) ||  \
+       defined(__AVR_ATmega164A__) ||  \
+       defined(__AVR_ATmega32__) ||    \
+       defined(__AVR_ATmega16__) ||    \
+       defined(__AVR_ATmega8535__)) && \
+    !defined(BOBUINO_PINOUT)
+
+#if defined(__AVR_ATmega324PB__)
+#define __avr_pin_to_port(P) \
+    (((P) <= 7) ? &PORTB : (((P) >= 8 && (P) <= 15) ? &PORTD : (((P) >= 16 && (P) <= 23) ? &PORTC : (((P) >= 24 && (P) <= 31) ? &PORTA : &PORTE))))
+#define __avr_pin_to_ddr(P) \
+    (((P) <= 7) ? &DDRB : (((P) >= 8 && (P) <= 15) ? &DDRD : (((P) >= 16 && (P) <= 23) ? &DDRC : (((P) >= 24 && (P) <= 31) ? &DDRA : &DDRE))))
+#define __avr_pin_to_pin(P) \
+    (((P) <= 7) ? &PINB : (((P) >= 8 && (P) <= 15) ? &PIND : (((P) >= 16 && (P) <= 23) ? &PINC : (((P) >= 24 && (P) <= 31) ? &PINA : &PINE))))
+#define __avr_pin_to_bit(P) \
+    (((P) <= 7) ? (P) : (((P) >= 8 && (P) <= 15) ? (P)-8 : (((P) >= 16 && (P) <= 23) ? (P)-16 : (((P) >= 16 && (P) <= 23) ? (P)-24 : (P)-32))))
+#elif defined(PORTA)
+#define __avr_pin_to_port(P) \
+    (((P) <= 7) ? &PORTB : (((P) >= 8 && (P) <= 15) ? &PORTD : (((P) >= 16 && (P) <= 23) ? &PORTC : &PORTA)))
+#define __avr_pin_to_ddr(P) \
+    (((P) <= 7) ? &DDRB : (((P) >= 8 && (P) <= 15) ? &DDRD : (((P) >= 16 && (P) <= 23) ? &DDRC : &DDRA)))
+#define __avr_pin_to_pin(P) \
+    (((P) <= 7) ? &PINB : (((P) >= 8 && (P) <= 15) ? &PIND : (((P) >= 16 && (P) <= 23) ? &PINC : &PINA)))
+#define __avr_pin_to_bit(P) \
+    (((P) <= 7) ? (P) : (((P) >= 8 && (P) <= 15) ? (P)-8 : (((P) >= 16 && (P) <= 23) ? (P)-16 : (P)-24)))
+#else
+#define __avr_pin_to_port(P) \
+    (((P) <= 7) ? &PORTB : (((P) >= 8 && (P) <= 15) ? &PORTD : &PORTC))
+#define __avr_pin_to_ddr(P) \
+    (((P) <= 7) ? &DDRB : (((P) >= 8 && (P) <= 15) ? &DDRD : &DDRC))
+#define __avr_pin_to_pin(P) \
+    (((P) <= 7) ? &PINB : (((P) >= 8 && (P) <= 15) ? &PIND : &PINC))
+#define __avr_pin_to_bit(P) \
+    (((P) <= 7) ? (P) : (((P) >= 8 && (P) <= 15) ? (P)-8 : (((P) >= 16 && (P) <= 23) ? (P)-16 : (P)-24)))
+#endif
+
+// --- Arduino Leonardo and ATmega16U4/32U4 based boards ---
+#elif (defined(ARDUINO_AVR_LEONARDO) || \
+       defined(__AVR_ATmega16U4__) ||   \
+       defined(__AVR_ATmega32U4__))
+#if defined(TEENSYDUINO)
+
+#define __avr_pin_to_port(P)                                                                                                     \
+    ((((P) <= 4) || ((P) >= 13 && (P) <= 15)) ? &PORTB : (((P) == 9 || (P) == 10) ? &PORTC : (((P) >= 16 && (P) <= 21)) ? &PORTF \
+                                                                                                                        : &PORTD))
+#define __avr_pin_to_ddr(P)                                                                                                   \
+    ((((P) <= 4) || ((P) >= 13 && (P) <= 15)) ? &DDRB : (((P) == 9 || (P) == 10) ? &DDRC : (((P) >= 16 && (P) <= 21)) ? &DDRF \
+                                                                                                                      : &DDRD))
+#define __avr_pin_to_pin(P)                                                                                                   \
+    ((((P) <= 4) || ((P) >= 13 && (P) <= 15)) ? &PINB : (((P) == 9 || (P) == 10) ? &PINC : (((P) >= 16 && (P) <= 21)) ? &PINF \
+                                                                                                                      : &PIND))
+#define __avr_pin_to_bit(P) \
+    (((P) <= 3) ? (P) : (((P) == 4 || (P) == 12) ? 7 : (((P) <= 8) ? (P)-5 : (((P) <= 10) ? (P)-3 : (((P) == 11) ? 6 : (((P) <= 15) ? (P)-9 : (((P) <= 19) ? 23 - (P) : (((P) <= 21) ? 21 - (P) : (P)-18))))))))
+#else
+
+#define __avr_pin_to_port(P)                                                                                                                                         \
+    ((((P) <= 4) || (P) == 6 || (P) == 12 || (P) == 24 || (P) == 25 || (P) == 29) ? &PORTD : (((P) == 5 || (P) == 13) ? &PORTC : (((P) >= 18 && (P) <= 23)) ? &PORTF \
+                                                                                                                                                            : (((P) == 7) ? &PORTE : &PORTB)))
+#define __avr_pin_to_ddr(P)                                                                                                                                       \
+    ((((P) <= 4) || (P) == 6 || (P) == 12 || (P) == 24 || (P) == 25 || (P) == 29) ? &DDRD : (((P) == 5 || (P) == 13) ? &DDRC : (((P) >= 18 && (P) <= 23)) ? &DDRF \
+                                                                                                                                                          : (((P) == 7) ? &DDRE : &DDRB)))
+#define __avr_pin_to_pin(P)                                                                                                                                       \
+    ((((P) <= 4) || (P) == 6 || (P) == 12 || (P) == 24 || (P) == 25 || (P) == 29) ? &PIND : (((P) == 5 || (P) == 13) ? &PINC : (((P) >= 18 && (P) <= 23)) ? &PINF \
+                                                                                                                                                          : (((P) == 7) ? &PINE : &PINB)))
+#define __avr_pin_to_bit(P) \
+    (((P) >= 8 && (P) <= 11) ? (P)-4 : (((P) >= 18 && (P) <= 21) ? 25 - (P) : (((P) == 0) ? 2 : (((P) == 1) ? 3 : (((P) == 2) ? 1 : (((P) == 3) ? 0 : (((P) == 4) ? 4 : (((P) == 6) ? 7 : (((P) == 13) ? 7 : (((P) == 14) ? 3 : (((P) == 15) ? 1 : (((P) == 16) ? 2 : (((P) == 17) ? 0 : (((P) == 22) ? 1 : (((P) == 23) ? 0 : (((P) == 24) ? 4 : (((P) == 25) ? 7 : (((P) == 26) ? 4 : (((P) == 27) ? 5 : 6)))))))))))))))))))
+#endif
+
+// --- Arduino Uno and ATmega168/328 based boards ---
+#elif (defined(ARDUINO_AVR_UNO) ||         \
+       defined(ARDUINO_AVR_DUEMILANOVE) || \
+       defined(__AVR_ATmega8__) ||         \
+       defined(__AVR_ATmega48__) ||        \
+       defined(__AVR_ATmega48P__) ||       \
+       defined(__AVR_ATmega48PB__) ||      \
+       defined(__AVR_ATmega88P__) ||       \
+       defined(__AVR_ATmega88PB__) ||      \
+       defined(__AVR_ATmega168__) ||       \
+       defined(__AVR_ATmega168PA__) ||     \
+       defined(__AVR_ATmega168PB__) ||     \
+       defined(__AVR_ATmega328__) ||       \
+       defined(__AVR_ATmega328P__) ||      \
+       defined(__AVR_ATmega328PB__))
+
+#if defined(__AVR_ATmega48PB__) || defined(__AVR_ATmega88PB__) || defined(__AVR_ATmega168PB__) || defined(__AVR_ATmega328PB__)
+#define __avr_pin_to_port(P) \
+    (((P) <= 7) ? &PORTD : (((P) >= 8 && (P) <= 13) ? &PORTB : (((P) >= 14 && (P) <= 19) ? &PORTC : &PORTE)))
+#define __avr_pin_to_ddr(P) \
+    (((P) <= 7) ? &DDRD : (((P) >= 8 && (P) <= 13) ? &DDRB : (((P) >= 14 && (P) <= 19) ? &DDRC : &DDRE)))
+#define __avr_pin_to_pin(P) \
+    (((P) <= 7) ? &PIND : (((P) >= 8 && (P) <= 13) ? &PINB : (((P) >= 14 && (P) <= 19) ? &PINC : &PINE)))
+#define __avr_pin_to_bit(P) \
+    (((P) <= 7) ? (P) : (((P) >= 8 && (P) <= 13) ? (P)-8 : (((P) >= 14 && (P) <= 19) ? (P)-14 : (((P) >= 20 && (P) <= 21) ? (P)-18 : (P)-22))))
+#else
+#define __avr_pin_to_port(P) \
+    (((P) <= 7) ? &PORTD : (((P) >= 8 && (P) <= 13) ? &PORTB : &PORTC))
+#define __avr_pin_to_ddr(P) \
+    (((P) <= 7) ? &DDRD : (((P) >= 8 && (P) <= 13) ? &DDRB : &DDRC))
+#define __avr_pin_to_pin(P) \
+    (((P) <= 7) ? &PIND : (((P) >= 8 && (P) <= 13) ? &PINB : &PINC))
+#define __avr_pin_to_bit(P) \
+    (((P) <= 7) ? (P) : (((P) >= 8 && (P) <= 13) ? (P)-8 : (P)-14))
+#endif
+
+// --- Arduino Uno WiFi Rev 2, Nano Every ---
+#elif defined(__AVR_ATmega4809__)
+
+#define __avr_pin_to_port(P)                                                                                 \
+    (((P) == 2 || (P) == 7) ? &VPORTA.OUT : ((P) == 5 || (P) == 9 || (P) == 10)                ? &VPORTB.OUT \
+                                        : ((P) == 4)                                           ? &VPORTC.OUT \
+                                        : (((P) >= 14 && (P) <= 17) || (P) == 20 || (P) == 21) ? &VPORTD.OUT \
+                                        : ((P) == 8 || (P) == 11 || (P) == 12 || (P) == 13)    ? &VPORTE.OUT \
+                                                                                               : &VPORTF.OUT)
+#define __avr_pin_to_ddr(P)                                                                                  \
+    (((P) == 2 || (P) == 7) ? &VPORTA.DIR : ((P) == 5 || (P) == 9 || (P) == 10)                ? &VPORTB.DIR \
+                                        : ((P) == 4)                                           ? &VPORTC.DIR \
+                                        : (((P) >= 14 && (P) <= 17) || (P) == 20 || (P) == 21) ? &VPORTD.DIR \
+                                        : ((P) == 8 || (P) == 11 || (P) == 12 || (P) == 13)    ? &VPORTE.DIR \
+                                                                                               : &VPORTF.DIR)
+#define __avr_pin_to_pin(P)                                                                                \
+    (((P) == 2 || (P) == 7) ? &VPORTA.IN : ((P) == 5 || (P) == 9 || (P) == 10)                ? &VPORTB.IN \
+                                       : ((P) == 4)                                           ? &VPORTC.IN \
+                                       : (((P) >= 14 && (P) <= 17) || (P) == 20 || (P) == 21) ? &VPORTD.IN \
+                                       : ((P) == 8 || (P) == 11 || (P) == 12 || (P) == 13)    ? &VPORTE.IN \
+                                                                                              : &VPORTF.IN)
+#define __avr_pin_to_bit(P)                                                                                       \
+    (((P) == 2 || (P) == 9 || (P) == 11 || (P) == 17) ? 0 : ((P) == 7 || (P) == 10 || (P) == 12 || (P) == 16) ? 1 \
+                                                        : ((P) == 5 || (P) == 13 || (P) == 15 || (P) == 18)   ? 2 \
+                                                        : ((P) == 9 || (P) == 14 || (P) == 19)                ? 3 \
+                                                        : ((P) == 6 || (P) == 20)                             ? 4 \
+                                                        : ((P) == 3 || (P) == 21)                             ? 5 \
+                                                                                                              : 6)
+
+// TinyCore
+// https://raw.githubusercontent.com/xukangmin/TinyCore/master/avr/package/package_tinycore_index.json
+// https://docs.tinycore.dev/en/latest/
+#elif defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__) || defined(__AVR_ATtiny3217__)
+#define __avr_pin_to_port(P) ((P) <= 5 ? &VPORTB.OUT : ((P) <= 9 ? &VPORTC.OUT : ((P) <= 16 ? &VPORTA.OUT : ((P) <= 18 ? &VPORTB.OUT : &VPORTC.OUT))))
+#define __avr_pin_to_ddr(P) ((P) <= 5 ? &VPORTB.DIR : ((P) <= 9 ? &VPORTC.DIR : ((P) <= 16 ? &VPORTA.DIR : ((P) <= 18 ? &VPORTB.DIR : &VPORTC.DIR))))
+#define __avr_pin_to_pin(P) ((P) <= 5 ? &VPORTB.IN : ((P) <= 9 ? &VPORTC.IN : ((P) <= 16 ? &VPORTA.IN : ((P) <= 18 ? &VPORTB.IN : &VPORTC.IN))))
+#define __avr_pin_to_bit(P) ((P) <= 3 ? (3 - P) : ((P) <= 5 ? (P) : ((P) <= 9 ? (P - 6) : ((P) <= 16 ? ((P)-9) : ((P) <= 18 ? ((P)-11) : ((P)-15))))))
+
+#elif defined(__AVR_ATtiny1614__)
+#define __avr_pin_to_port(P) ((P) <= 3 ? &VPORTA.OUT : ((P) <= 7 ? &VPORTB.OUT : &VPORTA.OUT))
+#define __avr_pin_to_ddr(P) ((P) <= 3 ? &VPORTA.DIR : ((P) <= 7 ? &VPORTB.DIR : &VPORTA.DIR))
+#define __avr_pin_to_pin(P) ((P) <= 3 ? &VPORTA.IN : ((P) <= 7 ? &VPORTB.IN : &VPORTA.IN))
+#define __avr_pin_to_bit(P) ((P) <= 3 ? (P + 4) : ((P) <= 7 ? (7 - P) : ((P) <= 10 ? (P - 7) : (P)-11)))
+
+#elif defined(__AVR_ATtiny816__)
+// https://github.com/Arduino-IRremote/Arduino-IRremote/discussions/1029
+#define __avr_pin_to_port(P) ((P) <= 3 ? &VPORTA.OUT : ((P) <= 9 ? &VPORTB.OUT : ((P) <= 13 ? &VPORTC.OUT : ((P) <= 17 ? &VPORTA.OUT : &VPORTC.OUT))))
+#define __avr_pin_to_ddr(P) ((P) <= 3 ? &VPORTA.DIR : ((P) <= 9 ? &VPORTB.DIR : ((P) <= 13 ? &VPORTC.DIR : ((P) <= 17 ? &VPORTA.DIR : &VPORTC.DIR))))
+#define __avr_pin_to_pin(P) ((P) <= 3 ? &VPORTA.IN : ((P) <= 9 ? &VPORTB.IN : ((P) <= 13 ? &VPORTC.IN : ((P) <= 17 ? &VPORTA.IN : &VPORTC.IN))))
+#define __avr_pin_to_bit(P) ((P) <= 3 ? (P + 4) : ((P) <= 9 ? (9 - P) : ((P) <= 13 ? (P - 10) : ((P) <= 16 ? (P - 13) : ((P)-17)))))
+
+// --- ATtinyX5 ---
+#elif defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
+// we have only PORTB
+#define __avr_pin_to_port(P) (&PORTB)
+#define __avr_pin_to_ddr(P) (&DDRB)
+#define __avr_pin_to_pin(P) (&PINB)
+#define __avr_pin_to_bit(P) (((P) <= 7) ? (P) : (((P) >= 8 && (P) <= 13) ? (P)-8 : (P)-14))
+
+// --- ATtiny88 ---
+#elif defined(__AVR_ATtiny88__)
+#if defined(ARDUINO_AVR_DIGISPARKPRO)
+#define __avr_pin_to_port(P) ((P) <= 7 ? &PORTD : ((P) <= 14 ? &PORTB : ((P) <= 18 ? &PORTA : &PORTC)))
+#define __avr_pin_to_ddr(P) ((P) <= 7 ? &DDRD : ((P) <= 14 ? &DDRB : ((P) <= 18 ? &DDRA : &DDRC)))
+#define __avr_pin_to_pin(P) ((P) <= 7 ? &PIND : ((P) <= 14 ? &PINB : ((P) <= 18 ? &PINA : &PINC)))
+#define __avr_pin_to_bit(P) ((P) <= 7 ? (P) : ((P) <= 13 ? ((P)-8) : ((P) == 14 ? 7 : ((P) <= 16 ? ((P)-14) : ((P) <= 18 ? ((P)-17) : ((P) == 25 ? 7 : ((P)-19)))))))
+#else
+#define __avr_pin_to_port(P) ((P) <= 7 ? &PORTD : ((P) <= 15 ? &PORTB : ((P) <= 22 ? &PORTC : ((P) <= 26 ? &PORTA : &PORTC))))
+#define __avr_pin_to_ddr(P) ((P) <= 7 ? &DDRD : ((P) <= 15 ? &DDRB : ((P) <= 22 ? &DDRC : ((P) <= 26 ? &DDRA : &DDRC))))
+#define __avr_pin_to_pin(P) ((P) <= 7 ? &PIND : ((P) <= 15 ? &PINB : ((P) <= 22 ? &PINC : ((P) <= 26 ? &PINA : &PINC))))
+#define __avr_pin_to_bit(P) ((P) <= 15 ? ((P)&0x7) : ((P) == 16 ? (7) : ((P) <= 22 ? ((P)-17) : ((P) == 27 ? (6) : ((P)-23)))))
+#endif
+
+// --- ATtinyX4 + ATtinyX7 ---
+#elif defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny441__) || defined(__AVR_ATtiny841__) || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__)
+#if defined(ARDUINO_AVR_DIGISPARKPRO) || PIN_PA7 == 5
+// Strange enumeration of pins on Digispark board and core library
+#define __avr_pin_to_port(P) (((P) <= 4) ? &PORTB : &PORTA)
+#define __avr_pin_to_ddr(P) (((P) <= 4) ? &DDRB : &DDRA)
+#define __avr_pin_to_pin(P) (((P) <= 4) ? &PINB : &PINA)
+#define __avr_pin_to_bit(P) (((P) <= 2) ? (P) : (((P) == 3) ? 6 : (((P) == 4) ? 3 : (((P) == 5) ? 7 : (P)-6))))
+#else
+// ATtinyX4: PORTA for 0 to 7, PORTB for 8 to 11
+// ATtinyX7: PORTA for 0 to 7, PORTB for 8 to 15
+#define __avr_pin_to_port(P) (((P) <= 7) ? &PORTA : &PORTB)
+#define __avr_pin_to_ddr(P) (((P) <= 7) ? &DDRA : &DDRB)
+#define __avr_pin_to_pin(P) (((P) <= 7) ? &PINA : &PINB)
+#endif
+#if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny441__) || defined(__AVR_ATtiny841__)
+// https://github.com/SpenceKonde/ATTinyCore/blob/v2.0.0-devThis-is-the-head-submit-PRs-against-this/avr/variants/tinyx41_cw/pins_arduino.h#L334
+// Clockwise layout
+#define __avr_pin_to_bit(P) (((P) <= 7) ? (P) : ((P) == 11 ? (3) : 10 - (P)))
+#else
+#define __avr_pin_to_bit(P) (((P) <= 7) ? (P) : (P)-8)
+#endif
+
+#endif
+
+// =====================================================================
+// ============================== IO FUNC ==============================
+// =====================================================================
+
+namespace gio {
+
+// mode
+_GIO_INLINE void mode(int P, int V) {
+#if defined(__avr_pin_to_port)
+    if (__builtin_constant_p(P) && __builtin_constant_p(V)) {
+        switch (V) {
+            case INPUT:
+                bitClear(*__avr_pin_to_ddr(P), __avr_pin_to_bit(P));
+                bitClear(*__avr_pin_to_port(P), __avr_pin_to_bit(P));
+                break;
+            case INPUT_PULLUP:
+                bitClear(*__avr_pin_to_ddr(P), __avr_pin_to_bit(P));
+                bitSet(*__avr_pin_to_port(P), __avr_pin_to_bit(P));
+                break;
+            case OUTPUT:
+                bitSet(*__avr_pin_to_ddr(P), __avr_pin_to_bit(P));
+                break;
+        }
+    } else
+#endif
+    {
+        switch (V) {
+            case INPUT:
+                greg_clr(portModeRegister(digitalPinToPort(P)), digitalPinToBitMask(P));
+                greg_clr(portOutputRegister(digitalPinToPort(P)), digitalPinToBitMask(P));
+                break;
+            case INPUT_PULLUP:
+                greg_clr(portModeRegister(digitalPinToPort(P)), digitalPinToBitMask(P));
+                greg_set(portOutputRegister(digitalPinToPort(P)), digitalPinToBitMask(P));
+                break;
+            case OUTPUT:
+                greg_set(portModeRegister(digitalPinToPort(P)), digitalPinToBitMask(P));
+                break;
+        }
+    }
+}
+
+// read
+_GIO_INLINE int read(int P) {
+#if defined(__avr_pin_to_pin)
+    if (__builtin_constant_p(P)) {
+        return (bitRead(*__avr_pin_to_pin(P), __avr_pin_to_bit(P))) ? 1 : 0;
+    } else
+#endif
+    {
+        return greg_read(portInputRegister(digitalPinToPort(P)), digitalPinToBitMask(P));
+    }
+}
+
+// high
+_GIO_INLINE void high(int P) {
+#if defined(__avr_pin_to_port)
+    if (__builtin_constant_p(P)) {
+        bitSet(*__avr_pin_to_port(P), __avr_pin_to_bit(P));
+    } else
+#endif
+    {
+        greg_set(portOutputRegister(digitalPinToPort(P)), digitalPinToBitMask(P));
+    }
+}
+
+// low
+_GIO_INLINE void low(int P) {
+#if defined(__avr_pin_to_port)
+    if (__builtin_constant_p(P)) {
+        bitClear(*__avr_pin_to_port(P), __avr_pin_to_bit(P));
+    } else
+#endif
+    {
+        greg_clr(portOutputRegister(digitalPinToPort(P)), digitalPinToBitMask(P));
+    }
+}
+
+// write
+_GIO_INLINE void write(int P, int V) {
+    V ? high(P) : low(P);
+}
+
+// toggle
+_GIO_INLINE void toggle(int P) {
+#if defined(__avr_pin_to_pin)
+    if (__builtin_constant_p(P)) {
+        bitSet(*__avr_pin_to_pin(P), __avr_pin_to_bit(P));
+    } else
+#endif
+    {
+        greg_set(portInputRegister(digitalPinToPort(P)), digitalPinToBitMask(P));
+    }
+}
+
+// init
+_GIO_INLINE void init(int P, int V = INPUT) {
+    mode(P, V);
+}
+
+}  // namespace gio
+
+#endif

+ 7 - 0
.pio/libdeps/pico/GyverIO/src/gio/gio_defs.h

@@ -0,0 +1,7 @@
+#pragma once
+
+#define _GIO_INLINE static inline __attribute__((always_inline))
+#define greg_set(reg, mask) *reg |= mask
+#define greg_clr(reg, mask) *reg &= ~mask
+#define greg_write(reg, mask, val) (val) ? greg_set(reg, mask) : greg_clr(reg, mask)
+#define greg_read(reg, mask) (bool)(*reg & mask)

+ 112 - 0
.pio/libdeps/pico/GyverIO/src/gio/gio_esp32.h

@@ -0,0 +1,112 @@
+// based on https://github.com/PaulStoffregen/OneWire/blob/master/util/OneWire_direct_gpio.h
+
+#pragma once
+#if defined(ESP32) || defined(ARDUINO_ARCH_ESP32)
+
+#include <Arduino.h>
+#include <driver/rtc_io.h>
+#include <soc/gpio_struct.h>
+
+#include "gio_defs.h"
+
+#if ESP_IDF_VERSION_MAJOR < 4
+#define _ESP32_IDF_V3_REG()                                                                               \
+    uint32_t rtc_reg(rtc_gpio_desc[pin].reg);                                                             \
+    if (rtc_reg) {                                                                                        \
+        ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_gpio_desc[pin].mux);                                  \
+        ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_gpio_desc[pin].pullup | rtc_gpio_desc[pin].pulldown); \
+    }
+#else
+#define _ESP32_IDF_V3_REG()
+#endif
+
+namespace gio {
+
+// mode
+_GIO_INLINE void mode(uint8_t pin, uint8_t mode) {
+    switch (mode) {
+        case INPUT:
+#if CONFIG_IDF_TARGET_ESP32C3
+            GPIO.enable_w1tc.val = (1ul << (pin));
+#else
+            if (digitalPinIsValid(pin)) {
+                _ESP32_IDF_V3_REG();
+                if (pin < 32) GPIO.enable_w1tc = (1ul << pin);
+                else GPIO.enable1_w1tc.val = (1ul << (pin - 32));
+            }
+#endif
+            break;
+
+        case OUTPUT:
+#if CONFIG_IDF_TARGET_ESP32C3
+            GPIO.enable_w1ts.val = (1ul << (pin));
+#else
+            if (digitalPinIsValid(pin)) {
+                _ESP32_IDF_V3_REG();
+                if (pin < 32) GPIO.enable_w1ts = (1ul << pin);
+                else GPIO.enable1_w1ts.val = (1ul << (pin - 32));
+            }
+#endif
+            break;
+
+        default:
+            pinMode(pin, mode);
+            break;
+    }
+}
+
+// read
+_GIO_INLINE int read(uint8_t pin) {
+#if CONFIG_IDF_TARGET_ESP32C3
+    return (GPIO.in.val >> pin) & 0x1;
+#else
+    if (digitalPinIsValid(pin)) {
+        if (pin < 32) return (GPIO.in >> pin) & 0x1;
+        else return (GPIO.in1.val >> (pin - 32)) & 0x1;
+    }
+#endif
+    return 0;
+}
+
+// low
+_GIO_INLINE void low(uint8_t pin) {
+#if CONFIG_IDF_TARGET_ESP32C3
+    GPIO.out_w1tc.val = (1ul << pin);
+#else
+    if (digitalPinIsValid(pin)) {
+        if (pin < 32) GPIO.out_w1tc = (1ul << pin);
+        else GPIO.out1_w1tc.val = (1ul << (pin - 32));
+    }
+#endif
+}
+
+// high
+_GIO_INLINE void high(uint8_t pin) {
+#if CONFIG_IDF_TARGET_ESP32C3
+    GPIO.out_w1ts.val = (1ul << pin);
+#else
+    if (digitalPinIsValid(pin)) {
+        if (pin < 32) GPIO.out_w1ts = (1ul << pin);
+        else GPIO.out1_w1ts.val = (1ul << (pin - 32));
+    }
+#endif
+}
+
+// write
+_GIO_INLINE void write(uint8_t pin, uint8_t val) {
+    val ? high(pin) : low(pin);
+}
+
+// toggle
+_GIO_INLINE void toggle(uint8_t pin) {
+    write(pin, !read(pin));
+}
+
+// init
+_GIO_INLINE void init(int P, int V = INPUT) {
+    pinMode(P, V);
+}
+
+}  // namespace gio
+
+#endif

+ 89 - 0
.pio/libdeps/pico/GyverIO/src/gio/gio_esp8266.h

@@ -0,0 +1,89 @@
+#pragma once
+#if defined(ESP8266) || defined(ARDUINO_ARCH_ESP8266)
+
+#include <Arduino.h>
+
+#include "gio_defs.h"
+
+namespace gio {
+
+// mode
+_GIO_INLINE void mode(uint8_t pin, uint8_t mode) {
+    // dsnt work
+    // if (mode == INPUT) GPE &= ~(1 << pin);
+    // else if (mode == OUTPUT) GPE |= (1 << pin);
+
+    switch (mode) {
+        case INPUT:
+        case INPUT_PULLUP:
+            if (pin < 16) {
+                GPF(pin) = GPFFS(GPFFS_GPIO(pin));
+                GPEC = (1 << pin);
+                GPC(pin) = (GPC(pin) & (0xF << GPCI)) | (1 << GPCD);
+                if (mode == INPUT_PULLUP) GPF(pin) |= (1 << GPFPU);
+            } else if (pin == 16) {
+                GPF16 = GP16FFS(GPFFS_GPIO(pin));
+                GPC16 = 0;
+                GP16E &= ~1;
+            }
+            break;
+
+        case OUTPUT:
+            if (pin < 16) {
+                GPF(pin) = GPFFS(GPFFS_GPIO(pin));
+                GPC(pin) = (GPC(pin) & (0xF << GPCI));
+                GPES = (1 << pin);
+            } else if (pin == 16) {
+                GPF16 = GP16FFS(GPFFS_GPIO(pin));
+                GPC16 = 0;
+                GP16E |= 1;
+            }
+            break;
+
+        default:
+            pinMode(pin, mode);
+            break;
+    }
+}
+
+// read
+_GIO_INLINE int read(uint8_t pin) {
+    return (pin < 16) ? GPIP(pin) : (GP16I & 0x01);
+}
+
+// low
+_GIO_INLINE void low(uint8_t pin) {
+    if (pin < 16) GPOC = (1 << pin);
+    else if (pin == 16) GP16O &= ~1;
+}
+
+// high
+_GIO_INLINE void high(uint8_t pin) {
+    if (pin < 16) GPOS = (1 << pin);
+    else if (pin == 16) GP16O |= 1;
+}
+
+// write
+_GIO_INLINE void write(uint8_t pin, uint8_t val) {
+    val ? high(pin) : low(pin);
+}
+
+// toggle
+_GIO_INLINE void toggle(uint8_t pin) {
+    if (pin < 16) {
+        if (GPIP(pin)) GPOC = (1 << pin);
+        else GPOS = (1 << pin);
+    } else if (pin == 16) {
+        if (GP16I & 0x01) GP16O &= ~1;
+        else GP16O |= 1;
+    }
+}
+
+// init
+_GIO_INLINE void init(int P, int V = INPUT) {
+    mode(P, V);
+}
+
+}  // namespace gio
+
+#endif

+ 79 - 0
.pio/libdeps/pico/GyverIO/src/utils/PinIO.h

@@ -0,0 +1,79 @@
+#pragma once
+
+#include <Arduino.h>
+
+#include "../gio/gio.h"
+
+// #define GIO_NO_MASK
+
+namespace gio {
+
+// single mode pin (output/input)
+class PinIO {
+   public:
+    PinIO() {}
+    PinIO(uint8_t npin, uint8_t mode = INPUT) {
+        init(npin, mode);
+    }
+
+    void init(uint8_t npin, uint8_t mode = INPUT) {
+        gio::init(npin, mode);
+#if defined(__AVR__) && !defined(GIO_NO_MASK)
+        if (mode == OUTPUT) reg = portOutputRegister(digitalPinToPort(npin));
+        else reg = portInputRegister(digitalPinToPort(npin));
+        mask = digitalPinToBitMask(npin);
+#else
+        pin = npin;
+#endif
+    }
+
+    void write(uint8_t val) {
+#if defined(__AVR__) && !defined(GIO_NO_MASK)
+        greg_write(reg, mask, val);
+#else
+        gio::write(pin, val);
+#endif
+    }
+
+    void high() {
+#if defined(__AVR__) && !defined(GIO_NO_MASK)
+        greg_set(reg, mask);
+#else
+        gio::high(pin);
+#endif
+    }
+
+    void low() {
+#if defined(__AVR__) && !defined(GIO_NO_MASK)
+        greg_clr(reg, mask);
+#else
+        gio::low(pin);
+#endif
+    }
+
+    void toggle() {
+#if defined(__AVR__) && !defined(GIO_NO_MASK)
+        *reg ^= mask;
+#else
+        gio::toggle(pin);
+#endif
+    }
+
+    int read() {
+#if defined(__AVR__) && !defined(GIO_NO_MASK)
+        return greg_read(reg, mask);
+#else
+        return gio::read(pin);
+#endif
+    }
+
+   private:
+#if defined(__AVR__) && !defined(GIO_NO_MASK)
+    volatile uint8_t *reg;
+    uint8_t mask = 0xff;
+#else
+    uint8_t pin;
+#endif
+};
+
+}  // namespace gio

+ 36 - 0
.pio/libdeps/pico/GyverIO/src/utils/PinT.h

@@ -0,0 +1,36 @@
+#pragma once
+
+#include <Arduino.h>
+
+#include "../gio/gio.h"
+#include "../gio/gio_defs.h"
+
+namespace gio {
+
+template <uint8_t pin>
+class PinT {
+   public:
+    PinT(uint8_t mode = INPUT) {
+        gio::init(pin, mode);
+    }
+    _GIO_INLINE void mode(uint8_t mode) {
+        gio::mode(pin, mode);
+    }
+    _GIO_INLINE void write(uint8_t val) {
+        gio::write(pin, val);
+    }
+    _GIO_INLINE void high() {
+        gio::high(pin);
+    }
+    _GIO_INLINE void low() {
+        gio::low(pin);
+    }
+    _GIO_INLINE void toggle() {
+        gio::toggle(pin);
+    }
+    _GIO_INLINE int read() {
+        return gio::read(pin);
+    }
+};
+
+}  // namespace gio

+ 146 - 0
.pio/libdeps/pico/GyverIO/src/utils/shift.cpp

@@ -0,0 +1,146 @@
+#include "shift.h"
+
+namespace gio {
+namespace shift {
+
+bool read(uint8_t dat_pin, uint8_t clk_pin, uint8_t order, uint8_t* data, uint16_t len, uint8_t delay) {
+    bool dif = 0;
+    if (order & 0b10) data += len - 1;
+
+#if defined(__AVR__) && !defined(GIO_NO_MASK)
+    if (!__builtin_constant_p(dat_pin) || !__builtin_constant_p(clk_pin)) {
+        volatile uint8_t* c_reg = portOutputRegister(digitalPinToPort(clk_pin));
+        volatile uint8_t* d_reg = portInputRegister(digitalPinToPort(dat_pin));
+        uint8_t c_mask = digitalPinToBitMask(clk_pin);
+        uint8_t d_mask = digitalPinToBitMask(dat_pin);
+        uint8_t val = 0;
+        while (len--) {
+            val = 0;
+            for (uint8_t i = 0; i < 8; i++) {
+                if (order & 0b01) {  // MSBFIRST
+                    val <<= 1;
+                    if (greg_read(d_reg, d_mask)) val |= 1;
+                } else {
+                    val >>= 1;
+                    if (greg_read(d_reg, d_mask)) val |= (1 << 7);
+                }
+                if (delay) delayMicroseconds(delay);
+                greg_set(c_reg, c_mask);
+                if (delay) delayMicroseconds(delay);
+                greg_clr(c_reg, c_mask);
+            }
+            if (!dif && *data != val) dif = 1;
+            *data = val;
+            data += (order & 0b10) ? -1 : 1;
+        }
+        greg_clr(d_reg, d_mask);
+    } else
+#endif
+    {
+        uint8_t val = 0;
+        while (len--) {
+            val = 0;
+            for (uint8_t i = 0; i < 8; i++) {
+                if (order & 0b01) {  // MSBFIRST
+                    val <<= 1;
+                    if (gio::read(dat_pin)) val |= 1;
+                } else {
+                    val >>= 1;
+                    if (gio::read(dat_pin)) val |= (1 << 7);
+                }
+                if (delay) delayMicroseconds(delay);
+                gio::high(clk_pin);
+                if (delay) delayMicroseconds(delay);
+                gio::low(clk_pin);
+            }
+            if (!dif && *data != val) dif = 1;
+            *data = val;
+            data += (order & 0b10) ? -1 : 1;
+        }
+    }
+    return dif;
+}
+
+uint8_t read_byte(uint8_t dat_pin, uint8_t clk_pin, uint8_t order, uint8_t delay) {
+    uint8_t value;
+    read(dat_pin, clk_pin, order, &value, 1, delay);
+    return value;
+}
+
+bool read_cs(uint8_t dat_pin, uint8_t clk_pin, uint8_t cs_pin, uint8_t order, uint8_t* data, uint16_t len, uint8_t delay) {
+    gio::low(cs_pin);
+    bool res = read(dat_pin, clk_pin, order, data, len, delay);
+    gio::high(cs_pin);
+    return res;
+}
+
+uint8_t read_cs_byte(uint8_t dat_pin, uint8_t clk_pin, uint8_t cs_pin, uint8_t order, uint8_t delay) {
+    uint8_t value;
+    read_cs(dat_pin, clk_pin, cs_pin, order, &value, 1, delay);
+    return value;
+}
+
+void send(uint8_t dat_pin, uint8_t clk_pin, uint8_t order, uint8_t* data, uint16_t len, uint8_t delay) {
+#if defined(__AVR__) && !defined(GIO_NO_MASK)
+    if (!__builtin_constant_p(dat_pin) || !__builtin_constant_p(clk_pin)) {
+        volatile uint8_t* c_reg = portOutputRegister(digitalPinToPort(clk_pin));
+        volatile uint8_t* d_reg = portOutputRegister(digitalPinToPort(dat_pin));
+        uint8_t c_mask = digitalPinToBitMask(clk_pin);
+        uint8_t d_mask = digitalPinToBitMask(dat_pin);
+        uint8_t val;
+        for (uint16_t b = 0; b < len; b++) {
+            val = data[(order & 0b10) ? (len - b - 1) : b];
+            for (uint8_t i = 0; i < 8; i++) {
+                if (order & 0b01) {  // MSBFIRST
+                    greg_write(d_reg, d_mask, val & (1 << 7));
+                    val <<= 1;
+                } else {
+                    greg_write(d_reg, d_mask, val & 1);
+                    val >>= 1;
+                }
+                if (delay) delayMicroseconds(delay);
+                greg_set(c_reg, c_mask);
+                if (delay) delayMicroseconds(delay);
+                greg_clr(c_reg, c_mask);
+            }
+        }
+        greg_clr(d_reg, d_mask);
+    } else
+#endif
+    {
+        for (uint16_t b = 0; b < len; b++) {
+            uint8_t val = data[(order & 0b10) ? (len - b - 1) : b];
+            for (uint8_t i = 0; i < 8; i++) {
+                if (order & 0b01) {  // MSBFIRST
+                    gio::write(dat_pin, val & (1 << 7));
+                    val <<= 1;
+                } else {
+                    gio::write(dat_pin, val & 1);
+                    val >>= 1;
+                }
+                if (delay) delayMicroseconds(delay);
+                gio::high(clk_pin);
+                if (delay) delayMicroseconds(delay);
+                gio::low(clk_pin);
+            }
+            gio::low(dat_pin);
+        }
+    }
+}
+
+void send_byte(uint8_t dat_pin, uint8_t clk_pin, uint8_t order, uint8_t data, uint8_t delay) {
+    send(dat_pin, clk_pin, order, &data, 1, delay);
+}
+
+void send_cs(uint8_t dat_pin, uint8_t clk_pin, uint8_t cs_pin, uint8_t order, uint8_t* data, uint16_t len, uint8_t delay) {
+    gio::low(cs_pin);
+    send(dat_pin, clk_pin, order, data, len, delay);
+    gio::high(cs_pin);
+}
+
+void send_cs_byte(uint8_t dat_pin, uint8_t clk_pin, uint8_t cs_pin, uint8_t order, uint8_t data, uint8_t delay) {
+    send_cs(dat_pin, clk_pin, cs_pin, order, &data, 1, delay);
+}
+
+}  // namespace shift
+}  // namespace gio

Some files were not shown because too many files changed in this diff