Optical Channel Tester — ESP32-C3 / ESP32-S3
Полностью рабочий Arduino IDE-проект для строгой проверки оптического цифрового канала. Устройство формирует PWM аппаратным LEDC, пропускает его через проверяемый канал и проверяет каждый завершённый период отдельно: частоту и заполнение. Поддерживаются роли SOLO, MASTER и SLAVE; роли выбираются только вручную.
Что реализовано
- неблокирующий конечный автомат без
pulseIn()и длинныхdelay(); - аппаратный LEDC с расчётом реально получившихся частоты, разрядности и duty;
- Arduino-ESP32 3.3.10: потоковый аппаратный RMT RX (
partial RX), ping-pong на C3 и DMA на S3; - безопасный fallback для старых Arduino-ESP32 3.x через GPIO edge ISR и аппаратный CPU cycle counter (верхний предел при этом автоматически ограничен 100 кГц);
- отбрасывание ровно
PWM_SETTLE_CYCLESполных периодов; - потоковая статистика без хранения всех периодов;
- немедленный FAIL по первому плохому периоду;
- десять измерительных участков на каждой частоте с промежуточным отображением результата;
- ESP-NOW discovery, handshake, CRC, session/stage/sequence, ACK, повторы и защита от старых пакетов;
- SSD1306 128×32: каждый экран всегда состоит ровно из двух строк;
- две кнопки, debounce, long press и repeat; после long press ложный short click не создаётся;
- Preferences/NVS с версией структуры, checksum, проверкой индексов и восстановлением defaults;
- работа через Serial при отсутствующем OLED;
- безопасное выключение PWM при PASS, FAIL, ABORT и потере связи.
- полный журнал действий в Serial с временными метками; OLED необязателен.
Файлы
Arduino sketch находится в каталоге OpticalChannelTester:
OpticalChannelTester.ino— стандартная точка входа Arduino IDE;Config.h— GPIO, тайм-ауты, пределы и все пользовательские массивы;Core.*— последовательность частот, ALL, допуски, статистика;Buttons.*— автомат двух кнопок;SettingsStore.*— NVS;Display.*— OLED и компактное форматирование;Log.*— журнал действий и Serial-зеркало интерфейса;Pwm.*— LEDC;Receiver.*— RMT RX / совместимый fallback;Measurement.*— строгая проверка периодов;Protocol.*,Radio.*— ESP-NOW;App.*— общий конечный автомат SOLO/MASTER/SLAVE.
Каталог tests содержит локальные unit-тесты чистой логики и автомата кнопок.
Требования Arduino IDE
Проверенная конфигурация:
- Arduino IDE 2.x;
- пакет плат esp32 by Espressif Systems 3.3.10;
- Adafruit GFX Library 1.12.1;
- Adafruit SSD1306 2.5.15;
- Adafruit BusIO устанавливается Library Manager как зависимость.
В Arduino IDE откройте OpticalChannelTester/OpticalChannelTester.ino. Не переносите один .ino отдельно: остальные вкладки являются частью скетча.
Для C3 выберите подходящую плату ESP32-C3, например ESP32C3 Dev Module. Для S3 — ESP32S3 Dev Module. Затем выберите порт и нажмите Verify/Upload.
GPIO
Все назначения находятся в начале Config.h и могут быть изменены до сборки.
Сейчас в Config.h включён #define MAKETKA. В этом профиле используются следующие назначения:
| Сигнал | ESP32-C3 | ESP32-S3 |
|---|---|---|
| PWM output | GPIO 3 | GPIO 12 |
| Optical RX input | GPIO 4 | GPIO 13 |
| START | GPIO 1 | GPIO 10 |
| MODE | GPIO 0 | GPIO 9 |
| OLED SDA | GPIO 6 | GPIO 44 |
| OLED SCL | GPIO 7 | GPIO 1 |
Если закомментировать #define MAKETKA, включается профиль производственной PCB:
| Сигнал | ESP32-C3 | ESP32-S3 |
|---|---|---|
| PWM output | GPIO 3 | GPIO 12 |
| Optical RX input | GPIO 4 | GPIO 13 |
| START | GPIO 10 | GPIO 4 |
| MODE | GPIO 20 | GPIO 5 |
| VBAT | GPIO 2 | GPIO 11 |
| Analog RX | GPIO 0 | GPIO 9 |
| OLED SDA | GPIO 6 | GPIO 44 |
| OLED SCL | GPIO 7 | GPIO 1 |
Оба профиля рассчитаны на установку S3 с совмещением контактов 5V и GND с модулем C3. Номера GPIO S3 выбраны по тому же физическому ряду разъёма, а не по совпадению номера GPIO.
На ESP32-C3 PWM формируется через LEDC с целым делителем. На ESP32-S3 используется отдельный аппаратный MCPWM со счётчиком 40 МГц и произвольным целым периодом.
OLED
Подключите SSD1306 128×32: VCC → 3.3 V, GND → GND, SDA/SCL по таблице. Адрес по умолчанию 0x3C. Если OLED не отвечает, тест продолжает работать и пишет диагностику в Serial 115200.
Работа вообще без OLED
OLED можно не подключать. Откройте Serial Monitor на 115200 baud: каждый экран всегда дублируется одной строкой вида:
[ 1250][UI ] MODE: SOLO | START=RUN
Полоса прогресса учитывает номер частоты и десять измерительных участков внутри неё. Например, диапазон из 11 частот даёт 110 последовательных позиций прогресса.
В Serial также выводятся:
- каждое распознанное нажатие START/MODE и текущее состояние автомата;
- вход, изменение и сохранение каждого пункта меню;
- загрузка, проверка и сохранение NVS;
- запуск/остановка PWM и реальные параметры LEDC;
- начало этапа, число отбрасываемых периодов и параметры измерительного окна;
- все действия ESP-NOW, peer, session/stage/sequence, ACK и повторы;
- статистика этапа, первый плохой период и итоговая причина завершения;
- все строки, которые были бы показаны на OLED.
Во время строгого измерительного окна отдельные импульсы намеренно не печатаются: они проверяются потоково, а итоговая статистика выводится после окна. Это предотвращает влияние Serial на точность и переполнение очереди RMT. Журнал действий включён параметрами в Config.h:
constexpr bool SERIAL_ACTION_LOG = true;
constexpr bool SERIAL_LOG_TIMESTAMPS = true;
Кнопки
По умолчанию задано:
#define BUTTON_ACTIVE_LEVEL LOW
Поэтому каждая кнопка подключается между своим GPIO и GND, а прошивка автоматически включает INPUT_PULLUP.
Если установить BUTTON_ACTIVE_LEVEL HIGH, подключайте кнопку между GPIO и 3.3 V; автоматически будет использован INPUT_PULLDOWN. Других изменений логики не требуется.
Подключение сигнала
SOLO
ESP GPIO_PWM -> вход передатчика оптического канала
выход приёмника оптического канала -> ESP GPIO_RX
GND ESP -> GND входной/выходной электроники (если канал не гальванически развязан)
MASTER / SLAVE
MASTER GPIO_PWM -> вход передатчика проверяемого канала
выход приёмника проверяемого канала -> SLAVE GPIO_RX
MASTER <~~~~ ESP-NOW Wi-Fi channel 6 ~~~~> SLAVE
На обеих ESP должны совпадать ESPNOW_WIFI_CHANNEL и версия прошивки. Slave сначала переводится в SLAVE READY кнопкой START, затем START нажимается на Master.
Пока бодрствующий Slave находится в SLAVE READY, радиоприём работает периодически: окно 20 мс каждые 100 мс, а собственный оптический передатчик Slave отключён уровнем PWM_SAFE_LEVEL. При переходе Slave в light sleep ESP-NOW и Wi-Fi полностью отключаются, поэтому радиопакет не может его разбудить. Master во время поиска посылает серию DISCOVER каждые 20 мс и одновременно переключает оптический выход между PWM_ACTIVE_LEVEL и PWM_SAFE_LEVEL каждые OPTICAL_WAKE_HALF_PERIOD_MS. Изменение уровня на оптическом входе пробуждает Slave через GPIO; после этого Slave запускает ESP-NOW и принимает повторяемый DISCOVER. Постоянный HIGH или LOW сам по себе повторных пробуждений не вызывает. После обнаружения Master прекращает wake-сигнал, а Slave включает непрерывный радиоприём на всю тестовую сессию. Параметры задаются в Config.h.
GPIO ESP32 допускают только логические уровни 0…3.3 В. Не подавайте 5 В на GPIO_RX; применяйте согласование уровня. Полярность выхода оптического приёмника задаётся однозначно его активным уровнем:
#define RX_ACTIVE_LEVEL HIGH // либо LOW
Управление
В ожидании:
- MODE short:
SOLO → MASTER → SLAVE → SOLO, выбранная роль сохраняется; - MODE long: открыть настройки;
- START short: начать тест;
- START long во время теста: ABORT, PWM немедленно выключается.
В настройках:
- MODE short: следующий параметр;
- MODE long: проверить диапазон, сохранить один раз в NVS и выйти;
- START short: следующее значение;
- START long: предыдущее значение, затем autorepeat при удержании.
Удержание обеих кнопок минимум 1.5 с при включении восстанавливает defaults.
Энергосбережение
В ожидании процессор работает на 80 МГц, во время теста и связанной MASTER/SLAVE-сессии — на 160 МГц. Пока Solo или Master бодрствует и тест не идёт, выход передатчика удерживается на PWM_ACTIVE_LEVEL; в режиме Slave передатчик всегда отключён уровнем PWM_SAFE_LEVEL. При запуске теста, остановленном PWM и после тайм-аута сна также используется PWM_SAFE_LEVEL — уровень полного выключения с минимальным потреблением тока. После тайм-аута бездействия в IDLE, MENU, FINISHED или SLAVE READY OLED выключается, а ESP переходит в непрерывный light sleep без периодических таймерных пробуждений. Экран и текущее состояние сохраняются. START и MODE будят любую роль, а фронт на оптическом входе дополнительно будит Slave. При использовании встроенного USB Serial/JTAG для следующей прошивки может потребоваться ручной вход в загрузчик кнопкой BOOT.
START и MODE будят устройство. Пробуждающее нажатие намеренно не выполняет действие и полностью игнорируется до отпускания кнопки; действие выполняет только следующее нажатие. Принятый DISCOVER также немедленно выводит Slave из энергосбережения. Тайм-аут задаётся IDLE_POWER_SAVE_TIMEOUT_MS в Config.h.
Настройка диапазона
START и END выбираются из отдельных массивов START_FREQ_OPTIONS_HZ и END_FREQ_OPTIONS_HZ в Config.h и зацикливаются независимо. Полный список точных частот заранее рассчитан для XTAL 40 МГц, целого делителя LEDC и таймера 1…14 бит и записан в TEST_FREQUENCIES_HZ. Сохранённые индексы всегда проверяются; после изменения массивов повреждённая/несовместимая настройка не приводит к выходу за границы.
Отдельной настройки STEP нет: тест начинается с выбранной START, проходит все соседние достижимые точки из полного списка и заканчивается на выбранной END.
ALL пересчитывается после изменения START, END или TEST TIME и отображается с точностью до целой секунды. Для каждой точки учитывается:
max(TEST_TIME, periods / RX_PROCESSING_PERIODS_PER_SECOND)
+ ожидание заполнения RMT-пакета × 10
+ PWM_SETTLE_CYCLES / actualFrequency × 10
+ OLED_PROGRESS_UPDATE_MS × 11
TEST TIME — это чистое время выборки сигнала, а не полная длительность этапа. На высоких частотах полная длительность заметно возрастает из-за проверки каждого периода. Расчёт использует консервативную производительность 300 тысяч периодов в секунду, полученную из реального журнала ESP32-C3.
Строгая проверка
После каждой перенастройки PWM приёмник:
- выбирает максимальную допустимую частоту RMT 20, 40 или 80 МГц с учётом частоты и duty;
- отбрасывает ровно
PWM_SETTLE_CYCLESполных периодов; - очищает статистику;
- проверяет все полные периоды десяти участков TEST TIME и между ними показывает промежуточный результат.
Все тесты формируют PWM с фиксированным duty TEST_DUTY_PCT = 50%. Настройки duty в меню нет, но фактически подаваемое значение по-прежнему отображается во время теста и передаётся между MASTER и SLAVE.
Частота и duty проверяются по среднему за MEASUREMENT_AVERAGING_PERIODS полных периодов (по умолчанию 100). Ошибка возникает, если среднее окна устойчиво выходит за выбранный допуск 1%, 2%, 5% или 10%. Для частоты последовательные периоды образуют один интервал от первого до последнего одинакового фронта, поэтому квантовочная неопределённость составляет один такт на всё окно и применяется только при наличии соседних значений RMT. Активные интервалы duty ограничены отдельной парой разных фронтов в каждом периоде, поэтому для duty допускается один такт активной длительности на каждый период даже тогда, когда всё короткое окно квантовалось в одно значение. Например, 41/80 при ожидаемых 50% объясняется одним тактом, а 42/80 при точности 1% уже завершается ошибкой.
Незавершённый период в начале и конце временного участка не учитывается. Перед перезапуском RMT между десятью участками незавершённое окно усреднения сбрасывается: новая фаза квантования не сравнивается с предыдущей. Общая статистика и итоговое среднее по всем принятым полным периодам этапа сохраняются.
Причины: NO SIGNAL, PERIOD OUT, DUTY OUT, EXTRA EDGE, GLITCH, LOST EDGE, DATA LOSS ERROR, LINK LOST, UNSUPPORTED, RESOLUTION, ABORTED.
Пределы
- C3: гарантированный проектный диапазон до 10 кГц; настраиваемый строгий предел по умолчанию 100 кГц;
- S3: строгий предел до 1 МГц только при рабочем RMT DMA;
- выше строгого аппаратного предела выдаётся
UNSUPPORTED, ослабленной проверки нет; - комбинации, для которых LEDC или RX timer не дают выбранную точность, завершаются
RESOLUTIONдо запуска заведомо неверного теста.
Реальный предел зависит от конкретной платы, разводки, формы фронтов, Wi-Fi нагрузки и оптического оборудования. Начинайте проверку с 100 Гц…10 кГц.
Первый запуск SOLO
- Соберите схему SOLO и ещё раз убедитесь, что на
GPIO_RXне бывает напряжения выше 3.3 В. - Откройте Serial Monitor на 115200 baud.
- Включите плату. На OLED должно быть
MODE: SOLO/START=RUN. - Если выбран другой режим, коротко нажимайте MODE до SOLO.
- Для первого опыта оставьте defaults: 100 Гц…10 кГц, duty 50%, accuracy 5%.
- Коротко нажмите START.
- Serial покажет запрошенные и фактические параметры LEDC, список частот, ALL и статистику каждой точки.
- Успех:
PASS .../START=AGAIN. Ошибка:FAIL AT ...и точная причина.
Проверка проекта
Целевая версия проекта — Arduino-ESP32 3.3.10. В доступной среде исходный код дополнительно собран Arduino CLI с установленным Arduino-ESP32 3.3.0:
| Target | Arduino-ESP32 | Flash | RAM | Результат |
|---|---|---|---|---|
| ESP32-C3 | 3.3.0 | 1,049,316 B (80%) | 44,312 B (13%) | PASS |
| ESP32-S3 | 3.3.0 | 980,407 B (74%) | 53,456 B (16%) | PASS |
Локальные unit-тесты: core tests: PASS, button tests: PASS. Они покрывают неделимый диапазон, END без дубля, ALL, границы допусков, немедленный FAIL, resolution, checksum настроек, CRC протокола и отсутствие short после long.
Физическое оборудование в этой среде недоступно, поэтому реальные оптические фронты, RMT DMA под длительной нагрузкой, дальность ESP-NOW и электрическая совместимость должны быть проверены на ваших платах. Сборка и программные тесты не выдаются за аппаратный тест.