Skip to content

Sample Workflow 2

Dave Horton edited this page Mar 26, 2025 · 31 revisions

Managing a Multi-File Project with VS Code and MPRemote

This example shows how to use the extension to manage a project with more than just a single main.py. An iterative process of development and testing is used to create a simple REST API on the ESP32. The "golden copy" project files are stored on the development host and then converted to a git repository for change control.

This tutorial uses an ESP32 and MicroPython 1.20.

Preparing the ESP32

We'll start from a fresh install of MicroPython, so grab an ESP32 you don't mind overwriting and flash it with the latest version of MicroPython.

Flashing firmware

I have another VS Code extension called ESPTool that provides a wrapper around esptool, letting you flash ESP from inside VS Code. But, you can always use the traditional command-line method if that's what you're used to. If you choose to install and use the ESPTool VS Code extension, here's how you use it to flash:

  1. Press CTRL + SHIFT + P and start typing write_flash to access the command ESPTool: Write firmware file (write_flash).
  2. Navigate the file selection dialog to find the MiscroPython BIN file you downloaded for your device.
  3. Choose Overwrite when prompted and watch the progress in the terminal window.

Testing

Restart the microcontroller and use MPRemote's devs command to verify it's attached and working. Use the ls and cat commands to inspect the boot.py that comes preinstalled to further verify flashing was successful.

The VS Code terminal window should look something like this:

PS C:\Users\Dave\Desktop\API Project> py.exe -m mpremote connect COM7 fs ls /
ls :/
         431 boot.py

PS C:\Users\Dave\Desktop\API Project> py.exe -m mpremote connect COM7 fs cat /boot.py
# This file is executed on every boot (including wake-boot from deepsleep)
#import esp
#esp.osdebug(None)
#import webrepl
#webrepl.start()

So far, so good.

Creating a project directory

Because there will be multiple files used with this project, and the development host will hold the "golden copy" of those files, we'll organize them in a parent directory called API Project.

  1. Create a directory somewhere on your development system and call it API Project
  2. Open this directory in VS Code (File > Open Folder...)
  3. Notice how API Project is shown in the Explorer pane on the left side of VS Code

We're ready to start coding.

Connecting to WiFi

In this first iteration of the project, we'll create a new boot.py that gets the ESP32 connected to WiFi.

Creating boot.py in VS Code

The first step is to create boot.py in the project folder. To do that, hover your mouse over API PROJECT in the VS Code Explorer pane and click on the New File icon. Give it a name of boot.py

Visual Studio Code will open the new, empty file as a tab in the editor pane. VS Code can determine from the extension that it's a Python language file and will display the Python logo next to the file name.

Copy the code below and paste it into the editor window.

from network import WLAN, STA_IF
from time import ticks_diff, ticks_ms
from config import WIFI_NAME, WIFI_PASS, WIFI_TIMEOUT

wlan = WLAN(STA_IF)
wlan.active(True)
print("Connecting to WiFi...", end='')
wlan.connect(WIFI_NAME, WIFI_PASS)
start_time = ticks_ms()
while not wlan.isconnected():
    if (ticks_diff(ticks_ms(), start_time) > WIFI_TIMEOUT * 1000):
        print(".", end='')
if (wlan.isconnected()):
    print(f"\n{wlan.ifconfig()[0]}")
else:
    print("Timed out.")

Save the file locally (Either File > Save, or CTRL + S)

Storing WiFi credentials in config.py

Notice the line in boot.py that reads from config import WIFI_NAME, WIFI_PASS, WIFI_TIMEOUT. For the WiFi connection to work, you'll need to supply your access point credentials in a file called config.py. Here's a sample:

WIFI_NAME = 'MyAccessPointSSID'
WIFI_PASS = 'Sup3rSecr3tP@ssword'
WIFI_TIMEOUT = 30

Open another file using the same method you did to create boot.py, but call this one config.py. Copy the contents from above and paste it into the resulting editor window. Adjust the credentials for your network and save the file.

Uploading files

To prepare for testing by you'll need to upload both boot.py and config.py to the root of the flash filesystem on your ESP32. Because there's more than one file, it's more convenient to use the MPRemote extension's sync command rather than the cp command we used in first workflow example.

Press CTRL + SHIFT + P and start typing sync. You should see the command shown below.

>MPRemote: Upload project files to remote filesystem (sync)

Select the command. After confirming the overwrite, all the files in your development host's project directory will be recursively copied to the flash filesystem of the ESP32 microcontroller and the directory layout will be preserved. So far it's only two files, but as we continue to add to the project, the advantages of sync over cp quickly become apparent.

Testing

Start a REPL prompt (CTRL + SHIFT + P, repl) and then restart the ESP32 board by pressing the RESET button. Watch the VS Code terminal window as it boots.

If all goes well, it will connect to WiFi and show the IP address. Exit the REPL prompt (CTRL-X) before moving on to the next step.

Installing Module Dependencies

The API in this example uses a MicroPython package called Thimble. It's installable with mip using the following package name.

github:DavesCodeMusings/thimble

Use the MPRemote VS Code extension's mip command to install the module. You should see thimble.py in the /lib directory when the installation is complete.

PS D:\Users\Dave\src\mpremote-vscode> py.exe -m mpremote connect COM7 fs ls /lib
ls :/lib
       16114 thimble.py

