mirror of
https://github.com/meshtastic/firmware.git
synced 2026-02-04 16:11:54 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
577499f126 | ||
|
|
09eac5e692 | ||
|
|
e9c551a419 | ||
|
|
39bac0db01 | ||
|
|
e7306448be | ||
|
|
51f9e537de | ||
|
|
715918d5ac | ||
|
|
a0e2b06970 | ||
|
|
bc702b18d8 | ||
|
|
5e2d8eac6a | ||
|
|
971543fab3 |
@@ -50,7 +50,6 @@ build_flags = -Wno-missing-field-initializers
|
|||||||
-DRADIOLIB_EXCLUDE_APRS=1
|
-DRADIOLIB_EXCLUDE_APRS=1
|
||||||
-DRADIOLIB_EXCLUDE_LORAWAN=1
|
-DRADIOLIB_EXCLUDE_LORAWAN=1
|
||||||
-DMESHTASTIC_EXCLUDE_DROPZONE=1
|
-DMESHTASTIC_EXCLUDE_DROPZONE=1
|
||||||
-DMESHTASTIC_EXCLUDE_REPLYBOT=1
|
|
||||||
-DMESHTASTIC_EXCLUDE_REMOTEHARDWARE=1
|
-DMESHTASTIC_EXCLUDE_REMOTEHARDWARE=1
|
||||||
-DMESHTASTIC_EXCLUDE_HEALTH_TELEMETRY=1
|
-DMESHTASTIC_EXCLUDE_HEALTH_TELEMETRY=1
|
||||||
-DMESHTASTIC_EXCLUDE_POWERSTRESS=1 ; exclude power stress test module from main firmware
|
-DMESHTASTIC_EXCLUDE_POWERSTRESS=1 ; exclude power stress test module from main firmware
|
||||||
|
|||||||
@@ -459,8 +459,6 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
|||||||
}
|
}
|
||||||
// if it's not HIGH - check the battery
|
// if it's not HIGH - check the battery
|
||||||
#endif
|
#endif
|
||||||
// If we have an EXT_PWR_DETECT pin and it indicates no external power, believe it.
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// technically speaking this should work for all(?) NRF52 boards
|
// technically speaking this should work for all(?) NRF52 boards
|
||||||
// but needs testing across multiple devices. NRF52 USB would not even work if
|
// but needs testing across multiple devices. NRF52 USB would not even work if
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
#include "graphics/niche/InkHUD/Tile.h"
|
|
||||||
#include <cstdint>
|
|
||||||
#ifdef MESHTASTIC_INCLUDE_INKHUD
|
#ifdef MESHTASTIC_INCLUDE_INKHUD
|
||||||
|
|
||||||
#include "./Applet.h"
|
#include "./Applet.h"
|
||||||
@@ -787,16 +785,6 @@ void InkHUD::Applet::drawHeader(std::string text)
|
|||||||
drawPixel(x, 0, BLACK);
|
drawPixel(x, 0, BLACK);
|
||||||
drawPixel(x, headerDivY, BLACK); // Dotted 50%
|
drawPixel(x, headerDivY, BLACK); // Dotted 50%
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dither near battery
|
|
||||||
if (settings->optionalFeatures.batteryIcon) {
|
|
||||||
constexpr uint16_t ditherSizePx = 4;
|
|
||||||
Tile *batteryTile = ((Applet *)inkhud->getSystemApplet("BatteryIcon"))->getTile();
|
|
||||||
const uint16_t batteryTileLeft = batteryTile->getLeft();
|
|
||||||
const uint16_t batteryTileTop = batteryTile->getTop();
|
|
||||||
const uint16_t batteryTileHeight = batteryTile->getHeight();
|
|
||||||
hatchRegion(batteryTileLeft - ditherSizePx, batteryTileTop, ditherSizePx, batteryTileHeight, 2, WHITE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the height of the standard applet header
|
// Get the height of the standard applet header
|
||||||
|
|||||||
@@ -48,27 +48,37 @@ int InkHUD::BatteryIconApplet::onPowerStatusUpdate(const meshtastic::Status *sta
|
|||||||
|
|
||||||
void InkHUD::BatteryIconApplet::onRender(bool full)
|
void InkHUD::BatteryIconApplet::onRender(bool full)
|
||||||
{
|
{
|
||||||
// Clear the region beneath the tile, including the border
|
// Fill entire tile
|
||||||
|
// - size of icon controlled by size of tile
|
||||||
|
int16_t l = 0;
|
||||||
|
int16_t t = 0;
|
||||||
|
uint16_t w = width();
|
||||||
|
int16_t h = height();
|
||||||
|
|
||||||
|
// Clear the region beneath the tile
|
||||||
// Most applets are drawing onto an empty frame buffer and don't need to do this
|
// Most applets are drawing onto an empty frame buffer and don't need to do this
|
||||||
// We do need to do this with the battery though, as it is an "overlay"
|
// We do need to do this with the battery though, as it is an "overlay"
|
||||||
fillRect(0, 0, width(), height(), WHITE);
|
fillRect(l, t, w, h, WHITE);
|
||||||
|
|
||||||
|
// Vertical centerline
|
||||||
|
const int16_t m = t + (h / 2);
|
||||||
|
|
||||||
// =====================
|
// =====================
|
||||||
// Draw battery outline
|
// Draw battery outline
|
||||||
// =====================
|
// =====================
|
||||||
|
|
||||||
// Positive terminal "bump"
|
// Positive terminal "bump"
|
||||||
|
const int16_t &bumpL = l;
|
||||||
|
const uint16_t bumpH = h / 2;
|
||||||
|
const int16_t bumpT = m - (bumpH / 2);
|
||||||
constexpr uint16_t bumpW = 2;
|
constexpr uint16_t bumpW = 2;
|
||||||
const int16_t &bumpL = 1;
|
|
||||||
const uint16_t bumpH = (height() - 2) / 2;
|
|
||||||
const int16_t bumpT = (1 + ((height() - 2) / 2)) - (bumpH / 2);
|
|
||||||
fillRect(bumpL, bumpT, bumpW, bumpH, BLACK);
|
fillRect(bumpL, bumpT, bumpW, bumpH, BLACK);
|
||||||
|
|
||||||
// Main body of battery
|
// Main body of battery
|
||||||
const int16_t bodyL = 1 + bumpW;
|
const int16_t bodyL = bumpL + bumpW;
|
||||||
const int16_t &bodyT = 1;
|
const int16_t &bodyT = t;
|
||||||
const int16_t &bodyH = height() - 2; // Handle top/bottom padding
|
const int16_t &bodyH = h;
|
||||||
const int16_t bodyW = (width() - 1) - bumpW; // Handle 1px left pad
|
const int16_t bodyW = w - bumpW;
|
||||||
drawRect(bodyL, bodyT, bodyW, bodyH, BLACK);
|
drawRect(bodyL, bodyT, bodyW, bodyH, BLACK);
|
||||||
|
|
||||||
// Erase join between bump and body
|
// Erase join between bump and body
|
||||||
@@ -79,13 +89,12 @@ void InkHUD::BatteryIconApplet::onRender(bool full)
|
|||||||
// ===================
|
// ===================
|
||||||
|
|
||||||
constexpr int16_t slicePad = 2;
|
constexpr int16_t slicePad = 2;
|
||||||
int16_t sliceL = bodyL + slicePad;
|
const int16_t sliceL = bodyL + slicePad;
|
||||||
const int16_t sliceT = bodyT + slicePad;
|
const int16_t sliceT = bodyT + slicePad;
|
||||||
const uint16_t sliceH = bodyH - (slicePad * 2);
|
const uint16_t sliceH = bodyH - (slicePad * 2);
|
||||||
uint16_t sliceW = bodyW - (slicePad * 2);
|
uint16_t sliceW = bodyW - (slicePad * 2);
|
||||||
|
|
||||||
sliceW = (sliceW * socRounded) / 100; // Apply percentage
|
sliceW = (sliceW * socRounded) / 100; // Apply percentage
|
||||||
sliceL += ((bodyW - (slicePad * 2)) - sliceW); // Shift slice to the battery's negative terminal, correcting drain direction
|
|
||||||
|
|
||||||
hatchRegion(sliceL, sliceT, sliceW, sliceH, 2, BLACK);
|
hatchRegion(sliceL, sliceT, sliceW, sliceH, 2, BLACK);
|
||||||
drawRect(sliceL, sliceT, sliceW, sliceH, BLACK);
|
drawRect(sliceL, sliceT, sliceW, sliceH, BLACK);
|
||||||
|
|||||||
@@ -510,10 +510,10 @@ void InkHUD::WindowManager::placeSystemTiles()
|
|||||||
const uint16_t batteryIconWidth = batteryIconHeight * 1.8;
|
const uint16_t batteryIconWidth = batteryIconHeight * 1.8;
|
||||||
inkhud->getSystemApplet("BatteryIcon")
|
inkhud->getSystemApplet("BatteryIcon")
|
||||||
->getTile()
|
->getTile()
|
||||||
->setRegion(inkhud->width() - batteryIconWidth - 1, // x
|
->setRegion(inkhud->width() - batteryIconWidth, // x
|
||||||
1, // y
|
2, // y
|
||||||
batteryIconWidth + 1, // width
|
batteryIconWidth, // width
|
||||||
batteryIconHeight + 2); // height
|
batteryIconHeight); // height
|
||||||
|
|
||||||
// Note: the tiles of placeholder and menu applets are manipulated specially
|
// Note: the tiles of placeholder and menu applets are manipulated specially
|
||||||
// - menuApplet borrows user tiles
|
// - menuApplet borrows user tiles
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "NodeDB.h"
|
#include "NodeDB.h"
|
||||||
#include "PowerFSM.h"
|
#include "PowerFSM.h"
|
||||||
#include "PowerMon.h"
|
#include "PowerMon.h"
|
||||||
|
#include "RadioLibInterface.h"
|
||||||
#include "ReliableRouter.h"
|
#include "ReliableRouter.h"
|
||||||
#include "airtime.h"
|
#include "airtime.h"
|
||||||
#include "buzz.h"
|
#include "buzz.h"
|
||||||
@@ -193,6 +194,8 @@ bool kb_found = false;
|
|||||||
// global bool to record that on-screen keyboard (OSK) is present
|
// global bool to record that on-screen keyboard (OSK) is present
|
||||||
bool osk_found = false;
|
bool osk_found = false;
|
||||||
|
|
||||||
|
unsigned long last_listen = 0;
|
||||||
|
|
||||||
// The I2C address of the RTC Module (if found)
|
// The I2C address of the RTC Module (if found)
|
||||||
ScanI2C::DeviceAddress rtc_found = ScanI2C::ADDRESS_NONE;
|
ScanI2C::DeviceAddress rtc_found = ScanI2C::ADDRESS_NONE;
|
||||||
// The I2C address of the Accelerometer (if found)
|
// The I2C address of the Accelerometer (if found)
|
||||||
@@ -1166,6 +1169,12 @@ void loop()
|
|||||||
#endif
|
#endif
|
||||||
power->powerCommandsCheck();
|
power->powerCommandsCheck();
|
||||||
|
|
||||||
|
if (RadioLibInterface::instance != nullptr && !Throttle::isWithinTimespanMs(last_listen, 1000 * 60) &&
|
||||||
|
!(RadioLibInterface::instance->isSending() || RadioLibInterface::instance->isActivelyReceiving())) {
|
||||||
|
RadioLibInterface::instance->startReceive();
|
||||||
|
LOG_DEBUG("attempting AGC reset");
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_STACK
|
#ifdef DEBUG_STACK
|
||||||
static uint32_t lastPrint = 0;
|
static uint32_t lastPrint = 0;
|
||||||
if (!Throttle::isWithinTimespanMs(lastPrint, 10 * 1000L)) {
|
if (!Throttle::isWithinTimespanMs(lastPrint, 10 * 1000L)) {
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ extern ScanI2C::DeviceAddress cardkb_found;
|
|||||||
extern uint8_t kb_model;
|
extern uint8_t kb_model;
|
||||||
extern bool kb_found;
|
extern bool kb_found;
|
||||||
extern bool osk_found;
|
extern bool osk_found;
|
||||||
|
extern unsigned long last_listen;
|
||||||
extern ScanI2C::DeviceAddress rtc_found;
|
extern ScanI2C::DeviceAddress rtc_found;
|
||||||
extern ScanI2C::DeviceAddress accelerometer_found;
|
extern ScanI2C::DeviceAddress accelerometer_found;
|
||||||
extern ScanI2C::FoundDevice rgb_found;
|
extern ScanI2C::FoundDevice rgb_found;
|
||||||
|
|||||||
@@ -2223,8 +2223,8 @@ bool NodeDB::restorePreferences(meshtastic_AdminMessage_BackupLocation location,
|
|||||||
} else if (location == meshtastic_AdminMessage_BackupLocation_SD) {
|
} else if (location == meshtastic_AdminMessage_BackupLocation_SD) {
|
||||||
// TODO: After more mainline SD card support
|
// TODO: After more mainline SD card support
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
return success;
|
return success;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Record an error that should be reported via analytics
|
/// Record an error that should be reported via analytics
|
||||||
|
|||||||
@@ -514,6 +514,8 @@ void RadioLibInterface::handleReceiveInterrupt()
|
|||||||
|
|
||||||
void RadioLibInterface::startReceive()
|
void RadioLibInterface::startReceive()
|
||||||
{
|
{
|
||||||
|
// Note the updated timestamp, to avoid unneeded AGC resets
|
||||||
|
last_listen = millis();
|
||||||
isReceiving = true;
|
isReceiving = true;
|
||||||
powerMon->setState(meshtastic_PowerMon_State_Lora_RXOn);
|
powerMon->setState(meshtastic_PowerMon_State_Lora_RXOn);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,6 @@
|
|||||||
#include "modules/StatusLEDModule.h"
|
#include "modules/StatusLEDModule.h"
|
||||||
#include "modules/SystemCommandsModule.h"
|
#include "modules/SystemCommandsModule.h"
|
||||||
#endif
|
#endif
|
||||||
#if !MESHTASTIC_EXCLUDE_REPLYBOT
|
|
||||||
#include "ReplyBotModule.h"
|
|
||||||
#endif
|
|
||||||
#if !MESHTASTIC_EXCLUDE_PKI
|
#if !MESHTASTIC_EXCLUDE_PKI
|
||||||
#include "KeyVerificationModule.h"
|
#include "KeyVerificationModule.h"
|
||||||
#endif
|
#endif
|
||||||
@@ -115,9 +112,7 @@ void setupModules()
|
|||||||
#if defined(LED_CHARGE) || defined(LED_PAIRING)
|
#if defined(LED_CHARGE) || defined(LED_PAIRING)
|
||||||
statusLEDModule = new StatusLEDModule();
|
statusLEDModule = new StatusLEDModule();
|
||||||
#endif
|
#endif
|
||||||
#if !MESHTASTIC_EXCLUDE_REPLYBOT
|
|
||||||
new ReplyBotModule();
|
|
||||||
#endif
|
|
||||||
#if !MESHTASTIC_EXCLUDE_ADMIN
|
#if !MESHTASTIC_EXCLUDE_ADMIN
|
||||||
adminModule = new AdminModule();
|
adminModule = new AdminModule();
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,183 +0,0 @@
|
|||||||
#include "configuration.h"
|
|
||||||
#if !MESHTASTIC_EXCLUDE_REPLYBOT
|
|
||||||
/*
|
|
||||||
* ReplyBotModule.cpp
|
|
||||||
*
|
|
||||||
* This module implements a simple reply bot for the Meshtastic firmware. It listens for
|
|
||||||
* specific text commands ("/ping", "/hello" and "/test") delivered either via a direct
|
|
||||||
* message (DM) or a broadcast on the primary channel. When a supported command is
|
|
||||||
* received the bot responds with a short status message that includes the hop count
|
|
||||||
* (minimum number of relays), RSSI and SNR of the received packet. To avoid spamming
|
|
||||||
* the network it enforces a per‑sender cooldown between responses. By default the
|
|
||||||
* module is enabled; define MESHTASTIC_EXCLUDE_REPLYBOT at build time to exclude it
|
|
||||||
* entirely. See the official firmware documentation for guidance on adding modules.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "Channels.h"
|
|
||||||
#include "MeshService.h"
|
|
||||||
#include "NodeDB.h"
|
|
||||||
#include "ReplyBotModule.h"
|
|
||||||
#include "mesh/MeshTypes.h"
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <cctype>
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
//
|
|
||||||
// Rate limiting data structures
|
|
||||||
//
|
|
||||||
// Each sender is tracked in a small ring buffer. When a message arrives from a
|
|
||||||
// sender we check the last time we responded to them. If the difference is
|
|
||||||
// less than the configured cooldown (different values for DM vs broadcast)
|
|
||||||
// the message is ignored; otherwise we update the last response time and
|
|
||||||
// proceed with replying.
|
|
||||||
|
|
||||||
struct ReplyBotCooldownEntry {
|
|
||||||
uint32_t from = 0;
|
|
||||||
uint32_t lastMs = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
static constexpr uint8_t REPLYBOT_COOLDOWN_SLOTS = 8; // ring buffer size
|
|
||||||
static constexpr uint32_t REPLYBOT_DM_COOLDOWN_MS = 15 * 1000; // 15 seconds for DMs
|
|
||||||
static constexpr uint32_t REPLYBOT_LF_COOLDOWN_MS = 60 * 1000; // 60 seconds for LongFast broadcasts
|
|
||||||
|
|
||||||
static ReplyBotCooldownEntry replybotCooldown[REPLYBOT_COOLDOWN_SLOTS];
|
|
||||||
static uint8_t replybotCooldownIdx = 0;
|
|
||||||
|
|
||||||
// Return true if a reply should be rate‑limited for this sender, updating the
|
|
||||||
// entry table as needed.
|
|
||||||
static bool replybotRateLimited(uint32_t from, uint32_t cooldownMs)
|
|
||||||
{
|
|
||||||
const uint32_t now = millis();
|
|
||||||
for (auto &e : replybotCooldown) {
|
|
||||||
if (e.from == from) {
|
|
||||||
// Found existing entry; check if cooldown expired
|
|
||||||
if ((uint32_t)(now - e.lastMs) < cooldownMs) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
e.lastMs = now;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// No entry found – insert new sender into the ring
|
|
||||||
replybotCooldown[replybotCooldownIdx].from = from;
|
|
||||||
replybotCooldown[replybotCooldownIdx].lastMs = now;
|
|
||||||
replybotCooldownIdx = (replybotCooldownIdx + 1) % REPLYBOT_COOLDOWN_SLOTS;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Constructor – registers a single text port and marks the module promiscuous
|
|
||||||
// so that broadcast messages on the primary channel are visible.
|
|
||||||
ReplyBotModule::ReplyBotModule() : SinglePortModule("replybot", meshtastic_PortNum_TEXT_MESSAGE_APP)
|
|
||||||
{
|
|
||||||
isPromiscuous = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReplyBotModule::setup()
|
|
||||||
{
|
|
||||||
// In future we may add a protobuf configuration; for now the module is
|
|
||||||
// always enabled when compiled in.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine whether we want to process this packet. We only care about
|
|
||||||
// plain text messages addressed to our port.
|
|
||||||
bool ReplyBotModule::wantPacket(const meshtastic_MeshPacket *p)
|
|
||||||
{
|
|
||||||
return (p && p->decoded.portnum == ourPortNum);
|
|
||||||
}
|
|
||||||
|
|
||||||
ProcessMessage ReplyBotModule::handleReceived(const meshtastic_MeshPacket &mp)
|
|
||||||
{
|
|
||||||
// Accept only direct messages to us or broadcasts on the Primary channel
|
|
||||||
// (regardless of modem preset: LongFast, MediumFast, etc).
|
|
||||||
|
|
||||||
const uint32_t ourNode = nodeDB->getNodeNum();
|
|
||||||
const bool isDM = (mp.to == ourNode);
|
|
||||||
const bool isPrimaryChannel = (mp.channel == channels.getPrimaryIndex()) && isBroadcast(mp.to);
|
|
||||||
if (!isDM && !isPrimaryChannel) {
|
|
||||||
return ProcessMessage::CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore empty payloads
|
|
||||||
if (mp.decoded.payload.size == 0) {
|
|
||||||
return ProcessMessage::CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy payload into a null‑terminated buffer
|
|
||||||
char buf[260];
|
|
||||||
memset(buf, 0, sizeof(buf));
|
|
||||||
size_t n = mp.decoded.payload.size;
|
|
||||||
if (n > sizeof(buf) - 1)
|
|
||||||
n = sizeof(buf) - 1;
|
|
||||||
memcpy(buf, mp.decoded.payload.bytes, n);
|
|
||||||
|
|
||||||
// React only to supported slash commands
|
|
||||||
if (!isCommand(buf)) {
|
|
||||||
return ProcessMessage::CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply rate limiting per sender depending on DM/broadcast
|
|
||||||
const uint32_t cooldownMs = isDM ? REPLYBOT_DM_COOLDOWN_MS : REPLYBOT_LF_COOLDOWN_MS;
|
|
||||||
if (replybotRateLimited(mp.from, cooldownMs)) {
|
|
||||||
return ProcessMessage::CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute hop count indicator – if the relay_node is non‑zero we know
|
|
||||||
// there was at least one relay. Some firmware builds support a hop_start
|
|
||||||
// field which could be used for more accurate counts, but here we use
|
|
||||||
// the available relay_node flag only.
|
|
||||||
// int hopsAway = mp.hop_start - mp.hop_limit;
|
|
||||||
int hopsAway = getHopsAway(mp);
|
|
||||||
|
|
||||||
// Normalize RSSI: if positive adjust down by 200 to align with typical values
|
|
||||||
int rssi = mp.rx_rssi;
|
|
||||||
if (rssi > 0) {
|
|
||||||
rssi -= 200;
|
|
||||||
}
|
|
||||||
float snr = mp.rx_snr;
|
|
||||||
|
|
||||||
// Build the reply message and send it back via DM
|
|
||||||
char reply[96];
|
|
||||||
snprintf(reply, sizeof(reply), "🎙️ Mic Check : %d Hops away | RSSI %d | SNR %.1f", hopsAway, rssi, snr);
|
|
||||||
sendDm(mp, reply);
|
|
||||||
return ProcessMessage::CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the message starts with one of the supported commands. Leading
|
|
||||||
// whitespace is skipped and commands must be followed by end‑of‑string or
|
|
||||||
// whitespace.
|
|
||||||
bool ReplyBotModule::isCommand(const char *msg) const
|
|
||||||
{
|
|
||||||
if (!msg)
|
|
||||||
return false;
|
|
||||||
while (*msg == ' ' || *msg == '\t')
|
|
||||||
msg++;
|
|
||||||
auto isEndOrSpace = [](char c) { return c == '\0' || std::isspace(static_cast<unsigned char>(c)); };
|
|
||||||
if (strncmp(msg, "/ping", 5) == 0 && isEndOrSpace(msg[5]))
|
|
||||||
return true;
|
|
||||||
if (strncmp(msg, "/hello", 6) == 0 && isEndOrSpace(msg[6]))
|
|
||||||
return true;
|
|
||||||
if (strncmp(msg, "/test", 5) == 0 && isEndOrSpace(msg[5]))
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send a direct message back to the originating node.
|
|
||||||
void ReplyBotModule::sendDm(const meshtastic_MeshPacket &rx, const char *text)
|
|
||||||
{
|
|
||||||
if (!text)
|
|
||||||
return;
|
|
||||||
meshtastic_MeshPacket *p = allocDataPacket();
|
|
||||||
p->to = rx.from;
|
|
||||||
p->channel = rx.channel;
|
|
||||||
p->want_ack = false;
|
|
||||||
p->decoded.want_response = false;
|
|
||||||
size_t len = strlen(text);
|
|
||||||
if (len > sizeof(p->decoded.payload.bytes)) {
|
|
||||||
len = sizeof(p->decoded.payload.bytes);
|
|
||||||
}
|
|
||||||
p->decoded.payload.size = len;
|
|
||||||
memcpy(p->decoded.payload.bytes, text, len);
|
|
||||||
service->sendToMesh(p);
|
|
||||||
}
|
|
||||||
#endif // MESHTASTIC_EXCLUDE_REPLYBOT
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "configuration.h"
|
|
||||||
#if !MESHTASTIC_EXCLUDE_REPLYBOT
|
|
||||||
#include "SinglePortModule.h"
|
|
||||||
#include "mesh/generated/meshtastic/mesh.pb.h"
|
|
||||||
|
|
||||||
class ReplyBotModule : public SinglePortModule
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ReplyBotModule();
|
|
||||||
void setup() override;
|
|
||||||
bool wantPacket(const meshtastic_MeshPacket *p) override;
|
|
||||||
ProcessMessage handleReceived(const meshtastic_MeshPacket &mp) override;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
bool isCommand(const char *msg) const;
|
|
||||||
void sendDm(const meshtastic_MeshPacket &rx, const char *text);
|
|
||||||
};
|
|
||||||
#endif // MESHTASTIC_EXCLUDE_REPLYBOT
|
|
||||||
Reference in New Issue
Block a user