diff --git a/.github/workflows/build_docker.yml b/.github/workflows/build_docker.yml new file mode 100644 index 000000000..bb5a394fd --- /dev/null +++ b/.github/workflows/build_docker.yml @@ -0,0 +1,72 @@ +name: Build Docker + +on: workflow_call + +permissions: + contents: write + packages: write + +jobs: + build-native: + runs-on: ubuntu-latest + steps: + - name: Install libs needed for native build + shell: bash + run: | + sudo apt-get update --fix-missing + sudo apt-get install -y libbluetooth-dev libgpiod-dev libyaml-cpp-dev openssl libssl-dev libulfius-dev liborcania-dev libusb-1.0-0-dev + + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: recursive + ref: ${{github.event.pull_request.head.ref}} + repository: ${{github.event.pull_request.head.repo.full_name}} + + - name: Upgrade python tools + shell: bash + run: | + python -m pip install --upgrade pip + pip install -U platformio adafruit-nrfutil + pip install -U meshtastic --pre + + - name: Upgrade platformio + shell: bash + run: | + pio upgrade + + - name: Build Native + run: bin/build-native.sh + + - name: Get release version string + run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT + id: version + + - name: Docker login + if: ${{ github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }} + uses: docker/login-action@v3 + with: + username: meshtastic + password: ${{ secrets.DOCKER_FIRMWARE_TOKEN }} + + - name: Docker setup + if: ${{ github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }} + uses: docker/setup-buildx-action@v3 + + - name: Docker build and push tagged versions + if: ${{ github.event_name == 'workflow_dispatch' }} + uses: docker/build-push-action@v6 + with: + context: . + file: ./Dockerfile + push: true + tags: meshtastic/meshtasticd:${{ steps.version.outputs.version }} + + - name: Docker build and push + if: ${{ github.ref == 'refs/heads/master' && github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }} + uses: docker/build-push-action@v6 + with: + context: . + file: ./Dockerfile + push: true + tags: meshtastic/meshtasticd:latest diff --git a/.github/workflows/build_native.yml b/.github/workflows/build_native.yml index d4b0c8d58..a57da5dfb 100644 --- a/.github/workflows/build_native.yml +++ b/.github/workflows/build_native.yml @@ -50,36 +50,3 @@ jobs: path: | release/meshtasticd_linux_x86_64 bin/config-dist.yaml - - - name: Docker login - if: ${{ github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }} - uses: docker/login-action@v3 - continue-on-error: true # FIXME: Failing docker login auth - with: - username: meshtastic - password: ${{ secrets.DOCKER_FIRMWARE_TOKEN }} - - - name: Docker setup - if: ${{ github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }} - continue-on-error: true # FIXME: Failing docker login auth - uses: docker/setup-buildx-action@v3 - - - name: Docker build and push tagged versions - if: ${{ github.event_name == 'workflow_dispatch' }} - continue-on-error: true # FIXME: Failing docker login auth - uses: docker/build-push-action@v6 - with: - context: . - file: ./Dockerfile - push: true - tags: meshtastic/device-simulator:${{ steps.version.outputs.version }} - - - name: Docker build and push - if: ${{ github.ref == 'refs/heads/master' && github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }} - continue-on-error: true # FIXME: Failing docker login auth - uses: docker/build-push-action@v6 - with: - context: . - file: ./Dockerfile - push: true - tags: meshtastic/device-simulator:latest diff --git a/.github/workflows/main_matrix.yml b/.github/workflows/main_matrix.yml index 37164b758..0109bef1a 100644 --- a/.github/workflows/main_matrix.yml +++ b/.github/workflows/main_matrix.yml @@ -37,7 +37,7 @@ jobs: else TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}} quick) fi - echo "Name: ${{ github.ref_name }} Base: ${{ github.base_ref }} Head: ${{ github.head_ref }} Ref: ${{ github.ref }} Targets: $TARGETS" + echo "Name: ${{ github.ref_name }} Base: ${{ github.base_ref }} } Ref: ${{ github.ref }} Targets: $TARGETS" echo "${{matrix.arch}}=$(jq -cn --argjson environments "$TARGETS" '{board: $environments}')" >> $GITHUB_OUTPUT outputs: esp32: ${{ steps.jsonStep.outputs.esp32 }} @@ -137,6 +137,11 @@ jobs: package-native: uses: ./.github/workflows/package_amd64.yml + build-docker: + if: ${{ github.event_name == 'workflow_dispatch' }} + uses: ./.github/workflows/build_docker.yml + secrets: inherit + after-checks: runs-on: ubuntu-latest if: ${{ github.event_name != 'workflow_dispatch' }} diff --git a/.github/workflows/update_protobufs.yml b/.github/workflows/update_protobufs.yml index f1c92b860..2732ab760 100644 --- a/.github/workflows/update_protobufs.yml +++ b/.github/workflows/update_protobufs.yml @@ -17,9 +17,9 @@ jobs: - name: Download nanopb run: | - wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.9-linux-x86.tar.gz - tar xvzf nanopb-0.4.9-linux-x86.tar.gz - mv nanopb-0.4.9-linux-x86 nanopb-0.4.9 + wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.9.1-linux-x86.tar.gz + tar xvzf nanopb-0.4.9.1-linux-x86.tar.gz + mv nanopb-0.4.9.1-linux-x86 nanopb-0.4.9 - name: Re-generate protocol buffers run: | diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml index 743f4214d..f2393592c 100644 --- a/.trunk/trunk.yaml +++ b/.trunk/trunk.yaml @@ -4,31 +4,32 @@ cli: plugins: sources: - id: trunk - ref: v1.6.4 + ref: v1.6.6 uri: https://github.com/trunk-io/plugins lint: enabled: - - trufflehog@3.83.6 + - prettier@3.4.2 + - trufflehog@3.86.1 - yamllint@1.35.1 - - bandit@1.7.10 - - checkov@3.2.287 + - bandit@1.8.0 + - checkov@3.2.334 - terrascan@1.19.9 - - trivy@0.56.2 + - trivy@0.58.0 #- trufflehog@3.63.2-rc0 - taplo@0.9.3 - - ruff@0.7.3 + - ruff@0.8.3 - isort@5.13.2 - - markdownlint@0.42.0 - - oxipng@9.1.2 + - markdownlint@0.43.0 + - oxipng@9.1.3 - svgo@3.3.2 - actionlint@1.7.4 - flake8@7.1.1 - - hadolint@2.12.0 + - hadolint@2.12.1-beta - shfmt@3.6.0 - shellcheck@0.10.0 - black@24.10.0 - git-diff-check - - gitleaks@8.21.1 + - gitleaks@8.21.2 - clang-format@16.0.3 #- prettier@3.3.3 ignore: @@ -39,11 +40,11 @@ runtimes: enabled: - python@3.10.8 - go@1.21.0 - - node@18.12.1 + - node@18.20.5 actions: disabled: - trunk-announce enabled: - trunk-fmt-pre-commit - trunk-check-pre-push - - trunk-upgrade-available \ No newline at end of file + - trunk-upgrade-available diff --git a/Dockerfile b/Dockerfile index fc34fbd4c..ca216e04b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,7 +14,7 @@ USER root # trunk-ignore(hadolint/DL3008): Use latest version of packages for buildchain RUN apt-get update && apt-get install --no-install-recommends -y wget python3 python3-pip python3-wheel python3-venv g++ zip git \ ca-certificates libgpiod-dev libyaml-cpp-dev libbluetooth-dev \ - libulfius-dev liborcania-dev libssl-dev pkg-config && \ + libusb-1.0-0-dev libulfius-dev liborcania-dev libssl-dev pkg-config && \ apt-get clean && rm -rf /var/lib/apt/lists/* && mkdir /tmp/firmware RUN groupadd -g 1000 mesh && useradd -ml -u 1000 -g 1000 mesh && chown mesh:mesh /tmp/firmware @@ -37,7 +37,7 @@ ENV TZ=Etc/UTC # trunk-ignore(terrascan/AC_DOCKER_0002): Known terrascan issue # trunk-ignore(hadolint/DL3008): Use latest version of packages for buildchain -RUN apt-get update && apt-get --no-install-recommends -y install libc-bin libc6 libgpiod2 libyaml-cpp0.7 libulfius2.7 liborcania2.3 libssl3 && \ +RUN apt-get update && apt-get --no-install-recommends -y install libc-bin libc6 libgpiod2 libyaml-cpp0.7 libulfius2.7 libusb-1.0-0-dev liborcania2.3 libssl3 && \ apt-get clean && rm -rf /var/lib/apt/lists/* RUN groupadd -g 1000 mesh && useradd -ml -u 1000 -g 1000 mesh @@ -51,4 +51,4 @@ VOLUME /home/mesh/data CMD [ "sh", "-cx", "./meshtasticd -d /home/mesh/data --hwid=${HWID:-$RANDOM}" ] -HEALTHCHECK NONE +HEALTHCHECK NONE \ No newline at end of file diff --git a/arch/nrf52/nrf52.ini b/arch/nrf52/nrf52.ini index 5e6d9a6b5..4791e6c8f 100644 --- a/arch/nrf52/nrf52.ini +++ b/arch/nrf52/nrf52.ini @@ -1,6 +1,6 @@ [nrf52_base] ; Instead of the standard nordicnrf52 platform, we use our fork which has our added variant files -platform = platformio/nordicnrf52@^10.6.0 +platform = platformio/nordicnrf52@^10.7.0 extends = arduino_base platform_packages = ; our custom Git version until they merge our PR diff --git a/arch/portduino/portduino.ini b/arch/portduino/portduino.ini index 6c6242bb0..713855739 100644 --- a/arch/portduino/portduino.ini +++ b/arch/portduino/portduino.ini @@ -26,6 +26,7 @@ lib_deps = ${radiolib_base.lib_deps} rweather/Crypto@^0.4.0 https://github.com/lovyan03/LovyanGFX.git#1401c28a47646fe00538d487adcb2eb3c72de805 + https://github.com/pine64/libch341-spi-userspace#8695637adeabf5abf5601d8e82cb0ba19ce9ec46 build_flags = ${arduino_base.build_flags} @@ -33,7 +34,9 @@ build_flags = -Isrc/platform/portduino -DRADIOLIB_EEPROM_UNSUPPORTED -DPORTDUINO_LINUX_HARDWARE + -lpthread -lstdc++fs -lbluetooth -lgpiod -lyaml-cpp + -std=c++17 \ No newline at end of file diff --git a/bin/config-dist.yaml b/bin/config-dist.yaml index ec262536f..49de1675b 100644 --- a/bin/config-dist.yaml +++ b/bin/config-dist.yaml @@ -12,13 +12,6 @@ Lora: # IRQ: 17 # Reset: 22 -# Module: sx1262 # pinedio -# CS: 0 -# IRQ: 10 -# Busy: 11 -# DIO2_AS_RF_SWITCH: true -# spidev: spidev0.1 - # Module: RF95 # Adafruit RFM9x # Reset: 25 # CS: 7 @@ -50,8 +43,6 @@ Lora: # TXen: x # TX and RX enable pins # RXen: x -# ch341_quirk: true # Uncomment this to use the chunked SPI transfer that seems to fix the ch341 - # spiSpeed: 2000000 ### Set gpio chip to use in /dev/. Defaults to 0. diff --git a/bin/config.d/OpenWRT-One-mikroBUS-LR-IOT-CLICK.yaml b/bin/config.d/OpenWRT-One-mikroBUS-LR-IOT-CLICK.yaml new file mode 100644 index 000000000..ca5b27ebc --- /dev/null +++ b/bin/config.d/OpenWRT-One-mikroBUS-LR-IOT-CLICK.yaml @@ -0,0 +1,9 @@ +## https://www.mikroe.com/lr-iot-click +Lora: + Module: lr1110 # OpenWRT ONE mikroBUS with LR-IOT-CLICK +# CS: 25 + IRQ: 10 + Busy: 12 +# Reset: 2 + spidev: spidev2.0 + DIO3_TCXO_VOLTAGE: 1.6 diff --git a/bin/config.d/femtofox/femtofox_EByte-E22-900M30S_Ebyte-E22-900M22S.yaml b/bin/config.d/femtofox/femtofox_EByte-E22-900M30S_Ebyte-E22-900M22S.yaml deleted file mode 100644 index 6c88b1eb2..000000000 --- a/bin/config.d/femtofox/femtofox_EByte-E22-900M30S_Ebyte-E22-900M22S.yaml +++ /dev/null @@ -1,16 +0,0 @@ ---- -Lora: -## Ebyte E22-900M30S, E22-900M22S with no external RF switching setup -## Will work with any module without RF switching, and with TCXO - Module: sx1262 - gpiochip: 1 # subtract 32 from the gpio numbers - DIO2_AS_RF_SWITCH: true - DIO3_TCXO_VOLTAGE: true - CS: 16 #pin6 / GPIO48 1C0 - IRQ: 23 #pin17 / GPIO55 1C7 - Busy: 22 #pin16 / GPIO54 1C6 - Reset: 25 #pin13 / GPIO57 1D1 - RXen: 24 #pin12 / GPIO56 1D0 - #TXen: bridge to DIO2 on E22 module - spidev: spidev0.0 - spiSpeed: 2000000 \ No newline at end of file diff --git a/bin/config.d/femtofox/femtofox_EByte-E22-900MM22S.yaml b/bin/config.d/femtofox/femtofox_EByte-E22-900MM22S.yaml deleted file mode 100644 index 451d5d3f4..000000000 --- a/bin/config.d/femtofox/femtofox_EByte-E22-900MM22S.yaml +++ /dev/null @@ -1,16 +0,0 @@ ---- -Lora: -## Ebyte E22-900MM22S with no external RF switching setup -## Will work with any module without RF switching and no TCXO - Module: sx1262 - gpiochip: 1 # subtract 32 from the gpio numbers - DIO2_AS_RF_SWITCH: true - DIO3_TCXO_VOLTAGE: true - CS: 16 #pin6 / GPIO48 1C0 - IRQ: 23 #pin17 / GPIO55 1C7 - Busy: 22 #pin16 / GPIO54 1C6 - Reset: 25 #pin13 / GPIO57 1D1 - RXen: 24 #pin12 / GPIO56 1D0 - #TXen: bridge to DIO2 on E22 module - spidev: spidev0.0 - spiSpeed: 2000000 \ No newline at end of file diff --git a/bin/config.d/femtofox/femtofox_Heltec-HT-RA62_Seeed-WIO-SX1262.yaml b/bin/config.d/femtofox/femtofox_Heltec-HT-RA62_Seeed-WIO-SX1262.yaml deleted file mode 100644 index d5f02b42c..000000000 --- a/bin/config.d/femtofox/femtofox_Heltec-HT-RA62_Seeed-WIO-SX1262.yaml +++ /dev/null @@ -1,14 +0,0 @@ ---- -Lora: -## Heltec HT-RA62, Seeed WIO SX1262 -## Will work with any module with automatic RF switching, and with TCXO - Module: sx1262 - gpiochip: 1 # subtract 32 from the gpio numbers - DIO2_AS_RF_SWITCH: true - DIO3_TCXO_VOLTAGE: true - CS: 16 #pin6 (GPIO pin 48 1C0) - IRQ: 23 #pin17 (GPIO pin 55 1C7) - Reset: 25 #pin13 (GPIO pin 57 1D1) - Busy: 22 #pin16 (GPIO pin 54 1C6) - spidev: spidev0.0 #pins are (CS=6, CLK=7, MOSI=8, MISO=9) - spiSpeed: 2000000 \ No newline at end of file diff --git a/bin/config.d/femtofox/femtofox_LR1121_TCXO.yaml b/bin/config.d/femtofox/femtofox_LR1121_TCXO.yaml new file mode 100644 index 000000000..7aa860f61 --- /dev/null +++ b/bin/config.d/femtofox/femtofox_LR1121_TCXO.yaml @@ -0,0 +1,20 @@ +--- +Lora: +## Ebyte E80-900M22S +## This is a bit experimental +## +## + Module: lr1121 + gpiochip: 1 # subtract 32 from the gpio numbers + DIO3_TCXO_VOLTAGE: 1.8 + CS: 16 #pin6 / GPIO48 1C0 + IRQ: 23 #pin17 / GPIO55 1C7 + Busy: 22 #pin16 / GPIO54 1C6 + Reset: 25 #pin13 / GPIO57 1D1 + + + spidev: spidev0.0 #pins are (CS=16, CLK=17, MOSI=18, MISO=19) + spiSpeed: 2000000 + +General: + MACAddressSource: eth0 diff --git a/bin/config.d/femtofox/femtofox_SX1262_TCXO.yaml b/bin/config.d/femtofox/femtofox_SX1262_TCXO.yaml new file mode 100644 index 000000000..a4dec870a --- /dev/null +++ b/bin/config.d/femtofox/femtofox_SX1262_TCXO.yaml @@ -0,0 +1,21 @@ +--- +Lora: +## Ebyte E22-900M30S, E22-900M22S with or without external RF switching setup +## HT-RA62 (Has internal switching, but whatever) +## Seeed WIO SX1262 (already has TXEN-DIO2 link, but needs RXEN) +## Will work with any module with or without RF switching, and with TCXO + Module: sx1262 + gpiochip: 1 # subtract 32 from the gpio numbers + DIO2_AS_RF_SWITCH: true + DIO3_TCXO_VOLTAGE: true + CS: 16 #pin6 / GPIO48 1C0 + IRQ: 23 #pin17 / GPIO55 1C7 + Busy: 22 #pin16 / GPIO54 1C6 + Reset: 25 #pin13 / GPIO57 1D1 + RXen: 24 #pin12 / GPIO56 1D0 # Not strictly needed for auto-switching, but why complicate things? +# TXen: bridge to DIO2 on E22 module + spidev: spidev0.0 #pins are (CS=16, CLK=17, MOSI=18, MISO=19) + spiSpeed: 2000000 + +General: + MACAddressSource: eth0 diff --git a/bin/config.d/femtofox/femtofox_SX1262_XTAL.yaml b/bin/config.d/femtofox/femtofox_SX1262_XTAL.yaml new file mode 100644 index 000000000..6b956f3e3 --- /dev/null +++ b/bin/config.d/femtofox/femtofox_SX1262_XTAL.yaml @@ -0,0 +1,21 @@ +--- +Lora: +## Ebyte E22-900MM22S with no external RF switching setup +## Waveshare SX126X XXXM, AI Thinker RA-01SH +## Will work with any module with or without RF switching and no TCXO + + Module: sx1262 + gpiochip: 1 # subtract 32 from the gpio numbers + DIO2_AS_RF_SWITCH: true + DIO3_TCXO_VOLTAGE: false + CS: 16 #pin6 / GPIO48 1C0 + IRQ: 23 #pin17 / GPIO55 1C7 + Busy: 22 #pin16 / GPIO54 1C6 + Reset: 25 #pin13 / GPIO57 1D1 + RXen: 24 #pin12 / GPIO56 1D0 # Not strictly needed for auto-switching, but why complicate things? +# TXen: bridge to DIO2 on E22 module + spidev: spidev0.0 #pins are (CS=16, CLK=17, MOSI=18, MISO=19) + spiSpeed: 2000000 + +General: + MACAddressSource: eth0 diff --git a/bin/config.d/femtofox/femtofox_Waveshare-SX126X-XXXM_AI-Thinker-RA-01SH.yaml b/bin/config.d/femtofox/femtofox_Waveshare-SX126X-XXXM_AI-Thinker-RA-01SH.yaml deleted file mode 100644 index 23834adec..000000000 --- a/bin/config.d/femtofox/femtofox_Waveshare-SX126X-XXXM_AI-Thinker-RA-01SH.yaml +++ /dev/null @@ -1,13 +0,0 @@ ---- -Lora: -## Waveshare SX126X XXXM, AI Thinker RA-01SH -## Will work with any module with automatic RF switching, and with no TCXO - Module: sx1262 - gpiochip: 1 # subtract 32 from the gpio numbers - DIO2_AS_RF_SWITCH: true - CS: 16 #pin6 (GPIO pin 48 1C0) - IRQ: 23 #pin17 (GPIO pin 55 1C7) - Reset: 25 #pin13 (GPIO pin 57 1D1) - Busy: 22 #pin16 (GPIO pin 54 1C6) - spidev: spidev0.0 #pins are (CS=6, CLK=7, MOSI=8, MISO=9) - spiSpeed: 2000000 diff --git a/bin/config.d/lora-meshstick-1262.yaml b/bin/config.d/lora-meshstick-1262.yaml new file mode 100644 index 000000000..3f8d6c617 --- /dev/null +++ b/bin/config.d/lora-meshstick-1262.yaml @@ -0,0 +1,11 @@ +Lora: + Module: sx1262 + CS: 0 + IRQ: 6 + Reset: 2 + Busy: 4 + spidev: ch341 + DIO3_TCXO_VOLTAGE: true +# USB_Serialnum: 12345678 + USB_PID: 0x5512 + USB_VID: 0x1A86 diff --git a/bin/config.d/lora-pinedio-usb-sx1262.yaml b/bin/config.d/lora-pinedio-usb-sx1262.yaml new file mode 100644 index 000000000..6b8a9fc95 --- /dev/null +++ b/bin/config.d/lora-pinedio-usb-sx1262.yaml @@ -0,0 +1,5 @@ +Lora: + Module: sx1262 + CS: 0 + IRQ: 10 + spidev: ch341 \ No newline at end of file diff --git a/boards/esp32-s3-zero.json b/boards/esp32-s3-zero.json new file mode 100644 index 000000000..76cb34fa0 --- /dev/null +++ b/boards/esp32-s3-zero.json @@ -0,0 +1,41 @@ +{ + "build": { + "arduino": { + "partitions": "default.csv", + "memory_type": "qio_qspi" + }, + "core": "esp32", + "extra_flags": [ + "-DARDUINO_ESP32S3_DEV", + "-DARDUINO_RUNNING_CORE=1", + "-DARDUINO_EVENT_RUNNING_CORE=1", + "-DARDUINO_USB_CDC_ON_BOOT=1", + "-DBOARD_HAS_PSRAM" + ], + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "qio", + "psram_type": "qio", + "hwids": [["0x303A", "0x1001"]], + "mcu": "esp32s3", + "variant": "esp32s3" + }, + "connectivity": ["wifi", "bluetooth"], + "debug": { + "default_tool": "esp-builtin", + "onboard_tools": ["esp-builtin"], + "openocd_target": "esp32s3.cfg" + }, + "frameworks": ["arduino", "espidf"], + "platforms": ["espressif32"], + "name": "Espressif ESP32-S3-FH4R2 (4 MB QD, 2MB PSRAM)", + "upload": { + "flash_size": "4MB", + "maximum_ram_size": 327680, + "maximum_size": 4194304, + "require_upload_port": true, + "speed": 921600 + }, + "url": "https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/hw-reference/esp32s3/user-guide-devkitc-1.html", + "vendor": "Espressif" +} diff --git a/boards/t-echo.json b/boards/t-echo.json index fcfc8c50b..f891da94f 100644 --- a/boards/t-echo.json +++ b/boards/t-echo.json @@ -48,6 +48,6 @@ "require_upload_port": true, "wait_for_upload_port": true }, - "url": "FIXME", - "vendor": "TTGO" + "url": "https://lilygo.cc/products/t-echo-lilygo", + "vendor": "LILYGO" } diff --git a/platformio.ini b/platformio.ini index cc08b33a0..41f1ca764 100644 --- a/platformio.ini +++ b/platformio.ini @@ -16,6 +16,7 @@ default_envs = tbeam ;default_envs = tlora-v2 ;default_envs = tlora-v2-1-1_6 ;default_envs = tlora-v2-1-1_6-tcxo +;default_envs = tlora-v3-3-0-tcxo ;default_envs = tlora-t3s3-v1 ;default_envs = t-echo ;default_envs = canaryone @@ -159,4 +160,5 @@ lib_deps = https://github.com/KodinLanewave/INA3221@1.0.1 mprograms/QMC5883LCompass@1.2.3 dfrobot/DFRobot_RTU@1.0.3 - https://github.com/meshtastic/DFRobot_LarkWeatherStation#4de3a9cadef0f6a5220a8a906cf9775b02b0040d \ No newline at end of file + https://github.com/meshtastic/DFRobot_LarkWeatherStation#4de3a9cadef0f6a5220a8a906cf9775b02b0040d + robtillaart/INA226@0.6.0 diff --git a/protobufs b/protobufs index 00c9c9932..2cffaf53e 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 00c9c9932ea50c14cdc44d497d2672a0031641ce +Subproject commit 2cffaf53e3faf1b6e41a8b8f05312f2f893be413 diff --git a/src/Power.cpp b/src/Power.cpp index a354b74e2..ae0908ec6 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -72,8 +72,9 @@ static const uint8_t ext_chrg_detect_value = EXT_CHRG_DETECT_VALUE; #endif #if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(ARCH_PORTDUINO) -INA260Sensor ina260Sensor; INA219Sensor ina219Sensor; +INA226Sensor ina226Sensor; +INA260Sensor ina260Sensor; INA3221Sensor ina3221Sensor; #endif @@ -413,7 +414,20 @@ class AnalogBatteryLevel : public HasBatteryLevel #ifdef EXT_CHRG_DETECT return digitalRead(EXT_CHRG_DETECT) == ext_chrg_detect_value; #else +#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && \ + !defined(DISABLE_INA_CHARGING_DETECTION) + if (hasINA()) { + // get current flow from INA sensor - negative value means power flowing into the battery + // default assuming BATTERY+ <--> INA_VIN+ <--> SHUNT RESISTOR <--> INA_VIN- <--> LOAD + LOG_DEBUG("Using INA on I2C addr 0x%x for charging detection", config.power.device_battery_ina_address); +#if defined(INA_CHARGING_DETECTION_INVERT) + return getINACurrent() > 0; +#else + return getINACurrent() < 0; +#endif + } return isBatteryConnect() && isVbusIn(); +#endif #endif } @@ -450,6 +464,9 @@ class AnalogBatteryLevel : public HasBatteryLevel { if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA219].first == config.power.device_battery_ina_address) { return ina219Sensor.getBusVoltageMv(); + } else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA226].first == + config.power.device_battery_ina_address) { + return ina226Sensor.getBusVoltageMv(); } else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA260].first == config.power.device_battery_ina_address) { return ina260Sensor.getBusVoltageMv(); @@ -460,6 +477,20 @@ class AnalogBatteryLevel : public HasBatteryLevel return 0; } + int16_t getINACurrent() + { + if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA219].first == config.power.device_battery_ina_address) { + return ina219Sensor.getCurrentMa(); + } else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA226].first == + config.power.device_battery_ina_address) { + return ina226Sensor.getCurrentMa(); + } else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA3221].first == + config.power.device_battery_ina_address) { + return ina3221Sensor.getCurrentMa(); + } + return 0; + } + bool hasINA() { if (!config.power.device_battery_ina_address) { @@ -469,6 +500,10 @@ class AnalogBatteryLevel : public HasBatteryLevel if (!ina219Sensor.isInitialized()) return ina219Sensor.runOnce() > 0; return ina219Sensor.isRunning(); + } else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA226].first == + config.power.device_battery_ina_address) { + if (!ina226Sensor.isInitialized()) + return ina226Sensor.runOnce() > 0; } else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA260].first == config.power.device_battery_ina_address) { if (!ina260Sensor.isInitialized()) @@ -1154,4 +1189,4 @@ bool Power::lipoInit() { return false; } -#endif \ No newline at end of file +#endif diff --git a/src/detect/ScanI2C.h b/src/detect/ScanI2C.h index 7fe3aac89..2473a6573 100644 --- a/src/detect/ScanI2C.h +++ b/src/detect/ScanI2C.h @@ -63,7 +63,8 @@ class ScanI2C MAX30102, TPS65233, MPR121KB, - CGRADSENS + CGRADSENS, + INA226 } DeviceType; // typedef uint8_t DeviceAddress; @@ -127,4 +128,4 @@ class ScanI2C private: bool shouldSuppressScreen = false; -}; \ No newline at end of file +}; diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp index ddcad9fd1..d84b3c838 100644 --- a/src/detect/ScanI2CTwoWire.cpp +++ b/src/detect/ScanI2CTwoWire.cpp @@ -250,8 +250,16 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xFE), 2); LOG_DEBUG("Register MFG_UID: 0x%x", registerValue); if (registerValue == 0x5449) { - logFoundDevice("INA260", (uint8_t)addr.address); - type = INA260; + registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xFF), 2); + LOG_DEBUG("Register DIE_UID: 0x%x", registerValue); + + if (registerValue == 0x2260) { + logFoundDevice("INA226", (uint8_t)addr.address); + type = INA226; + } else { + logFoundDevice("INA260", (uint8_t)addr.address); + type = INA260; + } } else { // Assume INA219 if INA260 ID is not found logFoundDevice("INA219", (uint8_t)addr.address); type = INA219; diff --git a/src/main.cpp b/src/main.cpp index 67fe137b8..8e7701bf3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -91,6 +91,7 @@ NRF52Bluetooth *nrf52Bluetooth = nullptr; #include "linux/LinuxHardwareI2C.h" #include "mesh/raspihttp/PiWebServer.h" #include "platform/portduino/PortduinoGlue.h" +#include "platform/portduino/USBHal.h" #include #include #include @@ -228,6 +229,9 @@ static OSThread *powerFSMthread; static OSThread *ambientLightingThread; RadioInterface *rIf = NULL; +#ifdef ARCH_PORTDUINO +RadioLibHal *RadioLibHAL = NULL; +#endif /** * Some platforms (nrf52) might provide an alterate version that suppresses calling delay from sleep. @@ -252,6 +256,17 @@ void printInfo() #ifndef PIO_UNIT_TESTING void setup() { +#if defined(T_DECK) + // GPIO10 manages all peripheral power supplies + // Turn on peripheral power immediately after MUC starts. + // If some boards are turned on late, ESP32 will reset due to low voltage. + // ESP32-C3(Keyboard) , MAX98357A(Audio Power Amplifier) , + // TF Card , Display backlight(AW9364DNR) , AN48841B(Trackball) , ES7210(Decoder) + pinMode(KB_POWERON, OUTPUT); + digitalWrite(KB_POWERON, HIGH); + delay(100); +#endif + concurrency::hasBeenSetup = true; #if ARCH_PORTDUINO SPISettings spiSettings(settingsMap[spiSpeed], MSBFIRST, SPI_MODE0); @@ -424,15 +439,6 @@ void setup() digitalWrite(AQ_SET_PIN, HIGH); #endif -#if defined(T_DECK) - // enable keyboard - pinMode(KB_POWERON, OUTPUT); - digitalWrite(KB_POWERON, HIGH); - // There needs to be a delay after power on, give LILYGO-KEYBOARD some startup time - // otherwise keyboard and touch screen will not work - delay(200); -#endif - // Currently only the tbeam has a PMU // PMU initialization needs to be placed before i2c scanning power = new Power(); @@ -588,6 +594,7 @@ void setup() scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::BMP_3XX, meshtastic_TelemetrySensorType_BMP3XX); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::BMP_085, meshtastic_TelemetrySensorType_BMP085); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::INA260, meshtastic_TelemetrySensorType_INA260); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::INA226, meshtastic_TelemetrySensorType_INA226); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::INA219, meshtastic_TelemetrySensorType_INA219); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::INA3221, meshtastic_TelemetrySensorType_INA3221); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MAX17048, meshtastic_TelemetrySensorType_MAX17048); @@ -718,12 +725,16 @@ void setup() pinMode(LORA_CS, OUTPUT); digitalWrite(LORA_CS, HIGH); SPI1.begin(false); -#else // HW_SPI1_DEVICE +#else // HW_SPI1_DEVICE SPI.setSCK(LORA_SCK); SPI.setTX(LORA_MOSI); SPI.setRX(LORA_MISO); SPI.begin(false); -#endif // HW_SPI1_DEVICE +#endif // HW_SPI1_DEVICE +#elif ARCH_PORTDUINO + if (settingsStrings[spidev] != "ch341") { + SPI.begin(); + } #elif !defined(ARCH_ESP32) // ARCH_RP2040 SPI.begin(); #else @@ -910,8 +921,11 @@ void setup() if (settingsMap[use_sx1262]) { if (!rIf) { LOG_DEBUG("Activate sx1262 radio on SPI port %s", settingsStrings[spidev].c_str()); - LockingArduinoHal *RadioLibHAL = - new LockingArduinoHal(SPI, spiSettings, (settingsMap[ch341Quirk] ? settingsMap[busy] : RADIOLIB_NC)); + if (settingsStrings[spidev] == "ch341") { + RadioLibHAL = ch341Hal; + } else { + RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); + } rIf = new SX1262Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset], settingsMap[busy]); if (!rIf->init()) { @@ -925,8 +939,7 @@ void setup() } else if (settingsMap[use_rf95]) { if (!rIf) { LOG_DEBUG("Activate rf95 radio on SPI port %s", settingsStrings[spidev].c_str()); - LockingArduinoHal *RadioLibHAL = - new LockingArduinoHal(SPI, spiSettings, (settingsMap[ch341Quirk] ? settingsMap[busy] : RADIOLIB_NC)); + RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); rIf = new RF95Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset], settingsMap[busy]); if (!rIf->init()) { @@ -941,7 +954,7 @@ void setup() } else if (settingsMap[use_sx1280]) { if (!rIf) { LOG_DEBUG("Activate sx1280 radio on SPI port %s", settingsStrings[spidev].c_str()); - LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); + RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); rIf = new SX1280Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset], settingsMap[busy]); if (!rIf->init()) { @@ -1001,7 +1014,7 @@ void setup() } else if (settingsMap[use_sx1268]) { if (!rIf) { LOG_DEBUG("Activate sx1268 radio on SPI port %s", settingsStrings[spidev].c_str()); - LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); + RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); rIf = new SX1268Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset], settingsMap[busy]); if (!rIf->init()) { @@ -1370,7 +1383,6 @@ void loop() mainDelay.delay(delayMsec); } } -#endif #if HAS_TFT void tft_task_handler(void *param = nullptr) @@ -1388,5 +1400,6 @@ void tft_task_handler(void *param = nullptr) #endif } } +#endif #endif diff --git a/src/mesh/CryptoEngine.cpp b/src/mesh/CryptoEngine.cpp index 94b9b6543..1624ab0d5 100644 --- a/src/mesh/CryptoEngine.cpp +++ b/src/mesh/CryptoEngine.cpp @@ -58,10 +58,16 @@ void CryptoEngine::clearKeys() * Encrypt a packet's payload using a key generated with Curve25519 and SHA256 * for a specific node. * - * @param bytes is updated in place + * @param toNode The MeshPacket `to` field. + * @param fromNode The MeshPacket `from` field. + * @param remotePublic The remote node's Curve25519 public key. + * @param packetId The MeshPacket `id` field. + * @param numBytes Number of bytes of plaintext in the bytes buffer. + * @param bytes Buffer containing plaintext input. + * @param bytesOut Output buffer to be populated with encrypted ciphertext. */ bool CryptoEngine::encryptCurve25519(uint32_t toNode, uint32_t fromNode, meshtastic_UserLite_public_key_t remotePublic, - uint64_t packetNum, size_t numBytes, uint8_t *bytes, uint8_t *bytesOut) + uint64_t packetNum, size_t numBytes, const uint8_t *bytes, uint8_t *bytesOut) { uint8_t *auth; long extraNonceTmp = random(); @@ -93,14 +99,18 @@ bool CryptoEngine::encryptCurve25519(uint32_t toNode, uint32_t fromNode, meshtas * Decrypt a packet's payload using a key generated with Curve25519 and SHA256 * for a specific node. * - * @param bytes is updated in place + * @param fromNode The MeshPacket `from` field. + * @param remotePublic The remote node's Curve25519 public key. + * @param packetId The MeshPacket `id` field. + * @param numBytes Number of bytes of ciphertext in the bytes buffer. + * @param bytes Buffer containing ciphertext input. + * @param bytesOut Output buffer to be populated with decrypted plaintext. */ bool CryptoEngine::decryptCurve25519(uint32_t fromNode, meshtastic_UserLite_public_key_t remotePublic, uint64_t packetNum, - size_t numBytes, uint8_t *bytes, uint8_t *bytesOut) + size_t numBytes, const uint8_t *bytes, uint8_t *bytesOut) { - uint8_t *auth; // set to last 8 bytes of text? - uint32_t extraNonce; // pointer was not really used - auth = bytes + numBytes - 12; + const uint8_t *auth = bytes + numBytes - 12; // set to last 8 bytes of text? + uint32_t extraNonce; // pointer was not really used memcpy(&extraNonce, auth + 8, sizeof(uint32_t)); // do not use dereference on potential non aligned pointers : (uint32_t *)(auth + 8); LOG_INFO("Random nonce value: %d", extraNonce); diff --git a/src/mesh/CryptoEngine.h b/src/mesh/CryptoEngine.h index 32862d95c..6bbcb3b8a 100644 --- a/src/mesh/CryptoEngine.h +++ b/src/mesh/CryptoEngine.h @@ -40,9 +40,9 @@ class CryptoEngine void clearKeys(); void setDHPrivateKey(uint8_t *_private_key); virtual bool encryptCurve25519(uint32_t toNode, uint32_t fromNode, meshtastic_UserLite_public_key_t remotePublic, - uint64_t packetNum, size_t numBytes, uint8_t *bytes, uint8_t *bytesOut); + uint64_t packetNum, size_t numBytes, const uint8_t *bytes, uint8_t *bytesOut); virtual bool decryptCurve25519(uint32_t fromNode, meshtastic_UserLite_public_key_t remotePublic, uint64_t packetNum, - size_t numBytes, uint8_t *bytes, uint8_t *bytesOut); + size_t numBytes, const uint8_t *bytes, uint8_t *bytesOut); virtual bool setDHPublicKey(uint8_t *publicKey); virtual void hash(uint8_t *bytes, size_t numBytes); diff --git a/src/mesh/LR11x0Interface.cpp b/src/mesh/LR11x0Interface.cpp index 30ef8f9af..ce4f912ba 100644 --- a/src/mesh/LR11x0Interface.cpp +++ b/src/mesh/LR11x0Interface.cpp @@ -48,8 +48,10 @@ template bool LR11x0Interface::init() digitalWrite(LR11X0_POWER_EN, HIGH); #endif +#if ARCH_PORTDUINO + float tcxoVoltage = (float)settingsMap[dio3_tcxo_voltage] / 1000; // FIXME: correct logic to default to not using TCXO if no voltage is specified for LR11x0_DIO3_TCXO_VOLTAGE -#if !defined(LR11X0_DIO3_TCXO_VOLTAGE) +#elif !defined(LR11X0_DIO3_TCXO_VOLTAGE) float tcxoVoltage = 0; // "TCXO reference voltage to be set on DIO3. Defaults to 1.6 V, set to 0 to skip." per // https://github.com/jgromes/RadioLib/blob/690a050ebb46e6097c5d00c371e961c1caa3b52e/src/modules/LR11x0/LR11x0.h#L471C26-L471C104 diff --git a/src/mesh/MemoryPool.h b/src/mesh/MemoryPool.h index d30404b9f..c4af3c4ac 100644 --- a/src/mesh/MemoryPool.h +++ b/src/mesh/MemoryPool.h @@ -2,6 +2,8 @@ #include #include +#include +#include #include "PointerQueue.h" @@ -9,6 +11,7 @@ template class Allocator { public: + Allocator() : deleter([this](T *p) { this->release(p); }) {} virtual ~Allocator() {} /// Return a queable object which has been prefilled with zeros. Panic if no buffer is available @@ -43,12 +46,32 @@ template class Allocator return p; } + /// Variations of the above methods that return std::unique_ptr instead of raw pointers. + using UniqueAllocation = std::unique_ptr &>; + /// Return a queable object which has been prefilled with zeros. + /// std::unique_ptr wrapped variant of allocZeroed(). + UniqueAllocation allocUniqueZeroed() { return UniqueAllocation(allocZeroed(), deleter); } + /// Return a queable object which has been prefilled with zeros - allow timeout to wait for available buffers (you probably + /// don't want this version). + /// std::unique_ptr wrapped variant of allocZeroed(TickType_t maxWait). + UniqueAllocation allocUniqueZeroed(TickType_t maxWait) { return UniqueAllocation(allocZeroed(maxWait), deleter); } + /// Return a queable object which is a copy of some other object + /// std::unique_ptr wrapped variant of allocCopy(const T &src, TickType_t maxWait). + UniqueAllocation allocUniqueCopy(const T &src, TickType_t maxWait = portMAX_DELAY) + { + return UniqueAllocation(allocCopy(src, maxWait), deleter); + } + /// Return a buffer for use by others virtual void release(T *p) = 0; protected: // Alloc some storage virtual T *alloc(TickType_t maxWait) = 0; + + private: + // std::unique_ptr Deleter function; calls release(). + const std::function deleter; }; /** diff --git a/src/mesh/MeshTypes.h b/src/mesh/MeshTypes.h index cf1b54c78..1d6bd342d 100644 --- a/src/mesh/MeshTypes.h +++ b/src/mesh/MeshTypes.h @@ -44,6 +44,7 @@ typedef int ErrorCode; /// Alloc and free packets to our global, ISR safe pool extern Allocator &packetPool; +using UniquePacketPoolPacket = Allocator::UniqueAllocation; /** * Most (but not always) of the time we want to treat packets 'from' the local phone (where from == 0), as if they originated on diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 57f2fb39a..994e59d35 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -71,6 +71,78 @@ static unsigned char userprefs_admin_key_1[] = USERPREFS_USE_ADMIN_KEY_1; static unsigned char userprefs_admin_key_2[] = USERPREFS_USE_ADMIN_KEY_2; #endif +#ifdef HELTEC_MESH_NODE_T114 + +uint32_t read8(uint8_t bits, uint8_t dummy, uint8_t cs, uint8_t sck, uint8_t mosi, uint8_t dc, uint8_t rst) +{ + uint32_t ret = 0; + uint8_t SDAPIN = mosi; + pinMode(SDAPIN, INPUT_PULLUP); + digitalWrite(dc, HIGH); + for (int i = 0; i < dummy; i++) { // any dummy clocks + digitalWrite(sck, HIGH); + delay(1); + digitalWrite(sck, LOW); + delay(1); + } + for (int i = 0; i < bits; i++) { // read results + ret <<= 1; + delay(1); + if (digitalRead(SDAPIN)) + ret |= 1; + ; + digitalWrite(sck, HIGH); + delay(1); + digitalWrite(sck, LOW); + } + return ret; +} + +void write9(uint8_t val, uint8_t dc_val, uint8_t cs, uint8_t sck, uint8_t mosi, uint8_t dc, uint8_t rst) +{ + pinMode(mosi, OUTPUT); + digitalWrite(dc, dc_val); + for (int i = 0; i < 8; i++) { // send command + digitalWrite(mosi, (val & 0x80) != 0); + delay(1); + digitalWrite(sck, HIGH); + delay(1); + digitalWrite(sck, LOW); + val <<= 1; + } +} + +uint32_t readwrite8(uint8_t cmd, uint8_t bits, uint8_t dummy, uint8_t cs, uint8_t sck, uint8_t mosi, uint8_t dc, uint8_t rst) +{ + digitalWrite(cs, LOW); + write9(cmd, 0, cs, sck, mosi, dc, rst); + uint32_t ret = read8(bits, dummy, cs, sck, mosi, dc, rst); + digitalWrite(cs, HIGH); + return ret; +} + +uint32_t get_st7789_id(uint8_t cs, uint8_t sck, uint8_t mosi, uint8_t dc, uint8_t rst) +{ + pinMode(cs, OUTPUT); + digitalWrite(cs, HIGH); + pinMode(cs, OUTPUT); + pinMode(sck, OUTPUT); + pinMode(mosi, OUTPUT); + pinMode(dc, OUTPUT); + pinMode(rst, OUTPUT); + digitalWrite(rst, LOW); // Hardware Reset + delay(10); + digitalWrite(rst, HIGH); + delay(10); + + uint32_t ID = 0; + ID = readwrite8(0x04, 24, 1, cs, sck, mosi, dc, rst); + ID = readwrite8(0x04, 24, 1, cs, sck, mosi, dc, rst); // ST7789 needs twice + return ID; +} + +#endif + bool meshtastic_DeviceState_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_iter_t *field) { if (ostream) { @@ -497,6 +569,12 @@ void NodeDB::installDefaultConfig(bool preserveKey = false) #if defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7789_CS) || \ defined(HX8357_CS) || defined(USE_ST7789) bool hasScreen = true; +#ifdef HELTEC_MESH_NODE_T114 + uint32_t st7789_id = get_st7789_id(ST7789_NSS, ST7789_SCK, ST7789_SDA, ST7789_RS, ST7789_RESET); + if (st7789_id == 0xFFFFFF) { + hasScreen = false; + } +#endif #elif ARCH_PORTDUINO bool hasScreen = false; if (settingsMap[displayPanel]) @@ -782,12 +860,12 @@ void NodeDB::installDefaultDeviceState() #ifdef USERPREFS_CONFIG_OWNER_LONG_NAME snprintf(owner.long_name, sizeof(owner.long_name), USERPREFS_CONFIG_OWNER_LONG_NAME); #else - snprintf(owner.long_name, sizeof(owner.long_name), "Meshtastic %02x%02x", ourMacAddr[4], ourMacAddr[5]); + snprintf(owner.long_name, sizeof(owner.long_name), "Meshtastic %04x", getNodeNum() & 0x0ffff); #endif #ifdef USERPREFS_CONFIG_OWNER_SHORT_NAME snprintf(owner.short_name, sizeof(owner.short_name), USERPREFS_CONFIG_OWNER_SHORT_NAME); #else - snprintf(owner.short_name, sizeof(owner.short_name), "%02x%02x", ourMacAddr[4], ourMacAddr[5]); + snprintf(owner.short_name, sizeof(owner.short_name), "%04x", getNodeNum() & 0x0ffff); #endif snprintf(owner.id, sizeof(owner.id), "!%08x", getNodeNum()); // Default node ID now based on nodenum memcpy(owner.macaddr, ourMacAddr, sizeof(owner.macaddr)); diff --git a/src/mesh/RadioLibInterface.cpp b/src/mesh/RadioLibInterface.cpp index 5f82a41ce..e416160eb 100644 --- a/src/mesh/RadioLibInterface.cpp +++ b/src/mesh/RadioLibInterface.cpp @@ -31,31 +31,7 @@ void LockingArduinoHal::spiEndTransaction() #if ARCH_PORTDUINO void LockingArduinoHal::spiTransfer(uint8_t *out, size_t len, uint8_t *in) { - if (busy == RADIOLIB_NC) { - spi->transfer(out, in, len); - } else { - uint16_t offset = 0; - - while (len) { - uint8_t block_size = (len < 20 ? len : 20); - spi->transfer((out != NULL ? out + offset : NULL), (in != NULL ? in + offset : NULL), block_size); - if (block_size == len) - return; - - // ensure GPIO is low - - uint32_t start = millis(); - while (digitalRead(busy)) { - if (!Throttle::isWithinTimespanMs(start, 2000)) { - LOG_ERROR("GPIO mid-transfer timeout, is it connected?"); - return; - } - } - - offset += block_size; - len -= block_size; - } - } + spi->transfer(out, in, len); } #endif diff --git a/src/mesh/RadioLibInterface.h b/src/mesh/RadioLibInterface.h index a5c2e30dd..d6101ae37 100644 --- a/src/mesh/RadioLibInterface.h +++ b/src/mesh/RadioLibInterface.h @@ -22,18 +22,11 @@ class LockingArduinoHal : public ArduinoHal { public: - LockingArduinoHal(SPIClass &spi, SPISettings spiSettings, RADIOLIB_PIN_TYPE _busy = RADIOLIB_NC) - : ArduinoHal(spi, spiSettings) - { -#if ARCH_PORTDUINO - busy = _busy; -#endif - }; + LockingArduinoHal(SPIClass &spi, SPISettings spiSettings) : ArduinoHal(spi, spiSettings){}; void spiBeginTransaction() override; void spiEndTransaction() override; #if ARCH_PORTDUINO - RADIOLIB_PIN_TYPE busy; void spiTransfer(uint8_t *out, size_t len, uint8_t *in) override; #endif diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index e714ef215..f55e7cc5a 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -37,7 +37,6 @@ static MemoryDynamic staticPool; Allocator &packetPool = staticPool; static uint8_t bytes[MAX_LORA_PAYLOAD_LEN + 1] __attribute__((__aligned__)); -static uint8_t ScratchEncrypted[MAX_LORA_PAYLOAD_LEN + 1] __attribute__((__aligned__)); /** * Constructor @@ -327,9 +326,6 @@ bool perhapsDecode(meshtastic_MeshPacket *p) } bool decrypted = false; ChannelIndex chIndex = 0; - memcpy(bytes, p->encrypted.bytes, - rawSize); // we have to copy into a scratch buffer, because these bytes are a union with the decoded protobuf - memcpy(ScratchEncrypted, p->encrypted.bytes, rawSize); #if !(MESHTASTIC_EXCLUDE_PKI) // Attempt PKI decryption first if (p->channel == 0 && isToUs(p) && p->to > 0 && !isBroadcast(p->to) && nodeDB->getMeshNode(p->from) != nullptr && @@ -337,7 +333,7 @@ bool perhapsDecode(meshtastic_MeshPacket *p) rawSize > MESHTASTIC_PKC_OVERHEAD) { LOG_DEBUG("Attempt PKI decryption"); - if (crypto->decryptCurve25519(p->from, nodeDB->getMeshNode(p->from)->user.public_key, p->id, rawSize, ScratchEncrypted, + if (crypto->decryptCurve25519(p->from, nodeDB->getMeshNode(p->from)->user.public_key, p->id, rawSize, p->encrypted.bytes, bytes)) { LOG_INFO("PKI Decryption worked!"); memset(&p->decoded, 0, sizeof(p->decoded)); @@ -349,8 +345,6 @@ bool perhapsDecode(meshtastic_MeshPacket *p) p->pki_encrypted = true; memcpy(&p->public_key.bytes, nodeDB->getMeshNode(p->from)->user.public_key.bytes, 32); p->public_key.size = 32; - // memcpy(bytes, ScratchEncrypted, rawSize); // TODO: Rename the bytes buffers - // chIndex = 8; } else { LOG_ERROR("PKC Decrypted, but pb_decode failed!"); return false; @@ -367,6 +361,9 @@ bool perhapsDecode(meshtastic_MeshPacket *p) for (chIndex = 0; chIndex < channels.getNumChannels(); chIndex++) { // Try to use this hash/channel pair if (channels.decryptForHash(chIndex, p->channel)) { + // we have to copy into a scratch buffer, because these bytes are a union with the decoded protobuf. Create a + // fresh copy for each decrypt attempt. + memcpy(bytes, p->encrypted.bytes, rawSize); // Try to decrypt the packet if we can crypto->decrypt(p->from, p->id, rawSize, bytes); @@ -515,9 +512,8 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) *node->user.public_key.bytes); return meshtastic_Routing_Error_PKI_FAILED; } - crypto->encryptCurve25519(p->to, getFrom(p), node->user.public_key, p->id, numbytes, bytes, ScratchEncrypted); + crypto->encryptCurve25519(p->to, getFrom(p), node->user.public_key, p->id, numbytes, bytes, p->encrypted.bytes); numbytes += MESHTASTIC_PKC_OVERHEAD; - memcpy(p->encrypted.bytes, ScratchEncrypted, numbytes); p->channel = 0; p->pki_encrypted = true; } else { diff --git a/src/mesh/SX126xInterface.cpp b/src/mesh/SX126xInterface.cpp index 002fb41ca..ed0267c5b 100644 --- a/src/mesh/SX126xInterface.cpp +++ b/src/mesh/SX126xInterface.cpp @@ -50,9 +50,7 @@ template bool SX126xInterface::init() #endif #if ARCH_PORTDUINO - float tcxoVoltage = 0; - if (settingsMap[dio3_tcxo_voltage]) - tcxoVoltage = 1.8; + float tcxoVoltage = (float)settingsMap[dio3_tcxo_voltage] / 1000; if (settingsMap[sx126x_ant_sw] != RADIOLIB_NC) { digitalWrite(settingsMap[sx126x_ant_sw], HIGH); pinMode(settingsMap[sx126x_ant_sw], OUTPUT); diff --git a/src/mesh/generated/meshtastic/admin.pb.cpp b/src/mesh/generated/meshtastic/admin.pb.cpp index 8b3fd3d1b..7ce3c74ce 100644 --- a/src/mesh/generated/meshtastic/admin.pb.cpp +++ b/src/mesh/generated/meshtastic/admin.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #include "meshtastic/admin.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/meshtastic/admin.pb.h b/src/mesh/generated/meshtastic/admin.pb.h index bbf633ef5..d9b8de384 100644 --- a/src/mesh/generated/meshtastic/admin.pb.h +++ b/src/mesh/generated/meshtastic/admin.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #ifndef PB_MESHTASTIC_MESHTASTIC_ADMIN_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_ADMIN_PB_H_INCLUDED diff --git a/src/mesh/generated/meshtastic/apponly.pb.cpp b/src/mesh/generated/meshtastic/apponly.pb.cpp index 64d43b7ee..8b1b3da19 100644 --- a/src/mesh/generated/meshtastic/apponly.pb.cpp +++ b/src/mesh/generated/meshtastic/apponly.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #include "meshtastic/apponly.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/meshtastic/apponly.pb.h b/src/mesh/generated/meshtastic/apponly.pb.h index dc08d9ff3..f4c33bd79 100644 --- a/src/mesh/generated/meshtastic/apponly.pb.h +++ b/src/mesh/generated/meshtastic/apponly.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #ifndef PB_MESHTASTIC_MESHTASTIC_APPONLY_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_APPONLY_PB_H_INCLUDED diff --git a/src/mesh/generated/meshtastic/atak.pb.cpp b/src/mesh/generated/meshtastic/atak.pb.cpp index 6dbc69fb4..a0368cf6b 100644 --- a/src/mesh/generated/meshtastic/atak.pb.cpp +++ b/src/mesh/generated/meshtastic/atak.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #include "meshtastic/atak.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/meshtastic/atak.pb.h b/src/mesh/generated/meshtastic/atak.pb.h index 15a86788b..8533bcbf9 100644 --- a/src/mesh/generated/meshtastic/atak.pb.h +++ b/src/mesh/generated/meshtastic/atak.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #ifndef PB_MESHTASTIC_MESHTASTIC_ATAK_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_ATAK_PB_H_INCLUDED diff --git a/src/mesh/generated/meshtastic/cannedmessages.pb.cpp b/src/mesh/generated/meshtastic/cannedmessages.pb.cpp index 9f51e9634..1f4ebc927 100644 --- a/src/mesh/generated/meshtastic/cannedmessages.pb.cpp +++ b/src/mesh/generated/meshtastic/cannedmessages.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #include "meshtastic/cannedmessages.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/meshtastic/cannedmessages.pb.h b/src/mesh/generated/meshtastic/cannedmessages.pb.h index 06d14b98f..8343c4d6e 100644 --- a/src/mesh/generated/meshtastic/cannedmessages.pb.h +++ b/src/mesh/generated/meshtastic/cannedmessages.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #ifndef PB_MESHTASTIC_MESHTASTIC_CANNEDMESSAGES_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_CANNEDMESSAGES_PB_H_INCLUDED diff --git a/src/mesh/generated/meshtastic/channel.pb.cpp b/src/mesh/generated/meshtastic/channel.pb.cpp index 52f923b13..6670a40fc 100644 --- a/src/mesh/generated/meshtastic/channel.pb.cpp +++ b/src/mesh/generated/meshtastic/channel.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #include "meshtastic/channel.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/meshtastic/channel.pb.h b/src/mesh/generated/meshtastic/channel.pb.h index 3d617ae39..ca4310bf1 100644 --- a/src/mesh/generated/meshtastic/channel.pb.h +++ b/src/mesh/generated/meshtastic/channel.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #ifndef PB_MESHTASTIC_MESHTASTIC_CHANNEL_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_CHANNEL_PB_H_INCLUDED diff --git a/src/mesh/generated/meshtastic/clientonly.pb.cpp b/src/mesh/generated/meshtastic/clientonly.pb.cpp index d99af8cf5..8f380a972 100644 --- a/src/mesh/generated/meshtastic/clientonly.pb.cpp +++ b/src/mesh/generated/meshtastic/clientonly.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #include "meshtastic/clientonly.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/meshtastic/clientonly.pb.h b/src/mesh/generated/meshtastic/clientonly.pb.h index bf32d7875..5109e20b2 100644 --- a/src/mesh/generated/meshtastic/clientonly.pb.h +++ b/src/mesh/generated/meshtastic/clientonly.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #ifndef PB_MESHTASTIC_MESHTASTIC_CLIENTONLY_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_CLIENTONLY_PB_H_INCLUDED diff --git a/src/mesh/generated/meshtastic/config.pb.cpp b/src/mesh/generated/meshtastic/config.pb.cpp index 23f4d542b..6fd2161ae 100644 --- a/src/mesh/generated/meshtastic/config.pb.cpp +++ b/src/mesh/generated/meshtastic/config.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #include "meshtastic/config.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/meshtastic/config.pb.h b/src/mesh/generated/meshtastic/config.pb.h index fab23ae34..8e2264e93 100644 --- a/src/mesh/generated/meshtastic/config.pb.h +++ b/src/mesh/generated/meshtastic/config.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #ifndef PB_MESHTASTIC_MESHTASTIC_CONFIG_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_CONFIG_PB_H_INCLUDED diff --git a/src/mesh/generated/meshtastic/connection_status.pb.cpp b/src/mesh/generated/meshtastic/connection_status.pb.cpp index d1495bb83..b0df459ad 100644 --- a/src/mesh/generated/meshtastic/connection_status.pb.cpp +++ b/src/mesh/generated/meshtastic/connection_status.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #include "meshtastic/connection_status.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/meshtastic/connection_status.pb.h b/src/mesh/generated/meshtastic/connection_status.pb.h index c433e370b..55559dcef 100644 --- a/src/mesh/generated/meshtastic/connection_status.pb.h +++ b/src/mesh/generated/meshtastic/connection_status.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #ifndef PB_MESHTASTIC_MESHTASTIC_CONNECTION_STATUS_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_CONNECTION_STATUS_PB_H_INCLUDED diff --git a/src/mesh/generated/meshtastic/device_ui.pb.cpp b/src/mesh/generated/meshtastic/device_ui.pb.cpp index 6e0cf0cc8..3a9e28725 100644 --- a/src/mesh/generated/meshtastic/device_ui.pb.cpp +++ b/src/mesh/generated/meshtastic/device_ui.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #include "meshtastic/device_ui.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/meshtastic/device_ui.pb.h b/src/mesh/generated/meshtastic/device_ui.pb.h index 107aa8846..0c4f5384e 100644 --- a/src/mesh/generated/meshtastic/device_ui.pb.h +++ b/src/mesh/generated/meshtastic/device_ui.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #ifndef PB_MESHTASTIC_MESHTASTIC_DEVICE_UI_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_DEVICE_UI_PB_H_INCLUDED diff --git a/src/mesh/generated/meshtastic/deviceonly.pb.cpp b/src/mesh/generated/meshtastic/deviceonly.pb.cpp index 92853f00d..aa020467a 100644 --- a/src/mesh/generated/meshtastic/deviceonly.pb.cpp +++ b/src/mesh/generated/meshtastic/deviceonly.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #include "meshtastic/deviceonly.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/meshtastic/deviceonly.pb.h b/src/mesh/generated/meshtastic/deviceonly.pb.h index e52a914e0..c0a0fee91 100644 --- a/src/mesh/generated/meshtastic/deviceonly.pb.h +++ b/src/mesh/generated/meshtastic/deviceonly.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #ifndef PB_MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_INCLUDED diff --git a/src/mesh/generated/meshtastic/localonly.pb.cpp b/src/mesh/generated/meshtastic/localonly.pb.cpp index 0a752a5a8..34391df73 100644 --- a/src/mesh/generated/meshtastic/localonly.pb.cpp +++ b/src/mesh/generated/meshtastic/localonly.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #include "meshtastic/localonly.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/meshtastic/localonly.pb.h b/src/mesh/generated/meshtastic/localonly.pb.h index 8f92b2a77..30f70ed90 100644 --- a/src/mesh/generated/meshtastic/localonly.pb.h +++ b/src/mesh/generated/meshtastic/localonly.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #ifndef PB_MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_INCLUDED diff --git a/src/mesh/generated/meshtastic/mesh.pb.cpp b/src/mesh/generated/meshtastic/mesh.pb.cpp index a9f42f979..6c5c7a4be 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.cpp +++ b/src/mesh/generated/meshtastic/mesh.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #include "meshtastic/mesh.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index da439c375..14ed76f70 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #ifndef PB_MESHTASTIC_MESHTASTIC_MESH_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_MESH_PB_H_INCLUDED @@ -220,6 +220,9 @@ typedef enum _meshtastic_HardwareModel { the same frame format. Runs on linux, see https://github.com/Jorropo/routastic */ meshtastic_HardwareModel_ROUTASTIC = 85, + /* Mesh-Tab, esp32 based + https://github.com/valzzu/Mesh-Tab */ + meshtastic_HardwareModel_MESH_TAB = 86, /* ------------------------------------------------------------------------------------------------------------------------------------------ Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits. ------------------------------------------------------------------------------------------------------------------------------------------ */ @@ -414,6 +417,8 @@ typedef enum _meshtastic_MeshPacket_Priority { meshtastic_MeshPacket_Priority_RESPONSE = 80, /* Higher priority for specific message types (portnums) to distinguish between other reliable packets. */ meshtastic_MeshPacket_Priority_HIGH = 100, + /* Higher priority alert message used for critical alerts which take priority over other reliable packets. */ + meshtastic_MeshPacket_Priority_ALERT = 110, /* Ack/naks are sent with very high priority to ensure that retransmission stops as soon as possible */ meshtastic_MeshPacket_Priority_ACK = 120, diff --git a/src/mesh/generated/meshtastic/module_config.pb.cpp b/src/mesh/generated/meshtastic/module_config.pb.cpp index c40041eab..9843f0e91 100644 --- a/src/mesh/generated/meshtastic/module_config.pb.cpp +++ b/src/mesh/generated/meshtastic/module_config.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #include "meshtastic/module_config.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/meshtastic/module_config.pb.h b/src/mesh/generated/meshtastic/module_config.pb.h index 8f7bb701d..697b965c5 100644 --- a/src/mesh/generated/meshtastic/module_config.pb.h +++ b/src/mesh/generated/meshtastic/module_config.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #ifndef PB_MESHTASTIC_MESHTASTIC_MODULE_CONFIG_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_MODULE_CONFIG_PB_H_INCLUDED diff --git a/src/mesh/generated/meshtastic/mqtt.pb.cpp b/src/mesh/generated/meshtastic/mqtt.pb.cpp index 74536cb79..2c32ef2e4 100644 --- a/src/mesh/generated/meshtastic/mqtt.pb.cpp +++ b/src/mesh/generated/meshtastic/mqtt.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #include "meshtastic/mqtt.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/meshtastic/mqtt.pb.h b/src/mesh/generated/meshtastic/mqtt.pb.h index 4d1027374..1726bc470 100644 --- a/src/mesh/generated/meshtastic/mqtt.pb.h +++ b/src/mesh/generated/meshtastic/mqtt.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #ifndef PB_MESHTASTIC_MESHTASTIC_MQTT_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_MQTT_PB_H_INCLUDED diff --git a/src/mesh/generated/meshtastic/paxcount.pb.cpp b/src/mesh/generated/meshtastic/paxcount.pb.cpp index 403288147..ff738bde9 100644 --- a/src/mesh/generated/meshtastic/paxcount.pb.cpp +++ b/src/mesh/generated/meshtastic/paxcount.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #include "meshtastic/paxcount.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/meshtastic/paxcount.pb.h b/src/mesh/generated/meshtastic/paxcount.pb.h index b6b51fdd5..06078aef7 100644 --- a/src/mesh/generated/meshtastic/paxcount.pb.h +++ b/src/mesh/generated/meshtastic/paxcount.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #ifndef PB_MESHTASTIC_MESHTASTIC_PAXCOUNT_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_PAXCOUNT_PB_H_INCLUDED diff --git a/src/mesh/generated/meshtastic/portnums.pb.cpp b/src/mesh/generated/meshtastic/portnums.pb.cpp index 8fca9af79..15a6ba372 100644 --- a/src/mesh/generated/meshtastic/portnums.pb.cpp +++ b/src/mesh/generated/meshtastic/portnums.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #include "meshtastic/portnums.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/meshtastic/portnums.pb.h b/src/mesh/generated/meshtastic/portnums.pb.h index df6cf32c2..d7dc47785 100644 --- a/src/mesh/generated/meshtastic/portnums.pb.h +++ b/src/mesh/generated/meshtastic/portnums.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #ifndef PB_MESHTASTIC_MESHTASTIC_PORTNUMS_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_PORTNUMS_PB_H_INCLUDED @@ -72,6 +72,8 @@ typedef enum _meshtastic_PortNum { /* Same as Text Message but originating from Detection Sensor Module. NOTE: This portnum traffic is not sent to the public MQTT starting at firmware version 2.2.9 */ meshtastic_PortNum_DETECTION_SENSOR_APP = 10, + /* Same as Text Message but used for critical alerts. */ + meshtastic_PortNum_ALERT_APP = 11, /* Provides a 'ping' service that replies to any packet it receives. Also serves as a small example module. ENCODING: ASCII Plaintext */ diff --git a/src/mesh/generated/meshtastic/powermon.pb.cpp b/src/mesh/generated/meshtastic/powermon.pb.cpp index 6a9b7551a..8838e165f 100644 --- a/src/mesh/generated/meshtastic/powermon.pb.cpp +++ b/src/mesh/generated/meshtastic/powermon.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #include "meshtastic/powermon.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/meshtastic/powermon.pb.h b/src/mesh/generated/meshtastic/powermon.pb.h index 5add85b85..9d4d94193 100644 --- a/src/mesh/generated/meshtastic/powermon.pb.h +++ b/src/mesh/generated/meshtastic/powermon.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #ifndef PB_MESHTASTIC_MESHTASTIC_POWERMON_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_POWERMON_PB_H_INCLUDED diff --git a/src/mesh/generated/meshtastic/remote_hardware.pb.cpp b/src/mesh/generated/meshtastic/remote_hardware.pb.cpp index 239950e7e..8942104b5 100644 --- a/src/mesh/generated/meshtastic/remote_hardware.pb.cpp +++ b/src/mesh/generated/meshtastic/remote_hardware.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #include "meshtastic/remote_hardware.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/meshtastic/remote_hardware.pb.h b/src/mesh/generated/meshtastic/remote_hardware.pb.h index ade250e93..9ab3413c3 100644 --- a/src/mesh/generated/meshtastic/remote_hardware.pb.h +++ b/src/mesh/generated/meshtastic/remote_hardware.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #ifndef PB_MESHTASTIC_MESHTASTIC_REMOTE_HARDWARE_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_REMOTE_HARDWARE_PB_H_INCLUDED diff --git a/src/mesh/generated/meshtastic/rtttl.pb.cpp b/src/mesh/generated/meshtastic/rtttl.pb.cpp index 61ad8b73f..c994741f3 100644 --- a/src/mesh/generated/meshtastic/rtttl.pb.cpp +++ b/src/mesh/generated/meshtastic/rtttl.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #include "meshtastic/rtttl.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/meshtastic/rtttl.pb.h b/src/mesh/generated/meshtastic/rtttl.pb.h index 0572265f7..b6e152dbf 100644 --- a/src/mesh/generated/meshtastic/rtttl.pb.h +++ b/src/mesh/generated/meshtastic/rtttl.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #ifndef PB_MESHTASTIC_MESHTASTIC_RTTTL_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_RTTTL_PB_H_INCLUDED diff --git a/src/mesh/generated/meshtastic/storeforward.pb.cpp b/src/mesh/generated/meshtastic/storeforward.pb.cpp index 71a232bf6..82db566a1 100644 --- a/src/mesh/generated/meshtastic/storeforward.pb.cpp +++ b/src/mesh/generated/meshtastic/storeforward.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #include "meshtastic/storeforward.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/meshtastic/storeforward.pb.h b/src/mesh/generated/meshtastic/storeforward.pb.h index 44ffd098c..75cff5205 100644 --- a/src/mesh/generated/meshtastic/storeforward.pb.h +++ b/src/mesh/generated/meshtastic/storeforward.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #ifndef PB_MESHTASTIC_MESHTASTIC_STOREFORWARD_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_STOREFORWARD_PB_H_INCLUDED diff --git a/src/mesh/generated/meshtastic/telemetry.pb.cpp b/src/mesh/generated/meshtastic/telemetry.pb.cpp index f6d39da6e..c79941fa5 100644 --- a/src/mesh/generated/meshtastic/telemetry.pb.cpp +++ b/src/mesh/generated/meshtastic/telemetry.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #include "meshtastic/telemetry.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/meshtastic/telemetry.pb.h b/src/mesh/generated/meshtastic/telemetry.pb.h index 874eef60f..85fe4bdc1 100644 --- a/src/mesh/generated/meshtastic/telemetry.pb.h +++ b/src/mesh/generated/meshtastic/telemetry.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #ifndef PB_MESHTASTIC_MESHTASTIC_TELEMETRY_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_TELEMETRY_PB_H_INCLUDED @@ -79,7 +79,9 @@ typedef enum _meshtastic_TelemetrySensorType { /* SCD40/SCD41 CO2, humidity, temperature sensor */ meshtastic_TelemetrySensorType_SCD4X = 32, /* ClimateGuard RadSens, radiation, Geiger-Muller Tube */ - meshtastic_TelemetrySensorType_RADSENS = 33 + meshtastic_TelemetrySensorType_RADSENS = 33, + /* High accuracy current and voltage */ + meshtastic_TelemetrySensorType_INA226 = 34 } meshtastic_TelemetrySensorType; /* Struct definitions */ @@ -304,8 +306,8 @@ extern "C" { /* Helper constants for enums */ #define _meshtastic_TelemetrySensorType_MIN meshtastic_TelemetrySensorType_SENSOR_UNSET -#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_RADSENS -#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_RADSENS+1)) +#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_INA226 +#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_INA226+1)) diff --git a/src/mesh/generated/meshtastic/xmodem.pb.cpp b/src/mesh/generated/meshtastic/xmodem.pb.cpp index 3960ccdaa..09ae41d35 100644 --- a/src/mesh/generated/meshtastic/xmodem.pb.cpp +++ b/src/mesh/generated/meshtastic/xmodem.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #include "meshtastic/xmodem.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/meshtastic/xmodem.pb.h b/src/mesh/generated/meshtastic/xmodem.pb.h index 76edc0df6..3410fda0f 100644 --- a/src/mesh/generated/meshtastic/xmodem.pb.h +++ b/src/mesh/generated/meshtastic/xmodem.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.9 */ +/* Generated by nanopb-0.4.9.1 */ #ifndef PB_MESHTASTIC_MESHTASTIC_XMODEM_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_XMODEM_PB_H_INCLUDED diff --git a/src/mesh/http/ContentHandler.cpp b/src/mesh/http/ContentHandler.cpp index 64f7164c9..2b88702ed 100644 --- a/src/mesh/http/ContentHandler.cpp +++ b/src/mesh/http/ContentHandler.cpp @@ -93,6 +93,7 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer) ResourceNode *nodeJsonScanNetworks = new ResourceNode("/json/scanNetworks", "GET", &handleScanNetworks); ResourceNode *nodeJsonBlinkLED = new ResourceNode("/json/blink", "POST", &handleBlinkLED); ResourceNode *nodeJsonReport = new ResourceNode("/json/report", "GET", &handleReport); + ResourceNode *nodeJsonNodes = new ResourceNode("/json/nodes", "GET", &handleNodes); ResourceNode *nodeJsonFsBrowseStatic = new ResourceNode("/json/fs/browse/static", "GET", &handleFsBrowseStatic); ResourceNode *nodeJsonDelete = new ResourceNode("/json/fs/delete/static", "DELETE", &handleFsDeleteStatic); @@ -112,6 +113,7 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer) secureServer->registerNode(nodeJsonFsBrowseStatic); secureServer->registerNode(nodeJsonDelete); secureServer->registerNode(nodeJsonReport); + secureServer->registerNode(nodeJsonNodes); // secureServer->registerNode(nodeUpdateFs); // secureServer->registerNode(nodeDeleteFs); secureServer->registerNode(nodeAdmin); @@ -680,6 +682,78 @@ void handleReport(HTTPRequest *req, HTTPResponse *res) delete value; } +void handleNodes(HTTPRequest *req, HTTPResponse *res) +{ + ResourceParameters *params = req->getParams(); + std::string content; + + if (!params->getQueryParameter("content", content)) { + content = "json"; + } + + if (content == "json") { + res->setHeader("Content-Type", "application/json"); + res->setHeader("Access-Control-Allow-Origin", "*"); + res->setHeader("Access-Control-Allow-Methods", "GET"); + } else { + res->setHeader("Content-Type", "text/html"); + res->println("
");
+    }
+
+    JSONArray nodesArray;
+
+    uint32_t readIndex = 0;
+    const meshtastic_NodeInfoLite *tempNodeInfo = nodeDB->readNextMeshNode(readIndex);
+    while (tempNodeInfo != NULL) {
+        if (tempNodeInfo->has_user) {
+            JSONObject node;
+
+            char id[16];
+            snprintf(id, sizeof(id), "!%08x", tempNodeInfo->num);
+
+            node["id"] = new JSONValue(id);
+            node["snr"] = new JSONValue(tempNodeInfo->snr);
+            node["via_mqtt"] = new JSONValue(BoolToString(tempNodeInfo->via_mqtt));
+            node["last_heard"] = new JSONValue((int)tempNodeInfo->last_heard);
+            node["position"] = new JSONValue();
+
+            if (nodeDB->hasValidPosition(tempNodeInfo)) {
+                JSONObject position;
+                position["latitude"] = new JSONValue((float)tempNodeInfo->position.latitude_i * 1e-7);
+                position["longitude"] = new JSONValue((float)tempNodeInfo->position.longitude_i * 1e-7);
+                position["altitude"] = new JSONValue((int)tempNodeInfo->position.altitude);
+                node["position"] = new JSONValue(position);
+            }
+
+            JSONObject user;
+            node["long_name"] = new JSONValue(tempNodeInfo->user.long_name);
+            node["short_name"] = new JSONValue(tempNodeInfo->user.short_name);
+            char macStr[18];
+            snprintf(macStr, sizeof(macStr), "%02X:%02X:%02X:%02X:%02X:%02X", tempNodeInfo->user.macaddr[0],
+                     tempNodeInfo->user.macaddr[1], tempNodeInfo->user.macaddr[2], tempNodeInfo->user.macaddr[3],
+                     tempNodeInfo->user.macaddr[4], tempNodeInfo->user.macaddr[5]);
+            node["mac_address"] = new JSONValue(macStr);
+            node["hw_model"] = new JSONValue(tempNodeInfo->user.hw_model);
+
+            nodesArray.push_back(new JSONValue(node));
+        }
+        tempNodeInfo = nodeDB->readNextMeshNode(readIndex);
+    }
+
+    // collect data to inner data object
+    JSONObject jsonObjInner;
+    jsonObjInner["nodes"] = new JSONValue(nodesArray);
+
+    // create json output structure
+    JSONObject jsonObjOuter;
+    jsonObjOuter["data"] = new JSONValue(jsonObjInner);
+    jsonObjOuter["status"] = new JSONValue("ok");
+    // serialize and write it to the stream
+    JSONValue *value = new JSONValue(jsonObjOuter);
+    res->print(value->Stringify().c_str());
+    delete value;
+}
+
 /*
     This supports the Apple Captive Network Assistant (CNA) Portal
 */
