MicroPython

Automated script to build MicroPython for the ESP32

The following script installs esp-idf, the required dependencies and then builds MicroPython for the ESP32.

apt -y update && apt -y install python3 python3-pip cmake
# Install esp-idf
git clone --depth 1 -b v4.4.3 https://github.com/espressif/esp-idf.git /opt/esp-idf
cd /opt/esp-idf && git submodule update --init --recursive && ./install.sh

# Clone micropython
git clone --depth 1 -b v1.19.1 https://github.com/micropython/micropython.git /opt/micropython

# Build micropython
cd /opt/micropython/ && make -C mpy-cross
# Build micropython@esp32
cd /opt/micropython/ports/esp32 && source /opt/esp-idf/export.sh && make submodules && make -j4

 

Posted by Uli Köhler in Embedded, ESP8266/ESP32, MicroPython

Computing the CRC8-ATM CRC in Python

The 8-bit CRC8-ATM polynomial is used in many embedded applications, including Trinamic UART-controlled stepper motor drivers like the TMC2209:

\text{CRC} = x^8 + x^2 + x^1 + x^0

The following code provides an example on how to compute this type of CRC in Python:

def compute_crc8_atm(datagram, initial_value=0):
    crc = initial_value
    # Iterate bytes in data
    for byte in datagram:
        # Iterate bits in byte
        for _ in range(0, 8):
            if (crc >> 7) ^ (byte & 0x01):
                crc = ((crc << 1) ^ 0x07) & 0xFF
            else:
                crc = (crc << 1) & 0xFF
            # Shift to next bit
            byte = byte >> 1
    return crc

This code has been field-verified for the TMC2209.

Posted by Uli Köhler in Algorithms, Embedded, MicroPython, Python

MicroPython ESP32 minimal UART example

This example shows how to use UART on the ESP32 using MicroPython. In this example, we use UART1 which is mapped to pins GPIO9 (RX) and GPIO10 (TX).

from machine import UART
uart = UART(1, 115200) # 1st argument: UART number: Hardware UART #1

# Write
uart.write("test")

# Read
print(uart.read()) # Read as much as possible using

Don’t know how to upload the file to MicroPython so it is automatically run on boot?

Posted by Uli Köhler in Embedded, MicroPython, Python

How to fix ESP32 MicroPython ‘ValueError: pin can only be input’

Problem:

You are trying to initialize an ESP32 pin in MicroPython using

import machine
machine.Pin(34, machine.Pin.OUT)

but you see an error message like

>>> machine.Pin(34, machine.Pin.OUT)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: pin can only be input

Solution:

On the ESP32, pins with numbers >= 34 are input-only pins!

You need to use other pins < 34 if you need output capability!

For reference, see the relevant MicroPython source code section:

// configure mode
if (args[ARG_mode].u_obj != mp_const_none) {
    mp_int_t pin_io_mode = mp_obj_get_int(args[ARG_mode].u_obj);
    if (self->id >= 34 && (pin_io_mode & GPIO_MODE_DEF_OUTPUT)) {
        mp_raise_ValueError("pin can only be input");
    } else {
        gpio_set_direction(self->id, pin_io_mode);
    }
}
Posted by Uli Köhler in Embedded, MicroPython, Python

How to upload files to MicroPython over USB/serial

In this post we will investigate how to connect to a wireless network on boot

First, install ampy – a tool to modify the MicroPython filesystem over a serial connection.

sudo pip3 install adafruit-ampy

Now prepare your script – we’ll use main.py in this example.

Upload the file to the board:

ampy -p /dev/ttyUSB* put main.py

This only takes about 2-4 seconds. In case ampy is still running after 10 seconds, you might need to

  • Stop ampy (Ctrl+C), reset the board using the RESET button and retry the command
  • Stop ampy (Ctrl+C). Detach USB and ensure the board is powered off (and not powered externally). Re-Attach USB and retry the command.
  • In case that doesn’t help, try re-flashing your board with the most recent version of MicroPython. See How to flash MicroPython to your ESP32 board in 30 seconds. This will also clear the internal filesystem and hence remove any file that might cause failure to boot properly.
Posted by Uli Köhler in Embedded, MicroPython, Python

MicroPython ESP32 blink example

This MicroPython code blinks GPIO2 which is connected to the LED on most ESP32 boards.

import machine
import time
led = machine.Pin(2, machine.Pin.OUT)
while True:
    led.value(1)
    time.sleep(1)
    led.value(0)
    time.sleep(1)

Don’t know how to upload the file to MicroPython so it is automatically run on boot?

Posted by Uli Köhler in Embedded, MicroPython, Python

