diff --git a/.github/workflows/genExamples.yml b/.github/workflows/genExamples.yml new file mode 100644 index 00000000..9f07ca90 --- /dev/null +++ b/.github/workflows/genExamples.yml @@ -0,0 +1,41 @@ +name: Update examples.json + +on: + workflow_dispatch: + inputs: + develop: + description: "Test with develop branch" + required: false + type: boolean + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Install Dependencies + run: | + pip install pycryptodomex + - name: Cache ~/.pico-sdk + uses: actions/cache@v4 + with: + path: ~/.pico-sdk + key: pico-sdk-${{ hashFiles('scripts/genExamples.py') }} + - name: Run genExamples.py + env: + SDK_VERSION: ${{ inputs.develop && '2-develop' || '' }} + run: | + python scripts/genExamples.py + - name: List errors + run: | + ls -la errors-pico* + - name: Upload Artifact + uses: actions/upload-artifact@v4 + with: + name: examples.json + path: | + data/0.17.0/examples.json + - name: Print diff + run: | + git diff data/0.17.0/examples.json diff --git a/data/0.17.0/examples.json b/data/0.17.0/examples.json index 276d1a29..dd99f0e1 100644 --- a/data/0.17.0/examples.json +++ b/data/0.17.0/examples.json @@ -756,6 +756,19 @@ ], "supportRiscV": true }, + "dev_multi_cdc": { + "path": "usb/device/dev_multi_cdc", + "name": "dev_multi_cdc", + "libPaths": [], + "libNames": [], + "boards": [ + "pico", + "pico_w", + "pico2", + "pico2_w" + ], + "supportRiscV": true + }, "dev_hid_composite": { "path": "usb/device/dev_hid_composite", "name": "dev_hid_composite", @@ -1342,6 +1355,28 @@ ], "supportRiscV": true }, + "picow_http_client": { + "path": "pico_w/wifi/http_client", + "name": "picow_http_client", + "libPaths": [], + "libNames": [], + "boards": [ + "pico_w", + "pico2_w" + ], + "supportRiscV": true + }, + "picow_http_client_verify": { + "path": "pico_w/wifi/http_client", + "name": "picow_http_client_verify", + "libPaths": [], + "libNames": [], + "boards": [ + "pico_w", + "pico2_w" + ], + "supportRiscV": true + }, "picow_blink": { "path": "pico_w/wifi/blink", "name": "picow_blink", @@ -1386,6 +1421,39 @@ ], "supportRiscV": true }, + "picow_tcpip_client": { + "path": "pico_w/wifi/tcp_client", + "name": "picow_tcpip_client", + "libPaths": [], + "libNames": [], + "boards": [ + "pico_w", + "pico2_w" + ], + "supportRiscV": true + }, + "picow_tls_client": { + "path": "pico_w/wifi/tls_client", + "name": "picow_tls_client", + "libPaths": [], + "libNames": [], + "boards": [ + "pico_w", + "pico2_w" + ], + "supportRiscV": true + }, + "picow_tls_verify": { + "path": "pico_w/wifi/tls_client", + "name": "picow_tls_verify", + "libPaths": [], + "libNames": [], + "boards": [ + "pico_w", + "pico2_w" + ], + "supportRiscV": true + }, "picow_udp_beacon": { "path": "pico_w/wifi/udp_beacon", "name": "picow_udp_beacon", @@ -1430,6 +1498,17 @@ ], "supportRiscV": true }, + "picow_httpd": { + "path": "pico_w/wifi/httpd", + "name": "picow_httpd", + "libPaths": [], + "libNames": [], + "boards": [ + "pico_w", + "pico2_w" + ], + "supportRiscV": true + }, "picow_ble_temp_reader": { "path": "pico_w/bt/standalone", "name": "picow_ble_temp_reader", @@ -1584,4 +1663,4 @@ ], "supportRiscV": true } -} +} \ No newline at end of file diff --git a/scripts/genExamples.py b/scripts/genExamples.py index 45078741..e4da5b9b 100644 --- a/scripts/genExamples.py +++ b/scripts/genExamples.py @@ -4,8 +4,14 @@ import glob import shutil import json +import multiprocessing +import configparser +import platform -from pico_project import GenerateCMake +from pico_project import GenerateCMake, copyExampleConfigs + +# This script is designed to be run on Linux +assert platform.system() == "Linux" boards = ["pico", "pico_w", "pico2", "pico2_w"] @@ -31,7 +37,120 @@ CURRENT_DATA_VERSION = "0.17.0" -SDK_VERSION = "2.1.1" +SDK_VERSION = os.environ.get("SDK_VERSION", "2.1.1") +ARM_TOOLCHAIN_VERSION = os.environ.get("ARM_TOOLCHAIN_VERSION", "14_2_Rel1") +RISCV_TOOLCHAIN_VERSION = os.environ.get( + "RISCV_TOOLCHAIN_VERSION", "RISCV_ZCB_RPI_2_1_1_3" +) + +# To test with develop SDK, uncomment the line below - this will clone the SDK & picotool, and build picotool & pioasm +# note: the 2- is required due to a VERSION_LESS check in pico-vscode.cmake +# SDK_VERSION = "2-develop" + +BUILD_TOOLS = not os.path.exists(os.path.expanduser(f"~/.pico-sdk/sdk/{SDK_VERSION}")) + +if "develop" in SDK_VERSION: + SDK_BRANCH = "develop" + PICOTOOL_BRANCH = "develop" + EXAMPLES_BRANCH = "develop" + BUILD_TOOLS = True # Always clone & build the latest when using develop +else: + SDK_BRANCH = SDK_VERSION + PICOTOOL_BRANCH = SDK_VERSION + EXAMPLES_BRANCH = f"sdk-{SDK_VERSION}" + +# Platform & toolchain setup +platform = "linux_x64" if platform.machine() == "x86_64" else "linux_arm64" +config = configparser.ConfigParser() +config.read( + f"{os.path.dirname(os.path.realpath(__file__))}/../data/{CURRENT_DATA_VERSION}/supportedToolchains.ini" +) + +# Download arm toolchain if not already downloaded +if not os.path.exists( + os.path.expanduser(f"~/.pico-sdk/toolchain/{ARM_TOOLCHAIN_VERSION}") +): + toolchain_url = config[ARM_TOOLCHAIN_VERSION][platform] + os.makedirs( + os.path.expanduser(f"~/.pico-sdk/toolchain/{ARM_TOOLCHAIN_VERSION}"), + exist_ok=True, + ) + os.system(f"wget {toolchain_url}") + os.system( + f"tar -xf {toolchain_url.split('/')[-1]} --strip-components 1 -C ~/.pico-sdk/toolchain/{ARM_TOOLCHAIN_VERSION}" + ) + os.system(f"rm {toolchain_url.split('/')[-1]}") + +# Download riscv toolchain if not already downloaded +if not os.path.exists( + os.path.expanduser(f"~/.pico-sdk/toolchain/{RISCV_TOOLCHAIN_VERSION}") +): + toolchain_url = config[RISCV_TOOLCHAIN_VERSION][platform] + os.makedirs( + os.path.expanduser(f"~/.pico-sdk/toolchain/{RISCV_TOOLCHAIN_VERSION}"), + exist_ok=True, + ) + os.system(f"wget {toolchain_url}") + os.system( + f"tar -xf {toolchain_url.split('/')[-1]} -C ~/.pico-sdk/toolchain/{RISCV_TOOLCHAIN_VERSION}" + ) + os.system(f"rm {toolchain_url.split('/')[-1]}") + +# Copy pico-vscode.cmake to ~/.pico-sdk/cmake/pico-vscode.cmake +os.makedirs(os.path.expanduser("~/.pico-sdk/cmake"), exist_ok=True) +shutil.copy( + f"{os.path.dirname(os.path.realpath(__file__))}/pico-vscode.cmake", + os.path.expanduser("~/.pico-sdk/cmake/pico-vscode.cmake"), +) + +if BUILD_TOOLS: + # Clone pico-sdk + try: + shutil.rmtree(os.path.expanduser(f"~/.pico-sdk/sdk/{SDK_VERSION}")) + except FileNotFoundError: + pass + os.system( + f"git -c advice.detachedHead=false clone https://github.com/raspberrypi/pico-sdk.git --depth=1 --branch {SDK_BRANCH} --recurse-submodules --shallow-submodules ~/.pico-sdk/sdk/{SDK_VERSION}" + ) + + # Clone & build picotool + try: + shutil.rmtree("picotool") + except FileNotFoundError: + pass + try: + shutil.rmtree("picotool-build") + except FileNotFoundError: + pass + try: + shutil.rmtree(os.path.expanduser(f"~/.pico-sdk/picotool/{SDK_VERSION}")) + except FileNotFoundError: + pass + os.system( + f"git -c advice.detachedHead=false clone https://github.com/raspberrypi/picotool.git --depth=1 --branch {PICOTOOL_BRANCH}" + ) + os.system( + f"cmake -S picotool -B picotool-build -GNinja -DPICO_SDK_PATH=~/.pico-sdk/sdk/{SDK_VERSION} -DPICOTOOL_FLAT_INSTALL=1 -DPICOTOOL_NO_LIBUSB=1" + ) + os.system(f"cmake --build picotool-build") + os.system( + f"cmake --install picotool-build --prefix ~/.pico-sdk/picotool/{SDK_VERSION}" + ) + + # Build pioasm + try: + shutil.rmtree("pioasm-build") + except FileNotFoundError: + pass + try: + shutil.rmtree(os.path.expanduser(f"~/.pico-sdk/tools/{SDK_VERSION}")) + except FileNotFoundError: + pass + os.system( + f"cmake -S ~/.pico-sdk/sdk/{SDK_VERSION}/tools/pioasm -B pioasm-build -GNinja -DPIOASM_FLAT_INSTALL=1 -DPIOASM_VERSION_STRING={SDK_VERSION}" + ) + os.system(f"cmake --build pioasm-build") + os.system(f"cmake --install pioasm-build --prefix ~/.pico-sdk/tools/{SDK_VERSION}") try: shutil.rmtree("pico-examples") @@ -43,11 +162,22 @@ except FileNotFoundError: pass os.system( - f"git -c advice.detachedHead=false clone https://github.com/raspberrypi/pico-examples.git --depth=1 --branch sdk-{SDK_VERSION}" + f"git -c advice.detachedHead=false clone https://github.com/raspberrypi/pico-examples.git --depth=1 --branch {EXAMPLES_BRANCH}" ) -os.environ["PICO_SDK_PATH"] = f"~/.pico-sdk/sdk/{SDK_VERSION}" -os.environ["WIFI_SSID"] = "Your Wi-Fi SSID" -os.environ["WIFI_PASSWORD"] = "Your Wi-Fi Password" + +PICO_SDK_PATH = f"~/.pico-sdk/sdk/{SDK_VERSION}" +configure_env = { + "PICO_SDK_PATH": PICO_SDK_PATH, + "WIFI_SSID": "Your Wi-Fi SSID", + "WIFI_PASSWORD": "Your Wi-Fi Password", + "TEST_TCP_SERVER_IP": "192.168.1.100", # This isn't read from environment variables, so also needs to be passed to cmake + "MQTT_SERVER": "myMQTTserver", +} + +os.environ["CFLAGS"] = "-Werror=cpp" +os.environ["CXXFLAGS"] = "-Werror=cpp" + +updated_examples = set() for board in boards: for platform in platforms[board]: @@ -55,15 +185,26 @@ shutil.rmtree("build") except FileNotFoundError: pass - toolchainVersion = "RISCV_RPI_2_1_1_3" if "riscv" in platform else "14_2_Rel1" + toolchainVersion = ( + RISCV_TOOLCHAIN_VERSION if "riscv" in platform else ARM_TOOLCHAIN_VERSION + ) toolchainPath = f"~/.pico-sdk/toolchain/{toolchainVersion}" picotoolDir = f"~/.pico-sdk/picotool/{SDK_VERSION}/picotool" + + # Setup env to find targets + for k, v in configure_env.items(): + os.environ[k] = v + os.system( - f"cmake -S pico-examples -B build -DPICO_BOARD={board} -DPICO_PLATFORM={platform} -DPICO_TOOLCHAIN_PATH={toolchainPath} -Dpicotool_DIR={picotoolDir}" + f"cmake -S pico-examples -B build -DPICO_BOARD={board} -DPICO_PLATFORM={platform} -DPICO_TOOLCHAIN_PATH={toolchainPath} -Dpicotool_DIR={picotoolDir} -DTEST_TCP_SERVER_IP=$TEST_TCP_SERVER_IP" ) os.system("cmake --build build --target help > targets.txt") + # Clear env for clean tests + for k in configure_env.keys(): + del os.environ[k] + targets = [] with open("targets.txt", "r") as f: @@ -131,51 +272,70 @@ lib_locs[k]["loc"] = v["locs"][0] # Test build them all - try: - shutil.rmtree("tmp") - shutil.rmtree("tmp-build") - except FileNotFoundError: - pass - for target, v in target_locs.items(): - os.mkdir("tmp-build") + + # Test build function + def test_build(target, v): + dir = f"tmp-{target}" + try: + shutil.rmtree(dir) + except FileNotFoundError: + pass + try: + shutil.rmtree(f"{dir}-build") + except FileNotFoundError: + pass + + os.mkdir(f"{dir}-build") loc = v["loc"] loc = loc.replace("/CMakeLists.txt", "") - shutil.copytree(loc, "tmp") + shutil.copytree(loc, dir) if len(v["libs"]): for lib in v["libs"]: shutil.copytree( lib_locs[lib]["loc"].replace("/CMakeLists.txt", ""), - f"tmp/{lib}", + f"{dir}/{lib}", ) params = { "projectName": target, "wantOverwrite": True, "wantConvert": True, "wantExample": True, - "wantThreadsafeBackground": False, - "wantPoll": False, "boardtype": board, "sdkVersion": SDK_VERSION, "toolchainVersion": toolchainVersion, "picotoolVersion": SDK_VERSION, "exampleLibs": v["libs"], } - GenerateCMake("tmp", params) - if params["wantThreadsafeBackground"] or params["wantPoll"]: - # Write lwipopts for examples - shutil.copy("lwipopts.h", "tmp/") - shutil.copy("pico-examples/pico_sdk_import.cmake", "tmp/") - - retcmake = os.system( - "cmake -S tmp -B tmp-build -DCMAKE_COMPILE_WARNING_AS_ERROR=ON" + GenerateCMake(dir, params) + copyExampleConfigs(dir) + + shutil.copy( + os.path.expanduser(f"{PICO_SDK_PATH}/external/pico_sdk_import.cmake"), + f"{dir}/", ) - retbuild = os.system("cmake --build tmp-build --parallel 22") + + retcmake = os.system(f"cmake -S {dir} -B {dir}-build -GNinja") + retbuild = os.system(f"cmake --build {dir}-build") + ret = None if retcmake or retbuild: print( f"Error occurred with {target} {v} - cmake {retcmake}, build {retbuild}" ) - shutil.copytree("tmp", f"errors-{board}-{platform}/{target}") + shutil.copytree(dir, f"errors-{board}-{platform}/{target}") else: + ret = (target, v, loc) + + shutil.rmtree(dir) + shutil.rmtree(f"{dir}-build") + return ret + + with multiprocessing.Pool(processes=os.cpu_count()) as pool: + ret = pool.starmap( + test_build, + [(target, v) for target, v in target_locs.items()], + ) + ret = filter(lambda x: x != None, ret) + for target, v, loc in ret: if examples.get(target) != None: example = examples[target] assert example["path"] == loc.replace(f"{walk_dir}/", "") @@ -204,12 +364,33 @@ "boards": [board], "supportRiscV": "riscv" in platform, } + # Write out after each platform + with open( + f"{os.path.dirname(os.path.realpath(__file__))}/../data/{CURRENT_DATA_VERSION}/examples.json", + "r", + ) as f: + current_examples = json.load(f) + + current_examples.update(examples) + updated_examples.update(examples.keys()) + + with open( + f"{os.path.dirname(os.path.realpath(__file__))}/../data/{CURRENT_DATA_VERSION}/examples.json", + "w", + ) as f: + json.dump(current_examples, f, indent=4) + +# Finalise list, removing any examples no longer supported +with open( + f"{os.path.dirname(os.path.realpath(__file__))}/../data/{CURRENT_DATA_VERSION}/examples.json", + "r", +) as f: + current_examples = dict(json.load(f)) - shutil.rmtree("tmp") - shutil.rmtree("tmp-build") +current_examples = {k: v for k, v in current_examples.items() if k in updated_examples} with open( f"{os.path.dirname(os.path.realpath(__file__))}/../data/{CURRENT_DATA_VERSION}/examples.json", "w", ) as f: - json.dump(examples, f, indent=4) + json.dump(current_examples, f, indent=4) diff --git a/scripts/mbedtls_config.h b/scripts/mbedtls_config.h new file mode 100644 index 00000000..fbfae97a --- /dev/null +++ b/scripts/mbedtls_config.h @@ -0,0 +1,75 @@ +#ifndef MBEDTLS_CONFIG_EXAMPLES_COMMON_H +#define MBEDTLS_CONFIG_EXAMPLES_COMMON_H + +/* Workaround for some mbedtls source files using INT_MAX without including limits.h */ +#include + +#define MBEDTLS_NO_PLATFORM_ENTROPY +#define MBEDTLS_ENTROPY_HARDWARE_ALT + +#define MBEDTLS_SSL_OUT_CONTENT_LEN 2048 + +#define MBEDTLS_ALLOW_PRIVATE_ACCESS +#define MBEDTLS_HAVE_TIME +#define MBEDTLS_PLATFORM_MS_TIME_ALT + +#define MBEDTLS_CIPHER_MODE_CBC +#define MBEDTLS_ECP_DP_SECP192R1_ENABLED +#define MBEDTLS_ECP_DP_SECP224R1_ENABLED +#define MBEDTLS_ECP_DP_SECP256R1_ENABLED +#define MBEDTLS_ECP_DP_SECP384R1_ENABLED +#define MBEDTLS_ECP_DP_SECP521R1_ENABLED +#define MBEDTLS_ECP_DP_SECP192K1_ENABLED +#define MBEDTLS_ECP_DP_SECP224K1_ENABLED +#define MBEDTLS_ECP_DP_SECP256K1_ENABLED +#define MBEDTLS_ECP_DP_BP256R1_ENABLED +#define MBEDTLS_ECP_DP_BP384R1_ENABLED +#define MBEDTLS_ECP_DP_BP512R1_ENABLED +#define MBEDTLS_ECP_DP_CURVE25519_ENABLED +#define MBEDTLS_KEY_EXCHANGE_RSA_ENABLED +#define MBEDTLS_PKCS1_V15 +#define MBEDTLS_SHA256_SMALLER +#define MBEDTLS_SSL_SERVER_NAME_INDICATION +#define MBEDTLS_AES_C +#define MBEDTLS_ASN1_PARSE_C +#define MBEDTLS_BIGNUM_C +#define MBEDTLS_CIPHER_C +#define MBEDTLS_CTR_DRBG_C +#define MBEDTLS_ENTROPY_C +#define MBEDTLS_ERROR_C +#define MBEDTLS_MD_C +#define MBEDTLS_MD5_C +#define MBEDTLS_OID_C +#define MBEDTLS_PKCS5_C +#define MBEDTLS_PK_C +#define MBEDTLS_PK_PARSE_C +#define MBEDTLS_PLATFORM_C +#define MBEDTLS_RSA_C +#define MBEDTLS_SHA1_C +#define MBEDTLS_SHA224_C +#define MBEDTLS_SHA256_C +#define MBEDTLS_SHA512_C +#define MBEDTLS_SSL_CLI_C +#define MBEDTLS_SSL_SRV_C +#define MBEDTLS_SSL_TLS_C +#define MBEDTLS_X509_CRT_PARSE_C +#define MBEDTLS_X509_USE_C +#define MBEDTLS_AES_FEWER_TABLES + +/* TLS 1.2 */ +#define MBEDTLS_SSL_PROTO_TLS1_2 +#define MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED +#define MBEDTLS_GCM_C +#define MBEDTLS_ECDH_C +#define MBEDTLS_ECP_C +#define MBEDTLS_ECDSA_C +#define MBEDTLS_ASN1_WRITE_C + +// The following is needed to parse a certificate +#define MBEDTLS_PEM_PARSE_C +#define MBEDTLS_BASE64_C + +// The following significantly speeds up mbedtls due to NIST optimizations. +#define MBEDTLS_ECP_NIST_OPTIM + +#endif diff --git a/scripts/pico_project.py b/scripts/pico_project.py index 4d57d708..650cb035 100644 --- a/scripts/pico_project.py +++ b/scripts/pico_project.py @@ -22,6 +22,8 @@ import csv import json +sourcefolder = os.path.dirname(os.path.abspath(__file__)) + CMAKELIST_FILENAME = "CMakeLists.txt" CMAKECACHE_FILENAME = "CMakeCache.txt" @@ -799,23 +801,19 @@ def GenerateCMake(folder, params): ) file.write(content) else: - if any( - [ - "pico_cyw43_arch_lwip_threadsafe_background" in line - for line in lines - ] - ): - print("Threadsafe Background") - params["wantThreadsafeBackground"] = True - if any(["pico_cyw43_arch_lwip_poll" in line for line in lines]): - print("Poll") - params["wantPoll"] = True for line in lines: if "WIFI_SSID" in line and "WIFI_SSID" not in cmake_header2: cmake_header2 += ( '\nset(WIFI_SSID "Your Wi-Fi SSID")\n' 'set(WIFI_PASSWORD "Your Wi-Fi Password")\n' ) + if ( + "TEST_TCP_SERVER_IP" in line + and "TEST_TCP_SERVER_IP" not in cmake_header2 + ): + cmake_header2 += '\nset(TEST_TCP_SERVER_IP "192.168.1.100") # Change this to your TCP server IP\n' + if "MQTT_SERVER" in line and "MQTT_SERVER" not in cmake_header2: + cmake_header2 += '\nset(MQTT_SERVER "myMQTTserver") # Change this to the host name of your MQTT server\n' # Write all headers file.write(cmake_header1) file.write(cmake_header_us) @@ -1326,6 +1324,28 @@ def generateProjectFiles( os.chdir(oldCWD) +def copyExampleConfigs(projectPath): + lwipopts_path = os.path.join(projectPath, "lwipopts.h") + if os.path.exists(lwipopts_path): + with open(lwipopts_path, "r") as f: + if "lwipopts_examples_common.h" in f.read(): + # Write lwipopts for examples + shutil.copy( + os.path.join(sourcefolder, "lwipopts.h"), + os.path.join(projectPath, "lwipopts_examples_common.h"), + ) + + mbedtls_config_path = os.path.join(projectPath, "mbedtls_config.h") + if os.path.exists(mbedtls_config_path): + with open(mbedtls_config_path, "r") as f: + if "mbedtls_config_examples_common.h" in f.read(): + # Write mbedtls_config for examples + shutil.copy( + os.path.join(sourcefolder, "mbedtls_config.h"), + os.path.join(projectPath, "mbedtls_config_examples_common.h"), + ) + + def DoEverything(params): if not os.path.exists(params["projectRoot"]): print("Invalid project path") @@ -1409,10 +1429,25 @@ def DoEverything(params): GenerateCMake(projectPath, params) + # Add examples common files if we are using examples if params["wantExample"]: - if params["wantThreadsafeBackground"] or params["wantPoll"]: - # Write lwipopts for examples - shutil.copy(sourcefolder + "/" + "lwipopts.h", projectPath / "lwipopts.h") + if os.path.exists(projectPath / "lwipopts.h"): + with open(projectPath / "lwipopts.h", "r") as f: + if "lwipopts_examples_common.h" in f.read(): + # Write lwipopts for examples + shutil.copy( + sourcefolder + "/" + "lwipopts.h", + projectPath / "lwipopts_examples_common.h", + ) + + if os.path.exists(projectPath / "mbedtls_config.h"): + with open(projectPath / "mbedtls_config.h", "r") as f: + if "mbedtls_config_examples_common.h" in f.read(): + # Write mbedtls_config for examples + shutil.copy( + sourcefolder + "/" + "mbedtls_config.h", + projectPath / "mbedtls_config_examples_common.h", + ) # Create a build folder, and run our cmake project build from it if not os.path.exists("build"): @@ -1453,8 +1488,6 @@ def DoEverything(params): # main execution starteth here if __name__ == "__main__": - sourcefolder = os.path.dirname(os.path.abspath(__file__)) - args = ParseCommandLine() if args.nouart: @@ -1494,8 +1527,6 @@ def DoEverything(params): "wantOverwrite": args.overwrite, "wantConvert": args.convert or args.example, "wantExample": args.example, - "wantThreadsafeBackground": False, - "wantPoll": False, "boardtype": args.boardtype, "features": args.feature, "projects": args.project,