diff --git a/src/mesh/http/ContentHandler.h b/src/mesh/http/ContentHandler.h
index 987e3ffef..2066a6d57 100644
--- a/src/mesh/http/ContentHandler.h
+++ b/src/mesh/http/ContentHandler.h
@@ -13,6 +13,7 @@ void handleFsBrowseStatic(HTTPRequest *req, HTTPResponse *res);
 void handleFsDeleteStatic(HTTPRequest *req, HTTPResponse *res);
 void handleBlinkLED(HTTPRequest *req, HTTPResponse *res);
 void handleReport(HTTPRequest *req, HTTPResponse *res);
+void handleNodes(HTTPRequest *req, HTTPResponse *res);
 void handleUpdateFs(HTTPRequest *req, HTTPResponse *res);
 void handleDeleteFsContent(HTTPRequest *req, HTTPResponse *res);
 void handleFs(HTTPRequest *req, HTTPResponse *res);
diff --git a/src/modules/RangeTestModule.cpp b/src/modules/RangeTestModule.cpp
index bf842ce55..c42839d97 100644
--- a/src/modules/RangeTestModule.cpp
+++ b/src/modules/RangeTestModule.cpp
@@ -155,8 +155,6 @@ ProcessMessage RangeTestModuleRadio::handleReceived(const meshtastic_MeshPacket
             LOG_DEBUG("mp.from          %d", mp.from);
             LOG_DEBUG("mp.rx_snr        %f", mp.rx_snr);
             LOG_DEBUG("mp.hop_limit     %d", mp.hop_limit);
-            // LOG_DEBUG("mp.decoded.position.latitude_i     %d", mp.decoded.position.latitude_i); // Deprecated
-            // LOG_DEBUG("mp.decoded.position.longitude_i    %d", mp.decoded.position.longitude_i); // Deprecated
             LOG_DEBUG("---- Node Information of Received Packet (mp.from):");
             LOG_DEBUG("n->user.long_name         %s", n->user.long_name);
             LOG_DEBUG("n->user.short_name        %s", n->user.short_name);
@@ -194,8 +192,6 @@ bool RangeTestModuleRadio::appendFile(const meshtastic_MeshPacket &mp)
         LOG_DEBUG("mp.from          %d", mp.from);
         LOG_DEBUG("mp.rx_snr        %f", mp.rx_snr);
         LOG_DEBUG("mp.hop_limit     %d", mp.hop_limit);
-        // LOG_DEBUG("mp.decoded.position.latitude_i     %d", mp.decoded.position.latitude_i);  // Deprecated
-        // LOG_DEBUG("mp.decoded.position.longitude_i    %d", mp.decoded.position.longitude_i); // Deprecated
         LOG_DEBUG("---- Node Information of Received Packet (mp.from):");
         LOG_DEBUG("n->user.long_name         %s", n->user.long_name);
         LOG_DEBUG("n->user.short_name        %s", n->user.short_name);
@@ -265,13 +261,21 @@ bool RangeTestModuleRadio::appendFile(const meshtastic_MeshPacket &mp)
         fileToAppend.printf("??:??:??,"); // Time
     }
 