How to upload files to MicroPython using WebREPL using webrepl_cli.py

First, clone the webrepl repository:

git clone https://github.com/micropython/webrepl.git

Now use ampy to initially setup your wifi connection and setup both wifi and WebREPL on on boot (see How to autoconnect to Wifi using MicroPython on your ESP32 board):

import network
station = network.WLAN(network.STA_IF)
station.active(True)
station.connect("MyWifi", "MyWifiPassword")

# Start webrepl
import webrepl
webrepl.start(password="Rua8ohje")

Upload via the serial port using

ampy -p /dev/ttyUSB0 put main.py

You should only have to do this once (except if you break you main.py). If the upload doesn’t work

Go to the directory where webrepl_cli.py is located:

cd webrepl

Now you can upload main.py using

./webrepl_cli.py -p Rua8ohje ../main.py espressif.local:/main.py

You might need to use a different host, but espressif.local seems to work out-of-the-box in many configurations.

The output should look like this:

op:put, host:espressif.local, port:8266, passwd:Rua8ohje.
../main.py -> /main.py
Remote WebREPL version: (1, 12, 0)
Sent 329 of 329 bytes

Now, reset your board using the reset button, so your updated main.py will be executed.

Posted by Uli Köhler in Embedded, MicroPython

How to run WebREPL without webrepl_setup in MicroPython

Problem:

You want to enable WebREPL on your MicroPython board using

import webrepl
webrepl.start()

but it is only showing this error message:

WebREPL is not configured, run 'import webrepl_setup'

However, you want to configure WebREPL programmatically instead of manually running it on every single board.

Solution:

Use

import webrepl
webrepl.start(password="Rua8ohje")

This will circumvent webrepl_setup completely and is compatible with an automated setup process.

