# Подключение, безопасность, калибровка

## Щупы

У датчика два кабеля, на каждом по паре штекеров-«бананов»:

- **красный** — измерительный вход («+»);
- **синий** — общий провод канала («−»).

Какой кабель программа называет К1, а какой К2, проще всего проверить так:
запустите опыт «Проверка с ESP32» и подайте сигнал на один кабель. Подпишите
кабели маркером или кусочком изоленты, чтобы на уроке не путаться.

Если штекер ни во что не втыкается, наденьте на него «крокодил» или зажмите в
гнезде штекера зачищенный провод.

## Безопасность

> **Сеть 220 В к датчику не подключать — ни напрямую, ни «через что-нибудь».**

По паспорту датчик выдерживает ±100 В, но между входами и корпусом USB-разъёма
всего 0,53 МОм. Корпус USB соединён с ноутбуком, а через блок питания ноутбука
и с сетевой землёй. Датчик **не развязан** с компьютером.

Разрешено подключать:

- батарейки и аккумуляторы;
- лабораторные блоки питания и адаптеры с низким напряжением на выходе;
- платы Arduino / ESP32 и собранные на уроке схемы с их питанием;
- **вторичную** обмотку понижающего трансформатора (6–12 В), когда сам
  трансформатор в корпусе и его сетевая часть закрыта.

Выбирайте предел с запасом: сначала ±25 В, потом уменьшайте. Если в таблице
замеров написано «Зашкал: да», напряжение вышло за предел.

## Если не работает

| Что пишет программа | Что делать |
|---|---|
| `Датчик не найден` | Проверьте, что после подключения появился `/dev/ttyUSB0`: `ls /dev/ttyUSB*`. Другой разъём USB, другой кабель. |
| `Нет доступа к /dev/ttyUSB0` | Нужен администратор, один раз: `sudo sh deploy/install.sh` (правило udev для всех, кто сидит за компьютером) или `sudo usermod -aG dialout $USER` и **перезайти в систему**. |
| `датчик не ответил на запрос «I»` | Отключите и снова подключите датчик. На этом порту может быть другое устройство: тогда укажите порт явно, например `python3 xstem-scope.py --port /dev/ttyUSB1`. |
| Страница пишет «нет связи с программой» | Программа в терминале закрыта или упала — запустите снова и обновите страницу. |
| «датчик отключён — вставьте его и нажмите «Пуск»» | USB выдернули. Вставьте обратно, через 5 секунд статус сменится на «остановлен» — нажмите «Пуск». Программу перезапускать не нужно. |
| Датчик только что вставили, «Пуск» — и кадров нет или они идут слишком часто | Первые секунды после включения датчик мерит со своими настройками. Нажмите «Стоп» и снова «Пуск». Программа сама ждёт эти секунды; на сайте выберите датчик в окне не сразу, а через 5 секунд после подключения. |
| Статус «ждём синхронизацию» | Сигнал не пересекает уровень синхронизации: проверьте уровень, канал и фронт или выберите «нет (свободно)». |
| Порт 8765 занят | `python3 xstem-scope.py --http 8766` и адрес `http://localhost:8766`. |
| Сайт в Chromium: датчика нет в окне выбора или «Не удалось подключиться» | Нет доступа к порту — то же правило udev, что и выше. В Ubuntu с Chromium из snap: `sudo snap connect chromium:raw-usb`. |
| Сайт: «Этот браузер не умеет работать с USB» | Браузер без Web Serial (например, старый Firefox): обновите его, откройте страницу в Chromium или запустите программу. |

Две копии программы одновременно с одним датчиком работать не могут.

## Генератор сигналов на ESP32

Чтобы проверить датчик, нужен сигнал, который заранее известен. Его даёт
ESP32 DevKit v1 с прошивкой [`src/main.cpp`](src/main.cpp). Она же —
генератор для показа на уроке: синусоида, треугольник, пила, ШИМ.

