From fea7505206f4b825be7666e1a262dfb13eb847c5 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Sun, 10 May 2026 22:11:11 -0400 Subject: [PATCH 1/3] supervisor/shared/usb/usb_msc_flash.c: support CIRCUITPY_SDCARD_USB in settings.toml Document CIRCUITPY_SDCARD_USB and rework settings.toml documentation --- docs/environment.rst | 124 +++++++++++++++----------- supervisor/shared/usb/usb_msc_flash.c | 34 +++++-- 2 files changed, 101 insertions(+), 57 deletions(-) diff --git a/docs/environment.rst b/docs/environment.rst index 36404eb81f928..a94154dfc13ba 100644 --- a/docs/environment.rst +++ b/docs/environment.rst @@ -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 `__. -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 onlya 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 ~~~~~~~~~~~~~~~~~~ @@ -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) diff --git a/supervisor/shared/usb/usb_msc_flash.c b/supervisor/shared/usb/usb_msc_flash.c index 27277bc5ac52f..478d959f8f63d 100644 --- a/supervisor/shared/usb/usb_msc_flash.c +++ b/supervisor/shared/usb/usb_msc_flash.c @@ -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 @@ -365,6 +366,26 @@ 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 +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; +} +#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) { @@ -372,17 +393,16 @@ bool tud_msc_test_unit_ready_cb(uint8_t lun) { 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); From d1fd820a848590dd0bc8fc6b7db7adeb1f5f953a Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Mon, 11 May 2026 13:38:13 -0400 Subject: [PATCH 2/3] Update docs/environment.rst Co-authored-by: Scott Shawcroft --- docs/environment.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/environment.rst b/docs/environment.rst index a94154dfc13ba..ed32cfe89dce2 100644 --- a/docs/environment.rst +++ b/docs/environment.rst @@ -20,7 +20,7 @@ Environment variables are sometimes used to store "secrets" such as Wi-Fi passwo keys. The ``settings.toml`` file *does not* make the secrets secure. It only separates them from the code. -CircuitPython supports onlya subset of the full TOML specification; see below for more details. +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. From b085a9727ea533e55d5a99f14d5de2eafb3626aa Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Mon, 11 May 2026 17:53:40 -0400 Subject: [PATCH 3/3] handle boards that support sdcardio but don't support settings.toml --- supervisor/shared/usb/usb_msc_flash.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/supervisor/shared/usb/usb_msc_flash.c b/supervisor/shared/usb/usb_msc_flash.c index 478d959f8f63d..6f15b508095c4 100644 --- a/supervisor/shared/usb/usb_msc_flash.c +++ b/supervisor/shared/usb/usb_msc_flash.c @@ -367,6 +367,7 @@ void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16 } #ifdef SDCARD_LUN +#if CIRCUITPY_SETTINGS_TOML typedef enum { SDCARD_USB_SETTING_NOT_YET_READ = 0, SDCARD_USB_SETTING_TRUE, @@ -384,6 +385,11 @@ static bool sdcard_usb_enabled(void) { } 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.