# 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` и могут быть изменены до сборки. | Сигнал | ESP32-C3 | ESP32-S3 | |---|---:|---:| | PWM output | GPIO 3 | GPIO 4 | | Optical RX input | GPIO 4 | GPIO 5 | | START | GPIO 0 | GPIO 6 | | MODE | GPIO 1 | GPIO 7 | | OLED SDA | GPIO 6 | GPIO 8 | | OLED SCL | GPIO 7 | GPIO 9 | ### OLED Подключите SSD1306 128×32: `VCC → 3.3 V`, `GND → GND`, `SDA/SCL` по таблице. Адрес по умолчанию `0x3C`. Если OLED не отвечает, тест продолжает работать и пишет диагностику в Serial 115200. ### Работа вообще без OLED OLED можно не подключать. Откройте Serial Monitor на **115200 baud**: каждый экран всегда дублируется одной строкой вида: ```text [ 1250][UI ] MODE: SOLO | START=RUN ``` В Serial также выводятся: - каждое распознанное нажатие START/MODE и текущее состояние автомата; - вход, изменение и сохранение каждого пункта меню; - загрузка, проверка и сохранение NVS; - запуск/остановка PWM и реальные параметры LEDC; - начало этапа, число отбрасываемых периодов и параметры измерительного окна; - все действия ESP-NOW, peer, session/stage/sequence, ACK и повторы; - статистика этапа, первый плохой период и итоговая причина завершения; - все строки, которые были бы показаны на OLED. Во время строгого измерительного окна отдельные импульсы намеренно не печатаются: они проверяются потоково, а итоговая статистика выводится после окна. Это предотвращает влияние Serial на точность и переполнение очереди RMT. Журнал действий включён параметрами в `Config.h`: ```cpp constexpr bool SERIAL_ACTION_LOG = true; constexpr bool SERIAL_LOG_TIMESTAMPS = true; ``` ### Кнопки По умолчанию задано: ```cpp #define BUTTON_ACTIVE_LEVEL LOW ``` Поэтому каждая кнопка подключается между своим GPIO и GND, а прошивка автоматически включает `INPUT_PULLUP`. Если установить `BUTTON_ACTIVE_LEVEL HIGH`, подключайте кнопку между GPIO и 3.3 V; автоматически будет использован `INPUT_PULLDOWN`. Других изменений логики не требуется. ## Подключение сигнала ### SOLO ```text ESP GPIO_PWM -> вход передатчика оптического канала выход приёмника оптического канала -> ESP GPIO_RX GND ESP -> GND входной/выходной электроники (если канал не гальванически развязан) ``` ### MASTER / SLAVE ```text MASTER GPIO_PWM -> вход передатчика проверяемого канала выход приёмника проверяемого канала -> SLAVE GPIO_RX MASTER <~~~~ ESP-NOW Wi-Fi channel 6 ~~~~> SLAVE ``` На обеих ESP должны совпадать `ESPNOW_WIFI_CHANNEL` и версия прошивки. Slave сначала переводится в `SLAVE READY` кнопкой START, затем START нажимается на Master. **GPIO ESP32 допускают только логические уровни 0…3.3 В.** Не подавайте 5 В на `GPIO_RX`; применяйте согласование уровня. Если оптический приёмник инвертирует сигнал, установите: ```cpp #define RX_SIGNAL_INVERTED true ``` ## Управление В ожидании: - 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. ## Настройка диапазона Редактируйте отдельные `constexpr`-массивы в `Config.h`. Начальная и конечная частоты намеренно находятся в разных массивах. Сохранённые индексы всегда проверяются; после изменения массивов повреждённая/несовместимая настройка не приводит к выходу за границы. Последовательность всегда начинается точно с START, идёт с STEP и завершается точно END. Например, `100…1000` с шагом `300` даёт `100, 400, 700, 1000`. Конечная точка не дублируется, вычисления выполняются через 64-битные промежуточные значения. `ALL` пересчитывается после изменения START, END, STEP, TEST TIME или REPEATS. Частота каждой точки предварительно запрашивается у LEDC с duty=0 (на выходе остаётся безопасный уровень), поэтому в расчёте используется фактически достижимая частота. Для каждой точки учитывается: ```text PWM_SETTLE_CYCLES / actualFrequency + TEST_TIME * REPEATS ``` ## Строгая проверка После каждой перенастройки PWM приёмник: 1. отбрасывает ровно `PWM_SETTLE_CYCLES` полных периодов; 2. очищает статистику; 3. непрерывно проверяет все полные периоды всех повторов. Незавершённый период в начале и конце окна не учитывается. Период, пересекающий границу повторов, не теряется. Для каждого периода отдельно вычисляются частота и duty; средние используются только для диагностики. Любой один выход за допуск немедленно завершает всю проверку. Причины: `NO SIGNAL`, `PERIOD OUT`, `DUTY OUT`, `EXTRA EDGE`, `GLITCH`, `LOST EDGE`, `TOO FEW PERIODS`, `LINK LOST`, `UNSUPPORTED`, `RESOLUTION`, `ABORTED`. ### Пределы - C3: гарантированный проектный диапазон до 10 кГц; настраиваемый строгий предел по умолчанию 100 кГц; - S3: строгий предел до 1 МГц только при рабочем RMT DMA; - выше строгого аппаратного предела выдаётся `UNSUPPORTED`, ослабленной проверки нет; - комбинации, для которых LEDC или RX timer не дают выбранную точность, завершаются `RESOLUTION` до запуска заведомо неверного теста. Реальный предел зависит от конкретной платы, разводки, формы фронтов, Wi-Fi нагрузки и оптического оборудования. Начинайте проверку с 100 Гц…10 кГц. ## Первый запуск SOLO 1. Соберите схему SOLO и ещё раз убедитесь, что на `GPIO_RX` не бывает напряжения выше 3.3 В. 2. Откройте Serial Monitor на 115200 baud. 3. Включите плату. На OLED должно быть `MODE: SOLO` / `START=RUN`. 4. Если выбран другой режим, коротко нажимайте MODE до SOLO. 5. Для первого опыта оставьте defaults: 100 Гц…10 кГц, duty 50%, accuracy 5%. 6. Коротко нажмите START. 7. Serial покажет запрошенные и фактические параметры LEDC, список частот, ALL и статистику каждой точки. 8. Успех: `PASS ...` / `START=REPEAT`. Ошибка: `FAIL AT ...` и точная причина. ## Проверка проекта Финальный исходный код собран Arduino CLI, использующим тот же builder, что и Arduino IDE: | Target | Arduino-ESP32 | Flash | RAM | Результат | |---|---:|---:|---:|---| | ESP32-C3 | 3.3.10 | 1,024,299 B (78%) | 39,460 B (12%) | PASS | | ESP32-S3 | 3.3.10 | 950,608 B (72%) | 48,620 B (14%) | PASS | Локальные unit-тесты: `core tests: PASS`, `button tests: PASS`. Они покрывают неделимый диапазон, END без дубля, ALL, границы допусков, немедленный FAIL, resolution, checksum настроек, CRC протокола и отсутствие short после long. Физическое оборудование в этой среде недоступно, поэтому реальные оптические фронты, RMT DMA под длительной нагрузкой, дальность ESP-NOW и электрическая совместимость должны быть проверены на ваших платах. Сборка и программные тесты не выдаются за аппаратный тест.