From ecbe3d054c9d1b2e60acac635844dc27902a5fc2 Mon Sep 17 00:00:00 2001 From: Kurtis Lew Date: Mon, 23 Oct 2023 20:53:01 -0700 Subject: [PATCH] feat(boards): Implement altar_i_uk --- app/boards/arm/altar_i_uk/CMakeLists.txt | 5 + app/boards/arm/altar_i_uk/Kconfig.board | 6 + app/boards/arm/altar_i_uk/Kconfig.defconfig | 28 +++ .../arm/altar_i_uk/altar_i_uk-pinctrl.dtsi | 18 ++ app/boards/arm/altar_i_uk/altar_i_uk.dts | 172 ++++++++++++++++++ app/boards/arm/altar_i_uk/altar_i_uk.keymap | 114 ++++++++++++ app/boards/arm/altar_i_uk/altar_i_uk.yaml | 14 ++ app/boards/arm/altar_i_uk/altar_i_uk.zmk.yml | 12 ++ .../arm/altar_i_uk/altar_i_uk_defconfig | 43 +++++ app/boards/arm/altar_i_uk/board.cmake | 6 + app/boards/arm/altar_i_uk/led_indicator.c | 126 +++++++++++++ 11 files changed, 544 insertions(+) create mode 100644 app/boards/arm/altar_i_uk/CMakeLists.txt create mode 100644 app/boards/arm/altar_i_uk/Kconfig.board create mode 100644 app/boards/arm/altar_i_uk/Kconfig.defconfig create mode 100644 app/boards/arm/altar_i_uk/altar_i_uk-pinctrl.dtsi create mode 100644 app/boards/arm/altar_i_uk/altar_i_uk.dts create mode 100644 app/boards/arm/altar_i_uk/altar_i_uk.keymap create mode 100644 app/boards/arm/altar_i_uk/altar_i_uk.yaml create mode 100644 app/boards/arm/altar_i_uk/altar_i_uk.zmk.yml create mode 100644 app/boards/arm/altar_i_uk/altar_i_uk_defconfig create mode 100644 app/boards/arm/altar_i_uk/board.cmake create mode 100644 app/boards/arm/altar_i_uk/led_indicator.c diff --git a/app/boards/arm/altar_i_uk/CMakeLists.txt b/app/boards/arm/altar_i_uk/CMakeLists.txt new file mode 100644 index 00000000000..15d0d231551 --- /dev/null +++ b/app/boards/arm/altar_i_uk/CMakeLists.txt @@ -0,0 +1,5 @@ +# Copyright (c) 2023 The ZMK Contributors +# SPDX-License-Identifier: MIT + +zephyr_library() +zephyr_library_include_directories(${CMAKE_SOURCE_DIR}/include) diff --git a/app/boards/arm/altar_i_uk/Kconfig.board b/app/boards/arm/altar_i_uk/Kconfig.board new file mode 100644 index 00000000000..1c9d18523c9 --- /dev/null +++ b/app/boards/arm/altar_i_uk/Kconfig.board @@ -0,0 +1,6 @@ +# Copyright (c) 2023 The ZMK Contributors +# SPDX-License-Identifier: MIT + +config BOARD_ALTAR_I_UK + bool "Altar I Keyboard" + depends on SOC_NRF52840_QIAA diff --git a/app/boards/arm/altar_i_uk/Kconfig.defconfig b/app/boards/arm/altar_i_uk/Kconfig.defconfig new file mode 100644 index 00000000000..8258095cebc --- /dev/null +++ b/app/boards/arm/altar_i_uk/Kconfig.defconfig @@ -0,0 +1,28 @@ +# Copyright (c) 2023 The ZMK Contributors +# SPDX-License-Identifier: MIT + +if BOARD_ALTAR_I_UK + +config ZMK_KEYBOARD_NAME + default "Altar I" + +if USB + +config USB_NRFX + default y + +config USB_DEVICE_STACK + default y + +endif # USB + +config BT_CTLR + default BT + +config ZMK_BLE + default y + +config ZMK_USB + default y + +endif # BOARD_ALTAR_I_UK diff --git a/app/boards/arm/altar_i_uk/altar_i_uk-pinctrl.dtsi b/app/boards/arm/altar_i_uk/altar_i_uk-pinctrl.dtsi new file mode 100644 index 00000000000..32e3dee45c3 --- /dev/null +++ b/app/boards/arm/altar_i_uk/altar_i_uk-pinctrl.dtsi @@ -0,0 +1,18 @@ +/* +* Copyright (c) 2023 The ZMK Contributors +* SPDX-License-Identifier: MIT +*/ + +&pinctrl { + pwm0_default: pwm0_default { + group1 { + psels = ; + }; + }; + pwm0_sleep: pwm0_sleep { + group1 { + psels = ; + low-power-enable; + }; + }; +}; diff --git a/app/boards/arm/altar_i_uk/altar_i_uk.dts b/app/boards/arm/altar_i_uk/altar_i_uk.dts new file mode 100644 index 00000000000..3b9594c4cca --- /dev/null +++ b/app/boards/arm/altar_i_uk/altar_i_uk.dts @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2023 The ZMK Contributors + * SPDX-License-Identifier: MIT + */ + +/dts-v1/; +#include +#include + +#include "altar_i_uk-pinctrl.dtsi" + +/ { + model = "Altar I UK, Rev 1"; + compatible = "altariuk,rev1"; + + chosen { + zephyr,code-partition = &code_partition; + zephyr,sram = &sram0; + zephyr,flash = &flash0; + zephyr,console = &cdc_acm_uart; + zmk,battery = &vbatt; + zmk,kscan = &kscan; + zmk,matrix_transform = &default_transform; + }; + + default_transform: keymap_transform_0 { + compatible = "zmk,matrix-transform"; + columns = <10>; + rows = <12>; + map = < + RC(0,0) RC(1,0) RC(0,1) RC(1,1) RC(0,2) RC(1,2) RC(0,3) RC(1,3) RC(0,4) RC(1,4) RC(0,5) RC(1,5) RC(0,6) RC(1,6) RC(7,6) + RC(2,0) RC(3,0) RC(2,1) RC(3,1) RC(2,2) RC(3,2) RC(2,3) RC(3,3) RC(2,4) RC(3,4) RC(2,5) RC(3,5) RC(2,6) RC(3,6) RC(0,7) + RC(4,0) RC(5,0) RC(4,1) RC(5,1) RC(4,2) RC(5,2) RC(4,3) RC(5,3) RC(4,4) RC(5,4) RC(4,5) RC(5,5) RC(4,6) RC(5,6) RC(0,8) + RC(6,0) RC(7,0) RC(6,1) RC(7,1) RC(6,2) RC(7,2) RC(6,3) RC(7,3) RC(6,4) RC(7,4) RC(6,5) RC(7,5) RC(6,6) RC(0,9) + RC(8,0) RC(9,0) RC(8,1) RC(9,1) RC(8,2) RC(9,2) RC(8,3) RC(9,3) RC(8,4) RC(9,4) RC(8,5) RC(9,5) RC(8,6) RC(9,6) + RC(10,0) RC(11,0) RC(10,1) RC(11,1) RC(10,4) RC(11,4) RC(10,5) RC(11,5) RC(10,6) RC(11,6) + >; + }; + + + kscan: kscan_composite { + compatible = "zmk,kscan-composite"; + rows = <12>; + columns = <10>; + + toggle { + kscan = <&kscan_toggle>; + column-offset = <7>; + }; + + main { + kscan = <&kscan_main>; + }; + }; + + kscan_toggle: kscan_toggle { + compatible = "zmk,kscan-gpio-direct"; + toggle-mode; + input-gpios + = <&gpio1 11 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&gpio1 10 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&gpio1 13 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + ; + }; + + kscan_main: kscan_main { + compatible = "zmk,kscan-gpio-matrix"; + diode-direction = "row2col"; + row-gpios + = <&gpio1 1 GPIO_ACTIVE_HIGH> + , <&gpio1 3 GPIO_ACTIVE_HIGH> + , <&gpio1 4 GPIO_ACTIVE_HIGH> + , <&gpio1 6 GPIO_ACTIVE_HIGH> + , <&gpio0 9 GPIO_ACTIVE_HIGH> + , <&gpio0 10 GPIO_ACTIVE_HIGH> + , <&gpio0 31 GPIO_ACTIVE_HIGH> + , <&gpio0 30 GPIO_ACTIVE_HIGH> + , <&gpio0 29 GPIO_ACTIVE_HIGH> + , <&gpio0 28 GPIO_ACTIVE_HIGH> + , <&gpio0 2 GPIO_ACTIVE_HIGH> + , <&gpio1 15 GPIO_ACTIVE_HIGH> + ; + col-gpios + = <&gpio0 4 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&gpio0 5 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&gpio0 7 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&gpio1 9 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&gpio0 12 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&gpio0 23 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&gpio1 2 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + ; + }; + + sensors { + compatible = "zmk,keymap-sensors"; + sensors = <&left_encoder>; + }; + + left_encoder: encoder_left { + compatible = "alps,ec11"; + label = "LEFT_ENCODER"; + a-gpios = <&gpio1 00 (GPIO_ACTIVE_HIGH | GPIO_PULL_UP)>; + b-gpios = <&gpio0 22 (GPIO_ACTIVE_HIGH | GPIO_PULL_UP)>; + resolution = <2>; + }; + + vbatt: vbatt { + compatible = "zmk,battery-voltage-divider"; + io-channels = <&adc 1>; + output-ohms = <2000000>; + full-ohms = <(2000000 + 820000)>; + }; +}; + +&adc { + status = "okay"; +}; + +&gpio0 { + status = "okay"; +}; + +&gpio1 { + status = "okay"; +}; + +&usbd { + status = "okay"; + cdc_acm_uart: cdc_acm_uart { + compatible = "zephyr,cdc-acm-uart"; + }; +}; + +&flash0 { + /* + * For more information, see: + * http://docs.zephyrproject.org/latest/devices/dts/flash_partitions.html + */ + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + sd_partition: partition@0 { + label = "softdevice"; + reg = <0x00000000 0x00026000>; + }; + code_partition: partition@26000 { + label = "code_partition"; + reg = <0x00026000 0x000c6000>; + }; + + /* + * The flash starting at 0x000ec000 and ending at + * 0x000f3fff is reserved for use by the application. + */ + + /* + * Storage partition will be used by FCB/LittleFS/NVS + * if enabled. + */ + storage_partition: partition@ec000 { + label = "storage"; + reg = <0x000ec000 0x00008000>; + }; + + boot_partition: partition@f4000 { + label = "adafruit_boot"; + reg = <0x000f4000 0x0000c000>; + }; + }; +}; diff --git a/app/boards/arm/altar_i_uk/altar_i_uk.keymap b/app/boards/arm/altar_i_uk/altar_i_uk.keymap new file mode 100644 index 00000000000..1aa5b47b3c9 --- /dev/null +++ b/app/boards/arm/altar_i_uk/altar_i_uk.keymap @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2023 The ZMK Contributors + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include +#include + +/ { + macros { + tog_bt0: tog_bt0 { + label = "tog_bt0"; + compatible = "zmk,behavior-macro"; + #binding-cells = <0>; + wait-ms = <40>; + tap-ms = <40>; + bindings + = <&out OUT_BLE &bt BT_SEL 0> + ; + }; + tog_bt1: tog_bt1 { + label = "tog_bt1"; + compatible = "zmk,behavior-macro"; + #binding-cells = <0>; + wait-ms = <40>; + tap-ms = <40>; + bindings + = <&out OUT_BLE &bt BT_SEL 1> + ; + }; + + zoom_in: zoom_in { + label = "zoom_in"; + compatible = "zmk,behavior-macro"; + #binding-cells = <0>; + bindings + = <¯o_press &kp LSHIFT &kp LGUI> + , <¯o_tap &kp EQUAL> + , <¯o_release &kp LSHIFT &kp LGUI> + ; + }; + }; +}; + +/ { + keymap { + compatible = "zmk,keymap"; + + default_layer { +// ----------------------------------------------------------------------------------------- +// | ESC | BRI_DN| BRI_UP| F3 | F4 | F5 | F6 | F7 | F8 | PREV | P/P | NEXT | MUTE | P/P | PAIR | +// | ~ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | - | = | BSPC | BT2 | +// | TAB | Q | W | E | R | T | Y | U | I | O | P | [ | ] | ENTER | BT1 | +// | CAPS | A | S | D | F | G | H | J | K | L | ; | ' | NUHS | | USB | +// | SHIFT | NUBS | Z | X | C | V | B | N | M | , | . | / | SHFT | UP | +// | FN1 | CTRL | ALT | WIN | SPACE | WIN | ALT | LFT | DWN | RGT | + + bindings = < + &kp ESC &kp C_BRI_DN &kp C_BRI_UP &kp F3 &kp F4 &kp F5 &kp F6 &kp F7 &kp F8 &kp C_PREV &kp C_PLAY_PAUSE &kp C_NEXT &kp K_MUTE &kp C_PLAY_PAUSE &bt BT_CLR + &kp NON_US_BSLH &kp N1 &kp N2 &kp N3 &kp N4 &kp N5 &kp N6 &kp N7 &kp N8 &kp N9 &kp N0 &kp MINUS &kp EQUAL &kp BSPC &tog_bt1 + &kp TAB &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P &kp LBKT &kp RBKT &kp RET &tog_bt0 + &kp CAPS &kp A &kp S &kp D &kp F &kp G &kp H &kp J &kp K &kp L &kp SEMI &kp SQT &kp NON_US_HASH &out OUT_USB + &kp LSHIFT &kp GRAVE &kp Z &kp X &kp C &kp V &kp B &kp N &kp M &kp COMMA &kp DOT &kp FSLH &kp RSHIFT &kp UP + &mo 1 &kp LCTRL &kp LALT &kp LGUI &kp SPACE &kp RGUI &kp RALT &kp LEFT &kp DOWN &kp RIGHT + >; + + sensor-bindings = <&inc_dec_kp C_VOL_UP C_VOL_DN>; + }; + + fn_layer { +// ----------------------------------------------------------------------------------------- +// | | F1 | F2 | | | | | | | F9 | F10 | F11 | F12 | | +// | | | | | | | | | | | | | | | +// | | | | | | | | | | | | | | | +// | | | | | | | | | | | | | | | RESET | +// | | | | | | | | | | | | | | | +// | | FN2 | | | | | | | | | + + bindings = < + &trans &kp F1 &kp F2 &trans &trans &trans &trans &trans &trans &kp F9 &kp F10 &kp F11 &kp F12 &trans &sys_reset + &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans + &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans + &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans + &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans + &trans &mo 2 &trans &trans &trans &trans &trans &trans &trans &trans + >; + + sensor-bindings = <&inc_dec_kp &zoom_in MINUS>; + }; + + sys_layer { +// ----------------------------------------------------------------------------------------- +// | | | | | | | | | | | | | | | +// | | | | | | | | | | | | | | | +// | | | | | | | | | | | | | | | +// | | | | | | | | | | | | | | | BOOTLOADER | +// | | | | | | | | | | | | | | | +// | | | | | | | | | | | + + bindings = < + &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &bootloader + &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &sys_reset + &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &sys_reset + &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &sys_reset + &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans + &trans &mo 2 &trans &trans &trans &trans &trans &trans &trans &trans + >; + + sensor-bindings =<&inc_dec_kp C_PREV C_NEXT>; + }; + }; +}; \ No newline at end of file diff --git a/app/boards/arm/altar_i_uk/altar_i_uk.yaml b/app/boards/arm/altar_i_uk/altar_i_uk.yaml new file mode 100644 index 00000000000..4698b4b385a --- /dev/null +++ b/app/boards/arm/altar_i_uk/altar_i_uk.yaml @@ -0,0 +1,14 @@ +identifier: ALTAR_I_UK +name: ALTAR I - UK +type: keyboard +arch: arm +toolchain: + - zephyr + - gnuarmemb +supported: + - adc + - usb_device + - ble + - ieee802154 + - pwm + - watchdog diff --git a/app/boards/arm/altar_i_uk/altar_i_uk.zmk.yml b/app/boards/arm/altar_i_uk/altar_i_uk.zmk.yml new file mode 100644 index 00000000000..2f66314b291 --- /dev/null +++ b/app/boards/arm/altar_i_uk/altar_i_uk.zmk.yml @@ -0,0 +1,12 @@ +file_format: "1" +id: altar_i_uk +name: ALTAR I - UK +type: board +arch: arm +features: + - keys + - encoder +outputs: + - usb + - ble +url: https://electronicmaterialsoffice.com/ diff --git a/app/boards/arm/altar_i_uk/altar_i_uk_defconfig b/app/boards/arm/altar_i_uk/altar_i_uk_defconfig new file mode 100644 index 00000000000..c7a8064e139 --- /dev/null +++ b/app/boards/arm/altar_i_uk/altar_i_uk_defconfig @@ -0,0 +1,43 @@ +# Copyright (c) 2023 The ZMK Contributors +# SPDX-License-Identifier: MIT + +CONFIG_SOC_SERIES_NRF52X=y +CONFIG_SOC_NRF52840_QIAA=y +CONFIG_BOARD_ALTAR_I_UK=y + +# Enable MPU +CONFIG_ARM_MPU=y + +# enable GPIO +CONFIG_GPIO=y + +CONFIG_USE_DT_CODE_PARTITION=y +CONFIG_BUILD_OUTPUT_UF2=y + +CONFIG_MPU_ALLOW_FLASH_WRITE=y +CONFIG_NVS=y +CONFIG_SETTINGS_NVS=y +CONFIG_FLASH=y +CONFIG_FLASH_PAGE_LAYOUT=y +CONFIG_FLASH_MAP=y + +# Enable the rotary encoder +CONFIG_EC11=y +CONFIG_EC11_TRIGGER_GLOBAL_THREAD=y + +# Enable LEDs +CONFIG_LED=y +CONFIG_LED_PWM=y +CONFIG_PWM=y +CONFIG_PINCTRL=y + +# Boost Bluetooth power +CONFIG_BT_CTLR_TX_PWR_PLUS_8=y + +# Enable and set deep sleep time +CONFIG_ZMK_SLEEP=y +CONFIG_ZMK_IDLE_SLEEP_TIMEOUT=7200000 +CONFIG_ZMK_BATTERY_REPORT_INTERVAL=180 + +# Windows Battery Reporting Fix +CONFIG_BT_GATT_ENFORCE_SUBSCRIPTION=n diff --git a/app/boards/arm/altar_i_uk/board.cmake b/app/boards/arm/altar_i_uk/board.cmake new file mode 100644 index 00000000000..dd78cfd93d8 --- /dev/null +++ b/app/boards/arm/altar_i_uk/board.cmake @@ -0,0 +1,6 @@ +# Copyright (c) 2023 The ZMK Contributors +# SPDX-License-Identifier: MIT + +board_runner_args(nrfjprog "--nrf-family=NRF52" "--softreset") +include(${ZEPHYR_BASE}/boards/common/blackmagicprobe.board.cmake) +include(${ZEPHYR_BASE}/boards/common/nrfjprog.board.cmake) diff --git a/app/boards/arm/altar_i_uk/led_indicator.c b/app/boards/arm/altar_i_uk/led_indicator.c new file mode 100644 index 00000000000..a570647b466 --- /dev/null +++ b/app/boards/arm/altar_i_uk/led_indicator.c @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2023 The ZMK Contributors + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#define INITIAL_DELAY 100 +#define BLINK_INTERVAL 300 +#define CAPS_LOCK_MASK 0x02 + +static const struct device *led_dev = DEVICE_DT_GET(DT_CHOSEN(zmk_led_indicator)); + +enum BOARD_STATE { + BOARD_BOOTING_1 = 1, + BOARD_BOOTING_2 = 2, + BOARD_PAIRING_1 = 3, + BOARD_PAIRING_2 = 4, + BOARD_READY = 5, +} static board_state; + +static int indicator_callback() { + if (board_state < BOARD_READY) { + return ZMK_EV_EVENT_BUBBLE; + } + + zmk_hid_indicators indicators = zmk_hid_indicators_get_current_profile(); + + if (indicators & CAPS_LOCK_MASK) { + led_on(led_dev, 0); + } else { + led_off(led_dev, 0); + } + + return ZMK_EV_EVENT_BUBBLE; +} + +ZMK_LISTENER(indicator_callback, indicator_callback); +ZMK_SUBSCRIPTION(indicator_callback, zmk_hid_indicators_changed); + +static void blink_callback(); + +static K_WORK_DELAYABLE_DEFINE(blink_work, blink_callback); + +static void blink_callback() { + if (board_state == BOARD_PAIRING_1) { + led_on(led_dev, 0); + board_state = BOARD_PAIRING_2; + + } else { + led_off(led_dev, 0); + board_state = BOARD_PAIRING_1; + } + + k_work_schedule(&blink_work, K_MSEC(BLINK_INTERVAL)); +} + +static int pairing_callback() { + if (board_state < BOARD_PAIRING_1) { + return ZMK_EV_EVENT_BUBBLE; + } + + if (zmk_ble_active_profile_is_open()) { + board_state = BOARD_PAIRING_1; + led_off(led_dev, 0); + k_work_schedule(&blink_work, K_MSEC(BLINK_INTERVAL)); + + } else { + k_work_cancel_delayable(&blink_work); + board_state = BOARD_READY; + indicator_callback(); + } + + return ZMK_EV_EVENT_BUBBLE; +} + +ZMK_LISTENER(pairing_callback, pairing_callback); +ZMK_SUBSCRIPTION(pairing_callback, zmk_ble_active_profile_changed); + +void booting_callback(); + +static K_WORK_DELAYABLE_DEFINE(booting_work, booting_callback); + +void booting_callback() { + if (board_state == BOARD_BOOTING_1) { + led_on(led_dev, 0); + board_state = BOARD_BOOTING_2; + k_work_schedule(&booting_work, K_MSEC(BLINK_INTERVAL)); + + } else if (board_state == BOARD_BOOTING_2) { + led_off(led_dev, 0); + board_state = BOARD_READY; + k_work_schedule(&booting_work, K_MSEC(BLINK_INTERVAL)); + + } else { + pairing_callback(); + } +} + +static int led_indicator_init() { + if (!device_is_ready(led_dev)) { + LOG_ERR("Caps Lock device is not ready"); + return -ENODEV; + } + + board_state = BOARD_BOOTING_1; + led_off(led_dev, 0); + k_work_schedule(&booting_work, K_MSEC(INITIAL_DELAY)); + + return 0; +} + +SYS_INIT(led_indicator_init, APPLICATION, 99);