-    fileToAppend.printf("%d,", getFrom(&mp));                     // From
-    fileToAppend.printf("%s,", n->user.long_name);                // Long Name
-    fileToAppend.printf("%f,", n->position.latitude_i * 1e-7);    // Sender Lat
-    fileToAppend.printf("%f,", n->position.longitude_i * 1e-7);   // Sender Long
-    fileToAppend.printf("%f,", gpsStatus->getLatitude() * 1e-7);  // RX Lat
-    fileToAppend.printf("%f,", gpsStatus->getLongitude() * 1e-7); // RX Long
-    fileToAppend.printf("%d,", gpsStatus->getAltitude());         // RX Altitude
+    fileToAppend.printf("%d,", getFrom(&mp));                   // From
+    fileToAppend.printf("%s,", n->user.long_name);              // Long Name
+    fileToAppend.printf("%f,", n->position.latitude_i * 1e-7);  // Sender Lat
+    fileToAppend.printf("%f,", n->position.longitude_i * 1e-7); // Sender Long
+    if (gpsStatus->getIsConnected() || config.position.fixed_position) {
+        fileToAppend.printf("%f,", gpsStatus->getLatitude() * 1e-7);  // RX Lat
+        fileToAppend.printf("%f,", gpsStatus->getLongitude() * 1e-7); // RX Long
+        fileToAppend.printf("%d,", gpsStatus->getAltitude());         // RX Altitude
+    } else {
+        // When the phone API is in use, the node info will be updated with position
+        meshtastic_NodeInfoLite *us = nodeDB->getMeshNode(nodeDB->getNodeNum());
+        fileToAppend.printf("%f,", us->position.latitude_i * 1e-7);  // RX Lat
+        fileToAppend.printf("%f,", us->position.longitude_i * 1e-7); // RX Long
+        fileToAppend.printf("%d,", us->position.altitude);           // RX Altitude
+    }
 
     fileToAppend.printf("%f,", mp.rx_snr); // RX SNR
 
@@ -292,4 +296,4 @@ bool RangeTestModuleRadio::appendFile(const meshtastic_MeshPacket &mp)
 #endif
 
     return 1;
-}
+}
\ No newline at end of file
diff --git a/src/modules/StoreForwardModule.cpp b/src/modules/StoreForwardModule.cpp
index 4cf06f5d2..0a6e1b4c4 100644
--- a/src/modules/StoreForwardModule.cpp
+++ b/src/modules/StoreForwardModule.cpp
@@ -73,11 +73,11 @@ void StoreForwardModule::populatePSRAM()
     LOG_DEBUG("Before PSRAM init: heap %d/%d PSRAM %d/%d", memGet.getFreeHeap(), memGet.getHeapSize(), memGet.getFreePsram(),
               memGet.getPsramSize());
 
-    /* Use a maximum of 2/3 the available PSRAM unless otherwise specified.
+    /* Use a maximum of 3/4 the available PSRAM unless otherwise specified.
         Note: This needs to be done after every thing that would use PSRAM
     */
     uint32_t numberOfPackets =
-        (this->records ? this->records : (((memGet.getFreePsram() / 3) * 2) / sizeof(PacketHistoryStruct)));
+        (this->records ? this->records : (((memGet.getFreePsram() / 4) * 3) / sizeof(PacketHistoryStruct)));
     this->records = numberOfPackets;
 #if defined(ARCH_ESP32)
     this->packetHistory = static_cast(ps_calloc(numberOfPackets, sizeof(PacketHistoryStruct)));
@@ -198,6 +198,9 @@ void StoreForwardModule::historyAdd(const meshtastic_MeshPacket &mp)
     this->packetHistory[this->packetHistoryTotalCount].to = mp.to;
     this->packetHistory[this->packetHistoryTotalCount].channel = mp.channel;
     this->packetHistory[this->packetHistoryTotalCount].from = getFrom(&mp);
+    this->packetHistory[this->packetHistoryTotalCount].id = mp.id;
+    this->packetHistory[this->packetHistoryTotalCount].reply_id = p.reply_id;
+    this->packetHistory[this->packetHistoryTotalCount].emoji = (bool)p.emoji;
     this->packetHistory[this->packetHistoryTotalCount].payload_size = p.payload.size;
     memcpy(this->packetHistory[this->packetHistoryTotalCount].payload, p.payload.bytes, meshtastic_Constants_DATA_PAYLOAD_LEN);
 
@@ -244,8 +247,11 @@ meshtastic_MeshPacket *StoreForwardModule::preparePayload(NodeNum dest, uint32_t
 
                 p->to = local ? this->packetHistory[i].to : dest; // PhoneAPI can handle original `to`
                 p->from = this->packetHistory[i].from;
+                p->id = this->packetHistory[i].id;
                 p->channel = this->packetHistory[i].channel;
+                p->decoded.reply_id = this->packetHistory[i].reply_id;
                 p->rx_time = this->packetHistory[i].time;
+                p->decoded.emoji = (uint32_t)this->packetHistory[i].emoji;
 
                 // Let's assume that if the server received the S&F request that the client is in range.
                 //   TODO: Make this configurable.
@@ -617,4 +623,4 @@ StoreForwardModule::StoreForwardModule()
         disable();
     }
 #endif
-}
+}
\ No newline at end of file
diff --git a/src/modules/StoreForwardModule.h b/src/modules/StoreForwardModule.h
index e3273470b..30db1625c 100644
--- a/src/modules/StoreForwardModule.h
+++ b/src/modules/StoreForwardModule.h
@@ -13,7 +13,10 @@ struct PacketHistoryStruct {
     uint32_t time;
     uint32_t to;
     uint32_t from;
+    uint32_t id;
     uint8_t channel;
+    uint32_t reply_id;
+    bool emoji;
     uint8_t payload[meshtastic_Constants_DATA_PAYLOAD_LEN];
     pb_size_t payload_size;
 };
diff --git a/src/modules/Telemetry/AirQualityTelemetry.cpp b/src/modules/Telemetry/AirQualityTelemetry.cpp
index 362d60252..6a8077f03 100644
--- a/src/modules/Telemetry/AirQualityTelemetry.cpp
+++ b/src/modules/Telemetry/AirQualityTelemetry.cpp
@@ -113,12 +113,18 @@ bool AirQualityTelemetryModule::getAirQualityTelemetry(meshtastic_Telemetry *m)
 
     m->time = getTime();
     m->which_variant = meshtastic_Telemetry_air_quality_metrics_tag;
+    m->variant.air_quality_metrics.has_pm10_standard = true;
     m->variant.air_quality_metrics.pm10_standard = data.pm10_standard;
+    m->variant.air_quality_metrics.has_pm25_standard = true;
     m->variant.air_quality_metrics.pm25_standard = data.pm25_standard;
+    m->variant.air_quality_metrics.has_pm100_standard = true;
     m->variant.air_quality_metrics.pm100_standard = data.pm100_standard;
 
+    m->variant.air_quality_metrics.has_pm10_environmental = true;
     m->variant.air_quality_metrics.pm10_environmental = data.pm10_env;
+    m->variant.air_quality_metrics.has_pm25_environmental = true;
     m->variant.air_quality_metrics.pm25_environmental = data.pm25_env;
+    m->variant.air_quality_metrics.has_pm100_environmental = true;
     m->variant.air_quality_metrics.pm100_environmental = data.pm100_env;
 
     LOG_INFO("Send: PM1.0(Standard)=%i, PM2.5(Standard)=%i, PM10.0(Standard)=%i", m->variant.air_quality_metrics.pm10_standard,
diff --git a/src/modules/Telemetry/PowerTelemetry.cpp b/src/modules/Telemetry/PowerTelemetry.cpp
index 367643849..10133fca5 100644
--- a/src/modules/Telemetry/PowerTelemetry.cpp
+++ b/src/modules/Telemetry/PowerTelemetry.cpp
@@ -56,6 +56,8 @@ int32_t PowerTelemetryModule::runOnce()
             // therefore, we should only enable the sensor loop if measurement is also enabled
             if (ina219Sensor.hasSensor() && !ina219Sensor.isInitialized())
                 result = ina219Sensor.runOnce();
+            if (ina226Sensor.hasSensor() && !ina226Sensor.isInitialized())
+                result = ina226Sensor.runOnce();
             if (ina260Sensor.hasSensor() && !ina260Sensor.isInitialized())
                 result = ina260Sensor.runOnce();
             if (ina3221Sensor.hasSensor() && !ina3221Sensor.isInitialized())
@@ -170,6 +172,8 @@ bool PowerTelemetryModule::getPowerTelemetry(meshtastic_Telemetry *m)
 #if HAS_TELEMETRY && !defined(ARCH_PORTDUINO)
     if (ina219Sensor.hasSensor())
         valid = ina219Sensor.getMetrics(m);
+    if (ina226Sensor.hasSensor())
+        valid = ina226Sensor.getMetrics(m);
     if (ina260Sensor.hasSensor())
         valid = ina260Sensor.getMetrics(m);
     if (ina3221Sensor.hasSensor())
@@ -253,4 +257,4 @@ bool PowerTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
     return false;
 }
 
-#endif
+#endif
\ No newline at end of file
diff --git a/src/modules/Telemetry/Sensor/CurrentSensor.h b/src/modules/Telemetry/Sensor/CurrentSensor.h
new file mode 100644
index 000000000..9827a9aa4
--- /dev/null
+++ b/src/modules/Telemetry/Sensor/CurrentSensor.h
@@ -0,0 +1,13 @@
+#include "configuration.h"
+
+#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
+
+#pragma once
+
+class CurrentSensor
+{
+  public:
+    virtual int16_t getCurrentMa() = 0;
+};
+
+#endif
\ No newline at end of file
diff --git a/src/modules/Telemetry/Sensor/INA219Sensor.cpp b/src/modules/Telemetry/Sensor/INA219Sensor.cpp
index de69163b4..ea47e265d 100644
--- a/src/modules/Telemetry/Sensor/INA219Sensor.cpp
+++ b/src/modules/Telemetry/Sensor/INA219Sensor.cpp
@@ -45,4 +45,9 @@ uint16_t INA219Sensor::getBusVoltageMv()
     return lround(ina219.getBusVoltage_V() * 1000);
 }
 
+int16_t INA219Sensor::getCurrentMa()
+{
+    return lround(ina219.getCurrent_mA());
+}
+
 #endif
\ No newline at end of file
diff --git a/src/modules/Telemetry/Sensor/INA219Sensor.h b/src/modules/Telemetry/Sensor/INA219Sensor.h
index 9dded067b..9b6a2fcca 100644
--- a/src/modules/Telemetry/Sensor/INA219Sensor.h
+++ b/src/modules/Telemetry/Sensor/INA219Sensor.h
@@ -3,11 +3,12 @@
 #if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
 
 #include "../mesh/generated/meshtastic/telemetry.pb.h"
+#include "CurrentSensor.h"
 #include "TelemetrySensor.h"
 #include "VoltageSensor.h"
 #include 
 
-class INA219Sensor : public TelemetrySensor, VoltageSensor
+class INA219Sensor : public TelemetrySensor, VoltageSensor, CurrentSensor
 {
   private:
     Adafruit_INA219 ina219;
@@ -20,6 +21,7 @@ class INA219Sensor : public TelemetrySensor, VoltageSensor
     virtual int32_t runOnce() override;
     virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
     virtual uint16_t getBusVoltageMv() override;
+    virtual int16_t getCurrentMa() override;
 };
 
 #endif
\ No newline at end of file
diff --git a/src/modules/Telemetry/Sensor/INA226Sensor.cpp b/src/modules/Telemetry/Sensor/INA226Sensor.cpp
new file mode 100644
index 000000000..1ee7cd92e
--- /dev/null
+++ b/src/modules/Telemetry/Sensor/INA226Sensor.cpp
@@ -0,0 +1,58 @@
+#include "configuration.h"
+
+#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
+
+#include "../mesh/generated/meshtastic/telemetry.pb.h"
+#include "INA226.h"
+#include "INA226Sensor.h"
+#include "TelemetrySensor.h"
+
+INA226Sensor::INA226Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_INA226, "INA226") {}
+
+int32_t INA226Sensor::runOnce()
+{
+    LOG_INFO("Init sensor: %s", sensorName);
+    if (!hasSensor()) {
+        return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
+    }
+
+    begin(nodeTelemetrySensorsMap[sensorType].second, nodeTelemetrySensorsMap[sensorType].first);
+
+    if (!status) {
+        status = ina226.begin();
+    }
+    return initI2CSensor();
+}
+
+void INA226Sensor::setup() {}
+
+void INA226Sensor::begin(TwoWire *wire, uint8_t addr)
+{
+    _wire = wire;
+    _addr = addr;
+    ina226 = INA226(_addr, _wire);
+    _wire->begin();
+}
+
+bool INA226Sensor::getMetrics(meshtastic_Telemetry *measurement)
+{
+    measurement->variant.environment_metrics.has_voltage = true;
+    measurement->variant.environment_metrics.has_current = true;
+
+    // mV conversion to V
+    measurement->variant.environment_metrics.voltage = ina226.getBusVoltage() / 1000;
+    measurement->variant.environment_metrics.current = ina226.getCurrent_mA();
+    return true;
+}
+
+uint16_t INA226Sensor::getBusVoltageMv()
+{
+    return lround(ina226.getBusVoltage());
+}
+
+int16_t INA226Sensor::getCurrentMa()
+{
+    return lround(ina226.getCurrent_mA());
+}
+
+#endif
\ No newline at end of file
diff --git a/src/modules/Telemetry/Sensor/INA226Sensor.h b/src/modules/Telemetry/Sensor/INA226Sensor.h
new file mode 100644
index 000000000..2f71c5b86
--- /dev/null
+++ b/src/modules/Telemetry/Sensor/INA226Sensor.h
@@ -0,0 +1,30 @@
+#include "configuration.h"
+
+#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
+
+#include "../mesh/generated/meshtastic/telemetry.pb.h"
+#include "CurrentSensor.h"
+#include "TelemetrySensor.h"
+#include "VoltageSensor.h"
+#include 
+
+class INA226Sensor : public TelemetrySensor, VoltageSensor, CurrentSensor
+{
+  private:
+    uint8_t _addr = INA_ADDR;
+    TwoWire *_wire = &Wire;
+    INA226 ina226 = INA226(_addr, _wire);
+
+  protected:
+    virtual void setup() override;
+    void begin(TwoWire *wire = &Wire, uint8_t addr = INA_ADDR);
+
+  public:
+    INA226Sensor();
+    virtual int32_t runOnce() override;
+    virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
+    virtual uint16_t getBusVoltageMv() override;
+    virtual int16_t getCurrentMa() override;
+};
+
+#endif
\ No newline at end of file
diff --git a/src/modules/Telemetry/Sensor/INA3221Sensor.cpp b/src/modules/Telemetry/Sensor/INA3221Sensor.cpp
index ed09856e2..7ac11dfde 100644
--- a/src/modules/Telemetry/Sensor/INA3221Sensor.cpp
+++ b/src/modules/Telemetry/Sensor/INA3221Sensor.cpp
@@ -102,4 +102,9 @@ uint16_t INA3221Sensor::getBusVoltageMv()
     return lround(ina3221.getVoltage(BAT_CH) * 1000);
 }
 
+int16_t INA3221Sensor::getCurrentMa()
+{
+    return lround(ina3221.getCurrent(BAT_CH));
+}
+
 #endif
\ No newline at end of file
diff --git a/src/modules/Telemetry/Sensor/INA3221Sensor.h b/src/modules/Telemetry/Sensor/INA3221Sensor.h
index d5121aab6..8eeda3e02 100644
--- a/src/modules/Telemetry/Sensor/INA3221Sensor.h
+++ b/src/modules/Telemetry/Sensor/INA3221Sensor.h
@@ -3,11 +3,12 @@
 #if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
 
 #include "../mesh/generated/meshtastic/telemetry.pb.h"
+#include "CurrentSensor.h"
 #include "TelemetrySensor.h"
 #include "VoltageSensor.h"
 #include 
 