Creating the API

Thimble takes many cues from Python's Flask when it comes to creating apps and routes. In this iteration, well start with a simple route for /api that returns 'Hello API!' in response to a GET request.

Defining a route

Open a new file in VS Code and paste the contents from below.

from thimble import Thimble

app = Thimble() 

@app.route('/api')
def hello(req):
    return 'Hello API!'

app.run(debug=True)

Save the file locally as main.py

Testing

Prepare for testing by uploading main.py to the root of ESP32's filesystem. You can use cp to upload just the main.py or sync to upload everything in the project directory, including main.py.

Viewing Debug Output in VS Code

  1. Start a REPL prompt (CTRL + SHIFT + P, repl).
  2. Reset the ESP32 board using the RESET button.
  3. Watch the VS Code terminal window.

You should see the ESP's IP address followed by a 'Listening' message showing that boot.py and main.py have executed successfully.

PS D:\Users\Dave\src\mpremote-vscode> py.exe -m mpremote connect COM7 repl
Connected to MicroPython at COM7
Use Ctrl-] or Ctrl-x to exit this shell
ESP-ROM:esp32s3-20210327
Build:Mar 27 2021
rst:0x1 (POWERON),boot:0x8 (SPI_FAST_FLASH_BOOT)
SPIWP:0xee
mode:DIO, clock div:1
load:0x3fce3808,len:0xf34
load:0x403c9700,len:0xc44
load:0x403cc700,len:0x2c78
entry 0x403c98f4
192.168.0.42
Listening on 0.0.0.0:80

Keep in mind your IP address will be unique to your network.

Accessing the API in a browser

Open a web browser and navigate to the IP address followed by /api. For example: http://192.168.0.42/api

Be sure to use http and not https. Some web browsers may try to use https.

You should see the Hello message displayed in your browser in plain text.

Hello API!

Looking at the VS Code terminal window, you'll see debug output giving information about the request. It'll look something like what's shown below.

Connection from client: 192.168.0.43
Request: {'path': '/api', 'headers': {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0', 'Upgrade-Insecure-Requests': '1', 'Accept-Language': 'en-US,en;q=0.5', 'DNT': '1', 'Accept-Encoding': 'gzip, deflate', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8', 'Host': '192.168.0.48', 'Connection': 'keep-alive'}, 'method': 'GET', 'http_version': 'HTTP/1.1', 'body': ''}
Connection closed for 192.168.0.43

When you're satisfied everything is working, press CTRL+C to cancel the running server. Then press CTRL+X to exit the REPL.

Serving static HTML content

In this final iteration of the project, we'll create a static HTML file to deliver a similar Hello message. Similar to Flask, the Thimble library will look for static content in the /static directory.

Creating the HTML file

Open a new file in VS Code and select HTML as the type. Paste the content from below into VS Code.

<!DOCTYPE html>

<html>
  <head>
    <title>Hello HTML!</title>
  </head>

  <body>
    <p>Hello HTML!</p>
  </body>
</html>

Save the file locally by first creating a new directory called static and then naming the file hello.html

Testing

Prepare for testing by using sync to upload hello.html and its parent directory. Verify everything looks okay with ls. You should see something similar to the example below. Remember hello.html is inside the static/ directory.

PS C:\Users\Dave\Desktop\API Project> py.exe -m mpremote connect COM5 fs ls /
ls :/
         478 boot.py
          68 config.py
           0 lib/
         125 main.py
           0 static/

Open a REPL prompt in VS Code and reset the ESP32 just like the previous step.

Use the URL in the browser that references the hello.html file. For example: http://192.168.0.42/hello.html

You should see the Hello message in your browser. You can use the browser feature to view source to verify it is indeed the same HTML content as on the ESP32.

Hello HTML!

Managing with Source Control

One of the advantages to using VS Code to develop your MicroPython code is its ability to easily interact with git repositories. In this final step, we'll look briefly at initializing a git repository for the code created so far. You can further safeguard your code and share it by pushing a copy to a GitHub or GitLab account.

To take advantage of git, you'll need to initialize a repository in the folder where your project files are stored. To do this, access the command palette and begin typing git init to find the command. Select the directory name from the selection list.

Notice how the Source Control icon indicates there are changes ready to be staged and committed. Once you stage and commit the initial copies of files, you'll have the ability to review any changes made and roll back if needed.

The sync command mentioned above will automatically skip the .git directory, so you won't end up with .git on your microcontroller's filesystem.

Summarizing the workflow

The idea of working this way is to keep the "golden copy" of the code and data files on the development host. Edits are done on the development host using VS Code and uploaded to the microcontroller using the MPRemote extension. Any required modules are pulled down using mip.

Advantages to this approach include:

  • The microcontroller can be flashed at anytime without fear of losing files. The "golden copy" is always on the development host.
  • The directory on the development host can be initialized as a git repository. VS Code integrates well with git, making it easy to share your MicroPython code.

Next Steps

Now that you've been through a sample project, you're ready to continue developing with VS Code and the MPRemote extension. If you find a bug or think of a useful feature for the extension, you can visit the project's GitHub page and add an issue. Please keep in mind I am developing this extension as a team of one in my limited spare time. I'll be concentrating mostly on bug fixes rather than feature requests and may be slow at responding.