Feature/add sx1276 support #2
Keine Dateien entsprechen Ihrer Suche
Binäre Datei nicht angezeigt.
@@ -0,0 +1,107 @@
|
|||||||
|
APRS SSID Recommendations 6 Feb 2012
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
Updated 6 Feb 2012 to clarify the title of this document to be
|
||||||
|
SSID RECOMMENDATIONS not to imply any kind of
|
||||||
|
decoding "standard". That original NMEA
|
||||||
|
decodign standard is covered in the original spec
|
||||||
|
Updated 9 June 2010 for more flexibility using 1,2,3,4 and 15
|
||||||
|
Revised 2 June 2004 to add -10, -11, 12 and -15
|
||||||
|
|
||||||
|
SSID's have seen two different uses in APRS. Initially as an ICON
|
||||||
|
indicator back in the early 1990's. But that is obsolete for over
|
||||||
|
a decade. Now SSID's are used as an informal way of indicating one
|
||||||
|
of several different typical APRS applications.
|
||||||
|
|
||||||
|
Since many small displays for the handheld and mobile operator show
|
||||||
|
nearby APRS station callsigns that flash up on the screen, it is nice
|
||||||
|
to have some idea of what type of station or activity might be
|
||||||
|
involved simply from the callsign SSID without having to push
|
||||||
|
buttons, search lists, or look at maps to find out more about them.
|
||||||
|
|
||||||
|
SSID RECOMMENDATIONS: It is very convenient to other mobile
|
||||||
|
operators or others looking at callsigns flashing by, to be able to
|
||||||
|
recognize some common applications at a glance. Here are the
|
||||||
|
recommendations for the 16 possible SSID's (the limit of 16 comes
|
||||||
|
from the 4 bits available in the AX.25 protocol. Note, The SSID of
|
||||||
|
zero is dropped by most display applications. So a callsign with no
|
||||||
|
SSID has an SSID of 0.
|
||||||
|
|
||||||
|
-0 Your primary station usually fixed and message capable
|
||||||
|
-1 generic additional station, digi, mobile, wx, etc
|
||||||
|
-2 generic additional station, digi, mobile, wx, etc
|
||||||
|
-3 generic additional station, digi, mobile, wx, etc
|
||||||
|
-4 generic additional station, digi, mobile, wx, etc
|
||||||
|
-5 Other networks (Dstar, Iphones, Androids, Blackberry's etc)
|
||||||
|
-6 Special activity, Satellite ops, camping or 6 meters, etc
|
||||||
|
-7 walkie talkies, HT's or other human portable
|
||||||
|
-8 boats, sailboats, RV's or second main mobile
|
||||||
|
-9 Primary Mobile (usually message capable)
|
||||||
|
-10 internet, Igates, echolink, winlink, AVRS, APRN, etc
|
||||||
|
-11 balloons, aircraft, spacecraft, etc
|
||||||
|
-12 APRStt, DTMF, RFID, devices, one-way trackers*, etc
|
||||||
|
-13 Weather stations
|
||||||
|
-14 Truckers or generally full time drivers
|
||||||
|
-15 generic additional station, digi, mobile, wx, etc
|
||||||
|
|
||||||
|
* One-way trackers should best use the -12 one-way SSID indicator
|
||||||
|
because the -9's usually mean a ham in full APRS communication
|
||||||
|
both message and voice. The -9's can be contacted by APRS message
|
||||||
|
or by Voice on his frequency included in his beacon, or on Voice
|
||||||
|
Alert if he is in simplex range. The -12's are just moving Icons
|
||||||
|
on the map and since they have no 2 way communication for ham radio
|
||||||
|
they are not generally of routine interest to other operators.
|
||||||
|
|
||||||
|
OBJECTS or INTERNET: In addition, Objects or internet generated
|
||||||
|
stations can have any SSID, not just the original 16, since
|
||||||
|
Objects are not constrained by the AX.25 header and can have a
|
||||||
|
9 byte name. Here are some common OBJECT/Internet SSID's:
|
||||||
|
|
||||||
|
-63 for PSK63 HF stations
|
||||||
|
-tt for APRS TouchTone users (DTMF)
|
||||||
|
-ID for RFID
|
||||||
|
-A through -Z for Dstar
|
||||||
|
|
||||||
|
SSID BACKGROUND: Originally, in 1992, we had to use the SSID as
|
||||||
|
a way of indicating the type of station that transmitted a raw
|
||||||
|
NMEA-0183 GPS sting. But in the mid 1990's we began indicating
|
||||||
|
any of the nearly 200 APRS symbols by the setting of the AX.25
|
||||||
|
TOCALL of "GPSxyz". The "xyz" characters define the symbol from
|
||||||
|
the standard APRS symbol table www.aprs.org/symbols.html.
|
||||||
|
|
||||||
|
The GPSxyz concept worked so well, the original SSID associations
|
||||||
|
are no longer a required part of the spec. But the conventions
|
||||||
|
that evolved from those early SSID's are still encouraged as
|
||||||
|
noted above, for easy recognition of station type or activity by
|
||||||
|
when only the callsign is seen.
|
||||||
|
|
||||||
|
The -1, -2, -3, -4 and -15 are kept generic so that anyone with
|
||||||
|
as many as 6 digipeaters, or 6 trackers or 6 weather stations or
|
||||||
|
6 vehicles can still have unique SSID's for each of his stations.
|
||||||
|
Beyond 6, people will just have to use any SSID that suits their
|
||||||
|
fancy. In some areas there might be 15 digipeaters all under
|
||||||
|
one guy's call!
|
||||||
|
|
||||||
|
SSID USAGE: The SSID's also might give a hint as to how someone
|
||||||
|
is getting into APRS whether via satellite, a one-way tracker, a
|
||||||
|
mobile, an HT or even via DTMF or an RFID device or whether he is
|
||||||
|
doing something special.
|
||||||
|
|
||||||
|
For example, if you are doing something special, change your
|
||||||
|
SSID to -6 to alert others to your excitement, or to make the
|
||||||
|
track-history begin and end on site, and not be tied to all
|
||||||
|
your other -9 travels. Or use -6 SSID for a packet sent
|
||||||
|
via the ISS or APRS satellite or for a 6 meter test so the
|
||||||
|
successful packet is preserved and not overwritten by the same
|
||||||
|
radio the next time you use it not via the ISS on the 144.39
|
||||||
|
national channel. By using separate SSID's the WEB data bases
|
||||||
|
will keep statistics and data separate from when you are working
|
||||||
|
normally on other bands with other SSID's.
|
||||||
|
|
||||||
|
So stick to the suggestions above for the obvious applications
|
||||||
|
where you can. Of course these are not rigid. If you have
|
||||||
|
more than 6 digipeaters, use any SSID you want. These are only
|
||||||
|
guidelines to hint at a station's possible application when all
|
||||||
|
you can see easily is the callsign on a screen or in a list....
|
||||||
|
|
||||||
|
de WB4APR, Bob
|
||||||
Binäre Datei nicht angezeigt.
@@ -0,0 +1,91 @@
|
|||||||
|
# FoxMash - Protocol
|
||||||
|
|
||||||
|
English version. In case of doubt, the English text shall be binding.
|
||||||
|
*[Deutsche Version anzeigen](foxmash-protocol_de.md)*
|
||||||
|
|
||||||
|
## Automatic Stations at the ARDF Transmitting Facilities (Foxes)
|
||||||
|
|
||||||
|
A fox regularly broadcasts its identifier and telemetry data.
|
||||||
|
This is done, whenever possible, at times when the fox is not transmitting on the 80m or 2m bands.
|
||||||
|
The purpose of this is, on the one hand, the time synchronization of adjacent nodes, and on the other hand, the regular official announcement of the call sign.
|
||||||
|
Usually, the call sign of the organizer or host of the direction-finding competition is used.
|
||||||
|
|
||||||
|
## Beacon
|
||||||
|
|
||||||
|
Composed based on the modified AX.25, APRS, and MeshCom protocols as follows:
|
||||||
|
|
||||||
|
Pseudo-Byte-Stream:
|
||||||
|
`{OE3TKT-03nnnnnn`
|
||||||
|
|
||||||
|
|
||||||
|
### Structure:
|
||||||
|
|
||||||
|
* **Byte 0: `{` (0x7B)**
|
||||||
|
* **Data Type Identifier (DTI):** Derived from the APRS & MeshCom protocol specification (see [APRS Protocol Reference](https://ui-view.net)).
|
||||||
|
* **Reasoning:** This data type identifier is currently marked as `[Unused]` in the specification, and the character visually resembles a station icon.
|
||||||
|
|
||||||
|
* **Bytes 1–6: `"OE3TKT"` (0x4F 0x45 0x33 0x54 0x4B 0x54)**
|
||||||
|
* **International Call Sign:** Formatted as a 1–2 character prefix, followed by one or more digits, and a 1–3 letter suffix.
|
||||||
|
* **Padding:** This field is strictly 6 characters wide. If the call sign is shorter, it must be right-padded with trailing spaces.
|
||||||
|
|
||||||
|
* **Bytes 7+: Station Number / SSID Identifier**
|
||||||
|
* **Standard Notation (IDs 0–99):** Uses a hyphen followed by a two-digit SSID (Bytes 7–9), for example, `"-03"` (0x2D 0x30 0x33).
|
||||||
|
* **Extended Notation (IDs ≥ 100):** If the station number exceeds 100, you may take any letters as described in https://aprs11/SSIDs.txt (e.g., `OE3TKT-S6` for any special 6th station).
|
||||||
|
* **Backup / Hot-Standby Nodes:** To designate a station as a replacement node or an immediate hot-standby node, the hyphen is replaced by a tilde symbol (e.g., `OE3TKT~03`).
|
||||||
|
|
||||||
|
* **Bytes 10–13: Telemetry Data (`nnnnnn` / Timestamp)**
|
||||||
|
* **Timestamp:** Signed 32-bit integer representing the number of milliseconds since the start of the competition day.
|
||||||
|
|
||||||
|
* **Byte 14: Own Battery Level**
|
||||||
|
* **Value:** Unsigned 8-bit integer (`unsigned char`) representing the node's own battery capacity in percent (0 to 100%).
|
||||||
|
A value of `100` (0x64) means fully charged, while `0` means empty.
|
||||||
|
Values above 100 can be used to indicate charging or mains power (e.g., `255`).
|
||||||
|
|
||||||
|
* **Bytes 15+: Heard Neighbor Nodes (Format: `xRSqxRSqxRSq...`)**
|
||||||
|
* Each neighbor node found in the mesh is represented by a multi-byte block containing its SSID, link quality, and battery status.
|
||||||
|
* **Bytes 15–16 (x):** Neighbor SSID (16-bit unsigned integer, e.g., `0x0003` for station number 3).
|
||||||
|
* **Byte 17 (R):** RSSI value + 164 stored as an `unsigned char` (e.g., `0x3A` = 58 ➡️ 58 - 164 = -106 dBm).
|
||||||
|
* **Byte 18 (S):** SNR value multiplied by 4 stored as a `signed char` (e.g., `0x42` = 66 ➡️ 66 / 4 = +16.5 dB).
|
||||||
|
* **Byte 19 (q):** Combined Quality and Battery Status Byte:
|
||||||
|
* **Bits 7–6 (Battery Status of the neighbor):**
|
||||||
|
* `00` = Normal (Battery OK / Powered)
|
||||||
|
* `01` = Low (Battery low, intervention recommended)
|
||||||
|
* `10` = Critical (Battery critical, node might shut down soon)
|
||||||
|
* `11` = Reserved
|
||||||
|
* **Bits 5–0 (Counter):** Moving average counter of how many times this station was heard within the last 30 minutes (Max value: 63). If this counter is ≥ 3, the neighbor is considered a stable link. Stations not heard at least once within 30 minutes are dropped from the list.
|
||||||
|
|
||||||
|
## Checkpoint Messages (Posten)
|
||||||
|
|
||||||
|
As soon as a participant checks in at a checkpoint using their RFID tag or identifier, they are registered at that node. The runner is assigned a timestamp and their ID (runner number). The mapping between the runner number and the RFID code is stored within the system. During the next available time slot, a checkpoint message is transmitted to the destination with QoS 2 (guaranteed packet delivery, duplicate detection, and filtering).
|
||||||
|
|
||||||
|
Messages are sent to the destination and optionally to the start node simultaneously. Both nodes must have an SSID < 15, as the remaining bits in the address byte are reserved for protocol flags.
|
||||||
|
|
||||||
|
This message packet is designed to be extremely short and efficient. It contains the node number, runner number, capture timestamp, and the current hop count.
|
||||||
|
|
||||||
|
Pseudo-Byte-Stream:
|
||||||
|
`vppMLzzzzRR`
|
||||||
|
|
||||||
|
### Structure (Checkpoint Message):
|
||||||
|
|
||||||
|
* **Byte 0: `(` (0x40)**
|
||||||
|
* **Data Type Identifier (DTI):** Special identifier designated for FoxMash checkpoint messages.
|
||||||
|
|
||||||
|
* **Bytes 1–2: Own SSID (`pp`)**
|
||||||
|
* **Value:** 16-bit unsigned integer (`uint16_t`) of the transmitting checkpoint node.
|
||||||
|
|
||||||
|
* **Byte 3: `M` (Target SSID, Hop Count & Flags)**
|
||||||
|
* **Bits 7–5 (Hop Count):** Dynamic routing counter. If the node sees the destination directly with good quality, this value is `0`. If no acknowledgment is received within 5 minutes, the permitted hop value is incremented by one. If the hop value exceeds the defined maximum, the packet is discarded.
|
||||||
|
* **Bit 4 (ACK Flag):** `0` for a standard data message, `1` for an acknowledgment packet (ACK).
|
||||||
|
* **Bits 3–0 (Target SSID):** The SSID of the destination node (Supports IDs from 0 to 15).
|
||||||
|
|
||||||
|
* **Bytes 4–5: `L` (Runner Number)**
|
||||||
|
* **Value:** 16-bit unsigned integer (`uint16_t`) for unique participant identification (Supports IDs from 0 to 65,535).
|
||||||
|
|
||||||
|
* **Bytes 6–9: `zzzz` (Timestamp)**
|
||||||
|
* **Value:** Signed 32-bit integer (milliseconds since the start of the day) capturing the exact time the runner checked in at the post. Also used to verify packet validity within the allowed time window.
|
||||||
|
|
||||||
|
* **Bytes 10+: `RR` (Routing List / Optional)**
|
||||||
|
* **Value:** Optional list of intermediate nodes. For example, when utilizing three relay nodes, this field is structured as `RR RR RR`. It remains empty if a direct connection is established.
|
||||||
|
|
||||||
|
### Acknowledgments and Retries
|
||||||
|
The maximum number of retries is configurable (default: 4). Every checkpoint packet must be acknowledged. The acknowledgment uses the exact same format (`vppMLzzzzRR`), but with **Bit 4 in Byte 3 (`M`) set to `1`**. The return routing follows the identical path in the reverse direction based on the route recorded in the packet (source routing). Remit nodes detect and discard duplicate packets by matching the runner's unique timestamp.
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
# FoxMash - Protokoll
|
||||||
|
|
||||||
|
Deutsche Übersetzung. Im Zweifelsfall ist die englischsprachige Version bindend.
|
||||||
|
*[View English Version](foxmash-protocol.md)*
|
||||||
|
|
||||||
|
## Automatische Stationen bei den ARDF-Sendeeinrichtungen (Füchsen)
|
||||||
|
|
||||||
|
Ein Fuchs sendet regelmäßig seine Kennung (Identifier) und Telemetriedaten aus.
|
||||||
|
Dies geschieht, wann immer möglich, zu Zeiten, in denen kein Sendebetrieb des Fuchses im 80m- oder 2m-Band stattfindet.
|
||||||
|
Dies dient einerseits der Zeitsynchronisation der Nachbarknoten und andererseits der regelmäßigen, behördlich vorgeschriebenen Aussendung des Rufzeichens.
|
||||||
|
Üblicherweise wird das Rufzeichen des Ausrichters bzw. Veranstalters des Peilwettbewerbs verwendet.
|
||||||
|
|
||||||
|
## Bake (Broadcast)
|
||||||
|
|
||||||
|
Setzt sich wie folgt aus den modifizierten AX.25-, APRS- und MeshCom-Protokollen zusammen:
|
||||||
|
|
||||||
|
Pseudo-Byte-Stream:
|
||||||
|
`{OE3TKT-03nnnnxxRSqxxRSqxxRSq`
|
||||||
|
|
||||||
|
### Struktur:
|
||||||
|
|
||||||
|
* **Byte 0: `{` (0x7B)**
|
||||||
|
* **Data Type Identifier (DTI):** Abgeleitet aus der APRS- & MeshCom-Protokollspezifikation (siehe [APRS Protocol Reference](https://ui-view.net)).
|
||||||
|
* **Begründung:** Dieser Datentyp-Bezeichner ist in der Spezifikation derzeit noch als `[Unused]` (unbenutzt) markiert, und das Zeichen sieht visuell wie ein Stationssymbol aus.
|
||||||
|
|
||||||
|
* **Bytes 1–6: `"OE3TKT"` (0x4F 0x45 0x33 0x54 0x4B 0x54)**
|
||||||
|
* **Internationales Rufzeichen:** Formatiert als 1–2 Zeichen langes Präfix, gefolgt von einer oder mehreren Ziffern und einem 1–3 Zeichen langen Suffix.
|
||||||
|
* **Padding (Auffüllung):** Dieses Feld ist strikt 6 Zeichen lang. Wenn das Rufzeichen kürzer ist, muss es rechtsbündig mit Leerzeichen aufgefüllt werden.
|
||||||
|
|
||||||
|
* **Bytes 7+: Stationsnummer / SSID-Kennung**
|
||||||
|
* **Standard-Notation (IDs 0–99):** Verwendet einen Bindestrich, gefolgt von einer zweistelligen SSID (Bytes 7–9), zum Beispiel `"-03"` (0x2D 0x30 0x33).
|
||||||
|
* **Erweiterte Notation (IDs ≥ 100):** Wenn die Stationsnummer 100 überschreitet, können beliebige Buchstaben verwendet werden, wie unter https://aprs11/SSIDs.txt beschrieben (z. B. `OE3TKT-S6` für eine beliebige spezielle 6. Station).
|
||||||
|
* **Ersatz- / Hot-Standby-Knoten:** Um eine Station als Ersatzstation oder sofort einsatzbereiten Knoten zu kennzeichnen, wird der Bindestrich durch ein Tilde-Zeichen ersetzt (z. B. `OE3TKT~03`).
|
||||||
|
|
||||||
|
* **Bytes 10–13: Telemetriedaten (`nnnnnn` / Zeitstempel)**
|
||||||
|
* **Zeitstempel:** Vorzeichenbehaftete 32-Bit-Ganzzahl (Signed 32-Bit Integer), die die Millisekunden seit Beginn des Wettbewerbstages darstellt.
|
||||||
|
Dieser Zeitstempel dient gleichzeitig auch als Nachrichten-ID, und die gesamte Nachricht wird gehasht und damit kann festgestellt werden, ob sie doppelt ist.
|
||||||
|
|
||||||
|
* **Byte 14: Eigener Batteriestand**
|
||||||
|
* **Wert:** Vorzeichenlose 8-Bit-Ganzzahl (`unsigned char`), die die eigene Batteriekapazität in Prozent (0 bis 100 %) darstellt. Ein Wert von `100` (0x64) bedeutet voll geladen, `0` bedeutet leer. Werte über 100 können optional für Netzbetrieb oder Ladevorgang reserviert werden (z. B. `255`).
|
||||||
|
|
||||||
|
* **Bytes 15+: Gehörte Nachbarknoten (Format: `xxRSqxxRSqxxRSq...`)**
|
||||||
|
* Jeder im Mesh gefundene Nachbarknoten wird durch einen Multi-Byte-Block dargestellt, der seine SSID, die Link-Qualität und den Batteriestatus enthält.
|
||||||
|
* **Bytes 15–16 (x):** Nachbar-SSID (16-Bit Unsigned Integer, z. B. `0x0003` für Stationsnummer 3).
|
||||||
|
* **Byte 17 (R):** RSSI-Wert + 164 als `unsigned char` (z. B. `0x3A` = 58 ➡️ 58 - 164 = -106 dBm).
|
||||||
|
* **Byte 18 (S):** Vierfacher SNR-Wert als `signed char` (z. B. `0x42` = 66 ➡️ 66 / 4 = +16.5 dB).
|
||||||
|
* **Byte 19 (q):** Kombiniertes Qualitäts- und Batteriestatus-Byte:
|
||||||
|
* **Bits 7–6 (Batteriezustand des Nachbarn):**
|
||||||
|
* `00` = Normal (Batterie OK / Netzbetrieb)
|
||||||
|
* `01` = Niedrig (Batterie schwach, Wartung empfohlen)
|
||||||
|
* `10` = Kritisch (Batterie kritisch, Knoten fällt bald aus)
|
||||||
|
* `11` = Reserviert
|
||||||
|
* **Bits 5–0 (Zähler):** Gleitender Zähler, wie oft diese Station in den letzten 30 Minuten gehört wurde (Maximalwert: 63). Wenn dieser Zähler ≥ 3 ist, gilt der Nachbar als stabiler Link. Stationen, die innerhalb von 30 Minuten kein einziges Mal gehört wurden, werden aus der Liste gelöscht.
|
||||||
|
|
||||||
|
Falls der eigne Knoten die Ziel/Start-Knoten nicht selbst sehen, so müssen
|
||||||
|
|
||||||
|
## Nachrichten von Posten
|
||||||
|
|
||||||
|
Sobald eine Person am Postenschirm seine Kennung bekannt gibt wird er an diesem Knoten registriert.
|
||||||
|
Ein Zeitstempel und seine ID wird zugewiesen (Läufernummer).
|
||||||
|
Die Läufernummer und die entsprechende RFID-Nummer ist im System hinterlegt und es wird beim nächsten Zeitschlitz eine Posten-Nachricht an das Ziel verschickt.
|
||||||
|
|
||||||
|
Das Ziel und der Start-Knoten ist dem System bekannt (muss nicht zwingend SSID 0 haben).
|
||||||
|
Jedoch: Muss eine SSID < 15 haben, da wir die anderen bits für Flags verwenden.
|
||||||
|
Eventuell wird an beide Knoten, Ziel und Start gleichzeitig verschickt.
|
||||||
|
Die QoS ist dabei nicht mehr 0 sondern 2. (Sicherstellung der Paketzustellung und doppelte Pakete werden erkannt und weggeworfen).
|
||||||
|
|
||||||
|
Dieses Nachrichtenpaket ist sehr kurz und effizient aufgebaut:
|
||||||
|
|
||||||
|
Wir brauchen folgende Informationen: Knotennummer, Läufernummer, Zeitstempel, und der wievielte Hopp es ist.
|
||||||
|
|
||||||
|
## Nachrichten von Posten
|
||||||
|
|
||||||
|
Sobald sich ein Teilnehmer am Postenschirm per RFID/Kennung anmeldet, wird er an diesem Knoten registriert. Dem Läufer wird ein Zeitstempel und seine ID (Läufernummer) zugewiesen. Die Verknüpfung zwischen Läufernummer und RFID-Code ist im System hinterlegt. Beim nächsten verfügbaren Zeitschlitz wird eine Posten-Nachricht mit der Dienstgüte QoS 2 (Sicherstellung der Paketzustellung, Erkennung und Verwerfen von Duplikaten) an das Ziel übermittelt.
|
||||||
|
|
||||||
|
Die Nachrichten werden an das Ziel und optional gleichzeitig an den Start-Knoten gesendet. Beide Knoten müssen eine SSID < 15 besitzen, da die verbleibenden Bits im Adress-Byte für Protokoll-Flags reserviert sind.
|
||||||
|
|
||||||
|
Dieses Nachrichtenpaket ist extrem kurz und effizient aufgebaut. Es enthält die Knotennummer, die Läufernummer, den Zeitstempel der Erfassung sowie den aktuellen Hop-Zähler.
|
||||||
|
|
||||||
|
Pseudo-Byte-Stream:
|
||||||
|
`vppMLzzzzRR`
|
||||||
|
|
||||||
|
### Struktur (Posten-Nachricht):
|
||||||
|
|
||||||
|
* **Byte 0: `(` (0x40)**
|
||||||
|
* **Data Type Identifier (DTI):** Spezieller Bezeichner für FoxMash-Postenmeldungen.
|
||||||
|
|
||||||
|
* **Bytes 1–2: Eigene SSID (`pp`)**
|
||||||
|
* **Wert:** 16-Bit vorzeichenlose Ganzzahl (`uint16_t`) des sendenden Posten-Knotens.
|
||||||
|
|
||||||
|
* **Byte 3: `M` (Ziel-SSID, Hop-Limit / TTL & Flags)**
|
||||||
|
* **Bits 7–5 (Hop-Limit / TTL):** Startwert für die maximale Anzahl an erlaubten Hops (Time-to-Live). Der Startknoten setzt diesen Wert (z. B. auf 3). Jeder weiterleitende Zwischenknoten verringert (dekrementiert) diesen Wert um 1. Erreicht der Wert `0`, wird das Paket nicht mehr weitergeleitet und verworfen.
|
||||||
|
* **Bit 4 (ACK-Flag):** `0` für eine normale Nachricht, `1` für ein Bestätigungspaket (ACK).
|
||||||
|
* **Bits 3–0 (Ziel-SSID):** Die SSID des Zielknotens (Unterstützt IDs von 0 bis 15).
|
||||||
|
|
||||||
|
* **Bytes 4–5: `L` (Läufernummer)**
|
||||||
|
* **Wert:** 16-Bit vorzeichenlose Ganzzahl (`uint16_t`) zur eindeutigen Identifikation des Teilnehmers (Unterstützt IDs von 0 bis 65.535).
|
||||||
|
|
||||||
|
* **Bytes 6–9: `zzzz` (Zeitstempel)**
|
||||||
|
* **Wert:** Vorzeichenbehaftete 32-Bit-Ganzzahl (Millisekunden seit Tagesbeginn), die den exakten Zeitpunkt der Registrierung des Läufers am Posten festhält. Dient auch zur Gültigkeitsprüfung im Zeitrahmen.
|
||||||
|
|
||||||
|
* **Bytes 10+: `RR` (Routingliste / Optional)**
|
||||||
|
* **Wert:** Optionale Liste der Zwischenknoten. Bei der Verwendung von beispielsweise drei Relaisknoten setzt sich das Feld aus `RR RR RR` zusammen. Bleibt leer, wenn eine Direktverbindung besteht.
|
||||||
|
|
||||||
|
### Bestätigung und Wiederholungen (Retries)
|
||||||
|
Die maximale Anzahl an Sendeversuchen (Retries) ist konfigurierbar (Standard: 4). Jedes Posten-Paket muss zwingend bestätigt werden. Die Bestätigung verwendet das exakt gleiche Format (`vppMLzzzzRR`), wobei jedoch **Bit 4 im Byte 3 (`M`) auf `1` gesetzt** ist. Das Antwort-Routing nutzt anhand des im Paket aufgezeichneten Pfades dieselbe Strecke in umgekehrter Richtung (Source Routing). Duplikate werden empfängerseitig anhand des eindeutigen Läufer-Zeitstempels erkannt und verworfen.
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
# Protocol MeshCom 4.0
|
||||||
|
|
||||||
|
## Stream - Example
|
||||||
|
```
|
||||||
|
:~1~9~SP3TUT-1,OE1XAR-12,OE3TKT-12>*:z okolic Pi~~y kto~~ tu jest?~~~~~(~a~
|
||||||
|
S
|
||||||
|
Hex: 3A BB 31 F3 39 93 53 50 33 54 55 54 2D 31 2C 4F 45 31 58 41 52 2D 31 32 2C 4F 45 33 54 4B 54 2D 31 32 3E 2A 3A 7A 20 6F 6B 6F 6C 69 63 20 50 69 C5 82 79 20 6B 74 6F C5 9B 20 74 75 20 6A 65 73 74 3F 00 03 88 16 C5 28 8C 61 7E
|
||||||
|
| =========== | ===================================================================================
|
||||||
|
| | | |
|
||||||
|
| | | > APRS Address Header (Source,Path, Destination) - "SP3TUT-1": Source Callsign of the transmitting amateur radio station from Poland, using SSID 1.
|
||||||
|
| | | > -
|
||||||
|
| | | >
|
||||||
|
| | > Flags & Hops [7]
|
||||||
|
B02 | > Message Identifier "~1~9" 0xBB31F339 - The unique 32-bit Message ID transmitted in Little-Endian format (least significant byte first).
|
||||||
|
| It is used by the mesh network to track packets and prevent duplication loops.
|
||||||
|
B01 > APRS Data Type Identifier ":" 0x3A - APRS Text Message, used for person-to-person Direct Messages (DMs), group messages, or general announcements.
|
||||||
|
Following this character, the parser expects a destination callsign, a tracking number, and the message content.
|
||||||
|
"@" 0x40 - APRS Position with Timestamp, used for automated position reports that include a precise time (such as weather node beacons or sttic trackers).
|
||||||
|
It tells the terminal to parse fixed-length chunks for latitude, longitude, and elevation.
|
||||||
|
"!" 0x21 - APRS Position without Timestamp, used for real-time mobile tracking (e.g., a node) with an integrated GPS sending live movement updates).
|
||||||
|
It skips the time fields to reduce airtime over LoRa.
|
||||||
|
";" 0x3B - APRS Object Report, used to map an "Object" rather than a station.
|
||||||
|
For example, a node can broadcast the location of a local hamfest, emergency shelter, or repeater.
|
||||||
|
It is followed by a fixed 9-character object name.
|
||||||
|
">" 0x3E - APRS Status Report: Used to transmit a station's current operating state or hardware information rather than geographic coordinates.
|
||||||
|
"$" 0x24 - APRS Raw NMEA Sentence: A legacy data type that allows raw GPS string passthrough (like $GPRMC or $GPGGA) directly from a serial GPS mouse.
|
||||||
|
Other DTI(Data Type Identifiers)
|
||||||
|
"=" 0x3D - Position with compressed APRS capability, a standard position beacon, but formatted in Base-91 compression to save airtime over busy RF networks.
|
||||||
|
"/" 0x2F - Mic-E Format Data, Microphone Encoder telemetry. Used by commercial radios (like Yaesu or Kenwood) to embed a GPS burst into a standard voice transmission PTT release.
|
||||||
|
"`" 0x60 - Mic-E Old Format, Legacy Microphone Encoder burst, rarely used by modern trackers but still supported for backward compatibility.
|
||||||
|
"_" 0x5F - Positionless Weather Report, used by automated weather stations (like fine-offset units or CWOP stations) to transmit pure wind speed, temperature, and barometric values without re-sending latitude/longitude.
|
||||||
|
")" 0x29 - Item Report, identical to a map Object (;), but used for temporary or small Points of Interest (POIs) with a much shorter name (1–9 characters) and no timestamp.
|
||||||
|
"<" 0x3C - Station Capabilities, sent by intelligent digipeaters and internet gateways (iGates) to report what features they support (e.g., whether they handle messaging or special path routing).
|
||||||
|
"T" 0x54 - Raw Telemetry Data, used for automated analog data streams. Frequently found on high-altitude balloon payloads or weather satellites transmitting five channels of changing mathematical numbers.
|
||||||
|
"S" 0x53 - Telemetry Structural Definition, defines the scale and offsets of raw numbers sent in the T packet (so client maps like aprs.fi know if a raw value of 142 means volts, millibars, or degrees).
|
||||||
|
"?" 0x3F - Query/Poll Command, a packet explicitly asking another station or a server to respond. For instance, sending ?APRSP asks surrounding nodes for their current paths.
|
||||||
|
"{" 0x7B - User-Defined / Proprietary, reserved entirely for experimental protocols or internal applications. MeshCom and similar experimental LoRa platforms utilize these spaces to run custom network setups.
|
||||||
|
```
|
||||||
|
## Callsign
|
||||||
|
```
|
||||||
|
Every callsign is build with [Personal Amateur Radio Callsign]-[SSID], SSID is a number between 1 and 99.
|
||||||
|
• -0 (or no suffix): Standard home station / fixed station (usually avoided by MeshCom for mobile operation).
|
||||||
|
• -1 to -3: Additional stations with potential special functions (e.g., digipeaters).
|
||||||
|
• -7: Portable stations, handheld radios (e.g., a Heltec or T-Echo node carried in a pocket).
|
||||||
|
• -9: Mobile stations (vehicles).
|
||||||
|
• -10: Gateways, digipeaters, or Winlink nodes.
|
||||||
|
• -11: Pure client stations without gateway functionality.
|
||||||
|
• -12: Gateway stations including mesh routing functionality (very common for permanently running master nodes with an internet/Wi-Fi connection).
|
||||||
|
• -13: Weather stations or telemetry sensors.
|
||||||
|
```
|
||||||
+1
-10
@@ -163,16 +163,7 @@ void process_command(const char* cmd) {
|
|||||||
|
|
||||||
if (valid_433 || valid_868) {
|
if (valid_433 || valid_868) {
|
||||||
if (lora_system != nullptr) {
|
if (lora_system != nullptr) {
|
||||||
// SX1276 formula: Frf = Ffreq * 2^19 / 32000000
|
lora_system->setFrequency(target_freq_hz);
|
||||||
// Using double to prevent truncation during division before bit masking
|
|
||||||
double fraction = (static_cast<double>(target_freq_hz) * 524288.0) / 32000000.0;
|
|
||||||
uint32_t frf = static_cast<uint32_t>(fraction);
|
|
||||||
|
|
||||||
uint8_t msb = static_cast<uint8_t>((frf >> 16) & 0xFFU);
|
|
||||||
uint8_t mid = static_cast<uint8_t>((frf >> 8) & 0xFFU);
|
|
||||||
uint8_t lsb = static_cast<uint8_t>(frf & 0xFFU);
|
|
||||||
|
|
||||||
lora_system->setFrequency(msb, mid, lsb);
|
|
||||||
printf("Success: Frequency shifted to %lu Hz.\r\n", (unsigned long)target_freq_hz);
|
printf("Success: Frequency shifted to %lu Hz.\r\n", (unsigned long)target_freq_hz);
|
||||||
} else {
|
} else {
|
||||||
printf("Error: LoraController handle is uninitialized.\r\n");
|
printf("Error: LoraController handle is uninitialized.\r\n");
|
||||||
|
|||||||
+39
-5
@@ -45,16 +45,50 @@ namespace Config {
|
|||||||
constexpr gpio_num_t LORA_CS_PIN = GPIO_NUM_18;
|
constexpr gpio_num_t LORA_CS_PIN = GPIO_NUM_18;
|
||||||
constexpr gpio_num_t LORA_RST_PIN = GPIO_NUM_23; // Adjusted configuration for T3_V1.6 hardware revision
|
constexpr gpio_num_t LORA_RST_PIN = GPIO_NUM_23; // Adjusted configuration for T3_V1.6 hardware revision
|
||||||
constexpr gpio_num_t LORA_IRQ_PIN = GPIO_NUM_26;
|
constexpr gpio_num_t LORA_IRQ_PIN = GPIO_NUM_26;
|
||||||
|
constexpr gpio_num_t LORA_DIO1_PIN = GPIO_NUM_33; // Important: PLL lock & Timeout in RX mode
|
||||||
|
|
||||||
// Semtech SX1276 LoRa Configuration Parameters
|
// Semtech SX1276 LoRa Configuration Parameters
|
||||||
// 433.175 MHz is calculated by: 433175000 / (32000000 / 2^19) = 7097139.2 = 0x6c4b33 // wrong: 0x06C4CD
|
// 433.175 MHz is calculated by: 433175000 / (32000000 / 2^19) = 7097139.2 = 0x6c4b33 // wrong: 0x06C4CD
|
||||||
constexpr uint8_t LORA_FRF_MSB = 0x6C;
|
constexpr uint32_t LORA_DEFAULT_FREQ_HZ = 433175000;
|
||||||
constexpr uint8_t LORA_FRF_MID = 0x4B;
|
constexpr uint8_t LORA_FRF_MSB = 0x6C; // @TODO: will be removed in v1.1
|
||||||
constexpr uint8_t LORA_FRF_LSB = 0x33;
|
constexpr uint8_t LORA_FRF_MID = 0x4B; // @TODO: will be removed in v1.1
|
||||||
|
constexpr uint8_t LORA_FRF_LSB = 0x33; // @TODO: will be removed in v1.1
|
||||||
|
constexpr uint32_t LORA_DEFAULT_BW_KHZ = 250;
|
||||||
|
constexpr uint8_t LORA_DEFAULT_SF = 11;
|
||||||
|
constexpr uint8_t LORA_DEFAULT_CR = 6; // CR 4/6 -> Denominator is 6
|
||||||
|
|
||||||
|
// Predefined LoRa Sync Word options for hardware correlation filtering, see docs/lora/device-settings.md
|
||||||
|
constexpr uint8_t LORA_SYNC_WORD_PUBLIC_NETWORK_MODE = 0x34; // MeshCom (ÖVSV standard for amateur radio mesh),
|
||||||
|
// The Things Network, Helium
|
||||||
|
constexpr uint8_t LORA_SYNC_WORD_TACTICAL_NETWORK_MODE = 0x24; // Meshtastic (Civilian off-grid tactical mesh network)
|
||||||
|
constexpr uint8_t LORA_SYNC_WORD_PRIVATE_NETWORK_MODE = 0x12; // Semtech Default (Factory default / Private point-to-point)
|
||||||
|
constexpr uint8_t LORA_SYNC_WORD_ISOLATED_ARRAY_A = 0x55; // Custom / Experimental individual networks (highly isolated)
|
||||||
|
constexpr uint8_t LORA_SYNC_WORD_ISOLATED_ARRAY_B = 0xAA; // Custom / Experimental individual networks (highly isolated)
|
||||||
|
|
||||||
|
// Active hardware synchronization configuration selector
|
||||||
|
constexpr uint8_t LORA_SYNC_WORD = LORA_SYNC_WORD_PUBLIC_NETWORK_MODE; // Selected default synchronization profile
|
||||||
|
|
||||||
|
// Expand hardware preamble length to 12 symbols for robust network correlation (MeshCom value)
|
||||||
|
constexpr uint8_t LORA_PREAMBLE_LENGTH_MSB = 0x00;
|
||||||
|
constexpr uint8_t LORA_PREAMBLE_LENGTH_LSB = 0x0C; // Semtech Factor Default is 0x08, MeshCom with 0x0C, i.e. 12 symbols
|
||||||
|
|
||||||
// Modem Config 1: BW 250kHz (0x80), CR 4/6 (0x04), Explicit Header (0x00) -> 0x84
|
// Modem Config 1: BW 250kHz (0x80), CR 4/6 (0x04), Explicit Header (0x00) -> 0x84
|
||||||
constexpr uint8_t LORA_MODEM_CONFIG_1 = 0x84;
|
// constexpr uint8_t LORA_MODEM_CONFIG_1 = 0x84; // @TODO: will be removed in v1.1
|
||||||
|
|
||||||
// Modem Config 2: SF11 (0xB0), TX Continuous off (0x00), CRC On (0x04) -> 0xB4
|
// Modem Config 2: SF11 (0xB0), TX Continuous off (0x00), CRC On (0x04) -> 0xB4
|
||||||
constexpr uint8_t LORA_MODEM_CONFIG_2 = 0xB4;
|
// constexpr uint8_t LORA_MODEM_CONFIG_2 = 0xB4; // @TODO: will be removed in v1.1
|
||||||
|
|
||||||
|
// --- DYNAMIC COMPILE-TIME BIT-SHIFTING EXPRESSIONS ---
|
||||||
|
|
||||||
|
// Helper to map bandwidth kHz to Semtech Register Bits (7-4 of RegModemConfig1)
|
||||||
|
constexpr uint8_t LORA_BW_REG_VAL = (LORA_DEFAULT_BW_KHZ == 62) ? 0x06 :
|
||||||
|
(LORA_DEFAULT_BW_KHZ == 125) ? 0x07 :
|
||||||
|
(LORA_DEFAULT_BW_KHZ == 250) ? 0x08 :
|
||||||
|
(LORA_DEFAULT_BW_KHZ == 500) ? 0x09 : 0x08;
|
||||||
|
|
||||||
|
// RegModemConfig1 (0x1D): Bandwidth (Bits 7-4), Coding Rate (Bits 3-1), ImplicitHeader (Bit 0 = 0)
|
||||||
|
constexpr uint8_t LORA_MODEM_CONFIG_1 = static_cast<uint8_t>((LORA_BW_REG_VAL << 4) | ((LORA_DEFAULT_CR - 4) << 1));
|
||||||
|
|
||||||
|
// RegModemConfig2 (0x1E): Spreading Factor (Bits 7-4), TxContinuous (Bit 3 = 0), RxPayloadCrcOn (Bit 2 = 1)
|
||||||
|
constexpr uint8_t LORA_MODEM_CONFIG_2 = static_cast<uint8_t>((LORA_DEFAULT_SF << 4) | 0x04U);
|
||||||
}
|
}
|
||||||
+145
-23
@@ -16,10 +16,16 @@
|
|||||||
|
|
||||||
const char* LoraController::TAG = "LORA";
|
const char* LoraController::TAG = "LORA";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Constructor initializing core FreeRTOS synchronization primitives.
|
||||||
|
*/
|
||||||
LoraController::LoraController() {
|
LoraController::LoraController() {
|
||||||
m_isrSemaphore = xSemaphoreCreateBinary();
|
m_isrSemaphore = xSemaphoreCreateBinary();
|
||||||
if (m_isrSemaphore == nullptr) {
|
m_stateMutex = xSemaphoreCreateMutex();
|
||||||
ESP_LOGE(TAG, "Fatal: Failed to allocate LoRa ISR synchronization semaphore.");
|
|
||||||
|
if (m_isrSemaphore == nullptr || m_stateMutex == nullptr) {
|
||||||
|
ESP_LOGE(TAG, "Fatal: Failed to allocate LoRa synchronization primitives.");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,8 +36,16 @@ LoraController::~LoraController() {
|
|||||||
if (m_isrSemaphore != nullptr) {
|
if (m_isrSemaphore != nullptr) {
|
||||||
vSemaphoreDelete(m_isrSemaphore);
|
vSemaphoreDelete(m_isrSemaphore);
|
||||||
}
|
}
|
||||||
|
if (m_stateMutex != nullptr) {
|
||||||
|
vSemaphoreDelete(m_stateMutex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Low-level driver method to extract a byte value from a transceiver configuration register.
|
||||||
|
* @param address The hardware register memory space tracking coordinate byte.
|
||||||
|
* @return The data register value content read back via the SPI transactions stack.
|
||||||
|
*/
|
||||||
uint8_t LoraController::readRegister(uint8_t address) {
|
uint8_t LoraController::readRegister(uint8_t address) {
|
||||||
if (m_spiHandle == nullptr) return 0;
|
if (m_spiHandle == nullptr) return 0;
|
||||||
|
|
||||||
@@ -49,6 +63,11 @@ uint8_t LoraController::readRegister(uint8_t address) {
|
|||||||
return rxData[1];
|
return rxData[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Low-level driver method to override an option byte parameter inside a transceiver hardware register.
|
||||||
|
* @param address The hardware register memory space tracking coordinate byte.
|
||||||
|
* @param value The option layout bits context to be committed directly into the transceiver.
|
||||||
|
*/
|
||||||
void LoraController::writeRegister(uint8_t address, uint8_t value) {
|
void LoraController::writeRegister(uint8_t address, uint8_t value) {
|
||||||
if (m_spiHandle == nullptr) return;
|
if (m_spiHandle == nullptr) return;
|
||||||
|
|
||||||
@@ -76,6 +95,10 @@ void IRAM_ATTR LoraController::gpioIsrHandler(void* arg) {
|
|||||||
}
|
}
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Orchestrates base interface configurations, system pin assignments, and checks silicon identities.
|
||||||
|
* @return ESP_OK upon passing functional signature checks, alternative error frameworks upon link drops.
|
||||||
|
*/
|
||||||
esp_err_t LoraController::initializeHardware() {
|
esp_err_t LoraController::initializeHardware() {
|
||||||
esp_err_t err;
|
esp_err_t err;
|
||||||
|
|
||||||
@@ -110,6 +133,9 @@ esp_err_t LoraController::initializeHardware() {
|
|||||||
}
|
}
|
||||||
ESP_LOGI(TAG, "SX1276 Handshake success. Silicon version confirmed: 0x12");
|
ESP_LOGI(TAG, "SX1276 Handshake success. Silicon version confirmed: 0x12");
|
||||||
|
|
||||||
|
// Invoke the modular modem configuration
|
||||||
|
applyModemConfig();
|
||||||
|
|
||||||
err = gpio_install_isr_service(0);
|
err = gpio_install_isr_service(0);
|
||||||
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
|
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
|
||||||
return err;
|
return err;
|
||||||
@@ -121,6 +147,7 @@ esp_err_t LoraController::initializeHardware() {
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
void LoraController::setModeReceiver() {
|
void LoraController::setModeReceiver() {
|
||||||
writeRegister(0x01, 0x80U); // Sleep + LoRa Mode
|
writeRegister(0x01, 0x80U); // Sleep + LoRa Mode
|
||||||
vTaskDelay(pdMS_TO_TICKS(10));
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
@@ -135,9 +162,56 @@ void LoraController::setModeReceiver() {
|
|||||||
|
|
||||||
writeRegister(0x40, 0x00U); // DIO0 map onto RxDone
|
writeRegister(0x40, 0x00U); // DIO0 map onto RxDone
|
||||||
writeRegister(0x01, 0x85U); // RXCONTINUOUS
|
writeRegister(0x01, 0x85U); // RXCONTINUOUS
|
||||||
ESP_LOGI(TAG, "Radio configured: 433.175 MHz | BW 250kHz | SF11 | CR 4/6");
|
ESP_LOGI(TAG, "Radio state changed to RXCONTINUOUS");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
void LoraController::setModeReceiver() {
|
||||||
|
// DIO0 auf RxDone mappen (Bit 7-6 auf 00)
|
||||||
|
writeRegister(0x40, 0x00U);
|
||||||
|
|
||||||
|
// In den kontinuierlichen Empfangsmodus schalten
|
||||||
|
writeRegister(0x01, 0x85U);
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "Radio state changed to RXCONTINUOUS");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Applies the modem profile, RF carrier frequency, and network sync word.
|
||||||
|
* Note: The transceiver must transition through SLEEP mode to apply these alterations safely.
|
||||||
|
*/
|
||||||
|
void LoraController::applyModemConfig() {
|
||||||
|
// Force Sleep + LoRa mode to unlock configuration registers
|
||||||
|
writeRegister(0x01, 0x80U);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
|
||||||
|
// Apply RF carrier center frequency (e.g., MeshCom 433.175 MHz)
|
||||||
|
writeRegister(0x06, Config::LORA_FRF_MSB);
|
||||||
|
writeRegister(0x07, Config::LORA_FRF_MID);
|
||||||
|
writeRegister(0x08, Config::LORA_FRF_LSB);
|
||||||
|
|
||||||
|
// just write the m_state frequency....
|
||||||
|
m_state.frequencyHz = Config::LORA_DEFAULT_FREQ_HZ; // @TODO: remove this line after implementation of the doubly linked list
|
||||||
|
|
||||||
|
// Configure modem bandwidth, spreading factor, and coding rate
|
||||||
|
writeRegister(0x1D, Config::LORA_MODEM_CONFIG_1);
|
||||||
|
writeRegister(0x1E, Config::LORA_MODEM_CONFIG_2);
|
||||||
|
|
||||||
|
// Set the network sync word (0x34 for open/public amateur networks like MeshCom)
|
||||||
|
writeRegister(0x39, Config::LORA_SYNC_WORD);
|
||||||
|
|
||||||
|
// Set the preamble length (0x0C instead of 0x08 for more stability, see docs/lora/device-settings.md)
|
||||||
|
writeRegister(0x20, Config::LORA_PREAMBLE_LENGTH_MSB);
|
||||||
|
writeRegister(0x21, Config::LORA_PREAMBLE_LENGTH_LSB);
|
||||||
|
|
||||||
|
// Transition to STANDBY to allow the internal PLL to lock and stabilize
|
||||||
|
writeRegister(0x01, 0x81U);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(5));
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Radio profile applied: 433.175 MHz | SF11 | BW 250kHz | SyncWord 0x34");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
void LoraController::setFrequency(uint8_t msb, uint8_t mid, uint8_t lsb) {
|
void LoraController::setFrequency(uint8_t msb, uint8_t mid, uint8_t lsb) {
|
||||||
if (m_spiHandle == nullptr) return;
|
if (m_spiHandle == nullptr) return;
|
||||||
|
|
||||||
@@ -153,6 +227,11 @@ void LoraController::setFrequency(uint8_t msb, uint8_t mid, uint8_t lsb) {
|
|||||||
writeRegister(0x01, 0x85U);
|
writeRegister(0x01, 0x85U);
|
||||||
ESP_LOGI(TAG, "Frequency dynamically shifted to: 0x%02X%02X%02X", msb, mid, lsb);
|
ESP_LOGI(TAG, "Frequency dynamically shifted to: 0x%02X%02X%02X", msb, mid, lsb);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
void LoraController::setFrequency(uint32_t freq_hz) {
|
||||||
|
// @TODO
|
||||||
|
}
|
||||||
|
|
||||||
void LoraController::setBandwidth(uint32_t bw_khz) {
|
void LoraController::setBandwidth(uint32_t bw_khz) {
|
||||||
if (m_spiHandle == nullptr) return;
|
if (m_spiHandle == nullptr) return;
|
||||||
@@ -204,6 +283,38 @@ void LoraController::taskTrampoline(void* pvParameters) {
|
|||||||
vTaskDelete(nullptr);
|
vTaskDelete(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Computes high-precision calibrated RSSI and SNR configurations based on frequency bands.
|
||||||
|
* @note Now uses high-precision float conversions to preserve the full 0.25 dB native hardware steps.
|
||||||
|
* @param rawRssi Unprocessed signal strength readout byte straight from RegRssiValue (0x1A).
|
||||||
|
* @param rawSnr Unprocessed signed signal-to-noise readout byte from RegPktSnrValue (0x1B).
|
||||||
|
* @param[out] outRssi Reference capturing the floating-point calibrated signal power in dBm.
|
||||||
|
* @param[out] outSnr Reference capturing the floating-point calibrated signal-to-noise ratio in dB.
|
||||||
|
*/
|
||||||
|
void LoraController::calculateTelemetry(uint8_t rawRssi, uint8_t rawSnr, float& outRssi, float& outSnr) {
|
||||||
|
uint32_t currentFreq = 0;
|
||||||
|
|
||||||
|
// Securely pull the active runtime frequency configuration
|
||||||
|
if (xSemaphoreTake(m_stateMutex, portMAX_DELAY) == pdTRUE) {
|
||||||
|
currentFreq = m_state.frequencyHz;
|
||||||
|
xSemaphoreGive(m_stateMutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. Precise SNR calculation (preserve native 0.25 dB resolution step by step)
|
||||||
|
float actualSnr = static_cast<float>(static_cast<int8_t>(rawSnr)) * 0.25f;
|
||||||
|
outSnr = actualSnr;
|
||||||
|
|
||||||
|
// 2. Select calibrated RSSI Offset base (Low-Band <= 525MHz vs High-Band > 525MHz)
|
||||||
|
float rssiOffset = (currentFreq <= 525000000) ? -164.0f : -157.0f;
|
||||||
|
|
||||||
|
// 3. Compute accurate RSSI factoring sub-noise floor channel regressions
|
||||||
|
if (actualSnr < 0.0f) {
|
||||||
|
outRssi = rssiOffset + static_cast<float>(rawRssi) + actualSnr;
|
||||||
|
} else {
|
||||||
|
outRssi = rssiOffset + static_cast<float>(rawRssi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void LoraController::run() {
|
void LoraController::run() {
|
||||||
if (initializeHardware() != ESP_OK) {
|
if (initializeHardware() != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "Critical: LoRa init failed. Thread aborted.");
|
ESP_LOGE(TAG, "Critical: LoRa init failed. Thread aborted.");
|
||||||
@@ -212,6 +323,7 @@ void LoraController::run() {
|
|||||||
|
|
||||||
setModeReceiver();
|
setModeReceiver();
|
||||||
extern DisplayController* display; // Reference the global display singleton
|
extern DisplayController* display; // Reference the global display singleton
|
||||||
|
static uint32_t packetCounter = 0; // overflow in abt. 272 years (1 packet every 2 seconds)
|
||||||
|
|
||||||
ESP_LOGI(TAG, "LoRa RX was initialized and is running");
|
ESP_LOGI(TAG, "LoRa RX was initialized and is running");
|
||||||
|
|
||||||
@@ -221,6 +333,7 @@ void LoraController::run() {
|
|||||||
writeRegister(0x12, static_cast<uint8_t>(irqFlags));
|
writeRegister(0x12, static_cast<uint8_t>(irqFlags));
|
||||||
|
|
||||||
if ((irqFlags & 0x40U) != 0U) { // RxDone Flag
|
if ((irqFlags & 0x40U) != 0U) { // RxDone Flag
|
||||||
|
packetCounter++;
|
||||||
LoraPacket packet = {};
|
LoraPacket packet = {};
|
||||||
packet.length = readRegister(0x13);
|
packet.length = readRegister(0x13);
|
||||||
|
|
||||||
@@ -231,36 +344,45 @@ void LoraController::run() {
|
|||||||
packet.payload[i] = readRegister(0x00);
|
packet.payload[i] = readRegister(0x00);
|
||||||
}
|
}
|
||||||
// Calculate the telemetry data
|
// Calculate the telemetry data
|
||||||
packet.rssi = static_cast<int8_t>(static_cast<int>(readRegister(0x1A)) - 157);
|
// packet.rssi = static_cast<int8_t>(static_cast<int>(readRegister(0x1A)) - 157);
|
||||||
packet.snr = static_cast<int8_t>(static_cast<int>(readRegister(0x1B)) / 4);
|
// packet.snr = static_cast<int8_t>(static_cast<int>(readRegister(0x1B)) / 4);
|
||||||
|
// Read raw values from transceiver registers
|
||||||
|
uint8_t rawRssiReg = readRegister(0x1A);
|
||||||
|
uint8_t rawSnrReg = readRegister(0x1B);
|
||||||
|
|
||||||
// LIVE-AUSGABE DIREKT AUF DIE CONSOLE (Garantiert un-gemutet!)
|
// Dynamically process telemetry parameters with floating point precision
|
||||||
|
calculateTelemetry(rawRssiReg, rawSnrReg, packet.rssi, packet.snr);
|
||||||
|
|
||||||
|
// Formatted terminal trace using fixed precision formatting (.1f)
|
||||||
printf("\r\n--------------------------------------------------\r\n");
|
printf("\r\n--------------------------------------------------\r\n");
|
||||||
printf("[LoRa RX] Paket erfolgreich empfangen!\r\n");
|
printf("[LoRa RX] Packet #%lu successfully received!\r\n", (unsigned long)packetCounter);
|
||||||
printf(" Laenge: %u Bytes\r\n", packet.length);
|
// printf(" Database ID : %lu\r\n", (unsigned long)nodeDbId);
|
||||||
printf(" RSSI : %d dBm (Signalstaerke)\r\n", packet.rssi);
|
printf(" Length : %u Bytes\r\n", packet.length);
|
||||||
printf(" SNR : %d dB (Rauschabstand)\r\n", packet.snr);
|
printf(" RSSI : %.1f dBm (Signal Strength)\r\n", packet.rssi);
|
||||||
//ESP_LOGI(TAG, "Incoming frame packet received! Size: %u bytes, RSSI: %d dBm, SNR: %d dB",
|
printf(" SNR : %.1f dB (Signal-to-Noise Ratio)\r\n", packet.snr);
|
||||||
// packet.length, packet.rssi, packet.snr);
|
printf(" Content: \"");
|
||||||
|
|
||||||
// ASCII-Klartext-Ausgabe für das Terminal formatiert
|
|
||||||
printf(" Inhalt: \"");
|
|
||||||
for (uint8_t i = 0; i < packet.length; i++) {
|
for (uint8_t i = 0; i < packet.length; i++) {
|
||||||
char c = static_cast<char>(packet.payload[i]);
|
char c = static_cast<char>(packet.payload[i]);
|
||||||
// Nur druckbare Zeichen ausgeben, um das Terminal nicht zu verwirren
|
// Only print printable characters to avoid confusing the terminal
|
||||||
if (c >= 32 && c <= 126) {
|
if (c >= 32 && c <= 126)
|
||||||
printf("%c", c);
|
printf("%c", c);
|
||||||
} else {
|
else
|
||||||
printf("."); // Punkt für nicht-druckbare Binärbytes
|
printf("~"); // not printable chars
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
printf("\"\r\n Hex : ");
|
||||||
printf("\"\r\n--------------------------------------------------\r\n");
|
for (uint8_t i = 0; i < packet.length; i++)
|
||||||
printf("%s # ", Config::PROJECT_TITLE); // CLI-Prompt wiederherstellen
|
printf("%02X ", packet.payload[i]);
|
||||||
|
|
||||||
|
printf("\r\n--------------------------------------------------\r\n");
|
||||||
|
printf("%s # ", Config::PROJECT_TITLE); // CLI prompt
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
|
||||||
// FIXED: Uses the new parameter order (line first, text second)
|
// FIXED: Uses the new parameter order (line first, text second)
|
||||||
if (display != nullptr) {
|
if (display != nullptr) {
|
||||||
display->printMessage(1, "LoRa Msg Received");
|
char displayBuffer[16];
|
||||||
|
snprintf(displayBuffer, sizeof(displayBuffer), "LoRa rx #%03u", (unsigned int)(packetCounter % 1000));
|
||||||
|
display->printMessage(2, displayBuffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+39
-8
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* @file lora.hpp
|
* @file lora.hpp
|
||||||
* @brief Thread-safe asynchronous Semtech SX1276 LoRa transceiver driver for FoxMesh
|
* @brief Thread-safe asynchronous Semtech SX1276 LoRa transceiver driver interface.
|
||||||
* @author OM Tom (OE3TKT)
|
* @author OM Tom (OE3TKT)
|
||||||
*
|
*
|
||||||
* @license SPDX-License-Identifier: MIT
|
* @license SPDX-License-Identifier: MIT
|
||||||
@@ -15,19 +15,26 @@
|
|||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/semphr.h"
|
#include "freertos/semphr.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
|
#include "lora_types.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Controller class orchestrating hardware configurations, data transfers, and metrics collection.
|
||||||
|
*/
|
||||||
class LoraController {
|
class LoraController {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Structure defining the localized packet buffer space.
|
* @brief Simple internal abstraction layer mirroring the raw incoming packet payload.
|
||||||
*/
|
*/
|
||||||
struct LoraPacket {
|
struct LoraPacket {
|
||||||
uint8_t payload[256]; ///< Raw data payload frame matching max SX1276 FIFO allocation
|
float rssi; /**< High-precision calibrated received signal power in dBm. */
|
||||||
uint8_t length; ///< Total number of active bytes captured inside the frame
|
float snr; /**< High-precision calibrated signal-to-noise ratio in dB. */
|
||||||
int8_t rssi; ///< Received Signal Strength Indicator metric in dBm
|
uint8_t length; /**< Payload size explicitly specified in bytes. */
|
||||||
int8_t snr; ///< Signal-to-Noise Ratio metric in dB
|
uint8_t payload[256]; /**< Fixed internal memory buffer capturing raw ingress bytes. Max SX1276 FIFO is 256. */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
explicit LoraController();
|
explicit LoraController();
|
||||||
virtual ~LoraController();
|
virtual ~LoraController();
|
||||||
|
|
||||||
@@ -55,7 +62,13 @@ public:
|
|||||||
* @param mid Middle Significant Byte of the frequency fraction.
|
* @param mid Middle Significant Byte of the frequency fraction.
|
||||||
* @param lsb Least Significant Byte of the frequency fraction.
|
* @param lsb Least Significant Byte of the frequency fraction.
|
||||||
*/
|
*/
|
||||||
void setFrequency(uint8_t msb, uint8_t mid, uint8_t lsb);
|
// void setFrequency(uint8_t msb, uint8_t mid, uint8_t lsb);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Thread-safely updates the operational state tracking variables and overrides transceiver registers.
|
||||||
|
* @param freq_hz Core RF targeting frequency structured in absolute Hertz scale units.
|
||||||
|
*/
|
||||||
|
void setFrequency(uint32_t freq_hz);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Dynamically updates the LoRa signal bandwidth at runtime.
|
* @brief Dynamically updates the LoRa signal bandwidth at runtime.
|
||||||
@@ -64,11 +77,15 @@ public:
|
|||||||
void setBandwidth(uint32_t bw_khz);
|
void setBandwidth(uint32_t bw_khz);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static const char* TAG;
|
||||||
|
|
||||||
spi_device_handle_t m_spiHandle = nullptr;
|
spi_device_handle_t m_spiHandle = nullptr;
|
||||||
TaskHandle_t m_taskHandle = nullptr;
|
TaskHandle_t m_taskHandle = nullptr;
|
||||||
SemaphoreHandle_t m_isrSemaphore = nullptr;
|
SemaphoreHandle_t m_isrSemaphore = nullptr;
|
||||||
|
|
||||||
static const char* TAG;
|
LoraStateTable m_state; ///< Internal volatile tracking record data context.
|
||||||
|
SemaphoreHandle_t m_stateMutex = nullptr; ///< Mutex primitive enforcing mutual exclusion over m_state modifications.
|
||||||
|
|
||||||
|
|
||||||
static void taskTrampoline(void* pvParameters);
|
static void taskTrampoline(void* pvParameters);
|
||||||
static void IRAM_ATTR gpioIsrHandler(void* arg);
|
static void IRAM_ATTR gpioIsrHandler(void* arg);
|
||||||
@@ -79,4 +96,18 @@ private:
|
|||||||
|
|
||||||
esp_err_t initializeHardware();
|
esp_err_t initializeHardware();
|
||||||
void setModeReceiver();
|
void setModeReceiver();
|
||||||
|
|
||||||
|
void applyModemConfig();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Evaluates raw register readings into calibrated RSSI and SNR telemetry metrics.
|
||||||
|
* @details Automatically compensates the RSSI offset base based on the active carrier frequency
|
||||||
|
* band (Low-Band vs High-Band boundary at 525 MHz) as well as sub-noise SNR conditions.
|
||||||
|
* @param rawRssi The raw digital output read from RegRssiValue (0x1A).
|
||||||
|
* @param rawSnr The raw digital twos-complement output read from RegPktSnrValue (0x1B).
|
||||||
|
* @param[out] outRssi Reference destination pointer receiving the calibrated RSSI value in dBm.
|
||||||
|
* @param[out] outSnr Reference destination pointer receiving the calibrated SNR value in dB.
|
||||||
|
*/
|
||||||
|
void calculateTelemetry(uint8_t rawRssi, uint8_t rawSnr, float& outRssi, float& outSnr);
|
||||||
|
|
||||||
};
|
};
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* @file lora_types.hpp
|
||||||
|
* @brief Definition of data structures, nodes, and state tables for the LoRa driver.
|
||||||
|
* @author OM Tom (OE3TKT)
|
||||||
|
*
|
||||||
|
* @license SPDX-License-Identifier: MIT
|
||||||
|
* @copyright Copyright (c) 2026 OM Tom (OE3TKT)
|
||||||
|
* For the full license terms, see the LICENSE file in the project root.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Runtime operational state table monitoring configurations and historical packet data.
|
||||||
|
* @note Optimized using "largest-to-smallest" data type ordering to completely eliminate memory padding.
|
||||||
|
*/
|
||||||
|
struct LoraStateTable {
|
||||||
|
// std::list<LoraPacketNode> packetList; ///< Central database component built on a doubly-linked list for versatile node operations. (Contains pointers)
|
||||||
|
size_t maxHistorySize = 10; ///< Soft-limit threshold capping the maximum number of history records stored before auto-purge. (4 or 8 Bytes)
|
||||||
|
uint32_t frequencyHz = 433175000; ///< RF carrier operating frequency configured in Hertz. (4 Bytes)
|
||||||
|
uint32_t totalPacketsReceived = 0; ///< Accumulator metric track-counting successful ingress frames. (4 Bytes)
|
||||||
|
uint32_t totalPacketsTransmitted = 0; ///< Accumulator metric track-counting egress frames transmitted. (4 Bytes)
|
||||||
|
uint32_t nextDatabaseId = 1; ///< Identity sequencer pointer controlling the next node's databaseId field. (4 Bytes)
|
||||||
|
uint32_t bandwidthKhz = 250; ///< Modem bandwidth performance target configured in kHz. (4 Bytes)
|
||||||
|
uint8_t spreadingFactor = 11; ///< LoRa Spreading Factor configuration (typically ranges between 6 and 12). (1 Byte)
|
||||||
|
uint8_t codingRate = 6; ///< Coding Rate denominator setting (e.g., value 6 yields a 4/6 coding protection overhead). (1 Byte)
|
||||||
|
bool isInitialized = false; ///< True if hardware was successfully validated and attached during initialization. (1 Byte)
|
||||||
|
|
||||||
|
// The three 1-byte variables at the bottom align perfectly into a 32-bit slot, leaving minimal trailing padding.
|
||||||
|
};
|
||||||
+2
-1
@@ -129,7 +129,8 @@ extern "C" {
|
|||||||
char msg[32];
|
char msg[32];
|
||||||
//display->printMessage(0, "FoxMesh " Config::PROJECT_VERSION " \x86" "OE3TKT");
|
//display->printMessage(0, "FoxMesh " Config::PROJECT_VERSION " \x86" "OE3TKT");
|
||||||
snprintf(msg, sizeof(msg), "FoxMesh %s OE3TKT", Config::PROJECT_VERSION);
|
snprintf(msg, sizeof(msg), "FoxMesh %s OE3TKT", Config::PROJECT_VERSION);
|
||||||
display->printMessage(1, msg);
|
display->printMessage(0, msg);
|
||||||
|
display->printMessage(1, "----------------------");
|
||||||
//display->printMessage(2, "Bärbel frühstückt ©");
|
//display->printMessage(2, "Bärbel frühstückt ©");
|
||||||
}
|
}
|
||||||
// Main system watchdog and status monitor loop
|
// Main system watchdog and status monitor loop
|
||||||
|
|||||||
In neuem Issue referenzieren
Einen Benutzer sperren