Instantly share code, notes, and snippets.
Created
April 21, 2025 23:31
-
Star
0
(0)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
Save Onefabis/d35c678a5eacc5fdf1e8a44626c3cf9f to your computer and use it in GitHub Desktop.
Позволяет эмулировать нажатие энкодеров и кнопок для головного устройства (T10, TS10 и аналогов). Для расширения функций, увеличения комбинаторики, возможности подключения нескольких энкодеров параллельно и для добавления программного антидребезга. Энкодер теперь может быть любым, поддерживаемым библиотекой Alex Gyver (EncButton) #гу #HU #HEADUN…
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <WiFi.h> | |
#include <AsyncTCP.h> // v3.3.7 | |
#include <ESPAsyncWebServer.h> // v3.7.3 | |
#include <ElegantOTA.h> // v3.1.7 | |
#include <EncButton.h> // v3.7.1 | |
#define ENC1_A 3 // Пин 1 виртуального энкодера ENC1 для магнитолы | |
#define ENC1_B 4 // Пин 2 виртуального энкодера ENC1 для магнитолы | |
#define ENC2_A 1 // Пин 1 виртуального энкодера ENC2 для магнитолы | |
#define ENC2_B 2 // Пин 2 виртуального энкодера ENC2 для магнитолы | |
#define KEY1 20 // Виртуальная кнопка 1 на KEY2 провод магнитолы | |
#define KEY2 21 // Виртуальная кнопка 2 на KEY2 провод магнитолы | |
#define STEP_DELAY 20 // Задержка между изменениями состояний (мс) | |
EncButton eb1(7, 6, 5); // Настройка физического энкодера 1 с кнопкой | |
EncButton eb2(10, 9, 8); // Настройка физического энкодера 2 с кнопкой | |
// Текущие состояния пинов | |
bool direction = true; // true = вправо, false = влево | |
// Установка глобальных переменных | |
bool assign_mode = false; | |
bool ota_enabled = false; | |
const char* ssid = "ESP32_AP"; // Название точки доступа ESP32 C3 | |
const char* password = "1212ESPAP2121"; // !!! ОБЯЗАТЕЛЬНО ИЗМЕНИТЬ НА СВОЙ !!! Пароль точки доступа ESP32 C3 | |
unsigned long ota_progress_millis = 0; | |
AsyncWebServer server(80); | |
void isr1() { // Вызов обработки физического энкодера 1 по прерыванию | |
eb1.tickISR(); | |
} | |
void isr2() { // Вызов обработки физического энкодера 2 по прерыванию | |
eb2.tickISR(); | |
} | |
void setup() { | |
Serial.begin(115200); | |
attachInterrupt(6, isr1, CHANGE); // Настройка прерывания энкодера 1 пина 1 | |
attachInterrupt(7, isr1, CHANGE); // Настройка прерывания энкодера 1 пина 2 | |
attachInterrupt(9, isr2, CHANGE); // Настройка прерывания энкодера 2 пина 1 | |
attachInterrupt(10, isr2, CHANGE); // Настройка прерывания энкодера 2 пина 2 | |
eb1.setEncISR(true); | |
eb2.setEncISR(true); | |
// Устанока всех пинов на режим выхода сигнала (OUTPUT) | |
pinMode(ENC1_A, OUTPUT); | |
pinMode(ENC1_B, OUTPUT); | |
pinMode(ENC2_A, OUTPUT); | |
pinMode(ENC2_B, OUTPUT); | |
pinMode(KEY1, OUTPUT); | |
pinMode(KEY2, OUTPUT); | |
// Установка всех уровней на высокий. Провода от магнитолы замыкаются через резисторы на землю | |
// для срабатывания, то есть, когда уровень становится LOW | |
digitalWrite(ENC1_A, HIGH); | |
digitalWrite(ENC1_B, HIGH); | |
digitalWrite(ENC2_A, HIGH); | |
digitalWrite(ENC2_B, HIGH); | |
digitalWrite(KEY1, HIGH); | |
digitalWrite(KEY2, HIGH); | |
} | |
// Эмуляция одного щелчка энкодера по часовой или против часовой стрелки | |
// Направление определяет переменная direction | |
void encoderClick(int repeat, int PIN_A, int PIN_B) { | |
if (direction) { | |
// Вращение вправо: A↓, B↓ → A↑, B↓ → A↑, B↑ | |
for ( int i=0; i<repeat; i++){ | |
digitalWrite(PIN_A, LOW); | |
digitalWrite(PIN_B, LOW); | |
delay(STEP_DELAY); | |
digitalWrite(PIN_A, HIGH); | |
delay(STEP_DELAY); | |
digitalWrite(PIN_B, HIGH); | |
delay(STEP_DELAY); | |
} | |
} else { | |
// Вращение влево: A↑, B↑ → A↑, B↓ → A↓, B↓ | |
digitalWrite(PIN_A, HIGH); | |
digitalWrite(PIN_B, HIGH); | |
delay(STEP_DELAY); | |
digitalWrite(PIN_B, LOW); | |
delay(STEP_DELAY); | |
digitalWrite(PIN_A, LOW); | |
delay(STEP_DELAY); | |
} | |
} | |
// Эмуляция нажатия на кнопку, сигнал идёт на провод KEY2 магнитолы | |
void buttonClick(int KEY){ | |
int delay_time = STEP_DELAY; // если выключен assign_mode, зажатие кнопки 20 милисекунд, в обычном режиме | |
if (assign_mode) delay_time = 3000; // если включен assign_mode, зажатие кнопки 3 секунды, чтобы успеть обучит магнитолу | |
digitalWrite(KEY, LOW); | |
delay(delay_time); | |
digitalWrite(KEY, HIGH); | |
} | |
// Запуск точки дотупа и включение OTA | |
void startAPandOTA() { | |
if (ota_enabled) return; | |
WiFi.softAP(ssid, password); | |
// Точка доступа, запись IP адреса точки доступа | |
Serial.println(WiFi.softAPIP()); | |
// Стартовая страница с ссылкой на страницу обновления прошивки | |
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ | |
String ip = WiFi.softAPIP().toString(); | |
String html = "<h1>ESP OTA. Add /update</h1>"; | |
html += "<p>Connect to the OTA page at: <a href='http://" + ip + "/update'>http://" + ip + "/update</a></p>"; | |
request->send(200, "text/html", html); | |
}); | |
ElegantOTA.begin(&server); | |
server.begin(); | |
ota_enabled = true; | |
Serial.println("OTA via AP started"); | |
} | |
// Отключение OTA для экономии расхода по току | |
void stopAPandOTA() { | |
if (!ota_enabled) return; | |
WiFi.softAPdisconnect(true); | |
ota_enabled = false; | |
Serial.println("OTA via AP stopped"); | |
} | |
void loop() { | |
if (ota_enabled) { // Цикл для работы OTA, только если включено OTA режим | |
ElegantOTA.loop(); | |
} | |
eb1.tick(); | |
eb2.tick(); | |
if (eb1.turn() && eb1.pressing()) { // Обработка поворота энкодера 1 с зажатой кнопкой | |
if (eb1.leftH()){ // Если поворот влево с зажатой кнопкой, меняем громкость на +/- 1 деление для обратной связи | |
direction = false; | |
encoderClick(1, ENC1_A, ENC1_B); // Громкость уменьшается на -1 для обозначения, что включен особый режим | |
delay(200); | |
direction = true; | |
encoderClick(1, ENC1_A, ENC1_B); // Громкость увеличивается на +1 для обозначения, что включен особый режим | |
assign_mode = false; // Отключается режим обучения кнопок руля, время нажатия кнопок обычное, 20 милисекунд | |
Serial.println("Assign mode stop"); | |
} | |
if (eb1.rightH()){ | |
direction = false; | |
encoderClick(1, ENC1_A, ENC1_B); // Громкость уменьшается на -1 для обозначения, что включен особый режим | |
delay(200); | |
direction = true; | |
encoderClick(1, ENC1_A, ENC1_B); // Громкость увеличивается на +1 для обозначения, что включен особый режим | |
assign_mode = true; // Включается режим обучения кнопок руля, время нажатия кнопок увеличенное, чтобы обучить магнитолу | |
Serial.println("Assign mode start"); | |
} | |
} else if (eb1.turn() && eb1.pressing()==false){ // Обычное вращение энкодера 1 | |
if (eb1.left()) direction = false; | |
if (eb1.right()) direction = true; | |
int repeat_steps = 1; | |
if (eb1.fast()) repeat_steps = 2; // Быстрое вращение громкости меняет уровень на +/- 2 деления | |
encoderClick(repeat_steps, ENC1_A, ENC1_B); // Смена уровня громкости | |
} | |
if (eb2.turn() && eb2.pressing()) { // Обработка поворота энкодера 2 с зажатой кнопкой | |
if (eb2.leftH()){ | |
direction = false; | |
encoderClick(1, ENC1_A, ENC1_B); // Громкость уменьшается на -1 для обозначения, что включен особый режим | |
delay(200); | |
direction = true; | |
encoderClick(1, ENC1_A, ENC1_B); // Громкость увеличивается на +1 для обозначения, что включен особый режим | |
startAPandOTA(); // Включение точки доступа и OTA режима | |
Serial.println("Start OTA"); | |
} | |
if (eb2.rightH()){ | |
direction = false; | |
encoderClick(1, ENC1_A, ENC1_B); // Громкость уменьшается на -1 для обозначения, что включен особый режим | |
delay(200); | |
direction = true; | |
encoderClick(1, ENC1_A, ENC1_B); // Громкость увеличивается на +1 для обозначения, что включен особый режим | |
stopAPandOTA(); // Выключение точки доступа и OTA режима для экономии расхода по току | |
Serial.println("Stop OTA"); | |
} | |
} else if (eb2.turn() && eb2.pressing()==false){ // Обычное вращение энкодера 2 | |
if (eb2.left()) direction = false; | |
if (eb2.right()) direction = true; | |
int repeat_steps = 1; | |
if (eb1.fast()) repeat_steps = 3; // Быстрое вращение энкодера переключает треки на +/- 3 трека | |
encoderClick(repeat_steps, ENC2_A, ENC2_B); // Смена аудиотрека | |
} | |
if (eb1.click()) { | |
buttonClick(KEY1); // Эмуляция нажатия кнопки 1 через резистор на KEY2 магнитолы | |
Serial.println("key 1 is pressed"); | |
} | |
if (eb2.click()) { | |
buttonClick(KEY2); // Эмуляция нажатия кнопки 2 через резистор на KEY2 магнитолы | |
Serial.println("key 2 is pressed"); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment