[EN] How to build MicroPython for esp32-C3.

This article discusses the previously recommended compiling (build) and use of MicroPython for esp32-C3 microcontrollers. The procedure is the same as for compiling for esp32-s2. In addition, we have solved the issue of RS232-to-USB from the board using CH340 to external pin using CP2102 instead and connecting the display module with OLED as Figure 1.

Figure 1 ESP32-C3 with OLED and external pins

Steps

Before compiling, you must have the ESP-IDF set, which can be read from the previous article or from the example of compiling for ESP32 via WSL when the compiler is already installed in the system, comes to the procedure for compiling MicroPython is as follows.

Download

Download the MicroPython source file from github with the following command.

git clone --recurse-submodules https://github.com/micropython/micropython.git

An example of the result of the work is shown in Figure 2.

git clone --recurse-submodules https://github.com/micropython/micropython.git
Figure 2 Result from git clone

Once the download is complete, use the command to move into the micropython folder as follows:

cd ~/micropython

Compiling mpy-cross

MicroPython’s python compiler mpy-cross provides bytecode with the extension .mpy which is useful in making the code smaller and easier to implement due to saving ROM/RAM space. It also requires mpy-cross to be compiled in MicroPython itself, so what needs to be done in step 2 is to compile mpy-cross as a tool for compiling MicroPython to another layer by going to mpy-xxx and then doing the following make:

cd mpy-cross

make clean

make

Once compiled, you will find a file named mpy-cross.

Compiling submodules of esp32

Now that mpy-cross is in place, the next step is to create a library that will be invoked with esp32-s2 by accessing the ports of the ESP32 as follows:

cd ~/micropython/ports/esp32

After that, compile with the following command

make  clean
make  submodules

Customize the Partition

Since the board we have has only 2MB Flash, we have to modify the details of the ROM partition in the file partitions.csv to be as follows

nvs,        data, nvs,     0x9000,   0x6000,
phy_init,   data, phy,     0xf000,   0x1000,
factory,    app,  factory, 0x10000,  0x160000,
vfs,        data, fat,     0x170000, 0x50000,

Compiling MicroPython

The command for compiling MicroPython to use with ESP32-C3 is to set the board to be GENNERIC_C3 as follows:

make BOARD=GENERIC_C3

When the compilation is finished without any ESP-IDF errors or forgetting mpy-cross, a folder named build-GENERIC_C3

Upload

Once compiled, the next step is to upload it to the board which must check the settings of the switch to be as shown in Figure 3, after that, order as follows

~/.espressif/python_env/idf4.4_py3.8_env/bin/python ../../../esp-idf/components/esptool_py/esptool/esptool.py -p (PORT) -b 460800 --before default_reset --after hard_reset --chip esp32c3  write_flash --flash_mode dio --flash_size detect --flash_freq 80m 0x0 build-GENERIC_C3/bootloader/bootloader.bin 0x8000 build-GENERIC_C3/partition_table/partition-table.bin 0x10000 build-GENERIC_C3/micropython.bin

Change (PORT) to the port directory, for example /dev/ttyUSB0.

Testing

Example of a program to read data from the board can be written as follows

##################################################################################
# coreInfo
##################################################################################
import gc
import os
import sys
import time
import machine as mc

##################################################################################
# system setting
##################################################################################
gc.enable()
gc.collect()

mc.freq(160000000)

##################################################################################
# system setting
##################################################################################
def show_hw_info():
    uname = os.uname()
    mem_total = gc.mem_alloc()+gc.mem_free()
    free_percent = "("+str((gc.mem_free())/mem_total*100.0)+"%)"
    alloc_percent = "("+str((gc.mem_alloc())/mem_total*100.0)+"%)"
    stat = os.statvfs('/flash')
    block_size = stat[0]
    total_blocks = stat[2]
    free_blocks  = stat[3]
    rom_total = (total_blocks * block_size)/1024
    rom_free = (free_blocks * block_size)/1024
    rom_usage = (rom_total-rom_free)
    rfree_percent = "("+str(rom_free/rom_total*100.0)+"%)"
    rusage_percent = "("+str(rom_usage/rom_total*100.0)+"%)"
    print("ID ............:",mc.unique_id())
    print("Platform ......:",sys.platform)
    print("Version .......:",sys.version)
    print("Memory")
    print("   total ......:",mem_total/1024,"KB")
    print("   usage ......:",gc.mem_alloc()/1024,"KB",alloc_percent)
    print("   free .......:",gc.mem_free()/1024,"KB",free_percent)
    print("ROM")
    print("   total ......:", rom_total,"KB" )
    print("   usage ......:", rom_usage,"KB",rfree_percent )
    print("   Free .......:", rom_free,"KB",rusage_percent )
    print("system name ...:",uname.sysname)
    print("node name .....:",uname.nodename)
    print("release .......:",uname.release)
    print("version .......:",uname.version)
    print("machine .......:",uname.machine)
    print("Frequency .....:",mc.freq())
    try:
        import ulab
        print("ulab ..........:",ulab.__version__)
    except:
        print("ulab ..........: None")
    
##################################################################################
# main program
##################################################################################
try:
    show_hw_info()
except KeyboardInterrupt:
    pass
print("end of program")


An example of the result of the work is shown in Figure 3.

Figure 3 Result of the program

An example of finding prime numbers from numbers 2 to 2000 from the following code results in Figure 4.

import gc
import time as tm
import machine as mc

gc.enable()
gc.collect()

mc.freq(160000000)

def is_prime(x):
    i = 2
    while (i < x):
        if x%i == 0:
            return False
        i = i+1
    if (i == x):
        return True
    return False

def test_prime_number(maxN):
    counter = 0
    t0 = tm.ticks_ms()
    for n in range(2, maxN):
        if is_prime(n):
            counter+=1
    t1 = tm.ticks_ms()
    print("Found {} in {} msecs.".format(counter,abs(t1-t0)))
    
test_prime_number(2000)
Figure 4 Result from prime numbers finding of ESP32-C3

Conclusion

From this article, you will find that creating a MicroPython for use with an ESP32-C3 microcontroller has the same steps as the ESP32, but the difference is that Flash memory size must be checked as it affects partition layout. And must check the compatibility of the RS232-USB module whether the operating system is supported or not. If not, you can connect the RS232-to-USB module supported by the operating system to GND, 5V or 3V3, RX and TX.

The main programming language is still python language like ESP32 that we have updated articles all the time and finally, have fun with programming.

Reference

  1. MicroPython

(C) 2020-2021, By Jarut Busarathid and Danai Jedsadathitikul
Updated 2022-01-04