| Вывод ESP32 | Сигнал |
|---|---|
| GPIO25 | выход генератора; после включения — меандр 100 Гц, 0 / 3,3 В |
| GPIO27 | всегда меандр 1 кГц, 0 / 3,3 В |
| GND | общий |

Прошить: в каталоге этого компонента выполнить `pio run -t upload`. Нужен
`pio` из pioarduino — проверено с pioarduino Core 6.2.0, поставленным через uv:
`uv tool install https://github.com/pioarduino/platformio-core/archive/refs/tags/v6.2.0.zip`.
Выпуск 6.2.0.post1 платформа отвергает: он называет себя 6.2.0-post.1, а это
по правилам версий «меньше 6.2.0». Если программа xSTEM Scope в это время
запущена, после прошивки перезапустите её: esptool оставляет порт платы на
460800 бод, и панель ESP32 на странице замолкает. Подключение: красный штекер — GPIO25, синий — GND.
Опыт «Проверка с ESP32» в программе настроен под это подключение.

Форму проще всего менять **прямо на странице xSTEM Scope**: панель «Генератор
ESP32» в правой колонке — форма, частота, размах или заполнение. Программа
`xstem-scope.py` находит плату сама (мосты CP2102 и CH340), а на сайте в
браузере нажмите «Подключить генератор» и выберите плату в окне браузера.

Те же команды можно вводить в мониторе порта (`pio device monitor`, 115200):

| Команда | Сигнал на GPIO25 | Что показать |
|---|---|---|
| `sin 100` | синусоида 100 Гц | амплитуда и действующее значение: отношение ≈1,41 |
| `tri 100` | треугольник | линейное нарастание и спад |
| `saw 100` | пила | так устроена развёртка в старых осциллографах и телевизорах |
| `sq 100` | меандр через ЦАП | сравнить с цифровым: фронты те же, размах меньше |
| `damp 200` | затухающие колебания 200 Гц пачками | колебательный контур, удар по камертону |
| `amp 1.0` | размах форм выше, В (0,1…2,9) | пределы ±2 и ±5 В, «Зашкал» |
| `pwm 1000 25` | ШИМ 1 кГц, заполнение 25 % | «Среднее» в таблице = 25 % от 3,3 В |
| `78` (1…255) | меандр 100 Гц 0 / (число / 255 · 3,3 В) | калибровка предела ±2 В |
| `0` | цифровой меандр 100 Гц 0 / 3,3 В | исходный режим |
| `?` | что сейчас на выходе | |

Формы со встроенного ЦАП идут вокруг 1,65 В (от 0,3 до 3 В). Чтобы сигнал
встал по центру экрана, включите у канала вход «~». ЦАП 8-битный: на
пределе ±2 В видны ступеньки — это тоже повод поговорить об оцифровке.

**Размах форм через ЦАП меньше заданного.** Прошивка считает, что код ЦАП 255 —
это 3,3 В, но встроенный ЦАП ESP32 до питания не дотягивает. Проверено
2026-11-04 на ESP32 DevKit v1: синусоида 100 Гц с «Размах 2,6 В» дала 2,41 В
(на 7 % меньше), то есть полная шкала ЦАП около 3,07 В; частота и форма точные
(100,03 Гц, отклонение от синусоиды не больше 38 мВ). Цифровой меандр с GPIO25
тем же датчиком — 3,27 В, так что ошибается ЦАП, а не датчик. Прошивку под одну
плату не подстраиваем: «осциллограф поймал генератор на неточности» — готовый
сюжет для урока. Для калибровки датчика берите цифровой меандр, а не ЦАП.

## Звуковая карта как генератор

В комплекте НАУРА есть жёлтая плата со значком «~»: аудиоштекер на три
контакта, две латунные клеммы, резистор 20 Ом. Штекер — в разъём наушников
ноутбука или телефона, зажимы любого канала датчика — на клеммы. Генератором
становится любой сайт «генератор тона»: синусоида от 20 Гц до 20 кГц, частоту
крутит сам ученик.

