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

This commit is contained in:
Jared Dohrman
2026-04-09 15:00:53 +10:00
orang tua 2ab5f5128f
melakukan 98fe78a379
27 mengubah file dengan 3412 tambahan dan 103 penghapusan
@@ -26,17 +26,15 @@ runs:
uv venv --allow-existing --seed
echo "$GITHUB_WORKSPACE/.venv/bin" >> "$GITHUB_PATH"
# Recommended EastMesh tag format:
# room-server-eastmesh-v1.0.1
# Active release tag formats:
# companion-wifi-v1.2.3
# repeater-mqtt-eastmesh-v1.0.1
# with OFFICIAL_MESHCORE_VERSION=v1.2.3 configured as a GitHub variable.
#
# This exports:
# FIRMWARE_VERSION=v1.2.3
# EASTMESH_VERSION=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.
# companion-wifi-v1.2.3 -> FIRMWARE_VERSION=v1.2.3, RELEASE_VERSION=v1.2.3
# 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
- name: Extract Version Info
shell: bash
run: |
@@ -56,17 +54,14 @@ runs:
EASTMESH_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
echo "OFFICIAL_MESHCORE_VERSION must be set for EastMesh release tags" >&2
exit 1
fi
EASTMESH_VERSION="${BASH_REMATCH[2]}"
elif [[ "$GIT_TAG_NAME" =~ ^(companion|repeater|room-server)-(.+)-eastmesh-(v[[:alnum:]._-]+)$ ]]; then
FIRMWARE_VERSION="${BASH_REMATCH[2]}"
EASTMESH_VERSION="${BASH_REMATCH[3]}"
elif [[ "$GIT_TAG_NAME" =~ ^(companion|repeater|room-server)-(.+)$ ]]; then
FIRMWARE_VERSION="${BASH_REMATCH[2]}"
EASTMESH_VERSION="${BASH_REMATCH[1]}"
elif [[ "$GIT_TAG_NAME" =~ ^companion-wifi-(v[[:alnum:]._-]+)$ ]]; then
FIRMWARE_VERSION="${BASH_REMATCH[1]}"
else
echo "Unsupported release tag format: $GIT_TAG_NAME" >&2
exit 1
@@ -1,4 +1,4 @@
name: Build Companion Firmwares
name: Build Companion WiFi Firmwares
permissions:
contents: write
@@ -7,7 +7,7 @@ on:
workflow_dispatch:
push:
tags:
- 'companion-*'
- 'companion-wifi-v*'
jobs:
@@ -24,19 +24,19 @@ jobs:
uses: ./.github/actions/setup-build-environment
- 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
uses: actions/upload-artifact@v7
with:
name: companion-firmwares
name: companion-wifi-firmwares
path: out
- name: Create Release
uses: softprops/action-gh-release@v2
if: startsWith(github.ref, 'refs/tags/')
with:
name: Companion Firmware ${{ env.RELEASE_VERSION }}
name: Companion WiFi Firmware ${{ env.RELEASE_VERSION }}
body: ""
draft: true
files: out/*
@@ -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:
contents: write
@@ -7,7 +7,7 @@ on:
workflow_dispatch:
push:
tags:
- 'room-server-*'
- 'repeater-mqtt-eastmesh-*'
jobs:
@@ -24,19 +24,19 @@ jobs:
uses: ./.github/actions/setup-build-environment
- 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
uses: actions/upload-artifact@v7
with:
name: room-server-firmwares
name: repeater-mqtt-firmwares
path: out
- name: Create Release
uses: softprops/action-gh-release@v2
if: startsWith(github.ref, 'refs/tags/')
with:
name: Room Server Firmware ${{ env.RELEASE_VERSION }}
name: Repeater MQTT Firmware ${{ env.RELEASE_VERSION }}
body: ""
draft: true
files: out/*
+19 -29
Melihat File
@@ -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:
- `companion-eastmesh-v1.0.1`
- `repeater-eastmesh-v1.0.1`
- `room-server-eastmesh-v1.0.1`
- `companion-wifi-v1.2.3`
- `repeater-mqtt-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:
- `companion-eastmesh-*` builds companion firmware
- `repeater-eastmesh-*` builds repeater firmware
- `room-server-eastmesh-*` builds room server firmware
- `companion-wifi-v*` builds companion WiFi firmware
- `repeater-mqtt-eastmesh-*` builds repeater MQTT firmware
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:
- `FIRMWARE_VERSION` comes from `OFFICIAL_MESHCORE_VERSION`
- `EASTMESH_VERSION` comes from the tag
- `companion-wifi` uses the version in the tag as `FIRMWARE_VERSION`
- `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:
- tag: `room-server-eastmesh-v1.0.1`
- tag: `repeater-mqtt-eastmesh-v1.0.1`
- repo variable: `OFFICIAL_MESHCORE_VERSION=v1.2.3`
- resulting build version: `v1.2.3-eastmesh-v1.0.1-abcdef`
@@ -59,23 +60,12 @@ Example:
Example:
```bash
git tag companion-eastmesh-v1.0.1
git tag repeater-eastmesh-v1.0.1
git tag room-server-eastmesh-v1.0.1
git push origin companion-eastmesh-v1.0.1 repeater-eastmesh-v1.0.1 room-server-eastmesh-v1.0.1
git tag companion-wifi-v1.2.3
git tag repeater-mqtt-eastmesh-v1.0.1
git push origin companion-wifi-v1.2.3 repeater-mqtt-eastmesh-v1.0.1
```
## Legacy Tags
## Supported Tags
The workflows still accept older tag formats for compatibility:
- `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`
- `companion-wifi-v1.2.3`
- `repeater-mqtt-eastmesh-v1.0.1`
+31 -3
Melihat File
@@ -32,7 +32,9 @@ Commands:
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-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-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.
Examples:
@@ -45,9 +47,15 @@ $ sh build.sh build-matching-firmwares <build-match-spec>
Build all companion firmwares
$ sh build.sh build-companion-firmwares
Build all companion WiFi firmwares
$ sh build.sh build-companion-wifi-firmwares
Build all 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
$ sh build.sh build-room-server-firmwares
@@ -155,20 +163,24 @@ build_firmware() {
exit 1
fi
# set firmware version string
# set artifact version string
# e.g: v1.2.3-eastmesh-v1.0.1-abcdef
FIRMWARE_VERSION_BASE="${FIRMWARE_VERSION}"
if [ -n "$EASTMESH_VERSION" ]; then
FIRMWARE_VERSION_BASE="${FIRMWARE_VERSION_BASE}-eastmesh-${EASTMESH_VERSION}"
fi
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
# e.g: RAK_4631_Repeater-v1.0.0-SHA
FIRMWARE_FILENAME="$1-${FIRMWARE_VERSION_STRING}"
# add firmware version info 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}\"'"
# 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}\"' -DCLIENT_VERSION='\"${CLIENT_VERSION_STRING}\"'"
# disable debug flags if requested
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 specific room server firmwares
@@ -298,8 +322,12 @@ elif [[ $1 == "build-firmwares" ]]; then
build_firmwares
elif [[ $1 == "build-companion-firmwares" ]]; then
build_companion_firmwares
elif [[ $1 == "build-companion-wifi-firmwares" ]]; then
build_companion_wifi_firmwares
elif [[ $1 == "build-repeater-firmwares" ]]; then
build_repeater_firmwares
elif [[ $1 == "build-repeater-mqtt-firmwares" ]]; then
build_repeater_mqtt_firmwares
elif [[ $1 == "build-room-server-firmwares" ]]; then
build_room_server_firmwares
fi
+229
Melihat File
@@ -461,6 +461,9 @@ void MyMesh::logRx(mesh::Packet *pkt, int len, float score) {
bridge.sendPacket(pkt);
}
#endif
#ifdef WITH_MQTT_UPLINK
mqtt.publishPacket(*pkt, false, (int)_radio->getLastRSSI(), _radio->getLastSNR());
#endif
if (_logging) {
File f = openAppend(PACKET_LOG_FILE);
@@ -487,6 +490,9 @@ void MyMesh::logTx(mesh::Packet *pkt, int len) {
bridge.sendPacket(pkt);
}
#endif
#ifdef WITH_MQTT_UPLINK
mqtt.publishPacket(*pkt, true, (int)_radio->getLastRSSI(), _radio->getLastSNR());
#endif
if (_logging) {
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)
, bridge(&_prefs, _mgr, &rtc)
#endif
#if defined(WITH_MQTT_UPLINK)
, mqtt(rtc, self_id)
#endif
{
last_millis = 0;
uptime_millis = 0;
@@ -915,6 +924,12 @@ void MyMesh::begin(FILESYSTEM *fs) {
bridge.begin();
}
#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_tx_power(_prefs.tx_power_dbm);
@@ -1079,6 +1094,10 @@ void MyMesh::formatPacketStatsReply(char *reply) {
getNumRecvFlood(), getNumRecvDirect());
}
void MyMesh::formatMemoryReply(char *reply, size_t reply_size) {
StatsFormatHelper::formatMemoryStats(reply, reply_size);
}
void MyMesh::saveIdentity(const mesh::LocalIdentity &new_id) {
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
IdentityStore store(*_fs, "");
@@ -1285,11 +1304,201 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply
sendNodeDiscoverReq();
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{
_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() {
#ifdef WITH_BRIDGE
bridge.loop();
@@ -1329,6 +1538,23 @@ void MyMesh::loop() {
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
uint32_t now = millis();
uptime_millis += now - last_millis;
@@ -1339,6 +1565,9 @@ void MyMesh::loop() {
bool MyMesh::hasPendingWork() const {
#if defined(WITH_BRIDGE)
if (bridge.isRunning()) return true; // bridge needs WiFi radio, can't sleep
#endif
#if defined(WITH_MQTT_UPLINK)
if (mqtt.isActive()) return true;
#endif
return _mgr->getOutboundTotal() > 0;
}
+11 -1
Melihat File
@@ -23,6 +23,10 @@
#define WITH_BRIDGE
#endif
#ifdef WITH_MQTT_UPLINK
#include <helpers/mqtt/MQTTUplink.h>
#endif
#include <helpers/AdvertDataHelpers.h>
#include <helpers/ArduinoHelpers.h>
#include <helpers/ClientACL.h>
@@ -80,7 +84,7 @@ struct NeighbourInfo {
#define PACKET_LOG_FILE "/packet_log"
class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
class MyMesh : public mesh::Mesh, public CommonCLICallbacks, public MQTTWebCommandRunner {
FILESYSTEM* _fs;
uint32_t last_millis;
uint64_t uptime_millis;
@@ -117,6 +121,9 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
#elif defined(WITH_ESPNOW_BRIDGE)
ESPNowBridge bridge;
#endif
#ifdef WITH_MQTT_UPLINK
MQTTUplink mqtt;
#endif
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);
@@ -208,12 +215,15 @@ public:
void formatStatsReply(char *reply) override;
void formatRadioStatsReply(char *reply) override;
void formatPacketStatsReply(char *reply) override;
void formatMemoryReply(char *reply, size_t reply_size) override;
mesh::LocalIdentity& getSelfId() override { return self_id; }
void saveIdentity(const mesh::LocalIdentity& new_id) override;
void clearStats() override;
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();
#if defined(WITH_BRIDGE)
+11
Melihat File
@@ -1,5 +1,6 @@
#include "UITask.h"
#include <Arduino.h>
#include <WiFi.h>
#include <helpers/CommonCLI.h>
#define AUTO_OFF_MILLIS 20000 // 20 seconds
@@ -77,6 +78,16 @@ void UITask::renderCurrScreen() {
_display->setCursor(0, 30);
sprintf(tmp, "BW: %03.2f CR: %d", _node_prefs->bw, _node_prefs->cr);
_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);
}
}
@@ -763,6 +763,10 @@ void MyMesh::formatPacketStatsReply(char *reply) {
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) {
while (*command == ' ')
command++; // skip leading spaces
+1
Melihat File
@@ -196,6 +196,7 @@ public:
void formatStatsReply(char *reply) override;
void formatRadioStatsReply(char *reply) override;
void formatPacketStatsReply(char *reply) override;
void formatMemoryReply(char *reply, size_t reply_size) override;
mesh::LocalIdentity& getSelfId() override { return self_id; }
+4
Melihat File
@@ -833,6 +833,10 @@ void SensorMesh::formatPacketStatsReply(char *reply) {
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) {
auto buf = telemetry.getBuffer();
uint8_t size = telemetry.getSize();
+1
Melihat File
@@ -73,6 +73,7 @@ public:
void formatStatsReply(char *reply) override;
void formatRadioStatsReply(char *reply) override;
void formatPacketStatsReply(char *reply) override;
void formatMemoryReply(char *reply, size_t reply_size) override;
mesh::LocalIdentity& getSelfId() override { return self_id; }
void saveIdentity(const mesh::LocalIdentity& new_id) override;
void clearStats() override { }
+2 -2
Melihat File
@@ -1,7 +1,7 @@
[project]
name = "meshcore-tooling"
name = "meshcore-eastmesh"
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"
dependencies = [
"platformio>=6.1.18,<7",
+2
Melihat File
@@ -882,6 +882,8 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
_callbacks->formatRadioStatsReply(reply);
} else if (sender_timestamp == 0 && memcmp(command, "stats-core", 10) == 0 && (command[10] == 0 || command[10] == ' ')) {
_callbacks->formatStatsReply(reply);
} else if (sender_timestamp == 0 && memcmp(command, "memory", 6) == 0 && (command[6] == 0 || command[6] == ' ')) {
_callbacks->formatMemoryReply(reply, 160);
} else {
strcpy(reply, "Unknown command");
}
+1
Melihat File
@@ -83,6 +83,7 @@ public:
virtual void formatStatsReply(char *reply) = 0;
virtual void formatRadioStatsReply(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 void saveIdentity(const mesh::LocalIdentity& new_id) = 0;
virtual void clearStats() = 0;
+20
Melihat File
@@ -1,6 +1,7 @@
#pragma once
#include "Mesh.h"
#include <Arduino.h>
class StatsFormatHelper {
public:
@@ -52,4 +53,23 @@ public:
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
Melihat File
@@ -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
Melihat File
@@ -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
Melihat File
@@ -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
Melihat File
@@ -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
Melihat File
@@ -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
File diff ditekan karena terlalu besar Load Diff
+176
Melihat File
@@ -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
Melihat File
@@ -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
Melihat File
@@ -331,7 +331,7 @@ wheels = [
]
[[package]]
name = "meshcore-tooling"
name = "meshcore-eastmesh"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
+941
Melihat File
@@ -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}