# План занятия: «Детектив в коде» — APDS-9960

Формат: один урок 40-45 минут (можно растянуть до спаренного, добавив время
на кейсы из блока IV). Аудитория — 14-17 лет, базовое знакомство с Arduino/C++
и ESP32 приветствуется, но не обязательно.

Цель занятия: собрать рабочий датчик приближения, столкнуться с реальной
инженерной проблемой (чип «врёт» о своём ID) и решить её через прямую работу
с регистрами, а не просто скопировать готовый код.

## Блок I. Физика (7-10 минут)

Заводим разговор с вопроса: «Как ваш телефон понимает, что вы поднесли его
к уху, и гасит экран?» — это тот же тип датчика.

- Показать электромагнитный спектр: видимый свет (400-700 нм) и ИК (940 нм
  у нашего датчика). Свет пульта от телевизора — тот же диапазон.
- Принцип: ИК-светодиод светит → отражается от объекта → фотодиод ловит →
  чем больше отражённого света, тем ближе объект.
- Подробности — [THEORY.md, разделы 1](THEORY.md#1-инфракрасный-дальномер-на-пальцах).

## Блок II. I2C и «детективная» завязка (10 минут)

- Схема подключения — 4 провода, показать [WIRING.md](WIRING.md).
- I2C как «общая линия с адресами»: наш датчик отвечает на `0x39`.
- **Сюжетный поворот**: запускаем код, печатаем ID датчика — и он не совпадает
  с тем, что написано в даташите (`0xAB`)! Многие датчики на руках у учеников
  ответят `0xA8`. Это не брак — это другая ревизия чипа на рынке.
- Обсудить: почему готовые библиотеки для APDS-9960 часто отказываются
  работать с такими модулями? (Они жёстко проверяют ID и останавливаются,
  если он не совпал.) Наш драйвер устроен иначе — см. [Apds9960.cpp:12-14](src/Apds9960.cpp#L12-L14):
  он не паникует из-за незнакомого ID, а просто запоминает его и продолжает работу.

## Блок III. Регистры и код (12-15 минут)

- Регистр — ячейка памяти внутри чипа, которой можно управлять напрямую.
  Таблица регистров — [THEORY.md, раздел 3](THEORY.md#3-регистры-которые-мы-используем).
- Разобрать `begin()` построчно ([Apds9960.cpp:5-19](src/Apds9960.cpp#L5-L19)):
  выключили → прочитали ID → настроили мощность ИК-диода и импульсы →
  включили → подождали прогрев.
- Запустить прошивку, открыть Serial Monitor, увидеть живые цифры `Prox:`.
- Обсудить, почему `readRegister`/`writeRegister` проверяют код возврата
  `endTransmission()` ([Apds9960.cpp:45-56](src/Apds9960.cpp#L45-L56)) —
  провод может отвалиться в любой момент, и код должен это заметить, а не
  молча показывать последнее старое значение.

## Блок IV. Калибровка и практика (10-15 минут)

- Запустить живой график [tools/proximity_view.py](tools/proximity_view.py) на
  проекторе или на компьютере каждого ученика (`pip install -r requirements.txt`,
  затем `python3 proximity_view.py <порт>`, Serial Monitor прошивки при этом
  должен быть закрыт). Числа в тексте скачут и их трудно оценить на глаз —
  на графике сразу видно, где реально проходит рука относительно линий порогов.
- Задание классу: поднести руку на разных расстояниях, посмотреть на график.
- Обсудить, почему у соседа по парте кривая идёт иначе (разные экземпляры
  клонов, разная одежда/цвет руки, освещение в кабинете).
- Каждый подбирает свои `THRESHOLD_NEAR/CLOSE/VERY_CLOSE` в
  [main.cpp:12-14](src/main.cpp#L12-L14) и добивается, чтобы линии порогов на
  графике проходили там, где удобно, а светодиод зажигался в нужный момент.
- Мини-кейсы для тех, кто закончил раньше:
  - Бесконтактный «выключатель» — датчик за листом бумаги/пластика.
  - Сигнализация: пищит/мигает, если рука дольше 2 секунд в зоне CLOSE.

## Вопросы для обсуждения (можно использовать как проверку понимания)

1. **Почему стандартная библиотека может не заработать сразу?**
   Потому что она жёстко проверяет ID устройства (регистр `0x92`) и
   останавливается, если он не совпал с ожидаемым. Наш драйвер этого не
   делает — см. блок II.

2. **Зачем мы сами пишем в регистры PPULSE и CONTROL, а не полагаемся на
   настройки «по умолчанию»?**
   Чтобы управлять физическими параметрами — мощностью ИК-подсветки и
   усилением приёмника — напрямую, а не гадать, что выставил производитель.

3. **Почему пороги NEAR/CLOSE/VERY_CLOSE не зашиты в драйвер, а лежат в
   `main.cpp`?**
   Потому что разброс сырых значений между экземплярами датчика большой —
   один порог не подойдёт всем. Калибровка — это часть работы с конкретным
   железом, а не константа библиотеки.

4. **Почему в этом компоненте нет распознавания жестов?**
   Инженерный компромисс: жесты требуют FIFO и заметно больше кода, а на
   клонах, которые чаще всего встречаются, работают нестабильно. Мы выбрали
   то, что стабильно работает на 100%, вместо красивой, но капризной функции.

## Адаптация по уровню класса

- **7-8 класс**: регистры — «магические числа», которые просто нужно
  оставить как есть. Фокус — собрать схему и подобрать пороги в `main.cpp`.
- **9-10 класс**: добавить обсуждение датащита и протокола I2C — почему
  адрес `0x39`, что такое `beginTransmission`/`requestFrom`.
- **11 класс / профиль**: разобрать `readRegister`/`writeRegister` целиком,
  обсудить обработку ошибок шины и что означает `isAlive()`
  ([Apds9960.cpp:32-34](src/Apds9960.cpp#L32-L34)) — как отличить «объект
  далеко» от «датчик отвалился».

## Материалы для раздачи

- Схема подключения из [WIRING.md](WIRING.md).
- Таблица регистров из [THEORY.md](THEORY.md#3-регистры-которые-мы-используем).
- Готовый шаблон кода ([src/main.cpp](src/main.cpp)) — ученики фокусируются
  на калибровке порогов, а не на написании драйвера с нуля.
