init
This commit is contained in:
10
.editorconfig
Normal file
10
.editorconfig
Normal file
@@ -0,0 +1,10 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
.pio
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
.vscode/ipch
|
||||
|
||||
src/secrets.h
|
||||
10
.vscode/extensions.json
vendored
Normal file
10
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"platformio.platformio-ide"
|
||||
],
|
||||
"unwantedRecommendations": [
|
||||
"ms-vscode.cpptools-extension-pack"
|
||||
]
|
||||
}
|
||||
17
README.md
Normal file
17
README.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# Fingerbot Meshtastic Bridge
|
||||
|
||||
Small PlatformIO project that processes Meshtastic messages as commands, forwarding them to preconfigured Fingerbot nodes.
|
||||
|
||||
## Available commands
|
||||
|
||||
Commands are parsed from Meshtastic messages prefixed with `FB`.
|
||||
|
||||
- `ping`: `PONG`
|
||||
- `help`: returns command help text
|
||||
- `status`: queue, uptime, configured devices
|
||||
- `push [device] [ms|short|long] ...`: queue push pattern
|
||||
- `identify [device]`: trigger identify on target device
|
||||
|
||||
## Secrets setup
|
||||
|
||||
Sensitive values are stored in `src/secrets.h`. Use `src/secrets.example.h` as a template and enter your values.
|
||||
12
platformio.ini
Normal file
12
platformio.ini
Normal file
@@ -0,0 +1,12 @@
|
||||
[env:fb-mt-bridge]
|
||||
platform = espressif32
|
||||
board = esp32-c3-devkitm-1
|
||||
framework = arduino
|
||||
monitor_speed = 115200
|
||||
build_flags =
|
||||
-D ARDUINO_USB_MODE=1
|
||||
-D ARDUINO_USB_CDC_ON_BOOT=1
|
||||
lib_deps =
|
||||
h2zero/NimBLE-Arduino @ ^1.4.1
|
||||
bblanchon/ArduinoJson @ ^7.0.0
|
||||
meshtastic/Meshtastic @ ^0.0.7
|
||||
77
src/main.cpp
Normal file
77
src/main.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
#include <Arduino.h>
|
||||
#include "utils/queue.h"
|
||||
#include "utils/meshtastic.h"
|
||||
#include "utils/ble.h"
|
||||
#include "utils/commands.h"
|
||||
#include "secrets.h"
|
||||
#include <string>
|
||||
|
||||
// Meshtastic settings
|
||||
#define MT_CHANNEL_DEFAULT 1
|
||||
#define MT_CHANNEL_CMD 0
|
||||
#define MT_QUEUE_SIZE 10
|
||||
|
||||
// Meshtastic serial settings
|
||||
#define PIN_RX 4
|
||||
#define PIN_TX 3
|
||||
#define PIN_LED 8
|
||||
#define BAUD_RATE 115200
|
||||
|
||||
// Fingerbot settings
|
||||
#define FB_CMD_PREFIX "FB "
|
||||
#define FB_PUSH_MS_DEFAULT 2000
|
||||
|
||||
MeshtasticBridge *MeshtasticBridge::activeInstance_ = nullptr;
|
||||
|
||||
namespace
|
||||
{
|
||||
const CommandConfig kCommandConfig = {
|
||||
.defaultPushMs = FB_PUSH_MS_DEFAULT,
|
||||
.deviceMap = secrets::COMMAND_DEVICES,
|
||||
.deviceMapSize = sizeof(secrets::COMMAND_DEVICES) / sizeof(secrets::COMMAND_DEVICES[0])};
|
||||
|
||||
const BleConfig kBleConfig = {
|
||||
.deviceName = secrets::BLE_DEVICE_NAME,
|
||||
.serviceUuid = secrets::BLE_SERVICE_UUID,
|
||||
.characteristicUuid = secrets::BLE_CHAR_UUID};
|
||||
|
||||
const MeshtasticConfig kMeshtasticConfig = {
|
||||
.pinRx = PIN_RX,
|
||||
.pinTx = PIN_TX,
|
||||
.baudRate = BAUD_RATE,
|
||||
.pinLed = PIN_LED,
|
||||
.defaultChannel = MT_CHANNEL_DEFAULT,
|
||||
.commandChannel = MT_CHANNEL_CMD,
|
||||
.commandPrefix = FB_CMD_PREFIX};
|
||||
|
||||
MessageQueue gMessageQueue(MT_QUEUE_SIZE);
|
||||
CommandProcessor gCommands(kCommandConfig, gMessageQueue);
|
||||
MeshtasticBridge gMeshtastic(kMeshtasticConfig, gMessageQueue, gCommands);
|
||||
BleBridge gBle(kBleConfig);
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(BAUD_RATE);
|
||||
|
||||
pinMode(PIN_LED, OUTPUT);
|
||||
digitalWrite(PIN_LED, HIGH);
|
||||
|
||||
delay(1000);
|
||||
|
||||
gBle.init();
|
||||
gMeshtastic.setup();
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
uint32_t now = millis();
|
||||
gMeshtastic.loop(now);
|
||||
|
||||
std::string targetMacAddr;
|
||||
PendingBleRequest request;
|
||||
if (gCommands.takeBleRequest(targetMacAddr, request))
|
||||
{
|
||||
gBle.executeRequest(targetMacAddr.c_str(), request);
|
||||
}
|
||||
}
|
||||
17
src/secrets.h.example
Normal file
17
src/secrets.h.example
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef FB_MT_BRIDGE_SECRETS_H
|
||||
#define FB_MT_BRIDGE_SECRETS_H
|
||||
|
||||
#include "utils/commands.h"
|
||||
|
||||
namespace secrets
|
||||
{
|
||||
constexpr char BLE_DEVICE_NAME[] = "REPLACE_ME_DEVICE_NAME";
|
||||
constexpr char BLE_SERVICE_UUID[] = "00000000-0000-0000-0000-000000000000";
|
||||
constexpr char BLE_CHAR_UUID[] = "00000000-0000-0000-0000-000000000000";
|
||||
|
||||
constexpr CommandDevice COMMAND_DEVICES[] = {
|
||||
{"n1", "aa:bb:cc:dd:ee:ff"},
|
||||
{"n2", "aa:bb:cc:dd:ee:00"}};
|
||||
}
|
||||
|
||||
#endif // FB_MT_BRIDGE_SECRETS_H
|
||||
83
src/utils/ble.h
Normal file
83
src/utils/ble.h
Normal file
@@ -0,0 +1,83 @@
|
||||
#ifndef FB_MT_BRIDGE_BLE_H
|
||||
#define FB_MT_BRIDGE_BLE_H
|
||||
|
||||
#include <NimBLEDevice.h>
|
||||
#include <Arduino.h>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include "commands.h"
|
||||
|
||||
struct BleConfig
|
||||
{
|
||||
const char *deviceName;
|
||||
const char *serviceUuid;
|
||||
const char *characteristicUuid;
|
||||
};
|
||||
|
||||
class BleBridge
|
||||
{
|
||||
public:
|
||||
explicit BleBridge(const BleConfig &config) : config_(config) {}
|
||||
|
||||
void init()
|
||||
{
|
||||
NimBLEDevice::init(config_.deviceName);
|
||||
Serial.println("[INF] [BLE] NimBLE initialized");
|
||||
}
|
||||
|
||||
void executePush(const char *macAddr, uint32_t pushMs)
|
||||
{
|
||||
PendingBleRequest request = {"pattern " + std::to_string(pushMs)};
|
||||
executeRequest(macAddr, request);
|
||||
}
|
||||
|
||||
void executeRequest(const char *macAddr, const PendingBleRequest &request)
|
||||
{
|
||||
NimBLEUUID serviceUUID(config_.serviceUuid);
|
||||
NimBLEUUID charUUID(config_.characteristicUuid);
|
||||
|
||||
Serial.printf("[INF] [BLE] Connecting to target node: %s\n", macAddr);
|
||||
|
||||
NimBLEAddress targetAddress(macAddr);
|
||||
NimBLEClient *pClient = NimBLEDevice::createClient();
|
||||
|
||||
if (!pClient->connect(targetAddress))
|
||||
{
|
||||
Serial.println("[ERR] [BLE] Failed to connect to target node");
|
||||
}
|
||||
else
|
||||
{
|
||||
NimBLERemoteService *pRemoteService = pClient->getService(serviceUUID);
|
||||
|
||||
if (pRemoteService == nullptr)
|
||||
{
|
||||
Serial.println("[ERR] [BLE] Service not found");
|
||||
}
|
||||
else
|
||||
{
|
||||
NimBLERemoteCharacteristic *pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
|
||||
|
||||
if (pRemoteCharacteristic == nullptr)
|
||||
{
|
||||
Serial.println("[ERR] [BLE] Characteristic not found");
|
||||
}
|
||||
else
|
||||
{
|
||||
pRemoteCharacteristic->writeValue(request.payload, false);
|
||||
Serial.printf("[INF] [BLE] Successfully sent payload '%s'\n", request.payload.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
pClient->disconnect();
|
||||
Serial.println("[INF] [BLE] Disconnected from target node");
|
||||
}
|
||||
|
||||
NimBLEDevice::deleteClient(pClient);
|
||||
}
|
||||
|
||||
private:
|
||||
const BleConfig config_;
|
||||
};
|
||||
|
||||
#endif // BLE_H
|
||||
268
src/utils/commands.h
Normal file
268
src/utils/commands.h
Normal file
@@ -0,0 +1,268 @@
|
||||
#ifndef FB_MT_BRIDGE_COMMANDS_H
|
||||
#define FB_MT_BRIDGE_COMMANDS_H
|
||||
|
||||
#include <cstdint>
|
||||
#include "queue.h"
|
||||
#include <cstring>
|
||||
#include <cctype>
|
||||
#include <Arduino.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
|
||||
struct CommandDevice
|
||||
{
|
||||
const char *shortname;
|
||||
const char *mac;
|
||||
};
|
||||
|
||||
struct CommandConfig
|
||||
{
|
||||
uint32_t defaultPushMs;
|
||||
const CommandDevice *deviceMap;
|
||||
size_t deviceMapSize;
|
||||
};
|
||||
|
||||
struct PendingBleRequest
|
||||
{
|
||||
std::string payload;
|
||||
};
|
||||
|
||||
class CommandProcessor
|
||||
{
|
||||
public:
|
||||
CommandProcessor(const CommandConfig &config, MessageQueue &queue)
|
||||
: config_(config), queue_(queue), triggerBle_(false), pendingRequest_({""}), targetMacAddr_("")
|
||||
{
|
||||
}
|
||||
|
||||
const char *handleFb(const char *command, bool readonly)
|
||||
{
|
||||
if (command == nullptr || *command == '\0')
|
||||
{
|
||||
return "ERR EMPTY";
|
||||
}
|
||||
|
||||
std::vector<std::string> tokens = tokenize(command);
|
||||
if (tokens.empty())
|
||||
{
|
||||
return "ERR EMPTY";
|
||||
}
|
||||
|
||||
if (strcasecmp(tokens[0].c_str(), "ping") == 0)
|
||||
{
|
||||
return "PONG";
|
||||
}
|
||||
|
||||
if (strcasecmp(tokens[0].c_str(), "help") == 0 || strcasecmp(tokens[0].c_str(), "?") == 0)
|
||||
{
|
||||
static char buffer[224];
|
||||
snprintf(buffer, sizeof(buffer), "HELP ping | status | push [device] [ms|short|long] | identify [device]");
|
||||
return buffer;
|
||||
}
|
||||
|
||||
if (strcasecmp(tokens[0].c_str(), "status") == 0)
|
||||
{
|
||||
static char buffer[224];
|
||||
size_t offset = snprintf(buffer, sizeof(buffer), "STATUS queue=%d uptime=%lu devices=", queue_.size(), millis());
|
||||
|
||||
for (size_t i = 0; i < config_.deviceMapSize && offset < sizeof(buffer) - 1; i++)
|
||||
{
|
||||
offset += snprintf(buffer + offset, sizeof(buffer) - offset, "%s%s", (i == 0) ? "" : ",", config_.deviceMap[i].shortname);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
if (strcasecmp(tokens[0].c_str(), "push") == 0)
|
||||
{
|
||||
if (readonly)
|
||||
{
|
||||
return "ERR RO";
|
||||
}
|
||||
|
||||
const CommandDevice *targetDevice = &config_.deviceMap[0];
|
||||
size_t startIndex = 1;
|
||||
if (tokens.size() > 1 && !isDurationToken(tokens[1].c_str()))
|
||||
{
|
||||
targetDevice = findDeviceByName(tokens[1].c_str());
|
||||
if (!targetDevice)
|
||||
{
|
||||
return "ERR UNKNOWN DEVICE";
|
||||
}
|
||||
|
||||
startIndex = 2;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> durations;
|
||||
durations.reserve((tokens.size() > startIndex) ? (tokens.size() - startIndex) : 1);
|
||||
|
||||
for (size_t i = startIndex; i < tokens.size(); i++)
|
||||
{
|
||||
uint32_t durationMs = 0;
|
||||
if (!parseDurationToken(tokens[i].c_str(), durationMs))
|
||||
{
|
||||
return "ERR PUSH ARG";
|
||||
}
|
||||
|
||||
durations.push_back(durationMs);
|
||||
}
|
||||
|
||||
if (durations.empty())
|
||||
{
|
||||
durations.push_back(config_.defaultPushMs);
|
||||
}
|
||||
|
||||
pendingRequest_.payload = "push";
|
||||
for (uint32_t duration : durations)
|
||||
{
|
||||
pendingRequest_.payload += " ";
|
||||
pendingRequest_.payload += std::to_string(duration);
|
||||
}
|
||||
targetMacAddr_ = targetDevice->mac;
|
||||
triggerBle_ = true;
|
||||
|
||||
static char buffer[96];
|
||||
snprintf(buffer, sizeof(buffer), "PUSH %d steps to %s (BLE Queued)", static_cast<int>(durations.size()), targetDevice->shortname);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
if (strcasecmp(tokens[0].c_str(), "identify") == 0)
|
||||
{
|
||||
if (readonly)
|
||||
{
|
||||
return "ERR RO";
|
||||
}
|
||||
|
||||
const CommandDevice *targetDevice = &config_.deviceMap[0];
|
||||
if (tokens.size() > 2)
|
||||
{
|
||||
return "ERR IDENT ARG";
|
||||
}
|
||||
|
||||
if (tokens.size() == 2)
|
||||
{
|
||||
targetDevice = findDeviceByName(tokens[1].c_str());
|
||||
if (!targetDevice)
|
||||
{
|
||||
return "ERR UNKNOWN DEVICE";
|
||||
}
|
||||
}
|
||||
|
||||
pendingRequest_.payload = "identify";
|
||||
targetMacAddr_ = targetDevice->mac;
|
||||
triggerBle_ = true;
|
||||
|
||||
static char buffer[96];
|
||||
snprintf(buffer, sizeof(buffer), "IDENTIFY to %s (BLE Queued)", targetDevice->shortname);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
return "ERR UNKNOWN";
|
||||
}
|
||||
|
||||
bool takeBleRequest(std::string &targetMac, PendingBleRequest &request)
|
||||
{
|
||||
if (!triggerBle_)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
triggerBle_ = false;
|
||||
targetMac = targetMacAddr_;
|
||||
request = pendingRequest_;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
static std::vector<std::string> tokenize(const char *command)
|
||||
{
|
||||
std::vector<std::string> tokens;
|
||||
std::istringstream stream(command);
|
||||
std::string token;
|
||||
while (stream >> token)
|
||||
{
|
||||
tokens.push_back(token);
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
|
||||
static bool isDurationToken(const char *token)
|
||||
{
|
||||
return isNumberToken(token) ||
|
||||
strcasecmp(token, "short") == 0 ||
|
||||
strcasecmp(token, "long") == 0;
|
||||
}
|
||||
|
||||
static bool parseDurationToken(const char *token, uint32_t &durationMs)
|
||||
{
|
||||
if (!token || *token == '\0')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isNumberToken(token))
|
||||
{
|
||||
durationMs = static_cast<uint32_t>(atoi(token));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (strcasecmp(token, "short") == 0)
|
||||
{
|
||||
durationMs = 700;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (strcasecmp(token, "long") == 0)
|
||||
{
|
||||
durationMs = 5000;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool isNumberToken(const char *token)
|
||||
{
|
||||
if (!token || *token == '\0')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const char *p = token; *p; ++p)
|
||||
{
|
||||
if (!isdigit(static_cast<unsigned char>(*p)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const CommandDevice *findDeviceByName(const char *name) const
|
||||
{
|
||||
if (!name || *name == '\0')
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < config_.deviceMapSize; i++)
|
||||
{
|
||||
if (strcasecmp(config_.deviceMap[i].shortname, name) == 0)
|
||||
{
|
||||
return &config_.deviceMap[i];
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const CommandConfig config_;
|
||||
MessageQueue &queue_;
|
||||
bool triggerBle_;
|
||||
PendingBleRequest pendingRequest_;
|
||||
std::string targetMacAddr_;
|
||||
};
|
||||
|
||||
#endif // COMMANDS_H
|
||||
146
src/utils/meshtastic.h
Normal file
146
src/utils/meshtastic.h
Normal file
@@ -0,0 +1,146 @@
|
||||
#ifndef FB_MT_BRIDGE_MESHTASTIC_H
|
||||
#define FB_MT_BRIDGE_MESHTASTIC_H
|
||||
|
||||
#include <cstdint>
|
||||
#include "queue.h"
|
||||
#include "commands.h"
|
||||
#include <Meshtastic.h>
|
||||
#include <cstring>
|
||||
#include <Arduino.h>
|
||||
|
||||
struct MeshtasticConfig
|
||||
{
|
||||
int pinRx;
|
||||
int pinTx;
|
||||
uint32_t baudRate;
|
||||
uint8_t pinLed;
|
||||
uint8_t defaultChannel;
|
||||
uint8_t commandChannel;
|
||||
const char *commandPrefix;
|
||||
};
|
||||
|
||||
class MeshtasticBridge
|
||||
{
|
||||
public:
|
||||
MeshtasticBridge(const MeshtasticConfig &config, MessageQueue &queue, CommandProcessor &commands)
|
||||
: config_(config), queue_(queue), commands_(commands), isConnected_(false)
|
||||
{
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
activeInstance_ = this;
|
||||
mt_serial_init(config_.pinRx, config_.pinTx, config_.baudRate);
|
||||
mt_request_node_report(onConnectedStatic);
|
||||
set_text_message_callback(onMessageStatic);
|
||||
}
|
||||
|
||||
void loop(uint32_t now)
|
||||
{
|
||||
bool canSend = ::mt_loop(now);
|
||||
bool queueHasItems = queue_.size() > 0;
|
||||
|
||||
if (queueHasItems > 0)
|
||||
{
|
||||
digitalWrite(config_.pinLed, LOW);
|
||||
|
||||
if (canSend)
|
||||
{
|
||||
Message currentJob;
|
||||
if (queue_.dequeue(currentJob))
|
||||
{
|
||||
mt_send_text(currentJob.response, currentJob.dest, currentJob.channel);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
digitalWrite(config_.pinLed, HIGH);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static MeshtasticBridge *activeInstance_;
|
||||
|
||||
static void onConnectedStatic(mt_node_t *node, mt_nr_progress_t progress)
|
||||
{
|
||||
(void)node;
|
||||
(void)progress;
|
||||
if (activeInstance_)
|
||||
{
|
||||
activeInstance_->onConnected();
|
||||
}
|
||||
}
|
||||
|
||||
static void onMessageStatic(uint32_t from, uint32_t to, uint8_t channel, const char *text)
|
||||
{
|
||||
if (activeInstance_)
|
||||
{
|
||||
activeInstance_->onMessage(from, to, channel, text);
|
||||
}
|
||||
}
|
||||
|
||||
void onConnected()
|
||||
{
|
||||
if (!isConnected_)
|
||||
{
|
||||
Serial.println("[INF] [MT] Connected to Meshtastic");
|
||||
isConnected_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void onMessage(uint32_t from, uint32_t to, uint8_t channel, const char *text)
|
||||
{
|
||||
Serial.printf("[INF] [MT] Received message (ch=%u, from=!%08x, to=!%08x)\n", channel, from, to);
|
||||
|
||||
if (text == nullptr)
|
||||
{
|
||||
Serial.println("[INF] [MT] Ignoring message: empty payload");
|
||||
return;
|
||||
}
|
||||
|
||||
if (to == BROADCAST_ADDR && channel == config_.defaultChannel)
|
||||
{
|
||||
Serial.println("[INF] [MT] Ignoring broadcast on default/public channel");
|
||||
return;
|
||||
}
|
||||
|
||||
size_t prefixLen = strlen(config_.commandPrefix);
|
||||
if (strncasecmp(text, config_.commandPrefix, prefixLen) != 0)
|
||||
{
|
||||
Serial.printf("[INF] [MT] Ignoring message: missing prefix '%s'\n", config_.commandPrefix);
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse command
|
||||
digitalWrite(config_.pinLed, LOW);
|
||||
|
||||
const char *command = text + prefixLen;
|
||||
while (*command == ' ')
|
||||
command++;
|
||||
|
||||
Serial.printf("[INF] [MT] Accepted command text: '%s'\n", command);
|
||||
|
||||
bool readonly = to != BROADCAST_ADDR || channel != config_.commandChannel;
|
||||
Serial.printf("[INF] [MT] Command mode: %s\n", readonly ? "readonly" : "execute");
|
||||
|
||||
const char *response = commands_.handleFb(command, readonly);
|
||||
Serial.printf("[INF] [MT] Command response: '%s'\n", response);
|
||||
|
||||
if (to == BROADCAST_ADDR)
|
||||
{
|
||||
Serial.printf("[INF] [MT] Routing response to broadcast on channel %u\n", channel);
|
||||
queue_.enqueue(BROADCAST_ADDR, channel, response);
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.printf("[INF] [MT] Routing response to sender !%08x on channel %u\n", from, config_.defaultChannel);
|
||||
queue_.enqueue(from, config_.defaultChannel, response);
|
||||
}
|
||||
}
|
||||
|
||||
const MeshtasticConfig config_;
|
||||
MessageQueue &queue_;
|
||||
CommandProcessor &commands_;
|
||||
bool isConnected_;
|
||||
};
|
||||
|
||||
#endif // MESHTASTIC_H
|
||||
73
src/utils/queue.h
Normal file
73
src/utils/queue.h
Normal file
@@ -0,0 +1,73 @@
|
||||
#ifndef FB_MT_BRIDGE_QUEUE_H
|
||||
#define FB_MT_BRIDGE_QUEUE_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <Arduino.h>
|
||||
|
||||
struct Message
|
||||
{
|
||||
uint32_t dest;
|
||||
uint8_t channel;
|
||||
char response[128];
|
||||
};
|
||||
|
||||
class MessageQueue
|
||||
{
|
||||
public:
|
||||
static constexpr int kMaxCapacity = 16;
|
||||
|
||||
explicit MessageQueue(int capacity)
|
||||
: capacity_(capacity > kMaxCapacity ? kMaxCapacity : capacity), head_(0), tail_(0), count_(0)
|
||||
{
|
||||
if (capacity_ <= 0)
|
||||
{
|
||||
capacity_ = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void enqueue(uint32_t dest, uint8_t channel, const char *response)
|
||||
{
|
||||
if (count_ >= capacity_)
|
||||
{
|
||||
Serial.printf("[ERR] [Q] Queue full (size=%d), dropping response\n", capacity_);
|
||||
return;
|
||||
}
|
||||
|
||||
queue_[head_].dest = dest;
|
||||
queue_[head_].channel = channel;
|
||||
strncpy(queue_[head_].response, response, sizeof(queue_[head_].response) - 1);
|
||||
queue_[head_].response[sizeof(queue_[head_].response) - 1] = '\0';
|
||||
|
||||
head_ = (head_ + 1) % capacity_;
|
||||
count_++;
|
||||
|
||||
Serial.printf("[INF] [Q] Queued response (head=%d, count=%d)\n", head_, count_);
|
||||
}
|
||||
|
||||
bool dequeue(Message &out)
|
||||
{
|
||||
if (count_ == 0)
|
||||
return false;
|
||||
|
||||
out = queue_[tail_];
|
||||
tail_ = (tail_ + 1) % capacity_;
|
||||
count_--;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int size() const
|
||||
{
|
||||
return count_;
|
||||
}
|
||||
|
||||
private:
|
||||
Message queue_[kMaxCapacity];
|
||||
int capacity_;
|
||||
int head_;
|
||||
int tail_;
|
||||
int count_;
|
||||
};
|
||||
|
||||
#endif // QUEUE_H
|
||||
Reference in New Issue
Block a user