-class INA3221Sensor : public TelemetrySensor, VoltageSensor
+class INA3221Sensor : public TelemetrySensor, VoltageSensor, CurrentSensor
 {
   private:
     INA3221 ina3221 = INA3221(INA3221_ADDR42_SDA);
@@ -35,6 +36,7 @@ class INA3221Sensor : public TelemetrySensor, VoltageSensor
     int32_t runOnce() override;
     bool getMetrics(meshtastic_Telemetry *measurement) override;
     virtual uint16_t getBusVoltageMv() override;
+    virtual int16_t getCurrentMa() override;
 };
 
 struct _INA3221Measurement {
diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp
index 967db04d6..d61e87855 100644
--- a/src/mqtt/MQTT.cpp
+++ b/src/mqtt/MQTT.cpp
@@ -19,24 +19,237 @@
 #include 
 #endif
 #include "Default.h"
+#if !defined(ARCH_NRF52) || NRF52_USE_JSON
 #include "serialization/JSON.h"
 #include "serialization/MeshPacketSerializer.h"
+#endif
 #include 
 #include 
+#include 
+#include 
 
-const int reconnectMax = 5;
+#include 
+#if defined(ARCH_PORTDUINO)
+#include 
+#elif !defined(ntohl)
+#include 
+#define ntohl __ntohl
+#endif
 
 MQTT *mqtt;
 
-static MemoryDynamic staticMqttPool;
-
-Allocator &mqttPool = staticMqttPool;
+namespace
+{
+constexpr int reconnectMax = 5;
 
 // FIXME - this size calculation is super sloppy, but it will go away once we dynamically alloc meshpackets
 static uint8_t bytes[meshtastic_MqttClientProxyMessage_size + 30]; // 12 for channel name and 16 for nodeid
 
 static bool isMqttServerAddressPrivate = false;
 
+// meshtastic_ServiceEnvelope that automatically releases dynamically allocated memory when it goes out of scope.
+struct DecodedServiceEnvelope : public meshtastic_ServiceEnvelope {
+    DecodedServiceEnvelope() = delete;
+    DecodedServiceEnvelope(const uint8_t *payload, size_t length)
+        : meshtastic_ServiceEnvelope(meshtastic_ServiceEnvelope_init_default),
+          validDecode(pb_decode_from_bytes(payload, length, &meshtastic_ServiceEnvelope_msg, this))
+    {
+    }
+    ~DecodedServiceEnvelope()
+    {
+        if (validDecode)
+            pb_release(&meshtastic_ServiceEnvelope_msg, this);
+    }
+    // Clients must check that this is true before using.
+    const bool validDecode;
+};
+
+inline void onReceiveProto(char *topic, byte *payload, size_t length)
+{
+    const DecodedServiceEnvelope e(payload, length);
+    if (!e.validDecode || e.channel_id == NULL || e.gateway_id == NULL || e.packet == NULL) {
+        LOG_ERROR("Invalid MQTT service envelope, topic %s, len %u!", topic, length);
+        return;
+    }
+    const meshtastic_Channel &ch = channels.getByName(e.channel_id);
+    if (strcmp(e.gateway_id, owner.id) == 0) {
+        // Generate an implicit ACK towards ourselves (handled and processed only locally!) for this message.
+        // We do this because packets are not rebroadcasted back into MQTT anymore and we assume that at least one node
+        // receives it when we get our own packet back. Then we'll stop our retransmissions.
+        if (isFromUs(e.packet))
+            routingModule->sendAckNak(meshtastic_Routing_Error_NONE, getFrom(e.packet), e.packet->id, ch.index);
+        else
+            LOG_INFO("Ignore downlink message we originally sent");
+        return;
+    }
+    if (isFromUs(e.packet)) {
+        LOG_INFO("Ignore downlink message we originally sent");
+        return;
+    }
+
+    // Find channel by channel_id and check downlink_enabled
+    if (!(strcmp(e.channel_id, "PKI") == 0 ||
+          (strcmp(e.channel_id, channels.getGlobalId(ch.index)) == 0 && ch.settings.downlink_enabled))) {
+        return;
+    }
+    LOG_INFO("Received MQTT topic %s, len=%u", topic, length);
+
+    UniquePacketPoolPacket p = packetPool.allocUniqueCopy(*e.packet);
+    p->via_mqtt = true; // Mark that the packet was received via MQTT
+
+    if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
+        if (moduleConfig.mqtt.encryption_enabled) {
+            LOG_INFO("Ignore decoded message on MQTT, encryption is enabled");
+            return;
+        }
+        if (p->decoded.portnum == meshtastic_PortNum_ADMIN_APP) {
+            LOG_INFO("Ignore decoded admin packet");
+            return;
+        }
+        p->channel = ch.index;
+    }
+
+    // PKI messages get accepted even if we can't decrypt
+    if (router && p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag && strcmp(e.channel_id, "PKI") == 0) {
+        const meshtastic_NodeInfoLite *tx = nodeDB->getMeshNode(getFrom(p.get()));
+        const meshtastic_NodeInfoLite *rx = nodeDB->getMeshNode(p->to);
+        // Only accept PKI messages to us, or if we have both the sender and receiver in our nodeDB, as then it's
+        // likely they discovered each other via a channel we have downlink enabled for
+        if (isToUs(p.get()) || (tx && tx->has_user && rx && rx->has_user))
+            router->enqueueReceivedMessage(p.release());
+    } else if (router && perhapsDecode(p.get())) // ignore messages if we don't have the channel key
+        router->enqueueReceivedMessage(p.release());
+}
+
+#if !defined(ARCH_NRF52) || NRF52_USE_JSON
+// returns true if this is a valid JSON envelope which we accept on downlink
+inline bool isValidJsonEnvelope(JSONObject &json)
+{
+    // if "sender" is provided, avoid processing packets we uplinked
+    return (json.find("sender") != json.end() ? (json["sender"]->AsString().compare(owner.id) != 0) : true) &&
+           (json.find("hopLimit") != json.end() ? json["hopLimit"]->IsNumber() : true) && // hop limit should be a number
+           (json.find("from") != json.end()) && json["from"]->IsNumber() &&
+           (json["from"]->AsNumber() == nodeDB->getNodeNum()) &&            // only accept message if the "from" is us
+           (json.find("type") != json.end()) && json["type"]->IsString() && // should specify a type
+           (json.find("payload") != json.end());                            // should have a payload
+}
+
+inline void onReceiveJson(byte *payload, size_t length)
+{
+    char payloadStr[length + 1];
+    memcpy(payloadStr, payload, length);
+    payloadStr[length] = 0; // null terminated string
+    std::unique_ptr json_value(JSON::Parse(payloadStr));
+    if (json_value == nullptr) {
+        LOG_ERROR("JSON received payload on MQTT but not a valid JSON");
+        return;
+    }
+
+    JSONObject json;
+    json = json_value->AsObject();
+
+    if (!isValidJsonEnvelope(json)) {
+        LOG_ERROR("JSON received payload on MQTT but not a valid envelope");
+        return;
+    }
+
+    // this is a valid envelope
+    if (json["type"]->AsString().compare("sendtext") == 0 && json["payload"]->IsString()) {
+        std::string jsonPayloadStr = json["payload"]->AsString();
+        LOG_INFO("JSON payload %s, length %u", jsonPayloadStr.c_str(), jsonPayloadStr.length());
+
+        // construct protobuf data packet using TEXT_MESSAGE, send it to the mesh
+        meshtastic_MeshPacket *p = router->allocForSending();
+        p->decoded.portnum = meshtastic_PortNum_TEXT_MESSAGE_APP;
+        if (json.find("channel") != json.end() && json["channel"]->IsNumber() &&
+            (json["channel"]->AsNumber() < channels.getNumChannels()))
+            p->channel = json["channel"]->AsNumber();
+        if (json.find("to") != json.end() && json["to"]->IsNumber())
+            p->to = json["to"]->AsNumber();
+        if (json.find("hopLimit") != json.end() && json["hopLimit"]->IsNumber())
+            p->hop_limit = json["hopLimit"]->AsNumber();
+        if (jsonPayloadStr.length() <= sizeof(p->decoded.payload.bytes)) {
+            memcpy(p->decoded.payload.bytes, jsonPayloadStr.c_str(), jsonPayloadStr.length());
+            p->decoded.payload.size = jsonPayloadStr.length();
+            service->sendToMesh(p, RX_SRC_LOCAL);
+        } else {
+            LOG_WARN("Received MQTT json payload too long, drop");
+        }
+    } else if (json["type"]->AsString().compare("sendposition") == 0 && json["payload"]->IsObject()) {
+        // invent the "sendposition" type for a valid envelope
+        JSONObject posit;
+        posit = json["payload"]->AsObject(); // get nested JSON Position
+        meshtastic_Position pos = meshtastic_Position_init_default;
+        if (posit.find("latitude_i") != posit.end() && posit["latitude_i"]->IsNumber())
+            pos.latitude_i = posit["latitude_i"]->AsNumber();
+        if (posit.find("longitude_i") != posit.end() && posit["longitude_i"]->IsNumber())
+            pos.longitude_i = posit["longitude_i"]->AsNumber();
+        if (posit.find("altitude") != posit.end() && posit["altitude"]->IsNumber())
+            pos.altitude = posit["altitude"]->AsNumber();
+        if (posit.find("time") != posit.end() && posit["time"]->IsNumber())
+            pos.time = posit["time"]->AsNumber();
+
+        // construct protobuf data packet using POSITION, send it to the mesh
+        meshtastic_MeshPacket *p = router->allocForSending();
+        p->decoded.portnum = meshtastic_PortNum_POSITION_APP;
+        if (json.find("channel") != json.end() && json["channel"]->IsNumber() &&
+            (json["channel"]->AsNumber() < channels.getNumChannels()))
+            p->channel = json["channel"]->AsNumber();
+        if (json.find("to") != json.end() && json["to"]->IsNumber())
+            p->to = json["to"]->AsNumber();
+        if (json.find("hopLimit") != json.end() && json["hopLimit"]->IsNumber())
+            p->hop_limit = json["hopLimit"]->AsNumber();
+        p->decoded.payload.size =
+            pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes), &meshtastic_Position_msg,
+                               &pos); // make the Data protobuf from position
+        service->sendToMesh(p, RX_SRC_LOCAL);
+    } else {
+        LOG_DEBUG("JSON ignore downlink message with unsupported type");
+    }
+}
+#endif
+
+/// Determines if the given IPAddress is a private IPv4 address, i.e. not routable on the public internet.
+bool isPrivateIpAddress(const IPAddress &ip)
+{
+    constexpr struct {
+        uint32_t network;
+        uint32_t mask;
+    } privateCidrRanges[] = {
+        {.network = 192u << 24 | 168 << 16, .mask = 0xffff0000}, // 192.168.0.0/16
+        {.network = 172u << 24 | 16 << 16, .mask = 0xfff00000},  // 172.16.0.0/12
+        {.network = 169u << 24 | 254 << 16, .mask = 0xffff0000}, // 169.254.0.0/16
+        {.network = 10u << 24, .mask = 0xff000000},              // 10.0.0.0/8
+        {.network = 127u << 24 | 1, .mask = 0xffffffff},         // 127.0.0.1/32
+    };
+    const uint32_t addr = ntohl(ip);
+    for (const auto &cidrRange : privateCidrRanges) {
+        if (cidrRange.network == (addr & cidrRange.mask)) {
+            LOG_INFO("MQTT server on a private IP");
+            return true;
+        }
+    }
+    return false;
+}
+
+// Separate a [:] string. Returns a pair containing the parsed host and port. If the port is
+// not present in the input string, or is invalid, the value of the `port` argument will be returned.
+std::pair parseHostAndPort(String server, uint16_t port = 0)
+{
+    const int delimIndex = server.indexOf(':');
+    if (delimIndex > 0) {
+        const long parsedPort = server.substring(delimIndex + 1, server.length()).toInt();
+        if (parsedPort < 1 || parsedPort > UINT16_MAX) {
+            LOG_WARN("Invalid MQTT port %d: %s", parsedPort, server.c_str());
+        } else {
+            port = parsedPort;
+        }
+        server[delimIndex] = 0;
+    }
+    return std::make_pair(std::move(server), port);
+}
+} // namespace
+
 void MQTT::mqttCallback(char *topic, byte *payload, unsigned int length)
 {
     mqtt->onReceive(topic, payload, length);
@@ -49,170 +262,32 @@ void MQTT::onClientProxyReceive(meshtastic_MqttClientProxyMessage msg)
 
 void MQTT::onReceive(char *topic, byte *payload, size_t length)
 {
-    meshtastic_ServiceEnvelope e = meshtastic_ServiceEnvelope_init_default;
-
-    if (moduleConfig.mqtt.json_enabled && (strncmp(topic, jsonTopic.c_str(), jsonTopic.length()) == 0)) {
-        // check if this is a json payload message by comparing the topic start
-        char payloadStr[length + 1];
-        memcpy(payloadStr, payload, length);
-        payloadStr[length] = 0; // null terminated string
-        JSONValue *json_value = JSON::Parse(payloadStr);
-        if (json_value != NULL) {
-            // check if it is a valid envelope
-            JSONObject json;
-            json = json_value->AsObject();
-
-            // parse the channel name from the topic string
-            // the topic has been checked above for having jsonTopic prefix, so just move past it
-            char *ptr = topic + jsonTopic.length();
-            ptr = strtok(ptr, "/") ? strtok(ptr, "/") : ptr; // if another "/" was added, parse string up to that character
-            meshtastic_Channel sendChannel = channels.getByName(ptr);
-            // We allow downlink JSON packets only on a channel named "mqtt"
-            if (strncasecmp(channels.getGlobalId(sendChannel.index), Channels::mqttChannel, strlen(Channels::mqttChannel)) == 0 &&
-                sendChannel.settings.downlink_enabled) {
-                if (isValidJsonEnvelope(json)) {
-                    // this is a valid envelope
-                    if (json["type"]->AsString().compare("sendtext") == 0 && json["payload"]->IsString()) {
-                        std::string jsonPayloadStr = json["payload"]->AsString();
-                        LOG_INFO("JSON payload %s, length %u", jsonPayloadStr.c_str(), jsonPayloadStr.length());
-
-                        // construct protobuf data packet using TEXT_MESSAGE, send it to the mesh
-                        meshtastic_MeshPacket *p = router->allocForSending();
-                        p->decoded.portnum = meshtastic_PortNum_TEXT_MESSAGE_APP;
-                        if (json.find("channel") != json.end() && json["channel"]->IsNumber() &&
-                            (json["channel"]->AsNumber() < channels.getNumChannels()))
-                            p->channel = json["channel"]->AsNumber();
-                        if (json.find("to") != json.end() && json["to"]->IsNumber())
-                            p->to = json["to"]->AsNumber();
-                        if (json.find("hopLimit") != json.end() && json["hopLimit"]->IsNumber())
-                            p->hop_limit = json["hopLimit"]->AsNumber();
-                        if (jsonPayloadStr.length() <= sizeof(p->decoded.payload.bytes)) {
-                            memcpy(p->decoded.payload.bytes, jsonPayloadStr.c_str(), jsonPayloadStr.length());
-                            p->decoded.payload.size = jsonPayloadStr.length();
-                            service->sendToMesh(p, RX_SRC_LOCAL);
-                        } else {
-                            LOG_WARN("Received MQTT json payload too long, drop");
-                        }
-                    } else if (json["type"]->AsString().compare("sendposition") == 0 && json["payload"]->IsObject()) {
-                        // invent the "sendposition" type for a valid envelope
-                        JSONObject posit;
-                        posit = json["payload"]->AsObject(); // get nested JSON Position
-                        meshtastic_Position pos = meshtastic_Position_init_default;
-                        if (posit.find("latitude_i") != posit.end() && posit["latitude_i"]->IsNumber())
-                            pos.latitude_i = posit["latitude_i"]->AsNumber();
-                        if (posit.find("longitude_i") != posit.end() && posit["longitude_i"]->IsNumber())
-                            pos.longitude_i = posit["longitude_i"]->AsNumber();
-                        if (posit.find("altitude") != posit.end() && posit["altitude"]->IsNumber())
-                            pos.altitude = posit["altitude"]->AsNumber();
-                        if (posit.find("time") != posit.end() && posit["time"]->IsNumber())
-                            pos.time = posit["time"]->AsNumber();
-
-                        // construct protobuf data packet using POSITION, send it to the mesh
-                        meshtastic_MeshPacket *p = router->allocForSending();
-                        p->decoded.portnum = meshtastic_PortNum_POSITION_APP;
-                        if (json.find("channel") != json.end() && json["channel"]->IsNumber() &&
-                            (json["channel"]->AsNumber() < channels.getNumChannels()))
-                            p->channel = json["channel"]->AsNumber();
-                        if (json.find("to") != json.end() && json["to"]->IsNumber())
-                            p->to = json["to"]->AsNumber();
-                        if (json.find("hopLimit") != json.end() && json["hopLimit"]->IsNumber())
-                            p->hop_limit = json["hopLimit"]->AsNumber();
-                        p->decoded.payload.size =
-                            pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes),
-                                               &meshtastic_Position_msg, &pos); // make the Data protobuf from position
-                        service->sendToMesh(p, RX_SRC_LOCAL);
-                    } else {
-                        LOG_DEBUG("JSON ignore downlink message with unsupported type");
-                    }
-                } else {
-                    LOG_ERROR("JSON received payload on MQTT but not a valid envelope");
-                }
-            } else {
-                LOG_WARN("JSON downlink received on channel not called 'mqtt' or without downlink enabled");
-            }
-        } else {
-            // no json, this is an invalid payload
-            LOG_ERROR("JSON received payload on MQTT but not a valid JSON");
-        }
-        delete json_value;
-    } else {
-        if (length == 0) {
-            LOG_WARN("Empty MQTT payload received, topic %s!", topic);
-            return;
-        } else if (!pb_decode_from_bytes(payload, length, &meshtastic_ServiceEnvelope_msg, &e)) {
-            LOG_ERROR("Invalid MQTT service envelope, topic %s, len %u!", topic, length);
-            return;
-        } else {
-            if (e.channel_id == NULL || e.gateway_id == NULL) {
-                LOG_ERROR("Invalid MQTT service envelope, topic %s, len %u!", topic, length);
-                return;
-            }
-            meshtastic_Channel ch = channels.getByName(e.channel_id);
-            if (strcmp(e.gateway_id, owner.id) == 0) {
-                // Generate an implicit ACK towards ourselves (handled and processed only locally!) for this message.
-                // We do this because packets are not rebroadcasted back into MQTT anymore and we assume that at least one node
-                // receives it when we get our own packet back. Then we'll stop our retransmissions.
-                if (e.packet && isFromUs(e.packet))
-                    routingModule->sendAckNak(meshtastic_Routing_Error_NONE, getFrom(e.packet), e.packet->id, ch.index);
-                else
-                    LOG_INFO("Ignore downlink message we originally sent");
-            } else {
-                // Find channel by channel_id and check downlink_enabled
-                if ((strcmp(e.channel_id, "PKI") == 0 && e.packet) ||
-                    (strcmp(e.channel_id, channels.getGlobalId(ch.index)) == 0 && e.packet && ch.settings.downlink_enabled)) {
-                    LOG_INFO("Received MQTT topic %s, len=%u", topic, length);
-                    meshtastic_MeshPacket *p = packetPool.allocCopy(*e.packet);
-                    p->via_mqtt = true; // Mark that the packet was received via MQTT
-
-                    if (isFromUs(p)) {
-                        LOG_INFO("Ignore downlink message we originally sent");
-                        packetPool.release(p);
-                        free(e.channel_id);
-                        free(e.gateway_id);
-                        free(e.packet);
-                        return;
-                    }
-                    if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
-                        if (moduleConfig.mqtt.encryption_enabled) {
-                            LOG_INFO("Ignore decoded message on MQTT, encryption is enabled");
-                            packetPool.release(p);
-                            free(e.channel_id);
-                            free(e.gateway_id);
-                            free(e.packet);
-                            return;
-                        }
-                        if (p->decoded.portnum == meshtastic_PortNum_ADMIN_APP) {
-                            LOG_INFO("Ignore decoded admin packet");
-                            packetPool.release(p);
-                            free(e.channel_id);
-                            free(e.gateway_id);
-                            free(e.packet);
-                            return;
-                        }
-                        p->channel = ch.index;
-                    }
-
-                    // PKI messages get accepted even if we can't decrypt
-                    if (router && p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag &&
-                        strcmp(e.channel_id, "PKI") == 0) {
-                        const meshtastic_NodeInfoLite *tx = nodeDB->getMeshNode(getFrom(p));
-                        const meshtastic_NodeInfoLite *rx = nodeDB->getMeshNode(p->to);
-                        // Only accept PKI messages to us, or if we have both the sender and receiver in our nodeDB, as then it's
-                        // likely they discovered each other via a channel we have downlink enabled for
-                        if (isToUs(p) || (tx && tx->has_user && rx && rx->has_user))
-                            router->enqueueReceivedMessage(p);
-                    } else if (router && perhapsDecode(p)) // ignore messages if we don't have the channel key
-                        router->enqueueReceivedMessage(p);
-                    else
-                        packetPool.release(p);
-                }
-            }
-        }
-        // make sure to free both strings and the MeshPacket (passing in NULL is acceptable)
-        free(e.channel_id);
-        free(e.gateway_id);
-        free(e.packet);
+    if (length == 0) {
+        LOG_WARN("Empty MQTT payload received, topic %s!", topic);
+        return;
     }
+
+    // check if this is a json payload message by comparing the topic start
+    if (moduleConfig.mqtt.json_enabled && (strncmp(topic, jsonTopic.c_str(), jsonTopic.length()) == 0)) {
+#if !defined(ARCH_NRF52) || NRF52_USE_JSON
+        // parse the channel name from the topic string
+        // the topic has been checked above for having jsonTopic prefix, so just move past it
+        char *channelName = topic + jsonTopic.length();
+        // if another "/" was added, parse string up to that character
+        channelName = strtok(channelName, "/") ? strtok(channelName, "/") : channelName;
+        // We allow downlink JSON packets only on a channel named "mqtt"
+        meshtastic_Channel &sendChannel = channels.getByName(channelName);
+        if (!(strncasecmp(channels.getGlobalId(sendChannel.index), Channels::mqttChannel, strlen(Channels::mqttChannel)) == 0 &&
+              sendChannel.settings.downlink_enabled)) {
+            LOG_WARN("JSON downlink received on channel not called 'mqtt' or without downlink enabled");
+            return;
+        }
+        onReceiveJson(payload, length);
+#endif
+        return;
+    }
+
+    onReceiveProto(topic, payload, length);
 }
 
 void mqttInit()
@@ -249,10 +324,9 @@ MQTT::MQTT() : concurrency::OSThread("mqtt"), mqttQueue(MAX_MQTT_QUEUE)
                 moduleConfig.mqtt.map_report_settings.publish_interval_secs, default_map_publish_interval_secs);
         }
 
-        isMqttServerAddressPrivate = isPrivateIpAddress(moduleConfig.mqtt.address);
-        if (isMqttServerAddressPrivate) {
-            LOG_INFO("MQTT server on a private IP");
-        }
+        IPAddress ip;
+        isMqttServerAddressPrivate =
+            ip.fromString(parseHostAndPort(moduleConfig.mqtt.address).first.c_str()) && isPrivateIpAddress(ip);
 
 #if HAS_NETWORKING
         if (!moduleConfig.mqtt.proxy_to_client_enabled)
@@ -368,14 +442,9 @@ void MQTT::reconnect()
         pubSub.setClient(mqttClient);
 #endif
 
-        String server = String(serverAddr);
-        int delimIndex = server.indexOf(':');
-        if (delimIndex > 0) {
-            String port = server.substring(delimIndex + 1, server.length());
-            server[delimIndex] = 0;
-            serverPort = port.toInt();
-            serverAddr = server.c_str();
-        }
+        std::pair hostAndPort = parseHostAndPort(serverAddr, serverPort);
+        serverAddr = hostAndPort.first.c_str();
+        serverPort = hostAndPort.second;
         pubSub.setServer(serverAddr, serverPort);
         pubSub.setBufferSize(512);
 
@@ -504,39 +573,37 @@ void MQTT::publishNodeInfo()
 }
 void MQTT::publishQueuedMessages()
 {
-    if (!mqttQueue.isEmpty()) {
-        LOG_DEBUG("Publish enqueued MQTT message");
-        meshtastic_ServiceEnvelope *env = mqttQueue.dequeuePtr(0);
-        size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, env);
-        std::string topic;
-        if (env->packet->pki_encrypted) {
-            topic = cryptTopic + "PKI/" + owner.id;
-        } else {
-            topic = cryptTopic + env->channel_id + "/" + owner.id;
-        }
-        LOG_INFO("publish %s, %u bytes from queue", topic.c_str(), numBytes);
+    if (mqttQueue.isEmpty())
+        return;
 
-        publish(topic.c_str(), bytes, numBytes, false);
+    LOG_DEBUG("Publish enqueued MQTT message");
+    const std::unique_ptr entry(mqttQueue.dequeuePtr(0));
+    LOG_INFO("publish %s, %u bytes from queue", entry->topic.c_str(), entry->envBytes.size());
+    publish(entry->topic.c_str(), entry->envBytes.data(), entry->envBytes.size(), false);
 
 #if !defined(ARCH_NRF52) ||                                                                                                      \
     defined(NRF52_USE_JSON) // JSON is not supported on nRF52, see issue #2804 ### Fixed by using ArduinoJson ###
-        if (moduleConfig.mqtt.json_enabled) {
-            // handle json topic
-            auto jsonString = MeshPacketSerializer::JsonSerialize(env->packet);
-            if (jsonString.length() != 0) {
-                std::string topicJson;
-                if (env->packet->pki_encrypted) {
-                    topicJson = jsonTopic + "PKI/" + owner.id;
-                } else {
-                    topicJson = jsonTopic + env->channel_id + "/" + owner.id;
-                }
-                LOG_INFO("JSON publish message to %s, %u bytes: %s", topicJson.c_str(), jsonString.length(), jsonString.c_str());
-                publish(topicJson.c_str(), jsonString.c_str(), false);
-            }
-        }
-#endif // ARCH_NRF52 NRF52_USE_JSON
-        mqttPool.release(env);
+    if (!moduleConfig.mqtt.json_enabled)
+        return;
+
+    // handle json topic
+    const DecodedServiceEnvelope env(entry->envBytes.data(), entry->envBytes.size());
+    if (!env.validDecode || env.packet == NULL || env.channel_id == NULL)
+        return;
+
+    auto jsonString = MeshPacketSerializer::JsonSerialize(env.packet);
+    if (jsonString.length() == 0)
+        return;
+
+    std::string topicJson;
+    if (env.packet->pki_encrypted) {
+        topicJson = jsonTopic + "PKI/" + owner.id;
+    } else {
+        topicJson = jsonTopic + env.channel_id + "/" + owner.id;
     }
+    LOG_INFO("JSON publish message to %s, %u bytes: %s", topicJson.c_str(), jsonString.length(), jsonString.c_str());
+    publish(topicJson.c_str(), jsonString.c_str(), false);
+#endif // ARCH_NRF52 NRF52_USE_JSON
 }
 
 void MQTT::onSend(const meshtastic_MeshPacket &mp_encrypted, const meshtastic_MeshPacket &mp_decoded, ChannelIndex chIndex)
@@ -575,59 +642,56 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp_encrypted, const meshtastic_Me
     // Either encrypted packet (we couldn't decrypt) is marked as pki_encrypted, or we could decode the PKI encrypted packet
     bool isPKIEncrypted = mp_encrypted.pki_encrypted || mp_decoded.pki_encrypted;
     // If it was to a channel, check uplink enabled, else must be pki_encrypted
-    if ((ch.settings.uplink_enabled && !isPKIEncrypted) || isPKIEncrypted) {
-        const char *channelId = isPKIEncrypted ? "PKI" : channels.getGlobalId(chIndex);
+    if (!(ch.settings.uplink_enabled || isPKIEncrypted))
+        return;
+    const char *channelId = isPKIEncrypted ? "PKI" : channels.getGlobalId(chIndex);
 
-        meshtastic_ServiceEnvelope *env = mqttPool.allocZeroed();
-        env->channel_id = (char *)channelId;
-        env->gateway_id = owner.id;
+    LOG_DEBUG("MQTT onSend - Publish ");
+    const meshtastic_MeshPacket *p;
+    if (moduleConfig.mqtt.encryption_enabled) {
+        p = &mp_encrypted;
+        LOG_DEBUG("encrypted message");
+    } else if (mp_decoded.which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
+        p = &mp_decoded;
+        LOG_DEBUG("portnum %i message", mp_decoded.decoded.portnum);
+    } else {
+        LOG_DEBUG("nothing, pkt not decrypted");
+        return; // Don't upload a still-encrypted PKI packet if not encryption_enabled
+    }
 
-        LOG_DEBUG("MQTT onSend - Publish ");
-        if (moduleConfig.mqtt.encryption_enabled) {
-            env->packet = (meshtastic_MeshPacket *)&mp_encrypted;
-            LOG_DEBUG("encrypted message");
-        } else if (mp_decoded.which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
-            env->packet = (meshtastic_MeshPacket *)&mp_decoded;
-            LOG_DEBUG("portnum %i message", env->packet->decoded.portnum);
-        } else {
-            LOG_DEBUG("nothing, pkt not decrypted");
-            mqttPool.release(env);
-            return; // Don't upload a still-encrypted PKI packet if not encryption_enabled
-        }
+    const meshtastic_ServiceEnvelope env = {
+        .packet = const_cast(p), .channel_id = const_cast(channelId), .gateway_id = owner.id};
+    size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, &env);
+    std::string topic = cryptTopic + channelId + "/" + owner.id;
 
-        if (moduleConfig.mqtt.proxy_to_client_enabled || this->isConnectedDirectly()) {
-            size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, env);
-            std::string topic = cryptTopic + channelId + "/" + owner.id;
-            LOG_DEBUG("MQTT Publish %s, %u bytes", topic.c_str(), numBytes);
-
-            publish(topic.c_str(), bytes, numBytes, false);
+    if (moduleConfig.mqtt.proxy_to_client_enabled || this->isConnectedDirectly()) {
+        LOG_DEBUG("MQTT Publish %s, %u bytes", topic.c_str(), numBytes);
+        publish(topic.c_str(), bytes, numBytes, false);
 
 #if !defined(ARCH_NRF52) ||                                                                                                      \
     defined(NRF52_USE_JSON) // JSON is not supported on nRF52, see issue #2804 ### Fixed by using ArduinoJson ###
-            if (moduleConfig.mqtt.json_enabled) {
-                // handle json topic
-                auto jsonString = MeshPacketSerializer::JsonSerialize((meshtastic_MeshPacket *)&mp_decoded);
-                if (jsonString.length() != 0) {
-                    std::string topicJson = jsonTopic + channelId + "/" + owner.id;
-                    LOG_INFO("JSON publish message to %s, %u bytes: %s", topicJson.c_str(), jsonString.length(),
-                             jsonString.c_str());
-                    publish(topicJson.c_str(), jsonString.c_str(), false);
-                }
-            }
+        if (!moduleConfig.mqtt.json_enabled)
+            return;
+        // handle json topic
+        auto jsonString = MeshPacketSerializer::JsonSerialize(&mp_decoded);
+        if (jsonString.length() == 0)
+            return;
+        std::string topicJson = jsonTopic + channelId + "/" + owner.id;
+        LOG_INFO("JSON publish message to %s, %u bytes: %s", topicJson.c_str(), jsonString.length(), jsonString.c_str());
+        publish(topicJson.c_str(), jsonString.c_str(), false);
 #endif // ARCH_NRF52 NRF52_USE_JSON
+    } else {
+        LOG_INFO("MQTT not connected, queue packet");
+        QueueEntry *entry;
+        if (mqttQueue.numFree() == 0) {
+            LOG_WARN("MQTT queue is full, discard oldest");
+            entry = mqttQueue.dequeuePtr(0);
         } else {
-            LOG_INFO("MQTT not connected, queue packet");
-            if (mqttQueue.numFree() == 0) {
-                LOG_WARN("MQTT queue is full, discard oldest");
-                meshtastic_ServiceEnvelope *d = mqttQueue.dequeuePtr(0);
-                if (d)
-                    mqttPool.release(d);
-            }
-            // make a copy of serviceEnvelope and queue it
-            meshtastic_ServiceEnvelope *copied = mqttPool.allocCopy(*env);
-            assert(mqttQueue.enqueue(copied, 0));
+            entry = new QueueEntry;
         }
-        mqttPool.release(env);
+        entry->topic = std::move(topic);
+        entry->envBytes.assign(bytes, numBytes);
+        assert(mqttQueue.enqueue(entry, 0));
     }
 }
 
@@ -636,148 +700,68 @@ void MQTT::perhapsReportToMap()
     if (!moduleConfig.mqtt.map_reporting_enabled || !(moduleConfig.mqtt.proxy_to_client_enabled || isConnectedDirectly()))
         return;
 
-    if (Throttle::isWithinTimespanMs(last_report_to_map, map_publish_interval_msecs)) {
+    if (Throttle::isWithinTimespanMs(last_report_to_map, map_publish_interval_msecs))
         return;
-    } else {
-        if (map_position_precision == 0 || (localPosition.latitude_i == 0 && localPosition.longitude_i == 0)) {
-            last_report_to_map = millis();
-            if (map_position_precision == 0)
-                LOG_WARN("MQTT Map report enabled, but precision is 0");
-            if (localPosition.latitude_i == 0 && localPosition.longitude_i == 0)
-                LOG_WARN("MQTT Map report enabled, but no position available");
-            return;
-        }
 
-        // Allocate ServiceEnvelope and fill it
-        meshtastic_ServiceEnvelope *se = mqttPool.allocZeroed();
-        se->channel_id = (char *)channels.getGlobalId(channels.getPrimaryIndex()); // Use primary channel as the channel_id
-        se->gateway_id = owner.id;
-
-        // Allocate MeshPacket and fill it
-        meshtastic_MeshPacket *mp = packetPool.allocZeroed();
-        mp->which_payload_variant = meshtastic_MeshPacket_decoded_tag;
-        mp->from = nodeDB->getNodeNum();
-        mp->to = NODENUM_BROADCAST;
-        mp->decoded.portnum = meshtastic_PortNum_MAP_REPORT_APP;
-
-        // Fill MapReport message
-        meshtastic_MapReport mapReport = meshtastic_MapReport_init_default;
-        memcpy(mapReport.long_name, owner.long_name, sizeof(owner.long_name));
-        memcpy(mapReport.short_name, owner.short_name, sizeof(owner.short_name));
-        mapReport.role = config.device.role;
-        mapReport.hw_model = owner.hw_model;
-        strncpy(mapReport.firmware_version, optstr(APP_VERSION), sizeof(mapReport.firmware_version));
-        mapReport.region = config.lora.region;
-        mapReport.modem_preset = config.lora.modem_preset;
-        mapReport.has_default_channel = channels.hasDefaultChannel();
-
-        // Set position with precision (same as in PositionModule)
-        if (map_position_precision < 32 && map_position_precision > 0) {
-            mapReport.latitude_i = localPosition.latitude_i & (UINT32_MAX << (32 - map_position_precision));
-            mapReport.longitude_i = localPosition.longitude_i & (UINT32_MAX << (32 - map_position_precision));
-            mapReport.latitude_i += (1 << (31 - map_position_precision));
-            mapReport.longitude_i += (1 << (31 - map_position_precision));
-        } else {
-            mapReport.latitude_i = localPosition.latitude_i;
-            mapReport.longitude_i = localPosition.longitude_i;
-        }
-        mapReport.altitude = localPosition.altitude;
-        mapReport.position_precision = map_position_precision;
-
-        mapReport.num_online_local_nodes = nodeDB->getNumOnlineMeshNodes(true);
-
-        // Encode MapReport message and set it to MeshPacket in ServiceEnvelope
-        mp->decoded.payload.size = pb_encode_to_bytes(mp->decoded.payload.bytes, sizeof(mp->decoded.payload.bytes),
-                                                      &meshtastic_MapReport_msg, &mapReport);
-        se->packet = mp;
-
-        size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, se);
-
-        LOG_INFO("MQTT Publish map report to %s", mapTopic.c_str());
-        publish(mapTopic.c_str(), bytes, numBytes, false);
-
-        // Release the allocated memory for ServiceEnvelope and MeshPacket
-        mqttPool.release(se);
-        packetPool.release(mp);
-
-        // Update the last report time
+    if (map_position_precision == 0 || (localPosition.latitude_i == 0 && localPosition.longitude_i == 0)) {
         last_report_to_map = millis();
-    }
-}
-
-bool MQTT::isValidJsonEnvelope(JSONObject &json)
-{
-    // if "sender" is provided, avoid processing packets we uplinked
-    return (json.find("sender") != json.end() ? (json["sender"]->AsString().compare(owner.id) != 0) : true) &&
-           (json.find("hopLimit") != json.end() ? json["hopLimit"]->IsNumber() : true) && // hop limit should be a number
-           (json.find("from") != json.end()) && json["from"]->IsNumber() &&
-           (json["from"]->AsNumber() == nodeDB->getNodeNum()) &&            // only accept message if the "from" is us
-           (json.find("type") != json.end()) && json["type"]->IsString() && // should specify a type
-           (json.find("payload") != json.end());                            // should have a payload
-}
-
-bool MQTT::isPrivateIpAddress(const char address[])
-{
-    // Min. length like 10.0.0.0 (8), max like 192.168.255.255:65535 (21)
-    size_t length = strlen(address);
-    if (length < 8 || length > 21) {
-        return false;
+        if (map_position_precision == 0)
+            LOG_WARN("MQTT Map report enabled, but precision is 0");
+        if (localPosition.latitude_i == 0 && localPosition.longitude_i == 0)
+            LOG_WARN("MQTT Map report enabled, but no position available");
+        return;
     }
 
-    // Ensure the address contains only digits and dots and maybe a colon.
-    // Some limited validation is done.
-    // Even if it's not a valid IP address, we will know it's not a domain.
-    bool hasColon = false;
-    int numDots = 0;
-    for (size_t i = 0; i < length; i++) {
-        if (!isdigit(address[i]) && address[i] != '.' && address[i] != ':') {
-            return false;
-        }
+    // Allocate MeshPacket and fill it
+    meshtastic_MeshPacket *mp = packetPool.allocZeroed();
+    mp->which_payload_variant = meshtastic_MeshPacket_decoded_tag;
+    mp->from = nodeDB->getNodeNum();
+    mp->to = NODENUM_BROADCAST;
+    mp->decoded.portnum = meshtastic_PortNum_MAP_REPORT_APP;
 
-        // Dots can't be the first character, immediately follow another dot,
-        // occur more than 3 times, or occur after a colon.
-        if (address[i] == '.') {
-            if (++numDots > 3 || i == 0 || address[i - 1] == '.' || hasColon) {
-                return false;
-            }
-        }
-        // There can only be a single colon, and it can only occur after 3 dots
-        else if (address[i] == ':') {
-            if (hasColon || numDots < 3) {
-                return false;
-            }
+    // Fill MapReport message
+    meshtastic_MapReport mapReport = meshtastic_MapReport_init_default;
+    memcpy(mapReport.long_name, owner.long_name, sizeof(owner.long_name));
+    memcpy(mapReport.short_name, owner.short_name, sizeof(owner.short_name));
+    mapReport.role = config.device.role;
+    mapReport.hw_model = owner.hw_model;
+    strncpy(mapReport.firmware_version, optstr(APP_VERSION), sizeof(mapReport.firmware_version));
+    mapReport.region = config.lora.region;
+    mapReport.modem_preset = config.lora.modem_preset;
+    mapReport.has_default_channel = channels.hasDefaultChannel();
 
-            hasColon = true;
-        }
+    // Set position with precision (same as in PositionModule)
+    if (map_position_precision < 32 && map_position_precision > 0) {
+        mapReport.latitude_i = localPosition.latitude_i & (UINT32_MAX << (32 - map_position_precision));
+        mapReport.longitude_i = localPosition.longitude_i & (UINT32_MAX << (32 - map_position_precision));
+        mapReport.latitude_i += (1 << (31 - map_position_precision));
+        mapReport.longitude_i += (1 << (31 - map_position_precision));
+    } else {
+        mapReport.latitude_i = localPosition.latitude_i;
+        mapReport.longitude_i = localPosition.longitude_i;
     }
+    mapReport.altitude = localPosition.altitude;
+    mapReport.position_precision = map_position_precision;
 
-    // Final validation for IPv4 address and port format.
-    // Note that the values of octets haven't been tested, only the address format.
-    if (numDots != 3) {
-        return false;
-    }
+    mapReport.num_online_local_nodes = nodeDB->getNumOnlineMeshNodes(true);
 
-    // Check the easy ones first.
-    if (strcmp(address, "127.0.0.1") == 0 || strncmp(address, "10.", 3) == 0 || strncmp(address, "192.168", 7) == 0 ||
-        strncmp(address, "169.254", 7) == 0) {
-        return true;
-    }
+    // Encode MapReport message into the MeshPacket
+    mp->decoded.payload.size =
+        pb_encode_to_bytes(mp->decoded.payload.bytes, sizeof(mp->decoded.payload.bytes), &meshtastic_MapReport_msg, &mapReport);
 
-    // See if it's definitely not a 172 address.
-    if (strncmp(address, "172", 3) != 0) {
-        return false;
-    }
+    // Encode the MeshPacket into a binary ServiceEnvelope and publish
+    const meshtastic_ServiceEnvelope se = {
+        .packet = mp,
+        .channel_id = (char *)channels.getGlobalId(channels.getPrimaryIndex()), // Use primary channel as the channel_id
+        .gateway_id = owner.id};
+    size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, &se);
 
-    // We know it's a 172 address, now see if the second octet is 2 digits.
-    if (address[6] != '.') {
-        return false;
-    }
+    LOG_INFO("MQTT Publish map report to %s", mapTopic.c_str());
+    publish(mapTopic.c_str(), bytes, numBytes, false);
 
-    // Copy the second octet into a secondary buffer we can null-terminate and parse.
-    char octet2[3];
-    strncpy(octet2, address + 4, 2);
-    octet2[2] = 0;
+    // Release the allocated memory for MeshPacket
+    packetPool.release(mp);
 
-    int octet2Num = atoi(octet2);
-    return octet2Num >= 16 && octet2Num <= 31;
-}
+    // Update the last report time
+    last_report_to_map = millis();
+}
\ No newline at end of file
diff --git a/src/mqtt/MQTT.h b/src/mqtt/MQTT.h
index 7e0378238..81892f6f3 100644
--- a/src/mqtt/MQTT.h
+++ b/src/mqtt/MQTT.h
@@ -5,7 +5,9 @@
 #include "concurrency/OSThread.h"
 #include "mesh/Channels.h"
 #include "mesh/generated/meshtastic/mqtt.pb.h"
