diff --git a/.github/workflows/core_build.yml b/.github/workflows/core_build.yml deleted file mode 100644 index 2b1404b02b..0000000000 --- a/.github/workflows/core_build.yml +++ /dev/null @@ -1,121 +0,0 @@ -name: Core Build - -on: - push: - branches: [ "master" ] - pull_request: - branches: [ "master" ] - workflow_dispatch: # manual trigger - -jobs: - setup: - runs-on: ubuntu-latest - outputs: - vortex_version_major: ${{ steps.set_version.outputs.vortex_version_major }} - vortex_version_minor: ${{ steps.set_version.outputs.vortex_version_minor }} - vortex_build_number: ${{ steps.set_version.outputs.vortex_build_number }} - vortex_version_number: ${{ steps.set_version.outputs.vortex_version_number }} - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 # Fetches all history for all branches and tags - - name: Determine Version and Build Number - id: set_version - run: | - # Fetch all tags - git fetch --depth=1 origin +refs/tags/*:refs/tags/* - # Get the latest tag that matches the branch suffix - LATEST_TAG=$(git tag --list | grep -E "^[[:digit:]]+\.[[:digit:]]+\$" | sort -V | tail -n1) - if [ -z "$LATEST_TAG" ]; then - echo "No matching tags found. Setting default version." - VERSION_MAJOR="0" - VERSION_MINOR="1" - BUILD_NUMBER="0" - else - echo "Found latest tag: $LATEST_TAG" - VERSION_MAJOR=$(echo $LATEST_TAG | cut -d. -f1) - VERSION_MINOR=$(echo $LATEST_TAG | cut -d. -f2) - BUILD_NUMBER=$(git rev-list --count $LATEST_TAG..HEAD) - fi - FULL_VERSION="$VERSION_MAJOR.$VERSION_MINOR.$BUILD_NUMBER" - echo "vortex_version_major=$VERSION_MAJOR" >> $GITHUB_OUTPUT - echo "vortex_version_minor=$VERSION_MINOR" >> $GITHUB_OUTPUT - echo "vortex_build_number=$BUILD_NUMBER" >> $GITHUB_OUTPUT - echo "vortex_version_number=$FULL_VERSION" >> $GITHUB_OUTPUT - echo "Version Number: $FULL_VERSION" - - test: - needs: setup - runs-on: ubuntu-latest - steps: - - name: Checkout current repository - uses: actions/checkout@v3 - - name: Update Package Lists - run: sudo apt-get update - - name: Install Dependencies - run: sudo apt-get install valgrind g++ make --fix-missing - - name: Build - run: | - export VORTEX_VERSION_MAJOR=${{ needs.setup.outputs.vortex_version_major }} - export VORTEX_VERSION_MINOR=${{ needs.setup.outputs.vortex_version_minor }} - export VORTEX_BUILD_NUMBER=${{ needs.setup.outputs.vortex_build_number }} - export VORTEX_VERSION_NUMBER=${{ needs.setup.outputs.vortex_version_number }} - make -j - working-directory: VortexEngine - - name: Set execute permissions for test script - run: chmod +x ./runtests.sh - working-directory: VortexEngine/tests - - name: Run general tests - run: ./runtests.sh --general - working-directory: VortexEngine/tests - - wasm: - needs: [setup, test] - runs-on: ubuntu-latest - steps: - - name: Checkout current repository - uses: actions/checkout@v3 - - name: Update Package Lists - run: sudo apt-get update - - name: Install Emscripten - run: | - sudo apt install -y cmake python3 - git clone https://github.com/emscripten-core/emsdk.git - cd emsdk - ./emsdk install latest - ./emsdk activate latest - working-directory: VortexEngine/VortexLib - - name: Build Webassembly - run: | - source ./emsdk/emsdk_env.sh - export VORTEX_VERSION_MAJOR=${{ needs.setup.outputs.vortex_version_major }} - export VORTEX_VERSION_MINOR=${{ needs.setup.outputs.vortex_version_minor }} - export VORTEX_BUILD_NUMBER=${{ needs.setup.outputs.vortex_build_number }} - export VORTEX_VERSION_NUMBER=${{ needs.setup.outputs.vortex_version_number }} - make -j wasm - working-directory: VortexEngine/VortexLib - - docs: - needs: [setup, test, wasm] - runs-on: ubuntu-latest - if: github.ref == 'refs/heads/master' - steps: - - name: Checkout current repository - uses: actions/checkout@v3 - - name: Update Package Lists - run: sudo apt-get update - - name: Install Dependencies - run: sudo apt-get install doxygen graphviz texlive --fix-missing - - name: Checkout doxygen-awesome - run: git clone https://github.com/jothepro/doxygen-awesome-css.git doxygen-awesome-css - - name: Generate Documentation - run: | - mkdir -p docs/core - doxygen Doxyfile - echo "Listing contents of docs/core:" - ls -R docs/core || echo "No files found in docs/core" - - name: Upload Doxygen Documentation as Artifact - uses: actions/upload-artifact@v3 - with: - name: doxygen-docs-core - path: docs/core diff --git a/.github/workflows/handle_build.yml b/.github/workflows/handle_build.yml new file mode 100644 index 0000000000..0eff047812 --- /dev/null +++ b/.github/workflows/handle_build.yml @@ -0,0 +1,183 @@ +name: Handle Build + +on: + push: + branches: [ "handle" ] + pull_request: + branches: [ "handle" ] + workflow_dispatch: # manual trigger + +jobs: + setup: + runs-on: ubuntu-latest + outputs: + vortex_version_major: ${{ steps.set_version.outputs.vortex_version_major }} + vortex_version_minor: ${{ steps.set_version.outputs.vortex_version_minor }} + vortex_build_number: ${{ steps.set_version.outputs.vortex_build_number }} + vortex_version_number: ${{ steps.set_version.outputs.vortex_version_number }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # Fetches all history for all branches and tags + - name: Determine Version and Build Number + id: set_version + run: | + BRANCH_SUFFIX="h" + # Fetch all tags + git fetch --depth=1 origin +refs/tags/*:refs/tags/* + # Get the latest tag that matches the branch suffix + LATEST_TAG=$(git tag --list "*${BRANCH_SUFFIX}" | sort -V | tail -n1) + if [ -z "$LATEST_TAG" ]; then + echo "No matching tags found. Setting default version." + VERSION_MAJOR="0" + VERSION_MINOR="1" + BUILD_NUMBER="0" + else + echo "Found latest tag: $LATEST_TAG" + VERSION_NUMBER=$(echo $LATEST_TAG | sed "s/${BRANCH_SUFFIX}//g") + VERSION_MAJOR=$(echo $VERSION_NUMBER | cut -d. -f1) + VERSION_MINOR=$(echo $VERSION_NUMBER | cut -d. -f2) + BUILD_NUMBER=$(git rev-list --count $LATEST_TAG..HEAD) + fi + FULL_VERSION="$VERSION_MAJOR.$VERSION_MINOR.$BUILD_NUMBER" + echo "vortex_version_major=$VERSION_MAJOR" >> $GITHUB_OUTPUT + echo "vortex_version_minor=$VERSION_MINOR" >> $GITHUB_OUTPUT + echo "vortex_build_number=$BUILD_NUMBER" >> $GITHUB_OUTPUT + echo "vortex_version_number=$FULL_VERSION" >> $GITHUB_OUTPUT + echo "Version Number: $FULL_VERSION" + + test: + needs: setup + runs-on: ubuntu-latest + steps: + - name: Checkout current repository + uses: actions/checkout@v4 + - name: Update Package Lists + run: sudo apt-get update + - name: Install Dependencies + run: sudo apt-get install valgrind g++ make --fix-missing + - name: Build + run: | + export VORTEX_VERSION_MAJOR=${{ needs.setup.outputs.vortex_version_major }} + export VORTEX_VERSION_MINOR=${{ needs.setup.outputs.vortex_version_minor }} + export VORTEX_BUILD_NUMBER=${{ needs.setup.outputs.vortex_build_number }} + export VORTEX_VERSION_NUMBER=${{ needs.setup.outputs.vortex_version_number }} + make -j + working-directory: VortexEngine + - name: Set execute permissions for test script + run: chmod +x ./runtests.sh + working-directory: VortexEngine/tests + - name: Run general tests + run: ./runtests.sh --general + working-directory: VortexEngine/tests + + embedded: + needs: [setup, test] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.x' + - name: Install Dependencies + run: make install + - name: Build Binary + run: | + export VORTEX_VERSION_MAJOR=${{ needs.setup.outputs.vortex_version_major }} + export VORTEX_VERSION_MINOR=${{ needs.setup.outputs.vortex_version_minor }} + export VORTEX_BUILD_NUMBER=${{ needs.setup.outputs.vortex_build_number }} + export VORTEX_VERSION_NUMBER=${{ needs.setup.outputs.vortex_version_number }} + make build + - name: Archive production artifacts + uses: actions/upload-artifact@v4 + with: + name: embedded firmware + path: | + build/VortexEngine.ino.bin + build/VortexEngine.ino.elf + build/VortexEngine.ino.map + build/VortexEngine.ino.hex + build/VortexEngine.ino.uf2 + - name: Archive production artifacts for deployment + uses: actions/upload-artifact@v4 + with: + name: firmware-artifact + path: build/VortexEngine.ino.uf2 + + wasm: + needs: [setup, test, embedded] + runs-on: ubuntu-latest + steps: + - name: Checkout current repository + uses: actions/checkout@v4 + - name: Update Package Lists + run: sudo apt-get update + - name: Install Emscripten + run: | + sudo apt install -y cmake python3 + git clone https://github.com/emscripten-core/emsdk.git + cd emsdk + ./emsdk install latest + ./emsdk activate latest + working-directory: VortexEngine/VortexLib + - name: Build Webassembly + run: | + source ./emsdk/emsdk_env.sh + export VORTEX_VERSION_MAJOR=${{ needs.setup.outputs.vortex_version_major }} + export VORTEX_VERSION_MINOR=${{ needs.setup.outputs.vortex_version_minor }} + export VORTEX_BUILD_NUMBER=${{ needs.setup.outputs.vortex_build_number }} + export VORTEX_VERSION_NUMBER=${{ needs.setup.outputs.vortex_version_number }} + make -j wasm + working-directory: VortexEngine/VortexLib + + docs: + needs: [setup, test, embedded, wasm] + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/handle' + steps: + - name: Checkout current repository + uses: actions/checkout@v4 + - name: Update Package Lists + run: sudo apt-get update + - name: Install Dependencies + run: sudo apt-get install doxygen graphviz texlive --fix-missing + - name: Checkout doxygen-awesome + run: git clone https://github.com/jothepro/doxygen-awesome-css.git doxygen-awesome-css + - name: Generate Documentation + run: | + mkdir -p docs/handle + doxygen Doxyfile + echo "Listing contents of docs/handle:" + ls -R docs/handle || echo "No files found in docs/handle" + - name: Upload Doxygen Documentation as Artifact + uses: actions/upload-artifact@v3 + with: + name: doxygen-docs-handle + path: docs/handle + + deploy: + needs: [setup, test, embedded, wasm, docs] + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/handle' + steps: + - uses: actions/checkout@v4 + - uses: actions/download-artifact@v4 + with: + name: firmware-artifact + path: build + - name: Rename and Deploy Firmware + run: | + DEVICE_TYPE="handle" + VERSIONED_FILENAME="VortexEngine-${DEVICE_TYPE}-${{ needs.setup.outputs.vortex_version_number }}.uf2" + mv build/VortexEngine.ino.uf2 build/$VERSIONED_FILENAME + echo "Version is ${{ needs.setup.outputs.vortex_version_number }}" + echo "Filename is is $VERSIONED_FILENAME" + curl -X POST \ + -F "file=@build/$VERSIONED_FILENAME" \ + -F "device=$DEVICE_TYPE" \ + -F "version=${{ needs.setup.outputs.vortex_version_number }}" \ + -F "category=firmware" \ + -F "clientApiKey=${{ secrets.VORTEX_COMMUNITY_API_KEY }}" \ + https://vortex.community/firmware/upload + diff --git a/Doxyfile b/Doxyfile index 9db6ecc800..0a6fc800b5 100644 --- a/Doxyfile +++ b/Doxyfile @@ -32,7 +32,7 @@ DOXYFILE_ENCODING = UTF-8 # title of most generated pages and in a few other places. # The default value is: My Project. -PROJECT_NAME = "Vortex Engine" +PROJECT_NAME = "Vortex Handle" # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version @@ -58,7 +58,7 @@ PROJECT_LOGO = # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. -OUTPUT_DIRECTORY = docs/core +OUTPUT_DIRECTORY = docs/handle # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000..c9a16662ae --- /dev/null +++ b/Makefile @@ -0,0 +1,70 @@ +.PHONY: all install build upload clean compute_version + +ARDUINO_CLI = ./bin/arduino-cli --verbose +BOARD = adafruit:samd:adafruit_trinket_m0 +PORT = /dev/ttyACM0 +PROJECT_NAME = VortexEngine/VortexEngine.ino +BUILD_PATH = build +CONFIG_FILE = $(HOME)/.arduino15/arduino-cli.yaml + +# The branch/tag suffix for this device +BRANCH_SUFFIX=h + +DEFINES=\ + -D VORTEX_VERSION_MAJOR=$(VORTEX_VERSION_MAJOR) \ + -D VORTEX_VERSION_MINOR=$(VORTEX_VERSION_MINOR) \ + -D VORTEX_BUILD_NUMBER=$(VORTEX_BUILD_NUMBER) \ + -D VORTEX_VERSION_NUMBER=$(VORTEX_VERSION_NUMBER) + +# Default target +all: build + +update-index: + $(ARDUINO_CLI) core update-index + +install: + sudo apt-get update + sudo apt-get install -y build-essential + mkdir -p $(HOME)/.arduino15 + if ! command -v $(ARDUINO_CLI) &> /dev/null ; then \ + curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | sudo sh ; \ + fi + echo 'board_manager: \n additional_urls: \n - https://adafruit.github.io/arduino-board-index/package_adafruit_index.json' | sudo tee $(CONFIG_FILE) + $(ARDUINO_CLI) core update-index --config-file $(CONFIG_FILE) + if ! $(ARDUINO_CLI) core list --config-file $(CONFIG_FILE) | grep -q '$(BOARD)' ; then \ + $(ARDUINO_CLI) core install adafruit:samd --config-file $(CONFIG_FILE) ; \ + fi + wget https://raw.githubusercontent.com/microsoft/uf2/master/utils/uf2conv.py + wget https://raw.githubusercontent.com/microsoft/uf2/master/utils/uf2families.json + chmod +x uf2conv.py uf2families.json + chmod +x rewrite_trinket_source.sh + ./rewrite_trinket_source.sh + +build: compute_version + $(ARDUINO_CLI) compile --fqbn $(BOARD) $(PROJECT_NAME) \ + --config-file $(CONFIG_FILE) \ + --build-path $(BUILD_PATH) \ + --build-property compiler.cpp.extra_flags="$(DEFINES)" \ + --build-property compiler.c.extra_flags="$(DEFINES)" + python3 uf2conv.py -c -b 0x2000 build/VortexEngine.ino.bin -o build/VortexEngine.ino.uf2 + @echo "== Success building Handle v$(VORTEX_VERSION_NUMBER) ==" + +upload: + $(ARDUINO_CLI) upload -p $(PORT) --fqbn $(BOARD) $(PROJECT_NAME) --config-file $(CONFIG_FILE) + +core-list: + $(ARDUINO_CLI) core list + +clean: + rm -rf $(BUILD_PATH) + +# calculate the version number of the build +compute_version: + $(eval LATEST_TAG ?= $(shell git fetch --depth=1 origin +refs/tags/*:refs/tags/* &> /dev/null && git tag --list "*$(BRANCH_SUFFIX)" | sort -V | tail -n1)) + $(eval VORTEX_VERSION_MAJOR ?= $(shell echo $(LATEST_TAG) | cut -d. -f1)) + $(eval VORTEX_VERSION_MINOR ?= $(shell echo $(LATEST_TAG) | sed 's/$(BRANCH_SUFFIX)$$//' | cut -d. -f2)) + $(eval VORTEX_BUILD_NUMBER ?= $(shell git rev-list --count $(LATEST_TAG)..HEAD)) + $(eval VORTEX_VERSION_MAJOR := $(if $(VORTEX_VERSION_MAJOR),$(VORTEX_VERSION_MAJOR),0)) + $(eval VORTEX_VERSION_MINOR := $(if $(VORTEX_VERSION_MINOR),$(VORTEX_VERSION_MINOR),1)) + $(eval VORTEX_BUILD_NUMBER := $(if $(VORTEX_BUILD_NUMBER),$(VORTEX_BUILD_NUMBER),0)) + $(eval VORTEX_VERSION_NUMBER := $(VORTEX_VERSION_MAJOR).$(VORTEX_VERSION_MINOR).$(VORTEX_BUILD_NUMBER)) diff --git a/VortexEngine/VortexCLI/Makefile b/VortexEngine/VortexCLI/Makefile index b4b2f2b541..71af9945fb 100644 --- a/VortexEngine/VortexCLI/Makefile +++ b/VortexEngine/VortexCLI/Makefile @@ -19,6 +19,9 @@ RANLIB=ranlib CFLAGS=-O2 -g -Wall +# The branch/tag suffix for this device +BRANCH_SUFFIX=h + # compiler defines DEFINES=\ -D VORTEX_LIB \ @@ -135,9 +138,9 @@ clean: # calculate the version number of the build compute_version: - $(eval LATEST_TAG ?= $(shell git fetch --depth=1 origin +refs/tags/*:refs/tags/* &> /dev/null && git tag --list | grep --invert-match '[a-zA-Z]' | sort -V | tail -n1)) + $(eval LATEST_TAG ?= $(shell git fetch --depth=1 origin +refs/tags/*:refs/tags/* &> /dev/null && git tag --list "*$(BRANCH_SUFFIX)" | sort -V | tail -n1)) $(eval VORTEX_VERSION_MAJOR ?= $(shell echo $(LATEST_TAG) | cut -d. -f1)) - $(eval VORTEX_VERSION_MINOR ?= $(shell echo $(LATEST_TAG) | cut -d. -f2)) + $(eval VORTEX_VERSION_MINOR ?= $(shell echo $(LATEST_TAG) | sed 's/$(BRANCH_SUFFIX)$$//' | cut -d. -f2)) $(eval VORTEX_BUILD_NUMBER ?= $(shell git rev-list --count $(LATEST_TAG)..HEAD)) $(eval VORTEX_VERSION_MAJOR := $(if $(VORTEX_VERSION_MAJOR),$(VORTEX_VERSION_MAJOR),0)) $(eval VORTEX_VERSION_MINOR := $(if $(VORTEX_VERSION_MINOR),$(VORTEX_VERSION_MINOR),1)) diff --git a/VortexEngine/VortexEngine.ino b/VortexEngine/VortexEngine.ino new file mode 100644 index 0000000000..014b37a8af --- /dev/null +++ b/VortexEngine/VortexEngine.ino @@ -0,0 +1,15 @@ +#include + +#include "src/VortexEngine.h" + +void setup() +{ + if (!VortexEngine::init()) { + // uhoh + } +} + +void loop() +{ + VortexEngine::tick(); +} diff --git a/VortexEngine/VortexLib/Makefile b/VortexEngine/VortexLib/Makefile index 98900f08b8..b3923ddeee 100644 --- a/VortexEngine/VortexLib/Makefile +++ b/VortexEngine/VortexLib/Makefile @@ -24,6 +24,9 @@ ifndef WASM CFLAGS += -g endif +# The branch/tag suffix for this device +BRANCH_SUFFIX=h + # compiler defines DEFINES=\ -D VORTEX_LIB \ @@ -146,9 +149,9 @@ clean: # calculate the version number of the build compute_version: - $(eval LATEST_TAG ?= $(shell git fetch --depth=1 origin +refs/tags/*:refs/tags/* &> /dev/null && git tag --list | grep --invert-match '[a-zA-Z]' | sort -V | tail -n1)) + $(eval LATEST_TAG ?= $(shell git fetch --depth=1 origin +refs/tags/*:refs/tags/* &> /dev/null && git tag --list "*$(BRANCH_SUFFIX)" | sort -V | tail -n1)) $(eval VORTEX_VERSION_MAJOR ?= $(shell echo $(LATEST_TAG) | cut -d. -f1)) - $(eval VORTEX_VERSION_MINOR ?= $(shell echo $(LATEST_TAG) | cut -d. -f2)) + $(eval VORTEX_VERSION_MINOR ?= $(shell echo $(LATEST_TAG) | sed 's/$(BRANCH_SUFFIX)$$//' | cut -d. -f2)) $(eval VORTEX_BUILD_NUMBER ?= $(shell git rev-list --count $(LATEST_TAG)..HEAD)) $(eval VORTEX_VERSION_MAJOR := $(if $(VORTEX_VERSION_MAJOR),$(VORTEX_VERSION_MAJOR),0)) $(eval VORTEX_VERSION_MINOR := $(if $(VORTEX_VERSION_MINOR),$(VORTEX_VERSION_MINOR),1)) diff --git a/VortexEngine/VortexLib/VortexLib.cpp b/VortexEngine/VortexLib/VortexLib.cpp index 09e5e61c77..71c20f972f 100644 --- a/VortexEngine/VortexLib/VortexLib.cpp +++ b/VortexEngine/VortexLib/VortexLib.cpp @@ -212,13 +212,6 @@ EMSCRIPTEN_BINDINGS(Vortex) { .value("LED_0", LedPos::LED_0) .value("LED_1", LedPos::LED_1) .value("LED_2", LedPos::LED_2) - .value("LED_3", LedPos::LED_3) - .value("LED_4", LedPos::LED_4) - .value("LED_5", LedPos::LED_5) - .value("LED_6", LedPos::LED_6) - .value("LED_7", LedPos::LED_7) - .value("LED_8", LedPos::LED_8) - .value("LED_9", LedPos::LED_9) .value("LED_COUNT", LedPos::LED_COUNT) .value("LED_LAST", LedPos::LED_LAST) .value("LED_ALL", LedPos::LED_ALL) diff --git a/VortexEngine/appmain.cpp b/VortexEngine/appmain.cpp deleted file mode 100644 index a80363ed5b..0000000000 --- a/VortexEngine/appmain.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "src/VortexEngine.h" - -int main() -{ - VortexEngine::init(); - for (;;) { - VortexEngine::tick(); - } - return 0; -} diff --git a/VortexEngine/src/Buttons/Button.cpp b/VortexEngine/src/Buttons/Button.cpp index e565dbbb5c..51466ff02d 100644 --- a/VortexEngine/src/Buttons/Button.cpp +++ b/VortexEngine/src/Buttons/Button.cpp @@ -8,6 +8,10 @@ #include "VortexLib.h" #endif +#ifdef VORTEX_EMBEDDED +#include +#endif + Button::Button() : m_pinNum(0), m_pressTime(0), @@ -46,12 +50,19 @@ bool Button::init(uint8_t pin) m_longClick = false; m_pinNum = pin; +#ifdef VORTEX_EMBEDDED + pinMode(m_pinNum, INPUT_PULLUP); +#endif return true; } bool Button::check() { +#ifdef VORTEX_EMBEDDED + return (digitalRead(m_pinNum) == 0); +#else return (Vortex::vcallbacks()->checkPinHook(m_pinNum) == 0); +#endif } void Button::update() diff --git a/VortexEngine/src/Leds/LedTypes.h b/VortexEngine/src/Leds/LedTypes.h index 73bcce80f3..a7a9b1b6b4 100644 --- a/VortexEngine/src/Leds/LedTypes.h +++ b/VortexEngine/src/Leds/LedTypes.h @@ -16,13 +16,6 @@ enum LedPos : uint8_t LED_0 = LED_FIRST, LED_1, LED_2, - LED_3, - LED_4, - LED_5, - LED_6, - LED_7, - LED_8, - LED_9, // the number of entries above LED_COUNT, @@ -63,6 +56,10 @@ enum LedPos : uint8_t // LED_ODDS = (LED_COUNT + 3), }; +// some helpers for microlight code +#define LED_TIP LED_0 +#define LED_TOP LED_1 + enum Pair : uint8_t { PAIR_FIRST = 0, @@ -70,16 +67,26 @@ enum Pair : uint8_t // one pair for each pair of leds, adjust this to be 2x the LED_COUNT PAIR_0 = PAIR_FIRST, PAIR_1, - PAIR_2, - PAIR_3, - PAIR_4, PAIR_COUNT, PAIR_LAST = (PAIR_COUNT - 1), }; // Compile-time check on the number of pairs and leds -static_assert(LED_COUNT == (PAIR_COUNT * 2), "Incorrect number of Pairs for Leds! Adjust the Led enum or Pair enum to match"); +// The handle only has 3 leds so the pair check is a little offset +static_assert(LED_COUNT == ((PAIR_COUNT * 2) - 1), "Incorrect number of Pairs for Leds! Adjust the Led enum or Pair enum to match"); + +// backwards compatibility for multi led patterns +#define LED_3 LED_0 +#define LED_4 LED_1 +#define LED_5 LED_2 +#define LED_6 LED_0 +#define LED_7 LED_1 +#define LED_8 LED_2 +#define LED_9 LED_0 +#define PAIR_2 PAIR_0 +#define PAIR_3 PAIR_1 +#define PAIR_4 PAIR_0 // check if an led is even or odd #define isEven(pos) ((pos % 2) == 0) @@ -159,6 +166,9 @@ inline LedPos ledmapGetNextLed(LedMap map, LedPos pos) #define MAP_PAIR_EVEN_EVENS (MAP_PAIR_EVEN(PAIR_3) | MAP_PAIR_EVEN(PAIR_1)) #define MAP_PAIR_EVEN_ODDS (MAP_PAIR_ODD(PAIR_3) | MAP_PAIR_ODD(PAIR_1)) +// ledmap of the side leds +#define MAP_SIDES (MAP_LED(LED_0) | MAP_LED(LED_2)) + // set a single led inline void ledmapSetLed(LedMap &map, LedPos pos) { diff --git a/VortexEngine/src/Leds/Leds.cpp b/VortexEngine/src/Leds/Leds.cpp index 0d1dd4c400..ab558e76f0 100644 --- a/VortexEngine/src/Leds/Leds.cpp +++ b/VortexEngine/src/Leds/Leds.cpp @@ -12,6 +12,49 @@ #include "../../VortexLib/VortexLib.h" #endif +#ifdef VORTEX_EMBEDDED +#include +#define ONBOARD_LED_SCK 8 +#define ONBOARD_LED_MOSI 7 +static void transfer(uint8_t byte) +{ + uint8_t startbit = 0x80; + bool lastmosi = !(byte & startbit); + for (uint8_t b = startbit; b != 0; b = b >> 1) { + delayMicroseconds(4); + bool towrite = byte & b; + if (lastmosi != towrite) { + digitalWrite(ONBOARD_LED_MOSI, towrite); + lastmosi = towrite; + } + digitalWrite(ONBOARD_LED_SCK, HIGH); + delayMicroseconds(4); + digitalWrite(ONBOARD_LED_SCK, LOW); + } +} +static void turnOffOnboardLED() +{ + // spi device begin + pinMode(ONBOARD_LED_SCK, OUTPUT); + digitalWrite(ONBOARD_LED_SCK, LOW); + pinMode(ONBOARD_LED_MOSI, OUTPUT); + digitalWrite(ONBOARD_LED_MOSI, HIGH); + + // Begin transaction, setting SPI frequency + static const SPISettings mySPISettings(8000000, MSBFIRST, SPI_MODE0); + SPI.beginTransaction(mySPISettings); + for (uint8_t i = 0; i < 4; i++) { + transfer(0x00); // begin frame + } + transfer(0xFF); // Pixel start + for (uint8_t i = 0; i < 3; i++) { + transfer(0x00); // R,G,B + } + transfer(0xFF); // end frame + SPI.endTransaction(); +} +#endif + // global brightness uint8_t Leds::m_brightness = DEFAULT_BRIGHTNESS; // array of led color values @@ -19,6 +62,10 @@ RGBColor Leds::m_ledColors[LED_COUNT] = { RGB_OFF }; bool Leds::init() { +#ifdef VORTEX_EMBEDDED + turnOffOnboardLED(); + SPI.begin(); +#endif #ifdef VORTEX_LIB Vortex::vcallbacks()->ledsInit(m_ledColors, LED_COUNT); #endif @@ -27,6 +74,9 @@ bool Leds::init() void Leds::cleanup() { +#ifdef VORTEX_EMBEDDED + SPI.end(); +#endif for (uint8_t i = 0; i < LED_COUNT; ++i) { m_ledColors[i].clear(); } @@ -260,6 +310,27 @@ void Leds::holdAll(RGBColor col) void Leds::update() { +#ifdef VORTEX_EMBEDDED + // the transaction prevents this from interfering with other communications + // on the pins that are used for SPI, for example IR is on pin 2 + static const SPISettings mySPISettings(12000000, MSBFIRST, SPI_MODE0); + SPI.beginTransaction(mySPISettings); + // Double start frame, normally 4, idk why it's double + for (uint8_t i = 0; i < 8; i++) { + SPI.transfer(0); + } + // Adjust brightness to 5 bits + uint8_t adjustedBrightness = 0b11100000 | ((m_brightness >> 3) & 0b00011111); + // LED frames + for (LedPos pos = LED_FIRST; pos < LED_COUNT; pos++) { + SPI.transfer(adjustedBrightness); // brightness + SPI.transfer(m_ledColors[pos].blue); // blue + SPI.transfer(m_ledColors[pos].green); // green + SPI.transfer(m_ledColors[pos].red); // red + } + // don't need to end the SPI frame apparently, just end transaction + SPI.endTransaction(); +#endif #ifdef VORTEX_LIB Vortex::vcallbacks()->ledsShow(); #endif diff --git a/VortexEngine/src/Log/Log.cpp b/VortexEngine/src/Log/Log.cpp index 19877548da..c79ff42b06 100644 --- a/VortexEngine/src/Log/Log.cpp +++ b/VortexEngine/src/Log/Log.cpp @@ -11,12 +11,27 @@ #include "VortexLib.h" #endif +#ifdef VORTEX_EMBEDDED +#include +#endif + #if LOGGING_LEVEL > 0 void InfoMsg(const char *msg, ...) { +#ifdef VORTEX_EMBEDDED + if (!SerialComs::isConnected()) { + return; + } +#endif va_list list; va_start(list, msg); +#ifdef VORTEX_EMBEDDED + char buf[2048] = {0}; + vsnprintf(buf, sizeof(buf), msg, list); + Serial.println(buf); +#else Vortex::printlog(NULL, NULL, 0, msg, list); +#endif va_end(list); } #endif @@ -24,9 +39,22 @@ void InfoMsg(const char *msg, ...) #if LOGGING_LEVEL > 1 void ErrorMsg(const char *func, const char *msg, ...) { +#ifdef VORTEX_EMBEDDED + if (!SerialComs::isConnected()) { + return; + } +#endif va_list list; va_start(list, msg); +#ifdef VORTEX_EMBEDDED + char fmt[2048] = {0}; + snprintf(fmt, sizeof(fmt), "%s(): %s", func, msg); + char buf[2048] = {0}; + vsnprintf(buf, sizeof(buf), fmt, list); + Serial.println(buf); +#else Vortex::printlog(NULL, func, 0, msg, list); +#endif va_end(list); } #endif @@ -34,6 +62,11 @@ void ErrorMsg(const char *func, const char *msg, ...) #if LOGGING_LEVEL > 2 void DebugMsg(const char *file, const char *func, int line, const char *msg, ...) { +#ifdef VORTEX_EMBEDDED + if (!SerialComs::isConnected()) { + return; + } +#endif va_list list; va_start(list, msg); const char *ptr = file + strlen(file); @@ -46,7 +79,15 @@ void DebugMsg(const char *file, const char *func, int line, const char *msg, ... } ptr--; } +#ifdef VORTEX_EMBEDDED + char fmt[2048] = {0}; + snprintf(fmt, sizeof(fmt), "%s:%d %s(): %s", file, line, func, msg); + char buf[2048] = {0}; + vsnprintf(buf, sizeof(buf), fmt, list); + Serial.println(buf); +#else Vortex::printlog(file, func, line, msg, list); +#endif va_end(list); } #endif diff --git a/VortexEngine/src/Menus/Menu.cpp b/VortexEngine/src/Menus/Menu.cpp index abfa81f77b..1063cb10c4 100644 --- a/VortexEngine/src/Menus/Menu.cpp +++ b/VortexEngine/src/Menus/Menu.cpp @@ -131,28 +131,16 @@ void Menu::nextBulbSelection() // do not allow multi led to select anything else //break; } + m_targetLeds = MAP_SIDES; + break; + case MAP_SIDES: m_targetLeds = MAP_LED(LED_FIRST); break; case MAP_LED(LED_LAST): - m_targetLeds = MAP_PAIR_EVENS; - break; - case MAP_PAIR_EVENS: - m_targetLeds = MAP_PAIR_ODDS; - break; - case MAP_PAIR_ODDS: - m_targetLeds = MAP_LED(LED_MULTI); - break; - case MAP_LED(LED_MULTI): m_targetLeds = MAP_LED_ALL; break; default: // LED_FIRST through LED_LAST - // do not allow multi led to select anything else - if (cur->isMultiLed()) { - //m_targetLeds = MAP_LED_ALL; - //break; - } - // iterate as normal - m_targetLeds = MAP_LED(((ledmapGetFirstLed(m_targetLeds) + 1) % (LED_COUNT + 1))); + m_targetLeds = MAP_LED((ledmapGetFirstLed(m_targetLeds) + 1) % (LED_COUNT)); break; } } diff --git a/VortexEngine/src/Menus/MenuList/ColorSelect.cpp b/VortexEngine/src/Menus/MenuList/ColorSelect.cpp index 50a069f254..bf24044f36 100644 --- a/VortexEngine/src/Menus/MenuList/ColorSelect.cpp +++ b/VortexEngine/src/Menus/MenuList/ColorSelect.cpp @@ -234,7 +234,6 @@ void ColorSelect::showSelection(ColorSelectState mode) return; case STATE_PICK_HUE2: hue = m_targetHue1 * (255 / 4) + (m_curSelection * (255 / 16)); - Leds::setIndex(LED_1, RGB_WHITE0); // force sat at hue level2 sat = 255; break; @@ -245,7 +244,7 @@ void ColorSelect::showSelection(ColorSelectState mode) val = vals[m_curSelection]; break; } - Leds::setMap(MAP_PAIR_EVENS, HSVColor(hue, sat, val)); + Leds::setAll(HSVColor(hue, sat, val)); } void ColorSelect::showFullSet(uint8_t offMs, uint8_t onMs) @@ -259,6 +258,7 @@ void ColorSelect::showFullSet(uint8_t offMs, uint8_t onMs) if ((now % offOnMs) < MS_TO_TICKS(onMs)) { Leds::setAll(m_colorset.get((now / offOnMs) % numCols)); } + Leds::setIndex(LED_1, 0x001000); } bool ColorSelect::isValidLedSelection(LedMap selection) const diff --git a/VortexEngine/src/Menus/MenuList/ModeSharing.cpp b/VortexEngine/src/Menus/MenuList/ModeSharing.cpp index e3391c1df2..6df36dbcd2 100644 --- a/VortexEngine/src/Menus/MenuList/ModeSharing.cpp +++ b/VortexEngine/src/Menus/MenuList/ModeSharing.cpp @@ -16,7 +16,9 @@ ModeSharing::ModeSharing(const RGBColor &col, bool advanced) : Menu(col, advanced), m_sharingMode(ModeShareState::SHARE_RECEIVE), - m_timeOutStartTime(0) + m_timeOutStartTime(0), + m_lastSendTime(0), + m_shouldEndSend(false) { } @@ -78,6 +80,14 @@ void ModeSharing::onShortClick() beginSendingIR(); DEBUG_LOG("Switched to send mode"); break; + case ModeShareState::SHARE_SEND_IR: + if (!IRSender::isSending()) { + beginReceivingIR(); + DEBUG_LOG("Switched to send mode"); + } else { + m_shouldEndSend = true; + } + break; default: break; } @@ -115,12 +125,14 @@ void ModeSharing::beginSendingIR() return; } m_sharingMode = ModeShareState::SHARE_SEND_IR; + Leds::clearAll(); + Leds::update(); // initialize it with the current mode data IRSender::loadMode(Modes::curMode()); // send the first chunk of data, leave if we're done if (!IRSender::send()) { - // when send has completed, stores time that last action was completed to calculate interval between sends - beginReceivingIR(); + // just set the last time and wait + m_lastSendTime = Time::getCurtime(); } } @@ -140,11 +152,19 @@ void ModeSharing::continueSendingIR() { // if the sender isn't sending then nothing to do if (!IRSender::isSending()) { + if (m_lastSendTime && m_lastSendTime < (Time::getCurtime() + MS_TO_TICKS(350))) { + if (m_shouldEndSend) { + beginReceivingIR(); + m_shouldEndSend = false; + } else { + beginSendingIR(); + } + } return; } if (!IRSender::send()) { - // when send has completed, stores time that last action was completed to calculate interval between sends - beginReceivingIR(); + // just set the last time and wait + m_lastSendTime = Time::getCurtime(); } } @@ -193,7 +213,8 @@ void ModeSharing::showSendModeVL() void ModeSharing::showSendModeIR() { // show a dim color when not sending - Leds::clearAll(); + Leds::setIndex(LED_1, RGB_CYAN1); + Leds::setIndex(LED_2, RGB_CYAN1); } void ModeSharing::showReceiveMode() diff --git a/VortexEngine/src/Menus/MenuList/ModeSharing.h b/VortexEngine/src/Menus/MenuList/ModeSharing.h index dc5adf2357..df2d0fe6c1 100644 --- a/VortexEngine/src/Menus/MenuList/ModeSharing.h +++ b/VortexEngine/src/Menus/MenuList/ModeSharing.h @@ -38,6 +38,10 @@ class ModeSharing : public Menu // the start time when checking for timing out uint32_t m_timeOutStartTime; + uint32_t m_lastSendTime; + + // whether to end the next send and go back to receive + bool m_shouldEndSend; }; #endif diff --git a/VortexEngine/src/Modes/DefaultModes.cpp b/VortexEngine/src/Modes/DefaultModes.cpp index a0f4be8b7c..08cd08439e 100644 --- a/VortexEngine/src/Modes/DefaultModes.cpp +++ b/VortexEngine/src/Modes/DefaultModes.cpp @@ -6,7 +6,14 @@ // the gloveset upon factory reset const default_mode_entry default_modes[MAX_MODES] = { { - PATTERN_DOPS, 5, { + PATTERN_VORTEX, MAP_LED_ALL, 5, { + RGB_RED, + RGB_GREEN, + RGB_BLUE, + 0xABAA00, + 0x5500AB + }, + PATTERN_VORTEX, MAP_LED_NONE, 5, { RGB_RED, RGB_GREEN, RGB_BLUE, @@ -14,41 +21,176 @@ const default_mode_entry default_modes[MAX_MODES] = { 0x5500AB } }, + { - PATTERN_GHOSTCRUSH, 5, { - RGB_WHITE, - RGB_WHITE, - RGB_OFF, - 0x700000, - RGB_OFF, + PATTERN_GAPCYCLE, MAP_LED_ALL, 4, { + 0x00553A, + 0x00CFFF, + 0x55001D, + 0x551200 + }, + PATTERN_GAPCYCLE, MAP_LED_NONE, 4, { + HSV(114, 255, 43), + HSV(159, 255, 128), + HSV(240, 255, 43), + HSV(9, 255, 43) } }, + { - PATTERN_WARPWORM, 2, { - RGB_GREEN, - 0x26004B, + PATTERN_TRACER, MAP_PAIR_EVENS, 3, { + 0x62007F, + 0x62007F, + 0x33FE00 + }, + PATTERN_BLENDSTROBE, MAP_PAIR_ODDS, 8, { + 0xFF0019, + 0x553600, + 0xABFF00, + 0x005502, + 0x00FFBA, + 0x003155, + 0x1E00FF, + 0x460055 } }, + { - PATTERN_PULSISH, 3, { - 0x00AB55, - 0x8D1C55, - 0x00001C + PATTERN_BLENDSTROBEGAP, MAP_PAIR_EVENS, 8, { + 0xFFAE00, + 0x3D5500, + 0x1BFF00, + 0x00552A, + 0x00E7FF, + 0x001955, + 0x4E00FF, + 0x4e0055 + }, + PATTERN_GHOSTCRUSH, MAP_PAIR_ODDS, 8, { + 0xFFAE00, + 0x3D5500, + 0x1BFF00, + 0x00552A, + 0x00E7FF, + 0x001955, + 0x4E00FF, + 0x4e0055 } }, { - PATTERN_ZIGZAG, 6, { - RGB_OFF, - 0x56D400, - 0x5500AB, - RGB_OFF, - RGB_RED, - 0x700000 + PATTERN_BLENDSTROBE, MAP_PAIR_EVENS, 6, { + 0x554A00, + 0x554A00, + 0x00AA90, + 0x00AA90, + 0xD200FF, + 0xD200FF + }, + PATTERN_DOUBLEDOPS, MAP_PAIR_ODDS, 8, { + 0xFF0033, + 0xFF0033, + 0xDB2F00, + 0xB76B00, + 0x938D00, + 0x4A6F00, + 0x164B00, + 0x002702 } }, + { - PATTERN_STROBE, 8, { + PATTERN_CHASER, MAP_LED_ALL, 5, { + 0x9FFF00, + 0x00FF66, + 0x009FFF, + 0x5A00FF, + 0xFF009F + }, + PATTERN_CHASER, MAP_LED_NONE, 5, { + HSV(0, 0, 255), + HSV(0, 0, 255), + HSV(0, 0, 0), + HSV(140, 255, 170), + HSV(0, 0, 0) + } + }, + + { + PATTERN_METEOR, MAP_LED_ALL, 5, { + 0x00AA90, + 0x00B1FF, + 0x300055, + 0xFF002D, + 0x541B00 + }, + PATTERN_METEOR, MAP_LED_NONE, 5, { + HSV(0, 0, 255), + HSV(0, 0, 255), + HSV(0, 0, 0), + HSV(170, 255, 170), + HSV(0, 0, 0) + } + }, + + { + PATTERN_DRIP, MAP_LED_ALL, 5, { + 0x0000FF, + 0x400055, + 0x54000B, + 0x525400, + 0x26AA00 + }, + PATTERN_DRIP, MAP_LED_NONE, 5, { + HSV(0, 0, 255), + HSV(0, 0, 255), + HSV(0, 0, 0), + HSV(200, 255, 170), + HSV(0, 0, 0) + } + }, + + { + PATTERN_HYPERSTROBE, MAP_LED_ALL, 2, { + 0xFFF600, + 0x000880 + }, + PATTERN_HYPERSTROBE, MAP_LED_NONE, 5, { + HSV(0, 0, 255), + HSV(0, 0, 255), + HSV(0, 0, 0), + HSV(230, 255, 170), + HSV(0, 0, 0) + } + }, + + { + PATTERN_SPARKLETRACE, MAP_LED_ALL, 6, { + 0x003755, + 0x003755, + 0xAA0072, + 0xAA0072, + 0xA5FF00, + 0xA5FF00 + }, + PATTERN_SPARKLETRACE, MAP_LED_NONE, 5, { + HSV(0, 0, 255), + HSV(0, 0, 255), + HSV(0, 0, 0), + HSV(0, 255, 170), + HSV(0, 0, 0) + } + }, + + { + PATTERN_DASHDOPS, MAP_LED_ALL, 5, { + 0xE2E7FF, + 0x010015, + 0x13090E, + 0x010015, + 0xE2E7FF + }, + PATTERN_DASHDOPS, MAP_LED_NONE, 8, { 0xD4002B, RGB_OFF, 0x0056AA, @@ -59,15 +201,9 @@ const default_mode_entry default_modes[MAX_MODES] = { RGB_OFF } }, + { - PATTERN_SNOWBALL, 3, { - 0x170600, - 0x00840A, - 0x12002A - } - }, - { - PATTERN_ULTRADOPS, 8, { + PATTERN_ULTRADOPS, MAP_LED_ALL, 8, { 0x1C0000, 0x4B2600, 0xABAA00, @@ -76,30 +212,30 @@ const default_mode_entry default_modes[MAX_MODES] = { 0x00001C, 0x26004B, 0x13000A - } - }, - { - PATTERN_VORTEX, 4, { - 0xAA0055, - 0x7070C5, - 0x0A0013, - 0x1C8E55, - } - }, - { - PATTERN_VORTEXWIPE, 8, { - RGB_RED, - 0x00001C, - 0x00001C, - 0x00001C, - 0x00001C, - 0x00001C, - 0x00001C, + }, + PATTERN_ULTRADOPS, MAP_LED_NONE, 8, { + 0x1C0000, + 0x4B2600, + 0xABAA00, + 0x001C00, + 0x00130A, 0x00001C, + 0x26004B, + 0x13000A } }, + { - PATTERN_GHOSTCRUSH, 7, { + PATTERN_GHOSTCRUSH, MAP_LED_ALL, 7, { + 0x26004B, + RGB_OFF, + RGB_GREEN, + RGB_WHITE, + RGB_GREEN, + RGB_OFF, + 0x26004B, + }, + PATTERN_GHOSTCRUSH, MAP_LED_NONE, 7, { 0x26004B, RGB_OFF, RGB_GREEN, @@ -109,15 +245,14 @@ const default_mode_entry default_modes[MAX_MODES] = { 0x26004B, } }, + { - PATTERN_VORTEXWIPE, 3, { - 0x00AB55, - 0x7F0081, - 0xAA381C, - } - }, - { - PATTERN_COMPLEMENTARY_BLEND, 3, { + PATTERN_COMPLEMENTARY_BLEND, MAP_LED_ALL, 3, { + RGB_RED, + RGB_GREEN, + RGB_BLUE + }, + PATTERN_COMPLEMENTARY_BLEND, MAP_LED_NONE, 3, { RGB_RED, RGB_GREEN, RGB_BLUE diff --git a/VortexEngine/src/Modes/DefaultModes.h b/VortexEngine/src/Modes/DefaultModes.h index 888e4fb7bf..a854ed65b6 100644 --- a/VortexEngine/src/Modes/DefaultModes.h +++ b/VortexEngine/src/Modes/DefaultModes.h @@ -3,13 +3,19 @@ #include "../Patterns/Patterns.h" #include "../VortexConfig.h" +#include "../Leds/LedTypes.h" // structure of the entries in the default modes array struct default_mode_entry { PatternID patternID; + LedMap map; uint8_t numColors; uint32_t cols[MAX_COLOR_SLOTS]; + PatternID patternID2; + LedMap map2; + uint8_t numColors2; + uint32_t cols2[MAX_COLOR_SLOTS]; }; // exposed global array of default modes diff --git a/VortexEngine/src/Modes/Modes.cpp b/VortexEngine/src/Modes/Modes.cpp index a81a6d72db..2b96c53745 100644 --- a/VortexEngine/src/Modes/Modes.cpp +++ b/VortexEngine/src/Modes/Modes.cpp @@ -390,8 +390,16 @@ bool Modes::setDefaults() // add each default mode with each of the given colors for (uint8_t i = 0; i < num_default_modes; ++i) { const default_mode_entry &def = default_modes[i]; - Colorset set(def.numColors, def.cols); - addMode(def.patternID, nullptr, &set); + Colorset set1(def.numColors, def.cols); + if (isMultiLedPatternID(def.patternID)) { + addMode(def.patternID, nullptr, &set1); + } else { + Mode tempMode; + tempMode.setPatternMap(def.map, def.patternID, nullptr, &set1); + Colorset set2(def.numColors2, def.cols2); + tempMode.setPatternMap(def.map2, def.patternID2, nullptr, &set2); + addMode(&tempMode); + } } return true; } diff --git a/VortexEngine/src/Serial/Serial.cpp b/VortexEngine/src/Serial/Serial.cpp index 5079e80455..e22067e1d5 100644 --- a/VortexEngine/src/Serial/Serial.cpp +++ b/VortexEngine/src/Serial/Serial.cpp @@ -3,6 +3,7 @@ #include "../Serial/ByteStream.h" #include "../Time/TimeControl.h" #include "../Time/Timings.h" +#include "../Menus/Menus.h" #include "../Log/Log.h" #include "../VortexEngine.h" @@ -12,14 +13,19 @@ #include #endif +#ifdef VORTEX_EMBEDDED +#include +#endif + bool SerialComs::m_serialConnected = false; uint32_t SerialComs::m_lastCheck = 0; +uint32_t SerialComs::m_lastConnected = 0; // init serial bool SerialComs::init() { // Try connecting serial ? - //checkSerial(); + checkSerial(); return true; } @@ -29,9 +35,33 @@ void SerialComs::cleanup() bool SerialComs::isConnected() { +#ifdef VORTEX_EMBEDDED + if (!Serial) { + m_serialConnected = false; + return false; + } + if (!isConnectedReal()) { + return false; + } +#endif return m_serialConnected; } +bool SerialComs::isConnectedReal() +{ +#ifdef VORTEX_EMBEDDED + uint32_t now = Time::getCurtime(); + if (!Serial.usb.connected()) { + m_lastConnected = now; + } else { + if (m_lastConnected && (now - m_lastConnected) > 1800) { + return false; + } + } +#endif + return true; +} + // check for any serial connection or messages bool SerialComs::checkSerial() { @@ -40,6 +70,9 @@ bool SerialComs::checkSerial() // already connected return true; } + if (m_serialConnected) { + return isConnectedReal(); + } uint32_t now = Time::getCurtime(); // don't check for serial too fast if (m_lastCheck && (now - m_lastCheck) < MAX_SERIAL_CHECK_INTERVAL) { @@ -54,17 +87,22 @@ bool SerialComs::checkSerial() Vortex::vcallbacks()->serialBegin(SERIAL_BAUD_RATE); #else // This will check if the serial communication is open - if (!Serial.available()) { + if (!Serial) { // serial is not connected return false; } - // Begin serial communications + // Begin serial communications (turns out this is actually a NO-OP in trinket source) Serial.begin(SERIAL_BAUD_RATE); + if (Menus::curMenuID() != MENU_EDITOR_CONNECTION) { + // directly open the editor connection menu because we are connected to USB serial + Menus::openMenu(MENU_EDITOR_CONNECTION); + } #endif #endif // serial is now connected m_serialConnected = true; - return true; + // rely on the low level 'real' connection now + return isConnectedReal(); } void SerialComs::write(const char *msg, ...) diff --git a/VortexEngine/src/Serial/Serial.h b/VortexEngine/src/Serial/Serial.h index 0121b5bbb0..7ce393361f 100644 --- a/VortexEngine/src/Serial/Serial.h +++ b/VortexEngine/src/Serial/Serial.h @@ -17,6 +17,9 @@ class SerialComs // whether serial is initialized static bool isConnected(); + // why do I need this + static bool isConnectedReal(); + // check for any serial connection or messages static bool checkSerial(); @@ -36,6 +39,7 @@ class SerialComs // whether serial communications are initialized static bool m_serialConnected; static uint32_t m_lastCheck; + static uint32_t m_lastConnected; }; #endif diff --git a/VortexEngine/src/Storage/Storage.cpp b/VortexEngine/src/Storage/Storage.cpp index 357cc6bd3c..a9c51fb41b 100644 --- a/VortexEngine/src/Storage/Storage.cpp +++ b/VortexEngine/src/Storage/Storage.cpp @@ -9,7 +9,7 @@ #include "../Log/Log.h" #ifdef VORTEX_LIB -#include "../VortexLib/VortexLib.h" +#include "VortexLib.h" #endif #ifndef VORTEX_EMBEDDED @@ -29,6 +29,13 @@ std::string Storage::m_storageFilename; #define STORAGE_FILENAME DEFAULT_STORAGE_FILENAME #endif +#ifdef VORTEX_EMBEDDED +#include +#define PAGE_SIZE 64 +__attribute__((__aligned__(256))) +const uint8_t _storagedata[(STORAGE_SIZE+255)/256*256] = { }; +#endif + uint32_t Storage::m_lastSaveSize = 0; Storage::Storage() @@ -69,7 +76,52 @@ bool Storage::write(uint16_t slot, ByteStream &buffer) // just in case buffer.recalcCRC(); #ifdef VORTEX_EMBEDDED - // implement device storage here + // the target slot to store in + uint32_t storage_slot = (uint32_t)_storagedata + (slot * MAX_MODE_SIZE); + + // Number of rows to erase for one slot + uint16_t rows_to_erase = MAX_MODE_SIZE / (PAGE_SIZE * 4); + // Erase only the rows containing the slot + for (uint16_t i = 0; i < rows_to_erase; ++i) { + // Set the address for the row to erase + NVMCTRL->ADDR.reg = (uint32_t)(storage_slot + (i * PAGE_SIZE * 4)) / 2; + // Execute the erase command + NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER; + // Wait for the erase operation to complete + while (!NVMCTRL->INTFLAG.bit.READY) {} + } + + // set the last save size + m_lastSaveSize = buffer.size(); + + // write out the buffer to storage + // Calculate data boundaries + uint32_t size = (buffer.rawSize() + 3) / 4; + volatile uint32_t *dst_addr = (volatile uint32_t *)storage_slot; + const uint8_t *src_addr = (uint8_t *)buffer.rawData(); + + // Disable automatic page write + NVMCTRL->CTRLB.bit.MANW = 1; + + // Do writes in pages + while (size) { + // Execute "PBC" Page Buffer Clear + NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_PBC; + while (NVMCTRL->INTFLAG.bit.READY == 0) {} + + // Fill page buffer + uint32_t i; + for (i = 0; i < (PAGE_SIZE / 4) && size; i++) { + *dst_addr = *(uint32_t *)(src_addr); + src_addr += sizeof(uint32_t); + dst_addr++; + size--; + } + + // Execute "WP" Write Page + NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_WP; + while (NVMCTRL->INTFLAG.bit.READY == 0) {} + } #elif defined(_WIN32) HANDLE hFile = CreateFile(STORAGE_FILENAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { @@ -118,7 +170,8 @@ bool Storage::read(uint16_t slot, ByteStream &buffer) return false; } #ifdef VORTEX_EMBEDDED - // implement device storage here + // read directly into the raw data of the byte array + memcpy(buffer.rawData(), (const void *)(_storagedata + (slot * MAX_MODE_SIZE)), size); #elif defined(_WIN32) HANDLE hFile = CreateFile(STORAGE_FILENAME, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { diff --git a/VortexEngine/src/Time/TimeControl.cpp b/VortexEngine/src/Time/TimeControl.cpp index 8493deb667..4a7a374041 100644 --- a/VortexEngine/src/Time/TimeControl.cpp +++ b/VortexEngine/src/Time/TimeControl.cpp @@ -9,6 +9,10 @@ #include "../Leds/Leds.h" +#ifdef VORTEX_EMBEDDED +#include +#endif + #if !defined(_WIN32) || defined(WASM) #include #include @@ -190,7 +194,7 @@ uint32_t Time::microseconds() void Time::delayMicroseconds(uint32_t us) { -#ifdef _WIN32 +#if defined(VORTEX_EMBEDDED) || defined(_WIN32) uint32_t newtime = microseconds() + us; while (microseconds() < newtime) { // busy loop diff --git a/VortexEngine/src/VortexConfig.h b/VortexEngine/src/VortexConfig.h index dc47583a5c..f47c7322dd 100644 --- a/VortexEngine/src/VortexConfig.h +++ b/VortexEngine/src/VortexConfig.h @@ -38,7 +38,7 @@ // the engine flavour, this should change for each device/flavour // of the engine that branches off from the main indefinitely -#define VORTEX_NAME "Core" +#define VORTEX_NAME "Handle" // the full name of this build for ex: // Vortex Engine v1.0 'Igneous' (built Tue Jan 31 19:03:55 2023) @@ -57,8 +57,8 @@ // Menu Trigger Threshold (in milliseconds) // -// How long the button must be held to trigger menu selection and -// begin blinking the first menu color +// How long the button must be held to trigger ring menu and begin +// filling the first menu color #define MENU_TRIGGER_TIME 1000 // Short Click Threshold (in milliseconds) @@ -176,7 +176,7 @@ // This should not be set to 0, it should be a specific maximum for // each separate device // -#define MAX_MODES 13 +#define MAX_MODES 14 // Default Tickrate in Ticks Per Second (TPS) // diff --git a/VortexEngine/src/VortexEngine.cpp b/VortexEngine/src/VortexEngine.cpp index 087931c7d9..bfd51e3b1b 100644 --- a/VortexEngine/src/VortexEngine.cpp +++ b/VortexEngine/src/VortexEngine.cpp @@ -178,6 +178,9 @@ void VortexEngine::runMainLogic() return; } + // check if the device has been plugged in + SerialComs::checkSerial(); + // if the menus are open and running then just return if (Menus::run()) { return; diff --git a/VortexEngine/src/Wireless/IRReceiver.cpp b/VortexEngine/src/Wireless/IRReceiver.cpp index 47ec50ef4d..19f83e1b50 100644 --- a/VortexEngine/src/Wireless/IRReceiver.cpp +++ b/VortexEngine/src/Wireless/IRReceiver.cpp @@ -9,6 +9,10 @@ #include "../Modes/Mode.h" #include "../Log/Log.h" +#ifdef VORTEX_EMBEDDED +#include +#endif + BitStream IRReceiver::m_irData; IRReceiver::RecvState IRReceiver::m_recvState = WAITING_HEADER_MARK; uint32_t IRReceiver::m_prevTime = 0; @@ -17,6 +21,9 @@ uint32_t IRReceiver::m_previousBytes = 0; bool IRReceiver::init() { +#ifdef VORTEX_EMBEDDED + pinMode(IR_RECEIVER_PIN, INPUT_PULLUP); +#endif m_irData.init(IR_RECV_BUF_SIZE); return true; } @@ -83,12 +90,18 @@ bool IRReceiver::receiveMode(Mode *pMode) bool IRReceiver::beginReceiving() { +#ifdef VORTEX_EMBEDDED + attachInterrupt(digitalPinToInterrupt(IR_RECEIVER_PIN), IRReceiver::recvPCIHandler, CHANGE); +#endif resetIRState(); return true; } bool IRReceiver::endReceiving() { +#ifdef VORTEX_EMBEDDED + detachInterrupt(digitalPinToInterrupt(IR_RECEIVER_PIN)); +#endif resetIRState(); return true; } diff --git a/VortexEngine/src/Wireless/IRSender.cpp b/VortexEngine/src/Wireless/IRSender.cpp index 1d127a1978..5ec181188c 100644 --- a/VortexEngine/src/Wireless/IRSender.cpp +++ b/VortexEngine/src/Wireless/IRSender.cpp @@ -11,6 +11,10 @@ #include "VortexLib.h" #endif +#ifdef VORTEX_EMBEDDED +#include +#endif + // the serial buffer for the data ByteStream IRSender::m_serialBuf; // a bit walker for the serial data @@ -30,8 +34,15 @@ uint32_t IRSender::m_blockSize = 0; // write total uint32_t IRSender::m_writeCounter = 0; +#if defined(VORTEX_EMBEDDED) +// Timer used for PWM, is initialized in initpwm() +Tcc *IR_TCCx; +#endif + bool IRSender::init() { + // initialize the IR device + initPWM(); return true; } @@ -111,6 +122,8 @@ void IRSender::beginSend() m_isSending = true; DEBUG_LOGF("[%zu] Beginning send size %u (blocks: %u remainder: %u blocksize: %u)", Time::microseconds(), m_size, m_numBlocks, m_remainder, m_blockSize); + // init sender before writing, is this necessary here? I think so + initPWM(); // wakeup the other receiver with a very quick mark/space sendMark(50); sendSpace(100); @@ -144,6 +157,9 @@ void IRSender::sendMark(uint16_t time) #ifdef VORTEX_LIB // send mark timing over socket Vortex::vcallbacks()->infraredWrite(true, time); +#else + startPWM(); + Time::delayMicroseconds(time); #endif } @@ -152,6 +168,80 @@ void IRSender::sendSpace(uint16_t time) #ifdef VORTEX_LIB // send space timing over socket Vortex::vcallbacks()->infraredWrite(false, time); +#else + stopPWM(); + Time::delayMicroseconds(time); +#endif +} + +// shamelessly stolen from IRLib2, thanks +void IRSender::initPWM() +{ +#if defined(VORTEX_EMBEDDED) + // initialize the output pin + pinMode(IR_SEND_PWM_PIN, OUTPUT); + digitalWrite(IR_SEND_PWM_PIN, LOW); + // setup the PWM + uint8_t port = g_APinDescription[IR_SEND_PWM_PIN].ulPort; // 0 + uint8_t pin = g_APinDescription[IR_SEND_PWM_PIN].ulPin; // 8 + ETCChannel IR_TCC_Channel = TCC0_CH0; + int8_t IR_PER_EorF = PORT_PMUX_PMUXE_E; + //println();Serial.print("Port:"); Serial.print(port,DEC); Serial.print(" Pin:"); Serial.println(pin,DEC); + // Enable the port multiplexer for the PWM channel on pin + PORT->Group[port].PINCFG[pin].bit.PMUXEN = 1; + + // Connect the TCC timer to the port outputs - port pins are paired odd PMUXO and even PMUXEII + // F & E peripherals specify the timers: TCC0, TCC1 and TCC2 + PORT->Group[port].PMUX[pin >> 1].reg |= IR_PER_EorF; + +// pinPeripheral (IR_SEND_PWM_PIN,PIO_TIMER_ALT); + // Feed GCLK0 to TCC0 and TCC1 + REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN | // Enable GCLK0 to TCC0 and TCC1 + GCLK_CLKCTRL_GEN_GCLK0 | // Select GCLK0 + GCLK_CLKCTRL_ID_TCC0_TCC1; // Feed GCLK0 to TCC0 and TCC1 + while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization + + // Normal (single slope) PWM operation: timers countinuously count up to PER + // register value and then is reset to 0 + IR_TCCx = (Tcc*) GetTC(IR_TCC_Channel); + IR_TCCx->WAVE.reg |= TCC_WAVE_WAVEGEN_NPWM; // Setup single slope PWM on TCCx + while (IR_TCCx->SYNCBUSY.bit.WAVE); // Wait for synchronization + + // Each timer counts up to a maximum or TOP value set by the PER register, + // this determines the frequency of the PWM operation. + uint32_t cc = F_CPU/(38*1000) - 1; + IR_TCCx->PER.reg = cc; // Set the frequency of the PWM on IR_TCCx + while(IR_TCCx->SYNCBUSY.bit.PER); + + // The CCx register value corresponds to the pulsewidth in microseconds (us) + // Set the duty cycle of the PWM on TCC0 to 33% + IR_TCCx->CC[GetTCChannelNumber(IR_TCC_Channel)].reg = cc/3; + while (IR_TCCx->SYNCBUSY.reg & TCC_SYNCBUSY_MASK); + //while(IR_TCCx->SYNCBUSY.bit.CC3); + + // Enable IR_TCCx timer but do not turn on PWM yet. Will turn it on later. + IR_TCCx->CTRLA.reg |= TCC_CTRLA_PRESCALER_DIV1; // Divide GCLK0 by 1 + while (IR_TCCx->SYNCBUSY.bit.ENABLE); + IR_TCCx->CTRLA.reg &= ~TCC_CTRLA_ENABLE; //initially off will turn on later + while (IR_TCCx->SYNCBUSY.bit.ENABLE); +#endif +} + +void IRSender::startPWM() +{ +#if defined(VORTEX_EMBEDDED) + // start the PWM + IR_TCCx->CTRLA.reg |= TCC_CTRLA_ENABLE; + while (IR_TCCx->SYNCBUSY.bit.ENABLE); +#endif +} + +void IRSender::stopPWM() +{ +#if defined(VORTEX_EMBEDDED) + // stop the PWM + IR_TCCx->CTRLA.reg &= ~TCC_CTRLA_ENABLE; + while (IR_TCCx->SYNCBUSY.bit.ENABLE); #endif } diff --git a/VortexEngine/src/Wireless/IRSender.h b/VortexEngine/src/Wireless/IRSender.h index 8f65fa7048..d8e9df30eb 100644 --- a/VortexEngine/src/Wireless/IRSender.h +++ b/VortexEngine/src/Wireless/IRSender.h @@ -34,6 +34,11 @@ class IRSender // send a mark/space by turning PWM on/off static void sendMark(uint16_t time); static void sendSpace(uint16_t time); + // Pulse-Width Modulator (IR Transmitter) + static void initPWM(); + // turn the IR transmitter on/off in realtime + static void startPWM(); + static void stopPWM(); // the serial buffer for the data static ByteStream m_serialBuf; diff --git a/VortexEngine/src/Wireless/VLConfig.h b/VortexEngine/src/Wireless/VLConfig.h index 2ee8490d67..00438aea9a 100644 --- a/VortexEngine/src/Wireless/VLConfig.h +++ b/VortexEngine/src/Wireless/VLConfig.h @@ -8,7 +8,7 @@ // Whether to enable the Visible Light system as a whole // #define VL_ENABLE_SENDER 1 -#define VL_ENABLE_RECEIVER 1 +#define VL_ENABLE_RECEIVER 0 // the size of IR blocks in bits #define VL_DEFAULT_BLOCK_SIZE 256 diff --git a/VortexEngine/tests/tests_general.tar.gz b/VortexEngine/tests/tests_general.tar.gz index 5c0e6df188..38f6ab4d8f 100644 Binary files a/VortexEngine/tests/tests_general.tar.gz and b/VortexEngine/tests/tests_general.tar.gz differ diff --git a/rewrite_trinket_source.sh b/rewrite_trinket_source.sh new file mode 100644 index 0000000000..989304938b --- /dev/null +++ b/rewrite_trinket_source.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +FILE_PATH="$HOME/.arduino15/packages/adafruit/hardware/samd/1.7.16/cores/arduino/USB/CDC.cpp" + +# Check if the file exists +if [ ! -f "$FILE_PATH" ]; then + echo "Error: File does not exist." + exit 1 +fi +# Read the specific line and check its content +CURRENT_LINE=$(sed -n '258p' "$FILE_PATH") + +if [ "$CURRENT_LINE" != " delay(10);" ]; then + if [ "$CURRENT_LINE" != " \/\/delay(10);" ]; then + echo "No changes made: line 258 does not match the expected content." + exit 1 + fi + echo "No changes made: line 258 has already been modified." +else + # Replace the content of line 258 + sed -i '258s/.*/ \/\/delay(10);/' "$FILE_PATH" + echo "Line 258, a delay(10), has been commented out in: [$FILE_PATH]" +fi + +# ============================================================================================= + +FILE_PATH2="$HOME/.arduino15/packages/adafruit/hardware/samd/1.7.16/cores/arduino/USB/USBAPI.h" +# Check if the file exists +if [ ! -f "$FILE_PATH2" ]; then + echo "Error: File does not exist." + exit 1 +fi +# Read the specific line and check its content +CURRENT_LINE=$(sed -n '182p' "$FILE_PATH2") +if [ "$CURRENT_LINE" != "private:" ]; then + if [ "$CURRENT_LINE" != "\/\/private:" ]; then + echo "No changes made: line 182 does not match the expected content." + exit 1 + fi + echo "No changes made: line 182 has already been modified." +else + # replace the content of line + sed -i '182s/.*/\/\/private:/' "$FILE_PATH2" + echo "Line 182, private: has been commented out in: [$FILE_PATH2]" +fi