diff --git a/README.md b/README.md index f30a98d..8711011 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,9 @@ This script is a command bot that connects to a [MeshCore](https://github.com/me > [!IMPORTANT] > 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 status and telemetry 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 bot is also able to fetch and log status data (uptime, TX air time, last SNR, noise floor...) from a repeater node. +The status data is logged to a Comma-Separated Values (CSV) file. +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. | 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: ```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`. -- `--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-interval` or `-i` (optional): The interval in minutes at which status and telemetry 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. +- `--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 data is to be logged. If this file is specified, the data will be logged in this file. ### Examples @@ -65,7 +65,7 @@ node meshcore-bot.js --port [SERIAL_PORT] --repeater-public-key-prefix [REPEATER node meshcore-bot.js --port "/dev/ttyUSB0" ``` -**With Repeater Status and Telemetry:** +**With Repeater Status:** ```bash node meshcore-bot.js \ @@ -74,7 +74,8 @@ node meshcore-bot.js \ --repeater-password "your-password" \ --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:** @@ -84,17 +85,17 @@ node meshcore-bot.js \ --repeater-public-key-prefix "935c6b694200644710a374c250c76f7aed9ec2ff3e60261447d4eda7c246ce5d" \ --repeater-password "your-password" \ --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: ```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 -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:08:32Z,3.96,3969,0,-110,-60,2033,1753,1401,700407,1676,77,1515,362,0,28,0,98 +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,3969,0,-111,-59,2029,1749,1399,700263,1672,77,1514,359,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 diff --git a/meshcore-bot.js b/meshcore-bot.js index 816ea15..00ee680 100755 --- a/meshcore-bot.js +++ b/meshcore-bot.js @@ -1,6 +1,6 @@ #!/usr/bin/env node -import { Constants, NodeJSSerialConnection, CayenneLpp } from "@liamcottle/meshcore.js"; +import { Constants, NodeJSSerialConnection } from "@liamcottle/meshcore.js"; import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; import fs from 'fs'; @@ -19,7 +19,7 @@ const argv = yargs(hideBin(process.argv)) .option('repeaterPublicKeyPrefix', { alias: 'r', type: 'string', - description: 'Public key of the repeater to fetch telemetry from' + description: 'Public key of the repeater to fetch status from' }) .option('repeaterInterval', { alias: 'i', @@ -36,7 +36,7 @@ const argv = yargs(hideBin(process.argv)) .option('csv', { alias: 'c', type: 'string', - description: 'CSV file to log telemetry to' + description: 'CSV file to log status to' }) .argv; @@ -45,16 +45,16 @@ const argv = yargs(hideBin(process.argv)) const port = argv.port; const repeaterPublicKeyPrefix = argv.repeaterPublicKeyPrefix; const repeaterPassword = argv.repeaterPassword; -const telemetryIntervalMinutes = argv.repeaterInterval; -const telemetryIntervalMs = telemetryIntervalMinutes * 60 * 1000; +const statusIntervalMinutes = argv.repeaterInterval; +const statusIntervalMs = statusIntervalMinutes * 60 * 1000; const csvFile = argv.csv; console.log(`Connecting to ${port}`); if(repeaterPublicKeyPrefix){ console.log(`Repeater public key prefix: ${repeaterPublicKeyPrefix}`); - console.log(`Telemetry interval: ${telemetryIntervalMinutes} minutes`); + console.log(`Status interval: ${statusIntervalMinutes} minutes`); 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); let reconnectInterval; -let telemetryInterval; +let statusInterval; // wait until connected connection.on("connected", async () => { @@ -122,12 +122,12 @@ connection.on("connected", async () => { } if(repeaterPublicKeyPrefix){ - // Start telemetry fetching interval - if (telemetryInterval) { - clearInterval(telemetryInterval); + // Start fetching interval + if (statusInterval) { + clearInterval(statusInterval); } - telemetryInterval = setInterval(() => getRepeaterTelemetry(repeaterPublicKeyPrefix, repeaterPassword), telemetryIntervalMs); - getRepeaterTelemetry(repeaterPublicKeyPrefix, repeaterPassword); // Also fetch immediately on connect + statusInterval = setInterval(() => getRepeater(repeaterPublicKeyPrefix, repeaterPassword), statusIntervalMs); + getRepeater(repeaterPublicKeyPrefix, repeaterPassword); // Also fetch immediately on connect } }); @@ -141,9 +141,9 @@ connection.on("disconnected", () => { await connection.connect(); }, 3000); - if (telemetryInterval) { - clearInterval(telemetryInterval); - telemetryInterval = null; + if (statusInterval) { + clearInterval(statusInterval); + statusInterval = null; } }); @@ -192,8 +192,8 @@ connection.on(Constants.PushCodes.Advert, async (advert) => { } }); -async function getRepeaterTelemetry(publicKeyPrefix, repeaterPassword) { - console.log("Fetching repeater status and telemetry..."); +async function getRepeater(publicKeyPrefix, repeaterPassword) { + console.log("Fetching repeater status..."); try { const contact = await connection.findContactByPublicKeyPrefix(Buffer.from(publicKeyPrefix, "hex")); if(!contact){ @@ -201,35 +201,19 @@ async function getRepeaterTelemetry(publicKeyPrefix, repeaterPassword) { return; } - // login to repeater and get repeater status telemetry + // login to repeater and get repeater status console.log("Logging in to repeater..."); await connection.login(contact.publicKey, repeaterPassword); // get repeater status console.log("Fetching status..."); 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); - // 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) { console.log("Write to CSV file..."); const header = [ 'timestamp', - 'lpp_volts', 'batt_milli_volts', 'curr_tx_queue_len', 'noise_floor', @@ -249,7 +233,6 @@ async function getRepeaterTelemetry(publicKeyPrefix, repeaterPassword) { ].join(',') + '\n'; const statusValues = [ timestamp, - lpp_volts, status.batt_milli_volts, status.curr_tx_queue_len, status.noise_floor, @@ -274,7 +257,7 @@ async function getRepeaterTelemetry(publicKeyPrefix, repeaterPassword) { } console.log("Done, waiting for the next interval."); } catch(e) { - console.error("Error fetching repeater status or telemetry", e); + console.error("Error fetching repeater status!", e); } }