Public Access
LoRa Scanner: a MeshCore preset, and its packets read
- MeshCore joins the presets (EU/UK Narrow: 869.618 MHz, 62.5 kHz, SF8, CR 4/8, sync 0x12), after the Meshtastic ones so stored choices keep their number; `lora preset MeshCore` on the console - lib/meshcore reads what MeshCore sends in clear: header, path, and a node's advert (name, kind, key, position) - the list row, the details, a Capture's viewer and the console echo read a packet by the sync word it was received on - the bandwidth is printed as 62.5, not 62 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0162FokPdvY2KsS4NBfwyWPk
This commit is contained in:
@@ -264,10 +264,9 @@ void FileViewer::openPacket(int index) {
|
||||
if (tap) {
|
||||
std::snprintf(line, sizeof line, "%.0f dBm, SNR %.1f dB, noise %.0f", rx.rssi, rx.snr, rx.noiseFloor);
|
||||
details_.push_back(line);
|
||||
std::snprintf(line, sizeof line, "%.4f MHz, SF%u, %.0f kHz", rx.frequencyHz / 1e6, rx.spreadingFactor, rx.bandwidthKHz);
|
||||
std::snprintf(line, sizeof line, "%.4f MHz, SF%u, %g kHz", rx.frequencyHz / 1e6, rx.spreadingFactor, rx.bandwidthKHz);
|
||||
details_.push_back(line);
|
||||
if (rx.syncWord == meshtastic::kSyncWord)
|
||||
for (auto& l : lora::headerLines(body + skip, have - skip)) details_.push_back(l);
|
||||
for (auto& l : lora::headerLines(body + skip, have - skip, lora::protocolOf(rx.syncWord))) details_.push_back(l);
|
||||
}
|
||||
for (auto& l : lora::hexDump(body + skip, have - skip)) details_.push_back(l);
|
||||
if (have < r.length) details_.push_back("(the first bytes only)");
|
||||
@@ -391,7 +390,7 @@ void FileViewer::refresh() {
|
||||
size_t have = std::min<size_t>(r.length, n - files::kPcapRecordSize);
|
||||
if (tap && files::parseLoraTap(body, have, rx)) {
|
||||
shown_.push_back(lora::row({body + lora::kLoraTapSize, have - lora::kLoraTapSize, rx.rssi, rx.snr, true,
|
||||
rx.syncWord == meshtastic::kSyncWord},
|
||||
lora::protocolOf(rx.syncWord)},
|
||||
clockTime(r.seconds)));
|
||||
} else {
|
||||
shown_.push_back(clockTime(r.seconds) + " " + std::to_string(r.length) + " bytes");
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <cstdio>
|
||||
#include <ctime>
|
||||
|
||||
#include "meshtastic_presets.h"
|
||||
#include "scan_presets.h"
|
||||
#include "packet_view.h"
|
||||
#include "sweep_view.h"
|
||||
#include "ui/canvas.h"
|
||||
@@ -21,8 +21,8 @@ constexpr uint32_t kMessageMs = 4000;
|
||||
}
|
||||
|
||||
void LoraScannerApp::onEnter() {
|
||||
int i = std::clamp<int>(settings_.getInt(Setting::LoraPreset), 0, meshtastic::kEu868PresetCount - 1);
|
||||
const meshtastic::Preset& p = meshtastic::kEu868Presets[i];
|
||||
int i = std::clamp<int>(settings_.getInt(Setting::LoraPreset), 0, lora::scanPresetCount() - 1);
|
||||
lora::ScanPreset p = lora::scanPreset(i);
|
||||
if (std::string(radio_.config().name) != p.name) radio_.setPreset(p);
|
||||
radio_.listen(RadioService::App, true);
|
||||
view_ = View::Packets;
|
||||
@@ -48,7 +48,7 @@ void LoraScannerApp::showSweep(bool on) {
|
||||
requestRedraw();
|
||||
}
|
||||
|
||||
bool LoraScannerApp::meshtasticSettings() const { return radio_.config().syncWord == meshtastic::kSyncWord; }
|
||||
lora::Protocol LoraScannerApp::protocol() const { return lora::protocolOf(radio_.config().syncWord); }
|
||||
|
||||
void LoraScannerApp::say(const std::string& text) {
|
||||
message_ = text;
|
||||
@@ -83,8 +83,8 @@ bool LoraScannerApp::onKey(const KeyEvent& e) {
|
||||
else if (e.key == Key::Select) {
|
||||
int i = presets_.selected();
|
||||
settings_.setInt(Setting::LoraPreset, i);
|
||||
radio_.setPreset(meshtastic::kEu868Presets[i]);
|
||||
say(std::string("Listening on ") + meshtastic::kEu868Presets[i].name);
|
||||
radio_.setPreset(lora::scanPreset(i));
|
||||
say(std::string("Listening on ") + lora::scanPreset(i).name);
|
||||
view_ = View::Packets;
|
||||
} else if (e.key == Key::Back) view_ = View::Packets;
|
||||
else return true;
|
||||
@@ -104,7 +104,7 @@ bool LoraScannerApp::onKey(const KeyEvent& e) {
|
||||
return true;
|
||||
}
|
||||
if (e.key == Key::Char && (e.ch == 'p' || e.ch == 'P')) {
|
||||
presets_.setCount(meshtastic::kEu868PresetCount);
|
||||
presets_.setCount(lora::scanPresetCount());
|
||||
presets_.select(settings_.getInt(Setting::LoraPreset));
|
||||
view_ = View::Presets;
|
||||
requestRedraw();
|
||||
@@ -133,8 +133,8 @@ void LoraScannerApp::openDetails() {
|
||||
details_.push_back(line);
|
||||
std::snprintf(line, sizeof line, "%.4f MHz, off by %.0f Hz", p.rx.frequencyHz / 1e6, p.frequencyError);
|
||||
details_.push_back(line);
|
||||
if (meshtasticSettings())
|
||||
for (auto& l : lora::headerLines(p.data, p.len)) details_.push_back(l);
|
||||
if (p.crcOk)
|
||||
for (auto& l : lora::headerLines(p.data, p.len, lora::protocolOf(p.rx.syncWord))) details_.push_back(l);
|
||||
for (auto& l : lora::hexDump(p.data, p.len)) details_.push_back(l);
|
||||
detailsTop_ = 0;
|
||||
view_ = View::Details;
|
||||
@@ -238,10 +238,12 @@ void LoraScannerApp::drawPackets(Canvas& c) {
|
||||
c.setFont(&fonts::body);
|
||||
c.setTextColor(theme::kMuted);
|
||||
c.drawString("No packets yet.", 4, list.y + 4);
|
||||
c.drawString(meshtasticSettings() ? "Meshtastic nodes in range show here." : "Custom settings.", 4,
|
||||
lora::Protocol heard = protocol();
|
||||
c.drawString(heard == lora::Protocol::Meshtastic ? "Meshtastic nodes in range show here."
|
||||
: heard == lora::Protocol::MeshCore ? "MeshCore nodes in range show here." : "Custom settings.", 4,
|
||||
list.y + 4 + theme::kLineHeight);
|
||||
} else {
|
||||
bool mesh = meshtasticSettings();
|
||||
lora::Protocol mesh = protocol();
|
||||
widgets::list(c, packets_, list, [&](int i) {
|
||||
RadioPacket p;
|
||||
if (!radio_.packet(newest - i, p)) return std::string("(gone)");
|
||||
@@ -265,14 +267,14 @@ void LoraScannerApp::drawPresets(Canvas& c) {
|
||||
const auto& area = theme::kContent;
|
||||
c.setFont(&fonts::small);
|
||||
c.setTextColor(theme::kMuted);
|
||||
c.drawString("Meshtastic presets allowed in EU868", 4, area.y + 2);
|
||||
c.drawString("Meshtastic in EU868, and MeshCore", 4, area.y + 2);
|
||||
widgets::list(
|
||||
c, presets_, {area.x, area.y + 12, area.w, (kListRows + 1) * theme::kLineHeight},
|
||||
[](int i) { return std::string(meshtastic::kEu868Presets[i].name); },
|
||||
[](int i) { return std::string(lora::scanPreset(i).name); },
|
||||
[](int i) {
|
||||
char v[24];
|
||||
const auto& p = meshtastic::kEu868Presets[i];
|
||||
std::snprintf(v, sizeof v, "SF%u %.0fk %.3f", p.sf, p.bwKHz, meshtastic::eu868FrequencyHz(p) / 1e6);
|
||||
lora::ScanPreset p = lora::scanPreset(i);
|
||||
std::snprintf(v, sizeof v, "SF%u %gk %.3f", p.sf, p.bwKHz, p.frequencyHz / 1e6);
|
||||
return std::string(v);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
namespace roro {
|
||||
|
||||
// The LoRa Scanner (docs/milestones/M3.md). Sniffer: the packets the radio hears, newest first,
|
||||
// with the Meshtastic header read (Q96) and a hex dump on Enter; p picks the preset (Q95), c starts
|
||||
// with the Meshtastic or MeshCore header read (Q96) and a hex dump on Enter; p picks the preset (Q95), c starts
|
||||
// or stops a Capture (Q97). The radio listens while the App is open (Q100). Tab: Sweep, the band's
|
||||
// RSSI as bars with peak hold and a waterfall (Q98); it pauses the Sniffer while shown (Q99).
|
||||
class LoraScannerApp : public App {
|
||||
@@ -40,7 +40,7 @@ class LoraScannerApp : public App {
|
||||
void showSweep(bool on);
|
||||
void openDetails();
|
||||
std::string timeOf(const RadioPacket& p) const;
|
||||
bool meshtasticSettings() const;
|
||||
lora::Protocol protocol() const; // whose packets the radio is set for
|
||||
void say(const std::string& text); // a line in the footer for a few seconds
|
||||
|
||||
RadioService& radio_;
|
||||
|
||||
Reference in New Issue
Block a user