Run MicroPython on ESP8266
MicroPython is a python implementation aimed to be running on MCUs such as ESP8266/ESP32, stm32 etc, all the ports can be found here, the following will only focused on ESP8266 module.
Although MicroPython can run on modules with a minimum flash size of 512KB, the recommended flash size is 1MB and above, as with 512KB build, there is no file system support, and the features depending on it such as WebREPL and upip will not work.
Environment Setup
The recommended way to build MicroPython is use a docker image, I’m gonna try the native way, install esp-open-sdk, which is described in MicroPython port to ESP8266.
Follow the instructions in README.md of esp-open-sdk to build the toolchain, before building the esp sdk, the following packages need to be installed:
$ sudo apt install -y help2man libtool-bin
Build SDK
Clone the esp-open-sdk repository:
$ git clone --recursive https://github.com/pfalcon/esp-open-sdk.git
The toolchain was generated with crosstool-NG, the required tarballs
will be downloaded during the build, with an exception of newlib-2.0.0
which URL
was broken, and need to download manually:
$ cd crosstool-NG/.build/tarballs
$ wget -c ftp://sourceware.org/pub/newlib/newlib-2.0.0.tar.gz
Simply do a make
and wait for its completion, this process takes a few minutes.
You will see the following message at the end:
Xtensa toolchain is built, to use it:
export PATH=/opt/workdir/esp-open-sdk/xtensa-lx106-elf/bin:$PATH
Espressif ESP8266 SDK is installed, its libraries and headers are merged with the toolchain
Append the export line to ~/.oh-my-zsh/custom/path.zsh
.
Build MicroPython for ESP8266
Clone micropython, submodules and build:
$ git clone --depth=1 https://github.com/micropython/micropython.git
$ make -C ports/esp8266 submodules
$ make -C mpy-cross
$ cd ports/esp8266
$ make
The firmware info will be dumped at the end of the build process:
[...]
LINK build-GENERIC/firmware.elf
text data bss dec hex filename
620460 1012 66376 687848 a7ee8 build-GENERIC/firmware.elf
Create build-GENERIC/firmware-combined.bin
esptool.py v1.2
flash 32896
.text 30764 at 0x40100000
.data 1012 at 0x3ffe8000
.rodata 1080 at 0x3ffe8400
padding 3968
irom0text 588616
total 625480
md5 1d6b745f49a0158a26a989ce529f2d06
Flash MicroPython to ESP8266
# Erase entire Flash before write
$ esptool.py erase_flash
$ esptool.py --baud 230400 write_flash -fs 4MB -fm dio -ff 80m \
0x00000 build-GENERIC/firmware-combined.bin
esptool.py v3.0
Found 2 serial ports
Serial port /dev/ttyUSB2
Connecting....
Detecting chip type... ESP8266
Chip is ESP8266EX
Features: WiFi
Crystal is 26MHz
MAC: 48:3f:da:49:c7:6d
Uploading stub...
Running stub...
Stub running...
Changing baud rate to 230400
Changed.
Configuring flash size...
Flash params set to 0x024f
Compressed 624676 bytes to 411637...
Wrote 624676 bytes (411637 compressed) at 0x00000000 in 18.3 seconds (effective 273.1 kbit/s)...
Hash of data verified.
Leaving...
Hard resetting via RTS pin...
Another way to flash the firmware is use the deploy
target:
$ make PORT=/dev/ttyUSB2 BAUD=230400 FLASH_MODE=dio deploy
Use make V=1 or set BUILD_VERBOSE in your environment to increase build verbosity.
Writing build-GENERIC/firmware-combined.bin to the board
esptool.py v1.2
Connecting...
Auto-detected Flash size: 32m
Running Cesanta flasher stub...
Flash params set to 0x0240
Writing 626688 @ 0x0... 626688 (100 %)
Wrote 626688 bytes at 0x0 in 27.3 seconds (183.7 kbit/s)...
Leaving...
NOTE the default baud rate 460800 does not work for my ESP8266 module.
You will see the REPL prompt after startup:
MicroPython f305c62a5-dirty on 2021-02-27; ESP module with ESP8266
Type "help()" for more information.
>>>
Blink LED
The blue LED on my nodemcu board is controlled by GPIO16, when the pin outputs 0, the LED will be turned on, otherwise will be off, the following python code illustrate how to control out value of gpio on ESP8266:
import machine
import time
def led_blink(num=16, interval=100):
pin = machine.Pin(num, machine.Pin.OUT)
while True:
pin.value(0) # Turn on LED
time.sleep_ms(interval)
pin.value(1) # Turn off LED
time.sleep_ms(interval)
led_blink()
Upload Python Script to ESP8266 Board
There are two python scripts that will be executed automatically on startup, the
first script is boot.py
which will be executed first, the other one is main.py
there is no main.py
on ESP8266 by default, create one and paste the above blink
code to it and upload to board with either ampy, rshell or
mpfshell.
$ python3 -m pip install --user adafruit-ampy
$ python3 -m pip install --user rshell
$ python3 -m pip install --user mpfshell
Before uploading, make sure ttyUSBx is not opened by any program.
Upload with ampy
# Check if the blink script works properly before upload
$ ampy -p /dev/ttyUSB0 run main.py
$ ampy -p /dev/ttyUSB0 put main.py
$ ampy -p /dev/ttyUSB0 ls
Upload with rshell
$ rshell -p /dev/ttyUSB0
Using buffer-size of 32
Connecting to /dev/ttyUSB0 (buffer-size 32)...
Trying to connect to REPL connected
Testing if ubinascii.unhexlify exists ... Y
Retrieving root directories ... /boot.py/
Setting time ... Feb 27, 2021 18:41:42
Evaluating board_name ... pyboard
Retrieving time epoch ... Jan 01, 2000
Welcome to rshell. Use Control-D (or the exit command) to exit rshell.
/tmp> boards
pyboard @ /dev/ttyUSB0 connected Epoch: 2000 Dirs: /boot.py /pyboard/boot.py
/tmp> cp /tmp/main.py /pyboard/
/tmp> cat /pyboard/main.py
import machine
import time
def led_blink(num=16, interval=100):
pin = machine.Pin(num, machine.Pin.OUT)
while True:
pin.value(0) # Turn on LED
time.sleep_ms(interval)
pin.value(1) # Turn off LED
time.sleep_ms(interval)
led_blink(interval=200)
The script will be copied to root directory during startup.
Upload with mpfshell
$ mpfshell ttyUSB0
Connected to esp8266
** Micropython File Shell v0.9.2, sw@kaltpost.de **
-- Running on Python 3.6 using PySerial 3.5 --
mpfs [/]> ls
Remote files in '/':
boot.py
main.py
mpfs [/]> get main.py led.py
mpfs [/]> put led.py main.py
or use below command to put main.py to board without entering shell:
$ mpfshell -n -c "open ttyUSB0; put main.py"
Troubleshooting
MicroPython can output verbose debug message to serial console if enabled, which is useful during development phase, enable it with:
import esp
esp.osdebug(0) # redirect vendor O/S debugging messages to UART(0)
esp.osdebug(None) # turn off vendor O/S debugging messages