9.2 KiB
Workflows
Workflows are the process used to 1) manipulate files on the CircuitPython device and 2) interact with the serial connection to CircuitPython. The serial connection is usually used to access the REPL.
Starting with CircuitPython 3.x we moved to a USB-only workflow. Prior to that, we used the serial connection alone to do the whole workflow. In CircuitPython 7.x, a BLE workflow was added with the advantage of working with mobile devices. CircuitPython 8.x added a web workflow that works over the local network (usually Wi-Fi) and a web browser. Other clients can also use the Web REST API. Boards should clearly document which workflows are supported.
Code for workflows lives in supervisor/shared
.
The workflow APIs are documented here.
USB
These USB interfaces are enabled by default on boards with USB support. They are usable once the device has been plugged into a host.
CIRCUITPY drive
CircuitPython exposes a standard mass storage (MSC) interface to enable file manipulation over a standard interface. This interface works underneath the file system at the block level so using it excludes other types of workflows from manipulating the file system at the same time.
CDC serial
CircuitPython exposes one CDC USB interface for CircuitPython serial. This is a standard serial USB interface.
TODO: Document how it designates itself from the user CDC.
Setting baudrate 1200 and disconnecting will reboot into a bootloader. (Used by Arduino to trigger a reset into bootloader.)
BLE
The BLE workflow is enabled for nRF boards. By default, to prevent malicious access, it is disabled.
To connect to the BLE workflow, press the reset button while the status led blinks blue quickly
after the safe mode blinks. The board will restart and broadcast the file transfer service UUID
(0xfebb
) along with the board's Creation IDs. This
public broadcast is done at a lower transmit level so the devices must be closer. On connection, the
device will need to pair and bond. Once bonded, the device will broadcast whenever disconnected
using a rotating key rather than a static one. Non-bonded devices won't be able to resolve it. After
connection, the central device can discover two default services. One for file transfer and one for
CircuitPython specifically that includes serial characteristics.
File Transfer API
CircuitPython uses an open File Transfer API to enable file system access.
CircuitPython Service
The base UUID for the CircuitPython service is ADAFXXXX-4369-7263-7569-7450794686e
. The XXXX
is
replaced by the four specific digits below. The service itself is 0001
.
TX - 0002
/ RX - 0003
These characteristic work just like the Nordic Uart Service (NUS) but have different UUIDs to prevent conflicts with user created NUS services.
Version - 0100
Read-only characteristic that returns the UTF-8 encoded version string.
Web
The web workflow is depends on adding Wi-Fi credentials into the /.env
file. The keys are
CIRCUITPY_WIFI_SSID
and CIRCUITPY_WIFI_PASSWORD
. Once these are defined, CircuitPython will
automatically connect to the network and start the webserver used for the workflow. The webserver
is on port 80. It also enables MDNS.
MDNS is used to resolve circuitpython.local
to a device specific
hostname of the form cpy-XXXXXX.local
. The XXXXXX
is based on network MAC address. The device
also provides the MDNS service with service type _circuitpython
and protocol _tcp
.
The web server is HTTP 1.1 and may use chunked responses so that it doesn't need to precompute content length.
/
The root welcome page links to the file system page and also displays other CircuitPython devices
found using MDNS service discovery. This allows web browsers to find other devices from one. (All
devices will respond to circuitpython.local
so the device redirected to may vary.)
CORS
The web server will allow requests from cpy-XXXXXX.local
, 127.0.0.1
, the device's IP and
code.circuitpython.org
. (circuitpython.local
requests will be redirected to cpy-XXXXXX.local
.)
File REST API
All file system related APIs are protected by HTTP basic authentication. It is NOT secure but will hopefully prevent some griefing in shared settings. The password is sent unencrypted so do not reuse a password with something important.
The password is taken from /.env
with the key CIRCUITPY_WEB_API_PASSWORD
. If this is unset, the
server will respond with 403 Forbidden
. When a password is set, but not provided in a request, it
will respond 401 Unauthorized
.
/fs/
The /fs/
page will respond with a directory browsing HTML once authenticated. This page is always
gzipped. If the Accept: application/json
header is provided, then the JSON representation of the
root will be returned.
OPTIONS
When requested with the OPTIONS
method, the server will respond with .
/fs/<directory path>/
Directory paths must end with a /. Otherwise, the path is assumed to be a file.
GET
Returns a JSON representation of the directory.
200 OK
- Directory exists and JSON returned404 Not Found
- Missing directory
PUT
Tries to make a directory at the given path. Request body is ignored. Returns:
204 No Content
- Directory exists201 Created
- Directory created409 Conflict
- USB is active and preventing file system modification404 Not Found
- Missing parent directory500 Server Error
- Other, unhandled error
Example:
sh curl -v -u :passw0rd -X PUT -L --location-trusted http://circuitpython.local/fs/lib/hello/world/
DELETE
Deletes the directory and all of its contents.
404 Not Found
- No directory409 Conflict
- USB is active and preventing file system modification
Example:
sh curl -v -u :passw0rd -X DELETE -L --location-trusted http://circuitpython.local/fs/lib/hello/world/
/fs/<file path>
GET
Returns the raw file contents. Content-Type
will be set based on extension:
text/plain
-.py
,.txt
text/javascript
-.js
text/html
-.html
application/json
-.json
application/octet-stream
- Everything else
Will return:
200 OK
- File exists and file returned404 Not Found
- Missing file
PUT
Stores the provided content to the file path. Returns:
201 Created
- File created and saved204 No Content
- File existed and overwritten404 Not Found
- Missing parent directory409 Conflict
- USB is active and preventing file system modification413 Payload Too Large
-Expect
header not sent and file is too large417 Expectation Failed
-Expect
header sent and file is too large500 Server Error
- Other, unhandled error
If the client sends the Expect
header, the server will reply with 100 Continue
when ok.
DELETE
Deletes the file.
404 Not Found
- File not found409 Conflict
- USB is active and preventing file system modification
Example:
sh curl -v -u :passw0rd -X DELETE -L --location-trusted http://circuitpython.local/fs/lib/hello/world.txt
/cp/
/cp/
serves basic info about the CircuitPython device and others discovered through MDNS. It is
not protected by basic auth in case the device is someone elses.
Only GET
requests are supported and will return XXX Method Not Allowed
otherwise.
/cp/version.json
Returns information about the device.
web_api_version
: Always1
. This versions the rest of the API and new versions may not be backwards compatible.version
: CircuitPython build version.build_date
: CircuitPython build date.board_name
: Human readable name of the board.mcu_name
: Human readable name of the microcontroller.board_id
: Board id used in code and on circuitpython.org.creator_id
: Creator ID for the board.creation_id
: Creation ID for the board, set by the creator.hostname
: MDNS hostname.port
: Port of CircuitPython Web Service.ip
: IP address of the device.
Example:
curl -v -L http://circuitpython.local/cp/version.json
{
"web_api_version": 1,
"version": "8.0.0-alpha.1-20-ge1d4518a9-dirty",
"build_date": "2022-06-24",
"board_name": "ESP32-S3-USB-OTG-N8",
"mcu_name": "ESP32S3",
"board_id": "espressif_esp32s3_usb_otg_n8",
"creator_id": 12346,
"creation_id": 28683,
"hostname": "cpy-f57ce8",
"port": 80,
"ip": "192.168.1.94"
}
/cp/devices.json
Returns information about other devices found on the network using MDNS.
total
: Total MDNS response count. May be more than indevices
if internal limits were hit.devices
: List of discovered devices.hostname
: MDNS hostnameinstance_name
: MDNS instance name. Defaults to human readable board name.port
: Port of CircuitPython Web APIip
: IP address
Example:
curl -v -L http://circuitpython.local/cp/devices.json
{
"total": 1,
"devices": [
{
"hostname": "cpy-951032",
"instance_name": "Adafruit Feather ESP32-S2 TFT",
"port": 80,
"ip": "192.168.1.235"
}
]
}
Static files
/favicon.ico
- Blinka/directory.js
- JavaScript for/fs/
/welcome.js
- JavaScript for/
WebSocket
Coming soon!