Как это работает
ESPLab — это не изображение работы платы, а её исполнение. Разница видна на первом же примере: скетч, который вы напишете, собирается тем же arduino-cli, что и на вашей машине, а получившаяся прошивка исполняется по инструкции.
Что происходит после нажатия «Запустить»
- Проект сохраняется, и его исходники уходят на сервер.
- Сервер считает хеш исходников. Если такую прошивку уже собирали,
она берётся из кеша — холодная сборка ESP32 занимает десятки секунд, и повторять её ради тех же байтов незачем.
- Иначе запускается
arduino-cliв одноразовом окружении **без
сети**. Собирается чужой код, и давать ему сеть незачем.
- Готовый
.binуезжает в браузер, где его подхватывает Web
Worker с моделью SoC.
- Модель загружает образ по сегментам, поднимает оба ядра и отдаёт
управление загрузчику — дальше исполняется настоящая прошивка.
Что исполняется, а что подменено
Исполняется: код Arduino, FreeRTOS, драйверы периферии из ESP-IDF, ваш скетч. Всё это обычные инструкции Xtensa, и модель проходит их одну за другой.
Подменены две вещи, и обе — потому что иначе нельзя:
- Функции ПЗУ. Кода в ПЗУ у нас нет, там заглушки. Незнакомая
функция не проглатывается молча: симуляция останавливается и называет её по имени.
- Радиочасть Wi-Fi. Приёмопередатчик работает через двоичные
библиотеки Espressif без исходников — исполнять там нечего. Подмена сделана на уровне сокетов, а не на уровне WiFi.begin(): прикладной код примера остаётся настоящим, поэтому урок про NTP учит NTP, а не показывает заранее известный ответ.
Почему это важнее, чем кажется
Симулятор, который изображает поведение, показывает то, что в него заложили. Симулятор, который исполняет прошивку, показывает то, что получилось, — включая ваши ошибки. Сторожевой таймер сработает, если вы заняли ядро надолго; delay() в обработчике прерывания уронит плату; неинициализированный указатель даст LoadProhibited с тем же адресом, что и на столе.
Это и есть то, ради чего стоит проверять в эмуляторе прежде, чем паять.