+#if !defined(ARCH_NRF52) || NRF52_USE_JSON
 #include "serialization/JSON.h"
+#endif
 #if HAS_WIFI
 #include 
 #if !defined(ARCH_PORTDUINO)
@@ -78,7 +80,11 @@ class MQTT : private concurrency::OSThread
     void start() { setIntervalFromNow(0); };
 
   protected:
-    PointerQueue mqttQueue;
+    struct QueueEntry {
+        std::string topic;
+        std::basic_string envBytes; // binary/pb_encode_to_bytes ServiceEnvelope
+    };
+    PointerQueue mqttQueue;
 
     int reconnectCount = 0;
 
@@ -117,17 +123,10 @@ class MQTT : private concurrency::OSThread
     // Check if we should report unencrypted information about our node for consumption by a map
     void perhapsReportToMap();
 
-    // returns true if this is a valid JSON envelope which we accept on downlink
-    bool isValidJsonEnvelope(JSONObject &json);
-
-    /// Determines if the given address is a private IPv4 address, i.e. not routable on the public internet.
-    /// These are the ranges: 127.0.0.1, 10.0.0.0-10.255.255.255, 172.16.0.0-172.31.255.255, 192.168.0.0-192.168.255.255.
-    bool isPrivateIpAddress(const char address[]);
-
     /// Return 0 if sleep is okay, veto sleep if we are connected to pubsub server
     // int preflightSleepCb(void *unused = NULL) { return pubSub.connected() ? 1 : 0; }
 };
 
 void mqttInit();
 
-extern MQTT *mqtt;
+extern MQTT *mqtt;
\ No newline at end of file
diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp
index fb399bf1e..f798d9103 100644
--- a/src/platform/portduino/PortduinoGlue.cpp
+++ b/src/platform/portduino/PortduinoGlue.cpp
@@ -21,9 +21,12 @@
 #include 
 #include 
 
+#include "platform/portduino/USBHal.h"
+
 std::map settingsMap;
 std::map settingsStrings;
 std::ofstream traceFile;
+Ch341Hal *ch341Hal = nullptr;
 char *configPath = nullptr;
 char *optionMac = nullptr;
 
@@ -87,7 +90,8 @@ void getMacAddr(uint8_t *dmac)
         if (strlen(optionMac) >= 12) {
             MAC_from_string(optionMac, dmac);
         } else {
-            uint32_t hwId = sscanf(optionMac, "%u", &hwId);
+            uint32_t hwId;
+            sscanf(optionMac, "%u", &hwId);
             dmac[0] = 0x80;
             dmac[1] = 0;
             dmac[2] = hwId >> 24;
@@ -103,7 +107,6 @@ void getMacAddr(uint8_t *dmac)
         struct hci_dev_info di;
         di.dev_id = 0;
         bdaddr_t bdaddr;
-        char addr[18];
         int btsock;
         btsock = socket(AF_BLUETOOTH, SOCK_RAW, 1);
         if (btsock < 0) { // If anything fails, just return with the default value
@@ -201,8 +204,36 @@ void portduinoSetup()
             }
         }
     }
-
+    // if we're using a usermode driver, we need to initialize it here, to get a serial number back for mac address
     uint8_t dmac[6] = {0};
+    if (settingsStrings[spidev] == "ch341") {
+        ch341Hal = new Ch341Hal(0);
+        if (settingsStrings[lora_usb_serial_num] != "") {
+            ch341Hal->serial = settingsStrings[lora_usb_serial_num];
+        }
+        ch341Hal->vid = settingsMap[lora_usb_vid];
+        ch341Hal->pid = settingsMap[lora_usb_pid];
+        ch341Hal->init();
+        if (!ch341Hal->isInit()) {
+            std::cout << "Could not initialize CH341 device!" << std::endl;
+            exit(EXIT_FAILURE);
+        }
+        char serial[9] = {0};
+        ch341Hal->getSerialString(serial, 8);
+        std::cout << "Serial " << serial << std::endl;
+        if (strlen(serial) == 8 && settingsStrings[mac_address].length() < 12) {
+            uint8_t hash[32] = {0};
+            memcpy(hash, serial, 8);
+            crypto->hash(hash, 8);
+            dmac[0] = (hash[0] << 4) | 2;
+            dmac[1] = hash[1];
+            dmac[2] = hash[2];
+            dmac[3] = hash[3];
+            dmac[4] = hash[4];
+            dmac[5] = hash[5];
+        }
+    }
+
     getMacAddr(dmac);
     if (dmac[0] == 0 && dmac[1] == 0 && dmac[2] == 0 && dmac[3] == 0 && dmac[4] == 0 && dmac[5] == 0) {
         std::cout << "*** Blank MAC Address not allowed!" << std::endl;
@@ -225,47 +256,11 @@ void portduinoSetup()
     // Need to bind all the configured GPIO pins so they're not simulated
     // TODO: Can we do this in the for loop above?
     // TODO: If one of these fails, we should log and terminate
-    if (settingsMap.count(cs) > 0 && settingsMap[cs] != RADIOLIB_NC) {
-        if (initGPIOPin(settingsMap[cs], gpioChipName) != ERRNO_OK) {
-            settingsMap[cs] = RADIOLIB_NC;
-        }
-    }
-    if (settingsMap.count(irq) > 0 && settingsMap[irq] != RADIOLIB_NC) {
-        if (initGPIOPin(settingsMap[irq], gpioChipName) != ERRNO_OK) {
-            settingsMap[irq] = RADIOLIB_NC;
-        }
-    }
-    if (settingsMap.count(busy) > 0 && settingsMap[busy] != RADIOLIB_NC) {
-        if (initGPIOPin(settingsMap[busy], gpioChipName) != ERRNO_OK) {
-            settingsMap[busy] = RADIOLIB_NC;
-        }
-    }
-    if (settingsMap.count(reset) > 0 && settingsMap[reset] != RADIOLIB_NC) {
-        if (initGPIOPin(settingsMap[reset], gpioChipName) != ERRNO_OK) {
-            settingsMap[reset] = RADIOLIB_NC;
-        }
-    }
-    if (settingsMap.count(sx126x_ant_sw) > 0 && settingsMap[sx126x_ant_sw] != RADIOLIB_NC) {
-        if (initGPIOPin(settingsMap[sx126x_ant_sw], gpioChipName) != ERRNO_OK) {
-            settingsMap[sx126x_ant_sw] = RADIOLIB_NC;
-        }
-    }
     if (settingsMap.count(user) > 0 && settingsMap[user] != RADIOLIB_NC) {
         if (initGPIOPin(settingsMap[user], gpioChipName) != ERRNO_OK) {
             settingsMap[user] = RADIOLIB_NC;
         }
     }
-    if (settingsMap.count(rxen) > 0 && settingsMap[rxen] != RADIOLIB_NC) {
-        if (initGPIOPin(settingsMap[rxen], gpioChipName) != ERRNO_OK) {
-            settingsMap[rxen] = RADIOLIB_NC;
-        }
-    }
-    if (settingsMap.count(txen) > 0 && settingsMap[txen] != RADIOLIB_NC) {
-        if (initGPIOPin(settingsMap[txen], gpioChipName) != ERRNO_OK) {
-            settingsMap[txen] = RADIOLIB_NC;
-        }
-    }
-
     if (settingsMap[displayPanel] != no_screen) {
         if (settingsMap[displayCS] > 0)
             initGPIOPin(settingsMap[displayCS], gpioChipName);
@@ -283,7 +278,43 @@ void portduinoSetup()
             initGPIOPin(settingsMap[touchscreenIRQ], gpioChipName);
     }
 
-    if (settingsStrings[spidev] != "") {
+    // Only initialize the radio pins when dealing with real, kernel controlled SPI hardware
+    if (settingsStrings[spidev] != "" && settingsStrings[spidev] != "ch341") {
+        if (settingsMap.count(cs) > 0 && settingsMap[cs] != RADIOLIB_NC) {
+            if (initGPIOPin(settingsMap[cs], gpioChipName) != ERRNO_OK) {
+                settingsMap[cs] = RADIOLIB_NC;
+            }
+        }
+        if (settingsMap.count(irq) > 0 && settingsMap[irq] != RADIOLIB_NC) {
+            if (initGPIOPin(settingsMap[irq], gpioChipName) != ERRNO_OK) {
+                settingsMap[irq] = RADIOLIB_NC;
+            }
+        }
+        if (settingsMap.count(busy) > 0 && settingsMap[busy] != RADIOLIB_NC) {
+            if (initGPIOPin(settingsMap[busy], gpioChipName) != ERRNO_OK) {
+                settingsMap[busy] = RADIOLIB_NC;
+            }
+        }
+        if (settingsMap.count(reset) > 0 && settingsMap[reset] != RADIOLIB_NC) {
+            if (initGPIOPin(settingsMap[reset], gpioChipName) != ERRNO_OK) {
+                settingsMap[reset] = RADIOLIB_NC;
+            }
+        }
+        if (settingsMap.count(sx126x_ant_sw) > 0 && settingsMap[sx126x_ant_sw] != RADIOLIB_NC) {
+            if (initGPIOPin(settingsMap[sx126x_ant_sw], gpioChipName) != ERRNO_OK) {
+                settingsMap[sx126x_ant_sw] = RADIOLIB_NC;
+            }
+        }
+        if (settingsMap.count(rxen) > 0 && settingsMap[rxen] != RADIOLIB_NC) {
+            if (initGPIOPin(settingsMap[rxen], gpioChipName) != ERRNO_OK) {
+                settingsMap[rxen] = RADIOLIB_NC;
+            }
+        }
+        if (settingsMap.count(txen) > 0 && settingsMap[txen] != RADIOLIB_NC) {
+            if (initGPIOPin(settingsMap[txen], gpioChipName) != ERRNO_OK) {
+                settingsMap[txen] = RADIOLIB_NC;
+            }
+        }
         SPI.begin(settingsStrings[spidev].c_str());
     }
     if (settingsStrings[traceFilename] != "") {
@@ -366,7 +397,10 @@ bool loadConfig(const char *configPath)
                 settingsMap[use_sx1268] = true;
             }
             settingsMap[dio2_as_rf_switch] = yamlConfig["Lora"]["DIO2_AS_RF_SWITCH"].as(false);
-            settingsMap[dio3_tcxo_voltage] = yamlConfig["Lora"]["DIO3_TCXO_VOLTAGE"].as(false);
+            settingsMap[dio3_tcxo_voltage] = yamlConfig["Lora"]["DIO3_TCXO_VOLTAGE"].as(0) * 1000;
+            if (settingsMap[dio3_tcxo_voltage] == 0 && yamlConfig["Lora"]["DIO3_TCXO_VOLTAGE"].as(false)) {
+                settingsMap[dio3_tcxo_voltage] = 1800; // default millivolts for "true"
+            }
             settingsMap[cs] = yamlConfig["Lora"]["CS"].as(RADIOLIB_NC);
             settingsMap[irq] = yamlConfig["Lora"]["IRQ"].as(RADIOLIB_NC);
             settingsMap[busy] = yamlConfig["Lora"]["Busy"].as(RADIOLIB_NC);
@@ -375,17 +409,24 @@ bool loadConfig(const char *configPath)
             settingsMap[rxen] = yamlConfig["Lora"]["RXen"].as(RADIOLIB_NC);
             settingsMap[sx126x_ant_sw] = yamlConfig["Lora"]["SX126X_ANT_SW"].as(RADIOLIB_NC);
             settingsMap[gpiochip] = yamlConfig["Lora"]["gpiochip"].as(0);
-            settingsMap[ch341Quirk] = yamlConfig["Lora"]["ch341_quirk"].as(false);
             settingsMap[spiSpeed] = yamlConfig["Lora"]["spiSpeed"].as(2000000);
+            settingsStrings[lora_usb_serial_num] = yamlConfig["Lora"]["USB_Serialnum"].as("");
+            settingsMap[lora_usb_pid] = yamlConfig["Lora"]["USB_PID"].as(0x5512);
+            settingsMap[lora_usb_vid] = yamlConfig["Lora"]["USB_VID"].as(0x1A86);
 
-            settingsStrings[spidev] = "/dev/" + yamlConfig["Lora"]["spidev"].as("spidev0.0");
-            if (settingsStrings[spidev].length() == 14) {
-                int x = settingsStrings[spidev].at(11) - '0';
-                int y = settingsStrings[spidev].at(13) - '0';
-                if (x >= 0 && x < 10 && y >= 0 && y < 10) {
-                    settingsMap[spidev] = x + y << 4;
-                    settingsMap[displayspidev] = settingsMap[spidev];
-                    settingsMap[touchscreenspidev] = settingsMap[spidev];
+            settingsStrings[spidev] = yamlConfig["Lora"]["spidev"].as("spidev0.0");
+            if (settingsStrings[spidev] != "ch341") {
+                settingsStrings[spidev] = "/dev/" + settingsStrings[spidev];
+                if (settingsStrings[spidev].length() == 14) {
+                    int x = settingsStrings[spidev].at(11) - '0';
+                    int y = settingsStrings[spidev].at(13) - '0';
+                    // Pretty sure this is always true
+                    if (x >= 0 && x < 10 && y >= 0 && y < 10) {
+                        // I believe this bit of weirdness is specifically for the new GUI
+                        settingsMap[spidev] = x + y << 4;
+                        settingsMap[displayspidev] = settingsMap[spidev];
+                        settingsMap[touchscreenspidev] = settingsMap[spidev];
+                    }
                 }
             }
         }
@@ -533,4 +574,4 @@ bool MAC_from_string(std::string mac_str, uint8_t *dmac)
     } else {
         return false;
     }
-}
+}
\ No newline at end of file
diff --git a/src/platform/portduino/PortduinoGlue.h b/src/platform/portduino/PortduinoGlue.h
index 3bcb55de4..5bc07df6a 100644
--- a/src/platform/portduino/PortduinoGlue.h
+++ b/src/platform/portduino/PortduinoGlue.h
@@ -2,6 +2,8 @@
 #include 
 #include 
 
