Как это работает

ESPLab — это не изображение работы платы, а её исполнение. Разница видна на первом же примере: скетч, который вы напишете, собирается тем же arduino-cli, что и на вашей машине, а получившаяся прошивка исполняется по инструкции.

Что происходит после нажатия «Запустить»

  1. Проект сохраняется, и его исходники уходят на сервер.
  2. Сервер считает хеш исходников. Если такую прошивку уже собирали,

она берётся из кеша — холодная сборка ESP32 занимает десятки секунд, и повторять её ради тех же байтов незачем.

  1. Иначе запускается arduino-cli в одноразовом окружении **без

сети**. Собирается чужой код, и давать ему сеть незачем.

  1. Готовый .bin уезжает в браузер, где его подхватывает Web

Worker с моделью SoC.

  1. Модель загружает образ по сегментам, поднимает оба ядра и отдаёт

управление загрузчику — дальше исполняется настоящая прошивка.

Что исполняется, а что подменено

Исполняется: код Arduino, FreeRTOS, драйверы периферии из ESP-IDF, ваш скетч. Всё это обычные инструкции Xtensa, и модель проходит их одну за другой.

Подменены две вещи, и обе — потому что иначе нельзя:

  • Функции ПЗУ. Кода в ПЗУ у нас нет, там заглушки. Незнакомая

функция не проглатывается молча: симуляция останавливается и называет её по имени.

  • Радиочасть Wi-Fi. Приёмопередатчик работает через двоичные

библиотеки Espressif без исходников — исполнять там нечего. Подмена сделана на уровне сокетов, а не на уровне WiFi.begin(): прикладной код примера остаётся настоящим, поэтому урок про NTP учит NTP, а не показывает заранее известный ответ.

Почему это важнее, чем кажется

Симулятор, который изображает поведение, показывает то, что в него заложили. Симулятор, который исполняет прошивку, показывает то, что получилось, — включая ваши ошибки. Сторожевой таймер сработает, если вы заняли ядро надолго; delay() в обработчике прерывания уронит плату; неинициализированный указатель даст LoadProhibited с тем же адресом, что и на столе.

Это и есть то, ради чего стоит проверять в эмуляторе прежде, чем паять.