Merge branch 'main' into webflow_edit_page
This commit is contained in:
commit
51006f5eeb
|
@ -143,7 +143,7 @@ jobs:
|
|||
|
||||
|
||||
mpy-cross-mac:
|
||||
runs-on: macos-10.15
|
||||
runs-on: macos-11
|
||||
steps:
|
||||
- name: Dump GitHub context
|
||||
env:
|
||||
|
@ -176,27 +176,25 @@ jobs:
|
|||
run: make -C mpy-cross -j2
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: mpy-cross-macos-catalina
|
||||
name: mpy-cross-macos-11-x64
|
||||
path: mpy-cross/mpy-cross
|
||||
- name: Select SDK for M1 build
|
||||
run: sudo xcode-select -switch /Applications/Xcode_12.3.app
|
||||
- name: Build mpy-cross (arm64)
|
||||
run: make -C mpy-cross -j2 -f Makefile.m1 V=2
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: mpy-cross-macos-bigsur-arm64
|
||||
name: mpy-cross-macos-11-arm64
|
||||
path: mpy-cross/mpy-cross-arm64
|
||||
- name: Make universal binary
|
||||
run: lipo -create -output mpy-cross-macos-universal mpy-cross/mpy-cross mpy-cross/mpy-cross-arm64
|
||||
run: lipo -create -output mpy-cross-macos-11-universal mpy-cross/mpy-cross mpy-cross/mpy-cross-arm64
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: mpy-cross-macos-universal
|
||||
path: mpy-cross-macos-universal
|
||||
name: mpy-cross-macos-11-universal
|
||||
path: mpy-cross-macos-11-universal
|
||||
- name: Upload mpy-cross build to S3
|
||||
run: |
|
||||
[ -z "$AWS_ACCESS_KEY_ID" ] || aws s3 cp mpy-cross-macos-universal s3://adafruit-circuit-python/bin/mpy-cross/mpy-cross-macos-universal-${{ env.CP_VERSION }} --no-progress --region us-east-1
|
||||
[ -z "$AWS_ACCESS_KEY_ID" ] || aws s3 cp mpy-cross/mpy-cross-arm64 s3://adafruit-circuit-python/bin/mpy-cross/mpy-cross-macos-bigsur-${{ env.CP_VERSION }}-arm64 --no-progress --region us-east-1
|
||||
[ -z "$AWS_ACCESS_KEY_ID" ] || aws s3 cp mpy-cross/mpy-cross s3://adafruit-circuit-python/bin/mpy-cross/mpy-cross-macos-catalina-${{ env.CP_VERSION }} --no-progress --region us-east-1
|
||||
[ -z "$AWS_ACCESS_KEY_ID" ] || aws s3 cp mpy-cross-macos-universal s3://adafruit-circuit-python/bin/mpy-cross/mpy-cross-macos-11-${{ env.CP_VERSION }}-universal --no-progress --region us-east-1
|
||||
[ -z "$AWS_ACCESS_KEY_ID" ] || aws s3 cp mpy-cross/mpy-cross-arm64 s3://adafruit-circuit-python/bin/mpy-cross/mpy-cross-macos-11-${{ env.CP_VERSION }}-arm64 --no-progress --region us-east-1
|
||||
[ -z "$AWS_ACCESS_KEY_ID" ] || aws s3 cp mpy-cross/mpy-cross s3://adafruit-circuit-python/bin/mpy-cross/mpy-cross-macos-11-${{ env.CP_VERSION }}-x64 --no-progress --region us-east-1
|
||||
env:
|
||||
AWS_PAGER: ''
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
|
|
23
conf.py
23
conf.py
|
@ -33,25 +33,6 @@ from sphinx import addnodes
|
|||
|
||||
tools_describe = str(pathlib.Path(__file__).parent / "tools/describe")
|
||||
|
||||
# Monkeypatch autoapi
|
||||
def _format_args(args_info, include_annotations=True, ignore_self=None):
|
||||
result = []
|
||||
|
||||
for i, (prefix, name, annotation, default) in enumerate(args_info):
|
||||
if i == 0 and ignore_self is not None and name == ignore_self:
|
||||
continue
|
||||
formatted = "{}{}{}{}".format(
|
||||
prefix or "",
|
||||
name or "",
|
||||
": {}".format(annotation) if annotation and include_annotations else "",
|
||||
(" = {}" if annotation else "={}").format(default) if default else "",
|
||||
)
|
||||
result.append(formatted)
|
||||
return ", ".join(result)
|
||||
|
||||
import autoapi.mappers.python.objects as objects
|
||||
objects._format_args = _format_args
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
|
@ -71,8 +52,8 @@ subprocess.check_output(["make", "stubs"])
|
|||
#modules_support_matrix = shared_bindings_matrix.support_matrix_excluded_boards()
|
||||
modules_support_matrix = shared_bindings_matrix.support_matrix_by_board()
|
||||
modules_support_matrix_reverse = defaultdict(list)
|
||||
for board, modules in modules_support_matrix.items():
|
||||
for module in modules[0]:
|
||||
for board, matrix_info in modules_support_matrix.items():
|
||||
for module in matrix_info["modules"]:
|
||||
modules_support_matrix_reverse[module].append(board)
|
||||
|
||||
modules_support_matrix_reverse = dict(
|
||||
|
|
|
@ -39,6 +39,10 @@ CIRCUITPY_WEB_API_PASSWORD
|
|||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Password required to make modifications to the board from the Web Workflow.
|
||||
|
||||
CIRCUITPY_WEB_API_PORT
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
TCP port number used for the web HTTP API. Defaults to 80 when omitted.
|
||||
|
||||
CIRCUITPY_WIFI_PASSWORD
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Wi-Fi password used to auto connect to CIRCUITPY_WIFI_SSID.
|
||||
|
|
|
@ -32,7 +32,7 @@ from concurrent.futures import ThreadPoolExecutor
|
|||
|
||||
SUPPORTED_PORTS = ['atmel-samd', 'broadcom', 'cxd56', 'espressif', 'litex', 'mimxrt10xx', 'nrf', 'raspberrypi', 'stm']
|
||||
|
||||
aliases_by_board = {
|
||||
ALIASES_BY_BOARD = {
|
||||
"circuitplayground_express": [
|
||||
"circuitplayground_express_4h",
|
||||
"circuitplayground_express_digikey_pycon2019",
|
||||
|
@ -43,7 +43,7 @@ aliases_by_board = {
|
|||
"pewpew10": ["pewpew13"],
|
||||
}
|
||||
|
||||
aliases_brand_names = {
|
||||
ALIASES_BRAND_NAMES = {
|
||||
"circuitplayground_express_4h":
|
||||
"Adafruit Circuit Playground Express 4-H",
|
||||
"circuitplayground_express_digikey_pycon2019":
|
||||
|
@ -58,7 +58,7 @@ aliases_brand_names = {
|
|||
"PewPew 13",
|
||||
}
|
||||
|
||||
additional_modules = {
|
||||
ADDITIONAL_MODULES = {
|
||||
"fontio": "CIRCUITPY_DISPLAYIO",
|
||||
"terminalio": "CIRCUITPY_DISPLAYIO",
|
||||
"adafruit_bus_device": "CIRCUITPY_BUSDEVICE",
|
||||
|
@ -66,7 +66,7 @@ additional_modules = {
|
|||
"usb": "CIRCUITPY_USB_HOST",
|
||||
}
|
||||
|
||||
frozen_excludes = ["examples", "docs", "tests", "utils", "conf.py", "setup.py"]
|
||||
FROZEN_EXCLUDES = ["examples", "docs", "tests", "utils", "conf.py", "setup.py"]
|
||||
"""Files and dirs at the root of a frozen directory that should be ignored.
|
||||
This is the same list as in the preprocess_frozen_modules script."""
|
||||
|
||||
|
@ -74,7 +74,7 @@ repository_urls = {}
|
|||
"""Cache of repository URLs for frozen modules."""
|
||||
|
||||
def get_circuitpython_root_dir():
|
||||
""" The path to the root './circuitpython' directory
|
||||
""" The path to the root './circuitpython' directory.
|
||||
"""
|
||||
file_path = pathlib.Path(__file__).resolve()
|
||||
root_dir = file_path.parent.parent
|
||||
|
@ -82,12 +82,40 @@ def get_circuitpython_root_dir():
|
|||
return root_dir
|
||||
|
||||
def get_shared_bindings():
|
||||
""" Get a list of modules in shared-bindings based on folder names
|
||||
""" Get a list of modules in shared-bindings based on folder names.
|
||||
"""
|
||||
shared_bindings_dir = get_circuitpython_root_dir() / "shared-bindings"
|
||||
return [item.name for item in shared_bindings_dir.iterdir()] + ["binascii", "errno", "json", "re", "ulab"]
|
||||
|
||||
|
||||
def get_board_mapping():
|
||||
"""
|
||||
Compiles the list of boards from the directories, with aliases and mapping
|
||||
to the port.
|
||||
"""
|
||||
boards = {}
|
||||
for port in SUPPORTED_PORTS:
|
||||
board_path = os.path.join("../ports", port, "boards")
|
||||
for board_path in os.scandir(board_path):
|
||||
if board_path.is_dir():
|
||||
board_files = os.listdir(board_path.path)
|
||||
board_id = board_path.name
|
||||
aliases = ALIASES_BY_BOARD.get(board_path.name, [])
|
||||
boards[board_id] = {
|
||||
"port": port,
|
||||
"download_count": 0,
|
||||
"aliases": aliases,
|
||||
}
|
||||
for alias in aliases:
|
||||
boards[alias] = {
|
||||
"port": port,
|
||||
"download_count": 0,
|
||||
"alias": True,
|
||||
"aliases": [],
|
||||
}
|
||||
return boards
|
||||
|
||||
|
||||
def read_mpconfig():
|
||||
""" Open 'circuitpy_mpconfig.mk' and return the contents.
|
||||
"""
|
||||
|
@ -112,8 +140,8 @@ def build_module_map():
|
|||
full_build = False
|
||||
for module in modules:
|
||||
full_name = module
|
||||
if module in additional_modules:
|
||||
search_identifier = additional_modules[module]
|
||||
if module in ADDITIONAL_MODULES:
|
||||
search_identifier = ADDITIONAL_MODULES[module]
|
||||
else:
|
||||
search_identifier = 'CIRCUITPY_'+module.lstrip("_").upper()
|
||||
re_pattern = f"{re.escape(search_identifier)}\s*\??=\s*(.+)"
|
||||
|
@ -204,27 +232,33 @@ def get_repository_url(directory):
|
|||
repository_urls[directory] = path
|
||||
return path
|
||||
|
||||
def frozen_modules_from_dirs(frozen_mpy_dirs):
|
||||
def frozen_modules_from_dirs(frozen_mpy_dirs, withurl):
|
||||
"""
|
||||
Go through the list of frozen directories and extract the python modules.
|
||||
Paths are of the type:
|
||||
$(TOP)/frozen/Adafruit_CircuitPython_CircuitPlayground
|
||||
$(TOP)/frozen/circuitpython-stage/meowbit
|
||||
Python modules are at the root of the path, and are python files or directories
|
||||
containing python files. Except the ones in the frozen_excludes list.
|
||||
containing python files. Except the ones in the FROZEN_EXCLUDES list.
|
||||
"""
|
||||
frozen_modules = []
|
||||
for frozen_path in filter(lambda x: x, frozen_mpy_dirs.split(" ")):
|
||||
source_dir = get_circuitpython_root_dir() / frozen_path[7:]
|
||||
url_repository = get_repository_url(source_dir)
|
||||
for sub in source_dir.glob("*"):
|
||||
if sub.name in frozen_excludes:
|
||||
if sub.name in FROZEN_EXCLUDES:
|
||||
continue
|
||||
if sub.name.endswith(".py"):
|
||||
frozen_modules.append((sub.name[:-3], url_repository))
|
||||
if withurl:
|
||||
frozen_modules.append((sub.name[:-3], url_repository))
|
||||
else:
|
||||
frozen_modules.append(sub.name[:-3])
|
||||
continue
|
||||
if next(sub.glob("**/*.py"), None): # tests if not empty
|
||||
frozen_modules.append((sub.name, url_repository))
|
||||
if withurl:
|
||||
frozen_modules.append((sub.name, url_repository))
|
||||
else:
|
||||
frozen_modules.append(sub.name)
|
||||
return frozen_modules
|
||||
|
||||
def lookup_setting(settings, key, default=''):
|
||||
|
@ -244,7 +278,7 @@ def all_ports_all_boards(ports=SUPPORTED_PORTS):
|
|||
continue
|
||||
yield (port, entry)
|
||||
|
||||
def support_matrix_by_board(use_branded_name=True):
|
||||
def support_matrix_by_board(use_branded_name=True, withurl=True):
|
||||
""" Compiles a list of the available core modules available for each
|
||||
board.
|
||||
"""
|
||||
|
@ -272,29 +306,49 @@ def support_matrix_by_board(use_branded_name=True):
|
|||
board_modules.append(base[module]['name'])
|
||||
board_modules.sort()
|
||||
|
||||
if "CIRCUITPY_BUILD_EXTENSIONS" in settings:
|
||||
board_extensions = [
|
||||
extension.strip() for extension in
|
||||
settings["CIRCUITPY_BUILD_EXTENSIONS"].split(",")
|
||||
]
|
||||
else:
|
||||
raise OSError(f"Board extensions undefined: {board_name}.")
|
||||
|
||||
frozen_modules = []
|
||||
if "FROZEN_MPY_DIRS" in settings:
|
||||
frozen_modules = frozen_modules_from_dirs(settings["FROZEN_MPY_DIRS"])
|
||||
frozen_modules = frozen_modules_from_dirs(settings["FROZEN_MPY_DIRS"], withurl)
|
||||
if frozen_modules:
|
||||
frozen_modules.sort()
|
||||
|
||||
# generate alias boards too
|
||||
board_matrix = [(board_name, (board_modules, frozen_modules))]
|
||||
if entry.name in aliases_by_board:
|
||||
for alias in aliases_by_board[entry.name]:
|
||||
board_matrix = [(
|
||||
board_name, {
|
||||
"modules": board_modules,
|
||||
"frozen_libraries": frozen_modules,
|
||||
"extensions": board_extensions,
|
||||
}
|
||||
)]
|
||||
if entry.name in ALIASES_BY_BOARD:
|
||||
for alias in ALIASES_BY_BOARD[entry.name]:
|
||||
if use_branded_name:
|
||||
if alias in aliases_brand_names:
|
||||
alias = aliases_brand_names[alias]
|
||||
if alias in ALIASES_BRAND_NAMES:
|
||||
alias = ALIASES_BRAND_NAMES[alias]
|
||||
else:
|
||||
alias = alias.replace("_"," ").title()
|
||||
board_matrix.append( (alias, (board_modules, frozen_modules)) )
|
||||
board_matrix.append((
|
||||
alias, {
|
||||
"modules": board_modules,
|
||||
"frozen_libraries": frozen_modules,
|
||||
"extensions": board_extensions,
|
||||
},
|
||||
))
|
||||
|
||||
return board_matrix # this is now a list of (board,modules)
|
||||
|
||||
executor = ThreadPoolExecutor(max_workers=os.cpu_count())
|
||||
mapped_exec = executor.map(support_matrix, all_ports_all_boards())
|
||||
# flatmap with comprehensions
|
||||
boards = dict(sorted([board for matrix in mapped_exec for board in matrix]))
|
||||
boards = dict(sorted([board for matrix in mapped_exec for board in matrix], key=lambda x: x[0]))
|
||||
|
||||
return boards
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ Read-only characteristic that returns the UTF-8 encoded version string.
|
|||
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.
|
||||
is on port 80 unless overridden by `CIRCUITPY_WEB_API_PORT`. It also enables MDNS.
|
||||
|
||||
Here is an example `/.env`:
|
||||
|
||||
|
@ -83,6 +83,8 @@ CIRCUITPY_WIFI_PASSWORD='secretpassword'
|
|||
|
||||
# To enable modifying files from the web. Change this too!
|
||||
CIRCUITPY_WEB_API_PASSWORD='passw0rd'
|
||||
|
||||
CIRCUITPY_WEB_API_PORT=80
|
||||
```
|
||||
|
||||
MDNS is used to resolve [`circuitpython.local`](http://circuitpython.local) to a device specific
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 3bdd335452ff14a53d1e840de043e3159cb3b829
|
||||
Subproject commit f993d5fac69f3a0cfa33988268666c462b72c0ec
|
|
@ -7,4 +7,4 @@ BUILD=build-arm64
|
|||
|
||||
include mpy-cross.mk
|
||||
# Because mpy-cross.mk unconditionally overwrites CC for Darwin, we must set it BELOW the inclusion
|
||||
CC := $(shell xcrun --sdk macosx11.1 --find clang) -isysroot $(shell xcrun --sdk macosx11.1 --show-sdk-path) -target arm64-apple-macos11
|
||||
CC := $(shell xcrun --find clang) -isysroot $(shell xcrun --show-sdk-path) -target arm64-apple-macos11
|
||||
|
|
|
@ -6,6 +6,8 @@ USB_MANUFACTURER = "Arduino"
|
|||
CHIP_VARIANT = SAMD21G18A
|
||||
CHIP_FAMILY = samd21
|
||||
|
||||
CIRCUITPY_BUILD_EXTENSIONS = bin,uf2
|
||||
|
||||
INTERNAL_FLASH_FILESYSTEM = 1
|
||||
LONGINT_IMPL = NONE
|
||||
CIRCUITPY_FULL_BUILD = 0
|
||||
|
|
|
@ -6,6 +6,8 @@ USB_MANUFACTURER = "Arduino"
|
|||
CHIP_VARIANT = SAMD21G18A
|
||||
CHIP_FAMILY = samd21
|
||||
|
||||
CIRCUITPY_BUILD_EXTENSIONS = bin,uf2
|
||||
|
||||
INTERNAL_FLASH_FILESYSTEM = 1
|
||||
LONGINT_IMPL = NONE
|
||||
CIRCUITPY_FULL_BUILD = 0
|
||||
|
|
|
@ -6,6 +6,8 @@ USB_MANUFACTURER = "Arduino"
|
|||
CHIP_VARIANT = SAMD21G18A
|
||||
CHIP_FAMILY = samd21
|
||||
|
||||
CIRCUITPY_BUILD_EXTENSIONS = bin,uf2
|
||||
|
||||
INTERNAL_FLASH_FILESYSTEM = 1
|
||||
LONGINT_IMPL = NONE
|
||||
CIRCUITPY_FULL_BUILD = 0
|
||||
|
|
|
@ -6,6 +6,8 @@ USB_MANUFACTURER = "Arduino"
|
|||
CHIP_VARIANT = SAMD21G18A
|
||||
CHIP_FAMILY = samd21
|
||||
|
||||
CIRCUITPY_BUILD_EXTENSIONS = bin,uf2
|
||||
|
||||
INTERNAL_FLASH_FILESYSTEM = 1
|
||||
LONGINT_IMPL = NONE
|
||||
CIRCUITPY_FULL_BUILD = 0
|
||||
|
|
|
@ -6,6 +6,8 @@ USB_MANUFACTURER = "Adafruit Industries LLC"
|
|||
CHIP_VARIANT = SAMD21G18A
|
||||
CHIP_FAMILY = samd21
|
||||
|
||||
CIRCUITPY_BUILD_EXTENSIONS = bin,uf2
|
||||
|
||||
INTERNAL_FLASH_FILESYSTEM = 1
|
||||
LONGINT_IMPL = NONE
|
||||
CIRCUITPY_FULL_BUILD = 0
|
||||
|
|
|
@ -6,6 +6,8 @@ USB_MANUFACTURER = "Adafruit Industries LLC"
|
|||
CHIP_VARIANT = SAMD21G18A
|
||||
CHIP_FAMILY = samd21
|
||||
|
||||
CIRCUITPY_BUILD_EXTENSIONS = bin,uf2
|
||||
|
||||
INTERNAL_FLASH_FILESYSTEM = 1
|
||||
LONGINT_IMPL = NONE
|
||||
CIRCUITPY_FULL_BUILD = 0
|
||||
|
|
|
@ -6,6 +6,8 @@ USB_MANUFACTURER = "Adafruit Industries LLC"
|
|||
CHIP_VARIANT = SAMD21G18A
|
||||
CHIP_FAMILY = samd21
|
||||
|
||||
CIRCUITPY_BUILD_EXTENSIONS = bin,uf2
|
||||
|
||||
INTERNAL_FLASH_FILESYSTEM = 1
|
||||
LONGINT_IMPL = NONE
|
||||
CIRCUITPY_FULL_BUILD = 0
|
||||
|
|
|
@ -6,6 +6,8 @@ USB_MANUFACTURER = "Adafruit Industries LLC"
|
|||
CHIP_VARIANT = SAMD21G18A
|
||||
CHIP_FAMILY = samd21
|
||||
|
||||
CIRCUITPY_BUILD_EXTENSIONS = bin,uf2
|
||||
|
||||
INTERNAL_FLASH_FILESYSTEM = 1
|
||||
LONGINT_IMPL = NONE
|
||||
CIRCUITPY_FULL_BUILD = 0
|
||||
|
|
|
@ -33,6 +33,8 @@ CIRCUITPY_USB_CDC = 0
|
|||
CIRCUITPY_USB_HID = 0
|
||||
CIRCUITPY_USB_MIDI = 0
|
||||
CIRCUITPY_VECTORIO = 0
|
||||
CIRCUITPY_BUSDEVICE = 0
|
||||
CIRCUITPY_BITMAPTOOLS = 0
|
||||
|
||||
CIRCUITPY_ANALOGIO = 1
|
||||
CIRCUITPY_AUDIOIO = 1
|
||||
|
@ -42,6 +44,7 @@ CIRCUITPY_KEYPAD = 1
|
|||
CIRCUITPY_MATH = 1
|
||||
CIRCUITPY_STAGE = 1
|
||||
CIRCUITPY_SYNTHIO = 1
|
||||
CIRCUITPY_ZLIB = 1
|
||||
|
||||
FROZEN_MPY_DIRS += $(TOP)/frozen/circuitpython-stage/pewpew_m4
|
||||
CIRCUITPY_DISPLAY_FONT = $(TOP)/ports/atmel-samd/boards/ugame10/brutalist-6.bdf
|
||||
|
|
|
@ -6,6 +6,8 @@ USB_MANUFACTURER = "Itaca Innovation"
|
|||
CHIP_VARIANT = SAMD21E18A
|
||||
CHIP_FAMILY = samd21
|
||||
|
||||
CIRCUITPY_BUILD_EXTENSIONS = bin,uf2
|
||||
|
||||
INTERNAL_FLASH_FILESYSTEM = 1
|
||||
LONGINT_IMPL = NONE
|
||||
CIRCUITPY_FULL_BUILD = 0
|
||||
|
|
|
@ -133,3 +133,5 @@ CIRCUITPY_RGBMATRIX ?= $(CIRCUITPY_FRAMEBUFFERIO)
|
|||
|
||||
endif # same51
|
||||
######################################################################
|
||||
|
||||
CIRCUITPY_BUILD_EXTENSIONS ?= uf2
|
||||
|
|
|
@ -4,3 +4,5 @@ USB_PRODUCT = "Zero"
|
|||
USB_MANUFACTURER = "Raspberry Pi"
|
||||
|
||||
CHIP_VARIANT = "bcm2835"
|
||||
|
||||
CIRCUITPY_BUILD_EXTENSIONS = disk.img.zip,kernel.img
|
||||
|
|
|
@ -4,3 +4,5 @@ USB_PRODUCT = "Zero W"
|
|||
USB_MANUFACTURER = "Raspberry Pi"
|
||||
|
||||
CHIP_VARIANT = "bcm2835"
|
||||
|
||||
CIRCUITPY_BUILD_EXTENSIONS = disk.img.zip,kernel.img
|
||||
|
|
|
@ -24,3 +24,5 @@ INTERNAL_FLASH_FILESYSTEM = 1
|
|||
|
||||
USB_NUM_ENDPOINT_PAIRS = 8
|
||||
USB_HIGHSPEED = 1
|
||||
|
||||
CIRCUITPY_BUILD_EXTENSIONS ?= disk.img.zip,kernel8.img
|
||||
|
|
|
@ -25,3 +25,5 @@ CIRCUITPY_TOUCHIO = 0
|
|||
CIRCUITPY_USB_HID = 0
|
||||
CIRCUITPY_USB_MIDI = 0
|
||||
INTERNAL_LIBM = 1
|
||||
|
||||
CIRCUITPY_BUILD_EXTENSIONS ?= spk
|
||||
|
|
|
@ -16,4 +16,4 @@ CIRCUITPY_ESP_FLASH_MODE = qio
|
|||
CIRCUITPY_ESP_FLASH_FREQ = 40m
|
||||
CIRCUITPY_ESP_FLASH_SIZE = 4MB
|
||||
|
||||
CIRCUITPY_PS2IO = 0
|
||||
OPTIMIZATION_FLAGS = -Os
|
||||
|
|
|
@ -37,6 +37,8 @@ STATIC const mp_rom_map_elem_t board_module_globals_table[] = {
|
|||
{ MP_ROM_QSTR(MP_QSTR_BUTTON_D), MP_ROM_PTR(&pin_GPIO11) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_D11), MP_ROM_PTR(&pin_GPIO11) },
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_BOOT0), MP_ROM_PTR(&pin_GPIO0) },
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_LIGHT), MP_ROM_PTR(&pin_GPIO3) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_GPIO3) },
|
||||
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
#include "shared-bindings/microcontroller/Pin.h"
|
||||
#include "supervisor/board.h"
|
||||
|
||||
#include "components/driver/include/driver/gpio.h"
|
||||
|
||||
void board_init(void) {
|
||||
// Debug UART
|
||||
#ifdef DEBUG
|
||||
common_hal_never_reset_pin(&pin_GPIO20);
|
||||
common_hal_never_reset_pin(&pin_GPIO21);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool board_requests_safe_mode(void) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool espressif_board_reset_pin_number(gpio_num_t pin_number) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void reset_board(void) {
|
||||
}
|
||||
|
||||
void board_deinit(void) {
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
#define MICROPY_HW_BOARD_NAME "Seeed Studio XIAO ESP32C3"
|
||||
#define MICROPY_HW_MCU_NAME "ESP32-C3FN4"
|
||||
|
||||
#define DEFAULT_I2C_BUS_SCL (&pin_GPIO7)
|
||||
#define DEFAULT_I2C_BUS_SDA (&pin_GPIO6)
|
||||
|
||||
#define DEFAULT_SPI_BUS_SCK (&pin_GPIO8)
|
||||
#define DEFAULT_SPI_BUS_MOSI (&pin_GPIO10)
|
||||
#define DEFAULT_SPI_BUS_MISO (&pin_GPIO9)
|
||||
|
||||
#define DEFAULT_UART_BUS_RX (&pin_GPIO20)
|
||||
#define DEFAULT_UART_BUS_TX (&pin_GPIO21)
|
||||
|
||||
#define CIRCUITPY_ESP_USB_SERIAL_JTAG (1)
|
|
@ -0,0 +1,10 @@
|
|||
CIRCUITPY_CREATOR_ID = 0x000C2886
|
||||
CIRCUITPY_CREATION_ID = 0x00C30001
|
||||
|
||||
IDF_TARGET = esp32c3
|
||||
|
||||
INTERNAL_FLASH_FILESYSTEM = 1
|
||||
|
||||
CIRCUITPY_ESP_FLASH_MODE = qio
|
||||
CIRCUITPY_ESP_FLASH_FREQ = 80m
|
||||
CIRCUITPY_ESP_FLASH_SIZE = 4MB
|
|
@ -0,0 +1,43 @@
|
|||
#include "shared-bindings/board/__init__.h"
|
||||
|
||||
STATIC const mp_rom_map_elem_t board_module_globals_table[] = {
|
||||
CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_D0), MP_ROM_PTR(&pin_GPIO2) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_A0), MP_ROM_PTR(&pin_GPIO2) },
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_D1), MP_ROM_PTR(&pin_GPIO3) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_GPIO3) },
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_D2), MP_ROM_PTR(&pin_GPIO4) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_GPIO4) },
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_D3), MP_ROM_PTR(&pin_GPIO5) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_GPIO5) },
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_D4), MP_ROM_PTR(&pin_GPIO6) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO6) },
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_D5), MP_ROM_PTR(&pin_GPIO7) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO7) },
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_D6), MP_ROM_PTR(&pin_GPIO21) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO21) },
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_D7), MP_ROM_PTR(&pin_GPIO20) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO20) },
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_D8), MP_ROM_PTR(&pin_GPIO8) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO8) },
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_D9), MP_ROM_PTR(&pin_GPIO9) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_GPIO9) },
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_D10), MP_ROM_PTR(&pin_GPIO10) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO10) },
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) },
|
||||
};
|
||||
MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table);
|
|
@ -0,0 +1,5 @@
|
|||
#
|
||||
# LWIP
|
||||
#
|
||||
CONFIG_LWIP_LOCAL_HOSTNAME="seeed-xiao-esp32c3"
|
||||
# end of LWIP
|
|
@ -204,5 +204,9 @@ mp_obj_t common_hal_mdns_server_find(mdns_server_obj_t *self, const char *servic
|
|||
}
|
||||
|
||||
void common_hal_mdns_server_advertise_service(mdns_server_obj_t *self, const char *service_type, const char *protocol, mp_int_t port) {
|
||||
mdns_service_add(NULL, service_type, protocol, port, NULL, 0);
|
||||
if (mdns_service_exists(service_type, protocol, NULL)) {
|
||||
mdns_service_port_set(service_type, protocol, port);
|
||||
} else {
|
||||
mdns_service_add(NULL, service_type, protocol, port, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,32 +60,35 @@ STATIC void socket_select_task(void *arg) {
|
|||
FD_SET(socket_change_fd, &errfds);
|
||||
int max_fd = socket_change_fd;
|
||||
for (size_t i = 0; i < MP_ARRAY_SIZE(open_socket_fds); i++) {
|
||||
if (open_socket_fds[i] < 0) {
|
||||
int sockfd = open_socket_fds[i];
|
||||
if (sockfd < 0) {
|
||||
continue;
|
||||
}
|
||||
max_fd = MAX(max_fd, open_socket_fds[i]);
|
||||
FD_SET(open_socket_fds[i], &readfds);
|
||||
FD_SET(open_socket_fds[i], &errfds);
|
||||
max_fd = MAX(max_fd, sockfd);
|
||||
FD_SET(sockfd, &readfds);
|
||||
FD_SET(sockfd, &errfds);
|
||||
}
|
||||
|
||||
int num_triggered = select(max_fd + 1, &readfds, NULL, &errfds, NULL);
|
||||
if (num_triggered < 0) {
|
||||
// Maybe bad file descriptor
|
||||
for (size_t i = 0; i < MP_ARRAY_SIZE(open_socket_fds); i++) {
|
||||
int sockfd = open_socket_fds[i];
|
||||
if (sockfd < 0) {
|
||||
continue;
|
||||
}
|
||||
if (FD_ISSET(sockfd, &errfds)) {
|
||||
int err;
|
||||
int optlen = sizeof(int);
|
||||
int ret = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &err, (socklen_t *)&optlen);
|
||||
if (ret < 0) {
|
||||
open_socket_fds[i] = -1;
|
||||
// Try again.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Check for bad file descriptor and queue up the background task before
|
||||
// circling around.
|
||||
if (num_triggered == -1 && errno == EBADF) {
|
||||
// One for the change fd and one for the closed socket.
|
||||
num_triggered = 2;
|
||||
}
|
||||
// Try and find the bad file and remove it from monitoring.
|
||||
for (size_t i = 0; i < MP_ARRAY_SIZE(open_socket_fds); i++) {
|
||||
int sockfd = open_socket_fds[i];
|
||||
if (sockfd < 0) {
|
||||
continue;
|
||||
}
|
||||
int err;
|
||||
int optlen = sizeof(int);
|
||||
int ret = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &err, (socklen_t *)&optlen);
|
||||
if (ret < 0) {
|
||||
open_socket_fds[i] = -1;
|
||||
// Raise num_triggered so that we skip the assert and queue the background task.
|
||||
num_triggered = 2;
|
||||
}
|
||||
}
|
||||
assert(num_triggered >= 0);
|
||||
|
@ -117,13 +120,13 @@ void socket_user_reset(void) {
|
|||
user_socket[i] = false;
|
||||
}
|
||||
socket_change_fd = eventfd(0, 0);
|
||||
// This task runs at a lower priority than CircuitPython and is used to wake CircuitPython
|
||||
// up when any open sockets have data to read. It allows us to sleep otherwise.
|
||||
// Run this at the same priority as CP so that the web workflow background task can be
|
||||
// queued while CP is running. Both tasks can still sleep and, therefore, sleep overall.
|
||||
(void)xTaskCreateStaticPinnedToCore(socket_select_task,
|
||||
"socket_select",
|
||||
2 * configMINIMAL_STACK_SIZE,
|
||||
NULL,
|
||||
0, // Run this at IDLE priority. We only need it when CP isn't running (at 1).
|
||||
uxTaskPriorityGet(NULL),
|
||||
socket_select_stack,
|
||||
&socket_select_task_handle,
|
||||
xPortGetCoreID());
|
||||
|
|
|
@ -40,6 +40,7 @@ CIRCUITPY_PARALLELDISPLAY = 0
|
|||
# Protomatter needs to support ESP32.
|
||||
CIRCUITPY_RGBMATRIX = 0
|
||||
CIRCUITPY_USB = 0
|
||||
CIRCUITPY_BUILD_EXTENSIONS ?= bin
|
||||
|
||||
else ifeq ($(IDF_TARGET),esp32c3)
|
||||
CIRCUITPY_AESIO = 0
|
||||
|
@ -57,17 +58,20 @@ CIRCUITPY_ROTARYIO = 0
|
|||
CIRCUITPY_TOUCHIO ?= 1
|
||||
CIRCUITPY_TOUCHIO_USE_NATIVE = 0
|
||||
CIRCUITPY_USB = 0
|
||||
CIRCUITPY_BUILD_EXTENSIONS ?= bin
|
||||
|
||||
else ifeq ($(IDF_TARGET),esp32s3)
|
||||
CIRCUITPY_BLEIO = 1
|
||||
CIRCUITPY_BLEIO_HCI = 0
|
||||
CIRCUITPY_IMAGECAPTURE = 0
|
||||
CIRCUITPY_PARALLELDISPLAY = 0
|
||||
CIRCUITPY_BUILD_EXTENSIONS ?= bin,uf2
|
||||
|
||||
else ifeq ($(IDF_TARGET),esp32s2)
|
||||
# No BLE on S2
|
||||
CIRCUITPY_BLEIO = 0
|
||||
CIRCUITPY_BLEIO_HCI = 0
|
||||
CIRCUITPY_BUILD_EXTENSIONS ?= bin,uf2
|
||||
endif
|
||||
|
||||
# From ESP32-S2/S3 Technical Reference Manual:
|
||||
|
|
|
@ -98,14 +98,23 @@ bool usb_serial_jtag_bytes_available(void) {
|
|||
}
|
||||
|
||||
void usb_serial_jtag_write(const char *text, uint32_t length) {
|
||||
if (USB_SERIAL_JTAG.fram_num.sof_frame_index > 0) {
|
||||
size_t total_written = 0;
|
||||
uint32_t start_time = supervisor_ticks_ms32();
|
||||
// Time out after 5 milliseconds in case usb isn't actually reading CDC.
|
||||
while (total_written < length && start_time - supervisor_ticks_ms32() < 5) {
|
||||
total_written += usb_serial_jtag_ll_write_txfifo((const uint8_t *)(text + total_written), length - total_written);
|
||||
RUN_BACKGROUND_TASKS;
|
||||
}
|
||||
usb_serial_jtag_ll_txfifo_flush();
|
||||
if (!usb_serial_jtag_connected()) {
|
||||
return;
|
||||
}
|
||||
size_t total_written = 0;
|
||||
while (total_written < length) {
|
||||
uint32_t start_time = supervisor_ticks_ms32();
|
||||
// Wait until we can write to the FIFO again. If it takes too long, then
|
||||
// assume we're disconnected.
|
||||
while (!usb_serial_jtag_ll_txfifo_writable()) {
|
||||
uint32_t now = supervisor_ticks_ms32();
|
||||
if (now - start_time > 200) {
|
||||
connected = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
total_written += usb_serial_jtag_ll_write_txfifo((const uint8_t *)(text + total_written), length - total_written);
|
||||
RUN_BACKGROUND_TASKS;
|
||||
}
|
||||
usb_serial_jtag_ll_txfifo_flush();
|
||||
}
|
||||
|
|
|
@ -29,3 +29,5 @@ CIRCUITPY_SDCARDIO = 0
|
|||
# Enable USB support
|
||||
CIRCUITPY_USB_HID = 1
|
||||
CIRCUITPY_USB_MIDI = 1
|
||||
|
||||
CIRCUITPY_BUILD_EXTENSIONS ?= dfu
|
||||
|
|
|
@ -21,3 +21,5 @@ CIRCUITPY_PULSEIO = 0
|
|||
CIRCUITPY_ROTARYIO = 0
|
||||
CIRCUITPY_USB_MIDI = 1
|
||||
LONGINT_IMPL = MPZ
|
||||
|
||||
CIRCUITPY_BUILD_EXTENSIONS ?= hex,uf2
|
||||
|
|
|
@ -6,4 +6,4 @@ USB_MANUFACTURER = "Invector Labs AB"
|
|||
MCU_CHIP = nrf52840
|
||||
|
||||
SPI_FLASH_FILESYSTEM = 1
|
||||
EXTERNAL_FLASH_DEVICES = "W25Q16JVxQ,W25Q32FV"
|
||||
EXTERNAL_FLASH_DEVICES = "W25Q16JVxQ,W25Q32FV,W25Q64FV"
|
||||
|
|
|
@ -5,6 +5,8 @@ USB_MANUFACTURER = "Electronut Labs"
|
|||
|
||||
MCU_CHIP = nrf52840
|
||||
|
||||
CIRCUITPY_BUILD_EXTENSIONS = hex
|
||||
|
||||
INTERNAL_FLASH_FILESYSTEM = 1
|
||||
CIRCUITPY_AUDIOIO = 0
|
||||
CIRCUITPY_DISPLAYIO = 1
|
||||
|
|
|
@ -5,5 +5,7 @@ USB_MANUFACTURER = "makerdiary"
|
|||
|
||||
MCU_CHIP = nrf52840
|
||||
|
||||
CIRCUITPY_BUILD_EXTENSIONS = hex
|
||||
|
||||
QSPI_FLASH_FILESYSTEM = 1
|
||||
EXTERNAL_FLASH_DEVICES = "MX25R6435F"
|
||||
|
|
|
@ -5,4 +5,6 @@ USB_MANUFACTURER = "makerdiary"
|
|||
|
||||
MCU_CHIP = nrf52840
|
||||
|
||||
CIRCUITPY_BUILD_EXTENSIONS = hex,uf2
|
||||
|
||||
INTERNAL_FLASH_FILESYSTEM = 1
|
||||
|
|
|
@ -3,6 +3,8 @@ CIRCUITPY_CREATION_ID = 0x80D8
|
|||
|
||||
MCU_CHIP = nrf52833
|
||||
|
||||
CIRCUITPY_BUILD_EXTENSIONS = combined.hex
|
||||
|
||||
INTERNAL_FLASH_FILESYSTEM = 1
|
||||
|
||||
# USB pins aren't used.
|
||||
|
|
|
@ -5,5 +5,7 @@ USB_MANUFACTURER = "Nordic Semiconductor"
|
|||
|
||||
MCU_CHIP = nrf52840
|
||||
|
||||
CIRCUITPY_BUILD_EXTENSIONS = bin,uf2
|
||||
|
||||
QSPI_FLASH_FILESYSTEM = 1
|
||||
EXTERNAL_FLASH_DEVICES = "MX25R6435F"
|
||||
|
|
|
@ -5,4 +5,6 @@ USB_MANUFACTURER = "Nordic Semiconductor"
|
|||
|
||||
MCU_CHIP = nrf52840
|
||||
|
||||
CIRCUITPY_BUILD_EXTENSIONS = bin,uf2
|
||||
|
||||
INTERNAL_FLASH_FILESYSTEM = 1
|
||||
|
|
|
@ -4,6 +4,8 @@ LD_TEMPLATE_FILE = boards/common.template.ld
|
|||
|
||||
INTERNAL_LIBM = 1
|
||||
|
||||
CIRCUITPY_BUILD_EXTENSIONS ?= uf2
|
||||
|
||||
# Number of USB endpoint pairs.
|
||||
USB_NUM_ENDPOINT_PAIRS = 8
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ STATIC const mp_rom_map_elem_t board_module_globals_table[] = {
|
|||
|
||||
{ MP_ROM_QSTR(MP_QSTR_SW_A), MP_ROM_PTR(&pin_GPIO14) },
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_GPIO16) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_LED_R), MP_ROM_PTR(&pin_GPIO16) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_LED_G), MP_ROM_PTR(&pin_GPIO17) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_LED_B), MP_ROM_PTR(&pin_GPIO18) },
|
||||
|
|
|
@ -36,6 +36,8 @@ CIRCUITPY_AUDIOMIXER = 1
|
|||
|
||||
INTERNAL_LIBM = 1
|
||||
|
||||
CIRCUITPY_BUILD_EXTENSIONS ?= uf2
|
||||
|
||||
# Number of USB endpoint pairs.
|
||||
USB_NUM_ENDPOINT_PAIRS = 8
|
||||
|
||||
|
|
|
@ -13,6 +13,8 @@ MCU_SERIES = F4
|
|||
MCU_VARIANT = STM32F401xE
|
||||
MCU_PACKAGE = LQFP64
|
||||
|
||||
CIRCUITPY_BUILD_EXTENSIONS = uf2
|
||||
|
||||
OPTIMIZATION_FLAGS = -Os
|
||||
|
||||
LD_COMMON = boards/common_default.ld
|
||||
|
|
|
@ -19,3 +19,5 @@ LD_BOOT = boards/STM32F405_boot.ld
|
|||
UF2_OFFSET = 0x8010000
|
||||
|
||||
CIRCUITPY_RGBMATRIX ?= 1
|
||||
|
||||
CIRCUITPY_BUILD_EXTENSIONS = bin,uf2
|
||||
|
|
|
@ -71,3 +71,5 @@ CIRCUITPY_BUSDEVICE = 0
|
|||
CIRCUITPY_KEYPAD = 1
|
||||
CIRCUITPY_RGBMATRIX = 0
|
||||
CIRCUITPY_RTC = 1
|
||||
|
||||
CIRCUITPY_BUILD_EXTENSIONS = bin,uf2
|
||||
|
|
|
@ -88,3 +88,4 @@ ifeq ($(MCU_SERIES),L4)
|
|||
endif
|
||||
|
||||
CIRCUITPY_PARALLELDISPLAY := 0
|
||||
CIRCUITPY_BUILD_EXTENSIONS ?= bin
|
||||
|
|
|
@ -17,6 +17,7 @@ polib
|
|||
# For pre-commit
|
||||
pyyaml
|
||||
black
|
||||
pre-commit
|
||||
|
||||
# for combining the Nordic SoftDevice with CircuitPython
|
||||
intelhex
|
||||
|
|
|
@ -161,6 +161,9 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mdns_server_find_obj, 1, _mdns_server_find);
|
|||
//| def advertise_service(self, *, service_type: str, protocol: str, port: int) -> None:
|
||||
//| """Respond to queries for the given service with the given port.
|
||||
//|
|
||||
//| ``service_type`` and ``protocol`` can only occur on one port. Any call after the first
|
||||
//| will update the entry's port.
|
||||
//|
|
||||
//| :param str service_type: The service type such as "_http"
|
||||
//| :param str protocol: The service protocol such as "_tcp"
|
||||
//| :param int port: The port used by the service"""
|
||||
|
|
|
@ -21,9 +21,9 @@ capable board, as well as each :term:`frozen module` included on it.
|
|||
{% for key, value in support_matrix|dictsort %}
|
||||
{{ '.. _' ~ key|replace(" ", "-") ~ ':' }}
|
||||
* - {{ key }}
|
||||
- {{ ':py:mod:`' ~ value[0]|join("`, :py:mod:`") ~ '`' }}
|
||||
- {{ ':py:mod:`' ~ value.modules|join("`, :py:mod:`") ~ '`' }}
|
||||
|
||||
{% for module in value[1] %}\
|
||||
{% for module in value.frozen_libraries %}\
|
||||
{% if loop.index == 1 %}**Frozen Modules:** {% endif %}\
|
||||
{% if loop.index > 1 %}, {% endif %}\
|
||||
{% if module[1] %}{{ '`' ~ module[0] ~ ' <' ~ module[1] ~ '>`__' }}\
|
||||
|
|
|
@ -177,6 +177,17 @@ int readline_process_char(int c) {
|
|||
vstr_cut_tail_bytes(rl.line, rl.line->len - rl.cursor_pos);
|
||||
// set redraw parameters
|
||||
redraw_from_cursor = true;
|
||||
#endif
|
||||
} else if (c == CHAR_CTRL_L) {
|
||||
// CTRL-L is clear screen / redraw. This specific sequence is used
|
||||
// (instead of a slightly more minimal sequence) for compatibility
|
||||
// with the built-in Terminal class
|
||||
mp_hal_stdout_tx_str("\x1b[;H\x1b[2J");
|
||||
mp_hal_stdout_tx_str(rl.prompt);
|
||||
mp_hal_stdout_tx_strn(rl.line->buf + rl.orig_line_len, rl.cursor_pos - rl.orig_line_len);
|
||||
// set redraw parameters
|
||||
redraw_from_cursor = true;
|
||||
#if MICROPY_REPL_EMACS_KEYS
|
||||
} else if (c == CHAR_CTRL_N) {
|
||||
// CTRL-N is go to next line in history
|
||||
goto down_arrow_key;
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#define CHAR_CTRL_E (5)
|
||||
#define CHAR_CTRL_F (6)
|
||||
#define CHAR_CTRL_K (11)
|
||||
#define CHAR_CTRL_L (12)
|
||||
#define CHAR_CTRL_N (14)
|
||||
#define CHAR_CTRL_P (16)
|
||||
#define CHAR_CTRL_U (21)
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
<title></title>
|
||||
<meta charset="UTF-8">
|
||||
<script src="/directory.js" defer=true></script>
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1><a href="/"><img src="/favicon.ico"/></a> <span id="path"></span></h1>
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<script src="/serial.js" defer=true></script>
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
</head>
|
||||
<body style="flex-direction: column; display: flex; height: 100%; width: 100%; margin: 0; font-size: 1rem;">
|
||||
<div style="flex: auto; display: flex; overflow: auto; flex-direction: column;">
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
body {
|
||||
max-width: 960px;
|
||||
margin: 20px auto;
|
||||
font-size: 18px;
|
||||
font-family: "Proxima Nova", Verdana, sans-serif;
|
||||
line-height: 20.7px;
|
||||
}
|
|
@ -5,17 +5,28 @@
|
|||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<script src="/welcome.js" defer=true></script>
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1><a href="/"><img src="/favicon.ico"/></a> Welcome!</h1>
|
||||
Welcome to CircuitPython's Web API. Go to the <a href="/fs/">file browser</a> to work with files in the CIRCUITPY drive. Go to the <a href="/cp/serial/">serial terminal</a> to see code output and interact with the REPL. Make sure you've set <code>CIRCUITPY_WEB_API_PASSWORD='somepassword'</code> in <code>/.env</code>. Provide the password when the browser prompts for it. Leave the username blank.
|
||||
<h2>Device Info</h2>
|
||||
Board: <a id="board"></a><br>
|
||||
Version: <span id="version"></span><br>
|
||||
Hostname: <a id="hostname"></a><br>
|
||||
IP: <a id="ip"></a>
|
||||
<h2>Other Devices</h2>
|
||||
Here are other CircuitPython devices on your network:
|
||||
|
||||
<p>Welcome to CircuitPython's Web API. Go to the <a href="/fs/">file browser</a> to work with files in the CIRCUITPY drive. Go to the <a href="/cp/serial/">serial terminal</a> to see code output and interact with the REPL. Make sure you've set <code>CIRCUITPY_WEB_API_PASSWORD='somepassword'</code> in <code>/.env</code>. Provide the password when the browser prompts for it. <strong>Leave the username blank.</strong></p>
|
||||
|
||||
<h2>Device Info:</h2>
|
||||
|
||||
<dl>
|
||||
<dt>Board:</dt>
|
||||
<dd><a id="board"></a></dd>
|
||||
<dt>Version:</dt>
|
||||
<dd><span id="version"></span></dd>
|
||||
<dt>Hostname:</dt>
|
||||
<dd><a id="hostname"></a></dd>
|
||||
<dt>IP:</dt>
|
||||
<dd><a id="ip"></a></dd>
|
||||
</dl>
|
||||
|
||||
<h2>Here are other CircuitPython devices on your network:</h2>
|
||||
<ul id="devices">
|
||||
</ul>
|
||||
</body>
|
||||
|
|
|
@ -44,7 +44,7 @@ async function find_devices() {
|
|||
li.appendChild(a);
|
||||
var port = "";
|
||||
if (device.port != 80) {
|
||||
port = ":" + version_info.port;
|
||||
port = ":" + device.port;
|
||||
}
|
||||
var server;
|
||||
if (mdns_works) {
|
||||
|
|
|
@ -98,6 +98,7 @@ typedef struct {
|
|||
static wifi_radio_error_t wifi_status = WIFI_RADIO_ERROR_NONE;
|
||||
|
||||
static mdns_server_obj_t mdns;
|
||||
static uint32_t web_api_port = 80;
|
||||
|
||||
static socketpool_socketpool_obj_t pool;
|
||||
static socketpool_socket_obj_t listening;
|
||||
|
@ -189,6 +190,9 @@ void supervisor_web_workflow_status(void) {
|
|||
}
|
||||
|
||||
mp_printf(&mp_plat_print, "%s", _our_ip_encoded);
|
||||
if (web_api_port != 80) {
|
||||
mp_printf(&mp_plat_print, ":%d", web_api_port);
|
||||
}
|
||||
// TODO: Use these unicode to show signal strength: ▂▄▆█
|
||||
}
|
||||
} else {
|
||||
|
@ -199,11 +203,6 @@ void supervisor_web_workflow_status(void) {
|
|||
void supervisor_start_web_workflow(void) {
|
||||
#if CIRCUITPY_WEB_WORKFLOW && CIRCUITPY_WIFI
|
||||
|
||||
if (common_hal_wifi_radio_get_enabled(&common_hal_wifi_radio_obj) &&
|
||||
wifi_radio_get_ipv4_address(&common_hal_wifi_radio_obj) != 0) {
|
||||
// Already started.
|
||||
return;
|
||||
}
|
||||
|
||||
char ssid[33];
|
||||
char password[64];
|
||||
|
@ -218,8 +217,10 @@ void supervisor_start_web_workflow(void) {
|
|||
password_len <= 0 || (size_t)password_len >= sizeof(password)) {
|
||||
return;
|
||||
}
|
||||
common_hal_wifi_init(false);
|
||||
common_hal_wifi_radio_set_enabled(&common_hal_wifi_radio_obj, true);
|
||||
if (!common_hal_wifi_radio_get_enabled(&common_hal_wifi_radio_obj)) {
|
||||
common_hal_wifi_init(false);
|
||||
common_hal_wifi_radio_set_enabled(&common_hal_wifi_radio_obj, true);
|
||||
}
|
||||
|
||||
// TODO: Do our own scan so that we can find the channel we want before calling connect.
|
||||
// Otherwise, connect will do a full slow scan to pick the best AP.
|
||||
|
@ -227,6 +228,9 @@ void supervisor_start_web_workflow(void) {
|
|||
// NUL terminate the strings because dotenv doesn't.
|
||||
ssid[ssid_len] = '\0';
|
||||
password[password_len] = '\0';
|
||||
// We can all connect again because it will return early if we're already connected to the
|
||||
// network. If we are connected to a different network, then it will disconnect before
|
||||
// attempting to connect to the given network.
|
||||
wifi_status = common_hal_wifi_radio_connect(
|
||||
&common_hal_wifi_radio_obj, (uint8_t *)ssid, ssid_len, (uint8_t *)password, password_len,
|
||||
0, 0.1, NULL, 0);
|
||||
|
@ -236,21 +240,47 @@ void supervisor_start_web_workflow(void) {
|
|||
return;
|
||||
}
|
||||
|
||||
mdns_server_construct(&mdns, true);
|
||||
common_hal_mdns_server_set_instance_name(&mdns, MICROPY_HW_BOARD_NAME);
|
||||
common_hal_mdns_server_advertise_service(&mdns, "_circuitpython", "_tcp", 80);
|
||||
char port_encoded[6];
|
||||
size_t port_len = 0;
|
||||
size_t new_port = web_api_port;
|
||||
#if CIRCUITPY_DOTENV
|
||||
port_len = dotenv_get_key("/.env", "CIRCUITPY_WEB_API_PORT", port_encoded, sizeof(port_encoded) - 1);
|
||||
#endif
|
||||
if (0 < port_len && port_len < sizeof(port_encoded)) {
|
||||
port_encoded[port_len] = '\0';
|
||||
new_port = strtoul(port_encoded, NULL, 10);
|
||||
}
|
||||
|
||||
pool.base.type = &socketpool_socketpool_type;
|
||||
common_hal_socketpool_socketpool_construct(&pool, &common_hal_wifi_radio_obj);
|
||||
bool first_start = mdns.base.type != &mdns_server_type;
|
||||
bool port_changed = new_port != web_api_port;
|
||||
|
||||
ESP_LOGI(TAG, "Starting web workflow");
|
||||
listening.base.type = &socketpool_socket_type;
|
||||
socketpool_socket(&pool, SOCKETPOOL_AF_INET, SOCKETPOOL_SOCK_STREAM, &listening);
|
||||
common_hal_socketpool_socket_settimeout(&listening, 0);
|
||||
// Bind to any ip.
|
||||
// TODO: Make this port .env configurable.
|
||||
common_hal_socketpool_socket_bind(&listening, "", 0, 80);
|
||||
common_hal_socketpool_socket_listen(&listening, 1);
|
||||
if (first_start) {
|
||||
ESP_LOGI(TAG, "Starting web workflow");
|
||||
mdns_server_construct(&mdns, true);
|
||||
mdns.base.type = &mdns_server_type;
|
||||
common_hal_mdns_server_set_instance_name(&mdns, MICROPY_HW_BOARD_NAME);
|
||||
pool.base.type = &socketpool_socketpool_type;
|
||||
common_hal_socketpool_socketpool_construct(&pool, &common_hal_wifi_radio_obj);
|
||||
|
||||
listening.base.type = &socketpool_socket_type;
|
||||
active.base.type = &socketpool_socket_type;
|
||||
active.num = -1;
|
||||
active.connected = false;
|
||||
|
||||
websocket_init();
|
||||
}
|
||||
if (port_changed) {
|
||||
common_hal_socketpool_socket_close(&listening);
|
||||
}
|
||||
if (first_start || port_changed) {
|
||||
web_api_port = new_port;
|
||||
common_hal_mdns_server_advertise_service(&mdns, "_circuitpython", "_tcp", web_api_port);
|
||||
socketpool_socket(&pool, SOCKETPOOL_AF_INET, SOCKETPOOL_SOCK_STREAM, &listening);
|
||||
common_hal_socketpool_socket_settimeout(&listening, 0);
|
||||
// Bind to any ip.
|
||||
common_hal_socketpool_socket_bind(&listening, "", 0, web_api_port);
|
||||
common_hal_socketpool_socket_listen(&listening, 1);
|
||||
}
|
||||
|
||||
mp_int_t api_password_len = dotenv_get_key("/.env", "CIRCUITPY_WEB_API_PASSWORD", _api_password + 1, sizeof(_api_password) - 2);
|
||||
if (api_password_len > 0) {
|
||||
|
@ -259,12 +289,6 @@ void supervisor_start_web_workflow(void) {
|
|||
_base64_in_place(_api_password, api_password_len + 1, sizeof(_api_password));
|
||||
}
|
||||
|
||||
active.base.type = &socketpool_socket_type;
|
||||
active.num = -1;
|
||||
active.connected = false;
|
||||
|
||||
websocket_init();
|
||||
|
||||
// TODO:
|
||||
// GET /cp/serial.txt
|
||||
// - Most recent 1k of serial output.
|
||||
|
@ -283,6 +307,10 @@ static void _send_raw(socketpool_socket_obj_t *socket, const uint8_t *buf, int l
|
|||
}
|
||||
}
|
||||
|
||||
STATIC void _print_raw(void *env, const char *str, size_t len) {
|
||||
_send_raw((socketpool_socket_obj_t *)env, (const uint8_t *)str, (size_t)len);
|
||||
}
|
||||
|
||||
static void _send_str(socketpool_socket_obj_t *socket, const char *str) {
|
||||
_send_raw(socket, (const uint8_t *)str, strlen(str));
|
||||
}
|
||||
|
@ -301,14 +329,19 @@ static void _send_strs(socketpool_socket_obj_t *socket, ...) {
|
|||
}
|
||||
|
||||
static void _send_chunk(socketpool_socket_obj_t *socket, const char *chunk) {
|
||||
char encoded_len[sizeof(size_t) * 2 + 1];
|
||||
int len = snprintf(encoded_len, sizeof(encoded_len), "%X", strlen(chunk));
|
||||
_send_raw(socket, (const uint8_t *)encoded_len, len);
|
||||
_send_raw(socket, (const uint8_t *)"\r\n", 2);
|
||||
mp_print_t _socket_print = {socket, _print_raw};
|
||||
mp_printf(&_socket_print, "%X\r\n", strlen(chunk));
|
||||
_send_raw(socket, (const uint8_t *)chunk, strlen(chunk));
|
||||
_send_raw(socket, (const uint8_t *)"\r\n", 2);
|
||||
}
|
||||
|
||||
STATIC void _print_chunk(void *env, const char *str, size_t len) {
|
||||
mp_print_t _socket_print = {env, _print_raw};
|
||||
mp_printf(&_socket_print, "%X\r\n", len);
|
||||
_send_raw((socketpool_socket_obj_t *)env, (const uint8_t *)str, len);
|
||||
_send_raw((socketpool_socket_obj_t *)env, (const uint8_t *)"\r\n", 2);
|
||||
}
|
||||
|
||||
// A bit of a misnomer because it sends all arguments as one chunk.
|
||||
// The last argument must be NULL! Otherwise, it won't stop.
|
||||
static void _send_chunks(socketpool_socket_obj_t *socket, ...) {
|
||||
|
@ -326,9 +359,9 @@ static void _send_chunks(socketpool_socket_obj_t *socket, ...) {
|
|||
}
|
||||
va_end(strs_to_count);
|
||||
|
||||
char encoded_len[sizeof(size_t) * 2 + 1];
|
||||
snprintf(encoded_len, sizeof(encoded_len), "%X", chunk_len);
|
||||
_send_strs(socket, encoded_len, "\r\n", NULL);
|
||||
|
||||
mp_print_t _socket_print = {socket, _print_raw};
|
||||
mp_printf(&_socket_print, "%X\r\n", chunk_len);
|
||||
|
||||
str = va_arg(strs_to_send, const char *);
|
||||
while (str != NULL) {
|
||||
|
@ -531,7 +564,12 @@ static void _reply_redirect(socketpool_socket_obj_t *socket, _request *request,
|
|||
_send_str(socket, "http");
|
||||
}
|
||||
|
||||
_send_strs(socket, "://", hostname, ".local", path, "\r\n", NULL);
|
||||
_send_strs(socket, "://", hostname, ".local", NULL);
|
||||
if (web_api_port != 80) {
|
||||
mp_print_t _socket_print = {socket, _print_raw};
|
||||
mp_printf(&_socket_print, ":%d", web_api_port);
|
||||
}
|
||||
_send_strs(socket, path, "\r\n", NULL);
|
||||
_cors_header(socket, request);
|
||||
_send_str(socket, "\r\n");
|
||||
}
|
||||
|
@ -540,6 +578,7 @@ static void _reply_directory_json(socketpool_socket_obj_t *socket, _request *req
|
|||
socketpool_socket_send(socket, (const uint8_t *)OK_JSON, strlen(OK_JSON));
|
||||
_cors_header(socket, request);
|
||||
_send_str(socket, "\r\n");
|
||||
mp_print_t _socket_print = {socket, _print_chunk};
|
||||
_send_chunk(socket, "[");
|
||||
bool first = true;
|
||||
|
||||
|
@ -560,7 +599,7 @@ static void _reply_directory_json(socketpool_socket_obj_t *socket, _request *req
|
|||
}
|
||||
// We use nanoseconds past Jan 1, 1970 for consistency with BLE API and
|
||||
// LittleFS.
|
||||
_send_chunk(socket, ", \"modified_ns\": ");
|
||||
_send_chunk(socket, ", ");
|
||||
|
||||
uint64_t truncated_time = timeutils_mktime(1980 + (file_info.fdate >> 9),
|
||||
(file_info.fdate >> 5) & 0xf,
|
||||
|
@ -569,15 +608,17 @@ static void _reply_directory_json(socketpool_socket_obj_t *socket, _request *req
|
|||
(file_info.ftime >> 5) & 0x1f,
|
||||
(file_info.ftime & 0x1f) * 2) * 1000000000ULL;
|
||||
|
||||
char encoded_number[32];
|
||||
snprintf(encoded_number, sizeof(encoded_number), "%lld", truncated_time);
|
||||
_send_chunks(socket, encoded_number, ", \"file_size\": ", NULL);
|
||||
// Use snprintf because mp_printf doesn't support 64 bit numbers by
|
||||
// default.
|
||||
char encoded_time[32];
|
||||
snprintf(encoded_time, sizeof(encoded_time), "%llu", truncated_time);
|
||||
mp_printf(&_socket_print, "\"modified_ns\": %s, ", encoded_time);
|
||||
size_t file_size = 0;
|
||||
if ((file_info.fattrib & AM_DIR) == 0) {
|
||||
file_size = file_info.fsize;
|
||||
}
|
||||
snprintf(encoded_number, sizeof(encoded_number), "%d", file_size);
|
||||
_send_chunks(socket, encoded_number, "}", NULL);
|
||||
mp_printf(&_socket_print, "\"file_size\": %d }", file_size);
|
||||
|
||||
first = false;
|
||||
res = f_readdir(dir, &file_info);
|
||||
}
|
||||
|
@ -587,12 +628,10 @@ static void _reply_directory_json(socketpool_socket_obj_t *socket, _request *req
|
|||
|
||||
static void _reply_with_file(socketpool_socket_obj_t *socket, _request *request, const char *filename, FIL *active_file) {
|
||||
uint32_t total_length = f_size(active_file);
|
||||
char encoded_len[10];
|
||||
snprintf(encoded_len, sizeof(encoded_len), "%d", total_length);
|
||||
|
||||
_send_strs(socket,
|
||||
"HTTP/1.1 200 OK\r\n",
|
||||
"Content-Length: ", encoded_len, "\r\n", NULL);
|
||||
_send_str(socket, "HTTP/1.1 200 OK\r\n");
|
||||
mp_print_t _socket_print = {socket, _print_raw};
|
||||
mp_printf(&_socket_print, "Content-Length: %d\r\n", total_length);
|
||||
// TODO: Make this a table to save space.
|
||||
if (_endswith(filename, ".txt") || _endswith(filename, ".py")) {
|
||||
_send_str(socket, "Content-Type: text/plain\r\n");
|
||||
|
@ -640,27 +679,23 @@ static void _reply_with_devices_json(socketpool_socket_obj_t *socket, _request *
|
|||
socketpool_socket_send(socket, (const uint8_t *)OK_JSON, strlen(OK_JSON));
|
||||
_cors_header(socket, request);
|
||||
_send_str(socket, "\r\n");
|
||||
char total_encoded[4];
|
||||
snprintf(total_encoded, sizeof(total_encoded), "%d", total_results);
|
||||
_send_chunks(socket, "{\"total\": ", total_encoded, ", \"devices\": [", NULL);
|
||||
mp_print_t _socket_print = {socket, _print_chunk};
|
||||
|
||||
mp_printf(&_socket_print, "{\"total\": %d, \"devices\": [", total_results);
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
if (i > 0) {
|
||||
_send_chunk(socket, ",");
|
||||
}
|
||||
const char *hostname = common_hal_mdns_remoteservice_get_hostname(&found_devices[i]);
|
||||
const char *instance_name = common_hal_mdns_remoteservice_get_instance_name(&found_devices[i]);
|
||||
char port_encoded[4];
|
||||
int port = common_hal_mdns_remoteservice_get_port(&found_devices[i]);
|
||||
snprintf(port_encoded, sizeof(port_encoded), "%d", port);
|
||||
char ip_encoded[4 * 4];
|
||||
uint32_t ipv4_address = mdns_remoteservice_get_ipv4_address(&found_devices[i]);
|
||||
uint8_t *octets = (uint8_t *)&ipv4_address;
|
||||
snprintf(ip_encoded, sizeof(ip_encoded), "%d.%d.%d.%d", octets[0], octets[1], octets[2], octets[3]);
|
||||
_send_chunks(socket,
|
||||
"{\"hostname\": \"", hostname, "\", ",
|
||||
"\"instance_name\": \"", instance_name, "\", ",
|
||||
"\"port\": ", port_encoded, ", ",
|
||||
"\"ip\": \"", ip_encoded, "\"}", NULL);
|
||||
mp_printf(&_socket_print,
|
||||
"{\"hostname\": \"%s\", "
|
||||
"\"instance_name\": \"%s\", "
|
||||
"\"port\": %d, "
|
||||
"\"ip\": \"%d.%d.%d.%d\"}", hostname, instance_name, port, octets[0], octets[1], octets[2], octets[3]);
|
||||
common_hal_mdns_remoteservice_deinit(&found_devices[i]);
|
||||
}
|
||||
_send_chunk(socket, "]}");
|
||||
|
@ -672,24 +707,22 @@ static void _reply_with_version_json(socketpool_socket_obj_t *socket, _request *
|
|||
_send_str(socket, OK_JSON);
|
||||
_cors_header(socket, request);
|
||||
_send_str(socket, "\r\n");
|
||||
char encoded_creator_id[11]; // 2 ** 32 is 10 decimal digits plus one for \0
|
||||
snprintf(encoded_creator_id, sizeof(encoded_creator_id), "%u", CIRCUITPY_CREATOR_ID);
|
||||
char encoded_creation_id[11]; // 2 ** 32 is 10 decimal digits plus one for \0
|
||||
snprintf(encoded_creation_id, sizeof(encoded_creation_id), "%u", CIRCUITPY_CREATION_ID);
|
||||
mp_print_t _socket_print = {socket, _print_chunk};
|
||||
|
||||
const char *hostname = common_hal_mdns_server_get_hostname(&mdns);
|
||||
_send_chunks(socket,
|
||||
"{\"web_api_version\": 1, ",
|
||||
"\"version\": \"", MICROPY_GIT_TAG, "\", ",
|
||||
"\"build_date\": \"", MICROPY_BUILD_DATE, "\", ",
|
||||
"\"board_name\": \"", MICROPY_HW_BOARD_NAME, "\", ",
|
||||
"\"mcu_name\": \"", MICROPY_HW_MCU_NAME, "\", ",
|
||||
"\"board_id\": \"", CIRCUITPY_BOARD_ID, "\", ",
|
||||
"\"creator_id\": ", encoded_creator_id, ", ",
|
||||
"\"creation_id\": ", encoded_creation_id, ", ",
|
||||
"\"hostname\": \"", hostname, "\", ",
|
||||
"\"port\": 80, ",
|
||||
"\"ip\": \"", _our_ip_encoded,
|
||||
"\"}", NULL);
|
||||
// Note: this leverages the fact that C concats consecutive string literals together.
|
||||
mp_printf(&_socket_print,
|
||||
"{\"web_api_version\": 1, "
|
||||
"\"version\": \"" MICROPY_GIT_TAG "\", "
|
||||
"\"build_date\": \"" MICROPY_BUILD_DATE "\", "
|
||||
"\"board_name\": \"" MICROPY_HW_BOARD_NAME "\", "
|
||||
"\"mcu_name\": \"" MICROPY_HW_MCU_NAME "\", "
|
||||
"\"board_id\": \"" CIRCUITPY_BOARD_ID "\", "
|
||||
"\"creator_id\": %u, "
|
||||
"\"creation_id\": %u, "
|
||||
"\"hostname\": \"%s\", "
|
||||
"\"port\": %d, "
|
||||
"\"ip\": \"%s\"}", CIRCUITPY_CREATOR_ID, CIRCUITPY_CREATION_ID, hostname, web_api_port, _our_ip_encoded);
|
||||
// Empty chunk signals the end of the response.
|
||||
_send_chunk(socket, "");
|
||||
}
|
||||
|
@ -863,6 +896,7 @@ STATIC_FILE(welcome_html);
|
|||
STATIC_FILE(welcome_js);
|
||||
STATIC_FILE(edit_html);
|
||||
STATIC_FILE(edit_js);
|
||||
STATIC_FILE(style_css);
|
||||
STATIC_FILE(serial_html);
|
||||
STATIC_FILE(serial_js);
|
||||
STATIC_FILE(blinka_16x16_ico);
|
||||
|
@ -1084,6 +1118,8 @@ static bool _reply(socketpool_socket_obj_t *socket, _request *request) {
|
|||
_REPLY_STATIC(socket, request, serial_js);
|
||||
} else if (strcmp(request->path, "/edit.js") == 0) {
|
||||
_REPLY_STATIC(socket, request, edit_js);
|
||||
} else if (strcmp(request->path, "/style.css") == 0) {
|
||||
_REPLY_STATIC(socket, request, style_css);
|
||||
} else if (strcmp(request->path, "/favicon.ico") == 0) {
|
||||
// TODO: Autogenerate this based on the blinka bitmap and change the
|
||||
// palette based on MAC address.
|
||||
|
@ -1193,7 +1229,12 @@ static void _process_request(socketpool_socket_obj_t *socket, _request *request)
|
|||
request->authenticated = strncmp(request->header_value, prefix, strlen(prefix)) == 0 &&
|
||||
strcmp(_api_password, request->header_value + strlen(prefix)) == 0;
|
||||
} else if (strcasecmp(request->header_key, "Host") == 0) {
|
||||
request->redirect = strcmp(request->header_value, "circuitpython.local") == 0;
|
||||
// Do a prefix check so that port is ignored. Length must be the same or the
|
||||
// header ends in :.
|
||||
const char *cp_local = "circuitpython.local";
|
||||
request->redirect = strncmp(request->header_value, cp_local, strlen(cp_local)) == 0 &&
|
||||
(strlen(request->header_value) == strlen(cp_local) ||
|
||||
request->header_value[strlen(cp_local)] == ':');
|
||||
} else if (strcasecmp(request->header_key, "Content-Length") == 0) {
|
||||
request->content_length = strtoul(request->header_value, NULL, 10);
|
||||
} else if (strcasecmp(request->header_key, "Expect") == 0) {
|
||||
|
@ -1279,6 +1320,9 @@ void supervisor_web_workflow_background(void) {
|
|||
// If we have a request in progress, continue working on it.
|
||||
if (common_hal_socketpool_socket_get_connected(&active)) {
|
||||
_process_request(&active, &active_request);
|
||||
} else {
|
||||
// Close the active socket if it is no longer connected.
|
||||
common_hal_socketpool_socket_close(&active);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,76 +19,11 @@ import adabot.github_requests as github
|
|||
sys.path.append("../docs")
|
||||
from shared_bindings_matrix import (
|
||||
SUPPORTED_PORTS,
|
||||
aliases_by_board,
|
||||
support_matrix_by_board,
|
||||
get_board_mapping,
|
||||
)
|
||||
|
||||
BIN = ("bin",)
|
||||
UF2 = ("uf2",)
|
||||
BIN_UF2 = ("bin", "uf2")
|
||||
HEX = ("hex",)
|
||||
HEX_UF2 = ("hex", "uf2")
|
||||
SPK = ("spk",)
|
||||
DFU = ("dfu",)
|
||||
BIN_DFU = ("bin", "dfu")
|
||||
COMBINED_HEX = ("combined.hex",)
|
||||
KERNEL8_IMG = ("disk.img.zip", "kernel8.img")
|
||||
KERNEL_IMG = ("disk.img.zip", "kernel.img")
|
||||
|
||||
# Default extensions
|
||||
extension_by_port = {
|
||||
"atmel-samd": UF2,
|
||||
"broadcom": KERNEL8_IMG,
|
||||
"cxd56": SPK,
|
||||
"espressif": BIN_UF2,
|
||||
"litex": DFU,
|
||||
"mimxrt10xx": HEX_UF2,
|
||||
"nrf": UF2,
|
||||
"raspberrypi": UF2,
|
||||
"stm": BIN,
|
||||
}
|
||||
|
||||
# Per board overrides
|
||||
extension_by_board = {
|
||||
# samd
|
||||
"arduino_mkr1300": BIN_UF2,
|
||||
"arduino_mkrzero": BIN_UF2,
|
||||
"arduino_nano_33_iot": BIN_UF2,
|
||||
"arduino_zero": BIN_UF2,
|
||||
"feather_m0_adalogger": BIN_UF2,
|
||||
"feather_m0_basic": BIN_UF2,
|
||||
"feather_m0_rfm69": BIN_UF2,
|
||||
"feather_m0_rfm9x": BIN_UF2,
|
||||
"uchip": BIN_UF2,
|
||||
# nRF52840 dev kits that may not have UF2 bootloaders,
|
||||
"makerdiary_nrf52840_mdk": HEX,
|
||||
"makerdiary_nrf52840_mdk_usb_dongle": HEX_UF2,
|
||||
"pca10056": BIN_UF2,
|
||||
"pca10059": BIN_UF2,
|
||||
"electronut_labs_blip": HEX,
|
||||
"microbit_v2": COMBINED_HEX,
|
||||
# stm32
|
||||
"meowbit_v121": UF2,
|
||||
"sparkfun_stm32_thing_plus": BIN_UF2,
|
||||
"swan_r5": BIN_UF2,
|
||||
# esp32
|
||||
"adafruit_feather_esp32_v2": BIN,
|
||||
# esp32c3
|
||||
"adafruit_qtpy_esp32c3": BIN,
|
||||
"ai_thinker_esp32-c3s": BIN,
|
||||
"ai_thinker_esp32-c3s-2m": BIN,
|
||||
"beetle-esp32-c3": BIN,
|
||||
"espressif_esp32c3_devkitm_1_n4": BIN,
|
||||
"lilygo_ttgo_t-01c3": BIN,
|
||||
"lolin_c3_mini": BIN,
|
||||
"microdev_micro_c3": BIN,
|
||||
"lilygo_ttgo_t-oi-plus": BIN,
|
||||
# broadcom
|
||||
"raspberrypi_zero": KERNEL_IMG,
|
||||
"raspberrypi_zero_w": KERNEL_IMG,
|
||||
}
|
||||
|
||||
language_allow_list = set(
|
||||
LANGUAGE_ALLOW_LIST = set(
|
||||
[
|
||||
"ID",
|
||||
"de_DE",
|
||||
|
@ -117,38 +52,10 @@ def get_languages(list_all=False):
|
|||
if f.name.endswith(".po"):
|
||||
languages.add(f.name[:-3])
|
||||
if not list_all:
|
||||
languages = languages & language_allow_list
|
||||
languages = languages & LANGUAGE_ALLOW_LIST
|
||||
return sorted(list(languages), key=str.casefold)
|
||||
|
||||
|
||||
def get_board_mapping():
|
||||
boards = {}
|
||||
for port in SUPPORTED_PORTS:
|
||||
board_path = os.path.join("../ports", port, "boards")
|
||||
for board_path in os.scandir(board_path):
|
||||
if board_path.is_dir():
|
||||
board_files = os.listdir(board_path.path)
|
||||
board_id = board_path.name
|
||||
extensions = extension_by_port[port]
|
||||
extensions = extension_by_board.get(board_path.name, extensions)
|
||||
aliases = aliases_by_board.get(board_path.name, [])
|
||||
boards[board_id] = {
|
||||
"port": port,
|
||||
"extensions": extensions,
|
||||
"download_count": 0,
|
||||
"aliases": aliases,
|
||||
}
|
||||
for alias in aliases:
|
||||
boards[alias] = {
|
||||
"port": port,
|
||||
"extensions": extensions,
|
||||
"download_count": 0,
|
||||
"alias": True,
|
||||
"aliases": [],
|
||||
}
|
||||
return boards
|
||||
|
||||
|
||||
def get_version_info():
|
||||
version = None
|
||||
sha = git("rev-parse", "--short", "HEAD").stdout.decode("utf-8")
|
||||
|
@ -283,7 +190,7 @@ def generate_download_info():
|
|||
|
||||
languages = get_languages()
|
||||
|
||||
support_matrix = support_matrix_by_board(use_branded_name=False)
|
||||
support_matrix = support_matrix_by_board(use_branded_name=False, withurl=False)
|
||||
|
||||
new_stable = "-" not in new_tag
|
||||
|
||||
|
@ -310,20 +217,19 @@ def generate_download_info():
|
|||
board_files = os.listdir(board_path.path)
|
||||
board_id = board_path.name
|
||||
board_info = board_mapping[board_id]
|
||||
|
||||
for alias in [board_id] + board_info["aliases"]:
|
||||
alias_info = board_mapping[alias]
|
||||
if alias not in current_info:
|
||||
changes["new_boards"].append(alias)
|
||||
current_info[alias] = {"downloads": 0, "versions": []}
|
||||
|
||||
new_version = {
|
||||
"stable": new_stable,
|
||||
"version": new_tag,
|
||||
"modules": support_matrix[alias][0],
|
||||
"languages": languages,
|
||||
"extensions": board_info["extensions"],
|
||||
"frozen_libraries": [frozen[0] for frozen in support_matrix[alias][1]],
|
||||
# add modules, extensions, frozen_libraries explicitly
|
||||
"modules": support_matrix[alias]["modules"],
|
||||
"extensions": support_matrix[alias]["extensions"],
|
||||
"frozen_libraries": support_matrix[alias]["frozen_libraries"],
|
||||
}
|
||||
current_info[alias]["downloads"] = alias_info["download_count"]
|
||||
current_info[alias]["versions"].append(new_version)
|
||||
|
@ -333,9 +239,10 @@ def generate_download_info():
|
|||
if changes["new_release"] and user:
|
||||
create_pr(changes, current_info, git_info, user)
|
||||
else:
|
||||
print("No new release to update")
|
||||
if "DEBUG" in os.environ:
|
||||
print(create_json(current_info).decode("utf8"))
|
||||
else:
|
||||
print("No new release to update")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
@ -12,6 +12,9 @@ import shutil
|
|||
import build_board_info as build_info
|
||||
import time
|
||||
|
||||
sys.path.append("../docs")
|
||||
from shared_bindings_matrix import get_settings_from_makefile
|
||||
|
||||
for port in build_info.SUPPORTED_PORTS:
|
||||
result = subprocess.run("rm -rf ../ports/{port}/build*".format(port=port), shell=True)
|
||||
|
||||
|
@ -39,6 +42,7 @@ for board in build_boards:
|
|||
bin_directory = "../bin/{}/".format(board)
|
||||
os.makedirs(bin_directory, exist_ok=True)
|
||||
board_info = all_boards[board]
|
||||
board_settings = get_settings_from_makefile("../ports/" + board_info["port"], board)
|
||||
|
||||
for language in languages:
|
||||
bin_directory = "../bin/{board}/{language}".format(board=board, language=language)
|
||||
|
@ -82,8 +86,12 @@ for board in build_boards:
|
|||
success = "\033[31mfailed\033[0m"
|
||||
|
||||
other_output = ""
|
||||
extensions = [
|
||||
extension.strip()
|
||||
for extension in board_settings["CIRCUITPY_BUILD_EXTENSIONS"].split(",")
|
||||
]
|
||||
|
||||
for extension in board_info["extensions"]:
|
||||
for extension in extensions:
|
||||
temp_filename = "../ports/{port}/{build}/firmware.{extension}".format(
|
||||
port=board_info["port"], build=build_dir, extension=extension
|
||||
)
|
||||
|
|
|
@ -21,6 +21,7 @@ import json
|
|||
import yaml
|
||||
|
||||
import build_board_info
|
||||
from shared_bindings_matrix import get_settings_from_makefile
|
||||
|
||||
PORT_TO_ARCH = {
|
||||
"atmel-samd": "arm",
|
||||
|
@ -53,6 +54,7 @@ def set_boards_to_build(build_all):
|
|||
all_board_ids = set()
|
||||
port_to_boards = {}
|
||||
board_to_port = {}
|
||||
board_settings = {}
|
||||
for board_id in boards_info_json:
|
||||
info = boards_info_json[board_id]
|
||||
if info.get("alias", False):
|
||||
|
@ -70,6 +72,9 @@ def set_boards_to_build(build_all):
|
|||
boards_to_build = set()
|
||||
board_pattern = re.compile(r"^ports/[^/]+/boards/([^/]+)/")
|
||||
port_pattern = re.compile(r"^ports/([^/]+)/")
|
||||
module_pattern = re.compile(
|
||||
r"^(ports/[^/]+/common-hal|shared-bindings|shared-module)/([^/]+)/"
|
||||
)
|
||||
for p in changed_files:
|
||||
# See if it is board specific
|
||||
board_matches = board_pattern.search(p)
|
||||
|
@ -80,7 +85,8 @@ def set_boards_to_build(build_all):
|
|||
|
||||
# See if it is port specific
|
||||
port_matches = port_pattern.search(p)
|
||||
if port_matches:
|
||||
module_matches = module_pattern.search(p)
|
||||
if port_matches and not module_matches:
|
||||
port = port_matches.group(1)
|
||||
if port != "unix":
|
||||
boards_to_build.update(port_to_boards[port])
|
||||
|
@ -94,6 +100,48 @@ def set_boards_to_build(build_all):
|
|||
if p.startswith("tests"):
|
||||
continue
|
||||
|
||||
# As a (nearly) last resort, for some certain files, we compute the settings from the
|
||||
# makefile for each board and determine whether to build them that way.
|
||||
if p.startswith("frozen") or p.startswith("supervisor") or module_matches:
|
||||
for board in all_board_ids:
|
||||
if board not in board_settings:
|
||||
board_settings[board] = get_settings_from_makefile(
|
||||
"../ports/" + board_to_port[board], board
|
||||
)
|
||||
settings = board_settings[board]
|
||||
|
||||
# Check frozen files to see if they are in each board.
|
||||
frozen = settings.get("FROZEN_MPY_DIRS", "")
|
||||
if frozen and p.startswith("frozen") and p in frozen:
|
||||
boards_to_build.add(board)
|
||||
continue
|
||||
|
||||
# Check supervisor files. This is useful for limiting workflow changes to the
|
||||
# relevant boards.
|
||||
supervisor = settings["SRC_SUPERVISOR"]
|
||||
if p.startswith("supervisor"):
|
||||
if p in supervisor:
|
||||
boards_to_build.add(board)
|
||||
continue
|
||||
|
||||
web_workflow = settings["CIRCUITPY_WEB_WORKFLOW"]
|
||||
while web_workflow.startswith("$("):
|
||||
web_workflow = settings[web_workflow[2:-1]]
|
||||
if (
|
||||
p.startswith("supervisor/shared/web_workflow/static/")
|
||||
and web_workflow != "0"
|
||||
):
|
||||
boards_to_build.add(board)
|
||||
continue
|
||||
|
||||
# Check module matches
|
||||
if module_matches:
|
||||
module = module_matches.group(2) + "/"
|
||||
if module in settings["SRC_PATTERNS"]:
|
||||
boards_to_build.add(board)
|
||||
continue
|
||||
continue
|
||||
|
||||
# Otherwise build it all
|
||||
boards_to_build = all_board_ids
|
||||
break
|
||||
|
@ -101,7 +149,7 @@ def set_boards_to_build(build_all):
|
|||
# Split boards by architecture.
|
||||
print("Building boards:")
|
||||
arch_to_boards = {"aarch": [], "arm": [], "riscv": [], "espressif": []}
|
||||
for board in boards_to_build:
|
||||
for board in sorted(boards_to_build):
|
||||
print(" ", board)
|
||||
port = board_to_port.get(board)
|
||||
# A board can appear due to its _deletion_ (rare)
|
||||
|
|
Loading…
Reference in New Issue