Getting Started with CircuitPython

CircuitPython is a spin-off of the well-known MicroPython. An efficient implementation of Python 3, with a small subset of the Python Stdlib, optimised for microcontrollers (Cortex-based MCU, for example).

Even thought Python 3 is a powerful language, this implementation fits in 256KB of Flash and uses around 16KB of RAM. So it is suitable for the majority of the mid-range ARM-based microcontrollers.

The main difference between MicroPython and CircuitPython, is that the latest is sponsored and maintained by Adafruit. One of the global leaders developing boards and add-ons for microcontrollers’ enthusiasts. Apart from that:

  • It’s based on MicroPython (stable) releases, and not the master branch.
  • It’s implementation is focus on Adafruit products (basically Atmel’s M0, M4 devices and ESP8266).
  • Floats and Doubles are enabled by default.
  • USB is natively supported on all boards (with capable hardware).
  • Does not support concurrency.

CircuitPython comes pre-installed on many Adafruit boards, but if it’s not present, or you have moved away from it (going to Arduino), you can always installed a CircuitPython (or MicroPython) bootloader that can be downloaded from:

Take into consideration that there are specific builds for each kind of board / CPU. Moreover, some Adafruit libraries, are not supported on MicroPython, for example the Bus Device library.

The Integrated Development Environment

CircuitPython (and MicroPython too) comes with an integrated REPL (Read-Eval-Print-Loop) that can be accessed using the built-in USB-Serial port present in all devices. But, some of this microcontrollers present a USB Drive where we can download Python (and other kind of) files, so the board can used them.

Adafruit recommends using the Mu Editor, but I find it very limited and mainly oriented to beginners when it comes to programming languages. Most of use (developers) use Visual Studio code, and more sophisticated IDEs, with features like:

  • Integrated version control (Git)
  • Autocomplete
  • Linters
  • Automatic code formatter
  • Included Markdown editor / preview for documentation

That’s why using an IDE like Visual Studio Code makes more sense when writing an advanced application. An extension for VS Code can be found on its MarketPlace. It’s compatible with CircuitPython latests stable release (5.3.x), it’s Open Source, but it has a few issues.

Visual Studio Code with CircuitPython extension demonstrating demo autocomplete feature.
Board selection in Visual Studio Code with CircuitPython extension.

Feel free to start coding with Mu if you want to start with a more stable IDE, which also was design specifically for CircuitPython.

Mu Editor (with Dark mode).
Mu Editor has different modes based on the target platform.

Libraries and Examples

Take into consideration downloading and installing Adafruit’s CircuitPython libraries, which can be fount at https://circuitpython.org/libraries. Also, it’s always a good idea to start looking at some examples in order to get familiar with it. A bundle with a lot of examples can be download from https://github.com/adafruit/Adafruit_CircuitPython_Bundle.

Keeping Circuit Python library up-to-date

CircUp is a dependency management tool for CircuitPython developed by Adafruit. It has a very simple CLI that let’s you list, install, remove and even update, any dependency needed inside your device.

Moreover, the version of each installed library is saved in a requirements.txt file in the working directory, so it’s really simple to keep dependencies organised and versiones on Git.

Installation is done using pip, either directly or using virtualenv. Python 3.5 or higher is requiered.

pip3 install --user circup
Usage: circup [OPTIONS] COMMAND [ARGS]...
  A tool to manage and update libraries on a CircuitPython device.
  --verbose  Comprehensive logging is sent to stdout.
  --version  Show the version and exit.
  --help     Show this message and exit.
  freeze     Output details of all the modules found on the connected...
  install    Install a named module onto the device.
  list       Lists all out of date modules found on the connected...
  show       Show a list of available modules in the bundle.
  uninstall  Uninstall a named module(s) from the connected device.
  update     Update modules on the device. Use --all to automatically update
             all modules.

MicroPython on an ESP32


Software on the host

ESPTools and Adafruit AMPY are required. Both can be installed using pip3. The recommended version of Python on the host is Python 3.7

pip3 install esptool --upgrade
pip3 install adafruit-ampy --upgrade

Firmware for the ESP32 module

MicroPython for ESP32. The latests firmware (compatible with ESP-IDF v4.x) when writing can be found at http://micropython.org/resources/firmware/esp32-idf4-20191220-v1.12.bin


First of all erase your ESP32 completely by running the following command. Where /dev/ttyUSB0 is the Virtual COM port created by your board.

$ esptool.py --chip esp32 -p /dev/ttyUSB0 erase_flash
esptool.py v2.8
Serial port /dev/ttyUSB0
Chip is ESP32D0WDQ6 (revision 1)
Features: WiFi, BT, Dual Core, Coding Scheme None
Crystal is 40MHz
MAC: 24:0a:c4:0c:94:78
Uploading stub...
Running stub...
Stub running...
Erasing flash (this may take a while)...
Chip erase completed successfully in 1.8s
Hard resetting via RTS pin...

