How to Write and Deploy Arduino sketches from Python
Automate Arduino sketches creation and deployment with Python and the arduino-cli

If you're an advanced Arduino user and experienced Python coder, you may gain a huge productivity boost in writing and deployment sketches thanks to this integration.
The everywhereml Python package is a wrapper around the arduino-cli and allows you to:
- generate Arduino sketches programmatically
- inject Python data into sketches
- compile and verify checkes
- upload to your board
How can these benefits turn into a productivity boost?
- you can generate a number of variations of the same sketch based on a configuration file
- you can integrate sketch compilation into your own build pipeline
- you can run benchmarks on many different boards
- you can create a script to flash many boards with ease
This page will function as a documentation page for how you can write, compile and deploy an Arduino sketch from Python.
Prerequisites
You must have the Arduino CLI installed (refer to the documentation).
Then you must have the everywhereml Python package installed.
pip install everywhereml>=0.2.2

Create a sketch
The everywhereml implements a few simple classes to create a sketch, mainly to wrap the sketch files and the arduino-cli itself.
FILENAME: main.py
from everywhereml.arduino import Sketch, Ino, H """ Create a sketch object. A sketch is defined by: - a name (required) - a folder (optional) If you leave the folder empty, the current working directory will be used. You can use the special name ':system:' to use the default Arduino sketches folder (as reported by the command `arduino-cli config dump`) """ sketch = Sketch(name="PyDuino", folder=":system:") """ Then you can add files to the project (either the .ino main file or C++ header files) """ sketch += Ino(""" #include "hello.h" void setup() { Serial.begin(115200); } void loop() { hello(); delay(1000); } """) sketch += H("hello.h", """ void hello() { Serial.println("hello"); } """
Once the sketch is defined, you can compile it.
Compile a sketch
To compile the sketch, you need to set the board you want to compile for. You can either use the exact name or a partial search query: the library will try its best to find a match. If more than one is found, you can choose which one to use.
FILENAME: main.py
""" Compile sketch for Arduino Nano 33 BLE board. The board you target must appear in the `arduino-cli board listall` command. If you know the FQBN (Fully Qualified Board Name), you can use that too. """ if sketch.compile(board='Nano 33 BLE').is_successful: print('Log', sketch.output) print('Sketch stats', sketch.stats) else: print('ERROR', sketch.output)
Log Sketch uses 83888 bytes (8%) of program storage space. Maximum is 983040 bytes. Global variables use 43920 bytes (16%) of dynamic memory, leaving 218224 bytes for local variables. Maximum is 262144 bytes. Sketch stats {'compile_time': 8.248049780999736, 'flash': 83888, 'flash_max': 983040, 'flash_percent': 0.08533528645833334, 'ram': 43920, 'ram_max': 262144, 'ram_percent': 0.16754150390625}
The sketch.output
attribute holds the response from the arduino-cli compile
command (both on success or error).
The sketch.stats
attribute holds a few statistics that can come useful for your own use-case:
-
compile_time
: how long it took to compile the sketch -
flash
(with max and percent): how much flash space the sketch takes -
ram
(with max and percent): how much RAM memory the sketch takes
How you use these numbers is up to you.
I used them to create a benchmark of TinyML models on different boards, for example.
Upload a sketch
Last but not least, you can upload the sketch to your board.
FILENAME: main.py
""" You can specify the exact port """ sketch.upload(port='/dev/ttyUSB0') """ Or even part of it. The library will look for the best match. """ sketch.upload(port='ttyUSB') sketch.upload(port='/dev/cu.usbmodem') print(sketch.output)
dfu-util 0.10-dev Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc. Copyright 2010-2021 Tormod Volden and Stefan Schmidt This program is Free Software and has ABSOLUTELY NO WARRANTY Please report bugs to http://sourceforge.net/p/dfu-util/tickets/ dfu-util: Warning: Invalid DFU suffix signature dfu-util: A valid DFU suffix will be required in a future dfu-util release Opening DFU capable USB device... Device ID 2341:035b Device DFU version 011a Claiming USB DFU Interface... Setting Alternate Interface #0 ... Determining device status... DFU state(2) = dfuIDLE, status(0) = No error condition is present DFU mode device DFU version 011a Device returned transfer size 4096 DfuSe interface name: "Internal Flash " Downloading element to address = 0x08040000, size = 136712 Erase [=========================] 100% 136712 bytes Erase done. Download [=========================] 100% 136712 bytes Download done. File downloaded successfully Transitioning to dfuMANIFEST state
Pretty neat, don't you think?
Do you think this feature will supercharge your development flow?
Tell us in the comments!
Having troubles? Ask a question
Related posts
python
Machine Learning for MicroPython
Run your favourite Machine Learning algorithms on MicroPython and CircuitPython boards