# APDS-9960 — датчик приближения (Proximity)

Компонент опрашивает оптический датчик APDS-9960 по I2C и отдаёт сырое значение
приближения объекта (0-255). Работает и с оригинальным чипом Broadcom/Avago, и
с распространённым на рынке клоном — подробнее о том, почему это важно и как их
отличить, см. [THEORY.md](THEORY.md).

Компонент реализует только режим **Proximity**. Датчик также умеет измерять
освещённость/цвет (ALS/RGB) и распознавать жесты (Gesture), но эти режимы
здесь не задействованы — у клонов, которые чаще всего оказываются в руках
учеников, они работают нестабильно.

## Файлы

- [include/Apds9960.h](include/Apds9960.h) / [src/Apds9960.cpp](src/Apds9960.cpp) — драйвер.
- [src/main.cpp](src/main.cpp) — тестовый стенд: печатает proximity в Serial Monitor
  и зажигает светодиод при приближении объекта.
- [tools/proximity_view.py](tools/proximity_view.py) — живой график proximity на компьютере
  (см. раздел "Визуализация" ниже).
- [THEORY.md](THEORY.md) — физика ИК-приближения, регистры, проблема клонов.
- [WIRING.md](WIRING.md) — схема подключения.
- [Lesson.md](Lesson.md) — методический план урока для учителя.

## Подключение (кратко)

| APDS-9960 | ESP32 (по умолчанию в `main.cpp`) |
|-----------|-----------------------------------|
| VCC       | 3.3V                               |
| GND       | GND                                 |
| SDA       | GPIO21                              |
| SCL       | GPIO22                              |

Полная схема, включая важное примечание про пин VL, — в [WIRING.md](WIRING.md).

## Быстрый старт

1. Собрать схему по [WIRING.md](WIRING.md).
2. Прошить: `pio run -e esp32dev -t upload`.
3. Открыть Serial Monitor на 115200 бод.
4. При старте датчик сообщает свой ID (см. [main.cpp:19-29](src/main.cpp#L19-L29)) —
   оригинал, известный клон или неопознанный чип. Любой из трёх статусов означает,
   что можно работать дальше.
5. Поднося руку к датчику, наблюдать изменение `Prox:` в мониторе.

## Визуализация

Serial Monitor с текстовыми числами — не самый наглядный формат для группы
учеников. `tools/proximity_view.py` читает тот же вывод и рисует живой график
PDATA с линиями текущих порогов калибровки:

```bash
cd tools
pip install -r requirements.txt
python3 proximity_view.py /dev/ttyUSB0   # порт по умолчанию — /dev/ttyUSB0
```

Serial Monitor прошивки на момент запуска скрипта должен быть закрыт — иначе
порт занят и Python не сможет его открыть. Подробнее, зачем нужен этот график
и что на нём смотреть, — [Lesson.md, блок IV](Lesson.md#блок-iv-калибровка-и-практика-10-15-минут).

## Калибровка порогов

Драйвер намеренно **не решает**, какое значение считать "близко" — сырой разброс
PDATA сильно отличается от экземпляра к экземпляру (см. [THEORY.md](THEORY.md),
раздел "Проблема клонов"). Пороги `THRESHOLD_NEAR/CLOSE/VERY_CLOSE` заданы как
пример в [main.cpp:12-14](src/main.cpp#L12-L14) — их нужно подобрать под свой
датчик, ориентируясь на реальные показания в Serial Monitor.

## API драйвера

| Метод | Назначение |
|-------|------------|
| `begin(sda, scl)` | Инициализация I2C и proximity-движка. `false` — только если чип вообще не отвечает на шине. |
| `readProximity(uint8_t &outValue)` | Сырое значение PDATA (регистр `0x9C`), 0-255. `false` при ошибке транзакции. |
| `getDeviceId()` | ID, прочитанный при `begin()` (регистр `0x92`). |
| `isAlive(timeoutMs)` | Была ли недавняя успешная транзакция с датчиком. |

## Используемые регистры

Полный разбор — в [THEORY.md](THEORY.md#регистры-которые-мы-используем). Здесь только
таблица соответствия коду ([Apds9960.h:29-33](include/Apds9960.h#L29-L33)):

| Регистр | Адрес | Роль в этом драйвере |
|---------|-------|-----------------------|
| ENABLE  | `0x80` | Включение чипа и proximity-движка (PON, PEN) |
| PPULSE  | `0x8E` | Число/длительность ИК-импульсов подсветки |
| CONTROL | `0x8F` | Мощность ИК-светодиода и усиление приёмника |
| ID      | `0x92` | Идентификатор чипа |
| PDATA   | `0x9C` | Результат измерения приближения |

## Известные ограничения

- Диапазон значений PDATA — 0-255, но реальный "рабочий" диапазон зависит от
  конкретного экземпляра датчика (клоны, состояние оптики, засветка).
- Gesture и ALS/RGB не реализованы (см. выше).
- Если `Prox` всегда равен 0 при поднесённой руке — проверьте перемычку VL,
  см. [WIRING.md](WIRING.md#примечание-про-пин-vl).