Then, write the flash with the download image (esp32-idf4-20191220-v1.12.bin is this example).

$ esptool.py --chip esp32 -p /dev/ttyUSB0 write_flash -z 0x1000 esp32-idf4-20191220-v1.12.bin
esptool.py v2.8
Serial port /dev/ttyUSB0
Chip is ESP32D0WDQ6 (revision 1)
Features: WiFi, BT, Dual Core, Coding Scheme None
Crystal is 40MHz
MAC: 24:0a:c4:0c:94:78
Uploading stub...
Running stub...
Stub running...
Configuring flash size...
Auto-detected Flash size: 4MB
Compressed 1408512 bytes to 894711...
Wrote 1408512 bytes (894711 compressed) at 0x00001000 in 78.9 seconds (effective 142.9 kbit/s)...
Hash of data verified.
Hard resetting via RTS pin...


After successfully flashing your ESP32 module, connect it to a Serial Monitor at 115200 baud. I like to use the embedded serial monitor that Arduino IDE has.

Perform a hard reset and you should see a console output like the one below:

flash read err, 1000
Falling back to built-in command interpreter.
>ets Jun  8 2016 00:22:57
configsip: 0, SPIWP:0xee
mode:DIO, clock div:2
entry 0x400806bc
 [0;32mI (519) cpu_start: Pro cpu up. [0m
 [0;32mI (519) cpu_start: Application information: [0m
 [0;32mI (519) cpu_start: Compile time:     Dec 20 2019 07:56:38 [0m
 [0;32mI (523) cpu_start: ELF file SHA256:  0000000000000000... [0m
 [0;32mI (529) cpu_start: ESP-IDF:          v4.0-beta1 [0m
 [0;32mI (534) cpu_start: Starting app cpu, entry point is 0x40083014 [0m
 [0;32mI (0) cpu_start: App cpu up. [0m
 [0;32mI (544) heap_init: Initializing. RAM available for dynamic allocation: [0m
 [0;32mI (551) heap_init: At 3FFAFF10 len 000000F0 (0 KiB): DRAM [0m
 [0;32mI (557) heap_init: At 3FFB6388 len 00001C78 (7 KiB): DRAM [0m
 [0;32mI (563) heap_init: At 3FFB9A20 len 00004108 (16 KiB): DRAM [0m
 [0;32mI (569) heap_init: At 3FFBDB5C len 00000004 (0 KiB): DRAM [0m
 [0;32mI (575) heap_init: At 3FFCC8A0 len 00013760 (77 KiB): DRAM [0m
 [0;32mI (582) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM [0m
 [0;32mI (588) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM [0m
 [0;32mI (594) heap_init: At 40099FB8 len 00006048 (24 KiB): IRAM [0m
 [0;32mI (601) cpu_start: Pro cpu start user code [0m
 [0;32mI (619) spi_flash: detected chip: generic [0m
 [0;32mI (619) spi_flash: flash io: dio [0m
 [0;32mI (620) cpu_start: Chip Revision: 1 [0m
 [0;33mW (621) cpu_start: Chip revision is higher than the one configured in menuconfig. Suggest to upgrade it. [0m
 [0;32mI (632) cpu_start: Starting scheduler on PRO CPU. [0m
 [0;32mI (0) cpu_start: Starting scheduler on APP CPU. [0m
MicroPython v1.12 on 2019-12-20; ESP32 module with ESP32
Type "help()" for more information.

Integrated Development Environment

Any Python IDE may be useful for programming in MicroPython (or CircuitPython if you are using an Adafruit compatible board). I personally recommend using Mu (https://codewith.mu/). Mu is programmed in Python, so it works on Windows, Linux and macOS. It also has a portable (pen drive friendly version) which it makes it ideal for students that uses university computers.

Select ESP MicroPython. This preference can be changed later.

Testing the module can be done using the Python REPL that be have already flashed. For example, importing the machine package can give us access to getting and settings properties of our board. For example, the Huzzah32 (ESP32) board runs at 240MHz, but when flashing MicroPython the clock is set at 160Mhz. That can be check running:

MicroPython v1.12 on 2019-12-20; ESP32 module with ESP32
Type "help()" for more information.
>>> import machine
>>> machine.freq()

Increasing the CPU frequency back to 240MHz can be done calling the same function as shown below:

>>> machine.freq(240000000)    # set frequency back to 240MHz
I (1604452) pm_esp32: Frequency switching config: CPU_MAX: 240, APB_MAX: 240, APB_MIN: 240, Light sleep: DISABLED
>>> machine.freq()