Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 74 additions & 50 deletions docs/environment.rst
Original file line number Diff line number Diff line change
@@ -1,65 +1,77 @@
Environment Variables
=====================

CircuitPython 8.0.0 introduces support for environment variables. Environment
variables are commonly used to store "secrets" such as Wi-Fi passwords and API
keys. This method *does not* make them secure. It only separates them from the
code.
CircuitPython provides support for environment variables. These values
can be examined by user code, and are also used as settings by CircuitPython during startup.

CircuitPython uses a file called ``settings.toml`` at the drive root (no
folder) as the environment. User code can access the values from the file
using `os.getenv()`. It is recommended to save any values used repeatedly in a
variable because `os.getenv()` will parse the ``settings.toml`` file contents
on every access.
CircuitPython looks for a file called ``settings.toml`` at the ``CIRCUITPY`` drive root
to find the values of environment variables,
The file format is a subset of the `TOML config file language <https://toml.io>`__.

CircuitPython only supports a subset of the full toml specification, see below
for more details. The subset is very "Python-like", which is a key reason we
selected the format.
User code can access the values from the file using either `os.getenv()` or `supervisor.get_setting()`
The value returned by `os.getenv()` is always a string, but `supervisor.get_setting()`
will parse a value into a Python object: a string, an integer, a float, or a boolean.

Due to technical limitations it probably also accepts some files that are
not valid TOML files; bugs of this nature are subject to change (i.e., be
fixed) without the usual deprecation period for incompatible changes.
Both `os.getenv()` and `supervisor.get_setting()`
read and parse the ``settings.toml`` file on every access.
It will save time to copy any values you use repeatedly into variables.

File format example:
Environment variables are sometimes used to store "secrets" such as Wi-Fi passwords and API
keys. The ``settings.toml`` file *does not* make the secrets secure. It only separates them from the
code.

.. code-block::
CircuitPython supports only a subset of the full TOML specification; see below for more details.
The subset is very "Python-like", which is a key reason the format was selected.
To make the code simpler, the implementation accepts some files that are
not valid TOML, but do not depend on this.

str_key="Hello world" # with trailing comment
int_key = 7
unicode_key="œuvre"
unicode_key2="\\u0153uvre" # same as above
unicode_key3="\\U00000153uvre" # same as above
escape_codes="supported, including \\r\\n\\"\\\\"
# comment
[subtable]
subvalue="cannot retrieve this using getenv"
The full TOML specification provides for tables labeled with table names in brackets, like
``[table_name]``.
CircuitPython does not support this and ignores any explicit inline TOML tables.

Here is an example ``settings.toml`` file.
Entries consist of a key and value, separated by an ``=`` sign.
Upper and lower case may both be used in the key name.

Details of the toml language subset
.. code-block::

# Comment.
CIRCUITPY_WIFI_PASSWORD = "mypassword"
GREETING="Hello world" # trailing comments are ok
REPEAT_COUNT = 7 # an integer
CIRCUITPY_SDCARD_USB = false # a boolean
delay = 0.75 # a float
FRENCH="œuvre" # unicode can be used
FRENCH2="\\u0153uvre" # same unicode string, using a 16-bit escape code
FRENCH3="\\U00000153uvre" # same unicode string, using a 32-bit escape code
STRING_WITH_ESCAPE_CODES="supported, including \\r\\n\\"\\\\"

Details of the TOML language subset
-----------------------------------

* The content is required to be in UTF-8 encoding
* The supported data types are string and integer
* Only basic strings are supported, not triple-quoted strings
* Only integers supported by strtol. (no 0o, no 0b, no underscores 1_000, 011
is 9, not 11)
* Only bare keys are supported
* The content must be in UTF-8 encoding
* The supported data types are strings, integers, floats, and booleans.
* Whitespace is allowed.
* Only basic strings are supported, not triple-quoted strings.
* Only integers supported by ``strtol()`` can be parsed:
no ``0o``, no ``0b``, no underscores ``1_000``, ``011`` is 9, not 11.
* Only bare keys are supported.
* Duplicate keys are not diagnosed.
* Comments are supported
* Only values from the "root table" can be retrieved
* due to technical limitations, the content of multi-line
strings can erroneously be parsed as a value.
* Comments are allowed.
* Only values from the "root table" can be retrieved.


CircuitPython behavior
----------------------

CircuitPython will also read the environment to configure its behavior. Some keys are read at
startup once and others are read on reload (ctrl-D in the REPL). If a reload doesn't change things,
then try a reset (a power cycle or pressing the reset button). Other keys are ignored by CircuitPython.
Here are the keys it uses:
On startup, CircuitPython looks for for certain key/value pairs to use as configuration values.
Some values are read only once, after a hard reset, and others are read on each reload (ctrl-D in the REPL).
If you edit ``settings.toml`` and a reload doesn't read your changes,
then try a hard reset (a power cycle or pressing the reset button).
You can also include any other key/value pairs in the file for use with your own code.

