├── .gitattributes
├── .github
└── workflows
│ └── tg-send.yml
├── LICENSE
├── README.md
├── README_EN.md
├── doc
├── btn_scheme.png
├── click.gif
├── encAli.png
├── enc_scheme.png
├── enc_type.png
├── hold.gif
└── step.gif
├── examples
├── callback
│ └── callback.ino
├── callback2
│ └── callback2.ino
├── demo
│ └── demo.ino
├── double
│ └── double.ino
├── doubleCallback
│ └── doubleCallback.ino
├── isr
│ └── isr.ino
├── one_button_3_var
│ └── one_button_3_var.ino
├── one_enc_3_var
│ └── one_enc_3_var.ino
└── virtual_buttons
│ ├── virtual_AnalogKey
│ └── virtual_AnalogKey.ino
│ ├── virtual_SimpleKeypad
│ └── virtual_SimpleKeypad.ino
│ └── virtual_SimpleKeypad_array
│ └── virtual_SimpleKeypad_array.ino
├── keywords.txt
├── library.properties
└── src
├── EncButton.h
└── core
├── Button.h
├── EncButton.h
├── Encoder.h
├── MultiButton.h
├── VirtButton.h
├── VirtEncButton.h
├── VirtEncoder.h
├── flags.h
├── io.cpp
├── io.h
├── self.cpp
└── self.h
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.github/workflows/tg-send.yml:
--------------------------------------------------------------------------------
1 |
2 | name: Telegram Message
3 | on:
4 | release:
5 | types: [published]
6 | jobs:
7 | build:
8 | name: Send Message
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: send telegram message on push
12 | uses: appleboy/telegram-action@master
13 | with:
14 | to: ${{ secrets.TELEGRAM_TO }}
15 | token: ${{ secrets.TELEGRAM_TOKEN }}
16 | disable_web_page_preview: true
17 | message: |
18 | ${{ github.event.repository.name }} v${{ github.event.release.tag_name }}
19 | ${{ github.event.release.body }}
20 | https://github.com/${{ github.repository }}
21 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 AlexGyver
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://github.com/GyverLibs/EncButton/releases/latest/download/EncButton.zip)
2 | [](https://registry.platformio.org/libraries/gyverlibs/EncButton)
3 | [](https://alexgyver.ru/)
4 | [](https://alexgyver.ru/support_alex/)
5 | [](https://github-com.translate.goog/GyverLibs/EncButton?_x_tr_sl=ru&_x_tr_tl=en)
6 |
7 | [](https://t.me/GyverLibs)
8 |
9 | # EncButton
10 |
11 | | ⚠️⚠️⚠️
**Новая версия v3 несовместима с предыдущими, смотри [документацию](#docs), [примеры](#example) и краткий [гайд по миграции](#migrate) с v2 на v3!**
⚠️⚠️⚠️ |
12 | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
13 |
14 | Лёгкая и очень функциональная библиотека для энкодера с кнопкой, энкодера или кнопки с Arduino
15 | - Кнопка
16 | - Обработка событий: нажатие, отпускание, клик, счётчик кликов, удержание, импульсное удержание, время удержания + предварительные клики для всех режимов
17 | - Программное подавление дребезга
18 | - Поддержка обработки двух одновременно нажимаемых кнопок как третьей кнопки
19 | - Энкодер
20 | - Обработка событий: обычный поворот, нажатый поворот, быстрый поворот
21 | - Поддержка четырёх типов инкрементальных энкодеров
22 | - Высокоточный алгоритм определения позиции
23 | - Буферизация в прерывании
24 | - Простое и понятное использование
25 | - Огромное количество возможностей и их комбинаций для разных сценариев использования даже одной кнопки
26 | - Виртуальный режим (например для работы с расширителем пинов)
27 | - Оптимизирована для работы в прерывании
28 | - Максимально быстрое чтение пинов для AVR, esp8266, esp32 (используется GyverIO)
29 | - Быстрые асинхронные алгоритмы опроса действий с кнопки и энкодера
30 | - Жёсткая оптимизация и небольшой вес во Flash и SRAM памяти: 5 байт SRAM (на экземпляр) и ~350 байт Flash на обработку кнопки
31 |
32 | Примеры сценариев использования:
33 | - Несколько кликов - включение режима (по кол-ву кликов)
34 | - Несколько кликов + короткое удержание - ещё вариант включения режима (по кол-ву кликов)
35 | - Несколько кликов + удержание - постепенное изменение значения выбранной переменной (по кол-ву кликов)
36 | - Несколько кликов выбирают переменную, энкодер её изменяет
37 | - Изменение шага изменения переменной при вращении энкодера - например уменьшение при зажатой кнопке и увеличение при быстром вращении
38 | - Навигация по меню при вращении энкодера, изменение переменной при вращении зажатого энкодера
39 | - Полноценная навигация по меню при использовании двух кнопок (одновременное удержание для перехода на следующий уровень, одновременное нажатие для возврата на предыдущий)
40 | - И так далее
41 |
42 | ### Совместимость
43 | Совместима со всеми Arduino платформами (используются Arduino-функции)
44 |
45 | ## Содержание
46 | - [Установка](#install)
47 | - [Информация](#info)
48 | - [Документация](#docs)
49 | - [Настройки компиляции](#config)
50 | - [Полное описание классов](#class)
51 | - [Обработка и опрос](#tick)
52 | - [Предварительные клики](#preclicks)
53 | - [Прямое чтение кнопки](#btnread)
54 | - [Погружение в цикл](#loop)
55 | - [Timeout](#timeout)
56 | - [Busy](#busy)
57 | - [Получение события](#actions)
58 | - [Оптимизация](#optimise)
59 | - [Коллбэки](#callback)
60 | - [Одновременное нажатие](#double)
61 | - [Прерывания](#isr)
62 | - [Массив кнопок/энкодеров](#array)
63 | - [Кастомные функции](#custom)
64 | - [Опрос по таймеру](#timer)
65 | - [Мини примеры, сценарии](#examples-mini)
66 | - [Миграция с v2](#migrate)
67 | - [Примеры](#example)
68 | - [Версии](#versions)
69 | - [Баги и обратная связь](#feedback)
70 |
71 |
72 | ## Установка
73 | - Для работы требуется библиотека [GyverIO](https://github.com/GyverLibs/GyverIO)
74 | - Библиотеку можно найти по названию **EncButton** и установить через менеджер библиотек в:
75 | - Arduino IDE
76 | - Arduino IDE v2
77 | - PlatformIO
78 | - [Скачать библиотеку](https://github.com/GyverLibs/EncButton/archive/refs/heads/main.zip) .zip архивом для ручной установки:
79 | - Распаковать и положить в *C:\Program Files (x86)\Arduino\libraries* (Windows x64)
80 | - Распаковать и положить в *C:\Program Files\Arduino\libraries* (Windows x32)
81 | - Распаковать и положить в *Документы/Arduino/libraries/*
82 | - (Arduino IDE) автоматическая установка из .zip: *Скетч/Подключить библиотеку/Добавить .ZIP библиотеку…* и указать скачанный архив
83 | - Читай более подробную инструкцию по установке библиотек [здесь](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)
84 | ### Обновление
85 | - Рекомендую всегда обновлять библиотеку: в новых версиях исправляются ошибки и баги, а также проводится оптимизация и добавляются новые фичи
86 | - Через менеджер библиотек IDE: найти библиотеку как при установке и нажать "Обновить"
87 | - Вручную: **удалить папку со старой версией**, а затем положить на её место новую. "Замену" делать нельзя: иногда в новых версиях удаляются файлы, которые останутся при замене и могут привести к ошибкам!
88 |
89 |
90 |
91 | ## Информация
92 | ### Энкодер
93 | #### Тип энкодера
94 | Библиотека поддерживает все 4 типа *инкрементальных* энкодеров, тип можно настроить при помощи `setEncType(тип)`:
95 | - `EB_STEP4_LOW` - активный низкий сигнал (подтяжка к VCC). Полный период (4 фазы) за один щелчок. *Установлен по умолчанию*
96 | - `EB_STEP4_HIGH` - активный высокий сигнал (подтяжка к GND). Полный период (4 фазы) за один щелчок
97 | - `EB_STEP2` - половина периода (2 фазы) за один щелчок
98 | - `EB_STEP1` - четверть периода (1 фаза) за один щелчок, а также энкодеры без фиксации
99 |
100 | 
101 |
102 | #### Рекомендации
103 | Для работы по сценарию "энкодер с кнопкой" рекомендую вот такие ([ссылка](https://ali.ski/cmPI2), [ссылка](https://ali.ski/sZbTK)) круглые китайские модули с распаянными цепями антидребезга (имеют тип `EB_STEP4_LOW` по классификации выше):
104 | 
105 |
106 | Самостоятельно обвязать энкодер можно по следующей схеме (RC фильтры на каналы энкодера + подтяжка всех пинов к VCC):
107 | 
108 |
109 | > Примечание: по умолчанию в библиотеке пины энкодера настроены на `INPUT` с расчётом на внешнюю подтяжку. Если у вас энкодер без подтяжки - можно использовать внутреннюю `INPUT_PULLUP`, указав это при инициализации энкодера (см. документацию ниже).
110 |
111 | ### Кнопка
112 | #### Уровень кнопки
113 | Кнопка может быть подключена к микроконтроллеру двумя способами и давать при нажатии высокий или низкий сигнал. В библиотеке предусмотрена настройка `setBtnLevel(уровень)`, где уровень - активный сигнал кнопки:
114 | - `HIGH` - кнопка подключает VCC. Установлен по умолчанию в `Virt`-библиотеках
115 | - `LOW` - кнопка подключает GND. Установлен по умолчанию в основных библиотеках
116 |
117 | 
118 |
119 | #### Подтяжка пина
120 | В схемах с микроконтроллерами чаще всего используется подключение кнопки к GND с подтяжкой пина к VCC. Подтяжка может быть внешней (режим пина нужно поставить `INPUT`) или внутренней (режим пина `INPUT_PULLUP`). В "реальных" проектах рекомендуется внешняя подтяжка, т.к. она менее подвержена помехам - у внутренней слишком высокое сопротивление.
121 |
122 |
123 |
124 | ## Документация
125 |
126 |
127 |
128 | ### Дефайны настроек
129 | Объявлять до подключения библиотеки
130 |
131 | ```cpp
132 |
133 | // отключить поддержку pressFor/holdFor/stepFor и счётчик степов (экономит 2 байта оперативки)
134 | #define EB_NO_FOR
135 |
136 | // отключить обработчик событий attach (экономит 2 байта оперативки)
137 | #define EB_NO_CALLBACK
138 |
139 | // отключить счётчик энкодера [VirtEncoder, Encoder, EncButton] (экономит 4 байта оперативки)
140 | #define EB_NO_COUNTER
141 |
142 | // отключить буферизацию энкодера (экономит 2 байта оперативки)
143 | #define EB_NO_BUFFER
144 |
145 | /*
146 | Настройка таймаутов для всех классов
147 | - Заменяет таймауты константами, изменить их из программы (SetXxxTimeout()) будет нельзя
148 | - Настройка влияет на все объявленные в программе кнопки/энкодеры
149 | - Экономит 1 байт оперативки на объект за каждый таймаут
150 | - Показаны значения по умолчанию в мс
151 | - Значения не ограничены 4000мс, как при установке из программы (SetXxxTimeout())
152 | */
153 | #define EB_DEB_TIME 50 // таймаут гашения дребезга кнопки (кнопка)
154 | #define EB_CLICK_TIME 500 // таймаут ожидания кликов (кнопка)
155 | #define EB_HOLD_TIME 600 // таймаут удержания (кнопка)
156 | #define EB_STEP_TIME 200 // таймаут импульсного удержания (кнопка)
157 | #define EB_FAST_TIME 30 // таймаут быстрого поворота (энкодер)
158 | #define EB_TOUT_TIME 1000 // таймаут действия (кнопка и энкодер)
159 | ```
160 |
161 |
162 |
163 | ### Классы
164 | Как работать с документацией: EncButton начиная с версии 3.0 представляет собой несколько библиотек (классов) для различных сценариев использования, они друг друга наследуют для расширения функциональности. Таким образом библиотека представляет собой "луковицу", каждый слой которой имеет доступ к функциям нижних слоёв:
165 | - Базовые классы:
166 | - `VirtButton` - базовый класс виртуальной кнопки, обеспечивает все возможности кнопки
167 | - `VirtEncoder` - базовый класс виртуального энкодера, определяет факт и направление вращения энкодера
168 | - `VirtEncButton` - базовый класс виртуального энкодера с кнопкой, обеспечивает опрос энкодера с учётом кнопки, *наследует VirtButton и VirtEncoder*
169 | - Основные классы:
170 | - `Button`, `ButtonT` - класс кнопки, *наследует VirtButton*
171 | - `Encoder`, `EncoderT` - класс энкодера, *наследует VirtEncoder*
172 | - `EncButton`, `EncButtonT` - класс энкодера с кнопкой, *наследует VirtEncButton, VirtButton, VirtEncoder*
173 |
174 | Таким образом для изучения всех доступных функций конкретной библиотеки нужно смотреть не только её, но и то что она наследует. Например для обработки кнопки при помощи `Button` нужно открыть ниже описание `Button` и `VirtButton`.
175 |
176 | > *Виртуальный* - без указания пина микроконтроллера, работает напрямую с переданным значением, например для опроса кнопок-энкодеров через расширители пинов и сдвиговые регистры.
177 |
178 | > `T`-версии библиотек требуют указания пинов константами (цифрами). Номера пинов будут храниться в памяти программы, это ускоряет работу и делает код легче на 1 байт за каждый пин.
179 |
180 | > Примечание: `#include ` подключает все инструменты библиотеки!
181 |
182 |
183 | Таблица функций кнопки
184 |
185 | | | VirtButton | VirtEncButton | Button | EncButton |
186 | | ----------------- | :--------: | :-----------: | :----: | :-------: |
187 | | read | | | ✔ | |
188 | | readBtn | | | | ✔ |
189 | | tickRaw | ✔ | ✔ | ✔ | ✔ |
190 | | setHoldTimeout | ✔ | ✔ | ✔ | ✔ |
191 | | setStepTimeout | ✔ | ✔ | ✔ | ✔ |
192 | | setClickTimeout | ✔ | ✔ | ✔ | ✔ |
193 | | setDebTimeout | ✔ | ✔ | ✔ | ✔ |
194 | | setTimeout | ✔ | ✔ | ✔ | ✔ |
195 | | setBtnLevel | ✔ | ✔ | ✔ | ✔ |
196 | | pressISR | ✔ | ✔ | ✔ | ✔ |
197 | | reset | ✔ | ✔ | ✔ | ✔ |
198 | | clear | ✔ | ✔ | ✔ | ✔ |
199 | | skipEvents | ✔ | ✔ | ✔ | ✔ |
200 | | attach | ✔ | ✔ | ✔ | ✔ |
201 | | detach | ✔ | ✔ | ✔ | ✔ |
202 | | press | ✔ | ✔ | ✔ | ✔ |
203 | | release | ✔ | ✔ | ✔ | ✔ |
204 | | click | ✔ | ✔ | ✔ | ✔ |
205 | | pressing | ✔ | ✔ | ✔ | ✔ |
206 | | hold | ✔ | ✔ | ✔ | ✔ |
207 | | holding | ✔ | ✔ | ✔ | ✔ |
208 | | step | ✔ | ✔ | ✔ | ✔ |
209 | | hasClicks | ✔ | ✔ | ✔ | ✔ |
210 | | getClicks | ✔ | ✔ | ✔ | ✔ |
211 | | getSteps | ✔ | ✔ | ✔ | ✔ |
212 | | releaseHold | ✔ | ✔ | ✔ | ✔ |
213 | | releaseStep | ✔ | ✔ | ✔ | ✔ |
214 | | releaseHoldStep | ✔ | ✔ | ✔ | ✔ |
215 | | waiting | ✔ | ✔ | ✔ | ✔ |
216 | | busy | ✔ | ✔ | ✔ | ✔ |
217 | | action | ✔ | ✔ | ✔ | ✔ |
218 | | getAction | ✔ | ✔ | ✔ | ✔ |
219 | | timeout | ✔ | ✔ | ✔ | ✔ |
220 | | pressFor | ✔ | ✔ | ✔ | ✔ |
221 | | holdFor | ✔ | ✔ | ✔ | ✔ |
222 | | stepFor | ✔ | ✔ | ✔ | ✔ |
223 |
224 |
225 |
226 | Таблица функций энкодера
227 |
228 | | | VirtEncoder | Encoder | VirtEncButton | EncButton |
229 | | -------------- | :---------: | :-----: | :-----------: | :-------: |
230 | | readEnc | | | | ✔ |
231 | | initEnc | ✔ | ✔ | ✔ | ✔ |
232 | | setEncReverse | ✔ | ✔ | ✔ | ✔ |
233 | | setEncType | ✔ | ✔ | ✔ | ✔ |
234 | | setEncISR | ✔ | ✔ | ✔ | ✔ |
235 | | clear | ✔ | ✔ | ✔ | ✔ |
236 | | turn | ✔ | ✔ | ✔ | ✔ |
237 | | dir | ✔ | ✔ | ✔ | ✔ |
238 | | tickRaw | ✔ | ✔ | ✔ | ✔ |
239 | | pollEnc | ✔ | ✔ | ✔ | ✔ |
240 | | counter | ✔ | ✔ | ✔ | ✔ |
241 | | setFastTimeout | | | ✔ | ✔ |
242 | | turnH | | | ✔ | ✔ |
243 | | fast | | | ✔ | ✔ |
244 | | right | | | ✔ | ✔ |
245 | | left | | | ✔ | ✔ |
246 | | rightH | | | ✔ | ✔ |
247 | | leftH | | | ✔ | ✔ |
248 | | action | | | ✔ | ✔ |
249 | | getAction | | | ✔ | ✔ |
250 | | timeout | | | ✔ | ✔ |
251 | | attach | | | ✔ | ✔ |
252 | | detach | | | ✔ | ✔ |
253 |
254 |
255 |
256 | VirtButton
257 |
258 | ```cpp
259 | // ================ НАСТРОЙКИ ================
260 | // установить таймаут удержания, умолч. 600 (макс. 4000 мс)
261 | void setHoldTimeout(uint16_t tout);
262 |
263 | // установить таймаут импульсного удержания, умолч. 200 (макс. 4000 мс)
264 | void setStepTimeout(uint16_t tout);
265 |
266 | // установить таймаут ожидания кликов, умолч. 500 (макс. 4000 мс)
267 | void setClickTimeout(uint16_t tout);
268 |
269 | // установить таймаут антидребезга, умолч. 50 (макс. 255 мс)
270 | void setDebTimeout(uint8_t tout);
271 |
272 | // установить время таймаута, умолч. 1000 (макс. 4000 мс)
273 | void setTimeout(const uint16_t tout);
274 |
275 | // установить уровень кнопки (HIGH - кнопка замыкает VCC, LOW - замыкает GND)
276 | // умолч. HIGH, то есть true - кнопка нажата
277 | void setBtnLevel(bool level);
278 |
279 | // подключить функцию-обработчик событий
280 | void attach(void (*handler)());
281 |
282 | // отключить функцию-обработчик событий
283 | void detach();
284 |
285 | // ================== СБРОС ==================
286 | // сбросить системные флаги (принудительно закончить обработку)
287 | void reset();
288 |
289 | // принудительно сбросить флаги событий
290 | void clear(bool resetTout = false);
291 |
292 | // игнорировать все события до отпускания кнопки
293 | void skipEvents();
294 |
295 | // ================ ОБРАБОТКА ================
296 | // обработка кнопки значением
297 | bool tick(bool s);
298 |
299 | // обработка виртуальной кнопки как одновременное нажатие двух других кнопок
300 | bool tick(VirtButton& b0, VirtButton& b1);
301 |
302 | // кнопка нажата в прерывании кнопки
303 | void pressISR();
304 |
305 | // обработка кнопки без сброса событий и вызова коллбэка
306 | bool tickRaw(bool s);
307 |
308 | // ================== ОПРОС ==================
309 | // кнопка нажата [событие]
310 | bool press();
311 | bool press(uint8_t clicks);
312 |
313 | // кнопка отпущена (в любом случае) [событие]
314 | bool release();
315 | bool release(uint8_t clicks);
316 |
317 | // клик по кнопке (отпущена без удержания) [событие]
318 | bool click();
319 | bool click(uint8_t clicks);
320 |
321 | // кнопка зажата (между press() и release()) [состояние]
322 | bool pressing();
323 | bool pressing(uint8_t clicks);
324 |
325 | // кнопка была удержана (больше таймаута) [событие]
326 | bool hold();
327 | bool hold(uint8_t clicks);
328 |
329 | // кнопка удерживается (больше таймаута) [состояние]
330 | bool holding();
331 | bool holding(uint8_t clicks);
332 |
333 | // импульсное удержание [событие]
334 | bool step();
335 | bool step(uint8_t clicks);
336 |
337 | // зафиксировано несколько кликов [событие]
338 | bool hasClicks();
339 | bool hasClicks(uint8_t clicks);
340 |
341 | // кнопка отпущена после удержания [событие]
342 | bool releaseHold();
343 | bool releaseHold(uint8_t clicks);
344 |
345 | // кнопка отпущена после импульсного удержания [событие]
346 | bool releaseStep();
347 | bool releaseStep(uint8_t clicks);
348 |
349 | // кнопка отпущена после удержания или импульсного удержания [событие]
350 | bool releaseHoldStep();
351 | bool releaseHoldStep(uint8_t clicks);
352 |
353 | // получить количество кликов
354 | uint8_t getClicks();
355 |
356 | // получить количество степов
357 | uint16_t getSteps();
358 |
359 | // кнопка ожидает повторных кликов (между click() и hasClicks()) [состояние]
360 | bool waiting();
361 |
362 | // идёт обработка (между первым нажатием и после ожидания кликов) [состояние]
363 | bool busy();
364 |
365 | // было действие с кнопки, вернёт код события [событие]
366 | uint16_t action();
367 | EBAction getAction();
368 |
369 | // ================== ВРЕМЯ ==================
370 | // после взаимодействия с кнопкой (или энкодером EncButton) время setTimeout, мс [событие]
371 | bool timeout();
372 |
373 | // после взаимодействия с кнопкой (или энкодером EncButton) время setTimeout, мс [состояние]
374 | bool timeoutState();
375 |
376 | // время, которое кнопка удерживается (с начала нажатия), мс
377 | uint16_t pressFor();
378 |
379 | // кнопка удерживается дольше чем (с начала нажатия), мс [состояние]
380 | bool pressFor(uint16_t ms);
381 |
382 | // время, которое кнопка удерживается (с начала удержания), мс
383 | uint16_t holdFor();
384 |
385 | // кнопка удерживается дольше чем (с начала удержания), мс [состояние]
386 | bool holdFor(uint16_t ms);
387 |
388 | // время, которое кнопка удерживается (с начала степа), мс
389 | uint16_t stepFor();
390 |
391 | // кнопка удерживается дольше чем (с начала степа), мс [состояние]
392 | bool stepFor(uint16_t ms);
393 | ```
394 |
395 |
396 | VirtEncoder
397 |
398 | ```cpp
399 | // ==================== НАСТРОЙКИ ====================
400 | // инвертировать направление энкодера (умолч. 0)
401 | void setEncReverse(bool rev);
402 |
403 | // установить тип энкодера (EB_STEP4_LOW, EB_STEP4_HIGH, EB_STEP2, EB_STEP1)
404 | void setEncType(uint8_t type);
405 |
406 | // использовать обработку энкодера в прерывании
407 | void setEncISR(bool use);
408 |
409 | // инициализация энкодера
410 | void initEnc(bool e0, bool e1);
411 |
412 | // инициализация энкодера совмещённым значением
413 | void initEnc(int8_t v);
414 |
415 | // сбросить флаги событий
416 | void clear();
417 |
418 | // ====================== ОПРОС ======================
419 | // был поворот [событие]
420 | bool turn();
421 |
422 | // направление энкодера (1 или -1) [состояние]
423 | int8_t dir();
424 |
425 | // счётчик
426 | int32_t counter;
427 |
428 | // ==================== ОБРАБОТКА ====================
429 | // опросить энкодер в прерывании. Вернёт 1 или -1 при вращении, 0 при остановке
430 | int8_t tickISR(bool e0, bool e1);
431 |
432 | // опросить энкодер. Вернёт 1 или -1 при вращении, 0 при остановке
433 | int8_t tick(bool e0, bool e1);
434 | int8_t tick(); // сама обработка в прерывании
435 |
436 | // опросить энкодер без сброса события поворота. Вернёт 1 или -1 при вращении, 0 при остановке
437 | int8_t tickRaw(bool e0, bool e1);
438 | int8_t tickRaw(); // сама обработка в прерывании
439 |
440 | // опросить энкодер без установки флагов на поворот (быстрее). Вернёт 1 или -1 при вращении, 0 при остановке
441 | int8_t pollEnc(bool e0, bool e1);
442 | ```
443 |
444 |
445 | VirtEncButton
446 |
447 | - Доступны функции из `VirtButton`
448 | - Доступны функции из `VirtEncoder`
449 |
450 | ```cpp
451 | // ================== НАСТРОЙКИ ==================
452 | // установить таймаут быстрого поворота, мс
453 | void setFastTimeout(uint8_t tout);
454 |
455 | // сбросить флаги энкодера и кнопки
456 | void clear(bool resetTout = false);
457 |
458 | // ==================== ОПРОС ====================
459 | // ЛЮБОЙ поворот энкодера [событие]
460 | bool turn();
461 |
462 | // нажатый поворот энкодера [событие]
463 | bool turnH();
464 |
465 | // быстрый поворот энкодера [состояние]
466 | bool fast();
467 |
468 | // ненажатый поворот направо [событие]
469 | bool right();
470 |
471 | // ненажатый поворот налево [событие]
472 | bool left();
473 |
474 | // нажатый поворот направо [событие]
475 | bool rightH();
476 |
477 | // нажатый поворот налево [событие]
478 | bool leftH();
479 |
480 | // было действие с кнопки или энкодера, вернёт код события [событие]
481 | uint16_t action();
482 | EBAction getAction();
483 |
484 | // ==================== ОБРАБОТКА ====================
485 | // обработка в прерывании (только энкодер). Вернёт 0 в покое, 1 или -1 при повороте
486 | int8_t tickISR(bool e0, bool e1);
487 |
488 | // обработка энкодера и кнопки
489 | bool tick(bool e0, bool e1, bool btn);
490 | bool tick(bool btn); // энкодер в прерывании
491 |
492 | // обработка энкодера и кнопки без сброса флагов и вызова коллбэка
493 | bool tickRaw(bool e0, bool e1, bool btn);
494 | bool tickRaw(bool btn); // энкодер в прерывании
495 | ```
496 |
497 |
498 | Button
499 |
500 | - Доступны функции из `VirtButton`
501 | - Режим кнопки по умолчанию - `LOW`
502 |
503 | ```cpp
504 | Button;
505 | Button(uint8_t pin); // с указанием пина
506 | Button(uint8_t npin, uint8_t mode); // + режим работы (умолч. INPUT_PULLUP)
507 | Button(uint8_t npin, uint8_t mode, uint8_t btnLevel); // + уровень кнопки (умолч. LOW)
508 | ```
509 | ```cpp
510 | // указать пин и его режим работы
511 | void init(uint8_t npin, uint8_t mode);
512 |
513 | // прочитать текущее значение кнопки (без дебаунса) с учётом setBtnLevel
514 | bool read();
515 |
516 | // функция обработки, вызывать в loop
517 | bool tick();
518 |
519 | // обработка кнопки без сброса событий и вызова коллбэка
520 | bool tickRaw();
521 | ```
522 |
523 |
524 | ButtonT
525 |
526 | - Доступны функции из `VirtButton`
527 | - Режим кнопки по умолчанию - `LOW`
528 |
529 | ```cpp
530 | ButtonT; // с указанием пина
531 | ButtonT (uint8_t mode); // + режим работы (умолч. INPUT_PULLUP)
532 | ButtonT (uint8_t mode, uint8_t btnLevel); // + уровень кнопки (умолч. LOW)
533 | ```
534 | ```cpp
535 | // указать режим работы
536 | void init(uint8_t mode);
537 |
538 | // прочитать текущее значение кнопки (без дебаунса) с учётом setBtnLevel
539 | bool read();
540 |
541 | // функция обработки, вызывать в loop
542 | bool tick();
543 | ```
544 |
545 |
546 | Encoder
547 |
548 | - Доступны функции из `VirtEncoder`
549 |
550 | ```cpp
551 | Encoder;
552 | Encoder(uint8_t encA, uint8_t encB); // с указанием пинов
553 | Encoder(uint8_t encA, uint8_t encB, uint8_t mode); // + режим работы (умолч. INPUT)
554 | ```
555 | ```cpp
556 | // указать пины и их режим работы
557 | void init(uint8_t encA, uint8_t encB, uint8_t mode);
558 |
559 | // функция обработки для вызова в прерывании энкодера
560 | int8_t tickISR();
561 |
562 | // функция обработки для вызова в loop
563 | int8_t tick();
564 | ```
565 |
566 |
567 | EncoderT
568 |
569 | - Доступны функции из `VirtEncoder`
570 |
571 | ```cpp
572 | EncoderT; // с указанием пинов
573 | EncoderT (uint8_t mode); // + режим работы (умолч. INPUT)
574 | ```
575 | ```cpp
576 | // указать режим работы пинов
577 | void init(uint8_t mode);
578 |
579 | // функция обработки для вызова в прерывании энкодера
580 | int8_t tickISR();
581 |
582 | // функция обработки для вызова в loop
583 | int8_t tick();
584 | ```
585 |
586 |
587 | EncButton
588 |
589 | - Доступны функции из `VirtButton`
590 | - Доступны функции из `VirtEncoder`
591 | - Доступны функции из `VirtEncButton`
592 |
593 | ```cpp
594 | EncButton;
595 |
596 | // настроить пины (энк, энк, кнопка)
597 | EncButton(uint8_t encA, uint8_t encB, uint8_t btn);
598 |
599 | // настроить пины (энк, энк, кнопка, pinmode энк, pinmode кнопка, уровень кнопки)
600 | EncButton(uint8_t encA, uint8_t encB, uint8_t btn, uint8_t modeEnc = INPUT, uint8_t modeBtn = INPUT_PULLUP, uint8_t btnLevel = LOW);
601 | ```
602 | ```cpp
603 | // настроить пины (энк, энк, кнопка, pinmode энк, pinmode кнопка, уровень кнопки)
604 | void init(uint8_t encA, uint8_t encB, uint8_t btn, uint8_t modeEnc = INPUT, uint8_t modeBtn = INPUT_PULLUP, uint8_t btnLevel = LOW);
605 |
606 | // функция обработки для вызова в прерывании энкодера
607 | int8_t tickISR();
608 |
609 | // функция обработки, вызывать в loop
610 | bool tick();
611 |
612 | // прочитать значение кнопки с учётом setBtnLevel
613 | bool readBtn();
614 |
615 | // прочитать значение энкодера
616 | int8_t readEnc();
617 | ```
618 |
619 |
620 | EncButtonT
621 |
622 | - Доступны функции из `VirtButton`
623 | - Доступны функции из `VirtEncoder`
624 | - Доступны функции из `VirtEncButton`
625 |
626 | ```cpp
627 | // с указанием пинов
628 | EncButtonT;
629 |
630 | // + режим работы пинов, уровень кнопки
631 | EncButtonT (uint8_t modeEnc = INPUT, uint8_t modeBtn = INPUT_PULLUP, uint8_t btnLevel = LOW);
632 | ```
633 | ```cpp
634 | // настроить режим работы пинов, уровень кнопки
635 | void init(uint8_t modeEnc = INPUT, uint8_t modeBtn = INPUT_PULLUP, uint8_t btnLevel = LOW);
636 |
637 | // функция обработки для вызова в прерывании энкодера
638 | int8_t tickISR();
639 |
640 | // функция обработки, вызывать в loop
641 | bool tick();
642 |
643 | // прочитать значение кнопки
644 | bool readBtn();
645 |
646 | // прочитать значение энкодера
647 | int8_t readEnc();
648 | ```
649 |
650 |
651 |
652 |
653 | ### Обработка и опрос
654 | Во всех библиотеках есть общая **функция обработки** (тикер `tick`), которая получает текущий сигнал с кнопки и энкодера
655 | - Эту функцию нужно однократно вызывать в основном цикле программы (для виртуальных - с передачей значения)
656 | - Функция возвращает `true` при наступлении события (для энкодера - `1` или `-1` при повороте, `0` при его отсутствии. Таким образом поворот в любую сторону расценивается как `true`)
657 | - Есть отдельные функции для вызова в прерывании, они имеют суффикс `ISR`, см. документацию ниже
658 |
659 | Библиотека обрабатывает сигнал внутри этой функции, результат можно получить из **функций опроса** событий. Они бывают двух типов:
660 | - `[событие]` - функция вернёт `true` однократно при наступлении события. Сбросится после следующего вызова функции обработки (например клик, поворот энкодера). За исключением события `timeout`
661 | - `[состояние]` - функция возвращает `true`, пока активно это состояние (например кнопка удерживается)
662 |
663 | Для простоты восприятия функцию обработки нужно размещать в начале цикла, а опросы делать ниже:
664 | ```cpp
665 | void loop() {
666 | btn.tick(); // опрос
667 |
668 | if (btn.click()) Serial.println("click"); // однократно выведет при клике
669 | if (btn.click()) Serial.println("click"); // тот же клик!
670 | }
671 | ```
672 | > В отличие от предыдущих версий библиотеки, функции опроса сбрасываются не внутри себя, а *внутри функции обработки*. Таким образом в примере выше при клике по кнопке в порт дважды выведется сообщение `click()`. Это позволяет использовать функции опроса по несколько раз за текущую итерацию цикла для создания сложной логики работы программы.
673 |
674 | #### Несколько функций обработки
675 | По очевидным причинам нельзя вызывать функцию обработки больше одного раза за цикл - каждый следующий вызов сбросит события от предыдущего и код будет работать некорректно. Вот так - нельзя:
676 | ```cpp
677 | // так нельзя
678 | void loop() {
679 | btn.tick();
680 | if (btn.click()) ...
681 |
682 | // ....
683 |
684 | btn.tick();
685 | if (btn.hold()) ...
686 | }
687 | ```
688 |
689 | Если очень нужно попасть в глухой цикл и опрашивать там кнопку, то вот так - можно:
690 | ```cpp
691 | // так можно
692 | void loop() {
693 | btn.tick();
694 | if (btn.click()) ...
695 |
696 | while (true) {
697 | btn.tick();
698 | if (btn.hold()) ...
699 | if (btn.click()) break;
700 | }
701 | }
702 | ```
703 |
704 | Если библиотека используется с подключенным обработчиком событий `attach()` (см. ниже), то можно вызывать `tick()` где угодно и сколько угодно раз, события будут обработаны в обработчике:
705 | ```cpp
706 | // так можно
707 | void cb() {
708 | switch (btn.action()) {
709 | // ...
710 | }
711 | }
712 |
713 | void setup() {
714 | btn.attach(cb);
715 | }
716 |
717 | void loop() {
718 | btn.tick();
719 | // ...
720 | btn.tick();
721 | // ...
722 | btn.tick();
723 | }
724 | ```
725 |
726 | #### "Загруженная" программа
727 | Библиотека EncButton - **асинхронная**: она не ждёт, пока закончится обработка кнопки, а позволяет программе выполняться дальше. Это означает, что для корректной работы библиотеки основной цикл программы должен выполняться как можно быстрее и не содержать задержек и других "глухих" циклов внутри себя. Для обеспечения правильной обработки кнопки не рекомендуется иметь в основном цикле задержки длительностью более 50-100 мс. Несколько советов:
728 | - Новичкам: изучить цикл уроков [как написать скетч](https://alexgyver.ru/lessons/how-to-sketch/)
729 | - Писать асинхронный код в `loop()`
730 | - Любую синхронную конструкцию на `delay()` можно сделать асинхронной при помощи `millis()`
731 | - Если в программе *каждая* итерация главного цикла выполняется дольше 50-100мс - в большинстве случаев программа написана неправильно, за исключением каких-то особых случаев
732 | - Подключить кнопку на аппаратное прерывание (см. ниже)
733 | - Избегать выполнения "тяжёлых" участков кода, пока идёт обработка кнопки, например поместив их в условие `if (!button.busy()) { тяжёлый код }`
734 | - Если оптимизировать основной цикл невозможно - вызывать тикер в другом "потоке" и использовать функцию-обработчик:
735 | - В прерывании таймера с периодом ~50мс или чаще
736 | - На другом ядре (например ESP32)
737 | - В другом таске FreeRTOS
738 | - Внутри `yield()` (внутри `delay()`)
739 |
740 | #### Раздельная обработка
741 | > Имеет смысл только при ручном опросе событий! При подключенной функции-обработчике достаточно вызывать обычный `tick()` между тяжёлыми участками программы
742 |
743 | Также в загруженной программе можно разделить обработку и сброс событий: вместо `tick()` использовать `tickRaw()` между тяжёлыми участками кода и ручной сброс `clear()`. Порядок следующий:
744 | - Опросить действия (click, press, turn...)
745 | - Вызвать `clear()`
746 | - Вызывать `tickRaw()` между тяжёлыми участками кода
747 |
748 | ```cpp
749 | void loop() {
750 | if (btn.click()) ...
751 | if (btn.press()) ...
752 | if (btn.step()) ...
753 |
754 | btn.clear();
755 |
756 | // ...
757 | btn.tickRaw();
758 | // ...
759 | btn.tickRaw();
760 | // ...
761 | btn.tickRaw();
762 | // ...
763 | }
764 | ```
765 | Это позволит опрашивать кнопку/энкодер в не очень хорошо написанной программе, где основной цикл завален тяжёлым кодом. Внутри `tickRaw()` накапливаются события, которые раз в цикл разбираются, а затем вручную сбрасываются.
766 |
767 | > В этом сценарии буферизация энкодера в прерывании не работает и не обрабатываются все события `releaseXxx`
768 |
769 | #### Обработка внутри delay
770 | Если сложно избавиться от `delay()` внутри главного цикла программы, то на некоторых платформах можно поместить свой код внутри него. Таким образом можно получить даже обработку энкодера в цикле с дилеями без использования прерываний:
771 | ```cpp
772 | // вставка кода в delay
773 | void yield() {
774 | eb.tickRaw();
775 | }
776 |
777 | void loop() {
778 | if (eb.click()) ...
779 | if (btn.turn()) ...
780 |
781 | eb.clear();
782 |
783 | // ...
784 | delay(10);
785 | // ...
786 | delay(50);
787 | // ...
788 | }
789 | ```
790 |
791 | > В этом сценарии буферизация энкодера в прерывании не работает и не обрабатываются все события `releaseXxx`
792 |
793 | #### Обработка кнопки
794 | Библиотека обрабатывает кнопку следующим образом:
795 | - Нажатие с программным подавлением дребезга (удержание дольше таймаута deb), результат - событие `press`, состояния `pressing` и `busy`
796 | - Удержание дольше таймаута удержания hold - событие `hold`, состояние `holding`
797 | - Удержание дольше таймаута удержания hold + таймаута степ - импульсное событие `step`, срабатывает с периодом step пока кнопка удерживается
798 | - Отпускание кнопки, результат - событие `release`, снятие состояний `pressing` и `holding`
799 | - Отпускание до таймаута удержания - событие `click`
800 | - Отпускание после удержания - событие `releaseHold`
801 | - Отпускание после импульсного удержания - событие `releaseStep`
802 | - События `releaseHold` и `releaseStep` взаимоисключающие, если кнопка была удержана до `step` - `releaseHold` уже не сработает
803 | - Ожидание нового клика в течение таймаута click, состояние `waiting`
804 | - Если нового клика нет - снятие состоятия `busy`, обработка закончена
805 | - Если кнопка снова нажата - обработка нового клика
806 | - Счётчик кликов `getClicks()` сбрасывается после событий `releaseHold`/`releaseStep`, которые проверяют предварительные клики. В общем обработчике `action()` это события `EB_REL_HOLD_C` или `EB_REL_STEP_C`
807 | - Количество сделанных кликов нужно проверять по событию `hasClicks`, а также можно опросить внутри почти всех событий кнопки, которые идут до `releaseXxx`
808 | - Если ожидается `timeout` - событие timeout периодом из `setTimeout`
809 | - Обработка кнопки в прерывании сообщает библиотеке о факте нажатия, вся остальная обработка выполняется штатно в `tick()`
810 |
811 | > Отличие `click(n)` от `hasClicks(n)`: `click(n)` вернёт `true` в любом случае при совпадении количества кликов, даже если будет сделано больше кликов. `hasClicks(n)` вернёт `true` только в том случае, если было сделано ровно указанное количество кликов и больше кликов не было!
812 |
813 | > Лучше один раз увидеть, чем сто раз прочитать. Запусти пример demo и понажимай на кнопку, или попробуй [онлайн-симуляцию в Wokwi](https://wokwi.com/projects/373591584298469377)
814 |
815 | ##### Click
816 | 
817 |
818 | ##### Hold
819 | 
820 |
821 | ##### Step
822 | 
823 |
824 | Онлайн-симуляция доступна [здесь](https://wokwi.com/projects/373591584298469377)
825 |
826 | #### Обработка энкодера
827 | - "Быстрым" поворотом считается поворот, совершённый менее чем за настроенный таймаут от предыдущего поворота
828 | - Обработанные в прерывании повороты становятся активными (вызывают события) после вызова `tick()`
829 | - Доступ к счётчику энкодера `counter` - это публичная переменная класса, можно делать с ней всё что угодно:
830 | ```cpp
831 | Serial.println(eb.counter); // читать
832 | eb.counter += 1234; // менять
833 | eb.counter = 0; // обнулять
834 | ```
835 |
836 | #### Обработка энкодера с кнопкой
837 | - Поворот энкодера при зажатой кнопке снимает и блокирует все последующие события и клики, за исключением события `release`. Состояния нажатой кнопки не изменяются
838 | - Поворот энкодера также влияет на системный таймаут (функция `timeout()`) - сработает через указанное время после поворота энкодера
839 | - Счётчик кликов доступен при нажатом повороте: несколько кликов, зажатие кнопки, поворот
840 |
841 |
842 |
843 | ### Предварительные клики
844 | Библиотека считает количество кликов по кнопке и некоторые функции опроса могут отдельно обрабатываться с *предварительными кликами*. Например 3 клика, затем удержание. Это очень сильно расширяет возможности одной кнопки. Есть два варианта работы с такими событиями:
845 | ```cpp
846 | // 1
847 | if (btn.hold()) {
848 | if (btn.getClicks() == 2) Serial.println("hold 2 clicks");
849 | }
850 |
851 | // 2
852 | if (btn.hold(2)) Serial.println("hold 2 clicks");
853 | ```
854 |
855 | В первом варианте можно получить количество кликов для дальнейшей обработки вручную, а во втором - библиотека сделает это сама, если количество кликов для действия заранее известно.
856 |
857 |
858 |
859 | ### Прямое чтение кнопки
860 | В некоторых сценариях бывает нужно получить состояние кнопки "здесь и сейчас", например определить удерживается ли кнопка сразу после запуска микроконтроллера (старта программы). Функцию `tick()` нужно вызывать постоянно в цикле, чтобы шла обработка кнопки с гашением дребезга контактов и прочими расчётами, поэтому конструкция следующего вида **работать не будет**:
861 | ```cpp
862 | void setup() {
863 | btn.tick();
864 | if (btn.press()) Serial.println("Кнопка нажата при старте");
865 | }
866 | ```
867 |
868 | Для таких сценариев помогут следующие функции, возвращают `true` если кнопка нажата:
869 | - `read()` для библиотек Button и ButtonT
870 | - `readBtn()` для библиотек EncButton и EncButtonT
871 |
872 | > Опрос кнопки выполняется с учётом настроенного ранее уровня кнопки (setBtnLevel)! Вручную дополнительно инвертировать логику не нужно:
873 |
874 | ```cpp
875 | void setup() {
876 | // btn.setBtnLevel(LOW); // можно настроить уровень
877 |
878 | if (btn.read()) Serial.println("Кнопка нажата при старте");
879 | }
880 | ```
881 |
882 |
883 |
884 | ### Погружение в цикл
885 | Допустим нужно обработать кнопку синхронно и с гашением дребезга. Например если кнопка зажата при старте микроконтроллера - получить её удержание или даже импульсное удержание внутри блока `setup`, то есть до начала выполнения основной программы. Можно воспользоваться состоянием `busy` и опрашивать кнопку из цикла:
886 | ```cpp
887 | void setup() {
888 | Serial.begin(115200);
889 |
890 | btn.tick();
891 | while (btn.busy()) {
892 | btn.tick();
893 | if (btn.hold()) Serial.println("hold");
894 | if (btn.step()) Serial.println("step");
895 | }
896 |
897 | Serial.println("program start");
898 | }
899 | ```
900 | Как это работает: первый тик опрашивает кнопку, если кнопка нажата - сразу же активируется состояние busy и система попадает в цикл `while`. Внутри него продолжаем тикать и получать события с кнопки. Когда кнопка будет отпущена и сработают все события - флаг busy опустится и программа автоматически покинет цикл. Можно переписать эту конструкцию на цикл с постусловием, более красиво:
901 | ```cpp
902 | do {
903 | btn.tick();
904 | if (btn.hold()) Serial.println("hold");
905 | if (btn.step()) Serial.println("step");
906 | } while (btn.busy());
907 | ```
908 |
909 |
910 |
911 | ### Timeout
912 | В связанных с кнопкой классах (Button, EncButton) есть функция `timeout()` - она однократно вернёт `true`, если после окончания действий с кнопкой/энкодером прошло указанное в `setTimeout` время. Это можно использовать для сохранения параметров после ввода, например:
913 | ```cpp
914 | void setup() {
915 | //eb.setTimeout(1500); // умолч. 1000
916 | }
917 | void loop() {
918 | eb.tick();
919 |
920 | // ...
921 |
922 | if (eb.timeout()) {
923 | // после взаимодействия с энкодером прошло 1000 мс
924 | // EEPROM.put(0, settings);
925 | }
926 | }
927 | ```
928 |
929 | В текущей версии обработка таймаута реализована не очень красиво ради экономии места: системный флаг таймаута активен всё время (`action` будет возвращать событие таймаута), сбрасывается в следующих случаях:
930 |
931 | - Вызов `timeout()` - данный метод однократно вернёт `true`, последующие вызовы будут возвращать `false` до нового действия
932 | - Автоматически сбросится после вызова обработчика, если он подключен
933 | - При вызове `clear(true)` - с флагом очистки таймаута
934 | - При вызове `reset()`
935 |
936 | Если нужно пробросить опрос таймаута через несколько вызовов - можно использовать `timeoutState()`, но последний вызов должен быть `timeout()`.
937 |
938 |
939 |
940 | ### Busy
941 | Функция `busy()` возвращает `true`, пока идёт обработка кнопки, т.е. пока система ожидает действий и выхода таймаутов. Это можно использовать для оптимизации кода, например избегать каких то долгих и тяжёлых частей программы на время обработки кнопки:
942 | ```cpp
943 | void loop() {
944 | eb.tick();
945 |
946 | // ...
947 |
948 | if (!eb.busy()) {
949 | // потенциально долгий и тяжёлый код
950 | }
951 | }
952 | ```
953 |
954 |
955 |
956 | ### Получение события
957 | Доступно во всех классах **с кнопкой**:
958 | - `VirtButton`
959 | - `Button`
960 | - `VirtEncButton`
961 | - `EncButton`
962 |
963 | Функция `action()` при наступлении события возвращает код события (отличный от нуля, что само по себе является индикацией наличия события):
964 | - `EB_PRESS` - нажатие на кнопку
965 | - `EB_HOLD` - кнопка удержана
966 | - `EB_STEP` - импульсное удержание
967 | - `EB_RELEASE` - кнопка отпущена
968 | - `EB_CLICK` - одиночный клик
969 | - `EB_CLICKS` - сигнал о нескольких кликах
970 | - `EB_TURN` - поворот энкодера
971 | - `EB_REL_HOLD` - кнопка отпущена после удержания
972 | - `EB_REL_HOLD_C` - кнопка отпущена после удержания с предв. кликами
973 | - `EB_REL_STEP` - кнопка отпущена после степа
974 | - `EB_REL_STEP_C` - кнопка отпущена после степа с предв. кликами
975 | - `EB_TIMEOUT` - прошёл таймаут после нажатия кнопки или поворота энкодера
976 |
977 | Полученный код события можно обработать через `switch`:
978 | ```cpp
979 | switch (eb.action()) {
980 | case EB_PRESS:
981 | // ...
982 | break;
983 | case EB_HOLD:
984 | // ...
985 | break;
986 | // ...
987 | }
988 | ```
989 |
990 | Есть аналогичная функция `getAction()`, вернёт то же самое, но с более читаемыми константами (удобно с автодополнением):
991 |
992 | - `EBAction::Press`
993 | - `EBAction::Hold`
994 | - `EBAction::Step`
995 | - `EBAction::Release`
996 | - `EBAction::Click`
997 | - `EBAction::Clicks`
998 | - `EBAction::Turn`
999 | - `EBAction::ReleaseHold`
1000 | - `EBAction::ReleaseHoldClicks`
1001 | - `EBAction::ReleaseStep`
1002 | - `EBAction::ReleaseStepClicks`
1003 | - `EBAction::Timeout`
1004 |
1005 | Полученный код события можно обработать через `switch`:
1006 | ```cpp
1007 | switch (eb.getAction()) {
1008 | case EBAction::Press:
1009 | // ...
1010 | break;
1011 | case EBAction::Hold:
1012 | // ...
1013 | break;
1014 | // ...
1015 | }
1016 | ```
1017 |
1018 | > Результат функций `action()`/`getAction()` сбрасывается после следующего вызова `tick()`, то есть доступен на всей текущей итерации основного цикла
1019 |
1020 |
1021 |
1022 | ### Оптимизация
1023 | #### Вес библиотеки
1024 | Для максимального уменьшения веса библиотеки (в частности в оперативной памяти) нужно задавать тайматуы константами через define (экономия 1 байт за таймаут), отключить обработчик событий, счётчики-буферы и использовать T-класс (экономия 1 байт за пин):
1025 | ```cpp
1026 | #define EB_NO_FOR
1027 | #define EB_NO_CALLBACK
1028 | #define EB_NO_COUNTER
1029 | #define EB_NO_BUFFER
1030 | #define EB_DEB_TIME 50 // таймаут гашения дребезга кнопки (кнопка)
1031 | #define EB_CLICK_TIME 500 // таймаут ожидания кликов (кнопка)
1032 | #define EB_HOLD_TIME 600 // таймаут удержания (кнопка)
1033 | #define EB_STEP_TIME 200 // таймаут импульсного удержания (кнопка)
1034 | #define EB_FAST_TIME 30 // таймаут быстрого поворота (энкодер)
1035 | #define EB_TOUT_TIME 1000 // таймаут действия (кнопка и энкодер)
1036 | #include
1037 | EncButtonT<2, 3, 4> eb;
1038 | ```
1039 | В таком случае энкодер с кнопкой займёт в SRAM всего 8 байт, а просто кнопка - 5.
1040 |
1041 | #### Скорость выполнения
1042 | Чтобы сократить время на проверку системных флагов событий (незначительно, но приятно) можно поместить все опросы в условие по `tick()`, так как `tick()` возвращает `true` только при наступлении **события**:
1043 | ```cpp
1044 | void loop() {
1045 | if (eb.tick()) {
1046 | if (eb.turn()) ...;
1047 | if (eb.click()) ...;
1048 | }
1049 | }
1050 | ```
1051 |
1052 | Также опрос событий при помощи функции `action()` выполняется быстрее, чем ручной опрос отдельных функций событий, поэтому максимально эффективно библиотека будет работать вот в таком формате:
1053 | ```cpp
1054 | void loop() {
1055 | if (eb.tick()) {
1056 | switch (eb.action()) {
1057 | case EB_PRESS:
1058 | // ...
1059 | break;
1060 | case EB_HOLD:
1061 | // ...
1062 | break;
1063 | // ...
1064 | }
1065 | }
1066 | }
1067 | ```
1068 |
1069 | Для опроса **состояний** кнопки `pressing()`, `holding()`, `waiting()` можно поместить их вовнутрь условия по `busy()`, чтобы не опрашивать состояния пока их гарантированно нет:
1070 | ```cpp
1071 | if (btn.busy()) {
1072 | if (btn.pressing())...
1073 | if (btn.holding())...
1074 | if (btn.waiting())...
1075 | }
1076 | ```
1077 |
1078 |
1079 |
1080 | ### Коллбэки
1081 | Можно подключить внешнюю функцию-обрбаотчик события, она будет вызвана при наступлении любого события. Данная возможность работает во всех классах **с кнопкой**:
1082 | - `VirtButton`
1083 | - `Button`
1084 | - `VirtEncButton`
1085 | - `EncButton`
1086 |
1087 | > Внутри коллбэка можно получить указатель на текущий объект (который вызвал коллбэк) из переменной `void* EB_self`
1088 |
1089 | ```cpp
1090 | EncButton eb(2, 3, 4);
1091 |
1092 | void callback() {
1093 | switch (eb.action()) {
1094 | case EB_PRESS:
1095 | // ...
1096 | break;
1097 | case EB_HOLD:
1098 | // ...
1099 | break;
1100 | // ...
1101 | }
1102 |
1103 | // здесь EB_self указатель на eb
1104 | }
1105 |
1106 | void setup() {
1107 | eb.attach(callback);
1108 | }
1109 |
1110 | void loop() {
1111 | eb.tick();
1112 | }
1113 | ```
1114 |
1115 |
1116 |
1117 | ### Одновременное нажатие
1118 | Библиотека нативно поддерживает работу с двумя одновременно нажатыми кнопками как с третьей кнопкой. Для этого нужно:
1119 | 1. Cоздать специальную кнопку `MultiButton`
1120 | 2. Передать виртуальной кнопке в обработку свои кнопки (это могут быть объекты классов `VirtButton`, `Button`, `EncButton` + их `T`-версии). **Мульти-кнопка сама опросит обе кнопки!**
1121 | 3. Опрашивать события или слушать обработчик
1122 |
1123 | ```cpp
1124 | Button b0(4);
1125 | Button b1(5);
1126 | MultiButton b2; // 1
1127 |
1128 | void loop() {
1129 | b2.tick(b0, b1); // 2
1130 |
1131 | // 3
1132 | if (b0.click()) Serial.println("b0 click");
1133 | if (b1.click()) Serial.println("b1 click");
1134 | if (b2.click()) Serial.println("b0+b1 click");
1135 | }
1136 | ```
1137 |
1138 | Библиотека сама "сбросит" лишние события с реальных кнопок, если они были нажаты вместе, за исключением события `press`. Таким образом получается полноценная третья кнопка из двух других с удобным опросом.
1139 |
1140 |
1141 |
1142 | ### Прерывания
1143 | #### Энкодер
1144 | Для обработки энкодера в загруженной программе нужно:
1145 | - Подключить оба его пина на аппаратные прерывания по `CHANGE`
1146 | - Установить `setEncISR(true)`
1147 | - Вызывать в обработчике специальный тикер для прерывания
1148 | - Основной тикер также нужно вызывать в `loop` для корреткной работы - события генерируются в основном тикере:
1149 | ```cpp
1150 | // пример для ATmega328 и EncButton
1151 | EncButton eb(2, 3, 4);
1152 |
1153 | /*
1154 | // esp8266/esp32
1155 | IRAM_ATTR void isr() {
1156 | eb.tickISR();
1157 | }
1158 | */
1159 |
1160 | void isr() {
1161 | eb.tickISR();
1162 | }
1163 | void setup() {
1164 | attachInterrupt(0, isr, CHANGE);
1165 | attachInterrupt(1, isr, CHANGE);
1166 | eb.setEncISR(true);
1167 | }
1168 | void loop() {
1169 | eb.tick();
1170 | }
1171 | ```
1172 |
1173 | Примечание: использование работы в прерывании позволяет корректно обрабатывать позицию энкодера и не пропустить новый поворот. Событие с поворотом, полученное из прерывания, станет доступно *после* вызова `tick` в основном цикле программы, что позволяет не нарушать последовательность работы основного цикла:
1174 | - Буферизация отключена: событие `turn` активируется только один раз, независимо от количества щелчков энкодера, совершённых между двумя вызовами `tick` (щелчки обработаны в прерывании)
1175 | - Буферизация включена: событие `turn` будет вызвано столько раз, сколько реально было щелчков энкодера, это позволяет вообще не пропускать повороты и не нагружать систему в прерывании. **Размер буфера - 5 необработанных щелчков энкодера**
1176 |
1177 | Примечания:
1178 | - Функция `setEncISR` работает только в не виртуальных классах. Если он включен - основной тикер `tick` просто не опрашивает пины энкодера, что экономит процессорное время. Обработка происходит только в прерывании
1179 | - Счётчик энкодера всегда имеет актуальное значение и может опережать буферизированные повороты в программе с большими задержками в основном цикле!
1180 | - На разных платформах прерывания могут работать по разному (например на ESPxx - нужно добавить функции аттрибут `IRAM_ATTR`, см. документацию на свою платформу!)
1181 | - Обработчик, подключенный в `attach()`, будет вызван из `tick()`, то есть *не из прерывания*!
1182 |
1183 | #### Виртуальные классы
1184 | В виртуальных есть тикер, в который не нужно передавать состояние энкодера, если он обрабатывается в прерывании, это позволяет не опрашивать пины в холостую. Например:
1185 |
1186 | ```cpp
1187 | VirtEncoder e;
1188 |
1189 | void isr() {
1190 | e.tickISR(digitalRead(2), digitalRead(3));
1191 | }
1192 | void setup() {
1193 | attachInterrupt(0, isr, CHANGE);
1194 | attachInterrupt(1, isr, CHANGE);
1195 |
1196 | e.setEncISR(1);
1197 | }
1198 | void loop() {
1199 | e.tick(); // не передаём состояния пинов
1200 | }
1201 | ```
1202 |
1203 | #### Кнопка
1204 | Для обработки кнопки в прерывании нужно:
1205 | - Подключить прерывание на **нажатие** кнопки с учётом её физического подключения и уровня:
1206 | - Если кнопка замыкает `LOW` - прерывание `FALLING`
1207 | - Если кнопка замыкает `HIGH` - прерывание `RISING`
1208 | - Вызывать `pressISR()` в обработчике прерывания
1209 |
1210 | ```cpp
1211 | Button b(2);
1212 |
1213 | /*
1214 | // esp8266/esp32
1215 | IRAM_ATTR void isr() {
1216 | b.pressISR();
1217 | }
1218 | */
1219 |
1220 | void isr() {
1221 | b.pressISR();
1222 | }
1223 | void setup() {
1224 | attachInterrupt(0, isr, FALLING);
1225 | }
1226 | void loop() {
1227 | b.tick();
1228 | }
1229 | ```
1230 |
1231 | Примечание: кнопка обрабатывается в основном `tick()`, а функция `pressISR()` всего лишь сообщает библиотеке, что кнопка была нажата вне `tick()`. Это позволяет не пропустить нажатие кнопки, пока программа была занята чем-то другим.
1232 |
1233 | #### Энкодер с кнопкой
1234 | Нужно подключить все три пина на прерывания и действовать как выше - и для кнопки, и для энкодера:
1235 |
1236 | ```cpp
1237 | void eisr() {
1238 | eb.tickISR();
1239 | }
1240 | void bisr() {
1241 | eb.pressISR();
1242 | }
1243 |
1244 | void setup() {
1245 | // номера прерываний "для примера"
1246 | attachInterrupt(0, eisr, CHANGE);
1247 | attachInterrupt(1, eisr, CHANGE);
1248 | eb.setEncISR(true);
1249 |
1250 | attachInterrupt(2, bisr, FALLING);
1251 | }
1252 |
1253 | void loop() {
1254 | eb.tick();
1255 | }
1256 | ```
1257 |
1258 |
1259 |
1260 | ### Массив кнопок/энкодеров
1261 | Создать массив можно только из нешаблонных классов (без буквы `T`), потому что номера пинов придётся указать уже в рантайме далее в программе. Например:
1262 | ```cpp
1263 | Button btns[5];
1264 | EncButton ebs[3];
1265 |
1266 | void setup() {
1267 | btns[0].init(2); // указать пин
1268 | btns[1].init(5);
1269 | btns[2].init(10);
1270 | // ...
1271 |
1272 | ebs[0].init(11, 12, 13, INPUT);
1273 | ebs[1].init(14, 15, 16);
1274 | // ...
1275 | }
1276 | void loop() {
1277 | for (int i = 0; i < 5; i++) btns[i].tick();
1278 | for (int i = 0; i < 3; i++) ebs[i].tick();
1279 |
1280 | if (btns[2].click()) Serial.println("btn2 click");
1281 | // ...
1282 | }
1283 | ```
1284 |
1285 |
1286 |
1287 | ### Кастомные функции
1288 | Библиотека поддерживает задание своих функций для чтения пина и получения времени без редактирования файлов библиотеки. Для этого нужно реализовать соответствующую функцию в своём .cpp или .ino файле:
1289 | - `bool EB_read(uint8_t pin)` - для своей функции чтения пина
1290 | - `void EB_mode(uint8_t pin, uint8_t mode)` - для своего аналога pinMode
1291 | - `uint32_t EB_uptime()` - для своего аналога millis()
1292 |
1293 | Пример:
1294 |
1295 | ```cpp
1296 | #include
1297 |
1298 | bool EB_read(uint8_t pin) {
1299 | return digitalRead(pin);
1300 | }
1301 |
1302 | void EB_mode(uint8_t pin, uint8_t mode) {
1303 | pinMode(pin, mode);
1304 | }
1305 |
1306 | uint32_t EB_uptime() {
1307 | return millis();
1308 | }
1309 | ```
1310 |
1311 |
1312 |
1313 | ### Опрос по таймеру
1314 | Иногда может понадобиться вызывать `tick()` не на каждой итерации, а по таймеру. Например для виртуальной кнопки с расширителя пинов, когда чтение расширителя пинов - долгая операция, и вызывать её часто не имеет смысла. Вот так делать нельзя, события будут активны в течение периода таймера!
1315 | ```cpp
1316 | void loop() {
1317 | // таймер на 50 мс
1318 | static uint32_t tmr;
1319 | if (millis() - tmr >= 50) {
1320 | tmr = millis();
1321 | btn.tick(readSomePin());
1322 | }
1323 |
1324 | // будет активно в течение 50 мс!!!
1325 | if (btn.click()) foo();
1326 | }
1327 | ```
1328 |
1329 | В данной ситуации нужно поступить так: тикать по таймеру, там же обрабатывать события и сбрасывать флаги в конце:
1330 | ```cpp
1331 | void loop() {
1332 | // таймер на 50 мс
1333 | static uint32_t tmr;
1334 | if (millis() - tmr >= 50) {
1335 | tmr = millis();
1336 | // тик
1337 | btn.tick(readSomePin());
1338 |
1339 | // разбор событий
1340 | if (btn.click()) foo();
1341 |
1342 | // сброс флагов
1343 | btn.clear();
1344 | }
1345 | }
1346 | ```
1347 |
1348 | Либо можно подключить обработчик и вызывать `clear()` в конце функции:
1349 | ```cpp
1350 | void callback() {
1351 | switch (btn.action()) {
1352 | // ...
1353 | }
1354 |
1355 | // сброс флагов
1356 | btn.clear();
1357 | }
1358 |
1359 | void loop() {
1360 | // таймер на 50 мс
1361 | static uint32_t tmr;
1362 | if (millis() - tmr >= 50) {
1363 | tmr = millis();
1364 | btn.tick(readSomePin());
1365 | }
1366 | }
1367 | ```
1368 |
1369 | В случае с вызовом по таймеру антидребезг будет частично обеспечиваться самим таймером и в библиотеке его можно отключить (поставить период 0).
1370 |
1371 | Для корректной работы таймаутов, состояний и счётчика кликов нужен другой подход: буферизировать прочитанные по таймеру состояния и передавать их в тик в основном цикле. Например так:
1372 | ```cpp
1373 | bool readbuf = 0; // буфер пина
1374 |
1375 | void loop() {
1376 | // таймер на 50 мс
1377 | static uint32_t tmr;
1378 | if (millis() - tmr >= 50) {
1379 | tmr = millis();
1380 | readbuf = readSomePin(); // чтение в буфер
1381 | }
1382 |
1383 | // тик из буфера
1384 | btn.tick(readbuf);
1385 |
1386 | if (btn.click()) foo();
1387 | }
1388 | ```
1389 |
1390 | ### Пропуск событий
1391 | EncButton позволяет кнопке работать в паре с энкодером для корректного отслеживания *нажатых поворотов* - при нажатом повороте события с кнопки будут пропущены, т.е. не обработается удержание и клик. Допустим кнопок несколько: они могут выполнять действия как сами по себе, так и в паре с энкодером (кнопка зажата и крутится энкодер, в программе меняется выбранное кнопкой значение). Чтобы при удержании кнопка не генерировала события (удержание, степ, клики...) можно включить пропуск событий. Он будет действовать **до отпускания кнопки**:
1392 |
1393 | ```cpp
1394 | if (btn.pressing() && enc.turn()) {
1395 | btn.skipEvents(); // зафиксирован поворот. Пропускаем события
1396 | // нажатый поворот
1397 | }
1398 |
1399 | if (btn.click()) {
1400 | // просто клик
1401 | }
1402 | ```
1403 |
1404 |
1405 |
1406 | ### Мини примеры, сценарии
1407 | ```cpp
1408 | // меняем значения переменных
1409 |
1410 | // поворот энкодера
1411 | if (enc.turn()) {
1412 | // меняем с шагом 5
1413 | var += 5 * enc.dir();
1414 |
1415 | // меняем с шагом 1 при обычном повороте, 10 при быстром
1416 | var += enc.fast() ? 10 : 1;
1417 |
1418 | // меняем с шагом 1 при обычном повороте, 10 при нажатом
1419 | var += enc.pressing() ? 10 : 1;
1420 |
1421 | // меняем одну переменную при повороте, другую - при нажатом повороте
1422 | if (enc.pressing()) var0++;
1423 | else var1++;
1424 |
1425 | // если кнопка нажата - доступны предварительные клики
1426 | // Выбираем переменную для изменения по предв. кликам
1427 | if (enc.pressing()) {
1428 | switch (enc.getClicks()) {
1429 | case 1: var0 += enc.dir();
1430 | break;
1431 | case 2: var1 += enc.dir();
1432 | break;
1433 | case 3: var2 += enc.dir();
1434 | break;
1435 | }
1436 | }
1437 | }
1438 |
1439 | // импульсное удержание на каждом шаге инкрементирует переменную
1440 | if (btn.step()) var++;
1441 |
1442 | // смена направления изменения переменной после отпускания из step
1443 | if (btn.step()) var += dir;
1444 | if (btn.releaseStep()) dir = -dir;
1445 |
1446 | // изменение выбранной переменной при помощи step
1447 | if (btn.step(1)) var1++; // клик-удержание
1448 | if (btn.step(2)) var2++; // клик-клик-удержание
1449 | if (btn.step(3)) var3++; // клик-клик-клик-удержание
1450 |
1451 | // если держать step больше 2 секунд - инкремент +5, пока меньше - +1
1452 | if (btn.step()) {
1453 | if (btn.stepFor(2000)) var += 5;
1454 | else var += 1;
1455 | }
1456 |
1457 | // включение режима по количеству кликов
1458 | if (btn.hasClicks()) mode = btn.getClicks();
1459 |
1460 | // включение режима по нескольким кликам и удержанию
1461 | if (btn.hold(1)) mode = 1; // клик-удержание
1462 | if (btn.hold(2)) mode = 2; // клик-клик-удержание
1463 | if (btn.hold(3)) mode = 3; // клик-клик-клик-удержание
1464 |
1465 | // или так
1466 | if (btn.hold()) mode = btn.getClicks();
1467 |
1468 | // кнопка отпущена, смотрим сколько её удерживали
1469 | if (btn.release()) {
1470 | // от 1 до 2 секунд
1471 | if (btn.pressFor() > 1000 && btn.pressFor() <= 2000) mode = 1;
1472 | // от 2 до 3 секунд
1473 | else if (btn.pressFor() > 2000 && btn.pressFor() <= 3000) mode = 2;
1474 | }
1475 | ```
1476 |
1477 |
1478 |
1479 | ## Гайд по миграции с v2 на v3
1480 | ### Инициализация
1481 | ```cpp
1482 | // ВИРТУАЛЬНЫЕ
1483 | VirtEncButton eb; // энкодер с кнопкой
1484 | VirtButton b; // кнопка
1485 | VirtEncoder e; // энкодер
1486 |
1487 | // РЕАЛЬНЫЕ
1488 | // энкодер с кнопкой
1489 | EncButton eb(enc0, enc1, btn); // пины энкодера и кнопки
1490 | EncButton eb(enc0, enc1, btn, modeEnc); // + режим пинов энкодера (умолч. INPUT)
1491 | EncButton eb(enc0, enc1, btn, modeEnc, modeBtn); // + режим пина кнопки (умолч. INPUT_PULLUP)
1492 | EncButton eb(enc0, enc1, btn, modeEnc, modeBtn, btnLevel); // + уровень кнопки (умолч. LOW)
1493 | // шаблонный
1494 | EncButton eb; // пины энкодера и кнопки
1495 | EncButton eb(modeEnc); // + режим пинов энкодера (умолч. INPUT)
1496 | EncButton eb(modeEnc, modeBtn); // + режим пина кнопки (умолч. INPUT_PULLUP)
1497 | EncButton eb(modeEnc, modeBtn, btnLevel); // + уровень кнопки (умолч. LOW)
1498 |
1499 | // кнопка
1500 | Button b(pin); // пин
1501 | Button b(pin, mode); // + режим пина кнопки (умолч. INPUT_PULLUP)
1502 | Button b(pin, mode, btnLevel); // + уровень кнопки (умолч. LOW)
1503 | // шаблонный
1504 | ButtonT b; // пин
1505 | ButtonT b(mode); // + режим пина кнопки (умолч. INPUT_PULLUP)
1506 | ButtonT b(mode, btnLevel); // + уровень кнопки (умолч. LOW)
1507 |
1508 | // энкодер
1509 | Encoder e(enc0, enc1); // пины энкодера
1510 | Encoder e(enc0, enc1, mode); // + режим пинов энкодера (умолч. INPUT)
1511 | // шаблонный
1512 | EncoderT e; // пины энкодера
1513 | EncoderT e(mode); // + режим пинов энкодера (умолч. INPUT)
1514 | ```
1515 |
1516 | ### Функции
1517 | | v2 | v3 |
1518 | | ----------- | ------------ |
1519 | | `held()` | `hold()` |
1520 | | `hold()` | `holding()` |
1521 | | `state()` | `pressing()` |
1522 | | `setPins()` | `init()` |
1523 |
1524 | - Изменился порядок указания пинов (см. доку выше)
1525 | - `clearFlags()` заменена на `clear()` (сбросить флаги событий) и `reset()` (сбросить системные флаги обработки, закончить обработку)
1526 |
1527 | ### Логика работы
1528 | В v3 функции опроса событий (click, turn...) не сбрасываются сразу после своего вызова - они сбрасываются при следующем вызове `tick()`, таким образом сохраняют своё значение во всех последующих вызовах на текущей итерации главного цикла программы. **Поэтому `tick()` нужно вызывать только 1 раз за цикл, иначе будут пропуски действий!** Читай об этом выше.
1529 |
1530 |
1531 | ## Примеры
1532 | Остальные примеры смотри в **examples**!
1533 |
1534 | Полное демо EncButton
1535 |
1536 | ```cpp
1537 | // #define EB_NO_FOR // отключить поддержку pressFor/holdFor/stepFor и счётчик степов (экономит 2 байта оперативки)
1538 | // #define EB_NO_CALLBACK // отключить обработчик событий attach (экономит 2 байта оперативки)
1539 | // #define EB_NO_COUNTER // отключить счётчик энкодера (экономит 4 байта оперативки)
1540 | // #define EB_NO_BUFFER // отключить буферизацию энкодера (экономит 1 байт оперативки)
1541 |
1542 | // #define EB_DEB_TIME 50 // таймаут гашения дребезга кнопки (кнопка)
1543 | // #define EB_CLICK_TIME 500 // таймаут ожидания кликов (кнопка)
1544 | // #define EB_HOLD_TIME 600 // таймаут удержания (кнопка)
1545 | // #define EB_STEP_TIME 200 // таймаут импульсного удержания (кнопка)
1546 | // #define EB_FAST_TIME 30 // таймаут быстрого поворота (энкодер)
1547 | // #define EB_TOUT_TIME 1000 // таймаут действия (кнопка и энкодер)
1548 |
1549 | #include
1550 | EncButton eb(2, 3, 4);
1551 | //EncButton eb(2, 3, 4, INPUT); // + режим пинов энкодера
1552 | //EncButton eb(2, 3, 4, INPUT, INPUT_PULLUP); // + режим пинов кнопки
1553 | //EncButton eb(2, 3, 4, INPUT, INPUT_PULLUP, LOW); // + уровень кнопки
1554 |
1555 | void setup() {
1556 | Serial.begin(115200);
1557 |
1558 | // показаны значения по умолчанию
1559 | eb.setBtnLevel(LOW);
1560 | eb.setClickTimeout(500);
1561 | eb.setDebTimeout(50);
1562 | eb.setHoldTimeout(600);
1563 | eb.setStepTimeout(200);
1564 |
1565 | eb.setEncReverse(0);
1566 | eb.setEncType(EB_STEP4_LOW);
1567 | eb.setFastTimeout(30);
1568 |
1569 | // сбросить счётчик энкодера
1570 | eb.counter = 0;
1571 | }
1572 |
1573 | void loop() {
1574 | eb.tick();
1575 |
1576 | // обработка поворота общая
1577 | if (eb.turn()) {
1578 | Serial.print("turn: dir ");
1579 | Serial.print(eb.dir());
1580 | Serial.print(", fast ");
1581 | Serial.print(eb.fast());
1582 | Serial.print(", hold ");
1583 | Serial.print(eb.pressing());
1584 | Serial.print(", counter ");
1585 | Serial.print(eb.counter);
1586 | Serial.print(", clicks ");
1587 | Serial.println(eb.getClicks());
1588 | }
1589 |
1590 | // обработка поворота раздельная
1591 | if (eb.left()) Serial.println("left");
1592 | if (eb.right()) Serial.println("right");
1593 | if (eb.leftH()) Serial.println("leftH");
1594 | if (eb.rightH()) Serial.println("rightH");
1595 |
1596 | // кнопка
1597 | if (eb.press()) Serial.println("press");
1598 | if (eb.click()) Serial.println("click");
1599 |
1600 | if (eb.release()) {
1601 | Serial.println("release");
1602 |
1603 | Serial.print("clicks: ");
1604 | Serial.print(eb.getClicks());
1605 | Serial.print(", steps: ");
1606 | Serial.print(eb.getSteps());
1607 | Serial.print(", press for: ");
1608 | Serial.print(eb.pressFor());
1609 | Serial.print(", hold for: ");
1610 | Serial.print(eb.holdFor());
1611 | Serial.print(", step for: ");
1612 | Serial.println(eb.stepFor());
1613 | }
1614 |
1615 | // состояния
1616 | // Serial.println(eb.pressing());
1617 | // Serial.println(eb.holding());
1618 | // Serial.println(eb.busy());
1619 | // Serial.println(eb.waiting());
1620 |
1621 | // таймаут
1622 | if (eb.timeout()) Serial.println("timeout!");
1623 |
1624 | // удержание
1625 | if (eb.hold()) Serial.println("hold");
1626 | if (eb.hold(3)) Serial.println("hold 3");
1627 |
1628 | // импульсное удержание
1629 | if (eb.step()) Serial.println("step");
1630 | if (eb.step(3)) Serial.println("step 3");
1631 |
1632 | // отпущена после импульсного удержания
1633 | if (eb.releaseStep()) Serial.println("release step");
1634 | if (eb.releaseStep(3)) Serial.println("release step 3");
1635 |
1636 | // отпущена после удержания
1637 | if (eb.releaseHold()) Serial.println("release hold");
1638 | if (eb.releaseHold(2)) Serial.println("release hold 2");
1639 |
1640 | // проверка на количество кликов
1641 | if (eb.hasClicks(3)) Serial.println("has 3 clicks");
1642 |
1643 | // вывести количество кликов
1644 | if (eb.hasClicks()) {
1645 | Serial.print("has clicks: ");
1646 | Serial.println(eb.getClicks());
1647 | }
1648 | }
1649 | ```
1650 |
1651 |
1652 | Подключение обработчика
1653 |
1654 | ```cpp
1655 | #include
1656 | EncButton eb(2, 3, 4);
1657 |
1658 | void callback() {
1659 | Serial.print("callback: ");
1660 | switch (eb.action()) {
1661 | case EB_PRESS:
1662 | Serial.println("press");
1663 | break;
1664 | case EB_HOLD:
1665 | Serial.println("hold");
1666 | break;
1667 | case EB_STEP:
1668 | Serial.println("step");
1669 | break;
1670 | case EB_RELEASE:
1671 | Serial.println("release");
1672 | break;
1673 | case EB_CLICK:
1674 | Serial.println("click");
1675 | break;
1676 | case EB_CLICKS:
1677 | Serial.print("clicks ");
1678 | Serial.println(eb.getClicks());
1679 | break;
1680 | case EB_TURN:
1681 | Serial.print("turn ");
1682 | Serial.print(eb.dir());
1683 | Serial.print(" ");
1684 | Serial.print(eb.fast());
1685 | Serial.print(" ");
1686 | Serial.println(eb.pressing());
1687 | break;
1688 | case EB_REL_HOLD:
1689 | Serial.println("release hold");
1690 | break;
1691 | case EB_REL_HOLD_C:
1692 | Serial.print("release hold clicks ");
1693 | Serial.println(eb.getClicks());
1694 | break;
1695 | case EB_REL_STEP:
1696 | Serial.println("release step");
1697 | break;
1698 | case EB_REL_STEP_C:
1699 | Serial.print("release step clicks ");
1700 | Serial.println(eb.getClicks());
1701 | break;
1702 | }
1703 | }
1704 |
1705 | void setup() {
1706 | Serial.begin(115200);
1707 | eb.attach(callback);
1708 | }
1709 |
1710 | void loop() {
1711 | eb.tick();
1712 | }
1713 | ```
1714 |
1715 |
1716 | Все типы кнопок
1717 |
1718 | ```cpp
1719 | #include
1720 |
1721 | Button btn(4);
1722 | ButtonT<5> btnt;
1723 | VirtButton btnv;
1724 |
1725 | void setup() {
1726 | Serial.begin(115200);
1727 | }
1728 |
1729 | void loop() {
1730 | // Button
1731 | btn.tick();
1732 | if (btn.click()) Serial.println("btn click");
1733 |
1734 | // ButtonT
1735 | btnt.tick();
1736 | if (btnt.click()) Serial.println("btnt click");
1737 |
1738 | // VirtButton
1739 | btnv.tick(!digitalRead(4)); // передать логическое значение
1740 | if (btnv.click()) Serial.println("btnv click");
1741 | }
1742 | ```
1743 |
1744 |
1745 | Все типы энкодеров
1746 |
1747 | ```cpp
1748 | #include
1749 |
1750 | Encoder enc(2, 3);
1751 | EncoderT<5, 6> enct;
1752 | VirtEncoder encv;
1753 |
1754 | void setup() {
1755 | Serial.begin(115200);
1756 | }
1757 |
1758 | void loop() {
1759 | // опрос одинаковый для всех, 3 способа:
1760 |
1761 | // 1
1762 | // tick вернёт 1 или -1, значит это шаг
1763 | if (enc.tick()) Serial.println(enc.counter);
1764 |
1765 | // 2
1766 | // можно опросить через turn()
1767 | enct.tick();
1768 | if (enct.turn()) Serial.println(enct.dir());
1769 |
1770 | // 3
1771 | // можно не использовать опросные функции, а получить направление напрямую
1772 | int8_t v = encv.tick(digitalRead(2), digitalRead(3));
1773 | if (v) Serial.println(v); // выведет 1 или -1
1774 | }
1775 | ```
1776 |
1777 |
1778 |
1779 | ## Версии
1780 |
1781 | Старые
1782 |
1783 | - v1.1 - пуллап отдельныи методом
1784 | - v1.2 - можно передать конструктору параметр INPUT_PULLUP / INPUT(умолч)
1785 | - v1.3 - виртуальное зажатие кнопки энкодера вынесено в отдельную функцию + мелкие улучшения
1786 | - v1.4 - обработка нажатия и отпускания кнопки
1787 | - v1.5 - добавлен виртуальный режим
1788 | - v1.6 - оптимизация работы в прерывании
1789 | - v1.6.1 - подтяжка по умолчанию INPUT_PULLUP
1790 | - v1.7 - большая оптимизация памяти, переделан FastIO
1791 | - v1.8 - индивидуальная настройка таймаута удержания кнопки (была общая на всех)
1792 | - v1.8.1 - убран FastIO
1793 | - v1.9 - добавлена отдельная отработка нажатого поворота и запрос направления
1794 | - v1.10 - улучшил обработку released, облегчил вес в режиме callback и исправил баги
1795 | - v1.11 - ещё больше всякой оптимизации + настройка уровня кнопки
1796 | - v1.11.1 - совместимость Digispark
1797 | - v1.12 - добавил более точный алгоритм энкодера EB_BETTER_ENC
1798 | - v1.13 - добавлен экспериментальный EncButton2
1799 | - v1.14 - добавлена releaseStep(). Отпускание кнопки внесено в дебаунс
1800 | - v1.15 - добавлен setPins() для EncButton2
1801 | - v1.16 - добавлен режим EB_HALFSTEP_ENC для полушаговых энкодеров
1802 | - v1.17 - добавлен step с предварительными кликами
1803 | - v1.18 - не считаем клики после активации step. hold() и held() тоже могут принимать предварительные клики. Переделан и улучшен дебаунс
1804 | - v1.18.1 - исправлена ошибка в releaseStep() (не возвращала результат)
1805 | - v1.18.2 - fix compiler warnings
1806 | - v1.19 - оптимизация скорости, уменьшен вес в sram
1807 | - v1.19.1 - ещё чутка увеличена производительность
1808 | - v1.19.2 - ещё немного увеличена производительность, спасибо XRay3D
1809 | - v1.19.3 - сделал высокий уровень кнопки по умолчанию в виртуальном режиме
1810 | - v1.19.4 - фикс EncButton2
1811 | - v1.20 - исправлена критическая ошибка в EncButton2
1812 | - v1.21 - EB_HALFSTEP_ENC теперь работает для обычного режима
1813 | - v1.22 - улучшен EB_HALFSTEP_ENC для обычного режима
1814 | - v1.23 - getDir() заменил на dir()
1815 | - v2.0
1816 | - Алгоритм EB_BETTER_ENC оптимизирован и установлен по умолчанию, дефайн EB_BETTER_ENC упразднён
1817 | - Добавлен setEncType() для настройки типа энкодера из программы, дефайн EB_HALFSTEP_ENC упразднён
1818 | - Добавлен setEncReverse() для смены направления энкодера из программы
1819 | - Добавлен setStepTimeout() для установки периода импульсного удержания, дефайн EB_STEP упразднён
1820 | - Мелкие улучшения и оптимизация
1821 |
1822 |
1823 | - v3.0
1824 | - Библиотека переписана с нуля, с предыдущими версиями несовместима!
1825 | - Полностью другая инициализация объекта
1826 | - Переименованы: hold()->holding(), held()->hold()
1827 | - Оптимизация Flash памяти: библиотека весит меньше, в некоторых сценариях - на несколько килобайт
1828 | - Оптимизация скорости выполнения кода, в том числе в прерывании
1829 | - На несколько байт меньше оперативной памяти, несколько уровней оптимизации на выбор
1830 | - Более простое, понятное и удобное использование
1831 | - Более читаемый исходный код
1832 | - Разбитие на классы для использования в разных сценариях
1833 | - Новые функции, возможности и обработчики для кнопки и энкодера
1834 | - Буферизация энкодера в прерывании
1835 | - Нативная обработка двух одновременно нажимаемых кнопок как третьей кнопки
1836 | - Поддержка 4-х типов энкодеров
1837 | - Переписана документация
1838 | - EncButton теперь заменяет GyverLibs/VirtualButton (архивирована)
1839 | - v3.1
1840 | - Расширена инициализация кнопки
1841 | - Убраны holdEncButton() и toggleEncButton()
1842 | - Добавлен turnH()
1843 | - Оптимизированы прерывания энкодера, добавлена setEncISR()
1844 | - Буферизация направления и быстрого поворота
1845 | - Сильно оптимизирована скорость работы action() (общий обработчик)
1846 | - Добавлено подключение внешней функции-обработчика событий
1847 | - Добавлена обработка кнопки в прерывании - pressISR()
1848 | - v3.2
1849 | - Добавлены функции tickRaw() и clear() для всех классов. Позволяет проводить раздельную обработку (см. доку)
1850 | - Улучшена обработка кнопки с использованием прерываний
1851 | - v3.3
1852 | - Добавлены функции получения времени удержания pressFor(), holdFor(), stepFor() (отключаемые)
1853 | - Добавлен счётчик степов getSteps() (отключаемый)
1854 | - v3.4
1855 | - Доступ к счётчику кликов во время нажатого поворота
1856 | - Добавлена функция detach()
1857 | - v3.5
1858 | - Добавлена зависимость GyverIO (ускорен опрос пинов)
1859 | - Добавлена возможность задать свои функции аптайма и чтения пина
1860 | - v3.5.2
1861 | - Оптимизация
1862 | - Упрощена замена кастомных функций
1863 | - Исправлена ошибка компиляции при использовании библиотеки в нескольких .cpp файлах
1864 | - v3.5.3
1865 | - Добавлено количество кликов в опрос press/release/click/pressing
1866 | - v3.5.5 - коллбэк на базе std::function для ESP
1867 | - v3.5.8 - добавлен метод releaseHoldStep()
1868 | - v3.5.11 - добавлен метод skipEvents() для игнорирования событий кнопки в сложных сценариях использования
1869 | - v3.6.0
1870 | - Добавлен класс MultiButton для корректного опроса нескольких кнопок с вызовом обработчика
1871 | - Добавлено подключение обработчика с передачей указателя на объект
1872 |
1873 |
1874 | ## Баги и обратная связь
1875 | При нахождении багов создавайте **Issue**, а лучше сразу пишите на почту [alex@alexgyver.ru](mailto:alex@alexgyver.ru)
1876 | Библиотека открыта для доработки и ваших **Pull Request**'ов!
1877 |
1878 | При сообщении о багах или некорректной работе библиотеки нужно обязательно указывать:
1879 | - Версия библиотеки
1880 | - Какой используется МК
1881 | - Версия SDK (для ESP)
1882 | - Версия Arduino IDE
1883 | - Корректно ли работают ли встроенные примеры, в которых используются функции и конструкции, приводящие к багу в вашем коде
1884 | - Какой код загружался, какая работа от него ожидалась и как он работает в реальности
1885 | - В идеале приложить минимальный код, в котором наблюдается баг. Не полотно из тысячи строк, а минимальный код
1886 |
--------------------------------------------------------------------------------
/README_EN.md:
--------------------------------------------------------------------------------
1 | This is an automatic translation, may be incorrect in some places. See sources and examples!
2 |
3 | # ENCBUTTON
4 |
5 | |⚠️⚠️⚠️
** 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! ** <***
⚠️⚠️⚠️ |
6 | | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
7 |
8 | A light and very functional library for an encoder with a button, encoder or buttons with Arduino
9 | - Button
10 | - processing of events: pressing, releasing, click, click counter, retention, impulse retention, deduction time + preliminary clicks for all modes
11 | - Program suppression of rubbish
12 | - support for processing two simultaneously pressed buttons as a third button
13 | - Encoder
14 | - Processing of events: a normal turn, pressed turn, fast turn
15 | - Support of four types of incidental encoders
16 | - high -precision algorithm for determining the position
17 | - buffer in interruption
18 | - simple and understandable use
19 | - a huge number of opportunities and their combinations for different scenarios for using even one button
20 | - virtual regime (for example, for working with a pain expander)
21 | - optimized to work in interruption
22 | - The fastest reading of pins for AVR, ESP8266, ESP32 (used by gyverio)
23 | - Fast asynchronous algorithms of survey of actions from the button and encoder
24 | - rigid optimization and low weight in Flash and SRAM memory: 5 bytes SRAM (on an instance) and ~ 350 bytes Flash to process the button
25 |
26 | Examples of use scenarios:
27 | - Several clicks - inclusion of the regime (according to the number of clicks)
28 | - Several clicks + short retention - another option for turning on the mode (according to the number of clicks)
29 | - several clicks + holding - a gradual change in the value of the selected variable (on the number of clicks)
30 | - Several clicks choose a variable, encoder changes it
31 | - 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
32 | - navigation by menu during the rotation of the encoder, a change in the variable during the rotation of a clamped encoder
33 | - 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)
34 | - And so on
35 |
36 | ## compatibility
37 | Compatible with all arduino platforms (used arduino functions)
38 |
39 | ## Content
40 | - [installation] (# Install)
41 | - [Information] (# Info)
42 | - [documentation] (#docs)
43 | - [compilation settings] (#config)
44 | - [full description of classes] (#class)
45 | - [processing and survey] (#tick)
46 | - [preliminary clicks] (# PRECLICS)
47 | - [direct reading of the button] (#btnread)
48 | - [immersion in the cycle] (#loop)
49 | - [Timeout] (# Timeout)
50 | - [Busy] (# Busy)
51 | - [receipt of an event]
52 | - [Optimization] (# Optimise)
53 | - [Collback] (#callback)
54 | - [Simultaneous pressing] (# Double)
55 | - [interruption] (# ISR)
56 | - [array of buttons/encoders] (# Array)
57 | - [custom functions] (# Custom)
58 | - [Timer survey] (# Timer)
59 | - [Mini examples, scenarios] (# Examples-Mini)
60 | - [migration with v2] (#migrate)
61 | - [Examples] (# Example)
62 | - [versions] (#varsions)
63 | - [bugs and feedback] (#fedback)
64 |
65 |
66 | ## Installation
67 | - For work, a library is required [gyverio] (https://github.com/gyverlibs/gyverio)
68 | -Library can be found by the name ** encbutton ** and installed through the library manager in:
69 | - Arduino ide
70 | - Arduino ide v2
71 | - Platformio
72 | - [download the library] (https://github.com/gyverlibs/encbuton/archive/refs/heads/main.zip) .Zip archive for manual installation:
73 | - unpack and put in * C: \ Program Files (X86) \ Arduino \ Libraries * (Windows X64)
74 | - unpack and put in * C: \ Program Files \ Arduino \ Libraries * (Windows X32)
75 | - unpack and put in *documents/arduino/libraries/ *
76 | - (Arduino id) Automatic installation from. Zip: * sketch/connect the library/add .Zip library ... * and specify downloaded archive
77 | - 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)
78 | ### Update
79 | - I recommend always updating the library: errors and bugs are corrected in the new versions, as well as optimization and new features are added
80 | - through the IDE library manager: find the library how to install and click "update"
81 | - 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!
82 |
83 |
84 |
85 | ## Information
86 | ## encoder
87 | #### type of encoder
88 | The library supports all 4 types of * incidental * encoders, the type can be configured using `setenctype (type)`:
89 | - `eb_step4_Low` - active low signal (tightening to VCC).Full period (4 phases) per click.*Set by default*
90 | - `eb_step4_high` - an active high signal (tightening to GND).Full period (4 phases) for one click
91 | - `eb_step2` - half of the period (2 phases) per click
92 | - `eb_step1` - a quarter of the period (1 phase) per click, as well as encoders without fixation
93 |
94 | ! [Diagram] (/doc/enc_type.png)
95 |
96 | #### Recommendations
97 | 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):
98 | ! [Scheme] (/doc/encali.png)
99 |
100 | You can tie an encoder yourself according to the following scheme (RC filters to the encoder channels + tightening of all pens to VCC):
101 | ! [Scheme] (/doc/enc_scheme.png)
102 |
103 | > 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).
104 |
105 | ### Button
106 | ### The level of the button
107 | 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:
108 | - `High` - the button connects the VCC.Installed by default in `virt`-bibliotexes
109 | - `Low` - the button connects GND.Set by default in the main libraries
110 |
111 | ! [Scheme] (/doc/btn_scheme.png)
112 |
113 | #### Pin
114 | 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.
115 |
116 |
117 |
118 | ## Documentation
119 |
120 |
121 |
122 | ### Defaine settings
123 | Be up to the library
124 |
125 | `` `CPP
126 |
127 | // Disable PressFor/Holdfor/StepFor support and Stepov counter (saves 2 bytes of RAM)
128 | #define eb_no_for
129 |
130 | // Disable the event processor attach (saves 2 bytes of RAM)
131 | #define eb_no_callback
132 |
133 | // Disable the encoder counter [Virtencoder, Encoder, Encbutton] (saves 4 bytes of RAM)
134 | #define eb_no_counter
135 |
136 | // Disconnect the buffer of the encoder (saves 2 bytes of RAM)
137 | #define eb_no_buffer
138 |
139 | /*
140 | Setting up timeouts for all classes
141 | - replaces the timauts constants, changeCranberries from the program (setxxxtimeout ()) will not be
142 | - Setting affects all buttons announced in the program/Encoders
143 | - saves 1 bytes of RAM for an object for each timeout
144 | - shows the default values in MS
145 | - values are not limited to 4000MS, as when installing from the program (SetXXXTimeout ())
146 | */
147 | #define eb_deb_time 50 // Timesout to extinguish the trim button (button)
148 | #define eb_click_time 500 // Click Stayout (button)
149 | #define eb_hold_time 600 // Maintenance Times (button)
150 | #define eb_step_time 200 // Impulse retention Timesout (button)
151 | #define EB_FAST_TIME 30 // TIMAUT RAM (ENCODER)
152 | `` `
153 |
154 |
155 |
156 | ### classes
157 | 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:
158 | - Basic classes:
159 | - `virtbutton` - the base class of the virtual button, provides all the possibilities of the buttons
160 | - `virtencoder` - the base class of the virtual encoder, determines the fact and direction of the rotation of the enkoder
161 | - `virtencbutton` - the base class of a virtual encoder with a button, provides an encoder poll taking into account the button, *inherits Virtbututton and Virtencoder *
162 | - Main classes:
163 | - `Button`,` buttont` - button class, *inherits virtbutton *
164 | - `Encoder`,` Encodert` - Encoder class, *inherits virtencoder *
165 | - `ENCBUTTON`,` ENCBUTTONT` - ENCODER class with a button, *inherits VirtenCbutton, Virtbutton, Virtencoder *
166 |
167 | 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`.
168 |
169 | > * 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.
170 |
171 | > `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.
172 |
173 | > Note: `# Include ` connects all the library tools!
174 |
175 |
176 | Table of Functions of the button Summary>
177 |
178 | ||Virtbutton |Virtencbutton |Button |Encbutton |
179 | | ---------------- |: -------------------------------- |: ----------------: |:--------: |: ---------: |
180 | |Read |||✔ ||
181 | |Readbtn ||||✔ |
182 | |TickRaW |✔ |✔ |✔ |✔ |
183 | |SetHoldtimeout |✔ |✔ |✔ |✔ |
184 | |Setsteptimeout |✔ |✔ |✔ |✔ |
185 | |SetClicktimeout |✔ |✔ |✔ |✔ |
186 | |Setdebtimeout |✔ |✔ |✔ |✔ |
187 | |Setbtnlevel |✔ |✔ |✔ |✔ |
188 | |Pressisr |✔ |✔ |✔ |✔ |
189 | |Reset |✔ |✔ |✔ |✔ |
190 | |Clear |✔ |✔ |✔ |✔ |
191 | |Attach |✔ |✔ |✔ |✔ |
192 | |Detach |✔ |✔ |✔ |✔ |
193 | |Press |✔ |✔ |✔ |✔ |
194 | |Release |✔ |✔ |✔ |✔ |
195 | |Click |✔ |✔ |✔ |✔ |
196 | |Pressing |✔ |✔ |✔ |✔ |
197 | |Hold |✔ |✔ |✔ |✔ |
198 | |Holding |✔ |✔ |✔ |✔ |
199 | |STEP |✔ |✔ |✔ |✔ |
200 | |Hasclicks |✔ |✔ |✔ |✔ |
201 | |GetClicks |✔ |✔ |✔ |✔ |
202 | |Getsteps |✔ |✔ |✔ |✔ |
203 | |Releasehold |✔ |✔ |✔ |✔ |
204 | |ReleaseStep |✔ |✔ |✔ |✔ |
205 | |Waiting |✔ |✔ |✔ |✔ |
206 | |Busy |✔ |✔ |✔ |✔ |
207 | |Action |✔ |✔ |✔ |✔ |
208 | |Timeout |✔ |✔ |✔ |✔ |
209 | |Pressfor |✔ |✔ |✔ |✔ |
210 | |Holdfor |✔ |✔ |✔ |✔ |
211 | |STEPFOR |✔ |✔ |✔ |✔ |
212 |
213 |
214 |
215 | Encoder functions table Summary>
216 |
217 | ||Virtencoder |Encoder |Virtencbutton |Encbutton |
218 | | -------------- |: ----------------------------- |: -------: |: -----------------------------: |: ---------: |
219 | |Readenc ||||✔ |
220 | |Initenc |✔ |✔ |✔ |✔ |
221 | |Setencreverse |✔ |✔ |✔ |✔ |
222 | |Setenctype |✔ |✔ |✔ |✔ |
223 | |Setencisr |✔ |✔ |✔ |✔ |
224 | |Clear |✔ |✔ |✔ |✔ |
225 | |Turn |✔ |✔ |✔ |✔ |
226 | |Dir |✔ |✔ |✔ |✔ |
227 | |TickRaW |✔ |✔ |✔ |✔ |
228 | |Pollenc |✔ |✔ |✔ |✔ |
229 | |Counter |✔ |✔ |✔ |✔ |
230 | |SetFasttimeout |||✔ |✔ |
231 | |Turnh |||✔ |✔ |
232 | |Fast |||✔ |✔ |
233 | |Right |||✔ |✔ |
234 | |Left |||✔ |✔ |
235 | |Righth |||✔ |✔ |
236 | |Lefth |||✔ |✔ |
237 | |Action |||✔ |✔ |
238 | |Timeout |||✔ |✔ |
239 | |Attach |||✔ |✔ |
240 | |Detach |||✔ |✔ |
241 |
242 |
243 |
244 | virtbutton
245 |
246 | `` `CPP
247 | // ==ward
248 | // Set the deduction timeout, silence.600 (max. 4000 ms)
249 | VOID SetHoldtimeout (Uint16_T Tout);
250 |
251 | // Install the timout of impulse retention, silence.200 (max. 4000 ms)
252 | VOID Setsteptimeout (Uint16_T Tout);
253 |
254 | // Install the expectations of clicks, silence.500 (max. 4000 ms)
255 | VOID setClicktimeout (Uint16_T Tout);
256 |
257 | // Install the Timout of the Anti -Direct, silence.50 (Max. 255 ms)
258 | VOID Setdebtimeout (Uint8_t Tout);
259 |
260 | // set the level of the button (HIGH - button closes VCC, Low - closes GND)
261 | // silent.High, that is, True - the button is pressed
262 | VOID setbtnlevel (Bool LEVEL);
263 |
264 | // Connect the function-processor of events (type VOID F ())
265 | VOID attach (VOID (*handler) ());
266 |
267 | // Disconnect the event-handle function
268 | VOID Detach ();
269 |
270 | // ==============ward
271 | // throw off system flags (forcibly finish processing)
272 | VOID Reset ();
273 |
274 | // forcibly drop the flags of events
275 | Void Clear ();
276 |
277 | // ====ward
278 | // button processing with value
279 | Bool Tick (Bool S);
280 |
281 | // Processing of the virtual button as simultaneous pressing two other buttons
282 | Bool Tick (Virtbutton & B0, Virtbutton & B1);
283 |
284 | // button pressed in the interruption of the button
285 | VOID Pressisr ();
286 |
287 | // Processing the button without reseting events and calling collobes
288 | Bool Tickrad (Bool S);
289 |
290 | // ================== Poll ================================ward
291 | // Pressure button [event]
292 | Bool Press ();
293 | Bool Press (Uint8_T Clicks);
294 |
295 | // button released (in any case) [evente]
296 | Bool Release ();
297 | Bool Release (Uint8_T Clicks);
298 |
299 | // click on the button (released without holding) [event]
300 | Bool click ();
301 | Bool Click (Uint8_T Clicks);
302 |
303 | // Squeezed button (between Press () and Release ()) [condition]
304 | Bool Pressing ();
305 | Bool Pressing (Uint8_T Clicks);
306 |
307 | // The button was withheld (more timeout) [event]
308 | Bool Hold ();
309 | Bool Hold (Uint8_T Clicks);
310 |
311 | // The button is held (more timeout) [condition]
312 | Bool Holding ();
313 | Bool Holding (Uint8_T Clicks);
314 |
315 | // Impulse retention [event]
316 | Bool Step ();
317 | Bool Step (Uint8_T Clicks);
318 |
319 | // Several clicks were recorded [event]
320 | Bool HasClicks ();
321 | Bool HasClicks (Uint8_T Clicks);
322 |
323 | // button released after holding [Event]
324 | Bool ReleaseHold ();
325 | Bool ReleaseHold (Uint8_T Clicks);
326 |
327 | // Button is released after impulse retention [Event]
328 | Bool ReleaseStep ();
329 | Bool ReleaseStep (Uint8_T Clicks);
330 |
331 | // get the number of clicks
332 | uint8_t getclicks ();
333 |
334 | // Get the number of steps
335 | uint16_t getsteps ();
336 |
337 | // button awaits repeated clicks (between Click () and HasClicks ()) [condition]
338 | Bool Waiting ();
339 |
340 | // processing (between the first press and after waiting for clicks) [condition]
341 | Bool Busy ();
342 |
343 | // there was an action from the button, the event code [event] will return
344 | Uint16_T Action ();
345 |
346 | // ==========================================================================
347 | // After interacting with the button (or enkoder Encbutton), the specified time has passed, ms [event]
348 | Bool Timeout (Uint16_T MS);
349 |
350 | // The time that the button is held (from the beginning of the press), ms
351 | uint16_t Pressfor ();
352 |
353 | // The button is held longer than (from the beginning of pressing), ms [condition]
354 | Bool Pressfor (Uint16_T MS);
355 |
356 | // The time that the button is held (from the beginning of retention), ms
357 | uint16_t holdfor ();
358 |
359 | // The button is held longer than (from the beginning of retention), ms [condition]
360 | Bool Holdfor (Uint16_T MS);
361 |
362 | // The time that the button is held (from the beginning of the step), ms
363 | uint16_t stepfor ();
364 |
365 | // The button is held longer than (from the beginning of the step), ms [condition]
366 | Bool StepFor (Uint16_T MS);
367 | `` `
368 |
369 |
370 | virtencoder
371 |
372 | `` `CPP
373 | // ===============================================
374 | // Invert the direction of the encoder (silence 0)
375 | VOID Setencreverse (Bool Rev);
376 |
377 | // Install the type of encoder (eb_step4_low, eb_step4_high, eb_step2, eb_step1)
378 | VOID Setenctype (Uint8_T Type);
379 |
380 | // use encoder processing in interruption
381 | VOID Setencisr (Bool Use);
382 |
383 | // Initialization of the encoder
384 | VOID Initenc (Bool E0, Bool E1);
385 |
386 | // Encoder initialization with a combined value
387 | VOID Initenc (int8_t v);
388 |
389 | // throw off the flags of events
390 | Void Clear ();
391 |
392 | // ===================== Support ==========================
393 | // there was a turn [event]
394 | Bool Turn ();
395 |
396 | // Direction of the encoder (1 or -1) [condition]
397 | int8_t die ();
398 |
399 | // counter
400 | Int32_T Counter;
401 |
402 | // ====ward
403 | // Interrogate the encoder in interruption.Will return 1 or -1 during rotation, 0 when stopping
404 | Int8_t Tickisr (Bool E0, Bool E1);
405 | Int8_T Tickisr (int8_t state);
406 |
407 | // Introduce the Encoder.Will return 1 or -1 during rotation, 0 when stopping
408 | Int8_T Tick (Bool E0, Bool E1);
409 | int8_t tick (int8_t state);
410 | int8_t tick ();// The processing itself in interruption
411 |
412 | // Introduce the Encoder without resetting the turning event.Will return 1 or -1 during rotation, 0 when stopping
413 | Int8_T TickRaW (Bool E0, Bool E1);
414 | Int8_T TickRaW (int8_t state);
415 | Int8_T TickRaW ();// The processing itself in interruption
416 |
417 | // Introduce the encoder without installing flags on a turn (faster).Will return 1 or -1 during rotation, 0 when stopping
418 | Int8_t Pollenc (Bool E0, Bool E1);
419 | Int8_t Pollenc (Int8_T State);
420 | `` `
421 |
422 |
423 | virtencbutton
424 |
425 | - Available functions from `virtbutton`
426 | - Available functions from `virtencoder`
427 |
428 | `` `CPP
429 | // ============ward
430 | // Install a timeout of fast turning, ms
431 | VOID setfasttimeout (Uint8_t Tout);
432 |
433 | // throw the flags of encoder and buttons
434 | Void Clear ();
435 |
436 | // =================== Poll =================================
437 | // Any turn of the encoder [event]
438 | Bool Turn ();
439 |
440 | // pressed turn of enkoder [event]
441 | Bool Turnh ();
442 |
443 | // Fast turning of the encoder [condition]
444 | Bool Fast ();
445 |
446 | // Open Turn to the right [event]
447 | Bool Right ();
448 |
449 | // Non -pressed turnfrom left [event]
450 | Bool Left ();
451 |
452 | // pressed turn to the right [event]
453 | Bool Righth ();
454 |
455 | // pressed turn to the left [event]
456 | Bool Lefth ();
457 |
458 | // there was an action from a button or encoder, will return the event code [event]
459 | Uint16_T Action ();
460 |
461 | // ====ward
462 | // processing in interruption (only encoder).Will return 0 at rest, 1 or -1 when turning
463 | Int8_t Tickisr (Bool E0, Bool E1);
464 | Int8_T Tickisr (int8_t e01);
465 |
466 | // processing of encoder and buttons
467 | Bool Tick (Bool E0, Bool E1, Bool BTN);
468 | Bool Tick (Int8_t E01, Bool BTN);
469 | Bool Tick (Bool BTN);// Encoder in interruption
470 |
471 | // Processing the encoder and buttons without discharge flags and calling collobes
472 | Bool Tickrad (Bool E0, Bool E1, Bool BTN);
473 | Bool Tickrade (Int8_t E01, Bool BTN);
474 | Bool Tickrade (Bool BTN);// Encoder in interruption
475 | `` `
476 |
477 |
478 | Button
479 |
480 | - Available functions from `virtbutton`
481 | - default buttons mode - `Low`
482 |
483 | `` `CPP
484 | Button;
485 | Button (uint8_t pin);// indicating Pin
486 | Button (uint8_t npin, uint8_t mode);// + mode of operation (silence input_pullup)
487 | Button (uint8_t npin, uint8_t mode, uint8_t btnlevel);// + button level (silence)
488 | `` `
489 | `` `CPP
490 | // indicate the pin and its operating mode
491 | VOID Init (uint8_t npin, uint8_t mode);
492 |
493 | // Read the current value of the button (without debate) taking into account Setbtnlevel
494 | Bool Read ();
495 |
496 | // processing function, call in loop
497 | Bool Tick ();
498 |
499 | // Processing the button without reseting events and calling collobes
500 | Bool Tickrade ();
501 | `` `
502 |
503 |
504 | buttont
505 |
506 | - Available functions from `virtbutton`
507 | - default buttons mode - `Low`
508 |
509 | `` `CPP
510 | Buttont ;// indicating Pin
511 | Buttont (uint8_t mode);// + mode of operation (silence input_pullup)
512 | Buttont (uint8_t mode, uint8_t btnlevel);// + button level (silence)
513 | `` `
514 | `` `CPP
515 | // specify the operating mode
516 | VOID Init (Uint8_t Mode);
517 |
518 | // Read the current value of the button (without debate) taking into account Setbtnlevel
519 | Bool Read ();
520 |
521 | // processing function, call in loop
522 | Bool Tick ();
523 | `` `
524 |
525 |
526 | encoder
527 |
528 | - Available functions from `virtencoder`
529 |
530 | `` `CPP
531 | Encoder;
532 | Encoder (Uint8_t Enca, Uint8_T ENCB);// indicating Pinov
533 | Encoder (Uint8_t Enca, Uint8_t Encb, Uint8_t Mode);// + mode of operation (silence. Input)
534 | `` `
535 | `` `CPP
536 | // Indicate pins and their operating mode
537 | VOID Init (Uint8_t Enca, Uint8_t Encb, Uint8_t Mode);
538 |
539 | // Function of processing for calling in an interruption of encoder
540 | int8_t tickisr ();
541 |
542 | // Function of processing for calling in LOOP
543 | int8_t tick ();
544 | `` `
545 |
546 |
547 | encodert
548 |
549 | - Available functions from `virtencoder`
550 |
551 | `` `CPP
552 | Encodert ;// indicating Pinov
553 | Encodert (Uint8_t Mode);// + mode of operation (silence. Input)
554 | `` `
555 | `` `CPP
556 | // specify the mode of operation of Pinov
557 | VOID Init (Uint8_t Mode);
558 |
559 | // Function of processing for calling in an interruption of encoder
560 | int8_t tickisr ();
561 |
562 | // Function of processing for calling in LOOP
563 | int8_t tick ();
564 | `` `
565 |
566 |
567 | encbutton
568 |
569 | - Available functions from `virtbutton`
570 | - Available functions from `virtencoder`
571 | - Available functions from `virtencbutton`
572 |
573 | `` `CPP
574 | ENCBUTTON;
575 |
576 | // Set the Pins (ENK, ENK, button)
577 | ENCBUTTON (UINT8_T ENCA, UINT8_T ENCB, UINT8_T BTN);
578 |
579 | // Reference Pins (ENK, ENK, button, Pinmode ENK, Pinmode button, button level)
580 | ENCBUTTON (UINT8_T ENCA, UINT8_T ENCB, UINT8_T BTN, UINT8_T MODEENC = Input, Uint8_t Modebtn = Input_Pullup, Uint8_T BTNLEVEL = LOW);
581 | `` `
582 | `` `CPP
583 | // Reference Pins (ENK, ENK, button, Pinmode ENK, Pinmode button, button level)
584 | VOID Init (Uint8_t Enca, Uint8_t Encb, Uint8_t BTN, UINT8_T MODEENC = Input, Uint8_t Modebtn = Input_Pullup, Uint8_T BTNLEVEL = LOW);
585 |
586 | // Function of processing for calling in an interruption of encoder
587 | int8_t tickisr ();
588 |
589 | // processing function, call in loop
590 | Bool Tick ();
591 |
592 | // Read the value of the button taking into account Setbtnlevel
593 | Bool Readbtn ();
594 |
595 | // Read the value of the encoder
596 | Int8_T Readenc ();
597 | `` `
598 |
599 |
600 | encbuttont
601 |
602 | - Available functions from `virtbutton`
603 | - Available functions from `viRencoder`
604 | - Available functions from `virtencbutton`
605 |
606 | `` `CPP
607 | // indicating Pinov
608 | ENCBUTTONT ;
609 |
610 | // + Pino operation mode, button level
611 | ENCBUTTONT (Uint8_t Modeenc = Input, Uint8_t Modebtn = Input_Pullup, Uint8_T BTNlevel = Low);
612 | `` `
613 | `` `CPP
614 | // Configure Pino operation mode, button level
615 | VOID Init (Uint8_t Modeenc = Input, Uint8_t Modebtn = Input_pullup, Uint8_t Btnlevel = Low);
616 |
617 | // Function of processing for calling in an interruption of encoder
618 | int8_t tickisr ();
619 |
620 | // processing function, call in loop
621 | Bool Tick ();
622 |
623 | // Read the button value
624 | Bool Readbtn ();
625 |
626 | // Read the value of the encoder
627 | Int8_T Readenc ();
628 | `` `
629 |
630 |
631 |
632 |
633 | ### Processing and Poll
634 | All libraries have a general ** function of processing ** (ticker `tick`), which receives the current signal from the button and encoder
635 | - this function must be caused once in the main cycle of the program (for virtual - with the transmission of the meaning)
636 | - 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`)
637 | - There are separate functions for calling in interruption, they have a suffix `isr`, see documentation below
638 |
639 | The library processes the signal inside this function, the result can be obtained from ** functions of the survey ** events.They are of two types:
640 | - `[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)
641 | - `[condition]` - the function returns `true`, while this condition is actively (for example, the button is held)
642 |
643 | For simplicity of perception, the processing function must be placed at the beginning of the cycle, and polls do below:
644 | `` `CPP
645 | VOID loop () {
646 | btn.tick ();// survey
647 |
648 | if (btn.click ()) serial.println ("click");// will display once when clicking
649 | if (btn.click ()) serial.println ("click");// The same click!
650 | }
651 | `` `
652 | > 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.
653 |
654 | #### several functions of processing
655 | 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:
656 | `` `CPP
657 | // you can not do it this way
658 | VOID loop () {
659 | btn.tick ();
660 | if (btn.click ()) ...
661 |
662 | // ....
663 |
664 | btn.tick ();
665 | if (btn.hold ()) ...
666 | }
667 | `` `
668 |
669 | If you really need to get into a deaf cycle and interrogate the button there, then it can: you can:
670 | `` `CPP
671 | // so it is possible
672 | VOID loop () {
673 | btn.tick ();
674 | if (btn.click ()) ...
675 |
676 | While (True) {
677 | btn.tick ();
678 | if (btn.hold ()) ...
679 | if (btn.click ()) Break;
680 | }
681 | }
682 | `` `
683 |
684 | 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:
685 | `` `CPP
686 | // so it is possible
687 | VOID CB () {
688 | switch (btn.action ()) {
689 | // ...
690 | }
691 | }
692 |
693 | VOID setup () {
694 | btn.attach (CB);
695 | }
696 |
697 | VOID loop () {
698 | btn.tick ();
699 | // ...
700 | btn.tick ();
701 | // ...
702 | btn.tick ();
703 | }
704 | `` `
705 |
706 | ### "loaded" program
707 | 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:
708 | -beginners: to study the lesson cycle [how to write a sketch] (https://alexgyver.ru/lessns/how-to-sketch/)
709 | - write asynchronous code in `loop ()`
710 | - Any synchronous structure on `delay ()` can be made asynchronous using `millis ()` `
711 | - 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
712 | - connect the button to the hardware interruption (see below)
713 | - 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}`} `
714 | - If it is impossible to optimize the main cycle - call the ticker in another "stream" and use the processor:
715 | - in interruption of a timer with a period of ~ 50ms or more often
716 | - on another core (for example, ESP32)
717 | - In another Task Freertos
718 | - inside `yield ()` (inside `delay ()`)
719 |
720 | #### Separate processing
721 | > 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
722 |
723 | 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:
724 | - Surrender actions (Click, Press, Turn ...)
725 | - Call `Clear ()`
726 | - call `tickRaW ()` between heavy sections of code
727 |
728 | `` `CPP
729 | VOID loop () {
730 | if (btn.click ()) ...
731 | if (btn.press ()) ...
732 | if (btn.step ()) ...
733 |
734 | btn.clear ();
735 |
736 | // ...
737 | BTN.TickRAW ();
738 | // ...
739 | BTN.TickRAW ();
740 | // ...
741 | BTN.TickRAW ();
742 | // ...
743 | }
744 | `` `
745 | 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.
746 |
747 | > In this scenario, the Encoder's buffering in the interruption does not work and all events are not processed `Releasexxx`
748 |
749 | #### Processing inside Delay
750 | 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:
751 | `` `CPP
752 | // Code insertion in Delay
753 | VOID yield () {
754 | EB.TickRAW ();
755 | }
756 |
757 | VOID loop () {
758 | if.click ()) ...
759 | if (btn.turn ()) ...
760 |
761 | eb.clear ();
762 |
763 | // ...
764 | Delay (10);
765 | // ...
766 | DELAY (50);
767 | // ...
768 | }
769 | `` `
770 |
771 | > In this scenario, the Encoder's buffering in the interruption does not work and all events are not processed `Releasexxx`
772 |
773 | #### button processing
774 | The library processes the button as follows:
775 | - Pressing with software suppression of rubbish (holding longer than the Deb time), the result is the event `Press`, the state of` Pressing` and `Busy`
776 | - retention longer than the Hold Hold time - the event `hold`, the state` holding`
777 | - holding longer than the Hold + Timeshot Timeshu Taimout - a pulse event `step`, triggers with the STEP period while the button holds
778 | - release of the button, the result - the event `Release`, the removal of the states` Pressing` and `Holding`
779 | - release to the deduction time - event `click`
780 | - release after holding - event `Releasehold`
781 | - release after impulse deduction - event `ReleaseStep`
782 | - Events `Releasehold` and` ReleaseStep` mutually exclusive, if the button was withheld `step` -` Releasehold` will no longer work
783 | - Waiting for a new click during the Click timeout, the state `Waiting`
784 | - If there is no new click - the removal of the state of `Busy`, the processing is completed
785 | - If the button is pressed again - processing a new click
786 | - 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`
787 | - 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`
788 | - If `Timeout` is expected - Timeout event with the specified period from the current moment
789 | - processing the button in the interruption informs the library about the fact of pressing, the rest of the processing is performed regularly in `Tick ()` `
790 |
791 | > 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!
792 |
793 | > 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)
794 |
795 | ##### Click
796 | ! [click] (/doc/click.gif)
797 |
798 | ##### Hold
799 | ! [Hold] (/doc/hold.gif)
800 |
801 | ##### STEP
802 | ! [STEP] (/DOC/STEP.GIF)
803 |
804 | Online symulation is available [here] (https://wokwi.com/projects/373591584298469377)
805 |
806 | #### Encoder Processing
807 | - "Fast" turn is considered a turn committed less than tuned timaut from the previous turn
808 | - the turns processed in interruption become active (cause events) after calling `tick ()`
809 | - Access to the encoder’s counter `Counter` is a public variable of the class, you can do anything with it:
810 | `` `CPP
811 | Serial.println (eb.counter);// read
812 | Eb.counter += 1234;// change
813 | eb.counter = 0;// Knock
814 | `` `
815 |
816 | #### encoder processing with a button
817 | - 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
818 | - The turning of the encoder also affects the system timout (the `timeout ()` function) will work after the indicated time after turning the enkoder
819 | - the Klikov counter is available when pressed: several clicks, click of a button, turn
820 |
821 |
822 |
823 | ### Preliminary clicks
824 | 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:
825 | `` `CPP
826 | // 1
827 | if (btn.hold ()) {
828 | if (btn.getclics () == 2) serial.println ("Hold 2 Clicks");
829 | }
830 |
831 | // 2
832 | if (btn.hold (2)) serial.println ("Hold 2 Clicks");
833 | `` `
834 |
835 | 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.
836 |
837 |
838 |
839 | ## direct reading button
840 | 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 **:
841 | `` `CPP
842 | VOID setup () {
843 | btn.tick ();
844 | if (btn.press ()) serial.println ("button pressed at start");
845 | }
846 | `` `
847 |
848 | The following functions will help for such scenarios, return `true` if the button is pressed:
849 | - `read ()` for libraries button and buttont
850 | - `Readbtn ()` for library libraries and encbuttont
851 |
852 | > The button survey is performed taking into account the previously configured button level (Setbtnlevel)!It is not necessary to manually invert the logic:
853 |
854 | `` `CPP
855 | VOID setup () {
856 | // btn.setbtnlevel (LOW);// you can configure the level
857 |
858 | if (btn.read ()) serial.println ("button pressed at start");
859 | }
860 | `` `
861 |
862 |
863 |
864 | ### immersion in the cycle
865 | 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:
866 | `` `CPP
867 | VOID setup () {
868 | Serial.Begin (115200);
869 |
870 | btn.tick ();
871 | While (btn.busy ()) {
872 | btn.tick ();
873 | if (btn.hold ()) serial.println ("Hold");
874 | if (btn.step ()) serial.println ("step");
875 | }
876 |
877 | Serial.println ("Program Start");
878 | }
879 | `` `
880 | 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:
881 | `` `CPP
882 | do {
883 | B.tn.tick ();
884 | if (btn.hold ()) serial.println ("Hold");
885 | if (btn.step ()) serial.println ("step");
886 | } While (btn.busy ());
887 | `` `
888 |
889 |
890 |
891 | ## Timeout
892 | 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:
893 | `` `CPP
894 | VOID loop () {
895 | eb.tick ();
896 |
897 | // ...
898 |
899 | if.timeout ()) {
900 | // after interaction with encoder 1 second passed
901 | // eeprom.put (0, settings);
902 | }
903 | }
904 | `` `
905 |
906 |
907 |
908 | ### Busy
909 | 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:
910 | `` `CPP
911 | VOID loop () {
912 | eb.tick ();
913 |
914 | // ...
915 |
916 | if (! eb.busy ()) {
917 | // Potentially long and difficult code
918 | }
919 | }
920 | `` `
921 |
922 |
923 |
924 | ### Obtaining an event
925 | Available in all classes ** with the ** button:
926 | - `virtbutton`
927 | - `Button`
928 | - `virtencbutton`
929 | - `encbutton`
930 |
931 | 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):
932 | - `eb_press` - click on the button
933 | - `eb_hold` - the button is kept
934 | - `eb_step` - impulse retention
935 | - `eb_release` - the button is released
936 | - `eb_click` - Single click
937 | - `eb_clicks` - A few click signal
938 | - `eb_turn` - turn of the encoder
939 | - `eb_rel_hold` - the button is released after holding
940 | - `EB_REL_HOLD_C` - the button is released after holding off the premises.clicks
941 | - `EB_REL_STEP` - the button is released after the step
942 | - `EB_REL_STEP_C` - the button is released after the step with a border.clicks
943 |
944 | > 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
945 |
946 | The obtained event code can be processed through `switch`:
947 | `` `CPP
948 | switch (eb.action ()) {
949 | Case eb_press:
950 | // ...
951 | Break;
952 | Case eb_hold:
953 | // ...
954 | Break;
955 | // ...
956 | }
957 | `` `
958 |
959 |
960 |
961 | ## Optimization
962 | #### Library weight
963 | 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):
964 | `` `CPP
965 | #define eb_no_for
966 | #define eb_no_callback
967 | #define eb_no_counter
968 | #define eb_no_buffer
969 | #define eb_deb_time 50 // Timesout to extinguish the trim button (button)
970 | #define eb_click_time 500 // Click Stayout (button)
971 | #define eb_hold_time 600 // Maintenance Times (button)
972 | #define eb_step_time 200 // Impulse retention Timesout (button)
973 | #define EB_FAST_TIME 30 // TIMAUT RAM (ENCODER)
974 | #include
975 | ENCBUTTONT <2, 3, 4> EB;
976 | `` `
977 | In this case, an encoder with a button will take only 8 bytes in SRAM, and just a button - 5.
978 |
979 | #### Fulfillment speed
980 | 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 **:
981 | `` `CPP
982 | VOID loop () {
983 | if (eb.tick ()) {
984 | if.turn ()) ...;
985 | if.click ()) ...;
986 | }
987 | }
988 | `` `
989 |
990 | 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:
991 | `` `CPP
992 | VOID loop () {
993 | if (eb.tick ()) {
994 | switch (eb.action ()) {
995 | Case eb_press:
996 | // ...
997 | Break;
998 | Case eb_hold:
999 | // ...
1000 | Break;
1001 | // ...
1002 | }
1003 | }
1004 | }
1005 | `` `
1006 |
1007 | 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:
1008 | `` `CPP
1009 | if (btn.busy ()) {
1010 | if (btn.pressing ()) ...
1011 | if (btn.holding ()) ...
1012 | if (btn.waiting ()) ...
1013 | }
1014 | `` `
1015 |
1016 |
1017 |
1018 | ### Collback
1019 | You can connect the external function-shacklerCranberry, it will be caused when any event occurs.This opportunity works in all classes ** with the ** button:
1020 | - `virtbutton`
1021 | - `Button`
1022 | - `virtencbutton`
1023 | - `encbutton`
1024 |
1025 | `` `CPP
1026 | ENCBUTTON EB (2, 3, 4);
1027 |
1028 | Void callback () {
1029 | switch (eb.action ()) {
1030 | Case eb_press:
1031 | // ...
1032 | Break;
1033 | Case eb_hold:
1034 | // ...
1035 | Break;
1036 | // ...
1037 | }
1038 | }
1039 |
1040 | VOID setup () {
1041 | eb.attach (callback);
1042 | }
1043 |
1044 | VOID loop () {
1045 | eb.tick ();
1046 | }
1047 | `` `
1048 |
1049 |
1050 |
1051 | ### Simultaneous pressing
1052 | The library supports work with two simultaneously pressed buttons as with the third button.For this you need:
1053 | 1. To make a virtual button `virtbutton`
1054 | 2. Call the processing of real buttons
1055 | 3. Pass these buttons to the virtual button to process (these can be objects of classes `virtbutton`,` button`, `encbutton` + their` t`- version)
1056 | 4. Next to interrogate events
1057 |
1058 | `` `CPP
1059 | Button b0 (4);
1060 | Button b1 (5);
1061 | Virtbutton B2;// 1
1062 |
1063 | VOID loop () {
1064 | b0.tick ();// 2
1065 | b1.tick ();// 2
1066 | B2.Tick (B0, B1);// 3
1067 |
1068 | // 4
1069 | if (b0.click ()) serial.println ("b0 click");
1070 | if (b1.click ()) serial.println ("b1 click");
1071 | if (b2.click ()) serial.println ("b0+b1 click");
1072 | }
1073 | `` `
1074 |
1075 | 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.
1076 |
1077 |
1078 |
1079 | ## interrupt
1080 | ### encoder
1081 | For processing an encoder in a loaded program you need:
1082 | - Connect both of his pins to hardware interruptions by `Change`
1083 | - install `setencisr (true)`
1084 | - call a special ticker for interruption in the handler
1085 | - The main ticker also needs to be called in `loop` for corrething work - events are generated in the main ticker:
1086 | `` `CPP
1087 | // Example for Atmega328 and Encbutton
1088 | ENCBUTTON EB (2, 3, 4);
1089 |
1090 | /*
1091 | // ESP8266/ESP32
1092 | IRAM_ATTR VOID ISR () {
1093 | eb.tickisr ();
1094 | }
1095 | */
1096 |
1097 | VOID isr () {
1098 | eb.tickisr ();
1099 | }
1100 | VOID setup () {
1101 | Attachinterrupt (0, Isr, Change);
1102 | Attachinterrupt (1, ISR, Change);
1103 | eb.setencisr (true);
1104 | }
1105 | VOID loop () {
1106 | eb.tick ();
1107 | }
1108 | `` `
1109 |
1110 | 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:
1111 | - 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)
1112 | - 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 **
1113 |
1114 | Notes:
1115 | - 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
1116 | - The encoder counter is always relevant and can be ahead of buffering turns in the program with large delays in the main cycle!
1117 | - 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!)
1118 | - a processor connected to `Attach ()` will be called from `Tick ()`, that is, *not from interruption *!
1119 |
1120 | ### virtual classes
1121 | 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:
1122 |
1123 | `` `CPP
1124 | Virtencoder e;
1125 |
1126 | VOID isr () {
1127 | E.tickisr (DigitalRead (2), DigitalRead (3));
1128 | }
1129 | VOID setup () {
1130 | Attachinterrupt (0, Isr, Change);
1131 | Attachinterrupt (1, ISR, Change);
1132 |
1133 | E.Setencisr (1);
1134 | }
1135 | VOID loop () {
1136 | E.tick ();// Do not transmit the states of Pinov
1137 | }
1138 | `` `
1139 |
1140 | #### Button
1141 | To process the button in the interruption, you need:
1142 | - Connect an interruption on ** press ** buttons taking into account its physical connection and level:
1143 | - If the button is deputy`Low` - Interruption` Falling`
1144 | - if the button closes `high` - interruption` rising`
1145 | - call `Pressisr ()` in the interruption processor
1146 |
1147 | `` `CPP
1148 | Button b (2);
1149 |
1150 | /*
1151 | // ESP8266/ESP32
1152 | IRAM_ATTR VOID ISR () {
1153 | B.Pressisr ();
1154 | }
1155 | */
1156 |
1157 | VOID isr () {
1158 | B.Pressisr ();
1159 | }
1160 | VOID setup () {
1161 | Attachinterrupt (0, ISR, Falling);
1162 | }
1163 | VOID loop () {
1164 | B.tick ();
1165 | }
1166 | `` `
1167 |
1168 | 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.
1169 |
1170 |
1171 |
1172 | ### Array of buttons/Encoder
1173 | 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:
1174 | `` `CPP
1175 | Button btns [5];
1176 | ENCBUTTON EBS [3];
1177 |
1178 | VOID setup () {
1179 | btns [0] .init (2);// Indicate the pin
1180 | btns [1] .init (5);
1181 | btns [2] .init (10);
1182 | // ...
1183 |
1184 | EBS [0] .init (11, 12, 13, Input);
1185 | EBS [1] .init (14, 15, 16);
1186 | // ...
1187 | }
1188 | VOID loop () {
1189 | for (int i = 0; i <5; i ++) btns [i] .Tick ();
1190 | for (int i = 0; i <3; i ++) EBS [i] .Tick ();
1191 |
1192 | if (btns [2] .Click ()) serial.println ("BTN2 click");
1193 | // ...
1194 | }
1195 | `` `
1196 |
1197 |
1198 |
1199 | ### Caste functions
1200 | 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.
1201 | - `bool eb_read (uint8_t pin)` - for its pine reading function
1202 | - `void eb_mode (uint8_t pin, uint8_t mode)` - for your analogue Pinmode
1203 | - `uint32_t eb_uptime ()` - for your analogue millis ()
1204 |
1205 | Example:
1206 |
1207 | `` `CPP
1208 | #include
1209 |
1210 | Bool eb_read (uint8_t pin) {
1211 | Return DigitalRead (PIN);
1212 | }
1213 |
1214 | VOID eb_mode (uint8_t pin, uint8_t mode) {
1215 | Pinmode (PIN, Mode);
1216 | }
1217 |
1218 | uint32_t eb_uptime () {
1219 | Return Millis ();
1220 | }
1221 | `` `
1222 |
1223 |
1224 |
1225 | ### Survey by timer
1226 | 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!
1227 | `` `CPP
1228 | VOID loop () {
1229 | // Timer for 50 ms
1230 | Static uint32_t tmr;
1231 | if (millis () - tmr> = 50) {
1232 | TMR = Millis ();
1233 | btn.tick (Readsomepin ());
1234 | }
1235 |
1236 | // will be actively within 50 ms !!!
1237 | if (btn.click ()) foo ();
1238 | }
1239 | `` `
1240 |
1241 | In this situation, you need to do this: tick along the timer, process events there and drop flags at the end:
1242 | `` `CPP
1243 | VOID loop () {
1244 | // Timer for 50 ms
1245 | Static uint32_t tmr;
1246 | if (millis () - tmr> = 50) {
1247 | TMR = Millis ();
1248 | // TIK
1249 | btn.tick (Readsomepin ());
1250 |
1251 | // analysis of events
1252 | if (btn.click ()) foo ();
1253 |
1254 | // Reset of the flags
1255 | btn.clear ();
1256 | }
1257 | }
1258 | `` `
1259 |
1260 | Or you can connect the handler and call `clear ()` at the end of the function:
1261 | `` `CPP
1262 | Void callback () {
1263 | switch (btn.action ()) {
1264 | // ...
1265 | }
1266 |
1267 | // Reset of the flags
1268 | btn.clear ();
1269 | }
1270 |
1271 | VOID loop () {
1272 | // Timer for 50 ms
1273 | Static uint32_t tmr;
1274 | if (millis () - tmr> = 50) {
1275 | TMR = Millis ();
1276 | btn.tick (Readsomepin ());
1277 | }
1278 | }
1279 | `` `
1280 |
1281 | 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).
1282 |
1283 | 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:
1284 | `` `CPP
1285 | Bool Readbuf = 0;// buffer Pina
1286 |
1287 | VOID loop () {
1288 | // Timer for 50 ms
1289 | Static uint32_t tmr;
1290 | if (millis () - tmr> = 50) {
1291 | TMR = Millis ();
1292 | Readbuf = Readsomepin ();// Reading in the buffer
1293 | }
1294 |
1295 | // tick from the buffer
1296 | BTN.Tick (Readbuf);
1297 |
1298 | if (btn.click ()) foo ();
1299 | }
1300 | `` `
1301 |
1302 |
1303 |
1304 | ### Mini examples, scripts
1305 | `` `CPP
1306 | // Change the values of the variables
1307 |
1308 | // Turn of the encoder
1309 | if (enc.turn ()) {
1310 | // Change in step 5
1311 | var += 5 * enc.dir ();
1312 |
1313 | // Change in step 1 with a normal turn, 10 with fast
1314 | Var += ENC.FAST ()?10: 1;
1315 |
1316 | // Change in step 1 with a normal turn, 10 with pressed
1317 | vAR += ENC.Pressing ()?10: 1;
1318 |
1319 | // Change one variable when turning, the other - with a pressed turn
1320 | if (enc.pressing ()) Var0 ++;
1321 | Else Var1 ++;
1322 |
1323 | // If the button is pressed - preliminary clicks are available
1324 | // Choose a variable for changes in the premises.Clicks
1325 | if (enc.pressing ()) {
1326 | Switch (enc.getClicks ()) {
1327 | CASE 1: VAR0 += ENC.DIR ();
1328 | Break;
1329 | CASE 2: VAR1 += ENC.DIR ();
1330 | Break;
1331 | CASE 3: VAR2 += ENC.DIR ();
1332 | Break;
1333 | }
1334 | }
1335 | }
1336 |
1337 | // Impulse retention at every step is increasing the variable
1338 | if (btn.step ()) var ++;
1339 |
1340 | // Change the direction of change in the variable after letting go from STEP
1341 | if (btn.step ()) var += dir;
1342 | if (btn.releastep ()) die = -dir;
1343 |
1344 | // Change the selected variable using STEP
1345 | if (btn.step (1)) Var1 ++;// Click-holding
1346 | if (btn.step (2)) Var2 ++;// Click-Click-holding
1347 | if (btn.step (3)) var3 ++;// Click-Click-Click-hold
1348 |
1349 | // if you keep the STEP for more than 2 seconds - an incremental +5, so far less - +1
1350 | if (btn.step ()) {
1351 | if (btn.stepfor (2000)) var += 5;
1352 | Else Var += 1;
1353 | }
1354 |
1355 | // inclusion of the mode by the number of clicks
1356 | if (btn.hasclicks ()) mode = btn.getclicks ();
1357 |
1358 | // inclusion of the mode by several clicks and retention
1359 | if (btn.hold (1)) mode = 1;// Click-holding
1360 | if (btn.hold (2)) mode = 2;// Click-Click-holding
1361 | if (btn.hold (3)) mode = 3;// Click-Click-Click-hold
1362 |
1363 | // or so
1364 | if (btn.hold ()) mode = btn.getclicks ();
1365 |
1366 | // Button is released, look how much it was held
1367 | if (btn.release ()) {
1368 | // from 1 to 2 seconds
1369 | if (btn.pressfor ()> 1000 && btn.pressfor () <= 2000) mod = 1;
1370 | // from 2 to 3 seconds
1371 | Else if (BTN.PressFor ()> 2000 && BTN.PressFor () <= 3000) Mode = 2;
1372 | }
1373 | `` `
1374 |
1375 |
1376 |
1377 | ## guide for migration from v2 to v3
1378 | ## H initialization
1379 | `` `CPP
1380 | // virtual
1381 | Virtencbutton eb;// Encoder with button
1382 | Virtbutton b;// button
1383 | Virtencoder e;// Encoder
1384 |
1385 | // Real
1386 | // Encoder with button
1387 | ENCBUTTON EB (ENC0, ENC1, BTN);// Encoder Pins and buttons
1388 | ENCBUTTON EB (ENC0, ENC1, BTN, MODEENC);// + Pino Pino Encoder Pin (silence. Input)
1389 | ENCBUTTON EB (ENC0, ENC1, BTN, MODEENC, MODEBTN);// + Pin mode buttons (silent. Input_pullup)
1390 | ENCBUTTON EB (ENC0, ENC1, BTN, MODEENC, MODEBTN, BTNlevel);// + button level (silence)
1391 | // template
1392 | ENCBUTTON EB;// Encoder Pins and buttons
1393 | ENCBUTTON EB (Modeenc);// + Pino Pino Encoder Pin (silence. Input)
1394 | ENCBUTTON EB (Modeenc, Modebtn);// + Pin mode buttons (silent. Input_pullup)
1395 | ENCBUTTON EB (Modeenc, Modebtn, Btnlevel);// + button level (silence)
1396 |
1397 | // button
1398 | Button b (pin);// PIN
1399 | Button b (PIN, Mode);// + Pin mode buttons (silent. Input_pullup)
1400 | Button B (PIN, Mode, Btnlevel);// + button level (silence)
1401 | // template
1402 | Buttont b;// PIN
1403 | Buttont b (mode);// + Pin mode buttons (silent. Input_pullup)
1404 | Buttont b (mode, btnlevel);// + button level (silence)
1405 |
1406 | // Encoder
1407 | ENCODER E (ENC0, ENC1);// Pines of Encoder
1408 | ENCODER E (ENC0, ENC1, Mode);// + Pino Pino Encoder Pin (silence. Input)
1409 | // template
1410 | Encodert e;// Pines of Encoder
1411 | Encodert E (Mode);// + Pino Pino Encoder Pin (silence. Input)
1412 | `` `
1413 |
1414 | ### functions
1415 | |v2 |v3 |
1416 | | ------------- | -------------------------------------
1417 | |`HELD ()` |`Hold ()` |
1418 | |`Hold ()` |`Holding ()` |
1419 | |`state ()` |`Pressing ()` |
1420 | |`setpins ()` |`Init ()` |
1421 |
1422 | - The procedure for indicating Pinov has changed (see DEMPLE above)
1423 | - `Clearflags ()` replaced by `Clear ()` (drop the flags of events) and `reset ()` (drop systemic flags of processing, finish processing)
1424 |
1425 | ### Logic of Work
1426 | 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.
1427 |
1428 |
1429 | ## Examples
1430 | The rest of the examples look at ** Examples **!
1431 |
1432 | Full demo encbutton
1433 |
1434 | `` `CPP
1435 | // #define eb_no_for // Disable Pressfor/Holdfor/StepFor support and Stepov counter (saves 2 bytes of RAM)
1436 | // #define eb_no_callback // Disable the event processor Attach (saves 2 bytes of RAM)
1437 | // #define eb_no_counter // Disable the enkoder counter (saves 4 bytes of RAM)
1438 | // #define EB_NO_BUFFER // Disable the buffer of the encoder (saves 1 byte of RAM)
1439 |
1440 | // #define eb_deb_time 50 // Timesout to darebells button (button)
1441 | // #define eb_click_time 500 // Clicks standstatics (button)
1442 | // #define eb_hold_time 600 // Maintenance Times (button)
1443 | // #define eb_step_time 200 // pulse retention rate (button)
1444 | // #define EB_FAST_TIME 30 // Quick turn Timesout (Encoder)
1445 |
1446 | #include
1447 | ENCBUTTON EB (2, 3, 4);
1448 | // ENCBUTTON EB (2, 3, 4, Input);// + Pino Pino mode
1449 | // ENCBUTTON EB (2, 3, 4, Input, Input_pullup);// + button pins mode
1450 | // ENCBUTTON EB (2, 3, 4, Input, Input_pullup, Low);// + button level
1451 |
1452 | VOID setup () {
1453 | Serial.Begin (115200);
1454 |
1455 | // shows the default values
1456 | eb.setbtnlevel (Low);
1457 | EB.SetClicktimeout (500);
1458 | eb.Setdebtimeout (50);
1459 | Eb.SetHoldtimeout (600);
1460 | eb.setsteptimeout (200);
1461 |
1462 | eb.setencreverse (0);
1463 | eb.setenctype (eb_step4_low);
1464 | eb.setfasttimeout (30);
1465 |
1466 | // throw the Encoder counter
1467 | eb.counter = 0;
1468 | }
1469 |
1470 | VOID loop () {
1471 | eb.tick ();
1472 |
1473 | // General rotation processing
1474 | if.turn ()) {
1475 | Serial.print ("Turn: Dir");
1476 | Serial.print (eb.dir ());
1477 | Serial.print (", fast");
1478 | Serial.print (eb.fast ());
1479 | Serial.print (", Hold");
1480 | Serial.print (eb.pressing ());
1481 | Serial.print (", Counter");
1482 | Serial.print (eb.counter);
1483 | Serial.print (", clicks");
1484 | Serial.println (eb.getClicks ());
1485 | }
1486 |
1487 | // Turning rotation processing
1488 | if.left ()) serial.println ("Left");
1489 | if.right ()) serial.println ("right");
1490 | if.left ()) serial.println ("Lefth");
1491 | if.righth ()) serial.println ("righth");
1492 |
1493 | // button
1494 | if.press ()) serial.println ("Press");
1495 | if.click ()) serial.println ("click");
1496 |
1497 | if.release ()) {
1498 | Serial.println ("Release");
1499 |
1500 | Serial.print ("Clicks:");
1501 | Serial.print (eb.getClicks ());
1502 | Serial.print (", stps:");
1503 | Serial.print (eb.getsteps ());
1504 | Serial.print (", Press for:");
1505 | Serial.print (eb.pressfor ());
1506 | Serial.print (", Hold for:");
1507 | Serial.print (eb.holdfor ());
1508 | Serial.print (", step for:");
1509 | Serial.println (eb.stepfor ());
1510 | }
1511 |
1512 | // States
1513 | // serial.println (eb.pressing ());
1514 | // serial.println (eb.holding ());
1515 | // serial.println (eb.busy ());
1516 | // serial.println (eb.waiting ());
1517 |
1518 | // Timesout
1519 | if (eb.timeout ()) serial.println ("Timeout!");
1520 |
1521 | // Holding
1522 | if.hold ()) serial.println ("Hold");
1523 | if.hold (3)) serial.println ("Hold 3");
1524 |
1525 | // Impulse retention
1526 | if.step ()) serial.println ("step");
1527 | if.step (3)) serial.println ("STEP 3");
1528 |
1529 | // released after impulse deduction
1530 | if (eb.releastep ()) serial.println ("Release Step");
1531 | if (eb.releastep (3)) serial.println ("Release Step 3");
1532 |
1533 | // released after holding
1534 | if.releasehold ()) serial.println ("Release Hold");
1535 | if (eb.releasehold (2)) serial.println ("Release Hold 2");
1536 |
1537 | // Check for the number of clicks
1538 | if.hasclicks (3)) Serial.println ("Has 3 Clicks");
1539 |
1540 | // Bring the number of clicks
1541 | if.hasclicks ()) {
1542 | Serial.print ("Has Clicks:");
1543 | Serial.println (eb.getClicks ());
1544 | }
1545 | }
1546 | `` `
1547 |
1548 |
1549 | connection of the handler
1550 |
1551 | `` `CPP
1552 | #include
1553 | ENCBUTTON EB (2, 3, 4);
1554 |
1555 | Void callback () {
1556 | Serial.print ("callback:");
1557 | switch (eb.action ()) {
1558 | Case eb_press:
1559 | Serial.println ("Press");
1560 | Break;
1561 | Case eb_hold:serial.println ("Hold");
1562 | Break;
1563 | Case eb_step:
1564 | Serial.println ("STEP");
1565 | Break;
1566 | Case eb_release:
1567 | Serial.println ("Release");
1568 | Break;
1569 | Case eb_click:
1570 | Serial.println ("click");
1571 | Break;
1572 | Case eb_clicks:
1573 | Serial.print ("Clicks");
1574 | Serial.println (eb.getClicks ());
1575 | Break;
1576 | Case eb_turn:
1577 | Serial.print ("turn");
1578 | Serial.print (eb.dir ());
1579 | Serial.print ("");
1580 | Serial.print (eb.fast ());
1581 | Serial.print ("");
1582 | Serial.println (eb.pressing ());
1583 | Break;
1584 | Case eb_rel_hold:
1585 | Serial.println ("Release Hold");
1586 | Break;
1587 | CASE EB_REL_HOLD_C:
1588 | Serial.print ("Release Hold Clicks");
1589 | Serial.println (eb.getClicks ());
1590 | Break;
1591 | CASE EB_REL_STEP:
1592 | Serial.println ("Release Step");
1593 | Break;
1594 | CASE EB_REL_STEP_C:
1595 | Serial.print ("Release Step Clicks");
1596 | Serial.println (eb.getClicks ());
1597 | Break;
1598 | }
1599 | }
1600 |
1601 | VOID setup () {
1602 | Serial.Begin (115200);
1603 | eb.attach (callback);
1604 | }
1605 |
1606 | VOID loop () {
1607 | eb.tick ();
1608 | }
1609 | `` `
1610 |
1611 |
1612 | All types of buttons
1613 |
1614 | `` `CPP
1615 | #include
1616 |
1617 | Button BTN (4);
1618 | Buttont <5> btnt;
1619 | Virtbutton BTNV;
1620 |
1621 | VOID setup () {
1622 | Serial.Begin (115200);
1623 | }
1624 |
1625 | VOID loop () {
1626 | // Button
1627 | btn.tick ();
1628 | if (btn.click ()) serial.println ("btn click");
1629 |
1630 | // Buttont
1631 | btnt.tick ();
1632 | if (btnt.click ()) serial.println ("BTNT CLICK");
1633 |
1634 | // virtbutton
1635 | btnv.tick (! DigitalRead (4));// transmit logical value
1636 | if (btnv.click ()) serial.println ("btnv click");
1637 | }
1638 | `` `
1639 |
1640 |
1641 | All types of encoder
1642 |
1643 | `` `CPP
1644 | #include
1645 |
1646 | ENCODER ENC (2, 3);
1647 | ENCODERT <5, 6> ENCT;
1648 | Virtencoder encv;
1649 |
1650 | VOID setup () {
1651 | Serial.Begin (115200);
1652 | }
1653 |
1654 | VOID loop () {
1655 | // The survey is the same for everyone, 3 ways:
1656 |
1657 | // 1
1658 | // Tick will return 1 or -1, then this is a step
1659 | if (enc.tick ()) serial.println (enc.counter);
1660 |
1661 | // 2
1662 | // can be interviewed through turn ()
1663 | enct.tick ();
1664 | if (enct.turn ()) serial.println (enct.dir ());
1665 |
1666 | // 3
1667 | // you can not use survey functions, but get the direction directly
1668 | int8_t v = encv.tick (DigitalRead (2), DigitalRead (3));
1669 | if (v) serial.println (v);// Derive 1 or -1
1670 | }
1671 | `` `
1672 |
1673 |
1674 |
1675 | ## versions
1676 |
1677 | Old Summary>
1678 |
1679 | - V1.1 - Pullap separately by the method
1680 | - V1.2 - You can transfer the parameter input_pullup / input (silent) to the designer
1681 | - V1.3 - Virtual clamping of the encoder button is made into a separate function + minor improvements
1682 | - V1.4 - Processing of pressing and releasing the button
1683 | - v1.5 - added virtual mode
1684 | - V1.6 - Optimization of work in interruption
1685 | - V1.6.1 - Saching by default Input_pullup
1686 | - V1.7 - a large memory optimization, remade Fastio
1687 | - V1.8 - Individual tuning of the TIMUUT Maintenance of the button (was common at all)
1688 | - v1.8.1 - removed Fastio
1689 | - v1.9 - added a separate development of a pressed turn and a request for direction
1690 | - V1.10 - improved ReleASDE processing, eased the weight in callback and corrected the bugs
1691 | - V1.11 - even more than any optimization + setting button level
1692 | - V1.11.1 - Digispark compatibility
1693 | - V1.12 - added a more accurate algorithm of enkoder Eb_better_enc
1694 | - V1.13 - Added experimental ENCBUTTON2
1695 | - V1.14 - added ReleaseStep ().The release of the button is included in the debate
1696 | - v1.15 - added Setpins () for Encbutton2
1697 | - V1.16 - added EB_HALFSTEP_Enc mode for hemisphere encoders
1698 | - v1.17 - added STEP with preliminary clicks
1699 | - V1.18 - We do not consider clicks after the activation of STEP.Hold () and Held () can also take preliminary clicks.Redistributed and improved debate
1700 | - V1.18.1 - Fixed error in ReleaseStep () (did not return the result)
1701 | - V1.18.2 - Fix Compiler Warnings
1702 | - V1.19 - speed optimization, reduced weight in SRAM
1703 | - v1.19.1 - still a bit increased performance
1704 | - v1.19.2 - not yetCranberries increased a lot of performance, thanks xray3d
1705 | - v1.19.3 - made a high level of the default button in virtual mode
1706 | - V1.19.4 - Fix Encbutton2
1707 | - V1.20 - Critical error is fixed in Encbutton2
1708 | - V1.21 - EB_HALFSTEP_ENC now works for a normal mode
1709 | - V1.22 - Improved EB_HALFSTEP_Enc for a normal mode
1710 | - V1.23 - Getdir () replaced with DIR ()
1711 | - V2.0
1712 | - The eb_better_enc algorithm is optimized and set by default, the define eb_better_enc is abolished
1713 | - added setenctype () to configure the type of encoder from the program, the define EB_HALFSTEP_ENC is abolished
1714 | - added Setencreverse () to change the direction of the encoder from the program
1715 | - added setteptimeout () to set the period of impulse deduction, the define EB_STEP is abolished
1716 | - Small improvements and optimization
1717 |
1718 |
1719 | - V3.0
1720 | - The library is rewritten from scratch, with previous versions is incompatible!
1721 | - completely different initialization of the object
1722 | -renamed: Hold ()-> Holding (), HELD ()-> HOLD ()
1723 | - Optimization of Flash memory: the library weighs less, in some scenarios - by several kilobytes
1724 | - optimization of the speed of code execution, including in interruption
1725 | - several bytes less than RAM, several optimization levels to choose from
1726 | - a simpler, understandable and convenient use
1727 | - more readable source code
1728 | - Breaking into classes for use in different scenarios
1729 | - new functions, capabilities and handlers for the button and encoder
1730 | - Encoder's buffer in interruption
1731 | - native processing of two simultaneously pressed buttons as a third button
1732 | - support of 4 types of encoder
1733 | - The documentation is rewritten
1734 | - Encbutton is now replacing Gyverlibs/Virtualbutton (archived)
1735 | - V3.1
1736 | - The initialization of the button is expanded
1737 | - removed Holdencbutton () and Toggleencbutton ()
1738 | - added Turnh ()
1739 | - Optimized the interruptions of encoder, added Setencisr ()
1740 | - Buerization of the direction and quick turn
1741 | - strongly optimized the speed of Action () (general processor)
1742 | - Added connection of the external function-processor of events
1743 | - Added button processing in interruption - Pressisr ()
1744 | - V3.2
1745 | - Added the functions of TickRaW () and Clear () for all classes.Allows for separate processing (see document)
1746 | - improved processing button using interruptions
1747 | - V3.3
1748 | - Added functions of receiving PressFor (), HoldFor (), StepFor () (disconnected)
1749 | - Added meter of the steps Getsteps () (disconnected)
1750 | - V3.4
1751 | - access to the click counter during a pressed turn
1752 | - Added function Detach ()
1753 | - V3.5
1754 | - added dependence of Gyverio (accelerated Pino survey)
1755 | - added the opportunity to set your pharmacy and pine reading functions
1756 | - V3.5.2
1757 | - Optimization
1758 | - Simplified replacement of custom functions
1759 | - Fixed a compilation error when using a library in several .cpp files
1760 | - V3.5.3
1761 | - Added the number of clicks to the Press/Release/Click/Pressing poll
1762 | - V3.5.5 - Collback based on the STD :: Function for ESP
1763 |
1764 |
1765 | ## bugs and feedback
1766 | Create ** Issue ** when you find the bugs, and better immediately write to the mail [alex@alexgyver.ru] (mailto: alex@alexgyver.ru)
1767 | The library is open for refinement and your ** pull Request ** 'ow!
1768 |
1769 | When reporting about bugs or incorrect work of the library, it is necessary to indicate:
1770 | - The version of the library
1771 | - What is MK used
1772 | - SDK version (for ESP)
1773 | - version of Arduino ide
1774 | - whether the built -in examples work correctly, in which the functions and designs are used, leading to a bug in your code
1775 | - what code has been loaded, what work was expected from it and how it works in reality
1776 | - Ideally, attach the minimum code in which the bug is observed.Not a canvas of a thousand lines, but a minimum code
--------------------------------------------------------------------------------
/doc/btn_scheme.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GyverLibs/EncButton/f15946cfa649fa66a39d1ee196a782dbee958e73/doc/btn_scheme.png
--------------------------------------------------------------------------------
/doc/click.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GyverLibs/EncButton/f15946cfa649fa66a39d1ee196a782dbee958e73/doc/click.gif
--------------------------------------------------------------------------------
/doc/encAli.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GyverLibs/EncButton/f15946cfa649fa66a39d1ee196a782dbee958e73/doc/encAli.png
--------------------------------------------------------------------------------
/doc/enc_scheme.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GyverLibs/EncButton/f15946cfa649fa66a39d1ee196a782dbee958e73/doc/enc_scheme.png
--------------------------------------------------------------------------------
/doc/enc_type.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GyverLibs/EncButton/f15946cfa649fa66a39d1ee196a782dbee958e73/doc/enc_type.png
--------------------------------------------------------------------------------
/doc/hold.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GyverLibs/EncButton/f15946cfa649fa66a39d1ee196a782dbee958e73/doc/hold.gif
--------------------------------------------------------------------------------
/doc/step.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GyverLibs/EncButton/f15946cfa649fa66a39d1ee196a782dbee958e73/doc/step.gif
--------------------------------------------------------------------------------
/examples/callback/callback.ino:
--------------------------------------------------------------------------------
1 | // опрос событий через функцию-обработчик
2 |
3 | #include
4 | #include
5 |
6 | EncButton eb(2, 3, 4);
7 |
8 | void cb() {
9 | // здесь EB_self - указатель на сам объект
10 |
11 | Serial.print("callback: ");
12 | switch (eb.action()) {
13 | case EB_PRESS:
14 | Serial.println("press");
15 | break;
16 | case EB_HOLD:
17 | Serial.println("hold");
18 | break;
19 | case EB_STEP:
20 | Serial.println("step");
21 | break;
22 | case EB_RELEASE:
23 | Serial.print("release. steps: ");
24 | Serial.print(eb.getSteps());
25 | Serial.print(", press for: ");
26 | Serial.print(eb.pressFor());
27 | Serial.print(", hold for: ");
28 | Serial.print(eb.holdFor());
29 | Serial.print(", step for: ");
30 | Serial.println(eb.stepFor());
31 | break;
32 | case EB_CLICK:
33 | Serial.println("click");
34 | break;
35 | case EB_CLICKS:
36 | Serial.print("clicks ");
37 | Serial.println(eb.getClicks());
38 | break;
39 | case EB_TURN:
40 | Serial.print("turn ");
41 | Serial.print(eb.dir());
42 | Serial.print(" ");
43 | Serial.print(eb.fast());
44 | Serial.print(" ");
45 | Serial.println(eb.pressing());
46 | break;
47 | case EB_REL_HOLD:
48 | Serial.println("release hold");
49 | break;
50 | case EB_REL_HOLD_C:
51 | Serial.print("release hold clicks ");
52 | Serial.println(eb.getClicks());
53 | break;
54 | case EB_REL_STEP:
55 | Serial.println("release step");
56 | break;
57 | case EB_REL_STEP_C:
58 | Serial.print("release step clicks ");
59 | Serial.println(eb.getClicks());
60 | break;
61 | default:
62 | Serial.println();
63 | }
64 | }
65 |
66 | void setup() {
67 | Serial.begin(115200);
68 | eb.attach(cb);
69 | }
70 |
71 | void loop() {
72 | eb.tick();
73 | }
--------------------------------------------------------------------------------
/examples/callback2/callback2.ino:
--------------------------------------------------------------------------------
1 | // опрос событий через функцию-обработчик
2 |
3 | #include
4 | #include
5 |
6 | EncButton eb(2, 3, 4);
7 |
8 | void cb() {
9 | // здесь EB_self - указатель на сам объект
10 |
11 | Serial.print("callback: ");
12 | switch (eb.getAction()) {
13 | case EBAction::Press:
14 | Serial.println("press");
15 | break;
16 | case EBAction::Hold:
17 | Serial.println("hold");
18 | break;
19 | case EBAction::Step:
20 | Serial.println("step");
21 | break;
22 | case EBAction::Release:
23 | Serial.print("release. steps: ");
24 | Serial.print(eb.getSteps());
25 | Serial.print(", press for: ");
26 | Serial.print(eb.pressFor());
27 | Serial.print(", hold for: ");
28 | Serial.print(eb.holdFor());
29 | Serial.print(", step for: ");
30 | Serial.println(eb.stepFor());
31 | break;
32 | case EBAction::Click:
33 | Serial.println("click");
34 | break;
35 | case EBAction::Clicks:
36 | Serial.print("clicks ");
37 | Serial.println(eb.getClicks());
38 | break;
39 | case EBAction::Turn:
40 | Serial.print("turn ");
41 | Serial.print(eb.dir());
42 | Serial.print(" ");
43 | Serial.print(eb.fast());
44 | Serial.print(" ");
45 | Serial.println(eb.pressing());
46 | break;
47 | case EBAction::ReleaseHold:
48 | Serial.println("release hold");
49 | break;
50 | case EBAction::ReleaseHoldClicks:
51 | Serial.print("release hold clicks ");
52 | Serial.println(eb.getClicks());
53 | break;
54 | case EBAction::ReleaseStep:
55 | Serial.println("release step");
56 | break;
57 | case EBAction::ReleaseStepClicks:
58 | Serial.print("release step clicks ");
59 | Serial.println(eb.getClicks());
60 | break;
61 | case EBAction::Timeout:
62 | Serial.println("timeout");
63 | break;
64 | default: break;
65 | }
66 | }
67 |
68 | void setup() {
69 | Serial.begin(115200);
70 | eb.attach(cb);
71 | }
72 |
73 | void loop() {
74 | eb.tick();
75 | }
--------------------------------------------------------------------------------
/examples/demo/demo.ino:
--------------------------------------------------------------------------------
1 | // полное демо
2 | #include
3 | // #define EB_NO_FOR // отключить поддержку pressFor/holdFor/stepFor и счётчик степов (экономит 2 байта оперативки)
4 | // #define EB_NO_CALLBACK // отключить обработчик событий attach (экономит 2 байта оперативки)
5 | // #define EB_NO_COUNTER // отключить счётчик энкодера (экономит 4 байта оперативки)
6 | // #define EB_NO_BUFFER // отключить буферизацию энкодера (экономит 1 байт оперативки)
7 |
8 | // #define EB_DEB_TIME 50 // таймаут гашения дребезга кнопки (кнопка)
9 | // #define EB_CLICK_TIME 500 // таймаут ожидания кликов (кнопка)
10 | // #define EB_HOLD_TIME 600 // таймаут удержания (кнопка)
11 | // #define EB_STEP_TIME 200 // таймаут импульсного удержания (кнопка)
12 | // #define EB_FAST_TIME 30 // таймаут быстрого поворота (энкодер)
13 | // #define EB_TOUT_TIME 1000 // таймаут действия (кнопка и энкодер)
14 |
15 | #include
16 | EncButton eb(2, 3, 4);
17 | // EncButton eb(2, 3, 4, INPUT); // + режим пинов энкодера
18 | // EncButton eb(2, 3, 4, INPUT, INPUT_PULLUP); // + режим пинов кнопки
19 |
20 | void setup() {
21 | Serial.begin(115200);
22 |
23 | // показаны значения по умолчанию
24 | eb.setBtnLevel(LOW);
25 | eb.setClickTimeout(500);
26 | eb.setDebTimeout(50);
27 | eb.setHoldTimeout(600);
28 | eb.setStepTimeout(200);
29 | eb.setTimeout(1000);
30 |
31 | eb.setEncReverse(0);
32 | eb.setEncType(EB_STEP4_LOW);
33 | eb.setFastTimeout(30);
34 |
35 | // сбросить счётчик энкодера
36 | eb.counter = 0;
37 | }
38 |
39 | void loop() {
40 | eb.tick();
41 |
42 | // обработка поворота общая
43 | if (eb.turn()) {
44 | Serial.print("turn: dir ");
45 | Serial.print(eb.dir());
46 | Serial.print(", fast ");
47 | Serial.print(eb.fast());
48 | Serial.print(", hold ");
49 | Serial.print(eb.pressing());
50 | Serial.print(", counter ");
51 | Serial.print(eb.counter);
52 | Serial.print(", clicks ");
53 | Serial.println(eb.getClicks());
54 | }
55 |
56 | // обработка поворота раздельная
57 | if (eb.left()) Serial.println("left");
58 | if (eb.right()) Serial.println("right");
59 | if (eb.leftH()) Serial.println("leftH");
60 | if (eb.rightH()) Serial.println("rightH");
61 |
62 | // кнопка
63 | if (eb.press()) Serial.println("press");
64 | if (eb.click()) Serial.println("click");
65 |
66 | if (eb.release()) {
67 | Serial.println("release");
68 |
69 | Serial.print("clicks: ");
70 | Serial.print(eb.getClicks());
71 | Serial.print(", steps: ");
72 | Serial.print(eb.getSteps());
73 | Serial.print(", press for: ");
74 | Serial.print(eb.pressFor());
75 | Serial.print(", hold for: ");
76 | Serial.print(eb.holdFor());
77 | Serial.print(", step for: ");
78 | Serial.println(eb.stepFor());
79 | }
80 |
81 | // состояния
82 | // Serial.println(eb.pressing());
83 | // Serial.println(eb.holding());
84 | // Serial.println(eb.busy());
85 | // Serial.println(eb.waiting());
86 |
87 | // таймаут
88 | if (eb.timeout()) Serial.println("timeout!");
89 |
90 | // удержание
91 | if (eb.hold()) Serial.println("hold");
92 | if (eb.hold(3)) Serial.println("hold 3");
93 |
94 | // импульсное удержание
95 | if (eb.step()) Serial.println("step");
96 | if (eb.step(3)) Serial.println("step 3");
97 |
98 | // отпущена после импульсного удержания
99 | if (eb.releaseStep()) Serial.println("release step");
100 | if (eb.releaseStep(3)) Serial.println("release step 3");
101 |
102 | // отпущена после удержания
103 | if (eb.releaseHold()) Serial.println("release hold");
104 | if (eb.releaseHold(2)) Serial.println("release hold 2");
105 |
106 | // проверка на количество кликов
107 | if (eb.hasClicks(3)) Serial.println("has 3 clicks");
108 |
109 | // вывести количество кликов
110 | if (eb.hasClicks()) {
111 | Serial.print("has clicks: ");
112 | Serial.println(eb.getClicks());
113 | }
114 | }
--------------------------------------------------------------------------------
/examples/double/double.ino:
--------------------------------------------------------------------------------
1 | // опрос одновременного нажатия двух кнопок как нажатия третьей кнопки (виртуальной)
2 | // библиотека сама сбросит события с первых двух кнопок, если они нажаты вместе
3 |
4 | #include
5 | #include
6 |
7 | Button b0(4);
8 | Button b1(5);
9 | VirtButton b2; // виртуальная
10 |
11 | void setup() {
12 | Serial.begin(115200);
13 | }
14 |
15 | void loop() {
16 | b0.tick();
17 | b1.tick();
18 |
19 | // обработка одновременного нажатия двух кнопок
20 | b2.tick(b0, b1);
21 |
22 | if (b0.click()) Serial.println("b0 click");
23 | if (b1.click()) Serial.println("b1 click");
24 |
25 | if (b2.click()) Serial.println("b0+b1 click");
26 | if (b2.step()) Serial.println("b0+b1 step");
27 | }
--------------------------------------------------------------------------------
/examples/doubleCallback/doubleCallback.ino:
--------------------------------------------------------------------------------
1 | // опрос одновременного нажатия двух кнопок как нажатия третьей кнопки
2 | // с корректным вызовом обработчиков
3 |
4 | #include
5 | #include
6 |
7 | Button b0(4);
8 | Button b1(5);
9 | MultiButton b12; // виртуальная
10 |
11 | void decode(uint16_t action) {
12 | switch (action) {
13 | case EB_PRESS:
14 | Serial.println("press");
15 | break;
16 | case EB_STEP:
17 | Serial.println("step");
18 | break;
19 | case EB_RELEASE:
20 | Serial.println("release");
21 | break;
22 | case EB_CLICK:
23 | Serial.println("click");
24 | break;
25 | case EB_CLICKS:
26 | Serial.println("clicks");
27 | break;
28 | case EB_REL_HOLD:
29 | Serial.println("release hold");
30 | break;
31 | case EB_REL_HOLD_C:
32 | Serial.println("release hold clicks ");
33 | break;
34 | case EB_REL_STEP:
35 | Serial.println("release step");
36 | break;
37 | case EB_REL_STEP_C:
38 | Serial.println("release step clicks ");
39 | break;
40 | case EB_TIMEOUT:
41 | Serial.println("timeout");
42 | break;
43 | }
44 | }
45 |
46 | void setup() {
47 | Serial.begin(115200);
48 |
49 | // обработчики
50 | b0.attach([]() {
51 | uint16_t action = static_cast(EB_self)->action();
52 | if (action != EB_HOLD) Serial.print("b0: ");
53 | decode(action);
54 | });
55 |
56 | b1.attach([]() {
57 | uint16_t action = static_cast(EB_self)->action();
58 | if (action != EB_HOLD) Serial.print("b1: ");
59 | decode(action);
60 | });
61 |
62 | b12.attach([]() {
63 | uint16_t action = static_cast(EB_self)->action();
64 | if (action != EB_HOLD) Serial.print("b0+b1: ");
65 | decode(action);
66 | });
67 | }
68 |
69 | void loop() {
70 | // обработка одновременного нажатия двух кнопок
71 | // обрабатываются все три кнопки
72 | b12.tick(b0, b1);
73 |
74 | // или вручную
75 | if (b0.click()) Serial.println("b0 click");
76 | if (b1.click()) Serial.println("b1 click");
77 | if (b12.click()) Serial.println("b0+b1 click");
78 | }
--------------------------------------------------------------------------------
/examples/isr/isr.ino:
--------------------------------------------------------------------------------
1 | // энкодер и прерывания
2 | #include
3 | #include
4 | EncButton eb(2, 3, 4);
5 |
6 | /*
7 | // esp8266/esp32
8 | IRAM_ATTR void isr() {
9 | eb.tickISR();
10 | }
11 | */
12 |
13 | void isr() {
14 | eb.tickISR();
15 | }
16 |
17 | void setup() {
18 | Serial.begin(115200);
19 | attachInterrupt(0, isr, CHANGE);
20 | attachInterrupt(1, isr, CHANGE);
21 | eb.setEncISR(true);
22 | }
23 |
24 | void loop() {
25 | eb.tick();
26 |
27 | if (eb.turn()) {
28 | Serial.print("turn: dir ");
29 | Serial.print(eb.dir());
30 | Serial.print(", fast ");
31 | Serial.print(eb.fast());
32 | Serial.print(", hold ");
33 | Serial.print(eb.pressing());
34 | Serial.print(", counter ");
35 | Serial.println(eb.counter);
36 | }
37 |
38 | delay(100); // имитация загруженной программы
39 | }
--------------------------------------------------------------------------------
/examples/one_button_3_var/one_button_3_var.ino:
--------------------------------------------------------------------------------
1 | #include
2 | // используем одну КНОПКУ для удобного изменения трёх переменных
3 | // первая - один клик, затем удержание (нажал-отпустил-нажал-держим)
4 | // вторая - два клика, затем удержание
5 | // третья - три клика, затем удержание
6 | // смотри монитор порта
7 |
8 | #include
9 | Button btn(4);
10 |
11 | // переменные для изменения
12 | int val_a, val_b, val_c;
13 |
14 | // шаги изменения (signed)
15 | int8_t step_a = 1;
16 | int8_t step_b = 5;
17 | int8_t step_c = 10;
18 |
19 | void setup() {
20 | Serial.begin(115200);
21 | }
22 |
23 | void loop() {
24 | btn.tick();
25 |
26 | // передаём количество предварительных кликов
27 | if (btn.step(1)) {
28 | val_a += step_a;
29 | Serial.print("val_a: ");
30 | Serial.println(val_a);
31 | }
32 | if (btn.step(2)) {
33 | val_b += step_b;
34 | Serial.print("val_b: ");
35 | Serial.println(val_b);
36 | }
37 | if (btn.step(3)) {
38 | val_c += step_c;
39 | Serial.print("val_c: ");
40 | Serial.println(val_c);
41 | }
42 |
43 | // разворачиваем шаг для изменения в обратную сторону
44 | // передаём количество предварительных кликов
45 | if (btn.releaseStep(1)) step_a = -step_a;
46 | if (btn.releaseStep(2)) step_b = -step_b;
47 | if (btn.releaseStep(3)) step_c = -step_c;
48 | }
--------------------------------------------------------------------------------
/examples/one_enc_3_var/one_enc_3_var.ino:
--------------------------------------------------------------------------------
1 | // управление тремя переменными при помощи энкодера:
2 | // - нащёлкай кнопкой нужную переменную (1, 2 или 3 клика)
3 | // - 1 переменная просто изменяется с постоянным шагом
4 | // - 2 переменная: шаг 1, при зажатой кнопке - шаг 5
5 | // - 3 переменная: шаг 1, при быстром вращении - шаг 5
6 |
7 | #include
8 | #include
9 | EncButton eb(2, 3, 4);
10 |
11 | int var1 = 0;
12 | int var2 = 0;
13 | int var3 = 0;
14 | uint8_t select = 1; // выбранная переменная
15 |
16 | void setup() {
17 | Serial.begin(115200);
18 | }
19 |
20 | void loop() {
21 | eb.tick();
22 |
23 | // выбор переменной для изменения
24 | if (eb.hasClicks()) {
25 | select = eb.getClicks();
26 | Serial.println(String("Select: ") + select);
27 | }
28 |
29 | if (eb.turn()) {
30 | // меняем переменную
31 | switch (select) {
32 | case 1:
33 | // изменение с шагом 5
34 | var1 += 5 * eb.dir();
35 | break;
36 | case 2:
37 | // изменение с шагом 1, при зажатой кнопке шаг 5
38 | var2 += (eb.pressing() ? 5 : 1) * eb.dir();
39 | break;
40 | case 3:
41 | // изменение с шагом 1, при быстром вращении шаг 5
42 | var3 += (eb.fast() ? 5 : 1) * eb.dir();
43 | break;
44 | }
45 | Serial.println(String("vars ") + var1 + ',' + var2 + ',' + var3);
46 | }
47 | }
--------------------------------------------------------------------------------
/examples/virtual_buttons/virtual_AnalogKey/virtual_AnalogKey.ino:
--------------------------------------------------------------------------------
1 | // пример работы в виртуальном режиме совместно с библиотекой AnalogKey
2 | // https://github.com/GyverLibs/AnalogKey
3 |
4 | #include
5 | VirtButton btn0;
6 | VirtButton btn1;
7 |
8 | #include
9 | // создаём массив значений сигналов с кнопок
10 | int16_t sigs[16] = {
11 | 1023, 927, 856, 783,
12 | 671, 632, 590, 560,
13 | 504, 480, 455, 440,
14 | 399, 319, 255, 230
15 | };
16 |
17 | // указываем пин, количество кнопок и массив значений
18 | AnalogKey keys;
19 |
20 | void setup() {
21 | Serial.begin(9600);
22 | }
23 |
24 | void loop() {
25 | btn0.tick(keys.status(0));
26 | btn1.tick(keys.status(1));
27 |
28 | // забираем действия с кнопок
29 | if (btn0.click()) Serial.println("click 0");
30 | if (btn0.hold()) Serial.println("hold 0");
31 |
32 | if (btn1.press()) Serial.println("press 1");
33 | if (btn1.step()) Serial.println("step 1");
34 | }
35 |
--------------------------------------------------------------------------------
/examples/virtual_buttons/virtual_SimpleKeypad/virtual_SimpleKeypad.ino:
--------------------------------------------------------------------------------
1 | // пример работы в виртуальном режиме совместно с библиотекой SimpleKeypad
2 | // https://github.com/maximebohrer/SimpleKeypad
3 |
4 | #include
5 | VirtButton btn0;
6 | VirtButton btn1;
7 |
8 | // пины подключения (по порядку штекера)
9 | byte colPins[] = {7, 6, 5, 4};
10 | byte rowPins[] = {11, 10, 9, 8};
11 |
12 | // массив имён кнопок
13 | char keys[4][4] = {
14 | {'1', '2', '3', 'A'},
15 | {'4', '5', '6', 'B'},
16 | {'7', '8', '9', 'C'},
17 | {'*', '0', '#', 'D'}
18 | };
19 |
20 | #include
21 | SimpleKeypad pad((char*)keys, rowPins, colPins, 4, 4);
22 |
23 | void setup() {
24 | Serial.begin(9600);
25 | btn0.setDebTimeout(0);
26 | btn1.setDebTimeout(0);
27 | }
28 |
29 | void loop() {
30 | btn0.tick(0);
31 | btn1.tick(0);
32 |
33 | // тикаем все кнопки, передавая сравнение с кодом кнопки в цикле
34 | // делаем это по таймеру, чтобы не опрашивать клавиатуру постоянно
35 | static uint32_t tmr;
36 | if (millis() - tmr >= 10) {
37 | tmr = millis();
38 | char key = pad.scan();
39 | btn0.tick(key == '1');
40 | btn1.tick(key == '2');
41 | }
42 |
43 | // забираем действия с кнопок
44 | if (btn0.click()) Serial.println("click 0");
45 | if (btn0.hold()) Serial.println("hold 0");
46 |
47 | if (btn1.press()) Serial.println("press 1");
48 | if (btn1.step()) Serial.println("step 1");
49 | }
50 |
--------------------------------------------------------------------------------
/examples/virtual_buttons/virtual_SimpleKeypad_array/virtual_SimpleKeypad_array.ino:
--------------------------------------------------------------------------------
1 | // пример работы в виртуальном режиме совместно с библиотекой SimpleKeypad
2 | // https://github.com/maximebohrer/SimpleKeypad
3 | // передаём EncButton сразу всю клавиатуру через массивы и циклы
4 |
5 | #include
6 | VirtButton btn[16];
7 |
8 | // пины подключения (по порядку штекера)
9 | byte colPins[] = {7, 6, 5, 4};
10 | byte rowPins[] = {11, 10, 9, 8};
11 |
12 | // массив имён кнопок
13 | char keys[4][4] = {
14 | {'1', '2', '3', 'A'},
15 | {'4', '5', '6', 'B'},
16 | {'7', '8', '9', 'C'},
17 | {'*', '0', '#', 'D'}
18 | };
19 |
20 | #include
21 | SimpleKeypad pad((char*)keys, rowPins, colPins, 4, 4);
22 |
23 | void setup() {
24 | Serial.begin(9600);
25 | for (int i = 0; i < 16; i++) btn[i].setDebTimeout(0);
26 | }
27 |
28 | void loop() {
29 | for (int i = 0; i < 16; i++) btn[i].tick(0);
30 |
31 | // массово тикаем все кнопки, передавая сравнение с кодом кнопки в цикле
32 | // делаем это по таймеру, чтобы не опрашивать клавиатуру постоянно
33 | static uint32_t tmr;
34 | if (millis() - tmr >= 10) {
35 | tmr = millis();
36 | char key = pad.scan();
37 | char* keysPtr = (char*)keys; // указатель для удобства опроса
38 | for (int i = 0; i < 16; i++) btn[i].tick(key == keysPtr[i]);
39 | }
40 |
41 | // забираем действия с кнопок
42 | if (btn[0].click()) Serial.println("click 0");
43 | if (btn[0].hold()) Serial.println("hold 0");
44 |
45 | if (btn[1].press()) Serial.println("press 1");
46 | if (btn[1].step()) Serial.println("step 1");
47 | }
48 |
--------------------------------------------------------------------------------
/keywords.txt:
--------------------------------------------------------------------------------
1 | #######################################
2 | # Syntax Coloring Map For EncButton
3 | #######################################
4 |
5 | #######################################
6 | # Datatypes (KEYWORD1)
7 | #######################################
8 | EncButton KEYWORD1
9 | EncButtonT KEYWORD1
10 | VirtEncButton KEYWORD1
11 | Button KEYWORD1
12 | ButtonT KEYWORD1
13 | VirtEncoder KEYWORD1
14 | VirtButton KEYWORD1
15 | MultiButton KEYWORD1
16 |
17 | EB_NO_COUNTER KEYWORD1
18 | EB_NO_BUFFER KEYWORD1
19 | EB_NO_CALLBACK KEYWORD1
20 | EB_NO_FOR KEYWORD1
21 |
22 | EB_DEB_TIME KEYWORD1
23 | EB_CLICK_TIME KEYWORD1
24 | EB_HOLD_TIME KEYWORD1
25 | EB_STEP_TIME KEYWORD1
26 | EB_FAST_TIME KEYWORD1
27 |
28 | #######################################
29 | # Methods and Functions (KEYWORD2)
30 | #######################################
31 |
32 | EB_read KEYWORD2
33 | EB_uptime KEYWORD2
34 | EB_mode KEYWORD2
35 |
36 | setHoldTimeout KEYWORD2
37 | setStepTimeout KEYWORD2
38 | setClickTimeout KEYWORD2
39 | setDebTimeout KEYWORD2
40 | setTimeout KEYWORD2
41 | setBtnLevel KEYWORD2
42 | reset KEYWORD2
43 | clear KEYWORD2
44 | press KEYWORD2
45 | release KEYWORD2
46 | click KEYWORD2
47 | pressing KEYWORD2
48 | hold KEYWORD2
49 | holding KEYWORD2
50 | step KEYWORD2
51 | hasClicks KEYWORD2
52 | getClicks KEYWORD2
53 | getSteps KEYWORD2
54 | releaseHold KEYWORD2
55 | releaseStep KEYWORD2
56 | releaseHoldStep KEYWORD2
57 | timeout KEYWORD2
58 | timeoutState KEYWORD2
59 | waiting KEYWORD2
60 | busy KEYWORD2
61 | action KEYWORD2
62 | getAction KEYWORD2
63 | attach KEYWORD2
64 | detach KEYWORD2
65 | pressISR KEYWORD2
66 | pressFor KEYWORD2
67 | holdFor KEYWORD2
68 | stepFor KEYWORD2
69 |
70 | setEncReverse KEYWORD2
71 | setEncType KEYWORD2
72 | initEnc KEYWORD2
73 |
74 | setFastTimeout KEYWORD2
75 | setEncISR KEYWORD2
76 | turn KEYWORD2
77 | turnH KEYWORD2
78 | right KEYWORD2
79 | left KEYWORD2
80 | rightH KEYWORD2
81 | leftH KEYWORD2
82 | encHolding KEYWORD2
83 | dir KEYWORD2
84 | fast KEYWORD2
85 |
86 | pollEnc KEYWORD2
87 | init KEYWORD2
88 | tickRaw KEYWORD2
89 | tickISR KEYWORD2
90 | tick KEYWORD2
91 | readBtn KEYWORD2
92 | readEnc KEYWORD2
93 |
94 | #######################################
95 | # Constants (LITERAL1)
96 | #######################################
97 | EB_PRESS LITERAL1
98 | EB_HOLD LITERAL1
99 | EB_STEP LITERAL1
100 | EB_RELEASE LITERAL1
101 | EB_CLICK LITERAL1
102 | EB_CLICKS LITERAL1
103 | EB_TURN LITERAL1
104 | EB_REL_HOLD LITERAL1
105 | EB_REL_HOLD_C LITERAL1
106 | EB_REL_STEP LITERAL1
107 | EB_REL_STEP_C LITERAL1
108 | EB_TIMEOUT LITERAL1
109 |
110 | EB_STEP4_LOW LITERAL1
111 | EB_STEP4_HIGH LITERAL1
112 | EB_STEP2 LITERAL1
113 | EB_STEP1 LITERAL1
114 |
115 | None LITERAL1
116 | Press LITERAL1
117 | Hold LITERAL1
118 | Step LITERAL1
119 | Release LITERAL1
120 | Click LITERAL1
121 | Clicks LITERAL1
122 | Turn LITERAL1
123 | ReleaseHold LITERAL1
124 | ReleaseHoldClicks LITERAL1
125 | ReleaseStep LITERAL1
126 | ReleaseStepClicks LITERAL1
127 | Timeout LITERAL1
--------------------------------------------------------------------------------
/library.properties:
--------------------------------------------------------------------------------
1 | name=EncButton
2 | version=3.7.3
3 | author=AlexGyver
4 | maintainer=AlexGyver
5 | sentence=Light and powerful library for button and encoder operation for Arduino
6 | paragraph=Debounce, click count, hold, step hold mode and many more. Maximum possibilities for button and encoder
7 | category=Sensors
8 | url=https://github.com/GyverLibs/EncButton
9 | architectures=*
10 | depends=GyverIO
--------------------------------------------------------------------------------
/src/EncButton.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 |
4 | #include "core/Button.h"
5 | #include "core/EncButton.h"
6 | #include "core/Encoder.h"
7 | #include "core/MultiButton.h"
8 | #include "core/VirtButton.h"
9 | #include "core/VirtEncButton.h"
10 | #include "core/VirtEncoder.h"
--------------------------------------------------------------------------------
/src/core/Button.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 |
4 | #include "VirtButton.h"
5 | #include "io.h"
6 |
7 | // ============= VAR PIN =============
8 | class Button : public VirtButton {
9 | public:
10 | Button(uint8_t npin = 0, uint8_t mode = INPUT_PULLUP, uint8_t btnLevel = LOW) {
11 | init(npin, mode, btnLevel);
12 | }
13 |
14 | // указать пин и его режим работы
15 | void init(uint8_t npin = 0, uint8_t mode = INPUT_PULLUP, uint8_t btnLevel = LOW) {
16 | pin = npin;
17 | EB_mode(pin, mode);
18 | setBtnLevel(btnLevel);
19 | }
20 |
21 | // прочитать текущее значение кнопки (без дебаунса)
22 | bool read() {
23 | return EB_read(pin) ^ bf.read(EB_INV);
24 | }
25 |
26 | // функция обработки, вызывать в loop
27 | bool tick() {
28 | return VirtButton::tick(EB_read(pin));
29 | }
30 |
31 | // обработка кнопки без сброса событий и вызова коллбэка
32 | bool tickRaw() {
33 | return VirtButton::tickRaw(EB_read(pin));
34 | }
35 |
36 | private:
37 | uint8_t pin;
38 | };
39 |
40 | // ============= TEMPLATE PIN =============
41 | template
42 | class ButtonT : public VirtButton {
43 | public:
44 | ButtonT(uint8_t mode = INPUT_PULLUP, uint8_t btnLevel = LOW) {
45 | init(mode, btnLevel);
46 | }
47 |
48 | // указать режим работы пина
49 | void init(uint8_t mode = INPUT_PULLUP, uint8_t btnLevel = LOW) {
50 | EB_mode(PIN, mode);
51 | setBtnLevel(btnLevel);
52 | }
53 |
54 | // прочитать текущее значение кнопки (без дебаунса)
55 | bool read() {
56 | return EB_read(PIN) ^ bf.read(EB_INV);
57 | }
58 |
59 | // функция обработки, вызывать в loop
60 | bool tick() {
61 | return VirtButton::tick(EB_read(PIN));
62 | }
63 |
64 | // обработка кнопки без сброса событий и вызова коллбэка
65 | bool tickRaw() {
66 | return VirtButton::tickRaw(EB_read(PIN));
67 | }
68 | };
--------------------------------------------------------------------------------
/src/core/EncButton.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 |
4 | #include "VirtEncButton.h"
5 | #include "io.h"
6 |
7 | // ===================== CLASS =====================
8 | class EncButton : public VirtEncButton {
9 | public:
10 | // настроить пины (энк, энк, кнопка, pinmode энк, pinmode кнопка)
11 | 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) {
12 | init(encA, encB, btn, modeEnc, modeBtn, btnLevel);
13 | }
14 |
15 | // настроить пины (энк, энк, кнопка, pinmode энк, pinmode кнопка)
16 | 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) {
17 | e0 = encA;
18 | e1 = encB;
19 | b = btn;
20 | EB_mode(e0, modeEnc);
21 | EB_mode(e1, modeEnc);
22 | EB_mode(b, modeBtn);
23 | setBtnLevel(btnLevel);
24 | initEnc(EB_read(e0), EB_read(e1));
25 | }
26 |
27 | // ====================== TICK ======================
28 | // функция обработки для вызова в прерывании энкодера
29 | int8_t tickISR() {
30 | return VirtEncButton::tickISR(EB_read(e0), EB_read(e1));
31 | }
32 |
33 | // функция обработки, вызывать в loop
34 | bool tick() {
35 | return ef.read(EB_EISR) ? VirtEncButton::tick(EB_read(b)) : VirtEncButton::tick(EB_read(e0), EB_read(e1), EB_read(b));
36 | }
37 |
38 | // функция обработки без сброса событий
39 | bool tickRaw() {
40 | return ef.read(EB_EISR) ? VirtEncButton::tickRaw(EB_read(b)) : VirtEncButton::tickRaw(EB_read(e0), EB_read(e1), EB_read(b));
41 | }
42 |
43 | // ====================== READ ======================
44 | // прочитать значение кнопки
45 | bool readBtn() {
46 | return EB_read(b) ^ bf.read(EB_INV);
47 | }
48 |
49 | // ===================== PRIVATE =====================
50 | private:
51 | uint8_t e0, e1, b;
52 | };
53 |
54 | // ===================== T CLASS =====================
55 | template
56 | class EncButtonT : public VirtEncButton {
57 | public:
58 | // настроить пины (энк, энк, кнопка, pinmode энк, pinmode кнопка)
59 | EncButtonT(uint8_t modeEnc = INPUT, uint8_t modeBtn = INPUT_PULLUP, uint8_t btnLevel = LOW) {
60 | init(modeEnc, modeBtn, btnLevel);
61 | }
62 |
63 | // настроить пины (pinmode энк, pinmode кнопка)
64 | void init(uint8_t modeEnc = INPUT, uint8_t modeBtn = INPUT_PULLUP, uint8_t btnLevel = LOW) {
65 | EB_mode(ENCA, modeEnc);
66 | EB_mode(ENCB, modeEnc);
67 | EB_mode(BTN, modeBtn);
68 | setBtnLevel(btnLevel);
69 | initEnc(EB_read(ENCA), EB_read(ENCB));
70 | }
71 |
72 | // ====================== TICK ======================
73 | // функция обработки для вызова в прерывании энкодера
74 | int8_t tickISR() {
75 | return VirtEncButton::tickISR(EB_read(ENCA), EB_read(ENCB));
76 | }
77 |
78 | // функция обработки, вызывать в loop
79 | bool tick() {
80 | return ef.read(EB_EISR) ? VirtEncButton::tick(EB_read(BTN)) : VirtEncButton::tick(EB_read(ENCA), EB_read(ENCB), EB_read(BTN));
81 | }
82 |
83 | // функция обработки без сброса событий
84 | bool tickRaw() {
85 | return ef.read(EB_EISR) ? VirtEncButton::tickRaw(EB_read(BTN)) : VirtEncButton::tickRaw(EB_read(ENCA), EB_read(ENCB), EB_read(BTN));
86 | }
87 |
88 | // ====================== READ ======================
89 | // прочитать значение кнопки
90 | bool readBtn() {
91 | return EB_read(BTN) ^ bf.read(EB_INV);
92 | }
93 | };
--------------------------------------------------------------------------------
/src/core/Encoder.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 |
4 | #include "VirtEncoder.h"
5 | #include "io.h"
6 |
7 | // ============= VAR PIN =============
8 | class Encoder : public VirtEncoder {
9 | public:
10 | // указать пины и их режим работы
11 | Encoder(uint8_t encA = 0, uint8_t encB = 0, uint8_t mode = INPUT) {
12 | init(encA, encB, mode);
13 | }
14 |
15 | // указать пины и их режим работы
16 | void init(uint8_t encA = 0, uint8_t encB = 0, uint8_t mode = INPUT) {
17 | e0 = encA;
18 | e1 = encB;
19 | EB_mode(e0, mode);
20 | EB_mode(e1, mode);
21 | initEnc(EB_read(e0), EB_read(e1));
22 | }
23 |
24 | // функция обработки для вызова в прерывании энкодера
25 | int8_t tickISR() {
26 | return VirtEncoder::tickISR(EB_read(e0), EB_read(e1));
27 | }
28 |
29 | // функция обработки, вызывать в loop
30 | int8_t tick() {
31 | return ef.read(EB_EISR) ? VirtEncoder::tick() : VirtEncoder::tick(EB_read(e0), EB_read(e1));
32 | }
33 |
34 | // обработка без сброса события поворота
35 | int8_t tickRaw() {
36 | return ef.read(EB_EISR) ? VirtEncoder::tickRaw() : VirtEncoder::tickRaw(EB_read(e0), EB_read(e1));
37 | }
38 |
39 | private:
40 | uint8_t e0, e1;
41 | };
42 |
43 | // ============= TEMPLATE PIN =============
44 | template
45 | class EncoderT : public VirtEncoder {
46 | public:
47 | // указать режим работы пинов
48 | EncoderT(uint8_t mode = INPUT) {
49 | init(mode);
50 | }
51 |
52 | // указать режим работы пинов
53 | void init(uint8_t mode = INPUT) {
54 | EB_mode(ENCA, mode);
55 | EB_mode(ENCB, mode);
56 | initEnc(EB_read(ENCA), EB_read(ENCB));
57 | }
58 |
59 | // функция обработки для вызова в прерывании энкодера
60 | int8_t tickISR() {
61 | return VirtEncoder::tickISR(EB_read(ENCA), EB_read(ENCB));
62 | }
63 |
64 | // функция обработки, вызывать в loop
65 | int8_t tick() {
66 | return ef.read(EB_EISR) ? VirtEncoder::tick() : VirtEncoder::tick(EB_read(ENCA), EB_read(ENCB));
67 | }
68 |
69 | // обработка без сброса события поворота
70 | int8_t tickRaw() {
71 | return ef.read(EB_EISR) ? VirtEncoder::tickRaw() : VirtEncoder::tickRaw(EB_read(ENCA), EB_read(ENCB));
72 | }
73 | };
--------------------------------------------------------------------------------
/src/core/MultiButton.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 |
4 | #include "VirtButton.h"
5 |
6 | class MultiButton : public VirtButton {
7 | public:
8 | template
9 | bool tick(T0& b0, T1& b1) {
10 | b0.clear();
11 | b1.clear();
12 | b0.tickRaw();
13 | b1.tickRaw();
14 |
15 | if (bf.read(EB_BOTH)) {
16 | if (!b0.pressing() && !b1.pressing()) bf.clear(EB_BOTH);
17 | if (!b0.pressing()) b0.reset();
18 | if (!b1.pressing()) b1.reset();
19 | b0.clear();
20 | b1.clear();
21 | return VirtButton::tick(true);
22 | } else {
23 | if (b0.pressing() && b1.pressing()) bf.set(EB_BOTH);
24 | b0.call();
25 | b1.call();
26 | return VirtButton::tick(false);
27 | }
28 | }
29 | };
--------------------------------------------------------------------------------
/src/core/VirtButton.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 |
4 | #include "flags.h"
5 | #include "io.h"
6 | #include "self.h"
7 |
8 | #ifndef __AVR__
9 | #include
10 | #endif
11 |
12 | // ===================== FLAGS ======================
13 | #define EB_PRESS (1 << 0) // нажатие на кнопку
14 | #define EB_HOLD (1 << 1) // кнопка удержана
15 | #define EB_STEP (1 << 2) // импульсное удержание
16 | #define EB_RELEASE (1 << 3) // кнопка отпущена
17 | #define EB_CLICK (1 << 4) // одиночный клик
18 | #define EB_CLICKS (1 << 5) // сигнал о нескольких кликах
19 | #define EB_TURN (1 << 6) // поворот энкодера
20 | #define EB_REL_HOLD (1 << 7) // кнопка отпущена после удержания
21 | #define EB_REL_HOLD_C (1 << 8) // кнопка отпущена после удержания с предв. кликами
22 | #define EB_REL_STEP (1 << 9) // кнопка отпущена после степа
23 | #define EB_REL_STEP_C (1 << 10) // кнопка отпущена после степа с предв. кликами
24 | #define EB_TIMEOUT (1 << 11) // прошёл таймаут после нажатия кнопки или поворота энкодера
25 |
26 | enum class EBAction {
27 | None = 0,
28 | Press = EB_PRESS,
29 | Hold = EB_HOLD,
30 | Step = EB_STEP,
31 | Release = EB_RELEASE,
32 | Click = EB_CLICK,
33 | Clicks = EB_CLICKS,
34 | Turn = EB_TURN,
35 | ReleaseHold = EB_REL_HOLD,
36 | ReleaseHoldClicks = EB_REL_HOLD_C,
37 | ReleaseStep = EB_REL_STEP,
38 | ReleaseStepClicks = EB_REL_STEP_C,
39 | Timeout = EB_TIMEOUT,
40 | };
41 |
42 | // =================== TOUT BUILD ===================
43 | #define EB_SHIFT 4
44 |
45 | // таймаут антидребезга, мс
46 | #ifdef EB_DEB_TIME
47 | #define EB_DEB_T (EB_DEB_TIME)
48 | #else
49 | #endif
50 |
51 | // таймаут между клтками, мс
52 | #ifdef EB_CLICK_TIME
53 | #define EB_CLICK_T (EB_CLICK_TIME)
54 | #define EB_GET_CLICK_TIME() ((uint16_t)EB_CLICK_T)
55 | #else
56 | #define EB_GET_CLICK_TIME() (uint16_t)(EB_CLICK_T << EB_SHIFT)
57 | #endif
58 |
59 | // таймаут удержания, мс
60 | #ifdef EB_HOLD_TIME
61 | #define EB_HOLD_T (EB_HOLD_TIME)
62 | #define EB_GET_HOLD_TIME() ((uint16_t)EB_HOLD_T)
63 | #else
64 | #define EB_GET_HOLD_TIME() (uint16_t)(EB_HOLD_T << EB_SHIFT)
65 | #endif
66 |
67 | // период степа, мс
68 | #ifdef EB_STEP_TIME
69 | #define EB_STEP_T (EB_STEP_TIME)
70 | #define EB_GET_STEP_TIME() ((uint16_t)EB_STEP_T)
71 | #else
72 | #define EB_GET_STEP_TIME() (uint16_t)(EB_STEP_T << EB_SHIFT)
73 | #endif
74 |
75 | // время таймаута, мс
76 | #ifdef EB_TOUT_TIME
77 | #define EB_TOUT_T (EB_TOUT_TIME)
78 | #define EB_GET_TOUT_TIME() ((uint16_t)EB_TOUT_T)
79 | #else
80 | #define EB_GET_TOUT_TIME() (uint16_t)(EB_TOUT_T << EB_SHIFT)
81 | #endif
82 |
83 | // =================== PACK FLAGS ===================
84 | #define EB_CLKS_R (1 << 0)
85 | #define EB_PRS_R (1 << 1)
86 | #define EB_HLD_R (1 << 2)
87 | #define EB_STP_R (1 << 3)
88 | #define EB_REL_R (1 << 4)
89 |
90 | #define EB_PRS (1 << 5)
91 | #define EB_HLD (1 << 6)
92 | #define EB_STP (1 << 7)
93 | #define EB_REL (1 << 8)
94 |
95 | #define EB_BUSY (1 << 9)
96 | #define EB_DEB (1 << 10)
97 | #define EB_TOUT (1 << 11)
98 | #define EB_INV (1 << 12)
99 | #define EB_BOTH (1 << 13)
100 | #define EB_BISR (1 << 14)
101 |
102 | #define EB_EHLD (1 << 15)
103 |
104 | // базовый класс кнопки
105 | class VirtButton {
106 | #ifdef __AVR__
107 | typedef void (*ActionHandler)();
108 | #else
109 | typedef std::function ActionHandler;
110 | #endif
111 |
112 | public:
113 | // ====================== SET ======================
114 | // установить таймаут удержания, умолч. 600 (макс. 4000 мс)
115 | void setHoldTimeout(const uint16_t tout) {
116 | #ifndef EB_HOLD_TIME
117 | EB_HOLD_T = tout >> EB_SHIFT;
118 | #endif
119 | }
120 |
121 | // установить таймаут импульсного удержания, умолч. 200 (макс. 4000 мс)
122 | void setStepTimeout(const uint16_t tout) {
123 | #ifndef EB_STEP_TIME
124 | EB_STEP_T = tout >> EB_SHIFT;
125 | #endif
126 | }
127 |
128 | // установить таймаут ожидания кликов, умолч. 500 (макс. 4000 мс)
129 | void setClickTimeout(const uint16_t tout) {
130 | #ifndef EB_CLICK_TIME
131 | EB_CLICK_T = tout >> EB_SHIFT;
132 | #endif
133 | }
134 |
135 | // установить таймаут антидребезга, умолч. 50 (макс. 255 мс)
136 | void setDebTimeout(const uint8_t tout) {
137 | #ifndef EB_DEB_TIME
138 | EB_DEB_T = tout;
139 | #endif
140 | }
141 |
142 | // установить время таймаута, умолч. 1000 (макс. 4000 мс)
143 | void setTimeout(const uint16_t tout) {
144 | #ifndef EB_TOUT_TIME
145 | EB_TOUT_T = tout >> EB_SHIFT;
146 | #endif
147 | }
148 |
149 | // установить уровень кнопки (HIGH - кнопка замыкает VCC, LOW - замыкает GND)
150 | void setBtnLevel(const bool level) {
151 | bf.write(EB_INV, !level);
152 | }
153 |
154 | // кнопка нажата в прерывании (не учитывает btnLevel!)
155 | void pressISR() {
156 | if (!bf.read(EB_DEB)) tmr = EB_uptime();
157 | bf.set(EB_DEB | EB_BISR);
158 | }
159 |
160 | // сбросить системные флаги (принудительно закончить обработку)
161 | void reset() {
162 | clicks = 0;
163 | bf.clear(~EB_INV); // все кроме EB_INV
164 | }
165 |
166 | // принудительно сбросить флаги событий
167 | void clear(bool resetTout = false) {
168 | if (resetTout && bf.read(EB_TOUT)) bf.clear(EB_TOUT);
169 | if (bf.read(EB_CLKS_R)) clicks = 0;
170 | if (bf.read(EB_CLKS_R | EB_STP_R | EB_PRS_R | EB_HLD_R | EB_REL_R)) {
171 | bf.clear(EB_CLKS_R | EB_STP_R | EB_PRS_R | EB_HLD_R | EB_REL_R);
172 | }
173 | }
174 |
175 | // игнорировать все события до отпускания кнопки
176 | void skipEvents() {
177 | bf.set(EB_EHLD);
178 | }
179 |
180 | // подключить функцию-обработчик событий (вида void f())
181 | void attach(ActionHandler handler) {
182 | #ifndef EB_NO_CALLBACK
183 | cb = handler;
184 | #else
185 | (void)handler;
186 | #endif
187 | }
188 |
189 | // отключить функцию-обработчик событий
190 | void detach() {
191 | #ifndef EB_NO_CALLBACK
192 | cb = nullptr;
193 | #endif
194 | }
195 |
196 | // ====================== GET ======================
197 | // кнопка нажата [событие]
198 | bool press() {
199 | return bf.read(EB_PRS_R);
200 | }
201 |
202 | // кнопка нажата с предварительными кликами [событие]
203 | bool press(const uint8_t num) {
204 | return (clicks == num) && press();
205 | }
206 |
207 | // кнопка отпущена (в любом случае) [событие]
208 | bool release() {
209 | return bf.eq(EB_REL_R | EB_REL, EB_REL_R | EB_REL);
210 | }
211 |
212 | // кнопка отпущена (в любом случае) с предварительными кликами [событие]
213 | bool release(const uint8_t num) {
214 | return (clicks == num) && release();
215 | }
216 |
217 | // клик по кнопке (отпущена без удержания) [событие]
218 | bool click() {
219 | return bf.eq(EB_REL_R | EB_REL | EB_HLD, EB_REL_R);
220 | }
221 |
222 | // клик по кнопке (отпущена без удержания) с предварительными кликами [событие]
223 | bool click(const uint8_t num) {
224 | return (clicks == num) && click();
225 | }
226 |
227 | // кнопка зажата (между press() и release()) [состояние]
228 | bool pressing() {
229 | return bf.read(EB_PRS);
230 | }
231 |
232 | // кнопка зажата (между press() и release()) с предварительными кликами [состояние]
233 | bool pressing(const uint8_t num) {
234 | return (clicks == num) && pressing();
235 | }
236 |
237 | // кнопка была удержана (больше таймаута) [событие]
238 | bool hold() {
239 | return bf.read(EB_HLD_R);
240 | }
241 |
242 | // кнопка была удержана (больше таймаута) с предварительными кликами [событие]
243 | bool hold(const uint8_t num) {
244 | return (clicks == num) && hold();
245 | }
246 |
247 | // кнопка удерживается (больше таймаута) [состояние]
248 | bool holding() {
249 | return bf.eq(EB_PRS | EB_HLD, EB_PRS | EB_HLD);
250 | }
251 |
252 | // кнопка удерживается (больше таймаута) с предварительными кликами [состояние]
253 | bool holding(const uint8_t num) {
254 | return (clicks == num) && holding();
255 | }
256 |
257 | // импульсное удержание [событие]
258 | bool step() {
259 | return bf.read(EB_STP_R);
260 | }
261 |
262 | // импульсное удержание с предварительными кликами [событие]
263 | bool step(const uint8_t num) {
264 | return (clicks == num) && step();
265 | }
266 |
267 | // зафиксировано несколько кликов [событие]
268 | bool hasClicks() {
269 | return bf.eq(EB_CLKS_R | EB_HLD, EB_CLKS_R);
270 | }
271 |
272 | // зафиксировано указанное количество кликов [событие]
273 | bool hasClicks(const uint8_t num) {
274 | return (clicks == num) && hasClicks();
275 | }
276 |
277 | // получить количество кликов
278 | uint8_t getClicks() {
279 | return clicks;
280 | }
281 |
282 | // получить количество степов
283 | uint16_t getSteps() {
284 | #ifndef EB_NO_FOR
285 | return ftmr ? ((stepFor() + EB_GET_STEP_TIME() - 1) / EB_GET_STEP_TIME()) : 0; // (x + y - 1) / y
286 | #endif
287 | return 0;
288 | }
289 |
290 | // кнопка отпущена после удержания [событие]
291 | bool releaseHold() {
292 | return bf.eq(EB_REL_R | EB_REL | EB_HLD | EB_STP, EB_REL_R | EB_HLD);
293 | }
294 |
295 | // кнопка отпущена после удержания с предварительными кликами [событие]
296 | bool releaseHold(const uint8_t num) {
297 | return clicks == num && bf.eq(EB_CLKS_R | EB_HLD | EB_STP, EB_CLKS_R | EB_HLD);
298 | }
299 |
300 | // кнопка отпущена после импульсного удержания [событие]
301 | bool releaseStep() {
302 | return bf.eq(EB_REL_R | EB_REL | EB_STP, EB_REL_R | EB_STP);
303 | }
304 |
305 | // кнопка отпущена после импульсного удержания с предварительными кликами [событие]
306 | bool releaseStep(const uint8_t num) {
307 | return clicks == num && bf.eq(EB_CLKS_R | EB_STP, EB_CLKS_R | EB_STP);
308 | }
309 |
310 | // кнопка отпущена после удержания или импульсного удержания [событие]
311 | bool releaseHoldStep() {
312 | return releaseHold() || releaseStep();
313 | }
314 |
315 | // кнопка отпущена после удержания или импульсного удержания с предварительными кликами [событие]
316 | bool releaseHoldStep(const uint8_t num) {
317 | return releaseHold(num) || releaseStep(num);
318 | }
319 |
320 | // кнопка ожидает повторных кликов [состояние]
321 | bool waiting() {
322 | return clicks && bf.eq(EB_PRS | EB_REL, 0);
323 | }
324 |
325 | // идёт обработка [состояние]
326 | bool busy() {
327 | return bf.read(EB_BUSY);
328 | }
329 |
330 | // было действие с кнопки, вернёт код события [событие]
331 | uint16_t action() {
332 | switch (bf.mask(0b111111111)) {
333 | case (EB_PRS | EB_PRS_R): return EB_PRESS;
334 | case (EB_PRS | EB_HLD | EB_HLD_R): return EB_HOLD;
335 | case (EB_PRS | EB_HLD | EB_STP | EB_STP_R): return EB_STEP;
336 | case (EB_REL | EB_REL_R):
337 | case (EB_REL | EB_REL_R | EB_HLD):
338 | case (EB_REL | EB_REL_R | EB_HLD | EB_STP):
339 | return EB_RELEASE;
340 | case (EB_REL_R): return EB_CLICK;
341 | case (EB_CLKS_R): return EB_CLICKS;
342 | case (EB_REL_R | EB_HLD): return EB_REL_HOLD;
343 | case (EB_CLKS_R | EB_HLD): return EB_REL_HOLD_C;
344 | case (EB_REL_R | EB_HLD | EB_STP): return EB_REL_STEP;
345 | case (EB_CLKS_R | EB_HLD | EB_STP): return EB_REL_STEP_C;
346 | }
347 | if (timeoutState()) return EB_TIMEOUT;
348 | return 0;
349 | }
350 |
351 | // было действие с кнопки, вернёт код события [событие]
352 | EBAction getAction() {
353 | return (EBAction)action();
354 | }
355 |
356 | // ====================== TIME ======================
357 | // после взаимодействия с кнопкой (или энкодером EncButton) время setTimeout, мс [событие]
358 | bool timeout() {
359 | if (timeoutState()) {
360 | bf.clear(EB_TOUT);
361 | return 1;
362 | }
363 | return 0;
364 | }
365 |
366 | // после взаимодействия с кнопкой (или энкодером EncButton) время setTimeout, мс [состояние]
367 | bool timeoutState() {
368 | return bf.read(EB_TOUT) && (uint16_t)((uint16_t)EB_uptime() - tmr) >= EB_GET_TOUT_TIME();
369 | }
370 |
371 | // время, которое кнопка удерживается (с начала нажатия), мс
372 | uint16_t pressFor() {
373 | #ifndef EB_NO_FOR
374 | if (ftmr) return (uint16_t)EB_uptime() - ftmr;
375 | #endif
376 | return 0;
377 | }
378 |
379 | // кнопка удерживается дольше чем (с начала нажатия), мс [состояние]
380 | bool pressFor(const uint16_t ms) {
381 | return pressFor() > ms;
382 | }
383 |
384 | // время, которое кнопка удерживается (с начала удержания), мс
385 | uint16_t holdFor() {
386 | #ifndef EB_NO_FOR
387 | if (bf.read(EB_HLD)) return pressFor() - EB_GET_HOLD_TIME();
388 | #endif
389 | return 0;
390 | }
391 |
392 | // кнопка удерживается дольше чем (с начала удержания), мс [состояние]
393 | bool holdFor(const uint16_t ms) {
394 | return holdFor() > ms;
395 | }
396 |
397 | // время, которое кнопка удерживается (с начала степа), мс
398 | uint16_t stepFor() {
399 | #ifndef EB_NO_FOR
400 | if (bf.read(EB_STP)) return pressFor() - EB_GET_HOLD_TIME() * 2;
401 | #endif
402 | return 0;
403 | }
404 |
405 | // кнопка удерживается дольше чем (с начала степа), мс [состояние]
406 | bool stepFor(uint16_t ms) {
407 | return stepFor() > ms;
408 | }
409 |
410 | // ====================== POLL ======================
411 | // обработка виртуальной кнопки как одновременное нажатие двух других кнопок
412 | bool tick(VirtButton& b0, VirtButton& b1) {
413 | if (bf.read(EB_BOTH)) {
414 | if (!b0.pressing() && !b1.pressing()) bf.clear(EB_BOTH);
415 | if (!b0.pressing()) b0.reset();
416 | if (!b1.pressing()) b1.reset();
417 | b0.clear();
418 | b1.clear();
419 | return tick(1);
420 | } else {
421 | if (b0.pressing() && b1.pressing()) bf.set(EB_BOTH);
422 | return tick(0);
423 | }
424 | }
425 |
426 | // обработка кнопки значением
427 | bool tick(bool s) {
428 | clear();
429 | s = pollBtn(s);
430 | #ifndef EB_NO_CALLBACK
431 | if (s || timeoutState()) call();
432 | #endif
433 | return s;
434 | }
435 |
436 | // обработка кнопки без сброса событий и вызова коллбэка
437 | bool tickRaw(const bool s) {
438 | return pollBtn(s);
439 | }
440 |
441 | // вызвать обработчик
442 | void call(bool force = false) { // todo force заменить на флаг
443 | #ifndef EB_NO_CALLBACK
444 | if (cb && (force || action())) {
445 | if (cb) {
446 | EB_self = this;
447 | cb();
448 | EB_self = nullptr;
449 | timeout(); // todo clear tout
450 | }
451 | }
452 | #else
453 | (void)force;
454 | #endif
455 | }
456 |
457 | uint8_t clicks;
458 |
459 | // deprecated
460 | void setButtonLevel(bool level) __attribute__((deprecated)) {
461 | bf.write(EB_INV, !level);
462 | }
463 |
464 | // после взаимодействия с кнопкой (или энкодером EncButton) прошло указанное время, мс [событие]
465 | bool timeout(const uint16_t tout) /*__attribute__((deprecated))*/ {
466 | if (timeoutState(tout)) {
467 | bf.clear(EB_TOUT);
468 | return 1;
469 | }
470 | return 0;
471 | }
472 |
473 | // после взаимодействия с кнопкой (или энкодером EncButton) прошло указанное время, мс [состояние]
474 | bool timeoutState(const uint16_t tout) /*__attribute__((deprecated))*/ {
475 | return (bf.read(EB_TOUT) && (uint16_t)((uint16_t)EB_uptime() - tmr) > tout);
476 | }
477 |
478 | // ====================== PRIVATE ======================
479 | protected:
480 | uint16_t tmr = 0;
481 | encb::Flags bf;
482 |
483 | #ifndef EB_NO_CALLBACK
484 | ActionHandler cb = nullptr;
485 | #endif
486 |
487 | private:
488 | #ifndef EB_NO_FOR
489 | uint16_t ftmr = 0;
490 | #endif
491 | #ifndef EB_DEB_TIME
492 | uint8_t EB_DEB_T = 50;
493 | #endif
494 | #ifndef EB_CLICK_TIME
495 | uint8_t EB_CLICK_T = (500 >> EB_SHIFT);
496 | #endif
497 | #ifndef EB_HOLD_TIME
498 | uint8_t EB_HOLD_T = (600 >> EB_SHIFT);
499 | #endif
500 | #ifndef EB_STEP_TIME
501 | uint8_t EB_STEP_T = (200 >> EB_SHIFT);
502 | #endif
503 | #ifndef EB_TOUT_TIME
504 | uint8_t EB_TOUT_T = (1000 >> EB_SHIFT);
505 | #endif
506 |
507 | bool pollBtn(bool s) {
508 | if (bf.read(EB_BISR)) {
509 | bf.clear(EB_BISR);
510 | s = 1;
511 | } else s ^= bf.read(EB_INV);
512 |
513 | if (!bf.read(EB_BUSY)) {
514 | if (s) bf.set(EB_BUSY);
515 | else return 0;
516 | }
517 |
518 | uint16_t ms = EB_uptime();
519 | uint16_t deb = ms - tmr;
520 |
521 | if (s) { // кнопка нажата
522 | if (!bf.read(EB_PRS)) { // кнопка не была нажата ранее
523 | if (!bf.read(EB_DEB) && EB_DEB_T) { // дебаунс ещё не сработал
524 | bf.set(EB_DEB); // будем ждать дебаунс
525 | tmr = ms; // сброс таймаута
526 | } else { // первое нажатие
527 | if (deb >= EB_DEB_T || !EB_DEB_T) { // ждём EB_DEB_TIME
528 | bf.set(EB_PRS | EB_PRS_R); // флаг на нажатие
529 | #ifndef EB_NO_FOR
530 | ftmr = ms;
531 | #endif
532 | tmr = ms; // сброс таймаута
533 | }
534 | }
535 | } else { // кнопка уже была нажата
536 | if (!bf.read(EB_EHLD)) {
537 | if (!bf.read(EB_HLD)) { // удержание ещё не зафиксировано
538 | if (deb >= EB_GET_HOLD_TIME()) { // ждём EB_HOLD_TIME - это удержание
539 | bf.set(EB_HLD_R | EB_HLD); // флаг что было удержание
540 | tmr = ms; // сброс таймаута
541 | }
542 | } else { // удержание зафиксировано
543 | if (deb >= (uint16_t)(bf.read(EB_STP) ? EB_GET_STEP_TIME() : EB_GET_HOLD_TIME())) {
544 | bf.set(EB_STP | EB_STP_R); // флаг степ
545 | tmr = ms; // сброс таймаута
546 | }
547 | }
548 | }
549 | }
550 | } else { // кнопка не нажата
551 | if (bf.read(EB_PRS)) { // но была нажата
552 | if (deb >= EB_DEB_T) { // ждём EB_DEB_TIME
553 | if (!bf.read(EB_HLD)) clicks++; // не удерживали - это клик
554 | if (bf.read(EB_EHLD)) clicks = 0; //
555 | bf.set(EB_REL | EB_REL_R); // флаг release
556 | bf.clear(EB_PRS); // кнопка отпущена
557 | }
558 | } else if (bf.read(EB_REL)) {
559 | if (!bf.read(EB_EHLD)) {
560 | bf.set(EB_REL_R); // флаг releaseHold / releaseStep
561 | }
562 | bf.clear(EB_REL | EB_EHLD);
563 | tmr = ms; // сброс таймаута
564 | } else if (clicks) { // есть клики, ждём EB_CLICK_TIME
565 | if (bf.read(EB_HLD | EB_STP) || deb >= EB_GET_CLICK_TIME()) bf.set(EB_CLKS_R); // флаг clicks
566 | #ifndef EB_NO_FOR
567 | else if (ftmr) ftmr = 0;
568 | #endif
569 | } else if (bf.read(EB_BUSY)) {
570 | bf.clear(EB_HLD | EB_STP | EB_BUSY);
571 | bf.set(EB_TOUT);
572 | #ifndef EB_NO_FOR
573 | ftmr = 0;
574 | #endif
575 | tmr = ms; // test!!
576 | }
577 | if (bf.read(EB_DEB)) bf.clear(EB_DEB); // сброс ожидания нажатия (дебаунс)
578 | }
579 | return bf.read(EB_CLKS_R | EB_PRS_R | EB_HLD_R | EB_STP_R | EB_REL_R);
580 | }
581 | };
582 |
--------------------------------------------------------------------------------
/src/core/VirtEncButton.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 |
4 | #include "VirtButton.h"
5 | #include "VirtEncoder.h"
6 | #include "io.h"
7 |
8 | #ifdef EB_FAST_TIME
9 | #define EB_FAST_T (EB_FAST_TIME)
10 | #endif
11 |
12 | // базовый клас энкодера с кнопкой
13 | class VirtEncButton : public VirtButton, public VirtEncoder {
14 | public:
15 | // ====================== SET ======================
16 | // установить таймаут быстрого поворота, мс
17 | void setFastTimeout(const uint8_t tout) {
18 | #ifndef EB_FAST_TIME
19 | EB_FAST_T = tout;
20 | #endif
21 | }
22 |
23 | // сбросить флаги энкодера и кнопки
24 | void clear(bool resetTout = false) {
25 | VirtButton::clear(resetTout);
26 | VirtEncoder::clear();
27 | }
28 |
29 | // ====================== GET ======================
30 | // нажатый поворот энкодера [событие]
31 | bool turnH() {
32 | return turn() && bf.read(EB_EHLD);
33 | }
34 |
35 | // быстрый поворот энкодера [состояние]
36 | bool fast() {
37 | return ef.read(EB_FAST);
38 | }
39 |
40 | // поворот направо [событие]
41 | bool right() {
42 | return ef.read(EB_DIR) && turn() && !bf.read(EB_EHLD);
43 | }
44 |
45 | // поворот налево [событие]
46 | bool left() {
47 | return !ef.read(EB_DIR) && turn() && !bf.read(EB_EHLD);
48 | }
49 |
50 | // нажатый поворот направо [событие]
51 | bool rightH() {
52 | return ef.read(EB_DIR) && turnH();
53 | }
54 |
55 | // нажатый поворот налево [событие]
56 | bool leftH() {
57 | return !ef.read(EB_DIR) && turnH();
58 | }
59 |
60 | // нажата кнопка энкодера. Аналог pressing() [состояние]
61 | bool encHolding() {
62 | return bf.read(EB_EHLD);
63 | }
64 |
65 | // было действие с кнопки или энкодера, вернёт код события [событие]
66 | uint16_t action() {
67 | return turn() ? EB_TURN : VirtButton::action();
68 | }
69 |
70 | // было действие с кнопки или энкодера, вернёт код события [событие]
71 | EBAction getAction() {
72 | return (EBAction)action();
73 | }
74 |
75 | // ====================== POLL ======================
76 | // ISR
77 | // обработка в прерывании (только энкодер). Вернёт 0 в покое, 1 или -1 при повороте
78 | int8_t tickISR(const bool e0, const bool e1) {
79 | int8_t state = VirtEncoder::pollEnc(e0, e1);
80 | if (state) {
81 | #ifdef EB_NO_BUFFER
82 | ef.set(EB_ISR_F);
83 | ef.write(EB_DIR, state > 0);
84 | ef.write(EB_FAST, _checkFast());
85 | #else
86 | for (uint8_t i = 0; i < 15; i += 3) {
87 | if (!(ebuffer & (1 << i))) {
88 | ebuffer |= (1 << i); // turn
89 | if (state > 0) ebuffer |= (1 << (i + 1)); // dir
90 | if (_checkFast()) ebuffer |= (1 << (i + 2)); // fast
91 | break;
92 | }
93 | }
94 | #endif
95 | }
96 | return state;
97 | }
98 |
99 | // TICK
100 | // обработка энкодера и кнопки
101 | bool tick(const bool e0, const bool e1, const bool btn) {
102 | clear();
103 | return _tick(_tickRaw(btn, pollEnc(e0, e1)));
104 | }
105 |
106 | // обработка энкодера (в прерывании) и кнопки
107 | bool tick(const bool btn) {
108 | clear();
109 | return _tick(_tickRaw(btn));
110 | }
111 |
112 | // RAW
113 | // обработка без сброса событий и вызова коллбэка
114 | bool tickRaw(const bool e0, const bool e1, bool btn) {
115 | return _tickRaw(btn, pollEnc(e0, e1));
116 | }
117 |
118 | // обработка без сброса событий и вызова коллбэка (кнопка)
119 | bool tickRaw(const bool btn) {
120 | return _tickRaw(btn);
121 | }
122 |
123 | // ===================== PRIVATE =====================
124 | protected:
125 | #ifndef EB_FAST_TIME
126 | uint8_t EB_FAST_T = 30;
127 | #endif
128 |
129 | #ifndef EB_NO_BUFFER
130 | uint16_t ebuffer = 0;
131 | #endif
132 |
133 | private:
134 | bool _checkFast() {
135 | uint16_t ms = EB_uptime();
136 | bool f = ms - tmr < EB_FAST_T;
137 | tmr = ms;
138 | return f;
139 | }
140 |
141 | inline bool _tick(bool f) {
142 | #ifndef EB_NO_CALLBACK
143 | if (f || timeoutState()) call(true);
144 | #endif
145 | return f;
146 | }
147 |
148 | bool _tickRaw(bool btn, int8_t estate = 0) {
149 | bool encf = 0;
150 | #ifdef EB_NO_BUFFER
151 | if (ef.read(EB_ISR_F)) {
152 | ef.clear(EB_ISR_F);
153 | encf = 1;
154 | }
155 | #else
156 | if (ebuffer) {
157 | ef.write(EB_DIR, ebuffer & 0b10);
158 | ef.write(EB_FAST, ebuffer & 0b100);
159 | ebuffer >>= 3;
160 | encf = 1;
161 | }
162 | #endif
163 | else if (estate) {
164 | ef.write(EB_DIR, estate > 0);
165 | ef.write(EB_FAST, _checkFast());
166 | encf = 1;
167 | }
168 | if (encf) {
169 | if (bf.read(EB_PRS)) bf.set(EB_EHLD); // зажать энкодер
170 | else clicks = 0;
171 | if (!bf.read(EB_TOUT)) bf.set(EB_TOUT); // таймаут
172 | ef.set(EB_ETRN_R); // флаг поворота
173 | }
174 | return VirtButton::tickRaw(btn) | encf;
175 | }
176 | };
--------------------------------------------------------------------------------
/src/core/VirtEncoder.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 |
4 | #include "flags.h"
5 | #include "io.h"
6 |
7 | // ===================== CONST ======================
8 | #define EB_STEP4_LOW 0
9 | #define EB_STEP4_HIGH 1
10 | #define EB_STEP2 2
11 | #define EB_STEP1 3
12 |
13 | // ===================== FLAGS ======================
14 | #define EB_TYPE (1 << 0)
15 | #define EB_REV (1 << 2)
16 | #define EB_FAST (1 << 3)
17 | #define EB_DIR (1 << 4)
18 | #define EB_ETRN_R (1 << 5)
19 | #define EB_ISR_F (1 << 6)
20 | #define EB_EISR (1 << 7)
21 |
22 | // базовый класс энкодера
23 | class VirtEncoder {
24 | public:
25 | VirtEncoder() {
26 | p0 = p1 = epos = 0;
27 | }
28 |
29 | // ====================== SET ======================
30 | // инвертировать направление энкодера
31 | void setEncReverse(const bool rev) {
32 | rev ? ef.set(EB_REV) : ef.clear(EB_REV);
33 | }
34 |
35 | // установить тип энкодера (EB_STEP4_LOW, EB_STEP4_HIGH, EB_STEP2, EB_STEP1)
36 | void setEncType(const uint8_t type) {
37 | ef.flags = (ef.flags & 0b11111100) | type;
38 | }
39 |
40 | // использовать обработку энкодера в прерывании
41 | void setEncISR(const bool use) {
42 | ef.write(EB_EISR, use);
43 | }
44 |
45 | // инициализация энкодера
46 | void initEnc(const bool e0, const bool e1) {
47 | p0 = e0, p1 = e1;
48 | }
49 |
50 | // сбросить флаги событий
51 | void clear() {
52 | if (ef.read(EB_ETRN_R)) ef.clear(EB_ETRN_R);
53 | }
54 |
55 | // ====================== ОПРОС ======================
56 | // был поворот [событие]
57 | bool turn() {
58 | return ef.read(EB_ETRN_R);
59 | }
60 |
61 | // направление энкодера (1 или -1) [состояние]
62 | int8_t dir() {
63 | return ef.read(EB_DIR) ? 1 : -1;
64 | }
65 |
66 | // ====================== POLL ======================
67 | // ISR
68 | // опросить энкодер в прерывании. Вернёт 1 или -1 при вращении, 0 при остановке
69 | int8_t tickISR(const bool e0, const bool e1) {
70 | int8_t state = pollEnc(e0, e1);
71 | if (state) {
72 | ef.set(EB_ISR_F);
73 | ef.write(EB_DIR, state > 0);
74 | }
75 | return state;
76 | }
77 |
78 | // TICK
79 | // опросить энкодер. Вернёт 1 или -1 при вращении, 0 при остановке
80 | int8_t tick(const bool e0, const bool e1) {
81 | int8_t state = tickRaw(e0, e1);
82 | if (!state) clear();
83 | return state;
84 | }
85 |
86 | // опросить энкодер (сам опрос в прерывании)
87 | int8_t tick() {
88 | int8_t state = tickRaw();
89 | if (!state) clear();
90 | return state;
91 | }
92 |
93 | // RAW
94 | // опросить энкодер без сброса события поворота
95 | int8_t tickRaw(const bool e0, const bool e1) {
96 | int8_t state = tickRaw();
97 | if (state) return state;
98 |
99 | state = pollEnc(e0, e1);
100 | if (state) {
101 | ef.write(EB_DIR, state > 0);
102 | ef.set(EB_ETRN_R);
103 | }
104 | return state;
105 | }
106 |
107 | // опросить энкодер без сброса события поворота (сам опрос в прерывании)
108 | int8_t tickRaw() {
109 | if (ef.read(EB_ISR_F)) {
110 | ef.clear(EB_ISR_F);
111 | ef.set(EB_ETRN_R);
112 | return dir();
113 | }
114 | return 0;
115 | }
116 |
117 | // POLL
118 | // опросить энкодер без установки события поворота (быстрее). Вернёт 1 или -1 при вращении, 0 при остановке
119 | int8_t pollEnc(const bool e0, const bool e1) {
120 | if (p0 ^ p1 ^ e0 ^ e1) {
121 | (p1 ^ e0) ? ++epos : --epos;
122 | p0 = e0, p1 = e1;
123 | if (!epos) return 0;
124 |
125 | switch (ef.mask(0b11)) {
126 | case EB_STEP4_LOW:
127 | if (!(e0 & e1)) return 0; // skip 01, 10, 00
128 | break;
129 | case EB_STEP4_HIGH:
130 | if (e0 | e1) return 0; // skip 01, 10, 11
131 | break;
132 | case EB_STEP2:
133 | if (e0 ^ e1) return 0; // skip 10 01
134 | break;
135 | }
136 | int8_t state = ((epos > 0) ^ ef.read(EB_REV)) ? -1 : 1;
137 | epos = 0;
138 | #ifndef EB_NO_COUNTER
139 | counter += state;
140 | #endif
141 | return state;
142 | }
143 | return 0;
144 | }
145 |
146 | #ifndef EB_NO_COUNTER
147 | int32_t counter = 0;
148 | #endif
149 |
150 | // ===================== PRIVATE =====================
151 | protected:
152 | encb::Flags ef;
153 |
154 | private:
155 | int8_t p0 : 2;
156 | int8_t p1 : 2; // signed 2 bit!
157 | int8_t epos : 4;
158 | };
--------------------------------------------------------------------------------
/src/core/flags.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 |
4 | namespace encb {
5 |
6 | template
7 | struct Flags {
8 | T flags = 0;
9 |
10 | inline T mask(const T x) __attribute__((always_inline)) {
11 | return flags & x;
12 | }
13 | inline void set(const T x) __attribute__((always_inline)) {
14 | flags |= x;
15 | }
16 | inline void clear(const T x) __attribute__((always_inline)) {
17 | flags &= ~x;
18 | }
19 | inline bool read(const T x) __attribute__((always_inline)) {
20 | return flags & x;
21 | }
22 | inline void write(const T x, const bool v) __attribute__((always_inline)) {
23 | v ? set(x) : clear(x);
24 | }
25 | inline bool eq(const T x, const T y) __attribute__((always_inline)) {
26 | return (flags & x) == y;
27 | }
28 | };
29 |
30 | } // namespace encb
--------------------------------------------------------------------------------
/src/core/io.cpp:
--------------------------------------------------------------------------------
1 | #include "io.h"
2 |
3 | bool __attribute__((weak)) EB_read(uint8_t pin) {
4 | return gio::read(pin);
5 | }
6 |
7 | void __attribute__((weak)) EB_mode(uint8_t pin, uint8_t mode) {
8 | gio::init(pin, mode);
9 | }
10 |
11 | uint32_t __attribute__((weak)) EB_uptime() {
12 | return millis();
13 | }
--------------------------------------------------------------------------------
/src/core/io.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 | #include
4 |
5 | bool EB_read(uint8_t pin);
6 | void EB_mode(uint8_t pin, uint8_t mode);
7 | uint32_t EB_uptime();
--------------------------------------------------------------------------------
/src/core/self.cpp:
--------------------------------------------------------------------------------
1 | #include "self.h"
2 |
3 | void* EB_self = nullptr;
--------------------------------------------------------------------------------
/src/core/self.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | extern void* EB_self;
--------------------------------------------------------------------------------