+#include "platform/portduino/USBHal.h"
+
 enum configNames {
     use_sx1262,
     cs,
@@ -13,13 +15,15 @@ enum configNames {
     rxen,
     dio2_as_rf_switch,
     dio3_tcxo_voltage,
-    ch341Quirk,
     use_rf95,
     use_sx1280,
     use_lr1110,
     use_lr1120,
     use_lr1121,
     use_sx1268,
+    lora_usb_serial_num,
+    lora_usb_pid,
+    lora_usb_vid,
     user,
     gpiochip,
     spidev,
@@ -70,8 +74,9 @@ enum { level_error, level_warn, level_info, level_debug, level_trace };
 extern std::map settingsMap;
 extern std::map settingsStrings;
 extern std::ofstream traceFile;
+extern Ch341Hal *ch341Hal;
 int initGPIOPin(int pinNum, std::string gpioChipname);
 bool loadConfig(const char *configPath);
 static bool ends_with(std::string_view str, std::string_view suffix);
 void getMacAddr(uint8_t *dmac);
-bool MAC_from_string(std::string mac_str, uint8_t *dmac);
\ No newline at end of file
+bool MAC_from_string(std::string mac_str, uint8_t *dmac);
diff --git a/src/platform/portduino/USBHal.h b/src/platform/portduino/USBHal.h
new file mode 100644
index 000000000..2b0302ced
--- /dev/null
+++ b/src/platform/portduino/USBHal.h
@@ -0,0 +1,194 @@
+#ifndef PI_HAL_LGPIO_H
+#define PI_HAL_LGPIO_H
+
+// include RadioLib
+#include "platform/portduino/PortduinoGlue.h"
+#include 
+#include 
+#include 
+#include 
+
+// include the library for Raspberry GPIO pins
+
+#define PI_RISING (PINEDIO_INT_MODE_RISING)
+#define PI_FALLING (PINEDIO_INT_MODE_FALLING)
+#define PI_INPUT (0)
+#define PI_OUTPUT (1)
+#define PI_LOW (0)
+#define PI_HIGH (1)
+
+#define CH341_PIN_CS (101)
+#define CH341_PIN_IRQ (0)
+
+// the HAL must inherit from the base RadioLibHal class
+// and implement all of its virtual methods
+class Ch341Hal : public RadioLibHal
+{
+  public:
+    // default constructor - initializes the base HAL and any needed private members
+    Ch341Hal(uint8_t spiChannel, uint32_t spiSpeed = 2000000, uint8_t spiDevice = 0, uint8_t gpioDevice = 0)
+        : RadioLibHal(PI_INPUT, PI_OUTPUT, PI_LOW, PI_HIGH, PI_RISING, PI_FALLING)
+    {
+    }
+
+    void getSerialString(char *_serial, size_t len)
+    {
+        if (!pinedio_is_init) {
+            return;
+        }
+        strncpy(_serial, pinedio.serial_number, len);
+    }
+
+    void init() override
+    {
+        // now the SPI
+        spiBegin();
+    }
+
+    void term() override
+    {
+        // stop the SPI
+        spiEnd();
+    }
+
+    // GPIO-related methods (pinMode, digitalWrite etc.) should check
+    // RADIOLIB_NC as an alias for non-connected pins
+    void pinMode(uint32_t pin, uint32_t mode) override
+    {
+        if (pin == RADIOLIB_NC) {
+            return;
+        }
+        pinedio_set_pin_mode(&pinedio, pin, mode);
+    }
+
+    void digitalWrite(uint32_t pin, uint32_t value) override
+    {
+        if (pin == RADIOLIB_NC) {
+            return;
+        }
+        pinedio_digital_write(&pinedio, pin, value);
+    }
+
+    uint32_t digitalRead(uint32_t pin) override
+    {
+        if (pin == RADIOLIB_NC) {
+            return 0;
+        }
+        return pinedio_digital_read(&pinedio, pin);
+    }
+
+    void attachInterrupt(uint32_t interruptNum, void (*interruptCb)(void), uint32_t mode) override
+    {
+        if ((interruptNum == RADIOLIB_NC)) {
+            return;
+        }
+        // LOG_DEBUG("Attach interrupt to pin %d", interruptNum);
+        pinedio_attach_interrupt(&this->pinedio, (pinedio_int_pin)interruptNum, (pinedio_int_mode)mode, interruptCb);
+    }
+
+    void detachInterrupt(uint32_t interruptNum) override
+    {
+        if ((interruptNum == RADIOLIB_NC)) {
+            return;
+        }
+        // LOG_DEBUG("Detach interrupt from pin %d", interruptNum);
+
+        pinedio_deattach_interrupt(&this->pinedio, (pinedio_int_pin)interruptNum);
+    }
+
+    void delay(unsigned long ms) override
+    {
+        if (ms == 0) {
+            sched_yield();
+            return;
+        }
+
+        usleep(ms * 1000);
+    }
+
+    void delayMicroseconds(unsigned long us) override
+    {
+        if (us == 0) {
+            sched_yield();
+            return;
+        }
+        usleep(us);
+    }
+
+    void yield() override { sched_yield(); }
+
+    unsigned long millis() override
+    {
+        struct timeval tv;
+        gettimeofday(&tv, NULL);
+        return (tv.tv_sec * 1000ULL) + (tv.tv_usec / 1000ULL);
+    }
+
+    unsigned long micros() override
+    {
+        struct timeval tv;
+        gettimeofday(&tv, NULL);
+        return (tv.tv_sec * 1000000ULL) + tv.tv_usec;
+    }
+
+    long pulseIn(uint32_t pin, uint32_t state, unsigned long timeout) override
+    {
+        fprintf(stderr, "pulseIn for pin %u is not supported!\n", pin);
+        return 0;
+    }
+
+    void spiBegin()
+    {
+        if (!pinedio_is_init) {
+            if (serial != "") {
+                strncpy(pinedio.serial_number, serial.c_str(), 8);
+                pinedio_set_option(&pinedio, PINEDIO_OPTION_SEARCH_SERIAL, 1);
+            }
+            pinedio_set_option(&pinedio, PINEDIO_OPTION_PID, pid);
+            pinedio_set_option(&pinedio, PINEDIO_OPTION_VID, vid);
+            int32_t ret = pinedio_init(&pinedio, NULL);
+            if (ret != 0) {
+                fprintf(stderr, "Could not open SPI: %d\n", ret);
+            } else {
+                pinedio_is_init = true;
+                // LOG_INFO("USB Serial: %s", pinedio.serial_number);
+                pinedio_set_option(&pinedio, PINEDIO_OPTION_AUTO_CS, 0);
+                pinedio_set_pin_mode(&pinedio, 3, true);
+                pinedio_set_pin_mode(&pinedio, 5, true);
+            }
+        }
+    }
+
+    void spiBeginTransaction() {}
+
+    void spiTransfer(uint8_t *out, size_t len, uint8_t *in)
+    {
+        int32_t result = pinedio_transceive(&this->pinedio, out, in, len);
+        if (result < 0) {
+            fprintf(stderr, "Could not perform SPI transfer: %d\n", result);
+        }
+    }
+
+    void spiEndTransaction() {}
+
+    void spiEnd()
+    {
+        if (pinedio_is_init) {
+            pinedio_deinit(&pinedio);
+            pinedio_is_init = false;
+        }
+    }
+
+    bool isInit() { return pinedio_is_init; }
+
+    std::string serial = "";
+    uint32_t pid = 0x5512;
+    uint32_t vid = 0x1A86;
+
+  private:
+    // the HAL can contain any additional private members
+    pinedio_inst pinedio = {0};
+    bool pinedio_is_init = false;
+};
+
+#endif
\ No newline at end of file
diff --git a/src/platform/rp2xx0/architecture.h b/src/platform/rp2xx0/architecture.h
index 8c7dfc0cd..506c19c83 100644
--- a/src/platform/rp2xx0/architecture.h
+++ b/src/platform/rp2xx0/architecture.h
@@ -33,4 +33,6 @@
 #define HW_VENDOR meshtastic_HardwareModel_RP2040_LORA
 #elif defined(RP2040_FEATHER_RFM95)
 #define HW_VENDOR meshtastic_HardwareModel_RP2040_FEATHER_RFM95
+#elif defined(PRIVATE_HW)
+#define HW_VENDOR meshtastic_HardwareModel_PRIVATE_HW
 #endif
\ No newline at end of file
diff --git a/src/power.h b/src/power.h
index 63335104b..ab55fc7e1 100644
--- a/src/power.h
+++ b/src/power.h
@@ -42,10 +42,12 @@ extern RTC_NOINIT_ATTR uint64_t RTC_reg_b;
 
 #if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(ARCH_PORTDUINO)
 #include "modules/Telemetry/Sensor/INA219Sensor.h"
+#include "modules/Telemetry/Sensor/INA226Sensor.h"
 #include "modules/Telemetry/Sensor/INA260Sensor.h"
 #include "modules/Telemetry/Sensor/INA3221Sensor.h"
-extern INA260Sensor ina260Sensor;
 extern INA219Sensor ina219Sensor;
+extern INA226Sensor ina226Sensor;
+extern INA260Sensor ina260Sensor;
 extern INA3221Sensor ina3221Sensor;
 #endif
 
@@ -99,4 +101,4 @@ class Power : private concurrency::OSThread
 #endif
 };
 
-extern Power *power;
\ No newline at end of file
+extern Power *power;
diff --git a/variants/diy/nrf52_promicro_diy_tcxo/Schematic_Pro-Micro_Pinouts 2024-12-14.pdf b/variants/diy/nrf52_promicro_diy_tcxo/Schematic_Pro-Micro_Pinouts 2024-12-14.pdf
new file mode 100644
index 000000000..de87af141
--- /dev/null
+++ b/variants/diy/nrf52_promicro_diy_tcxo/Schematic_Pro-Micro_Pinouts 2024-12-14.pdf	
@@ -0,0 +1,9836 @@
+%PDF-1.4
+%߬
+3 0 obj
+<>
+endobj
+4 0 obj
+<<
+/Length 102720
+>>
+stream
+0.20 w
+0 G
+2 J
+0 j
+100 M
+1.00 g
+[] 0 d
+0.00 826.80 1169.00 -826.80 re
+f
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+216.000 806.000 m 
+216.000 816.000 l
+216.000 20.000 m 
+216.000 10.000 l
+412.000 806.000 m 
+412.000 816.000 l
+412.000 20.000 m 
+412.000 10.000 l
+608.000 806.000 m 
+608.000 816.000 l
+608.000 20.000 m 
+608.000 10.000 l
+804.000 806.000 m 
+804.000 816.000 l
+804.000 20.000 m 
+804.000 10.000 l
+1000.000 806.000 m 
+1000.000 816.000 l
+1000.000 20.000 m 
+1000.000 10.000 l
+20.000 610.000 m 
+10.000 610.000 l
+1149.000 610.000 m 
+1159.000 610.000 l
+20.000 414.000 m 
+10.000 414.000 l
+1149.000 414.000 m 
+1159.000 414.000 l
+20.000 218.000 m 
+10.000 218.000 l
+1149.000 218.000 m 
+1159.000 218.000 l
+20.000 22.000 m 
+10.000 22.000 l
+1149.000 22.000 m 
+1159.000 22.000 l
+S
+10.00 w
+BT
+/F1 9 Tf
+9.00 TL
+0.533 0.000 0.000 rg
+11.50 708.00 Td
+(A) Tj
+ET
+10.00 w
+BT
+/F1 9 Tf
+9.00 TL
+0.533 0.000 0.000 rg
+1150.50 708.00 Td
+(A) Tj
+ET
+10.00 w
+BT
+/F1 9 Tf
+9.00 TL
+0.533 0.000 0.000 rg
+11.50 512.00 Td
+(B) Tj
+ET
+10.00 w
+BT
+/F1 9 Tf
+9.00 TL
+0.533 0.000 0.000 rg
+1150.50 512.00 Td
+(B) Tj
+ET
+10.00 w
+BT
+/F1 9 Tf
+9.00 TL
+0.533 0.000 0.000 rg
+11.50 316.00 Td
+(C) Tj
+ET
+10.00 w
+BT
+/F1 9 Tf
+9.00 TL
+0.533 0.000 0.000 rg
+1150.50 316.00 Td
+(C) Tj
+ET
+10.00 w
+BT
+/F1 9 Tf
+9.00 TL
+0.533 0.000 0.000 rg
+11.50 120.00 Td
+(D) Tj
+ET
+10.00 w
+BT
+/F1 9 Tf
+9.00 TL
+0.533 0.000 0.000 rg
+1150.50 120.00 Td
+(D) Tj
+ET
+10.00 w
+BT
+/F1 9 Tf
+9.00 TL
+0.533 0.000 0.000 rg
+118.00 807.50 Td
+(1) Tj
+ET
+10.00 w
+BT
+/F1 9 Tf
+9.00 TL
+0.533 0.000 0.000 rg
+118.00 11.50 Td
+(1) Tj
+ET
+10.00 w
+BT
+/F1 9 Tf
+9.00 TL
+0.533 0.000 0.000 rg
+314.00 807.50 Td
+(2) Tj
+ET
+10.00 w
+BT
+/F1 9 Tf
+9.00 TL
+0.533 0.000 0.000 rg
+314.00 11.50 Td
+(2) Tj
+ET
+10.00 w
+BT
+/F1 9 Tf
+9.00 TL
+0.533 0.000 0.000 rg
+510.00 807.50 Td
+(3) Tj
+ET
+10.00 w
+BT
+/F1 9 Tf
+9.00 TL
+0.533 0.000 0.000 rg
+510.00 11.50 Td
+(3) Tj
+ET
+10.00 w
+BT
+/F1 9 Tf
+9.00 TL
+0.533 0.000 0.000 rg
+706.00 807.50 Td
+(4) Tj
+ET
+10.00 w
+BT
+/F1 9 Tf
+9.00 TL
+0.533 0.000 0.000 rg
+706.00 11.50 Td
+(4) Tj
+ET
+10.00 w
+BT
+/F1 9 Tf
+9.00 TL
+0.533 0.000 0.000 rg
+902.00 807.50 Td
+(5) Tj
+ET
+10.00 w
+BT
+/F1 9 Tf
+9.00 TL
+0.533 0.000 0.000 rg
+902.00 11.50 Td
+(5) Tj
+ET
+2 J
+0 j
+100 M
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+20.00 806.00 1129.00 -786.00 re
+S
+2 J
+0 j
+100 M
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+10.00 816.00 1149.00 -806.00 re
+S
+2 J
+0 j
+100 M
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+705.00 100.00 444.00 -80.00 re
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+0.00 g
+[] 0 d
+705.100 60.750 m 
+1148.630 60.750 l
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+0.00 g
+[] 0 d
+809.630 40.750 m 
+1148.630 40.750 l
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+0.00 g
+[] 0 d
+1069.610 99.930 m 
+1069.630 60.750 l
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+0.00 g
+[] 0 d
+1069.630 60.750 m 
+1069.630 40.750 l
+S
+10.00 w
+BT
+/F1 11 Tf
+11.00 TL
+0.533 0.000 0.000 rg
+710.00 87.00 Td
+(TITLE:) Tj
+ET
+10.00 w
+BT
+/F1 13 Tf
+13.00 TL
+0.000 0.000 1.000 rg
+767.62 74.41 Td
+(Pro-Micro Pinouts) Tj
+ET
+10.00 w
+BT
+/F1 11 Tf
+11.00 TL
+0.533 0.000 0.000 rg
+1074.62 73.75 Td
+(REV:) Tj
+ET
+10.00 w
+BT
+/F1 12 Tf
+12.00 TL
+0.000 0.000 1.000 rg
+1112.62 73.75 Td
+(1.0) Tj
+ET
+10.00 w
+BT
+/F1 11 Tf
+11.00 TL
+0.533 0.000 0.000 rg
+814.62 25.00 Td
+(Date:) Tj
+ET
+10.00 w
+BT
+/F1 12 Tf
+12.00 TL
+0.000 0.000 1.000 rg
+861.62 24.52 Td
+(2024-12-17) Tj
+ET
+10.00 w
+BT
+/F1 11 Tf
+11.00 TL
+0.533 0.000 0.000 rg
+1073.62 45.00 Td
+(Sheet:) Tj
+ET
+10.00 w
+BT
+/F1 12 Tf
+12.00 TL
+0.000 0.000 1.000 rg
+1118.62 44.52 Td
+(1/1) Tj
+ET
+10.00 w
+BT
+/F1 11 Tf
+11.00 TL
+0.533 0.000 0.000 rg
+953.62 24.75 Td
+(Drawn By:) Tj
+ET
+10.00 w
+BT
+/F1 11 Tf
+11.00 TL
+0.533 0.000 0.000 rg
+814.62 46.75 Td
+(Company:) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+0.00 g
+[] 0 d
+809.630 60.750 m 
+809.630 20.750 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+541.000 684.000 m 
+549.000 676.000 l
+549.000 684.000 m 
+541.000 676.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+665.000 680.000 m 
+635.000 680.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+986.000 649.000 m 
+994.000 641.000 l
+994.000 649.000 m 
+986.000 641.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+541.000 614.000 m 
+549.000 606.000 l
+549.000 614.000 m 
+541.000 606.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+541.000 624.000 m 
+549.000 616.000 l
+549.000 624.000 m 
+541.000 616.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+541.000 644.000 m 
+549.000 636.000 l
+549.000 644.000 m 
+541.000 636.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+631.000 634.000 m 
+639.000 626.000 l
+639.000 634.000 m 
+631.000 626.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 804.82 611.00 Tm
+(VCC) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+830.000 615.000 m 
+840.000 615.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+830.000 610.000 m 
+830.000 620.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 797.50 621.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+830.000 625.000 m 
+840.000 625.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+830.000 634.000 m 
+830.000 616.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+828.000 631.000 m 
+828.000 619.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+826.000 628.000 m 
+826.000 622.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+824.000 626.000 m 
+824.000 624.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 1006.50 651.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+1000.000 655.000 m 
+990.000 655.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+1000.000 646.000 m 
+1000.000 664.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+1002.000 649.000 m 
+1002.000 661.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+1004.000 652.000 m 
+1004.000 658.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+1006.000 654.000 m 
+1006.000 656.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 1011.94 671.15 Tm
+(IRQ) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+990.000 675.000 m 
+995.000 680.000 l
+1010.000 680.000 l
+1010.000 670.000 l
+995.000 670.000 l
+990.000 675.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 797.25 651.75 Tm
+(SCK) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+840.000 655.000 m 
+835.000 650.000 l
+820.000 650.000 l
+820.000 660.000 l
+835.000 660.000 l
+840.000 655.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 804.69 631.45 Tm
+(CS) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+840.000 635.000 m 
+835.000 630.000 l
+820.000 630.000 l
+820.000 640.000 l
+835.000 640.000 l
+840.000 635.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 791.48 661.75 Tm
+(MOSI) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+840.000 665.000 m 
+835.000 660.000 l
+820.000 660.000 l
+820.000 670.000 l
+835.000 670.000 l
+840.000 665.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 791.41 671.75 Tm
+(MISO) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+840.000 675.000 m 
+835.000 670.000 l
+820.000 670.000 l
+820.000 680.000 l
+835.000 680.000 l
+840.000 675.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 790.06 641.57 Tm
+(NRST) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+840.000 645.000 m 
+835.000 640.000 l
+820.000 640.000 l
+820.000 650.000 l
+835.000 650.000 l
+840.000 645.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 1011.92 661.15 Tm
+(BUSY) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+990.000 665.000 m 
+995.000 670.000 l
+1010.000 670.000 l
+1010.000 660.000 l
+995.000 660.000 l
+990.000 665.000 l
+S
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+908.96 708.00 Td
+(Seeed-wio-SX1262) Tj
+ET
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+908.96 717.00 Td
+(SEEED_WIO-SX1262) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+862.00 682.00 Td
+(RF_SW) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+849.28 686.00 Td
+(1) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+840.000 685.000 m 
+860.000 685.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+862.00 672.00 Td
+(MISO) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+849.28 676.00 Td
+(2) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+840.000 675.000 m 
+860.000 675.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+862.00 662.00 Td
+(MOSI) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+849.28 666.00 Td
+(3) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+840.000 665.000 m 
+860.000 665.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+862.00 652.00 Td
+(CLK) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+849.28 656.00 Td
+(4) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+840.000 655.000 m 
+860.000 655.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+862.00 642.00 Td
+(RST) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+849.28 646.00 Td
+(5) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+840.000 645.000 m 
+860.000 645.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+862.00 632.00 Td
+(NSS) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+849.28 636.00 Td
+(6) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+840.000 635.000 m 
+860.000 635.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+862.00 622.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+849.28 626.00 Td
+(7) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+840.000 625.000 m 
+860.000 625.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+862.00 612.00 Td
+(VCC) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+849.28 616.00 Td
+(8) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+840.000 615.000 m 
+860.000 615.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+949.58 642.00 Td
+(ANT) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+975.00 646.00 Td
+(9) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+990.000 645.000 m 
+970.000 645.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+947.36 652.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+975.00 656.00 Td
+(10) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+990.000 655.000 m 
+970.000 655.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+943.57 662.00 Td
+(BUSY) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+975.00 666.00 Td
+(11) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+990.000 665.000 m 
+970.000 665.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+944.49 672.00 Td
+(DIO1) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+975.00 676.00 Td
+(12) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+990.000 675.000 m 
+970.000 675.000 l
+S
+2 J
+0 j
+100 M
+1.00 w
+0.00 G
+[] 0 d
+860.00 705.00 110.00 -110.00 re
+S
+1.00 w
+0.00 G
+[] 0 d
+965.00 615.00 m 965.00 623.28 958.28 630.00 950.00 630.00 c
+941.72 630.00 935.00 623.28 935.00 615.00 c
+935.00 606.72 941.72 600.00 950.00 600.00 c
+958.28 600.00 965.00 606.72 965.00 615.00 c
+S
+2 J
+0 j
+100 M
+1.00 w
+0.00 G
+[] 0 d
+930.00 635.00 40.00 -40.00 re
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+535.000 660.000 m 
+545.000 660.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 499.82 655.93 Tm
+(VCC) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+525.000 660.000 m 
+535.000 660.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+525.000 655.000 m 
+525.000 665.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+520.000 670.000 m 
+545.000 670.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 652.00 699.09 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+665.000 690.000 m 
+665.000 680.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+674.000 690.000 m 
+656.000 690.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+671.000 692.000 m 
+659.000 692.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+668.000 694.000 m 
+662.000 694.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+666.000 696.000 m 
+664.000 696.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 507.00 689.09 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+520.000 680.000 m 
+520.000 670.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+529.000 680.000 m 
+511.000 680.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+526.000 682.000 m 
+514.000 682.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+523.000 684.000 m 
+517.000 684.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+521.000 686.000 m 
+519.000 686.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 622.00 582.76 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+635.000 600.000 m 
+635.000 610.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+626.000 600.000 m 
+644.000 600.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+629.000 598.000 m 
+641.000 598.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+632.000 596.000 m 
+638.000 596.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+634.000 594.000 m 
+636.000 594.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 656.92 616.15 Tm
+(BUSY) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+635.000 620.000 m 
+640.000 625.000 l
+655.000 625.000 l
+655.000 615.000 l
+640.000 615.000 l
+635.000 620.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 656.55 636.15 Tm
+(SCK) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+635.000 640.000 m 
+640.000 645.000 l
+655.000 645.000 l
+655.000 635.000 l
+640.000 635.000 l
+635.000 640.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 656.92 646.15 Tm
+(MISO) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+635.000 650.000 m 
+640.000 655.000 l
+655.000 655.000 l
+655.000 645.000 l
+640.000 645.000 l
+635.000 650.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 656.85 656.15 Tm
+(MOSI) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+635.000 660.000 m 
+640.000 665.000 l
+655.000 665.000 l
+655.000 655.000 l
+640.000 655.000 l
+635.000 660.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 657.00 666.45 Tm
+(CS) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+635.000 670.000 m 
+640.000 675.000 l
+655.000 675.000 l
+655.000 665.000 l
+640.000 665.000 l
+635.000 670.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 504.19 626.40 Tm
+(IRQ) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+545.000 630.000 m 
+540.000 625.000 l
+525.000 625.000 l
+525.000 635.000 l
+540.000 635.000 l
+545.000 630.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 495.06 646.57 Tm
+(NRST) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+545.000 650.000 m 
+540.000 645.000 l
+525.000 645.000 l
+525.000 655.000 l
+540.000 655.000 l
+545.000 650.000 l
+S
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+584.95 693.33 Td
+(RA-01SH) Tj
+ET
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+584.95 702.33 Td
+(HT-RA62) Tj
+ET
+2 J
+0 j
+100 M
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+557.000 690.000 m 
+623.000 690.000 l
+624.105 690.000 625.000 689.105 625.000 688.000 c
+625.000 602.000 l
+625.000 600.895 623.895 600.000 623.000 600.000 c
+557.000 600.000 l
+555.895 600.000 555.000 601.105 555.000 602.000 c
+555.000 688.000 l
+555.000 689.105 556.105 690.000 557.000 690.000 c
+S
+1.00 w
+0.53 0.00 0.00 RG
+0.53 0.00 0.00 rg
+[] 0 d
+561.50 685.00 m 561.50 685.83 560.83 686.50 560.00 686.50 c
+559.17 686.50 558.50 685.83 558.50 685.00 c
+558.50 684.17 559.17 683.50 560.00 683.50 c
+560.83 683.50 561.50 684.17 561.50 685.00 c
+B
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+558.70 676.00 Td
+(ANT) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+548.78 681.00 Td
+(1) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+545.000 680.000 m 
+555.000 680.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+558.70 666.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+548.78 671.00 Td
+(2) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+[] 0 d
+545.000 670.000 m 
+555.000 670.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+558.70 656.00 Td
+(3.3V) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+548.78 661.00 Td
+(3) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+545.000 660.000 m 
+555.000 660.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+558.70 646.00 Td
+(RESET) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+548.78 651.00 Td
+(4) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+545.000 650.000 m 
+555.000 650.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+558.70 636.00 Td
+(TXEN) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+548.78 641.00 Td
+(5) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+545.000 640.000 m 
+555.000 640.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+558.70 626.00 Td
+(DIO1) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+548.78 631.00 Td
+(6) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+545.000 630.000 m 
+555.000 630.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+558.70 616.00 Td
+(DIO2) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+548.78 621.00 Td
+(7) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+545.000 620.000 m 
+555.000 620.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+558.70 606.00 Td
+(DIO3) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+548.78 611.00 Td
+(8) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+545.000 610.000 m 
+555.000 610.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+600.66 606.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+625.50 611.00 Td
+(9) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+[] 0 d
+635.000 610.000 m 
+625.000 610.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+596.87 616.00 Td
+(BUSY) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+625.50 621.00 Td
+(10) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+635.000 620.000 m 
+625.000 620.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+596.46 626.00 Td
+(RXEN) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+625.50 631.00 Td
+(11) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+635.000 630.000 m 
+625.000 630.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+602.64 636.00 Td
+(SCK) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+625.50 641.00 Td
+(12) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+635.000 640.000 m 
+625.000 640.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+596.71 646.00 Td
+(MISO) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+625.50 651.00 Td
+(13) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+635.000 650.000 m 
+625.000 650.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+596.71 656.00 Td
+(MOSI) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+625.50 661.00 Td
+(14) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+635.000 660.000 m 
+625.000 660.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+602.27 666.00 Td
+(NSS) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+625.50 671.00 Td
+(15) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+635.000 670.000 m 
+625.000 670.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+600.66 676.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+625.50 681.00 Td
+(16) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+[] 0 d
+635.000 680.000 m 
+625.000 680.000 l
+S
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+153.95 479.05 Td
+(AMC-U_FL) Tj
+ET
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+153.95 488.16 Td
+(U6) Tj
+ET
+2 J
+0 j
+100 M
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+165.00 465.00 20.00 -20.00 re
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+0.00 1.00 -1.00 0.00 174.00 434.29 Tm
+(1) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+175.000 425.000 m 
+175.000 445.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+159.28 456.00 Td
+(2) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+155.000 455.000 m 
+165.000 455.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+185.00 456.00 Td
+(3) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+195.000 455.000 m 
+185.000 455.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+0.00 1.00 -1.00 0.00 174.00 465.00 Tm
+(4) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+175.000 475.000 m 
+175.000 465.000 l
+S
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+177.00 455.00 m 177.00 456.10 176.10 457.00 175.00 457.00 c
+173.90 457.00 173.00 456.10 173.00 455.00 c
+173.00 453.90 173.90 453.00 175.00 453.00 c
+176.10 453.00 177.00 453.90 177.00 455.00 c
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+0.00 g
+[] 0 d
+175.000 453.000 m 
+175.000 445.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+506.000 394.000 m 
+514.000 386.000 l
+514.000 394.000 m 
+506.000 386.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+646.000 394.000 m 
+654.000 386.000 l
+654.000 394.000 m 
+646.000 386.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 666.50 415.93 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+660.000 420.000 m 
+650.000 420.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+660.000 411.000 m 
+660.000 429.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+662.000 414.000 m 
+662.000 426.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+664.000 417.000 m 
+664.000 423.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+666.000 419.000 m 
+666.000 421.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+650.000 430.000 m 
+650.000 400.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+796.000 389.000 m 
+804.000 381.000 l
+804.000 389.000 m 
+796.000 381.000 l
+S
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+573.96 503.33 Td
+(E22-900M22S) Tj
+ET
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+573.96 512.33 Td
+(E22-900M22S) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+532.00 377.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+513.57 381.00 Td
+(22) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+510.000 380.000 m 
+530.000 380.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+532.00 387.00 Td
+(ANT) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+513.57 391.00 Td
+(21) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+510.000 390.000 m 
+530.000 390.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+532.00 397.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+513.57 401.00 Td
+(20) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+510.000 400.000 m 
+530.000 400.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+532.00 417.00 Td
+(NSS) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+513.57 421.00 Td
+(19) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+510.000 420.000 m 
+530.000 420.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+532.00 427.00 Td
+(SCK) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+513.57 431.00 Td
+(18) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+510.000 430.000 m 
+530.000 430.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+532.00 437.00 Td
+(MOSI) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+513.57 441.00 Td
+(17) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+510.000 440.000 m 
+530.000 440.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+532.00 447.00 Td
+(MISO) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+513.57 451.00 Td
+(16) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+510.000 450.000 m 
+530.000 450.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+532.00 457.00 Td
+(NRST) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+513.57 461.00 Td
+(15) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+510.000 460.000 m 
+530.000 460.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+532.00 467.00 Td
+(BUSY) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+513.57 471.00 Td
+(14) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+510.000 470.000 m 
+530.000 470.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+532.00 477.00 Td
+(DIO1) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+513.57 481.00 Td
+(13) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+510.000 480.000 m 
+530.000 480.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+532.00 487.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+513.57 491.00 Td
+(12) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+510.000 490.000 m 
+530.000 490.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+607.36 487.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+635.00 491.00 Td
+(11) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+650.000 490.000 m 
+630.000 490.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+607.36 477.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+635.00 481.00 Td
+(10) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+650.000 480.000 m 
+630.000 480.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+609.29 467.00 Td
+(VCC) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+635.00 471.00 Td
+(9) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+650.000 470.000 m 
+630.000 470.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+604.49 457.00 Td
+(DIO2) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+635.00 461.00 Td
+(8) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+650.000 460.000 m 
+630.000 460.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+603.87 447.00 Td
+(TXEN) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+635.00 451.00 Td
+(7) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+650.000 450.000 m 
+630.000 450.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+603.16 437.00 Td
+(RXEN) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+635.00 441.00 Td
+(6) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+650.000 440.000 m 
+630.000 440.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+607.36 427.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+635.00 431.00 Td
+(5) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+650.000 430.000 m 
+630.000 430.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+607.36 417.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+635.00 421.00 Td
+(4) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+650.000 420.000 m 
+630.000 420.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+607.36 397.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+635.00 401.00 Td
+(3) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+650.000 400.000 m 
+630.000 400.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+607.36 387.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+635.00 391.00 Td
+(2) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+650.000 390.000 m 
+630.000 390.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+607.36 377.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+635.00 381.00 Td
+(1) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+650.000 380.000 m 
+630.000 380.000 l
+S
+2 J
+0 j
+100 M
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+532.000 500.000 m 
+628.000 500.000 l
+629.105 500.000 630.000 499.105 630.000 498.000 c
+630.000 362.000 l
+630.000 360.895 628.895 360.000 628.000 360.000 c
+532.000 360.000 l
+530.895 360.000 530.000 361.105 530.000 362.000 c
+530.000 498.000 l
+530.000 499.105 531.105 500.000 532.000 500.000 c
+S
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+863.96 498.33 Td
+(E22-900M30S) Tj
+ET
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+863.96 507.33 Td
+(E22-900M30S) Tj
+ET
+2 J
+0 j
+100 M
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+822.000 495.000 m 
+918.000 495.000 l
+919.105 495.000 920.000 494.105 920.000 493.000 c
+920.000 357.000 l
+920.000 355.895 918.895 355.000 918.000 355.000 c
+822.000 355.000 l
+820.895 355.000 820.000 356.105 820.000 357.000 c
+820.000 493.000 l
+820.000 494.105 821.105 495.000 822.000 495.000 c
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+897.36 372.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+925.00 376.00 Td
+(1) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+940.000 375.000 m 
+920.000 375.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+897.36 382.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+925.00 386.00 Td
+(2) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+940.000 385.000 m 
+920.000 385.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+897.36 392.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+925.00 396.00 Td
+(3) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+940.000 395.000 m 
+920.000 395.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+897.36 412.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+925.00 416.00 Td
+(4) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+940.000 415.000 m 
+920.000 415.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+897.36 422.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+925.00 426.00 Td
+(5) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+940.000 425.000 m 
+920.000 425.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+893.16 432.00 Td
+(RXEN) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+925.00 436.00 Td
+(6) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+940.000 435.000 m 
+920.000 435.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+893.87 442.00 Td
+(TXEN) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+925.00 446.00 Td
+(7) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+940.000 445.000 m 
+920.000 445.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+894.49 452.00 Td
+(DIO2) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+925.00 456.00 Td
+(8) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+940.000 455.000 m 
+920.000 455.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+899.29 462.00 Td
+(VCC) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+925.00 466.00 Td
+(9) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+940.000 465.000 m 
+920.000 465.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+899.29 472.00 Td
+(VCC) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+925.00 476.00 Td
+(10) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+940.000 475.000 m 
+920.000 475.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+897.36 482.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+925.00 486.00 Td
+(11) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+940.000 485.000 m 
+920.000 485.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+822.00 482.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+803.57 486.00 Td
+(12) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+800.000 485.000 m 
+820.000 485.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+822.00 472.00 Td
+(DIO1) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+803.57 476.00 Td
+(13) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+800.000 475.000 m 
+820.000 475.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+822.00 462.00 Td
+(BUSY) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+803.57 466.00 Td
+(14) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+800.000 465.000 m 
+820.000 465.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+822.00 452.00 Td
+(NRST) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+803.57 456.00 Td
+(15) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+800.000 455.000 m 
+820.000 455.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+822.00 442.00 Td
+(MISO) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+803.57 446.00 Td
+(16) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+800.000 445.000 m 
+820.000 445.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+822.00 432.00 Td
+(MOSI) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+803.57 436.00 Td
+(17) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+800.000 435.000 m 
+820.000 435.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+822.00 422.00 Td
+(SCK) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+803.57 426.00 Td
+(18) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+800.000 425.000 m 
+820.000 425.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+822.00 412.00 Td
+(NSS) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+803.57 416.00 Td
+(19) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+800.000 415.000 m 
+820.000 415.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+822.00 392.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+803.57 396.00 Td
+(20) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+800.000 395.000 m 
+820.000 395.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+822.00 382.00 Td
+(ANT) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+803.57 386.00 Td
+(21) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+800.000 385.000 m 
+820.000 385.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+822.00 372.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+803.57 376.00 Td
+(22) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+800.000 375.000 m 
+820.000 375.000 l
+S
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+293.99 488.33 Td
+(E22-400MM22S) Tj
+ET
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+293.99 497.33 Td
+(E22-900MM22S) Tj
+ET
+2 J
+0 j
+100 M
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+267.000 485.000 m 
+333.000 485.000 l
+334.105 485.000 335.000 484.105 335.000 483.000 c
+335.000 377.000 l
+335.000 375.895 333.895 375.000 333.000 375.000 c
+267.000 375.000 l
+265.895 375.000 265.000 376.105 265.000 377.000 c
+265.000 483.000 l
+265.000 484.105 266.105 485.000 267.000 485.000 c
+S
+1.00 w
+0.53 0.00 0.00 RG
+0.53 0.00 0.00 rg
+[] 0 d
+271.50 480.00 m 271.50 480.83 270.83 481.50 270.00 481.50 c
+269.17 481.50 268.50 480.83 268.50 480.00 c
+268.50 479.17 269.17 478.50 270.00 478.50 c
+270.83 478.50 271.50 479.17 271.50 480.00 c
+B
+BT
+/F1 9 Tf
+9.00 TL
+1.000 0.000 0.000 rg
+268.70 471.00 Td
+(VCC) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+1.000 0.000 0.000 rg
+258.79 476.00 Td
+(1) Tj
+ET
+1 J
+1 j
+1.00 w
+1.00 0.00 0.00 RG
+[] 0 d
+255.000 475.000 m 
+265.000 475.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+268.70 461.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+258.79 466.00 Td
+(2) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+[] 0 d
+255.000 465.000 m 
+265.000 465.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+268.70 451.00 Td
+(NRST) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+258.79 456.00 Td
+(3) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+255.000 455.000 m 
+265.000 455.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+268.70 441.00 Td
+(NC) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+258.79 446.00 Td
+(4) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+255.000 445.000 m 
+265.000 445.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+268.70 431.00 Td
+(NC) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+258.79 436.00 Td
+(5) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+255.000 435.000 m 
+265.000 435.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+268.70 421.00 Td
+(ANT) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+258.79 426.00 Td
+(6) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+255.000 425.000 m 
+265.000 425.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+268.70 411.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+258.79 416.00 Td
+(7) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+[] 0 d
+255.000 415.000 m 
+265.000 415.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+268.70 401.00 Td
+(NC) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+258.79 406.00 Td
+(8) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+255.000 405.000 m 
+265.000 405.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+268.70 391.00 Td
+(TXEN) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+258.79 396.00 Td
+(9) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+255.000 395.000 m 
+265.000 395.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+268.70 381.00 Td
+(RXEN) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+253.07 386.00 Td
+(10) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+255.000 385.000 m 
+265.000 385.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+306.87 381.00 Td
+(BUSY) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+335.50 386.00 Td
+(11) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+345.000 385.000 m 
+335.000 385.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+306.71 391.00 Td
+(MISO) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+335.50 396.00 Td
+(12) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+345.000 395.000 m 
+335.000 395.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+306.71 401.00 Td
+(MOSI) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+335.50 406.00 Td
+(13) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+345.000 405.000 m 
+335.000 405.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+312.27 411.00 Td
+(NSS) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+335.50 416.00 Td
+(14) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+345.000 415.000 m 
+335.000 415.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+312.64 421.00 Td
+(SCK) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+335.50 426.00 Td
+(15) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+345.000 425.000 m 
+335.000 425.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+310.66 431.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+335.50 436.00 Td
+(16) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+[] 0 d
+345.000 435.000 m 
+335.000 435.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+318.29 441.00 Td
+(NC) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+335.50 446.00 Td
+(17) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+345.000 445.000 m 
+335.000 445.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+307.79 451.00 Td
+(DIO3) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+335.50 456.00 Td
+(18) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+345.000 455.000 m 
+335.000 455.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+307.79 461.00 Td
+(DIO2) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+335.50 466.00 Td
+(19) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+345.000 465.000 m 
+335.000 465.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+307.79 471.00 Td
+(DIO1) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+335.50 476.00 Td
+(20) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+345.000 475.000 m 
+335.000 475.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 366.70 381.60 Tm
+(BUSY) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+345.000 385.000 m 
+350.000 390.000 l
+365.000 390.000 l
+365.000 380.000 l
+350.000 380.000 l
+345.000 385.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 203.19 381.40 Tm
+(RXEN) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+255.000 385.000 m 
+250.000 380.000 l
+235.000 380.000 l
+235.000 390.000 l
+250.000 390.000 l
+255.000 385.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 366.71 421.60 Tm
+(SCK) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+345.000 425.000 m 
+350.000 430.000 l
+365.000 430.000 l
+365.000 420.000 l
+350.000 420.000 l
+345.000 425.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 367.00 391.60 Tm
+(MISO) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+345.000 395.000 m 
+350.000 400.000 l
+365.000 400.000 l
+365.000 390.000 l
+350.000 390.000 l
+345.000 395.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 367.00 401.60 Tm
+(MOSI) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+345.000 405.000 m 
+350.000 410.000 l
+365.000 410.000 l
+365.000 400.000 l
+350.000 400.000 l
+345.000 405.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 367.00 411.60 Tm
+(CS) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+345.000 415.000 m 
+350.000 420.000 l
+365.000 420.000 l
+365.000 410.000 l
+350.000 410.000 l
+345.000 415.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 367.01 461.15 Tm
+(DIO2) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+345.000 465.000 m 
+350.000 470.000 l
+365.000 470.000 l
+365.000 460.000 l
+350.000 460.000 l
+345.000 465.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 367.00 471.60 Tm
+(IRQ) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+345.000 475.000 m 
+350.000 480.000 l
+365.000 480.000 l
+365.000 470.000 l
+350.000 470.000 l
+345.000 475.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 203.74 391.40 Tm
+(TXEN) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+255.000 395.000 m 
+250.000 390.000 l
+235.000 390.000 l
+235.000 400.000 l
+250.000 400.000 l
+255.000 395.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 205.06 451.57 Tm
+(NRST) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+255.000 455.000 m 
+250.000 450.000 l
+235.000 450.000 l
+235.000 460.000 l
+250.000 460.000 l
+255.000 455.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 367.01 451.15 Tm
+(DIO3) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+345.000 455.000 m 
+350.000 460.000 l
+365.000 460.000 l
+365.000 450.000 l
+350.000 450.000 l
+345.000 455.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 460.06 456.57 Tm
+(NRST) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+510.000 460.000 m 
+505.000 455.000 l
+490.000 455.000 l
+490.000 465.000 l
+505.000 465.000 l
+510.000 460.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 671.71 446.60 Tm
+(TXEN) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+650.000 450.000 m 
+655.000 455.000 l
+670.000 455.000 l
+670.000 445.000 l
+655.000 445.000 l
+650.000 450.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 469.19 476.40 Tm
+(IRQ) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+510.000 480.000 m 
+505.000 475.000 l
+490.000 475.000 l
+490.000 485.000 l
+505.000 485.000 l
+510.000 480.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 672.01 456.15 Tm
+(DIO2) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+650.000 460.000 m 
+655.000 465.000 l
+670.000 465.000 l
+670.000 455.000 l
+655.000 455.000 l
+650.000 460.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 474.69 416.40 Tm
+(CS) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+510.000 420.000 m 
+505.000 415.000 l
+490.000 415.000 l
+490.000 425.000 l
+505.000 425.000 l
+510.000 420.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 460.61 436.40 Tm
+(MOSI) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+510.000 440.000 m 
+505.000 435.000 l
+490.000 435.000 l
+490.000 445.000 l
+505.000 445.000 l
+510.000 440.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 460.61 446.40 Tm
+(MISO) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+510.000 450.000 m 
+505.000 445.000 l
+490.000 445.000 l
+490.000 455.000 l
+505.000 455.000 l
+510.000 450.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 466.77 426.40 Tm
+(SCK) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+510.000 430.000 m 
+505.000 425.000 l
+490.000 425.000 l
+490.000 435.000 l
+505.000 435.000 l
+510.000 430.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 671.71 436.60 Tm
+(RXEN) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+650.000 440.000 m 
+655.000 445.000 l
+670.000 445.000 l
+670.000 435.000 l
+655.000 435.000 l
+650.000 440.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 458.85 466.40 Tm
+(BUSY) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+510.000 470.000 m 
+505.000 465.000 l
+490.000 465.000 l
+490.000 475.000 l
+505.000 475.000 l
+510.000 470.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 467.50 395.92 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+500.000 400.000 m 
+510.000 400.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+500.000 409.000 m 
+500.000 391.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+498.000 406.000 m 
+498.000 394.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+496.000 403.000 m 
+496.000 397.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+494.000 401.000 m 
+494.000 399.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 467.50 375.92 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+500.000 380.000 m 
+510.000 380.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+500.000 389.000 m 
+500.000 371.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+498.000 386.000 m 
+498.000 374.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+496.000 383.000 m 
+496.000 377.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+494.000 381.000 m 
+494.000 379.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 637.00 352.76 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+650.000 370.000 m 
+650.000 380.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+641.000 370.000 m 
+659.000 370.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+644.000 368.000 m 
+656.000 368.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+647.000 366.000 m 
+653.000 366.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+649.000 364.000 m 
+651.000 364.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 637.00 509.13 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+650.000 500.000 m 
+650.000 490.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+659.000 500.000 m 
+641.000 500.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+656.000 502.000 m 
+644.000 502.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+653.000 504.000 m 
+647.000 504.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+651.000 506.000 m 
+649.000 506.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 467.50 485.92 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+500.000 490.000 m 
+510.000 490.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+500.000 499.000 m 
+500.000 481.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+498.000 496.000 m 
+498.000 484.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+496.000 493.000 m 
+496.000 487.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+494.000 491.000 m 
+494.000 489.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 361.50 430.92 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+355.000 435.000 m 
+345.000 435.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+355.000 426.000 m 
+355.000 444.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+357.000 429.000 m 
+357.000 441.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+359.000 432.000 m 
+359.000 438.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+361.000 434.000 m 
+361.000 436.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 212.50 410.93 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+245.000 415.000 m 
+255.000 415.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+245.000 424.000 m 
+245.000 406.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+243.000 421.000 m 
+243.000 409.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+241.000 418.000 m 
+241.000 412.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+239.000 416.000 m 
+239.000 414.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 212.50 460.93 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+245.000 465.000 m 
+255.000 465.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+245.000 474.000 m 
+245.000 456.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+243.000 471.000 m 
+243.000 459.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+241.000 468.000 m 
+241.000 462.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+239.000 466.000 m 
+239.000 464.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+940.000 370.000 m 
+940.000 425.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 757.50 480.92 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+790.000 485.000 m 
+800.000 485.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+790.000 494.000 m 
+790.000 476.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+788.000 491.000 m 
+788.000 479.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+786.000 488.000 m 
+786.000 482.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+784.000 486.000 m 
+784.000 484.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 927.00 504.09 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+940.000 495.000 m 
+940.000 485.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+949.000 495.000 m 
+931.000 495.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+946.000 497.000 m 
+934.000 497.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+943.000 499.000 m 
+937.000 499.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+941.000 501.000 m 
+939.000 501.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 757.50 370.92 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+790.000 375.000 m 
+800.000 375.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+790.000 384.000 m 
+790.000 366.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+788.000 381.000 m 
+788.000 369.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+786.000 378.000 m 
+786.000 372.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+784.000 376.000 m 
+784.000 374.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 757.50 390.92 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+790.000 395.000 m 
+800.000 395.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+790.000 404.000 m 
+790.000 386.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+788.000 401.000 m 
+788.000 389.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+786.000 398.000 m 
+786.000 392.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+784.000 396.000 m 
+784.000 394.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 748.85 461.40 Tm
+(BUSY) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+800.000 465.000 m 
+795.000 460.000 l
+780.000 460.000 l
+780.000 470.000 l
+795.000 470.000 l
+800.000 465.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 961.71 431.60 Tm
+(RXEN) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+940.000 435.000 m 
+945.000 440.000 l
+960.000 440.000 l
+960.000 430.000 l
+945.000 430.000 l
+940.000 435.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 756.77 421.40 Tm
+(SCK) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+800.000 425.000 m 
+795.000 420.000 l
+780.000 420.000 l
+780.000 430.000 l
+795.000 430.000 l
+800.000 425.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 750.61 441.40 Tm
+(MISO) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+800.000 445.000 m 
+795.000 440.000 l
+780.000 440.000 l
+780.000 450.000 l
+795.000 450.000 l
+800.000 445.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 750.61 431.40 Tm
+(MOSI) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+800.000 435.000 m 
+795.000 430.000 l
+780.000 430.000 l
+780.000 440.000 l
+795.000 440.000 l
+800.000 435.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 764.69 411.40 Tm
+(CS) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+800.000 415.000 m 
+795.000 410.000 l
+780.000 410.000 l
+780.000 420.000 l
+795.000 420.000 l
+800.000 415.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 962.01 451.15 Tm
+(DIO2) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+940.000 455.000 m 
+945.000 460.000 l
+960.000 460.000 l
+960.000 450.000 l
+945.000 450.000 l
+940.000 455.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 759.19 471.40 Tm
+(IRQ) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+800.000 475.000 m 
+795.000 470.000 l
+780.000 470.000 l
+780.000 480.000 l
+795.000 480.000 l
+800.000 475.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 961.71 441.60 Tm
+(TXEN) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+940.000 445.000 m 
+945.000 450.000 l
+960.000 450.000 l
+960.000 440.000 l
+945.000 440.000 l
+940.000 445.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 750.06 451.57 Tm
+(NRST) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+800.000 455.000 m 
+795.000 450.000 l
+780.000 450.000 l
+780.000 460.000 l
+795.000 460.000 l
+800.000 455.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 927.00 342.76 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+940.000 360.000 m 
+940.000 370.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+931.000 360.000 m 
+949.000 360.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+934.000 358.000 m 
+946.000 358.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+937.000 356.000 m 
+943.000 356.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+939.000 354.000 m 
+941.000 354.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 243.00 487.00 Tm
+(VCC) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+255.000 485.000 m 
+255.000 475.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+250.000 485.000 m 
+260.000 485.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 660.50 465.93 Tm
+(VCC) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+660.000 470.000 m 
+650.000 470.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+660.000 475.000 m 
+660.000 465.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+175.000 425.000 m 
+210.000 425.000 l
+255.000 425.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 142.00 429.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+155.000 445.000 m 
+155.000 455.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+146.000 445.000 m 
+164.000 445.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+149.000 443.000 m 
+161.000 443.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+152.000 441.000 m 
+158.000 441.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+154.000 439.000 m 
+156.000 439.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 182.00 428.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+195.000 445.000 m 
+195.000 455.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+186.000 445.000 m 
+204.000 445.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+189.000 443.000 m 
+201.000 443.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+192.000 441.000 m 
+198.000 441.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+194.000 439.000 m 
+196.000 439.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 950.50 460.92 Tm
+(+5V) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+950.000 465.000 m 
+940.000 465.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+950.000 470.000 m 
+950.000 460.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+650.000 490.000 m 
+650.000 480.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+940.000 465.000 m 
+940.000 475.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+231.000 294.000 m 
+239.000 286.000 l
+239.000 294.000 m 
+231.000 286.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+371.000 244.000 m 
+379.000 236.000 l
+379.000 244.000 m 
+371.000 236.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+371.000 284.000 m 
+379.000 276.000 l
+379.000 284.000 m 
+371.000 276.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+371.000 274.000 m 
+379.000 266.000 l
+379.000 274.000 m 
+371.000 266.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+231.000 204.000 m 
+239.000 196.000 l
+239.000 204.000 m 
+231.000 196.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+371.000 204.000 m 
+379.000 196.000 l
+379.000 204.000 m 
+371.000 196.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 397.71 246.28 Tm
+(NRST) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+375.000 250.000 m 
+380.000 255.000 l
+395.000 255.000 l
+395.000 245.000 l
+380.000 245.000 l
+375.000 250.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 397.00 256.60 Tm
+(IRQ) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+375.000 260.000 m 
+380.000 265.000 l
+395.000 265.000 l
+395.000 255.000 l
+380.000 255.000 l
+375.000 260.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 183.85 236.40 Tm
+(BUSY) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+235.000 240.000 m 
+230.000 235.000 l
+215.000 235.000 l
+215.000 245.000 l
+230.000 245.000 l
+235.000 240.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 199.69 246.40 Tm
+(CS) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+235.000 250.000 m 
+230.000 245.000 l
+215.000 245.000 l
+215.000 255.000 l
+230.000 255.000 l
+235.000 250.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 185.61 266.40 Tm
+(MOSI) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+235.000 270.000 m 
+230.000 265.000 l
+215.000 265.000 l
+215.000 275.000 l
+230.000 275.000 l
+235.000 270.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 191.77 256.40 Tm
+(SCK) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+235.000 260.000 m 
+230.000 255.000 l
+215.000 255.000 l
+215.000 265.000 l
+230.000 265.000 l
+235.000 260.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 185.61 276.40 Tm
+(MISO) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+235.000 280.000 m 
+230.000 275.000 l
+215.000 275.000 l
+215.000 285.000 l
+230.000 285.000 l
+235.000 280.000 l
+S
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+298.96 313.33 Td
+(E80-900M2213S) Tj
+ET
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+298.96 322.33 Td
+(E80-900M2213S) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+257.00 187.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+238.57 191.00 Td
+(22) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+235.000 190.000 m 
+255.000 190.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+257.00 197.00 Td
+(ANT_2.4) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+238.57 201.00 Td
+(21) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+235.000 200.000 m 
+255.000 200.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+257.00 207.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+238.57 211.00 Td
+(20) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+235.000 210.000 m 
+255.000 210.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+257.00 227.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+238.57 231.00 Td
+(19) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+235.000 230.000 m 
+255.000 230.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+257.00 237.00 Td
+(BUSY) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+238.57 241.00 Td
+(18) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+235.000 240.000 m 
+255.000 240.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+257.00 247.00 Td
+(NSS) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+238.57 251.00 Td
+(17) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+235.000 250.000 m 
+255.000 250.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+257.00 257.00 Td
+(SCK) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+238.57 261.00 Td
+(16) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+235.000 260.000 m 
+255.000 260.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+257.00 267.00 Td
+(MOSI) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+238.57 271.00 Td
+(15) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+235.000 270.000 m 
+255.000 270.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+257.00 277.00 Td
+(MISO) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+238.57 281.00 Td
+(14) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+235.000 280.000 m 
+255.000 280.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+257.00 287.00 Td
+(NC) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+238.57 291.00 Td
+(13) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+235.000 290.000 m 
+255.000 290.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+257.00 297.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+238.57 301.00 Td
+(12) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+235.000 300.000 m 
+255.000 300.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+332.36 297.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+360.00 301.00 Td
+(11) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+375.000 300.000 m 
+355.000 300.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+334.29 287.00 Td
+(VCC) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+360.00 291.00 Td
+(10) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+375.000 290.000 m 
+355.000 290.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+329.49 277.00 Td
+(DIO7) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+360.00 281.00 Td
+(9) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+375.000 280.000 m 
+355.000 280.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+329.49 267.00 Td
+(DIO8) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+360.00 271.00 Td
+(8) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+375.000 270.000 m 
+355.000 270.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+329.49 257.00 Td
+(DIO9) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+360.00 261.00 Td
+(7) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+375.000 260.000 m 
+355.000 260.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+328.32 247.00 Td
+(NRST) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+360.00 251.00 Td
+(6) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+375.000 250.000 m 
+355.000 250.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+339.99 237.00 Td
+(NC) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+360.00 241.00 Td
+(5) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+375.000 240.000 m 
+355.000 240.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+332.36 227.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+360.00 231.00 Td
+(4) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+375.000 230.000 m 
+355.000 230.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+332.36 207.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+360.00 211.00 Td
+(3) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+375.000 210.000 m 
+355.000 210.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+311.72 197.00 Td
+(ANT_900) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+360.00 201.00 Td
+(2) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+375.000 200.000 m 
+355.000 200.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+332.36 187.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+360.00 191.00 Td
+(1) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+375.000 190.000 m 
+355.000 190.000 l
+S
+2 J
+0 j
+100 M
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+257.000 310.000 m 
+353.000 310.000 l
+354.105 310.000 355.000 309.105 355.000 308.000 c
+355.000 172.000 l
+355.000 170.895 353.895 170.000 353.000 170.000 c
+257.000 170.000 l
+255.895 170.000 255.000 171.105 255.000 172.000 c
+255.000 308.000 l
+255.000 309.105 256.105 310.000 257.000 310.000 c
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 362.00 163.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+375.000 180.000 m 
+375.000 190.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+366.000 180.000 m 
+384.000 180.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+369.000 178.000 m 
+381.000 178.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+372.000 176.000 m 
+378.000 176.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+374.000 174.000 m 
+376.000 174.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 391.50 206.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+385.000 210.000 m 
+375.000 210.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+385.000 201.000 m 
+385.000 219.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+387.000 204.000 m 
+387.000 216.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+389.000 207.000 m 
+389.000 213.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+391.000 209.000 m 
+391.000 211.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 391.50 226.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+385.000 230.000 m 
+375.000 230.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+385.000 221.000 m 
+385.000 239.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+387.000 224.000 m 
+387.000 236.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+389.000 227.000 m 
+389.000 233.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+391.000 229.000 m 
+391.000 231.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 391.50 296.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+385.000 300.000 m 
+375.000 300.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+385.000 291.000 m 
+385.000 309.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+387.000 294.000 m 
+387.000 306.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+389.000 297.000 m 
+389.000 303.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+391.000 299.000 m 
+391.000 301.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 192.50 296.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+225.000 300.000 m 
+235.000 300.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+225.000 309.000 m 
+225.000 291.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+223.000 306.000 m 
+223.000 294.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+221.000 303.000 m 
+221.000 297.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+219.000 301.000 m 
+219.000 299.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 192.50 226.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+225.000 230.000 m 
+235.000 230.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+225.000 239.000 m 
+225.000 221.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+223.000 236.000 m 
+223.000 224.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+221.000 233.000 m 
+221.000 227.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+219.000 231.000 m 
+219.000 229.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 192.50 206.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+225.000 210.000 m 
+235.000 210.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+225.000 219.000 m 
+225.000 201.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+223.000 216.000 m 
+223.000 204.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+221.000 213.000 m 
+221.000 207.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+219.000 211.000 m 
+219.000 209.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 192.50 186.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+225.000 190.000 m 
+235.000 190.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+225.000 199.000 m 
+225.000 181.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+223.000 196.000 m 
+223.000 184.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+221.000 193.000 m 
+221.000 187.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+219.000 191.000 m 
+219.000 189.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 386.00 286.00 Tm
+(VCC) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+385.000 290.000 m 
+375.000 290.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+385.000 295.000 m 
+385.000 285.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+160.000 675.000 m 
+160.000 680.000 l
+160.000 685.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+145.000 655.000 m 
+160.000 655.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+145.000 645.000 m 
+160.000 645.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+145.000 635.000 m 
+160.000 635.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+145.000 625.000 m 
+160.000 625.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+145.000 615.000 m 
+160.000 615.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+145.000 605.000 m 
+160.000 605.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 112.70 591.40 Tm
+(P1.06) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+160.000 595.000 m 
+155.000 590.000 l
+140.000 590.000 l
+140.000 600.000 l
+155.000 600.000 l
+160.000 595.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 112.70 701.40 Tm
+(P0.06) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+160.000 705.000 m 
+155.000 700.000 l
+140.000 700.000 l
+140.000 710.000 l
+155.000 710.000 l
+160.000 705.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 112.70 691.40 Tm
+(P0.08) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+160.000 695.000 m 
+155.000 690.000 l
+140.000 690.000 l
+140.000 700.000 l
+155.000 700.000 l
+160.000 695.000 l
+S
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+417.00 632.75 Td
+(1.5M) Tj
+ET
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+417.00 641.75 Td
+(R2) Tj
+ET
+2 J
+0 j
+100 M
+1.00 w
+0.63 0.00 0.00 RG
+[] 0 d
+405.00 655.00 10.00 -20.00 re
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+410.000 655.000 m 
+410.000 665.000 l
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+410.000 635.000 m 
+410.000 625.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+0.00 1.00 -1.00 0.00 413.70 727.50 Tm
+(Batt) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+410.000 705.000 m 
+405.000 710.000 l
+405.000 725.000 l
+415.000 725.000 l
+415.000 710.000 l
+410.000 705.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+186.000 549.000 m 
+194.000 541.000 l
+194.000 549.000 m 
+186.000 541.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+196.000 549.000 m 
+204.000 541.000 l
+204.000 549.000 m 
+196.000 541.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+206.000 549.000 m 
+214.000 541.000 l
+214.000 549.000 m 
+206.000 541.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+276.000 719.000 m 
+284.000 711.000 l
+284.000 719.000 m 
+276.000 711.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+156.000 719.000 m 
+164.000 711.000 l
+164.000 719.000 m 
+156.000 711.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 397.00 599.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+410.000 615.000 m 
+410.000 625.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+401.000 615.000 m 
+419.000 615.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+404.000 613.000 m 
+416.000 613.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+407.000 611.000 m 
+413.000 611.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+409.000 609.000 m 
+411.000 609.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+410.000 665.000 m 
+280.000 665.000 l
+S
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+417.00 672.75 Td
+(1M) Tj
+ET
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+417.00 681.75 Td
+(R1) Tj
+ET
+2 J
+0 j
+100 M
+1.00 w
+0.63 0.00 0.00 RG
+[] 0 d
+405.00 695.00 10.00 -20.00 re
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+410.000 695.000 m 
+410.000 705.000 l
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+410.000 675.000 m 
+410.000 665.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+290.000 685.000 m 
+280.000 685.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 312.01 681.07 Tm
+(RBtn) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+290.000 685.000 m 
+295.000 690.000 l
+310.000 690.000 l
+310.000 680.000 l
+295.000 680.000 l
+290.000 685.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 99.24 621.60 Tm
+(UBtn) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+145.000 625.000 m 
+140.000 620.000 l
+125.000 620.000 l
+125.000 630.000 l
+140.000 630.000 l
+145.000 625.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 117.50 676.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+150.000 680.000 m 
+160.000 680.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+150.000 689.000 m 
+150.000 671.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+148.000 686.000 m 
+148.000 674.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+146.000 683.000 m 
+146.000 677.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+144.000 681.000 m 
+144.000 679.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+290.000 595.000 m 
+280.000 595.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+290.000 655.000 m 
+280.000 655.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+290.000 605.000 m 
+280.000 605.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+290.000 625.000 m 
+280.000 625.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 92.64 631.40 Tm
+(GPSen) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+145.000 635.000 m 
+140.000 630.000 l
+125.000 630.000 l
+125.000 640.000 l
+140.000 640.000 l
+145.000 635.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 93.85 651.40 Tm
+(GPSrx) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+145.000 655.000 m 
+140.000 650.000 l
+125.000 650.000 l
+125.000 660.000 l
+140.000 660.000 l
+145.000 655.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 94.40 641.40 Tm
+(GPStx) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+145.000 645.000 m 
+140.000 640.000 l
+125.000 640.000 l
+125.000 650.000 l
+140.000 650.000 l
+145.000 645.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+290.000 615.000 m 
+280.000 615.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+290.000 635.000 m 
+280.000 635.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+290.000 645.000 m 
+280.000 645.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+345.000 695.000 m 
+280.000 695.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+345.000 675.000 m 
+280.000 675.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 312.00 601.60 Tm
+(IRQ) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+290.000 605.000 m 
+295.000 610.000 l
+310.000 610.000 l
+310.000 600.000 l
+295.000 600.000 l
+290.000 605.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 312.50 591.60 Tm
+(NRST) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+290.000 595.000 m 
+295.000 600.000 l
+310.000 600.000 l
+310.000 590.000 l
+295.000 590.000 l
+290.000 595.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 356.00 671.00 Tm
+(VCC) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+355.000 675.000 m 
+345.000 675.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+355.000 680.000 m 
+355.000 670.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 312.00 621.60 Tm
+(CS) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+290.000 625.000 m 
+295.000 630.000 l
+310.000 630.000 l
+310.000 620.000 l
+295.000 620.000 l
+290.000 625.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 312.00 631.60 Tm
+(MOSI) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+290.000 635.000 m 
+295.000 640.000 l
+310.000 640.000 l
+310.000 630.000 l
+295.000 630.000 l
+290.000 635.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 312.00 641.60 Tm
+(MISO) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+290.000 645.000 m 
+295.000 650.000 l
+310.000 650.000 l
+310.000 640.000 l
+295.000 640.000 l
+290.000 645.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 311.84 611.60 Tm
+(SCk) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+290.000 615.000 m 
+295.000 620.000 l
+310.000 620.000 l
+310.000 610.000 l
+295.000 610.000 l
+290.000 615.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 311.70 651.60 Tm
+(BUSY) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+290.000 655.000 m 
+295.000 660.000 l
+310.000 660.000 l
+310.000 650.000 l
+295.000 650.000 l
+290.000 655.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 361.50 691.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+355.000 695.000 m 
+345.000 695.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+355.000 686.000 m 
+355.000 704.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+357.000 689.000 m 
+357.000 701.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+359.000 692.000 m 
+359.000 698.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+361.000 694.000 m 
+361.000 696.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 302.50 701.30 Tm
+(Batt) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+280.000 705.000 m 
+285.000 710.000 l
+300.000 710.000 l
+300.000 700.000 l
+285.000 700.000 l
+280.000 705.000 l
+S
+10.00 w
+BT
+/F1 13 Tf
+13.00 TL
+0.000 0.000 1.000 rg
+1015.00 25.00 Td
+(Nom De Tom) Tj
+ET
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 647.00 251.60 Tm
+(IRQ) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+625.000 255.000 m 
+630.000 260.000 l
+645.000 260.000 l
+645.000 250.000 l
+630.000 250.000 l
+625.000 255.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 513.00 297.00 Tm
+(VCC) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+525.000 295.000 m 
+525.000 285.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+520.000 295.000 m 
+530.000 295.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+625.000 175.000 m 
+625.000 215.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 612.00 149.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+625.000 165.000 m 
+625.000 175.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+616.000 165.000 m 
+634.000 165.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+619.000 163.000 m 
+631.000 163.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+622.000 161.000 m 
+628.000 161.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+624.000 159.000 m 
+626.000 159.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 474.33 201.85 Tm
+(BUSY) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+525.000 205.000 m 
+520.000 200.000 l
+505.000 200.000 l
+505.000 210.000 l
+520.000 210.000 l
+525.000 205.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 473.74 191.85 Tm
+(RXEN) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+525.000 195.000 m 
+520.000 190.000 l
+505.000 190.000 l
+505.000 200.000 l
+520.000 200.000 l
+525.000 195.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 482.41 231.85 Tm
+(SCK) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+525.000 235.000 m 
+520.000 230.000 l
+505.000 230.000 l
+505.000 240.000 l
+520.000 240.000 l
+525.000 235.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 476.41 251.85 Tm
+(MISO) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+525.000 255.000 m 
+520.000 250.000 l
+505.000 250.000 l
+505.000 260.000 l
+520.000 260.000 l
+525.000 255.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 476.48 241.85 Tm
+(MOSI) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+525.000 245.000 m 
+520.000 240.000 l
+505.000 240.000 l
+505.000 250.000 l
+520.000 250.000 l
+525.000 245.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 489.69 221.45 Tm
+(CS) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+525.000 225.000 m 
+520.000 220.000 l
+505.000 220.000 l
+505.000 230.000 l
+520.000 230.000 l
+525.000 225.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 647.01 241.15 Tm
+(DIO2) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+625.000 245.000 m 
+630.000 250.000 l
+645.000 250.000 l
+645.000 240.000 l
+630.000 240.000 l
+625.000 245.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 474.33 181.85 Tm
+(TXEN) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+525.000 185.000 m 
+520.000 180.000 l
+505.000 180.000 l
+505.000 190.000 l
+520.000 190.000 l
+525.000 185.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 475.06 261.57 Tm
+(NRST) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+525.000 265.000 m 
+520.000 260.000 l
+505.000 260.000 l
+505.000 270.000 l
+520.000 270.000 l
+525.000 265.000 l
+S
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+568.96 298.33 Td
+(SX1262_MOD) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+584.58 282.00 Td
+(ANT) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+610.00 286.00 Td
+(16) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+625.000 285.000 m 
+605.000 285.000 l
+S
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+568.96 307.33 Td
+(CORE_SX1262) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+582.36 202.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+610.00 206.00 Td
+(2) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+625.000 205.000 m 
+605.000 205.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+547.00 192.00 Td
+(RXEN) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+534.28 196.00 Td
+(3) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+525.000 195.000 m 
+545.000 195.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+547.00 182.00 Td
+(TXEN) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+534.28 186.00 Td
+(4) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+525.000 185.000 m 
+545.000 185.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+579.49 242.00 Td
+(DIO2) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+610.00 246.00 Td
+(5) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+625.000 245.000 m 
+605.000 245.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+579.49 252.00 Td
+(DIO1) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+610.00 256.00 Td
+(6) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+625.000 255.000 m 
+605.000 255.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+582.36 192.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+610.00 196.00 Td
+(7) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+625.000 195.000 m 
+605.000 195.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+547.00 282.00 Td
+(3V3) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+534.28 286.00 Td
+(8) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+525.000 285.000 m 
+545.000 285.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+547.00 202.00 Td
+(BUSY) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+534.28 206.00 Td
+(9) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+525.000 205.000 m 
+545.000 205.000 l
+S
+1.00 w
+0.53 0.00 0.00 RG
+545.00 265.00 m 545.00 266.66 543.66 268.00 542.00 268.00 c
+540.34 268.00 539.00 266.66 539.00 265.00 c
+539.00 263.34 540.34 262.00 542.00 262.00 c
+543.66 262.00 545.00 263.34 545.00 265.00 c
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+547.00 262.00 Td
+(RST) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+528.57 266.00 Td
+(10) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+525.000 265.000 m 
+539.000 265.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+547.00 252.00 Td
+(MISO) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+528.57 256.00 Td
+(11) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+525.000 255.000 m 
+545.000 255.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+547.00 242.00 Td
+(MOSI) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+528.57 246.00 Td
+(12) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+525.000 245.000 m 
+545.000 245.000 l
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+545.000 232.000 m 
+548.000 235.000 l
+545.000 238.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+547.00 232.00 Td
+(CLK) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+528.57 236.00 Td
+(13) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+525.000 235.000 m 
+545.000 235.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+547.00 222.00 Td
+(CS) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+528.57 226.00 Td
+(14) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+525.000 225.000 m 
+545.000 225.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+582.36 182.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+610.00 186.00 Td
+(15) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+625.000 185.000 m 
+605.000 185.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+582.36 212.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+610.00 216.00 Td
+(1) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+625.000 215.000 m 
+605.000 215.000 l
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+0.00 g
+[] 0 d
+550.000 295.000 m 
+600.000 295.000 l
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+0.00 g
+[] 0 d
+605.000 290.000 m 
+605.000 180.000 l
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+0.00 g
+[] 0 d
+600.000 175.000 m 
+550.000 175.000 l
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+0.00 g
+[] 0 d
+545.000 180.000 m 
+545.000 290.000 l
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+545.000 290.000 m 
+545.000 295.000 545.000 295.000 550.000 295.000 c
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+600.000 295.000 m 
+605.000 295.000 605.000 295.000 605.000 290.000 c
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+605.000 180.000 m 
+605.000 175.000 605.000 175.000 600.000 175.000 c
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+545.000 180.000 m 
+545.000 175.000 545.000 175.000 550.000 175.000 c
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 737.72 681.45 Tm
+(MCU_RXEN) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+820.000 685.000 m 
+815.000 680.000 l
+800.000 680.000 l
+800.000 690.000 l
+815.000 690.000 l
+820.000 685.000 l
+S
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+1115.00 362.67 Td
+(100uF) Tj
+ET
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+1115.00 371.67 Td
+(C1) Tj
+ET
+1 J
+1 j
+1.00 w
+0.63 0.00 0.00 RG
+0.00 g
+[] 0 d
+1113.000 373.000 m 
+1097.000 373.000 l
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+1105.000 365.000 m 
+1105.000 355.000 l
+S
+1 J
+1 j
+1.00 w
+0.63 0.00 0.00 RG
+0.00 g
+[] 0 d
+1105.000 385.000 m 
+1105.000 377.000 l
+S
+1 J
+1 j
+1.00 w
+0.63 0.00 0.00 RG
+0.00 g
+[] 0 d
+1097.000 377.000 m 
+1113.000 377.000 l
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+1105.000 385.000 m 
+1105.000 395.000 l
+S
+1 J
+1 j
+1.00 w
+0.63 0.00 0.00 RG
+0.00 g
+[] 0 d
+1105.000 373.000 m 
+1105.000 365.000 l
+S
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+1065.00 362.67 Td
+(100uF) Tj
+ET
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+1065.00 371.67 Td
+(C2) Tj
+ET
+1 J
+1 j
+1.00 w
+0.63 0.00 0.00 RG
+0.00 g
+[] 0 d
+1063.000 373.000 m 
+1047.000 373.000 l
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+1055.000 365.000 m 
+1055.000 355.000 l
+S
+1 J
+1 j
+1.00 w
+0.63 0.00 0.00 RG
+0.00 g
+[] 0 d
+1055.000 385.000 m 
+1055.000 377.000 l
+S
+1 J
+1 j
+1.00 w
+0.63 0.00 0.00 RG
+0.00 g
+[] 0 d
+1047.000 377.000 m 
+1063.000 377.000 l
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+1055.000 385.000 m 
+1055.000 395.000 l
+S
+1 J
+1 j
+1.00 w
+0.63 0.00 0.00 RG
+0.00 g
+[] 0 d
+1055.000 373.000 m 
+1055.000 365.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 1092.00 329.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+1105.000 345.000 m 
+1105.000 355.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+1096.000 345.000 m 
+1114.000 345.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+1099.000 343.000 m 
+1111.000 343.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+1102.000 341.000 m 
+1108.000 341.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+1104.000 339.000 m 
+1106.000 339.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 1043.00 407.00 Tm
+(VCC) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+1055.000 405.000 m 
+1055.000 395.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+1050.000 405.000 m 
+1060.000 405.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 1042.00 327.76 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+1055.000 345.000 m 
+1055.000 355.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+1046.000 345.000 m 
+1064.000 345.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+1049.000 343.000 m 
+1061.000 343.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+1052.000 341.000 m 
+1058.000 341.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+1054.000 339.000 m 
+1056.000 339.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 1095.00 407.00 Tm
+(+5V) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+1105.000 405.000 m 
+1105.000 395.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+1100.000 405.000 m 
+1110.000 405.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 102.98 611.40 Tm
+(SCL) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+145.000 615.000 m 
+140.000 610.000 l
+125.000 610.000 l
+125.000 620.000 l
+140.000 620.000 l
+145.000 615.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 101.11 601.40 Tm
+(SDA) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+145.000 605.000 m 
+140.000 600.000 l
+125.000 600.000 l
+125.000 610.000 l
+140.000 610.000 l
+145.000 605.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+820.000 685.000 m 
+840.000 685.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 77.72 661.45 Tm
+(MCU_RXEN) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+160.000 665.000 m 
+155.000 660.000 l
+140.000 660.000 l
+140.000 670.000 l
+155.000 670.000 l
+160.000 665.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+160.000 715.000 m 
+165.000 715.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+621.000 289.000 m 
+629.000 281.000 l
+629.000 289.000 m 
+621.000 281.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+251.000 439.000 m 
+259.000 431.000 l
+259.000 439.000 m 
+251.000 431.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+251.000 449.000 m 
+259.000 441.000 l
+259.000 449.000 m 
+251.000 441.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+341.000 449.000 m 
+349.000 441.000 l
+349.000 449.000 m 
+341.000 441.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+251.000 409.000 m 
+259.000 401.000 l
+259.000 409.000 m 
+251.000 401.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+171.000 479.000 m 
+179.000 471.000 l
+179.000 479.000 m 
+171.000 471.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 432.69 661.28 Tm
+(ADC) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+410.000 665.000 m 
+415.000 670.000 l
+430.000 670.000 l
+430.000 660.000 l
+415.000 660.000 l
+410.000 665.000 l
+S
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+213.86 728.26 Td
+(PRO_MICRO_NRF52840_29P) Tj
+ET
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+213.86 737.11 Td
+(PRO-MICRO) Tj
+ET
+2 J
+0 j
+100 M
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+180.00 725.00 80.00 -160.00 re
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+234.94 702.00 Td
+(BATIN) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+266.00 706.00 Td
+(25) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+280.000 705.000 m 
+260.000 705.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+240.95 692.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+266.00 696.00 Td
+(24) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+280.000 695.000 m 
+260.000 695.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+243.04 682.00 Td
+(RST) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+266.00 686.00 Td
+(23) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+280.000 685.000 m 
+260.000 685.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+226.28 672.00 Td
+(3.3v Out) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+266.00 676.00 Td
+(22) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+280.000 675.000 m 
+260.000 675.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+236.90 632.00 Td
+(P1.15) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+266.00 636.00 Td
+(18) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+280.000 635.000 m 
+260.000 635.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+236.90 662.00 Td
+(P0.31) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+266.00 666.00 Td
+(21) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+280.000 665.000 m 
+260.000 665.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+183.00 692.00 Td
+(P0.08) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+168.28 696.00 Td
+(3) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+160.000 695.000 m 
+180.000 695.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+236.90 622.00 Td
+(P1.13) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+266.00 626.00 Td
+(17) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+280.000 625.000 m 
+260.000 625.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+236.90 652.00 Td
+(P0.29) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+266.00 656.00 Td
+(20) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+280.000 655.000 m 
+260.000 655.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+236.90 612.00 Td
+(P1.11) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+266.00 616.00 Td
+(16) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+280.000 615.000 m 
+260.000 615.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+236.90 642.00 Td
+(P0.02) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+266.00 646.00 Td
+(19) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+280.000 645.000 m 
+260.000 645.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+183.00 682.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+168.28 686.00 Td
+(4) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+160.000 685.000 m 
+180.000 685.000 l
+S
+1.00 w
+0.55 0.14 0.14 RG
+180.00 705.00 m 180.00 706.66 178.66 708.00 177.00 708.00 c
+175.34 708.00 174.00 706.66 174.00 705.00 c
+174.00 703.34 175.34 702.00 177.00 702.00 c
+178.66 702.00 180.00 703.34 180.00 705.00 c
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+183.00 702.00 Td
+(P0.06) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+168.28 706.00 Td
+(2) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+160.000 705.000 m 
+174.000 705.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+236.90 592.00 Td
+(P0.09) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+266.00 596.00 Td
+(14) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+280.000 595.000 m 
+260.000 595.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+236.90 602.00 Td
+(P0.10) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+266.00 606.00 Td
+(15) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+280.000 605.000 m 
+260.000 605.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+183.00 602.00 Td
+(P1.04) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+162.57 606.00 Td
+(12) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+160.000 605.000 m 
+180.000 605.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+183.00 672.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+168.28 676.00 Td
+(5) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+160.000 675.000 m 
+180.000 675.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+183.00 662.00 Td
+(P0.17) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+168.28 666.00 Td
+(6) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+160.000 665.000 m 
+180.000 665.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+183.00 652.00 Td
+(P0.20) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+168.28 656.00 Td
+(7) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+160.000 655.000 m 
+180.000 655.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+183.00 642.00 Td
+(P0.22) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+168.28 646.00 Td
+(8) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+160.000 645.000 m 
+180.000 645.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+183.00 632.00 Td
+(P0.24) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+168.28 636.00 Td
+(9) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+160.000 635.000 m 
+180.000 635.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+183.00 622.00 Td
+(P1.00) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+162.57 626.00 Td
+(10) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+160.000 625.000 m 
+180.000 625.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+183.00 612.00 Td
+(P0.11) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+162.57 616.00 Td
+(11) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+160.000 615.000 m 
+180.000 615.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+183.00 592.00 Td
+(P1.06) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+162.57 596.00 Td
+(13) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+160.000 595.000 m 
+180.000 595.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+0.00 1.00 -1.00 0.00 193.00 568.00 Tm
+(P1.01) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+0.00 1.00 -1.00 0.00 189.00 547.57 Tm
+(27) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+190.000 545.000 m 
+190.000 565.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+0.00 1.00 -1.00 0.00 203.00 568.00 Tm
+(P1.02) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+0.00 1.00 -1.00 0.00 199.00 547.57 Tm
+(28) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+200.000 545.000 m 
+200.000 565.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+0.00 1.00 -1.00 0.00 213.00 568.00 Tm
+(P1.07) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+0.00 1.00 -1.00 0.00 209.00 547.57 Tm
+(29) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+210.000 545.000 m 
+210.000 565.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+234.94 712.00 Td
+(BATIN) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+266.00 716.00 Td
+(26) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+280.000 715.000 m 
+260.000 715.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+183.00 712.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+168.28 716.00 Td
+(1) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+160.000 715.000 m 
+180.000 715.000 l
+S
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+864.25 253.00 Td
+(RA-02_C9900010926) Tj
+ET
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+864.25 262.00 Td
+(RA-02) Tj
+ET
+2 J
+0 j
+100 M
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+837.000 250.000 m 
+903.000 250.000 l
+904.105 250.000 905.000 249.105 905.000 248.000 c
+905.000 162.000 l
+905.000 160.895 903.895 160.000 903.000 160.000 c
+837.000 160.000 l
+835.895 160.000 835.000 161.105 835.000 162.000 c
+835.000 248.000 l
+835.000 249.105 836.105 250.000 837.000 250.000 c
+S
+1.00 w
+0.53 0.00 0.00 RG
+0.53 0.00 0.00 rg
+[] 0 d
+841.50 245.00 m 841.50 245.83 840.83 246.50 840.00 246.50 c
+839.17 246.50 838.50 245.83 838.50 245.00 c
+838.50 244.17 839.17 243.50 840.00 243.50 c
+840.83 243.50 841.50 244.17 841.50 245.00 c
+B
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+838.70 236.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+828.78 241.00 Td
+(1) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+[] 0 d
+825.000 240.000 m 
+835.000 240.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+838.70 226.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+828.78 231.00 Td
+(2) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+[] 0 d
+825.000 230.000 m 
+835.000 230.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+838.70 216.00 Td
+(3.3V) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+828.78 221.00 Td
+(3) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+825.000 220.000 m 
+835.000 220.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+838.70 206.00 Td
+(RESET) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+828.78 211.00 Td
+(4) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+825.000 210.000 m 
+835.000 210.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+838.70 196.00 Td
+(DIO0) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+828.78 201.00 Td
+(5) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+825.000 200.000 m 
+835.000 200.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+838.70 186.00 Td
+(DIO1) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+828.78 191.00 Td
+(6) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+825.000 190.000 m 
+835.000 190.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+838.70 176.00 Td
+(DIO2) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+828.78 181.00 Td
+(7) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+825.000 180.000 m 
+835.000 180.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+838.70 166.00 Td
+(DIO3) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+828.78 171.00 Td
+(8) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+825.000 170.000 m 
+835.000 170.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+880.66 166.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+905.50 171.00 Td
+(9) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+[] 0 d
+915.000 170.000 m 
+905.000 170.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+877.79 176.00 Td
+(DIO4) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+905.50 181.00 Td
+(10) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+915.000 180.000 m 
+905.000 180.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+877.79 186.00 Td
+(DIO5) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+905.50 191.00 Td
+(11) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+915.000 190.000 m 
+905.000 190.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+882.64 196.00 Td
+(SCK) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+905.50 201.00 Td
+(12) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+915.000 200.000 m 
+905.000 200.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+876.71 206.00 Td
+(MISO) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+905.50 211.00 Td
+(13) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+915.000 210.000 m 
+905.000 210.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+876.71 216.00 Td
+(MOSI) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+905.50 221.00 Td
+(14) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+915.000 220.000 m 
+905.000 220.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+882.27 226.00 Td
+(NSS) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+905.50 231.00 Td
+(15) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+915.000 230.000 m 
+905.000 230.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+880.66 236.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+905.50 241.00 Td
+(16) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+[] 0 d
+915.000 240.000 m 
+905.000 240.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 773.85 196.40 Tm
+(BUSY) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+825.000 200.000 m 
+820.000 195.000 l
+805.000 195.000 l
+805.000 205.000 l
+820.000 205.000 l
+825.000 200.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 937.00 226.60 Tm
+(CS) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+915.000 230.000 m 
+920.000 235.000 l
+935.000 235.000 l
+935.000 225.000 l
+920.000 225.000 l
+915.000 230.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 936.71 196.60 Tm
+(SCK) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+915.000 200.000 m 
+920.000 205.000 l
+935.000 205.000 l
+935.000 195.000 l
+920.000 195.000 l
+915.000 200.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 937.00 216.60 Tm
+(MOSI) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+915.000 220.000 m 
+920.000 225.000 l
+935.000 225.000 l
+935.000 215.000 l
+920.000 215.000 l
+915.000 220.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 937.00 206.60 Tm
+(MISO) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+915.000 210.000 m 
+920.000 215.000 l
+935.000 215.000 l
+935.000 205.000 l
+920.000 205.000 l
+915.000 210.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 775.06 206.57 Tm
+(NRST) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+825.000 210.000 m 
+820.000 205.000 l
+805.000 205.000 l
+805.000 215.000 l
+820.000 215.000 l
+825.000 210.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 784.19 186.40 Tm
+(IRQ) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+825.000 190.000 m 
+820.000 185.000 l
+805.000 185.000 l
+805.000 195.000 l
+820.000 195.000 l
+825.000 190.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 931.50 166.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+925.000 170.000 m 
+915.000 170.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+925.000 161.000 m 
+925.000 179.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+927.000 164.000 m 
+927.000 176.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+929.000 167.000 m 
+929.000 173.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+931.000 169.000 m 
+931.000 171.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 931.50 236.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+925.000 240.000 m 
+915.000 240.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+925.000 231.000 m 
+925.000 249.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+927.000 234.000 m 
+927.000 246.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+929.000 237.000 m 
+929.000 243.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+931.000 239.000 m 
+931.000 241.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 782.50 236.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+815.000 240.000 m 
+825.000 240.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+815.000 249.000 m 
+815.000 231.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+813.000 246.000 m 
+813.000 234.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+811.000 243.000 m 
+811.000 237.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+809.000 241.000 m 
+809.000 239.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+825.000 240.000 m 
+825.000 230.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 789.82 216.00 Tm
+(VCC) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+815.000 220.000 m 
+825.000 220.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+815.000 215.000 m 
+815.000 225.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+911.000 194.000 m 
+919.000 186.000 l
+919.000 194.000 m 
+911.000 186.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+911.000 184.000 m 
+919.000 176.000 l
+919.000 184.000 m 
+911.000 176.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+821.000 184.000 m 
+829.000 176.000 l
+829.000 184.000 m 
+821.000 176.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+821.000 174.000 m 
+829.000 166.000 l
+829.000 174.000 m 
+821.000 166.000 l
+S
+0.80 0.00 0.00 rg
+652.50 420.00 m 652.50 421.38 651.38 422.50 650.00 422.50 c
+648.62 422.50 647.50 421.38 647.50 420.00 c
+647.50 418.62 648.62 417.50 650.00 417.50 c
+651.38 417.50 652.50 418.62 652.50 420.00 c
+f
+0.80 0.00 0.00 rg
+942.50 415.00 m 942.50 416.38 941.38 417.50 940.00 417.50 c
+938.62 417.50 937.50 416.38 937.50 415.00 c
+937.50 413.62 938.62 412.50 940.00 412.50 c
+941.38 412.50 942.50 413.62 942.50 415.00 c
+f
+0.80 0.00 0.00 rg
+942.50 395.00 m 942.50 396.38 941.38 397.50 940.00 397.50 c
+938.62 397.50 937.50 396.38 937.50 395.00 c
+937.50 393.62 938.62 392.50 940.00 392.50 c
+941.38 392.50 942.50 393.62 942.50 395.00 c
+f
+0.80 0.00 0.00 rg
+942.50 385.00 m 942.50 386.38 941.38 387.50 940.00 387.50 c
+938.62 387.50 937.50 386.38 937.50 385.00 c
+937.50 383.62 938.62 382.50 940.00 382.50 c
+941.38 382.50 942.50 383.62 942.50 385.00 c
+f
+0.80 0.00 0.00 rg
+942.50 375.00 m 942.50 376.38 941.38 377.50 940.00 377.50 c
+938.62 377.50 937.50 376.38 937.50 375.00 c
+937.50 373.62 938.62 372.50 940.00 372.50 c
+941.38 372.50 942.50 373.62 942.50 375.00 c
+f
+0.80 0.00 0.00 rg
+652.50 490.00 m 652.50 491.38 651.38 492.50 650.00 492.50 c
+648.62 492.50 647.50 491.38 647.50 490.00 c
+647.50 488.62 648.62 487.50 650.00 487.50 c
+651.38 487.50 652.50 488.62 652.50 490.00 c
+f
+0.80 0.00 0.00 rg
+942.50 465.00 m 942.50 466.38 941.38 467.50 940.00 467.50 c
+938.62 467.50 937.50 466.38 937.50 465.00 c
+937.50 463.62 938.62 462.50 940.00 462.50 c
+941.38 462.50 942.50 463.62 942.50 465.00 c
+f
+0.80 0.00 0.00 rg
+412.50 665.00 m 412.50 666.38 411.38 667.50 410.00 667.50 c
+408.62 667.50 407.50 666.38 407.50 665.00 c
+407.50 663.62 408.62 662.50 410.00 662.50 c
+411.38 662.50 412.50 663.62 412.50 665.00 c
+f
+0.80 0.00 0.00 rg
+162.50 680.00 m 162.50 681.38 161.38 682.50 160.00 682.50 c
+158.62 682.50 157.50 681.38 157.50 680.00 c
+157.50 678.62 158.62 677.50 160.00 677.50 c
+161.38 677.50 162.50 678.62 162.50 680.00 c
+f
+0.80 0.00 0.00 rg
+627.50 185.00 m 627.50 186.38 626.38 187.50 625.00 187.50 c
+623.62 187.50 622.50 186.38 622.50 185.00 c
+622.50 183.62 623.62 182.50 625.00 182.50 c
+626.38 182.50 627.50 183.62 627.50 185.00 c
+f
+0.80 0.00 0.00 rg
+627.50 195.00 m 627.50 196.38 626.38 197.50 625.00 197.50 c
+623.62 197.50 622.50 196.38 622.50 195.00 c
+622.50 193.62 623.62 192.50 625.00 192.50 c
+626.38 192.50 627.50 193.62 627.50 195.00 c
+f
+0.80 0.00 0.00 rg
+627.50 205.00 m 627.50 206.38 626.38 207.50 625.00 207.50 c
+623.62 207.50 622.50 206.38 622.50 205.00 c
+622.50 203.62 623.62 202.50 625.00 202.50 c
+626.38 202.50 627.50 203.62 627.50 205.00 c
+f
+0.80 0.00 0.00 rg
+827.50 240.00 m 827.50 241.38 826.38 242.50 825.00 242.50 c
+823.62 242.50 822.50 241.38 822.50 240.00 c
+822.50 238.62 823.62 237.50 825.00 237.50 c
+826.38 237.50 827.50 238.62 827.50 240.00 c
+f
+q
+102.00 0 0 20.00 706.00 30.50 cm
+/I0 Do
+Q
+endstream
+endobj
+1 0 obj
+<>
+endobj
+5 0 obj
+<<
+/Descent -209
+/CapHeight 727
+/StemV 0
+/Type /FontDescriptor
+/Flags 32
+/FontBBox [-559 -303 1446 1050]
+/FontName /Verdana
+/ItalicAngle 0
+/Ascent 1005
+>>
+endobj
+6 0 obj
+<>
+endobj
+7 0 obj
+<<
+/Type /Font
+/BaseFont /Times-Roman
+/Subtype /Type1
+/Encoding /WinAnsiEncoding
+/FirstChar 32
+/LastChar 255
+>>
+endobj
+8 0 obj
+<<
+/Descent -325
+/CapHeight 500
+/StemV 80
+/Type /FontDescriptor
+/Flags 32
+/FontBBox [-665 -325 2000 1006]
+/FontName /Arial
+/ItalicAngle 0
+/Ascent 1006
+>>
+endobj
+9 0 obj
+<>
+endobj
+10 0 obj
+<<
+/Type /XObject
+/Subtype /Image
+/Width 520
+/Height 105
+/ColorSpace /DeviceRGB
+/BitsPerComponent 8
+/DecodeParms <>
+/SMask 11 0 R
+/Length 6251
+/Filter /FlateDecode
+>>
+stream
+xKqǣ{$V>:dm]uXbřZ>ـlhy-a$`!qMrN|G^_LUz8gfdf0~	SEQe"EQaAQEۢ&\gn_@QJk#o(sdAO%1GŘf!Eykn!׍&쯯),rlUӳR(p\DUnGo),m(.@Qҳ\9TEYdd#ܼsu.)rUXWqvkG<7yW(Jk)˶yns
?'ȶ섡RtOQ:oNsk(kȯ`U7(ƬjY8n+jRݠ(ʚK@+U೸֙ٚ4Bd^T7(
+W㊞g)}]
4TIQc2+D+H	]noh6<+S`?w~N*c{!q~j`z&VPRe 4hy4AQ5!vQo_}?cEQր*Q	>yû(>?-hQV₞ɡ7ߦb~>HRǸ!<^}v~[TTaYUmAX$;9lφ"w9$3;yFQ<b~x-Q.}|	ӱu
+=9ԭ,jic>9R#bɪ|X_4eb'6~/vK!I"Nbejӎa?f򃔼b꧃1N3ȺwErU4;q5A6<0tϤ"/R>X$%AZ1c{od6^Sr)k-䔎zD/⇺l,bzs&gGL1HXɲUS]ҋRBrēwLwX|楔]2CI2w,!S*Nf&Ybd]]9U`~qQ]e3}GؑJRYQ඾r֌.͌uNO79ĝ(0'OvJg{d	٥wq9
d+,өQBV=>qU6FB%1ڷD؈_gVPW)By24qaJԌ$pk8q]t	q.!y-8́},wȷP%?C>4pTa߇8'ZNm19 m>QeMi$-:1̱mt^"\˩
84R!t	"!W'
}g>fd5hDuO3|m0I?x]r
fR4H48BKrYA1cL:{%o;gZpD1[{P|=`2(K^6gr_9Nو? 9,zYD4<*W)*/ws8y+<.E	嚞˹/u%NQA>9ϴch@93hvs~~I7k[|9)s&*þ[<#e
+ٜӓ(m!èkzP\`\̞hOa^Q]Bvl:P]}~Ո~zyfduۅjz'>)o\n`i%4Nk4GRI]/[|M=.-uV'W3A̯
_:<`I-jFLP]KcH&Ϙ~
W178YnW=!l 3n4)ZPeS	V5JR1=9ʾ0ɰAʛt1_d%ghl3%c
+nŔZ!g?+sDNoz8'"L5(BY;q5~'ųZ"慊o?JaЛc\%\MsɔiLq]PE['Uʎ_z7(C3rt$@EBe~9IoN@2?ԫ܍Q972ý.3@v^_}xZɶn\rR.ɳzz_MDI"t:V^Õ
+2/;؁y9
+©h	
+5vWP޻H#n
6*8W$1{ eB*rb4$!&?˵bJŰrtǀYGȥI	+~ǰװO
+NRT62Oy\Yw񑭖C{kKl/,4_}5iCpQ~"(Py.x1m0Mrڧ/ug]|&S_.=ę!V!t=)l7	t:ݤ'REgH&w9Zr]3ܺrc;hH9yK *g\mcbG`M@Qj<|)~Et
+8;N/$%4qI_KSp{pkx;Vi7u_N$C`F67ˣM-|2(/5)M*>]	&%[aǫG$	+B=etHLEy0{oD̔KMpT7FDA7?7nL
aRqQ[^1@(R@lk7	\pRa
[yC;eDx{RSZ\;GɞJh^ipNbH݈x;^XRƼ\gbMBTK-Pp;mpP.v:j;>*o
+ĽIG1|%z((y}D
+ć%O1$(wO _=Â7)^'*Bݲ#ULc6p7nPȫN+\^y;PjG82ܚG?!Vne2doŭϼ
+q1JO텘"ӻA n_5mE~qTiwu	8+i*x؆	Vu+^f|`(pp8ԔK}
+Zq9=	%&6紦uKe	}FX\q5&HOhӴTGw;G*@ӡ`n]0ss}ZWhNѰt\\ JYEpN1'{im[/`5a!np)'6}ڟBUǭ~嗩op:Vf/fVF[
!t'<}
S1ţc8}.4baΉ"+)!u(ofO|
qB	K?wCi!bѕ`+bjb1+/miɭg.X:rgUKK,3rڐf">[)˚+Djbna5[oL"pe|r(L^e̟gQYm5P]tݬĝ鐭xj1ՅDe\X[DV7TEF6/C@hbo1A`PoT:-݈}Vq!,J_j%D1#7;D$I(St(0'KK\SW}/;*b>)9]Jw+[CDi,ТˣRIᅖZe@l=Qbk tjAT`&wm3@] r28\ݸREMP+*zE❩{~|ԃMlwǶpZfo@3/M`cw$VLQ.%D6v]/p*4B)Ddb5~|
+J
+$ ۝zW3ovѨ}.R{L%o#4>݂q)C?.$a,nl&vJ,c>l*U4v)ɢ+iͿUM`娘lSƩP0؄Ha>"IUh+VQ7aJQr.6ҷǀf3@ŕ+z$e&SR#}[M"ڊPңey׀UA_bP1%2rJTd)J2&2MhIu*k
+F\UQkM}v(FjbS(I"kc
QO%)Ǧ(4L0gUwX^4'R_hXgmׯR9YiF|yLʕ.-%]g^\ϜTRSL1hAI6E	JZt)(PŠ(\@(rF-
+endstream
+endobj
+11 0 obj
+<<
+/Type /XObject
+/Subtype /Image
+/Width 520
+/Height 105
+/ColorSpace /DeviceGray
+/BitsPerComponent 8
+/DecodeParms <>
+/Length 6577
+/Filter /FlateDecode
+>>
+stream
+x]M?c=ȳJQj"*"xIJhGQ"o袌4(`B0_}{}3W3[g{{{k p*Bn"5-߯Ds0uN\'!WfBKl5Gt\1g}vD]EԄ\᾽3珧/Sډ&B!ދIɻ:fVW&>ľ~Jz\z
"`O>o/t[mGT]/u }EM4(uBW+I~
@SvXMq:~=|EP(BxF=hƕnWV/Ϗjpw,z~PvЬ]9ޕ,gv{NM[u)+++?DPɩ~^vJu?	)2tGqL
+Ï*ʋIȌ'#GR$$뵏[<ҡ䔊]]L'{KG9x)Lԉ
+0G6.ݡ92P-b|N2NU#4}K&jG
DԷc#By&ZdQ%ND#BݵԚv>)s\RPB>Wiк3k.
+p5|aΈ	ԈQQ5xO!XdAB"9g~2P<Z2s1v/tFtd/4Yx7wa鮋(:Bc7
E`~wa'S>{~
+0ㄠ)9ZmΔ>EE-#
+_xEbeAvc]DeCӏOs@j=oj+58`~W/x}љ^VzMQv7GU|CEbTK0CEңU%ێ^NO{K-q"+W`No`VW~md1JwKf.*(c*s=
+@qǭ/G|#A8tf:Vo	2g| <ʬ3i|r>V[%{gX7닅;%6&Z8(˰ؚ4+{>3'HgvM=MBt:LnukTl4ӉHDuNşvGXX~!,!5:M_cssQI>k#+0~ǎj'1\%P8ՠ*
+C"hoW!;yTv9.:64lLd6cm)gӕzʅwLd`:kj/f5O'3w2^$b^~ejS[z]*_~c#eI2[BӧC.⴨Q({z+WnݺB<=p2nCQ{w)[ב{\zZl[h.jyMt9]'pn7Fخ8Qp%| kN»3g2{ۜ#Հػ/r	@`Six΀?_wijpo"9{sS]r61B)z<;h!/
eT	nGAQis79Wbp k=i~ݵ)KX[n
+K3w̧$Apbz#D*J)2$)Wo&9VWeޱL\4@)69ֆՠH23`O@?zm~ GBQI
?=))Q,p1'nўXfb,̎ZY06GYw1=n32"0pyq0WH|,|WXm`R)^o;{ƌ&?Se
*:D?d6G+RQsi9+v
+EqwKmfP7JgNJ<ۀ3n-*:侟HvlwRs^0GjFy-NHu{T8xЪ̽
lmd`|8X~g.ty	H/m'4/8Nvor(dO9‹@1	bՄoA*e1Z쎕&L`- lR,l1xwݞ9t>D} p1~&)%,,0^j