**Плата меряет разность левого и правого канала.** Проверено 2026-11-07: тон
1000 Гц только в левом канале — 0,16 В, только в правом — 0,16 В, в
противофазе — 0,31 В, одинаково в оба — почти ноль. Сайты-генераторы играют
одинаково в оба канала, поэтому перед опытом баланс выхода «Наушники» —
до упора в одну сторону (Параметры → Звук). Иначе на экране ровная линия, и
кажется, что сломан датчик.

**Размах** — около 0,2 В при громкости 34 % и 0,8 В при 70–80 %, предел ±2 В.
Звуковой выход пропускает только переменное: постоянного напряжения и
частот ниже ~20 Гц с него не получить.

Без браузера тон даёт `speaker-test -t sine -f 1000 -c 2`: он играет каналы по
очереди, поэтому работает и без баланса, но выше 8000 Гц не идёт (просили
9000 — получили 8000).

Опыт с этой платой — урок [«Прибор врёт»](Lesson.md#урок-прибор-врёт-8-класс-45-мин).

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

Датчик передаёт не вольты, а код АЦП от 0 до 4095 (подробно в
[THEORY.md](THEORY.md)). Чтобы перевести код в вольты, нужно знать, сколько
милливольт приходится на один код на каждом пределе. Мы измерили это на
меандре 3,3 В от ESP32:

| Предел | GAIN | мВ на код | Чем измерено |
|---|---|---|---|
| ±90 В | 0 | 44,1 | меандр 3,3 В = 75 кодов |
| ±25 В | 1 | 12,03 | меандр 3,3 В = 274 кода |
| ±5 В | 2 | 2,48 | меандр 3,3 В = 1330 кодов |
| ±2 В | 3 | 0,99 | один и тот же сигнал ЦАП на ±5 В и на ±2 В: отношение 2,50 |

Это общая калибровка: она записана в [`app/b202.json`](app/b202.json),
раздел `calibration.default`, и действует для любого датчика, у которого нет
своей.

### Своя калибровка датчика

Датчики из одной коробки отличаются на несколько процентов. Чтобы каждый
показывал точно, его калибровку записывают по серийному номеру. Номер страница
пишет в заголовке после «№», рядом — какая калибровка действует: «своя» или
«общая».

1. Замерьте мультиметром напряжение на выводе 3V3 у ESP32. Оно может быть,
   например, 3,28 В, а не ровно 3,3. Лучше — подать один и тот же сигнал на
   датчик и на Rigol.
2. Подайте меандр с GPIO25 и посмотрите «Размах» в таблице замеров на каждом
   пределе.
3. Новое значение = общее × (мультиметр / размах).
4. Замкните красный и синий штекеры каналов, получите кадр и запишите среднее:
   это код нуля. Он у каждого канала на каждом пределе свой, 2037–2045 вместо
   идеальных 2048.
5. Добавьте датчик в `calibration.sensors` в `b202.json`:

   ```json
   "sensors": {
     "00417": {
       "mv_per_code": [44.3, 12.1, 2.47, 0.99],
       "zero_code": [[2041, 2040, 2043, 2044], [2038, 2039, 2040, 2042]],
       "date": "2026-11",
       "by": "Rigol DHO804, меандр 1 кГц"
     }
   }
   ```

   В `mv_per_code` и `zero_code` числа идут по GAIN 0, 1, 2, 3 — то есть
   ±90, ±25, ±5, ±2 В; в `zero_code` первая строка — К1, вторая — К2. Можно
   записать только часть: чего нет, возьмётся из общей. `date` обязательна —
   по ней видно, когда пора перепроверять.

После отправки в репозиторий проверка CI убедится, что файл не сломан, и
калибровка появится на сайте. Компьютерам с программой — раздать заново
(`sh deploy.sh hosts.txt`, [GUIDE.md](GUIDE.md)).

**Ноль на месте.** Кнопка «Обнулить» на странице делает шаг 4 сама и
запоминает ноль в этом браузере для этого датчика и предела. Ноль, сохранённый
в браузере, важнее записанного в `b202.json`. Ноль, сохранённый до появления
серийных номеров, больше не используется: нажмите «Обнулить» ещё раз.
