mirror of
https://github.com/mikecarper/MeshCore.git
synced 2026-09-27 04:07:57 +00:00
feat(mqtt): add EastIdahoMesh preset and enhance CLI preset listing
Added the EastIdahoMesh preset to the MQTT bridge implementation, increasing the total number of built-in presets. Updated the CLI to support listing available MQTT presets with pagination, improving user experience when configuring MQTT connections. Adjusted related documentation to reflect these changes.
This commit is contained in:
@@ -77,7 +77,7 @@ get mqtt.status
|
||||
|
||||
The MQTT bridge implementation provides:
|
||||
- Up to 6 MQTT connection slots with built-in presets
|
||||
- Built-in presets for LetsMesh Analyzer (US/EU), MeshMapper, MeshRank, Waev, Meshomatic, CascadiaMesh, and TennMesh
|
||||
- Built-in presets for LetsMesh Analyzer (US/EU), MeshMapper, MeshRank, Waev, Meshomatic, CascadiaMesh, EastIdahoMesh, and TennMesh
|
||||
- Custom broker support with username/password authentication
|
||||
- JWT (Ed25519 device signing) authentication for most preset brokers; TennMesh uses a fixed username/password (plain MQTT)
|
||||
- WSS (WebSocket Secure), direct MQTT/TLS, and plain MQTT (TennMesh) transport
|
||||
@@ -107,6 +107,7 @@ The MQTT bridge uses a slot-based architecture with up to 6 concurrent connectio
|
||||
| `nashmesh` | mqtt://mqtt.nashme.sh:1883 | Username/password (fixed in firmware) | Plain MQTT |
|
||||
| `chimesh` | wss://mqtt.chimesh.org:443 | JWT (Ed25519) | WSS |
|
||||
| `meshat.se` | mqtts://mqtt.meshat.se:8883 | Username/password (fixed in firmware) | MQTT over TLS |
|
||||
| `eastidahomesh` | wss://broker.eastidahomesh.net:443 | None | WSS |
|
||||
| `custom` | User-configured | Username/Password | MQTT or WSS |
|
||||
| `none` | (disabled) | — | — |
|
||||
|
||||
@@ -237,6 +238,7 @@ Each slot (1-6) supports the following commands:
|
||||
- `set mqttN.preset nashmesh` - Set slot N to NashMesh
|
||||
- `set mqttN.preset chimesh` - Set slot N to ChicagolandMesh
|
||||
- `set mqttN.preset meshat.se` - Set slot N to Meshat.se
|
||||
- `set mqttN.preset eastidahomesh` - Set slot N to EastIdahoMesh (WSS/TLS, no auth; packets on `meshcore/{IATA}/{PUBLIC_KEY}/packets`)
|
||||
- `set mqttN.preset custom` - Set slot N to custom broker (configure server/port/username/password)
|
||||
- `set mqttN.preset none` - Disable slot N
|
||||
- `set mqttN.server <hostname>` - Set custom server hostname for slot N
|
||||
@@ -313,7 +315,7 @@ When a slot's preset is `custom`, you can define a custom topic template using p
|
||||
|
||||
If no custom topic is set, custom slots default to: `meshcore/{iata}/{device}/{type}`
|
||||
|
||||
**Note:** Topic templates only apply to `custom` preset slots. Built-in presets (analyzer-us, analyzer-eu, meshmapper, meshrank, tennmesh, etc.) always use their hardcoded topic format.
|
||||
**Note:** Topic templates only apply to `custom` preset slots. Built-in presets (analyzer-us, analyzer-eu, meshmapper, meshrank, eastidahomesh, tennmesh, etc.) always use their hardcoded topic format.
|
||||
|
||||
### MQTT Shared Commands
|
||||
|
||||
@@ -322,6 +324,8 @@ These settings apply across all MQTT slots:
|
||||
#### Get Commands
|
||||
- `get mqtt.origin` - Get device origin name
|
||||
- `get mqtt.iata` - Get IATA code
|
||||
- `get mqtt.presets` - List available MQTT presets (paginated, comma-separated)
|
||||
- `get mqtt.presets <start>` - Continue list from index shown in `... next:<idx>`
|
||||
- `get mqtt.status` - Get MQTT status summary (connection info per slot)
|
||||
- `get mqtt.packets` - Get packet message setting (on/off)
|
||||
- `get mqtt.raw` - Get raw message setting (on/off)
|
||||
@@ -504,7 +508,7 @@ Minimal raw packet data for map integration.
|
||||
|
||||
### Slot-Based Preset System
|
||||
- Up to 6 concurrent MQTT connections (with PSRAM), 2 without PSRAM
|
||||
- Built-in presets for LetsMesh Analyzer (US/EU), MeshMapper, MeshRank, Waev, Meshomatic, CascadiaMesh, and TennMesh
|
||||
- Built-in presets for LetsMesh Analyzer (US/EU), MeshMapper, MeshRank, Waev, Meshomatic, CascadiaMesh, EastIdahoMesh, and TennMesh
|
||||
- Custom broker support with username/password auth and custom topic templates
|
||||
- JWT (Ed25519) for most preset brokers; MeshRank uses token-in-topic; TennMesh uses fixed username/password over plain MQTT
|
||||
- WSS (WebSocket Secure), direct MQTT over TLS, and plain MQTT (TennMesh)
|
||||
|
||||
@@ -41,6 +41,64 @@ static uint32_t _atoi(const char* sp) {
|
||||
return n;
|
||||
}
|
||||
|
||||
#ifdef WITH_MQTT_BRIDGE
|
||||
static int getMQTTPresetNameCount() {
|
||||
// Include virtual presets accepted by CLI parser.
|
||||
return MQTT_PRESET_COUNT + 2; // built-ins + custom + none
|
||||
}
|
||||
|
||||
static const char* getMQTTPresetNameByIndex(int index) {
|
||||
if (index < MQTT_PRESET_COUNT) return MQTT_PRESETS[index].name;
|
||||
if (index == MQTT_PRESET_COUNT) return MQTT_PRESET_CUSTOM;
|
||||
if (index == MQTT_PRESET_COUNT + 1) return MQTT_PRESET_NONE;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void formatMQTTPresetListReply(char* reply, size_t reply_size, int start) {
|
||||
if (!reply || reply_size == 0) return;
|
||||
reply[0] = '\0';
|
||||
|
||||
const int total = getMQTTPresetNameCount();
|
||||
if (start < 0 || start >= total) {
|
||||
snprintf(reply, reply_size, "Error: preset list start must be 0-%d", total - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
// Keep room for continuation marker and null terminator.
|
||||
const size_t reserve_for_next = 18;
|
||||
size_t used = 0;
|
||||
bool wrote_any = false;
|
||||
|
||||
int index = start;
|
||||
while (index < total) {
|
||||
const char* name = getMQTTPresetNameByIndex(index);
|
||||
if (!name) break;
|
||||
size_t name_len = strlen(name);
|
||||
size_t room = reply_size - used;
|
||||
if (room <= reserve_for_next) break;
|
||||
size_t needed = name_len + (wrote_any ? 1 : 0); // comma separator
|
||||
if (needed >= room - reserve_for_next) break;
|
||||
if (wrote_any) {
|
||||
reply[used++] = ',';
|
||||
}
|
||||
memcpy(reply + used, name, name_len);
|
||||
used += name_len;
|
||||
reply[used] = '\0';
|
||||
wrote_any = true;
|
||||
index++;
|
||||
}
|
||||
|
||||
if (!wrote_any) {
|
||||
strcpy(reply, "Error: list page too small");
|
||||
return;
|
||||
}
|
||||
|
||||
if (index < total) {
|
||||
snprintf(reply + used, reply_size - used, "... next:%d", index);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool isValidName(const char *n) {
|
||||
while (*n) {
|
||||
if (*n == '[' || *n == ']' || *n == '/' || *n == '\\' || *n == ':' || *n == ',' || *n == '?' || *n == '*') return false;
|
||||
@@ -1362,7 +1420,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
|
||||
}
|
||||
}
|
||||
} else {
|
||||
strcpy(reply, "Error: valid presets are: analyzer-us, analyzer-eu, meshmapper, meshrank, waev, meshomatic, cascadiamesh, tennmesh, nashmesh, custom, none");
|
||||
strcpy(reply, "Error: unknown preset. Use 'get mqtt.presets'");
|
||||
}
|
||||
} else if (memcmp(subcmd, "server ", 7) == 0) {
|
||||
StrHelper::strncpy(_prefs->mqtt_slot_host[slot], &subcmd[7], sizeof(_prefs->mqtt_slot_host[slot]));
|
||||
@@ -1605,6 +1663,23 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
|
||||
sprintf(reply, "> %s", _prefs->mqtt_origin);
|
||||
} else if (memcmp(config, "mqtt.iata", 9) == 0) {
|
||||
sprintf(reply, "> %s", _prefs->mqtt_iata);
|
||||
} else if (memcmp(config, "mqtt.presets", 12) == 0 && (config[12] == '\0' || config[12] == ' ')) {
|
||||
int start = 0;
|
||||
if (config[12] == ' ') {
|
||||
const char* start_arg = &config[13];
|
||||
if (*start_arg == '\0') {
|
||||
strcpy(reply, "Error: usage get mqtt.presets [start]");
|
||||
return;
|
||||
}
|
||||
for (const char* sp = start_arg; *sp; sp++) {
|
||||
if (*sp < '0' || *sp > '9') {
|
||||
strcpy(reply, "Error: usage get mqtt.presets [start]");
|
||||
return;
|
||||
}
|
||||
}
|
||||
start = (int)_atoi(start_arg);
|
||||
}
|
||||
formatMQTTPresetListReply(reply, 160, start);
|
||||
} else if (memcmp(config, "mqtt.status", 11) == 0) {
|
||||
MQTTBridge::formatMqttStatusReply(reply, 160, _prefs);
|
||||
} else if (memcmp(config, "mqtt.packets", 12) == 0) {
|
||||
|
||||
@@ -99,7 +99,7 @@ static const char ISRG_ROOT_X1[] PROGMEM =
|
||||
"-----END CERTIFICATE-----\n";
|
||||
|
||||
// Number of built-in presets
|
||||
static const int MQTT_PRESET_COUNT = 11;
|
||||
static const int MQTT_PRESET_COUNT = 12;
|
||||
|
||||
// Built-in preset definitions (stored in flash)
|
||||
static const MQTTPresetDef MQTT_PRESETS[MQTT_PRESET_COUNT] = {
|
||||
@@ -114,6 +114,7 @@ static const MQTTPresetDef MQTT_PRESETS[MQTT_PRESET_COUNT] = {
|
||||
{ "nashmesh", "mqtt://mqtt.nashme.sh:1883", nullptr, nullptr, MQTT_AUTH_USERPASS, MQTT_TOPIC_MESHCORE, 0, true, 55, "meshdev", "large4cats" },
|
||||
{ "chimesh", "wss://mqtt.chimesh.org:443", "mqtt.chimesh.org", ISRG_ROOT_X1, MQTT_AUTH_JWT, MQTT_TOPIC_MESHCORE, 0, true, 55, nullptr, nullptr },
|
||||
{ "meshat.se", "mqtts://mqtt.meshat.se:8883", nullptr, ISRG_ROOT_X1, MQTT_AUTH_USERPASS, MQTT_TOPIC_MESHCORE, 0, true, 55, "msh", "msh" },
|
||||
{ "eastidahomesh","wss://broker.eastidahomesh.net:443", nullptr, ISRG_ROOT_X1, MQTT_AUTH_NONE, MQTT_TOPIC_MESHCORE, 0, true, 55, nullptr, nullptr },
|
||||
};
|
||||
|
||||
// Find a preset by name, returns nullptr if not found
|
||||
|
||||
Reference in New Issue
Block a user