4>N}춖1w6miW0crAI~aߊ?*{JIڢ	&ѿ+qTnLdz.~vKDFyо\y%[6mjrg">tCrRY)S@ڂ`ZرB8]`.f1J|!\2Yc:H6c/Qۜvo^aL%	Y"#qwǛqW_i,2Kz=◧IdV^\Y.G+ǻ6Dў4S)UrCD
+7#lg]|H'bRNUt~i'
n+
f&ݍPr'Rd:4".;mi氧/=j훌wJc	neWCn/PA5^b6I#6O%2A|ǧ㽎ԛ^MN@e.z	1ۓ	ý6(-+j	6g);8)tKTn?pgN{砱
+L!KI 4aҎ:
+r \4l'qK)o5UY#/h16ƁB2dV
yK
+6F8"ƍSSK_dRh4*_?LѫVS[+?޺IzE=B"ҔH׸3n+4d(b"`W~D}Gerp{)/B"hMh@#j~4-f`IkLtؐΒԣI4&ݿ֬_7KR$hyhJw3Tu0W|P&L/Y50_d&ןZB.B4eӋPB?%
+xYŰt#m#C`Xhѩp'zOCd~7p?DQaBC%'Qn'w 9{JCitWq,YO֦0:෤'҉FF߹:iy* ~%k/aXxQT4	EZ%{sIb&EjMQw4붘.qT7P@j@LJ7AhY<3P`P(&ia8=n5'>t^B͗Ym\_p2]KN/*5^1@&OVJ`
+hN%:OwoF|r`oX"-[ATw	m0y(
ǂہ[-68*6_558$Cne;h҉1c;[jȱ&gיDvoc߮I|RZ>BO>rBWȉT!9Oe	0+:BcbBP9i{6mB'e>Ty#xOZH{tT<`	3ރә@2l-&@WA>KZumx(11f8RuJWD+(f_ld9qmx1A65nd3RL~
+\rt4z*yKo܀LlYwO/;LoXeBG4WOpWCx) aR$OB?* ʹdՌ]lI yсK|3ܺ60s]n@{)R]#*1ѢX5K,-j."n^`W.8ufR;=wT"a´㓽l(.~iX\kf=Uaۏ*{IcEa1ʹTsxXQ,~Qė@梽R,@UR-J))J$}aͣ/)UXQ3[e¶5KFNe`fEїi	Ċ|r|+de&4\!qaj)2Z[E]oz#HFR?2.beгPd
+1^a4S)]Hph^@7
+WmNIY]|9ŋK]@toYɱJmͯ7^Al2+XWZ0^߱]^025_f4#/UGCJG[<~7ݲԎۭ5bչk}y1Ԭ0r2euZQM/6Z,`|S2ʺMJ(83w	[n
+}~s<_t)OΎT+(Zd%I5|`T뉃
+*͑tb"F42/*>>/ԣ@ ;$X2wׂiM=agCʽFWXl˨+RT^Or

Cwp]"H:u"##ҘRN/_z#)Kj=r+Vy<+Xn\G!!B_8V'w#Ev·s`EPR7«À!!ZAuTLmV#"oƐlogc"c';oHok/WE0]IXF]!wE0Xy-lx3ōaQ
+
tjMnUBQf<_E	X4f.]A0FIϋ*> ;\DPp#AT ?7
+endstream
+endobj
+2 0 obj
+<<
+/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
+/Font <<
+/F1 6 0 R
+/F2 7 0 R
+/F3 9 0 R
+>>
+/XObject <<
+/I0 10 0 R
+>>
+>>
+endobj
+12 0 obj
+<<
+/Producer (jsPDF 0.0.0)
+/CreationDate (D:20241217154849-00'00')
+>>
+endobj
+13 0 obj
+<<
+/Type /Catalog
+/Pages 1 0 R
+/OpenAction [3 0 R /FitH null]
+/PageLayout /OneColumn
+>>
+endobj
+xref
+0 14
+0000000000 65535 f 
+0000102899 00000 n 
+0000118853 00000 n 
+0000000015 00000 n 
+0000000125 00000 n 
+0000102956 00000 n 
+0000103126 00000 n 
+0000104180 00000 n 
+0000104307 00000 n 
+0000104476 00000 n 
+0000105520 00000 n 
+0000112030 00000 n 
+0000118988 00000 n 
+0000119074 00000 n 
+trailer
+<<
+/Size 14
+/Root 13 0 R
+/Info 12 0 R
+/ID [ <906A4C76C35816C42EB6FFD13B3B7D92> <906A4C76C35816C42EB6FFD13B3B7D92> ]
+>>
+startxref
+119178
+%%EOF
\ No newline at end of file
diff --git a/variants/diy/nrf52_promicro_diy_tcxo/rfswitch.h b/variants/diy/nrf52_promicro_diy_tcxo/rfswitch.h
new file mode 100644
index 000000000..2258c3135
--- /dev/null
+++ b/variants/diy/nrf52_promicro_diy_tcxo/rfswitch.h
@@ -0,0 +1,17 @@
+#include "RadioLib.h"
+
+// RF Switch Matrix SubG RFO_HP_LF / RFO_LP_LF / RFI_[NP]_LF0
+// DIO5 -> RFSW0_V1
+// DIO6 -> RFSW1_V2
+// DIO7 -> ANT_CTRL_ON + ESP_IO9/LR_GPS_ANT_DC_EN -> RFI_GPS (Bias-T GPS) (LR11x0 only)
+
+static const uint32_t rfswitch_dio_pins[] = {RADIOLIB_LR11X0_DIO5, RADIOLIB_LR11X0_DIO6, RADIOLIB_LR11X0_DIO7, RADIOLIB_NC,
+                                             RADIOLIB_NC};
+
+static const Module::RfSwitchMode_t rfswitch_table[] = {
+    // mode                  DIO5  DIO6  DIO7
+    {LR11x0::MODE_STBY, {LOW, LOW, LOW}},  {LR11x0::MODE_RX, {HIGH, LOW, LOW}},
+    {LR11x0::MODE_TX, {LOW, HIGH, LOW}},   {LR11x0::MODE_TX_HP, {LOW, HIGH, LOW}},
+    {LR11x0::MODE_TX_HF, {LOW, LOW, LOW}}, {LR11x0::MODE_GNSS, {LOW, LOW, HIGH}},
+    {LR11x0::MODE_WIFI, {LOW, LOW, LOW}},  END_OF_MODE_TABLE,
+};
\ No newline at end of file
diff --git a/variants/diy/nrf52_promicro_diy_tcxo/variant.h b/variants/diy/nrf52_promicro_diy_tcxo/variant.h
index 5c535ba1e..6ffb86cff 100644
--- a/variants/diy/nrf52_promicro_diy_tcxo/variant.h
+++ b/variants/diy/nrf52_promicro_diy_tcxo/variant.h
@@ -122,12 +122,13 @@ NRF52 PRO MICRO PIN ASSIGNMENT
 #define USE_SX1262
 #define USE_RF95
 #define USE_SX1268