Note: At the time of writing this you can only use passwords with 8 characters max! (see How to fix MicroPython WebREPL ValueError in File &#8222;webrepl.py&#8220;, line 72, in start )

Posted by Uli Köhler in Embedded, MicroPython, Python

How to fix MicroPython WebREPL ValueError in File “webrepl.py”, line 72, in start

Problem:

You want to configure your MicroPython WebREPL programmatically using webrepl.start(password="...") but you see a stacktrace like

>>> webrepl.start(password="Rua8ohjedo")
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
 File "webrepl.py", line 72, in start
ValueError:

Solution:

Use a shorter password with 8 characters max:

webrepl.start(password="Rua8ohje")

 

Posted by Uli Köhler in Embedded, MicroPython, Python

How to autoconnect to Wifi using MicroPython on your ESP32 board

In this post we will investigate how to connect to a wireless network on boot

First, install ampy – a tool to modify the MicroPython filesystem over a serial connection.

sudo pip3 install adafruit-ampy

Now download main.py and save it in your current working directory and insert your wifi credentials:

import network
station = network.WLAN(network.STA_IF)
station.active(True)
station.connect("YourWifiName", "EnterYourWifiPasswordHere")

Upload the file to the board:

ampy -p /dev/ttyUSB* put main.py

In case ampy shows no output within 5 seconds, try resetting the board, waiting for 5-10 seconds and retrying the upload using ampy

Note: You can list the files on the board’s filesystem using

ampy -p /dev/ttyUSB0 ls

You can verify the content of main.py using

ampy -p /dev/ttyUSB0 get main.py
Posted by Uli Köhler in Embedded, MicroPython, Python

How to connect your ESP32 MicroPython board to your Wifi in 20 seconds

Didn’t flash MicroPython on your ESP32 board yet? See How to flash MicroPython to your ESP32 board in 30 seconds

Copy this script and enter your Wifi credentials:

import network
station = network.WLAN(network.STA_IF)
station.active(True)
station.connect("YourWifiName", "EnterYourWifiPasswordHere")

Now you need to open a REPL to MicroPython via USB.

If your Wifi is within range of the board and the password is correct, your should see output like

>>> import network
>>> station = network.WLAN(network.STA_IF)
I (457700) wifi: wifi driver task: 3ffd2a80, prio:23, stack:3584, core=0
I (459702) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
I (459712) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
I (459832) wifi: wifi firmware version: 10f4364
I (459832) wifi: config NVS flash: enabled
I (459832) wifi: config nano formating: disabled
I (459832) wifi: Init dynamic tx buffer num: 32
I (459842) wifi: Init data frame dynamic rx buffer num: 32
I (459842) wifi: Init management frame dynamic rx buffer num: 32
I (459852) wifi: Init management short buffer num: 32
I (459852) wifi: Init static rx buffer size: 1600
I (459862) wifi: Init static rx buffer num: 10
I (459862) wifi: Init dynamic rx buffer num: 32
>>> station.active(True)
W (459872) phy_init: failed to load RF calibration data (0x1102), falling back to full calibration
I (460012) phy: phy_version: 4102, 2fa7a43, Jul 15 2019, 13:06:06, 0, 2
I (460062) wifi: mode : sta (24:6f:28:b0:28:b4)
True
I (460062) wifi: STA_START
>>> station.connect("MyWifi", "ThisIsMyWifiPassword")
>>> I (461782) wifi: new:<5,0>, old:<1,0>, ap:<255,255>, sta:<5,0>, prof:1
I (462632) wifi: state: init -> auth (b0)
I (462632) wifi: state: auth -> assoc (0)
I (462642) wifi: state: assoc -> run (10)
I (462672) wifi: connected with MyWifi, channel 5, BW20, bssid = 9a:c7:44:33:22:11
I (462672) wifi: pm start, type: 1

I (462672) network: CONNECTED
I (463672) tcpip_adapter: sta ip: 192.168.178.42, mask: 255.255.255.0, gw: 192.168.178.1
I (463672) network: GOT_IP
>>>

Note that the output might take a few seconds to appear since it might take some time to connect to the access point.

Note the IP address (192.168.178.42 in this example)

Posted by Uli Köhler in Embedded, MicroPython

How to get into a MicroPython REPL using USB in 10 seconds

On Linux, use picocom:

picocom /dev/ttyUSB0 -b115200

and press enter to get a REPL (or press the RESET button on the board if that does not work).

You should now see the

>>>

prompt.

Don’t have picocom installed?

sudo apt -y install picocom

Got permission denied errors?

sudo usermod -a -G dialout $USER

then logout and log back in or reboot!

Posted by Uli Köhler in MicroPython

How to flash MicroPython to your ESP32 board in 30 seconds

This script does all the neccessary steps to flash MicroPython to your ESP32 board on Linux. Run this script with your board plugged in via USB:

#!/bin/sh
# Call without arguments
# Download esptool
git clone https://github.com/espressif/esptool.git -b v2.8
cd esptool
# Erase flash. Press the reset button while Connecting.
python3 esptool.py --chip esp32 --port /dev/ttyUSB* erase_flash
# Download firmware
wget -qO micropython.bin https://micropython.org/resources/firmware/esp32-idf4-20191220-v1.12.bin
# Upload firmware to board
python3 esptool.py --chip esp32 --port /dev/ttyUSB* --baud 460800 write_flash -z 0x1000 micropython.bin

Remember to press the RESET button on the board if the script is telling you Connecting.... Not sure which button it is? Just press either one, and if the output doesn’t show Chip is ESP32 within 2 seconds, press the other one.

This script was built for my ESP-WROOM-32 board.

Now you can try to get into a REPL using USB.

Posted by Uli Köhler in Embedded, MicroPython

How to fix MicroPython I2C no data

Problem:

You’ve configured MicroPython’s I2C similar to this (in my case on the ESP8266 but this applies to many MCUs):

i2c = machine.I2C(-1, machine.Pin(5), machine.Pin(4))

but you can’t find any devices on the bus:

>>> i2c.scan()
[]

Solution:

Likely you forgot to configure the pins as pullups. I2C needs pullups to work, and many MCUs (like the ESP8266) provide support for integrated (weak) pull-ups.

p4 = machine.Pin(4, mode=machine.Pin.OUT, pull=machine.Pin.PULL_UP)
p5 = machine.Pin(5, mode=machine.Pin.OUT, pull=machine.Pin.PULL_UP)
i2c = machine.I2C(-1, p5, p4)

i2c.scan() # [47]

You can also verify this by checking with a multimeter or an oscilloscope: When no communication is going on on the I2C bus, the voltage should be equivalent to the supply voltage of your MCU (usually 3.3V or 5V – 0V indicates a missing pullup or some other error).

Posted by Uli Köhler in Electronics, MicroPython, Python

How to fix MicroPython ‘ValueError: invalid I2C peripheral’

If you see the error message

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: invalid I2C peripheral

you are likely running code like this:

import machine

i2c = machine.I2C(machine.Pin(5), machine.Pin(4))

Solution

The MicroPython API has changed (source: forum). You need to use this syntax instead:

import machine

i2c = machine.I2C(-1, machine.Pin(5), machine.Pin(4))

-1 is the I2C ID that selects a specific peripheral. -1 selects a software I2C implementation which can work on most pins. See the MicroPython I2C class documentation for more details.

Posted by Uli Köhler in Electronics, MicroPython, Python