Merge branch 'dev' into rak-advert-hw-encryption
This commit is contained in:
71 files changed
+3465
-181
No files matched your search
@@ -25,5 +25,14 @@ runs:
|
||||
- name: Extract Version from Git Tag
|
||||
shell: bash
|
||||
run: |
|
||||
GIT_TAG_NAME="${GITHUB_REF#refs/tags/}"
|
||||
echo "GIT_TAG_VERSION=${GIT_TAG_NAME##*-}" >> $GITHUB_ENV
|
||||
if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
|
||||
# triggered by a tag push (e.g: refs/tags/companion-v1.2.3)
|
||||
GIT_TAG_NAME="${GITHUB_REF#refs/tags/}"
|
||||
VERSION_STRING="${GIT_TAG_NAME##*-}"
|
||||
else
|
||||
# triggered by a workflow dispatch (e.g: refs/heads/main)
|
||||
# strip "refs/heads/" prefix and replace any remaining "/" with "-" to protect file paths
|
||||
BRANCH_NAME="${GITHUB_REF#refs/heads/}"
|
||||
VERSION_STRING=$(echo "$BRANCH_NAME" | tr '/' '-')
|
||||
fi
|
||||
echo "GIT_TAG_VERSION=${VERSION_STRING}" >> $GITHUB_ENV
|
||||
@@ -10,33 +10,8 @@ on:
|
||||
- 'companion-*'
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- name: Clone Repo
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Build Environment
|
||||
uses: ./.github/actions/setup-build-environment
|
||||
|
||||
- name: Build Firmwares
|
||||
env:
|
||||
FIRMWARE_VERSION: ${{ env.GIT_TAG_VERSION }}
|
||||
run: /usr/bin/env bash build.sh build-companion-firmwares
|
||||
|
||||
- name: Upload Workflow Artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: companion-firmwares
|
||||
path: out
|
||||
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v3
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
with:
|
||||
name: Companion Firmware ${{ env.GIT_TAG_VERSION }}
|
||||
body: ""
|
||||
draft: true
|
||||
files: out/*
|
||||
build-companion-firmwares:
|
||||
uses: ./.github/workflows/firmware-builder.yml
|
||||
with:
|
||||
firmware_type: 'companion'
|
||||
release_title_prefix: 'Companion Firmware'
|
||||
@@ -10,33 +10,8 @@ on:
|
||||
- 'repeater-*'
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- name: Clone Repo
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Build Environment
|
||||
uses: ./.github/actions/setup-build-environment
|
||||
|
||||
- name: Build Firmwares
|
||||
env:
|
||||
FIRMWARE_VERSION: ${{ env.GIT_TAG_VERSION }}
|
||||
run: /usr/bin/env bash build.sh build-repeater-firmwares
|
||||
|
||||
- name: Upload Workflow Artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: repeater-firmwares
|
||||
path: out
|
||||
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v3
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
with:
|
||||
name: Repeater Firmware ${{ env.GIT_TAG_VERSION }}
|
||||
body: ""
|
||||
draft: true
|
||||
files: out/*
|
||||
build-repeater-firmwares:
|
||||
uses: ./.github/workflows/firmware-builder.yml
|
||||
with:
|
||||
firmware_type: 'repeater'
|
||||
release_title_prefix: 'Repeater Firmware'
|
||||
@@ -10,33 +10,8 @@ on:
|
||||
- 'room-server-*'
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- name: Clone Repo
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Build Environment
|
||||
uses: ./.github/actions/setup-build-environment
|
||||
|
||||
- name: Build Firmwares
|
||||
env:
|
||||
FIRMWARE_VERSION: ${{ env.GIT_TAG_VERSION }}
|
||||
run: /usr/bin/env bash build.sh build-room-server-firmwares
|
||||
|
||||
- name: Upload Workflow Artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: room-server-firmwares
|
||||
path: out
|
||||
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v3
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
with:
|
||||
name: Room Server Firmware ${{ env.GIT_TAG_VERSION }}
|
||||
body: ""
|
||||
draft: true
|
||||
files: out/*
|
||||
build-room-server-firmwares:
|
||||
uses: ./.github/workflows/firmware-builder.yml
|
||||
with:
|
||||
firmware_type: 'room-server'
|
||||
release_title_prefix: 'Room Server Firmware'
|
||||
@@ -0,0 +1,90 @@
|
||||
name: Firmware Builder
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
firmware_type:
|
||||
required: true
|
||||
type: string
|
||||
release_title_prefix:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
|
||||
generate-build-matrix:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
targets: ${{ steps.get-build-targets.outputs.targets }}
|
||||
steps:
|
||||
|
||||
- name: Clone Repo
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Build Environment
|
||||
uses: ./.github/actions/setup-build-environment
|
||||
|
||||
- name: Get Build Targets
|
||||
id: get-build-targets
|
||||
run: |
|
||||
# get list of firmwares to build
|
||||
TARGET_LIST=$(/usr/bin/env bash build.sh get-${{ inputs.firmware_type }}-firmwares-to-build)
|
||||
|
||||
# convert targets separated by new line into a json array string
|
||||
JSON_ARRAY=$(echo "$TARGET_LIST" | jq -R -s -c 'split("\n") | map(select(length > 0))')
|
||||
|
||||
# use json array as targets result
|
||||
echo "targets=$JSON_ARRAY" >> $GITHUB_OUTPUT
|
||||
|
||||
build:
|
||||
needs: generate-build-matrix
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true # don't fail entire build if one board fails to build
|
||||
strategy:
|
||||
matrix:
|
||||
target: ${{ fromJson(needs.generate-build-matrix.outputs.targets) }}
|
||||
fail-fast: false # don't cancel other builds if one board fails to build
|
||||
steps:
|
||||
|
||||
- name: Clone Repo
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Build Environment
|
||||
uses: ./.github/actions/setup-build-environment
|
||||
|
||||
- name: Build Firmware
|
||||
env:
|
||||
FIRMWARE_VERSION: ${{ env.GIT_TAG_VERSION }}
|
||||
run: /usr/bin/env bash build.sh build-firmware ${{ matrix.target }}
|
||||
|
||||
- name: Upload Workflow Artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: "${{ matrix.target }}"
|
||||
path: out
|
||||
|
||||
create-release:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
if: startsWith(github.ref, 'refs/tags/') # only create release for tagged builds
|
||||
steps:
|
||||
|
||||
- name: Clone Repo
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Build Environment
|
||||
uses: ./.github/actions/setup-build-environment
|
||||
|
||||
- name: Download All Artifacts
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
merge-multiple: true
|
||||
path: out
|
||||
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v3
|
||||
with:
|
||||
name: "${{ inputs.release_title_prefix }} ${{ env.GIT_TAG_VERSION }}"
|
||||
body: ""
|
||||
draft: true
|
||||
files: out/*
|
||||
@@ -31,8 +31,11 @@ jobs:
|
||||
- Heltec_v3_room_server
|
||||
# nRF52
|
||||
- RAK_4631_companion_radio_ble
|
||||
- RAK_4631_companion_radio_ethernet
|
||||
- RAK_4631_repeater
|
||||
- RAK_4631_repeater_ethernet
|
||||
- RAK_4631_room_server
|
||||
- RAK_4631_room_server_ethernet
|
||||
# RP2040
|
||||
- PicoW_repeater
|
||||
# STM32
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
name: 'Run Stale Bot'
|
||||
on:
|
||||
schedule:
|
||||
- cron: '30 1 * * *' # daily at 1:30am
|
||||
workflow_dispatch: {}
|
||||
|
||||
permissions:
|
||||
actions: write
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
close-issues:
|
||||
# only run on main repo, not forks
|
||||
if: github.repository == 'meshcore-dev/MeshCore'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Close Stale Issues
|
||||
uses: actions/stale@v10
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# auto close issues
|
||||
days-before-issue-stale: 60
|
||||
days-before-issue-close: 7
|
||||
exempt-issue-labels: "keep-open"
|
||||
stale-issue-label: "stale"
|
||||
stale-issue-message: "This issue is stale because it has been open for 60 days with no activity. Remove the stale label or add a comment if this issue is still relevant, otherwise this issue will automatically close in 7 days."
|
||||
close-issue-message: "This issue was closed because it has been inactive for 7 days since being marked as stale."
|
||||
# don't auto close prs
|
||||
days-before-pr-stale: -1
|
||||
days-before-pr-close: -1
|
||||
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
Security fixes are applied to the latest release only. We do not backport
|
||||
fixes to older versions.
|
||||
|
||||
| Version | Supported |
|
||||
|---------|-----------|
|
||||
| 1.15+ | ✅ |
|
||||
| <1.15 | ❌ |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
**Please do not report security vulnerabilities through public GitHub issues.**
|
||||
|
||||
Use GitHub's private vulnerability reporting instead:
|
||||
1. Go to the **Security** tab of this repository
|
||||
2. Click **Report a vulnerability**
|
||||
3. Fill in the details and submit
|
||||
|
||||
### What to include
|
||||
|
||||
A useful report tells us:
|
||||
- Which component or file is affected
|
||||
- What an attacker can do (impact) and under what conditions
|
||||
- A minimal reproduction case or proof-of-concept if you have one
|
||||
- Whether you believe it is remotely exploitable
|
||||
|
||||
You do not need a working exploit to report. An incomplete report is better
|
||||
than no report.
|
||||
|
||||
## What to expect
|
||||
|
||||
This is a volunteer-maintained open-source project. We will do our best to
|
||||
respond in a reasonable timeframe, but cannot commit to specific deadlines.
|
||||
|
||||
We ask that you give us a fair opportunity to investigate and address the
|
||||
issue before any public disclosure. If you have not heard back after
|
||||
**90 days**, feel free to follow up or proceed with disclosure at your
|
||||
discretion.
|
||||
|
||||
## Scope
|
||||
|
||||
In scope:
|
||||
- Remote code execution, memory corruption, or denial-of-service via crafted
|
||||
radio packets
|
||||
- Authentication or encryption bypasses
|
||||
- Vulnerabilities in the packet routing or path handling logic
|
||||
|
||||
Out of scope:
|
||||
- Physical access attacks (e.g., JTAG, UART extraction of keys)
|
||||
- Regulatory compliance (duty cycle, frequency restrictions)
|
||||
- Jamming or other physical-layer radio interference
|
||||
- Issues in third-party libraries (RadioLib, Crypto, etc.) — report those
|
||||
upstream
|
||||
- "Best practice" suggestions without a demonstrated attack path
|
||||
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "esp32s3_out.ld",
|
||||
"partitions": "default_16MB.csv",
|
||||
"memory_type": "qio_opi"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "80000000L",
|
||||
"flash_mode": "qio",
|
||||
"psram_type": "opi",
|
||||
"hwids": [["0x303A", "0x1001"]],
|
||||
"mcu": "esp32s3",
|
||||
"variant": "heltec_rc32"
|
||||
},
|
||||
"connectivity": ["wifi", "bluetooth", "lora"],
|
||||
"debug": {
|
||||
"default_tool": "esp-builtin",
|
||||
"onboard_tools": ["esp-builtin"],
|
||||
"openocd_target": "esp32s3.cfg"
|
||||
},
|
||||
"frameworks": ["arduino", "espidf"],
|
||||
"name": "Heltec RC32 (16 MB FLASH, 8 MB PSRAM)",
|
||||
"upload": {
|
||||
"flash_size": "16MB",
|
||||
"maximum_ram_size": 327680,
|
||||
"maximum_size": 16777216,
|
||||
"use_1200bps_touch": true,
|
||||
"wait_for_upload_port": true,
|
||||
"require_upload_port": true,
|
||||
"speed": 921600
|
||||
},
|
||||
"url": "https://heltec.org/",
|
||||
"vendor": "Heltec"
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "esp32s3_out.ld",
|
||||
"partitions": "default_16MB.csv",
|
||||
"memory_type": "qio_opi"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "80000000L",
|
||||
"flash_mode": "qio",
|
||||
"psram_type": "opi",
|
||||
"hwids": [["0x303A", "0x1001"]],
|
||||
"mcu": "esp32s3",
|
||||
"variant": "heltec_v4_r8"
|
||||
},
|
||||
"connectivity": ["wifi", "bluetooth", "lora"],
|
||||
"debug": {
|
||||
"default_tool": "esp-builtin",
|
||||
"onboard_tools": ["esp-builtin"],
|
||||
"openocd_target": "esp32s3.cfg"
|
||||
},
|
||||
"frameworks": ["arduino", "espidf"],
|
||||
"name": "heltec_wifi_lora_32 v4 r8 (16 MB FLASH, 8 MB PSRAM)",
|
||||
"upload": {
|
||||
"flash_size": "16MB",
|
||||
"maximum_ram_size": 327680,
|
||||
"maximum_size": 16777216,
|
||||
"use_1200bps_touch": true,
|
||||
"wait_for_upload_port": true,
|
||||
"require_upload_port": true,
|
||||
"speed": 921600
|
||||
},
|
||||
"url": "https://heltec.org/",
|
||||
"vendor": "heltec"
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# exit when any command fails
|
||||
set -e
|
||||
|
||||
global_usage() {
|
||||
cat - <<EOF
|
||||
Usage:
|
||||
@@ -93,7 +96,7 @@ get_pio_envs_ending_with_string() {
|
||||
# $1 should be the environment name
|
||||
get_platform_for_env() {
|
||||
local env_name=$1
|
||||
echo "$PIO_CONFIG_JSON" | python3 -c "
|
||||
printf '%s' "$PIO_CONFIG_JSON" | python3 -c "
|
||||
import sys, json, re
|
||||
data = json.load(sys.stdin)
|
||||
for section, options in data:
|
||||
@@ -275,4 +278,11 @@ elif [[ $1 == "build-repeater-firmwares" ]]; then
|
||||
build_repeater_firmwares
|
||||
elif [[ $1 == "build-room-server-firmwares" ]]; then
|
||||
build_room_server_firmwares
|
||||
elif [[ $1 == "get-companion-firmwares-to-build" ]]; then
|
||||
get_pio_envs_ending_with_string "_companion_radio_usb"
|
||||
get_pio_envs_ending_with_string "_companion_radio_ble"
|
||||
elif [[ $1 == "get-repeater-firmwares-to-build" ]]; then
|
||||
get_pio_envs_ending_with_string "_repeater"
|
||||
elif [[ $1 == "get-room-server-firmwares-to-build" ]]; then
|
||||
get_pio_envs_ending_with_string "_room_server"
|
||||
fi
|
||||
+48
-1
@@ -19,6 +19,7 @@ This document provides an overview of CLI commands that can be sent to MeshCore
|
||||
- [GPS](#gps-when-gps-support-is-compiled-in)
|
||||
- [Sensors](#sensors-when-sensor-support-is-compiled-in)
|
||||
- [Bridge](#bridge-when-bridge-support-is-compiled-in)
|
||||
- [Ethernet](#ethernet-when-ethernet-support-is-compiled-in)
|
||||
|
||||
---
|
||||
|
||||
@@ -28,12 +29,25 @@ This document provides an overview of CLI commands that can be sent to MeshCore
|
||||
**Usage:**
|
||||
- `reboot`
|
||||
|
||||
**Note:** No reply is sent.
|
||||
|
||||
---
|
||||
|
||||
### Power-off the node
|
||||
**Usage:**
|
||||
- `poweroff`, or
|
||||
- `shutdown`
|
||||
|
||||
**Note:** No reply is sent.
|
||||
|
||||
---
|
||||
|
||||
### Reset the clock and reboot
|
||||
**Usage:**
|
||||
- `clkreboot`
|
||||
|
||||
**Note:** No reply is sent.
|
||||
|
||||
---
|
||||
|
||||
### Sync the clock with the remote device
|
||||
@@ -660,10 +674,21 @@ This document provides an overview of CLI commands that can be sent to MeshCore
|
||||
**Parameters:**
|
||||
- `value`: Maximum flood hop count (0-64) for a packet without a scope (no region set)
|
||||
|
||||
**Default:** `0xFF` - indicates it hasn't been set, will track flood.max until it is.
|
||||
**Default:** `64` - (`0xFF` indicates it hasn't been set, will track flood.max until it is.)
|
||||
|
||||
**Note:** An alternative to `region denyf *`, setting `flood.max.unscoped` to a lower value such as `3` would allow for local unscoped messages to propagate, while preventing noisy neighbors from flooding a local region.
|
||||
|
||||
---
|
||||
|
||||
#### Limit the number of hops for an advert flood message
|
||||
**Usage:**
|
||||
- `get flood.max.advert`
|
||||
- `set flood.max.advert <value>`
|
||||
|
||||
**Parameters:**
|
||||
- `value`: Maximum flood hop count (0-64) for an advert packet
|
||||
|
||||
**Default:** `8`
|
||||
|
||||
---
|
||||
|
||||
@@ -1128,3 +1153,25 @@ region save
|
||||
**Note:** Returns an error on boards without power management support.
|
||||
|
||||
---
|
||||
|
||||
### Ethernet (when Ethernet support is compiled in)
|
||||
|
||||
Ethernet support is available on RAK4631 boards with a RAK13800 (W5100S) Ethernet module. Use the `_ethernet` firmware variants (e.g. `RAK_4631_repeater_ethernet`) to enable this feature.
|
||||
|
||||
---
|
||||
|
||||
#### View Ethernet connection status
|
||||
**Usage:**
|
||||
- `eth.status`
|
||||
|
||||
**Output:**
|
||||
- `ETH: <ip>:<port>` when connected (e.g. `ETH: 192.168.1.50:23`)
|
||||
- `ETH: not connected` when Ethernet is not active
|
||||
|
||||
**Notes:**
|
||||
- Available on repeater and room server firmware only. Companion radio ethernet firmware does not expose a CLI.
|
||||
- The Ethernet interface obtains an IP address via DHCP automatically on boot.
|
||||
- A TCP server listens on port 23 (default) for CLI connections.
|
||||
- Connect with any TCP client (e.g. `nc`, PuTTY) to access the same CLI available over serial.
|
||||
|
||||
---
|
||||
+27
@@ -83,6 +83,7 @@ A list of frequently-asked questions and answers for MeshCore
|
||||
- [7.5. Q: What is the format of a contact or channel QR code?](#75-q-what-is-the-format-of-a-contact-or-channel-qr-code)
|
||||
- [7.6. Q: How do I connect to the companion via Wi-Fi, e.g. using a Heltec V3?](#76-q-how-do-i-connect-to-the-companion-via-wi-fi-eg-using-a-heltec-v3)
|
||||
- [7.7. Q: I have a Station G2, or a Heltec V4, or an Ikoka Stick, or a radio with an EByte E22-900M30S or an EByte E22-900M33S module, what should their transmit power be set to?](#77-q-i-have-a-station-g2-or-a-heltec-v4-or-an-ikoka-stick-or-a-radio-with-an-ebyte-e22-900m30s-or-an-ebyte-e22-900m33s-module-what-should-their-transmit-power-be-set-to)
|
||||
- [7.8. Q: How do I use Ethernet with a RAK4631?](#78-q-how-do-i-use-ethernet-with-a-rak4631)
|
||||
|
||||
## 1. Introduction
|
||||
|
||||
@@ -820,3 +821,29 @@ For companion radios, you can set these radios' transmit power in the smartphone
|
||||
| **Ikoka Stick E22-900M33S** | 2W Model | 9 dBm | 2W | **DO NOT EXCEED** (Risk of burn out) [data sheet](https://www.cdebyte.com/pdf-down.aspx?id=4216) Refer to your local government's requirements |
|
||||
| **Heltec V4** | Standard Output | 10 dBm | 22 dBm (~0.15W) | |
|
||||
| | High Output | 22 dBm | 28 dBm (~0.5W to 0.6W) | |
|
||||
|
||||
---
|
||||
|
||||
### 7.8. Q: How do I use Ethernet with a RAK4631?
|
||||
**A:**
|
||||
MeshCore supports Ethernet on RAK4631 boards using the [RAK13800](https://docs.rakwireless.com/product-categories/wisblock/rak13800/datasheet/) WisBlock Ethernet module (based on the W5100S chip).
|
||||
|
||||
**Hardware required:**
|
||||
- RAK4631 WisBlock Core
|
||||
- RAK19007 or RAK19018 WisBlock Base Board (with an available IO slot)
|
||||
- RAK13800 WisBlock Ethernet module
|
||||
- Ethernet cable connected to a network with a DHCP server
|
||||
|
||||
**Firmware:**
|
||||
Flash one of the Ethernet-enabled firmware variants:
|
||||
- `RAK_4631_repeater_ethernet` - Repeater with Ethernet CLI access
|
||||
- `RAK_4631_room_server_ethernet` - Room server with Ethernet CLI access
|
||||
- `RAK_4631_companion_radio_ethernet` - Companion radio over Ethernet (replaces BLE)
|
||||
|
||||
**Connecting:**
|
||||
- The device obtains an IP address via DHCP automatically on boot.
|
||||
- For repeaters and room servers, connect to the device on TCP port 23 using any TCP client (e.g. `nc <ip> 23` or PuTTY in raw mode). This gives you the same CLI available over serial/USB.
|
||||
- For companion radio firmware, the Ethernet interface replaces BLE as the transport to companion apps. Connect on TCP port 5000 (same as the WiFi companion radio).
|
||||
- Use the `eth.status` CLI command to check connection status and see the assigned IP address.
|
||||
|
||||
---
|
||||
+3
-1
@@ -12,8 +12,10 @@ meshcore://channel/add?name=Public&secret=8b3387e9c5cdea6ac9e5edbaa115cd72
|
||||
|
||||
**Parameters**:
|
||||
|
||||
- `name`: Channel name (URL-encoded if needed)
|
||||
- `name`: Channel name (URL-encoded)
|
||||
- `secret`: 16-byte secret represented as 32 hex characters
|
||||
- `region_scope`: Region Scope (optional, URL-encoded if provided)
|
||||
- Supported by MeshCore App v1.47.0+
|
||||
|
||||
## Add Contact
|
||||
|
||||
|
||||
@@ -657,6 +657,11 @@ uint8_t MyMesh::onContactRequest(const ContactInfo &contact, uint32_t sender_tim
|
||||
// query other sensors -- target specific
|
||||
sensors.querySensors(permissions, telemetry);
|
||||
|
||||
float temperature = board.getMCUTemperature();
|
||||
if(!isnan(temperature)) { // Supported boards with built-in temperature sensor. ESP32-C3 may return NAN
|
||||
telemetry.addTemperature(TELEM_CHANNEL_SELF, temperature); // Built-in MCU Temperature
|
||||
}
|
||||
|
||||
memcpy(reply, &sender_timestamp,
|
||||
4); // reflect sender_timestamp back in response packet (kind of like a 'tag')
|
||||
|
||||
@@ -1640,6 +1645,11 @@ void MyMesh::handleCmdFrame(size_t len) {
|
||||
} else if (cmd_frame[0] == CMD_SEND_TELEMETRY_REQ && len == 4) { // 'self' telemetry request
|
||||
telemetry.reset();
|
||||
telemetry.addVoltage(TELEM_CHANNEL_SELF, (float)board.getBattMilliVolts() / 1000.0f);
|
||||
float temperature = board.getMCUTemperature();
|
||||
if(!isnan(temperature)) { // Supported boards with built-in temperature sensor. ESP32-C3 may return NAN
|
||||
telemetry.addTemperature(TELEM_CHANNEL_SELF, temperature); // Built-in MCU Temperature
|
||||
}
|
||||
|
||||
// query other sensors -- target specific
|
||||
sensors.querySensors(0xFF, telemetry);
|
||||
|
||||
|
||||
@@ -18,13 +18,13 @@ static uint32_t _atoi(const char* sp) {
|
||||
#include <CustomLFS_QSPIFlash.h>
|
||||
DataStore store(InternalFS, QSPIFlash, rtc_clock);
|
||||
#else
|
||||
#if defined(EXTRAFS)
|
||||
#include <CustomLFS.h>
|
||||
CustomLFS ExtraFS(0xD4000, 0x19000, 128);
|
||||
DataStore store(InternalFS, ExtraFS, rtc_clock);
|
||||
#else
|
||||
DataStore store(InternalFS, rtc_clock);
|
||||
#endif
|
||||
#if defined(EXTRAFS)
|
||||
#include <CustomLFS.h>
|
||||
CustomLFS ExtraFS(0xD4000, 0x19000, 128);
|
||||
DataStore store(InternalFS, ExtraFS, rtc_clock);
|
||||
#else
|
||||
DataStore store(InternalFS, rtc_clock);
|
||||
#endif
|
||||
#endif
|
||||
#elif defined(RP2040_PLATFORM)
|
||||
#include <LittleFS.h>
|
||||
@@ -74,6 +74,9 @@ static uint32_t _atoi(const char* sp) {
|
||||
#ifdef BLE_PIN_CODE
|
||||
#include <helpers/nrf52/SerialBLEInterface.h>
|
||||
SerialBLEInterface serial_interface;
|
||||
#elif defined(ETHERNET_ENABLED)
|
||||
#include <helpers/nrf52/SerialEthernetInterface.h>
|
||||
SerialEthernetInterface serial_interface;
|
||||
#else
|
||||
#include <helpers/ArduinoSerialInterface.h>
|
||||
ArduinoSerialInterface serial_interface;
|
||||
@@ -113,7 +116,6 @@ void halt() {
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
board.begin();
|
||||
|
||||
#ifdef HAS_EXTERNAL_WATCHDOG
|
||||
@@ -162,10 +164,23 @@ void setup() {
|
||||
|
||||
#ifdef BLE_PIN_CODE
|
||||
serial_interface.begin(BLE_NAME_PREFIX, the_mesh.getNodePrefs()->node_name, the_mesh.getBLEPin());
|
||||
the_mesh.startInterface(serial_interface);
|
||||
#elif defined(ETHERNET_ENABLED)
|
||||
Serial.print("Waiting for serial to connect...\n");
|
||||
unsigned long timeout = millis();
|
||||
while (!Serial) {
|
||||
if ((millis() - timeout) < 5000) { delay(100); } else { break; }
|
||||
}
|
||||
Serial.println("Initializing Ethernet adapter...");
|
||||
if (serial_interface.begin()) {
|
||||
the_mesh.startInterface(serial_interface);
|
||||
} else {
|
||||
Serial.println("ETH: Init failed, continuing without Ethernet (mesh only)");
|
||||
}
|
||||
#else
|
||||
serial_interface.begin(Serial);
|
||||
#endif
|
||||
the_mesh.startInterface(serial_interface);
|
||||
#endif
|
||||
#elif defined(RP2040_PLATFORM)
|
||||
LittleFS.begin();
|
||||
store.begin();
|
||||
@@ -256,7 +271,11 @@ void loop() {
|
||||
#ifdef HAS_EXTERNAL_WATCHDOG
|
||||
external_watchdog.loop();
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef ETHERNET_ENABLED
|
||||
serial_interface.loop();
|
||||
#endif
|
||||
|
||||
if (!the_mesh.hasPendingWork()) {
|
||||
#if defined(NRF52_PLATFORM)
|
||||
board.sleep(0); // nrf ignores seconds param, sleeps whenever possible
|
||||
|
||||
@@ -697,9 +697,6 @@ void UITask::shutdown(bool restart){
|
||||
if (restart) {
|
||||
_board->reboot();
|
||||
} else {
|
||||
// still necessary until all boards are refactored to use poweroff
|
||||
_display->turnOff();
|
||||
radio_driver.powerOff();
|
||||
// Power off board including radio, display, GPS and components
|
||||
_board->powerOff();
|
||||
}
|
||||
@@ -750,6 +747,16 @@ void UITask::loop() {
|
||||
c = handleTripleClick(KEY_SELECT);
|
||||
}
|
||||
#endif
|
||||
#if defined(UI_HAS_ROTARY_INPUT)
|
||||
RotaryInputEvent rotaryEv = rotary_input.poll();
|
||||
if (c == 0 && _display != NULL && _display->isOn()) {
|
||||
if (rotaryEv == RotaryInputEvent::Next) {
|
||||
c = KEY_NEXT;
|
||||
} else if (rotaryEv == RotaryInputEvent::Prev) {
|
||||
c = KEY_PREV;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if defined(PIN_USER_BTN_ANA)
|
||||
if (abs(millis() - _analogue_pin_read_millis) > 10) {
|
||||
int ev = analog_btn.check();
|
||||
|
||||
@@ -307,8 +307,6 @@ void UITask::shutdown(bool restart){
|
||||
if (restart) {
|
||||
_board->reboot();
|
||||
} else {
|
||||
_display->turnOff();
|
||||
radio_driver.powerOff();
|
||||
// Power off board including radio, display, GPS and components
|
||||
_board->powerOff();
|
||||
}
|
||||
|
||||
@@ -566,8 +566,6 @@ void UITask::shutdown(bool restart){
|
||||
if (restart) {
|
||||
_board->reboot();
|
||||
} else {
|
||||
_display->turnOff();
|
||||
radio_driver.powerOff();
|
||||
// Power off board including radio, display, GPS and components
|
||||
_board->powerOff();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "UITask.h"
|
||||
#include "target.h"
|
||||
#include <Arduino.h>
|
||||
#include <helpers/CommonCLI.h>
|
||||
|
||||
@@ -9,6 +10,8 @@
|
||||
#define AUTO_OFF_MILLIS 20000 // 20 seconds
|
||||
#define BOOT_SCREEN_MILLIS 4000 // 4 seconds
|
||||
|
||||
#define POWEROFF_DELAY 3000
|
||||
|
||||
// 'meshcore', 128x13px
|
||||
static const uint8_t meshcore_logo [] PROGMEM = {
|
||||
0x3c, 0x01, 0xe3, 0xff, 0xc7, 0xff, 0x8f, 0x03, 0x87, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe,
|
||||
@@ -29,9 +32,14 @@ static const uint8_t meshcore_logo [] PROGMEM = {
|
||||
void UITask::begin(NodePrefs* node_prefs, const char* build_date, const char* firmware_version) {
|
||||
_prevBtnState = HIGH;
|
||||
_auto_off = millis() + AUTO_OFF_MILLIS;
|
||||
_started_at = millis();
|
||||
_node_prefs = node_prefs;
|
||||
_display->turnOn();
|
||||
|
||||
#if defined(PIN_USER_BTN) && defined(DISPLAY_CLASS)
|
||||
user_btn.begin();
|
||||
#endif
|
||||
|
||||
// strip off dash and commit hash by changing dash to null terminator
|
||||
// e.g: v1.2.3-abcdef -> v1.2.3
|
||||
char *version = strdup(firmware_version);
|
||||
@@ -47,7 +55,7 @@ void UITask::begin(NodePrefs* node_prefs, const char* build_date, const char* fi
|
||||
|
||||
void UITask::renderCurrScreen() {
|
||||
char tmp[80];
|
||||
if (millis() < BOOT_SCREEN_MILLIS) { // boot screen
|
||||
if (millis() < _started_at + BOOT_SCREEN_MILLIS) { // boot screen
|
||||
// meshcore logo
|
||||
_display->setColor(DisplayDriver::BLUE);
|
||||
int logoWidth = 128;
|
||||
@@ -57,24 +65,34 @@ void UITask::renderCurrScreen() {
|
||||
const char* website = "https://meshcore.io";
|
||||
_display->setColor(DisplayDriver::LIGHT);
|
||||
_display->setTextSize(1);
|
||||
uint16_t websiteWidth = _display->getTextWidth(website);
|
||||
_display->setCursor((_display->width() - websiteWidth) / 2, 22);
|
||||
_display->print(website);
|
||||
_display->drawTextCentered(_display->width() / 2, 22, website);
|
||||
|
||||
// version info
|
||||
_display->setColor(DisplayDriver::LIGHT);
|
||||
_display->setTextSize(1);
|
||||
uint16_t versionWidth = _display->getTextWidth(_version_info);
|
||||
_display->setCursor((_display->width() - versionWidth) / 2, 35);
|
||||
_display->print(_version_info);
|
||||
_display->drawTextCentered(_display->width() / 2, 35, _version_info);
|
||||
|
||||
// node type
|
||||
const char* node_type = "< Repeater >";
|
||||
uint16_t typeWidth = _display->getTextWidth(node_type);
|
||||
_display->setCursor((_display->width() - typeWidth) / 2, 48);
|
||||
_display->print(node_type);
|
||||
} else { // home screen
|
||||
// node name
|
||||
_display->drawTextCentered(_display->width() / 2, 48, node_type);
|
||||
} else if (_powering_off_at > 0) {
|
||||
// meshcore logo
|
||||
_display->setColor(DisplayDriver::BLUE);
|
||||
int logoWidth = 128;
|
||||
_display->drawXbm((_display->width() - logoWidth) / 2, 3, meshcore_logo, logoWidth, 13);
|
||||
|
||||
// meshcore website
|
||||
const char* website = "https://meshcore.io";
|
||||
_display->setColor(DisplayDriver::LIGHT);
|
||||
_display->setTextSize(1);
|
||||
_display->drawTextCentered(_display->width()/ 2, 22, website);
|
||||
|
||||
// Powering off
|
||||
const char* poweroff_string = "Turning OFF";
|
||||
uint16_t poffWidth = _display->getTextWidth(poweroff_string);
|
||||
_display->setCursor((_display->width() - poffWidth) / 2, 48);
|
||||
_display->drawTextCentered(_display->width()/2, 48, poweroff_string);
|
||||
} else {
|
||||
_display->setCursor(0, 0);
|
||||
_display->setTextSize(1);
|
||||
_display->setColor(DisplayDriver::GREEN);
|
||||
@@ -94,21 +112,19 @@ void UITask::renderCurrScreen() {
|
||||
}
|
||||
|
||||
void UITask::loop() {
|
||||
#ifdef PIN_USER_BTN
|
||||
if (millis() >= _next_read) {
|
||||
int btnState = digitalRead(PIN_USER_BTN);
|
||||
if (btnState != _prevBtnState) {
|
||||
if (btnState == USER_BTN_PRESSED) { // pressed?
|
||||
if (_display->isOn()) {
|
||||
// TODO: any action ?
|
||||
} else {
|
||||
_display->turnOn();
|
||||
}
|
||||
_auto_off = millis() + AUTO_OFF_MILLIS; // extend auto-off timer
|
||||
}
|
||||
_prevBtnState = btnState;
|
||||
#if defined(PIN_USER_BTN) && defined(DISPLAY_CLASS)
|
||||
int ev = user_btn.check();
|
||||
if (ev == BUTTON_EVENT_CLICK) {
|
||||
if (_display->isOn()) {
|
||||
// TODO: any action ?
|
||||
} else {
|
||||
_display->turnOn();
|
||||
}
|
||||
_next_read = millis() + 200; // 5 reads per second
|
||||
_auto_off = millis() + AUTO_OFF_MILLIS; // extend auto-off timer
|
||||
} else if (ev == BUTTON_EVENT_LONG_PRESS) {
|
||||
_display->turnOn();
|
||||
Serial.println("Powering Off");
|
||||
_powering_off_at = millis() + POWEROFF_DELAY;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -124,4 +140,13 @@ void UITask::loop() {
|
||||
_display->turnOff();
|
||||
}
|
||||
}
|
||||
|
||||
if (_powering_off_at > 0) { // power off timer armed
|
||||
#ifdef LED_PIN
|
||||
digitalWrite(LED_PIN, LED_STATE_ON); // switch on the led until poweroff
|
||||
#endif
|
||||
if (millis() > _powering_off_at) {
|
||||
_board->powerOff(); // should not return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,15 +4,18 @@
|
||||
#include <helpers/CommonCLI.h>
|
||||
|
||||
class UITask {
|
||||
mesh::MainBoard* _board;
|
||||
DisplayDriver* _display;
|
||||
unsigned long _next_read, _next_refresh, _auto_off;
|
||||
int _prevBtnState;
|
||||
NodePrefs* _node_prefs;
|
||||
char _version_info[32];
|
||||
unsigned long _powering_off_at = 0;
|
||||
unsigned long _started_at = 0;
|
||||
|
||||
void renderCurrScreen();
|
||||
public:
|
||||
UITask(DisplayDriver& display) : _display(&display) { _next_read = _next_refresh = 0; }
|
||||
UITask(mesh::MainBoard& board, DisplayDriver& display) : _board(&board), _display(&display) { _next_read = _next_refresh = 0; }
|
||||
void begin(NodePrefs* node_prefs, const char* build_date, const char* firmware_version);
|
||||
|
||||
void loop();
|
||||
|
||||
@@ -5,7 +5,12 @@
|
||||
|
||||
#ifdef DISPLAY_CLASS
|
||||
#include "UITask.h"
|
||||
static UITask ui_task(display);
|
||||
static UITask ui_task(board, display);
|
||||
#endif
|
||||
|
||||
#ifdef ETHERNET_ENABLED
|
||||
#define ETHERNET_CLI_BANNER "MeshCore Repeater CLI"
|
||||
#include <helpers/nrf52/EthernetCLI.h>
|
||||
#endif
|
||||
|
||||
StdRNG fast_rng;
|
||||
@@ -18,6 +23,9 @@ void halt() {
|
||||
}
|
||||
|
||||
static char command[160];
|
||||
#ifdef ETHERNET_ENABLED
|
||||
static char ethernet_command[160];
|
||||
#endif
|
||||
|
||||
// For power saving
|
||||
unsigned long POWERSAVING_FIRSTSLEEP_SECS = 120; // The first sleep (if enabled) from boot
|
||||
@@ -90,6 +98,9 @@ void setup() {
|
||||
mesh::Utils::printHex(Serial, the_mesh.self_id.pub_key, PUB_KEY_SIZE); Serial.println();
|
||||
|
||||
command[0] = 0;
|
||||
#ifdef ETHERNET_ENABLED
|
||||
ethernet_command[0] = 0;
|
||||
#endif
|
||||
|
||||
sensors.begin();
|
||||
|
||||
@@ -99,6 +110,10 @@ void setup() {
|
||||
ui_task.begin(the_mesh.getNodePrefs(), FIRMWARE_BUILD_DATE, FIRMWARE_VERSION);
|
||||
#endif
|
||||
|
||||
#ifdef ETHERNET_ENABLED
|
||||
ethernet_start_task();
|
||||
#endif
|
||||
|
||||
// send out initial zero hop Advertisement to the mesh
|
||||
#if ENABLE_ADVERT_ON_BOOT == 1
|
||||
the_mesh.sendSelfAdvertisement(16000, false);
|
||||
@@ -108,6 +123,7 @@ void setup() {
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Handle Serial CLI
|
||||
int len = strlen(command);
|
||||
while (Serial.available() && len < sizeof(command)-1) {
|
||||
char c = Serial.read();
|
||||
@@ -126,7 +142,14 @@ void loop() {
|
||||
Serial.print('\n');
|
||||
command[len - 1] = 0; // replace newline with C string null terminator
|
||||
char reply[160];
|
||||
reply[0] = 0;
|
||||
#ifdef ETHERNET_ENABLED
|
||||
if (!ethernet_handle_command(command, reply)) {
|
||||
the_mesh.handleCommand(0, command, reply);
|
||||
}
|
||||
#else
|
||||
the_mesh.handleCommand(0, command, reply); // NOTE: there is no sender_timestamp via serial!
|
||||
#endif
|
||||
if (reply[0]) {
|
||||
Serial.print(" -> "); Serial.println(reply);
|
||||
}
|
||||
@@ -134,7 +157,20 @@ void loop() {
|
||||
command[0] = 0; // reset command buffer
|
||||
}
|
||||
|
||||
#if defined(PIN_USER_BTN) && defined(_SEEED_SENSECAP_SOLAR_H_)
|
||||
#ifdef ETHERNET_ENABLED
|
||||
ethernet_loop_maintain();
|
||||
if (ethernet_read_line(ethernet_command, sizeof(ethernet_command))) {
|
||||
char reply[160];
|
||||
reply[0] = 0;
|
||||
if (!ethernet_handle_command(ethernet_command, reply)) {
|
||||
the_mesh.handleCommand(0, ethernet_command, reply);
|
||||
}
|
||||
ethernet_send_reply(reply);
|
||||
ethernet_command[0] = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(PIN_USER_BTN) && defined(_SEEED_SENSECAP_SOLAR_H_) && !defined(DISPLAY_CLASS)
|
||||
// Hold the user button to power off the SenseCAP Solar repeater.
|
||||
int btnState = digitalRead(PIN_USER_BTN);
|
||||
if (btnState == LOW) {
|
||||
@@ -160,9 +196,6 @@ void loop() {
|
||||
external_watchdog.loop();
|
||||
#endif
|
||||
if (the_mesh.getNodePrefs()->powersaving_enabled && !the_mesh.hasPendingWork()) {
|
||||
#ifdef HAS_EXTERNAL_WATCHDOG
|
||||
external_watchdog.feed();
|
||||
#endif
|
||||
#if defined(NRF52_PLATFORM)
|
||||
board.sleep(0); // nrf ignores seconds param, sleeps whenever possible
|
||||
#else
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
|
||||
#include "MyMesh.h"
|
||||
|
||||
#ifdef ETHERNET_ENABLED
|
||||
#define ETHERNET_CLI_BANNER "MeshCore Room Server CLI"
|
||||
#include <helpers/nrf52/EthernetCLI.h>
|
||||
#endif
|
||||
|
||||
#ifdef DISPLAY_CLASS
|
||||
#include "UITask.h"
|
||||
static UITask ui_task(display);
|
||||
@@ -17,6 +22,9 @@ void halt() {
|
||||
}
|
||||
|
||||
static char command[MAX_POST_TEXT_LEN+1];
|
||||
#ifdef ETHERNET_ENABLED
|
||||
static char ethernet_command[MAX_POST_TEXT_LEN+1];
|
||||
#endif
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
@@ -71,6 +79,9 @@ void setup() {
|
||||
mesh::Utils::printHex(Serial, the_mesh.self_id.pub_key, PUB_KEY_SIZE); Serial.println();
|
||||
|
||||
command[0] = 0;
|
||||
#ifdef ETHERNET_ENABLED
|
||||
ethernet_command[0] = 0;
|
||||
#endif
|
||||
|
||||
sensors.begin();
|
||||
|
||||
@@ -80,6 +91,10 @@ void setup() {
|
||||
ui_task.begin(the_mesh.getNodePrefs(), FIRMWARE_BUILD_DATE, FIRMWARE_VERSION);
|
||||
#endif
|
||||
|
||||
#ifdef ETHERNET_ENABLED
|
||||
ethernet_start_task();
|
||||
#endif
|
||||
|
||||
// send out initial zero hop Advertisement to the mesh
|
||||
#if ENABLE_ADVERT_ON_BOOT == 1
|
||||
the_mesh.sendSelfAdvertisement(16000, false);
|
||||
@@ -105,7 +120,14 @@ void loop() {
|
||||
if (len > 0 && command[len - 1] == '\r') { // received complete line
|
||||
command[len - 1] = 0; // replace newline with C string null terminator
|
||||
char reply[160];
|
||||
reply[0] = 0;
|
||||
#ifdef ETHERNET_ENABLED
|
||||
if (!ethernet_handle_command(command, reply)) {
|
||||
the_mesh.handleCommand(0, command, reply);
|
||||
}
|
||||
#else
|
||||
the_mesh.handleCommand(0, command, reply); // NOTE: there is no sender_timestamp via serial!
|
||||
#endif
|
||||
if (reply[0]) {
|
||||
Serial.print(" -> "); Serial.println(reply);
|
||||
}
|
||||
@@ -113,6 +135,19 @@ void loop() {
|
||||
command[0] = 0; // reset command buffer
|
||||
}
|
||||
|
||||
#ifdef ETHERNET_ENABLED
|
||||
ethernet_loop_maintain();
|
||||
if (ethernet_read_line(ethernet_command, sizeof(ethernet_command))) {
|
||||
char reply[160];
|
||||
reply[0] = 0;
|
||||
if (!ethernet_handle_command(ethernet_command, reply)) {
|
||||
the_mesh.handleCommand(0, ethernet_command, reply);
|
||||
}
|
||||
ethernet_send_reply(reply);
|
||||
ethernet_command[0] = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
the_mesh.loop();
|
||||
sensors.loop();
|
||||
#ifdef DISPLAY_CLASS
|
||||
|
||||
+52
-11
@@ -280,16 +280,29 @@ void NRF52Board::sleep(uint32_t secs) {
|
||||
|
||||
// Temperature from NRF52 MCU
|
||||
float NRF52Board::getMCUTemperature() {
|
||||
NRF_TEMP->TASKS_START = 1; // Start temperature measurement
|
||||
|
||||
long startTime = millis();
|
||||
while (NRF_TEMP->EVENTS_DATARDY == 0) { // Wait for completion. Should complete in 50us
|
||||
if(millis() - startTime > 5) { // To wait 5ms just in case
|
||||
NRF_TEMP->TASKS_STOP = 1;
|
||||
uint8_t sd_enabled = 0;
|
||||
sd_softdevice_is_enabled(&sd_enabled);
|
||||
if (sd_enabled) {
|
||||
uint32_t err_code;
|
||||
int32_t temp;
|
||||
err_code = sd_temp_get(&temp);
|
||||
if (err_code == NRF_SUCCESS) {
|
||||
return (float)temp * 0.25f;
|
||||
} else {
|
||||
return NAN;
|
||||
}
|
||||
} else {
|
||||
NRF_TEMP->TASKS_START = 1; // Start temperature measurement
|
||||
|
||||
long startTime = millis();
|
||||
while (NRF_TEMP->EVENTS_DATARDY == 0) { // Wait for completion. Should complete in 50us
|
||||
if(millis() - startTime > 5) { // To wait 5ms just in case
|
||||
NRF_TEMP->TASKS_STOP = 1;
|
||||
return NAN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
NRF_TEMP->EVENTS_DATARDY = 0; // Clear event flag
|
||||
|
||||
int32_t temp = NRF_TEMP->TEMP; // In 0.25 *C units
|
||||
@@ -298,17 +311,41 @@ float NRF52Board::getMCUTemperature() {
|
||||
return temp * 0.25f; // Convert to *C
|
||||
}
|
||||
|
||||
void NRF52Board::powerOff() {
|
||||
void NRF52Board::shutdownPeripherals() {
|
||||
// Power off the display if any
|
||||
#ifdef DISPLAY_CLASS
|
||||
display.turnOff();
|
||||
if (display.isOn()) {
|
||||
display.turnOff();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Prep LoRa radio for power down
|
||||
#ifdef P_LORA_RESET
|
||||
digitalWrite(P_LORA_RESET, HIGH); // preload OUT latch so pinMode can't glitch NRESET low
|
||||
pinMode(P_LORA_RESET, OUTPUT);
|
||||
digitalWrite(P_LORA_RESET, LOW); // deliberate hardware reset (datasheet: >=100us)
|
||||
delayMicroseconds(200);
|
||||
digitalWrite(P_LORA_RESET, HIGH);
|
||||
#endif
|
||||
#if defined(P_LORA_SCLK) && defined(P_LORA_MISO) && defined(P_LORA_MOSI)
|
||||
SPI.setPins(P_LORA_MISO, P_LORA_SCLK, P_LORA_MOSI);
|
||||
SPI.begin(); // SPI may not be started on some shutdown paths, need it to shut down radio
|
||||
#endif
|
||||
#ifdef P_LORA_BUSY
|
||||
pinMode(P_LORA_BUSY, INPUT);
|
||||
uint32_t started_at = millis();
|
||||
while (digitalRead(P_LORA_BUSY) && millis() - started_at < 10) {} //wait for radio to be ready
|
||||
#endif
|
||||
#ifdef P_LORA_NSS
|
||||
pinMode(P_LORA_NSS, OUTPUT);
|
||||
digitalWrite(P_LORA_NSS, HIGH);
|
||||
#endif
|
||||
// Power off LoRa
|
||||
radio_driver.powerOff();
|
||||
|
||||
// Keep LoRa inactive during deepsleep
|
||||
digitalWrite(P_LORA_NSS, HIGH);
|
||||
#ifdef P_LORA_NSS
|
||||
digitalWrite(P_LORA_NSS, HIGH);
|
||||
#endif
|
||||
|
||||
// Power off GPS if any
|
||||
if(sensors.getLocationProvider() != NULL) {
|
||||
@@ -318,6 +355,10 @@ void NRF52Board::powerOff() {
|
||||
// Flush serial buffers
|
||||
Serial.flush();
|
||||
delay(100);
|
||||
}
|
||||
|
||||
void NRF52Board::powerOff() {
|
||||
shutdownPeripherals();
|
||||
|
||||
// Enter SYSTEMOFF
|
||||
uint8_t sd_enabled = 0;
|
||||
|
||||
@@ -50,6 +50,7 @@ public:
|
||||
virtual uint8_t getStartupReason() const override { return startup_reason; }
|
||||
virtual float getMCUTemperature() override;
|
||||
virtual void reboot() override { NVIC_SystemReset(); }
|
||||
virtual void shutdownPeripherals();
|
||||
virtual void powerOff() override;
|
||||
virtual bool getBootloaderVersion(char* version, size_t max_len) override;
|
||||
virtual bool startOTAUpdate(const char *id, char reply[]) override;
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef ETHERNET_ENABLED
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <SPI.h>
|
||||
#include <RAK13800_W5100S.h>
|
||||
#include <helpers/nrf52/EthernetMac.h>
|
||||
|
||||
#define PIN_SPI1_MISO (29)
|
||||
#define PIN_SPI1_MOSI (30)
|
||||
#define PIN_SPI1_SCK (3)
|
||||
|
||||
static SPIClass ETHERNET_SPI_PORT(NRF_SPIM1, PIN_SPI1_MISO, PIN_SPI1_SCK, PIN_SPI1_MOSI);
|
||||
|
||||
#define PIN_ETHERNET_POWER_EN WB_IO2
|
||||
#define PIN_ETHERNET_RESET 21
|
||||
#define PIN_ETHERNET_SS 26
|
||||
|
||||
#ifndef ETHERNET_TCP_PORT
|
||||
#define ETHERNET_TCP_PORT 23 // telnet port for CLI access
|
||||
#endif
|
||||
|
||||
#ifndef ETHERNET_CLI_BANNER
|
||||
#define ETHERNET_CLI_BANNER "MeshCore CLI"
|
||||
#endif
|
||||
|
||||
#define ETHERNET_RETRY_INTERVAL_MS 30000
|
||||
|
||||
static EthernetServer ethernet_server(ETHERNET_TCP_PORT);
|
||||
static EthernetClient ethernet_client;
|
||||
static volatile bool ethernet_running = false;
|
||||
|
||||
// FreeRTOS task: handles hw init, DHCP, and retries in the background
|
||||
static void ethernet_task(void* param) {
|
||||
(void)param;
|
||||
|
||||
Serial.println("ETH: Initializing hardware");
|
||||
// WB_IO2 (power enable) is already driven HIGH by early constructor
|
||||
// in RAK4631Board.cpp to support POE boot.
|
||||
// Skip hardware reset — the W5100S comes out of power-on reset cleanly,
|
||||
// and toggling reset kills the PHY link which breaks POE power.
|
||||
pinMode(PIN_ETHERNET_RESET, OUTPUT);
|
||||
digitalWrite(PIN_ETHERNET_RESET, HIGH);
|
||||
|
||||
ETHERNET_SPI_PORT.begin();
|
||||
Ethernet.init(ETHERNET_SPI_PORT, PIN_ETHERNET_SS);
|
||||
|
||||
uint8_t mac[6];
|
||||
generateEthernetMac(mac);
|
||||
Serial.printf("ETH: MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
|
||||
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
|
||||
// Retry loop: keep trying until we get an IP
|
||||
while (!ethernet_running) {
|
||||
Serial.println("ETH: Attempting DHCP...");
|
||||
if (Ethernet.begin(mac, 10000, 2000) == 0) {
|
||||
if (Ethernet.hardwareStatus() == EthernetNoHardware) {
|
||||
Serial.println("ETH: Hardware not found, giving up");
|
||||
vTaskDelete(NULL);
|
||||
return;
|
||||
}
|
||||
if (Ethernet.linkStatus() == LinkOFF) {
|
||||
Serial.println("ETH: Cable not connected, will retry");
|
||||
} else {
|
||||
Serial.println("ETH: DHCP failed, will retry");
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(ETHERNET_RETRY_INTERVAL_MS));
|
||||
continue;
|
||||
}
|
||||
|
||||
IPAddress ip = Ethernet.localIP();
|
||||
Serial.printf("ETH: IP: %u.%u.%u.%u\n", ip[0], ip[1], ip[2], ip[3]);
|
||||
Serial.printf("ETH: Listening on TCP port %d\n", ETHERNET_TCP_PORT);
|
||||
ethernet_server.begin();
|
||||
ethernet_running = true;
|
||||
}
|
||||
|
||||
// DHCP succeeded, task is done
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void ethernet_start_task() {
|
||||
xTaskCreate(ethernet_task, "eth_init", 1024, NULL, 1, NULL);
|
||||
}
|
||||
|
||||
// Format ethernet status into reply buffer. Returns true if command was handled.
|
||||
static bool ethernet_handle_command(const char* command, char* reply) {
|
||||
if (strcmp(command, "eth.status") == 0) {
|
||||
if (!ethernet_running) {
|
||||
strcpy(reply, "ETH: not connected");
|
||||
} else {
|
||||
IPAddress ip = Ethernet.localIP();
|
||||
sprintf(reply, "ETH: %u.%u.%u.%u:%d", ip[0], ip[1], ip[2], ip[3], ETHERNET_TCP_PORT);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for new TCP client connections, replacing any existing connection.
|
||||
// Use accept() (not available()) so we only see newly-accepted sockets;
|
||||
// available() also returns existing connected sockets that have data, which
|
||||
// would force us to disambiguate every inbound packet from a real new client.
|
||||
static void ethernet_check_client() {
|
||||
auto newClient = ethernet_server.accept();
|
||||
if (newClient) {
|
||||
if (ethernet_client) ethernet_client.stop();
|
||||
ethernet_client = newClient;
|
||||
IPAddress ip = ethernet_client.remoteIP();
|
||||
Serial.printf("ETH: Client connected from %u.%u.%u.%u\n", ip[0], ip[1], ip[2], ip[3]);
|
||||
ethernet_client.println(ETHERNET_CLI_BANNER);
|
||||
}
|
||||
}
|
||||
|
||||
// Call from loop() to maintain DHCP and check for new clients
|
||||
static void ethernet_loop_maintain() {
|
||||
if (ethernet_running) {
|
||||
ethernet_check_client();
|
||||
Ethernet.maintain();
|
||||
}
|
||||
}
|
||||
|
||||
// Read a line from the Ethernet client into the command buffer.
|
||||
// Returns true when a complete line is ready to process (command is null-terminated).
|
||||
// The caller should process the command and then reset ethernet_command[0] = 0.
|
||||
static bool ethernet_read_line(char* ethernet_command, size_t buf_size) {
|
||||
if (!ethernet_running || !ethernet_client || !ethernet_client.connected()) return false;
|
||||
|
||||
int elen = strlen(ethernet_command);
|
||||
while (ethernet_client.available() && elen < (int)buf_size - 1) {
|
||||
char c = ethernet_client.read();
|
||||
if (c == '\n' && elen == 0) continue; // ignore leading LF (from CR+LF)
|
||||
if (c == '\r' || c == '\n') { ethernet_command[elen++] = '\r'; break; }
|
||||
ethernet_command[elen++] = c;
|
||||
ethernet_command[elen] = 0;
|
||||
}
|
||||
if (elen == (int)buf_size - 1) {
|
||||
ethernet_command[buf_size - 1] = '\r';
|
||||
}
|
||||
|
||||
if (elen > 0 && ethernet_command[elen - 1] == '\r') {
|
||||
ethernet_command[elen - 1] = 0;
|
||||
ethernet_client.println();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Send a reply to the Ethernet client
|
||||
static void ethernet_send_reply(const char* reply) {
|
||||
if (reply[0]) {
|
||||
ethernet_client.print(" -> "); ethernet_client.println(reply);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ETHERNET_ENABLED
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
static inline void generateEthernetMac(uint8_t mac[6]) {
|
||||
uint32_t device_id = NRF_FICR->DEVICEID[0];
|
||||
mac[0] = 0x02;
|
||||
mac[1] = 0x92;
|
||||
mac[2] = 0x1F;
|
||||
mac[3] = (device_id >> 16) & 0xFF;
|
||||
mac[4] = (device_id >> 8) & 0xFF;
|
||||
mac[5] = device_id & 0xFF;
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
#ifdef ETHERNET_ENABLED
|
||||
|
||||
#include "SerialEthernetInterface.h"
|
||||
#include "EthernetMac.h"
|
||||
#include <SPI.h>
|
||||
#include <EthernetUdp.h>
|
||||
|
||||
#define PIN_SPI1_MISO (29) // (0 + 29)
|
||||
#define PIN_SPI1_MOSI (30) // (0 + 30)
|
||||
#define PIN_SPI1_SCK (3) // (0 + 3)
|
||||
|
||||
SPIClass ETHERNET_SPI_PORT(NRF_SPIM1, PIN_SPI1_MISO, PIN_SPI1_SCK, PIN_SPI1_MOSI);
|
||||
|
||||
#define PIN_ETHERNET_POWER_EN WB_IO2 // output, high to enable
|
||||
#define PIN_ETHERNET_RESET 21
|
||||
#define PIN_ETHERNET_SS 26
|
||||
|
||||
#define RECV_STATE_IDLE 0
|
||||
#define RECV_STATE_HDR_FOUND 1
|
||||
#define RECV_STATE_LEN1_FOUND 2
|
||||
#define RECV_STATE_LEN2_FOUND 3
|
||||
|
||||
bool SerialEthernetInterface::begin() {
|
||||
|
||||
ETHERNET_DEBUG_PRINTLN("Ethernet initializing");
|
||||
|
||||
// WB_IO2 (power enable) is already driven HIGH by early constructor
|
||||
// in RAK4631Board.cpp to support POE boot.
|
||||
// Skip hardware reset — the W5100S comes out of power-on reset cleanly,
|
||||
// and toggling reset kills the PHY link which breaks POE power.
|
||||
#ifdef PIN_ETHERNET_RESET
|
||||
pinMode(PIN_ETHERNET_RESET, OUTPUT);
|
||||
digitalWrite(PIN_ETHERNET_RESET, HIGH);
|
||||
#endif
|
||||
|
||||
uint8_t mac[6];
|
||||
generateEthernetMac(mac);
|
||||
ETHERNET_DEBUG_PRINTLN(
|
||||
"Ethernet MAC: %02X:%02X:%02X:%02X:%02X:%02X",
|
||||
mac[0],
|
||||
mac[1],
|
||||
mac[2],
|
||||
mac[3],
|
||||
mac[4],
|
||||
mac[5]);
|
||||
ETHERNET_DEBUG_PRINTLN("Init");
|
||||
ETHERNET_SPI_PORT.begin();
|
||||
Ethernet.init(ETHERNET_SPI_PORT, PIN_ETHERNET_SS);
|
||||
|
||||
// Use static IP if build flags are defined, otherwise DHCP
|
||||
#if defined(ETHERNET_STATIC_IP) && defined(ETHERNET_STATIC_GATEWAY) && defined(ETHERNET_STATIC_SUBNET) && defined(ETHERNET_STATIC_DNS)
|
||||
IPAddress ip(ETHERNET_STATIC_IP);
|
||||
IPAddress gateway(ETHERNET_STATIC_GATEWAY);
|
||||
IPAddress subnet(ETHERNET_STATIC_SUBNET);
|
||||
IPAddress dns(ETHERNET_STATIC_DNS);
|
||||
Ethernet.begin(mac, ip, dns, gateway, subnet);
|
||||
#else
|
||||
ETHERNET_DEBUG_PRINTLN("Begin");
|
||||
if (Ethernet.begin(mac) == 0) {
|
||||
ETHERNET_DEBUG_PRINTLN("Begin failed.");
|
||||
|
||||
// DHCP failed -- let's figure out why
|
||||
if (Ethernet.hardwareStatus() == EthernetNoHardware) // Check for Ethernet hardware present.
|
||||
{
|
||||
ETHERNET_DEBUG_PRINTLN("Ethernet hardware not found.");
|
||||
return false;
|
||||
}
|
||||
if (Ethernet.linkStatus() == LinkOFF) // No physical connection
|
||||
{
|
||||
ETHERNET_DEBUG_PRINTLN("Ethernet cable not connected.");
|
||||
return false;
|
||||
}
|
||||
ETHERNET_DEBUG_PRINTLN("Ethernet: DHCP failed for unknown reason.");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
ETHERNET_DEBUG_PRINTLN("Ethernet begin complete");
|
||||
ETHERNET_DEBUG_PRINT_IP("IP", Ethernet.localIP());
|
||||
ETHERNET_DEBUG_PRINT_IP("Subnet", Ethernet.subnetMask());
|
||||
ETHERNET_DEBUG_PRINT_IP("Gateway", Ethernet.gatewayIP());
|
||||
|
||||
server.begin(); // start listening for clients
|
||||
ETHERNET_DEBUG_PRINTLN("Ethernet: listening on TCP port: %d", ETHERNET_TCP_PORT);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SerialEthernetInterface::enable() {
|
||||
if (_isEnabled) return;
|
||||
|
||||
_isEnabled = true;
|
||||
clearBuffers();
|
||||
}
|
||||
|
||||
void SerialEthernetInterface::disable() {
|
||||
_isEnabled = false;
|
||||
}
|
||||
|
||||
size_t SerialEthernetInterface::writeFrame(const uint8_t src[], size_t len) {
|
||||
if (len > MAX_FRAME_SIZE) {
|
||||
ETHERNET_DEBUG_PRINTLN("writeFrame(), frame too big, len=%d\n", len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (deviceConnected && len > 0) {
|
||||
if (send_queue_len >= FRAME_QUEUE_SIZE) {
|
||||
ETHERNET_DEBUG_PRINTLN("writeFrame(), send_queue is full!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
send_queue[send_queue_len].len = len; // add to send queue
|
||||
memcpy(send_queue[send_queue_len].buf, src, len);
|
||||
send_queue_len++;
|
||||
|
||||
return len;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool SerialEthernetInterface::isWriteBusy() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t SerialEthernetInterface::checkRecvFrame(uint8_t dest[]) {
|
||||
// Use accept() (not available()) so we only see newly-accepted sockets.
|
||||
// available() also returns existing connected sockets that have data,
|
||||
// which would cause us to treat each inbound packet as a "new client"
|
||||
// and stop() the underlying socket — disconnecting the companion.
|
||||
auto newClient = server.accept();
|
||||
if (newClient) {
|
||||
IPAddress new_ip = newClient.remoteIP();
|
||||
uint16_t new_port = newClient.remotePort();
|
||||
ETHERNET_DEBUG_PRINTLN(
|
||||
"New client accepted %u.%u.%u.%u:%u",
|
||||
new_ip[0],
|
||||
new_ip[1],
|
||||
new_ip[2],
|
||||
new_ip[3],
|
||||
new_port);
|
||||
|
||||
deviceConnected = false;
|
||||
if (client) {
|
||||
ETHERNET_DEBUG_PRINTLN("Closing previous client");
|
||||
client.stop();
|
||||
}
|
||||
_state = RECV_STATE_IDLE;
|
||||
_frame_len = 0;
|
||||
_rx_len = 0;
|
||||
client = newClient;
|
||||
ETHERNET_DEBUG_PRINTLN("Switched to new client");
|
||||
}
|
||||
|
||||
if (client.connected()) {
|
||||
if (!deviceConnected) {
|
||||
ETHERNET_DEBUG_PRINTLN(
|
||||
"Got connection %u.%u.%u.%u:%u",
|
||||
client.remoteIP()[0],
|
||||
client.remoteIP()[1],
|
||||
client.remoteIP()[2],
|
||||
client.remoteIP()[3],
|
||||
client.remotePort());
|
||||
deviceConnected = true;
|
||||
}
|
||||
} else {
|
||||
if (deviceConnected) {
|
||||
deviceConnected = false;
|
||||
ETHERNET_DEBUG_PRINTLN("Disconnected");
|
||||
}
|
||||
}
|
||||
|
||||
if (deviceConnected) {
|
||||
if (send_queue_len > 0) { // first, check send queue
|
||||
|
||||
_last_write = millis();
|
||||
int len = send_queue[0].len;
|
||||
|
||||
#if ETHERNET_RAW_LINE
|
||||
ETHERNET_DEBUG_PRINTLN("TX line len=%d", len);
|
||||
client.write(send_queue[0].buf, len);
|
||||
client.write("\r\n", 2);
|
||||
#else
|
||||
uint8_t pkt[3+len]; // use same header as serial interface so client can delimit frames
|
||||
pkt[0] = '>';
|
||||
pkt[1] = (len & 0xFF); // LSB
|
||||
pkt[2] = (len >> 8); // MSB
|
||||
memcpy(&pkt[3], send_queue[0].buf, send_queue[0].len);
|
||||
ETHERNET_DEBUG_PRINTLN("Sending frame len=%d", len);
|
||||
#if ETHERNET_DEBUG_LOGGING && ARDUINO
|
||||
ETHERNET_DEBUG_PRINTLN("TX frame len=%d", len);
|
||||
#endif
|
||||
client.write(pkt, 3 + len);
|
||||
#endif
|
||||
send_queue_len--;
|
||||
for (int i = 0; i < send_queue_len; i++) { // delete top item from queue
|
||||
send_queue[i] = send_queue[i + 1];
|
||||
}
|
||||
} else {
|
||||
while (client.available()) {
|
||||
int c = client.read();
|
||||
if (c < 0) break;
|
||||
|
||||
#if ETHERNET_RAW_LINE
|
||||
if (c == '\r' || c == '\n') {
|
||||
if (_rx_len == 0) {
|
||||
continue;
|
||||
}
|
||||
uint16_t out_len = _rx_len;
|
||||
if (out_len > MAX_FRAME_SIZE) {
|
||||
out_len = MAX_FRAME_SIZE;
|
||||
}
|
||||
memcpy(dest, _rx_buf, out_len);
|
||||
_rx_len = 0;
|
||||
return out_len;
|
||||
}
|
||||
if (_rx_len < MAX_FRAME_SIZE) {
|
||||
_rx_buf[_rx_len] = (uint8_t)c;
|
||||
_rx_len++;
|
||||
}
|
||||
#else
|
||||
switch (_state) {
|
||||
case RECV_STATE_IDLE:
|
||||
if (c == '<') {
|
||||
_state = RECV_STATE_HDR_FOUND;
|
||||
}
|
||||
break;
|
||||
case RECV_STATE_HDR_FOUND:
|
||||
_frame_len = (uint8_t)c;
|
||||
_state = RECV_STATE_LEN1_FOUND;
|
||||
break;
|
||||
case RECV_STATE_LEN1_FOUND:
|
||||
_frame_len |= ((uint16_t)c) << 8;
|
||||
_rx_len = 0;
|
||||
_state = _frame_len > 0 ? RECV_STATE_LEN2_FOUND : RECV_STATE_IDLE;
|
||||
break;
|
||||
default:
|
||||
if (_rx_len < MAX_FRAME_SIZE) {
|
||||
_rx_buf[_rx_len] = (uint8_t)c;
|
||||
}
|
||||
_rx_len++;
|
||||
if (_rx_len >= _frame_len) {
|
||||
if (_frame_len > MAX_FRAME_SIZE) {
|
||||
_frame_len = MAX_FRAME_SIZE;
|
||||
}
|
||||
#if ETHERNET_DEBUG_LOGGING && ARDUINO
|
||||
ETHERNET_DEBUG_PRINTLN("RX frame len=%d", _frame_len);
|
||||
#endif
|
||||
memcpy(dest, _rx_buf, _frame_len);
|
||||
_state = RECV_STATE_IDLE;
|
||||
return _frame_len;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool SerialEthernetInterface::isConnected() const {
|
||||
return deviceConnected;
|
||||
}
|
||||
|
||||
void SerialEthernetInterface::loop() {
|
||||
Ethernet.maintain();
|
||||
}
|
||||
|
||||
#endif // ETHERNET_ENABLED
|
||||
@@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef ETHERNET_ENABLED
|
||||
|
||||
#include "helpers/BaseSerialInterface.h"
|
||||
#include <SPI.h>
|
||||
#include <RAK13800_W5100S.h>
|
||||
|
||||
#ifndef ETHERNET_TCP_PORT
|
||||
#define ETHERNET_TCP_PORT 5000
|
||||
#endif
|
||||
// define ETHERNET_RAW_LINE=1 to use raw line-based CLI instead of framed packets
|
||||
|
||||
class SerialEthernetInterface : public BaseSerialInterface {
|
||||
bool deviceConnected;
|
||||
bool _isEnabled;
|
||||
unsigned long _last_write;
|
||||
uint8_t _state;
|
||||
uint16_t _frame_len;
|
||||
uint16_t _rx_len;
|
||||
uint8_t _rx_buf[MAX_FRAME_SIZE];
|
||||
|
||||
EthernetServer server;
|
||||
EthernetClient client;
|
||||
|
||||
struct Frame {
|
||||
uint8_t len;
|
||||
uint8_t buf[MAX_FRAME_SIZE];
|
||||
};
|
||||
|
||||
#define FRAME_QUEUE_SIZE 4
|
||||
int send_queue_len;
|
||||
Frame send_queue[FRAME_QUEUE_SIZE];
|
||||
|
||||
void clearBuffers() {
|
||||
send_queue_len = 0;
|
||||
_state = 0;
|
||||
_frame_len = 0;
|
||||
_rx_len = 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
public:
|
||||
SerialEthernetInterface() : server(EthernetServer(ETHERNET_TCP_PORT)) {
|
||||
deviceConnected = false;
|
||||
_isEnabled = false;
|
||||
_last_write = 0;
|
||||
send_queue_len = 0;
|
||||
_state = 0;
|
||||
_frame_len = 0;
|
||||
_rx_len = 0;
|
||||
}
|
||||
bool begin();
|
||||
|
||||
// BaseSerialInterface methods
|
||||
void enable() override;
|
||||
void disable() override;
|
||||
bool isEnabled() const override { return _isEnabled; }
|
||||
|
||||
bool isConnected() const override;
|
||||
bool isWriteBusy() const override;
|
||||
|
||||
size_t writeFrame(const uint8_t src[], size_t len) override;
|
||||
size_t checkRecvFrame(uint8_t dest[]) override;
|
||||
|
||||
void loop();
|
||||
};
|
||||
|
||||
|
||||
#if ETHERNET_DEBUG_LOGGING && ARDUINO
|
||||
#include <Arduino.h>
|
||||
#define ETHERNET_DEBUG_PRINT(F, ...) Serial.printf("ETH: " F, ##__VA_ARGS__)
|
||||
#define ETHERNET_DEBUG_PRINTLN(F, ...) Serial.printf("ETH: " F "\n", ##__VA_ARGS__)
|
||||
#define ETHERNET_DEBUG_PRINT_IP(name, ip) Serial.printf(name ": %u.%u.%u.%u" "\n", ip[0], ip[1], ip[2], ip[3])
|
||||
#else
|
||||
#define ETHERNET_DEBUG_PRINT(...) {}
|
||||
#define ETHERNET_DEBUG_PRINTLN(...) {}
|
||||
#define ETHERNET_DEBUG_PRINT_IP(...) {}
|
||||
#endif
|
||||
|
||||
#endif // ETHERNET_ENABLED
|
||||
@@ -4,6 +4,10 @@
|
||||
#include "MeshCore.h"
|
||||
|
||||
class CustomLR1110 : public LR1110 {
|
||||
uint32_t _preambleMillis = 66;
|
||||
uint32_t _maxPayloadMillis = 3934;
|
||||
uint32_t _activityAt = 0;
|
||||
bool _headerSeen = false;
|
||||
bool _rx_boosted = false;
|
||||
|
||||
public:
|
||||
@@ -32,9 +36,49 @@ class CustomLR1110 : public LR1110 {
|
||||
bool getRxBoostedGainMode() const { return _rx_boosted; }
|
||||
|
||||
bool isReceiving() {
|
||||
uint16_t irq = getIrqStatus();
|
||||
bool detected = ((irq & RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID) || (irq & RADIOLIB_LR11X0_IRQ_PREAMBLE_DETECTED));
|
||||
return detected;
|
||||
uint32_t irq = getIrqStatus();
|
||||
bool preamble = irq & RADIOLIB_LR11X0_IRQ_PREAMBLE_DETECTED; // bit 4
|
||||
bool header = irq & RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID; // bit 5
|
||||
bool hdrErr = irq & RADIOLIB_LR11X0_IRQ_HEADER_ERR; // bit 6
|
||||
uint32_t now = millis();
|
||||
if (hdrErr) {
|
||||
clearIrqState(RADIOLIB_LR11X0_IRQ_PREAMBLE_DETECTED | RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID | RADIOLIB_LR11X0_IRQ_HEADER_ERR);
|
||||
_activityAt = 0;
|
||||
_headerSeen = false;
|
||||
return false;
|
||||
}
|
||||
if (header) {
|
||||
if (!_headerSeen) { _headerSeen = true; _activityAt = now; };
|
||||
if (now - _activityAt > _maxPayloadMillis) {
|
||||
MESH_DEBUG_PRINTLN("Clearing header IRQ after %ums", _maxPayloadMillis);
|
||||
clearIrqState(RADIOLIB_LR11X0_IRQ_PREAMBLE_DETECTED | RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID | RADIOLIB_LR11X0_IRQ_HEADER_ERR);
|
||||
_activityAt = 0; _headerSeen = false;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (preamble) {
|
||||
if (_activityAt == 0) _activityAt = now;
|
||||
if (now - _activityAt > _preambleMillis) {
|
||||
clearIrqState(RADIOLIB_LR11X0_IRQ_PREAMBLE_DETECTED);
|
||||
_activityAt = 0;
|
||||
MESH_DEBUG_PRINTLN("Clearing preamble IRQ after %ums", _preambleMillis);
|
||||
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
_activityAt = 0; _headerSeen = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
void setPreambleMillis(uint32_t preambleMillis) {
|
||||
_preambleMillis = preambleMillis;
|
||||
MESH_DEBUG_PRINTLN("Set _preambleMillis=%u", _preambleMillis);
|
||||
}
|
||||
void setMaxPayloadMillis(uint32_t payloadMillis) {
|
||||
_maxPayloadMillis = payloadMillis;
|
||||
MESH_DEBUG_PRINTLN("Set _maxPayloadMillis=%u", _maxPayloadMillis);
|
||||
}
|
||||
|
||||
uint8_t getSpreadingFactor() const { return spreadingFactor; }
|
||||
|
||||
@@ -14,6 +14,9 @@ public:
|
||||
((CustomLR1110 *)_radio)->setBandwidth(bw);
|
||||
((CustomLR1110 *)_radio)->setCodingRate(cr);
|
||||
updatePreamble(sf);
|
||||
PacketMillis pm = calcMaxPacketMillis(sf, bw, cr, preambleLengthForSF(sf));
|
||||
((CustomLR1110 *)_radio)->setPreambleMillis(pm.preambleMillis);
|
||||
((CustomLR1110 *)_radio)->setMaxPayloadMillis(pm.payloadMillis);
|
||||
}
|
||||
|
||||
void doResetAGC() override { lr11x0ResetAGC((LR11x0 *)_radio, ((CustomLR1110 *)_radio)->getFreqMHz()); }
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
#include <RadioLib.h>
|
||||
#include "MeshCore.h"
|
||||
|
||||
#define SX126X_IRQ_HEADER_VALID 0b0000010000 // 4 4 valid LoRa header received
|
||||
#define SX126X_IRQ_PREAMBLE_DETECTED 0x04
|
||||
|
||||
class CustomSX1262 : public SX1262 {
|
||||
uint32_t _preambleMillis = 66;
|
||||
uint32_t _maxPayloadMillis = 3934;
|
||||
uint32_t _activityAt = 0;
|
||||
bool _headerSeen = false;
|
||||
|
||||
public:
|
||||
CustomSX1262(Module *mod) : SX1262(mod) { }
|
||||
|
||||
@@ -99,9 +101,49 @@ class CustomSX1262 : public SX1262 {
|
||||
}
|
||||
|
||||
bool isReceiving() {
|
||||
uint16_t irq = getIrqFlags();
|
||||
bool detected = (irq & SX126X_IRQ_HEADER_VALID) || (irq & SX126X_IRQ_PREAMBLE_DETECTED);
|
||||
return detected;
|
||||
uint32_t irq = getIrqFlags();
|
||||
bool preamble = irq & RADIOLIB_SX126X_IRQ_PREAMBLE_DETECTED; // bit 2
|
||||
bool header = irq & RADIOLIB_SX126X_IRQ_HEADER_VALID; // bit 4
|
||||
bool hdrErr = irq & RADIOLIB_SX126X_IRQ_HEADER_ERR; // bit 5
|
||||
uint32_t now = millis();
|
||||
if (hdrErr) {
|
||||
clearIrqFlags(RADIOLIB_SX126X_IRQ_PREAMBLE_DETECTED | RADIOLIB_SX126X_IRQ_HEADER_VALID | RADIOLIB_SX126X_IRQ_HEADER_ERR | RADIOLIB_SX126X_IRQ_SYNC_WORD_VALID);
|
||||
_activityAt = 0;
|
||||
_headerSeen = false;
|
||||
return false;
|
||||
}
|
||||
if (header) {
|
||||
if (!_headerSeen) { _headerSeen = true; _activityAt = now; };
|
||||
if (now - _activityAt > _maxPayloadMillis) {
|
||||
MESH_DEBUG_PRINTLN("Clearing header IRQ after %ums", _maxPayloadMillis);
|
||||
clearIrqFlags(RADIOLIB_SX126X_IRQ_PREAMBLE_DETECTED | RADIOLIB_SX126X_IRQ_HEADER_VALID | RADIOLIB_SX126X_IRQ_HEADER_ERR | RADIOLIB_SX126X_IRQ_SYNC_WORD_VALID);
|
||||
_activityAt = 0; _headerSeen = false;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (preamble) {
|
||||
if (_activityAt == 0) _activityAt = now;
|
||||
if (now - _activityAt > _preambleMillis) {
|
||||
clearIrqFlags(RADIOLIB_SX126X_IRQ_PREAMBLE_DETECTED);
|
||||
_activityAt = 0;
|
||||
MESH_DEBUG_PRINTLN("Clearing preamble IRQ after %ums", _preambleMillis);
|
||||
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
_activityAt = 0; _headerSeen = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
void setPreambleMillis(uint32_t preambleMillis) {
|
||||
_preambleMillis = preambleMillis;
|
||||
MESH_DEBUG_PRINTLN("Set _preambleMillis=%u", _preambleMillis);
|
||||
}
|
||||
void setMaxPayloadMillis(uint32_t payloadMillis) {
|
||||
_maxPayloadMillis = payloadMillis;
|
||||
MESH_DEBUG_PRINTLN("Set _maxPayloadMillis=%u", _maxPayloadMillis);
|
||||
}
|
||||
|
||||
bool getRxBoostedGainMode() {
|
||||
|
||||
@@ -18,6 +18,9 @@ public:
|
||||
((CustomSX1262 *)_radio)->setBandwidth(bw);
|
||||
((CustomSX1262 *)_radio)->setCodingRate(cr);
|
||||
updatePreamble(sf);
|
||||
PacketMillis pm = calcMaxPacketMillis(sf, bw, cr, preambleLengthForSF(sf));
|
||||
((CustomSX1262 *)_radio)->setPreambleMillis(pm.preambleMillis);
|
||||
((CustomSX1262 *)_radio)->setMaxPayloadMillis(pm.payloadMillis);
|
||||
}
|
||||
|
||||
bool isReceivingPacket() override {
|
||||
|
||||
@@ -228,3 +228,21 @@ float RadioLibWrapper::packetScoreInt(float snr, int sf, int packet_len) {
|
||||
|
||||
return max(0.0, min(1.0, success_rate_based_on_snr * collision_penalty));
|
||||
}
|
||||
|
||||
PacketMillis RadioLibWrapper::calcMaxPacketMillis(uint8_t sf, float bw, uint8_t cr, uint8_t preambleSymbols) {
|
||||
// based on RadioLib's calculateTimeOnAir()
|
||||
uint32_t tsym_us = ((uint32_t)10000 << sf) / (bw * 10);
|
||||
uint32_t sfCoeff1_x4 = (sf == 5 || sf == 6) ? 25 : 17; // 6.25 : 4.25, semtech magic numbers to account for sync word + sfd
|
||||
|
||||
// preamble + syncword + sfd + header
|
||||
uint32_t preamble_us = (((preambleSymbols + 8) * 4 + sfCoeff1_x4) * tsym_us) / 4;
|
||||
|
||||
// airtime for max packet at current radio settings
|
||||
uint32_t total_us = _radio->getTimeOnAir(MAX_TRANS_UNIT);
|
||||
// airtime for payload only (no preamble, header or SOF)
|
||||
uint32_t payload_us = total_us > preamble_us ? total_us - preamble_us : 4000 - preamble_us; // fallback to 4 secs at worst case
|
||||
// rescale payload_us for max possible CR
|
||||
if (cr >= 5 && cr < 8) { payload_us = (payload_us * 8) / cr; }
|
||||
|
||||
return PacketMillis {(preamble_us + 999) / 1000, (payload_us + 999) / 1000};
|
||||
}
|
||||
@@ -6,6 +6,10 @@
|
||||
#ifdef USE_CC310_HW_CRYPTO
|
||||
#include <Adafruit_nRFCrypto.h>
|
||||
#endif
|
||||
struct PacketMillis {
|
||||
uint32_t preambleMillis; // preamble-detect -> header-valid deadline
|
||||
uint32_t payloadMillis; // header-valid -> rx-done deadline
|
||||
};
|
||||
|
||||
class RadioLibWrapper : public mesh::Radio {
|
||||
protected:
|
||||
@@ -51,6 +55,7 @@ public:
|
||||
virtual uint8_t getSpreadingFactor() const { return LORA_SF; }
|
||||
static uint16_t preambleLengthForSF(uint8_t sf) { return sf <= 8 ? 32 : 16; }
|
||||
void updatePreamble(uint8_t sf) { _preamble_sf = sf; _radio->setPreambleLength(preambleLengthForSF(sf)); }
|
||||
PacketMillis calcMaxPacketMillis(uint8_t sf, float bw, uint8_t cr, uint8_t preambleSymbols);
|
||||
virtual int16_t performChannelScan();
|
||||
|
||||
int getNoiseFloor() const override { return _noise_floor; }
|
||||
|
||||
@@ -807,6 +807,13 @@ void EnvironmentSensorManager::rakGPSInit(){
|
||||
|
||||
bool EnvironmentSensorManager::gpsIsAwake(uint8_t ioPin){
|
||||
|
||||
#if defined(ETHERNET_ENABLED) && defined(RAK_BOARD)
|
||||
if (ioPin == WB_IO2) {
|
||||
// WB_IO2 powers the Ethernet module on RAK baseboards.
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
//set initial waking state
|
||||
pinMode(ioPin,OUTPUT);
|
||||
digitalWrite(ioPin,LOW);
|
||||
|
||||
@@ -0,0 +1,553 @@
|
||||
#include "NV3001BDisplay.h"
|
||||
#include <Arduino.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef SPI_FREQUENCY
|
||||
#define SPI_FREQUENCY 8000000
|
||||
#endif
|
||||
|
||||
#ifndef PIN_TFT_SCL
|
||||
#error "PIN_TFT_SCL must be defined"
|
||||
#endif
|
||||
|
||||
#ifndef PIN_TFT_SDA
|
||||
#error "PIN_TFT_SDA must be defined"
|
||||
#endif
|
||||
|
||||
#ifndef PIN_TFT_CS
|
||||
#error "PIN_TFT_CS must be defined"
|
||||
#endif
|
||||
|
||||
#ifndef PIN_TFT_DC
|
||||
#error "PIN_TFT_DC must be defined"
|
||||
#endif
|
||||
|
||||
#ifndef PIN_TFT_MISO
|
||||
#define PIN_TFT_MISO -1
|
||||
#endif
|
||||
|
||||
#ifndef PIN_TFT_RST
|
||||
#define PIN_TFT_RST -1
|
||||
#endif
|
||||
|
||||
#ifndef PIN_TFT_EN
|
||||
#define PIN_TFT_EN -1
|
||||
#endif
|
||||
|
||||
#ifndef PIN_TFT_BL
|
||||
#define PIN_TFT_BL -1
|
||||
#endif
|
||||
|
||||
#ifndef PIN_TFT_EN_ACTIVE
|
||||
#define PIN_TFT_EN_ACTIVE LOW
|
||||
#endif
|
||||
|
||||
#ifndef PIN_TFT_BL_ACTIVE
|
||||
#define PIN_TFT_BL_ACTIVE HIGH
|
||||
#endif
|
||||
|
||||
#ifndef DISPLAY_ROTATION
|
||||
#define DISPLAY_ROTATION 0
|
||||
#endif
|
||||
|
||||
#ifndef NV3001B_SCREEN_WIDTH
|
||||
#define NV3001B_SCREEN_WIDTH 220
|
||||
#endif
|
||||
|
||||
#ifndef NV3001B_SCREEN_HEIGHT
|
||||
#define NV3001B_SCREEN_HEIGHT 128
|
||||
#endif
|
||||
|
||||
#ifndef DISPLAY_SCALE_X
|
||||
#define DISPLAY_SCALE_X ((float)NV3001B_SCREEN_WIDTH / NV3001B_LOGICAL_WIDTH)
|
||||
#endif
|
||||
|
||||
#ifndef DISPLAY_SCALE_Y
|
||||
#define DISPLAY_SCALE_Y ((float)NV3001B_SCREEN_HEIGHT / NV3001B_LOGICAL_HEIGHT)
|
||||
#endif
|
||||
|
||||
#define NV3001B_SWRESET 0x01
|
||||
#define NV3001B_SLPOUT 0x11
|
||||
#define NV3001B_DISPON 0x29
|
||||
#define NV3001B_CASET 0x2A
|
||||
#define NV3001B_RASET 0x2B
|
||||
#define NV3001B_RAMWR 0x2C
|
||||
#define NV3001B_MADCTL 0x36
|
||||
#define NV3001B_COLMOD 0x3A
|
||||
|
||||
#define NV3001B_MADCTL_MY 0x80
|
||||
#define NV3001B_MADCTL_MX 0x40
|
||||
#define NV3001B_MADCTL_MV 0x20
|
||||
#define NV3001B_MADCTL_RGB 0x00
|
||||
|
||||
#ifndef NV3001B_TEXT_SIZE1_SCALE_X
|
||||
#define NV3001B_TEXT_SIZE1_SCALE_X 1
|
||||
#endif
|
||||
|
||||
#ifndef NV3001B_TEXT_SIZE1_SCALE_Y
|
||||
#define NV3001B_TEXT_SIZE1_SCALE_Y 2
|
||||
#endif
|
||||
|
||||
#ifndef NV3001B_TEXT_SIZE2_SCALE_X
|
||||
#define NV3001B_TEXT_SIZE2_SCALE_X 2
|
||||
#endif
|
||||
|
||||
#ifndef NV3001B_TEXT_SIZE2_SCALE_Y
|
||||
#define NV3001B_TEXT_SIZE2_SCALE_Y 3
|
||||
#endif
|
||||
|
||||
static uint16_t mapColor(DisplayDriver::Color c) {
|
||||
switch (c) {
|
||||
case DisplayDriver::DARK: return 0x0000;
|
||||
case DisplayDriver::LIGHT: return 0xffff;
|
||||
case DisplayDriver::RED: return 0xf800;
|
||||
case DisplayDriver::GREEN: return 0x07e0;
|
||||
case DisplayDriver::BLUE: return 0x001f;
|
||||
case DisplayDriver::YELLOW: return 0xffe0;
|
||||
case DisplayDriver::ORANGE: return 0xfd20;
|
||||
default: return 0xffff;
|
||||
}
|
||||
}
|
||||
|
||||
static int scaleX(int x) {
|
||||
return (int)(x * DISPLAY_SCALE_X);
|
||||
}
|
||||
|
||||
static int scaleY(int y) {
|
||||
return (int)(y * DISPLAY_SCALE_Y);
|
||||
}
|
||||
|
||||
static int scaleWidth(int x, int w) {
|
||||
if (w <= 0) return 0;
|
||||
int scaled = scaleX(x + w) - scaleX(x);
|
||||
return scaled > 0 ? scaled : 1;
|
||||
}
|
||||
|
||||
static int scaleHeight(int y, int h) {
|
||||
if (h <= 0) return 0;
|
||||
int scaled = scaleY(y + h) - scaleY(y);
|
||||
return scaled > 0 ? scaled : 1;
|
||||
}
|
||||
|
||||
static uint8_t nv3001bMADCTL(uint8_t rotation) {
|
||||
uint8_t madctl;
|
||||
switch (rotation & 3) {
|
||||
case 0:
|
||||
madctl = NV3001B_MADCTL_MY | NV3001B_MADCTL_MV | NV3001B_MADCTL_RGB;
|
||||
break;
|
||||
case 1:
|
||||
madctl = NV3001B_MADCTL_MY | NV3001B_MADCTL_MX | NV3001B_MADCTL_RGB;
|
||||
break;
|
||||
case 2:
|
||||
madctl = NV3001B_MADCTL_RGB;
|
||||
break;
|
||||
default:
|
||||
madctl = NV3001B_MADCTL_MX | NV3001B_MADCTL_MV | NV3001B_MADCTL_RGB;
|
||||
break;
|
||||
}
|
||||
return madctl;
|
||||
}
|
||||
|
||||
static const uint8_t font5x7[] PROGMEM = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x07, 0x00, 0x07, 0x00, 0x14,
|
||||
0x7f, 0x14, 0x7f, 0x14, 0x24, 0x2a, 0x7f, 0x2a, 0x12, 0x23, 0x13, 0x08, 0x64, 0x62, 0x36, 0x49,
|
||||
0x55, 0x22, 0x50, 0x00, 0x05, 0x03, 0x00, 0x00, 0x00, 0x1c, 0x22, 0x41, 0x00, 0x00, 0x41, 0x22,
|
||||
0x1c, 0x00, 0x14, 0x08, 0x3e, 0x08, 0x14, 0x08, 0x08, 0x3e, 0x08, 0x08, 0x00, 0x50, 0x30, 0x00,
|
||||
0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x60, 0x60, 0x00, 0x00, 0x20, 0x10, 0x08, 0x04, 0x02,
|
||||
0x3e, 0x51, 0x49, 0x45, 0x3e, 0x00, 0x42, 0x7f, 0x40, 0x00, 0x42, 0x61, 0x51, 0x49, 0x46, 0x21,
|
||||
0x41, 0x45, 0x4b, 0x31, 0x18, 0x14, 0x12, 0x7f, 0x10, 0x27, 0x45, 0x45, 0x45, 0x39, 0x3c, 0x4a,
|
||||
0x49, 0x49, 0x30, 0x01, 0x71, 0x09, 0x05, 0x03, 0x36, 0x49, 0x49, 0x49, 0x36, 0x06, 0x49, 0x49,
|
||||
0x29, 0x1e, 0x00, 0x36, 0x36, 0x00, 0x00, 0x00, 0x56, 0x36, 0x00, 0x00, 0x08, 0x14, 0x22, 0x41,
|
||||
0x00, 0x14, 0x14, 0x14, 0x14, 0x14, 0x00, 0x41, 0x22, 0x14, 0x08, 0x02, 0x01, 0x51, 0x09, 0x06,
|
||||
0x32, 0x49, 0x79, 0x41, 0x3e, 0x7e, 0x11, 0x11, 0x11, 0x7e, 0x7f, 0x49, 0x49, 0x49, 0x36, 0x3e,
|
||||
0x41, 0x41, 0x41, 0x22, 0x7f, 0x41, 0x41, 0x22, 0x1c, 0x7f, 0x49, 0x49, 0x49, 0x41, 0x7f, 0x09,
|
||||
0x09, 0x09, 0x01, 0x3e, 0x41, 0x49, 0x49, 0x7a, 0x7f, 0x08, 0x08, 0x08, 0x7f, 0x00, 0x41, 0x7f,
|
||||
0x41, 0x00, 0x20, 0x40, 0x41, 0x3f, 0x01, 0x7f, 0x08, 0x14, 0x22, 0x41, 0x7f, 0x40, 0x40, 0x40,
|
||||
0x40, 0x7f, 0x02, 0x0c, 0x02, 0x7f, 0x7f, 0x04, 0x08, 0x10, 0x7f, 0x3e, 0x41, 0x41, 0x41, 0x3e,
|
||||
0x7f, 0x09, 0x09, 0x09, 0x06, 0x3e, 0x41, 0x51, 0x21, 0x5e, 0x7f, 0x09, 0x19, 0x29, 0x46, 0x46,
|
||||
0x49, 0x49, 0x49, 0x31, 0x01, 0x01, 0x7f, 0x01, 0x01, 0x3f, 0x40, 0x40, 0x40, 0x3f, 0x1f, 0x20,
|
||||
0x40, 0x20, 0x1f, 0x3f, 0x40, 0x38, 0x40, 0x3f, 0x63, 0x14, 0x08, 0x14, 0x63, 0x07, 0x08, 0x70,
|
||||
0x08, 0x07, 0x61, 0x51, 0x49, 0x45, 0x43, 0x00, 0x7f, 0x41, 0x41, 0x00, 0x02, 0x04, 0x08, 0x10,
|
||||
0x20, 0x00, 0x41, 0x41, 0x7f, 0x00, 0x04, 0x02, 0x01, 0x02, 0x04, 0x40, 0x40, 0x40, 0x40, 0x40,
|
||||
0x00, 0x01, 0x02, 0x04, 0x00, 0x20, 0x54, 0x54, 0x54, 0x78, 0x7f, 0x48, 0x44, 0x44, 0x38, 0x38,
|
||||
0x44, 0x44, 0x44, 0x20, 0x38, 0x44, 0x44, 0x48, 0x7f, 0x38, 0x54, 0x54, 0x54, 0x18, 0x08, 0x7e,
|
||||
0x09, 0x01, 0x02, 0x0c, 0x52, 0x52, 0x52, 0x3e, 0x7f, 0x08, 0x04, 0x04, 0x78, 0x00, 0x44, 0x7d,
|
||||
0x40, 0x00, 0x20, 0x40, 0x44, 0x3d, 0x00, 0x7f, 0x10, 0x28, 0x44, 0x00, 0x00, 0x41, 0x7f, 0x40,
|
||||
0x00, 0x7c, 0x04, 0x18, 0x04, 0x78, 0x7c, 0x08, 0x04, 0x04, 0x78, 0x38, 0x44, 0x44, 0x44, 0x38,
|
||||
0x7c, 0x14, 0x14, 0x14, 0x08, 0x08, 0x14, 0x14, 0x18, 0x7c, 0x7c, 0x08, 0x04, 0x04, 0x08, 0x48,
|
||||
0x54, 0x54, 0x54, 0x20, 0x04, 0x3f, 0x44, 0x40, 0x20, 0x3c, 0x40, 0x40, 0x20, 0x7c, 0x1c, 0x20,
|
||||
0x40, 0x20, 0x1c, 0x3c, 0x40, 0x30, 0x40, 0x3c, 0x44, 0x28, 0x10, 0x28, 0x44, 0x0c, 0x50, 0x50,
|
||||
0x50, 0x3c, 0x44, 0x64, 0x54, 0x4c, 0x44, 0x00, 0x08, 0x36, 0x41, 0x00, 0x00, 0x00, 0x7f, 0x00,
|
||||
0x00, 0x00, 0x41, 0x36, 0x08, 0x00, 0x08, 0x08, 0x2a, 0x1c, 0x08, 0x00, 0x06, 0x09, 0x09, 0x06
|
||||
};
|
||||
|
||||
static int textPixelScaleX(uint8_t size) {
|
||||
return size <= 1 ? NV3001B_TEXT_SIZE1_SCALE_X : NV3001B_TEXT_SIZE2_SCALE_X;
|
||||
}
|
||||
|
||||
static int textPixelScaleY(uint8_t size) {
|
||||
return size <= 1 ? NV3001B_TEXT_SIZE1_SCALE_Y : NV3001B_TEXT_SIZE2_SCALE_Y;
|
||||
}
|
||||
|
||||
static void setupOptionalOutput(int pin, int level) {
|
||||
if (pin < 0) return;
|
||||
|
||||
pinMode(pin, OUTPUT);
|
||||
digitalWrite(pin, level);
|
||||
}
|
||||
|
||||
static void writeOptionalPin(int pin, int level) {
|
||||
if (pin < 0) return;
|
||||
|
||||
digitalWrite(pin, level);
|
||||
}
|
||||
|
||||
void NV3001BDisplay::writeCommand(uint8_t cmd) {
|
||||
spi.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, SPI_MODE0));
|
||||
digitalWrite(PIN_TFT_DC, LOW);
|
||||
digitalWrite(PIN_TFT_CS, LOW);
|
||||
spi.transfer(cmd);
|
||||
digitalWrite(PIN_TFT_CS, HIGH);
|
||||
spi.endTransaction();
|
||||
}
|
||||
|
||||
void NV3001BDisplay::writeBytes(const uint8_t* data, size_t len) {
|
||||
if (!data || len == 0) return;
|
||||
|
||||
spi.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, SPI_MODE0));
|
||||
digitalWrite(PIN_TFT_DC, HIGH);
|
||||
digitalWrite(PIN_TFT_CS, LOW);
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
spi.transfer(data[i]);
|
||||
}
|
||||
digitalWrite(PIN_TFT_CS, HIGH);
|
||||
spi.endTransaction();
|
||||
}
|
||||
|
||||
void NV3001BDisplay::writeCommandData(uint8_t cmd, const uint8_t* data, size_t len) {
|
||||
writeCommand(cmd);
|
||||
writeBytes(data, len);
|
||||
}
|
||||
|
||||
void NV3001BDisplay::setAddrWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h) {
|
||||
uint16_t x2 = x + w - 1;
|
||||
uint16_t y2 = y + h - 1;
|
||||
uint8_t data[4];
|
||||
|
||||
data[0] = x >> 8;
|
||||
data[1] = x & 0xff;
|
||||
data[2] = x2 >> 8;
|
||||
data[3] = x2 & 0xff;
|
||||
writeCommandData(NV3001B_CASET, data, sizeof(data));
|
||||
|
||||
data[0] = y >> 8;
|
||||
data[1] = y & 0xff;
|
||||
data[2] = y2 >> 8;
|
||||
data[3] = y2 & 0xff;
|
||||
writeCommandData(NV3001B_RASET, data, sizeof(data));
|
||||
|
||||
writeCommand(NV3001B_RAMWR);
|
||||
}
|
||||
|
||||
void NV3001BDisplay::writeColor(uint16_t rgb, uint32_t count) {
|
||||
uint8_t hi = rgb >> 8;
|
||||
uint8_t lo = rgb & 0xff;
|
||||
|
||||
spi.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, SPI_MODE0));
|
||||
digitalWrite(PIN_TFT_DC, HIGH);
|
||||
digitalWrite(PIN_TFT_CS, LOW);
|
||||
while (count--) {
|
||||
spi.transfer(hi);
|
||||
spi.transfer(lo);
|
||||
}
|
||||
digitalWrite(PIN_TFT_CS, HIGH);
|
||||
spi.endTransaction();
|
||||
}
|
||||
|
||||
void NV3001BDisplay::initPanel() {
|
||||
#define CMD0(C) do { writeCommand(C); } while (0)
|
||||
#define CMD1(C, A) do { const uint8_t d[] = { A }; writeCommandData(C, d, sizeof(d)); } while (0)
|
||||
#define CMD2(C, A, B) do { const uint8_t d[] = { A, B }; writeCommandData(C, d, sizeof(d)); } while (0)
|
||||
|
||||
CMD0(NV3001B_SWRESET);
|
||||
delay(120);
|
||||
CMD1(0xFF, 0xA5);
|
||||
CMD1(0x41, 0x00);
|
||||
CMD1(0x50, 0x02);
|
||||
CMD1(0x52, 0x6E);
|
||||
CMD1(0x57, 0x02);
|
||||
CMD1(0x46, 0x11);
|
||||
CMD2(0x47, 0x00, 0x01);
|
||||
CMD2(0x8F, 0x22, 0x03);
|
||||
CMD1(0x9A, 0x78);
|
||||
CMD1(0x9B, 0x78);
|
||||
CMD1(0x9C, 0xA0);
|
||||
CMD1(0x9D, 0x17);
|
||||
CMD1(0x9E, 0xC1);
|
||||
CMD1(0x83, 0x5A);
|
||||
CMD1(0x84, 0xB6);
|
||||
CMD1(0xFF, 0xA5);
|
||||
CMD1(0x85, 0x5F);
|
||||
CMD1(0x6E, 0x0F);
|
||||
CMD1(0x7E, 0x0F);
|
||||
CMD1(0x60, 0x00);
|
||||
CMD1(0x70, 0x00);
|
||||
CMD1(0x6D, 0x33);
|
||||
CMD1(0x7D, 0x37);
|
||||
CMD1(0x61, 0x09);
|
||||
CMD1(0x71, 0x0A);
|
||||
CMD1(0x6C, 0x2A);
|
||||
CMD1(0x7C, 0x36);
|
||||
CMD1(0x62, 0x11);
|
||||
CMD1(0x72, 0x10);
|
||||
CMD1(0x68, 0x4E);
|
||||
CMD1(0x78, 0x4E);
|
||||
CMD1(0x66, 0x36);
|
||||
CMD1(0x76, 0x3C);
|
||||
CMD1(0x1A, 0x1C);
|
||||
CMD1(0x7B, 0x14);
|
||||
CMD1(0x63, 0x0D);
|
||||
CMD1(0x73, 0x0A);
|
||||
CMD1(0x6A, 0x16);
|
||||
CMD1(0x7A, 0x12);
|
||||
CMD1(0x64, 0x0B);
|
||||
CMD1(0x74, 0x0A);
|
||||
CMD1(0x69, 0x08);
|
||||
CMD1(0x79, 0x0A);
|
||||
CMD1(0x65, 0x06);
|
||||
CMD1(0x75, 0x07);
|
||||
CMD1(0x67, 0x23);
|
||||
CMD1(0x77, 0x44);
|
||||
CMD1(0xE0, 0x00);
|
||||
CMD1(0xE9, 0x30);
|
||||
CMD1(0xEB, 0xB7);
|
||||
CMD1(0xEC, 0x00);
|
||||
CMD1(0xED, 0x11);
|
||||
CMD1(0xF0, 0xB7);
|
||||
CMD1(0x53, 0x04);
|
||||
CMD1(0x54, 0x04);
|
||||
CMD1(0x55, 0x40);
|
||||
CMD1(0x56, 0x40);
|
||||
CMD2(0xA0, 0x60, 0x01);
|
||||
CMD1(0xA1, 0x84);
|
||||
CMD1(0xA2, 0x85);
|
||||
CMD2(0xAB, 0x00, 0x02);
|
||||
CMD2(0xAC, 0x00, 0x06);
|
||||
CMD2(0xAD, 0x00, 0x03);
|
||||
CMD2(0xAE, 0x00, 0x07);
|
||||
CMD1(0xC7, 0x01);
|
||||
CMD1(0xB9, 0x82);
|
||||
CMD1(0xBA, 0x83);
|
||||
CMD1(0xBB, 0x00);
|
||||
CMD1(0xBC, 0x81);
|
||||
CMD1(0xBD, 0x02);
|
||||
CMD1(0xBE, 0x01);
|
||||
CMD1(0xBF, 0x04);
|
||||
CMD1(0xC0, 0x03);
|
||||
CMD1(0xC8, 0x55);
|
||||
CMD1(0xC9, 0xC9);
|
||||
CMD1(0xCA, 0xC8);
|
||||
CMD1(0xCB, 0xCB);
|
||||
CMD1(0xCC, 0xCA);
|
||||
CMD1(0xCD, 0x55);
|
||||
CMD1(0xCE, 0xCE);
|
||||
CMD1(0xCF, 0xCD);
|
||||
CMD1(0xD0, 0xD0);
|
||||
CMD1(0xD1, 0xCF);
|
||||
CMD1(0xF2, 0x46);
|
||||
CMD1(0xA8, 0x04);
|
||||
CMD1(0xA9, 0xB0);
|
||||
CMD1(0xAA, 0xA3);
|
||||
CMD1(0xB6, 0x00);
|
||||
CMD1(0xB7, 0xB0);
|
||||
CMD1(0xB8, 0xA3);
|
||||
CMD1(0xC4, 0x03);
|
||||
CMD1(0xC5, 0xB0);
|
||||
CMD1(0xC6, 0xA3);
|
||||
CMD1(0x80, 0x10);
|
||||
CMD1(0xFF, 0x00);
|
||||
CMD1(0x35, 0x00);
|
||||
CMD0(NV3001B_SLPOUT);
|
||||
delay(120);
|
||||
CMD1(NV3001B_COLMOD, 0x05);
|
||||
CMD1(NV3001B_MADCTL, nv3001bMADCTL(DISPLAY_ROTATION));
|
||||
CMD0(NV3001B_DISPON);
|
||||
delay(10);
|
||||
|
||||
#undef CMD0
|
||||
#undef CMD1
|
||||
#undef CMD2
|
||||
}
|
||||
|
||||
void NV3001BDisplay::fillPhysicalRect(int x, int y, int w, int h) {
|
||||
if (!is_on || w <= 0 || h <= 0) return;
|
||||
|
||||
if (x < 0) {
|
||||
w += x;
|
||||
x = 0;
|
||||
}
|
||||
if (y < 0) {
|
||||
h += y;
|
||||
y = 0;
|
||||
}
|
||||
if (x + w > NV3001B_SCREEN_WIDTH) w = NV3001B_SCREEN_WIDTH - x;
|
||||
if (y + h > NV3001B_SCREEN_HEIGHT) h = NV3001B_SCREEN_HEIGHT - y;
|
||||
if (w <= 0 || h <= 0) return;
|
||||
|
||||
setAddrWindow(x, y, w, h);
|
||||
writeColor(color, (uint32_t)w * h);
|
||||
}
|
||||
|
||||
void NV3001BDisplay::drawChar(int x, int y, char ch) {
|
||||
if (ch < 32 || ch > 127) ch = '?';
|
||||
|
||||
uint16_t index = (uint16_t)(ch - 32) * 5;
|
||||
int scale_x = textPixelScaleX(text_size);
|
||||
int scale_y = textPixelScaleY(text_size);
|
||||
for (int col = 0; col < 5; col++) {
|
||||
uint8_t line = pgm_read_byte(font5x7 + index + col);
|
||||
for (int row = 0; row < 7; row++) {
|
||||
if (line & (1 << row)) {
|
||||
fillPhysicalRect(x + col * scale_x, y + row * scale_y, scale_x, scale_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool NV3001BDisplay::begin() {
|
||||
if (is_on) return true;
|
||||
|
||||
if (periph_power) periph_power->claim();
|
||||
|
||||
setupOptionalOutput(PIN_TFT_EN, PIN_TFT_EN_ACTIVE);
|
||||
setupOptionalOutput(PIN_TFT_BL, !PIN_TFT_BL_ACTIVE);
|
||||
pinMode(PIN_TFT_CS, OUTPUT);
|
||||
pinMode(PIN_TFT_DC, OUTPUT);
|
||||
digitalWrite(PIN_TFT_CS, HIGH);
|
||||
digitalWrite(PIN_TFT_DC, HIGH);
|
||||
delay(20);
|
||||
|
||||
spi.begin(PIN_TFT_SCL, PIN_TFT_MISO, PIN_TFT_SDA, PIN_TFT_CS);
|
||||
if (PIN_TFT_RST >= 0) {
|
||||
pinMode(PIN_TFT_RST, OUTPUT);
|
||||
digitalWrite(PIN_TFT_RST, HIGH);
|
||||
delay(10);
|
||||
digitalWrite(PIN_TFT_RST, LOW);
|
||||
delay(20);
|
||||
digitalWrite(PIN_TFT_RST, HIGH);
|
||||
delay(120);
|
||||
}
|
||||
|
||||
initPanel();
|
||||
is_on = true;
|
||||
color = 0x0000;
|
||||
fillPhysicalRect(0, 0, NV3001B_SCREEN_WIDTH, NV3001B_SCREEN_HEIGHT);
|
||||
color = 0xffff;
|
||||
text_size = 1;
|
||||
cursor_x = 0;
|
||||
cursor_y = 0;
|
||||
writeOptionalPin(PIN_TFT_BL, PIN_TFT_BL_ACTIVE);
|
||||
return true;
|
||||
}
|
||||
|
||||
void NV3001BDisplay::turnOn() {
|
||||
begin();
|
||||
}
|
||||
|
||||
void NV3001BDisplay::turnOff() {
|
||||
if (!is_on) return;
|
||||
|
||||
writeOptionalPin(PIN_TFT_BL, !PIN_TFT_BL_ACTIVE);
|
||||
writeOptionalPin(PIN_TFT_EN, !PIN_TFT_EN_ACTIVE);
|
||||
is_on = false;
|
||||
if (periph_power) periph_power->release();
|
||||
}
|
||||
|
||||
void NV3001BDisplay::clear() {
|
||||
uint16_t saved = color;
|
||||
color = 0x0000;
|
||||
fillPhysicalRect(0, 0, NV3001B_SCREEN_WIDTH, NV3001B_SCREEN_HEIGHT);
|
||||
color = saved;
|
||||
}
|
||||
|
||||
void NV3001BDisplay::startFrame(Color bkg) {
|
||||
color = mapColor(bkg);
|
||||
fillPhysicalRect(0, 0, NV3001B_SCREEN_WIDTH, NV3001B_SCREEN_HEIGHT);
|
||||
color = 0xffff;
|
||||
text_size = 1;
|
||||
cursor_x = 0;
|
||||
cursor_y = 0;
|
||||
}
|
||||
|
||||
void NV3001BDisplay::setTextSize(int sz) {
|
||||
text_size = sz < 1 ? 1 : sz;
|
||||
}
|
||||
|
||||
void NV3001BDisplay::setColor(Color c) {
|
||||
color = mapColor(c);
|
||||
}
|
||||
|
||||
void NV3001BDisplay::setCursor(int x, int y) {
|
||||
cursor_x = scaleX(x);
|
||||
cursor_y = scaleY(y);
|
||||
}
|
||||
|
||||
void NV3001BDisplay::print(const char* str) {
|
||||
if (!str || !is_on) return;
|
||||
|
||||
int scale_x = textPixelScaleX(text_size);
|
||||
int scale_y = textPixelScaleY(text_size);
|
||||
while (*str) {
|
||||
if (*str == '\n') {
|
||||
cursor_x = 0;
|
||||
cursor_y += 8 * scale_y;
|
||||
} else if (*str == '\r') {
|
||||
cursor_x = 0;
|
||||
} else {
|
||||
drawChar(cursor_x, cursor_y, *str);
|
||||
cursor_x += 6 * scale_x;
|
||||
}
|
||||
str++;
|
||||
}
|
||||
}
|
||||
|
||||
void NV3001BDisplay::fillRect(int x, int y, int w, int h) {
|
||||
fillPhysicalRect(scaleX(x), scaleY(y), scaleWidth(x, w), scaleHeight(y, h));
|
||||
}
|
||||
|
||||
void NV3001BDisplay::drawRect(int x, int y, int w, int h) {
|
||||
int x1 = scaleX(x);
|
||||
int y1 = scaleY(y);
|
||||
int sw = scaleWidth(x, w);
|
||||
int sh = scaleHeight(y, h);
|
||||
|
||||
fillPhysicalRect(x1, y1, sw, 1);
|
||||
fillPhysicalRect(x1, y1 + sh - 1, sw, 1);
|
||||
fillPhysicalRect(x1, y1, 1, sh);
|
||||
fillPhysicalRect(x1 + sw - 1, y1, 1, sh);
|
||||
}
|
||||
|
||||
void NV3001BDisplay::drawXbm(int x, int y, const uint8_t* bits, int w, int h) {
|
||||
if (!bits || !is_on) return;
|
||||
|
||||
int byte_width = (w + 7) / 8;
|
||||
for (int j = 0; j < h; j++) {
|
||||
for (int i = 0; i < w; i++) {
|
||||
uint8_t byte = pgm_read_byte(bits + j * byte_width + i / 8);
|
||||
if (byte & (0x80 >> (i & 7))) {
|
||||
fillPhysicalRect(scaleX(x + i), scaleY(y + j), scaleWidth(x + i, 1), scaleHeight(y + j, 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t NV3001BDisplay::getTextWidth(const char* str) {
|
||||
if (!str) return 0;
|
||||
|
||||
uint16_t len = 0;
|
||||
while (str[len] && str[len] != '\n' && str[len] != '\r') len++;
|
||||
return (uint16_t)((len * 6 * textPixelScaleX(text_size)) / DISPLAY_SCALE_X);
|
||||
}
|
||||
|
||||
void NV3001BDisplay::endFrame() {
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
|
||||
#include "DisplayDriver.h"
|
||||
#include <SPI.h>
|
||||
#include <helpers/RefCountedDigitalPin.h>
|
||||
|
||||
#ifndef NV3001B_LOGICAL_WIDTH
|
||||
#define NV3001B_LOGICAL_WIDTH 128
|
||||
#endif
|
||||
|
||||
#ifndef NV3001B_LOGICAL_HEIGHT
|
||||
#define NV3001B_LOGICAL_HEIGHT 64
|
||||
#endif
|
||||
|
||||
#ifndef NV3001B_PANEL_WIDTH
|
||||
#define NV3001B_PANEL_WIDTH 128
|
||||
#endif
|
||||
|
||||
#ifndef NV3001B_PANEL_HEIGHT
|
||||
#define NV3001B_PANEL_HEIGHT 220
|
||||
#endif
|
||||
|
||||
#ifndef NV3001B_SPI_HOST
|
||||
#define NV3001B_SPI_HOST HSPI
|
||||
#endif
|
||||
|
||||
class NV3001BDisplay : public DisplayDriver {
|
||||
SPIClass spi;
|
||||
RefCountedDigitalPin* periph_power;
|
||||
bool is_on = false;
|
||||
uint16_t color = 0xffff;
|
||||
uint8_t text_size = 1;
|
||||
int cursor_x = 0;
|
||||
int cursor_y = 0;
|
||||
|
||||
void writeCommand(uint8_t cmd);
|
||||
void writeBytes(const uint8_t* data, size_t len);
|
||||
void writeCommandData(uint8_t cmd, const uint8_t* data, size_t len);
|
||||
void setAddrWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h);
|
||||
void writeColor(uint16_t rgb, uint32_t count);
|
||||
void fillPhysicalRect(int x, int y, int w, int h);
|
||||
void initPanel();
|
||||
void drawChar(int x, int y, char ch);
|
||||
|
||||
public:
|
||||
NV3001BDisplay(RefCountedDigitalPin* power = nullptr) :
|
||||
DisplayDriver(NV3001B_LOGICAL_WIDTH, NV3001B_LOGICAL_HEIGHT), spi(NV3001B_SPI_HOST), periph_power(power) { }
|
||||
|
||||
bool begin();
|
||||
static const char* driverName() { return "NV3001B"; }
|
||||
static uint16_t physicalWidth() { return NV3001B_PANEL_WIDTH; }
|
||||
static uint16_t physicalHeight() { return NV3001B_PANEL_HEIGHT; }
|
||||
|
||||
bool isOn() override { return is_on; }
|
||||
void turnOn() override;
|
||||
void turnOff() override;
|
||||
void clear() override;
|
||||
void startFrame(Color bkg = DARK) override;
|
||||
void setTextSize(int sz) override;
|
||||
void setColor(Color c) override;
|
||||
void setCursor(int x, int y) override;
|
||||
void print(const char* str) override;
|
||||
void fillRect(int x, int y, int w, int h) override;
|
||||
void drawRect(int x, int y, int w, int h) override;
|
||||
void drawXbm(int x, int y, const uint8_t* bits, int w, int h) override;
|
||||
uint16_t getTextWidth(const char* str) override;
|
||||
void endFrame() override;
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
enum class RotaryInputEvent : uint8_t {
|
||||
None,
|
||||
Next,
|
||||
Prev,
|
||||
};
|
||||
|
||||
class RotaryInput {
|
||||
public:
|
||||
virtual ~RotaryInput() = default;
|
||||
|
||||
virtual bool begin() = 0;
|
||||
virtual RotaryInputEvent poll() = 0;
|
||||
virtual bool isReady() const = 0;
|
||||
};
|
||||
@@ -255,6 +255,10 @@ static const uint8_t PROGMEM
|
||||
0x00, 0x00, // XSTART = 0
|
||||
0x00, 0x9F }, // XEND = 159
|
||||
|
||||
Rcmd2invert[] = { // Tracker V1, part 2
|
||||
1, // 1 command in list:
|
||||
ST77XX_INVON, 0 }, // 1: Display is inverted
|
||||
|
||||
Rcmd3[] = { // 7735R init, part 3 (red or green tab)
|
||||
2, // 2 commands in list:
|
||||
ST7735_GMCTRP1, 16 , // 1: Gamma Adjustments (pos. polarity), 16 args + delay:
|
||||
@@ -447,8 +451,13 @@ bool ST7735Display::begin() {
|
||||
|
||||
_height = 80;
|
||||
_width = 160;
|
||||
#if defined(HELTEC_LORA_V3) // Tracker v1
|
||||
_colstart = 26;
|
||||
_rowstart = 1;
|
||||
#else
|
||||
_colstart = 24;
|
||||
_rowstart = 0;
|
||||
#endif
|
||||
|
||||
_resetAndInit();
|
||||
|
||||
@@ -474,6 +483,8 @@ void ST7735Display::_resetAndInit() {
|
||||
displayInit(Rcmd2green160x80);
|
||||
//uint8_t madctl = ST77XX_MADCTL_MY | ST77XX_MADCTL_MV |ST7735_MADCTL_BGR;//Adjust color to BGR
|
||||
//display.sendCommand(ST77XX_MADCTL, &madctl, 1);
|
||||
#elif defined(HELTEC_LORA_V3) // Tracker v1
|
||||
displayInit(Rcmd2invert); // invert RGB
|
||||
#endif
|
||||
displayInit(Rcmd3);
|
||||
setRotation(DISPLAY_ROTATION);
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
#include "ST7789LCDDisplay.h"
|
||||
|
||||
#ifndef PIN_TFT_MISO
|
||||
#define PIN_TFT_MISO -1
|
||||
#endif
|
||||
|
||||
#ifndef DISPLAY_ROTATION
|
||||
#define DISPLAY_ROTATION 3
|
||||
#endif
|
||||
@@ -29,8 +33,8 @@ bool ST7789LCDDisplay::begin() {
|
||||
}
|
||||
|
||||
// Im not sure if this is just a t-deck problem or not, if your display is slow try this.
|
||||
#if defined(LILYGO_TDECK) || defined(HELTEC_LORA_V4_TFT)
|
||||
displaySPI.begin(PIN_TFT_SCL, -1, PIN_TFT_SDA, PIN_TFT_CS);
|
||||
#if defined(LILYGO_TDECK) || defined(HELTEC_LORA_V4_TFT) || defined(HELTEC_V4_R8_TFT)
|
||||
displaySPI.begin(PIN_TFT_SCL, PIN_TFT_MISO, PIN_TFT_SDA, PIN_TFT_CS);
|
||||
#endif
|
||||
|
||||
display.init(DISPLAY_WIDTH, DISPLAY_HEIGHT);
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <helpers/RefCountedDigitalPin.h>
|
||||
|
||||
class ST7789LCDDisplay : public DisplayDriver {
|
||||
#if defined(LILYGO_TDECK) || defined(HELTEC_LORA_V4_TFT)
|
||||
#if defined(LILYGO_TDECK) || defined(HELTEC_LORA_V4_TFT) || defined(HELTEC_V4_R8_TFT)
|
||||
SPIClass displaySPI;
|
||||
#endif
|
||||
Adafruit_ST7789 display;
|
||||
@@ -25,7 +25,7 @@ public:
|
||||
{
|
||||
_isOn = false;
|
||||
}
|
||||
#elif defined(LILYGO_TDECK) || defined(HELTEC_LORA_V4_TFT)
|
||||
#elif defined(LILYGO_TDECK) || defined(HELTEC_LORA_V4_TFT) || defined(HELTEC_V4_R8_TFT)
|
||||
ST7789LCDDisplay(RefCountedDigitalPin* peripher_power=NULL) : DisplayDriver(128, 64),
|
||||
displaySPI(HSPI),
|
||||
display(&displaySPI, PIN_TFT_CS, PIN_TFT_DC, PIN_TFT_RST),
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
#include "HeltecRC32Board.h"
|
||||
|
||||
void HeltecRC32Board::begin() {
|
||||
ESP32Board::begin();
|
||||
|
||||
pinMode(PIN_ADC_CTRL, OUTPUT);
|
||||
digitalWrite(PIN_ADC_CTRL, !ADC_CTRL_ENABLED);
|
||||
|
||||
#ifdef SENSOR_RST_PIN
|
||||
pinMode(SENSOR_RST_PIN, OUTPUT);
|
||||
digitalWrite(SENSOR_RST_PIN, HIGH);
|
||||
#endif
|
||||
|
||||
#ifdef LED_POWER
|
||||
pinMode(LED_POWER, OUTPUT);
|
||||
digitalWrite(LED_POWER, LOW);
|
||||
#endif
|
||||
|
||||
periph_power.begin();
|
||||
periph_power.claim();
|
||||
|
||||
esp_reset_reason_t reason = esp_reset_reason();
|
||||
if (reason == ESP_RST_DEEPSLEEP) {
|
||||
long wakeup_source = esp_sleep_get_ext1_wakeup_status();
|
||||
if (wakeup_source & (1L << P_LORA_DIO_1)) {
|
||||
startup_reason = BD_STARTUP_RX_PACKET;
|
||||
}
|
||||
|
||||
rtc_gpio_hold_dis((gpio_num_t)P_LORA_NSS);
|
||||
rtc_gpio_deinit((gpio_num_t)P_LORA_DIO_1);
|
||||
}
|
||||
}
|
||||
|
||||
void HeltecRC32Board::powerOff() {
|
||||
enterDeepSleep(0);
|
||||
}
|
||||
|
||||
void HeltecRC32Board::onBeforeTransmit() {
|
||||
digitalWrite(P_LORA_TX_LED, HIGH);
|
||||
}
|
||||
|
||||
void HeltecRC32Board::onAfterTransmit() {
|
||||
digitalWrite(P_LORA_TX_LED, LOW);
|
||||
}
|
||||
|
||||
uint16_t HeltecRC32Board::getBattMilliVolts() {
|
||||
analogReadResolution(12);
|
||||
analogSetAttenuation(ADC_2_5db);
|
||||
digitalWrite(PIN_ADC_CTRL, ADC_CTRL_ENABLED);
|
||||
delay(10);
|
||||
uint32_t raw = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
raw += analogReadMilliVolts(PIN_VBAT_READ);
|
||||
}
|
||||
raw = raw / 8;
|
||||
digitalWrite(PIN_ADC_CTRL, !ADC_CTRL_ENABLED);
|
||||
|
||||
return (adc_mult * raw);
|
||||
}
|
||||
|
||||
bool HeltecRC32Board::setAdcMultiplier(float multiplier) {
|
||||
adc_mult = multiplier == 0.0f ? ADC_MULTIPLIER : multiplier;
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* HeltecRC32Board::getManufacturerName() const {
|
||||
return "Heltec RC32";
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <driver/rtc_io.h>
|
||||
#include <helpers/ESP32Board.h>
|
||||
#include <helpers/RefCountedDigitalPin.h>
|
||||
|
||||
#ifndef ADC_MULTIPLIER
|
||||
#define ADC_MULTIPLIER 4.9f
|
||||
#endif
|
||||
|
||||
class HeltecRC32Board : public ESP32Board {
|
||||
protected:
|
||||
float adc_mult = ADC_MULTIPLIER;
|
||||
|
||||
public:
|
||||
RefCountedDigitalPin periph_power;
|
||||
|
||||
HeltecRC32Board() : periph_power(SENSOR_POWER_CTRL_PIN, SENSOR_POWER_ON){}
|
||||
|
||||
void begin();
|
||||
void onBeforeTransmit() override;
|
||||
void onAfterTransmit() override;
|
||||
void powerOff() override;
|
||||
uint16_t getBattMilliVolts() override;
|
||||
bool setAdcMultiplier(float multiplier) override;
|
||||
float getAdcMultiplier() const override { return adc_mult; }
|
||||
const char* getManufacturerName() const override;
|
||||
};
|
||||
@@ -0,0 +1,122 @@
|
||||
#include "HeltecRC32RotaryInput.h"
|
||||
|
||||
#include <Wire.h>
|
||||
|
||||
namespace {
|
||||
constexpr uint8_t TCA6408_ADDR = 0x20;
|
||||
constexpr uint8_t TCA6408_INPUT_REG = 0x00;
|
||||
constexpr uint8_t TCA6408_POLARITY_REG = 0x02;
|
||||
constexpr uint8_t TCA6408_CONFIG_REG = 0x03;
|
||||
constexpr uint8_t TCA6408_ROTARY_A_MASK = 0x01;
|
||||
constexpr uint8_t TCA6408_ROTARY_B_MASK = 0x02;
|
||||
constexpr uint8_t TCA6408_ROTARY_MASK = TCA6408_ROTARY_A_MASK | TCA6408_ROTARY_B_MASK;
|
||||
constexpr uint32_t TCA6408_DEBOUNCE_MS = 5;
|
||||
}
|
||||
|
||||
bool HeltecRC32RotaryInput::begin() {
|
||||
initialized = true;
|
||||
ready = false;
|
||||
input_state = TCA6408_ROTARY_MASK;
|
||||
active_low_phase = false;
|
||||
|
||||
if (periph_power && !power_claimed) {
|
||||
periph_power->claim();
|
||||
power_claimed = true;
|
||||
delay(12);
|
||||
}
|
||||
|
||||
if (!writeRegister(TCA6408_POLARITY_REG, 0x00) || !writeRegister(TCA6408_CONFIG_REG, 0xFF)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t state = 0;
|
||||
if (!readInput(state)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
input_state = state & TCA6408_ROTARY_MASK;
|
||||
ready = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
RotaryInputEvent HeltecRC32RotaryInput::poll() {
|
||||
if (!initialized) {
|
||||
begin();
|
||||
return RotaryInputEvent::None;
|
||||
}
|
||||
|
||||
if (!ready) {
|
||||
return RotaryInputEvent::None;
|
||||
}
|
||||
|
||||
uint8_t new_state = 0;
|
||||
if (!readInput(new_state)) {
|
||||
return RotaryInputEvent::None;
|
||||
}
|
||||
|
||||
new_state &= TCA6408_ROTARY_MASK;
|
||||
RotaryInputEvent event = handleTransition(new_state);
|
||||
input_state = new_state;
|
||||
return event;
|
||||
}
|
||||
|
||||
bool HeltecRC32RotaryInput::writeRegister(uint8_t reg, uint8_t value) {
|
||||
Wire.beginTransmission(TCA6408_ADDR);
|
||||
Wire.write(reg);
|
||||
Wire.write(value);
|
||||
return Wire.endTransmission() == 0;
|
||||
}
|
||||
|
||||
bool HeltecRC32RotaryInput::readInput(uint8_t& value) {
|
||||
Wire.beginTransmission(TCA6408_ADDR);
|
||||
Wire.write(TCA6408_INPUT_REG);
|
||||
if (Wire.endTransmission(false) != 0) {
|
||||
return false;
|
||||
}
|
||||
if (Wire.requestFrom(TCA6408_ADDR, static_cast<uint8_t>(1)) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
value = Wire.read();
|
||||
return true;
|
||||
}
|
||||
|
||||
RotaryInputEvent HeltecRC32RotaryInput::handleTransition(uint8_t newState) {
|
||||
uint8_t changed = (input_state ^ newState) & TCA6408_ROTARY_MASK;
|
||||
RotaryInputEvent event = RotaryInputEvent::None;
|
||||
bool a_low = (newState & TCA6408_ROTARY_A_MASK) == 0;
|
||||
bool b_low = (newState & TCA6408_ROTARY_B_MASK) == 0;
|
||||
|
||||
if (!a_low && !b_low) {
|
||||
active_low_phase = false;
|
||||
}
|
||||
|
||||
if (!active_low_phase && (changed & TCA6408_ROTARY_A_MASK) && a_low && !b_low) {
|
||||
event = RotaryInputEvent::Prev;
|
||||
active_low_phase = true;
|
||||
} else if (!active_low_phase && (changed & TCA6408_ROTARY_B_MASK) && b_low && !a_low) {
|
||||
event = RotaryInputEvent::Next;
|
||||
active_low_phase = true;
|
||||
}
|
||||
|
||||
if (event == RotaryInputEvent::None && !active_low_phase && (changed & TCA6408_ROTARY_A_MASK)) {
|
||||
bool a_rising = (newState & TCA6408_ROTARY_A_MASK) != 0;
|
||||
if (a_rising && b_low) {
|
||||
event = RotaryInputEvent::Prev;
|
||||
}
|
||||
}
|
||||
|
||||
if (event == RotaryInputEvent::None && !active_low_phase && (changed & TCA6408_ROTARY_B_MASK)) {
|
||||
bool b_rising = (newState & TCA6408_ROTARY_B_MASK) != 0;
|
||||
if (b_rising && a_low) {
|
||||
event = RotaryInputEvent::Next;
|
||||
}
|
||||
}
|
||||
|
||||
if (event == RotaryInputEvent::None || (millis() - last_event_ms) < TCA6408_DEBOUNCE_MS) {
|
||||
return RotaryInputEvent::None;
|
||||
}
|
||||
|
||||
last_event_ms = millis();
|
||||
return event;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <helpers/RefCountedDigitalPin.h>
|
||||
#include <helpers/ui/RotaryInput.h>
|
||||
|
||||
class HeltecRC32RotaryInput : public RotaryInput {
|
||||
public:
|
||||
explicit HeltecRC32RotaryInput(RefCountedDigitalPin* periphPower = nullptr) : periph_power(periphPower) { }
|
||||
|
||||
bool begin() override;
|
||||
RotaryInputEvent poll() override;
|
||||
bool isReady() const override { return ready; }
|
||||
|
||||
private:
|
||||
bool writeRegister(uint8_t reg, uint8_t value);
|
||||
bool readInput(uint8_t& value);
|
||||
RotaryInputEvent handleTransition(uint8_t newState);
|
||||
|
||||
uint8_t input_state = 0x03;
|
||||
uint32_t last_event_ms = 0;
|
||||
bool ready = false;
|
||||
bool initialized = false;
|
||||
bool power_claimed = false;
|
||||
bool active_low_phase = false;
|
||||
RefCountedDigitalPin* periph_power = nullptr;
|
||||
};
|
||||
@@ -0,0 +1,60 @@
|
||||
#ifndef Pins_Arduino_h
|
||||
#define Pins_Arduino_h
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define USB_VID 0x303a
|
||||
#define USB_PID 0x1001
|
||||
|
||||
static const uint8_t TX = 43;
|
||||
static const uint8_t RX = 44;
|
||||
|
||||
static const uint8_t SDA = 21;
|
||||
static const uint8_t SCL = 18;
|
||||
|
||||
static const uint8_t SS = 10;
|
||||
static const uint8_t MOSI = 12;
|
||||
static const uint8_t MISO = 13;
|
||||
static const uint8_t SCK = 11;
|
||||
|
||||
static const uint8_t A0 = 1;
|
||||
static const uint8_t A1 = 2;
|
||||
static const uint8_t A2 = 3;
|
||||
static const uint8_t A3 = 4;
|
||||
static const uint8_t A4 = 5;
|
||||
static const uint8_t A5 = 6;
|
||||
static const uint8_t A6 = 7;
|
||||
static const uint8_t A7 = 8;
|
||||
static const uint8_t A8 = 9;
|
||||
static const uint8_t A9 = 10;
|
||||
static const uint8_t A10 = 11;
|
||||
static const uint8_t A11 = 12;
|
||||
static const uint8_t A12 = 13;
|
||||
static const uint8_t A13 = 14;
|
||||
static const uint8_t A14 = 15;
|
||||
static const uint8_t A15 = 16;
|
||||
static const uint8_t A16 = 17;
|
||||
static const uint8_t A17 = 18;
|
||||
static const uint8_t A18 = 19;
|
||||
static const uint8_t A19 = 20;
|
||||
|
||||
static const uint8_t T1 = 1;
|
||||
static const uint8_t T2 = 2;
|
||||
static const uint8_t T3 = 3;
|
||||
static const uint8_t T4 = 4;
|
||||
static const uint8_t T5 = 5;
|
||||
static const uint8_t T6 = 6;
|
||||
static const uint8_t T7 = 7;
|
||||
static const uint8_t T8 = 8;
|
||||
static const uint8_t T9 = 9;
|
||||
static const uint8_t T10 = 10;
|
||||
static const uint8_t T11 = 11;
|
||||
static const uint8_t T12 = 12;
|
||||
static const uint8_t T13 = 13;
|
||||
static const uint8_t T14 = 14;
|
||||
|
||||
static const uint8_t RST_LoRa = 9;
|
||||
static const uint8_t BUSY_LoRa = 1;
|
||||
static const uint8_t DIO1_LoRa = 14;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,348 @@
|
||||
[Heltec_RC32]
|
||||
extends = esp32_base
|
||||
board = heltec-rc32
|
||||
board_build.partitions = default_16MB.csv
|
||||
build_flags =
|
||||
${esp32_base.build_flags}
|
||||
${sensor_base.build_flags}
|
||||
-I variants/heltec_rc32
|
||||
-I src/helpers/ui
|
||||
-D HELTEC_RC32
|
||||
-D USE_SX1262
|
||||
-D ESP32_CPU_FREQ=160
|
||||
-D RADIO_CLASS=CustomSX1262
|
||||
-D WRAPPER_CLASS=CustomSX1262Wrapper
|
||||
-D P_LORA_DIO_1=14
|
||||
-D P_LORA_NSS=10
|
||||
-D P_LORA_RESET=9
|
||||
-D P_LORA_BUSY=1
|
||||
-D P_LORA_SCLK=11
|
||||
-D P_LORA_MISO=13
|
||||
-D P_LORA_MOSI=12
|
||||
-D LORA_TX_POWER=22
|
||||
-D PIN_USER_BTN=0
|
||||
-D PIN_BOARD_SDA=21
|
||||
-D PIN_BOARD_SCL=18
|
||||
-D SENSOR_POWER_CTRL_PIN=46
|
||||
-D SENSOR_POWER_ON=HIGH
|
||||
-D SENSOR_RST_PIN=2
|
||||
-D P_LORA_TX_LED=47
|
||||
-D PIN_BUZZER=48
|
||||
-D PIN_TFT_SCL=17
|
||||
-D PIN_TFT_SDA=38
|
||||
-D PIN_TFT_CS=39
|
||||
-D PIN_TFT_DC=16
|
||||
-D PIN_TFT_RST=4
|
||||
-D PIN_TFT_EN=6
|
||||
-D PIN_TFT_EN_ACTIVE=LOW
|
||||
-D PIN_TFT_BL=5
|
||||
-D PIN_TFT_BL_ACTIVE=HIGH
|
||||
-D SPI_FREQUENCY=8000000
|
||||
-D PIN_GPS_TX=44
|
||||
-D PIN_GPS_RX=43
|
||||
-D PIN_GPS_EN=45
|
||||
-D PIN_GPS_EN_ACTIVE=HIGH
|
||||
-D PIN_GPS_RESET=40
|
||||
-D PIN_GPS_RESET_ACTIVE=LOW
|
||||
-D PIN_GPS_PPS=41
|
||||
-D GPS_BAUD_RATE=9600
|
||||
-D ENV_INCLUDE_GPS=1
|
||||
-D PIN_ADC_CTRL=15
|
||||
-D PIN_VBAT_READ=7
|
||||
-D ADC_CTRL_ENABLED=HIGH
|
||||
-D ADC_MULTIPLIER=4.9
|
||||
-D SX126X_DIO2_AS_RF_SWITCH=true
|
||||
-D SX126X_DIO3_TCXO_VOLTAGE=1.8
|
||||
-D SX126X_CURRENT_LIMIT=140
|
||||
-D SX126X_RX_BOOSTED_GAIN=1
|
||||
-D SX126X_REGISTER_PATCH=1 ; Patch register 0x8B5 for improved RX
|
||||
build_src_filter = ${esp32_base.build_src_filter}
|
||||
+<../variants/heltec_rc32>
|
||||
+<helpers/sensors>
|
||||
lib_deps =
|
||||
${esp32_base.lib_deps}
|
||||
${sensor_base.lib_deps}
|
||||
|
||||
[Heltec_RC32_with_display]
|
||||
extends = Heltec_RC32
|
||||
build_flags =
|
||||
${Heltec_RC32.build_flags}
|
||||
-D HELTEC_RC32_WITH_DISPLAY
|
||||
-D DISPLAY_CLASS=NV3001BDisplay
|
||||
build_src_filter = ${Heltec_RC32.build_src_filter}
|
||||
+<helpers/ui/NV3001BDisplay.cpp>
|
||||
+<helpers/ui/MomentaryButton.cpp>
|
||||
lib_deps =
|
||||
${Heltec_RC32.lib_deps}
|
||||
|
||||
[env:heltec_rc32_without_display_repeater]
|
||||
extends = Heltec_RC32
|
||||
build_flags =
|
||||
${Heltec_RC32.build_flags}
|
||||
-D ADVERT_NAME='"Heltec RC32 Repeater"'
|
||||
-D ADVERT_LAT=0.0
|
||||
-D ADVERT_LON=0.0
|
||||
-D ADMIN_PASSWORD='"password"'
|
||||
-D MAX_NEIGHBOURS=50
|
||||
build_src_filter = ${Heltec_RC32.build_src_filter}
|
||||
+<../examples/simple_repeater>
|
||||
lib_deps =
|
||||
${Heltec_RC32.lib_deps}
|
||||
${esp32_ota.lib_deps}
|
||||
bakercp/CRC32 @ ^2.0.0
|
||||
|
||||
[env:heltec_rc32_without_display_repeater_bridge_espnow]
|
||||
extends = Heltec_RC32
|
||||
build_flags =
|
||||
${Heltec_RC32.build_flags}
|
||||
-D ADVERT_NAME='"ESPNow Bridge"'
|
||||
-D ADVERT_LAT=0.0
|
||||
-D ADVERT_LON=0.0
|
||||
-D ADMIN_PASSWORD='"password"'
|
||||
-D MAX_NEIGHBOURS=50
|
||||
-D WITH_ESPNOW_BRIDGE=1
|
||||
build_src_filter = ${Heltec_RC32.build_src_filter}
|
||||
+<helpers/bridges/ESPNowBridge.cpp>
|
||||
+<../examples/simple_repeater>
|
||||
lib_deps =
|
||||
${Heltec_RC32.lib_deps}
|
||||
${esp32_ota.lib_deps}
|
||||
|
||||
[env:heltec_rc32_without_display_room_server]
|
||||
extends = Heltec_RC32
|
||||
build_flags =
|
||||
${Heltec_RC32.build_flags}
|
||||
-D ADVERT_NAME='"Heltec RC32 Room"'
|
||||
-D ADVERT_LAT=0.0
|
||||
-D ADVERT_LON=0.0
|
||||
-D ADMIN_PASSWORD='"password"'
|
||||
-D ROOM_PASSWORD='"hello"'
|
||||
build_src_filter = ${Heltec_RC32.build_src_filter}
|
||||
+<../examples/simple_room_server>
|
||||
lib_deps =
|
||||
${Heltec_RC32.lib_deps}
|
||||
${esp32_ota.lib_deps}
|
||||
|
||||
[env:heltec_rc32_without_display_companion_radio_usb]
|
||||
extends = Heltec_RC32
|
||||
build_flags =
|
||||
${Heltec_RC32.build_flags}
|
||||
-I examples/companion_radio/ui-new
|
||||
-D DISPLAY_CLASS=NullDisplayDriver
|
||||
-D MAX_CONTACTS=350
|
||||
-D MAX_GROUP_CHANNELS=40
|
||||
build_src_filter = ${Heltec_RC32.build_src_filter}
|
||||
+<helpers/ui/MomentaryButton.cpp>
|
||||
+<helpers/ui/buzzer.cpp>
|
||||
+<helpers/esp32/*.cpp>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
+<../examples/companion_radio/ui-new/*.cpp>
|
||||
lib_deps =
|
||||
${Heltec_RC32.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
end2endzone/NonBlockingRTTTL@^1.3.0
|
||||
|
||||
[env:heltec_rc32_without_display_companion_radio_ble]
|
||||
extends = Heltec_RC32
|
||||
build_flags =
|
||||
${Heltec_RC32.build_flags}
|
||||
-I examples/companion_radio/ui-new
|
||||
-D DISPLAY_CLASS=NullDisplayDriver
|
||||
-D MAX_CONTACTS=350
|
||||
-D MAX_GROUP_CHANNELS=40
|
||||
-D BLE_PIN_CODE=123456
|
||||
-D AUTO_SHUTDOWN_MILLIVOLTS=3400
|
||||
-D BLE_DEBUG_LOGGING=1
|
||||
-D OFFLINE_QUEUE_SIZE=256
|
||||
build_src_filter = ${Heltec_RC32.build_src_filter}
|
||||
+<helpers/ui/MomentaryButton.cpp>
|
||||
+<helpers/ui/buzzer.cpp>
|
||||
+<helpers/esp32/*.cpp>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
+<../examples/companion_radio/ui-new/*.cpp>
|
||||
lib_deps =
|
||||
${Heltec_RC32.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
end2endzone/NonBlockingRTTTL@^1.3.0
|
||||
|
||||
[env:heltec_rc32_without_display_companion_radio_wifi]
|
||||
extends = Heltec_RC32
|
||||
build_flags =
|
||||
${Heltec_RC32.build_flags}
|
||||
-I examples/companion_radio/ui-new
|
||||
-D DISPLAY_CLASS=NullDisplayDriver
|
||||
-D MAX_CONTACTS=350
|
||||
-D MAX_GROUP_CHANNELS=40
|
||||
-D WIFI_DEBUG_LOGGING=1
|
||||
-D WIFI_SSID='"myssid"'
|
||||
-D WIFI_PWD='"mypwd"'
|
||||
-D OFFLINE_QUEUE_SIZE=256
|
||||
build_src_filter = ${Heltec_RC32.build_src_filter}
|
||||
+<helpers/ui/MomentaryButton.cpp>
|
||||
+<helpers/ui/buzzer.cpp>
|
||||
+<helpers/esp32/*.cpp>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
+<../examples/companion_radio/ui-new/*.cpp>
|
||||
lib_deps =
|
||||
${Heltec_RC32.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
end2endzone/NonBlockingRTTTL@^1.3.0
|
||||
|
||||
[env:heltec_rc32_without_display_sensor]
|
||||
extends = Heltec_RC32
|
||||
build_flags =
|
||||
${Heltec_RC32.build_flags}
|
||||
-D ADVERT_NAME='"Heltec RC32 Sensor"'
|
||||
-D ADVERT_LAT=0.0
|
||||
-D ADVERT_LON=0.0
|
||||
-D ADMIN_PASSWORD='"password"'
|
||||
-D ENV_PIN_SDA=21
|
||||
-D ENV_PIN_SCL=18
|
||||
build_src_filter = ${Heltec_RC32.build_src_filter}
|
||||
+<../examples/simple_sensor>
|
||||
lib_deps =
|
||||
${Heltec_RC32.lib_deps}
|
||||
${esp32_ota.lib_deps}
|
||||
|
||||
[env:heltec_rc32_repeater]
|
||||
extends = Heltec_RC32_with_display
|
||||
build_flags =
|
||||
${Heltec_RC32_with_display.build_flags}
|
||||
-D ADVERT_NAME='"Heltec RC32 Repeater"'
|
||||
-D ADVERT_LAT=0.0
|
||||
-D ADVERT_LON=0.0
|
||||
-D ADMIN_PASSWORD='"password"'
|
||||
-D MAX_NEIGHBOURS=50
|
||||
build_src_filter = ${Heltec_RC32_with_display.build_src_filter}
|
||||
+<../examples/simple_repeater>
|
||||
lib_deps =
|
||||
${Heltec_RC32_with_display.lib_deps}
|
||||
${esp32_ota.lib_deps}
|
||||
bakercp/CRC32 @ ^2.0.0
|
||||
|
||||
[env:heltec_rc32_repeater_bridge_espnow]
|
||||
extends = Heltec_RC32_with_display
|
||||
build_flags =
|
||||
${Heltec_RC32_with_display.build_flags}
|
||||
-D ADVERT_NAME='"ESPNow Bridge"'
|
||||
-D ADVERT_LAT=0.0
|
||||
-D ADVERT_LON=0.0
|
||||
-D ADMIN_PASSWORD='"password"'
|
||||
-D MAX_NEIGHBOURS=50
|
||||
-D WITH_ESPNOW_BRIDGE=1
|
||||
build_src_filter = ${Heltec_RC32_with_display.build_src_filter}
|
||||
+<helpers/bridges/ESPNowBridge.cpp>
|
||||
+<../examples/simple_repeater>
|
||||
lib_deps =
|
||||
${Heltec_RC32_with_display.lib_deps}
|
||||
${esp32_ota.lib_deps}
|
||||
|
||||
[env:heltec_rc32_room_server]
|
||||
extends = Heltec_RC32_with_display
|
||||
build_flags =
|
||||
${Heltec_RC32_with_display.build_flags}
|
||||
-D ADVERT_NAME='"Heltec RC32 Room"'
|
||||
-D ADVERT_LAT=0.0
|
||||
-D ADVERT_LON=0.0
|
||||
-D ADMIN_PASSWORD='"password"'
|
||||
-D ROOM_PASSWORD='"hello"'
|
||||
build_src_filter = ${Heltec_RC32_with_display.build_src_filter}
|
||||
+<../examples/simple_room_server>
|
||||
lib_deps =
|
||||
${Heltec_RC32_with_display.lib_deps}
|
||||
${esp32_ota.lib_deps}
|
||||
|
||||
[env:heltec_rc32_terminal_chat]
|
||||
extends = Heltec_RC32
|
||||
build_flags =
|
||||
${Heltec_RC32.build_flags}
|
||||
-D MAX_CONTACTS=350
|
||||
-D MAX_GROUP_CHANNELS=1
|
||||
build_src_filter = ${Heltec_RC32.build_src_filter}
|
||||
+<../examples/simple_secure_chat/main.cpp>
|
||||
lib_deps =
|
||||
${Heltec_RC32.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
|
||||
[env:heltec_rc32_companion_radio_usb]
|
||||
extends = Heltec_RC32_with_display
|
||||
build_flags =
|
||||
${Heltec_RC32_with_display.build_flags}
|
||||
-I examples/companion_radio/ui-new
|
||||
-D UI_HAS_ROTARY_INPUT
|
||||
-D MAX_CONTACTS=350
|
||||
-D MAX_GROUP_CHANNELS=40
|
||||
build_src_filter = ${Heltec_RC32_with_display.build_src_filter}
|
||||
+<helpers/ui/buzzer.cpp>
|
||||
+<helpers/esp32/*.cpp>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
+<../examples/companion_radio/ui-new/*.cpp>
|
||||
lib_deps =
|
||||
${Heltec_RC32_with_display.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
end2endzone/NonBlockingRTTTL@^1.3.0
|
||||
|
||||
[env:heltec_rc32_companion_radio_ble]
|
||||
extends = Heltec_RC32_with_display
|
||||
build_flags =
|
||||
${Heltec_RC32_with_display.build_flags}
|
||||
-I examples/companion_radio/ui-new
|
||||
-D UI_HAS_ROTARY_INPUT
|
||||
-D MAX_CONTACTS=350
|
||||
-D MAX_GROUP_CHANNELS=40
|
||||
-D BLE_PIN_CODE=123456
|
||||
-D AUTO_SHUTDOWN_MILLIVOLTS=3400
|
||||
-D BLE_DEBUG_LOGGING=1
|
||||
-D OFFLINE_QUEUE_SIZE=256
|
||||
build_src_filter = ${Heltec_RC32_with_display.build_src_filter}
|
||||
+<helpers/ui/buzzer.cpp>
|
||||
+<helpers/esp32/*.cpp>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
+<../examples/companion_radio/ui-new/*.cpp>
|
||||
lib_deps =
|
||||
${Heltec_RC32_with_display.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
end2endzone/NonBlockingRTTTL@^1.3.0
|
||||
|
||||
[env:heltec_rc32_companion_radio_wifi]
|
||||
extends = Heltec_RC32_with_display
|
||||
build_flags =
|
||||
${Heltec_RC32_with_display.build_flags}
|
||||
-I examples/companion_radio/ui-new
|
||||
-D UI_HAS_ROTARY_INPUT
|
||||
-D MAX_CONTACTS=350
|
||||
-D MAX_GROUP_CHANNELS=40
|
||||
-D WIFI_DEBUG_LOGGING=1
|
||||
-D WIFI_SSID='"myssid"'
|
||||
-D WIFI_PWD='"mypwd"'
|
||||
-D OFFLINE_QUEUE_SIZE=256
|
||||
build_src_filter = ${Heltec_RC32_with_display.build_src_filter}
|
||||
+<helpers/ui/buzzer.cpp>
|
||||
+<helpers/esp32/*.cpp>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
+<../examples/companion_radio/ui-new/*.cpp>
|
||||
lib_deps =
|
||||
${Heltec_RC32_with_display.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
end2endzone/NonBlockingRTTTL@^1.3.0
|
||||
|
||||
[env:heltec_rc32_sensor]
|
||||
extends = Heltec_RC32_with_display
|
||||
build_flags =
|
||||
${Heltec_RC32_with_display.build_flags}
|
||||
-D ADVERT_NAME='"Heltec RC32 Sensor"'
|
||||
-D ADVERT_LAT=0.0
|
||||
-D ADVERT_LON=0.0
|
||||
-D ADMIN_PASSWORD='"password"'
|
||||
-D ENV_PIN_SDA=21
|
||||
-D ENV_PIN_SCL=18
|
||||
build_src_filter = ${Heltec_RC32_with_display.build_src_filter}
|
||||
+<../examples/simple_sensor>
|
||||
lib_deps =
|
||||
${Heltec_RC32_with_display.lib_deps}
|
||||
${esp32_ota.lib_deps}
|
||||
|
||||
[env:heltec_rc32_kiss_modem]
|
||||
extends = Heltec_RC32
|
||||
build_src_filter = ${Heltec_RC32.build_src_filter}
|
||||
+<../examples/kiss_modem/>
|
||||
@@ -0,0 +1,52 @@
|
||||
#include <Arduino.h>
|
||||
#include "target.h"
|
||||
#if defined(UI_HAS_ROTARY_INPUT)
|
||||
#include "HeltecRC32RotaryInput.h"
|
||||
#endif
|
||||
|
||||
HeltecRC32Board board;
|
||||
|
||||
#if defined(P_LORA_SCLK)
|
||||
static SPIClass spi(FSPI);
|
||||
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi);
|
||||
#else
|
||||
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY);
|
||||
#endif
|
||||
|
||||
WRAPPER_CLASS radio_driver(radio, board);
|
||||
|
||||
ESP32RTCClock fallback_clock;
|
||||
AutoDiscoverRTCClock rtc_clock(fallback_clock);
|
||||
|
||||
#if ENV_INCLUDE_GPS
|
||||
#include <helpers/sensors/MicroNMEALocationProvider.h>
|
||||
MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock, PIN_GPS_RESET, PIN_GPS_EN);
|
||||
EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea);
|
||||
#else
|
||||
EnvironmentSensorManager sensors;
|
||||
#endif
|
||||
|
||||
#ifdef DISPLAY_CLASS
|
||||
DISPLAY_CLASS display;
|
||||
MomentaryButton user_btn(PIN_USER_BTN, 1000, true);
|
||||
#if defined(UI_HAS_ROTARY_INPUT)
|
||||
static HeltecRC32RotaryInput rotaryInputImpl(&board.periph_power);
|
||||
RotaryInput& rotary_input = rotaryInputImpl;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
bool radio_init() {
|
||||
fallback_clock.begin();
|
||||
rtc_clock.begin(Wire);
|
||||
|
||||
#if defined(P_LORA_SCLK)
|
||||
return radio.std_init(&spi);
|
||||
#else
|
||||
return radio.std_init();
|
||||
#endif
|
||||
}
|
||||
|
||||
mesh::LocalIdentity radio_new_identity() {
|
||||
RadioNoiseListener rng(radio);
|
||||
return mesh::LocalIdentity(&rng);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#define RADIOLIB_STATIC_ONLY 1
|
||||
#include <RadioLib.h>
|
||||
#include <HeltecRC32Board.h>
|
||||
#include <helpers/AutoDiscoverRTCClock.h>
|
||||
#include <helpers/radiolib/CustomSX1262Wrapper.h>
|
||||
#include <helpers/radiolib/RadioLibWrappers.h>
|
||||
#include <helpers/sensors/EnvironmentSensorManager.h>
|
||||
|
||||
#ifdef DISPLAY_CLASS
|
||||
#include <helpers/ui/MomentaryButton.h>
|
||||
#if defined(UI_HAS_ROTARY_INPUT)
|
||||
#include <helpers/ui/RotaryInput.h>
|
||||
#endif
|
||||
#ifdef HELTEC_RC32_WITH_DISPLAY
|
||||
#include <helpers/ui/NV3001BDisplay.h>
|
||||
#else
|
||||
#include <helpers/ui/NullDisplayDriver.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
extern HeltecRC32Board board;
|
||||
extern WRAPPER_CLASS radio_driver;
|
||||
extern AutoDiscoverRTCClock rtc_clock;
|
||||
extern EnvironmentSensorManager sensors;
|
||||
|
||||
#ifdef DISPLAY_CLASS
|
||||
extern DISPLAY_CLASS display;
|
||||
extern MomentaryButton user_btn;
|
||||
#if defined(UI_HAS_ROTARY_INPUT)
|
||||
extern RotaryInput& rotary_input;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
bool radio_init();
|
||||
mesh::LocalIdentity radio_new_identity();
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef _VARIANT_HELTEC_RC32_
|
||||
#define _VARIANT_HELTEC_RC32_
|
||||
|
||||
#define BUTTON_PIN 0
|
||||
|
||||
#define I2C_SCL 18
|
||||
#define I2C_SDA 21
|
||||
#define SENSOR_INT_PIN 42
|
||||
#define PERIPHERAL_WARMUP_MS 100
|
||||
|
||||
#define LORA_SCK 11
|
||||
#define LORA_MISO 13
|
||||
#define LORA_MOSI 12
|
||||
#define LORA_CS 10
|
||||
#define LORA_DIO0 RADIOLIB_NC
|
||||
#define LORA_DIO1 14
|
||||
#define LORA_RESET 9
|
||||
|
||||
#define SX126X_CS LORA_CS
|
||||
#define SX126X_DIO1 LORA_DIO1
|
||||
#define SX126X_BUSY 1
|
||||
#define SX126X_RESET LORA_RESET
|
||||
|
||||
#define BATTERY_PIN 7
|
||||
#define ADC_CHANNEL ADC_CHANNEL_6
|
||||
#define ADC_CTRL 15
|
||||
#define ADC_ATTENUATION ADC_ATTEN_DB_2_5
|
||||
|
||||
#endif
|
||||
@@ -23,7 +23,7 @@ build_src_filter = ${nrf52_base.build_src_filter}
|
||||
lib_deps =
|
||||
${nrf52_base.lib_deps}
|
||||
${sensor_base.lib_deps}
|
||||
adafruit/Adafruit ST7735 and ST7789 Library @ ^1.11.0
|
||||
bodmer/TFT_eSPI @ ^2.4.31
|
||||
debug_tool = jlink
|
||||
upload_protocol = nrfutil
|
||||
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
#include "HeltecV4R8Board.h"
|
||||
|
||||
void HeltecV4R8Board::begin() {
|
||||
ESP32Board::begin();
|
||||
|
||||
periph_power.begin();
|
||||
periph_power.claim(); // R8 VEXT also feeds the LoRa antenna boost rail.
|
||||
|
||||
loRaFEMControl.init();
|
||||
|
||||
#ifdef PIN_TOUCH_RST
|
||||
pinMode(PIN_TOUCH_RST, OUTPUT);
|
||||
digitalWrite(PIN_TOUCH_RST, HIGH);
|
||||
delay(10);
|
||||
digitalWrite(PIN_TOUCH_RST, LOW);
|
||||
delay(100);
|
||||
digitalWrite(PIN_TOUCH_RST, HIGH);
|
||||
#endif
|
||||
|
||||
esp_reset_reason_t reason = esp_reset_reason();
|
||||
if (reason == ESP_RST_DEEPSLEEP) {
|
||||
long wakeup_source = esp_sleep_get_ext1_wakeup_status();
|
||||
if (wakeup_source & (1 << P_LORA_DIO_1)) {
|
||||
startup_reason = BD_STARTUP_RX_PACKET;
|
||||
}
|
||||
|
||||
rtc_gpio_hold_dis((gpio_num_t)P_LORA_NSS);
|
||||
rtc_gpio_deinit((gpio_num_t)P_LORA_DIO_1);
|
||||
}
|
||||
}
|
||||
|
||||
void HeltecV4R8Board::onBeforeTransmit(void) {
|
||||
digitalWrite(P_LORA_TX_LED, HIGH);
|
||||
loRaFEMControl.setTxModeEnable();
|
||||
}
|
||||
|
||||
void HeltecV4R8Board::onAfterTransmit(void) {
|
||||
digitalWrite(P_LORA_TX_LED, LOW);
|
||||
loRaFEMControl.setRxModeEnable();
|
||||
}
|
||||
|
||||
void HeltecV4R8Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) {
|
||||
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
|
||||
|
||||
rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY);
|
||||
rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1);
|
||||
|
||||
rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS);
|
||||
loRaFEMControl.setRxModeEnableWhenMCUSleep();
|
||||
|
||||
if (pin_wake_btn < 0) {
|
||||
esp_sleep_enable_ext1_wakeup((1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH);
|
||||
} else {
|
||||
esp_sleep_enable_ext1_wakeup((1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH);
|
||||
}
|
||||
|
||||
if (secs > 0) {
|
||||
esp_sleep_enable_timer_wakeup(secs * 1000000);
|
||||
}
|
||||
|
||||
esp_deep_sleep_start();
|
||||
}
|
||||
|
||||
void HeltecV4R8Board::powerOff() {
|
||||
enterDeepSleep(0);
|
||||
}
|
||||
|
||||
uint16_t HeltecV4R8Board::getBattMilliVolts() {
|
||||
analogReadResolution(12);
|
||||
|
||||
uint32_t raw = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
raw += analogReadMilliVolts(PIN_VBAT_READ);
|
||||
}
|
||||
raw = raw / 8;
|
||||
|
||||
return (adc_mult * raw);
|
||||
}
|
||||
|
||||
const char* HeltecV4R8Board::getManufacturerName() const {
|
||||
#ifdef HELTEC_V4_R8_TFT
|
||||
return "Heltec V4 R8 TFT";
|
||||
#else
|
||||
return "Heltec V4 R8 OLED";
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <driver/rtc_io.h>
|
||||
#include <helpers/ESP32Board.h>
|
||||
#include <helpers/RefCountedDigitalPin.h>
|
||||
#include "LoRaFEMControl.h"
|
||||
|
||||
#ifndef ADC_MULTIPLIER
|
||||
#define ADC_MULTIPLIER (4.9f * 1.035f)
|
||||
#endif
|
||||
|
||||
class HeltecV4R8Board : public ESP32Board {
|
||||
protected:
|
||||
float adc_mult = ADC_MULTIPLIER;
|
||||
|
||||
public:
|
||||
RefCountedDigitalPin periph_power;
|
||||
LoRaFEMControl loRaFEMControl;
|
||||
|
||||
HeltecV4R8Board() : periph_power(PIN_VEXT_EN, PIN_VEXT_EN_ACTIVE) { }
|
||||
|
||||
void begin();
|
||||
void onBeforeTransmit(void) override;
|
||||
void onAfterTransmit(void) override;
|
||||
void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1);
|
||||
void powerOff() override;
|
||||
uint16_t getBattMilliVolts() override;
|
||||
bool setAdcMultiplier(float multiplier) override {
|
||||
if (multiplier == 0.0f) {
|
||||
adc_mult = ADC_MULTIPLIER;
|
||||
} else {
|
||||
adc_mult = multiplier;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
float getAdcMultiplier() const override { return adc_mult; }
|
||||
const char* getManufacturerName() const override;
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
#include "LoRaFEMControl.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <driver/rtc_io.h>
|
||||
#include <esp_sleep.h>
|
||||
|
||||
void LoRaFEMControl::init(void) {
|
||||
pinMode(P_LORA_PA_POWER, OUTPUT);
|
||||
digitalWrite(P_LORA_PA_POWER, HIGH);
|
||||
rtc_gpio_hold_dis((gpio_num_t)P_LORA_PA_POWER);
|
||||
|
||||
esp_reset_reason_t reason = esp_reset_reason();
|
||||
if (reason != ESP_RST_DEEPSLEEP) {
|
||||
delay(1);
|
||||
}
|
||||
|
||||
rtc_gpio_hold_dis((gpio_num_t)P_LORA_KCT8103L_PA_CSD);
|
||||
rtc_gpio_hold_dis((gpio_num_t)P_LORA_KCT8103L_PA_CTX);
|
||||
|
||||
pinMode(P_LORA_KCT8103L_PA_CSD, OUTPUT);
|
||||
digitalWrite(P_LORA_KCT8103L_PA_CSD, HIGH);
|
||||
pinMode(P_LORA_KCT8103L_PA_CTX, OUTPUT);
|
||||
digitalWrite(P_LORA_KCT8103L_PA_CTX, lna_enabled ? LOW : HIGH);
|
||||
}
|
||||
|
||||
void LoRaFEMControl::setSleepModeEnable(void) {
|
||||
digitalWrite(P_LORA_KCT8103L_PA_CSD, LOW);
|
||||
}
|
||||
|
||||
void LoRaFEMControl::setTxModeEnable(void) {
|
||||
digitalWrite(P_LORA_KCT8103L_PA_CSD, HIGH);
|
||||
digitalWrite(P_LORA_KCT8103L_PA_CTX, HIGH);
|
||||
}
|
||||
|
||||
void LoRaFEMControl::setRxModeEnable(void) {
|
||||
digitalWrite(P_LORA_KCT8103L_PA_CSD, HIGH);
|
||||
digitalWrite(P_LORA_KCT8103L_PA_CTX, lna_enabled ? LOW : HIGH);
|
||||
}
|
||||
|
||||
void LoRaFEMControl::setRxModeEnableWhenMCUSleep(void) {
|
||||
digitalWrite(P_LORA_PA_POWER, HIGH);
|
||||
rtc_gpio_hold_en((gpio_num_t)P_LORA_PA_POWER);
|
||||
|
||||
digitalWrite(P_LORA_KCT8103L_PA_CSD, HIGH);
|
||||
rtc_gpio_hold_en((gpio_num_t)P_LORA_KCT8103L_PA_CSD);
|
||||
digitalWrite(P_LORA_KCT8103L_PA_CTX, lna_enabled ? LOW : HIGH);
|
||||
rtc_gpio_hold_en((gpio_num_t)P_LORA_KCT8103L_PA_CTX);
|
||||
}
|
||||
|
||||
void LoRaFEMControl::setLNAEnable(bool enabled) {
|
||||
lna_enabled = enabled;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
typedef enum {
|
||||
KCT8103L_PA,
|
||||
OTHER_FEM_TYPES
|
||||
} LoRaFEMType;
|
||||
|
||||
class LoRaFEMControl {
|
||||
public:
|
||||
LoRaFEMControl() { }
|
||||
virtual ~LoRaFEMControl() { }
|
||||
void init(void);
|
||||
void setSleepModeEnable(void);
|
||||
void setTxModeEnable(void);
|
||||
void setRxModeEnable(void);
|
||||
void setRxModeEnableWhenMCUSleep(void);
|
||||
void setLNAEnable(bool enabled);
|
||||
bool isLnaCanControl(void) { return true; }
|
||||
void setLnaCanControl(bool can_control) { }
|
||||
LoRaFEMType getFEMType(void) const { return KCT8103L_PA; }
|
||||
|
||||
private:
|
||||
bool lna_enabled = false;
|
||||
};
|
||||
@@ -0,0 +1,56 @@
|
||||
#ifndef Pins_Arduino_h
|
||||
#define Pins_Arduino_h
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define USB_VID 0x303a
|
||||
#define USB_PID 0x1001
|
||||
|
||||
static const uint8_t TX = 43;
|
||||
static const uint8_t RX = 44;
|
||||
|
||||
static const uint8_t SDA = 17;
|
||||
static const uint8_t SCL = 18;
|
||||
|
||||
static const uint8_t SS = 8;
|
||||
static const uint8_t MOSI = 10;
|
||||
static const uint8_t MISO = 11;
|
||||
static const uint8_t SCK = 9;
|
||||
|
||||
static const uint8_t A0 = 1;
|
||||
static const uint8_t A1 = 2;
|
||||
static const uint8_t A2 = 3;
|
||||
static const uint8_t A3 = 4;
|
||||
static const uint8_t A4 = 5;
|
||||
static const uint8_t A5 = 6;
|
||||
static const uint8_t A6 = 7;
|
||||
static const uint8_t A7 = 8;
|
||||
static const uint8_t A8 = 9;
|
||||
static const uint8_t A9 = 10;
|
||||
static const uint8_t A10 = 11;
|
||||
static const uint8_t A11 = 12;
|
||||
static const uint8_t A12 = 13;
|
||||
static const uint8_t A13 = 14;
|
||||
static const uint8_t A14 = 15;
|
||||
static const uint8_t A15 = 16;
|
||||
static const uint8_t A16 = 17;
|
||||
static const uint8_t A17 = 18;
|
||||
static const uint8_t A18 = 19;
|
||||
static const uint8_t A19 = 20;
|
||||
|
||||
static const uint8_t T1 = 1;
|
||||
static const uint8_t T2 = 2;
|
||||
static const uint8_t T3 = 3;
|
||||
static const uint8_t T4 = 4;
|
||||
static const uint8_t T5 = 5;
|
||||
static const uint8_t T6 = 6;
|
||||
static const uint8_t T7 = 7;
|
||||
static const uint8_t T8 = 8;
|
||||
static const uint8_t T9 = 9;
|
||||
static const uint8_t T10 = 10;
|
||||
static const uint8_t T11 = 11;
|
||||
static const uint8_t T12 = 12;
|
||||
static const uint8_t T13 = 13;
|
||||
static const uint8_t T14 = 14;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,342 @@
|
||||
[Heltec_v4_r8]
|
||||
extends = esp32_base
|
||||
board = heltec_v4_r8
|
||||
build_flags =
|
||||
${esp32_base.build_flags}
|
||||
${sensor_base.build_flags}
|
||||
-I variants/heltec_v4_r8
|
||||
-D HELTEC_V4_R8
|
||||
-D USE_SX1262
|
||||
-D ESP32_CPU_FREQ=80
|
||||
-D RADIO_CLASS=CustomSX1262
|
||||
-D WRAPPER_CLASS=CustomSX1262Wrapper
|
||||
-D P_LORA_DIO_1=14
|
||||
-D P_LORA_NSS=8
|
||||
-D P_LORA_RESET=12
|
||||
-D P_LORA_BUSY=13
|
||||
-D P_LORA_SCLK=9
|
||||
-D P_LORA_MISO=11
|
||||
-D P_LORA_MOSI=10
|
||||
-D P_LORA_PA_POWER=7
|
||||
-D P_LORA_KCT8103L_PA_CSD=2
|
||||
-D P_LORA_KCT8103L_PA_CTX=5
|
||||
-D P_LORA_TX_LED=46
|
||||
-D PIN_USER_BTN=0
|
||||
-D PIN_VEXT_EN=40
|
||||
-D PIN_VEXT_EN_ACTIVE=LOW
|
||||
-D ADC_MULTIPLIER=5.0715f
|
||||
-D PIN_VBAT_READ=1
|
||||
-D LORA_TX_POWER=10
|
||||
-D MAX_LORA_TX_POWER=22
|
||||
-D SX126X_REGISTER_PATCH=1
|
||||
-D SX126X_DIO2_AS_RF_SWITCH=true
|
||||
-D SX126X_DIO3_TCXO_VOLTAGE=1.8
|
||||
-D SX126X_CURRENT_LIMIT=140
|
||||
-D SX126X_RX_BOOSTED_GAIN=1
|
||||
-D PIN_GPS_RX=38
|
||||
-D PIN_GPS_TX=39
|
||||
-D PIN_GPS_EN=42
|
||||
-D PIN_GPS_EN_ACTIVE=LOW
|
||||
-D ENV_INCLUDE_GPS=1
|
||||
build_src_filter = ${esp32_base.build_src_filter}
|
||||
+<../variants/heltec_v4_r8>
|
||||
+<helpers/sensors>
|
||||
lib_deps =
|
||||
${esp32_base.lib_deps}
|
||||
${sensor_base.lib_deps}
|
||||
|
||||
[heltec_v4_r8_oled]
|
||||
extends = Heltec_v4_r8
|
||||
build_flags =
|
||||
${Heltec_v4_r8.build_flags}
|
||||
-D HELTEC_V4_R8_OLED
|
||||
-D PIN_BOARD_SDA=17
|
||||
-D PIN_BOARD_SCL=18
|
||||
-D PIN_OLED_RESET=21
|
||||
build_src_filter = ${Heltec_v4_r8.build_src_filter}
|
||||
lib_deps = ${Heltec_v4_r8.lib_deps}
|
||||
|
||||
[heltec_v4_r8_tft]
|
||||
extends = Heltec_v4_r8
|
||||
build_flags =
|
||||
${Heltec_v4_r8.build_flags}
|
||||
-D HELTEC_V4_R8_TFT
|
||||
-D PIN_BOARD_SDA=17
|
||||
-D PIN_BOARD_SCL=18
|
||||
-D DISPLAY_SCALE_X=2.5
|
||||
-D DISPLAY_SCALE_Y=3.75
|
||||
-D PIN_TFT_RST=-1
|
||||
-D PIN_TFT_VDD_CTL=-1
|
||||
-D PIN_TFT_LEDA_CTL=44
|
||||
-D PIN_TFT_LEDA_CTL_ACTIVE=HIGH
|
||||
-D PIN_TFT_CS=47
|
||||
-D PIN_TFT_DC=48
|
||||
-D PIN_TFT_SCL=16
|
||||
-D PIN_TFT_SDA=15
|
||||
-D PIN_TFT_MISO=45
|
||||
-D PIN_BUZZER=4
|
||||
-D PIN_TOUCH_RST=21
|
||||
build_src_filter = ${Heltec_v4_r8.build_src_filter}
|
||||
+<helpers/ui/buzzer.cpp>
|
||||
lib_deps =
|
||||
${Heltec_v4_r8.lib_deps}
|
||||
adafruit/Adafruit ST7735 and ST7789 Library @ ^1.11.0
|
||||
end2endzone/NonBlockingRTTTL@^1.3.0
|
||||
|
||||
[env:heltec_v4_r8_repeater]
|
||||
extends = heltec_v4_r8_oled
|
||||
build_flags =
|
||||
${heltec_v4_r8_oled.build_flags}
|
||||
-D DISPLAY_CLASS=SSD1306Display
|
||||
-D ADVERT_NAME='"Heltec R8 Repeater"'
|
||||
-D ADVERT_LAT=0.0
|
||||
-D ADVERT_LON=0.0
|
||||
-D ADMIN_PASSWORD='"password"'
|
||||
-D MAX_NEIGHBOURS=50
|
||||
build_src_filter = ${heltec_v4_r8_oled.build_src_filter}
|
||||
+<helpers/ui/SSD1306Display.cpp>
|
||||
+<../examples/simple_repeater>
|
||||
lib_deps =
|
||||
${heltec_v4_r8_oled.lib_deps}
|
||||
${esp32_ota.lib_deps}
|
||||
bakercp/CRC32 @ ^2.0.0
|
||||
|
||||
[env:heltec_v4_r8_room_server]
|
||||
extends = heltec_v4_r8_oled
|
||||
build_flags =
|
||||
${heltec_v4_r8_oled.build_flags}
|
||||
-D DISPLAY_CLASS=SSD1306Display
|
||||
-D ADVERT_NAME='"Heltec R8 Room"'
|
||||
-D ADVERT_LAT=0.0
|
||||
-D ADVERT_LON=0.0
|
||||
-D ADMIN_PASSWORD='"password"'
|
||||
-D ROOM_PASSWORD='"hello"'
|
||||
build_src_filter = ${heltec_v4_r8_oled.build_src_filter}
|
||||
+<helpers/ui/SSD1306Display.cpp>
|
||||
+<../examples/simple_room_server>
|
||||
lib_deps =
|
||||
${heltec_v4_r8_oled.lib_deps}
|
||||
${esp32_ota.lib_deps}
|
||||
|
||||
[env:heltec_v4_r8_terminal_chat]
|
||||
extends = heltec_v4_r8_oled
|
||||
build_flags =
|
||||
${heltec_v4_r8_oled.build_flags}
|
||||
-D MAX_CONTACTS=350
|
||||
-D MAX_GROUP_CHANNELS=1
|
||||
build_src_filter = ${heltec_v4_r8_oled.build_src_filter}
|
||||
+<../examples/simple_secure_chat/main.cpp>
|
||||
lib_deps =
|
||||
${heltec_v4_r8_oled.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
|
||||
[env:heltec_v4_r8_companion_radio_usb]
|
||||
extends = heltec_v4_r8_oled
|
||||
build_flags =
|
||||
${heltec_v4_r8_oled.build_flags}
|
||||
-I examples/companion_radio/ui-new
|
||||
-D MAX_CONTACTS=350
|
||||
-D MAX_GROUP_CHANNELS=40
|
||||
-D DISPLAY_CLASS=SSD1306Display
|
||||
build_src_filter = ${heltec_v4_r8_oled.build_src_filter}
|
||||
+<helpers/ui/SSD1306Display.cpp>
|
||||
+<helpers/ui/MomentaryButton.cpp>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
+<../examples/companion_radio/ui-new/*.cpp>
|
||||
lib_deps =
|
||||
${heltec_v4_r8_oled.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
|
||||
[env:heltec_v4_r8_companion_radio_ble]
|
||||
extends = heltec_v4_r8_oled
|
||||
build_flags =
|
||||
${heltec_v4_r8_oled.build_flags}
|
||||
-I examples/companion_radio/ui-new
|
||||
-D MAX_CONTACTS=350
|
||||
-D MAX_GROUP_CHANNELS=40
|
||||
-D DISPLAY_CLASS=SSD1306Display
|
||||
-D BLE_PIN_CODE=123456
|
||||
-D AUTO_SHUTDOWN_MILLIVOLTS=3400
|
||||
-D BLE_DEBUG_LOGGING=1
|
||||
-D OFFLINE_QUEUE_SIZE=256
|
||||
build_src_filter = ${heltec_v4_r8_oled.build_src_filter}
|
||||
+<helpers/ui/SSD1306Display.cpp>
|
||||
+<helpers/ui/MomentaryButton.cpp>
|
||||
+<helpers/esp32/*.cpp>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
+<../examples/companion_radio/ui-new/*.cpp>
|
||||
lib_deps =
|
||||
${heltec_v4_r8_oled.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
|
||||
[env:heltec_v4_r8_companion_radio_wifi]
|
||||
extends = heltec_v4_r8_oled
|
||||
build_flags =
|
||||
${heltec_v4_r8_oled.build_flags}
|
||||
-I examples/companion_radio/ui-new
|
||||
-D MAX_CONTACTS=350
|
||||
-D MAX_GROUP_CHANNELS=40
|
||||
-D OFFLINE_QUEUE_SIZE=256
|
||||
-D DISPLAY_CLASS=SSD1306Display
|
||||
-D WIFI_DEBUG_LOGGING=1
|
||||
-D WIFI_SSID='"myssid"'
|
||||
-D WIFI_PWD='"mypwd"'
|
||||
build_src_filter = ${heltec_v4_r8_oled.build_src_filter}
|
||||
+<helpers/ui/SSD1306Display.cpp>
|
||||
+<helpers/ui/MomentaryButton.cpp>
|
||||
+<helpers/esp32/*.cpp>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
+<../examples/companion_radio/ui-new/*.cpp>
|
||||
lib_deps =
|
||||
${heltec_v4_r8_oled.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
|
||||
[env:heltec_v4_r8_sensor]
|
||||
extends = heltec_v4_r8_oled
|
||||
build_flags =
|
||||
${heltec_v4_r8_oled.build_flags}
|
||||
-D ADVERT_NAME='"Heltec R8 Sensor"'
|
||||
-D ADVERT_LAT=0.0
|
||||
-D ADVERT_LON=0.0
|
||||
-D ADMIN_PASSWORD='"password"'
|
||||
-D DISPLAY_CLASS=SSD1306Display
|
||||
build_src_filter = ${heltec_v4_r8_oled.build_src_filter}
|
||||
+<helpers/ui/SSD1306Display.cpp>
|
||||
+<../examples/simple_sensor>
|
||||
lib_deps =
|
||||
${heltec_v4_r8_oled.lib_deps}
|
||||
${esp32_ota.lib_deps}
|
||||
|
||||
[env:heltec_v4_r8_tft_repeater]
|
||||
extends = heltec_v4_r8_tft
|
||||
build_flags =
|
||||
${heltec_v4_r8_tft.build_flags}
|
||||
-D DISPLAY_CLASS=ST7789LCDDisplay
|
||||
-D ADVERT_NAME='"Heltec R8 Repeater"'
|
||||
-D ADVERT_LAT=0.0
|
||||
-D ADVERT_LON=0.0
|
||||
-D ADMIN_PASSWORD='"password"'
|
||||
-D MAX_NEIGHBOURS=50
|
||||
build_src_filter = ${heltec_v4_r8_tft.build_src_filter}
|
||||
+<helpers/ui/ST7789LCDDisplay.cpp>
|
||||
+<../examples/simple_repeater>
|
||||
lib_deps =
|
||||
${heltec_v4_r8_tft.lib_deps}
|
||||
${esp32_ota.lib_deps}
|
||||
bakercp/CRC32 @ ^2.0.0
|
||||
|
||||
[env:heltec_v4_r8_tft_room_server]
|
||||
extends = heltec_v4_r8_tft
|
||||
build_flags =
|
||||
${heltec_v4_r8_tft.build_flags}
|
||||
-D DISPLAY_CLASS=ST7789LCDDisplay
|
||||
-D ADVERT_NAME='"Heltec R8 Room"'
|
||||
-D ADVERT_LAT=0.0
|
||||
-D ADVERT_LON=0.0
|
||||
-D ADMIN_PASSWORD='"password"'
|
||||
-D ROOM_PASSWORD='"hello"'
|
||||
build_src_filter = ${heltec_v4_r8_tft.build_src_filter}
|
||||
+<helpers/ui/ST7789LCDDisplay.cpp>
|
||||
+<../examples/simple_room_server>
|
||||
lib_deps =
|
||||
${heltec_v4_r8_tft.lib_deps}
|
||||
${esp32_ota.lib_deps}
|
||||
|
||||
[env:heltec_v4_r8_tft_terminal_chat]
|
||||
extends = heltec_v4_r8_tft
|
||||
build_flags =
|
||||
${heltec_v4_r8_tft.build_flags}
|
||||
-D MAX_CONTACTS=350
|
||||
-D MAX_GROUP_CHANNELS=1
|
||||
build_src_filter = ${heltec_v4_r8_tft.build_src_filter}
|
||||
+<../examples/simple_secure_chat/main.cpp>
|
||||
lib_deps =
|
||||
${heltec_v4_r8_tft.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
|
||||
[env:heltec_v4_r8_tft_companion_radio_usb]
|
||||
extends = heltec_v4_r8_tft
|
||||
build_flags =
|
||||
${heltec_v4_r8_tft.build_flags}
|
||||
-I examples/companion_radio/ui-new
|
||||
-D MAX_CONTACTS=350
|
||||
-D MAX_GROUP_CHANNELS=40
|
||||
-D DISPLAY_CLASS=ST7789LCDDisplay
|
||||
build_src_filter = ${heltec_v4_r8_tft.build_src_filter}
|
||||
+<helpers/ui/ST7789LCDDisplay.cpp>
|
||||
+<helpers/ui/MomentaryButton.cpp>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
+<../examples/companion_radio/ui-new/*.cpp>
|
||||
lib_deps =
|
||||
${heltec_v4_r8_tft.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
|
||||
[env:heltec_v4_r8_tft_companion_radio_ble]
|
||||
extends = heltec_v4_r8_tft
|
||||
build_flags =
|
||||
${heltec_v4_r8_tft.build_flags}
|
||||
-I examples/companion_radio/ui-new
|
||||
-D DISPLAY_CLASS=ST7789LCDDisplay
|
||||
-D MAX_CONTACTS=350
|
||||
-D MAX_GROUP_CHANNELS=40
|
||||
-D BLE_PIN_CODE=123456
|
||||
-D AUTO_SHUTDOWN_MILLIVOLTS=3400
|
||||
-D BLE_DEBUG_LOGGING=1
|
||||
-D OFFLINE_QUEUE_SIZE=256
|
||||
build_src_filter = ${heltec_v4_r8_tft.build_src_filter}
|
||||
+<helpers/ui/ST7789LCDDisplay.cpp>
|
||||
+<helpers/ui/MomentaryButton.cpp>
|
||||
+<helpers/esp32/*.cpp>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
+<../examples/companion_radio/ui-new/*.cpp>
|
||||
lib_deps =
|
||||
${heltec_v4_r8_tft.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
|
||||
[env:heltec_v4_r8_tft_companion_radio_wifi]
|
||||
extends = heltec_v4_r8_tft
|
||||
build_flags =
|
||||
${heltec_v4_r8_tft.build_flags}
|
||||
-I examples/companion_radio/ui-new
|
||||
-D MAX_CONTACTS=350
|
||||
-D MAX_GROUP_CHANNELS=40
|
||||
-D OFFLINE_QUEUE_SIZE=256
|
||||
-D DISPLAY_CLASS=ST7789LCDDisplay
|
||||
-D WIFI_DEBUG_LOGGING=1
|
||||
-D WIFI_SSID='"myssid"'
|
||||
-D WIFI_PWD='"mypwd"'
|
||||
build_src_filter = ${heltec_v4_r8_tft.build_src_filter}
|
||||
+<helpers/ui/ST7789LCDDisplay.cpp>
|
||||
+<helpers/ui/MomentaryButton.cpp>
|
||||
+<helpers/esp32/*.cpp>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
+<../examples/companion_radio/ui-new/*.cpp>
|
||||
lib_deps =
|
||||
${heltec_v4_r8_tft.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
|
||||
[env:heltec_v4_r8_tft_sensor]
|
||||
extends = heltec_v4_r8_tft
|
||||
build_flags =
|
||||
${heltec_v4_r8_tft.build_flags}
|
||||
-D ADVERT_NAME='"Heltec R8 Sensor"'
|
||||
-D ADVERT_LAT=0.0
|
||||
-D ADVERT_LON=0.0
|
||||
-D ADMIN_PASSWORD='"password"'
|
||||
-D DISPLAY_CLASS=ST7789LCDDisplay
|
||||
build_src_filter = ${heltec_v4_r8_tft.build_src_filter}
|
||||
+<helpers/ui/ST7789LCDDisplay.cpp>
|
||||
+<../examples/simple_sensor>
|
||||
lib_deps =
|
||||
${heltec_v4_r8_tft.lib_deps}
|
||||
${esp32_ota.lib_deps}
|
||||
|
||||
[env:heltec_v4_r8_kiss_modem]
|
||||
extends = Heltec_v4_r8
|
||||
build_src_filter = ${Heltec_v4_r8.build_src_filter}
|
||||
+<../examples/kiss_modem/>
|
||||
|
||||
[env:heltec_v4_r8_tft_kiss_modem]
|
||||
extends = heltec_v4_r8_tft
|
||||
build_src_filter = ${heltec_v4_r8_tft.build_src_filter}
|
||||
+<../examples/kiss_modem/>
|
||||
@@ -0,0 +1,45 @@
|
||||
#include <Arduino.h>
|
||||
#include "target.h"
|
||||
|
||||
HeltecV4R8Board board;
|
||||
|
||||
#if defined(P_LORA_SCLK)
|
||||
static SPIClass spi;
|
||||
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi);
|
||||
#else
|
||||
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY);
|
||||
#endif
|
||||
|
||||
WRAPPER_CLASS radio_driver(radio, board);
|
||||
|
||||
ESP32RTCClock fallback_clock;
|
||||
AutoDiscoverRTCClock rtc_clock(fallback_clock);
|
||||
|
||||
#if ENV_INCLUDE_GPS
|
||||
#include <helpers/sensors/MicroNMEALocationProvider.h>
|
||||
MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock, GPS_RESET, GPS_EN, &board.periph_power);
|
||||
EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea);
|
||||
#else
|
||||
EnvironmentSensorManager sensors;
|
||||
#endif
|
||||
|
||||
#ifdef DISPLAY_CLASS
|
||||
DISPLAY_CLASS display(&board.periph_power);
|
||||
MomentaryButton user_btn(PIN_USER_BTN, 1000, true);
|
||||
#endif
|
||||
|
||||
bool radio_init() {
|
||||
fallback_clock.begin();
|
||||
rtc_clock.begin(Wire);
|
||||
|
||||
#if defined(P_LORA_SCLK)
|
||||
return radio.std_init(&spi);
|
||||
#else
|
||||
return radio.std_init();
|
||||
#endif
|
||||
}
|
||||
|
||||
mesh::LocalIdentity radio_new_identity() {
|
||||
RadioNoiseListener rng(radio);
|
||||
return mesh::LocalIdentity(&rng);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#define RADIOLIB_STATIC_ONLY 1
|
||||
#include <RadioLib.h>
|
||||
#include <HeltecV4R8Board.h>
|
||||
#include <helpers/AutoDiscoverRTCClock.h>
|
||||
#include <helpers/SensorManager.h>
|
||||
#include <helpers/radiolib/CustomSX1262Wrapper.h>
|
||||
#include <helpers/radiolib/RadioLibWrappers.h>
|
||||
#include <helpers/sensors/EnvironmentSensorManager.h>
|
||||
#ifdef DISPLAY_CLASS
|
||||
#ifdef HELTEC_V4_R8_OLED
|
||||
#include <helpers/ui/SSD1306Display.h>
|
||||
#elif defined(HELTEC_V4_R8_TFT)
|
||||
#include <helpers/ui/ST7789LCDDisplay.h>
|
||||
#endif
|
||||
#include <helpers/ui/MomentaryButton.h>
|
||||
#endif
|
||||
|
||||
extern HeltecV4R8Board board;
|
||||
extern WRAPPER_CLASS radio_driver;
|
||||
extern AutoDiscoverRTCClock rtc_clock;
|
||||
extern EnvironmentSensorManager sensors;
|
||||
|
||||
#ifdef DISPLAY_CLASS
|
||||
extern DISPLAY_CLASS display;
|
||||
extern MomentaryButton user_btn;
|
||||
#endif
|
||||
|
||||
bool radio_init();
|
||||
mesh::LocalIdentity radio_new_identity();
|
||||
@@ -47,9 +47,9 @@ public:
|
||||
return "LilyGo T-Impulse-Plus";
|
||||
}
|
||||
|
||||
void powerOff() override {
|
||||
void shutdownPeripherals() override {
|
||||
// power off system
|
||||
NRF52Board::powerOff();
|
||||
NRF52Board::shutdownPeripherals();
|
||||
|
||||
// turn off 3.3v
|
||||
digitalWrite(RT9080_EN, LOW);
|
||||
|
||||
@@ -23,8 +23,8 @@ public:
|
||||
return "LilyGo T-Echo";
|
||||
}
|
||||
|
||||
void powerOff() override {
|
||||
NRF52Board::powerOff();
|
||||
void shutdownPeripherals() override {
|
||||
NRF52Board::shutdownPeripherals();
|
||||
#ifdef LED_RED
|
||||
digitalWrite(LED_RED, HIGH);
|
||||
#endif
|
||||
|
||||
@@ -87,11 +87,11 @@ void TechoCardBoard::turnOffLeds() {
|
||||
}
|
||||
}
|
||||
|
||||
void TechoCardBoard::powerOff() {
|
||||
void TechoCardBoard::shutdownPeripherals() {
|
||||
nrf_gpio_cfg_sense_input(BUTTON_PIN, NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_SENSE_LOW);
|
||||
turnOffLeds();
|
||||
digitalWrite(PIN_PWR_EN, LOW);
|
||||
NRF52Board::powerOff();
|
||||
NRF52Board::shutdownPeripherals();
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -28,7 +28,7 @@ public:
|
||||
return "LilyGo T-Echo Card";
|
||||
}
|
||||
|
||||
void powerOff() override;
|
||||
void shutdownPeripherals() override;
|
||||
|
||||
void toggleTorch();
|
||||
void turnOffLeds();
|
||||
|
||||
@@ -21,8 +21,8 @@ public:
|
||||
return "LilyGo T-Echo Lite";
|
||||
}
|
||||
|
||||
void powerOff() override {
|
||||
NRF52Board::powerOff();
|
||||
void shutdownPeripherals() override {
|
||||
NRF52Board::shutdownPeripherals();
|
||||
|
||||
digitalWrite(PIN_VBAT_MEAS_EN, LOW);
|
||||
#ifdef LED_RED
|
||||
|
||||
@@ -1,8 +1,21 @@
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
#include "nrf_gpio.h"
|
||||
|
||||
#include "RAK4631Board.h"
|
||||
|
||||
#ifdef ETHERNET_ENABLED
|
||||
// Drive WB_IO2 HIGH as early as possible using direct register access.
|
||||
// WB_IO2 (P1.02, Arduino pin 34) controls the WisBlock slot power switch.
|
||||
// With POE through RAK13800, this must be enabled before the framework
|
||||
// initializes or the board will brownout from insufficient power delivery.
|
||||
// Priority 102 runs just after SystemInit.
|
||||
static void __attribute__((constructor(102))) rak4631_early_poe_power() {
|
||||
nrf_gpio_cfg_output(NRF_GPIO_PIN_MAP(1, 2)); // WB_IO2 = P1.02
|
||||
nrf_gpio_pin_set(NRF_GPIO_PIN_MAP(1, 2));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef NRF52_POWER_MANAGEMENT
|
||||
// Static configuration for power management
|
||||
// Values set in variant.h defines
|
||||
@@ -36,6 +49,7 @@ void RAK4631Board::begin() {
|
||||
pinMode(PIN_USER_BTN_ANA, INPUT_PULLUP);
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(PIN_BOARD_SDA) && defined(PIN_BOARD_SCL)
|
||||
Wire.setPins(PIN_BOARD_SDA, PIN_BOARD_SCL);
|
||||
#endif
|
||||
|
||||
@@ -54,6 +54,25 @@ build_src_filter = ${rak4631.build_src_filter}
|
||||
+<helpers/ui/SSD1306Display.cpp>
|
||||
+<../examples/simple_repeater>
|
||||
|
||||
[env:RAK_4631_repeater_ethernet]
|
||||
extends = rak4631
|
||||
build_flags =
|
||||
${rak4631.build_flags}
|
||||
-D DISPLAY_CLASS=SSD1306Display
|
||||
-D ADVERT_NAME='"RAK4631 Repeater"'
|
||||
-D ADVERT_LAT=0.0
|
||||
-D ADVERT_LON=0.0
|
||||
-D ADMIN_PASSWORD='"password"'
|
||||
-D MAX_NEIGHBOURS=50
|
||||
-D ETHERNET_ENABLED=1
|
||||
; -D MESH_DEBUG=1
|
||||
build_src_filter = ${rak4631.build_src_filter}
|
||||
+<helpers/ui/SSD1306Display.cpp>
|
||||
+<../examples/simple_repeater>
|
||||
lib_deps =
|
||||
${rak4631.lib_deps}
|
||||
https://github.com/RAKWireless/RAK13800-W5100S/archive/1.0.2.zip
|
||||
|
||||
[env:RAK_4631_repeater_bridge_rs232_serial1]
|
||||
extends = rak4631
|
||||
build_flags =
|
||||
@@ -116,6 +135,25 @@ build_src_filter = ${rak4631.build_src_filter}
|
||||
+<helpers/ui/SSD1306Display.cpp>
|
||||
+<../examples/simple_room_server>
|
||||
|
||||
[env:RAK_4631_room_server_ethernet]
|
||||
extends = rak4631
|
||||
build_flags =
|
||||
${rak4631.build_flags}
|
||||
-D DISPLAY_CLASS=SSD1306Display
|
||||
-D ADVERT_NAME='"RAK4631 Room Server"'
|
||||
-D ADVERT_LAT=0.0
|
||||
-D ADVERT_LON=0.0
|
||||
-D ADMIN_PASSWORD='"password"'
|
||||
-D ROOM_PASSWORD='"hello"'
|
||||
-D ETHERNET_ENABLED=1
|
||||
; -D MESH_DEBUG=1
|
||||
build_src_filter = ${rak4631.build_src_filter}
|
||||
+<helpers/ui/SSD1306Display.cpp>
|
||||
+<../examples/simple_room_server>
|
||||
lib_deps =
|
||||
${rak4631.lib_deps}
|
||||
https://github.com/RAKWireless/RAK13800-W5100S/archive/1.0.2.zip
|
||||
|
||||
[env:RAK_4631_companion_radio_usb]
|
||||
extends = rak4631
|
||||
board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld
|
||||
@@ -137,6 +175,35 @@ lib_deps =
|
||||
${rak4631.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
|
||||
|
||||
[env:RAK_4631_companion_radio_ethernet]
|
||||
extends = rak4631
|
||||
board_build.ldscript = boards/nrf52840_s140_v6.ld
|
||||
board_upload.maximum_size = 712704
|
||||
build_unflags =
|
||||
-D EXTRAFS=1
|
||||
build_flags =
|
||||
${rak4631.build_flags}
|
||||
-I examples/companion_radio/ui-new
|
||||
-D PIN_USER_BTN=9
|
||||
-D PIN_USER_BTN_ANA=31
|
||||
-D DISPLAY_CLASS=SSD1306Display
|
||||
-D MAX_CONTACTS=350
|
||||
-D MAX_GROUP_CHANNELS=40
|
||||
-D ETHERNET_ENABLED=1
|
||||
; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1
|
||||
; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1
|
||||
; -D ETHERNET_DEBUG_LOGGING=1
|
||||
build_src_filter = ${rak4631.build_src_filter}
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
+<../examples/companion_radio/ui-new/*.cpp>
|
||||
+<helpers/nrf52/SerialEthernetInterface.cpp>
|
||||
lib_deps =
|
||||
${rak4631.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
https://github.com/RAKWireless/RAK13800-W5100S/archive/1.0.2.zip
|
||||
|
||||
|
||||
[env:RAK_4631_companion_radio_ble]
|
||||
extends = rak4631
|
||||
board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld
|
||||
@@ -200,4 +267,4 @@ build_flags =
|
||||
build_src_filter = ${rak4631.build_src_filter}
|
||||
+<../examples/kiss_modem/>
|
||||
lib_deps =
|
||||
${rak4631.lib_deps}
|
||||
${rak4631.lib_deps}
|
||||
@@ -44,7 +44,7 @@ build_flags =
|
||||
-D ADVERT_LAT=0.0
|
||||
-D ADVERT_LON=0.0
|
||||
-D ADMIN_PASSWORD='"password"'
|
||||
-D MAX_NEIGHBOURS=8
|
||||
-D MAX_NEIGHBOURS=50
|
||||
; -D MESH_PACKET_LOGGING=1
|
||||
; -D MESH_DEBUG=1
|
||||
lib_deps =
|
||||
|
||||
@@ -47,7 +47,7 @@ public:
|
||||
|
||||
#ifdef PIN_USER_BTN
|
||||
// configure button press to wake up when in powered off state
|
||||
nrf_gpio_cfg_sense_input(digitalPinToInterrupt(g_ADigitalPinMap[PIN_USER_BTN]), NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_SENSE_LOW);
|
||||
nrf_gpio_cfg_sense_input(digitalPinToInterrupt(g_ADigitalPinMap[PIN_USER_BTN]), NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_SENSE_LOW);
|
||||
#endif
|
||||
|
||||
NRF52Board::powerOff();
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#ifdef DISPLAY_CLASS
|
||||
DISPLAY_CLASS display;
|
||||
MomentaryButton user_btn(PIN_USER_BTN, 1000, true, true);
|
||||
#endif
|
||||
|
||||
XiaoNrf52Board board;
|
||||
|
||||
@@ -11,7 +11,9 @@
|
||||
|
||||
#ifdef DISPLAY_CLASS
|
||||
#include <helpers/ui/NullDisplayDriver.h>
|
||||
#include <helpers/ui/MomentaryButton.h>
|
||||
extern DISPLAY_CLASS display;
|
||||
extern MomentaryButton user_btn;
|
||||
#endif
|
||||
|
||||
extern XiaoNrf52Board board;
|
||||
|
||||
Reference in New Issue
Block a user