diff --git a/.env b/.env index dd345354..627d95c0 100644 --- a/.env +++ b/.env @@ -1,2 +1 @@ OPENSHOCK_API_DOMAIN=api.shocklink.net -OPENSHOCK_FW_VERSION=0.8.1 diff --git a/.env.development b/.env.development index 03ad6d04..818a93ef 100644 --- a/.env.development +++ b/.env.development @@ -1 +1,5 @@ +# Included by WebUI/ (DO NOT RENAME THIS FILE!) +# Intended for development builds (locally). LOG_LEVEL=VERBOSE +OPENSHOCK_FW_VERSION=local +OPENSHOCK_FW_COMMIT=local diff --git a/.env.production b/.env.production index 03164678..bf8f693d 100644 --- a/.env.production +++ b/.env.production @@ -1 +1,3 @@ +# Included by WebUI/ (DO NOT RENAME THIS FILE!) +# Intended for all CI firmware builds. LOG_LEVEL=INFO diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index 8ed9322c..8b781cbd 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -21,6 +21,7 @@ env: PYTHON_VERSION: 3.11 OPENSHOCK_API_DOMAIN: api.shocklink.net OPENSHOCK_FW_VERSION: master-${{ github.sha }} + OPENSHOCK_FW_COMMIT: ${{ github.sha }} jobs: diff --git a/.github/workflows/ci-tag.yml b/.github/workflows/ci-tag.yml index d14900e8..3e675ba8 100644 --- a/.github/workflows/ci-tag.yml +++ b/.github/workflows/ci-tag.yml @@ -9,6 +9,7 @@ name: ci-tag env: OPENSHOCK_API_DOMAIN: api.shocklink.net OPENSHOCK_FW_VERSION: ${{ github.ref_name }} + OPENSHOCK_FW_COMMIT: ${{ github.sha }} jobs: diff --git a/boards/Wemos-D1-Mini-ESP32/board.json b/boards/Wemos-D1-Mini-ESP32.json similarity index 100% rename from boards/Wemos-D1-Mini-ESP32/board.json rename to boards/Wemos-D1-Mini-ESP32.json diff --git a/boards/Wemos-Lolin-S2-Mini/board.json b/boards/Wemos-Lolin-S2-Mini.json similarity index 100% rename from boards/Wemos-Lolin-S2-Mini/board.json rename to boards/Wemos-Lolin-S2-Mini.json diff --git a/boards/Wemos-Lolin-S3.json b/boards/Wemos-Lolin-S3.json new file mode 100644 index 00000000..82b81be5 --- /dev/null +++ b/boards/Wemos-Lolin-S3.json @@ -0,0 +1,34 @@ +{ + "build": { + "arduino": { + "ldscript": "esp32s3_out.ld", + "partitions": "default_16MB.csv", + "memory_type": "qio_opi" + }, + "core": "esp32", + "extra_flags": ["-DBOARD_HAS_PSRAM", "-DARDUINO_LOLIN_S3", "-DARDUINO_USB_MODE=1"], + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "qio", + "hwids": [["0x303A", "0x1001"]], + "mcu": "esp32s3", + "variant": "lolin_s3" + }, + "connectivity": ["wifi", "bluetooth"], + "debug": { + "openocd_target": "esp32s3.cfg" + }, + "frameworks": ["arduino", "espidf"], + "name": "WEMOS LOLIN S3", + "upload": { + "flash_size": "16MB", + "maximum_ram_size": 327680, + "maximum_size": 16777216, + "use_1200bps_touch": true, + "wait_for_upload_port": true, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://www.wemos.cc/en/latest/s3/index.html", + "vendor": "WEMOS" +} diff --git a/boards/Wemos-Lolin-S3/board.json b/boards/Wemos-Lolin-S3/board.json deleted file mode 100644 index dd27537f..00000000 --- a/boards/Wemos-Lolin-S3/board.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "build": { - "arduino": { - "ldscript": "esp32s3_out.ld", - "partitions": "default_16MB.csv", - "memory_type": "qio_opi" - }, - "core": "esp32", - "extra_flags": [ - "-DBOARD_HAS_PSRAM", - "-DARDUINO_LOLIN_S3", - "-DARDUINO_USB_MODE=1", - "-DARDUINO_USB_CDC_ON_BOOT=1" - ], - "f_cpu": "240000000L", - "f_flash": "80000000L", - "flash_mode": "qio", - "hwids": [ - [ - "0x303A", - "0x1001" - ] - ], - "mcu": "esp32s3", - "variant": "lolin_s3" - }, - "connectivity": [ - "wifi", - "bluetooth" - ], - "debug": { - "openocd_target": "esp32s3.cfg" - }, - "frameworks": [ - "arduino", - "espidf" - ], - "name": "WEMOS LOLIN S3", - "upload": { - "flash_size": "16MB", - "maximum_ram_size": 327680, - "maximum_size": 16777216, - "use_1200bps_touch": true, - "wait_for_upload_port": true, - "require_upload_port": true, - "speed": 460800 - }, - "url": "https://www.wemos.cc/en/latest/s3/index.html", - "vendor": "WEMOS" -} diff --git a/include/SerialInputHandler.h b/include/SerialInputHandler.h index 522eb498..e6d52e00 100644 --- a/include/SerialInputHandler.h +++ b/include/SerialInputHandler.h @@ -4,4 +4,7 @@ namespace OpenShock::SerialInputHandler { void Update(); + + void PrintWelcomeHeader(); + void PrintVersionInfo(); } // namespace OpenShock::SerialInputHandler diff --git a/platformio.ini b/platformio.ini index e0c673b0..2d786a08 100644 --- a/platformio.ini +++ b/platformio.ini @@ -38,7 +38,7 @@ monitor_speed = 115200 ; https://docs.platformio.org/en/stable/boards/espressif32/wemos_d1_mini32.html [env:Wemos-D1-Mini-ESP32] -board = OpenShock-Wemos-D1-Mini-ESP32 +board = Wemos-D1-Mini-ESP32 board_build.partitions = boards/Wemos-D1-Mini-ESP32/partitions.csv build_flags = -DOPENSHOCK_LED_GPIO=2 @@ -46,27 +46,27 @@ build_flags = ; https://docs.platformio.org/en/latest/boards/espressif32/lolin_s2_mini.html [env:Wemos-Lolin-S2-Mini] -board = OpenShock-Wemos-Lolin-S2-Mini +board = Wemos-Lolin-S2-Mini board_build.partitions = boards/Wemos-Lolin-S2-Mini/partitions.csv build_flags = -DOPENSHOCK_LED_GPIO=15 ; https://docs.platformio.org/en/latest/boards/espressif32/lolin_s3.html [env:Wemos-Lolin-S3] -board = OpenShock-Wemos-Lolin-S3 +board = Wemos-Lolin-S3 board_build.partitions = boards/Wemos-Lolin-S3/partitions.csv build_flags = -DOPENSHOCK_LED_WS2812B=38 [env:Pishock-2023] -board = OpenShock-Wemos-D1-Mini-ESP32 +board = Wemos-D1-Mini-ESP32 board_build.partitions = boards/Wemos-D1-Mini-ESP32/partitions.csv build_flags = -DOPENSHOCK_LED_GPIO=2 -DOPENSHOCK_TX_PIN=12 [env:Pishock-Lite-2021] -board = OpenShock-Wemos-D1-Mini-ESP32 +board = Wemos-D1-Mini-ESP32 board_build.partitions = boards/Wemos-D1-Mini-ESP32/partitions.csv build_flags = -DOPENSHOCK_LED_GPIO=2 diff --git a/scripts/embed_env_vars.py b/scripts/embed_env_vars.py index 914dc5b9..89d59ec0 100644 --- a/scripts/embed_env_vars.py +++ b/scripts/embed_env_vars.py @@ -1,4 +1,5 @@ -from utils import pioenv, sysenv, dotenv +from typing import Mapping +from utils import pioenv, sysenv, dotenv, shorthands # This file is invoked by PlatformIO during build. # See 'extra_scripts' in 'platformio.ini'. @@ -18,12 +19,9 @@ # If we are running in CI and either building the master branch, # or building a PR that will merge into the master branch, THEN # we will build in RELEASE mode. -is_ci = sysenv.get_bool('CI', False) -is_branch_master = sysenv.get_string('GITHUB_REF_NAME', '') == 'master' -is_pr_into_master = ( - sysenv.get_string('GITHUB_BASE_REF', '') == 'master' - and sysenv.get_string('GITHUB_EVENT_NAME', '') == 'pull_request' -) +is_ci = shorthands.is_github_ci() +is_branch_master = shorthands.get_github_ref_name() == 'master' +is_pr_into_master = shorthands.is_github_pr_into('master') is_release_build = is_ci and (is_branch_master or is_pr_into_master) # Get the build type string. @@ -33,6 +31,16 @@ # Read the correct .env file. dot = dotenv.DotEnv(project_dir, dotenv_type) + +# Find env variables based on only the pioenv and sysenv. +def get_pio_firmware_vars() -> dict[str, str | int | bool]: + vars = {} + vars['OPENSHOCK_FW_BOARD'] = pio.get_string('PIOENV') + vars['OPENSHOCK_FW_CHIP'] = pio.get_string('BOARD_MCU') + vars['OPENSHOCK_FW_MODE'] = pio_build_type + return vars + + ####################################################### # UPDATING BUILD PARAMETERS # ####################################################### @@ -54,7 +62,7 @@ def parse_pio_build_flags(raw_flags: list[str]) -> tuple[dict[str, str | int | b flag_dict[flag] = True else: leftover_flags.append(flag) - return (flag_dict, []) + return (flag_dict, leftover_flags) # Serialize CPP Defines. @@ -75,13 +83,36 @@ def serialize_cpp_defines(raw_defines: dict[str, str | int | bool]) -> dict[str, return result_defines +# Copy key/value pairs from "src" into "dest" only if those keys don't exist in "dest" yet. +def merge_missing_keys(dest: dict[str, str | int | bool], src: Mapping[str, str | int | bool]): + for k, v in src.items(): + if k not in dest: + dest[k] = v + + +# Print a dictionary for debugging purposes. +def print_dump(name: str, map: Mapping[str, str | int | bool]) -> None: + print('%s:' % name) + for k, v in map.items(): + print(' %s = %s' % (k, v)) + + # Fetch the current build flags and group them into (CPP Defines, Other Flags). raw_build_flags = pio.get_string_array('BUILD_FLAGS', []) (cpp_defines, remaining_build_flags) = parse_pio_build_flags(raw_build_flags) # Gets all the environment variables prefixed with 'OPENSHOCK_' and add them as CPP Defines. -openshock_vars = dot.get_all_prefixed('OPENSHOCK_') -cpp_defines.update(openshock_vars) +sys_openshock_vars = sysenv.get_all_prefixed('OPENSHOCK_') +pio_openshock_vars = get_pio_firmware_vars() +dot_openshock_vars = dot.get_all_prefixed('OPENSHOCK_') + +print_dump('Sys OpenShock vars', sys_openshock_vars) +print_dump('PIO OpenShock vars', pio_openshock_vars) +print_dump('Dotenv OpenShock vars', dot_openshock_vars) + +merge_missing_keys(cpp_defines, sys_openshock_vars) +merge_missing_keys(cpp_defines, pio_openshock_vars) +merge_missing_keys(cpp_defines, dot_openshock_vars) # Gets the log level from environment variables. # TODO: Delete get_loglevel and use... something more generic. @@ -89,6 +120,10 @@ def serialize_cpp_defines(raw_defines: dict[str, str | int | bool]) -> dict[str, if log_level_int is None: raise ValueError('LOG_LEVEL must be set in environment variables.') cpp_defines['CORE_DEBUG_LEVEL'] = log_level_int + +# Serialize and inject CPP Defines. +print_dump('CPP Defines', cpp_defines) + cpp_defines = serialize_cpp_defines(cpp_defines) print('Build type: ' + pio_build_type) @@ -98,3 +133,5 @@ def serialize_cpp_defines(raw_defines: dict[str, str | int | bool]) -> dict[str, env['BUILD_TYPE'] = pio_build_type env['BUILD_FLAGS'] = remaining_build_flags env.Append(CPPDEFINES=list(cpp_defines.items())) + +print(env.Dump()) diff --git a/scripts/utils/dotenv.py b/scripts/utils/dotenv.py index b611f362..771b1609 100644 --- a/scripts/utils/dotenv.py +++ b/scripts/utils/dotenv.py @@ -1,5 +1,6 @@ import os from pathlib import Path +from typing import Mapping LOGLEVEL_MAP = { 'none': (0, 'LOG_NONE'), @@ -57,24 +58,18 @@ def __init__(self, path: str | Path, environment: str): if env_file.exists(): self.__read_dotenv(env_file) - def get_str(self, key: str, dotenv: bool = True): - return self.dotenv_vars.get(key, os.environ.get(key)) + def get_string(self, key: str): + return self.dotenv_vars.get(key) - def get_all_prefixed(self, prefix: str, dotenv: bool = True): + def get_all_prefixed(self, prefix: str) -> Mapping[str, str]: result: dict[str, str] = {} - for key, value in os.environ.items(): + for key, value in self.dotenv_vars.items(): if key.startswith(prefix): result[key] = value - - if dotenv: - for key, value in self.dotenv_vars.items(): - if key.startswith(prefix): - result[key] = value - return result - def get_loglevel(self, key: str, dotenv: bool = True) -> int | None: - value = self.get_str(key, dotenv) + def get_loglevel(self, key: str) -> int | None: + value = self.get_string(key) if value == None: return None diff --git a/scripts/utils/shorthands.py b/scripts/utils/shorthands.py index 03e656c9..a5da4260 100644 --- a/scripts/utils/shorthands.py +++ b/scripts/utils/shorthands.py @@ -5,26 +5,31 @@ # The short ref name of the branch or tag that triggered the workflow run. # This value matches the branch or tag name shown on GitHub. For example, `feature-branch-1`. def get_github_ref_name(): - return sysenv.get_string('GITHUB_REF_NAME') + return sysenv.get_string('GITHUB_REF_NAME', '') # The name of the base ref or target branch of the pull request in a workflow run. # This is only set when the event that triggers a workflow run is either `pull_request` # or `pull_request_target`. For example, `main`. def get_github_base_ref(): - return sysenv.get_string('GITHUB_BASE_REF') + return sysenv.get_string('GITHUB_BASE_REF', '') # The name of the event that triggered the workflow. For example, `workflow_dispatch`. def get_github_event_name(): - return sysenv.get_string('GITHUB_EVENT_NAME') + return sysenv.get_string('GITHUB_EVENT_NAME', '') # Whether the current environment is a Github CI environment. def is_github_ci(): - return sysenv.get_bool('CI') and sysenv.get_bool('GITHUB_ACTIONS') + return sysenv.get_bool('CI', False) and sysenv.get_bool('GITHUB_ACTIONS', False) # Whether the current environment, assuming is_github_ci() == True, is caused by a pull request event. def is_github_pr(): return get_github_event_name() == 'pull_request' + + +# Checks whether the event is a pull_request with the specified branch as base_ref. +def is_github_pr_into(branch: str) -> bool: + return is_github_pr() and get_github_base_ref() == branch diff --git a/scripts/utils/sysenv.py b/scripts/utils/sysenv.py index e00edee0..c1ee085f 100644 --- a/scripts/utils/sysenv.py +++ b/scripts/utils/sysenv.py @@ -18,3 +18,11 @@ def get_string(key: str, default: str | None = None) -> str: if default != None: return default raise ValueError('Failed to get environment string: %s' % key) from ex + + +def get_all_prefixed(prefix: str) -> dict[str, str]: + result = {} + for key, value in os.environ.items(): + if key.startswith(prefix): + result[key] = value + return result diff --git a/src/SerialInputHandler.cpp b/src/SerialInputHandler.cpp index 699d6e73..7b347540 100644 --- a/src/SerialInputHandler.cpp +++ b/src/SerialInputHandler.cpp @@ -16,6 +16,7 @@ int findChar(const char* buffer, std::size_t bufferSize, char c) { return -1; } + int findLineEnd(const char* buffer, int bufferSize) { for (int i = 0; i < bufferSize; i++) { if (buffer[i] == '\r' || buffer[i] == '\n' || buffer[i] == '\0') { @@ -25,6 +26,7 @@ int findLineEnd(const char* buffer, int bufferSize) { return -1; } + int findLineStart(const char* buffer, int bufferSize, int lineEnd) { if (lineEnd < 0) return -1; @@ -37,16 +39,32 @@ int findLineStart(const char* buffer, int bufferSize, int lineEnd) { return -1; } -void handleSerialCommand(char* command, std::size_t commandLength) { +void handleZeroArgCommand(char* command, std::size_t commandLength) { if (strcmp(command, "restart") == 0) { Serial.println("Restarting ESP..."); ESP.restart(); return; } + if (strcmp(command, "help") == 0) { + SerialInputHandler::PrintWelcomeHeader(); + Serial.println("help print this menu"); + Serial.println("version print version information"); + Serial.println("restart restart the board"); + Serial.println("rmtpin set radio pin to \n"); + return; + } + + if (strcmp(command, "version") == 0) { + Serial.print("\n"); + SerialInputHandler::PrintVersionInfo(); + return; + } + Serial.println("SYS|Error|Command not found"); } -void handleSerialCommand(char* command, std::size_t commandLength, char* arg, std::size_t argLength) { + +void handleSingleArgCommand(char* command, std::size_t commandLength, char* arg, std::size_t argLength) { if (strcmp(command, "authtoken") == 0) { OpenShock::FileUtils::TryWriteFile("/authToken", arg, argLength); return; @@ -64,6 +82,7 @@ void handleSerialCommand(char* command, std::size_t commandLength, char* arg, st Serial.println("SYS|Error|Command not found"); } + void processSerialLine(char* data, std::size_t length) { int delimiter = findChar(data, length, ' '); if (delimiter == 0) { @@ -73,12 +92,12 @@ void processSerialLine(char* data, std::size_t length) { // Handle arg-less commands if (delimiter <= 0) { - handleSerialCommand(data, length); + handleZeroArgCommand(data, length); return; } else { length = delimiter; data[delimiter] = '\0'; - handleSerialCommand(data, length, data + delimiter + 1, length - delimiter - 1); + handleSingleArgCommand(data, length, data + delimiter + 1, length - delimiter - 1); } } @@ -125,3 +144,19 @@ void SerialInputHandler::Update() { } } } + +void SerialInputHandler::PrintWelcomeHeader() { + Serial.println("\n============== OPENSHOCK =============="); + Serial.println(" Contribute @ github.com/OpenShock"); + Serial.println(" Discuss @ discord.gg/AHcCbXbEcF"); + Serial.println(" Type 'help' for available commands"); + Serial.println("=======================================\n"); +} + +void SerialInputHandler::PrintVersionInfo() { + Serial.println(" Version: " OPENSHOCK_FW_VERSION); + Serial.println(" Build: " OPENSHOCK_FW_MODE); + Serial.println(" Commit: " OPENSHOCK_FW_COMMIT); + Serial.println(" Board: " OPENSHOCK_FW_BOARD); + Serial.println(" Chip: " OPENSHOCK_FW_CHIP); +} diff --git a/src/main.cpp b/src/main.cpp index 84395428..8317e94f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -24,7 +24,8 @@ void setup() { ESP.restart(); } - ESP_LOGI(TAG, "==== OpenShock v%s ====", OpenShock::Constants::Version); + OpenShock::SerialInputHandler::PrintWelcomeHeader(); + OpenShock::SerialInputHandler::PrintVersionInfo(); OpenShock::CommandHandler::Init();