Full Code of dakhnod/BLEnky for AI

main 239ff4abdf82 cached
137 files
678.0 KB
196.5k tokens
276 symbols
1 requests
Download .txt
Showing preview only (722K chars total). Download the full file or copy to clipboard to get everything.
Repository: dakhnod/BLEnky
Branch: main
Commit: 239ff4abdf82
Files: 137
Total size: 678.0 KB

Directory structure:
gitextract_i37ezxwn/

├── .github/
│   └── workflows/
│       ├── build.yml
│       ├── firmware-test.yml
│       ├── jinja.yml
│       ├── release-build.yml
│       └── release-build.yml.template
├── .gitignore
├── .gitmodules
├── CITATION.cff
├── LICENSE
├── Makefile
├── README.md
├── blink.gpioasm
├── docs/
│   ├── AUTOMATION_IO_SERVICE.md
│   ├── BINARY_SENSOR_SERVICE.md
│   ├── COMPILATION.md
│   ├── CONFIGURATION.md
│   ├── FEATURES.md
│   ├── FIRST_STEPS.md
│   ├── FLASHING.md
│   ├── GPIO_ASM_SERVICE.md
│   └── boards/
│       ├── GENERIC.md
│       ├── GENERIC_UF2.md
│       ├── NORDIC_DFU.md
│       ├── NRF52840_DONGLE.md
│       ├── PRO_MICRO.md
│       └── XIAO_BLE.md
├── openocd/
│   └── raspi-bcm2385.tcl
├── src/
│   ├── ble/
│   │   ├── helpers/
│   │   │   ├── ble_helpers.c
│   │   │   └── ble_helpers.h
│   │   ├── sensor_ble.c
│   │   ├── sensor_ble.h
│   │   └── services/
│   │       ├── automation_io/
│   │       │   ├── ble_automation_io_service.c
│   │       │   └── ble_automation_io_service.h
│   │       ├── binary_sensor/
│   │       │   ├── ble_binary_sensor_service.c
│   │       │   └── ble_binary_sensor_service.h
│   │       ├── ble_gpio_asm/
│   │       │   ├── ble_gpio_asm.c
│   │       │   └── ble_gpio_asm.h
│   │       ├── configuration/
│   │       │   ├── ble_configuration_service.c
│   │       │   └── ble_configuration_service.h
│   │       ├── cycling_speed_cadence/
│   │       │   ├── ble_cycling_speed_cadence.c
│   │       │   └── ble_cycling_speed_cadence.h
│   │       ├── hid/
│   │       │   ├── ble_hid.c
│   │       │   └── ble_hid.h
│   │       └── temperature/
│   │           ├── ble_temperature_service.c
│   │           └── ble_temperature_service.h
│   ├── config/
│   │   ├── .gitignore
│   │   ├── bsp/
│   │   │   └── generated/
│   │   │       ├── 96boards_96b_nitrogen.h
│   │   │       ├── aconno_acn52832.h
│   │   │       ├── adafruit_adafruit_feather_nrf52840.h
│   │   │       ├── adafruit_adafruit_itsybitsy.h
│   │   │       ├── adafruit_nrf52_adafruit_feather.h
│   │   │       ├── arduino_arduino_nano_33_ble.h
│   │   │       ├── arduino_arduino_nicla_sense_me.h
│   │   │       ├── atmarktechno_degu_evk.h
│   │   │       ├── bbc_bbc_microbit.h
│   │   │       ├── bcdevices_blueclover_plt_demo_v2.h
│   │   │       ├── croxel_croxel_cx1825.h
│   │   │       ├── ebyte_ebyte_e73_tbb.h
│   │   │       ├── electronut_nrf52840_blip.h
│   │   │       ├── electronut_nrf52840_papyr.h
│   │   │       ├── ezurio_bl652_dvk.h
│   │   │       ├── ezurio_bl654_dvk.h
│   │   │       ├── ezurio_bl654_sensor_board.h
│   │   │       ├── ezurio_bl654_usb.h
│   │   │       ├── ezurio_bt510.h
│   │   │       ├── ezurio_bt610.h
│   │   │       ├── ezurio_mg100.h
│   │   │       ├── ezurio_pinnacle_100_dvk.h
│   │   │       ├── ezurio_rm1xx_dvk.h
│   │   │       ├── holyiot_holyiot_yj16019.h
│   │   │       ├── makerdiary_nrf52832_mdk.h
│   │   │       ├── makerdiary_nrf52840_mdk.h
│   │   │       ├── makerdiary_nrf52840_mdk_usb_dongle.h
│   │   │       ├── nordic_nrf21540dk.h
│   │   │       ├── nordic_nrf51dk.h
│   │   │       ├── nordic_nrf51dongle.h
│   │   │       ├── nordic_nrf52840dk.h
│   │   │       ├── nordic_nrf52840dongle.h
│   │   │       ├── nordic_nrf52dk.h
│   │   │       ├── nordic_nrf9160dk.h
│   │   │       ├── nordic_thingy52.h
│   │   │       ├── other_ctcc.h
│   │   │       ├── others_promicro_nrf52840.h
│   │   │       ├── panasonic_pan1770_evb.h
│   │   │       ├── panasonic_pan1780_evb.h
│   │   │       ├── particle_nrf51_blenano.h
│   │   │       ├── particle_nrf52_blenano2.h
│   │   │       ├── phytec_reel_board.h
│   │   │       ├── pine64_pinetime_devkit0.h
│   │   │       ├── qorvo_decawave_dwm1001_dev.h
│   │   │       ├── rakwireless_rak4631.h
│   │   │       ├── rakwireless_rak5010.h
│   │   │       ├── raytac_raytac_mdbt50q_db_40.h
│   │   │       ├── ruuvi_ruuvi_ruuvitag.h
│   │   │       ├── seeed_xiao_ble.h
│   │   │       ├── sparkfun_micromod.h
│   │   │       ├── sparkfun_nrf52_sparkfun.h
│   │   │       ├── u_blox_ubx_bmd300eval.h
│   │   │       ├── u_blox_ubx_bmd340eval.h
│   │   │       ├── u_blox_ubx_bmd345eval.h
│   │   │       ├── u_blox_ubx_bmd380eval.h
│   │   │       ├── u_blox_ubx_evkannab1.h
│   │   │       ├── u_blox_ubx_evkninab1.h
│   │   │       ├── u_blox_ubx_evkninab3.h
│   │   │       ├── vngiotlab_nrf51_vbluno51.h
│   │   │       ├── vngiotlab_nrf52_vbluno52.h
│   │   │       ├── waveshare_nrf51_ble400.h
│   │   │       ├── wurth_we_proteus2ev.h
│   │   │       └── wurth_we_proteus3ev.h
│   │   ├── feature_config.h
│   │   ├── feature_config.template.h.jinja
│   │   ├── nrf51/
│   │   │   └── sdk_config.h
│   │   └── nrf52/
│   │       └── sdk_config.h
│   ├── error_handler/
│   │   └── error_handler.c
│   ├── gpio/
│   │   ├── sensor_gpio.c
│   │   └── sensor_gpio.h
│   ├── helpers/
│   │   ├── encoding.c
│   │   └── encoding.h
│   ├── linker/
│   │   ├── nrf51822_qfaa.ld
│   │   ├── nrf51822_qfac.ld
│   │   ├── nrf52832_qfaa.ld
│   │   └── nrf52840_qfaa.ld
│   ├── main.c
│   ├── persistence/
│   │   ├── pin_configuration.c
│   │   └── pin_configuration.h
│   ├── sleep/
│   │   ├── sleep.c
│   │   └── sleep.h
│   ├── storage/
│   │   ├── preconfiguration.c
│   │   ├── preconfiguration.h
│   │   ├── storage.h
│   │   ├── storage.nrf51.c
│   │   └── storage.nrf52.c
│   ├── timer/
│   │   ├── sensor_timer.c
│   │   └── sensor_timer.h
│   └── watchdog/
│       ├── watchdog.c
│       └── watchdog.h
└── zephyr_convert.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/workflows/build.yml
================================================
name: Reusable build workflow

on: 
  workflow_call:
    inputs:
      chip:
        required: true
        type: string
      publish_hex_no_crystal:
        required: false
        type: boolean
        default: true
      publish_hex_crystal:
        required: false
        type: boolean
        default: true
      publish_zip_no_crystal:
        required: false
        type: boolean
        default: true
      publish_zip_no_crystal_with_sd:
        required: false
        type: boolean
        default: true
      publish_zip_crystal:
        required: false
        type: boolean
        default: true
      publish_zip_crystal_with_sd:
        required: false
        type: boolean
        default: true
      publish_uf2:
        required: false
        type: boolean
        default: true
      board:
        required: false
        type: string
        default: generic

jobs:
  build:
    runs-on: [self-hosted, firmware-builder]
    env:
      CHIP: ${{ inputs.chip }}
      BLE_ROOT: ../../../../
      BOARD: ${{ inputs.board }}

    steps:
    - uses: actions/checkout@v4
      with:
        submodules: true
    - name: parse feature template
      uses: cuchi/jinja2-action@v1.2.2
      with:
        template: src/config/feature_config.template.h.jinja
        output_file: src/config/feature_config.template.h

    - name: set PATH
      run: echo "../../../../" >> $GITHUB_PATH

    - name: build firmware zip (no crystal)
      if: ${{ inputs.publish_zip_no_crystal }}
      run: make sign
      env:
        LFCLK_SRC_XTAL: 0

    - name: build firmware hex (no crystal)
      if: ${{ inputs.publish_hex_no_crystal }}
      run: make hex
      env:
        LFCLK_SRC_XTAL: 0

    - name: build firmware + SD zip (no crystal)
      if: ${{ inputs.publish_zip_no_crystal_with_sd }}
      run: make sign_sd
      env:
        LFCLK_SRC_XTAL: 0

    - name: build firmware zip (crystal)
      if: ${{ inputs.publish_zip_crystal }}
      run: make sign
      env:
        LFCLK_SRC_XTAL: 1

    - name: build firmware hex (crystal)
      if: ${{ inputs.publish_hex_crystal }}
      run: make hex
      env:
        LFCLK_SRC_XTAL: 1

    - name: Download uf2conv.py
      if: ${{ inputs.publish_uf2 }}
      run: |
        wget https://raw.githubusercontent.com/microsoft/uf2/refs/heads/master/utils/uf2families.json
        wget https://raw.githubusercontent.com/microsoft/uf2/refs/heads/master/utils/uf2conv.py

    - name: Create uf2 file
      if: ${{ inputs.publish_uf2 }}
      run: |
        make uf2
      env:
        LFCLK_SRC_XTAL: 1

    - name: build firmware + SD (crystal)
      if: ${{ inputs.publish_zip_crystal_with_sd }}
      run: make sign_sd
      env:
        LFCLK_SRC_XTAL: 1

    - name: Upload firmwares to latest release
      uses: svenstaro/upload-release-action@2.7.0
      with:
        file: BLEnky*
        file_glob: true
        overwrite: true

================================================
FILE: .github/workflows/firmware-test.yml
================================================
name: Automated firmware test

on: [workflow_dispatch, push]

concurrency:
  group: firmware-test

jobs:
  build:
    runs-on: [self-hosted, firmware-builder]

    steps:
    - uses: actions/checkout@v4
      with:
        submodules: true
    - name: parse feature template
      uses: cuchi/jinja2-action@v1.2.2
      with:
        template: src/config/feature_config.template.h.jinja
        output_file: src/config/feature_config.template.h
    - name: build firmware
      run: |
        make BLE_ROOT=../../../../ CHIP=NRF51822 LFCLK_SRC_XTAL=1
    - name: store firmware
      uses: actions/upload-artifact@v4
      with:
        name: firmware-file
        path: _build/nrf51822_xxac.hex

  flash:
    runs-on: [self-hosted, firmware-flasher]
    needs: build

    steps:
    - name: delete old firmware
      run: rm -f /home/home/ram/nrf51822_xxac.hex
    - name: download firmware
      uses: actions/download-artifact@v4
      with:
        name: firmware-file
        path: /home/home/ram/
    - name: flash firmware
      run: echo -e "program /home/home/ram/nrf51822_xxac.hex verify; program /home/home/Projects/BLEnky/testing/settings.hex verify reset \n exit" | nc localhost 4444

  test:
    runs-on: [self-hosted, firmware-flasher]
    needs: flash

    steps:
    - uses: actions/checkout@v4
      with:
        repository: dakhnod/BLEnky-testing-kit
        submodules: true
        path: testing-kit
    - name: run test script
      run: python -u main.py -a "F2:B4:78:83:18:E1" -o 6 13 19 26 -i 12 16 20 21
      working-directory: testing-kit


================================================
FILE: .github/workflows/jinja.yml
================================================
name: Jinja build

on: [workflow_dispatch, push]

jobs:
  parse:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v4
    - name: parse feature template
      uses: rondefreitas/jinja2-action@v1.2.3
      with:
        template: src/config/feature_config.template.h.jinja
        output_file: src/config/feature_config.template.h
    - uses: stefanzweifel/git-auto-commit-action@v5
      with:
        commit_message: Auto-parse Jinja template
        status_options: '--untracked-files=all'
          


================================================
FILE: .github/workflows/release-build.yml
================================================
jobs:
  build_96boards_96b_nitrogen:
    uses: ./.github/workflows/build.yml
    with:
      board: 96boards_96b_nitrogen
      chip: NRF52832
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_aconno_acn52832:
    uses: ./.github/workflows/build.yml
    with:
      board: aconno_acn52832
      chip: NRF52832
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_adafruit_adafruit_feather_nrf52840:
    uses: ./.github/workflows/build.yml
    with:
      board: adafruit_adafruit_feather_nrf52840
      chip: NRF52840
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_adafruit_adafruit_itsybitsy:
    uses: ./.github/workflows/build.yml
    with:
      board: adafruit_adafruit_itsybitsy
      chip: NRF52840
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_adafruit_nrf52_adafruit_feather:
    uses: ./.github/workflows/build.yml
    with:
      board: adafruit_nrf52_adafruit_feather
      chip: NRF52832
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_arduino_arduino_nano_33_ble:
    uses: ./.github/workflows/build.yml
    with:
      board: arduino_arduino_nano_33_ble
      chip: NRF52840
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_arduino_arduino_nicla_sense_me:
    uses: ./.github/workflows/build.yml
    with:
      board: arduino_arduino_nicla_sense_me
      chip: NRF52832
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_atmarktechno_degu_evk:
    uses: ./.github/workflows/build.yml
    with:
      board: atmarktechno_degu_evk
      chip: NRF52840
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_bbc_bbc_microbit:
    uses: ./.github/workflows/build.yml
    with:
      board: bbc_bbc_microbit
      chip: NRF51822
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_bcdevices_blueclover_plt_demo_v2:
    uses: ./.github/workflows/build.yml
    with:
      board: bcdevices_blueclover_plt_demo_v2
      chip: NRF52832
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_croxel_croxel_cx1825:
    uses: ./.github/workflows/build.yml
    with:
      board: croxel_croxel_cx1825
      chip: NRF52840
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_ebyte_ebyte_e73_tbb:
    uses: ./.github/workflows/build.yml
    with:
      board: ebyte_ebyte_e73_tbb
      chip: NRF52832
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_electronut_nrf52840_blip:
    uses: ./.github/workflows/build.yml
    with:
      board: electronut_nrf52840_blip
      chip: NRF52840
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_electronut_nrf52840_papyr:
    uses: ./.github/workflows/build.yml
    with:
      board: electronut_nrf52840_papyr
      chip: NRF52840
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_ezurio_bl652_dvk:
    uses: ./.github/workflows/build.yml
    with:
      board: ezurio_bl652_dvk
      chip: NRF52832
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_ezurio_bl654_dvk:
    uses: ./.github/workflows/build.yml
    with:
      board: ezurio_bl654_dvk
      chip: NRF52840
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_ezurio_bl654_sensor_board:
    uses: ./.github/workflows/build.yml
    with:
      board: ezurio_bl654_sensor_board
      chip: NRF52840
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_ezurio_bl654_usb:
    uses: ./.github/workflows/build.yml
    with:
      board: ezurio_bl654_usb
      chip: NRF52840
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_ezurio_bt510:
    uses: ./.github/workflows/build.yml
    with:
      board: ezurio_bt510
      chip: NRF52840
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_ezurio_bt610:
    uses: ./.github/workflows/build.yml
    with:
      board: ezurio_bt610
      chip: NRF52840
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_ezurio_mg100:
    uses: ./.github/workflows/build.yml
    with:
      board: ezurio_mg100
      chip: NRF52840
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_ezurio_pinnacle_100_dvk:
    uses: ./.github/workflows/build.yml
    with:
      board: ezurio_pinnacle_100_dvk
      chip: NRF52840
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_ezurio_rm1xx_dvk:
    uses: ./.github/workflows/build.yml
    with:
      board: ezurio_rm1xx_dvk
      chip: NRF51822
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_holyiot_holyiot_yj16019:
    uses: ./.github/workflows/build.yml
    with:
      board: holyiot_holyiot_yj16019
      chip: NRF52832
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_makerdiary_nrf52832_mdk:
    uses: ./.github/workflows/build.yml
    with:
      board: makerdiary_nrf52832_mdk
      chip: NRF52832
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_makerdiary_nrf52840_mdk:
    uses: ./.github/workflows/build.yml
    with:
      board: makerdiary_nrf52840_mdk
      chip: NRF52840
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_makerdiary_nrf52840_mdk_usb_dongle:
    uses: ./.github/workflows/build.yml
    with:
      board: makerdiary_nrf52840_mdk_usb_dongle
      chip: NRF52840
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_nordic_nrf21540dk:
    uses: ./.github/workflows/build.yml
    with:
      board: nordic_nrf21540dk
      chip: NRF52840
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_nordic_nrf51dk:
    uses: ./.github/workflows/build.yml
    with:
      board: nordic_nrf51dk
      chip: NRF51822
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_nordic_nrf51dongle:
    uses: ./.github/workflows/build.yml
    with:
      board: nordic_nrf51dongle
      chip: NRF51822
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_nordic_nrf52840dk:
    uses: ./.github/workflows/build.yml
    with:
      board: nordic_nrf52840dk
      chip: NRF52840
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_nordic_nrf52840dongle:
    uses: ./.github/workflows/build.yml
    with:
      board: nordic_nrf52840dongle
      chip: NRF52840
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_nordic_nrf52dk:
    uses: ./.github/workflows/build.yml
    with:
      board: nordic_nrf52dk
      chip: NRF52832
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_nordic_nrf9160dk:
    uses: ./.github/workflows/build.yml
    with:
      board: nordic_nrf9160dk
      chip: NRF52840
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_nordic_thingy52:
    uses: ./.github/workflows/build.yml
    with:
      board: nordic_thingy52
      chip: NRF52832
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_nrf51822:
    uses: ./.github/workflows/build.yml
    with:
      chip: NRF51822
      publish_uf2: false
  build_nrf52832:
    uses: ./.github/workflows/build.yml
    with:
      chip: NRF52832
  build_nrf52840:
    uses: ./.github/workflows/build.yml
    with:
      chip: NRF52840
  build_other_ctcc:
    uses: ./.github/workflows/build.yml
    with:
      board: other_ctcc
      chip: NRF52840
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_others_promicro_nrf52840:
    uses: ./.github/workflows/build.yml
    with:
      board: others_promicro_nrf52840
      chip: NRF52840
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: true
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: false
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_panasonic_pan1770_evb:
    uses: ./.github/workflows/build.yml
    with:
      board: panasonic_pan1770_evb
      chip: NRF52840
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_panasonic_pan1780_evb:
    uses: ./.github/workflows/build.yml
    with:
      board: panasonic_pan1780_evb
      chip: NRF52840
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_particle_nrf51_blenano:
    uses: ./.github/workflows/build.yml
    with:
      board: particle_nrf51_blenano
      chip: NRF51822
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_particle_nrf52_blenano2:
    uses: ./.github/workflows/build.yml
    with:
      board: particle_nrf52_blenano2
      chip: NRF52832
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_phytec_reel_board:
    uses: ./.github/workflows/build.yml
    with:
      board: phytec_reel_board
      chip: NRF52840
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_pine64_pinetime_devkit0:
    uses: ./.github/workflows/build.yml
    with:
      board: pine64_pinetime_devkit0
      chip: NRF52832
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_qorvo_decawave_dwm1001_dev:
    uses: ./.github/workflows/build.yml
    with:
      board: qorvo_decawave_dwm1001_dev
      chip: NRF52832
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_rakwireless_rak4631:
    uses: ./.github/workflows/build.yml
    with:
      board: rakwireless_rak4631
      chip: NRF52840
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_rakwireless_rak5010:
    uses: ./.github/workflows/build.yml
    with:
      board: rakwireless_rak5010
      chip: NRF52840
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_raytac_raytac_mdbt50q_db_40:
    uses: ./.github/workflows/build.yml
    with:
      board: raytac_raytac_mdbt50q_db_40
      chip: NRF52840
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_ruuvi_ruuvi_ruuvitag:
    uses: ./.github/workflows/build.yml
    with:
      board: ruuvi_ruuvi_ruuvitag
      chip: NRF52832
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_seeed_xiao_ble:
    uses: ./.github/workflows/build.yml
    with:
      board: seeed_xiao_ble
      chip: NRF52840
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: true
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: false
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_sparkfun_micromod:
    uses: ./.github/workflows/build.yml
    with:
      board: sparkfun_micromod
      chip: NRF52840
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_sparkfun_nrf52_sparkfun:
    uses: ./.github/workflows/build.yml
    with:
      board: sparkfun_nrf52_sparkfun
      chip: NRF52832
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_u_blox_ubx_bmd300eval:
    uses: ./.github/workflows/build.yml
    with:
      board: u_blox_ubx_bmd300eval
      chip: NRF52832
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_u_blox_ubx_bmd340eval:
    uses: ./.github/workflows/build.yml
    with:
      board: u_blox_ubx_bmd340eval
      chip: NRF52840
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_u_blox_ubx_bmd345eval:
    uses: ./.github/workflows/build.yml
    with:
      board: u_blox_ubx_bmd345eval
      chip: NRF52840
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_u_blox_ubx_bmd380eval:
    uses: ./.github/workflows/build.yml
    with:
      board: u_blox_ubx_bmd380eval
      chip: NRF52840
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_u_blox_ubx_evkannab1:
    uses: ./.github/workflows/build.yml
    with:
      board: u_blox_ubx_evkannab1
      chip: NRF52832
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_u_blox_ubx_evkninab1:
    uses: ./.github/workflows/build.yml
    with:
      board: u_blox_ubx_evkninab1
      chip: NRF52832
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_u_blox_ubx_evkninab3:
    uses: ./.github/workflows/build.yml
    with:
      board: u_blox_ubx_evkninab3
      chip: NRF52840
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_vngiotlab_nrf51_vbluno51:
    uses: ./.github/workflows/build.yml
    with:
      board: vngiotlab_nrf51_vbluno51
      chip: NRF51822
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_vngiotlab_nrf52_vbluno52:
    uses: ./.github/workflows/build.yml
    with:
      board: vngiotlab_nrf52_vbluno52
      chip: NRF52832
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_waveshare_nrf51_ble400:
    uses: ./.github/workflows/build.yml
    with:
      board: waveshare_nrf51_ble400
      chip: NRF51822
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_wurth_we_proteus2ev:
    uses: ./.github/workflows/build.yml
    with:
      board: wurth_we_proteus2ev
      chip: NRF52832
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
  build_wurth_we_proteus3ev:
    uses: ./.github/workflows/build.yml
    with:
      board: wurth_we_proteus3ev
      chip: NRF52840
      publish_hex_crystal: true
      publish_hex_no_crystal: false
      publish_uf2: false
      publish_zip_crystal: false
      publish_zip_crystal_with_sd: true
      publish_zip_no_crystal: false
      publish_zip_no_crystal_with_sd: false
name: Automated firmware build for release
'on':
  release:
    types:
    - released
    - prereleased


================================================
FILE: .github/workflows/release-build.yml.template
================================================
name: Automated firmware build for release

'on':
  release:
    types: [released, prereleased]

jobs:
  build_nrf51822:
    uses: ./.github/workflows/build.yml
    with:
      chip: NRF51822
      publish_uf2: false

  build_nrf52832:
    uses: ./.github/workflows/build.yml
    with:
      chip: NRF52832

  build_nrf52840:
    uses: ./.github/workflows/build.yml
    with:
      chip: NRF52840

================================================
FILE: .gitignore
================================================
_build/
*.zip
.vscode/
test/
nohup.out
*.hex
*.bin
.DS_Store
venv


================================================
FILE: .gitmodules
================================================
[submodule "src/common"]
	path = src/common
	url = git@github.com:dakhnod/nRF51-common.git
[submodule "src/gpioasm"]
	path = src/gpioasm
	url = git@github.com:dakhnod/gpioASM-C-runtime.git


================================================
FILE: CITATION.cff
================================================
cff-version: 1.2.0
message: "If you use this software, please cite it as below."
authors:
- family-names: "Dakhno"
  given-names: "Daniel"
  orcid: "https://orcid.org/0009-0003-8245-6537"
title: "BLEnky"
version: 1.0.0
doi: 10.5281/zenodo.13694971
date-released: 2024-09-05
url: "https://github.com/dakhnod/BLEnky"


================================================
FILE: LICENSE
================================================
Copyright (c) 2024 Daniel Dakhno

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to use
the Software for non-commercial purposes, including the rights
to copy, modify, merge, publish and distribute
copies of the Software, and to permit persons to whom the Software is
furnished to do so.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: Makefile
================================================
OUTPUT_DIRECTORY := _build

BLE_ROOT ?= ../..

APPLICATION_HEX ?= $(OUTPUT_DIRECTORY)/$(TARGETS).hex
KEY_FILE ?= $(BLE_ROOT)/private.pem
PROJECT_ID ?= BLEnky

ifndef BOARD
BOARD := generic
endif

ifneq ($(BOARD), generic)
OUT_ZIP_SD ?= $(PROJECT_ID)_$(FIRMWARE_VERSION)_$(BOARD).zip
OUT_UF2 ?= $(PROJECT_ID)_$(FIRMWARE_VERSION)_$(BOARD).uf2
OUT_HEX ?= $(PROJECT_ID)_$(FIRMWARE_VERSION)_$(BOARD).hex
else
OUT_ZIP_SD ?= $(PROJECT_ID)_$(FIRMWARE_VERSION)_$(CHIP)_$(XTAL_LABEL)_SD_$(SOFTDEVICE_VERSION)_generic.zip
OUT_ZIP ?= $(PROJECT_ID)_$(FIRMWARE_VERSION)_$(CHIP)_$(XTAL_LABEL)_generic.zip
OUT_UF2 ?= $(PROJECT_ID)_$(FIRMWARE_VERSION)_$(CHIP)_$(XTAL_LABEL)_generic.uf2
OUT_HEX ?= $(PROJECT_ID)_$(FIRMWARE_VERSION)_$(CHIP)_$(XTAL_LABEL)_generic.hex
endif

SHELL := /bin/bash

PROJ_DIR := .
CUSTOM_INCLUDES_DIR = $(PROJ_DIR)/src/common
ADB_TARGET ?= Pixel-5
ADB_DIRECTORY ?= /sdcard/dfu

FIRMWARE_VERSION ?= $(shell git describe --tags --abbrev=0 | cut -c 2-)

CONFIG_SUBDIR = $(shell echo nrf$(FAMILY) | tr A-Z a-z)

# Source files common to all targets
SRC_FILES_COMMON += \
  $(SDK_ROOT)/components/ble/peer_manager/peer_manager.c \
  $(SDK_ROOT)/components/ble/peer_manager/peer_data_storage.c \
  $(SDK_ROOT)/components/ble/peer_manager/peer_database.c \
  $(SDK_ROOT)/components/ble/peer_manager/security_manager.c \
  $(SDK_ROOT)/components/ble/peer_manager/security_dispatcher.c \
  $(SDK_ROOT)/components/ble/peer_manager/id_manager.c \
  $(SDK_ROOT)/components/ble/peer_manager/gatt_cache_manager.c \
  $(SDK_ROOT)/components/ble/peer_manager/gatts_cache_manager.c \
  $(SDK_ROOT)/components/ble/peer_manager/peer_id.c \
  $(SDK_ROOT)/components/ble/peer_manager/pm_buffer.c \
  $(SDK_ROOT)/components/ble/common/ble_conn_state.c \
  $(SDK_ROOT)/components/libraries/util/sdk_mapped_flags.c \
  $(SDK_ROOT)/components/libraries/button/app_button.c \
  $(SDK_ROOT)/components/libraries/util/app_error.c \
  $(SDK_ROOT)/components/libraries/util/app_error_weak.c \
  $(SDK_ROOT)/components/libraries/scheduler/app_scheduler.c \
  $(SDK_ROOT)/components/libraries/fifo/app_fifo.c \
  $(SDK_ROOT)/components/libraries/timer/app_timer.c \
  $(SDK_ROOT)/components/libraries/uart/app_uart_fifo.c \
  $(SDK_ROOT)/components/libraries/util/app_util_platform.c \
  $(SDK_ROOT)/components/libraries/hardfault/hardfault_implementation.c \
  $(SDK_ROOT)/components/libraries/util/nrf_assert.c \
  $(SDK_ROOT)/components/libraries/uart/retarget.c \
  $(SDK_ROOT)/external/segger_rtt/SEGGER_RTT.c \
  $(SDK_ROOT)/external/segger_rtt/SEGGER_RTT_printf.c \
  $(SDK_ROOT)/components/ble/common/ble_advdata.c \
  $(SDK_ROOT)/components/ble/ble_advertising/ble_advertising.c \
  $(SDK_ROOT)/components/ble/common/ble_conn_params.c \
  $(SDK_ROOT)/components/ble/common/ble_srv_common.c \
  $(SDK_ROOT)/components/libraries/crc32/crc32.c \
  $(SDK_ROOT)/components/libraries/fds/fds.c \
  $(SDK_ROOT)/components/libraries/pwm/app_pwm.c \
  $(SDK_ROOT)/components/ble/ble_services/ble_dis/ble_dis.c \
  $(CUSTOM_INCLUDES_DIR)/services/battery_service/battery.c \
  $(PROJ_DIR)/src/ble/services/binary_sensor/ble_binary_sensor_service.c \
  $(PROJ_DIR)/src/ble/services/automation_io/ble_automation_io_service.c \
  $(PROJ_DIR)/src/ble/services/configuration/ble_configuration_service.c \
  $(PROJ_DIR)/src/ble/services/ble_gpio_asm/ble_gpio_asm.c \
  $(PROJ_DIR)/src/ble/services/cycling_speed_cadence/ble_cycling_speed_cadence.c \
  $(PROJ_DIR)/src/ble/services/hid/ble_hid.c \
  $(PROJ_DIR)/src/ble/services/temperature/ble_temperature_service.c \
  $(PROJ_DIR)/src/ble/helpers/ble_helpers.c \
  $(PROJ_DIR)/src/ble/sensor_ble.c \
  $(PROJ_DIR)/src/gpio/sensor_gpio.c \
  $(PROJ_DIR)/src/helpers/encoding.c \
  $(PROJ_DIR)/src/timer/sensor_timer.c \
  $(PROJ_DIR)/src/error_handler/error_handler.c \
  $(PROJ_DIR)/src/gpioasm/gpioasm.c \
  $(PROJ_DIR)/src/persistence/pin_configuration.c \
  $(PROJ_DIR)/src/storage/preconfiguration.c \
  $(PROJ_DIR)/src/sleep/sleep.c \
  $(PROJ_DIR)/src/watchdog/watchdog.c \
  $(PROJ_DIR)/src/main.c \

# Include folders common to all targets
INC_FOLDERS += \
  $(SDK_ROOT)/components/ble/ble_services/ble_ancs_c \
  $(SDK_ROOT)/components/ble/ble_services/ble_ias_c \
  $(SDK_ROOT)/components/libraries/pwm \
  $(SDK_ROOT)/components/libraries/usbd/class/cdc/acm \
  $(SDK_ROOT)/components/libraries/usbd/class/hid/generic \
  $(SDK_ROOT)/components/libraries/usbd/class/msc \
  $(SDK_ROOT)/components/libraries/usbd/class/hid \
  $(SDK_ROOT)/components/libraries/scheduler \
  $(SDK_ROOT)/components/ble/ble_services/ble_gls \
  $(SDK_ROOT)/components/libraries/fstorage \
  $(SDK_ROOT)/components/libraries/fifo \
  $(SDK_ROOT)/components/ble/ble_advertising \
  $(SDK_ROOT)/components/ble/ble_services/ble_bas_c \
  $(SDK_ROOT)/components/ble/ble_services/ble_hrs_c \
  $(SDK_ROOT)/components/libraries/queue \
  $(SDK_ROOT)/components/ble/ble_dtm \
  $(SDK_ROOT)/components/toolchain/cmsis/include \
  $(SDK_ROOT)/components/ble/ble_services/ble_rscs_c \
  $(SDK_ROOT)/components/drivers_nrf/twi_master \
  $(SDK_ROOT)/components/drivers_nrf/spi_master \
  $(SDK_ROOT)/components/ble/common \
  $(SDK_ROOT)/components/ble/ble_services/ble_lls \
  $(SDK_ROOT)/components/libraries/bsp \
  $(SDK_ROOT)/components/ble/ble_services/ble_bas \
  $(SDK_ROOT)/components/libraries/experimental_section_vars \
  $(SDK_ROOT)/components/ble/ble_services/ble_ans_c \
  $(SDK_ROOT)/components/libraries/slip \
  $(SDK_ROOT)/external/segger_rtt \
  $(SDK_ROOT)/components/libraries/csense_drv \
  $(SDK_ROOT)/components/ble/ble_services/ble_ias \
  $(SDK_ROOT)/components/libraries/usbd/class/hid/mouse \
  $(SDK_ROOT)/components \
  $(SDK_ROOT)/components/libraries/scheduler \
  $(SDK_ROOT)/components/ble/ble_services/ble_lbs \
  $(SDK_ROOT)/components/ble/ble_services/ble_hts \
  $(SDK_ROOT)/components/libraries/crc16 \
  $(SDK_ROOT)/components/libraries/util \
  $(SDK_ROOT)/components/libraries/usbd/class/cdc \
  $(SDK_ROOT)/components/libraries/csense \
  $(SDK_ROOT)/components/libraries/low_power_pwm \
  $(SDK_ROOT)/components/libraries/hardfault \
  $(SDK_ROOT)/components/ble/ble_services/ble_cscs \
  $(SDK_ROOT)/components/libraries/uart \
  $(SDK_ROOT)/components/libraries/hci \
  $(SDK_ROOT)/components/libraries/usbd/class/hid/kbd \
  $(SDK_ROOT)/components/libraries/timer \
  $(SDK_ROOT)/components/toolchain \
  $(SDK_ROOT)/components/libraries/led_softblink \
  $(SDK_ROOT)/components/ble/ble_services/ble_cts_c \
  $(SDK_ROOT)/components/ble/ble_services/ble_hids \
  $(SDK_ROOT)/components/libraries/crc32 \
  $(SDK_ROOT)/components/libraries/usbd/class/audio \
  $(SDK_ROOT)/components/ble/peer_manager \
  $(SDK_ROOT)/components/ble/ble_services/ble_tps \
  $(SDK_ROOT)/components/ble/ble_services/ble_dis \
  $(SDK_ROOT)/components/ble/nrf_ble_qwr \
  $(SDK_ROOT)/components/libraries/button \
  $(SDK_ROOT)/components/libraries/usbd \
  $(SDK_ROOT)/components/ble/ble_services/ble_lbs_c \
  $(SDK_ROOT)/components/ble/ble_racp \
  $(SDK_ROOT)/components/toolchain/gcc \
  $(SDK_ROOT)/components/libraries/fds \
  $(SDK_ROOT)/components/ble/ble_services/ble_rscs \
  $(SDK_ROOT)/components/ble/ble_services/ble_hrs \
  $(SDK_ROOT)/components/libraries/crc32 \
  $(PROJ_DIR)/src/ble/services/automation_io/ \
  $(PROJ_DIR)/src/ble/services/binary_sensor/ \
  $(PROJ_DIR)/src/ble/services/configuration/ \
  $(PROJ_DIR)/src/ble/services/ble_gpio_asm/ \
  $(PROJ_DIR)/src/ble/services/cycling_speed_cadence/ \
  $(PROJ_DIR)/src/ble/services/hid/ \
  $(PROJ_DIR)/src/ble/services/temperature/ \
  $(PROJ_DIR)/src/ble/helpers/ \
  $(PROJ_DIR)/src/ble/ \
  $(PROJ_DIR)/src/helpers/ \
  $(PROJ_DIR)/src/config/$(CONFIG_SUBDIR)/ \
  $(PROJ_DIR)/src/config/ \
  $(PROJ_DIR)/src/config/bsp/ \
  $(PROJ_DIR)/src/config/bsp/generated/ \
  $(PROJ_DIR)/src/gpio/ \
  $(PROJ_DIR)/src/storage/ \
  $(PROJ_DIR)/src/gpioasm/ \
  $(PROJ_DIR)/src/timer/ \
  $(PROJ_DIR)/src/persistence/ \
  $(PROJ_DIR)/src/watchdog/ \
  $(PROJ_DIR)/src/sleep/ \
  $(CUSTOM_INCLUDES_DIR)/services/battery_service \

