diff --git a/.github/workflows/build-zmk-firmware.yml b/.github/workflows/build-zmk-firmware.yml new file mode 100644 index 00000000..83658260 --- /dev/null +++ b/.github/workflows/build-zmk-firmware.yml @@ -0,0 +1,208 @@ +name: Reusable user config build + +on: + push: + paths: + - "zmk-config/**" + pull_request: + paths: + - "zmk-config/**" + workflow_dispatch: + +permissions: + contents: read + +env: + BUILD_MATRIX_PATH: "zmk-config/build.yaml" + CONFIG_PATH: "zmk-config" + FALLBACK_BINARY: "bin" + ARCHIVE_NAME: "firmware" + +jobs: + matrix: + runs-on: ubuntu-22.04 + name: Fetch Build Keyboards + outputs: + build_matrix: ${{ env.BUILD_MATRIX }} + steps: + - name: Checkout + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + with: + persist-credentials: false + + - name: Fetch Build Matrix + run: | + echo "BUILD_MATRIX=$(yq -oj -I0 \"${BUILD_MATRIX_PATH}\")" >> "$GITHUB_ENV" + yq -oj "${BUILD_MATRIX_PATH}" + + build: + permissions: + actions: write + contents: read + runs-on: ubuntu-latest + container: + image: zmkfirmware/zmk-build-arm:stable + needs: matrix + name: Build + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.matrix.outputs.build_matrix) }} + steps: + - name: Act Workaround # https://github.com/nektos/act/issues/973 + if: ${{ env.ACT }} + run: | + apt-get update && apt-get install -y curl unzip + curl -fsSL https://deb.nodesource.com/setup_22.x | bash && apt install -y nodejs + + - name: Checkout + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + with: + persist-credentials: false + + - name: Create build directory + run: | + echo "BUILD_DIR=$(mktemp -d)" >> $GITHUB_ENV + + - name: Prepare variables + shell: bash -x {0} + env: + board: ${{ matrix.board }} + shield: ${{ matrix.shield }} + artifact_name: ${{ matrix.artifact-name }} + snippet: ${{ matrix.snippet }} + run: | + if [ -e zephyr/module.yml ]; then + zmk_load_arg=" -DZMK_EXTRA_MODULES='${GITHUB_WORKSPACE}'" + new_tmp_dir="${TMPDIR:-/tmp}/zmk-config" + mkdir -p "${new_tmp_dir}" + echo "BASE_DIR=${new_tmp_dir}" >> $GITHUB_ENV + else + echo "BASE_DIR=${GITHUB_WORKSPACE}" >> $GITHUB_ENV + fi + + if [ -n "${snippet}" ]; then + extra_west_args="-S \"${snippet}\"" + fi + + echo "ZEPHYR_VERSION=${ZEPHYR_VERSION}" >> $GITHUB_ENV + echo "EXTRA_WEST_ARGS=${extra_west_args}" >> $GITHUB_ENV + echo "EXTRA_CMAKE_ARGS=${shield:+-DSHIELD=\"$shield\"}${zmk_load_arg}" >> $GITHUB_ENV + echo "DISPLAY_NAME=${shield:+$shield - }${board}" >> $GITHUB_ENV + echo "ARTIFACT_NAME=${artifact_name:-${shield:+$shield-}${board//\//_}-zmk}" >> $GITHUB_ENV + + - name: Copy config files to isolated temporary directory + run: | + if [ "${BASE_DIR}" != "${GITHUB_WORKSPACE}" ]; then + mkdir "${BASE_DIR}/${CONFIG_PATH}" + cp -R "${CONFIG_PATH}"/* "${BASE_DIR}/${CONFIG_PATH}/" + fi + + - name: Cache west modules + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 + continue-on-error: true + env: + cache_name: cache-zephyr-${{ env.ZEPHYR_VERSION }}-modules + with: + path: | + ${{ env.BASE_DIR }}/modules/ + ${{ env.BASE_DIR }}/tools/ + ${{ env.BASE_DIR }}/zephyr/ + ${{ env.BASE_DIR }}/bootloader/ + ${{ env.BASE_DIR }}/zmk/ + key: ${{ runner.os }}-build-${{ env.cache_name }}-${{ hashFiles('**/west.yml', '**/build.yaml') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache_name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + + - name: West Init + working-directory: ${{ env.BASE_DIR }} + run: west init -l "${BASE_DIR}/${CONFIG_PATH}" + + - name: West Update + working-directory: ${{ env.BASE_DIR }} + run: west update --fetch-opt=--filter=tree:0 + + - name: Check ZMK revision + working-directory: ${{ env.BASE_DIR }} + run: | + zmk_revision=$(west list -f "{revision}" zmk) + echo "zmk_revision=${zmk_revision}" >> $GITHUB_ENV + echo "ZMK revision: ${zmk_revision}" + + - name: West Zephyr export + working-directory: ${{ env.BASE_DIR }} + run: west zephyr-export + + - name: West Build (${{ env.DISPLAY_NAME }}) + working-directory: ${{ env.BASE_DIR }} + shell: sh -x {0} + run: west build -s zmk/app -d "${BUILD_DIR}" -b "${MATRIX_BOARD}" ${EXTRA_WEST_ARGS} -- -DZMK_CONFIG=${BASE_DIR}/${CONFIG_PATH} ${EXTRA_CMAKE_ARGS} ${MATRIX_CMAKE_ARGS} + env: + MATRIX_BOARD: ${{ matrix.board }} + MATRIX_CMAKE_ARGS: ${{ matrix.cmake-args }} + + - name: Warn about building from main if build fails + if: failure() && env.zmk_revision == 'main' + run: | + echo "# Consider Pinning ZMK" >> $GITHUB_STEP_SUMMARY + echo "Your recent build failure might be the result of breaking changes made to ZMK's main branch." >> $GITHUB_STEP_SUMMARY + echo "Consider [pinning your ZMK version](https://zmk.dev/blog/2025/06/20/pinned-zmk) to a release for increased stability." >> $GITHUB_STEP_SUMMARY + echo "See also the [list of released versions](https://github.com/zmkfirmware/zmk/releases)." >> $GITHUB_STEP_SUMMARY + echo "If you wish to stay on main, check the most recent pending release PR for breaking changes. [Our blog](https://zmk.dev/blog) may have upgrade information if breaking changes are significant." >> $GITHUB_STEP_SUMMARY + + - name: ${{ env.DISPLAY_NAME }} Kconfig file + run: | + if [ -f "${BUILD_DIR}/zephyr/.config" ] + then + grep -v -e "^#" -e "^$" "${BUILD_DIR}/zephyr/.config" | sort + else + echo "No Kconfig output" + fi + if: ${{ !cancelled() }} + + - name: ${{ env.DISPLAY_NAME }} Devicetree file + run: | + if [ -f "${BUILD_DIR}/zephyr/zephyr.dts" ] + then + cat "${BUILD_DIR}/zephyr/zephyr.dts" + elif [ -f "${BUILD_DIR}/zephyr/zephyr.dts.pre" ] + then + cat -s "${BUILD_DIR}/zephyr/zephyr.dts.pre" + else + echo "No Devicetree output" + fi + if: ${{ !cancelled() }} + + - name: Rename artifacts + shell: sh -x {0} + run: | + mkdir "${BUILD_DIR}/artifacts" + if [ -f "${BUILD_DIR}/zephyr/zmk.uf2" ] + then + cp "${BUILD_DIR}/zephyr/zmk.uf2" "${BUILD_DIR}/artifacts/${ARTIFACT_NAME}.uf2" + elif [ -f "${BUILD_DIR}/zephyr/zmk.${FALLBACK_BINARY}" ] + then + cp "${BUILD_DIR}/zephyr/zmk.${FALLBACK_BINARY}" "${BUILD_DIR}/artifacts/${ARTIFACT_NAME}.${FALLBACK_BINARY}" + fi + + - name: Archive (${{ env.DISPLAY_NAME }}) + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: artifact-${{ env.ARTIFACT_NAME }} + path: ${{ env.BUILD_DIR }}/artifacts + + merge: + permissions: + actions: write + contents: read + runs-on: ubuntu-latest + needs: build + name: Merge Output Artifacts + steps: + - name: Merge Artifacts + uses: actions/upload-artifact/merge@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: ${{ env.ARCHIVE_NAME }} + pattern: artifact-* + delete-merged: true diff --git a/nix/outputs/devShells.nix b/nix/outputs/devShells.nix index 2d890d9f..8e87da39 100644 --- a/nix/outputs/devShells.nix +++ b/nix/outputs/devShells.nix @@ -14,6 +14,7 @@ gha = ciBaseInputs ++ (with pkgs; [ + act gh zizmor ]); diff --git a/zmk-config/build.yaml b/zmk-config/build.yaml new file mode 100644 index 00000000..42b1b851 --- /dev/null +++ b/zmk-config/build.yaml @@ -0,0 +1,23 @@ +# This file generates the GitHub Actions matrix +# For simple board + shield combinations, add them +# to the top level board and shield arrays, for more +# control, add individual board + shield combinations to +# the `include` property, e.g: +# +# board: [ "nice_nano_v2" ] +# shield: [ "corne_left", "corne_right" ] +# include: +# - board: bdn9_rev2 +# - board: nice_nano_v2 +# shield: reviung41 +# +--- +include: + - board: nice_nano_v2 + shield: urchin_left nice_view_adapter nice_view_gem + snippet: studio-rpc-usb-uart + cmake-args: -DCONFIG_ZMK_STUDIO=y + - board: nice_nano_v2 + shield: urchin_right nice_view_adapter nice_view_gem + - board: nice_nano_v2 + shield: settings_reset diff --git a/zmk-config/urchin.conf b/zmk-config/urchin.conf new file mode 100644 index 00000000..55f5c11b --- /dev/null +++ b/zmk-config/urchin.conf @@ -0,0 +1,30 @@ +# enable bluetooth +CONFIG_BT=y + +# increase bluetooth signal power +CONFIG_BT_CTLR_TX_PWR_PLUS_8=y + +# enable deep sleep support +CONFIG_ZMK_SLEEP=y + +# the keyboard disconnects from bluetooth. +# uses very little power, but it may take +# a few seconds to reconnect after waking. +# 1.800.000 ms = 30 minutes +CONFIG_ZMK_IDLE_SLEEP_TIMEOUT=1800000 + +# "Eager Debouncing" +# Trying to lower the input lag. +# CONFIG_ZMK_KSCAN_DEBOUNCE_PRESS_MS=1 +# CONFIG_ZMK_KSCAN_DEBOUNCE_RELEASE_MS=5 + +# --- DISPLAY SETTINGS --- + +# enable display +CONFIG_ZMK_DISPLAY=y +# custom status screen (nice-view-gem) +CONFIG_ZMK_DISPLAY_STATUS_SCREEN_CUSTOM=y +# turn off gem animation to save battery +CONFIG_NICE_VIEW_GEM_ANIMATION=n + +# source: https://github.com/M165437/nice-view-gem/blob/main/README.md diff --git a/zmk-config/urchin.json b/zmk-config/urchin.json new file mode 100644 index 00000000..15798f7f --- /dev/null +++ b/zmk-config/urchin.json @@ -0,0 +1,46 @@ +{ + "layouts": { + "default_transform": { + "layout": [ + { "row": 0, "col": 0, "x": -2, "y": 0.75 }, + { "row": 0, "col": 1, "x": -1, "y": 0 }, + { "row": 0, "col": 2, "x": 0, "y": -0.25 }, + { "row": 0, "col": 3, "x": 1, "y": 0 }, + { "row": 0, "col": 4, "x": 2, "y": 0.25 }, + { "row": 0, "col": 5, "x": 6, "y": 0.25 }, + { "row": 0, "col": 6, "x": 7, "y": 0 }, + { "row": 0, "col": 7, "x": 8, "y": -0.25 }, + { "row": 0, "col": 8, "x": 9, "y": 0 }, + { "row": 0, "col": 9, "x": 10, "y": 0.5 }, + + { "row": 1, "col": 0, "x": -2, "y": 1.75 }, + { "row": 1, "col": 1, "x": -1, "y": 1 }, + { "row": 1, "col": 2, "x": 0, "y": 0.75 }, + { "row": 1, "col": 3, "x": 1, "y": 1 }, + { "row": 1, "col": 4, "x": 2, "y": 1.25 }, + { "row": 1, "col": 5, "x": 6, "y": 1.25 }, + { "row": 1, "col": 6, "x": 7, "y": 1 }, + { "row": 1, "col": 7, "x": 8, "y": 0.75 }, + { "row": 1, "col": 8, "x": 9, "y": 1 }, + { "row": 1, "col": 9, "x": 10, "y": 1.5 }, + + { "row": 2, "col": 0, "x": -2, "y": 2.75 }, + { "row": 2, "col": 1, "x": -1, "y": 2 }, + { "row": 2, "col": 2, "x": 0, "y": 1.75 }, + { "row": 2, "col": 3, "x": 1, "y": 2 }, + { "row": 2, "col": 4, "x": 2, "y": 2.25 }, + { "row": 2, "col": 5, "x": 6, "y": 2.25 }, + { "row": 2, "col": 6, "x": 7, "y": 2 }, + { "row": 2, "col": 7, "x": 8, "y": 1.75 }, + { "row": 2, "col": 8, "x": 9, "y": 2 }, + { "row": 2, "col": 9, "x": 10, "y": 2.5 }, + + { "row": 3, "col": 3, "x": 0.5, "y": 3.75, "r": 15, "rx": 2.98, "ry": 8.395 }, + { "row": 3, "col": 4, "x": 0.75, "y": 3.5, "r": 30, "rx": 1.73, "ry": 7.895 }, + { "row": 3, "col": 5, "x": 7.75, "y": 4, "r": -30, "rx": 6.48, "ry": 9.145 }, + { "row": 3, "col": 6, "x": 7.5, "y": 3.57, "r": -15, "rx": 6.73, "ry": 8.395 } + ] + } + }, + "sensors": [] +} diff --git a/zmk-config/urchin.keymap b/zmk-config/urchin.keymap new file mode 100644 index 00000000..44be38cf --- /dev/null +++ b/zmk-config/urchin.keymap @@ -0,0 +1,90 @@ +#include +#include + +&mt { + flavor = "tap-preferred"; +}; + +/ { + combos { + compatible = "zmk,combos"; + combo_left_return { + timeout-ms = <50>; + key-positions = <11 12>; + bindings = <&kp RETURN>; + }; + combo_left_esc { + timeout-ms = <50>; + key-positions = <10 11 12 13>; + bindings = <&kp ESC>; + }; + combo_right_return { + timeout-ms = <50>; + key-positions = <17 18>; + bindings = <&kp RETURN>; + }; + combo_right_esc { + timeout-ms = <50>; + key-positions = <16 17 18 19>; + bindings = <&kp ESC>; + }; + combo_mute { + timeout-ms = <50>; + key-positions = <8 9>; + bindings = <&kp C_MUTE>; + layers = <3 4>; + }; + }; + keymap { + compatible = "zmk,keymap"; + layer_0 { + bindings = < + &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P + &mt LCTRL A &mt LGUI S &mt LALT D &mt LSHIFT F &kp G &kp H &mt RSHIFT J &mt RALT K &mt RGUI L &mt RCTRL SEMICOLON + &kp Z &kp X &kp C &kp V &kp B &kp N &kp M &kp COMMA &kp DOT &kp SLASH + &mt LSHIFT ESC < 1 SPACE < 2 BACKSPACE &mt RGUI RETURN + >; + }; + layer_1 { + bindings = < + &none &none &none &none &none &kp HOME &kp PG_DN &kp PG_UP &kp END &kp DEL + &kp LCTRL &kp LGUI &kp LALT &kp LSHIFT &none &kp LEFT &mt RSHIFT DOWN &mt RALT UP &mt RGUI RIGHT &kp RCTRL + &none &none &none &none &none &none &none &none &none &kp INS + &mt LSHIFT ESC &none &mo 3 &mt RGUI RETURN + >; + }; + layer_2 { + bindings = < + &none &none &kp LBKT &kp RBKT &none &kp PIPE &none &none &none &none + &mt LCTRL N1 &mt LGUI N2 &mt LALT N3 &mt LSHIFT N4 &kp N5 &kp N6 &mt RSHIFT N7 &mt RALT N8 &mt RGUI N9 &kp N0 + &kp LS(N9) &kp LS(N0) &kp EQUAL &kp PLUS &kp MINUS &kp BACKSLASH &kp SINGLE_QUOTE &kp DOUBLE_QUOTES &kp TILDE &kp GRAVE + &mt LSHIFT ESC < 3 TAB &none &mt RGUI RETURN + >; + }; + layer_3 { + bindings = < + &none &none &none &none &none &kp C_PREV &kp C_VOL_DN &kp C_VOL_UP &kp C_NEXT &kp C_PLAY_PAUSE + &mt LCTRL F1 &mt LGUI F2 &mt LALT F3 &mt LSHIFT F4 &kp F5 &kp F6 &mt RSHIFT F7 &mt RALT F8 &mt RGUI F9 &kp F10 + &none &none &kp F11 &none &none &none &none &kp F12 &none &tog 4 + &mt LSHIFT ESC &none &none &mt RGUI RETURN + >; + }; + layer_4 { + bindings = < + &kp LALT &kp Q &kp W &kp E &kp R &kp C_PREV &kp C_VOL_DN &kp C_VOL_UP &kp C_NEXT &kp C_PLAY_PAUSE + &kp LSHIFT &kp A &kp S &kp D &kp F &none &none &none &none &none + &kp LCTRL &kp Z &kp X &kp C &kp V &none &none &none &none &none + &kp ESC &kp SPACE &tog 4 &none + >; + }; + }; +}; + +// template { +// bindings = < +// &none &none &none &none &none &none &none &none &none &none +// &kp LCTRL &kp LGUI &kp LALT &kp LSHIFT &none &none &kp RSHIFT &kp RALT &kp RGUI &kp RCTRL +// &none &none &none &none &none &none &none &none &none &none +// &none &none &none &none +// >; +// }; */ diff --git a/zmk-config/west.yml b/zmk-config/west.yml new file mode 100644 index 00000000..5e6c578d --- /dev/null +++ b/zmk-config/west.yml @@ -0,0 +1,27 @@ +manifest: + remotes: + # zmk official + - name: zmkfirmware + url-base: https://github.com/zmkfirmware + # bravekarma + - name: caksoylar + url-base: https://github.com/caksoylar + # kyek / duckyb + - name: duckyb + url-base: https://github.com/duckyb + # nice-view-gem + - name: m165437 + url-base: https://github.com/M165437 + projects: + - name: zmk + remote: zmkfirmware + revision: v0.3.0 + import: app/west.yml + - name: urchin-zmk-module + remote: duckyb + revision: main + - name: nice-view-gem + remote: m165437 + revision: v0.3.0 + self: + path: config