MicroPython — искусство малых форм
Можно ли писать на Python для микроконтроллеров, где RAM измеряется сотнями килобайт, а вместо привычного цикла разработки — прошивка через USB-порт? MicroPython говорит, что можно: компактная реализация Python3, REPL прямо на устройстве и обновление кода без перекомпиляции. Но где у этой магии границы и сколько на самом деле стоит «питон в железе» по скорости?
Сергей Степанов — CEO «Тяга», PhD в области теоретической физики, который имел дело с бозоном Хиггса, программировал роботов для игры в футбол и собрал CNC-станок, — на практике разбирает, как устроен MicroPython внутри и как выжать из него максимум.
В докладе: — как устроен MicroPython: REPL, WebREPL по вебсокетам, boot.py и main.py, frozen modules в прошивке — организация памяти на примере ESP32: 4 МБ Flash, 520 КБ RAM, ручной gc.collect() и настройка порога сборки мусора — работа с железом через GPIO, I2C, SPI, UART и кроссплатформенность ESP32, STM32, RP2040, PyBoard — эмиттеры кода bytecode vs @native vs @viper и во что они компилируются на ARM — живой замер скорости счёта импульсов: C-код до 360 кГц / 2.5 МГц, @viper до 70 кГц, @native до 50 кГц, чистый Python до 20 кГц — инструменты pyboard.py и mpremote: заливка файлов, mip install и Python на всех этапах мониторинга через PySide6
Презентация
1 / 22Текст презентации
Слайд 1: MicroPython - искусство
MicroPython - искусство малых форм Сергей Степанов CEO “Тяга”
Слайд 2: ● PhD в области теоретической физики,
● PhD в области теоретической физики, имел дело с бозоном Хиггса ● Программировал роботов для игры в футбол ● Собрал CNC-станок ● Устал от всего этого embedded software и захотел облегчить всем жизнь Кто? Когда? Зачем?
Слайд 3: Что получилось
Что получилось 📱 Компактная реализация Python3 Оптимизированная версия Python для микроконтроллеров с ограниченными ресурсами 🔧 Низкоуровневый доступ Прямая работа с GPIO, I2C, SPI, UART и другими аппаратными интерфейсами ⚙ Кроссплатформенность Единый код для ESP32, STM32, RP2040, PyBoard и других платформ ⚡ Интерактивная разработка REPL (Read-Eval-Print Loop) для быстрого прототипирования и отладки прямо на устройстве Закидываем новые библиотеки и апдейты кода без компиляции, на работающее устройство
Слайд 4: 🌐 Популярность
🌐 Популярность Open source (MIT лицензия), активное сообщество, пользовательские библиотеки для популярных IoT сценариев
Слайд 5: REPL (Read-Eval-Print Loop)
REPL (Read-Eval-Print Loop) Подключаем МК к USB порту - драйвера CP210x USB to UART bridge
Слайд 6: WebREPL (по вебсокетам)
WebREPL (по вебсокетам) Активируем на МК webrepl Подключаемся к WiFi ( перед этим обеспечиваем надежное питание, 0.2 А, 5 В) Тормозииит
Слайд 7: Организация памяти на примере ESP32
Организация памяти на примере ESP32 📦 Flash Memory (4MB) │ Bootloader │ ~28 KB │ Partition Table │ ~4 KB │ MicroPython Firmware │ ~1.5 MB │ • Интерпретатор │ │ • Встроенные модули │ │ • Frozen modules │ │ Файловая система │ ~2 MB │ /boot.py, /main.py │ │ /lib/* (библиотеки) │ │ Ваши скрипты │ ⚡ RAM (520 KB на ESP32) ┌─────────────────────────┐ High │ Stack │ ~8-16 KB │ • Вызовы функций │ │ • Локальные переменные │ ├─────────────────────────┤ ↓ растет вниз │ ...свободно... │ ├─────────────────────────┤ ↑ растет вверх │ Heap (Python объекты) │ ~100-150 KB │ • Строки, списки │ │ • Словари, объекты │ │ • Bytecode в RAM │ │ → gc.collect() здесь! │ │ Системная память │ ~300 KB │ • WiFi/BT буферы │ │ • FreeRTOS │ │ • Драйверы │ └─────────────────────────┘ Low import gc gc.collect() # Принудительная сборка мусора print(gc.mem_free()) # Свободная память gc.threshold(gc.mem_free() // 4 + gc.mem_alloc()) # Настройка порога GC Frozen Module — это Python- модуль, который компилируется в байт-код и "замораживается" прямо в прошивке MicroPython. Вместо хранения в файловой системе, такой модуль становится частью исполняемого образа
Слайд 8: Этапы загрузки
Этапы загрузки Вызываются: boot.py - всякие подготовительные одноразовые действия, например подключение к WiFi main.py - тут следует распологать вызов основного кода прикладной программы (можно циклить)
Слайд 9: Общаемся нормально
Общаемся нормально при живом REPL! с другого конца просто пишем в порт
Слайд 11: pyboard.py
pyboard.py Скриптик, который вы запускаете на своём ПК для взаимодействия с МК Выполнение команд интерпретатором Операции с файлам (ls, cp, mv, rm, mkdir, rmdir) import pyboard pyb = pyboard.Pyboard('/dev/ttyACM0', 115200) pyb.enter_raw_repl() ret = pyb.exec('print(1+1)') print(ret)
Слайд 12: Новые фичи
Новые фичи mpremote расширяет pyboard.py pip install --user mpremote mpremote cp utils/driver.py :utils/driver.py + exec "import app" mpremote mip install gitlab:org/repo@branch не pip, но mip! модули лишаются приставки u* uselect -> select, uasyncio -> asyncio
Слайд 13: Эмиттеры кода: bytecode vs native vs viper
Эмиттеры кода: bytecode vs native vs viper 🐍 Обычный Python Преобразование: Python → Bytecode → VM Выполнение: Интерпретатор выполняет байт-код в виртуальной машине Скорость: 1x (базовая) Гибкость: Максимальная 🐉 @native Преобразование: Python → Native ARM code Выполнение: Прямое выполнение процессором, но с Python объектами Скорость: ~2-5x Гибкость: Средняя 🐲 @viper Преобразование: Python → Typed Native code Выполнение: Прямое выполнение с машинными типами (int, uint) Скорость: ~5-10x Гибкость: Ограниченная # Python код def add(a, b): return a + b # Компилируется в байт-код: LOAD_FAST 0 (a) LOAD_FAST 1 (b) BINARY_OP_ADD RETURN_VALUE # VM интерпретирует каждую # инструкцию в цикле: while (pc < code_end) { opcode = *pc++; switch(opcode) { ... } @micropython.native def add(a, b): return a + b # Компилируется в ARM: ldr r0, [sp, #0] ; load a ldr r1, [sp, #4] ; load b bl mp_binary_add ; Python add bx lr ; return @micropython.viper def add(a: int, b: int) -> int: return a + b # Компилируется в ARM: add r0, r0, r1 ; r0 = r0 + r1 bx lr ; return
Слайд 14: # Глобальная блокировка для потоков
# Глобальная блокировка для потоков thread_lock = _thread.allocate_lock() def worker_thread(thread_id): while True: with thread_lock: # Только один поток может выполнять этот блок print(f"Thread {thread_id} in critical section") time.sleep(2) # Даже с sleep - другие потоки ждут time.sleep(0.5) # Вне критической секции # Отключаем прерывания irq_state = machine.disable_irq() # Критическая секция # Включаем прерывание machine.enable_irq(irq_state)
Слайд 15: Экспериментальная проверка скорости
Экспериментальная проверка скорости
Слайд 16: Код для счёта импульсов на пине
Код для счёта импульсов на пине
Слайд 17: LED PWM Controller
LED PWM Controller 5 MHz - easy trying to count pulses
Слайд 18: Считаем импульсы частотой от 0 до ∞ кГц!
Считаем импульсы частотой от 0 до ∞ кГц!
Слайд 19: C-код - до 360 кГц / 2.5 МГц
C-код - до 360 кГц / 2.5 МГц @Viper - до 70 кГц @Native - до 50 кГц Python - до 20 кГц
Слайд 20: Питон на всех этапах мониторинга и управления
Питон на всех этапах мониторинга и управления MicroPython Python PySide6 (Python + QT) USB Ethernet
Слайд 21: Чего достигли с этими питонами
Чего достигли с этими питонами
Слайд 22: Спасибо за внимание!
Спасибо за внимание! [email protected] @thrust_stepanov https://github.com/in-space-we-thrust
Другие доклады митапа
- КУFastAPI с Clean Architecture Коспан Улан
- ЕДКак работает стартап на миллиард долларов Ерзат Дулат
- ЭСМикросервисы во Freedom Travel Эдуард Сидиропуло



