ifeq ($(CHIP), NRF51822)
FAMILY = 51
TARGETS = nrf51822_xxac

SOFTDEVICE_VERSION = S130_2.0.1
SOFTDEVICE_ID = 0x87

$(OUTPUT_DIRECTORY)/$(TARGETS).out: \
  LINKER_SCRIPT  := src/linker/nrf51822_qfac.ld

SRC_FILES = \
  $(SDK_ROOT)/components/toolchain/gcc/gcc_startup_nrf51.S \
  $(SRC_FILES_COMMON) \
  $(SDK_ROOT)/components/toolchain/system_nrf51.c \
  $(SDK_ROOT)/components/libraries/timer/app_timer_appsh.c \
  $(SDK_ROOT)/components/libraries/util/sdk_errors.c \
  $(SDK_ROOT)/components/libraries/log/src/nrf_log_backend_serial.c \
  $(SDK_ROOT)/components/libraries/log/src/nrf_log_frontend.c \
  $(SDK_ROOT)/components/libraries/fstorage/fstorage.c \
  $(SDK_ROOT)/components/ble/peer_manager/pm_mutex.c \
  $(SDK_ROOT)/external/segger_rtt/RTT_Syscalls_GCC.c \
  $(SDK_ROOT)/components/softdevice/common/softdevice_handler/softdevice_handler.c \
  $(SDK_ROOT)/components/drivers_nrf/clock/nrf_drv_clock.c \
  $(SDK_ROOT)/components/drivers_nrf/common/nrf_drv_common.c \
  $(SDK_ROOT)/components/drivers_nrf/uart/nrf_drv_uart.c \
  $(SDK_ROOT)/components/drivers_nrf/spi_master/nrf_drv_spi.c \
  $(SDK_ROOT)/components/drivers_nrf/ppi/nrf_drv_ppi.c \
  $(SDK_ROOT)/components/drivers_nrf/timer/nrf_drv_timer.c \
  $(SDK_ROOT)/components/drivers_nrf/wdt/nrf_drv_wdt.c \
  $(SDK_ROOT)/components/drivers_nrf/gpiote/nrf_drv_gpiote.c \
  $(SDK_ROOT)/components/drivers_nrf/hal/nrf_nvmc.c \
  $(PROJ_DIR)/src/storage/storage.nrf51.c \

INC_FOLDERS += \
  $(SDK_ROOT)/components/softdevice/s130/headers \
  $(SDK_ROOT)/components/softdevice/s130/headers/nrf51 \
  $(SDK_ROOT)/components/softdevice/common/softdevice_handler \
  $(SDK_ROOT)/components/libraries/log \
  $(SDK_ROOT)/components/libraries/log/src \
  $(SDK_ROOT)/components/drivers_nrf/uart \
  $(SDK_ROOT)/components/drivers_nrf/gpiote \
  $(SDK_ROOT)/components/drivers_nrf/i2s \
  $(SDK_ROOT)/components/drivers_nrf/common \
  $(SDK_ROOT)/components/drivers_nrf/wdt \
  $(SDK_ROOT)/components/drivers_nrf/hal \
  $(SDK_ROOT)/components/drivers_nrf/rtc \
  $(SDK_ROOT)/components/drivers_nrf/ppi \
  $(SDK_ROOT)/components/drivers_nrf/twis_slave \
  $(SDK_ROOT)/components/drivers_nrf/pwm \
  $(SDK_ROOT)/components/drivers_nrf/delay \
  $(SDK_ROOT)/components/drivers_nrf/timer \
  $(SDK_ROOT)/components/drivers_nrf/rng \
  $(SDK_ROOT)/components/drivers_nrf/spi_slave \
  $(SDK_ROOT)/components/drivers_nrf/power \
  $(SDK_ROOT)/components/drivers_nrf/qdec \
  $(SDK_ROOT)/components/drivers_nrf/pdm \
  $(SDK_ROOT)/components/drivers_nrf/swi \
  $(SDK_ROOT)/components/drivers_nrf/lpcomp \
  $(SDK_ROOT)/components/drivers_nrf/clock \
  $(SDK_ROOT)/components/drivers_nrf/usbd \
  $(SDK_ROOT)/components/drivers_nrf/hal \
  $(SDK_ROOT)/components/drivers_nrf/saadc \
  $(SDK_ROOT)/components/drivers_nrf/comp \
  $(SDK_ROOT)/components/drivers_nrf/adc \
  $(SDK_ROOT)/components/libraries/twi \
  $(SDK_ROOT)/components/libraries/gpiote \
  $(SDK_ROOT)/components/device \

SDK_ROOT ?= $(BLE_ROOT)/SDK/12.3.0
SOFTDEVICE_HEX = $(SDK_ROOT)/components/softdevice/s130/hex/s130_nrf51_2.0.1_softdevice.hex

CFLAGS += -DNRF51
CFLAGS += -DS130
CFLAGS += -DNRF51822
CFLAGS += -DFAMILY=51
CFLAGS += -DNRF_SD_BLE_API_VERSION=2
CFLAGS += -DFDS_VIRTUAL_PAGE_SIZE=256
CFLAGS += -DAPP_TIMER_TICKS_COMPAT\(time,prescaler\)=APP_TIMER_TICKS\(time,prescaler\)
CFLAGS += -mcpu=cortex-m0
CFLAGS += -mfloat-abi=soft
CFLAGS += -DHARDWARE_PIN_COUNT=32

ASMFLAGS += -DNRF51
ASMFLAGS += -DS130
ASMFLAGS += -DNRF51822
ASMFLAGS += -DNRF_SD_BLE_API_VERSION=2
ASMFLAGS += -x assembler-with-cpp

LDFLAGS += -mcpu=cortex-m0
LDFLAGS += -mthumb -mabi=aapcs -L $(TEMPLATE_PATH) -T$(LINKER_SCRIPT)

else ifeq ($(CHIP), NRF52832)
FAMILY = 52
TARGETS = nrf52832_xxac

SOFTDEVICE_VERSION = S132_6.1.1
SOFTDEVICE_ID = 0xB7
UF2_FAMILY = 0x72721d4e

$(OUTPUT_DIRECTORY)/$(TARGETS).out: \
  LINKER_SCRIPT  := src/linker/nrf52832_qfaa.ld

SRC_FILES = \
  $(SDK_ROOT)/modules/nrfx/mdk/gcc_startup_nrf52.S \
  $(SDK_ROOT)/modules/nrfx/mdk/system_nrf52.c \
  $(SDK_ROOT)/components/libraries/crypto/backend/oberon/oberon_backend_ecc.c \
  $(SDK_ROOT)/components/libraries/crypto/backend/nrf_hw/nrf_hw_backend_rng.c \
  $(SDK_ROOT)/components/libraries/crypto/nrf_crypto_shared.c \


INC_FOLDERS += \
  $(SDK_ROOT)/components/softdevice/s132/headers \
  $(SDK_ROOT)/components/softdevice/s132/headers/nrf52 \

SOFTDEVICE_HEX = $(SDK_ROOT)/components/softdevice/s132/hex/s132_nrf52_6.1.1_softdevice.hex

CFLAGS += -DS132
CFLAGS += -DNRF52
CFLAGS += -DHARDWARE_PIN_COUNT=32
CFLAGS += -DNRF_CRYPTO_ALLOCATOR=3
CFLAGS += -DNRF_CRYPTO_BACKEND_OBERON_ENABLED=1
CFLAGS += -DNRF_CRYPTO_BACKEND_NRF_HW_RNG_ENABLED=1

ASMFLAGS += -DS132
ASMFLAGS += -DNRF52
else ifeq ($(CHIP), NRF52840)
FAMILY = 52
TARGETS = nrf52840_xxaa

SOFTDEVICE_VERSION = S140_6.1.1
SOFTDEVICE_ID = 0xAE
UF2_FAMILY = 0xada52840

$(OUTPUT_DIRECTORY)/$(TARGETS).out: \
  LINKER_SCRIPT  := src/linker/nrf52840_qfaa.ld

SRC_FILES = \
  $(SDK_ROOT)/modules/nrfx/mdk/gcc_startup_nrf52840.S \
  $(SDK_ROOT)/modules/nrfx/mdk/system_nrf52840.c \
  $(SDK_ROOT)/components/libraries/crypto/backend/cc310/cc310_backend_ecc.c \
  $(SDK_ROOT)/components/libraries/crypto/backend/cc310/cc310_backend_rng.c \
  $(SDK_ROOT)/components/libraries/crypto/backend/cc310/cc310_backend_init.c \
  $(SDK_ROOT)/components/libraries/crypto/backend/cc310/cc310_backend_mutex.c \
  $(SDK_ROOT)/components/libraries/crypto/backend/cc310/cc310_backend_shared.c \

INC_FOLDERS += \
  $(SDK_ROOT)/components/softdevice/s140/headers \
  $(SDK_ROOT)/components/softdevice/s140/headers/nrf52

SOFTDEVICE_HEX = $(SDK_ROOT)/components/softdevice/s140/hex/s140_nrf52_6.1.1_softdevice.hex

CFLAGS += -DS140
CFLAGS += -DNRF52840_XXAA
CFLAGS += -DHARDWARE_PIN_COUNT=64
CFLAGS += -DNRF_CRYPTO_BACKEND_CC310_ENABLED=1

ASMFLAGS += -DS140
ASMFLAGS += -DNRF52840_XXAA
else
$(error please specify CHIP=NRF51822 / NRF52832 / NRF52840)
endif

ifeq ($(FAMILY), 52)
SRC_FILES += \
  $(SRC_FILES_COMMON) \
  $(SDK_ROOT)/modules/nrfx/drivers/src/nrfx_clock.c \
  $(SDK_ROOT)/modules/nrfx/drivers/src/nrfx_gpiote.c \
  $(SDK_ROOT)/modules/nrfx/drivers/src/nrfx_ppi.c \
  $(SDK_ROOT)/modules/nrfx/drivers/src/nrfx_timer.c \
  $(SDK_ROOT)/modules/nrfx/drivers/src/nrfx_wdt.c \
  $(SDK_ROOT)/modules/nrfx/drivers/src/nrfx_rng.c \
  $(SDK_ROOT)/components/libraries/balloc/nrf_balloc.c \
  $(SDK_ROOT)/components/libraries/fstorage/nrf_fstorage.c \
  $(SDK_ROOT)/components/libraries/fstorage/nrf_fstorage_sd.c \
  $(SDK_ROOT)/components/libraries/fstorage/nrf_fstorage_nvmc.c \
  $(SDK_ROOT)/components/libraries/log/src/nrf_log_frontend.c \
  $(SDK_ROOT)/components/libraries/memobj/nrf_memobj.c \
  $(SDK_ROOT)/components/libraries/experimental_section_vars/nrf_section_iter.c \
  $(SDK_ROOT)/components/libraries/log/src/nrf_log_backend_rtt.c \
  $(SDK_ROOT)/components/libraries/log/src/nrf_log_backend_serial.c \
  $(SDK_ROOT)/components/libraries/log/src/nrf_log_default_backends.c \
  $(SDK_ROOT)/components/libraries/log/src/nrf_log_str_formatter.c \
  $(SDK_ROOT)/components/libraries/atomic_fifo/nrf_atfifo.c \
  $(SDK_ROOT)/components/libraries/atomic/nrf_atomic.c \
  $(SDK_ROOT)/components/libraries/strerror/nrf_strerror.c \
  $(SDK_ROOT)/components/libraries/ringbuf/nrf_ringbuf.c \
  $(SDK_ROOT)/components/libraries/crc32/crc32.c \
  $(SDK_ROOT)/components/libraries/util/app_error_handler_gcc.c \
  $(SDK_ROOT)/components/libraries/crypto/nrf_crypto_ecc.c \
  $(SDK_ROOT)/components/libraries/crypto/nrf_crypto_rng.c \
  $(SDK_ROOT)/components/libraries/crypto/nrf_crypto_init.c \
  $(SDK_ROOT)/components/libraries/mem_manager/mem_manager.c \
  $(SDK_ROOT)/components/libraries/atomic_flags/nrf_atflags.c \
  $(SDK_ROOT)/components/libraries/queue/nrf_queue.c \
  $(SDK_ROOT)/components/softdevice/common/nrf_sdh.c \
  $(SDK_ROOT)/components/softdevice/common/nrf_sdh_ble.c \
  $(SDK_ROOT)/components/softdevice/common/nrf_sdh_soc.c \
  $(SDK_ROOT)/components/ble/nrf_ble_gatt/nrf_ble_gatt.c \
  $(SDK_ROOT)/components/ble/peer_manager/nrf_ble_lesc.c \
  $(SDK_ROOT)/integration/nrfx/legacy/nrf_drv_clock.c \
  $(SDK_ROOT)/integration/nrfx/legacy/nrf_drv_uart.c \
  $(SDK_ROOT)/integration/nrfx/legacy/nrf_drv_spi.c \
  $(SDK_ROOT)/integration/nrfx/legacy/nrf_drv_ppi.c \
  $(SDK_ROOT)/integration/nrfx/legacy/nrf_drv_rng.c \
  $(SDK_ROOT)/modules/nrfx/hal/nrf_nvmc.c \
  $(SDK_ROOT)/external/segger_rtt/SEGGER_RTT_Syscalls_GCC.c \
  $(SDK_ROOT)/external/fprintf/nrf_fprintf.c \
  $(SDK_ROOT)/external/fprintf/nrf_fprintf_format.c \
  $(SDK_ROOT)/components/ble/peer_manager/auth_status_tracker.c \
  $(PROJ_DIR)/src/storage/storage.nrf52.c \


INC_FOLDERS += \
  $(SDK_ROOT)/modules/nrfx/mdk \
  $(SDK_ROOT)/modules/nrfx/hal \
  $(SDK_ROOT)/components/libraries/strerror \
  $(SDK_ROOT)/components/softdevice/common \
  $(SDK_ROOT)/components/libraries/log \
  $(SDK_ROOT)/components/libraries/log/src \
  $(SDK_ROOT)/components/libraries/memobj \
  $(SDK_ROOT)/components/libraries/balloc \
  $(SDK_ROOT)/components/libraries/svc \
  $(SDK_ROOT)/components/libraries/atomic \
  $(SDK_ROOT)/components/libraries/atomic_fifo \
  $(SDK_ROOT)/components/libraries/atomic_flags \
  $(SDK_ROOT)/components/libraries/delay \
  $(SDK_ROOT)/components/libraries/mutex \
  $(SDK_ROOT)/components/libraries/ringbuf \
  $(SDK_ROOT)/components/libraries/stack_info \
  $(SDK_ROOT)/components/libraries/mem_manager \
  $(SDK_ROOT)/components/libraries/crypto \
  $(SDK_ROOT)/components/libraries/crypto/backend/cc310 \
  $(SDK_ROOT)/components/libraries/crypto/backend/cc310_bl \
  $(SDK_ROOT)/components/libraries/crypto/backend/mbedtls \
  $(SDK_ROOT)/components/libraries/crypto/backend/oberon \
  $(SDK_ROOT)/components/libraries/crypto/backend/optiga \
  $(SDK_ROOT)/components/libraries/crypto/backend/micro_ecc \
  $(SDK_ROOT)/components/libraries/crypto/backend/nrf_sw \
  $(SDK_ROOT)/components/libraries/crypto/backend/nrf_hw \
  $(SDK_ROOT)/components/libraries/crypto/backend/cifra \
  $(SDK_ROOT)/components/ble/nrf_ble_gatt \
  $(SDK_ROOT)/modules/nrfx \
  $(SDK_ROOT)/modules/nrfx/drivers/include \
  $(SDK_ROOT)/integration/nrfx \
  $(SDK_ROOT)/integration/nrfx/legacy \
  $(SDK_ROOT)/external/fprintf \
  $(SDK_ROOT)/external/nrf_cc310/include \
  $(SDK_ROOT)/external/nrf_oberon \
  $(SDK_ROOT)/external/nrf_oberon/include \
  $(SDK_ROOT)/external/mbedtls/include \

LIB_FILES += \
  $(SDK_ROOT)/external/nrf_cc310/lib/cortex-m4/hard-float/libnrf_cc310_0.9.12.a \
  $(SDK_ROOT)/external/nrf_oberon/lib/cortex-m4/hard-float/liboberon_2.0.7.a \
  $(SDK_ROOT)/components/nfc/t2t_lib/nfc_t2t_lib_gcc.a \

CFLAGS += -DNRF_SD_BLE_API_VERSION=6
CFLAGS += -DAPP_TIMER_TICKS_COMPAT\(time,prescaler\)=APP_TIMER_TICKS\(time\)
CFLAGS += -DNRF_DFU_SETTINGS_COMPATIBILITY_MODE=1
CFLAGS += -DCONFIG_NFCT_PINS_AS_GPIOS=1
CFLAGS += -DMBEDTLS_MEMORY_BUFFER_ALLOC_C
CFLAGS += -DMBEDTLS_PLATFORM_MEMORY
CFLAGS += -DFAMILY=52
CFLAGS += -mcpu=cortex-m4
CFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16

ASMFLAGS += -DNRF_SD_BLE_API_VERSION=6

LDFLAGS += -mcpu=cortex-m4
LDFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16
LDFLAGS += -mthumb -mabi=aapcs -L $(SDK_ROOT)/modules/nrfx/mdk -T$(LINKER_SCRIPT)

SDK_ROOT ?= $(BLE_ROOT)/SDK/15.3.0
endif

# Libraries common to all targets
LIB_FILES += \

# C flags common to all targets
CFLAGS += -DSOFTDEVICE_PRESENT
CFLAGS += -DBLE_STACK_SUPPORT_REQD
CFLAGS += -DSWI_DISABLE0
CFLAGS += -mthumb -mabi=aapcs
CFLAGS += -Wall -Werror -O3 -g3
# keep every function in separate section, this allows linker to discard unused ones
CFLAGS += -ffunction-sections -fdata-sections -fno-strict-aliasing
CFLAGS += -fno-builtin --short-enums 
CFLAGS += -DNRF_DFU_SETTINGS_VERSION=1
CFLAGS += -DUSE_DFU
#CFLAGS += -DUSE_SPI
#CFLAGS += -DUSE_UART
CFLAGS += -DBUTTON_PIN=BUTTON_0
CFLAGS += -DFIRMWARE_VERSION=\"$(FIRMWARE_VERSION)\"

ifeq ($(LFCLK_SRC_XTAL), 0)
CFLAGS += -DCLOCK_CONFIG_LF_SRC=0
CFLAGS += -DNRF_SDH_CLOCK_LF_SRC=0
CFLAGS += -DNRF_SDH_CLOCK_LF_RC_CTIV=16
CFLAGS += -DNRF_SDH_CLOCK_LF_RC_TEMP_CTIV=2
CFLAGS += -DNRF_SDH_CLOCK_LF_ACCURACY=1
XTAL_LABEL = INTERNAL_RC
else ifeq ($(LFCLK_SRC_XTAL), 1)
CFLAGS += -DCLOCK_CONFIG_LF_SRC=1
CFLAGS += -DNRF_SDH_CLOCK_LF_SRC=1
CFLAGS += -DNRF_SDH_CLOCK_LF_RC_CTIV=0
CFLAGS += -DNRF_SDH_CLOCK_LF_RC_TEMP_CTIV=0
CFLAGS += -DNRF_SDH_CLOCK_LF_ACCURACY=7
XTAL_LABEL = EXTERNAL_XTAL
else
$(error please specify LFCLK_SRC_XTAL=0 / 1)
endif

ifeq ($(DEBUG), 1)
CFLAGS += -DDEBUG=1
endif

ifneq ($(BOARD), generic)
CFLAGS += -DBLENKY_BSP_FILE=\"$(BOARD).h\"
endif

# C++ flags common to all targets
CXXFLAGS += \

# Assembler flags common to all targets
ASMFLAGS += -DSOFTDEVICE_PRESENT
ASMFLAGS += -DBLE_STACK_SUPPORT_REQD
ASMFLAGS += -DSWI_DISABLE0

# let linker to dump unused sections
LDFLAGS += -Wl,--gc-sections
# use newlib in nano version
LDFLAGS += --specs=nano.specs -lc -lnosys


.PHONY: $(TARGETS) default all clean help flash flash_softdevice erase merge_softdevice applicaiton_zip push sign sign_sd reset config feature_config

# Default target - first one defined
default: $(TARGETS)

# Print all targets that can be built
help:
	@echo following targets are available:
	@echo 	$(TARGETS)

TEMPLATE_PATH := $(SDK_ROOT)/components/toolchain/gcc

include $(TEMPLATE_PATH)/Makefile.common

$(foreach target, $(TARGETS), $(call define_target, $(target)))

# Flash the program
flash: $(APPLICATION_HEX)
	@echo Flashing: $<
	# scp $(APPLICATION_HEX) home:ram
	echo -e "program `realpath $(APPLICATION_HEX)` verify reset \n exit" | nc localhost 4444

# Flash softdevice
flash_softdevice:
	@echo Flashing: $(SOFTDEVICE_HEX)
	nrfjprog --program $(SOFTDEVICE_HEX) -f nrf$(FAMILY) --verify --sectorerase
	nrfjprog --reset -f nrf$(FAMILY)

erase:
	nrfjprog --eraseall -f nrf$(FAMILY)

merge_softdevice: $(APPLICATION_HEX) $(SOFTDEVICE_HEX)
	mergehex -m \
		$(APPLICATION_HEX) \
		$(SOFTDEVICE_HEX) \
	-o application_with_softdevice.hex

$(OUT_ZIP): $(APPLICATION_HEX)
	rm -f $(OUT_ZIP)
	ls -lh $(APPLICATION_HEX)
	nrfutil pkg generate \
    --application $(APPLICATION_HEX) \
    --debug-mode \
    --key-file $(KEY_FILE) \
    --sd-req 0x00,$(SOFTDEVICE_ID) \
    --hw-version $(FAMILY) \
    $(OUT_ZIP) \

$(OUT_ZIP_SD): $(APPLICATION_HEX)
	rm -f $(OUT_ZIP_SD)
	ls -lh $(APPLICATION_HEX)
	nrfutil pkg generate \
    --application $(APPLICATION_HEX) \
    --debug-mode \
    --key-file $(KEY_FILE) \
    --sd-req 0x00,$(SOFTDEVICE_ID) \
    --sd-id $(SOFTDEVICE_ID) \
    --softdevice $(SOFTDEVICE_HEX) \
    --hw-version $(FAMILY) \
    $(OUT_ZIP_SD) \

$(OUT_UF2): $(APPLICATION_HEX)
	python uf2conv.py -f $(UF2_FAMILY) -c -o $(OUT_UF2) $(APPLICATION_HEX)

$(OUT_HEX): $(APPLICATION_HEX)
	cp $(APPLICATION_HEX) $(OUT_HEX)

sign: $(OUT_ZIP)

sign_sd: $(OUT_ZIP_SD)

uf2: $(OUT_UF2)

hex: $(OUT_HEX)

push: $(OUT_ZIP)
	adb connect $(ADB_TARGET)
	adb shell mkdir -p $(ADB_DIRECTORY)
	adb push $(OUT_ZIP) $(ADB_DIRECTORY)

config: src/config/sdk_config.h
	java -jar $(BLE_ROOT)/CMSIS_Configuration_Wizard.jar src/config/sdk_config.h

feature_config: src/config/feature_config.h
	java -jar $(BLE_ROOT)/CMSIS_Configuration_Wizard.jar src/config/feature_config.template.h
	
reset:
	nrfjprog --reset

rtt_viewer_start:
	sed -i -r 's/(Frame[XY]) = .*/\1 = 0/' ~/.config/SEGGERJLinkRTTViewerSettings.ini
	nohup JLinkRTTViewer --autoconnect &
	sleep 1

rtt_viewer_stop:
	killall JLinkRTTViewer || true
	sleep 0.5


================================================
FILE: README.md
================================================
# BLEnky

