feat: add MQTT repeater support, web config panel, and EastMesh-specific firmware workflows

Этот коммит содержится в:
Jared Dohrman
2026-04-09 15:00:53 +10:00
родитель 2ab5f5128f
Коммит 98fe78a379
27 изменённых файлов: 3412 добавлений и 103 удалений
+10 -15
Просмотреть файл
@@ -26,17 +26,15 @@ runs:
uv venv --allow-existing --seed uv venv --allow-existing --seed
echo "$GITHUB_WORKSPACE/.venv/bin" >> "$GITHUB_PATH" echo "$GITHUB_WORKSPACE/.venv/bin" >> "$GITHUB_PATH"
# Recommended EastMesh tag format: # Active release tag formats:
# room-server-eastmesh-v1.0.1 # companion-wifi-v1.2.3
# repeater-mqtt-eastmesh-v1.0.1
# with OFFICIAL_MESHCORE_VERSION=v1.2.3 configured as a GitHub variable. # with OFFICIAL_MESHCORE_VERSION=v1.2.3 configured as a GitHub variable.
# #
# This exports: # This exports:
# FIRMWARE_VERSION=v1.2.3 # companion-wifi-v1.2.3 -> FIRMWARE_VERSION=v1.2.3, RELEASE_VERSION=v1.2.3
# EASTMESH_VERSION=v1.0.1 # repeater-mqtt-eastmesh-v1.0.1 -> FIRMWARE_VERSION=v1.2.3, EASTMESH_VERSION=v1.0.1,
# RELEASE_VERSION=v1.2.3-eastmesh-v1.0.1 # RELEASE_VERSION=v1.2.3-eastmesh-v1.0.1
#
# Legacy tags like "room-server-v1.2.3" and
# "room-server-v1.2.3-eastmesh-v1.0.1" remain supported.
- name: Extract Version Info - name: Extract Version Info
shell: bash shell: bash
run: | run: |
@@ -56,17 +54,14 @@ runs:
EASTMESH_VERSION="" EASTMESH_VERSION=""
FIRMWARE_VERSION="$STATIC_OFFICIAL_VERSION" FIRMWARE_VERSION="$STATIC_OFFICIAL_VERSION"
if [[ "$GIT_TAG_NAME" =~ ^(companion|repeater|room-server)-eastmesh-(v[[:alnum:]._-]+)$ ]]; then if [[ "$GIT_TAG_NAME" =~ ^repeater-mqtt-eastmesh-(v[[:alnum:]._-]+)$ ]]; then
if [[ -z "$STATIC_OFFICIAL_VERSION" ]]; then if [[ -z "$STATIC_OFFICIAL_VERSION" ]]; then
echo "OFFICIAL_MESHCORE_VERSION must be set for EastMesh release tags" >&2 echo "OFFICIAL_MESHCORE_VERSION must be set for EastMesh release tags" >&2
exit 1 exit 1
fi fi
EASTMESH_VERSION="${BASH_REMATCH[2]}" EASTMESH_VERSION="${BASH_REMATCH[1]}"
elif [[ "$GIT_TAG_NAME" =~ ^(companion|repeater|room-server)-(.+)-eastmesh-(v[[:alnum:]._-]+)$ ]]; then elif [[ "$GIT_TAG_NAME" =~ ^companion-wifi-(v[[:alnum:]._-]+)$ ]]; then
FIRMWARE_VERSION="${BASH_REMATCH[2]}" FIRMWARE_VERSION="${BASH_REMATCH[1]}"
EASTMESH_VERSION="${BASH_REMATCH[3]}"
elif [[ "$GIT_TAG_NAME" =~ ^(companion|repeater|room-server)-(.+)$ ]]; then
FIRMWARE_VERSION="${BASH_REMATCH[2]}"
else else
echo "Unsupported release tag format: $GIT_TAG_NAME" >&2 echo "Unsupported release tag format: $GIT_TAG_NAME" >&2
exit 1 exit 1
@@ -1,4 +1,4 @@
name: Build Companion Firmwares name: Build Companion WiFi Firmwares
permissions: permissions:
contents: write contents: write
@@ -7,7 +7,7 @@ on:
workflow_dispatch: workflow_dispatch:
push: push:
tags: tags:
- 'companion-*' - 'companion-wifi-v*'
jobs: jobs:
@@ -24,19 +24,19 @@ jobs:
uses: ./.github/actions/setup-build-environment uses: ./.github/actions/setup-build-environment
- name: Build Firmwares - name: Build Firmwares
run: /usr/bin/env bash build.sh build-companion-firmwares run: /usr/bin/env bash build.sh build-companion-wifi-firmwares
- name: Upload Workflow Artifacts - name: Upload Workflow Artifacts
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v7
with: with:
name: companion-firmwares name: companion-wifi-firmwares
path: out path: out
- name: Create Release - name: Create Release
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v2
if: startsWith(github.ref, 'refs/tags/') if: startsWith(github.ref, 'refs/tags/')
with: with:
name: Companion Firmware ${{ env.RELEASE_VERSION }} name: Companion WiFi Firmware ${{ env.RELEASE_VERSION }}
body: "" body: ""
draft: true draft: true
files: out/* files: out/*
-42
Просмотреть файл
@@ -1,42 +0,0 @@
name: Build Repeater Firmwares
permissions:
contents: write
on:
workflow_dispatch:
push:
tags:
- 'repeater-*'
jobs:
build:
runs-on: ubuntu-latest
env:
OFFICIAL_MESHCORE_VERSION: ${{ vars.OFFICIAL_MESHCORE_VERSION }}
steps:
- name: Clone Repo
uses: actions/checkout@v6
- name: Setup Build Environment
uses: ./.github/actions/setup-build-environment
- name: Build Firmwares
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@v2
if: startsWith(github.ref, 'refs/tags/')
with:
name: Repeater Firmware ${{ env.RELEASE_VERSION }}
body: ""
draft: true
files: out/*
@@ -1,4 +1,4 @@
name: Build Room Server Firmwares name: Build Repeater MQTT Firmwares
permissions: permissions:
contents: write contents: write
@@ -7,7 +7,7 @@ on:
workflow_dispatch: workflow_dispatch:
push: push:
tags: tags:
- 'room-server-*' - 'repeater-mqtt-eastmesh-*'
jobs: jobs:
@@ -24,19 +24,19 @@ jobs:
uses: ./.github/actions/setup-build-environment uses: ./.github/actions/setup-build-environment
- name: Build Firmwares - name: Build Firmwares
run: /usr/bin/env bash build.sh build-room-server-firmwares run: /usr/bin/env bash build.sh build-repeater-mqtt-firmwares
- name: Upload Workflow Artifacts - name: Upload Workflow Artifacts
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v7
with: with:
name: room-server-firmwares name: repeater-mqtt-firmwares
path: out path: out
- name: Create Release - name: Create Release
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v2
if: startsWith(github.ref, 'refs/tags/') if: startsWith(github.ref, 'refs/tags/')
with: with:
name: Room Server Firmware ${{ env.RELEASE_VERSION }} name: Repeater MQTT Firmware ${{ env.RELEASE_VERSION }}
body: "" body: ""
draft: true draft: true
files: out/* files: out/*
+19 -29
Просмотреть файл
@@ -16,17 +16,16 @@ This value becomes the base `FIRMWARE_VERSION` used by the release workflows.
Push one or more of the following tag formats to trigger the matching firmware release workflow: Push one or more of the following tag formats to trigger the matching firmware release workflow:
- `companion-eastmesh-v1.0.1` - `companion-wifi-v1.2.3`
- `repeater-eastmesh-v1.0.1` - `repeater-mqtt-eastmesh-v1.0.1`
- `room-server-eastmesh-v1.0.1`
Replace `v1.0.1` with the EastMesh release version you want to publish. Use the upstream MeshCore version in `companion-wifi-v1.2.3`.
Use the EastMesh release version in `repeater-mqtt-eastmesh-v1.0.1`.
Each tag triggers a separate workflow: Each tag triggers a separate workflow:
- `companion-eastmesh-*` builds companion firmware - `companion-wifi-v*` builds companion WiFi firmware
- `repeater-eastmesh-*` builds repeater firmware - `repeater-mqtt-eastmesh-*` builds repeater MQTT firmware
- `room-server-eastmesh-*` builds room server firmware
You can push one, or more tags on the same commit, and they will all build separately. You can push one, or more tags on the same commit, and they will all build separately.
@@ -34,16 +33,18 @@ You can push one, or more tags on the same commit, and they will all build separ
During the GitHub Actions build: During the GitHub Actions build:
- `FIRMWARE_VERSION` comes from `OFFICIAL_MESHCORE_VERSION` - `companion-wifi` uses the version in the tag as `FIRMWARE_VERSION`
- `EASTMESH_VERSION` comes from the tag - `repeater-mqtt` uses `OFFICIAL_MESHCORE_VERSION` as `FIRMWARE_VERSION`
- `repeater-mqtt` uses the EastMesh version from the tag as `EASTMESH_VERSION`
The final firmware/artifact version string becomes: The resulting version string depends on the workflow:
- `v1.2.3-eastmesh-v1.0.1-<commit>` - `companion-wifi`: `v1.2.3-<commit>`
- `repeater-mqtt`: `v1.2.3-eastmesh-v1.0.1-<commit>`
Example: Example:
- tag: `room-server-eastmesh-v1.0.1` - tag: `repeater-mqtt-eastmesh-v1.0.1`
- repo variable: `OFFICIAL_MESHCORE_VERSION=v1.2.3` - repo variable: `OFFICIAL_MESHCORE_VERSION=v1.2.3`
- resulting build version: `v1.2.3-eastmesh-v1.0.1-abcdef` - resulting build version: `v1.2.3-eastmesh-v1.0.1-abcdef`
@@ -59,23 +60,12 @@ Example:
Example: Example:
```bash ```bash
git tag companion-eastmesh-v1.0.1 git tag companion-wifi-v1.2.3
git tag repeater-eastmesh-v1.0.1 git tag repeater-mqtt-eastmesh-v1.0.1
git tag room-server-eastmesh-v1.0.1 git push origin companion-wifi-v1.2.3 repeater-mqtt-eastmesh-v1.0.1
git push origin companion-eastmesh-v1.0.1 repeater-eastmesh-v1.0.1 room-server-eastmesh-v1.0.1
``` ```
## Legacy Tags ## Supported Tags
The workflows still accept older tag formats for compatibility: - `companion-wifi-v1.2.3`
- `repeater-mqtt-eastmesh-v1.0.1`
- `companion-v1.2.3`
- `repeater-v1.2.3`
- `room-server-v1.2.3`
- `room-server-v1.2.3-eastmesh-v1.0.1`
However, the recommended EastMesh release format is:
- `companion-eastmesh-v1.0.1`
- `repeater-eastmesh-v1.0.1`
- `room-server-eastmesh-v1.0.1`
+31 -3
Просмотреть файл
@@ -32,7 +32,9 @@ Commands:
build-firmwares: Build all firmwares for all targets. build-firmwares: Build all firmwares for all targets.
build-matching-firmwares <build-match-spec>: Build all firmwares for build targets containing the string given for <build-match-spec>. build-matching-firmwares <build-match-spec>: Build all firmwares for build targets containing the string given for <build-match-spec>.
build-companion-firmwares: Build all companion firmwares for all build targets. build-companion-firmwares: Build all companion firmwares for all build targets.
build-companion-wifi-firmwares: Build all companion WiFi firmwares for all build targets.
build-repeater-firmwares: Build all repeater firmwares for all build targets. build-repeater-firmwares: Build all repeater firmwares for all build targets.
build-repeater-mqtt-firmwares: Build all repeater MQTT firmwares for all build targets.
build-room-server-firmwares: Build all chat room server firmwares for all build targets. build-room-server-firmwares: Build all chat room server firmwares for all build targets.
Examples: Examples:
@@ -45,9 +47,15 @@ $ sh build.sh build-matching-firmwares <build-match-spec>
Build all companion firmwares Build all companion firmwares
$ sh build.sh build-companion-firmwares $ sh build.sh build-companion-firmwares
Build all companion WiFi firmwares
$ sh build.sh build-companion-wifi-firmwares
Build all repeater firmwares Build all repeater firmwares
$ sh build.sh build-repeater-firmwares $ sh build.sh build-repeater-firmwares
Build all repeater MQTT firmwares
$ sh build.sh build-repeater-mqtt-firmwares
Build all chat room server firmwares Build all chat room server firmwares
$ sh build.sh build-room-server-firmwares $ sh build.sh build-room-server-firmwares
@@ -155,20 +163,24 @@ build_firmware() {
exit 1 exit 1
fi fi
# set firmware version string # set artifact version string
# e.g: v1.2.3-eastmesh-v1.0.1-abcdef # e.g: v1.2.3-eastmesh-v1.0.1-abcdef
FIRMWARE_VERSION_BASE="${FIRMWARE_VERSION}" FIRMWARE_VERSION_BASE="${FIRMWARE_VERSION}"
if [ -n "$EASTMESH_VERSION" ]; then if [ -n "$EASTMESH_VERSION" ]; then
FIRMWARE_VERSION_BASE="${FIRMWARE_VERSION_BASE}-eastmesh-${EASTMESH_VERSION}" FIRMWARE_VERSION_BASE="${FIRMWARE_VERSION_BASE}-eastmesh-${EASTMESH_VERSION}"
fi fi
FIRMWARE_VERSION_STRING="${FIRMWARE_VERSION_BASE}-${COMMIT_HASH}" FIRMWARE_VERSION_STRING="${FIRMWARE_VERSION_BASE}-${COMMIT_HASH}"
CLIENT_VERSION_STRING="eastmesh-${COMMIT_HASH}"
if [ -n "$EASTMESH_VERSION" ]; then
CLIENT_VERSION_STRING="eastmesh-${EASTMESH_VERSION}-${COMMIT_HASH}"
fi
# craft filename # craft filename
# e.g: RAK_4631_Repeater-v1.0.0-SHA # e.g: RAK_4631_Repeater-v1.0.0-SHA
FIRMWARE_FILENAME="$1-${FIRMWARE_VERSION_STRING}" FIRMWARE_FILENAME="$1-${FIRMWARE_VERSION_STRING}"
# add firmware version info to end of existing platformio build flags in environment vars # add build metadata to end of existing platformio build flags in environment vars
export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -DFIRMWARE_BUILD_DATE='\"${FIRMWARE_BUILD_DATE}\"' -DFIRMWARE_VERSION='\"${FIRMWARE_VERSION_STRING}\"'" export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -DFIRMWARE_BUILD_DATE='\"${FIRMWARE_BUILD_DATE}\"' -DFIRMWARE_VERSION='\"${FIRMWARE_VERSION}\"' -DCLIENT_VERSION='\"${CLIENT_VERSION_STRING}\"'"
# disable debug flags if requested # disable debug flags if requested
disable_debug_flags disable_debug_flags
@@ -255,6 +267,18 @@ build_companion_firmwares() {
} }
build_companion_wifi_firmwares() {
build_all_firmwares_by_suffix "_companion_radio_wifi"
}
build_repeater_mqtt_firmwares() {
build_all_firmwares_by_suffix "_repeater_mqtt"
}
build_room_server_firmwares() { build_room_server_firmwares() {
# # build specific room server firmwares # # build specific room server firmwares
@@ -298,8 +322,12 @@ elif [[ $1 == "build-firmwares" ]]; then
build_firmwares build_firmwares
elif [[ $1 == "build-companion-firmwares" ]]; then elif [[ $1 == "build-companion-firmwares" ]]; then
build_companion_firmwares build_companion_firmwares
elif [[ $1 == "build-companion-wifi-firmwares" ]]; then
build_companion_wifi_firmwares
elif [[ $1 == "build-repeater-firmwares" ]]; then elif [[ $1 == "build-repeater-firmwares" ]]; then
build_repeater_firmwares build_repeater_firmwares
elif [[ $1 == "build-repeater-mqtt-firmwares" ]]; then
build_repeater_mqtt_firmwares
elif [[ $1 == "build-room-server-firmwares" ]]; then elif [[ $1 == "build-room-server-firmwares" ]]; then
build_room_server_firmwares build_room_server_firmwares
fi fi
+229
Просмотреть файл
@@ -461,6 +461,9 @@ void MyMesh::logRx(mesh::Packet *pkt, int len, float score) {
bridge.sendPacket(pkt); bridge.sendPacket(pkt);
} }
#endif #endif
#ifdef WITH_MQTT_UPLINK
mqtt.publishPacket(*pkt, false, (int)_radio->getLastRSSI(), _radio->getLastSNR());
#endif
if (_logging) { if (_logging) {
File f = openAppend(PACKET_LOG_FILE); File f = openAppend(PACKET_LOG_FILE);
@@ -487,6 +490,9 @@ void MyMesh::logTx(mesh::Packet *pkt, int len) {
bridge.sendPacket(pkt); bridge.sendPacket(pkt);
} }
#endif #endif
#ifdef WITH_MQTT_UPLINK
mqtt.publishPacket(*pkt, true, (int)_radio->getLastRSSI(), _radio->getLastSNR());
#endif
if (_logging) { if (_logging) {
File f = openAppend(PACKET_LOG_FILE); File f = openAppend(PACKET_LOG_FILE);
@@ -840,6 +846,9 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
#if defined(WITH_ESPNOW_BRIDGE) #if defined(WITH_ESPNOW_BRIDGE)
, bridge(&_prefs, _mgr, &rtc) , bridge(&_prefs, _mgr, &rtc)
#endif #endif
#if defined(WITH_MQTT_UPLINK)
, mqtt(rtc, self_id)
#endif
{ {
last_millis = 0; last_millis = 0;
uptime_millis = 0; uptime_millis = 0;
@@ -915,6 +924,12 @@ void MyMesh::begin(FILESYSTEM *fs) {
bridge.begin(); bridge.begin();
} }
#endif #endif
#if defined(WITH_MQTT_UPLINK)
board.setInhibitSleep(true);
mqtt.setNodeNameSource(_prefs.node_name);
mqtt.setWebCommandRunner(this);
mqtt.begin(_fs);
#endif
radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr);
radio_set_tx_power(_prefs.tx_power_dbm); radio_set_tx_power(_prefs.tx_power_dbm);
@@ -1079,6 +1094,10 @@ void MyMesh::formatPacketStatsReply(char *reply) {
getNumRecvFlood(), getNumRecvDirect()); getNumRecvFlood(), getNumRecvDirect());
} }
void MyMesh::formatMemoryReply(char *reply, size_t reply_size) {
StatsFormatHelper::formatMemoryStats(reply, reply_size);
}
void MyMesh::saveIdentity(const mesh::LocalIdentity &new_id) { void MyMesh::saveIdentity(const mesh::LocalIdentity &new_id) {
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
IdentityStore store(*_fs, ""); IdentityStore store(*_fs, "");
@@ -1285,11 +1304,201 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply
sendNodeDiscoverReq(); sendNodeDiscoverReq();
strcpy(reply, "OK - Discover sent"); strcpy(reply, "OK - Discover sent");
} }
#ifdef WITH_MQTT_UPLINK
} else if (memcmp(command, "mqtt.owner ", 11) == 0) {
if (mqtt.setOwnerPublicKey(&command[11])) {
strcpy(reply, "OK");
} else {
strcpy(reply, "Err - owner must be 64 hex chars");
}
} else if (memcmp(command, "mqtt.email ", 11) == 0) {
if (mqtt.setOwnerEmail(&command[11])) {
strcpy(reply, "OK");
} else {
strcpy(reply, "Err - bad mqtt.email");
}
} else if (memcmp(command, "get mqtt.status", 15) == 0) {
mqtt.formatStatusReply(reply, 160);
} else if (strcmp(command, "get web.status") == 0 || strcmp(command, "get web") == 0) {
mqtt.formatWebStatusReply(reply, 160);
} else if (memcmp(command, "get wifi.status", 15) == 0) {
mqtt.formatWifiStatusReply(reply, 160);
} else if (memcmp(command, "get wifi.ssid", 13) == 0) {
sprintf(reply, "> %s", mqtt.getWifiSSID()[0] ? mqtt.getWifiSSID() : "-");
} else if (memcmp(command, "get wifi.powersaving", 20) == 0) {
sprintf(reply, "> %s", mqtt.getWifiPowerSave());
} else if (memcmp(command, "get mqtt.iata", 13) == 0) {
sprintf(reply, "> %s", mqtt.getIata());
} else if (memcmp(command, "get mqtt.owner", 14) == 0) {
sprintf(reply, "> %s", mqtt.getOwnerPublicKey()[0] ? mqtt.getOwnerPublicKey() : "-");
} else if (memcmp(command, "get mqtt.email", 14) == 0) {
sprintf(reply, "> %s", mqtt.getOwnerEmail()[0] ? mqtt.getOwnerEmail() : "-");
} else if (memcmp(command, "get mqtt.packets", 16) == 0) {
sprintf(reply, "> %s", mqtt.isPacketsEnabled() ? "on" : "off");
} else if (memcmp(command, "get mqtt.raw", 12) == 0) {
sprintf(reply, "> %s", mqtt.isRawEnabled() ? "on" : "off");
} else if (memcmp(command, "get mqtt.tx", 11) == 0) {
sprintf(reply, "> %s", mqtt.isTxEnabled() ? "on" : "off");
} else if (memcmp(command, "get mqtt.eastmesh-au", 20) == 0 || memcmp(command, "get mqtt.eastmesh.au", 20) == 0) {
sprintf(reply, "> %s", mqtt.isEndpointEnabled(0x01) ? "on" : "off");
} else if (memcmp(command, "get mqtt.letsmesh-eu", 21) == 0 || memcmp(command, "get mqtt.letsmesh.eu", 21) == 0) {
sprintf(reply, "> %s", mqtt.isEndpointEnabled(0x02) ? "on" : "off");
} else if (memcmp(command, "get mqtt.letsmesh-us", 21) == 0 || memcmp(command, "get mqtt.letsmesh.us", 21) == 0) {
sprintf(reply, "> %s", mqtt.isEndpointEnabled(0x04) ? "on" : "off");
} else if (memcmp(command, "set mqtt.tx ", 12) == 0) {
mqtt.setTxEnabled(memcmp(&command[12], "on", 2) == 0);
strcpy(reply, "OK");
} else if (memcmp(command, "set web ", 8) == 0) {
mqtt.setWebEnabled(memcmp(&command[8], "on", 2) == 0);
strcpy(reply, "OK");
} else if (memcmp(command, "set.web ", 8) == 0) {
mqtt.setWebEnabled(memcmp(&command[8], "on", 2) == 0);
strcpy(reply, "OK");
} else if (memcmp(command, "set wifi.ssid ", 14) == 0) {
if (mqtt.setWifiSSID(&command[14])) {
strcpy(reply, "OK");
} else {
strcpy(reply, "Err - bad wifi.ssid");
}
} else if (memcmp(command, "set wifi.pwd ", 13) == 0) {
if (mqtt.setWifiPassword(&command[13])) {
strcpy(reply, "OK");
} else {
strcpy(reply, "Err - bad wifi.pwd");
}
} else if (memcmp(command, "set wifi.powersaving ", 21) == 0) {
if (mqtt.setWifiPowerSave(&command[21])) {
strcpy(reply, "OK");
} else {
strcpy(reply, "Err - use none|min|max");
}
} else if (memcmp(command, "set mqtt.iata ", 14) == 0) {
if (mqtt.setIata(&command[14])) {
strcpy(reply, "OK");
} else {
strcpy(reply, "Err - bad mqtt.iata");
}
} else if (memcmp(command, "set mqtt.owner ", 15) == 0) {
if (mqtt.setOwnerPublicKey(&command[15])) {
strcpy(reply, "OK");
} else {
strcpy(reply, "Err - owner must be 64 hex chars");
}
} else if (memcmp(command, "set mqtt.email ", 15) == 0) {
if (mqtt.setOwnerEmail(&command[15])) {
strcpy(reply, "OK");
} else {
strcpy(reply, "Err - bad mqtt.email");
}
} else if (memcmp(command, "set mqtt.packets ", 17) == 0) {
mqtt.setPacketsEnabled(memcmp(&command[17], "on", 2) == 0);
strcpy(reply, "OK");
} else if (memcmp(command, "set mqtt.raw ", 13) == 0) {
mqtt.setRawEnabled(memcmp(&command[13], "on", 2) == 0);
strcpy(reply, "OK");
} else if (memcmp(command, "set mqtt.eastmesh-au ", 21) == 0 || memcmp(command, "set mqtt.eastmesh.au ", 21) == 0) {
mqtt.setEndpointEnabled(0x01, memcmp(&command[21], "on", 2) == 0);
strcpy(reply, "OK");
} else if (memcmp(command, "set mqtt.letsmesh-eu ", 21) == 0 || memcmp(command, "set mqtt.letsmesh.eu ", 21) == 0) {
mqtt.setEndpointEnabled(0x02, memcmp(&command[21], "on", 2) == 0);
strcpy(reply, "OK");
} else if (memcmp(command, "set mqtt.letsmesh-us ", 21) == 0 || memcmp(command, "set mqtt.letsmesh.us ", 21) == 0) {
mqtt.setEndpointEnabled(0x04, memcmp(&command[21], "on", 2) == 0);
strcpy(reply, "OK");
#endif
} else{ } else{
_cli.handleCommand(sender_timestamp, command, reply); // common CLI commands _cli.handleCommand(sender_timestamp, command, reply); // common CLI commands
} }
} }
void MyMesh::runWebCommand(const char* command, char* reply, size_t reply_size) {
if (reply_size == 0) {
return;
}
reply[0] = 0;
if (command == nullptr) {
strncpy(reply, "Err - empty command", reply_size - 1);
reply[reply_size - 1] = 0;
return;
}
auto matches_exact = [command](const char* candidate) -> bool {
return strcmp(command, candidate) == 0;
};
auto matches_prefix = [command](const char* candidate) -> bool {
size_t len = strlen(candidate);
return strncmp(command, candidate, len) == 0;
};
bool allowed =
matches_exact("get mqtt.status") ||
matches_exact("get web.status") ||
matches_exact("get web") ||
matches_exact("advert") ||
matches_exact("reboot") ||
matches_exact("start ota") ||
matches_exact("get wifi.status") ||
matches_exact("get wifi.powersaving") ||
matches_exact("memory") ||
matches_exact("get mqtt.iata") ||
matches_exact("get mqtt.owner") ||
matches_exact("get mqtt.email") ||
matches_exact("get mqtt.packets") ||
matches_exact("get mqtt.raw") ||
matches_exact("get mqtt.tx") ||
matches_exact("get mqtt.eastmesh-au") ||
matches_exact("get mqtt.eastmesh.au") ||
matches_exact("get mqtt.letsmesh-eu") ||
matches_exact("get mqtt.letsmesh.eu") ||
matches_exact("get mqtt.letsmesh-us") ||
matches_exact("get mqtt.letsmesh.us") ||
matches_exact("get name") ||
matches_exact("get lat") ||
matches_exact("get lon") ||
matches_exact("get advert.interval") ||
matches_exact("get flood.advert.interval") ||
matches_exact("get flood.max") ||
matches_exact("get owner.info") ||
matches_exact("get guest.password") ||
matches_prefix("set wifi.ssid ") ||
matches_prefix("set wifi.pwd ") ||
matches_prefix("set wifi.powersaving ") ||
matches_prefix("set mqtt.iata ") ||
matches_prefix("set mqtt.owner ") ||
matches_prefix("set mqtt.email ") ||
matches_prefix("set mqtt.packets ") ||
matches_prefix("set mqtt.raw ") ||
matches_prefix("set mqtt.tx ") ||
matches_prefix("set web ") ||
matches_prefix("set.web ") ||
matches_prefix("set mqtt.eastmesh-au ") ||
matches_prefix("set mqtt.eastmesh.au ") ||
matches_prefix("set mqtt.letsmesh-eu ") ||
matches_prefix("set mqtt.letsmesh.eu ") ||
matches_prefix("set mqtt.letsmesh-us ") ||
matches_prefix("set mqtt.letsmesh.us ") ||
matches_prefix("set name ") ||
matches_prefix("set lat ") ||
matches_prefix("set lon ") ||
matches_prefix("set guest.password ") ||
matches_prefix("set prv.key ") ||
matches_prefix("set advert.interval ") ||
matches_prefix("set flood.advert.interval ") ||
matches_prefix("set flood.max ") ||
matches_prefix("set owner.info ");
if (!allowed) {
strncpy(reply, "Err - command not allowlisted for web access", reply_size - 1);
reply[reply_size - 1] = 0;
return;
}
char command_buf[192];
StrHelper::strncpy(command_buf, command, sizeof(command_buf));
handleCommand(0, command_buf, reply);
reply[reply_size - 1] = 0;
}
void MyMesh::loop() { void MyMesh::loop() {
#ifdef WITH_BRIDGE #ifdef WITH_BRIDGE
bridge.loop(); bridge.loop();
@@ -1329,6 +1538,23 @@ void MyMesh::loop() {
dirty_contacts_expiry = 0; dirty_contacts_expiry = 0;
} }
#ifdef WITH_MQTT_UPLINK
MQTTStatusSnapshot mqtt_status{};
mqtt_status.battery_mv = static_cast<int>(board.getBattMilliVolts());
mqtt_status.uptime_secs = static_cast<uint32_t>(uptime_millis / 1000);
mqtt_status.error_flags = _err_flags;
mqtt_status.queue_len = static_cast<uint16_t>(_mgr->getOutboundTotal());
mqtt_status.noise_floor = static_cast<int>(_radio->getNoiseFloor());
mqtt_status.tx_air_secs = static_cast<uint32_t>(getTotalAirTime() / 1000);
mqtt_status.rx_air_secs = static_cast<uint32_t>(getReceiveAirTime() / 1000);
mqtt_status.recv_errors = radio_driver.getPacketsRecvErrors();
mqtt_status.radio_freq = _prefs.freq;
mqtt_status.radio_bw = _prefs.bw;
mqtt_status.radio_sf = _prefs.sf;
mqtt_status.radio_cr = _prefs.cr;
mqtt.loop(mqtt_status);
#endif
// update uptime // update uptime
uint32_t now = millis(); uint32_t now = millis();
uptime_millis += now - last_millis; uptime_millis += now - last_millis;
@@ -1339,6 +1565,9 @@ void MyMesh::loop() {
bool MyMesh::hasPendingWork() const { bool MyMesh::hasPendingWork() const {
#if defined(WITH_BRIDGE) #if defined(WITH_BRIDGE)
if (bridge.isRunning()) return true; // bridge needs WiFi radio, can't sleep if (bridge.isRunning()) return true; // bridge needs WiFi radio, can't sleep
#endif
#if defined(WITH_MQTT_UPLINK)
if (mqtt.isActive()) return true;
#endif #endif
return _mgr->getOutboundTotal() > 0; return _mgr->getOutboundTotal() > 0;
} }
+11 -1
Просмотреть файл
@@ -23,6 +23,10 @@
#define WITH_BRIDGE #define WITH_BRIDGE
#endif #endif
#ifdef WITH_MQTT_UPLINK
#include <helpers/mqtt/MQTTUplink.h>
#endif
#include <helpers/AdvertDataHelpers.h> #include <helpers/AdvertDataHelpers.h>
#include <helpers/ArduinoHelpers.h> #include <helpers/ArduinoHelpers.h>
#include <helpers/ClientACL.h> #include <helpers/ClientACL.h>
@@ -80,7 +84,7 @@ struct NeighbourInfo {
#define PACKET_LOG_FILE "/packet_log" #define PACKET_LOG_FILE "/packet_log"
class MyMesh : public mesh::Mesh, public CommonCLICallbacks { class MyMesh : public mesh::Mesh, public CommonCLICallbacks, public MQTTWebCommandRunner {
FILESYSTEM* _fs; FILESYSTEM* _fs;
uint32_t last_millis; uint32_t last_millis;
uint64_t uptime_millis; uint64_t uptime_millis;
@@ -117,6 +121,9 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
#elif defined(WITH_ESPNOW_BRIDGE) #elif defined(WITH_ESPNOW_BRIDGE)
ESPNowBridge bridge; ESPNowBridge bridge;
#endif #endif
#ifdef WITH_MQTT_UPLINK
MQTTUplink mqtt;
#endif
void putNeighbour(const mesh::Identity& id, uint32_t timestamp, float snr); void putNeighbour(const mesh::Identity& id, uint32_t timestamp, float snr);
uint8_t handleLoginReq(const mesh::Identity& sender, const uint8_t* secret, uint32_t sender_timestamp, const uint8_t* data, bool is_flood); uint8_t handleLoginReq(const mesh::Identity& sender, const uint8_t* secret, uint32_t sender_timestamp, const uint8_t* data, bool is_flood);
@@ -208,12 +215,15 @@ public:
void formatStatsReply(char *reply) override; void formatStatsReply(char *reply) override;
void formatRadioStatsReply(char *reply) override; void formatRadioStatsReply(char *reply) override;
void formatPacketStatsReply(char *reply) override; void formatPacketStatsReply(char *reply) override;
void formatMemoryReply(char *reply, size_t reply_size) override;
mesh::LocalIdentity& getSelfId() override { return self_id; } mesh::LocalIdentity& getSelfId() override { return self_id; }
void saveIdentity(const mesh::LocalIdentity& new_id) override; void saveIdentity(const mesh::LocalIdentity& new_id) override;
void clearStats() override; void clearStats() override;
void handleCommand(uint32_t sender_timestamp, char* command, char* reply); void handleCommand(uint32_t sender_timestamp, char* command, char* reply);
void runWebCommand(const char* command, char* reply, size_t reply_size) override;
const char* getWebAdminPassword() const override { return _prefs.password; }
void loop(); void loop();
#if defined(WITH_BRIDGE) #if defined(WITH_BRIDGE)
+11
Просмотреть файл
@@ -1,5 +1,6 @@
#include "UITask.h" #include "UITask.h"
#include <Arduino.h> #include <Arduino.h>
#include <WiFi.h>
#include <helpers/CommonCLI.h> #include <helpers/CommonCLI.h>
#define AUTO_OFF_MILLIS 20000 // 20 seconds #define AUTO_OFF_MILLIS 20000 // 20 seconds
@@ -77,6 +78,16 @@ void UITask::renderCurrScreen() {
_display->setCursor(0, 30); _display->setCursor(0, 30);
sprintf(tmp, "BW: %03.2f CR: %d", _node_prefs->bw, _node_prefs->cr); sprintf(tmp, "BW: %03.2f CR: %d", _node_prefs->bw, _node_prefs->cr);
_display->print(tmp); _display->print(tmp);
// WiFi IP
_display->setCursor(0, 40);
if (WiFi.status() == WL_CONNECTED) {
IPAddress ip = WiFi.localIP();
snprintf(tmp, sizeof(tmp), "IP: %u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
} else {
snprintf(tmp, sizeof(tmp), "IP: -");
}
_display->print(tmp);
} }
} }
+4
Просмотреть файл
@@ -763,6 +763,10 @@ void MyMesh::formatPacketStatsReply(char *reply) {
getNumRecvFlood(), getNumRecvDirect()); getNumRecvFlood(), getNumRecvDirect());
} }
void MyMesh::formatMemoryReply(char *reply, size_t reply_size) {
StatsFormatHelper::formatMemoryStats(reply, reply_size);
}
void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply) { void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply) {
while (*command == ' ') while (*command == ' ')
command++; // skip leading spaces command++; // skip leading spaces
+1
Просмотреть файл
@@ -196,6 +196,7 @@ public:
void formatStatsReply(char *reply) override; void formatStatsReply(char *reply) override;
void formatRadioStatsReply(char *reply) override; void formatRadioStatsReply(char *reply) override;
void formatPacketStatsReply(char *reply) override; void formatPacketStatsReply(char *reply) override;
void formatMemoryReply(char *reply, size_t reply_size) override;
mesh::LocalIdentity& getSelfId() override { return self_id; } mesh::LocalIdentity& getSelfId() override { return self_id; }
+4
Просмотреть файл
@@ -833,6 +833,10 @@ void SensorMesh::formatPacketStatsReply(char *reply) {
getNumRecvFlood(), getNumRecvDirect()); getNumRecvFlood(), getNumRecvDirect());
} }
void SensorMesh::formatMemoryReply(char *reply, size_t reply_size) {
StatsFormatHelper::formatMemoryStats(reply, reply_size);
}
float SensorMesh::getTelemValue(uint8_t channel, uint8_t type) { float SensorMesh::getTelemValue(uint8_t channel, uint8_t type) {
auto buf = telemetry.getBuffer(); auto buf = telemetry.getBuffer();
uint8_t size = telemetry.getSize(); uint8_t size = telemetry.getSize();
+1
Просмотреть файл
@@ -73,6 +73,7 @@ public:
void formatStatsReply(char *reply) override; void formatStatsReply(char *reply) override;
void formatRadioStatsReply(char *reply) override; void formatRadioStatsReply(char *reply) override;
void formatPacketStatsReply(char *reply) override; void formatPacketStatsReply(char *reply) override;
void formatMemoryReply(char *reply, size_t reply_size) override;
mesh::LocalIdentity& getSelfId() override { return self_id; } mesh::LocalIdentity& getSelfId() override { return self_id; }
void saveIdentity(const mesh::LocalIdentity& new_id) override; void saveIdentity(const mesh::LocalIdentity& new_id) override;
void clearStats() override { } void clearStats() override { }
+2 -2
Просмотреть файл
@@ -1,7 +1,7 @@
[project] [project]
name = "meshcore-tooling" name = "meshcore-eastmesh"
version = "0.1.0" version = "0.1.0"
description = "Python tooling for MeshCore development workflows" description = "Python tooling for EastMesh workflows on top of MeshCore"
requires-python = ">=3.11" requires-python = ">=3.11"
dependencies = [ dependencies = [
"platformio>=6.1.18,<7", "platformio>=6.1.18,<7",
+2
Просмотреть файл
@@ -882,6 +882,8 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
_callbacks->formatRadioStatsReply(reply); _callbacks->formatRadioStatsReply(reply);
} else if (sender_timestamp == 0 && memcmp(command, "stats-core", 10) == 0 && (command[10] == 0 || command[10] == ' ')) { } else if (sender_timestamp == 0 && memcmp(command, "stats-core", 10) == 0 && (command[10] == 0 || command[10] == ' ')) {
_callbacks->formatStatsReply(reply); _callbacks->formatStatsReply(reply);
} else if (sender_timestamp == 0 && memcmp(command, "memory", 6) == 0 && (command[6] == 0 || command[6] == ' ')) {
_callbacks->formatMemoryReply(reply, 160);
} else { } else {
strcpy(reply, "Unknown command"); strcpy(reply, "Unknown command");
} }
+1
Просмотреть файл
@@ -83,6 +83,7 @@ public:
virtual void formatStatsReply(char *reply) = 0; virtual void formatStatsReply(char *reply) = 0;
virtual void formatRadioStatsReply(char *reply) = 0; virtual void formatRadioStatsReply(char *reply) = 0;
virtual void formatPacketStatsReply(char *reply) = 0; virtual void formatPacketStatsReply(char *reply) = 0;
virtual void formatMemoryReply(char *reply, size_t reply_size) = 0;
virtual mesh::LocalIdentity& getSelfId() = 0; virtual mesh::LocalIdentity& getSelfId() = 0;
virtual void saveIdentity(const mesh::LocalIdentity& new_id) = 0; virtual void saveIdentity(const mesh::LocalIdentity& new_id) = 0;
virtual void clearStats() = 0; virtual void clearStats() = 0;
+20
Просмотреть файл
@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "Mesh.h" #include "Mesh.h"
#include <Arduino.h>
class StatsFormatHelper { class StatsFormatHelper {
public: public:
@@ -52,4 +53,23 @@ public:
driver.getPacketsRecvErrors() driver.getPacketsRecvErrors()
); );
} }
static void formatMemoryStats(char* reply, size_t reply_size) {
if (reply == nullptr || reply_size == 0) {
return;
}
#if defined(ESP32)
snprintf(reply, reply_size,
"{\"heap_free\":%u,\"heap_min\":%u,\"heap_max\":%u,\"psram_free\":%u,\"psram_min\":%u,\"psram_max\":%u}",
ESP.getFreeHeap(),
ESP.getMinFreeHeap(),
ESP.getMaxAllocHeap(),
ESP.getFreePsram(),
ESP.getMinFreePsram(),
ESP.getMaxAllocPsram()
);
#else
snprintf(reply, reply_size, "{\"supported\":false}");
#endif
}
}; };
+105
Просмотреть файл
@@ -0,0 +1,105 @@
#include "JWTHelper.h"
#ifdef WITH_MQTT_UPLINK
#include <ctype.h>
#include <ed_25519.h>
#include <mbedtls/base64.h>
#include <string.h>
size_t JWTHelper::base64UrlEncode(const uint8_t* input, size_t input_len, char* output, size_t output_size) {
if (input == nullptr || output == nullptr || output_size < 2) {
return 0;
}
size_t encoded_len = 0;
if (mbedtls_base64_encode(reinterpret_cast<unsigned char*>(output), output_size - 1, &encoded_len, input,
input_len) != 0) {
return 0;
}
for (size_t i = 0; i < encoded_len; ++i) {
if (output[i] == '+') {
output[i] = '-';
} else if (output[i] == '/') {
output[i] = '_';
}
}
while (encoded_len > 0 && output[encoded_len - 1] == '=') {
encoded_len--;
}
output[encoded_len] = 0;
return encoded_len;
}
bool JWTHelper::createAuthToken(const mesh::LocalIdentity& identity, const char* audience, time_t issued_at,
time_t expires_at, char* token, size_t token_size, const char* owner,
const char* email) {
if (audience == nullptr || token == nullptr || token_size < 32 || issued_at <= 0 || expires_at <= issued_at) {
return false;
}
static const char header_json[] = "{\"alg\":\"Ed25519\",\"typ\":\"JWT\"}";
char header[96];
size_t header_len = base64UrlEncode(reinterpret_cast<const uint8_t*>(header_json), strlen(header_json), header,
sizeof(header));
if (header_len == 0) {
return false;
}
char public_key_hex[65];
mesh::Utils::toHex(public_key_hex, identity.pub_key, PUB_KEY_SIZE);
for (size_t i = 0; public_key_hex[i] != 0; ++i) {
public_key_hex[i] = toupper(static_cast<unsigned char>(public_key_hex[i]));
}
char payload_json[384];
int payload_json_len = snprintf(payload_json, sizeof(payload_json),
"{\"publicKey\":\"%s\",\"aud\":\"%s\",\"iat\":%lu,\"exp\":%lu",
public_key_hex, audience, static_cast<unsigned long>(issued_at),
static_cast<unsigned long>(expires_at));
if (payload_json_len <= 0 || static_cast<size_t>(payload_json_len) >= sizeof(payload_json)) {
return false;
}
if (owner != nullptr && owner[0] != 0) {
payload_json_len += snprintf(payload_json + payload_json_len, sizeof(payload_json) - payload_json_len,
",\"owner\":\"%s\"", owner);
}
if (email != nullptr && email[0] != 0) {
payload_json_len += snprintf(payload_json + payload_json_len, sizeof(payload_json) - payload_json_len,
",\"email\":\"%s\"", email);
}
if (payload_json_len <= 0 || static_cast<size_t>(payload_json_len) + 2 >= sizeof(payload_json)) {
return false;
}
payload_json[payload_json_len++] = '}';
payload_json[payload_json_len] = 0;
char payload[384];
size_t payload_len = base64UrlEncode(reinterpret_cast<const uint8_t*>(payload_json), payload_json_len, payload,
sizeof(payload));
if (payload_len == 0) {
return false;
}
char signing_input[512];
int signing_input_len = snprintf(signing_input, sizeof(signing_input), "%s.%s", header, payload);
if (signing_input_len <= 0 || static_cast<size_t>(signing_input_len) >= sizeof(signing_input)) {
return false;
}
uint8_t signature[64];
identity.sign(signature, reinterpret_cast<const uint8_t*>(signing_input), signing_input_len);
char signature_hex[129];
for (size_t i = 0; i < sizeof(signature); ++i) {
snprintf(&signature_hex[i * 2], 3, "%02X", signature[i]);
}
signature_hex[128] = 0;
int total_len = snprintf(token, token_size, "%s.%s", signing_input, signature_hex);
return total_len > 0 && static_cast<size_t>(total_len) < token_size;
}
#endif
+18
Просмотреть файл
@@ -0,0 +1,18 @@
#pragma once
#ifdef WITH_MQTT_UPLINK
#include <Arduino.h>
#include <Identity.h>
class JWTHelper {
public:
static bool createAuthToken(const mesh::LocalIdentity& identity, const char* audience, time_t issued_at,
time_t expires_at, char* token, size_t token_size, const char* owner = nullptr,
const char* email = nullptr);
private:
static size_t base64UrlEncode(const uint8_t* input, size_t input_len, char* output, size_t output_size);
};
#endif
+112
Просмотреть файл
@@ -0,0 +1,112 @@
#pragma once
namespace mqtt_ca_certs {
// EastMesh currently presents a Let's Encrypt E7-issued certificate.
static const char kEastmeshIsrgRootX1Pem[] =
R"PEM(-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
-----END CERTIFICATE-----
)PEM";
// LetsMesh currently presents a certificate issued by Google Trust Services WE1.
static const char kLetsmeshWe1Pem[] =
R"PEM(-----BEGIN CERTIFICATE-----
MIICnzCCAiWgAwIBAgIQf/MZd5csIkp2FV0TttaF4zAKBggqhkjOPQQDAzBHMQsw
CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU
MBIGA1UEAxMLR1RTIFJvb3QgUjQwHhcNMjMxMjEzMDkwMDAwWhcNMjkwMjIwMTQw
MDAwWjA7MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVR29vZ2xlIFRydXN0IFNlcnZp
Y2VzMQwwCgYDVQQDEwNXRTEwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARvzTr+
Z1dHTCEDhUDCR127WEcPQMFcF4XGGTfn1XzthkubgdnXGhOlCgP4mMTG6J7/EFmP
LCaY9eYmJbsPAvpWo4H+MIH7MA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggr
BgEFBQcDAQYIKwYBBQUHAwIwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU
kHeSNWfE/6jMqeZ72YB5e8yT+TgwHwYDVR0jBBgwFoAUgEzW63T/STaj1dj8tT7F
avCUHYwwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzAChhhodHRwOi8vaS5wa2ku
Z29vZy9yNC5jcnQwKwYDVR0fBCQwIjAgoB6gHIYaaHR0cDovL2MucGtpLmdvb2cv
ci9yNC5jcmwwEwYDVR0gBAwwCjAIBgZngQwBAgEwCgYIKoZIzj0EAwMDaAAwZQIx
AOcCq1HW90OVznX+0RGU1cxAQXomvtgM8zItPZCuFQ8jSBJSjz5keROv9aYsAm5V
sQIwJonMaAFi54mrfhfoFNZEfuNMSQ6/bIBiNLiyoX46FohQvKeIoJ99cx7sUkFN
7uJW
-----END CERTIFICATE-----
)PEM";
static const char kCombinedPem[] =
R"PEM(-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIICnzCCAiWgAwIBAgIQf/MZd5csIkp2FV0TttaF4zAKBggqhkjOPQQDAzBHMQsw
CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU
MBIGA1UEAxMLR1RTIFJvb3QgUjQwHhcNMjMxMjEzMDkwMDAwWhcNMjkwMjIwMTQw
MDAwWjA7MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVR29vZ2xlIFRydXN0IFNlcnZp
Y2VzMQwwCgYDVQQDEwNXRTEwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARvzTr+
Z1dHTCEDhUDCR127WEcPQMFcF4XGGTfn1XzthkubgdnXGhOlCgP4mMTG6J7/EFmP
LCaY9eYmJbsPAvpWo4H+MIH7MA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggr
BgEFBQcDAQYIKwYBBQUHAwIwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU
kHeSNWfE/6jMqeZ72YB5e8yT+TgwHwYDVR0jBBgwFoAUgEzW63T/STaj1dj8tT7F
avCUHYwwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzAChhhodHRwOi8vaS5wa2ku
Z29vZy9yNC5jcnQwKwYDVR0fBCQwIjAgoB6gHIYaaHR0cDovL2MucGtpLmdvb2cv
ci9yNC5jcmwwEwYDVR0gBAwwCjAIBgZngQwBAgEwCgYIKoZIzj0EAwMDaAAwZQIx
AOcCq1HW90OVznX+0RGU1cxAQXomvtgM8zItPZCuFQ8jSBJSjz5keROv9aYsAm5V
sQIwJonMaAFi54mrfhfoFNZEfuNMSQ6/bIBiNLiyoX46FohQvKeIoJ99cx7sUkFN
7uJW
-----END CERTIFICATE-----
)PEM";
} // namespace mqtt_ca_certs
+82
Просмотреть файл
@@ -0,0 +1,82 @@
#include "MQTTPrefs.h"
#ifdef WITH_MQTT_UPLINK
#include <helpers/TxtDataHelpers.h>
#include <string.h>
namespace {
constexpr uint32_t kFixedStatusIntervalMs = 300000;
}
void MQTTPrefsStore::setDefaults(MQTTPrefs& prefs) {
memset(&prefs, 0, sizeof(prefs));
prefs.magic = kMagic;
prefs.enabled_mask = 0x01;
prefs.packets_enabled = 1;
prefs.raw_enabled = 0;
prefs.status_enabled = 1;
prefs.tx_enabled = 0;
prefs.web_enabled = 1;
prefs.wifi_powersave = 0;
prefs.status_interval_ms = kFixedStatusIntervalMs;
StrHelper::strncpy(prefs.iata, MQTT_DEFAULT_IATA, sizeof(prefs.iata));
#ifdef WIFI_SSID
StrHelper::strncpy(prefs.wifi_ssid, WIFI_SSID, sizeof(prefs.wifi_ssid));
#endif
#ifdef WIFI_PWD
StrHelper::strncpy(prefs.wifi_pwd, WIFI_PWD, sizeof(prefs.wifi_pwd));
#endif
}
bool MQTTPrefsStore::load(FILESYSTEM* fs, MQTTPrefs& prefs) {
setDefaults(prefs);
if (fs == nullptr || !fs->exists(kFilename)) {
return false;
}
#if defined(RP2040_PLATFORM)
File file = fs->open(kFilename, "r");
#else
File file = fs->open(kFilename);
#endif
if (!file) {
return false;
}
size_t bytes_to_read = min(static_cast<size_t>(file.size()), sizeof(prefs));
bool ok = bytes_to_read >= sizeof(prefs.magic) &&
file.read(reinterpret_cast<uint8_t*>(&prefs), bytes_to_read) == bytes_to_read;
file.close();
if (!ok || prefs.magic != kMagic) {
return false;
}
if (prefs.wifi_powersave > 2) {
prefs.wifi_powersave = 0;
}
prefs.web_enabled = prefs.web_enabled ? 1 : 0;
prefs.status_interval_ms = kFixedStatusIntervalMs;
prefs.enabled_mask &= 0x07;
return true;
}
bool MQTTPrefsStore::save(FILESYSTEM* fs, const MQTTPrefs& prefs) {
if (fs == nullptr) {
return false;
}
fs->remove(kFilename);
#if defined(RP2040_PLATFORM)
File file = fs->open(kFilename, "w");
#else
File file = fs->open(kFilename, "w", true);
#endif
if (!file) {
return false;
}
bool ok = file.write(reinterpret_cast<const uint8_t*>(&prefs), sizeof(prefs)) == sizeof(prefs);
file.close();
return ok;
}
#endif
+40
Просмотреть файл
@@ -0,0 +1,40 @@
#pragma once
#ifdef WITH_MQTT_UPLINK
#include <helpers/IdentityStore.h>
#include <stdint.h>
#ifndef MQTT_DEFAULT_IATA
#define MQTT_DEFAULT_IATA "MEL"
#endif
struct MQTTPrefs {
uint32_t magic;
uint8_t enabled_mask;
uint8_t packets_enabled;
uint8_t raw_enabled;
uint8_t status_enabled;
uint8_t tx_enabled;
uint8_t web_enabled;
uint8_t wifi_powersave;
uint32_t status_interval_ms;
char iata[8];
char wifi_ssid[33];
char wifi_pwd[65];
char owner_public_key[65];
char owner_email[96];
};
class MQTTPrefsStore {
public:
static void setDefaults(MQTTPrefs& prefs);
static bool load(FILESYSTEM* fs, MQTTPrefs& prefs);
static bool save(FILESYSTEM* fs, const MQTTPrefs& prefs);
private:
static constexpr uint32_t kMagic = 0x4D515454;
static constexpr const char* kFilename = "/mqtt_prefs";
};
#endif
Разница между файлами не показана из-за своего большого размера Загрузить разницу
+176
Просмотреть файл
@@ -0,0 +1,176 @@
#pragma once
#ifdef WITH_MQTT_UPLINK
#include <Mesh.h>
#include <helpers/CommonCLI.h>
#include <target.h>
#include "JWTHelper.h"
#include "MQTTPrefs.h"
#if defined(ESP_PLATFORM)
#include <WiFi.h>
#include <esp_https_server.h>
#include <mqtt_client.h>
#endif
class MQTTWebCommandRunner {
public:
virtual ~MQTTWebCommandRunner() = default;
virtual void runWebCommand(const char* command, char* reply, size_t reply_size) = 0;
virtual const char* getWebAdminPassword() const = 0;
};
struct MQTTStatusSnapshot {
int battery_mv;
uint32_t uptime_secs;
uint16_t error_flags;
uint16_t queue_len;
int noise_floor;
uint32_t tx_air_secs;
uint32_t rx_air_secs;
uint32_t recv_errors;
float radio_freq;
float radio_bw;
uint8_t radio_sf;
uint8_t radio_cr;
};
class MQTTUplink {
public:
explicit MQTTUplink(mesh::RTCClock& rtc, mesh::LocalIdentity& identity);
void begin(FILESYSTEM* fs);
void end();
void loop(const MQTTStatusSnapshot& snapshot);
void publishPacket(const mesh::Packet& packet, bool is_tx, int rssi, float snr);
bool isRunning() const { return _running; }
bool isActive() const;
void formatStatusReply(char* reply, size_t reply_size) const;
void formatWebStatusReply(char* reply, size_t reply_size) const;
bool setEndpointEnabled(uint8_t bit, bool enabled);
bool isEndpointEnabled(uint8_t bit) const;
bool setPacketsEnabled(bool enabled);
bool isPacketsEnabled() const { return _prefs.packets_enabled != 0; }
bool setRawEnabled(bool enabled);
bool isRawEnabled() const { return _prefs.raw_enabled != 0; }
bool setStatusEnabled(bool enabled);
bool isStatusEnabled() const { return _prefs.status_enabled != 0; }
bool setTxEnabled(bool enabled);
bool isTxEnabled() const { return _prefs.tx_enabled != 0; }
bool setWebEnabled(bool enabled);
bool isWebEnabled() const { return _prefs.web_enabled != 0; }
bool setIata(const char* iata);
const char* getIata() const { return _prefs.iata; }
void setNodeNameSource(const char* node_name) { _node_name = node_name; }
void setWebCommandRunner(MQTTWebCommandRunner* runner) { _web_runner = runner; }
bool setWifiSSID(const char* ssid);
bool setWifiPassword(const char* pwd);
const char* getWifiSSID() const { return _prefs.wifi_ssid; }
bool setWifiPowerSave(const char* mode);
const char* getWifiPowerSave() const;
bool setOwnerPublicKey(const char* owner_public_key);
const char* getOwnerPublicKey() const { return _prefs.owner_public_key; }
bool setOwnerEmail(const char* owner_email);
const char* getOwnerEmail() const { return _prefs.owner_email; }
void formatWifiStatusReply(char* reply, size_t reply_size) const;
void reconnectWifi();
private:
#if defined(ESP_PLATFORM)
struct BrokerSpec {
const char* key;
const char* label;
const char* host;
const char* uri;
uint8_t bit;
};
struct BrokerState {
const BrokerSpec* spec;
esp_mqtt_client_handle_t client;
bool connected;
unsigned long last_connect_attempt;
time_t token_expires_at;
char username[70];
char token[512];
char client_id[48];
char status_topic[128];
char packets_topic[128];
char raw_topic[128];
char offline_payload[256];
};
struct WebRouteContext {
MQTTUplink* self;
};
#endif
FILESYSTEM* _fs;
mesh::RTCClock* _rtc;
mesh::LocalIdentity* _identity;
MQTTPrefs _prefs;
bool _running;
bool _wifi_started;
bool _sntp_started;
bool _have_time_sync;
unsigned long _wifi_sta_started_at;
unsigned long _last_wifi_attempt;
unsigned long _last_status_publish;
MQTTStatusSnapshot _last_status;
char _device_id[65];
const char* _node_name;
MQTTWebCommandRunner* _web_runner;
#if defined(ESP_PLATFORM)
static constexpr uint8_t kEastmeshBit = 0x01;
static constexpr uint8_t kLetsmeshEuBit = 0x02;
static constexpr uint8_t kLetsmeshUsBit = 0x04;
static const BrokerSpec kBrokerSpecs[3];
BrokerState _brokers[3];
httpd_handle_t _web_server;
char _web_token[33];
WebRouteContext _web_route_context;
static void handleMqttEvent(void* handler_args, esp_event_base_t base, int32_t event_id, void* event_data);
static esp_err_t handleWebIndex(httpd_req_t* req);
static esp_err_t handleWebLogin(httpd_req_t* req);
static esp_err_t handleWebCommand(httpd_req_t* req);
void ensureWebServer();
void stopWebServer();
bool startWebServer();
bool isWebAuthorized(httpd_req_t* req) const;
bool readRequestBody(httpd_req_t* req, char* buffer, size_t buffer_size) const;
void refreshWebToken();
void ensureWifi();
void updateTimeSync();
bool hasEnabledBroker() const;
void refreshIdentityStrings();
void refreshBrokerState(BrokerState& broker);
void ensureBroker(BrokerState& broker);
void destroyBroker(BrokerState& broker);
bool refreshToken(BrokerState& broker);
void publishStatus(bool online);
void publishOnlineStatus(BrokerState& broker);
void queuePublish(BrokerState& broker, const char* topic, const char* payload, bool retain);
int buildStatusJson(char* buffer, size_t buffer_size, bool online) const;
int buildPacketJson(char* buffer, size_t buffer_size, const mesh::Packet& packet, bool is_tx, int rssi,
float snr) const;
int buildRawJson(char* buffer, size_t buffer_size, const mesh::Packet& packet, bool is_tx, int rssi, float snr) const;
static wifi_ps_type_t toEspPowerSave(uint8_t mode);
static const char* getPowerSaveLabel(uint8_t mode);
static void escapeJsonString(const char* input, char* output, size_t output_size);
static void makeSafeToken(const char* input, char* output, size_t output_size);
static void bytesToHexUpper(const uint8_t* src, size_t len, char* dst, size_t dst_size);
static void formatIsoTimestamp(time_t ts, char* dst, size_t dst_size);
#endif
bool savePrefs();
};
#endif
+25
Просмотреть файл
@@ -0,0 +1,25 @@
#pragma once
namespace mqtt_web_panel_cert {
static const char kServerCertPem[] =
"-----BEGIN CERTIFICATE-----\n"
"MIIBiDCCAS2gAwIBAgIUYPagAbDjmNMHx+sqAC5qvOnlywIwCgYIKoZIzj0EAwIw\n"
"GTEXMBUGA1UEAwwObWVzaGNvcmUtbG9jYWwwHhcNMjYwNDA5MDE0MjAzWhcNMzYw\n"
"NDA2MDE0MjAzWjAZMRcwFQYDVQQDDA5tZXNoY29yZS1sb2NhbDBZMBMGByqGSM49\n"
"AgEGCCqGSM49AwEHA0IABJWLgQ/4sDJKykqR9a5brDLR9RikwoZ/6n1Od38rbfzV\n"
"ykP2LJIdrxU6zS1gzLk33klycB7AWFL1W7klStdiSpmjUzBRMB0GA1UdDgQWBBSk\n"
"dpP2r+UPriHT+hXLiyFdjkFgmTAfBgNVHSMEGDAWgBSkdpP2r+UPriHT+hXLiyFd\n"
"jkFgmTAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0kAMEYCIQCWS5ZRbON4\n"
"71RrwcogEzGuQTNdkbtkRc+517nZ63ZOawIhAPwSy/0yMyhqAoOMpVnhIb+w995f\n"
"zH4nApL2Q2U0KI2l\n"
"-----END CERTIFICATE-----\n";
static const char kServerKeyPem[] =
"-----BEGIN EC PRIVATE KEY-----\n"
"MHcCAQEEIGX71nLSiA8qUJXuiZRNzaGizNbMqgOxALSPd/HzfzWAoAoGCCqGSM49\n"
"AwEHoUQDQgAElYuBD/iwMkrKSpH1rlusMtH1GKTChn/qfU53fytt/NXKQ/Yskh2v\n"
"FTrNLWDMuTfeSXJwHsBYUvVbuSVK12JKmQ==\n"
"-----END EC PRIVATE KEY-----\n";
}
Generated
+1 -1
Просмотреть файл
@@ -331,7 +331,7 @@ wheels = [
] ]
[[package]] [[package]]
name = "meshcore-tooling" name = "meshcore-eastmesh"
version = "0.1.0" version = "0.1.0"
source = { virtual = "." } source = { virtual = "." }
dependencies = [ dependencies = [
+941
Просмотреть файл
@@ -0,0 +1,941 @@
[env:T_Beam_S3_Supreme_SX1262_repeater_mqtt]
extends = T_Beam_S3_Supreme_SX1262
build_flags =
${T_Beam_S3_Supreme_SX1262.build_flags}
-D ADVERT_NAME='"T-Beam S3 Supreme SX1262 Repeater"'
-D ADVERT_LAT=0
-D ADVERT_LON=0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
build_src_filter = ${T_Beam_S3_Supreme_SX1262.build_src_filter}
+<../examples/simple_repeater>
+<helpers/mqtt/*.cpp>
lib_deps =
${T_Beam_S3_Supreme_SX1262.lib_deps}
${esp32_ota.lib_deps}
[env:LilyGo_TLora_V2_1_1_6_repeater_mqtt]
extends = LilyGo_TLora_V2_1_1_6
build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter}
+<../examples/simple_repeater>
+<helpers/mqtt/*.cpp>
build_flags =
${LilyGo_TLora_V2_1_1_6.build_flags}
-D ADVERT_NAME='"TLora-V2.1-1.6 Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
lib_deps =
${LilyGo_TLora_V2_1_1_6.lib_deps}
${esp32_ota.lib_deps}
[env:LilyGo_TBeam_1W_repeater_mqtt]
extends = LilyGo_TBeam_1W
build_flags =
${LilyGo_TBeam_1W.build_flags}
-D ADVERT_NAME='"T-Beam 1W Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
-D PERSISTANT_GPS=1
-D ENV_SKIP_GPS_DETECT=1
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
build_src_filter = ${LilyGo_TBeam_1W.build_src_filter}
+<../examples/simple_repeater>
+<helpers/mqtt/*.cpp>
lib_deps =
${LilyGo_TBeam_1W.lib_deps}
${esp32_ota.lib_deps}
[env:Xiao_C3_repeater_mqtt]
extends = Xiao_esp32_C3
build_src_filter = ${Xiao_esp32_C3.build_src_filter}
+<../examples/simple_repeater/*.cpp>
+<helpers/mqtt/*.cpp>
build_flags =
${Xiao_esp32_C3.build_flags}
-D ADVERT_NAME='"Xiao C3 Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
lib_deps =
${Xiao_esp32_C3.lib_deps}
${esp32_ota.lib_deps}
bakercp/CRC32 @ ^2.0.0
[env:Ebyte_EoRa-S3_Repeater_mqtt]
extends = Ebyte_EoRa-S3
build_flags =
${Ebyte_EoRa-S3.build_flags}
-D DISPLAY_CLASS=SSD1306Display
-D ADVERT_NAME='"EORA_S3-1262 Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
build_src_filter = ${Ebyte_EoRa-S3.build_src_filter}
+<helpers/ui/SSD1306Display.cpp>
+<../examples/simple_repeater>
+<helpers/mqtt/*.cpp>
lib_deps =
${Ebyte_EoRa-S3.lib_deps}
${esp32_ota.lib_deps}
[env:RAK_3112_repeater_mqtt]
extends = rak3112
build_flags =
${rak3112.build_flags}
-D ADVERT_NAME='"RAK3112 Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
build_src_filter = ${rak3112.build_src_filter}
+<../examples/simple_repeater>
+<helpers/mqtt/*.cpp>
lib_deps =
${rak3112.lib_deps}
${esp32_ota.lib_deps}
bakercp/CRC32 @ ^2.0.0
[env:Tbeam_SX1262_repeater_mqtt]
extends = LilyGo_TBeam_SX1262
build_flags =
${LilyGo_TBeam_SX1262.build_flags}
-D ADVERT_NAME='"Tbeam SX1262 Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
build_src_filter = ${LilyGo_TBeam_SX1262.build_src_filter}
+<../examples/simple_repeater>
+<helpers/mqtt/*.cpp>
lib_deps =
${LilyGo_TBeam_SX1262.lib_deps}
${esp32_ota.lib_deps}
[env:Heltec_E290_repeater_mqtt]
extends = Heltec_E290_base
build_flags =
${Heltec_E290_base.build_flags}
-D DISPLAY_CLASS=E290Display
-D ADVERT_NAME='"Heltec E290 Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
build_src_filter = ${Heltec_E290_base.build_src_filter}
+<helpers/ui/E290Display.cpp>
+<../examples/simple_repeater>
+<helpers/mqtt/*.cpp>
lib_deps =
${Heltec_E290_base.lib_deps}
${esp32_ota.lib_deps}
bakercp/CRC32 @ ^2.0.0
[env:Tbeam_SX1276_repeater_mqtt]
extends = LilyGo_TBeam_SX1276
build_flags =
${LilyGo_TBeam_SX1276.build_flags}
-D ADVERT_NAME='"Tbeam Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
-D PERSISTANT_GPS=1
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
build_src_filter = ${LilyGo_TBeam_SX1276.build_src_filter}
+<../examples/simple_repeater>
+<helpers/mqtt/*.cpp>
lib_deps =
${LilyGo_TBeam_SX1276.lib_deps}
${esp32_ota.lib_deps}
[env:Heltec_E213_repeater_mqtt]
extends = Heltec_E213_base
build_flags =
${Heltec_E213_base.build_flags}
-D DISPLAY_CLASS=E213Display
-D ADVERT_NAME='"Heltec E213 Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
build_src_filter = ${Heltec_E213_base.build_src_filter}
+<helpers/ui/E213Display.cpp>
+<../examples/simple_repeater>
+<helpers/mqtt/*.cpp>
lib_deps =
${Heltec_E213_base.lib_deps}
${esp32_ota.lib_deps}
bakercp/CRC32 @ ^2.0.0
[env:Heltec_Wireless_Tracker_repeater_mqtt]
extends = Heltec_tracker_base
build_flags =
${Heltec_tracker_base.build_flags}
-D DISPLAY_ROTATION=1
-D DISPLAY_CLASS=ST7735Display
-D ADVERT_NAME='"Heltec Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
build_src_filter = ${Heltec_tracker_base.build_src_filter}
+<helpers/ui/ST7735Display.cpp>
+<../examples/simple_repeater>
+<helpers/mqtt/*.cpp>
lib_deps =
${Heltec_tracker_base.lib_deps}
${esp32_ota.lib_deps}
[env:M5Stack_Unit_C6L_repeater_mqtt]
extends = M5Stack_Unit_C6L
build_src_filter = ${M5Stack_Unit_C6L.build_src_filter}
+<../examples/simple_repeater/*.cpp>
+<helpers/mqtt/*.cpp>
build_flags =
${M5Stack_Unit_C6L.build_flags}
-D ADVERT_NAME='"Unit C6L Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
lib_deps =
${M5Stack_Unit_C6L.lib_deps}
[env:LilyGo_TDeck_repeater_mqtt]
extends = LilyGo_TDeck
build_flags =
${LilyGo_TDeck.build_flags}
-D ADVERT_NAME='"TDeck Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
build_src_filter = ${LilyGo_TDeck.build_src_filter}
+<../examples/simple_repeater>
+<helpers/ui/ST7789LCDDisplay.cpp>
+<helpers/mqtt/*.cpp>
lib_deps =
${LilyGo_TDeck.lib_deps}
${esp32_ota.lib_deps}
[env:Station_G2_repeater_mqtt]
extends = Station_G2
build_flags =
${Station_G2.build_flags}
-D ADVERT_NAME='"Station G2 Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
build_src_filter = ${Station_G2.build_src_filter}
+<../examples/simple_repeater>
+<helpers/mqtt/*.cpp>
lib_deps =
${Station_G2.lib_deps}
${esp32_ota.lib_deps}
[env:Station_G2_logging_repeater_mqtt]
extends = Station_G2
build_flags =
${Station_G2.build_flags}
-D ADVERT_NAME='"Station G2 Logging Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
-D MESH_PACKET_LOGGING=1
-D SX126X_RX_BOOSTED_GAIN=1
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
build_src_filter = ${Station_G2.build_src_filter}
+<../examples/simple_repeater>
+<helpers/mqtt/*.cpp>
lib_deps =
${Station_G2.lib_deps}
${esp32_ota.lib_deps}
[env:LilyGo_T3S3_sx1262_repeater_mqtt]
extends = LilyGo_T3S3_sx1262
build_flags =
${LilyGo_T3S3_sx1262.build_flags}
-D DISPLAY_CLASS=SSD1306Display
-D ADVERT_NAME='"T3S3-1262 Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter}
+<helpers/ui/SSD1306Display.cpp>
+<../examples/simple_repeater>
+<helpers/mqtt/*.cpp>
lib_deps =
${LilyGo_T3S3_sx1262.lib_deps}
${esp32_ota.lib_deps}
[env:Meshadventurer_sx1262_repeater_mqtt]
extends = Meshadventurer
build_src_filter = ${Meshadventurer.build_src_filter}
+<../examples/simple_repeater>
+<helpers/ui/SSD1306Display.cpp>
+<helpers/mqtt/*.cpp>
build_flags =
${Meshadventurer.build_flags}
-D RADIO_CLASS=CustomSX1262
-D WRAPPER_CLASS=CustomSX1262Wrapper
-D LORA_TX_POWER=22
-D ADVERT_NAME='"Meshadventurer Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
lib_deps =
${Meshadventurer.lib_deps}
${esp32_ota.lib_deps}
[env:Meshadventurer_sx1268_repeater_mqtt]
extends = Meshadventurer
build_src_filter = ${Meshadventurer.build_src_filter}
+<../examples/simple_repeater>
+<helpers/ui/SSD1306Display.cpp>
+<helpers/mqtt/*.cpp>
build_flags =
${Meshadventurer.build_flags}
-D RADIO_CLASS=CustomSX1268
-D WRAPPER_CLASS=CustomSX1268Wrapper
-D LORA_TX_POWER=22
-D ADVERT_NAME='"Meshadventurer Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
lib_deps =
${Meshadventurer.lib_deps}
${esp32_ota.lib_deps}
[env:Heltec_Wireless_Paper_repeater_mqtt]
extends = Heltec_Wireless_Paper_base
build_flags =
${Heltec_Wireless_Paper_base.build_flags}
-D DISPLAY_CLASS=E213Display
-D ADVERT_NAME='"Heltec WP Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
build_src_filter = ${Heltec_Wireless_Paper_base.build_src_filter}
+<helpers/ui/E213Display.cpp>
+<../examples/simple_repeater>
+<helpers/mqtt/*.cpp>
lib_deps =
${Heltec_Wireless_Paper_base.lib_deps}
${esp32_ota.lib_deps}
bakercp/CRC32 @ ^2.0.0
[env:heltec_v4_repeater_mqtt]
extends = heltec_v4_oled
build_flags =
${heltec_v4_oled.build_flags}
-D DISPLAY_CLASS=SSD1306Display
-D ADVERT_NAME='"Heltec MQTT Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
build_src_filter = ${heltec_v4_oled.build_src_filter}
+<helpers/mqtt/*.cpp>
+<helpers/ui/SSD1306Display.cpp>
+<../examples/simple_repeater>
lib_deps =
${heltec_v4_oled.lib_deps}
${esp32_ota.lib_deps}
bakercp/CRC32 @ ^2.0.0
[env:heltec_v4_tft_repeater_mqtt]
extends = heltec_v4_tft
build_flags =
${heltec_v4_tft.build_flags}
-D DISPLAY_CLASS=ST7789LCDDisplay
-D ADVERT_NAME='"Heltec Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
build_src_filter = ${heltec_v4_tft.build_src_filter}
+<helpers/ui/ST7789LCDDisplay.cpp>
+<../examples/simple_repeater>
+<helpers/mqtt/*.cpp>
lib_deps =
${heltec_v4_tft.lib_deps}
${esp32_ota.lib_deps}
bakercp/CRC32 @ ^2.0.0
[env:heltec_tracker_v2_repeater_mqtt]
extends = Heltec_tracker_v2
build_flags =
${Heltec_tracker_v2.build_flags}
-D DISPLAY_CLASS=ST7735Display
-D ADVERT_NAME='"Heltec Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
build_src_filter = ${Heltec_tracker_v2.build_src_filter}
+<helpers/ui/ST7735Display.cpp>
+<../examples/simple_repeater>
+<helpers/mqtt/*.cpp>
lib_deps =
${Heltec_tracker_v2.lib_deps}
${esp32_ota.lib_deps}
bakercp/CRC32 @ ^2.0.0
[env:Heltec_v3_repeater_mqtt]
extends = Heltec_lora32_v3
build_flags =
${Heltec_lora32_v3.build_flags}
-D DISPLAY_CLASS=SSD1306Display
-D ADVERT_NAME='"Heltec Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
build_src_filter = ${Heltec_lora32_v3.build_src_filter}
+<helpers/ui/SSD1306Display.cpp>
+<../examples/simple_repeater>
+<helpers/mqtt/*.cpp>
lib_deps =
${Heltec_lora32_v3.lib_deps}
${esp32_ota.lib_deps}
bakercp/CRC32 @ ^2.0.0
[env:Heltec_WSL3_repeater_mqtt]
extends = Heltec_lora32_v3
build_flags =
${Heltec_lora32_v3.build_flags}
-D ADVERT_NAME='"Heltec Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
build_src_filter = ${Heltec_lora32_v3.build_src_filter}
+<../examples/simple_repeater>
+<helpers/mqtt/*.cpp>
lib_deps =
${Heltec_lora32_v3.lib_deps}
${esp32_ota.lib_deps}
bakercp/CRC32 @ ^2.0.0
[env:LilyGo_T3S3_sx1276_repeater_mqtt]
extends = LilyGo_T3S3_sx1276
build_flags =
${LilyGo_T3S3_sx1276.build_flags}
-D DISPLAY_CLASS=SSD1306Display
-D ADVERT_NAME='"T3S3-1276 Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
build_src_filter = ${LilyGo_T3S3_sx1276.build_src_filter}
+<helpers/ui/SSD1306Display.cpp>
+<../examples/simple_repeater>
+<helpers/mqtt/*.cpp>
lib_deps =
${LilyGo_T3S3_sx1276.lib_deps}
${esp32_ota.lib_deps}
[env:LilyGo_Tlora_C6_repeater_mqtt_]
extends = tlora_c6
build_src_filter = ${tlora_c6.build_src_filter}
+<../examples/simple_repeater/*.cpp>
+<helpers/mqtt/*.cpp>
build_flags =
${tlora_c6.build_flags}
-D ADVERT_NAME='"Tlora C6 Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
lib_deps =
${tlora_c6.lib_deps}
[env:Xiao_C6_repeater_mqtt_]
extends = Xiao_C6
build_src_filter = ${Xiao_C6.build_src_filter}
+<../examples/simple_repeater/*.cpp>
+<helpers/mqtt/*.cpp>
build_flags =
${Xiao_C6.build_flags}
-D ADVERT_NAME='"Xiao C6 Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
lib_deps =
${Xiao_C6.lib_deps}
[env:Meshimi_repeater_mqtt_]
extends = Meshimi
build_src_filter = ${Meshimi.build_src_filter}
+<../examples/simple_repeater/*.cpp>
+<helpers/mqtt/*.cpp>
build_flags =
${Meshimi.build_flags}
-D ADVERT_NAME='"Meshimi Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
lib_deps =
${Meshimi.lib_deps}
[env:WHY2025_badge_repeater_mqtt_]
extends = WHY2025_badge
build_src_filter = ${WHY2025_badge.build_src_filter}
+<../examples/simple_repeater/*.cpp>
+<helpers/mqtt/*.cpp>
build_flags =
${WHY2025_badge.build_flags}
-D ADVERT_NAME='"WHY2025 Badge Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
lib_deps =
${WHY2025_badge.lib_deps}
[env:Heltec_v2_repeater_mqtt]
extends = Heltec_lora32_v2
build_flags =
${Heltec_lora32_v2.build_flags}
-D DISPLAY_CLASS=SSD1306Display
-D ADVERT_NAME='"Heltec Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
build_src_filter = ${Heltec_lora32_v2.build_src_filter}
+<../examples/simple_repeater>
+<helpers/ui/SSD1306Display.cpp>
+<helpers/ui/MomentaryButton.cpp>
+<helpers/mqtt/*.cpp>
lib_deps =
${Heltec_lora32_v2.lib_deps}
${esp32_ota.lib_deps}
[env:nibble_screen_connect_repeater_mqtt]
extends = nibble_screen_connect_base
build_flags =
${nibble_screen_connect_base.build_flags}
-D DISPLAY_CLASS=SSD1306Display
-D ADVERT_NAME='"Nibble Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
build_src_filter = ${nibble_screen_connect_base.build_src_filter}
+<helpers/ui/SSD1306Display.cpp>
+<../examples/simple_repeater>
+<helpers/mqtt/*.cpp>
lib_deps =
${nibble_screen_connect_base.lib_deps}
${esp32_ota.lib_deps}
[env:ThinkNode_M2_Repeater_mqtt]
extends = ThinkNode_M2
build_src_filter = ${ThinkNode_M2.build_src_filter}
+<../examples/simple_repeater/*.cpp>
+<helpers/mqtt/*.cpp>
build_flags =
${ThinkNode_M2.build_flags}
-D ADVERT_NAME='"Thinknode M2 Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=8
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
lib_deps =
${ThinkNode_M2.lib_deps}
${esp32_ota.lib_deps}
[env:ThinkNode_M5_Repeater_mqtt]
extends = ThinkNode_M5
build_src_filter = ${ThinkNode_M5.build_src_filter}
+<../examples/simple_repeater/*.cpp>
+<helpers/mqtt/*.cpp>
build_flags =
${ThinkNode_M5.build_flags}
-D ADVERT_NAME='"Thinknode M5 Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=8
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
lib_deps =
${ThinkNode_M5.lib_deps}
${esp32_ota.lib_deps}
[env:Heltec_T190_repeater_mqtt_]
extends = Heltec_T190_base
build_flags =
${Heltec_T190_base.build_flags}
-D ADVERT_NAME='"Heltec T190 Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
build_src_filter = ${Heltec_T190_base.build_src_filter}
+<../examples/simple_repeater>
+<helpers/mqtt/*.cpp>
lib_deps =
${Heltec_T190_base.lib_deps}
${esp32_ota.lib_deps}
[env:Tenstar_C3_sx1262_repeater_mqtt]
extends = Tenstar_esp32_C3
build_src_filter = ${Tenstar_esp32_C3.build_src_filter}
+<../examples/simple_repeater/*.cpp>
+<helpers/mqtt/*.cpp>
build_flags =
${Tenstar_esp32_C3.build_flags}
-D RADIO_CLASS=CustomSX1262
-D WRAPPER_CLASS=CustomSX1262Wrapper
-D SX126X_RX_BOOSTED_GAIN=1
-D LORA_TX_POWER=22
-D ADVERT_NAME='"Tenstar C3 Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
lib_deps =
${Tenstar_esp32_C3.lib_deps}
${esp32_ota.lib_deps}
[env:Tenstar_C3_sx1268_repeater_mqtt]
extends = Tenstar_esp32_C3
build_src_filter = ${Tenstar_esp32_C3.build_src_filter}
+<../examples/simple_repeater/*.cpp>
+<helpers/mqtt/*.cpp>
build_flags =
${Tenstar_esp32_C3.build_flags}
-D RADIO_CLASS=CustomSX1268
-D WRAPPER_CLASS=CustomSX1268Wrapper
-D LORA_TX_POWER=22
-D ADVERT_NAME='"Tenstar C3 Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
lib_deps =
${Tenstar_esp32_C3.lib_deps}
${esp32_ota.lib_deps}
[env:Heltec_ct62_repeater_mqtt]
extends = Heltec_ct62
build_flags =
${Heltec_ct62.build_flags}
;-D ARDUINO_USB_MODE=1
;-D ARDUINO_USB_CDC_ON_BOOT=1
-D ADVERT_NAME='"HT-CT62 Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
build_src_filter = ${Heltec_ct62.build_src_filter}
+<../examples/simple_repeater>
+<helpers/mqtt/*.cpp>
lib_deps =
${Heltec_ct62.lib_deps}
${esp32_ota.lib_deps}
[env:Xiao_S3_WIO_repeater_mqtt]
extends = Xiao_S3_WIO
build_src_filter = ${Xiao_S3_WIO.build_src_filter}
+<../examples/simple_repeater/*.cpp>
+<helpers/mqtt/*.cpp>
build_flags =
${Xiao_S3_WIO.build_flags}
-D ADVERT_NAME='"XiaoS3 Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
lib_deps =
${Xiao_S3_WIO.lib_deps}
${esp32_ota.lib_deps}
[env:Generic_E22_sx1262_repeater_mqtt]
extends = Generic_E22
build_src_filter = ${Generic_E22.build_src_filter}
+<../examples/simple_repeater/*.cpp>
+<helpers/mqtt/*.cpp>
build_flags =
${Generic_E22.build_flags}
-D RADIO_CLASS=CustomSX1262
-D WRAPPER_CLASS=CustomSX1262Wrapper
-D LORA_TX_POWER=22
-D ADVERT_NAME='"E22 Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
lib_deps =
${Generic_E22.lib_deps}
${esp32_ota.lib_deps}
[env:Generic_E22_sx1268_repeater_mqtt]
extends = Generic_E22
build_src_filter = ${Generic_E22.build_src_filter}
+<../examples/simple_repeater/*.cpp>
+<helpers/mqtt/*.cpp>
build_flags =
${Generic_E22.build_flags}
-D RADIO_CLASS=CustomSX1268
-D WRAPPER_CLASS=CustomSX1268Wrapper
-D LORA_TX_POWER=22
-D ADVERT_NAME='"E22 Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
-D WITH_MQTT_UPLINK=1
-D MQTT_DEBUG=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
-D MQTT_DEFAULT_IATA='"MEL"'
-D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=1
lib_deps =
${Generic_E22.lib_deps}
${esp32_ota.lib_deps}