+#define USE_LR1121
 
 // RF95 CONFIG
 
-#define LORA_DIO0 (0 + 29) // P0.10 IRQ
+#define LORA_DIO0 (0 + 29) // P0.29 BUSY
 #define LORA_DIO1 (0 + 10) // P0.10 IRQ
-#define LORA_RESET (0 + 9) // P0.09
+#define LORA_RESET (0 + 9) // P0.09 NRST
 
 // RX/TX for RFM95/SX127x
 #define RF95_RXEN (0 + 17)    // P0.17
@@ -143,6 +144,19 @@ NRF52 PRO MICRO PIN ASSIGNMENT
 #define SX126X_RXEN (0 + 17)     // P0.17
 #define SX126X_TXEN RADIOLIB_NC  // Assuming that DIO2 is connected to TXEN pin. If not, TXEN must be connected.
 
+// LR1121
+#ifdef USE_LR1121
+#define LR1121_IRQ_PIN (0 + 10)      // P0.10 IRQ
+#define LR1121_NRESET_PIN LORA_RESET // P0.09 NRST
+#define LR1121_BUSY_PIN (0 + 29)     // P0.29 BUSY
+#define LR1121_SPI_NSS_PIN LORA_CS   // P1.13
+#define LR1121_SPI_SCK_PIN LORA_SCK
+#define LR1121_SPI_MOSI_PIN LORA_MOSI
+#define LR1121_SPI_MISO_PIN LORA_MISO
+#define LR11X0_DIO3_TCXO_VOLTAGE 1.8
+#define LR11X0_DIO_AS_RF_SWITCH
+#endif
+
 // #define SX126X_MAX_POWER 8 set this if using a high-power board!
 
 /*
@@ -164,6 +178,7 @@ settings.
 | Seeed        | Wio-SX1262       | yes  | Int       | Sooooo cute!                          |
 | AI-Thinker   | RA-02            | No   | Int       | SX1278 **433mhz band only**           |
 | RF Solutions | RFM95            | No   | Int       | Untested                              |
+| Ebyte        | E80-900M2213S    | Yes  | Int       | LR1121 radio                          |
 
 */
 
@@ -179,4 +194,4 @@ extern float tcxoVoltage; // make this available everywhere
  *        Arduino objects - C++ only
  *----------------------------------------------------------------------------*/
 
-#endif
+#endif
\ No newline at end of file
diff --git a/variants/nibble_esp32/platformio.ini b/variants/nibble_esp32/platformio.ini
new file mode 100644
index 000000000..24d2ee2a5
--- /dev/null
+++ b/variants/nibble_esp32/platformio.ini
@@ -0,0 +1,6 @@
+[env:nibble-esp32]
+extends = esp32s3_base
+board = esp32-s3-zero
+board_level = extra
+build_flags = 
+  ${esp32_base.build_flags} -D PRIVATE_HW -I variants/nibble_esp32
\ No newline at end of file
diff --git a/variants/nibble_esp32/variant.h b/variants/nibble_esp32/variant.h
new file mode 100644
index 000000000..8ffbd9d59
--- /dev/null
+++ b/variants/nibble_esp32/variant.h
@@ -0,0 +1,18 @@
+#define I2C_SDA 11 // I2C pins for this board
+#define I2C_SCL 10
+
+#define LED_PIN 1 // If defined we will blink this LED
+
+#define BUTTON_PIN 0 // If defined, this will be used for user button presses
+#define BUTTON_NEED_PULLUP
+
+#define USE_RF95
+#define LORA_SCK 6
+#define LORA_MISO 7
+#define LORA_MOSI 8
+#define LORA_CS 9
+#define LORA_DIO0 5 // a No connect on the SX1262 module
+#define LORA_RESET 4
+
+#define LORA_DIO1 RADIOLIB_NC
+#define LORA_DIO2 RADIOLIB_NC
\ No newline at end of file
diff --git a/variants/nibble_rp2040/platformio.ini b/variants/nibble_rp2040/platformio.ini
new file mode 100644
index 000000000..ad987895f
--- /dev/null
+++ b/variants/nibble_rp2040/platformio.ini
@@ -0,0 +1,17 @@
+[env:nibble-rp2040]
+extends = rp2040_base
+board = rpipico
+board_level = extra
+upload_protocol = picotool
+
+# add our variants files to the include and src paths
+build_flags = ${rp2040_base.build_flags} 
+  -DPRIVATE_HW
+  -Ivariants/nibble_rp2040
+  -DDEBUG_RP2040_PORT=Serial
+  -DHW_SPI1_DEVICE
+  -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m0plus"
+lib_deps =
+  ${rp2040_base.lib_deps}
+debug_build_flags = ${rp2040_base.build_flags}, -g
+debug_tool = cmsis-dap ; for e.g. Picotool
\ No newline at end of file
diff --git a/variants/nibble_rp2040/variant.h b/variants/nibble_rp2040/variant.h
new file mode 100644
index 000000000..ac105b439
--- /dev/null
+++ b/variants/nibble_rp2040/variant.h
@@ -0,0 +1,18 @@
+#define ARDUINO_ARCH_AVR
+
+#define BUTTON_PIN -1 // Pin 17 used for antenna switching via DIO4
+
+#define LED_PIN 1
+
+#define HAS_CPU_SHUTDOWN 1
+
+#define USE_RFM95
+#define LORA_SCK 10
+#define LORA_MISO 12
+#define LORA_MOSI 11
+#define LORA_CS 13
+
+#define LORA_DIO0 14
+#define LORA_RESET 15
+#define LORA_DIO1 RADIOLIB_NC
+#define LORA_DIO2 RADIOLIB_NC
diff --git a/variants/nugget_s2_lora/platformio.ini b/variants/nugget_s2_lora/platformio.ini
new file mode 100644
index 000000000..2a7ff1013
--- /dev/null
+++ b/variants/nugget_s2_lora/platformio.ini
@@ -0,0 +1,6 @@
+[env:nugget-s2-lora]
+extends = esp32s2_base
+board = lolin_s2_mini
+board_level = extra
+build_flags = 
+  ${esp32s2_base.build_flags} -D PRIVATE_HW -I variants/nugget_s2_lora
\ No newline at end of file
diff --git a/variants/nugget_s2_lora/variant.h b/variants/nugget_s2_lora/variant.h
new file mode 100644
index 000000000..2d123d603
--- /dev/null
+++ b/variants/nugget_s2_lora/variant.h
@@ -0,0 +1,23 @@
+#define I2C_SDA 34 // I2C pins for this board
+#define I2C_SCL 36
+
+#define LED_PIN 15 // If defined we will blink this LED
+
+#define HAS_NEOPIXEL                         // Enable the use of neopixels
+#define NEOPIXEL_COUNT 3                     // How many neopixels are connected
+#define NEOPIXEL_DATA 12                     // gpio pin used to send data to the neopixels
+#define NEOPIXEL_TYPE (NEO_GRB + NEO_KHZ800) // type of neopixels in use
+
+#define BUTTON_PIN 0 // If defined, this will be used for user button presses
+#define BUTTON_NEED_PULLUP
+
+#define USE_RF95
+#define LORA_SCK 6
+#define LORA_MISO 8
+#define LORA_MOSI 10
+#define LORA_CS 13
+#define LORA_DIO0 16
+#define LORA_RESET 5
+
+#define LORA_DIO1 RADIOLIB_NC
+#define LORA_DIO2 RADIOLIB_NC
\ No newline at end of file
diff --git a/variants/nugget_s3_lora/platformio.ini b/variants/nugget_s3_lora/platformio.ini
new file mode 100644
index 000000000..729a3ef23
--- /dev/null
+++ b/variants/nugget_s3_lora/platformio.ini
@@ -0,0 +1,6 @@
+[env:nugget-s3-lora]
+extends = esp32s3_base
+board = lolin_s3_mini
+board_level = extra
+build_flags = 
+  ${esp32s3_base.build_flags} -D PRIVATE_HW -I variants/nugget_s3_lora
\ No newline at end of file
diff --git a/variants/nugget_s3_lora/variant.h b/variants/nugget_s3_lora/variant.h
new file mode 100644
index 000000000..488fe4e44
--- /dev/null
+++ b/variants/nugget_s3_lora/variant.h
@@ -0,0 +1,23 @@
+#define I2C_SDA 34 // I2C pins for this board
+#define I2C_SCL 38
+
+#define LED_PIN 15 // If defined we will blink this LED
+
+#define HAS_NEOPIXEL                         // Enable the use of neopixels
+#define NEOPIXEL_COUNT 3                     // How many neopixels are connected
+#define NEOPIXEL_DATA 10                     // gpio pin used to send data to the neopixels
+#define NEOPIXEL_TYPE (NEO_GRB + NEO_KHZ800) // type of neopixels in use
+
+#define BUTTON_PIN 0 // If defined, this will be used for user button presses
+#define BUTTON_NEED_PULLUP
+
+#define USE_RF95
+#define LORA_SCK 6
+#define LORA_MISO 7
+#define LORA_MOSI 8
+#define LORA_CS 9
+#define LORA_DIO0 16 // a No connect on the SX1262 module
+#define LORA_RESET 4
+
+#define LORA_DIO1 RADIOLIB_NC
+#define LORA_DIO2 RADIOLIB_NC
\ No newline at end of file
diff --git a/variants/portduino-buildroot/platformio.ini b/variants/portduino-buildroot/platformio.ini
index 3c8f21537..3fbd26910 100644
--- a/variants/portduino-buildroot/platformio.ini
+++ b/variants/portduino-buildroot/platformio.ini
@@ -1,11 +1,8 @@
 [env:buildroot]
 extends = portduino_base
-; The pkg-config commands below optionally add link flags.
-; the || : is just a "or run the null command" to avoid returning an error code
+; Optional libraries should be appended to `PLATFORMIO_BUILD_FLAGS`
+; environment variable in the buildroot environment.
 build_flags = ${portduino_base.build_flags} -O0 -I variants/portduino-buildroot
-  -std=c++17
-  !pkg-config --libs libulfius --silence-errors || :
-  !pkg-config --libs openssl --silence-errors || :
 board = buildroot
 lib_deps = ${portduino_base.lib_deps}
-build_src_filter = ${portduino_base.build_src_filter}
+build_src_filter = ${portduino_base.build_src_filter}
\ No newline at end of file
diff --git a/variants/rp2040-lora/variant.h b/variants/rp2040-lora/variant.h
index f1826605f..92b067457 100644
--- a/variants/rp2040-lora/variant.h
+++ b/variants/rp2040-lora/variant.h
@@ -15,7 +15,7 @@
 // rxd = 9
 
 #define EXT_NOTIFY_OUT 22
-#undef BUTTON_PIN // Pin 17 used for antenna switching via DIO4
+#define BUTTON_PIN -1 // Pin 17 used for antenna switching via DIO4
 
 #define LED_PIN PIN_LED
 
@@ -57,4 +57,4 @@
 #define SX126X_DIO2_AS_RF_SWITCH // Antenna switch CTRL
 #define SX126X_RXEN LORA_DIO4    // Antenna switch !CTRL via GPIO17
 // #define SX126X_DIO3_TCXO_VOLTAGE 1.8
-#endif
\ No newline at end of file
+#endif
diff --git a/variants/tlora_v2_1_16_tcxo/platformio.ini b/variants/tlora_v2_1_16_tcxo/platformio.ini
index e54c1a920..538fd81b0 100644
--- a/variants/tlora_v2_1_16_tcxo/platformio.ini
+++ b/variants/tlora_v2_1_16_tcxo/platformio.ini
@@ -1,5 +1,6 @@
 [env:tlora-v2-1-1_6-tcxo]
 extends = esp32_base
+board_level = extra
 board = ttgo-lora32-v21
 build_flags = 
   ${esp32_base.build_flags}
diff --git a/variants/tlora_v2_1_18/platformio.ini b/variants/tlora_v2_1_18/platformio.ini
index 36d6a3157..48a001ced 100644
--- a/variants/tlora_v2_1_18/platformio.ini
+++ b/variants/tlora_v2_1_18/platformio.ini
@@ -1,5 +1,6 @@
 [env:tlora-v2-1-1_8]
 extends = esp32_base
+board_level = extra
 board = ttgo-lora32-v21
 
 build_flags = 
diff --git a/variants/tlora_v3_3_0_tcxo/platformio.ini b/variants/tlora_v3_3_0_tcxo/platformio.ini
new file mode 100644
index 000000000..4066d64b0
--- /dev/null
+++ b/variants/tlora_v3_3_0_tcxo/platformio.ini
@@ -0,0 +1,11 @@
+[env:tlora-v3-3-0-tcxo]
+extends = esp32_base
+board = ttgo-lora32-v21
+board_level = extra
+build_flags = 
+  ${esp32_base.build_flags}
+  -D TLORA_V2_1_16
+  -I variants/tlora_v2_1_16
+  -D GPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely.
+  -D LORA_TCXO_GPIO=12
+  -D BUTTON_PIN=0
\ No newline at end of file