Core CircuitPython keys
^^^^^^^^^^^^^^^^^^^^^^^
Keys that affect CircuitPython behavior
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

CIRCUITPY_BLE_NAME
~~~~~~~~~~~~~~~~~~
Expand All @@ -83,24 +95,36 @@ Used to avoid "Pystack exhausted" errors when the code can't be reworked to avoi
CIRCUITPY_WEB_API_PASSWORD
~~~~~~~~~~~~~~~~~~~~~~~~~~
Password required to make modifications to the board from the Web Workflow.
If the password is not specified, the Web Workflow is not enabled.

CIRCUITPY_WEB_API_PORT
~~~~~~~~~~~~~~~~~~~~~~
TCP port number used for the web HTTP API. Defaults to 80 when omitted.
TCP port number used for the Web Workflow HTTP API. Defaults to 80 when omitted.

CIRCUITPY_WEB_INSTANCE_NAME
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Name the board advertises as for the WEB workflow. Defaults to human readable board name if omitted.
Hostname the board advertises as, using mDNS, for the Web Workflow.
Defaults to a semi-unique name if omitted.

CIRCUITPY_WIFI_SSID
~~~~~~~~~~~~~~~~~~~
CIRCUITPY_WIFI_PASSWORD
~~~~~~~~~~~~~~~~~~~~~~~
Wi-Fi password used to auto connect to CIRCUITPY_WIFI_SSID.
If these values are specified,
CircuitPython will connect automatically to a local WiFi network using the supplied SSID
and password before ``boot.py`` and/or ``code.py`` are run.

CIRCUITPY_SDCARD_USB
^^^^^^^^^^^^^^^^^^^^
Present a mounted SD card as a USB MSC device. If the board has default pins for an SD card socket,
the card is mounted automatically on startup.
Only one card can be presented.
Defaults to ``true``.
SD card presentation can slow down board startup,
so set this to ``false`` if you don't need this feature.

CIRCUITPY_WIFI_SSID
~~~~~~~~~~~~~~~~~~~
Wi-Fi SSID to auto-connect to even if user code is not running.

Additional board specific keys
Additional board-specific keys
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

CIRCUITPY_DISPLAY_WIDTH (Sunton, MaTouch)
Expand Down
40 changes: 33 additions & 7 deletions supervisor/shared/usb/usb_msc_flash.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "shared-module/storage/__init__.h"
#include "supervisor/filesystem.h"
#include "supervisor/shared/reload.h"
#include "supervisor/shared/settings.h"

#define MSC_FLASH_BLOCK_SIZE 512

Expand Down Expand Up @@ -365,24 +366,49 @@ void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16
memcpy(product_rev, CFG_TUD_MSC_PRODUCT_REV, strlen(CFG_TUD_MSC_PRODUCT_REV));
}

#ifdef SDCARD_LUN
#if CIRCUITPY_SETTINGS_TOML
typedef enum {
SDCARD_USB_SETTING_NOT_YET_READ = 0,
SDCARD_USB_SETTING_TRUE,
SDCARD_USB_SETTING_FALSE,
} sdcard_usb_setting_state_t;

static sdcard_usb_setting_state_t _sdcard_usb_setting_state = SDCARD_USB_SETTING_NOT_YET_READ;

// Read only once to save file access time.
static bool sdcard_usb_enabled(void) {
if (_sdcard_usb_setting_state == SDCARD_USB_SETTING_NOT_YET_READ) {
bool setting = true;
(void)settings_get_bool("CIRCUITPY_SDCARD_USB", &setting);
_sdcard_usb_setting_state = setting ? SDCARD_USB_SETTING_TRUE : SDCARD_USB_SETTING_FALSE;
}
return _sdcard_usb_setting_state == SDCARD_USB_SETTING_TRUE;
}
#else
static bool sdcard_usb_enabled(void) {
return CIRCUITPY_SDCARD_USB;
}
#endif
#endif

// Invoked when received Test Unit Ready command.
// return true allowing host to read/write this LUN e.g SD card inserted
bool tud_msc_test_unit_ready_cb(uint8_t lun) {
if (lun >= LUN_COUNT) {
return false;
}

#ifdef SDCARD_LUN
if (lun == SDCARD_LUN) {
automount_sd_card();
}
#endif

fs_user_mount_t *current_mount = get_vfs(lun);
if (current_mount == NULL) {
return false;
}
if (ejected[lun] || eject_once[lun]) {

if (ejected[lun] || eject_once[lun]
#ifdef SDCARD_LUN
|| (lun == SDCARD_LUN && !sdcard_usb_enabled())
#endif
) {
eject_once[lun] = false;
// Set 0x3a for media not present.
tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3A, 0x00);
Expand Down
Loading