[![Firmware test badge](https://github.com/dakhnod/blenky/actions/workflows/firmware-test.yml/badge.svg 'Firmware test badge')](https://github.com/dakhnod/BLEnky/actions/workflows/firmware-test.yml)


![Running sequence](https://user-images.githubusercontent.com/26143255/189000402-cf582116-7096-429b-8a44-aa2442ba5524.gif)

Want to make a device smart and super low power withing minutes? Here you go!

Got your hardware ready? Try [first steps](docs/FIRST_STEPS.md).

You can see this Project in action [here](https://youtu.be/jnUlXBZHBno), [here](https://youtu.be/GZ5C588gBdo) and [here](https://youtu.be/10ko1Ppw78A).
An exhaustive list of Projects can be found [here](https://daniel.nullco.de/) under `Projects/Software/Automation/BLEnky`.

Firmware for cheap, nRF51 or nRF52-based IO-modules to control LEDs, relays, servos, motors (and more) and read from buttons, sensors (etc.), build Cycling sensors and HID controlelrs via Bluetooth low energy.
An IO module can have inputs and outputs that can be read from and written to.

All of the features can be [enabled/disabled](docs/FEATURES.md) to save ram and flash storage.

To summarize, this is basically like Tasmota for the nRF51/52, that can be powered from a coin cell for a long time.

|Board|Supported|LF-Crystal available|Pin configuration template available|Remarks|
|-----|---------|-------|---------|-----|
|nice!nano|✅|✅|||
|[Big beacon thing](https://de.aliexpress.com/item/32988225162.html?spm=a2g0o.productlist.main.3.1281Dxz4Dxz4uj&algo_pvid=0cb69d44-82b4-4ea6-9012-2a53f58d2dcc&algo_exp_id=0cb69d44-82b4-4ea6-9012-2a53f58d2dcc-1&pdp_npi=4%40dis%21EUR%213.09%213.09%21%21%213.14%213.14%21%402103890917383147672042275e555a%2166814403390%21sea%21DE%211858291489%21X&curPageLogUid=25giPJzksnGr&utparam-url=scene%3Asearch%7Cquery_from%3A)|✅||✅|
|[Small beacon thing](https://de.aliexpress.com/item/32872483730.html?spm=a2g0o.productlist.main.41.1281Dxz4Dxz4uj&algo_pvid=0cb69d44-82b4-4ea6-9012-2a53f58d2dcc&algo_exp_id=0cb69d44-82b4-4ea6-9012-2a53f58d2dcc-20&pdp_npi=4%40dis%21EUR%215.51%214.79%21%21%215.59%214.86%21%402103890917383147672042275e555a%2165507258407%21sea%21DE%211858291489%21X&curPageLogUid=NJ5twh8e5mzM&utparam-url=scene%3Asearch%7Cquery_from%3A)|✅|✅|✅|
|WS51822-S4AT|✅||✅|
|HolyIOT 17095|✅|||whole board is only 9x9mm!|
|[Feasycom FSC-BT630](https://www.feasycom.com/product/fsc-bt630/)|✅|✅| |
|BLE400|✅|✅|✅|
|[XIAO nRF52840](https://www.seeedstudio.com/Seeed-XIAO-BLE-nRF52840-p-5201.html)|✅|✅||
|[nRF52840 dongle](https://www.nordicsemi.com/Products/Development-hardware/nRF52840-Dongle/Download?lang=en#infotabs)|✅|✅||use `dfu usb-serial -pkg dongle.zip --port /dev/ttyACMX` to flash.
|Any board with an nRF51|✅|?||
|Any board with an nRF52|✅|?||

With this firmware you can use every available pin on the chip as an input or an output.
Well, until a buffer overruns or the RAM runs out or something...

This firmware allows for flashing an nRF51/nRF52 once and control and configuration via a [website](https://ble.nullco.de),
without needing to reflash the chip.

Furthermore, it gives you the oppurtunity to not only write and read bits to/from pins, but also to upload little [gpioASM](https://github.com/dakhnod/gpioASM) programms that run offline on the chip.

Learn about my other projects [here](https://daniel.nullco.de).

## Table of contents

1. [First steps](docs/FIRST_STEPS.md)
2. [Configuration](docs/CONFIGURATION.md)
3. [gpioASM](docs/GPIO_ASM_SERVICE.md)
4. [Compilation](docs/COMPILATION.md)
5. [Programming the chip](docs/FLASHING.md)
6. [Feature configuration](docs/FEATURES.md)
7. [Pin inputs/outputs (Automation IO)](docs/AUTOMATION_IO_SERVICE.md)
8. [Pin inputs (Binary Sensor Service)](docs/BINARY_SENSOR_SERVICE.md)

## Roadmap

Here are a few things I have planned for the future

- [x] Add analog (PWM) outputs for servo control
- [ ] Add analog inputs to read out voltage(s)
- [x] Add debounce settings to allow for quicker button presses
- [x] Add selection of common boards to website


================================================
FILE: blink.gpioasm
================================================
label start

write_digital 1
sleep_ms 100
write_digital 0
sleep_ms 100

jump_count start 9

write_digital i

================================================
FILE: docs/AUTOMATION_IO_SERVICE.md
================================================
## TOC

1. [Interfacing with digital output pins](#output-digital-pins)
2. [Interfacing with analog output pins](#output-analog-pins)
3. [Interfacing with input pins](#input-pins)


## Pin interface protocol

The firmware exposes endpoints for reading from input pins and writing to output pins,
if any are configured. To configure pins, read about the [configuration protocol](CONFIGURATION.md).

When controlling pins, each bit is represented by two bits.
This part of the protocol mostly adheres to the [Bluetooth Automation IO Service](https://www.bluetooth.com/de/specifications/specs/automation-io-service-1-0/).

If inputs and outputs are configured, there are two characteristics with the UUID `00002a56-0000-1000-8000-00805f9b34fb` exposed.
The one allowing to be written to is the output-endpoint, the other one the input.
Each characteristic exposes a Descriptor with the UUID `00002909-0000-1000-8000-00805f9b34fb`, exposing the amount of configured pins.

### Output digital pins

When a pin is configured as an output, here is what the bit combinations mean:
```
0b00: output LOW
0b01: output HIGH
0b10: output high-impedance (not supported by this firmware)
0b11: ignore pin
```
All of the pins to be written are concatenated into the minimum amount of bytes needed and send to the characteristic `00002a56-0000-1000-8000-00805f9b34fb` (the one allowing writes).
The bytes are padded with 0b11.
Here are a few examples of one and multiple pins:
```
[LOW]
0b00111111

[HIGH]
0b01111111

[LOW, LOW]
0b00001111

[HIGH, HIGH]
0b01011111

[HIGH, LOW, LOW, HIGH]
0b01000001

[HIGH, LOW, LOW, HIGH, LOW]
0b01000001 00111111
```

If you want to set only one pin instead of all of them, you can send 0b11 as the other pin states.
Sending `0b11110011` for instance will only touch the third output and set it to LOW.
Sending `0b01110000` will set the first pin to HIGH and the third and fourth pin to LOW,
while leaving the second pin output as it is.

### Output analog pins

For every pin that is configured as an analog output, the IO Service will expose a characteristic with the UUID
`00002a58-0000-1000-8000-00805f9b34fb`.
This characteristic accepts a two-byte little-endian unsigned integer representing the output PWM duty cycle.
The PWM pulse width ranges from 0us to 20000us.
Due to a PWM frequency of 50HZ an analog output can be used to control a standard servo by writing values
from 1000us to 2000us, even outside of that if your servo supports that.
Writing `0xDC05` for instance would set the output to a duty cycle of 1500us, setting a servo to center position.
The value 0xffff will be ignored.

It should noted that having any analog pins configured raises the boards current consumption to a minimum of 1mA.

### Input pins

If configured, the characteristic with the UUID `00002a56-0000-1000-8000-00805f9b34fb` that is not not writable will expose information about input pins.
Every pin is represented by two bits, just like the outputs:
```
0b00: pin reads LOW
0b01: pin reads HIGH
0b10: can't remember, not supported in this firmware
0b11: pin not available, most likely padding
```
Bytes from the characteristic are read from left to right,
bits within the bytes are also read from left to right.

Here are a few examples:
```
0b00111111: one input pin, reading LOW
0b01111111: one input pin, reading HIGH
0b01010101: four input pins, all reading HIGH
0b01001111: two input pins, first HIGH, second LOW
0b00000100 01011111: six input pins, reading LOW, LOW, HIGH, LOW, HIGH, HIGH
```
The input-characteristic can be read or subscribed to using notifications.
When notifications are enabled, each change on one of the input pins will trigger a notification containing all input pin states via the above described format.

================================================
FILE: docs/BINARY_SENSOR_SERVICE.md
================================================
# Binary Sensor Service

Is at least one input is configured, the device also exposes a Binary Sensor Service that
server information about the state of the first configured input pin and the number of times it went 1->0.

You can read about the Binary Sensor Service [here](https://www.bluetooth.com/specifications/specs/binary-sensor-service-1-0/).

================================================
FILE: docs/COMPILATION.md
================================================
# Compilation

1. Set up SDK_ROOT in the Makefile to point at an unpacked nRF5-sdk (version 12.3) folder. You can download the SDK [here](https://www.nordicsemi.com/Products/Development-software/nRF5-SDK/Download#infotabs).
2. First, you need to install `gcc-arm-none-eabi-4_9-2015q3`. Other versions of the compiler will lead to weird behaviour.
3. Then, in the root project directory where the `Makefile` is located, you can run `make default` to compile.

Here is a list of commands in the Makefile:
```
default: compile
flash: flash compiled hex to chip
reset: reboot the chip
flash_softdevice: flash the softdevice configured in the Makefile
erase: erase program and configuration data on the chip
clean: clean the build directory
```

================================================
FILE: docs/CONFIGURATION.md
================================================
# Configuration protocol

## TOC

1. [Saving space](#saving-space)
1. [Configuring output pins](#output-pins)
2. [Configuring input pins](#input-pins)
3. [Configuring connection parameters](#connection-parameters-configuration)

## While most of this should still work, the recommendet method for configuring the firmware is using `make feature_config`.

## Saving space

In order to save space, for example to flash to a nrf51822_qfaa, a few features need to be turned off:

1. Lower the input and output pin count as much as possible
2. Set NRF_LOG_ENABLED to 0 using `make config` or in src/config/sdk_config.h
3. Disable BLE_BONDING via `make feature_config` or in src/feature_config.h

## Pin configuration

Input and output pins can be configured without reprogramming the device by sending a configuration packet to the characteristic with the UUID  `9c100001-5cf1-8fa7-1549-01fdc1d171dc`.
This packet is constructed of four bits per pin, aka two pins per byte.

The leftmost bit per pin decides whether the pin is an output or an input:
```
0xxx: output
1xxx: input
```

The rightmost bit defines the inversion of the pin.
When that bit is set, the output/input of the pin will be opposite of the voltage level reading.
```
xxx0: not inverted
xxx1: inverted
```

### Output pins
The second byte decides whether the pin is a digital or an analog (PWM) output.

```
00xx: digital output
01xx: analog output
```

The third byte defines the default pin state for digital output pins.
```
0x0x: LOW after startup
0x1x: HIGH after startup
```
The inversion bit inverts the real output on the pin.

Here are a few examples:
```
0000: pin configured as digital output, default LOW, not inverted
0010: pin configured as digital output, default HIGH, not inverted
0001: pin configured as digital output, default LOW, inverted (real output will be HIGH by default due to inversion)
0011: pin configured as digital output, default HIGH, inverted (real output will be LOW by default)
0100: pin configured as analog output, non-inverted output
0101: pin configured as analog output, inverted output
```

### Input pins

Here the inversion bit also applies and inverts the reading.

Bit 2 and 3 define the input pullup/down.
```
100x: no pullup/pulldown
101x: pullup enabled
110x: pulldown enabled
```

Here a few examples:
```
1000: input, no pull, not inverted
1010: input, pullup, not inverted
1100: input, pulldown, not inverted
1001: input, no pull, inverted
1011: input, pullup, inverted
1101: input, pulldown, inverted
```

### Disabled pins
Setting the bits to `1111` disables that pin.

## Packet structure
The pin bits are read and applied to pins starting from pin 0.
Sending just `0000` would enable pin 0 as output.
Since only bytes can be sent, That affect can be achieved by sending `11110000`.

Lets say we want to use pin 2 as an inverted output and pin 6 as input. Our packet would look like this:

```
1111 # disabling pin 0
1111 # disabling pin 1
0001 # pin 2 as output with inversion
1111 # disabling pin 3
1111 # disabling pin 4
1111 # disabling pin 5
1000 # enabling pin 6 as input
1111 # disabling pin 7, need bits for padding
```
or
```
 pin 7                               pin 0
0b1111 1000 1111 1111 1111 0001 1111 1111
```
or
`0xF8FFF1FF`.

After sending the payload the packet will be padded with `0xff` to reach 16 bytes and stored in the flash.
The microcontroller will then reboot and apply the new configuration.
The configuration persists reboots, needs to be done only once.

Also, the configuration characteristic can be read to discover the configuration already present on the device.


## Connection parameters configuration

The modules preferred connection parameters can be configured by sending data to the Characteristic with UUID `9c100002-5cf1-8fa7-1549-01fdc1d171dc`.

The packet has to include the following fields (all in milliseconds except for slave_latency, little endian):

```
typedef struct {
  uint16_t min_conn_interval;
  uint16_t max_conn_interval;
  uint16_t slave_latency;
  uint16_t conn_sup_timeout;
  uint16_t advertising_interval;
} ble_configuration_connection_params_packet_t;
```

So, here's how an example packet looks like:
Min conn interval: 100ms (0x0064)
Max conn interval: 300ms (0x012C)
Slave latency: 3 (0x0003)
Supervision timeout: 2500ms (0x09C4)
Advertising interval: 1000ms (0x03E8)

And here's the assembled packet:

0x6400 2C01 0300 C409 E803

All of the Bluetooth constraints apply:

- min_conn_interval range: 7.5ms - 4000ms
- max_conn_interval range: 7.5ms - 4000ms
- min_conn_interval needs to be smaller than max_conn_interval
- Max conn_sup_timeout: 3200ms
- conn_sup_timeout needs to be greater than `(max_conn_interval * 2) * (slave_latency + 1)`
- Advertisement interval range: 20ms - 1024ms

================================================
FILE: docs/FEATURES.md
================================================
# Features

The source includes a few features, that can be turned on or off during compilation.
The feature configuration can be configured by using the [CMSIS configuration wizard](https://sourceforge.net/projects/cmsisconfig/) like this: `java -jar CMSIS_Configuration_Wizard.jar src/config/feature_config.h`.

To preserve memory, the project should be configured as minimal as possible.

## GPIO configuration

### GPIO_INPUT_COUNT_MAX

This option sets the amount of memory allocated for input pins.

### GPIO_OUTPUT_COUNT_MAX

This option sets the amount of memory allocated for output pins.

### GPIO_DEBOUNCE_TIMEOUT_MS

Repeated inputs on one input will be ignored if they occur within the debounce timeout.

### Battery profile

This feature adds a service to monitor the charging state and voltage of the battery

### Binary sensor profile

Adds a binary sensor service that includes only the first input.
The format and protocol follows the binary sensor profile.

### Automation IO

Adds an Automation IO profile that includes all inputs and outputs.
The format and protocol follows the Automation IO profile.

### gpioASM

This feature adds the [gpioASM](https://github.com/dakhnod/gpioASM) service to upload compiled gpioASM.
Also, the gpioASM engine/runtime is enabled so that the code can be executed.

### Cycling speed / cadence

Adds a `cycling speed and cadence` service that reads out the input with index 0.
The service periodically reports the revolution count and last revolution event time.

### HID

Experimental feature

### Sleep mode

This feature, if enabled, puts the chip to sleep mode.

In sleep mode, the chip cannot be connected to.

Any GPIO input wakes up the chip again.

#### Configuration

##### Sleep mode
- Light sleep: Clock keeps ticking, ram is retained. Needs a bit of power
- Deep sleep: Clock off, RAM wiped. Chip will be rebooted on wakeup. Needs very little power

##### Sleep timeout
Once there is no GPIO input for this amount of minutes, the chip goes to sleep mode according to the configuration above.

Technically, the timeout will most likely not be met exactly.

If set to e.g. 10 minutes, the chip will go to sleep in 10 to 11 minutes.

================================================
FILE: docs/FIRST_STEPS.md
================================================
# First steps

Right, so you got a shiny new board.
Chances are there is a freshly baked preconfigured firmware waiting just for you.
Just check the pictures if anything looks like your board and click on it.
After flashing your file feel free to re-configure BLEnky using the section at the bottom of the page.

If you don't find your board here, try looking through the [latest release](https://github.com/dakhnod/BLEnky/releases/latest) for a package that sounds like your board.
If your board shows up as a drive and says something about UF2, follow [these instructions](boards/GENERIC_UF2.md).
If it just shows up as a Serial port, it likely has the nordic bootloader installed, go with [this](boards/NORDIC_DFU.md).
If it doesn't show up at all, follow [this](boards/GENERIC.md).

[<img height="100" src="https://files.seeedstudio.com/wiki/XIAO-BLE/nrf52840_front.jpg">](boards/XIAO_BLE.md)
[<img height="100" src="https://docs-be.nordicsemi.com/bundle/ncs-latest/page/zephyr/_images/others_promicro_nrf52840.webp?_LANG=enus">](boards/PRO_MICRO.md)
[<img height="100" src="https://camo.githubusercontent.com/b0ed7718a2938ed9660fe2611832c7d0cc850ea262c49a9e873610f651241ffe/68747470733a2f2f6769746875622d70726f64756374696f6e2d757365722d61737365742d3632313064662e73332e616d617a6f6e6177732e636f6d2f3835323534372f3239353031303232332d66363464356466372d633465642d343862382d613961382d6162663534653031343739312e6a7067">](boards/PRO_MICRO.md)
[<img height="100" src="https://www.nordicsemi.com/-/media/Images/Products/DevKits/nRF52-Series/nRF52840-Dongle/nRF52840-Dongle-rev2-prod-page.png?h=750&iar=0&mw=350&w=350&hash=994DC726E9F3DBB12056C07F5DC0A802">](boards/NRF52840_DONGLE.md)

================================================
FILE: docs/FLASHING.md
================================================
# Flashing

## Flashing the nRF51 via bootloader

This is the recommended way to flash the firmware since it allows for
updates over the air (over BLE).

### Flashing using an ESP32

This is a good chance to try out https://github.com/atc1441/ESP32_nRF52_SWD.

I have no experience with this. Feel free to open an issue and contribute some documentation.

### Flashing the bootloader using a Raspberry Pi

Firstly, get openocd to run. I (cannot remember why) compiled the source from https://github.com/openocd-org/openocd myself on the RasPi.
Feel free to try out https://github.com/raspberrypi/openocd aswell. Not sure what the difference is.

Then download `bootloader_with_softdevice_delay.hex` [here](https://github.com/dakhnod/nRF51-GPIO-BLE-Bridge/releases/v0.1.0)

You can use this command to flash the hex (mind the references to the nrf51.cfg, change to nrf52.cfg if needed):
```
openocd -s ${OPENOCD_INSTALL_DIR}/tcl -f openocd/raspi-bcm2385.tcl -f target/nrf51.cfg -c "program bootloader_with_softdevice_delay.hex verify reset exit"
```
with `openocd/raspi-bcm2385.tcl` using [pins](https://pinout.xyz/) 11 for `swd` and 25 for `swio`, but feel free to change those.

### Flashing the bootloader using JLink

Get the bootloader [here](https://github.com/dakhnod/nRF51-GPIO-BLE-Bridge/releases/v0.1.0)
and flash the hex via nrfjprog, or any other program capable of swd.
Here's how to do it using nrfjprog
```
nrfjprog --chiperase
nrfjprog --program path_to_bootloader.hex
nrfjprog --reset
```

### Rebooting into bootloader

If you just installed the bootloader after a wipe, the device is already in bootloader mode.
You can proceed with the installation.

If you can cut power to the device, power-cycle it.
For a few seconds, you'll be able to connect to the device named "DfuTarg".
If you fail to connect in time, power-cycle and try again.

If you can't power-cycle the device, you can restart it by performing the following:
1. Enable notifications on the characteristic `8e400001-f315-4f60-9fb8-838830daea51`
2. Write the bootloader secret to characteristic `8e400001-f315-4f60-9fb8-838830daea51`.
You can find and change the secret [here](https://github.com/dakhnod/nRF51-common/blob/master/services/dfu_service/bootloader_secret.h)
3. The device should disconnect and reboot into bootloader more. With this way of manually triggering bootloader mode, it will not boot back into the application after a few seconds.


### Flashing through bootloader

First, you need to download the application .zip file from the [latest release](https://github.com/dakhnod/nRF51-GPIO-BLE-Bridge/releases/latest). 

You should use an app like "nRF Connect" to connect to the device in bootloader mode named "DfuTarg".
There, you can tap on "DFU" and select the zip file.
When flashing is done the device will boot back into the application and you are done.

## Flashing without bootloader

To flash the hex directly, you can either compile the code yourself or download the nrf51822__xxac.hex from the [latest release](https://github.com/dakhnod/nRF51-GPIO-BLE-Bridge/releases/latest).

Then, you need to flash the softdevice.
This step needs to be done only once, not with every update.

```
nrfjprog --chiperase
nrfjprog --program path_to_softdevice.hex
```

Then, flash the application.

```
nrfjprog --chiperase
nrfjprog --program path_to_application.hex
nrfjprog --reset
```

And you should be up and running.

================================================
FILE: docs/GPIO_ASM_SERVICE.md
================================================
# gpioASM Service

the gpioASM Service exposes an endpoint for uploading [gpioASM](https://github.com/dakhnod/gpioASM/blob/main/docs/GPIO_ASM.md) executables.

The easiest way to do this is via the "gpioASM upload" segment on the [website](https://ble.nullco.de/#gpio-asm-upload).

Just upload your gpioASM source and it will be compiled, loaded onto the chip and executed.

The service has the UUID `b1190000-2a74-d5a2-784f-c1cdb3862ab0`.

## gpioASM Upload characteristic

the upload characteristic has the UUID `b1190001-2a74-d5a2-784f-c1cdb3862ab0`.

The gpioASM executable should be split up into packets sized max. `MTU - 1`.

Every packet is prefixed by a sequence number.

The sequence number has the leftmost bit set if any packets follow.
An unset leftmost bit indicates the terminating packet and starts gpioASM execution on the chip.
Here is an example of sequence numbers: 

`[0b10000000, 0b10000001, 0b10000010, 0b00000011]`

================================================
FILE: docs/boards/GENERIC.md
================================================
# Generic board / without bootloader

If your board does not appear to have a bootloader at all, get the hex file for your board, or the generic hex file.
Then follow [these instruction](FLASHING.md).

Once the firmware is copied/flashed, the flash drive will disappear and you should be able to head to https://ble.nullco.de and connect to your board. You just saved yourself years of development!

================================================
FILE: docs/boards/GENERIC_UF2.md
================================================
# UF2 based boards

For these boards, find the right `.uf2` firmware in [in the latest release](https://github.com/dakhnod/BLEnky/releases/latest) and copy it onto the drive that should appear once you connect the board.
If you can't find anything suiting, get the generic build and configure it through the website.

Just make sure, using the text file on the drive, that the Softdevice version is 6.1.1.

Once the firmware is copied/flashed, the flash drive will disappear and you should be able to head to https://ble.nullco.de and connect to your board. You just saved yourself years of development!

To get back into the bootloader, a quick doble reset should suffice.

================================================
FILE: docs/boards/NORDIC_DFU.md
================================================
# Nordic DFU based boards

For these boards, find the right firmware in [in the latest release](https://github.com/dakhnod/BLEnky/releases/latest).
To flash these, you need to plugin the dongle and install nrfutil.
If you can't find anything suiting, get the generic build and configure it through the website.

Then get this file and run
```
nrfutil dfu serial -p DONGLE_PORT -pkg FIRMWARE_FILE.zip
```

Once the firmware is copied/flashed, the flash drive will disappear and you should be able to head to https://ble.nullco.de and connect to your board. You just saved yourself years of development!

To get back into the bootloader, a quick doble reset should suffice.

================================================
FILE: docs/boards/NRF52840_DONGLE.md
================================================
# nRF52840 Dongle

These boards come pre-flashed with the nordic bootloader.
To flash these, you need to plugin the dongle and install nrfutil.

Then get this file and run
```
nrfutil dfu serial -p DONGLE_PORT -pkg FIRMWARE_FILE.zip
```
The file to look for contains `nordic_nrf52840dongle.zip`.
Firmware files can be found [in the releases](https://github.com/dakhnod/BLEnky/releases/latest).

Once the firmware is copied/flashed, the flash drive will disappear and you should be able to head to https://ble.nullco.de and connect to your board. You just saved yourself years of development!

To get back into the bootloader, a quick doble reset should suffice.

================================================
FILE: docs/boards/PRO_MICRO.md
================================================
# Pro micro / Nice nano

## Flashing

These boards come pre-flashed with a uf2 bootloader, so you just need to copy the right file onto the board, which appears as a flash drive.
The firmware file to search for contains `others_promicro_nrf52840.uf2`.
Firmware files can be found [in the releases](https://github.com/dakhnod/BLEnky/releases/latest).

Once the firmware is copied/flashed, the flash drive will disappear and you should be able to head to https://ble.nullco.de and connect to your board. You just saved yourself years of development!

To get back into the bootloader, a quick doble reset should suffice.

## Low power

In order to get the lowest power ingest (microamperes), supply around 3V to the pin marked in this picture:

![PXL_20251222_225933250](https://github.com/user-attachments/assets/f777dd82-d1de-4d68-a1eb-dd396289568c)

Then, configure pin 0.13 as output and default low, to disable the LDO.
I managed to get around 10uA of power consumption this way.
As far as I have discovered so far, no other pin is connected to the 3V input of the nRF.
As such, all other power pins consume more power.

You can also use the B+/RAW pins to supply power, but that result in around 30uA of power consumption.

Please open an Issue if I am wrong.

Looks like providing 3V voltage to the B+/RAW pin also causes very low power consumption.
I measured below 0.2mA on B+/RAW, so that may work out aswell.
Setting 0.13 to low reduces to around 30uA, but you should provide around 3V I believe.

With a Lithium battery connected to B+/RAW you should leave the LDO on (0.13 at high), which will lead to around 200uA power consumption.
With a 1000mA battery this gives you around 200 days of battery life.

If you measure power consumption around 200uA, try [this](https://github.com/joric/nrfmicro/wiki/Alternatives/dd5782fb56855cc7e24e884f1e423d664da34db1) under "Workarounds".


================================================
FILE: docs/boards/XIAO_BLE.md
================================================
# XIAO BLE

On these boards, you need to update the bootloader first.
Thus install `adafruit-nrfutil` using pip.
Then, get [this file](https://github.com/user-attachments/files/19794746/xiao_nrf52840_ble_bootloader-0.9.2-10-g4329614-dirty_s140_6.1.1.zip) and run `adafruit-nrfutil dfu serial -p BOARD_PORT -pkg xiao_nrf52840_ble_bootloader-0.9.2-10-g4329614-dirty_s140_6.1.1.zip`.
After, make sure that the .txt on the board shows Softdevice version 6.1.1.
After, you just need to copy the right file onto the board, which appears as a flash drive.
The firmware file to search for contains `xiao_ble.uf2`.
Firmware files can be found [in the releases](https://github.com/dakhnod/BLEnky/releases/latest).

Once the firmware is copied/flashed, the flash drive will disappear and you should be able to head to https://ble.nullco.de and connect to your board. You just saved yourself years of development!

To get back into the bootloader, a quick doble reset should suffice.

================================================
FILE: openocd/raspi-bcm2385.tcl
================================================
set WORKAREASIZE 0

adapter driver bcm2835gpio

bcm2835gpio peripheral_base 0xFE000000

bcm2835gpio speed_coeffs 236181 60

adapter gpio swclk 11
adapter gpio swdio 25

transport select swd


================================================
FILE: src/ble/helpers/ble_helpers.c
================================================
#include "ble_helpers.h"
#include "ble.h"
#include "app_error.h"
#include "nrf_log.h"
#include "feature_config.h"


ret_code_t ble_helper_characteristic_add(ble_helper_characteristic_init_t *init) {
  ble_gatts_char_handles_t p_handles;

  ble_gatts_attr_md_t cccd_md = {
      .vloc = BLE_GATTS_VLOC_STACK
  };

  BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);
  SET_MODE_SECURE(&cccd_md.write_perm);

  ble_gatts_char_pf_t char_pf = {
      .unit = 0x2700,
      .format = BLE_GATT_CPF_FORMAT_STRUCT,
      .name_space = 0x01,
      .exponent = 0x00,
      .desc = init->description
  };

  ble_gatts_char_md_t char_md = {
      .char_props.read = init->is_readable,
      .char_props.write = init->is_writable,
      .char_props.notify = init->is_notifiable,
      .char_props.indicate = 0,
      .p_cccd_md = &cccd_md
  };

  ble_gatts_attr_md_t user_description_metadata;
  if(init->description_str != NULL){
    user_description_metadata.vlen = 0x00;
    user_description_metadata.vloc = BLE_GATTS_VLOC_STACK;
    user_description_metadata.rd_auth = 0x00;
    user_description_metadata.wr_auth = 0x00;

    char_md.p_user_desc_md = &user_description_metadata;

    SET_MODE_SECURE(&user_description_metadata.read_perm);
    BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&user_description_metadata.write_perm);

    char_md.p_char_user_desc = (uint8_t *)init->description_str;
    char_md.char_user_desc_max_size = strlen(init->description_str);
    char_md.char_user_desc_size = strlen(init->description_str);
  }

  if (init->description > 0) {
    char_md.p_char_pf = &char_pf;
  }

  uint8_t type = init->uuid_type;
  if(type == BLE_UUID_TYPE_UNKNOWN){
    type = BLE_UUID_TYPE_BLE;
  }
  ble_uuid_t ble_uuid = {
      .type = type,
      .uuid = init->uuid
  };

  ble_gatts_attr_md_t attr_md = {
      .vloc = init->location_user ? BLE_GATTS_VLOC_USER : BLE_GATTS_VLOC_STACK,
      .rd_auth = init->authorize_read,
      .wr_auth = init->authorize_write,
      .vlen = 1,
  };

  SET_MODE_SECURE(&attr_md.read_perm);
  SET_MODE_SECURE(&attr_md.write_perm);

  ble_gatts_attr_t attr_char_value = {
      .p_uuid = &ble_uuid,
      .p_attr_md = &attr_md,
      .max_len = init->max_length,
  };

  if((init->initial_value_length > 0) && (init->initial_value != NULL)){
    attr_char_value.init_len = init->initial_value_length;
    attr_char_value.p_value = init->initial_value;
  }

  ret_code_t err_code = sd_ble_gatts_characteristic_add(
    init->service_handle,
    &char_md,
    &attr_char_value,
    &p_handles
  );

  APP_ERROR_CHECK(err_code);

  if(init->value_handle != 0x00) {
    *(init->value_handle) = p_handles.value_handle;
  }
  if(init->cccd_handle != 0x00) {
    *(init->cccd_handle) = p_handles.cccd_handle;
  }

  if (init->number_of_digitals > 0) {
    uint16_t number_of_digitals_handle;

    ble_gatts_attr_md_t number_of_digitals_metadata = {
        .vlen = 0x00,
        .vloc = BLE_GATTS_VLOC_STACK,
        .rd_auth = 0,
        .wr_auth = 0
    };

    SET_MODE_SECURE(&number_of_digitals_metadata.read_perm);

    BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&number_of_digitals_metadata.write_perm);

    ble_uuid_t number_of_digitals_uuid = {
        .type = BLE_UUID_TYPE_BLE,
        .uuid = 0x2909
    };

    ble_gatts_attr_t number_of_digitals_descriptor_attributes = {
        .init_offs = 0,
        .init_len = 1,
        .max_len = 1,
        .p_uuid = &number_of_digitals_uuid,
        .p_value = &init->number_of_digitals,
        .p_attr_md = &number_of_digitals_metadata
    };

    err_code = sd_ble_gatts_descriptor_add(
      p_handles.value_handle,
      &number_of_digitals_descriptor_attributes,
      &number_of_digitals_handle
    );


    APP_ERROR_CHECK(err_code);
  }

  return err_code;
}

================================================
FILE: src/ble/helpers/ble_helpers.h
================================================
#include "sdk_common.h"
#include "stdint.h"
#include "feature_config.h"

#if STATIC_PASSKEY_ENABLED == 1
#define SET_MODE_SECURE BLE_GAP_CONN_SEC_MODE_SET_ENC_WITH_MITM
#else
#define SET_MODE_SECURE BLE_GAP_CONN_SEC_MODE_SET_OPEN
#endif

typedef struct {
  uint16_t service_handle;
  uint16_t uuid;
  uint8_t uuid_type;
  char *description_str;
  uint8_t number_of_digitals;
  uint8_t description;
  uint8_t is_writable;
  uint8_t is_readable;
  uint8_t is_notifiable;
  uint8_t authorize_read;
  uint8_t authorize_write;
  uint16_t max_length;
  uint16_t *value_handle;
  uint16_t *cccd_handle;
  uint8_t *initial_value;
  uint16_t initial_value_length;
  bool location_user;
} ble_helper_characteristic_init_t;

ret_code_t ble_helper_characteristic_add(ble_helper_characteristic_init_t*);

================================================
FILE: src/ble/sensor_ble.c
================================================
#include "sensor_ble.h"

#include "ble_configuration_service.h"
#include "ble_cycling_speed_cadence.h"
#include "ble_gpio_asm.h"
#include "app_error.h"
#include "ble_dis.h"
#include "nrf_delay.h"
#include "feature_config.h"
#include "ble_hid.h"
#include "peer_manager.h"
#include "ble_conn_state.h"
#include "fds.h"
#include "sleep.h"
#include "ble_temperature_service.h"
#include "ble_helpers.h"

#define FIRST_CONN_PARAMS_UPDATE_DELAY  APP_TIMER_TICKS_COMPAT(5000, APP_TIMER_PRESCALER) /**< Time from initiating event (connect or start of notification) to first time sd_ble_gap_conn_param_update is called (15 seconds). */
#define NEXT_CONN_PARAMS_UPDATE_DELAY   APP_TIMER_TICKS_COMPAT(5000, APP_TIMER_PRESCALER)  /**< Time between each call to sd_ble_gap_conn_param_update after the first call (5 seconds). */

#define MAX_CONN_PARAMS_UPDATE_COUNT    3   

#define APP_ADV_INTERVAL_FAST           MSEC_TO_UNITS(ADVERTISEMENT_INTERVAL_FAST, UNIT_0_625_MS)
#define APP_ADV_INTERVAL_SLOW           MSEC_TO_UNITS(ADVERTISEMENT_INTERVAL_SLOW, UNIT_0_625_MS)

#define BLE_NO_OUTPUTS_DEFAULT_MIN_CONN_INTERVAL   MSEC_TO_UNITS(100, UNIT_1_25_MS)
#define BLE_NO_OUTPUTS_DEFAULT_MAX_CONN_INTERVAL   MSEC_TO_UNITS(300, UNIT_1_25_MS)
#define BLE_NO_OUTPUTS_DEFAULT_SLAVE_LATENCY       15
#define BLE_NO_OUTPUTS_DEFAULT_CONN_SUP_TIMEOUT    MSEC_TO_UNITS(31000, UNIT_10_MS)

#define BLE_OUTPUTS_DEFAULT_MIN_CONN_INTERVAL      MSEC_TO_UNITS(100, UNIT_1_25_MS)
#define BLE_OUTPUTS_DEFAULT_MAX_CONN_INTERVAL      MSEC_TO_UNITS(300, UNIT_1_25_MS)
#define BLE_OUTPUTS_DEFAULT_SLAVE_LATENCY          0
#define BLE_OUTPUTS_DEFAULT_CONN_SUP_TIMEOUT       MSEC_TO_UNITS(6100, UNIT_10_MS)

#define STATUS_BATTERY_POSITION          6
#define STATUS_BATTERY_LEVEL_FULL     0b00
#define STATUS_BATTERY_LEVEL_MEDIUM   0b01
#define STATUS_BATTERY_LEVEL_LOW      0b10
#define STATUS_BATTERY_LEVEL_CRITICAL 0b11

#define NRF_BLE_MAX_MTU_SIZE    64

#define APP_BLE_OBSERVER_PRIO       3                                   /**< Application's BLE observer priority. You shouldn't need to modify this value. */

bool is_advertising = false;

uint16_t connection_handle = BLE_CONN_HANDLE_INVALID;    /**< Handle of the current connection. */

uint16_t advertising_interval = APP_ADV_INTERVAL_SLOW;

bool advertising_initialized = false;

#ifndef S130                                        /**< GATT module instance. */
BLE_ADVERTISING_DEF(m_advertising);
NRF_BLE_GATT_DEF(m_gatt);
#endif

#if FEATURE_ENABLED(CUSTOM_ADVERTISEMENT_DATA)
bool custom_advertisement_running = false;
void custom_data_advertisement_stop();
#endif
ble_gap_addr_t ble_address;

void peer_manager_event_handler(pm_evt_t const *p_evt)
{
    ret_code_t err_code;
    switch (p_evt->evt_id)
    {
    case PM_EVT_BONDED_PEER_CONNECTED:
        NRF_LOG_DEBUG("PM_EVT_BONDED_PEER_CONNECTED\n");
        // Update the rank of the peer.
        err_code = pm_peer_rank_highest(p_evt->peer_id);
        break;
    case PM_EVT_CONN_SEC_START:
        NRF_LOG_DEBUG("PM_EVT_CONN_SEC_START\n");
        break;
    case PM_EVT_CONN_SEC_SUCCEEDED:
        // Update the rank of the peer.
        NRF_LOG_DEBUG("PM_EVT_CONN_SEC_SUCCEEDED\n");
        err_code = pm_peer_rank_highest(p_evt->peer_id);
        break;
    case PM_EVT_CONN_SEC_FAILED:
        // In some cases, when securing fails, it can be restarted directly. Sometimes it can be
        // restarted, but only after changing some Security Parameters. Sometimes, it cannot be
        // restarted until the link is disconnected and reconnected. Sometimes it is impossible
        // to secure the link, or the peer device does not support it. How to handle this error
        // is highly application-dependent.
        NRF_LOG_DEBUG("PM_EVT_CONN_SEC_FAILED\n");
        break;
    case PM_EVT_CONN_SEC_CONFIG_REQ:
    {
        // A connected peer (central) is trying to pair, but the Peer Manager already has a bond
        // for that peer. Setting allow_repairing to false rejects the pairing request.
        // If this event is ignored (pm_conn_sec_config_reply is not called in the event
        // handler), the Peer Manager assumes allow_repairing to be false.
        NRF_LOG_DEBUG("PM_EVT_CONN_SEC_CONFIG_REQ\n");
        pm_conn_sec_config_t conn_sec_config = {.allow_repairing = true};
        pm_conn_sec_config_reply(p_evt->conn_handle, &conn_sec_config);
    }
    break;
    case PM_EVT_STORAGE_FULL:
        // Run garbage collection on the flash.
        NRF_LOG_DEBUG("PM_EVT_STORAGE_FULL\n");
        err_code = fds_gc();
        if (err_code == FDS_ERR_BUSY || err_code == FDS_ERR_NO_SPACE_IN_QUEUES)
        {
            // Retry.
        }
        else
        {
            APP_ERROR_CHECK(err_code);
        }
        break;
    case PM_EVT_ERROR_UNEXPECTED:
        // Assert.
        NRF_LOG_DEBUG("PM_EVT_ERROR_UNEXPECTED\n");
        APP_ERROR_CHECK(p_evt->params.error_unexpected.error);
        break;
    case PM_EVT_PEER_DATA_UPDATE_SUCCEEDED:
        NRF_LOG_DEBUG("PM_EVT_PEER_DATA_UPDATE_SUCCEEDED\n");
        break;
    case PM_EVT_PEER_DATA_UPDATE_FAILED:
        // Assert.
        NRF_LOG_DEBUG("PM_EVT_PEER_DATA_UPDATE_FAILED\n");
        APP_ERROR_CHECK_BOOL(false);
        break;
    case PM_EVT_PEER_DELETE_SUCCEEDED:
        NRF_LOG_DEBUG("PM_EVT_PEER_DELETE_SUCCEEDED\n");
        break;
    case PM_EVT_PEER_DELETE_FAILED:
        // Assert.
        NRF_LOG_DEBUG("PM_EVT_PEER_DELETE_FAILED\n");
        APP_ERROR_CHECK(p_evt->params.peer_delete_failed.error);
        break;
    case PM_EVT_PEERS_DELETE_SUCCEEDED:
        // At this point it is safe to start advertising or scanning.
        NRF_LOG_DEBUG("PM_EVT_PEERS_DELETE_SUCCEEDED\n");
        break;
    case PM_EVT_PEERS_DELETE_FAILED:
        // Assert.
        NRF_LOG_DEBUG("PM_EVT_PEERS_DELETE_FAILED\n");
        APP_ERROR_CHECK(p_evt->params.peers_delete_failed_evt.error);
        break;
    case PM_EVT_LOCAL_DB_CACHE_APPLIED:
        NRF_LOG_DEBUG("PM_EVT_LOCAL_DB_CACHE_APPLIED\n");
        break;
    case PM_EVT_LOCAL_DB_CACHE_APPLY_FAILED:
        // The local database has likely changed, send service changed indications.
        NRF_LOG_DEBUG("PM_EVT_LOCAL_DB_CACHE_APPLY_FAILED\n");
        pm_local_database_has_changed();
        break;
    case PM_EVT_SERVICE_CHANGED_IND_SENT:
        NRF_LOG_DEBUG("PM_EVT_SERVICE_CHANGED_IND_SENT\n");
        break;
    case PM_EVT_SERVICE_CHANGED_IND_CONFIRMED:
        NRF_LOG_DEBUG("PM_EVT_SERVICE_CHANGED_IND_CONFIRMED");
        break;
    #ifndef S130
    case PM_EVT_CONN_SEC_PARAMS_REQ:
        {
            ble_gap_sec_params_t sec_param;

            memset(&sec_param, 0, sizeof(ble_gap_sec_params_t));

            bool mitm = false;
            uint32_t caps = BLE_GAP_IO_CAPS_NONE;

            #if STATIC_PASSKEY_ENABLED == 1
            if(strlen(BLE_BONDIG_PASSKEY) != 6){
                NRF_LOG_ERROR("Passkey needs to be six digits long");
            }
            mitm = true;
            caps = BLE_GAP_IO_CAPS_DISPLAY_ONLY;
            static ble_opt_t passkey_opt;
            passkey_opt.gap_opt.passkey.p_passkey = (uint8_t*) BLE_BONDIG_PASSKEY;
            err_code = sd_ble_opt_set(BLE_GAP_OPT_PASSKEY, &passkey_opt);
            APP_ERROR_CHECK(err_code);
            #endif

            // Security parameters to be used for all security procedures.
            sec_param.bond           = true;
            sec_param.mitm           = mitm;
            sec_param.io_caps        = caps;
            sec_param.min_key_size   = 7;
            sec_param.max_key_size   = 16;
            sec_param.kdist_own.enc  = 1;
            sec_param.kdist_own.id   = 1;
            sec_param.kdist_peer.enc = 1;
            sec_param.kdist_peer.id  = 1;

            err_code = pm_conn_sec_params_reply(p_evt->conn_handle,
                                               &sec_param,
                                                p_evt->params.conn_sec_params_req.p_context);
            APP_ERROR_CHECK(err_code);
        } break;
    #endif
    default:
        NRF_LOG_DEBUG("unhandled PM event:%d\n", p_evt->evt_id);

    }
}

void peer_manager_init()
{
    ret_code_t err_code;
    err_code = pm_init();
    APP_ERROR_CHECK(err_code);

    uint32_t caps = BLE_GAP_IO_CAPS_NONE;
    bool mitm = false;

    #if STATIC_PASSKEY_ENABLED == 1
    if(strlen(BLE_BONDIG_PASSKEY) != 6){
        NRF_LOG_ERROR("Passkey needs to be six digits long");
    }
    mitm = true;
    caps = BLE_GAP_IO_CAPS_DISPLAY_ONLY;
    static ble_opt_t passkey_opt;
    passkey_opt.gap_opt.passkey.p_passkey = (uint8_t*) BLE_BONDIG_PASSKEY;
    err_code = sd_ble_opt_set(BLE_GAP_OPT_PASSKEY, &passkey_opt);
    APP_ERROR_CHECK(err_code);
    #endif

    ble_gap_sec_params_t sec_param = {
        .bond = true,
        .mitm = mitm,
        .lesc = false,
        .keypress = false,
        .io_caps = caps,
        .min_key_size = 7,
        .max_key_size = 16,
        .kdist_own.enc = 1,
        .kdist_own.id = 1,
        .kdist_peer.enc = 1,
        .kdist_peer.id = 1,
    };

    err_code = pm_sec_params_set(&sec_param);
    APP_ERROR_CHECK(err_code);

    err_code = pm_register(peer_manager_event_handler);
    APP_ERROR_CHECK(err_code);
}



// Simple event handler to handle errors during initialization.
void fds_evt_handler(fds_evt_t const *const p_fds_evt)
{
    switch (p_fds_evt->id)
    {
    case FDS_EVT_INIT:
        if (p_fds_evt->result == FDS_SUCCESS)
        {
            NRF_LOG_DEBUG("fds init success\n");
        }
        else
        {
            NRF_LOG_ERROR("fds init error: %d\n", p_fds_evt->result);
        }
        break;
    case FDS_EVT_WRITE:
        if (p_fds_evt->result == FDS_SUCCESS)
        {
            NRF_LOG_DEBUG("fds write success\n");
        }
        else
        {
            NRF_LOG_ERROR("fds write error: %d\n", p_fds_evt->result);
        }
        break;
    default:
        break;
    }
}

void filesystem_init()
{
    ret_code_t err_code = fds_register(fds_evt_handler);
    APP_ERROR_CHECK(err_code);

    err_code = fds_init();
    APP_ERROR_CHECK(err_code);
}

void ble_init() {
    uint8_t device_name[LENGTH_DEVICE_NAME];
    uint32_t device_name_length;

    storage_read_device_name(device_name, &device_name_length);

    if(device_name_length == 0){
        // set default device name
        ble_gap_addr_t addr;

        #ifdef S130
        APP_ERROR_CHECK(sd_ble_gap_address_get(&addr));
        #else
        APP_ERROR_CHECK(sd_ble_gap_addr_get(&addr));
        #endif
        

        snprintf((char*) device_name, LENGTH_DEVICE_NAME, DEVICE_NAME " %02X:%02X:%02X", addr.addr[2], addr.addr[1], addr.addr[0]);

        gap_params_init(
            device_name,
            strlen((char*) device_name)
        );
    }else {
        gap_params_init(
            device_name,
            device_name_length
        );
    }
    conn_params_init();
    services_init();
    advertising_init();

    #if FEATURE_ENABLED(BLE_BONDING)
    filesystem_init();
    peer_manager_init();
    #endif

    #ifdef S130
    APP_ERROR_CHECK(sd_ble_gap_address_get(&ble_address));
    #else
    APP_ERROR_CHECK(sd_ble_gap_addr_get(&ble_address));
    ble_advertising_conn_cfg_tag_set(&m_advertising, 1);
    #endif

    // allow flash operation to complete. 
    // Shitty solution, but for some reason there is no sys_evt fired to indicate a finished flash operation
    nrf_delay_ms(3); 
}

void ble_handle_input_change(int highest_changed_index)
{
    // these services only process a single changed input
    for(int input_index = 0; input_index <= highest_changed_index; input_index++) {
        gpio_config_input_digital_t *config = gpio_find_input_by_index(input_index);
        if(!config->changed){
            continue;
        }

        #if FEATURE_ENABLED(CYCLING_SPEED_CADENCE)
        ble_csc_handle_input_change(input_index, config);
        #endif

        #if FEATURE_ENABLED(HID)
        ble_hid_handle_input_change(input_index, config);
        #endif

        #if FEATURE_ENABLED(BINARY_SENSOR)
        ble_bss_handle_input_change(input_index, config);
        #endif
    }

    // threse services should have access to all changed pins at once
    #if FEATURE_ENABLED(AUTOMATION_IO)
    ble_aio_handle_input_change(highest_changed_index);
    #endif

    #if FEATURE_ENABLED(GPIO_ASM)
    ble_gpio_asm_handle_input_change();
    #endif

    #if FEATURE_ENABLED(CUSTOM_ADVERTISEMENT_DATA)
    if(custom_advertisement_running){
        // currently in custom data advertisement mode
        custom_data_advertisement_stop();
    }
    #endif

    bool is_connected = (connection_handle != BLE_CONN_HANDLE_INVALID);
    if(!is_connected && !is_advertising){
        // awoke from light sleep mode or custom advertising mode
        advertising_start();
    }
}

void ble_handle_device_name_write(const ble_gatts_evt_write_t *write_evt){
    uint16_t len = write_evt->len;
    const uint8_t *data = write_evt->data;

    storage_store_device_name(data, len);
}

void ble_on_write(const ble_evt_t *p_ble_evt) {
  const ble_gatts_evt_write_t *write_evt = &p_ble_evt
    ->evt
    .gatts_evt
    .params
    .write;

  uint16_t handle = write_evt->handle;
  uint16_t uuid = write_evt->uuid.uuid;

  UNUSED_PARAMETER(handle);

  if (uuid == BLE_UUID_GAP_CHARACTERISTIC_DEVICE_NAME) {
    ble_handle_device_name_write(write_evt);
    return;
  }
}

void on_ble_evt(const ble_evt_t *p_ble_evt) {
    uint32_t err_code;

    switch (p_ble_evt->header.evt_id) {
        case BLE_GAP_EVT_CONNECTED:
            NRF_LOG_INFO("connected\r\n");
            is_advertising = false;
            connection_handle = p_ble_evt->evt.gap_evt.conn_handle;
            break; // BLE_GAP_EVT_CONNECTED

        case BLE_GAP_EVT_DISCONNECTED:{
            uint8_t reason = p_ble_evt->evt.gap_evt.params.disconnected.reason;
            NRF_LOG_DEBUG("disconnected (reason: 0x%x)\r\n", reason);
            connection_handle = BLE_CONN_HANDLE_INVALID;
            break; // BLE_GAP_EVT_DISCONNECTED
        }

        case BLE_GATTS_EVT_WRITE:
            ble_on_write(p_ble_evt);
            break;

        case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
            // Only pass through to PM if feature is enabled
            #if FEATURE_ENABLED(BLE_BONDING)
            break;
            #endif

            err_code = sd_ble_gap_sec_params_reply(connection_handle,
                BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP,
                NULL,
                NULL);
            APP_ERROR_CHECK(err_code);

            break; // BLE_GAP_EVT_SEC_PARAMS_REQUEST
            
        case BLE_GATTS_EVT_SYS_ATTR_MISSING:
            // No system attributes have been stored.
            err_code = sd_ble_gatts_sys_attr_set(connection_handle, NULL, 0, 0);
            APP_ERROR_CHECK(err_code);
            break; // BLE_GATTS_EVT_SYS_ATTR_MISSING

        case BLE_GATTC_EVT_TIMEOUT:
            // Disconnect on GATT Client timeout event.
            NRF_LOG_DEBUG("GATT Client Timeout.\r\n");
            err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle,
                BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
            APP_ERROR_CHECK(err_code);
            break; // BLE_GATTC_EVT_TIMEOUT

        case BLE_GATTS_EVT_TIMEOUT:
            // Disconnect on GATT Server timeout event.
            NRF_LOG_DEBUG("GATT Server Timeout.\r\n");
            err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gatts_evt.conn_handle,
                BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
            APP_ERROR_CHECK(err_code);
            break; // BLE_GATTS_EVT_TIMEOUT

        case BLE_EVT_USER_MEM_REQUEST:
            err_code = sd_ble_user_mem_reply(p_ble_evt->evt.gattc_evt.conn_handle, NULL);
            APP_ERROR_CHECK(err_code);
            break; // BLE_EVT_USER_MEM_REQUEST

        case BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST:
        {
            ble_gatts_evt_rw_authorize_request_t  req;
            ble_gatts_rw_authorize_reply_params_t auth_reply;

            req = p_ble_evt->evt.gatts_evt.params.authorize_request;

            if (req.type != BLE_GATTS_AUTHORIZE_TYPE_INVALID) {
                if ((req.request.write.op == BLE_GATTS_OP_PREP_WRITE_REQ) ||
                    (req.request.write.op == BLE_GATTS_OP_EXEC_WRITE_REQ_NOW) ||
                    (req.request.write.op == BLE_GATTS_OP_EXEC_WRITE_REQ_CANCEL)) {
                    if (req.type == BLE_GATTS_AUTHORIZE_TYPE_WRITE) {
                        auth_reply.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE;
                    }
                    else {
                        auth_reply.type = BLE_GATTS_AUTHORIZE_TYPE_READ;
                    }
                    auth_reply.params.write.gatt_status = APP_FEATURE_NOT_SUPPORTED;
                    err_code = sd_ble_gatts_rw_authorize_reply(p_ble_evt->evt.gatts_evt.conn_handle,
                        &auth_reply);
                    APP_ERROR_CHECK(err_code);
                }
            }
        }
        break; // BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST

#if (NRF_SD_BLE_API_VERSION >= 3)
        case BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST:
            err_code = sd_ble_gatts_exchange_mtu_reply(p_ble_evt->evt.gatts_evt.conn_handle,
                NRF_BLE_MAX_MTU_SIZE);
            APP_ERROR_CHECK(err_code);
            break; // BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST
            

        case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST: {
            ble_gap_evt_t const * p_gap_evt = &p_ble_evt->evt.gap_evt;
            // Accepting parameters requested by peer.
            err_code = sd_ble_gap_conn_param_update(p_gap_evt->conn_handle,
                                                    &p_gap_evt->params.conn_param_update_request.conn_params);
            APP_ERROR_CHECK(err_code);
            break;
        }
        case BLE_GAP_EVT_DATA_LENGTH_UPDATE_REQUEST: {
            ble_gap_data_length_params_t dl_params;

            // Clearing the struct will effectivly set members to @ref BLE_GAP_DATA_LENGTH_AUTO
            memset(&dl_params, 0, sizeof(ble_gap_data_length_params_t));
            err_code = sd_ble_gap_data_length_update(p_ble_evt->evt.gap_evt.conn_handle, &dl_params, NULL);
            APP_ERROR_CHECK(err_code);
            break;
        }
        case BLE_GAP_EVT_PHY_UPDATE_REQUEST:
        {
            NRF_LOG_DEBUG("PHY update request.");
            ble_gap_phys_t const phys =
            {
                .rx_phys = BLE_GAP_PHY_AUTO,
                .tx_phys = BLE_GAP_PHY_AUTO,
            };
            err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys);
            APP_ERROR_CHECK(err_code);
        } break;
        case BLE_GAP_EVT_PHY_UPDATE:
            NRF_LOG_DEBUG("PHY update.");
            break;
        case BLE_GAP_EVT_TIMEOUT:
            NRF_LOG_DEBUG("BLE_GAP_EVT_TIMEOUT\n");
            break;
        case BLE_GAP_EVT_ADV_SET_TERMINATED:
            break;
        case BLE_GAP_EVT_DATA_LENGTH_UPDATE: 
            // nothing to do
            break;
        case BLE_GAP_EVT_CONN_PARAM_UPDATE:
            // nothing to do
            break;
        case BLE_GAP_EVT_PASSKEY_DISPLAY:
            // nothing to do
            break;
        case BLE_GAP_EVT_CONN_SEC_UPDATE:
            NRF_LOG_DEBUG("BLE evt BLE_GAP_EVT_CONN_SEC_UPDATE");
            break;
        case BLE_GAP_EVT_AUTH_STATUS:
            break;
#endif

        default:
            NRF_LOG_DEBUG("unhandled ble evt: %d\n", p_ble_evt->header.evt_id);
            // No implementation needed.
            break;
    }
}

#ifdef S130
void ble_evt_dispatch(ble_evt_t *p_ble_evt) {
#else
void ble_evt_dispatch(const ble_evt_t *p_ble_evt, void * p_context) {
#endif
    #ifdef S130
    ble_conn_state_on_ble_evt(p_ble_evt);
    #endif

    #if defined S130 && FEATURE_ENABLED(BLE_BONDING)
    pm_on_ble_evt(p_ble_evt);
    #endif

    on_ble_evt(p_ble_evt);

    #if FEATURE_ENABLED(AUTOMATION_IO)
    ble_aio_on_ble_evt(p_ble_evt);
    #endif

    #if FEATURE_ENABLED(BINARY_SENSOR)
    ble_bss_on_ble_evt(p_ble_evt);
    #endif

    #ifdef S130
    ble_conn_params_on_ble_evt(p_ble_evt);
    #endif

    if(p_ble_evt->header.evt_id == BLE_GAP_EVT_DISCONNECTED){
        // disallow advertising if the sleep module forbids it

        bool can_advertise = true;
        #if FEATURE_ENABLED(SLEEP_MODE)
        uint8_t reason = p_ble_evt->evt.gap_evt.params.disconnected.reason;
        bool graceful_disconnect = (reason == BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
        if((SLEEP_AFTER_DISCONNECT == 1) && graceful_disconnect){
            can_advertise = false;
        }else{
            can_advertise = sleep_get_allow_advertise();
        }
        #endif
        
        if(can_advertise){
            #ifdef S130
            ble_advertising_on_ble_evt(p_ble_evt);
            #else
            ble_advertising_on_ble_evt(p_ble_evt, &m_advertising);
            #endif
        }
    }else if(p_ble_evt->header.evt_id == BLE_GAP_EVT_TIMEOUT){
        #if FEATURE_ENABLED(CUSTOM_ADVERTISEMENT_DATA)
            if(custom_advertisement_running){
                NRF_LOG_DEBUG("returning to slow advertising\n");
                custom_data_advertisement_stop();
                ret_code_t err_code = ble_advertising_start(BLE_ADV_MODE_SLOW);
                APP_ERROR_CHECK(err_code);
            }else{
                ble_advertising_on_ble_evt(p_ble_evt);
            }
        #else
            #ifdef S130
            ble_advertising_on_ble_evt(p_ble_evt);
            #else
            ble_advertising_on_ble_evt(p_ble_evt, &m_advertising);
            #endif
        #endif
    }else{
        #ifdef S130
        ble_advertising_on_ble_evt(p_ble_evt);
        #else
        ble_advertising_on_ble_evt(p_ble_evt, &m_advertising);
        #endif
    }

    #if FEATURE_ENABLED(BATTERY_PROFILE)
    ble_bas_on_ble_evt(p_ble_evt);
    #endif

    ble_configuration_on_ble_event(p_ble_evt);

    #if FEATURE_ENABLED(GPIO_ASM)
    ble_gpio_asm_on_ble_evt(p_ble_evt);
    #endif

    #if FEATURE_ENABLED(CYCLING_SPEED_CADENCE)
    ble_csc_on_ble_evt(p_ble_evt);
    #endif

    #if FEATURE_ENABLED(HID)
    ble_hid_on_ble_evt(p_ble_evt);
    #endif

    ble_temperature_on_ble_evt(p_ble_evt);
}


void power_manage(void) {
    uint32_t err_code = sd_app_evt_wait();

    APP_ERROR_CHECK(err_code);
}


void on_conn_params_evt(ble_conn_params_evt_t *p_evt) {
    uint32_t err_code;

    if (p_evt->evt_type == BLE_CONN_PARAMS_EVT_FAILED) {
        err_code = sd_ble_gap_disconnect(connection_handle, BLE_HCI_CONN_INTERVAL_UNACCEPTABLE);
        APP_ERROR_CHECK(err_code);
    }
}

void conn_params_error_handler(uint32_t nrf_error) {
    APP_ERROR_HANDLER(nrf_error);
}

/**@brief Function for initializing the Connection Parameters module.
 */
void conn_params_init(void) {
    uint32_t               err_code;
    ble_conn_params_init_t cp_init;

    memset(&cp_init, 0, sizeof(cp_init));

    cp_init.p_conn_params = NULL;
    cp_init.first_conn_params_update_delay = FIRST_CONN_PARAMS_UPDATE_DELAY;
    cp_init.next_conn_params_update_delay = NEXT_CONN_PARAMS_UPDATE_DELAY;
    cp_init.max_conn_params_update_count = MAX_CONN_PARAMS_UPDATE_COUNT;
    cp_init.start_on_notify_cccd_handle = BLE_GATT_HANDLE_INVALID;
    cp_init.disconnect_on_fail = false;
    cp_init.evt_handler = on_conn_params_evt;
    cp_init.error_handler = conn_params_error_handler;

    err_code = ble_conn_params_init(&cp_init);
    APP_ERROR_CHECK(err_code);
}

void set_addr_from_data(uint8_t *key) {
	/* copy first 6 bytes */

    ble_gap_addr_t address = {
        .addr_type = BLE_GAP_ADDR_TYPE_RANDOM_STATIC
    };

    memcpy(address.addr, key, 6);

    #ifdef S130
    APP_ERROR_CHECK(sd_ble_gap_address_set(BLE_GAP_ADDR_CYCLE_MODE_NONE, &address));
    #else
    APP_ERROR_CHECK(sd_ble_gap_addr_set(&address));
    #endif
}

#if FEATURE_ENABLED(CUSTOM_ADVERTISEMENT_DATA)
void custom_data_advertisement_start(){
    if(custom_advertisement_running){
        return;
    }
    uint8_t data[] = { ADVERTISEMENT_CUSTOM_DATA };

    uint8_t battery_level = battery_level_get();

    uint8_t status_battery = STATUS_BATTERY_LEVEL_FULL;

    if(battery_level < 25){
        status_battery = STATUS_BATTERY_LEVEL_CRITICAL;
    }else if(battery_level < 50){
        status_battery = STATUS_BATTERY_LEVEL_LOW;
    }else if(battery_level < 75){
        status_battery = STATUS_BATTERY_LEVEL_MEDIUM;
    }

    data[12] |= status_battery << STATUS_BATTERY_POSITION;

    set_addr_from_data(data);

    ret_code_t err_code = sd_ble_gap_adv_data_set(
        data + 6,
        sizeof(data) - 6,
        NULL,
        0
    );
    APP_ERROR_CHECK(err_code);

    NRF_LOG_DEBUG("after advdata\n");

    static ble_gap_adv_params_t m_adv_params = {
        #if CUSTOM_ADVERTISEMENT_CONNECTABLE == 1
        .type        = BLE_GAP_ADV_TYPE_ADV_IND,
        #else
        .type        = BLE_GAP_ADV_TYPE_ADV_NONCONN_IND,
        #endif
        .p_peer_addr = NULL,
        .fp          = BLE_GAP_ADV_FP_ANY,
        .interval    = MSEC_TO_UNITS(ADVERTISEMENT_INTERVAL_CUSTOM_DATA, UNIT_0_625_MS),
        .timeout     = ADVERTISEMENT_TIMEOUT_CUSTOM_DATA
    };

    err_code = sd_ble_gap_adv_start(&m_adv_params);
    APP_ERROR_CHECK(err_code);

    custom_advertisement_running = true;
    is_advertising = true;
}
#endif

#if FEATURE_ENABLED(CUSTOM_ADVERTISEMENT_DATA)
void custom_data_advertisement_stop(){
    if(!custom_advertisement_running){
        return;
    }

    ret_code_t err_code = sd_ble_gap_adv_stop();
    // advertisement should not be running anyways, so we expect a INVALID_STATE error
    // APP_ERROR_CHECK(err_code);

    // restore real address
    #ifdef S130
    APP_ERROR_CHECK(sd_ble_gap_address_set(BLE_GAP_ADDR_CYCLE_MODE_NONE, &ble_address));
    #else
    APP_ERROR_CHECK(sd_ble_gap_addr_set(&ble_address));
    #endif
    APP_ERROR_CHECK(err_code);

    // calling this to restore old advertisement data
    advertising_init();

    custom_advertisement_running = false;
    is_advertising = false;
}
#endif

void advertising_event_handler(ble_adv_evt_t event) {
    is_advertising = event != BLE_ADV_EVT_IDLE;
    switch (event) {
        case BLE_ADV_EVT_FAST:
            NRF_LOG_DEBUG("advertising mode BLE_ADV_EVT_FAST\n");
            break;
        case BLE_ADV_EVT_SLOW:
            NRF_LOG_DEBUG("advertising mode BLE_ADV_EVT_SLOW\n");
            break;
        case BLE_ADV_EVT_IDLE:
            NRF_LOG_DEBUG("advertising mode BLE_ADV_EVT_IDLE\n");
            #if FEATURE_ENABLED(CUSTOM_ADVERTISEMENT_DATA)
            NRF_LOG_DEBUG("advertising mode custom\n");
            custom_data_advertisement_start();
            #endif
            break;
        default:
            NRF_LOG_DEBUG("advertising mode UNKNOWN\n");
            break;
    }
}

void advertising_init() {
    ret_code_t err_code;

    uint8_t uuid_len = 0;
    ble_uuid_t uuids[4]; // size may be updated in the future

    #if FEATURE_ENABLED(BINARY_SENSOR)
        uuids[uuid_len].uuid = UUID_BINARY_SENSOR_SERVICE;
        uuids[uuid_len].type = BLE_UUID_TYPE_BLE;
        uuid_len++;
    #endif

    #if FEATURE_ENABLED(AUTOMATION_IO)
        uuids[uuid_len].uuid = UUID_AUTOMATION_IO_SERVICE;
        uuids[uuid_len].type = BLE_UUID_TYPE_BLE;
        uuid_len++;
    #endif

    #if FEATURE_ENABLED(CYCLING_SPEED_CADENCE)
        uuids[uuid_len].uuid = UUID_CSC_SERVICE;
        uuids[uuid_len].type = BLE_UUID_TYPE_BLE;
        uuid_len++;
    #endif

    #if FEATURE_ENABLED(HID)
        uuids[uuid_len].uuid = BLE_UUID_HID_SERVICE;
        uuids[uuid_len].type = BLE_UUID_TYPE_BLE;
        uuid_len++;
    #endif

    #ifdef S130
    ble_adv_modes_config_t advertising_modes_config = {
      .ble_adv_whitelist_enabled = false,
      .ble_adv_directed_enabled = false,
      .ble_adv_directed_slow_enabled = false,
      .ble_adv_fast_enabled = true,
      .ble_adv_slow_enabled = true,

      .ble_adv_fast_interval = APP_ADV_INTERVAL_FAST,
      .ble_adv_fast_timeout = ADVERTISEMENT_TIMEOUT_FAST,
      .ble_adv_slow_interval = advertising_interval,
      .ble_adv_slow_timeout = ADVERTISEMENT_TIMEOUT_SLOW,
    };

    ble_advdata_t advertisement_data = {
      .name_type = BLE_ADVDATA_FULL_NAME,
      .include_appearance = true,
      .flags = BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED | BLE_GAP_ADV_FLAG_LE_GENERAL_DISC_MODE,
      .uuids_complete = {
          .uuid_cnt = uuid_len,
          .p_uuids = uuids
      }
        // clearly something forgotten here
    };

    err_code = ble_advertising_init(
        &advertisement_data,
        NULL,
        &advertising_modes_config,
        advertising_event_handler,
        NULL
    );
    #else
    ble_advertising_init_t init;

    memset(&init, 0, sizeof(init));

    init.advdata.name_type                = BLE_ADVDATA_FULL_NAME;
    init.advdata.include_appearance       = true;
    init.advdata.flags                    = BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED | BLE_GAP_ADV_FLAG_LE_GENERAL_DISC_MODE;
    init.advdata.uuids_complete.uuid_cnt  = uuid_len;
    init.advdata.uuids_complete.p_uuids   = uuids;

    init.config.ble_adv_fast_enabled      = true;
    init.config.ble_adv_fast_interval     = APP_ADV_INTERVAL_FAST;
    init.config.ble_adv_fast_timeout      = ADVERTISEMENT_TIMEOUT_FAST;

    init.config.ble_adv_slow_enabled      = true;
    init.config.ble_adv_slow_interval      = advertising_interval;
    init.config.ble_adv_slow_timeout      = ADVERTISEMENT_TIMEOUT_SLOW;

    init.evt_handler   = advertising_event_handler;

    err_code = ble_advertising_init(&m_advertising, &init);
    #endif

    APP_ERROR_CHECK(err_code);

    advertising_initialized = true;
}


/**@brief Function for starting advertising.
 */
void advertising_start() {
    if(!advertising_initialized) {
        return;
    }

    ret_code_t err_code;

    #ifdef S130
    err_code = ble_advertising_start(BLE_ADV_MODE_FAST);
    #else
    err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);
    #endif
    APP_ERROR_CHECK(err_code);
}

#if FAMILY == 51
void sys_evt_dispatch(uint32_t sys_evt) {
    storage_on_sys_evt(sys_evt);
    ble_advertising_on_sys_evt(sys_evt);
}
#else
void sys_evt_dispatch(uint32_t sys_evt, void * p_contextt) {
    ble_advertising_on_sys_evt(sys_evt, &m_advertising);
}
#endif

void ble_disable_rf(){
    if(connection_handle != BLE_CONN_HANDLE_INVALID){
        sd_ble_gap_disconnect(connection_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
        connection_handle = BLE_CONN_HANDLE_INVALID;
    }
    advertising_stop();
}

void ble_stack_init(void) {
    uint32_t err_code;

    #if FAMILY == 51
    nrf_clock_lf_cfg_t clock_lf_cfg = {
        .source        = NRF_SDH_CLOCK_LF_SRC,
        .rc_ctiv       = NRF_SDH_CLOCK_LF_RC_CTIV,
        .rc_temp_ctiv  = NRF_SDH_CLOCK_LF_RC_TEMP_CTIV,
        .xtal_accuracy = NRF_SDH_CLOCK_LF_ACCURACY
    };

    // Initialize the SoftDevice handler module.
    SOFTDEVICE_HANDLER_INIT(&clock_lf_cfg, NULL);

    ble_enable_params_t ble_enable_params;
    err_code = softdevice_enable_get_default_config(CENTRAL_LINK_COUNT,
        PERIPHERAL_LINK_COUNT,
        &ble_enable_params);
    ble_enable_params.common_enable_params.vs_uuid_count = 3;
    APP_ERROR_CHECK(err_code);

    //Check the ram settings against the used number of links
    CHECK_RAM_START_ADDR(CENTRAL_LINK_COUNT, PERIPHERAL_LINK_COUNT);

        // Subscribe for BLE events.
    err_code = softdevice_ble_evt_handler_set(ble_evt_dispatch);
    APP_ERROR_CHECK(err_code);

    err_code = softdevice_sys_evt_handler_set(sys_evt_dispatch);
    APP_ERROR_CHECK(err_code);

    err_code = softdevice_enable(&ble_enable_params);
    APP_ERROR_CHECK(err_code);
    #else
    err_code = nrf_sdh_enable_request();
    APP_ERROR_CHECK(err_code);

    // Fetch the start address of the application RAM.
    uint32_t ram_start = 0;
    err_code = nrf_sdh_ble_app_ram_start_get(&ram_start);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_sdh_ble_default_cfg_set(1, &ram_start);

    // Enable BLE stack.
    err_code = nrf_sdh_ble_enable(&ram_start);
    APP_ERROR_CHECK(err_code);

    NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_dispatch, NULL);
    NRF_SDH_SOC_OBSERVER(m_adv_soc_obs, BLE_ADV_SOC_OBSERVER_PRIO, sys_evt_dispatch, NULL);
    #endif
}

/**@brief Function for the GAP initialization.
 *
 * @details This function sets up all the necessary GAP (Generic Access Profile) parameters of the
 *          device including the device name, appearance, and the preferred connection parameters.
 */
void gap_params_init(uint8_t *device_name, uint32_t device_name_length) {
    uint32_t                err_code;
    ble_gap_conn_sec_mode_t sec_mode;

    SET_MODE_SECURE(&sec_mode);

    err_code = sd_ble_gap_device_name_set(&sec_mode,
        device_name,
        device_name_length
    );
    APP_ERROR_CHECK(err_code);

    ble_gap_conn_params_t gap_conn_params;

    uint8_t params_data[10];
    storage_read_connection_params_configuration(params_data);

    if (params_data[0] != 0xff) {
        ble_configuration_connection_params_packet_t *params =
            (ble_configuration_connection_params_packet_t *)params_data;

        gap_conn_params.min_conn_interval = MSEC_TO_UNITS(params->min_conn_interval, UNIT_1_25_MS);
        gap_conn_params.max_conn_interval = MSEC_TO_UNITS(params->max_conn_interval, UNIT_1_25_MS);
        gap_conn_params.slave_latency = params->slave_latency;
        gap_conn_params.conn_sup_timeout = MSEC_TO_UNITS(params->conn_sup_timeout, UNIT_10_MS);

        advertising_interval = MSEC_TO_UNITS(params->advertising_interval, UNIT_0_625_MS);

        err_code = sd_ble_gap_ppcp_set(&gap_conn_params);

        if (err_code == NRF_SUCCESS) {
            return;
        }
        #ifdef NRF51
        NRF_LOG_ERROR("failed setting stored connection parameters: %s\n", (uint32_t)ERR_TO_STR(err_code));
        #else
        NRF_LOG_ERROR("failed setting stored connection parameters: %d\n", err_code);
        #endif
    }
    else {
        NRF_LOG_WARNING("Connection params not configured\n");
    }

    // if we have no outputs, we can sleep a lot more since we don't have to receive any data
    // if no pins are configured at all though we should stay latent for configuration
    if(gpio_get_output_digital_pin_count() == 0 && gpio_get_input_digital_pin_count() > 0){
        gap_conn_params.min_conn_interval = BLE_NO_OUTPUTS_DEFAULT_MIN_CONN_INTERVAL;
        gap_conn_params.max_conn_interval = BLE_NO_OUTPUTS_DEFAULT_MAX_CONN_INTERVAL;
        gap_conn_params.slave_latency     = BLE_NO_OUTPUTS_DEFAULT_SLAVE_LATENCY;
        gap_conn_params.conn_sup_timeout  = BLE_NO_OUTPUTS_DEFAULT_CONN_SUP_TIMEOUT;
    }else{
        gap_conn_params.min_conn_interval = BLE_OUTPUTS_DEFAULT_MIN_CONN_INTERVAL;
        gap_conn_params.max_conn_interval = BLE_OUTPUTS_DEFAULT_MAX_CONN_INTERVAL;
        gap_conn_params.slave_latency     = BLE_OUTPUTS_DEFAULT_SLAVE_LATENCY;
        gap_conn_params.conn_sup_timeout  = BLE_OUTPUTS_DEFAULT_CONN_SUP_TIMEOUT;
    }
    err_code = sd_ble_gap_ppcp_set(&gap_conn_params);
    APP_ERROR_CHECK(err_code);

    #if FEATURE_ENABLED(HID)
    // set appearance to gamepad
    err_code = sd_ble_gap_appearance_set(0x03C4);
    APP_ERROR_CHECK(err_code);
    #elif FEATURE_ENABLED(CYCLING_SPEED_CADENCE)
    // set appearance to speed sensor
    // may include cadence sensor later
    err_code = sd_ble_gap_appearance_set(0x0482);
    APP_ERROR_CHECK(err_code);
    #endif
}


uint32_t bas_init() {
    return ble_bas_init(&bas_init);
}

void ble_handle_connection_parameters_configuration_update(ble_configuration_connection_params_packet_t *packet) {
    // should to some validation here

    ble_gap_conn_params_t real_params = {
        .min_conn_interval = MSEC_TO_UNITS(packet->min_conn_interval, UNIT_1_25_MS),
        .max_conn_interval = MSEC_TO_UNITS(packet->max_conn_interval, UNIT_1_25_MS),
        .slave_latency = packet->slave_latency,
        .conn_sup_timeout = MSEC_TO_UNITS(packet->conn_sup_timeout, UNIT_10_MS),
    };

    NRF_LOG_DEBUG("min interval: %d\n", packet->min_conn_interval);
    NRF_LOG_DEBUG("max interval: %d\n", packet->max_conn_interval);
    NRF_LOG_DEBUG("slave latency: %d\n", packet->slave_latency);
    NRF_LOG_DEBUG("sup timeout: %d\n", packet->conn_sup_timeout);
    NRF_LOG_DEBUG("adv interval: %d\n", packet->advertising_interval);

    ret_code_t err_code;

    #ifdef S130
    err_code = ble_conn_params_change_conn_params(
        &real_params
    );
    #else
    err_code = ble_conn_params_change_conn_params(
        0x00,
        &real_params
    );
    #endif

    if (err_code != NRF_SUCCESS) {
        #ifdef NRF51
        NRF_LOG_ERROR("update failed: %s\n", (uint32_t)ERR_TO_STR(err_code));
        #else
        NRF_LOG_ERROR("update failed: %d\n", err_code);
        #endif
    }
    else {
        NRF_LOG_DEBUG("udpate success\n");
    }
    // seems to lock up the chip
    // APP_ERROR_CHECK(err_code);
}

ret_code_t dis_init(){
    char *manufacturer = "https://github.com/dakhnod/BLEnky";
    char *version_fw = FIRMWARE_VERSION;
    ble_dis_init_t init = {
        .fw_rev_str = {
            .p_str = (uint8_t*) version_fw,
            .length = strlen(version_fw)
        },
        .manufact_name_str = {
            .p_str = (uint8_t*) manufacturer,
            .length = strlen(manufacturer)
        }
    };

    #ifdef S130
    ble_srv_security_mode_t sec_mode;
    SET_MODE_SECURE(&sec_mode.read_perm);
    BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&sec_mode.write_perm);
    init.dis_attr_md = sec_mode;
    #else
    #if STATIC_PASSKEY_ENABLED == 1
    init.dis_char_rd_sec = SEC_MITM;
    #else
    init.dis_char_rd_sec = SEC_OPEN;
    #endif
    #endif

    return ble_dis_init(&init);
}

/**@brief Function for initializing services that will be used by the application.
 */
void services_init(void) {
    ret_code_t err_code;

    err_code = dis_init();
    APP_ERROR_CHECK(err_code);

    #if FEATURE_ENABLED(BATTERY_PROFILE)
    err_code = bas_init();
    APP_ERROR_CHECK(err_code);
    #endif

    err_code = ble_configuration_service_init(ble_handle_connection_parameters_configuration_update);
    APP_ERROR_CHECK(err_code);

    #if FEATURE_ENABLED(AUTOMATION_IO)
    err_code = ble_aio_init();
    APP_ERROR_CHECK(err_code);
    #endif

    #if FEATURE_ENABLED(GPIO_ASM)
    ble_gpio_asm_init();
    #endif

    #if FEATURE_ENABLED(HID)
    err_code = ble_hid_init();
    APP_ERROR_CHECK(err_code);
    #endif

    #if FEATURE_ENABLED(CYCLING_SPEED_CADENCE)
    err_code = ble_csc_init();
    APP_ERROR_CHECK(err_code);
    #endif

    err_code = ble_temperature_init();
    APP_ERROR_CHECK(err_code);

    // TODO: add BSS init here
}

void
advertising_stop() {
    if (!is_advertising) {
        return;
    }
    #ifdef S130
    uint32_t err = sd_ble_gap_adv_stop();
    #else
    uint32_t err = sd_ble_gap_adv_stop(m_advertising.adv_handle);
    #endif
    APP_ERROR_CHECK(err);
    is_advertising = false;
}


================================================
FILE: src/ble/sensor_ble.h
================================================
#ifndef SENSOR_BLE_H
#define SENSOR_BLE_H

#include "ble_conn_params.h"
#include "ble_srv_common.h"
#include "ble_advdata.h"
#include "sensor_gpio.h"
#include "battery.h"
#include "app_timer.h"
#include "ble_hci.h"
#include "ble_advertising.h"
#include "ble_binary_sensor_service.h"
#include "ble_automation_io_service.h"
#include "storage.h"

#ifdef S130
#include "softdevice_handler.h"
#include "fstorage.h"
#else
#include "nrf_sdh.h"
#include "nrf_sdh_ble.h"
#include "nrf_sdh_soc.h"
#include "nrf_fstorage.h"
#include "nrf_ble_gatt.h"
#endif

#define APP_FEATURE_NOT_SUPPORTED       BLE_GATT_STATUS_ATTERR_APP_BEGIN + 2        /**< Reply when unsupported features are requested. */

#define CENTRAL_LINK_COUNT              0                                 /**< Number of central links used by the application. When changing this number remember to adjust the RAM settings*/
#define PERIPHERAL_LINK_COUNT           1                                 /**< Number of peripheral links used by the application. When changing this number remember to adjust the RAM settings*/


/**< Value of the RTC1 PRESCALER register. */

void on_ble_evt(const ble_evt_t *p_ble_evt);
#ifdef S130
void ble_evt_dispatch(ble_evt_t *p_ble_evt);
#else
void ble_evt_dispatch(const ble_evt_t *p_ble_evt, void * p_context);
#endif
void power_manage(void);
void on_conn_params_evt(ble_conn_params_evt_t *p_evt);
void conn_params_error_handler(uint32_t nrf_error);
void conn_params_init(void);
void advertising_stack_init(void);
void advertising_event_handler(ble_adv_evt_t event);
void advertising_data_init(void);
void advertising_init(void);
void advertising_start(void);
void ble_stack_init(void);
void gap_params_init(uint8_t *device_name, uint32_t device_name_length);
uint32_t bas_init(void);
void services_init(void);
void advertising_stop(void);
void ble_init(void);
void ble_handle_input_change(int highest_changed_index);
void ble_disable_rf();


#endif


================================================
FILE: src/ble/services/automation_io/ble_automation_io_service.c
================================================
#include "sdk_common.h"
#include "ble_automation_io_service.h"
#include <string.h>
#include "ble_l2cap.h"
#include "ble_srv_common.h"
#include "app_error.h"
#include "storage.h"
#include "ble_binary_sensor_service.h"
#include "ble_configuration_service.h"
#include "ble_helpers.h"
#include "encoding.h"
#include "feature_config.h"

uint16_t ble_aio_connection_handle = BLE_CONN_HANDLE_INVALID;

uint16_t ble_aio_service_handle = BLE_GATT_HANDLE_INVALID;

uint16_t ble_aio_digital_out_write_handle = BLE_GATT_HANDLE_INVALID;
uint16_t ble_aio_digital_out_cccd_handle = BLE_GATT_HANDLE_INVALID;

uint16_t ble_aio_digital_in_write_handle = BLE_GATT_HANDLE_INVALID;
uint16_t ble_aio_digital_in_cccd_handle = BLE_GATT_HANDLE_INVALID;

uint16_t ble_aio_analog_out_write_handles[GPIO_OUTPUT_ANALOG_PIN_LIMIT];

uint8_t ble_aio_send_digital_input_updates = false;
uint32_t ble_aio_output_analog_pin_count;

void ble_aio_on_connect(const ble_evt_t *p_ble_evt)
{
    ble_aio_connection_handle = p_ble_evt->evt.gap_evt.conn_handle;
}

void ble_aio_on_disconnect(const ble_evt_t *p_ble_evt)
{
    UNUSED_PARAMETER(p_ble_evt);
    ble_aio_connection_handle = BLE_CONN_HANDLE_INVALID;
    ble_aio_send_digital_input_updates = false;
}

void handle_pin_digital_in_cccd_write(const ble_gatts_evt_write_t *write_evt)
{
    if (write_evt->len == 2)
    {
        ble_aio_send_digital_input_updates = ble_srv_is_notification_enabled(write_evt->data);
    }
}

void ble_aio_handle_pin_digital_data(
    const uint8_t *pin_data,
    uint32_t pin_data_length)
{
    uint32_t available_output_count = gpio_get_output_digital_pin_count();
    uint32_t sent_output_count = pin_data_length * 4;

    uint32_t parsed_output_count = MIN(available_output_count, sent_output_count);

    for (int index = 0; index < parsed_output_count; index++)
    {
        uint8_t output_bits = encoding_get_pin_bits(pin_data, pin_data_length, index);
        gpio_write_output_digital_pin(index, output_bits);
    }
}


void ble_aio_handle_pin_analog_data(
    uint32_t index,
    uint16_t duty_cycle
    )
{
    if(duty_cycle == 0xffff){
        return;
    }
    if(index > gpio_get_output_analog_pin_count()){
        NRF_LOG_ERROR("writing to unconfigured analog channel %i\n", index);
        return;
    }
    gpio_write_output_analog_pin_us(index, duty_cycle);
}

void handle_digital_out_write(const ble_gatts_evt_write_t *write_evt)
{
    const uint8_t *data = write_evt->data;
    uint32_t len = write_evt->len;

    ble_aio_handle_pin_digital_data(data, len);
}

void handle_pin_analog_out_write(uint32_t index, const ble_gatts_evt_write_t *write_evt)
{
    if (write_evt->len != 2)
    {
        NRF_LOG_ERROR("wrong analog value\n");
        return;
    }

    uint16_t value = *((uint16_t *)write_evt->data);

    if (value == 0xffff)
    {
        NRF_LOG_DEBUG("ignoring analog %i value\n", index);
        return;
    }

    gpio_write_output_analog_pin_us(index, value);
}

void ble_aio_authorize_digital_out()
{
    uint32_t output_count = gpio_get_output_digital_pin_count();
    uint32_t data_length = encoding_get_byte_count_from_pins(output_count);

    uint8_t data[data_length];
    uint8_t output_states[output_count];

    gpio_encode_output_states(output_states);

    encode_states_to_bytes(output_states, output_count, data, data_length);

    ble_gatts_rw_authorize_reply_params_t authorize_params = {
        .type = BLE_GATTS_AUTHORIZE_TYPE_READ,
        .params.read = {
            .gatt_status = BLE_GATT_STATUS_SUCCESS,
            .update = 1,
            .offset = 0,
            .len = data_length,
            .p_data = data}};

    sd_ble_gatts_rw_authorize_reply(
        ble_aio_connection_handle,
        &authorize_params);
}

ret_code_t ble_aio_characteristic_digital_output_add()
{
    ble_helper_characteristic_init_t init = {
        .service_handle = ble_aio_service_handle,
        #if AUTOMATION_IO_DIFFERENT_UUIDS == 1
        .uuid = UUID_DIGITAL_CHARACTERISTIC_OUTPUT,
        #else
        .uuid = UUID_DIGITAL_CHARACTERISTIC,
        #endif
        .description_str = "Digital output",
        .number_of_digitals = gpio_get_output_digital_pin_count(),
        .description = 0x01,
        .is_writable = true,
        .is_readable = true,
        .authorize_read = true,
        .max_length = encoding_get_byte_count_from_pins(gpio_get_output_digital_pin_count()),
        .value_handle = &ble_aio_digital_out_write_handle,
        .cccd_handle = &ble_aio_digital_out_cccd_handle
    };
    return ble_helper_characteristic_add(&init);
}

ret_code_t ble_aio_characteristic_analog_output_add(uint32_t index)
{
    ble_helper_characteristic_init_t init = {
        .service_handle = ble_aio_service_handle,
        .uuid = UUID_ANALOG_CHARACTERISTIC,
        .description_str = "Analog output",
        .number_of_digitals = 0x00,
        .description = (uint8_t)(index + 1),
        .is_writable = true,
        .is_readable = true,
        .max_length = 2,
        .value_handle = ble_aio_analog_out_write_handles + index,
        .cccd_handle = &ble_aio_digital_out_cccd_handle
    };
    return ble_helper_characteristic_add(&init);
}

ret_code_t ble_aio_characteristic_digital_input_add()
{
    ble_helper_characteristic_init_t init = {
        .service_handle = ble_aio_service_handle,
        .uuid = UUID_DIGITAL_CHARACTERISTIC,
        .description_str = "Digital input",
        .number_of_digitals = gpio_get_input_digital_pin_count(),
        .description = 0x02,
        .is_readable = true,
        .is_notifiable = true,
        .authorize_read = true,
        .max_length = encoding_get_byte_count_from_pins(gpio_get_input_digital_pin_count()),
        .value_handle = &ble_aio_digital_in_write_handle,
        .cccd_handle = &ble_aio_digital_in_cccd_handle
    };
    return ble_helper_characteristic_add(&init);
}

void ble_aio_on_write(const ble_evt_t *p_ble_evt)
{
    const ble_gatts_evt_write_t *write_evt = &p_ble_evt
                                            ->evt
                                            .gatts_evt
                                            .params
                                            .write;

    uint16_t handle = write_evt->handle;

    if (handle == ble_aio_digital_out_write_handle)
    {
        handle_digital_out_write(write_evt);
        return;
    }
    if (handle == ble_aio_digital_in_cccd_handle)
    {
        handle_pin_digital_in_cccd_write(write_evt);
        return;
    }
    for (uint32_t i = 0; i < ble_aio_output_analog_pin_count; i++)
    {
        if (handle == ble_aio_analog_out_write_handles[i])
        {
            handle_pin_analog_out_write(i, write_evt);
            return;
        }
    }
}

void ble_aio_authorize_digital_in()
{
    uint32_t input_count = gpio_get_input_digital_pin_count();
    uint32_t data_length = encoding_get_byte_count_from_pins(input_count);

    uint8_t data[data_length];
    uint8_t input_states[input_count];

    gpio_encode_input_states(input_states);

    encode_states_to_bytes(input_states, input_count, data, data_length);

    ble_gatts_rw_authorize_reply_params_t authorize_params = {
        .type = BLE_GATTS_AUTHORIZE_TYPE_READ,
        .params.read = {
            .gatt_status = BLE_GATT_STATUS_SUCCESS,
            .update = 1,
            .offset = 0,
            .len = data_length,
            .p_data = data}};

    sd_ble_gatts_rw_authorize_reply(
        ble_aio_connection_handle,
        &authorize_params);
}

void ble_aio_update_digital_in_states()
{
    if (ble_aio_connection_handle == BLE_CONN_HANDLE_INVALID || (!ble_aio_send_digital_input_updates))
    {
        return;
    }

    uint32_t input_count = gpio_get_input_digital_pin_count();
    uint32_t data_length = encoding_get_byte_count_from_pins(input_count);

    uint8_t data[data_length];
    uint8_t input_states[input_count];

    gpio_encode_input_states(input_states);

    encode_states_to_bytes(input_states, input_count, data, data_length);

    ret_code_t err_code;

    uint16_t len = data_length;

    ble_gatts_hvx_params_t params = {
        .handle = ble_aio_digital_in_write_handle,
        .type = BLE_GATT_HVX_NOTIFICATION,
        .offset = 0,
        .p_len = &len,
        .p_data = data};

    err_code = sd_ble_gatts_hvx(
        ble_aio_connection_handle,
        &params);
    APP_ERROR_CHECK(err_code);
}

void write_bit(uint8_t *byte, uint32_t position, uint8_t value)
{
    uint8_t shifted = 0b1 << position;
    if (value)
    {
        (*byte) |= shifted;
    }
    else
    {
        (*byte) &= ~shifted;
    }
}

void encode_states_to_bytes(uint8_t *states, uint32_t state_count, uint8_t *buffer, uint16_t buffer_len)
{
    uint32_t byte_index = 0;
    uint32_t bit_index = 0;
    uint8_t *current_byte = 0;

    for (uint32_t i = 0; i < state_count; i++)
    {
        byte_index = buffer_len - (i / 4) - 1;
        bit_index = (i % 4) * 2;

        uint8_t state = states[i];
        current_byte = buffer + byte_index;

        write_bit(current_byte, bit_index + 0, (state & 0b01) >> 0);
        write_bit(current_byte, bit_index + 1, (state & 0b10) >> 1);
    }

    // set remaining bits of last byte to 0b11
    for (uint32_t i = bit_index + 2; i < 8; i++)
    {
        write_bit(current_byte, i, 1);
    }
}

void ble_aio_on_authorize(const ble_evt_t *p_ble_evt)
{
    const ble_gatts_evt_rw_authorize_request_t *req = &(p_ble_evt
                                                      ->evt.gatts_evt
                                                      .params
                                                      .authorize_request);

    if (req->type == BLE_GATTS_AUTHORIZE_TYPE_READ)
    {

        uint16_t handle = req
                              ->request
                              .read
                              .handle;

        if (handle == ble_aio_digital_out_write_handle)
        {
            ble_aio_authorize_digital_out();
            return;
        }
        if (handle == ble_aio_digital_in_write_handle)
        {
            ble_aio_authorize_digital_in();
            return;
        }
        return;
    }
}

void ble_aio_on_ble_evt(const ble_evt_t *p_ble_evt)
{
    switch (p_ble_evt->header.evt_id)
    {
    case BLE_GAP_EVT_CONNECTED:
        ble_aio_on_connect(p_ble_evt);
        break;

    case BLE_GAP_EVT_DISCONNECTED:
        ble_aio_on_disconnect(p_ble_evt);
        break;

    case BLE_GATTS_EVT_WRITE:
        ble_aio_on_write(p_ble_evt);
        break;

    case BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST:
        ble_aio_on_authorize(p_ble_evt);
        break;

    default:
        // No implementation needed.
        break;
    }
}

void ble_aio_handle_input_change(int highest_changed_index)
{
    if (ble_aio_connection_handle == BLE_CONN_HANDLE_INVALID || (!ble_aio_send_digital_input_updates))
    {
        return;
    }

    // only need this many bytes since we only report the changed pin
    uint32_t input_count = highest_changed_index + 1;

    uint16_t data_length = encoding_get_byte_count_from_pins(input_count);

    uint8_t data[data_length];
    uint8_t input_states[input_count];

    // set every state before out changed index to undefined
    for(uint8_t i = 0; i < input_count; i++){
        gpio_config_input_digital_t *config = gpio_find_input_by_index(i);
        if(!config->changed){
            input_states[i] = 0b11;
            continue;
        }
        input_states[i] = config->state;
    }

    encode_states_to_bytes(input_states, input_count, data, data_length);

    ret_code_t err_code;

    ble_gatts_hvx_params_t params = {
        .handle = ble_aio_digital_in_write_handle,
        .type = BLE_GATT_HVX_NOTIFICATION,
        .offset = 0,
        .p_len = &data_length,
        .p_data = data
    };

    err_code = sd_ble_gatts_hvx(
        ble_aio_connection_handle,
        &params);
    APP_ERROR_CHECK(err_code);
}

ret_code_t ble_aio_init()
{
    ret_code_t err_code;
    ble_uuid_t ble_uuid;

    BLE_UUID_BLE_ASSIGN(ble_uuid, UUID_AUTOMATION_IO_SERVICE);

    // uint32_t output_digital_pin_count = gpio_get_output_digital_pin_count();
    uint32_t output_analog_pin_count = gpio_get_output_analog_pin_count();
    // uint32_t input_digital_pin_count = gpio_get_input_digital_pin_count();

    ble_aio_output_analog_pin_count = output_analog_pin_count;

    err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &ble_aio_service_handle);
    APP_ERROR_CHECK(err_code);

    err_code = ble_aio_characteristic_digital_input_add();
    APP_ERROR_CHECK(err_code);

    err_code = ble_aio_characteristic_digital_output_add();
    APP_ERROR_CHECK(err_code);

    for (uint32_t i = 0; i < output_analog_pin_count; i++)
    {
        err_code = ble_aio_characteristic_analog_output_add(i);
    }

    ble_aio_update_digital_in_states();
    
    #if FEATURE_ENABLED(BINARY_SENSOR)
    err_code = ble_bss_init();
    APP_ERROR_CHECK(err_code);
    #endif

    return NRF_SUCCESS;
}


================================================
FILE: src/ble/services/automation_io/ble_automation_io_service.h
================================================
#ifndef BLE_AIO_H
#define BLE_AIO_H

#include <stdint.h>
#include <stdbool.h>
#include "ble.h"
#include "ble_srv_common.h"
#include "nrf_log.h"
#include "sensor_gpio.h"
// #include "nrf_ble_gatt.h"

#ifdef __cplusplus
extern "C" {
#endif

#define UUID_AUTOMATION_IO_SERVICE         0x1815
#define UUID_DIGITAL_CHARACTERISTIC        0x2A56
#define UUID_DIGITAL_CHARACTERISTIC_OUTPUT 0x2A57
#define UUID_ANALOG_CHARACTERISTIC         0x2A58
#define UUID_PIN_CONFIG_CHARACTERISTIC     0x0001
#define UUID_BLE_CONFIG_CHARACTERISTIC     0x0002
#define UUID_GPIO_ASM_CHARACTERISTIC       0x0003

    uint32_t ble_aio_init();


    void ble_aio_on_ble_evt(const ble_evt_t *p_ble_evt);

    void ble_aio_on_authorize(const ble_evt_t *p_ble_evt);
    void encode_states_to_bytes(uint8_t *states, uint32_t state_count, uint8_t *buffer, uint16_t buffer_len);
    uint32_t ble_aio_get_byte_count_from_pins(uint32_t pin_count);
    void ble_aio_handle_input_change(int highest_changed_index);
    void ble_aio_handle_pin_digital_data(const uint8_t *pin_data, uint32_t pin_data_length);
    void ble_aio_handle_pin_analog_data(uint32_t index, uint16_t duty_cycle);

#ifdef __cplusplus
}
#endif

#endif // BLE_AIO_H

/** @} */


================================================
FILE: src/ble/services/binary_sensor/ble_binary_sensor_service.c
================================================
#include "sdk_common.h"
#include "ble_binary_sensor_service.h"
#include <string.h>
#include "ble_l2cap.h"
#include "ble_srv_common.h"
#include "app_error.h"
#include "feature_config.h"

uint16_t ble_bss_connection_handle = BLE_CONN_HANDLE_INVALID;
uint16_t ble_bss_service_handle;
uint16_t ble_bss_control_handle;
uint16_t ble_bss_response_handle;
uint16_t ble_bss_cccd_handle;

bool send_responses = false;
bool send_updates = false;

enum opening_closing_state_t current_state_ = OPEN;
uint16_t event_count_ = 0;

void ble_bss_on_connect(ble_evt_t *p_ble_evt) {
    ble_bss_connection_handle = p_ble_evt->evt.gap_evt.conn_handle;
}

void ble_bss_on_disconnect(ble_evt_t *p_ble_evt) {
    UNUSED_PARAMETER(p_ble_evt);
    ble_bss_connection_handle = BLE_CONN_HANDLE_INVALID;
    send_updates = false;
    send_responses = false;
}

void on_bss_cccd_write(ble_gatts_evt_write_t *p_evt_write) {
    if (p_evt_write->len == 2) {
        send_responses = ble_srv_is_indication_enabled(p_evt_write->data);
    }
}

void on_control_characteristic_write(ble_gatts_evt_write_t *write) {
    uint16_t length = write->len;
    uint8_t *data = write->data;

    parse_full_packet_with_split_header(data, length);
}

void parse_full_packet_with_split_header(uint8_t *data, uint16_t length) {
    if (length < 1) {
        return;
    }
    //uint8_t header = data[0];
    // here we could buffer split messages
    parse_packet(data + 1, length - 1);
}

void parse_packet(uint8_t *data, uint16_t length) {
    if (length < 4) {
        return;
    }
    message_header_t *message_header = (message_header_t *)data;
    message_parameter_t params[message_header->parameter_count];
    uint8_t *payload_ptr = data + 4;
    for (uint8_t i = 0; i < message_header->parameter_count; i++) {
        message_parameter_without_data_t *parameter = (message_parameter_without_data_t *)payload_ptr;

        params[i].parameter_id = parameter->parameter_id;
        params[i].parameter_length = parameter->parameter_length;
        params[i].data = payload_ptr + 4;

        payload_ptr += parameter->parameter_length + 4;
    }

    NRF_LOG_DEBUG("message_id: %i, parameter count: %i\n", message_header->message_id, message_header->parameter_count);
    parse_packet_decoded(message_header->message_id, params, message_header->parameter_count);
}

void parse_packet_decoded(enum message_id_t message_id, message_parameter_t *parameters, uint8_t parameter_count) {
    switch (message_id) {
        case MESSAGE_ID_GET_SENSOR_STATUS_COMMAND:
            parse_get_sensor_command(parameters, parameter_count);
            break;
        case MESSAGE_ID_SET_SENSOR_STATUS_COMMAND:
            parse_set_sensor_command(parameters, parameter_count);
            break;

        default:
            break;
    }
}

void parse_set_sensor_command(message_parameter_t *parameters, uint8_t parameter_count) {
    if (parameter_count != 2) {
        respond_set_sensor_command(FAILURE);
        return;
    }
    enum sensor_type_t sensor_type = parameters[0].data[0];
    enum report_state_t report_state = parameters[1].data[0];

    handle_set_sensor_command(sensor_type, report_state);
}

void handle_set_sensor_command(enum sensor_type_t sensor_type, enum report_state_t report_state) {
    if (sensor_type != OPENING_CLOSING) {
        respond_set_sensor_command(FAILURE);
        return;
    }

    send_updates = report_state;
    respond_set_sensor_command(OK);
};

void parse_get_sensor_command(message_parameter_t *parameters, uint8_t parameter_count) {
    if (parameter_count < 1) {
        respond_get_sensor_command(FAILURE, 0, 0);
    }
    enum sensor_type_t sensor_type = parameters[0].data[0];

    handle_get_sensor_command(sensor_type);
}

void handle_get_sensor_command(enum sensor_type_t sensor_type) {
    if (sensor_type == OPENING_CLOSING) {
        handle_get_single_open_close_sensor_command();
    }
    else if (sensor_type == VIBRATION_DETECTION) {
        handle_get_single_vibration_sensor_command();
    }
    else if (sensor_type == HUMAN_DETECTION) {
        handle_get_single_human_sensor_command();
    }
    else if (sensor_type == MULTIPLE_OPENING_CLOSING) {
        handle_get_multiple_open_close_sensor_command();
    }
    else if (sensor_type == MULTIPLE_VIBRATION_DETECTION) {
        handle_get_multiple_vibration_sensor_command();
    }
    else if (sensor_type == MULTIPLE_HUMAN_DETECTION) {
        handle_get_multiple_human_sensor_command();
    }
}

void handle_get_multiple_open_close_sensor_command() {
    respond_get_sensor_command(FAILURE, 0, 0);
}

void handle_get_multiple_human_sensor_command() {
    respond_get_sensor_command(FAILURE, 0, 0);
}

void handle_get_multiple_vibration_sensor_command() {
    respond_get_sensor_command(FAILURE, 0, 0);
}

void handle_get_single_human_sensor_command() {
    respond_get_sensor_command(FAILURE, 0, 0);
}

void handle_get_single_vibration_sensor_command() {
    respond_get_sensor_command(FAILURE, 0, 0);
}

void handle_get_single_open_close_sensor_command() {
    respond_get_sensor_command(OK, current_state_, event_count_);
}

void respond_set_sensor_command(enum result_code_t result_code) {
    respond_set_sensor(MESSAGE_ID_SET_SENSOR_STATUS_RESPONSE, result_code);
}

void respond_get_sensor_command(enum result_code_t result_code, enum opening_closing_state_t state, uint16_t count) {
    respond_get_sensor(MESSAGE_ID_GET_SENSOR_STATUS_RESPONSE, result_code, state, count);
}

void respond_get_sensor_event(enum result_code_t result_code, enum opening_closing_state_t state, uint16_t count) {
    respond_get_sensor(MESSAGE_ID_SENSOR_STATUS_EVENT, result_code, state, count);
}



ret_code_t send_message_with_header(enum message_id_t message_id, message_parameter_t *parameters, uint8_t parameter_count) {
    int data_length = 5; // header

    for (uint8_t i = 0; i < parameter_count; i++) {
        data_length += parameters[i].parameter_length + 4;
    }

    uint8_t data[data_length];

    data[0] = 0b10000001; // split flags: server->client | execute flag
    data[1] = 0x00; // RFU
    data[2] = message_id; // RFU
    data[3] = 0x00; // RFU
    data[4] = parameter_count;

    uint8_t *buffer_ptr = data + 5;

    for (int i = 0; i < parameter_count; i++) {
        memcpy(buffer_ptr, parameters + i, 4);                                      // copy header
        memcpy(buffer_ptr + 4, parameters[i].data, parameters[i].parameter_length); // copy data
        buffer_ptr += parameters[i].parameter_length + 4;
    }

    ret_code_t err_code = ble_bss_response_send(data, data_length);
    if (err_code == NRF_ERROR_INVALID_STATE) {
        return NRF_SUCCESS;
    }
    APP_ERROR_CHECK(err_code);

    return err_code;
}

void respond_set_sensor(enum message_id_t message_id, enum result_code_t result_code) {
    message_parameter_t parameters[] = {
        {
            .parameter_id = RESULT_CODE,
            .parameter_length = 1,
            .data = &result_code
        }
    };

    send_message_with_header(message_id, parameters, 1);
}

void respond_get_sensor(enum message_id_t message_id, enum result_code_t result_code, enum opening_closing_state_t state, uint16_t count) {
    uint16_t data = (count & 0b11111111111) | (state << 11);

    message_parameter_t parameters[] = {
        {
            .parameter_id = RESULT_CODE,
            .parameter_length = 1,
            .data = (uint8_t *)&result_code
        },
        {
            .parameter_id = SENSOR_STATUS,
            .parameter_length = 2,
            .data = (uint8_t *)&data
         }
    };
    send_message_with_header(message_id, parameters, 2);
}

void ble_bss_on_write(ble_evt_t *p_ble_evt) {
    ble_gatts_evt_write_t *p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;

    if (p_evt_write->handle == ble_bss_cccd_handle) {
        on_bss_cccd_write(p_evt_write);
    }
    else if (p_evt_write->handle == ble_bss_control_handle) {
        on_control_characteristic_write(p_evt_write);
    }
}

void ble_bss_set_state(enum opening_closing_state_t state, uint16_t count) {
    current_state_ = state;
    event_count_ = count;

    if (send_updates) {
        respond_get_sensor_event(OK, state, count);
    }
}

void ble_bss_handle_input_change(uint32_t index, gpio_config_input_digital_t *config)
{
    if (config->pin != BINARY_SENSOR_PIN)
    {
        return;
    }
    ble_bss_set_state(config->state, (uint16_t)config->trigger_count);
}

void ble_bss_on_ble_evt(ble_evt_t *p_ble_evt) {
    switch (p_ble_evt->header.evt_id) {
        case BLE_GAP_EVT_CONNECTED:
            ble_bss_on_connect(p_ble_evt);
            break;

        case BLE_GAP_EVT_DISCONNECTED:
            ble_bss_on_disconnect(p_ble_evt);
            break;

        case BLE_GATTS_EVT_WRITE:
            ble_bss_on_write(p_ble_evt);
            break;

        default:
            // No implementation needed.
            break;
    }
}

uint32_t characteristic_control_point_add()

{
    ble_gatts_char_handles_t p_handles;

    ble_gatts_attr_md_t cccd_md = {
        .vloc = BLE_GATTS_VLOC_STACK
    };
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.write_perm);

    ble_gatts_char_md_t char_md = {
        .char_props.read = 0,
        .char_props.write = 1,
        .char_props.notify = 0,
        .p_char_user_desc = NULL,
        .p_char_pf = NULL,
        .p_user_desc_md = NULL,
        .p_cccd_md = &cccd_md,
        .p_sccd_md = NULL
    };

    ble_uuid_t ble_uuid = {
        .type = BLE_UUID_TYPE_BLE,
        .uuid = UUID_BINARY_SENSOR_CONTROL_POINT
    };

    ble_gatts_attr_md_t attr_md = {
        .vloc = BLE_GATTS_VLOC_STACK,
        .rd_auth = 0,
        .wr_auth = 0,
        .vlen = 0
    };
    BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&attr_md.read_perm);
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.write_perm);

    ble_gatts_attr_t attr_char_value = {
        .p_uuid = &ble_uuid,
        .p_attr_md = &attr_md,
        .max_len = 19,
    };
    uint32_t code = sd_ble_gatts_characteristic_add(ble_bss_service_handle,
        &char_md,
        &attr_char_value,
        &p_handles);

    ble_bss_control_handle = p_handles.value_handle;

    return code;
}

uint32_t characteristic_response_add()

{
    ble_gatts_char_handles_t p_handles;

    ble_gatts_attr_md_t cccd_md = {
        .vloc = BLE_GATTS_VLOC_STACK };
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.write_perm);

    ble_gatts_char_md_t char_md = {
        .char_props.read = 0,
        .char_props.write = 0,
        .char_props.notify = 0,
        .char_props.indicate = 1,
        .p_char_user_desc = NULL,
        .p_char_pf = NULL,
        .p_user_desc_md = NULL,
        .p_cccd_md = &cccd_md,
        .p_sccd_md = NULL
    };

    ble_uuid_t ble_uuid = {
        .type = BLE_UUID_TYPE_BLE,
        .uuid = UUID_BINARY_SENSOR_RESPONSE
    };

    ble_gatts_attr_md_t attr_md = {
        .vloc = BLE_GATTS_VLOC_STACK,
        .rd_auth = 0,
        .wr_auth = 0,
        .vlen = 1
    };
    BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&attr_md.read_perm);
    BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&attr_md.write_perm);

    ble_gatts_attr_t attr_char_value = {
        .p_uuid = &ble_uuid,
        .p_attr_md = &attr_md,
        .max_len = 19
    };
    uint32_t code = sd_ble_gatts_characteristic_add(ble_bss_service_handle,
        &char_md,
        &attr_char_value,
        &p_handles);

    ble_bss_response_handle = p_handles.value_handle;
    ble_bss_cccd_handle = p_handles.cccd_handle;

    return code;
}

uint32_t ble_bss_init() {
    uint32_t err_code;
    ble_uuid_t ble_uuid;

    // Add service
    BLE_UUID_BLE_ASSIGN(ble_uuid, UUID_BINARY_SENSOR_SERVICE);

    err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &ble_bss_service_handle);
    if (err_code != NRF_SUCCESS) {
        return err_code;
    }

    err_code = characteristic_control_point_add();
    if (err_code != NRF_SUCCESS) {
        return err_code;
    }

    err_code = characteristic_response_add();
    if (err_code != NRF_SUCCESS) {
        return err_code;
    }

    return NRF_SUCCESS;
}

ret_code_t ble_bss_response_send(uint8_t *data, uint8_t length) {
    if (ble_bss_connection_handle == BLE_CONN_HANDLE_INVALID) {
        return NRF_ERROR_INVALID_STATE;
    }
    if (!send_responses) {
        return NRF_ERROR_INVALID_STATE;
    }

    uint16_t len_written = length;

    ble_gatts_hvx_params_t hvx_params = {
        .handle = ble_bss_response_handle,
        .type = BLE_GATT_HVX_INDICATION,
        .offset = 0,
        .p_len = &len_written,
        .p_data = data
    };

    ret_code_t err_code = sd_ble_gatts_hvx(ble_bss_connection_handle, &hvx_params);

    return err_code;
}

================================================
FILE: src/ble/services/binary_sensor/ble_binary_sensor_service.h
================================================
#ifndef BLE_BSS
#define BLE_BSS

#include <stdint.h>
#include <stdbool.h>
#include "ble.h"
#include "ble_srv_common.h"
#include "nrf_log.h"
#include "sensor_gpio.h"
// #include "nrf_ble_gatt.h"

#ifdef __cplusplus
extern "C" {
#endif

#define UUID_BINARY_SENSOR_SERVICE       0x183B
#define UUID_BINARY_SENSOR_CONTROL_POINT 0x2B2B
#define UUID_BINARY_SENSOR_RESPONSE      0x2B2C

enum message_id_t {
    MESSAGE_ID_GET_SENSOR_STATUS_COMMAND,
    MESSAGE_ID_GET_SENSOR_STATUS_RESPONSE,
    MESSAGE_ID_SET_SENSOR_STATUS_COMMAND,
    MESSAGE_ID_SET_SENSOR_STATUS_RESPONSE,
    MESSAGE_ID_SENSOR_STATUS_EVENT
};

enum result_code_t {
    OK,
    FAILURE
};

enum sensor_type_t {
    OPENING_CLOSING,
    HUMAN_DETECTION,
    VIBRATION_DETECTION,
    MULTIPLE_OPENING_CLOSING = 0x80,
    MULTIPLE_HUMAN_DETECTION,
    MULTIPLE_VIBRATION_DETECTION,
};

enum parameter_id_t {
    RESULT_CODE,
    CANCEL,
    SENSOR_TYPE,
    REPORT_STATUS,
    SENSOR_STATUS = 0x0A,
    MULTIPLE_SENSOR_STATUS,
    NAME
};

enum opening_closing_state_t {
    CLOSED,
    OPEN
};

enum report_state_t {
    OFF,
    ON
};

typedef struct {
    uint8_t parameter_id;
    uint8_t parameter_length;
    uint16_t reserved;
    uint8_t *data;
} message_parameter_t;

typedef struct {
    uint8_t parameter_id;
    uint8_t parameter_length;
} message_parameter_without_data_t;

typedef struct {
    uint8_t rfu_1;
    uint8_t message_id;
    uint8_t rfu_2;
    uint8_t parameter_count;
} message_header_t;



void parse_full_packet_with_split_header(uint8_t *data, uint16_t length);
void parse_packet_decoded(enum message_id_t message_id, message_parameter_t *parameters, uint8_t parameter_count);
void parse_packet(uint8_t *data, uint16_t length);
void parse_set_sensor_command(message_parameter_t *parameters, uint8_t parameter_count);
void handle_set_sensor_command(enum sensor_type_t sensor_type, enum report_state_t report_state);
void parse_get_sensor_command(message_parameter_t *parameters, uint8_t parameter_count);
void handle_get_sensor_command(enum sensor_type_t sensor_type);
void handle_get_multiple_open_close_sensor_command();
void handle_get_multiple_human_sensor_command();
void handle_get_multiple_vibration_sensor_command();
void handle_get_single_human_sensor_command();
void handle_get_single_vibration_sensor_command();
void handle_get_single_open_close_sensor_command();
void respond_set_sensor_command(enum result_code_t result_code);
void respond_get_sensor_command(enum result_code_t result_code, enum opening_closing_state_t state, uint16_t count);
void respond_get_sensor_event(enum result_code_t result_code, enum opening_closing_state_t state, uint16_t count);
ret_code_t send_message_with_header(enum message_id_t message_id, message_parameter_t *parameters, uint8_t parameter_count);
void respond_set_sensor(enum message_id_t message_id, enum result_code_t result_code);
void respond_get_sensor(enum message_id_t message_id, enum result_code_t result_code, enum opening_closing_state_t state, uint16_t count);
ret_code_t ble_bss_response_send(uint8_t *data, uint8_t data_length);
void ble_bss_set_state(enum opening_closing_state_t, uint16_t);
void ble_bss_handle_input_change(uint32_t index, gpio_config_input_digital_t *config);


uint32_t ble_bss_init();


/**@brief Function for handling the GATT module's events.
 *
 * @details Handles all events from the GATT module of interest to the Heart Rate Service.
 *
 * @param[in]   p_hrs      Heart Rate Service structure.
 * @param[in]   p_gatt_evt  Event received from the GATT module.
 */
// void ble_bss_on_gatt_evt(nrf_ble_gatt_evt_t * p_gatt_evt);


/**@brief Function for handling the Application's BLE Stack events.
 *
 * @details Handles all events from the BLE stack of interest to the Heart Rate Service.
 *
 * @param[in]   p_hrs      Heart Rate Service structure.
 * @param[in]   p_ble_evt  Event received from the BLE stack.
 */
void ble_bss_on_ble_evt(ble_evt_t * p_ble_evt);

/**@brief Function for sending heart rate measurement if notification has been enabled.
 *
 * @details The application calls this function after having performed a heart rate measurement.
 *          If notification has been enabled, the heart rate measurement data is encoded and sent to
 *          the client.
 *
 * @param[in]   p_hrs                    Heart Rate Service structure.
 * @param[in]   heart_rate               New heart rate measurement.
 *
 * @return      NRF_SUCCESS on success, otherwise an error code.
 */
ret_code_t ble_bss_response_send(uint8_t *data, uint8_t data_length);


#ifdef __cplusplus
}
#endif

#endif // BLE_HRS_H__

/** @} */


================================================
FILE: src/ble/services/ble_gpio_asm/ble_gpio_asm.c
================================================
#include "ble_gpio_asm.h"
#include "ble_helpers.h"
#include "encoding.h"
#include "app_error.h"
#include "ble_automation_io_service.h"
#include "gpioasm.h"
#include "sensor_timer.h"

uint16_t ble_gpio_asm_connection_handle;
uint16_t ble_gpio_asm_service_handle;
uint16_t ble_gpio_asm_characteristic_data_handle;

uint8_t ble_gpio_asm_custom_uuid_type;

gpioasm_engine_t engine;

ret_code_t ble_gpio_asm_characteristic_asm_data_add()
{
  ble_helper_characteristic_init_t init = {
    .service_handle = ble_gpio_asm_service_handle,
    .uuid = UUID_GPIO_ASM_DATA,
    .uuid_type = ble_gpio_asm_custom_uuid_type,
    .description_str = "gpioASM data",
    .is_writable = true,
    .authorize_write = true,
    .max_length = 20,
    .value_handle = &ble_gpio_asm_characteristic_data_handle,
  };
  return ble_helper_characteristic_add(&init);
}


uint8_t ble_gpio_asm_handle_data_write(const uint8_t *data, uint32_t length)
{
    static uint8_t is_overflown = false;

    if (length == 0)
    {
        gpioasm_stop(&engine);
        return is_overflown;
    }

    uint8_t result = gpioasm_push_packet(&engine, data, length);
    if (result == PUSH_OVERFLOW)
    {
        NRF_LOG_ERROR("buffer overflown\n");
        is_overflown = true;
        return is_overflown;
    }
    if (result == PUSH_FIRST_PACKET)
    {
        is_overflown = false;
        NRF_LOG_DEBUG("first packet\n");
        return is_overflown;
    }
    if (result == PUSH_FINAL_PACKET)
    {
        if (is_overflown)
        {
            NRF_LOG_ERROR("not starting sequence due to overflow\n");
            return is_overflown;
        }
        NRF_LOG_DEBUG("last packet\n");
        gpioasm_start(&engine);
    }
    return is_overflown;
}

void ble_gpio_asm_handle_input_change()
{
    gpioasm_handle_digital_input_update(&engine);
}

void ble_gpio_asm_on_connect(const ble_evt_t *p_ble_evt)
{
    ble_gpio_asm_connection_handle = p_ble_evt->evt.gap_evt.conn_handle;
}

void ble_gpio_asm_on_disconnect(const ble_evt_t *p_ble_evt)
{
    UNUSED_PARAMETER(p_ble_evt);
    ble_gpio_asm_connection_handle = BLE_CONN_HANDLE_INVALID;
}


void ble_gpio_asm_authorize_data_write(const ble_gatts_evt_write_t *write_req)
{
    uint16_t status = BLE_GATT_STATUS_SUCCESS;

    ble_gatts_rw_authorize_reply_params_t authorize_params = {
        .type = BLE_GATTS_AUTHORIZE_TYPE_WRITE,
        .params.write = {
            .update = 1,
            .offset = 0,
            .len = write_req->len,
            .p_data = write_req->data}};

    if(ble_gpio_asm_handle_data_write(write_req->data, write_req->len)){
        status = BLE_GATT_STATUS_ATTERR_INVALID_ATT_VAL_LENGTH;
    }

    authorize_params.params.write.gatt_status = status;

    sd_ble_gatts_rw_authorize_reply(
        ble_gpio_asm_connection_handle,
        &authorize_params);
}

void ble_gpio_asm_on_authorize(const ble_evt_t *p_ble_evt)
{
    const ble_gatts_evt_rw_authorize_request_t *req = &(p_ble_evt
                                                      ->evt.gatts_evt
                                                      .params
                                                      .authorize_request);

    if (req->type == BLE_GATTS_AUTHORIZE_TYPE_READ)
    {
        /*
        uint16_t handle = req
                              ->request
                              .read
                              .handle;
        */
    }
    else if (req->type == BLE_GATTS_AUTHORIZE_TYPE_WRITE)
    {
        uint16_t handle = req
                              ->request
                              .write
                              .handle;

        if (handle == ble_gpio_asm_characteristic_data_handle)
        {
            ble_gpio_asm_authorize_data_write(&(req->request.write));
            return;
        }
        return;
    }
}

void ble_gpio_asm_on_ble_evt(const ble_evt_t *p_ble_evt)
{
    switch (p_ble_evt->header.evt_id)
    {
    case BLE_GAP_EVT_CONNECTED:
        ble_gpio_asm_on_connect(p_ble_evt);
        break;

    case BLE_GAP_EVT_DISCONNECTED:
        ble_gpio_asm_on_disconnect(p_ble_evt);
        break;

    case BLE_GATTS_EVT_WRITE:
        // ble_gpio_asm_on_write(p_ble_evt);
        break;

    case BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST:
        ble_gpio_asm_on_authorize(p_ble_evt);
        break;

    default:
        // No implementation needed.
        break;
    }
}

void ble_gpioasm_timer_handler(void *engine, uint64_t timeout, bool start){
    if(start){
        timer_gpioasm_start(timeout);
    }else{
        timer_gpioasm_stop();
    }
}

void ble_gpioasm_timer_timeout_handler(){
    gpioasm_handle_timer_timeout(&engine);
}

void ble_gpio_asm_init(){
    ret_code_t err_code;

    ble_uuid128_t vs_uuid = {
        .uuid128 = UUID_GPIO_ASM_BASE
    };

    err_code = sd_ble_uuid_vs_add(&vs_uuid, &ble_gpio_asm_custom_uuid_type);
    APP_ERROR_CHECK(err_code);

    ble_uuid_t uuid_service = {
        .uuid = UUID_GPIO_ASM_SERVICE,
        .type = ble_gpio_asm_custom_uuid_type
    };

    err_code = sd_ble_gatts_service_add(
        BLE_GATTS_SRVC_TYPE_PRIMARY,
        &uuid_service,
        &ble_gpio_asm_service_handle
    );
    APP_ERROR_CHECK(err_code);

    err_code = ble_gpio_asm_characteristic_asm_data_add();
    APP_ERROR_CHECK(err_code);

    gpioasm_engine_init_t engine_init = {
        .timer_handler = ble_gpioasm_timer_handler,
        .pin_digital_output_handler = gpio_write_output_digital_pin,
        .pin_analog_output_handler = ble_aio_handle_pin_analog_data,
        .pin_digital_input_provider = gpio_get_input_digital_state
    };

    gpioasm_init(&engine, &engine_init);
    timer_sequence_set_timeout_handler(ble_gpioasm_timer_timeout_handler);
}

================================================
FILE: src/ble/services/ble_gpio_asm/ble_gpio_asm.h
================================================
#include "stdint.h"
#include "sdk_common.h"
#include "ble_srv_common.h"
#include "sensor_gpio.h"

#define UUID_GPIO_ASM_SERVICE   0x0000
#define UUID_GPIO_ASM_DATA      0x0001

#define UUID_GPIO_ASM_BASE { 0xb0, 0x2a, 0x86, 0xb3, 0xcd, 0xc1, 0x4f, 0x78, 0xa2, 0xd5, 0x74, 0x2a, 0x81, 0xab, 0x19, 0xb1 }

void ble_gpio_asm_init();
void ble_gpio_asm_on_ble_evt(const ble_evt_t *p_ble_evt);
void ble_gpio_asm_handle_input_change();

================================================
FILE: src/ble/services/configuration/ble_configuration_service.c
================================================
#include "ble_configuration_service.h"
#include "app_error.h"
#include "ble_helpers.h"
#include "sensor_timer.h"
#include "storage.h"
#include "ble_hci.h"
#include "nrf_soc.h"

uint8_t configuration_custom_uuid_type;

uint16_t ble_configuration_service_handle;

uint16_t ble_configuration_pin_configuration_handle = BLE_GATT_HANDLE_INVALID;
uint16_t ble_configuration_connection_parameters_handle = BLE_GATT_HANDLE_INVALID;
uint16_t ble_reboot_handle = BLE_GATT_HANDLE_INVALID;

uint16_t ble_configuration_connection_handle = BLE_CONN_HANDLE_INVALID;

bool reboot_scheduled = false;

ble_configuration_connection_params_update_handler_t ble_configuration_connection_params_update_handler;

ret_code_t ble_configuration_characteristic_pin_configuration_add() {
  ble_helper_characteristic_init_t init = {
    .service_handle = ble_configuration_service_handle,
    .uuid = UUID_CHARACTERISTIC_PIN_CONFIGURATION,
    .uuid_type = configuration_custom_uuid_type,
    .description_str = "Pin configuration",
    .is_writable = true,
    .is_readable = true,
    .max_length = PIN_CONFIGURATION_LENGTH,
    .value_handle = &ble_configuration_pin_configuration_handle,
  };
  return ble_helper_characteristic_add(&init);
}

ret_code_t ble_configuration_characteristic_connections_parameters_add() {
  ble_helper_characteristic_init_t init = {
    .service_handle = ble_configuration_service_handle,
    .uuid = UUID_CHARACTERISTIC_CONNECITON_PARAMS_CONFIGURATION,
    .uuid_type = configuration_custom_uuid_type,
    .description_str = "Connection parameters configuration",
    .is_writable = true,
    .is_readable = true,
    .authorize_write = true,
    .max_length = 10,
    .value_handle = &ble_configuration_connection_parameters_handle,
  };
  return ble_helper_characteristic_add(&init);
}

ret_code_t ble_reboot_characteristic_add() {
  ble_helper_characteristic_init_t init = {
    .service_handle = ble_configuration_service_handle,
    .uuid = UUID_CHARACTERISTIC_REBOOT,
    .uuid_type = configuration_custom_uuid_type,
    .description_str = "Write to GPREGRET and reboot",
    .is_writable = true,
    .is_readable = true,
    .authorize_write = true,
    .authorize_read = true,
    .max_length = 1,
    .value_handle = &ble_reboot_handle,
  };
  return ble_helper_characteristic_add(&init);
}

ret_code_t ble_configuration_characteristic_data_set(uint32_t handle, uint8_t *data, uint32_t data_length) {
  ble_gatts_value_t value = {
      .offset = 0,
      .len = data_length,
      .p_value = data
  };

  return sd_ble_gatts_value_set(
    BLE_CONN_HANDLE_INVALID,
    handle,
    &value
  );
}

ret_code_t ble_configuration_pin_configuraion_data_set(uint8_t data[]) {
  return ble_configuration_characteristic_data_set(
    ble_configuration_pin_configuration_handle,
    data,
    PIN_CONFIGURATION_LENGTH
  );
}

ret_code_t ble_configuration_connection_params_configuraion_data_set(uint8_t data[10]) {
  return ble_configuration_characteristic_data_set(
    ble_configuration_connection_parameters_handle,
    data,
    10
  );
}

void ble_configuration_restore_values() {
  ret_code_t err_code;

  uint8_t storage_data[PIN_CONFIGURATION_LENGTH]; // size 16 to cover both endpoints
  storage_read_pin_configuration(storage_data);
  err_code = ble_configuration_pin_configuraion_data_set(storage_data);
  APP_ERROR_CHECK(err_code);

  storage_read_connection_params_configuration(storage_data);
  err_code = ble_configuration_connection_params_configuraion_data_set(storage_data);
  APP_ERROR_CHECK(err_code);
}

ret_code_t ble_configuration_service_init(ble_configuration_connection_params_update_handler_t connection_params_update_handler) {

  ret_code_t err_code;

  ble_uuid128_t vs_uuid = {
      .uuid128 = CUSTOM_UUID_BASE_CONFIGURATION_SERVICE
  };

  err_code = sd_ble_uuid_vs_add(&vs_uuid, &configuration_custom_uuid_type);
  APP_ERROR_CHECK(err_code);


  ble_uuid_t ble_uuid = {
    .type = configuration_custom_uuid_type,
    .uuid = UUID_SERVICE_CONFIGURATION
  };

  err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &ble_configuration_service_handle);
  APP_ERROR_CHECK(err_code);

  err_code = ble_configuration_characteristic_pin_configuration_add();
  APP_ERROR_CHECK(err_code);

  err_code = ble_configuration_characteristic_connections_parameters_add();
  APP_ERROR_CHECK(err_code);

  err_code = ble_reboot_characteristic_add();
  APP_ERROR_CHECK(err_code);

  ble_configuration_restore_values();

  ble_configuration_connection_params_update_handler = connection_params_update_handler;

  return NRF_SUCCESS;
}

void ble_configuration_on_connect(const ble_evt_t *p_ble_evt) {
  ble_configuration_connection_handle = p_ble_evt->evt.gap_evt.conn_handle;
}

void ble_configuration_on_disconnect(const ble_evt_t *p_ble_evt) {
  ble_configuration_connection_handle = BLE_CONN_HANDLE_INVALID;

  if(reboot_scheduled) {
    NVIC_SystemReset();
  }
}

void ble_configuration_handle_connection_params_configuration_data(const uint8_t *data) {
  // ble_configuration_connection_params_packet_t *packet = (ble_configuration_connection_params_packet_t *)data;

  storage_store_connection_params_configuration(data);

  sd_ble_gap_disconnect(
    ble_configuration_connection_handle,
    BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION
  );

  // doesn't work for some reason hence we just reboot
  // ble_configuration_connection_params_update_handler(packet);
}

void ble_configuration_authorize_read_reboot(const ble_gatts_evt_read_t *read_req) {
  uint32_t value;

  #ifdef S130
  sd_power_gpregret_get(&value);
  #else
  sd_power_gpregret_get(0, &value);
  #endif

  ble_gatts_rw_authorize_reply_params_t authorize_params = {
    .type = BLE_GATTS_AUTHORIZE_TYPE_READ,
      .params.read = {
        .gatt_status = BLE_GATT_STATUS_SUCCESS,
        .update = 1,
        .offset = 0,
        .len = 1,
        .p_data = (uint8_t*) &value
      }
  };

  sd_ble_gatts_rw_authorize_reply(
    ble_configuration_connection_handle,
    &authorize_params
  );
}

void ble_configuration_authorize_write_reboot(const ble_gatts_evt_write_t *write_req) {
  if(write_req->len > 0) {
    #ifdef S130
    APP_ERROR_CHECK(sd_power_gpregret_clr(~0));
    APP_ERROR_CHECK(sd_power_gpregret_set(write_req->data[0]));
    #else
    APP_ERROR_CHECK(sd_power_gpregret_clr(0, ~0));
    APP_ERROR_CHECK(sd_power_gpregret_set(0, write_req->data[0]));
    #endif
  }

  reboot_scheduled = true;

  ble_gatts_rw_authorize_reply_params_t authorize_params = {
    .type = BLE_GATTS_AUTHORIZE_TYPE_WRITE,
    .params = {
      .write = {
        .gatt_status = BLE_GATT_STATUS_SUCCESS,
        .update = 1,
        .offset = 0,
        .len = write_req->len,
        .p_data = write_req->data
      }
    }
  };

  sd_ble_gatts_rw_authorize_reply(
    ble_configuration_connection_handle,
    &authorize_params
  );

  sd_ble_gap_disconnect(
    ble_configuration_connection_handle,
    BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION
  );
}

void ble_configuration_authorize_connection_params_write(const ble_gatts_evt_write_t *write_req) {
  uint16_t status = BLE_GATT_STATUS_ATTERR_WRITE_NOT_PERMITTED;

  ble_gatts_rw_authorize_reply_params_t authorize_params = {
    .type = BLE_GATTS_AUTHORIZE_TYPE_WRITE,
    .params.write = {
        .update = 1,
        .offset = 0,
        .len = write_req->len,
        .p_data = write_req->data
      }
  };

  if (write_req->len != 10) {
    status = BLE_GATT_STATUS_ATTERR_INVALID_ATT_VAL_LENGTH;
  }
  else {
    ble_configuration_connection_params_packet_t *packet = (ble_configuration_connection_params_packet_t *)write_req->data;

    ble_gap_conn_params_t real_params = {
      .min_conn_interval = MSEC_TO_UNITS(packet->min_conn_interval, UNIT_1_25_MS),
      .max_conn_interval = MSEC_TO_UNITS(packet->max_conn_interval, UNIT_1_25_MS),
      .slave_latency = packet->slave_latency,
      .conn_sup_timeout = MSEC_TO_UNITS(packet->conn_sup_timeout, UNIT_10_MS)
    };

    if (real_params.min_conn_interval < BLE_GAP_CP_MIN_CONN_INTVL_MIN) {
      NRF_LOG_ERROR("min connection interval min too small\n");
    }
    else if (real_params.min_conn_interval > BLE_GAP_CP_MIN_CONN_INTVL_MAX) {
      NRF_LOG_ERROR("min connection interval min too big\n");
    }
    else if (real_params.max_conn_interval < BLE_GAP_CP_MAX_CONN_INTVL_MIN) {
      NRF_LOG_ERROR("max connection interval min too small\n");
    }
    else if (real_params.max_conn_interval > BLE_GAP_CP_MAX_CONN_INTVL_MAX) {
      NRF_LOG_ERROR("max connection interval min too big\n");
    }
    else if (real_params.min_conn_interval > real_params.max_conn_interval) {
      NRF_LOG_ERROR("min connection interval cannot be bigger than max\n");
    }
    else if (real_params.slave_latency > BLE_GAP_CP_SLAVE_LATENCY_MAX) {
      NRF_LOG_ERROR("slave latency too big\n");
    }
    else if (real_params.conn_sup_timeout < BLE_GAP_CP_CONN_SUP_TIMEOUT_MIN) {
      NRF_LOG_ERROR("sup timeout too small\n");
    }
    else if (real_params.conn_sup_timeout > BLE_GAP_CP_CONN_SUP_TIMEOUT_MAX) {
      NRF_LOG_ERROR("sup timeout too big\n");
    }
    else if (packet->advertising_interval < BLE_GAP_ADV_INTERVAL_MIN) {
      NRF_LOG_ERROR("adv interval too small\n");
    }
    else if (packet->advertising_interval > BLE_GAP_ADV_INTERVAL_MAX) {
      NRF_LOG_ERROR("adv interval too big\n");
    }
    else if (packet->conn_sup_timeout <= ((packet->max_conn_interval * 2) * (packet->slave_latency + 1))) {
      NRF_LOG_ERROR("sup timeout smaller than effective connection interval\n");
    }
    else {
      ble_configuration_handle_connection_params_configuration_data(write_req->data);
      status = BLE_GATT_STATUS_SUCCESS;
    }
  }

  authorize_params.params.write.gatt_status = status;

  sd_ble_gatts_rw_authorize_reply(
    ble_configuration_connection_handle,
    &authorize_params
  );
}

void ble_configuration_on_authorize(const ble_evt_t *p_ble_evt) {
  const ble_gatts_evt_rw_authorize_request_t *req = &(p_ble_evt
    ->evt.gatts_evt
    .params
    .authorize_request);
  uint16_t handle = req
    ->request
    .write
    .handle;

  if (req->type == BLE_GATTS_AUTHORIZE_TYPE_WRITE) {
    const ble_gatts_evt_write_t *write_req = &(req->request.write);
    if (handle == ble_configuration_connection_parameters_handle) {
      ble_configuration_authorize_connection_params_write(write_req);
      return;
    }

    if (handle == ble_reboot_handle) {
      ble_configuration_authorize_write_reboot(write_req);
      return;
    }
  }else if(req->type == BLE_GATTS_AUTHORIZE_TYPE_READ) {
    const ble_gatts_evt_read_t *read_req = &(req->request.read);

    if(handle == ble_reboot_handle) {
      ble_configuration_authorize_read_reboot(read_req);
      return;
    }
  }
}


void ble_configuration_handle_pin_configuration_write(const ble_gatts_evt_write_t *write_evt) {
  const uint8_t *data = write_evt->data;
  const uint32_t len = write_evt->len;

  uint32_t writable_data_length = MIN(PIN_CONFIGURATION_LENGTH, len);
  static uint8_t data_to_write[PIN_CONFIGURATION_LENGTH];

  memcpy(data_to_write, data, writable_data_length);

  for (uint32_t i = writable_data_length; i < PIN_CONFIGURATION_LENGTH; i++) {
    data_to_write[i] = 0xFF;
  }

  storage_store_pin_configuration(data_to_write);

  // disconnect from host for reboot
  sd_ble_gap_disconnect(
    ble_configuration_connection_handle,
    BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION
  );
}

void ble_configuration_handle_connection_params_configuration_write(const ble_gatts_evt_write_t *write_evt) {
  const uint8_t *data = write_evt->data;
  const uint32_t len = write_evt->len;

  NRF_LOG_DEBUG("fehler\n");

  if (len != 10) {
    NRF_LOG_ERROR("connection parameters config must be 20 bytes in length (%d)\n", len);
    return;
  }

  ble_configuration_handle_connection_params_configuration_data(data);
}

void ble_configuration_on_write(const ble_evt_t *p_ble_evt) {
  const ble_gatts_evt_write_t *write_evt = &p_ble_evt
    ->evt
    .gatts_evt
    .params
    .write;

  uint16_t handle = write_evt->handle;

  if (handle == ble_configuration_pin_configuration_handle) {
    ble_configuration_handle_pin_configuration_write(write_evt);
    return;
  }
  if (handle == ble_configuration_connection_parameters_handle) {
    ble_configuration_handle_connection_params_configuration_write(write_evt);
    return;
  }
}

void ble_configuration_on_ble_event(const ble_evt_t *p_ble_evt) {

  switch (p_ble_evt->header.evt_id) {
    case BLE_GAP_EVT_CONNECTED:
      ble_configuration_on_connect(p_ble_evt);
      break;

    case BLE_GAP_EVT_DISCONNECTED:
      ble_configuration_on_disconnect(p_ble_evt);
      break;

    case BLE_GATTS_EVT_WRITE:
      ble_configuration_on_write(p_ble_evt);
      break;

    case BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST:
      ble_configuration_on_authorize(p_ble_evt);
      break;

    default:
      // No implementation needed.
      break;
  }
}

uint8_t ble_configuration_service_get_custom_uuid_type() {
  return configuration_custom_uuid_type;
}

================================================
FILE: src/ble/services/configuration/ble_configuration_service.h
================================================
#include "stdint.h"
#include "ble.h"
#include "ble_srv_common.h"
#include "sdk_common.h"


#define CUSTOM_UUID_BASE_CONFIGURATION_SERVICE {0xdc, 0x71, 0xd1, 0xc1, 0xfd, 0x01, 0x49, 0x15, 0xa7, 0x8f, 0xf1, 0x5c, 0x00, 0x00, 0x10, 0x9c}

#define UUID_SERVICE_CONFIGURATION 0x0000
#define UUID_CHARACTERISTIC_PIN_CONFIGURATION 0x0001
#define UUID_CHARACTERISTIC_CONNECITON_PARAMS_CONFIGURATION 0x0002
#define UUID_CHARACTERISTIC_REBOOT 0x0003

typedef struct {
  uint16_t min_conn_interval;
  uint16_t max_conn_interval;
  uint16_t slave_latency;
  uint16_t conn_sup_timeout;
  uint16_t advertising_interval;

} ble_configuration_connection_params_packet_t;

typedef void (*ble_configuration_connection_params_update_handler_t)(ble_configuration_connection_params_packet_t *);

uint8_t ble_configuration_service_get_custom_uuid_type();
ret_code_t ble_configuration_service_init(ble_configuration_connection_params_update_handler_t connection_params_update_handler);
void ble_configuration_on_ble_event(const ble_evt_t *p_ble_evt);

================================================
FILE: src/ble/services/cycling_speed_cadence/ble_cycling_speed_cadence.c
================================================
#include "sdk_common.h"
#include "ble_cycling_speed_cadence.h"
#include <string.h>
#include "ble_l2cap.h"
#include "app_error.h"
#include "ble_helpers.h"
#include "sensor_gpio.h"
#include "nrf_log.h"
#include "feature_config.h"

// a CSC measurement that is unchanged (bike standing still) will be re-reported this amount of times
#define STANDSTILL_REPORT_COUNT_LIMIT 1

uint16_t ble_csc_connection_handle = BLE_CONN_HANDLE_INVALID;

uint16_t ble_csc_service_handle = BLE_GATT_HANDLE_INVALID;

uint16_t ble_csc_measurement_write_handle = BLE_GATT_HANDLE_INVALID;
uint16_t ble_csc_measurement_cccd_handle = BLE_GATT_HANDLE_INVALID;

uint32_t last_revolution_count = 0;
uint32_t last_reported_revolution_count = 0xFFFFFFFF;
uint16_t last_revolution_time = 0;
uint8_t standstill_report_count = 0xFF;

APP_TIMER_DEF(measurement_report_timer);


uint32_t get_rtc1_ticks(){
    return NRF_RTC1->COUNTER;
}

uint16_t get_time_units(){
    return (get_rtc1_ticks() / 32) % 65536;
}

void csc_encode(uint8_t buffer[7]){
    buffer[0] = CSC_MEAS_FLAG_MASK_WHEEL_REV_DATA_PRESENT;

    // copy wheel revolutions
    memcpy(buffer + 1, &last_revolution_count, 4);

    // copy lasst wheel revolution time
    memcpy(buffer + 5, &last_revolution_time, 2);
}

void ble_csc_measurement_report(){
    ret_code_t err_code;

    if(ble_csc_connection_handle == BLE_CONN_HANDLE_INVALID){
        // no client connected
        return;
    }

    uint16_t len = 7;
    static uint8_t buffer[7];

    csc_encode(buffer);

    ble_gatts_hvx_params_t params = {
        .handle = ble_csc_measurement_write_handle,
        .type = BLE_GATT_HVX_NOTIFICATION,
        .offset = 0,
        .p_len = &len,
        .p_data = buffer
    };

    err_code = sd_ble_gatts_hvx(
        ble_csc_connection_handle,
        &params);
    APP_ERROR_CHECK(err_code);
}

void measurement_timer_timeout_handler(void *context){
    // no change in revolution count, no report needed
    if(last_revolution_count == last_reported_revolution_count){
        if(standstill_report_count >= STANDSTILL_REPORT_COUNT_LIMIT){
            return;
        }
        standstill_report_count++;
    }

    last_reported_revolution_count = last_revolution_count;
    ble_csc_measurement_report();
}

void ble_csc_timer_init(){
    ret_code_t err_code = app_timer_create(
        &measurement_report_timer,
        APP_TIMER_MODE_REPEATED,
        measurement_timer_timeout_handler
    );
    APP_ERROR_CHECK(err_code);
}

void ble_csc_on_connect(ble_evt_t *p_ble_evt)
{
    ble_csc_connection_handle = p_ble_evt->evt.gap_evt.conn_handle;
}

void ble_csc_on_disconnect(ble_evt_t *p_ble_evt)
{
    UNUSED_PARAMETER(p_ble_evt);
    ble_csc_connection_handle = BLE_CONN_HANDLE_INVALID;
}

void handle_speed_measurement_notification_enabled(uint8_t enabled){
    NRF_LOG_DEBUG("csc measurement enabled: %d\n", enabled);

    ret_code_t err_code;

    if(enabled){
        err_code = app_timer_start(
            measurement_report_timer,
            MEASUREMENT_REPORT_INTERVAL,
            NULL
        );
    }else{
        err_code = app_timer_stop(measurement_report_timer);
    }

    APP_ERROR_CHECK(err_code);
}

void handle_csc_measurement_cccd_write(ble_gatts_evt_write_t *write_evt)
{
    if (write_evt->len == 2)
    {
        handle_speed_measurement_notification_enabled(
            ble_srv_is_notification_enabled(write_evt->data)
        );
    }
}

ret_code_t ble_csc_characteristic_measurement_add()
{
  ble_helper_characteristic_init_t init = {
    .service_handle = ble_csc_service_handle,
    .uuid = UUID_CSC_CHARACTERISTIC_SPEED_MEASUREMENT,
    .is_readable = true,
    .is_notifiable = true,
    .authorize_read = true,
    .max_length = 7,
    .value_handle = &ble_csc_measurement_write_handle,
    .cccd_handle = &ble_csc_measurement_cccd_handle
  };
  return ble_helper_characteristic_add(&init);
}



void ble_csc_on_write(ble_evt_t *p_ble_evt)
{
    ble_gatts_evt_write_t *write_evt = &p_ble_evt
                                            ->evt
                                            .gatts_evt
                                            .params
                                            .write;

    uint16_t handle = write_evt->handle;

    if (handle == ble_csc_measurement_cccd_handle)
    {
        handle_csc_measurement_cccd_write(write_evt);
        return;
    }
}

void ble_csc_on_authroize_measurement()
{
    uint8_t buffer[7];

    csc_encode(buffer);

    ble_gatts_rw_authorize_reply_params_t authorize_params = {
        .type = BLE_GATTS_AUTHORIZE_TYPE_READ,
        .params.read = {
            .gatt_status = BLE_GATT_STATUS_SUCCESS,
            .update = 1,
            .offset = 0,
            .len = 7,
            .p_data = buffer
        }
    };

    sd_ble_gatts_rw_authorize_reply(
        ble_csc_connection_handle,
        &authorize_params
    );
}

void ble_csc_on_authorize(ble_evt_t *p_ble_evt)
{
    ble_gatts_evt_rw_authorize_request_t *req = &(p_ble_evt
                                                      ->evt.gatts_evt
                                                      .params
                                                      .authorize_request);

    if (req->type == BLE_GATTS_AUTHORIZE_TYPE_READ)
    {
        uint16_t handle = req
                              ->request
                              .read
                              .handle;

        if (handle == ble_csc_measurement_write_handle)
        {
            ble_csc_on_authroize_measurement();
            return;
        }
        return;
    }
}

void ble_csc_on_ble_evt(ble_evt_t *p_ble_evt)
{
    switch (p_ble_evt->header.evt_id)
    {
    case BLE_GAP_EVT_CONNECTED:
        ble_csc_on_connect(p_ble_evt);
        break;

    case BLE_GAP_EVT_DISCONNECTED:
        ble_csc_on_disconnect(p_ble_evt);
        break;

    case BLE_GATTS_EVT_WRITE:
        ble_csc_on_write(p_ble_evt);
        break;

    case BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST:
        ble_csc_on_authorize(p_ble_evt);
        break;

    default:
        // No implementation needed.
        break;
    }
}

void ble_csc_handle_sensor_trigger(uint32_t trigger_count){
    last_revolution_count = trigger_count;
    standstill_report_count = 0;
    last_revolution_time = get_time_units();
}

void ble_csc_handle_input_change(uint32_t index, gpio_config_input_digital_t *config)
{
    if (config->pin != CSC_WHEEL_SENSOR_PIN)
    {
        return;
    }
    if(config->state == 0){
        return;
    }
    ble_csc_handle_sensor_trigger(config->trigger_count);
}

ret_code_t ble_csc_init()
{
    ret_code_t err_code;
    ble_uuid_t ble_uuid;

    BLE_UUID_BLE_ASSIGN(ble_uuid, UUID_CSC_SERVICE);

    err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &ble_csc_service_handle);
    APP_ERROR_CHECK(err_code);

    err_code = ble_csc_characteristic_measurement_add();
    APP_ERROR_CHECK(err_code);

    ble_csc_timer_init();

    return NRF_SUCCESS;
}


================================================
FILE: src/ble/services/cycling_speed_cadence/ble_cycling_speed_cadence.h
================================================
#include "ble_srv_common.h"
#include "app_timer.h"
#include "sensor_timer.h"
#include "sensor_gpio.h"

#define UUID_CSC_SERVICE                          0x1816
#define UUID_CSC_CHARACTERISTIC_SPEED_MEASUREMENT 0x2A5B
#define UUID_CSC_CHARACTERISTIC_FEATURE           0x2A5C

#define MEASUREMENT_REPORT_INTERVAL APP_TIMER_TICKS_COMPAT(5000, APP_TIMER_PRESCALER)

#define CSC_MEAS_FLAG_MASK_WHEEL_REV_DATA_PRESENT (0x01 << 0)  /**< Wheel revolution data present flag bit. */

void ble_csc_on_ble_evt(ble_evt_t *);
ret_code_t ble_csc_init();
void ble_csc_handle_input_change(uint32_t, gpio_config_input_digital_t *);

================================================
FILE: src/ble/services/hid/ble_hid.c
================================================
#include "ble_hid.h"
#include "sdk_common.h"
#include <string.h>
#include "ble_l2cap.h"
#include "app_error.h"
#include "ble_helpers.h"
#include "sensor_gpio.h"
#include "nrf_log.h"
#include "feature_config.h"

#define HID_REPORT_ID_GAMEPAD 0x01

uint16_t ble_hid_connection_handle = BLE_CONN_HANDLE_INVALID;

uint16_t ble_hid_service_handle;

uint16_t ble_hid_characteristic_information_handle;

uint16_t ble_hid_characteristic_report_value_handle;
uint16_t ble_hid_characteristic_report_cccd_handle;
uint16_t ble_hid_characteristic_control_point_value_handle;

bool ble_hid_report_notification_enabled = false;

uint8_t descriptor_value[] = {
  0x05, 0x01,   // Usage Page (Generic Desktop)
  0x09, 0x05,   // Usage (Game Pad)
  0xA1, 0x01,   // Collection (Application)
  0x85, HID_REPORT_ID_GAMEPAD,   // Report ID (1)
  0x05, 0x09,   // Usage Page (Button)
  0x19, 0x01,   // Usage Minimum (Button 1)
  0x29, 0x10,   // Usage Maximum (Button 16)
  0x15, 0x00,   // Logical Minimum (0)
  0x25, 0x01,   // Logical Maximum (1)
  0x75, 0x01,   // Report Size (1)
  0x95, 0x10,   // Report Count (16)
  0x81, 0x02,   // Input (Data, Variable, Absolute) - 4 buttons

  0x05, 0x01,   // Usage Page (Generic Desktop)
  0x09, 0x39,   // Usage (Hat Switch)
  0x15, 0x01,   // Logical Minimum (1)
  0x25, 0x08,   // Logical Maximum (8)
  0x35, 0x00,   // Physical Minimum (0)
  0x46, 0x3B, 0x01, // Physical Maximum (315)
  0x65, 0x14,   // Unit (Eng Rot:Angular Pos)
  0x75, 0x04,   // Report Size (4)
  0x95, 0x01,   // Report Count (1)
  0x81, 0x42,   // Input (Data, Variable, Absolute, Null State) - hat switch
  0x95, 0x01,   // Report count (1)
  0x75, 0x04,   // Report size (4)
  0x81, 0x03,   // Input (Cnst,Var,Abs)

  0xC0          // End Collection
};

uint8_t report_data[] = {
    // 0x01, // (left out, since present in descriptor) report id, as specified in report map
    0x00, // buttons, on bit each
    0x00,
    0x00, // hat switch rotation
};

uint8_t information_value[] = {
    0x01, 0x10, // spec version 1.10
    0x00, // no country code
    0x00 // no special features
};


void ble_hid_handle_input_change(uint32_t index, gpio_config_input_digital_t *config)
{
  uint32_t input_count = gpio_get_input_digital_pin_count();
  uint8_t input_states[input_count];
  gpio_encode_input_states(input_states);

  #if HID_GAMEPAD_ENABLED == 1

  static struct {
    uint8_t gamepad_up_pressed : 1;
    uint8_t gamepad_down_pressed : 1;
    uint8_t gamepad_left_pressed : 1;
    uint8_t gamepad_right_pressed : 1;
  } gamepad = {0};

  if(false){} // this is just the starting point for the following cases
  #if (HID_D_PAD_UP_ENABLED == 1)
  else if(HID_D_PAD_UP_PIN == config->pin) gamepad.gamepad_up_pressed = config->state;
  #endif
  #if (HID_D_PAD_DOWN_ENABLED == 1)
  else if(HID_D_PAD_DOWN_PIN == config->pin) gamepad.gamepad_down_pressed = config->state;
  #endif
  #if (HID_D_PAD_LEFT_ENABLED == 1)
  else if(HID_D_PAD_LEFT_PIN == config->pin) gamepad.gamepad_left_pressed = config->state;
  #endif
  #if (HID_D_PAD_RIGHT_ENABLED == 1)
  else if(HID_D_PAD_RIGHT_PIN == config->pin) gamepad.gamepad_right_pressed = config->state;
  #endif


  uint8_t bits = *((uint8_t*)&gamepad);

  uint8_t rotation = 0;
  if(bits == 0b0001){
    rotation = 1;
  }else if(bits == 0b0101){
    rotation = 8;
  }else if(bits == 0b1001){
    rotation = 2;
  }else if(bits == 0b0010){
    rotation = 5;
  }else if(bits == 0b0110){
    rotation = 6;
  }else if(bits == 0b1010){
    rotation = 4;
  }else if(bits == 0b0100){
    rotation = 7;
  }else if(bits == 0b1000){
    rotation = 3;
  }
  NRF_LOG_DEBUG("bits: %x, rotation: %d\n", bits, rotation);
  // rotation <<= 4; // make room for buttons
  report_data[2] = rotation;

  if(false){}
  #if HID_BUTTON_A_ENABLED == 1
  else if(config->pin == HID_BUTTON_A_PIN){
    if(config->state) report_data[0] |=  0b01;
    else              report_data[0] &= ~0b01;
  }
  #endif
  #if HID_BUTTON_B_ENABLED == 1
  else if(config->pin == HID_BUTTON_B_PIN){
    if(config->state) report_data[0] |=  0b10;
    else              report_data[0] &= ~0b10;
  }
  #endif
  #if HID_BUTTON_START_ENABLED == 1
  else if(config->pin == HID_BUTTON_START_PIN){
    if(config->state) report_data[1] |=  0b1000;
    else              report_data[1] &= ~0b1000;
  }
  #endif
  #if HID_BUTTON_SELECT_ENABLED == 1
  else if(config->pin == HID_BUTTON_SELECT_PIN){
    if(config->state) report_data[1] |=  0b0100;
    else              report_data[1] &= ~0b0100;
  }
  #endif

  #endif
  
  if(ble_hid_connection_handle == BLE_CONN_HANDLE_INVALID){
      // no client connected
      return;
  }

  // Disabling this since for some reason
  // Android doesn't re-enable notifications on the report
  /*
  if(!ble_hid_report_notification_enabled){
    return;
  }
  */

  uint16_t len = sizeof(report_data);

  ble_gatts_hvx_params_t params = {
      .handle = ble_hid_characteristic_report_value_handle,
      .type = BLE_GATT_HVX_NOTIFICATION,
      .p_len = &len,
      .p_data = report_data
  };

  ret_code_t err_code = sd_ble_gatts_hvx(
      ble_hid_connection_handle,
      &params
  );
  if(err_code == 0x3401){
    NRF_LOG_ERROR("hvx not enabled...\n");
    return;
  }
  APP_ERROR_CHECK(err_code);
}

void ble_hid_on_connect(const ble_evt_t *p_ble_evt)
{
    ble_hid_connection_handle = p_ble_evt->evt.gap_evt.conn_handle;
}

void ble_hid_on_disconnect(const ble_evt_t *p_ble_evt)
{
    UNUSED_PARAMETER(p_ble_evt);
    ble_hid_connection_handle = BLE_CONN_HANDLE_INVALID;
    ble_hid_report_notification_enabled = false;
}

void handle_hid_report_cccd_write(const ble_gatts_evt_write_t *write_evt)
{
    if (write_evt->len == 2)
    {
        ble_hid_report_notification_enabled = ble_srv_is_notification_enabled(write_evt->data);
        NRF_LOG_DEBUG("notification enabled: %d\n", ble_hid_report_notification_enabled);
    }
}

void ble_hid_on_write(const ble_evt_t *p_ble_evt)
{
    const ble_gatts_evt_write_t *write_evt = &p_ble_evt
                                            ->evt
                                            .gatts_evt
                                            .params
                                            .write;

    uint16_t handle = write_evt->handle;

    if (handle == ble_hid_characteristic_report_cccd_handle)
    {
        NRF_LOG_DEBUG("writing cccd");
        handle_hid_report_cccd_write(write_evt);
        return;
    }
    
    if (handle == ble_hid_characteristic_control_point_value_handle)
    {
        NRF_LOG_DEBUG("writing control point");
        return;
    }

    NRF_LOG_DEBUG("writing unknown thingathing %d %d %d %d %d", handle, ble_hid_characteristic_information_handle, ble_hid_characteristic_report_cccd_handle, ble_hid_characteristic_report_value_handle, ble_hid_characteristic_control_point_value_handle);
}

void ble_hid_on_ble_evt(const ble_evt_t *p_ble_evt)
{
    switch (p_ble_evt->header.evt_id)
    {
    case BLE_GAP_EVT_CONNECTED:
        ble_hid_on_connect(p_ble_evt);
        break;

    case BLE_GAP_EVT_DISCONNECTED:
        ble_hid_on_disconnect(p_ble_evt);
        break;

    case BLE_GATTS_EVT_WRITE:
        NRF_LOG_DEBUG("hid write\n");
        ble_hid_on_write(p_ble_evt);
        break;

    default:
        // No implementation needed.
        break;
    }
}

ret_code_t ble_hid_characteristic_informatioin_add()
{
  ble_helper_characteristic_init_t init = {
    .service_handle = ble_hid_servic
Download .txt
gitextract_i37ezxwn/

├── .github/
│   └── workflows/
│       ├── build.yml
│       ├── firmware-test.yml
│       ├── jinja.yml
│       ├── release-build.yml
│       └── release-build.yml.template
├── .gitignore
├── .gitmodules
├── CITATION.cff
├── LICENSE
├── Makefile
├── README.md
├── blink.gpioasm
├── docs/
│   ├── AUTOMATION_IO_SERVICE.md
│   ├── BINARY_SENSOR_SERVICE.md
│   ├── COMPILATION.md
│   ├── CONFIGURATION.md
│   ├── FEATURES.md
│   ├── FIRST_STEPS.md
│   ├── FLASHING.md
│   ├── GPIO_ASM_SERVICE.md
│   └── boards/
│       ├── GENERIC.md
│       ├── GENERIC_UF2.md
│       ├── NORDIC_DFU.md
│       ├── NRF52840_DONGLE.md
│       ├── PRO_MICRO.md
│       └── XIAO_BLE.md
├── openocd/
│   └── raspi-bcm2385.tcl
├── src/
│   ├── ble/
│   │   ├── helpers/
│   │   │   ├── ble_helpers.c
│   │   │   └── ble_helpers.h
│   │   ├── sensor_ble.c
│   │   ├── sensor_ble.h
│   │   └── services/
│   │       ├── automation_io/
│   │       │   ├── ble_automation_io_service.c
│   │       │   └── ble_automation_io_service.h
│   │       ├── binary_sensor/
│   │       │   ├── ble_binary_sensor_service.c
│   │       │   └── ble_binary_sensor_service.h
│   │       ├── ble_gpio_asm/
│   │       │   ├── ble_gpio_asm.c
│   │       │   └── ble_gpio_asm.h
│   │       ├── configuration/
│   │       │   ├── ble_configuration_service.c
│   │       │   └── ble_configuration_service.h
│   │       ├── cycling_speed_cadence/
│   │       │   ├── ble_cycling_speed_cadence.c
│   │       │   └── ble_cycling_speed_cadence.h
│   │       ├── hid/
│   │       │   ├── ble_hid.c
│   │       │   └── ble_hid.h
│   │       └── temperature/
│   │           ├── ble_temperature_service.c
│   │           └── ble_temperature_service.h
│   ├── config/
│   │   ├── .gitignore
│   │   ├── bsp/
│   │   │   └── generated/
│   │   │       ├── 96boards_96b_nitrogen.h
│   │   │       ├── aconno_acn52832.h
│   │   │       ├── adafruit_adafruit_feather_nrf52840.h
│   │   │       ├── adafruit_adafruit_itsybitsy.h
│   │   │       ├── adafruit_nrf52_adafruit_feather.h
│   │   │       ├── arduino_arduino_nano_33_ble.h
│   │   │       ├── arduino_arduino_nicla_sense_me.h
│   │   │       ├── atmarktechno_degu_evk.h
│   │   │       ├── bbc_bbc_microbit.h
│   │   │       ├── bcdevices_blueclover_plt_demo_v2.h
│   │   │       ├── croxel_croxel_cx1825.h
│   │   │       ├── ebyte_ebyte_e73_tbb.h
│   │   │       ├── electronut_nrf52840_blip.h
│   │   │       ├── electronut_nrf52840_papyr.h
│   │   │       ├── ezurio_bl652_dvk.h
│   │   │       ├── ezurio_bl654_dvk.h
│   │   │       ├── ezurio_bl654_sensor_board.h
│   │   │       ├── ezurio_bl654_usb.h
│   │   │       ├── ezurio_bt510.h
│   │   │       ├── ezurio_bt610.h
│   │   │       ├── ezurio_mg100.h
│   │   │       ├── ezurio_pinnacle_100_dvk.h
│   │   │       ├── ezurio_rm1xx_dvk.h
│   │   │       ├── holyiot_holyiot_yj16019.h
│   │   │       ├── makerdiary_nrf52832_mdk.h
│   │   │       ├── makerdiary_nrf52840_mdk.h
│   │   │       ├── makerdiary_nrf52840_mdk_usb_dongle.h
│   │   │       ├── nordic_nrf21540dk.h
│   │   │       ├── nordic_nrf51dk.h
│   │   │       ├── nordic_nrf51dongle.h
│   │   │       ├── nordic_nrf52840dk.h
│   │   │       ├── nordic_nrf52840dongle.h
│   │   │       ├── nordic_nrf52dk.h
│   │   │       ├── nordic_nrf9160dk.h
│   │   │       ├── nordic_thingy52.h
│   │   │       ├── other_ctcc.h
│   │   │       ├── others_promicro_nrf52840.h
│   │   │       ├── panasonic_pan1770_evb.h
│   │   │       ├── panasonic_pan1780_evb.h
│   │   │       ├── particle_nrf51_blenano.h
│   │   │       ├── particle_nrf52_blenano2.h
│   │   │       ├── phytec_reel_board.h
│   │   │       ├── pine64_pinetime_devkit0.h
│   │   │       ├── qorvo_decawave_dwm1001_dev.h
│   │   │       ├── rakwireless_rak4631.h
│   │   │       ├── rakwireless_rak5010.h
│   │   │       ├── raytac_raytac_mdbt50q_db_40.h
│   │   │       ├── ruuvi_ruuvi_ruuvitag.h
│   │   │       ├── seeed_xiao_ble.h
│   │   │       ├── sparkfun_micromod.h
│   │   │       ├── sparkfun_nrf52_sparkfun.h
│   │   │       ├── u_blox_ubx_bmd300eval.h
│   │   │       ├── u_blox_ubx_bmd340eval.h
│   │   │       ├── u_blox_ubx_bmd345eval.h
│   │   │       ├── u_blox_ubx_bmd380eval.h
│   │   │       ├── u_blox_ubx_evkannab1.h
│   │   │       ├── u_blox_ubx_evkninab1.h
│   │   │       ├── u_blox_ubx_evkninab3.h
│   │   │       ├── vngiotlab_nrf51_vbluno51.h
│   │   │       ├── vngiotlab_nrf52_vbluno52.h
│   │   │       ├── waveshare_nrf51_ble400.h
│   │   │       ├── wurth_we_proteus2ev.h
│   │   │       └── wurth_we_proteus3ev.h
│   │   ├── feature_config.h
│   │   ├── feature_config.template.h.jinja
│   │   ├── nrf51/
│   │   │   └── sdk_config.h
│   │   └── nrf52/
│   │       └── sdk_config.h
│   ├── error_handler/
│   │   └── error_handler.c
│   ├── gpio/
│   │   ├── sensor_gpio.c
│   │   └── sensor_gpio.h
│   ├── helpers/
│   │   ├── encoding.c
│   │   └── encoding.h
│   ├── linker/
│   │   ├── nrf51822_qfaa.ld
│   │   ├── nrf51822_qfac.ld
│   │   ├── nrf52832_qfaa.ld
│   │   └── nrf52840_qfaa.ld
│   ├── main.c
│   ├── persistence/
│   │   ├── pin_configuration.c
│   │   └── pin_configuration.h
│   ├── sleep/
│   │   ├── sleep.c
│   │   └── sleep.h
│   ├── storage/
│   │   ├── preconfiguration.c
│   │   ├── preconfiguration.h
│   │   ├── storage.h
│   │   ├── storage.nrf51.c
│   │   └── storage.nrf52.c
│   ├── timer/
│   │   ├── sensor_timer.c
│   │   └── sensor_timer.h
│   └── watchdog/
│       ├── watchdog.c
│       └── watchdog.h
└── zephyr_convert.py
Download .txt
SYMBOL INDEX (276 symbols across 25 files)

FILE: src/ble/helpers/ble_helpers.c
  function ret_code_t (line 8) | ret_code_t ble_helper_characteristic_add(ble_helper_characteristic_init_...

FILE: src/ble/helpers/ble_helpers.h
  type ble_helper_characteristic_init_t (line 11) | typedef struct {

FILE: src/ble/sensor_ble.c
  function peer_manager_event_handler (line 65) | void peer_manager_event_handler(pm_evt_t const *p_evt)
  function peer_manager_init (line 204) | void peer_manager_init()
  function fds_evt_handler (line 249) | void fds_evt_handler(fds_evt_t const *const p_fds_evt)
  function filesystem_init (line 278) | void filesystem_init()
  function ble_init (line 287) | void ble_init() {
  function ble_handle_input_change (line 337) | void ble_handle_input_change(int highest_changed_index)
  function ble_handle_device_name_write (line 382) | void ble_handle_device_name_write(const ble_gatts_evt_write_t *write_evt){
  function ble_on_write (line 389) | void ble_on_write(const ble_evt_t *p_ble_evt) {
  function on_ble_evt (line 407) | void on_ble_evt(const ble_evt_t *p_ble_evt) {
  function ble_evt_dispatch (line 565) | void ble_evt_dispatch(const ble_evt_t *p_ble_evt, void * p_context) {
  function power_manage (line 657) | void power_manage(void) {
  function on_conn_params_evt (line 664) | void on_conn_params_evt(ble_conn_params_evt_t *p_evt) {
  function conn_params_error_handler (line 673) | void conn_params_error_handler(uint32_t nrf_error) {
  function conn_params_init (line 679) | void conn_params_init(void) {
  function set_addr_from_data (line 698) | void set_addr_from_data(uint8_t *key) {
  function custom_data_advertisement_start (line 715) | void custom_data_advertisement_start(){
  function custom_data_advertisement_stop (line 768) | void custom_data_advertisement_stop(){
  function advertising_event_handler (line 793) | void advertising_event_handler(ble_adv_evt_t event) {
  function advertising_init (line 815) | void advertising_init() {
  function advertising_start (line 909) | void advertising_start() {
  function sys_evt_dispatch (line 925) | void sys_evt_dispatch(uint32_t sys_evt) {
  function sys_evt_dispatch (line 930) | void sys_evt_dispatch(uint32_t sys_evt, void * p_contextt) {
  function ble_disable_rf (line 935) | void ble_disable_rf(){
  function ble_stack_init (line 943) | void ble_stack_init(void) {
  function gap_params_init (line 1001) | void gap_params_init(uint8_t *device_name, uint32_t device_name_length) {
  function bas_init (line 1073) | uint32_t bas_init() {
  function ble_handle_connection_parameters_configuration_update (line 1077) | void ble_handle_connection_parameters_configuration_update(ble_configura...
  function ret_code_t (line 1120) | ret_code_t dis_init(){
  function services_init (line 1152) | void services_init(void) {
  function advertising_stop (line 1191) | void

FILE: src/ble/services/automation_io/ble_automation_io_service.c
  function ble_aio_on_connect (line 29) | void ble_aio_on_connect(const ble_evt_t *p_ble_evt)
  function ble_aio_on_disconnect (line 34) | void ble_aio_on_disconnect(const ble_evt_t *p_ble_evt)
  function handle_pin_digital_in_cccd_write (line 41) | void handle_pin_digital_in_cccd_write(const ble_gatts_evt_write_t *write...
  function ble_aio_handle_pin_digital_data (line 49) | void ble_aio_handle_pin_digital_data(
  function ble_aio_handle_pin_analog_data (line 66) | void ble_aio_handle_pin_analog_data(
  function handle_digital_out_write (line 81) | void handle_digital_out_write(const ble_gatts_evt_write_t *write_evt)
  function handle_pin_analog_out_write (line 89) | void handle_pin_analog_out_write(uint32_t index, const ble_gatts_evt_wri...
  function ble_aio_authorize_digital_out (line 108) | void ble_aio_authorize_digital_out()
  function ret_code_t (line 134) | ret_code_t ble_aio_characteristic_digital_output_add()
  function ret_code_t (line 156) | ret_code_t ble_aio_characteristic_analog_output_add(uint32_t index)
  function ret_code_t (line 173) | ret_code_t ble_aio_characteristic_digital_input_add()
  function ble_aio_on_write (line 191) | void ble_aio_on_write(const ble_evt_t *p_ble_evt)
  function ble_aio_authorize_digital_in (line 221) | void ble_aio_authorize_digital_in()
  function ble_aio_update_digital_in_states (line 247) | void ble_aio_update_digital_in_states()
  function write_bit (line 281) | void write_bit(uint8_t *byte, uint32_t position, uint8_t value)
  function encode_states_to_bytes (line 294) | void encode_states_to_bytes(uint8_t *states, uint32_t state_count, uint8...
  function ble_aio_on_authorize (line 319) | void ble_aio_on_authorize(const ble_evt_t *p_ble_evt)
  function ble_aio_on_ble_evt (line 348) | void ble_aio_on_ble_evt(const ble_evt_t *p_ble_evt)
  function ble_aio_handle_input_change (line 374) | void ble_aio_handle_input_change(int highest_changed_index)
  function ret_code_t (line 417) | ret_code_t ble_aio_init()

FILE: src/ble/services/binary_sensor/ble_binary_sensor_service.c
  type opening_closing_state_t (line 18) | enum opening_closing_state_t
  function ble_bss_on_connect (line 21) | void ble_bss_on_connect(ble_evt_t *p_ble_evt) {
  function ble_bss_on_disconnect (line 25) | void ble_bss_on_disconnect(ble_evt_t *p_ble_evt) {
  function on_bss_cccd_write (line 32) | void on_bss_cccd_write(ble_gatts_evt_write_t *p_evt_write) {
  function on_control_characteristic_write (line 38) | void on_control_characteristic_write(ble_gatts_evt_write_t *write) {
  function parse_full_packet_with_split_header (line 45) | void parse_full_packet_with_split_header(uint8_t *data, uint16_t length) {
  function parse_packet (line 54) | void parse_packet(uint8_t *data, uint16_t length) {
  function parse_packet_decoded (line 75) | void parse_packet_decoded(enum message_id_t message_id, message_paramete...
  function parse_set_sensor_command (line 89) | void parse_set_sensor_command(message_parameter_t *parameters, uint8_t p...
  function handle_set_sensor_command (line 100) | void handle_set_sensor_command(enum sensor_type_t sensor_type, enum repo...
  function parse_get_sensor_command (line 110) | void parse_get_sensor_command(message_parameter_t *parameters, uint8_t p...
  function handle_get_sensor_command (line 119) | void handle_get_sensor_command(enum sensor_type_t sensor_type) {
  function handle_get_multiple_open_close_sensor_command (line 140) | void handle_get_multiple_open_close_sensor_command() {
  function handle_get_multiple_human_sensor_command (line 144) | void handle_get_multiple_human_sensor_command() {
  function handle_get_multiple_vibration_sensor_command (line 148) | void handle_get_multiple_vibration_sensor_command() {
  function handle_get_single_human_sensor_command (line 152) | void handle_get_single_human_sensor_command() {
  function handle_get_single_vibration_sensor_command (line 156) | void handle_get_single_vibration_sensor_command() {
  function handle_get_single_open_close_sensor_command (line 160) | void handle_get_single_open_close_sensor_command() {
  function respond_set_sensor_command (line 164) | void respond_set_sensor_command(enum result_code_t result_code) {
  function respond_get_sensor_command (line 168) | void respond_get_sensor_command(enum result_code_t result_code, enum ope...
  function respond_get_sensor_event (line 172) | void respond_get_sensor_event(enum result_code_t result_code, enum openi...
  function ret_code_t (line 178) | ret_code_t send_message_with_header(enum message_id_t message_id, messag...
  function respond_set_sensor (line 210) | void respond_set_sensor(enum message_id_t message_id, enum result_code_t...
  function respond_get_sensor (line 222) | void respond_get_sensor(enum message_id_t message_id, enum result_code_t...
  function ble_bss_on_write (line 240) | void ble_bss_on_write(ble_evt_t *p_ble_evt) {
  function ble_bss_set_state (line 251) | void ble_bss_set_state(enum opening_closing_state_t state, uint16_t coun...
  function ble_bss_handle_input_change (line 260) | void ble_bss_handle_input_change(uint32_t index, gpio_config_input_digit...
  function ble_bss_on_ble_evt (line 269) | void ble_bss_on_ble_evt(ble_evt_t *p_ble_evt) {
  function characteristic_control_point_add (line 289) | uint32_t characteristic_control_point_add()
  function characteristic_response_add (line 340) | uint32_t characteristic_response_add()
  function ble_bss_init (line 392) | uint32_t ble_bss_init() {
  function ret_code_t (line 417) | ret_code_t ble_bss_response_send(uint8_t *data, uint8_t length) {

FILE: src/ble/services/binary_sensor/ble_binary_sensor_service.h
  type message_id_t (line 20) | enum message_id_t {
  type result_code_t (line 28) | enum result_code_t {
  type sensor_type_t (line 33) | enum sensor_type_t {
  type parameter_id_t (line 42) | enum parameter_id_t {
  type opening_closing_state_t (line 52) | enum opening_closing_state_t {
  type report_state_t (line 57) | enum report_state_t {
  type message_parameter_t (line 62) | typedef struct {
  type message_parameter_without_data_t (line 69) | typedef struct {
  type message_header_t (line 74) | typedef struct {
  type message_id_t (line 84) | enum message_id_t
  type sensor_type_t (line 87) | enum sensor_type_t
  type report_state_t (line 87) | enum report_state_t
  type sensor_type_t (line 89) | enum sensor_type_t
  type result_code_t (line 96) | enum result_code_t
  type result_code_t (line 97) | enum result_code_t
  type opening_closing_state_t (line 97) | enum opening_closing_state_t
  type result_code_t (line 98) | enum result_code_t
  type opening_closing_state_t (line 98) | enum opening_closing_state_t
  type message_id_t (line 99) | enum message_id_t
  type message_id_t (line 100) | enum message_id_t
  type result_code_t (line 100) | enum result_code_t
  type message_id_t (line 101) | enum message_id_t
  type result_code_t (line 101) | enum result_code_t
  type opening_closing_state_t (line 101) | enum opening_closing_state_t
  type opening_closing_state_t (line 103) | enum opening_closing_state_t

FILE: src/ble/services/ble_gpio_asm/ble_gpio_asm.c
  function ret_code_t (line 17) | ret_code_t ble_gpio_asm_characteristic_asm_data_add()
  function ble_gpio_asm_handle_data_write (line 33) | uint8_t ble_gpio_asm_handle_data_write(const uint8_t *data, uint32_t len...
  function ble_gpio_asm_handle_input_change (line 69) | void ble_gpio_asm_handle_input_change()
  function ble_gpio_asm_on_connect (line 74) | void ble_gpio_asm_on_connect(const ble_evt_t *p_ble_evt)
  function ble_gpio_asm_on_disconnect (line 79) | void ble_gpio_asm_on_disconnect(const ble_evt_t *p_ble_evt)
  function ble_gpio_asm_authorize_data_write (line 86) | void ble_gpio_asm_authorize_data_write(const ble_gatts_evt_write_t *writ...
  function ble_gpio_asm_on_authorize (line 109) | void ble_gpio_asm_on_authorize(const ble_evt_t *p_ble_evt)
  function ble_gpio_asm_on_ble_evt (line 141) | void ble_gpio_asm_on_ble_evt(const ble_evt_t *p_ble_evt)
  function ble_gpioasm_timer_handler (line 167) | void ble_gpioasm_timer_handler(void *engine, uint64_t timeout, bool start){
  function ble_gpioasm_timer_timeout_handler (line 175) | void ble_gpioasm_timer_timeout_handler(){
  function ble_gpio_asm_init (line 179) | void ble_gpio_asm_init(){

FILE: src/ble/services/configuration/ble_configuration_service.c
  function ret_code_t (line 23) | ret_code_t ble_configuration_characteristic_pin_configuration_add() {
  function ret_code_t (line 37) | ret_code_t ble_configuration_characteristic_connections_parameters_add() {
  function ret_code_t (line 52) | ret_code_t ble_reboot_characteristic_add() {
  function ret_code_t (line 68) | ret_code_t ble_configuration_characteristic_data_set(uint32_t handle, ui...
  function ret_code_t (line 82) | ret_code_t ble_configuration_pin_configuraion_data_set(uint8_t data[]) {
  function ret_code_t (line 90) | ret_code_t ble_configuration_connection_params_configuraion_data_set(uin...
  function ble_configuration_restore_values (line 98) | void ble_configuration_restore_values() {
  function ret_code_t (line 111) | ret_code_t ble_configuration_service_init(ble_configuration_connection_p...
  function ble_configuration_on_connect (line 147) | void ble_configuration_on_connect(const ble_evt_t *p_ble_evt) {
  function ble_configuration_on_disconnect (line 151) | void ble_configuration_on_disconnect(const ble_evt_t *p_ble_evt) {
  function ble_configuration_handle_connection_params_configuration_data (line 159) | void ble_configuration_handle_connection_params_configuration_data(const...
  function ble_configuration_authorize_read_reboot (line 173) | void ble_configuration_authorize_read_reboot(const ble_gatts_evt_read_t ...
  function ble_configuration_authorize_write_reboot (line 199) | void ble_configuration_authorize_write_reboot(const ble_gatts_evt_write_...
  function ble_configuration_authorize_connection_params_write (line 236) | void ble_configuration_authorize_connection_params_write(const ble_gatts...
  function ble_configuration_on_authorize (line 309) | void ble_configuration_on_authorize(const ble_evt_t *p_ble_evt) {
  function ble_configuration_handle_pin_configuration_write (line 341) | void ble_configuration_handle_pin_configuration_write(const ble_gatts_ev...
  function ble_configuration_handle_connection_params_configuration_write (line 363) | void ble_configuration_handle_connection_params_configuration_write(cons...
  function ble_configuration_on_write (line 377) | void ble_configuration_on_write(const ble_evt_t *p_ble_evt) {
  function ble_configuration_on_ble_event (line 396) | void ble_configuration_on_ble_event(const ble_evt_t *p_ble_evt) {
  function ble_configuration_service_get_custom_uuid_type (line 421) | uint8_t ble_configuration_service_get_custom_uuid_type() {

FILE: src/ble/services/configuration/ble_configuration_service.h
  type ble_configuration_connection_params_packet_t (line 14) | typedef struct {

FILE: src/ble/services/cycling_speed_cadence/ble_cycling_speed_cadence.c
  function get_rtc1_ticks (line 29) | uint32_t get_rtc1_ticks(){
  function get_time_units (line 33) | uint16_t get_time_units(){
  function csc_encode (line 37) | void csc_encode(uint8_t buffer[7]){
  function ble_csc_measurement_report (line 47) | void ble_csc_measurement_report(){
  function measurement_timer_timeout_handler (line 74) | void measurement_timer_timeout_handler(void *context){
  function ble_csc_timer_init (line 87) | void ble_csc_timer_init(){
  function ble_csc_on_connect (line 96) | void ble_csc_on_connect(ble_evt_t *p_ble_evt)
  function ble_csc_on_disconnect (line 101) | void ble_csc_on_disconnect(ble_evt_t *p_ble_evt)
  function handle_speed_measurement_notification_enabled (line 107) | void handle_speed_measurement_notification_enabled(uint8_t enabled){
  function handle_csc_measurement_cccd_write (line 125) | void handle_csc_measurement_cccd_write(ble_gatts_evt_write_t *write_evt)
  function ret_code_t (line 135) | ret_code_t ble_csc_characteristic_measurement_add()
  function ble_csc_on_write (line 152) | void ble_csc_on_write(ble_evt_t *p_ble_evt)
  function ble_csc_on_authroize_measurement (line 169) | void ble_csc_on_authroize_measurement()
  function ble_csc_on_authorize (line 192) | void ble_csc_on_authorize(ble_evt_t *p_ble_evt)
  function ble_csc_on_ble_evt (line 215) | void ble_csc_on_ble_evt(ble_evt_t *p_ble_evt)
  function ble_csc_handle_sensor_trigger (line 241) | void ble_csc_handle_sensor_trigger(uint32_t trigger_count){
  function ble_csc_handle_input_change (line 247) | void ble_csc_handle_input_change(uint32_t index, gpio_config_input_digit...
  function ret_code_t (line 259) | ret_code_t ble_csc_init()

FILE: src/ble/services/hid/ble_hid.c
  function ble_hid_handle_input_change (line 70) | void ble_hid_handle_input_change(uint32_t index, gpio_config_input_digit...
  function ble_hid_on_connect (line 185) | void ble_hid_on_connect(const ble_evt_t *p_ble_evt)
  function ble_hid_on_disconnect (line 190) | void ble_hid_on_disconnect(const ble_evt_t *p_ble_evt)
  function handle_hid_report_cccd_write (line 197) | void handle_hid_report_cccd_write(const ble_gatts_evt_write_t *write_evt)
  function ble_hid_on_write (line 206) | void ble_hid_on_write(const ble_evt_t *p_ble_evt)
  function ble_hid_on_ble_evt (line 232) | void ble_hid_on_ble_evt(const ble_evt_t *p_ble_evt)
  function ret_code_t (line 255) | ret_code_t ble_hid_characteristic_informatioin_add()
  function ret_code_t (line 269) | ret_code_t ble_hid_characteristic_report_map_add()
  function ret_code_t (line 284) | ret_code_t ble_hid_characteristic_control_point_add()
  function ret_code_t (line 297) | ret_code_t ble_hid_characteristic_report_add()
  function ret_code_t (line 350) | ret_code_t ble_hid_init()

FILE: src/ble/services/temperature/ble_temperature_service.c
  function ble_temperature_on_connect (line 20) | void ble_temperature_on_connect(const ble_evt_t *p_ble_evt)
  function ble_temperature_on_disconnect (line 25) | void ble_temperature_on_disconnect(const ble_evt_t *p_ble_evt)
  function ble_temperature_on_cccd_write (line 31) | void ble_temperature_on_cccd_write(ble_gatts_evt_write_t *write_evt)
  function ret_code_t (line 39) | ret_code_t ble_temperature_characteristic_add()
  function ble_temperature_authorize_temperature (line 53) | void ble_temperature_authorize_temperature(const ble_evt_t *p_ble_evt){
  function ble_temperature_on_ble_evt (line 94) | void ble_temperature_on_ble_evt(const ble_evt_t *p_ble_evt)
  function ret_code_t (line 120) | ret_code_t ble_temperature_init()

FILE: src/error_handler/error_handler.c
  function HardFault_Handler (line 13) | void HardFault_Handler(){
  function app_error_fault_handler (line 21) | void

FILE: src/gpio/sensor_gpio.c
  type direction_t (line 18) | typedef enum {
  type gpio_config_t (line 23) | typedef struct {
  function gpio_config_t (line 39) | gpio_config_t *find_gpio_config_by_index(uint32_t index, direction_t dir...
  function gpio_config_output_digital_t (line 53) | gpio_config_output_digital_t *find_gpio_output_by_index(uint32_t index){
  function gpio_config_input_digital_t (line 61) | gpio_config_input_digital_t *gpio_find_input_by_index(uint32_t index){
  function gpio_write_output_digital_pin (line 69) | void gpio_write_output_digital_pin(uint32_t index, uint8_t new_state) {
  function gpio_get_output_digital_pin_count (line 106) | uint32_t gpio_get_output_digital_pin_count() {
  function gpio_get_output_analog_pin_count (line 110) | uint32_t gpio_get_output_analog_pin_count() {
  function gpio_get_input_digital_pin_count (line 114) | uint32_t gpio_get_input_digital_pin_count() {
  function gpio_get_output_digital_state (line 118) | uint8_t gpio_get_output_digital_state(uint32_t index) {
  function gpio_get_input_digital_state (line 126) | bool gpio_get_input_digital_state(uint32_t index) {
  function gpio_encode_states (line 134) | void gpio_encode_states(uint8_t *buffer, direction_t direction){
  function gpio_encode_output_states (line 153) | void gpio_encode_output_states(uint8_t *buffer) {
  function gpio_encode_input_states (line 157) | void gpio_encode_input_states(uint8_t *buffer) {
  function gpio_configure_aio_outputs_digital (line 161) | void gpio_configure_aio_outputs_digital() {
  function gpio_write_output_analog_pin_ticks (line 173) | void gpio_write_output_analog_pin_ticks(uint32_t index, uint16_t value){
  function gpio_write_output_analog_pin_us (line 196) | void gpio_write_output_analog_pin_us(uint32_t index, uint16_t us){
  function gpio_configure_aio_outputs_analog (line 200) | void gpio_configure_aio_outputs_analog(){
  function on_pin_changed (line 208) | void on_pin_changed(uint32_t index) {
  function gpio_debounce_timeout_handler (line 232) | void gpio_debounce_timeout_handler(uint32_t timer_index) {
  function gpio_pin_toggle_handler (line 247) | void gpio_pin_toggle_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polari...
  function gpio_configure_aio_inputs_digital (line 278) | void gpio_configure_aio_inputs_digital() {
  function gpio_handle_parse_output_digital (line 315) | void gpio_handle_parse_output_digital(uint32_t index, uint32_t pin, uint...
  function gpio_handle_parse_output_analog (line 324) | void gpio_handle_parse_output_analog(uint32_t index, uint32_t pin, uint8...
  function gpio_handle_parse_input_digital (line 334) | void gpio_handle_parse_input_digital(uint32_t index, uint32_t pin, uint8...
  function gpio_init (line 343) | void gpio_init(gpio_input_change_handler_t input_change_handler) {

FILE: src/gpio/sensor_gpio.h
  type gpio_config_output_digital_t (line 13) | typedef struct
  type gpio_config_input_digital_t (line 21) | typedef struct

FILE: src/helpers/encoding.c
  function encoding_get_pin_bits (line 5) | uint8_t encoding_get_pin_bits(const uint8_t *pin_data, uint32_t pin_data...
  function encoding_get_byte_count_from_pins (line 18) | uint32_t encoding_get_byte_count_from_pins(uint32_t pin_count)

FILE: src/main.c
  function main_handle_input_change (line 31) | void main_handle_input_change(int highest_changed_index)
  function main (line 39) | int main(void) {

FILE: src/persistence/pin_configuration.c
  function is_pin_enabled (line 13) | uint8_t is_pin_enabled(uint8_t pin_byte) {
  function is_output_pin_enabled (line 17) | uint8_t is_output_pin_enabled(uint8_t pin_byte) {
  function is_output_pin_analog (line 21) | uint8_t is_output_pin_analog(uint8_t pin_byte) {
  function get_output_digital_pin_default_state (line 25) | uint8_t get_output_digital_pin_default_state(uint8_t pin_byte) {
  function is_input_pin_enabled (line 29) | uint8_t is_input_pin_enabled(uint8_t pin_byte) {
  function get_input_digital_pin_pull (line 33) | uint8_t get_input_digital_pin_pull(uint8_t pin_byte) {
  function get_pin_invert (line 37) | uint8_t get_pin_invert(uint8_t pin_byte) {
  function parse_pin_byte (line 41) | void parse_pin_byte(uint32_t pin_index, uint8_t pin_data) {
  function pin_data_for_each_pin (line 77) | void pin_data_for_each_pin(uint8_t *pin_data, uint32_t length, void (*ha...
  function count_up_if_enabled (line 85) | void count_up_if_enabled(uint32_t pin_index, uint8_t pin_data) {
  function get_pin_count_output_digital (line 100) | uint32_t get_pin_count_output_digital() {
  function get_pin_count_output_analog (line 104) | uint32_t get_pin_count_output_analog() {
  function get_pin_count_input_digital (line 108) | uint32_t get_pin_count_input_digital() {
  function pin_configuration_init (line 112) | void pin_configuration_init() {
  function pin_configuration_parse (line 122) | void pin_configuration_parse(
  function pin_configuration_data_read (line 137) | void pin_configuration_data_read(uint8_t *data) {

FILE: src/sleep/sleep.c
  function sleep_timer_stop (line 29) | void sleep_timer_stop(){
  function sleep_timer_start (line 38) | void sleep_timer_start(){
  function sleep_timeout_handler (line 51) | void sleep_timeout_handler(void *context){
  function sleep_handle_gpio_event (line 70) | void sleep_handle_gpio_event(){
  function sleep_get_allow_advertise (line 77) | bool sleep_get_allow_advertise(){
  function sleep_init (line 81) | void sleep_init(sleep_enter_handler_t sleep_enter_handler_){

FILE: src/storage/preconfiguration.c
  function preconfiguration_load (line 37) | void preconfiguration_load(uint8_t *data) {

FILE: src/storage/storage.nrf51.c
  function fs_evt_handler (line 30) | void fs_evt_handler(fs_evt_t const *const evt, fs_ret_t result) {
  function storage_erase (line 56) | void storage_erase(){
  function checksum_compute (line 70) | uint32_t checksum_compute(uint8_t *data, uint32_t length){
  function storage_read (line 74) | void storage_read(uint32_t offset, uint8_t *buffer, uint32_t length) {
  function storage_checksum_check (line 79) | void storage_checksum_check(){
  function storage_init (line 128) | void storage_init() {
  function storage_on_sys_evt (line 139) | void storage_on_sys_evt(uint32_t sys_evt) {
  function storage_read_pin_configuration (line 143) | void storage_read_pin_configuration(uint8_t *buffer) {
  function storage_read_connection_params_configuration (line 152) | void storage_read_connection_params_configuration(uint8_t *buffer) {
  function storage_read_device_name (line 156) | void storage_read_device_name(uint8_t *buffer, uint32_t *length_) {
  function storage_store (line 176) | void storage_store(uint32_t offset, const uint8_t *data, uint32_t length...
  function storage_store_pin_configuration (line 215) | void storage_store_pin_configuration(uint8_t *data) {
  function storage_store_connection_params_configuration (line 219) | void storage_store_connection_params_configuration(const uint8_t *data) {
  function storage_store_device_name (line 223) | void storage_store_device_name(const uint8_t *name, int length) {

FILE: src/storage/storage.nrf52.c
  function fs_evt_handler (line 26) | void fs_evt_handler(nrf_fstorage_evt_t * p_evt) {
  function storage_erase (line 51) | void storage_erase(){
  function checksum_compute (line 65) | uint32_t checksum_compute(uint8_t *data, uint32_t length){
  function storage_read (line 69) | void storage_read(uint32_t offset, uint8_t *buffer, uint32_t length) {
  function storage_checksum_check (line 74) | void storage_checksum_check(){
  function storage_init (line 119) | void storage_init() {
  function storage_read_pin_configuration (line 146) | void storage_read_pin_configuration(uint8_t *buffer) {
  function storage_read_connection_params_configuration (line 155) | void storage_read_connection_params_configuration(uint8_t *buffer) {
  function storage_read_device_name (line 159) | void storage_read_device_name(uint8_t *buffer, uint32_t *length_) {
  function storage_store (line 179) | void storage_store(uint32_t offset, const uint8_t *data, uint32_t length...
  function storage_store_pin_configuration (line 214) | void storage_store_pin_configuration(uint8_t *data) {
  function storage_store_connection_params_configuration (line 218) | void storage_store_connection_params_configuration(const uint8_t *data) {
  function storage_store_device_name (line 222) | void storage_store_device_name(const uint8_t *name, int length) {

FILE: src/timer/sensor_timer.c
  function debounce_timeout_handler (line 17) | void debounce_timeout_handler(void *context) {
  function sensor_timer_debounce_timer_start (line 21) | void sensor_timer_debounce_timer_start(uint32_t timer_index) {
  function sensor_timer_initialize_debounce_timers (line 30) | void sensor_timer_initialize_debounce_timers(uint32_t input_count, debou...
  function timer_gpioasm_stop (line 46) | void timer_gpioasm_stop() {
  function timer_gpioasm_start_ticks (line 50) | void timer_gpioasm_start_ticks(uint64_t total_ticks, uint64_t *remaining...
  function timer_sequence_timeout_handler (line 62) | void timer_sequence_timeout_handler(void *context) {
  function timer_sequence_set_timeout_handler (line 73) | void timer_sequence_set_timeout_handler(sequence_timer_handler_t timeout...
  function timer_gpioasm_start (line 77) | void timer_gpioasm_start(uint64_t millis) {
  function timer_init (line 82) | void timer_init() {

FILE: src/watchdog/watchdog.c
  function watchdog_timeout_handler (line 10) | void watchdog_timeout_handler(){
  function radio_notification_init (line 22) | uint32_t radio_notification_init()
  function SWI1_IRQHandler (line 48) | void SWI1_IRQHandler(bool radio_evt)
  function watchdog_init (line 53) | void watchdog_init(){
  function watchdog_feed (line 68) | void watchdog_feed(){

FILE: zephyr_convert.py
  function main (line 7) | def main():
Condensed preview — 137 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (742K chars).
[
  {
    "path": ".github/workflows/build.yml",
    "chars": 2903,
    "preview": "name: Reusable build workflow\n\non: \n  workflow_call:\n    inputs:\n      chip:\n        required: true\n        type: string"
  },
  {
    "path": ".github/workflows/firmware-test.yml",
    "chars": 1568,
    "preview": "name: Automated firmware test\n\non: [workflow_dispatch, push]\n\nconcurrency:\n  group: firmware-test\n\njobs:\n  build:\n    ru"
  },
  {
    "path": ".github/workflows/jinja.yml",
    "chars": 525,
    "preview": "name: Jinja build\n\non: [workflow_dispatch, push]\n\njobs:\n  parse:\n    runs-on: ubuntu-latest\n    \n    steps:\n    - uses: "
  },
  {
    "path": ".github/workflows/release-build.yml",
    "chars": 24491,
    "preview": "jobs:\n  build_96boards_96b_nitrogen:\n    uses: ./.github/workflows/build.yml\n    with:\n      board: 96boards_96b_nitroge"
  },
  {
    "path": ".github/workflows/release-build.yml.template",
    "chars": 396,
    "preview": "name: Automated firmware build for release\n\n'on':\n  release:\n    types: [released, prereleased]\n\njobs:\n  build_nrf51822:"
  },
  {
    "path": ".gitignore",
    "chars": 66,
    "preview": "_build/\n*.zip\n.vscode/\ntest/\nnohup.out\n*.hex\n*.bin\n.DS_Store\nvenv\n"
  },
  {
    "path": ".gitmodules",
    "chars": 189,
    "preview": "[submodule \"src/common\"]\n\tpath = src/common\n\turl = git@github.com:dakhnod/nRF51-common.git\n[submodule \"src/gpioasm\"]\n\tpa"
  },
  {
    "path": "CITATION.cff",
    "chars": 315,
    "preview": "cff-version: 1.2.0\nmessage: \"If you use this software, please cite it as below.\"\nauthors:\n- family-names: \"Dakhno\"\n  giv"
  },
  {
    "path": "LICENSE",
    "chars": 850,
    "preview": "Copyright (c) 2024 Daniel Dakhno\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this s"
  },
  {
    "path": "Makefile",
    "chars": 23329,
    "preview": "OUTPUT_DIRECTORY := _build\n\nBLE_ROOT ?= ../..\n\nAPPLICATION_HEX ?= $(OUTPUT_DIRECTORY)/$(TARGETS).hex\nKEY_FILE ?= $(BLE_R"
  },
  {
    "path": "README.md",
    "chars": 4017,
    "preview": "# BLEnky\n\n[![Firmware test badge](https://github.com/dakhnod/blenky/actions/workflows/firmware-test.yml/badge.svg 'Firmw"
  },
  {
    "path": "blink.gpioasm",
    "chars": 107,
    "preview": "label start\n\nwrite_digital 1\nsleep_ms 100\nwrite_digital 0\nsleep_ms 100\n\njump_count start 9\n\nwrite_digital i"
  },
  {
    "path": "docs/AUTOMATION_IO_SERVICE.md",
    "chars": 3750,
    "preview": "## TOC\n\n1. [Interfacing with digital output pins](#output-digital-pins)\n2. [Interfacing with analog output pins](#output"
  },
  {
    "path": "docs/BINARY_SENSOR_SERVICE.md",
    "chars": 350,
    "preview": "# Binary Sensor Service\n\nIs at least one input is configured, the device also exposes a Binary Sensor Service that\nserve"
  },
  {
    "path": "docs/COMPILATION.md",
    "chars": 739,
    "preview": "# Compilation\n\n1. Set up SDK_ROOT in the Makefile to point at an unpacked nRF5-sdk (version 12.3) folder. You can downlo"
  },
  {
    "path": "docs/CONFIGURATION.md",
    "chars": 4775,
    "preview": "# Configuration protocol\n\n## TOC\n\n1. [Saving space](#saving-space)\n1. [Configuring output pins](#output-pins)\n2. [Config"
  },
  {
    "path": "docs/FEATURES.md",
    "chars": 2195,
    "preview": "# Features\n\nThe source includes a few features, that can be turned on or off during compilation.\nThe feature configurati"
  },
  {
    "path": "docs/FIRST_STEPS.md",
    "chars": 1679,
    "preview": "# First steps\n\nRight, so you got a shiny new board.\nChances are there is a freshly baked preconfigured firmware waiting "
  },
  {
    "path": "docs/FLASHING.md",
    "chars": 3419,
    "preview": "# Flashing\n\n## Flashing the nRF51 via bootloader\n\nThis is the recommended way to flash the firmware since it allows for\n"
  },
  {
    "path": "docs/GPIO_ASM_SERVICE.md",
    "chars": 938,
    "preview": "# gpioASM Service\n\nthe gpioASM Service exposes an endpoint for uploading [gpioASM](https://github.com/dakhnod/gpioASM/bl"
  },
  {
    "path": "docs/boards/GENERIC.md",
    "chars": 398,
    "preview": "# Generic board / without bootloader\n\nIf your board does not appear to have a bootloader at all, get the hex file for yo"
  },
  {
    "path": "docs/boards/GENERIC_UF2.md",
    "chars": 673,
    "preview": "# UF2 based boards\n\nFor these boards, find the right `.uf2` firmware in [in the latest release](https://github.com/dakhn"
  },
  {
    "path": "docs/boards/NORDIC_DFU.md",
    "chars": 671,
    "preview": "# Nordic DFU based boards\n\nFor these boards, find the right firmware in [in the latest release](https://github.com/dakhn"
  },
  {
    "path": "docs/boards/NRF52840_DONGLE.md",
    "chars": 661,
    "preview": "# nRF52840 Dongle\n\nThese boards come pre-flashed with the nordic bootloader.\nTo flash these, you need to plugin the dong"
  },
  {
    "path": "docs/boards/PRO_MICRO.md",
    "chars": 1888,
    "preview": "# Pro micro / Nice nano\n\n## Flashing\n\nThese boards come pre-flashed with a uf2 bootloader, so you just need to copy the "
  },
  {
    "path": "docs/boards/XIAO_BLE.md",
    "chars": 971,
    "preview": "# XIAO BLE\n\nOn these boards, you need to update the bootloader first.\nThus install `adafruit-nrfutil` using pip.\nThen, g"
  },
  {
    "path": "openocd/raspi-bcm2385.tcl",
    "chars": 190,
    "preview": "set WORKAREASIZE 0\n\nadapter driver bcm2835gpio\n\nbcm2835gpio peripheral_base 0xFE000000\n\nbcm2835gpio speed_coeffs 236181 "
  },
  {
    "path": "src/ble/helpers/ble_helpers.c",
    "chars": 3742,
    "preview": "#include \"ble_helpers.h\"\n#include \"ble.h\"\n#include \"app_error.h\"\n#include \"nrf_log.h\"\n#include \"feature_config.h\"\n\n\nret_"
  },
  {
    "path": "src/ble/helpers/ble_helpers.h",
    "chars": 790,
    "preview": "#include \"sdk_common.h\"\n#include \"stdint.h\"\n#include \"feature_config.h\"\n\n#if STATIC_PASSKEY_ENABLED == 1\n#define SET_MOD"
  },
  {
    "path": "src/ble/sensor_ble.c",
    "chars": 39046,
    "preview": "#include \"sensor_ble.h\"\n\n#include \"ble_configuration_service.h\"\n#include \"ble_cycling_speed_cadence.h\"\n#include \"ble_gpi"
  },
  {
    "path": "src/ble/sensor_ble.h",
    "chars": 1939,
    "preview": "#ifndef SENSOR_BLE_H\n#define SENSOR_BLE_H\n\n#include \"ble_conn_params.h\"\n#include \"ble_srv_common.h\"\n#include \"ble_advdat"
  },
  {
    "path": "src/ble/services/automation_io/ble_automation_io_service.c",
    "chars": 12961,
    "preview": "#include \"sdk_common.h\"\n#include \"ble_automation_io_service.h\"\n#include <string.h>\n#include \"ble_l2cap.h\"\n#include \"ble_"
  },
  {
    "path": "src/ble/services/automation_io/ble_automation_io_service.h",
    "chars": 1212,
    "preview": "#ifndef BLE_AIO_H\n#define BLE_AIO_H\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"ble.h\"\n#include \"ble_srv_common."
  },
  {
    "path": "src/ble/services/binary_sensor/ble_binary_sensor_service.c",
    "chars": 12764,
    "preview": "#include \"sdk_common.h\"\n#include \"ble_binary_sensor_service.h\"\n#include <string.h>\n#include \"ble_l2cap.h\"\n#include \"ble_"
  },
  {
    "path": "src/ble/services/binary_sensor/ble_binary_sensor_service.h",
    "chars": 4605,
    "preview": "#ifndef BLE_BSS\n#define BLE_BSS\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"ble.h\"\n#include \"ble_srv_common.h\"\n#"
  },
  {
    "path": "src/ble/services/ble_gpio_asm/ble_gpio_asm.c",
    "chars": 5672,
    "preview": "#include \"ble_gpio_asm.h\"\n#include \"ble_helpers.h\"\n#include \"encoding.h\"\n#include \"app_error.h\"\n#include \"ble_automation"
  },
  {
    "path": "src/ble/services/ble_gpio_asm/ble_gpio_asm.h",
    "chars": 428,
    "preview": "#include \"stdint.h\"\n#include \"sdk_common.h\"\n#include \"ble_srv_common.h\"\n#include \"sensor_gpio.h\"\n\n#define UUID_GPIO_ASM_"
  },
  {
    "path": "src/ble/services/configuration/ble_configuration_service.c",
    "chars": 13003,
    "preview": "#include \"ble_configuration_service.h\"\n#include \"app_error.h\"\n#include \"ble_helpers.h\"\n#include \"sensor_timer.h\"\n#includ"
  },
  {
    "path": "src/ble/services/configuration/ble_configuration_service.h",
    "chars": 1027,
    "preview": "#include \"stdint.h\"\n#include \"ble.h\"\n#include \"ble_srv_common.h\"\n#include \"sdk_common.h\"\n\n\n#define CUSTOM_UUID_BASE_CONF"
  },
  {
    "path": "src/ble/services/cycling_speed_cadence/ble_cycling_speed_cadence.c",
    "chars": 6951,
    "preview": "#include \"sdk_common.h\"\n#include \"ble_cycling_speed_cadence.h\"\n#include <string.h>\n#include \"ble_l2cap.h\"\n#include \"app_"
  },
  {
    "path": "src/ble/services/cycling_speed_cadence/ble_cycling_speed_cadence.h",
    "chars": 613,
    "preview": "#include \"ble_srv_common.h\"\n#include \"app_timer.h\"\n#include \"sensor_timer.h\"\n#include \"sensor_gpio.h\"\n\n#define UUID_CSC_"
  },
  {
    "path": "src/ble/services/hid/ble_hid.c",
    "chars": 10642,
    "preview": "#include \"ble_hid.h\"\n#include \"sdk_common.h\"\n#include <string.h>\n#include \"ble_l2cap.h\"\n#include \"app_error.h\"\n#include "
  },
  {
    "path": "src/ble/services/hid/ble_hid.h",
    "chars": 703,
    "preview": "#include \"ble_srv_common.h\"\n#include \"sensor_gpio.h\"\n\n#define BLE_UUID_OFFSET 0x0000\n\n#define BLE_UUID_HID_SERVICE      "
  },
  {
    "path": "src/ble/services/temperature/ble_temperature_service.c",
    "chars": 3658,
    "preview": "#include \"sdk_common.h\"\n#include \"ble_automation_io_service.h\"\n#include <string.h>\n#include \"ble_l2cap.h\"\n#include \"ble_"
  },
  {
    "path": "src/ble/services/temperature/ble_temperature_service.h",
    "chars": 477,
    "preview": "#ifndef ble_temperature_H\n#define ble_temperature_H\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"ble.h\"\n#include "
  },
  {
    "path": "src/config/.gitignore",
    "chars": 19,
    "preview": "feature_config.*.h\n"
  },
  {
    "path": "src/config/bsp/generated/96boards_96b_nitrogen.h",
    "chars": 431,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_29_MODE\n#define GPIO_CONFIGURATION_PIN_29_MODE 2\n#define GPIO_CONFIGURATION_PIN_29_INVERT"
  },
  {
    "path": "src/config/bsp/generated/aconno_acn52832.h",
    "chars": 390,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_22_MODE\n#define GPIO_CONFIGURATION_PIN_22_MODE 2\n#define GPIO_CONFIGURATION_PIN_22_INVERT"
  },
  {
    "path": "src/config/bsp/generated/adafruit_adafruit_feather_nrf52840.h",
    "chars": 301,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_42_MODE\n#define GPIO_CONFIGURATION_PIN_42_MODE 2\n#define GPIO_CONFIGURATION_PIN_42_INVERT"
  },
  {
    "path": "src/config/bsp/generated/adafruit_adafruit_itsybitsy.h",
    "chars": 298,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_6_MODE\n#define GPIO_CONFIGURATION_PIN_6_MODE 2\n#define GPIO_CONFIGURATION_PIN_6_INVERT 0\n"
  },
  {
    "path": "src/config/bsp/generated/adafruit_nrf52_adafruit_feather.h",
    "chars": 431,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_17_MODE\n#define GPIO_CONFIGURATION_PIN_17_MODE 2\n#define GPIO_CONFIGURATION_PIN_17_INVERT"
  },
  {
    "path": "src/config/bsp/generated/arduino_arduino_nano_33_ble.h",
    "chars": 647,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_24_MODE\n#define GPIO_CONFIGURATION_PIN_24_MODE 2\n#define GPIO_CONFIGURATION_PIN_24_INVERT"
  },
  {
    "path": "src/config/bsp/generated/arduino_arduino_nicla_sense_me.h",
    "chars": 171,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_21_MODE\n#define GPIO_CONFIGURATION_PIN_21_MODE 1\n#define GPIO_CONFIGURATION_PIN_21_INVERT"
  },
  {
    "path": "src/config/bsp/generated/atmarktechno_degu_evk.h",
    "chars": 1033,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_39_MODE\n#define GPIO_CONFIGURATION_PIN_39_MODE 2\n#define GPIO_CONFIGURATION_PIN_39_INVERT"
  },
  {
    "path": "src/config/bsp/generated/bbc_bbc_microbit.h",
    "chars": 342,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_17_MODE\n#define GPIO_CONFIGURATION_PIN_17_MODE 1\n#define GPIO_CONFIGURATION_PIN_17_INVERT"
  },
  {
    "path": "src/config/bsp/generated/bcdevices_blueclover_plt_demo_v2.h",
    "chars": 342,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_26_MODE\n#define GPIO_CONFIGURATION_PIN_26_MODE 1\n#define GPIO_CONFIGURATION_PIN_26_INVERT"
  },
  {
    "path": "src/config/bsp/generated/croxel_croxel_cx1825.h",
    "chars": 428,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_8_MODE\n#define GPIO_CONFIGURATION_PIN_8_MODE 2\n#define GPIO_CONFIGURATION_PIN_8_INVERT 1\n"
  },
  {
    "path": "src/config/bsp/generated/ebyte_ebyte_e73_tbb.h",
    "chars": 602,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_17_MODE\n#define GPIO_CONFIGURATION_PIN_17_MODE 2\n#define GPIO_CONFIGURATION_PIN_17_INVERT"
  },
  {
    "path": "src/config/bsp/generated/electronut_nrf52840_blip.h",
    "chars": 561,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_13_MODE\n#define GPIO_CONFIGURATION_PIN_13_MODE 2\n#define GPIO_CONFIGURATION_PIN_13_INVERT"
  },
  {
    "path": "src/config/bsp/generated/electronut_nrf52840_papyr.h",
    "chars": 561,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_13_MODE\n#define GPIO_CONFIGURATION_PIN_13_MODE 2\n#define GPIO_CONFIGURATION_PIN_13_INVERT"
  },
  {
    "path": "src/config/bsp/generated/ezurio_bl652_dvk.h",
    "chars": 602,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_17_MODE\n#define GPIO_CONFIGURATION_PIN_17_MODE 2\n#define GPIO_CONFIGURATION_PIN_17_INVERT"
  },
  {
    "path": "src/config/bsp/generated/ezurio_bl654_dvk.h",
    "chars": 1204,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_13_MODE\n#define GPIO_CONFIGURATION_PIN_13_MODE 2\n#define GPIO_CONFIGURATION_PIN_13_INVERT"
  },
  {
    "path": "src/config/bsp/generated/ezurio_bl654_sensor_board.h",
    "chars": 301,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_16_MODE\n#define GPIO_CONFIGURATION_PIN_16_MODE 2\n#define GPIO_CONFIGURATION_PIN_16_INVERT"
  },
  {
    "path": "src/config/bsp/generated/ezurio_bl654_usb.h",
    "chars": 130,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_13_MODE\n#define GPIO_CONFIGURATION_PIN_13_MODE 2\n#define GPIO_CONFIGURATION_PIN_13_INVERT"
  },
  {
    "path": "src/config/bsp/generated/ezurio_bt510.h",
    "chars": 598,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_22_MODE\n#define GPIO_CONFIGURATION_PIN_22_MODE 2\n#define GPIO_CONFIGURATION_PIN_22_INVERT"
  },
  {
    "path": "src/config/bsp/generated/ezurio_bt610.h",
    "chars": 602,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_39_MODE\n#define GPIO_CONFIGURATION_PIN_39_MODE 2\n#define GPIO_CONFIGURATION_PIN_39_INVERT"
  },
  {
    "path": "src/config/bsp/generated/ezurio_mg100.h",
    "chars": 557,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_39_MODE\n#define GPIO_CONFIGURATION_PIN_39_MODE 2\n#define GPIO_CONFIGURATION_PIN_39_INVERT"
  },
  {
    "path": "src/config/bsp/generated/ezurio_pinnacle_100_dvk.h",
    "chars": 1192,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_36_MODE\n#define GPIO_CONFIGURATION_PIN_36_MODE 2\n#define GPIO_CONFIGURATION_PIN_36_INVERT"
  },
  {
    "path": "src/config/bsp/generated/ezurio_rm1xx_dvk.h",
    "chars": 167,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_5_MODE\n#define GPIO_CONFIGURATION_PIN_5_MODE 1\n#define GPIO_CONFIGURATION_PIN_5_INVERT 1\n"
  },
  {
    "path": "src/config/bsp/generated/holyiot_holyiot_yj16019.h",
    "chars": 301,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_29_MODE\n#define GPIO_CONFIGURATION_PIN_29_MODE 2\n#define GPIO_CONFIGURATION_PIN_29_INVERT"
  },
  {
    "path": "src/config/bsp/generated/makerdiary_nrf52832_mdk.h",
    "chars": 561,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_22_MODE\n#define GPIO_CONFIGURATION_PIN_22_MODE 2\n#define GPIO_CONFIGURATION_PIN_22_INVERT"
  },
  {
    "path": "src/config/bsp/generated/makerdiary_nrf52840_mdk.h",
    "chars": 561,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_22_MODE\n#define GPIO_CONFIGURATION_PIN_22_MODE 2\n#define GPIO_CONFIGURATION_PIN_22_INVERT"
  },
  {
    "path": "src/config/bsp/generated/makerdiary_nrf52840_mdk_usb_dongle.h",
    "chars": 561,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_23_MODE\n#define GPIO_CONFIGURATION_PIN_23_MODE 2\n#define GPIO_CONFIGURATION_PIN_23_INVERT"
  },
  {
    "path": "src/config/bsp/generated/nordic_nrf21540dk.h",
    "chars": 1204,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_13_MODE\n#define GPIO_CONFIGURATION_PIN_13_MODE 2\n#define GPIO_CONFIGURATION_PIN_13_INVERT"
  },
  {
    "path": "src/config/bsp/generated/nordic_nrf51dk.h",
    "chars": 1204,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_21_MODE\n#define GPIO_CONFIGURATION_PIN_21_MODE 2\n#define GPIO_CONFIGURATION_PIN_21_INVERT"
  },
  {
    "path": "src/config/bsp/generated/nordic_nrf51dongle.h",
    "chars": 390,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_21_MODE\n#define GPIO_CONFIGURATION_PIN_21_MODE 2\n#define GPIO_CONFIGURATION_PIN_21_INVERT"
  },
  {
    "path": "src/config/bsp/generated/nordic_nrf52840dk.h",
    "chars": 1204,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_13_MODE\n#define GPIO_CONFIGURATION_PIN_13_MODE 2\n#define GPIO_CONFIGURATION_PIN_13_INVERT"
  },
  {
    "path": "src/config/bsp/generated/nordic_nrf52840dongle.h",
    "chars": 685,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_6_MODE\n#define GPIO_CONFIGURATION_PIN_6_MODE 2\n#define GPIO_CONFIGURATION_PIN_6_INVERT 1\n"
  },
  {
    "path": "src/config/bsp/generated/nordic_nrf52dk.h",
    "chars": 1204,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_17_MODE\n#define GPIO_CONFIGURATION_PIN_17_MODE 2\n#define GPIO_CONFIGURATION_PIN_17_INVERT"
  },
  {
    "path": "src/config/bsp/generated/nordic_nrf9160dk.h",
    "chars": 1176,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_2_MODE\n#define GPIO_CONFIGURATION_PIN_2_MODE 2\n#define GPIO_CONFIGURATION_PIN_2_INVERT 0\n"
  },
  {
    "path": "src/config/bsp/generated/nordic_thingy52.h",
    "chars": 171,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_11_MODE\n#define GPIO_CONFIGURATION_PIN_11_MODE 1\n#define GPIO_CONFIGURATION_PIN_11_INVERT"
  },
  {
    "path": "src/config/bsp/generated/other_ctcc.h",
    "chars": 260,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_23_MODE\n#define GPIO_CONFIGURATION_PIN_23_MODE 2\n#define GPIO_CONFIGURATION_PIN_23_INVERT"
  },
  {
    "path": "src/config/bsp/generated/others_promicro_nrf52840.h",
    "chars": 130,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_15_MODE\n#define GPIO_CONFIGURATION_PIN_15_MODE 2\n#define GPIO_CONFIGURATION_PIN_15_INVERT"
  },
  {
    "path": "src/config/bsp/generated/panasonic_pan1770_evb.h",
    "chars": 1204,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_13_MODE\n#define GPIO_CONFIGURATION_PIN_13_MODE 2\n#define GPIO_CONFIGURATION_PIN_13_INVERT"
  },
  {
    "path": "src/config/bsp/generated/panasonic_pan1780_evb.h",
    "chars": 1204,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_13_MODE\n#define GPIO_CONFIGURATION_PIN_13_MODE 2\n#define GPIO_CONFIGURATION_PIN_13_INVERT"
  },
  {
    "path": "src/config/bsp/generated/particle_nrf51_blenano.h",
    "chars": 130,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_19_MODE\n#define GPIO_CONFIGURATION_PIN_19_MODE 2\n#define GPIO_CONFIGURATION_PIN_19_INVERT"
  },
  {
    "path": "src/config/bsp/generated/particle_nrf52_blenano2.h",
    "chars": 130,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_11_MODE\n#define GPIO_CONFIGURATION_PIN_11_MODE 2\n#define GPIO_CONFIGURATION_PIN_11_INVERT"
  },
  {
    "path": "src/config/bsp/generated/phytec_reel_board.h",
    "chars": 130,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_13_MODE\n#define GPIO_CONFIGURATION_PIN_13_MODE 2\n#define GPIO_CONFIGURATION_PIN_13_INVERT"
  },
  {
    "path": "src/config/bsp/generated/pine64_pinetime_devkit0.h",
    "chars": 691,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_14_MODE\n#define GPIO_CONFIGURATION_PIN_14_MODE 2\n#define GPIO_CONFIGURATION_PIN_14_INVERT"
  },
  {
    "path": "src/config/bsp/generated/qorvo_decawave_dwm1001_dev.h",
    "chars": 687,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_14_MODE\n#define GPIO_CONFIGURATION_PIN_14_MODE 2\n#define GPIO_CONFIGURATION_PIN_14_INVERT"
  },
  {
    "path": "src/config/bsp/generated/rakwireless_rak4631.h",
    "chars": 260,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_36_MODE\n#define GPIO_CONFIGURATION_PIN_36_MODE 2\n#define GPIO_CONFIGURATION_PIN_36_INVERT"
  },
  {
    "path": "src/config/bsp/generated/rakwireless_rak5010.h",
    "chars": 130,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_12_MODE\n#define GPIO_CONFIGURATION_PIN_12_MODE 2\n#define GPIO_CONFIGURATION_PIN_12_INVERT"
  },
  {
    "path": "src/config/bsp/generated/raytac_raytac_mdbt50q_db_40.h",
    "chars": 1074,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_13_MODE\n#define GPIO_CONFIGURATION_PIN_13_MODE 2\n#define GPIO_CONFIGURATION_PIN_13_INVERT"
  },
  {
    "path": "src/config/bsp/generated/ruuvi_ruuvi_ruuvitag.h",
    "chars": 431,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_17_MODE\n#define GPIO_CONFIGURATION_PIN_17_MODE 2\n#define GPIO_CONFIGURATION_PIN_17_INVERT"
  },
  {
    "path": "src/config/bsp/generated/seeed_xiao_ble.h",
    "chars": 387,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_26_MODE\n#define GPIO_CONFIGURATION_PIN_26_MODE 2\n#define GPIO_CONFIGURATION_PIN_26_INVERT"
  },
  {
    "path": "src/config/bsp/generated/sparkfun_micromod.h",
    "chars": 130,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_13_MODE\n#define GPIO_CONFIGURATION_PIN_13_MODE 2\n#define GPIO_CONFIGURATION_PIN_13_INVERT"
  },
  {
    "path": "src/config/bsp/generated/sparkfun_nrf52_sparkfun.h",
    "chars": 294,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_7_MODE\n#define GPIO_CONFIGURATION_PIN_7_MODE 2\n#define GPIO_CONFIGURATION_PIN_7_INVERT 1\n"
  },
  {
    "path": "src/config/bsp/generated/u_blox_ubx_bmd300eval.h",
    "chars": 1204,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_17_MODE\n#define GPIO_CONFIGURATION_PIN_17_MODE 2\n#define GPIO_CONFIGURATION_PIN_17_INVERT"
  },
  {
    "path": "src/config/bsp/generated/u_blox_ubx_bmd340eval.h",
    "chars": 1204,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_13_MODE\n#define GPIO_CONFIGURATION_PIN_13_MODE 2\n#define GPIO_CONFIGURATION_PIN_13_INVERT"
  },
  {
    "path": "src/config/bsp/generated/u_blox_ubx_bmd345eval.h",
    "chars": 1204,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_13_MODE\n#define GPIO_CONFIGURATION_PIN_13_MODE 2\n#define GPIO_CONFIGURATION_PIN_13_INVERT"
  },
  {
    "path": "src/config/bsp/generated/u_blox_ubx_bmd380eval.h",
    "chars": 1204,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_13_MODE\n#define GPIO_CONFIGURATION_PIN_13_MODE 2\n#define GPIO_CONFIGURATION_PIN_13_INVERT"
  },
  {
    "path": "src/config/bsp/generated/u_blox_ubx_evkannab1.h",
    "chars": 732,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_27_MODE\n#define GPIO_CONFIGURATION_PIN_27_MODE 2\n#define GPIO_CONFIGURATION_PIN_27_INVERT"
  },
  {
    "path": "src/config/bsp/generated/u_blox_ubx_evkninab1.h",
    "chars": 729,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_8_MODE\n#define GPIO_CONFIGURATION_PIN_8_MODE 2\n#define GPIO_CONFIGURATION_PIN_8_INVERT 1\n"
  },
  {
    "path": "src/config/bsp/generated/u_blox_ubx_evkninab3.h",
    "chars": 728,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_13_MODE\n#define GPIO_CONFIGURATION_PIN_13_MODE 2\n#define GPIO_CONFIGURATION_PIN_13_INVERT"
  },
  {
    "path": "src/config/bsp/generated/vngiotlab_nrf51_vbluno51.h",
    "chars": 298,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_7_MODE\n#define GPIO_CONFIGURATION_PIN_7_MODE 2\n#define GPIO_CONFIGURATION_PIN_7_INVERT 0\n"
  },
  {
    "path": "src/config/bsp/generated/vngiotlab_nrf52_vbluno52.h",
    "chars": 301,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_12_MODE\n#define GPIO_CONFIGURATION_PIN_12_MODE 2\n#define GPIO_CONFIGURATION_PIN_12_INVERT"
  },
  {
    "path": "src/config/bsp/generated/waveshare_nrf51_ble400.h",
    "chars": 992,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_18_MODE\n#define GPIO_CONFIGURATION_PIN_18_MODE 2\n#define GPIO_CONFIGURATION_PIN_18_INVERT"
  },
  {
    "path": "src/config/bsp/generated/wurth_we_proteus2ev.h",
    "chars": 425,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_0_MODE\n#define GPIO_CONFIGURATION_PIN_0_MODE 2\n#define GPIO_CONFIGURATION_PIN_0_INVERT 0\n"
  },
  {
    "path": "src/config/bsp/generated/wurth_we_proteus3ev.h",
    "chars": 421,
    "preview": "#ifndef GPIO_CONFIGURATION_PIN_0_MODE\n#define GPIO_CONFIGURATION_PIN_0_MODE 2\n#define GPIO_CONFIGURATION_PIN_0_INVERT 0\n"
  },
  {
    "path": "src/config/feature_config.h",
    "chars": 93,
    "preview": "\n// write override from feature_config.template.h here\n\n#include \"feature_config.template.h\"\n"
  },
  {
    "path": "src/config/feature_config.template.h.jinja",
    "chars": 6403,
    "preview": "#ifndef FEATURE_CONFIG_H\n#define FEATURE_CONFIG_H\n\n#define APP_TIMER_PRESCALER 0\n\n#ifdef BLENKY_BSP_FILE\n#include BLENKY"
  },
  {
    "path": "src/config/nrf51/sdk_config.h",
    "chars": 79631,
    "preview": "\n\n#ifndef SDK_CONFIG_H\n#define SDK_CONFIG_H\n// <<< Use Configuration Wizard in Context Menu >>>\\n\n#ifdef USE_APP_CONFIG\n"
  },
  {
    "path": "src/config/nrf52/sdk_config.h",
    "chars": 306562,
    "preview": "/**\r\n * Copyright (c) 2017 - 2019, Nordic Semiconductor ASA\r\n *\r\n * All rights reserved.\r\n *\r\n * Redistribution and use "
  },
  {
    "path": "src/error_handler/error_handler.c",
    "chars": 1619,
    "preview": "\n#include \"nrf_log.h\"\n#include \"nrf.h\"\n#include \"app_error.h\"\n#include \"watchdog.h\"\n\n#ifdef NRF52\n#include \"nrf_strerror"
  },
  {
    "path": "src/gpio/sensor_gpio.c",
    "chars": 11785,
    "preview": "#include \"sensor_gpio.h\"\n#include \"pin_configuration.h\"\n#include \"app_timer.h\"\n#include \"sensor_timer.h\"\n#include \"app_p"
  },
  {
    "path": "src/gpio/sensor_gpio.h",
    "chars": 1395,
    "preview": "#ifndef SENSOR_GPIO_H\n#define SENSOR_GPIO_H\n\n#include \"nrf_drv_gpiote.h\"\n#include \"nrf_log.h\"\n#include \"pin_configuratio"
  },
  {
    "path": "src/helpers/encoding.c",
    "chars": 580,
    "preview": "#include \"encoding.h\"\n\n#include \"sdk_common.h\"\n\nuint8_t encoding_get_pin_bits(const uint8_t *pin_data, uint32_t pin_data"
  },
  {
    "path": "src/helpers/encoding.h",
    "chars": 186,
    "preview": "#include \"stdint.h\"\n\nuint8_t encoding_get_pin_bits(const uint8_t *pin_data, uint32_t pin_data_length, uint32_t pin_index"
  },
  {
    "path": "src/linker/nrf51822_qfaa.ld",
    "chars": 941,
    "preview": "/* Linker script to configure memory regions. */\n\nSEARCH_DIR(.)\nGROUP(-lgcc -lc -lnosys)\n\nMEMORY\n{\n  /* flash length is "
  },
  {
    "path": "src/linker/nrf51822_qfac.ld",
    "chars": 976,
    "preview": "/* Linker script to configure memory regions. */\n\nSEARCH_DIR(.)\nGROUP(-lgcc -lc -lnosys)\n\nMEMORY\n{\n  /* flash length is "
  },
  {
    "path": "src/linker/nrf52832_qfaa.ld",
    "chars": 3415,
    "preview": "/* Linker script to configure memory regions. */\n\nSEARCH_DIR(.)\nGROUP(-lgcc -lc -lnosys)\n\nMEMORY\n{\n  /* flash length is "
  },
  {
    "path": "src/linker/nrf52840_qfaa.ld",
    "chars": 3416,
    "preview": "/* Linker script to configure memory regions. */\n\nSEARCH_DIR(.)\nGROUP(-lgcc -lc -lnosys)\n\nMEMORY\n{\n  /* flash length is "
  },
  {
    "path": "src/main.c",
    "chars": 1492,
    "preview": "#if FAMILY == 51\n#define NRF_LOG_MODULE_NAME \"APP\"\n#else\n#define NRF_LOG_MODULE_NAME APP\n#endif\n\n#include <stdint.h>\n#in"
  },
  {
    "path": "src/persistence/pin_configuration.c",
    "chars": 3788,
    "preview": "#include \"pin_configuration.h\"\n#include \"nrf_log.h\"\n#include \"storage.h\"\n\nuint32_t pin_count_output_digital = 0;\nuint32_"
  },
  {
    "path": "src/persistence/pin_configuration.h",
    "chars": 726,
    "preview": "#include \"stdint.h\"\n\ntypedef void (*pin_output_analog_handler_t)(uint32_t index, uint32_t pin, uint8_t invert);\ntypedef "
  },
  {
    "path": "src/sleep/sleep.c",
    "chars": 2305,
    "preview": "#include \"feature_config.h\"\n#if FEATURE_ENABLED(SLEEP_MODE)\n\n#include \"sleep.h\"\n#include \"app_timer.h\"\n#include \"nrf_log"
  },
  {
    "path": "src/sleep/sleep.h",
    "chars": 272,
    "preview": "#include \"sensor_gpio.h\"\n#include \"ble_srv_common.h\"\n\n#define SLEEP_MODE_SYSTEM_ON  0x00\n#define SLEEP_MODE_SYSTEM_OFF 0"
  },
  {
    "path": "src/storage/preconfiguration.c",
    "chars": 3847,
    "preview": "#include <stdint.h>\n#include <stdbool.h>\n#include <feature_config.h>\n#include <nrf_log.h>\n\n/*\nThis is awful.\nI am sorry "
  },
  {
    "path": "src/storage/preconfiguration.h",
    "chars": 42,
    "preview": "void preconfiguration_load(uint8_t *data);"
  },
  {
    "path": "src/storage/storage.h",
    "chars": 1234,
    "preview": "#if FAMILY == 51\n#include \"fstorage.h\"\n#else\n#include \"nrf_fstorage.h\"\n#endif\n#include \"nrf_log.h\"\n\n#define PIN_CONFIGUR"
  },
  {
    "path": "src/storage/storage.nrf51.c",
    "chars": 6580,
    "preview": "#include \"storage.h\"\n#include \"app_timer.h\"\n#include \"crc32.h\"\n#include \"nrf_delay.h\"\n#include \"feature_config.h\"\n#inclu"
  },
  {
    "path": "src/storage/storage.nrf52.c",
    "chars": 6038,
    "preview": "#include \"storage.h\"\n#include \"app_timer.h\"\n#include \"crc32.h\"\n#include \"nrf_delay.h\"\n#include \"feature_config.h\"\n#inclu"
  },
  {
    "path": "src/timer/sensor_timer.c",
    "chars": 2907,
    "preview": "#include \"sensor_timer.h\"\n#include \"nrf_log.h\"\n#include \"feature_config.h\"\n\n#define DEBOUNCE_TIMEOUT APP_TIMER_TICKS_COM"
  },
  {
    "path": "src/timer/sensor_timer.h",
    "chars": 905,
    "preview": "#ifndef SENSOR_TIMER_H\n#define SENSOR_TIMER_H\n\n#include \"app_timer.h\"\n#ifdef NRF51\n#include \"app_timer_appsh.h\"\n#endif\n#"
  },
  {
    "path": "src/watchdog/watchdog.c",
    "chars": 1721,
    "preview": "#include <nrf_drv_wdt.h>\n#include <app_error.h>\n#include <nrf_log.h>\n#include <nrf_nvic.h>\n#include <nrf_soc.h>\n#include"
  },
  {
    "path": "src/watchdog/watchdog.h",
    "chars": 44,
    "preview": "void watchdog_init();\n\nvoid watchdog_feed();"
  },
  {
    "path": "zephyr_convert.py",
    "chars": 5278,
    "preview": "import sys\nimport yaml\nimport os\nimport os.path\nimport re\n\ndef main():\n    boards_path = sys.argv[1]\n\n    gpio_expressio"
  }
]

About this extraction

This page contains the full source code of the dakhnod/BLEnky GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 137 files (678.0 KB), approximately 196.5k tokens, and a symbol index with 276 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!