• В отличие от Youtube, здесь не место толерастии и инклюзивности! Так что думайте что пишите, второй попытки вам никто не даст.

    Внебрачные дети Петросяна и прочих комиков, пожалуйста, тренируйтесь в своем "остро"умии где-нибудь в другом месте!

    Граждане альтернативно-одаренные, проходите, пожалуйста, мимо! Спасибо!

    Кто-то остался? :)

    В отличие от Youtube, здесь не место толерастии и инклюзивности! Так что думайте что пишите, второй попытки вам никто не даст.

    Внебрачные дети Петросяна и прочих комиков, пожалуйста, тренируйтесь в своем "остро"умии где-нибудь в другом месте!

    Граждане альтернативно-одаренные, проходите, пожалуйста, мимо! Спасибо!

    Кто-то остался? :)

    Бесплатный
  • Ближайший ролик будет посвящен увеличению точности RTC часов в МК ESP32. Поскольку настройками тактирования RTC нельзя управлять в Arduino Framework, будем компиллировать дурино-скетчи под ESP-IDF. Не пугайтесь, это не так сложно, главная проблема кроется в подключении сторонних библиотек...

    Ближайший ролик будет посвящен увеличению точности RTC часов в МК ESP32. Поскольку настройками тактирования RTC нельзя управлять в Arduino Framework, будем компиллировать дурино-скетчи под ESP-IDF. Не пугайтесь, это не так сложно, главная проблема кроется в подключении сторонних библиотек...

    Бесплатный
  • "Рыба" спящего Matter датчика температуры, влажности, давления и уровня углекислого газа для Arduino for ESP 3.3.6: github.com/MoonFox2006/Matter_AirQ

    "Рыба" спящего Matter датчика температуры, влажности, давления и уровня углекислого газа для Arduino for ESP 3.3.6: github.com/MoonFox2006/Matter_AirQ

    Бесплатный
  • Я уже ранее делал "библиотеку" работы со светодиодами на МК ESP32 разных серий через аппаратную поддержку LEDC, которая позволяет в том числе мигать светиками без дополнительного таймера, но Espressif в очередном обновлении ESP-IDF все "сломали", поэтому пришлось написать новую "библиотеку", зато теперь с поддержкой и модного молодежного C5.

    Яркость 10 бит (0-1023), частота PWM 1 кГц


    github.com/MoonFox2006/Blink32

    Я уже ранее делал "библиотеку" работы со светодиодами на МК ESP32 разных серий через аппаратную поддержку LEDC, которая позволяет в том числе мигать светиками без дополнительного таймера, но Espressif в очередном обновлении ESP-IDF все "сломали", поэтому пришлось написать новую "библиотеку", зато теперь с поддержкой и модного молодежного C5.

    Яркость 10 бит (0-1023), частота PWM 1 кГц


    github.com/MoonFox2006/Blink32

    Бесплатный
  • На фоне непрекращающегося роста цен на оперативную и флеш память, компания Espressif приняла решение выпустить новые "антикризисные" ревизии своих популярных микроконтроллеров серии ESP32 с уменьшенным объемом SRAM и Flash. Изменения коснутся всех актуальных линеек.

    В ближайшее время выйдет новая версия ESP-IDF с поддержкой измененных параметров микроконтроллеров. В наименовании обновленных чипов появится цифра 1 после номера серии, т.е. ESP321, ESP32-S21, ESP32-S31 и т.д.

    На фоне непрекращающегося роста цен на оперативную и флеш память, компания Espressif приняла решение выпустить новые "антикризисные" ревизии своих популярных микроконтроллеров серии ESP32 с уменьшенным объемом SRAM и Flash. Изменения коснутся всех актуальных линеек.

    В ближайшее время выйдет новая версия ESP-IDF с поддержкой измененных параметров микроконтроллеров. В наименовании обновленных чипов появится цифра 1 после номера серии, т.е. ESP321, ESP32-S21, ESP32-S31 и т.д.

    Бесплатный
  • Для ИИ вы всегда правы, какую бы ахинею вы не просили помочь сделать. ИИ для вашего удовольствия придумает то, чего нет в действительности, лишь бы "решить" задачу.

    Вот Qwen, например, придумал в строго задокументированном в спецификациях кластеров Matter несуществующий атрибут, чтобы заданная ему задача имела "решение".

    Короче, видимо все восторги о написании кода ИИ исходят или от лохов или от веб-"программистов" (читай "верстальщиков"). Вот я к веб-разработке не имею никакого отношения, и да, любой ИИ делает странички лучше моих в разы. :) Правда половина скриптов не всегда работает, но надо же и вам чем-то заняться, а не только промптить лежа на диване...

    Для ИИ вы всегда правы, какую бы ахинею вы не просили помочь сделать. ИИ для вашего удовольствия придумает то, чего нет в действительности, лишь бы "решить" задачу.

    Вот Qwen, например, придумал в строго задокументированном в спецификациях кластеров Matter несуществующий атрибут, чтобы заданная ему задача имела "решение".

    Короче, видимо все восторги о написании кода ИИ исходят или от лохов или от веб-"программистов" (читай "верстальщиков"). Вот я к веб-разработке не имею никакого отношения, и да, любой ИИ делает странички лучше моих в разы. :) Правда половина скриптов не всегда работает, но надо же и вам чем-то заняться, а не только промптить лежа на диване...

    Бесплатный
  • Угробил все выходные на тщетные попытки сделать прототип SED (sleeppy end device) Matter устройства на ESP32-C3.

    Дурина последней версии 3.3.7 содержит ошибку в библиотеке поддержки Matter, благо на предыдущей версии 3.3.6 крашей удается избегать. Я бы использовал голый ESP-IDF, но прошивка собирается, Алиса из колонки с поддержкой Matter начинает договариваться, но под конец заявляет, что ничего не нашла. Под дуриной комиссионинг завершается успехом.

    В итоге вроде бы удалось написать рыбу для дурины Matter устройства, спящую глубоким сном между сбросом показаний датчика.

    Самое занятное, что в этих ваших интернетах для ESP подобного решения я не нашел, только просьбы к разработчикам добавить пример реализации.

    Как появится время, сниму небольшой ролик.

    PS: а вообще все начиналось из-за Matter для ESP32-S2, и тут все не так однозначно... У этого МК мало того, что нет поддержки BLE, которая используется для комиссионинга, так еще и не смотря на не самый малый объем ОЗУ, в последовательный его регион секция .bss кода Matter не влезает. Влезает при переносе части .bss в PSRAM, но мне нужно получить код для МК без PSRAM. И как я уже говорил выше, у меня код для ESP-IDF не завершает комиссионинг с Алисой. Шляпа...

    Угробил все выходные на тщетные попытки сделать прототип SED (sleeppy end device) Matter устройства на ESP32-C3.

    Дурина последней версии 3.3.7 содержит ошибку в библиотеке поддержки Matter, благо на предыдущей версии 3.3.6 крашей удается избегать. Я бы использовал голый ESP-IDF, но прошивка собирается, Алиса из колонки с поддержкой Matter начинает договариваться, но под конец заявляет, что ничего не нашла. Под дуриной комиссионинг завершается успехом.

    В итоге вроде бы удалось написать рыбу для дурины Matter устройства, спящую глубоким сном между сбросом показаний датчика.

    Самое занятное, что в этих ваших интернетах для ESP подобного решения я не нашел, только просьбы к разработчикам добавить пример реализации.

    Как появится время, сниму небольшой ролик.

    PS: а вообще все начиналось из-за Matter для ESP32-S2, и тут все не так однозначно... У этого МК мало того, что нет поддержки BLE, которая используется для комиссионинга, так еще и не смотря на не самый малый объем ОЗУ, в последовательный его регион секция .bss кода Matter не влезает. Влезает при переносе части .bss в PSRAM, но мне нужно получить код для МК без PSRAM. И как я уже говорил выше, у меня код для ESP-IDF не завершает комиссионинг с Алисой. Шляпа...

    Бесплатный
  • Бесплатный
  • У вас заболел зуб. Надо бы пойти к стоматологу, но денег жалко. Ну бывает. Но зуб-то болит! Сильно! Ок, берем зеркало, шуруповерт и пассатижи. Мы же видели, как эти алчные стоматологи работают, что там сложного? Так, что-то неудобно шуруповертом в рот лезть. И пассатижи большие слишком. И зеркало неудобное. И непонятно что именно делать... Ладно, пойдем в поликлинику.

    "Доктор, я вот тут пытался себе зуб вылечить, но что-то не получается. Я вот и инструменты свои принес. Вы мне не подскажете как это лучше сделать?"

    Что значит "бред какой-то"?! :)

    Ну ведь пишут же страждущие, что пыжатся к моему коду прилепить что-то нужное только им, но никак не получается. Не могу ли я подсказать как это лучше сделать. Они даже могут "свой" неработающий (и чаще всего даже синтаксически неверный) франкенштейн-код прислать...

    Но есть и те, кто сразу четко спрашивает, могу ли я для них адаптировать свой проект под новые задачи за денежное вознаграждение.

    Мое многострадальное "умное" реле на 8266, которому уже лет 10, знавало много первых и несколько вторых. Но первых в разы больше... :)

    PS: для непонятливых. Программирование - не комбинаторика! Переставляй не переставляй непонятные слова - толку не будет! Попытка разобрать ваши тщетные старания займет больше времени, чем написание сразу как надо. Не цените свое время - это вовсе не значит, что другие также не ценят собственное. Я вот бесценный. :) Не путать с "бесплатным". :)

    У вас заболел зуб. Надо бы пойти к стоматологу, но денег жалко. Ну бывает. Но зуб-то болит! Сильно! Ок, берем зеркало, шуруповерт и пассатижи. Мы же видели, как эти алчные стоматологи работают, что там сложного? Так, что-то неудобно шуруповертом в рот лезть. И пассатижи большие слишком. И зеркало неудобное. И непонятно что именно делать... Ладно, пойдем в поликлинику.

    "Доктор, я вот тут пытался себе зуб вылечить, но что-то не получается. Я вот и инструменты свои принес. Вы мне не подскажете как это лучше сделать?"

    Что значит "бред какой-то"?! :)

    Ну ведь пишут же страждущие, что пыжатся к моему коду прилепить что-то нужное только им, но никак не получается. Не могу ли я подсказать как это лучше сделать. Они даже могут "свой" неработающий (и чаще всего даже синтаксически неверный) франкенштейн-код прислать...

    Но есть и те, кто сразу четко спрашивает, могу ли я для них адаптировать свой проект под новые задачи за денежное вознаграждение.

    Мое многострадальное "умное" реле на 8266, которому уже лет 10, знавало много первых и несколько вторых. Но первых в разы больше... :)

    PS: для непонятливых. Программирование - не комбинаторика! Переставляй не переставляй непонятные слова - толку не будет! Попытка разобрать ваши тщетные старания займет больше времени, чем написание сразу как надо. Не цените свое время - это вовсе не значит, что другие также не ценят собственное. Я вот бесценный. :) Не путать с "бесплатным". :)

    Бесплатный
  • В свое время для упрощения работы с платами на МК CH32V003 я написал бутлоадер, который позволяет прошивать МК через любой (как я наивно полагал) USB-UART вместо проприетарного программатора. Написал консольную утилиту для прошивки, взаимодействующую с бутлоадером. И первые попавшиеся под руку USB-UART мосты у меня прекрасно работали. Задержку бутлоадера для перепрошивки я выбрал в 1.5 сек., что считал разумным компромиссом между практичностью и ловкостью.

    На днях немного обновил функционал на своих устройствах как раз на МК CH32V003 с моим бутлоадером, успешно обновил тестовый экземпляр подручным USB-UART из программатора WCH Link-E... А потом решил проверить другие мосты. Первым в руки попал мост на CH340G. И он не смог! Т.е. компьютер пропел, что нашел новое устройство после подключения, но драйвер винды позволяет работать с новым COM портом только секунды через 3.

    Я даже пересобрал бутлоадер с задержкой в 3 сек., но и это не помогло.

    Зато USB-UART мост на CP2102 справился с задачей без проблем.

    В итоге в ролике для владельцев моего обновленного устройства я рекомендовал перебирать имеющиеся в хозяйстве каждого уважающего себя DIY'щика USB-UART мосты, пока все не получится. Была надежда, что всякие FTDI и PL также должны справиться...

    И вот мой подписчик Павел провел серию экспериментов, в его случае разные FTDI также не справились, как и успешный в моем случае CP2102, но справился CP2104 (странно, драйвер у CP210x одинаковый, и по моему мнению проблема именно в нем, а не в самом железе).

    Короче, при случае запасайтесь CP2102/2104, а лучше целым зоопарком :)

    В свое время для упрощения работы с платами на МК CH32V003 я написал бутлоадер, который позволяет прошивать МК через любой (как я наивно полагал) USB-UART вместо проприетарного программатора. Написал консольную утилиту для прошивки, взаимодействующую с бутлоадером. И первые попавшиеся под руку USB-UART мосты у меня прекрасно работали. Задержку бутлоадера для перепрошивки я выбрал в 1.5 сек., что считал разумным компромиссом между практичностью и ловкостью.

    На днях немного обновил функционал на своих устройствах как раз на МК CH32V003 с моим бутлоадером, успешно обновил тестовый экземпляр подручным USB-UART из программатора WCH Link-E... А потом решил проверить другие мосты. Первым в руки попал мост на CH340G. И он не смог! Т.е. компьютер пропел, что нашел новое устройство после подключения, но драйвер винды позволяет работать с новым COM портом только секунды через 3.

    Я даже пересобрал бутлоадер с задержкой в 3 сек., но и это не помогло.

    Зато USB-UART мост на CP2102 справился с задачей без проблем.

    В итоге в ролике для владельцев моего обновленного устройства я рекомендовал перебирать имеющиеся в хозяйстве каждого уважающего себя DIY'щика USB-UART мосты, пока все не получится. Была надежда, что всякие FTDI и PL также должны справиться...

    И вот мой подписчик Павел провел серию экспериментов, в его случае разные FTDI также не справились, как и успешный в моем случае CP2102, но справился CP2104 (странно, драйвер у CP210x одинаковый, и по моему мнению проблема именно в нем, а не в самом железе).

    Короче, при случае запасайтесь CP2102/2104, а лучше целым зоопарком :)

    Бесплатный
  • Бесплатный
  • Судя по всему, чиновники-дегенераты все-таки приняли решение о блокировке Телеграм в РФ. В анусе Дурова уже торчат французский, британский, американский и черт знает еще чей паяльники, наш отечественный туда уже не помещается, так что видимо договорняка не будет.

    Посему, пока я не заброшу эту площадку, текстовые записи помимо роликов (включая местные эксклюзивы по подписке) будут только здесь. При существующей пассивности аудитории, продлится это явно недолго. Так что если кто-то еще надумает (зачем-то) подписаться, не делайте этого на период более месяца!

    Спасибо всем, кто решил частично компенсировать мои затраты! И также я благодарен всем пожелавшим воздержаться за столь нужную мне демотивацию, чтобы не тратить на вас свое время. Что очевидно, первых в 150 раз меньше, так что каждый из них стоит 149 остальных. :)


    Как я ранее писал в нечитаемой на данный момент без спец-средств телеге, темой ближайшего ролика будет разбор "библиотеки" для ESP32 и дурины по работе с кнопками. Код содержит некоторые специфичные для МК моменты, поэтому может быть интересен.

    Судя по всему, чиновники-дегенераты все-таки приняли решение о блокировке Телеграм в РФ. В анусе Дурова уже торчат французский, британский, американский и черт знает еще чей паяльники, наш отечественный туда уже не помещается, так что видимо договорняка не будет.

    Посему, пока я не заброшу эту площадку, текстовые записи помимо роликов (включая местные эксклюзивы по подписке) будут только здесь. При существующей пассивности аудитории, продлится это явно недолго. Так что если кто-то еще надумает (зачем-то) подписаться, не делайте этого на период более месяца!

    Спасибо всем, кто решил частично компенсировать мои затраты! И также я благодарен всем пожелавшим воздержаться за столь нужную мне демотивацию, чтобы не тратить на вас свое время. Что очевидно, первых в 150 раз меньше, так что каждый из них стоит 149 остальных. :)


    Как я ранее писал в нечитаемой на данный момент без спец-средств телеге, темой ближайшего ролика будет разбор "библиотеки" для ESP32 и дурины по работе с кнопками. Код содержит некоторые специфичные для МК моменты, поэтому может быть интересен.

    Бесплатный
  • Пример "библиотеки" для чтения энкодера на МК серии ESP32 под ESP-IDF. Во второй части ролика будет разбор деталей реализации.

    Ссылка на код проекта для ESP-IDF из видео: github.com/MoonFox2006/ESP32_Encoder_IDF

    Спасибо всем, кто поддерживает мое "творчество"!

    Пример "библиотеки" для чтения энкодера на МК серии ESP32 под ESP-IDF. Во второй части ролика будет разбор деталей реализации.

    Ссылка на код проекта для ESP-IDF из видео: github.com/MoonFox2006/ESP32_Encoder_IDF

    Спасибо всем, кто поддерживает мое "творчество"!

    Бесплатный
  • Основной уровень

  • Пример "библиотеки" для чтения энкодера на МК серии ESP32. Во второй части ролика будет разбор деталей реализации.

    Ссылка на код проекта для Arduino из видео: github.com/MoonFox2006/ESP32_Encoder


    Пример "библиотеки" для чтения энкодера на МК серии ESP32. Во второй части ролика будет разбор деталей реализации.

    Ссылка на код проекта для Arduino из видео: github.com/MoonFox2006/ESP32_Encoder

    Бесплатный
  • Попросил бесплатный Qwen Code в VS Code создать BLE HID регулятор громкости на энкодере для ESP32-C3 в ESP-IDF.

    Ну то, что код не откомпилируется, сомнений не было никаких. И даже попытка его причесать самому успехом не увенчалась. Придуманные ИИ библиотеки не гуглятся.

    Но давайте посмотрим на то, как ИИ реализовал опрос энкодера (выжимка из кода только по энкодеру):

    // Метки событий для RTOS
    #define ENCODER_EVENT    (1 << 0)
    #define BUTTON_EVENT    (1 << 1)
    #define BLE_EVENT      (1 << 2)
    #define SLEEP_EVENT     (1 << 3)
    
    // Глобальные переменные
    static EventGroupHandle_t event_group;
    
    // Очередь для событий энкодера
    typedef struct {
      int8_t direction; // 1 = clockwise, -1 = counter-clockwise, 0 = button
      bool button_pressed;
    } encoder_event_t;
    
    static QueueHandle_t encoder_queue = NULL;
    
    // Состояние энкодера
    static volatile int8_t encoder_state = 0;
    static volatile int8_t last_encoded_state = 0;
    
    // Обработка прерывания энкодера
    static void IRAM_ATTR encoder_isr_handler(void *arg) {
      int8_t pin_a = gpio_get_level(ENCODER_PIN_A);
      int8_t pin_b = gpio_get_level(ENCODER_PIN_B);
    
      // Gray code decoding
      static const int8_t enc_states[] = {0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0};
    
      encoder_state = ((encoder_state << 2) | (pin_b << 1) | pin_a) & 0x0F;
      int8_t direction = enc_states[encoder_state];
    
      if (direction != 0) {
        encoder_event_t event = {
          .direction = direction,
          .button_pressed = false
        };
        xQueueSendFromISR(encoder_queue, &event, NULL);
      }
    }
    
    // Обработка прерывания кнопки
    static void IRAM_ATTR button_isr_handler(void *arg) {
      encoder_event_t event = {
        .direction = 0,
        .button_pressed = true
      };
      xQueueSendFromISR(encoder_queue, &event, NULL);
    }
    
    // Инициализация энкодера
    static void init_encoder(void) {
      encoder_queue = xQueueCreate(10, sizeof(encoder_event_t));
    
      // Конфигурация пинов энкодера
      gpio_config_t io_conf = {
        .pin_bit_mask = (1ULL << ENCODER_PIN_A) | (1ULL << ENCODER_PIN_B),
        .mode = GPIO_MODE_INPUT,
        .pull_up_en = GPIO_PULLUP_ENABLE,
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
        .intr_type = GPIO_INTR_ANYEDGE
      };
      gpio_config(&io_conf);
    
      // Установка прерываний
      gpio_install_isr_service(0);
      gpio_isr_handler_add(ENCODER_PIN_A, encoder_isr_handler, NULL);
      gpio_isr_handler_add(ENCODER_PIN_B, encoder_isr_handler, NULL);
    
      // Конфигурация кнопки (если подключена)
      if (ENCODER_PIN_BUTTON >= 0) {
        gpio_config_t btn_conf = {
          .pin_bit_mask = (1ULL << ENCODER_PIN_BUTTON),
          .mode = GPIO_MODE_INPUT,
          .pull_up_en = GPIO_PULLUP_ENABLE,
          .pull_down_en = GPIO_PULLDOWN_DISABLE,
          .intr_type = GPIO_INTR_NEGEDGE
        };
        gpio_config(&btn_conf);
        gpio_isr_handler_add(ENCODER_PIN_BUTTON, button_isr_handler, NULL);
      }
    
      ESP_LOGI(TAG, "Encoder initialized (A: GPIO%d, B: GPIO%d, BTN: GPIO%d)", 
           ENCODER_PIN_A, ENCODER_PIN_B, ENCODER_PIN_BUTTON);
    }
    
    // Задача обработки энкодера
    static void encoder_task(void *pvParameters) {
      encoder_event_t event;
    
      while (1) {
        if (xQueueReceive(encoder_queue, &event, portMAX_DELAY) == pdTRUE) {
          if (event.button_pressed) {
            // Дебаунс кнопки
            vTaskDelay(pdMS_TO_TICKS(DEBOUNCE_DELAY_MS));
            if (gpio_get_level(ENCODER_PIN_BUTTON) == 0) {
              xEventGroupSetBits(event_group, BUTTON_EVENT);
            }
          } else if (event.direction != 0) {
            xEventGroupSetBits(event_group, ENCODER_EVENT);
          }
        }
      }
    }
    
    // Задача обработки событий
    static void event_handler_task(void *pvParameters) {
      EventBits_t uxBits;
      static int8_t last_direction = 0;
      static uint32_t last_event_time = 0;
    
      while (1) {
        uxBits = xEventGroupWaitBits(event_group, 
                       ENCODER_EVENT | BUTTON_EVENT | SLEEP_EVENT,
                       pdTRUE, // Clear bits on exit
                       pdFALSE, // Any bit
                       portMAX_DELAY);
    
        uint32_t current_time = xTaskGetTickCount();
    
        if (uxBits & SLEEP_EVENT) {
    ...
        }
    
        if (uxBits & BUTTON_EVENT) {
          send_button_press();
          last_event_time = current_time;
          last_direction = 0;
        }
    
        if (uxBits & ENCODER_EVENT) {
          int8_t pin_a = gpio_get_level(ENCODER_PIN_A);
          int8_t pin_b = gpio_get_level(ENCODER_PIN_B);
    
          // Определение направления
          int8_t direction = 0;
          if (pin_a != pin_b) {
            direction = (pin_a > pin_b) ? 1 : -1;
          }
    
          // Защита от дребезга и слишком быстрой прокрутки
          if (direction != 0 && (direction != last_direction || 
            (current_time - last_event_time) > pdMS_TO_TICKS(100))) {
            send_volume_control(direction);
            last_direction = direction;
            last_event_time = current_time;
          }
        }
      }
    }
    

    Т.е. он сделал два обработчика прерываний (для кнопки и двух контактов энкодера), в них помещает события в очередь, в одной задаче эту очередь перебирает, в ней зачем-то борется с дребезгом кнопки, хотя это можно было бы сделать в самом прерывании, и устанавливает биты событий, которые разбирает в еще одной задаче, где и принимает решение, куда же энкодер повернулся. А чтобы уснуть, в таймере также устанавливается бит события, который обрабатывается в задаче №2...

    Надо ли говорить, что эффективность этого мягко скажем сомнительна... Оленекод во всей красе, беспощадный в своей избыточности. Не хватает еще пары задач, ну чтобы просто нескучно было. :)

    И даже если платные генераторы кода могут написать что-то, что сразу компилируется, это максимум основа для ручной доводки. Правда если закрытая часть ESP-IDF и пр. проприетарных библиотек уже некоторое время пишется ИИ... :)

    PS: следующий видеоролик будет посвящен как раз разбору кода чтения энкодера "здорового человека" (надеюсь :) ) для МК ESP32-C3 (не имеющего PCNT периферии).

    Попросил бесплатный Qwen Code в VS Code создать BLE HID регулятор громкости на энкодере для ESP32-C3 в ESP-IDF.

    Ну то, что код не откомпилируется, сомнений не было никаких. И даже попытка его причесать самому успехом не увенчалась. Придуманные ИИ библиотеки не гуглятся.

    Но давайте посмотрим на то, как ИИ реализовал опрос энкодера (выжимка из кода только по энкодеру):

    // Метки событий для RTOS
    #define ENCODER_EVENT    (1 << 0)
    #define BUTTON_EVENT    (1 << 1)
    #define BLE_EVENT      (1 << 2)
    #define SLEEP_EVENT     (1 << 3)
    
    // Глобальные переменные
    static EventGroupHandle_t event_group;
    
    // Очередь для событий энкодера
    typedef struct {
      int8_t direction; // 1 = clockwise, -1 = counter-clockwise, 0 = button
      bool button_pressed;
    } encoder_event_t;
    
    static QueueHandle_t encoder_queue = NULL;
    
    // Состояние энкодера
    static volatile int8_t encoder_state = 0;
    static volatile int8_t last_encoded_state = 0;
    
    // Обработка прерывания энкодера
    static void IRAM_ATTR encoder_isr_handler(void *arg) {
      int8_t pin_a = gpio_get_level(ENCODER_PIN_A);
      int8_t pin_b = gpio_get_level(ENCODER_PIN_B);
    
      // Gray code decoding
      static const int8_t enc_states[] = {0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0};
    
      encoder_state = ((encoder_state << 2) | (pin_b << 1) | pin_a) & 0x0F;
      int8_t direction = enc_states[encoder_state];
    
      if (direction != 0) {
        encoder_event_t event = {
          .direction = direction,
          .button_pressed = false
        };
        xQueueSendFromISR(encoder_queue, &event, NULL);
      }
    }
    
    // Обработка прерывания кнопки
    static void IRAM_ATTR button_isr_handler(void *arg) {
      encoder_event_t event = {
        .direction = 0,
        .button_pressed = true
      };
      xQueueSendFromISR(encoder_queue, &event, NULL);
    }
    
    // Инициализация энкодера
    static void init_encoder(void) {
      encoder_queue = xQueueCreate(10, sizeof(encoder_event_t));
    
      // Конфигурация пинов энкодера
      gpio_config_t io_conf = {
        .pin_bit_mask = (1ULL << ENCODER_PIN_A) | (1ULL << ENCODER_PIN_B),
        .mode = GPIO_MODE_INPUT,
        .pull_up_en = GPIO_PULLUP_ENABLE,
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
        .intr_type = GPIO_INTR_ANYEDGE
      };
      gpio_config(&io_conf);
    
      // Установка прерываний
      gpio_install_isr_service(0);
      gpio_isr_handler_add(ENCODER_PIN_A, encoder_isr_handler, NULL);
      gpio_isr_handler_add(ENCODER_PIN_B, encoder_isr_handler, NULL);
    
      // Конфигурация кнопки (если подключена)
      if (ENCODER_PIN_BUTTON >= 0) {
        gpio_config_t btn_conf = {
          .pin_bit_mask = (1ULL << ENCODER_PIN_BUTTON),
          .mode = GPIO_MODE_INPUT,
          .pull_up_en = GPIO_PULLUP_ENABLE,
          .pull_down_en = GPIO_PULLDOWN_DISABLE,
          .intr_type = GPIO_INTR_NEGEDGE
        };
        gpio_config(&btn_conf);
        gpio_isr_handler_add(ENCODER_PIN_BUTTON, button_isr_handler, NULL);
      }
    
      ESP_LOGI(TAG, "Encoder initialized (A: GPIO%d, B: GPIO%d, BTN: GPIO%d)", 
           ENCODER_PIN_A, ENCODER_PIN_B, ENCODER_PIN_BUTTON);
    }
    
    // Задача обработки энкодера
    static void encoder_task(void *pvParameters) {
      encoder_event_t event;
    
      while (1) {
        if (xQueueReceive(encoder_queue, &event, portMAX_DELAY) == pdTRUE) {
          if (event.button_pressed) {
            // Дебаунс кнопки
            vTaskDelay(pdMS_TO_TICKS(DEBOUNCE_DELAY_MS));
            if (gpio_get_level(ENCODER_PIN_BUTTON) == 0) {
              xEventGroupSetBits(event_group, BUTTON_EVENT);
            }
          } else if (event.direction != 0) {
            xEventGroupSetBits(event_group, ENCODER_EVENT);
          }
        }
      }
    }
    
    // Задача обработки событий
    static void event_handler_task(void *pvParameters) {
      EventBits_t uxBits;
      static int8_t last_direction = 0;
      static uint32_t last_event_time = 0;
    
      while (1) {
        uxBits = xEventGroupWaitBits(event_group, 
                       ENCODER_EVENT | BUTTON_EVENT | SLEEP_EVENT,
                       pdTRUE, // Clear bits on exit
                       pdFALSE, // Any bit
                       portMAX_DELAY);
    
        uint32_t current_time = xTaskGetTickCount();
    
        if (uxBits & SLEEP_EVENT) {
    ...
        }
    
        if (uxBits & BUTTON_EVENT) {
          send_button_press();
          last_event_time = current_time;
          last_direction = 0;
        }
    
        if (uxBits & ENCODER_EVENT) {
          int8_t pin_a = gpio_get_level(ENCODER_PIN_A);
          int8_t pin_b = gpio_get_level(ENCODER_PIN_B);
    
          // Определение направления
          int8_t direction = 0;
          if (pin_a != pin_b) {
            direction = (pin_a > pin_b) ? 1 : -1;
          }
    
          // Защита от дребезга и слишком быстрой прокрутки
          if (direction != 0 && (direction != last_direction || 
            (current_time - last_event_time) > pdMS_TO_TICKS(100))) {
            send_volume_control(direction);
            last_direction = direction;
            last_event_time = current_time;
          }
        }
      }
    }
    

    Т.е. он сделал два обработчика прерываний (для кнопки и двух контактов энкодера), в них помещает события в очередь, в одной задаче эту очередь перебирает, в ней зачем-то борется с дребезгом кнопки, хотя это можно было бы сделать в самом прерывании, и устанавливает биты событий, которые разбирает в еще одной задаче, где и принимает решение, куда же энкодер повернулся. А чтобы уснуть, в таймере также устанавливается бит события, который обрабатывается в задаче №2...

    Надо ли говорить, что эффективность этого мягко скажем сомнительна... Оленекод во всей красе, беспощадный в своей избыточности. Не хватает еще пары задач, ну чтобы просто нескучно было. :)

    И даже если платные генераторы кода могут написать что-то, что сразу компилируется, это максимум основа для ручной доводки. Правда если закрытая часть ESP-IDF и пр. проприетарных библиотек уже некоторое время пишется ИИ... :)

    PS: следующий видеоролик будет посвящен как раз разбору кода чтения энкодера "здорового человека" (надеюсь :) ) для МК ESP32-C3 (не имеющего PCNT периферии).

    Бесплатный