mirror of
https://github.com/Cyclenerd/meshcore-bot.git
synced 2026-03-28 17:42:46 +01:00
telemetry removed to use less bandwidth
This commit is contained in:
29
README.md
29
README.md
@@ -5,9 +5,9 @@ This script is a command bot that connects to a [MeshCore](https://github.com/me
|
|||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
> To prevent spam in public channels, this bot only responds in private channels!
|
> To prevent spam in public channels, this bot only responds in private channels!
|
||||||
|
|
||||||
The bot is also able to fetch and log status (uptime, TX air time, last SNR, noise floor...) and telemetry sensor data (currently only voltage) from a repeater node.
|
The bot is also able to fetch and log status data (uptime, TX air time, last SNR, noise floor...) from a repeater node.
|
||||||
The status and telemetry data is logged to a Comma-Separated Values (CSV) file.
|
The status data is logged to a Comma-Separated Values (CSV) file.
|
||||||
The interval at which the status and telemetry data is fetched can be configured.
|
The interval at which the status data is fetched can be configured.
|
||||||
This bot is ideal for testing MeshCore setup with repeater and distance of communication.
|
This bot is ideal for testing MeshCore setup with repeater and distance of communication.
|
||||||
|
|
||||||
| Client | Bot |
|
| Client | Bot |
|
||||||
@@ -48,14 +48,14 @@ This bot is ideal for testing MeshCore setup with repeater and distance of commu
|
|||||||
To run the bot use the following command:
|
To run the bot use the following command:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
node meshcore-bot.js --port [SERIAL_PORT] --repeater-public-key-prefix [REPEATER_PUBLIC_KEY_PREFIX] --repeater-password [REPEATER_PASSWORD] --repeater-interval [TELEMETRY_INTERVAL_MINUTES] --csv [CSV_FILE]
|
node meshcore-bot.js --port [SERIAL_PORT] --repeater-public-key-prefix [REPEATER_PUBLIC_KEY_PREFIX] --repeater-password [REPEATER_PASSWORD] --repeater-interval [STATUS_INTERVAL_MINUTES] --csv [CSV_FILE]
|
||||||
```
|
```
|
||||||
|
|
||||||
- `--port` or `-s` (optional): The serial port of the MeshCore device. Defaults to `/dev/cu.usbmodem1101`.
|
- `--port` or `-s` (optional): The serial port of the MeshCore device. Defaults to `/dev/cu.usbmodem1101`.
|
||||||
- `--repeater-public-key-prefix` or `-r` (optional): The public key prefix of a repeater node to fetch status and telemetry from. If provided, this feature is enabled.
|
- `--repeater-public-key-prefix` or `-r` (optional): The public key prefix of a repeater node to fetch status from. If provided, this feature is enabled.
|
||||||
- `--repeater-password` or `-p` (optional): The password for the repeater. By default, this is an empty string.
|
- `--repeater-password` or `-p` (optional): The password for the repeater. By default, this is an empty string.
|
||||||
- `--repeater-interval` or `-i` (optional): The interval in minutes at which status and telemetry data is retrieved from the repeater. The default value is `15`.
|
- `--repeater-interval` or `-i` (optional): The interval in minutes at which status data is retrieved from the repeater. The default value is `15`.
|
||||||
- `--csv` or `-c` (optional): The CSV file in which the repeater's status and telemetry data is to be logged. If this file is specified, the data will be logged in this file.
|
- `--csv` or `-c` (optional): The CSV file in which the repeater's status data is to be logged. If this file is specified, the data will be logged in this file.
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ node meshcore-bot.js --port [SERIAL_PORT] --repeater-public-key-prefix [REPEATER
|
|||||||
node meshcore-bot.js --port "/dev/ttyUSB0"
|
node meshcore-bot.js --port "/dev/ttyUSB0"
|
||||||
```
|
```
|
||||||
|
|
||||||
**With Repeater Status and Telemetry:**
|
**With Repeater Status:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
node meshcore-bot.js \
|
node meshcore-bot.js \
|
||||||
@@ -74,7 +74,8 @@ node meshcore-bot.js \
|
|||||||
--repeater-password "your-password" \
|
--repeater-password "your-password" \
|
||||||
--repeater-interval 30
|
--repeater-interval 30
|
||||||
```
|
```
|
||||||
This will connect to the device on `/dev/ttyUSB0` and fetch telemetry from the specified repeater every 30 minutes.
|
|
||||||
|
This will connect to the device on `/dev/ttyUSB0` and fetch status data from the specified repeater every 30 minutes.
|
||||||
|
|
||||||
**With Repeater and CSV Logging:**
|
**With Repeater and CSV Logging:**
|
||||||
|
|
||||||
@@ -84,17 +85,17 @@ node meshcore-bot.js \
|
|||||||
--repeater-public-key-prefix "935c6b694200644710a374c250c76f7aed9ec2ff3e60261447d4eda7c246ce5d" \
|
--repeater-public-key-prefix "935c6b694200644710a374c250c76f7aed9ec2ff3e60261447d4eda7c246ce5d" \
|
||||||
--repeater-password "your-password" \
|
--repeater-password "your-password" \
|
||||||
--repeater-interval 30 \
|
--repeater-interval 30 \
|
||||||
--csv "telemetry.csv"
|
--csv "status.csv"
|
||||||
```
|
```
|
||||||
|
|
||||||
This will do the same as the previous example, but it will also log the telemetry data to `telemetry.csv`.
|
This will do the same as the previous example, but it will also log the status data to `status.csv`.
|
||||||
|
|
||||||
Example CSV:
|
Example CSV:
|
||||||
|
|
||||||
```csv
|
```csv
|
||||||
timestamp,lpp_volts,batt_milli_volts,curr_tx_queue_len,noise_floor,last_rssi,n_packets_recv,n_packets_sent,total_air_time_secs,total_up_time_secs,n_sent_flood,n_sent_direct,n_recv_flood,n_recv_direct,err_events,last_snr,n_direct_dups,n_flood_dups
|
timestamp,batt_milli_volts,curr_tx_queue_len,noise_floor,last_rssi,n_packets_recv,n_packets_sent,total_air_time_secs,total_up_time_secs,n_sent_flood,n_sent_direct,n_recv_flood,n_recv_direct,err_events,last_snr,n_direct_dups,n_flood_dups
|
||||||
2025-09-12T19:06:07Z,3.97,3969,0,-111,-59,2029,1749,1399,700263,1672,77,1514,359,0,28,0,98
|
2025-09-12T19:06:07Z,3969,0,-111,-59,2029,1749,1399,700263,1672,77,1514,359,0,28,0,98
|
||||||
2025-09-12T19:08:32Z,3.96,3969,0,-110,-60,2033,1753,1401,700407,1676,77,1515,362,0,28,0,98
|
2025-09-12T19:08:32Z,3969,0,-110,-60,2033,1753,1401,700407,1676,77,1515,362,0,28,0,98
|
||||||
```
|
```
|
||||||
|
|
||||||
### Commands
|
### Commands
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
import { Constants, NodeJSSerialConnection, CayenneLpp } from "@liamcottle/meshcore.js";
|
import { Constants, NodeJSSerialConnection } from "@liamcottle/meshcore.js";
|
||||||
import yargs from 'yargs';
|
import yargs from 'yargs';
|
||||||
import { hideBin } from 'yargs/helpers';
|
import { hideBin } from 'yargs/helpers';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
@@ -19,7 +19,7 @@ const argv = yargs(hideBin(process.argv))
|
|||||||
.option('repeaterPublicKeyPrefix', {
|
.option('repeaterPublicKeyPrefix', {
|
||||||
alias: 'r',
|
alias: 'r',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'Public key of the repeater to fetch telemetry from'
|
description: 'Public key of the repeater to fetch status from'
|
||||||
})
|
})
|
||||||
.option('repeaterInterval', {
|
.option('repeaterInterval', {
|
||||||
alias: 'i',
|
alias: 'i',
|
||||||
@@ -36,7 +36,7 @@ const argv = yargs(hideBin(process.argv))
|
|||||||
.option('csv', {
|
.option('csv', {
|
||||||
alias: 'c',
|
alias: 'c',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'CSV file to log telemetry to'
|
description: 'CSV file to log status to'
|
||||||
})
|
})
|
||||||
.argv;
|
.argv;
|
||||||
|
|
||||||
@@ -45,16 +45,16 @@ const argv = yargs(hideBin(process.argv))
|
|||||||
const port = argv.port;
|
const port = argv.port;
|
||||||
const repeaterPublicKeyPrefix = argv.repeaterPublicKeyPrefix;
|
const repeaterPublicKeyPrefix = argv.repeaterPublicKeyPrefix;
|
||||||
const repeaterPassword = argv.repeaterPassword;
|
const repeaterPassword = argv.repeaterPassword;
|
||||||
const telemetryIntervalMinutes = argv.repeaterInterval;
|
const statusIntervalMinutes = argv.repeaterInterval;
|
||||||
const telemetryIntervalMs = telemetryIntervalMinutes * 60 * 1000;
|
const statusIntervalMs = statusIntervalMinutes * 60 * 1000;
|
||||||
const csvFile = argv.csv;
|
const csvFile = argv.csv;
|
||||||
|
|
||||||
console.log(`Connecting to ${port}`);
|
console.log(`Connecting to ${port}`);
|
||||||
if(repeaterPublicKeyPrefix){
|
if(repeaterPublicKeyPrefix){
|
||||||
console.log(`Repeater public key prefix: ${repeaterPublicKeyPrefix}`);
|
console.log(`Repeater public key prefix: ${repeaterPublicKeyPrefix}`);
|
||||||
console.log(`Telemetry interval: ${telemetryIntervalMinutes} minutes`);
|
console.log(`Status interval: ${statusIntervalMinutes} minutes`);
|
||||||
if (csvFile) {
|
if (csvFile) {
|
||||||
console.log(`Logging telemetry to: ${csvFile}`);
|
console.log(`Logging status to: ${csvFile}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ if(repeaterPublicKeyPrefix){
|
|||||||
const connection = new NodeJSSerialConnection(port);
|
const connection = new NodeJSSerialConnection(port);
|
||||||
|
|
||||||
let reconnectInterval;
|
let reconnectInterval;
|
||||||
let telemetryInterval;
|
let statusInterval;
|
||||||
|
|
||||||
// wait until connected
|
// wait until connected
|
||||||
connection.on("connected", async () => {
|
connection.on("connected", async () => {
|
||||||
@@ -122,12 +122,12 @@ connection.on("connected", async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(repeaterPublicKeyPrefix){
|
if(repeaterPublicKeyPrefix){
|
||||||
// Start telemetry fetching interval
|
// Start fetching interval
|
||||||
if (telemetryInterval) {
|
if (statusInterval) {
|
||||||
clearInterval(telemetryInterval);
|
clearInterval(statusInterval);
|
||||||
}
|
}
|
||||||
telemetryInterval = setInterval(() => getRepeaterTelemetry(repeaterPublicKeyPrefix, repeaterPassword), telemetryIntervalMs);
|
statusInterval = setInterval(() => getRepeater(repeaterPublicKeyPrefix, repeaterPassword), statusIntervalMs);
|
||||||
getRepeaterTelemetry(repeaterPublicKeyPrefix, repeaterPassword); // Also fetch immediately on connect
|
getRepeater(repeaterPublicKeyPrefix, repeaterPassword); // Also fetch immediately on connect
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -141,9 +141,9 @@ connection.on("disconnected", () => {
|
|||||||
await connection.connect();
|
await connection.connect();
|
||||||
}, 3000);
|
}, 3000);
|
||||||
|
|
||||||
if (telemetryInterval) {
|
if (statusInterval) {
|
||||||
clearInterval(telemetryInterval);
|
clearInterval(statusInterval);
|
||||||
telemetryInterval = null;
|
statusInterval = null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -192,8 +192,8 @@ connection.on(Constants.PushCodes.Advert, async (advert) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
async function getRepeaterTelemetry(publicKeyPrefix, repeaterPassword) {
|
async function getRepeater(publicKeyPrefix, repeaterPassword) {
|
||||||
console.log("Fetching repeater status and telemetry...");
|
console.log("Fetching repeater status...");
|
||||||
try {
|
try {
|
||||||
const contact = await connection.findContactByPublicKeyPrefix(Buffer.from(publicKeyPrefix, "hex"));
|
const contact = await connection.findContactByPublicKeyPrefix(Buffer.from(publicKeyPrefix, "hex"));
|
||||||
if(!contact){
|
if(!contact){
|
||||||
@@ -201,35 +201,19 @@ async function getRepeaterTelemetry(publicKeyPrefix, repeaterPassword) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// login to repeater and get repeater status telemetry
|
// login to repeater and get repeater status
|
||||||
console.log("Logging in to repeater...");
|
console.log("Logging in to repeater...");
|
||||||
await connection.login(contact.publicKey, repeaterPassword);
|
await connection.login(contact.publicKey, repeaterPassword);
|
||||||
// get repeater status
|
// get repeater status
|
||||||
console.log("Fetching status...");
|
console.log("Fetching status...");
|
||||||
const timestamp = getTimestamp(); // Store timestamp of first fetch for CSV
|
const timestamp = getTimestamp(); // Store timestamp of first fetch for CSV
|
||||||
const status = await connection.getStatus(contact.publicKey);
|
const status = await connection.getStatus(contact.publicKey, 5000);
|
||||||
console.log(`[${timestamp}] Repeater status`, status);
|
console.log(`[${timestamp}] Repeater status`, status);
|
||||||
// get repeater telemetry
|
|
||||||
console.log("Fetching telemetry...");
|
|
||||||
const telemetry = await connection.getTelemetry(contact.publicKey);
|
|
||||||
console.log(`[${getTimestamp()}] Repeater telemetry`, telemetry);
|
|
||||||
// parse telemetry
|
|
||||||
const parsedTelemetry = CayenneLpp.parse(telemetry.lppSensorData);
|
|
||||||
console.log(`[${getTimestamp()}] Decoded repeater telemetry`, parsedTelemetry);
|
|
||||||
// find battery voltage telemetry on channel 1
|
|
||||||
const lpp_volts = parsedTelemetry.find((item) => item.channel === 1 && item.type === CayenneLpp.LPP_VOLTAGE)?.value;
|
|
||||||
|
|
||||||
if(lpp_volts !== undefined) {
|
|
||||||
console.log(`LPP Voltage: ${lpp_volts} V`);
|
|
||||||
} else {
|
|
||||||
console.log("LPP Voltage not found in telemetry");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (csvFile) {
|
if (csvFile) {
|
||||||
console.log("Write to CSV file...");
|
console.log("Write to CSV file...");
|
||||||
const header = [
|
const header = [
|
||||||
'timestamp',
|
'timestamp',
|
||||||
'lpp_volts',
|
|
||||||
'batt_milli_volts',
|
'batt_milli_volts',
|
||||||
'curr_tx_queue_len',
|
'curr_tx_queue_len',
|
||||||
'noise_floor',
|
'noise_floor',
|
||||||
@@ -249,7 +233,6 @@ async function getRepeaterTelemetry(publicKeyPrefix, repeaterPassword) {
|
|||||||
].join(',') + '\n';
|
].join(',') + '\n';
|
||||||
const statusValues = [
|
const statusValues = [
|
||||||
timestamp,
|
timestamp,
|
||||||
lpp_volts,
|
|
||||||
status.batt_milli_volts,
|
status.batt_milli_volts,
|
||||||
status.curr_tx_queue_len,
|
status.curr_tx_queue_len,
|
||||||
status.noise_floor,
|
status.noise_floor,
|
||||||
@@ -274,7 +257,7 @@ async function getRepeaterTelemetry(publicKeyPrefix, repeaterPassword) {
|
|||||||
}
|
}
|
||||||
console.log("Done, waiting for the next interval.");
|
console.log("Done, waiting for the next interval.");
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
console.error("Error fetching repeater status or telemetry", e);
|
console.error("Error fetching repeater status!", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user