611 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			611 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #pragma Once
 | |
| #include <Arduino.h>
 | |
| 
 | |
| //#define SILENT              // turn off serial output
 | |
| #define CC_MY_NODE_NUM      0xC00BCE11
 | |
| #define CC_MY_REGION        meshtastic_Config_LoRaConfig_RegionCode_EU_868     // see regions[] below
 | |
| #define CC_MY_LORA_PRESET   meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST // LONG FAST is default preset
 | |
| #define CC_LORA_USE_PRESET  true   // set true to use modem preset
 | |
| #define CC_CHANNEL_NAME     "LongFast"
 | |
| #define CC_CHANNEL_SLOT     0  // "0" for calculated slot. Any other number = chosen slot. Count starts with 1.
 | |
| 
 | |
| #define CC_MY_LORA_BW       125.0   // use these settings, if not using a modem preset
 | |
| #define CC_MY_LORA_SF       9
 | |
| #define CC_MY_LORA_CR       5
 | |
| #define CC_MY_LORA_POWER    20      // 0 = max legal power for region
 | |
| #define CC_MY_LORA_FREQ     0.0     // if you want to override frequency calculation: Freq in MHz (e.g. 869.4)
 | |
| 
 | |
| #define CC_MAX_POWER        22      // TX power setting. Absolute Max for CubeCell is 22, enforced by RadioLib.
 | |
| 
 | |
| #define CC_MONITOR_ONLY     false   // set true to suppress transmitting of packets (just monitor the traffic)
 | |
| 
 | |
| #define CC_SIGNAL_NEOPIXEL  // signal received packets with a green blink, signal transmits with red blink 
 | |
| //#define CC_SIGNAL_GPIO13  // signal received packets with the green LED on HTCC-AB02A
 | |
|                             // http://community.heltec.cn/t/htcc-ab02a-has-a-secret-green-led/3092/7
 | |
| 
 | |
| #define MAX_ID_LIST         64 // number of stored packet IDs to prevent unnecesary repeating
 | |
| #define MAX_NODE_LIST       20 // number of stored known nodes
 | |
| #define MAX_TX_QUEUE        8 // max number of packets which can be waiting for transmission
 | |
| 
 | |
| /// 16 bytes of random PSK for our _public_ default channel that all devices power up on (AES128)
 | |
| /// Meshtastic default key (AQ==):
 | |
| static const uint8_t mypsk[] = {0xd4, 0xf1, 0xbb, 0x3a, 0x20, 0x29, 0x07, 0x59,
 | |
|                                 0xf0, 0xbc, 0xff, 0xab, 0xcf, 0x4e, 0x69, 0x01};
 | |
| // No Crypto = all zero
 | |
| 
 | |
| #ifndef SILENT
 | |
|     #define MSG(...) Serial.printf(__VA_ARGS__)
 | |
|     #define MSGFLOAT(a,b) Serial.print(a); Serial.print(b)
 | |
| #else
 | |
|     #define MSG(...)
 | |
|     #define MSGFLOAT(a,b)
 | |
| #endif
 | |
| 
 | |
| #define PACKET_FLAGS_HOP_LIMIT_MASK 0x07
 | |
| #define PACKET_FLAGS_WANT_ACK_MASK 0x08
 | |
| #define PACKET_FLAGS_VIA_MQTT_MASK 0x10
 | |
| #define PACKET_FLAGS_HOP_START_MASK 0xE0
 | |
| #define PACKET_FLAGS_HOP_START_SHIFT 5
 | |
| 
 | |
| #include <RadioLib.h>
 | |
| 
 | |
| /**************/
 | |
| #ifdef CUBECELL
 | |
| 
 | |
| // Heltec borked the Arduino.h
 | |
| #ifdef __cplusplus
 | |
| #undef min
 | |
| #undef max
 | |
| #undef abs
 | |
| #include <algorithm>
 | |
|   using std::abs;
 | |
|   using std::max;
 | |
|   using std::min;
 | |
| #endif /* __cplusplus */
 | |
| 
 | |
| #include "cyPm.c"  // for reliable sleep we use MCU_deepSleep()
 | |
| extern  uint32_t systime;   // CubeCell global system time count, Millis
 | |
| SX1262 radio = new Module(RADIOLIB_BUILTIN_MODULE);
 | |
| 
 | |
| #ifdef CC_SIGNAL_NEOPIXEL
 | |
| #include "CubeCell_NeoPixel.h"
 | |
| CubeCell_NeoPixel pixels(1, RGB, NEO_GRB + NEO_KHZ800);
 | |
| #endif
 | |
| 
 | |
| #endif // CUBECELL
 | |
| /****************/
 | |
| 
 | |
| //#include <assert.h>
 | |
| #include <pb.h>
 | |
| #include <MeshTypes.h>
 | |
| #include <pb_decode.h>
 | |
| #include <pb_encode.h>
 | |
| #include <CryptoEngine.h>
 | |
| #include <time.h>
 | |
| /*
 | |
| extern "C"
 | |
| { 
 | |
| #include <mesh/compression/unishox2.h>
 | |
| }
 | |
| */
 | |
| 
 | |
| #define MAX_RHPACKETLEN 256
 | |
| 
 | |
| // struct to store the raw packet data (buf, size) and the time of receiving
 | |
| typedef struct {
 | |
|     size_t   size;
 | |
|     uint8_t  buf[MAX_RHPACKETLEN];
 | |
|     uint32_t packetTime;
 | |
| } Packet_t;
 | |
| 
 | |
| typedef struct {
 | |
|     uint32_t to, from;
 | |
|     uint32_t id;
 | |
|     uint8_t flags;
 | |
|     uint8_t channel;
 | |
|     uint8_t next_hop;
 | |
|     uint8_t relay_node;
 | |
| } PacketHeader; // see Meshtastic RadioInterface.h
 | |
| 
 | |
| class PacketQueueClass {
 | |
| private:
 | |
|     Packet_t Queue[MAX_TX_QUEUE];
 | |
| public:
 | |
|     void clear(void);
 | |
|     uint8_t Count = 0;
 | |
|     void add(Packet_t* p);
 | |
|     bool pop(void);
 | |
| } txQueue;
 | |
| 
 | |
| class idStoreClass {
 | |
| private:
 | |
|     uint32_t storage[MAX_ID_LIST];
 | |
| public:
 | |
|     void clear(void);
 | |
|     // add() returns false if the message id is already known
 | |
|     bool add(uint32_t id);
 | |
| } msgID;
 | |
| 
 | |
| class NodeStoreClass {
 | |
| private:
 | |
|     meshtastic_NodeInfo nodeDB[MAX_NODE_LIST];
 | |
|     void add(meshtastic_NodeInfo* Node);
 | |
| public:
 | |
|     void clear(void);
 | |
|     void update(meshtastic_NodeInfo* Node);
 | |
|     // get the short name of the node. "" if unknown.
 | |
|     char* get(uint32_t num);
 | |
| } NodeDB;
 | |
| 
 | |
| CryptoKey psk;
 | |
| meshtastic_MeshPacket mp;
 | |
| meshtastic_NodeInfo theNode;
 | |
| bool repeatPacket = false;
 | |
| int err = RADIOLIB_ERR_NONE;
 | |
| bool PacketReceived = false;
 | |
| bool PacketSent = false;
 | |
| bool isReceiving = false;
 | |
| 
 | |
| /* Packet we are currently wanting to send 
 | |
|  * .packetTime is the time we started trying to send it.
 | |
|  * packet will be dropped after a minute.
 | |
|  */ 
 | |
| Packet_t PacketToSend;
 | |
| Packet_t* p = NULL;
 | |
| 
 | |
| // global dio1 flag - will be set to true, when dio1 is active
 | |
| volatile bool dio1 = false;
 | |
| 
 | |
| // interrupt service routine for setting the dio1 flag
 | |
| void ISR_dio1Action(void) {
 | |
|      dio1 = true;
 | |
| }
 | |
| 
 | |
| void MCU_deepsleep(void);
 | |
| void startReceive(void);
 | |
| bool perhapsSend(Packet_t* p);
 | |
| bool perhapsDecode(Packet_t* p);
 | |
| void printPacket(void);
 | |
| void printVariants(void);
 | |
| 
 | |
| void signalizeRX_ON(void);
 | |
| void signalizeTX_ON(void);
 | |
| void signalizeLED_OFF(void);
 | |
| void init_signalize(void);
 | |
| 
 | |
| /**************
 | |
|  * Meshtastic *     https://github.com/meshtastic/firmware
 | |
|  **************/
 | |
|     float   bw = 0;
 | |
|     uint8_t sf = 0;
 | |
|     uint8_t cr = 0;
 | |
|     int8_t power = CC_MY_LORA_POWER; // 0 = max legal power for region
 | |
|     float   freq = 0;
 | |
|     float    snr = 5.0;
 | |
|     uint32_t activeReceiveStart = 0;
 | |
| 
 | |
| /** Slottime is the minimum time to wait, consisting of:
 | |
|       - CAD duration (maximum of SX126x and SX127x);
 | |
|       - roundtrip air propagation time (assuming max. 30km between nodes);
 | |
|       - Tx/Rx turnaround time (maximum of SX126x and SX127x);
 | |
|       - MAC processing time (measured on T-beam) */
 | |
|     uint32_t slotTimeMsec; //= 8.5 * pow(2, sf) / bw + 0.2 + 0.4 + 7;  --> calculated at applyModemConfig()
 | |
|     uint16_t preambleLength = 16;      // 8 is default, but we use longer to increase the amount of sleep time when receiving
 | |
|     uint32_t preambleTimeMsec = 165;   // calculated on startup, this is the default for LongFast
 | |
|     uint32_t maxPacketTimeMsec = 3246; // calculated on startup, this is the default for LongFast
 | |
|     const uint32_t PROCESSING_TIME_MSEC =
 | |
|         4500;                // time to construct, process and construct a packet again (empirically determined)
 | |
|     const uint8_t CWmin = 2; // minimum CWsize
 | |
|     const uint8_t CWmax = 8; // maximum CWsize
 | |
| 
 | |
| #define PACKET_FLAGS_HOP_MASK 0x07
 | |
| #define PACKET_FLAGS_WANT_ACK_MASK 0x08
 | |
| #define PACKET_FLAGS_VIA_MQTT_MASK 0x10
 | |
| 
 | |
| /// helper function for encoding a record as a protobuf, any failures to encode are fatal and we will panic
 | |
| /// returns the encoded packet size
 | |
| size_t pb_encode_to_bytes(uint8_t *destbuf, size_t destbufsize, const pb_msgdesc_t *fields, const void *src_struct)
 | |
| {
 | |
|     pb_ostream_t stream = pb_ostream_from_buffer(destbuf, destbufsize);
 | |
|     if (!pb_encode(&stream, fields, src_struct)) {
 | |
|         MSG("[ERROR]Panic: can't encode protobuf reason='%s'\n", PB_GET_ERROR(&stream));
 | |
|         //assert(0); // If this assert fails it probably means you made a field too large for the max limits specified in mesh.options
 | |
|     } else {
 | |
|         return stream.bytes_written;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /// helper function for decoding a record as a protobuf, we will return false if the decoding failed
 | |
| bool pb_decode_from_bytes(const uint8_t *srcbuf, size_t srcbufsize, const pb_msgdesc_t *fields, void *dest_struct)
 | |
| {
 | |
|     pb_istream_t stream = pb_istream_from_buffer(srcbuf, srcbufsize);
 | |
|     if (!pb_decode(&stream, fields, dest_struct)) {
 | |
|         MSG("[ERROR]Can't decode protobuf reason='%s', pb_msgdesc %p\n", PB_GET_ERROR(&stream), fields);
 | |
|         return false;
 | |
|     } else {
 | |
|         return true;
 | |
|     }
 | |
| }
 | |
| 
 | |
| #define RDEF(name, freq_start, freq_end, duty_cycle, spacing, power_limit, audio_permitted, frequency_switching, wide_lora)      \
 | |
|     {                                                                                                                            \
 | |
|         meshtastic_Config_LoRaConfig_RegionCode_##name, freq_start, freq_end, duty_cycle, spacing, power_limit, audio_permitted, \
 | |
|             frequency_switching, wide_lora, #name                                                                                \
 | |
|     }
 | |
| 
 | |
| struct RegionInfo {
 | |
|     meshtastic_Config_LoRaConfig_RegionCode code;
 | |
|     float freqStart;
 | |
|     float freqEnd;
 | |
|     float dutyCycle;
 | |
|     float spacing;
 | |
|     uint8_t powerLimit; // Or zero for not set
 | |
|     bool audioPermitted;
 | |
|     bool freqSwitching;
 | |
|     bool wideLora;
 | |
|     const char *name; // EU433 etc
 | |
| };
 | |
| 
 | |
| const RegionInfo *myRegion;
 | |
| 
 | |
| const RegionInfo regions[] = {
 | |
|     /*
 | |
|         https://link.springer.com/content/pdf/bbm%3A978-1-4842-4357-2%2F1.pdf
 | |
|         https://www.thethingsnetwork.org/docs/lorawan/regional-parameters/
 | |
|     */
 | |
|     RDEF(US, 902.0f, 928.0f, 100, 0, 30, true, false, false),
 | |
| 
 | |
|     /*
 | |
|         https://lora-alliance.org/wp-content/uploads/2020/11/lorawan_regional_parameters_v1.0.3reva_0.pdf
 | |
|      */
 | |
|     RDEF(EU_433, 433.0f, 434.0f, 10, 0, 12, true, false, false),
 | |
| 
 | |
|     /*
 | |
|         https://www.thethingsnetwork.org/docs/lorawan/duty-cycle/
 | |
|         https://www.thethingsnetwork.org/docs/lorawan/regional-parameters/
 | |
|         https://www.legislation.gov.uk/uksi/1999/930/schedule/6/part/III/made/data.xht?view=snippet&wrap=true
 | |
| 
 | |
|         audio_permitted = false per regulation
 | |
| 
 | |
|         Special Note:
 | |
|         The link above describes LoRaWAN's band plan, stating a power limit of 16 dBm. This is their own suggested specification,
 | |
|         we do not need to follow it. The European Union regulations clearly state that the power limit for this frequency range is
 | |
|        500 mW, or 27 dBm. It also states that we can use interference avoidance and spectrum access techniques to avoid a duty
 | |
|        cycle. (Please refer to section 4.21 in the following document)
 | |
|         https://ec.europa.eu/growth/tools-databases/tris/index.cfm/ro/search/?trisaction=search.detail&year=2021&num=528&dLang=EN
 | |
|      */
 | |
|     RDEF(EU_868, 869.4f, 869.65f, 10, 0, 27, false, false, false),
 | |
| 
 | |
|     /*
 | |
|         https://lora-alliance.org/wp-content/uploads/2020/11/lorawan_regional_parameters_v1.0.3reva_0.pdf
 | |
|      */
 | |
|     RDEF(CN, 470.0f, 510.0f, 100, 0, 19, true, false, false),
 | |
| 
 | |
|     /*
 | |
|         https://lora-alliance.org/wp-content/uploads/2020/11/lorawan_regional_parameters_v1.0.3reva_0.pdf
 | |
|      */
 | |
|     RDEF(JP, 920.8f, 927.8f, 100, 0, 16, true, false, false),
 | |
| 
 | |
|     /*
 | |
|         https://www.iot.org.au/wp/wp-content/uploads/2016/12/IoTSpectrumFactSheet.pdf
 | |
|         https://iotalliance.org.nz/wp-content/uploads/sites/4/2019/05/IoT-Spectrum-in-NZ-Briefing-Paper.pdf
 | |
|      */
 | |
|     RDEF(ANZ, 915.0f, 928.0f, 100, 0, 30, true, false, false),
 | |
| 
 | |
|     /*
 | |
|         https://digital.gov.ru/uploaded/files/prilozhenie-12-k-reshenyu-gkrch-18-46-03-1.pdf
 | |
| 
 | |
|         Note:
 | |
|             - We do LBT, so 100% is allowed.
 | |
|      */
 | |
|     RDEF(RU, 868.7f, 869.2f, 100, 0, 20, true, false, false),
 | |
| 
 | |
|     /*
 | |
|         ???
 | |
|      */
 | |
|     RDEF(KR, 920.0f, 923.0f, 100, 0, 0, true, false, false),
 | |
| 
 | |
|     /*
 | |
|         ???
 | |
|      */
 | |
|     RDEF(TW, 920.0f, 925.0f, 100, 0, 0, true, false, false),
 | |
| 
 | |
|     /*
 | |
|         https://lora-alliance.org/wp-content/uploads/2020/11/lorawan_regional_parameters_v1.0.3reva_0.pdf
 | |
|      */
 | |
|     RDEF(IN, 865.0f, 867.0f, 100, 0, 30, true, false, false),
 | |
| 
 | |
|     /*
 | |
|          https://rrf.rsm.govt.nz/smart-web/smart/page/-smart/domain/licence/LicenceSummary.wdk?id=219752
 | |
|          https://iotalliance.org.nz/wp-content/uploads/sites/4/2019/05/IoT-Spectrum-in-NZ-Briefing-Paper.pdf
 | |
|       */
 | |
|     RDEF(NZ_865, 864.0f, 868.0f, 100, 0, 36, true, false, false),
 | |
| 
 | |
|     /*
 | |
|        https://lora-alliance.org/wp-content/uploads/2020/11/lorawan_regional_parameters_v1.0.3reva_0.pdf
 | |
|     */
 | |
|     RDEF(TH, 920.0f, 925.0f, 100, 0, 16, true, false, false),
 | |
| 
 | |
|     /*
 | |
|         433,05-434,7 Mhz 10 mW
 | |
|         https://nkrzi.gov.ua/images/upload/256/5810/PDF_UUZ_19_01_2016.pdf
 | |
|     */
 | |
|     RDEF(UA_433, 433.0f, 434.7f, 10, 0, 10, true, false, false),
 | |
| 
 | |
|     /*
 | |
|         868,0-868,6 Mhz 25 mW
 | |
|         https://nkrzi.gov.ua/images/upload/256/5810/PDF_UUZ_19_01_2016.pdf
 | |
|     */
 | |
|     RDEF(UA_868, 868.0f, 868.6f, 1, 0, 14, true, false, false),
 | |
| 
 | |
|     /*
 | |
|         Malaysia
 | |
|         433 - 435 MHz at 100mW, no restrictions.
 | |
|         https://www.mcmc.gov.my/skmmgovmy/media/General/pdf/Short-Range-Devices-Specification.pdf
 | |
|     */
 | |
|     RDEF(MY_433, 433.0f, 435.0f, 100, 0, 20, true, false, false),
 | |
| 
 | |
|     /*
 | |
|         Malaysia
 | |
|         919 - 923 Mhz at 500mW, no restrictions.
 | |
|         923 - 924 MHz at 500mW with 1% duty cycle OR frequency hopping.
 | |
|         Frequency hopping is used for 919 - 923 MHz.
 | |
|         https://www.mcmc.gov.my/skmmgovmy/media/General/pdf/Short-Range-Devices-Specification.pdf
 | |
|     */
 | |
|     RDEF(MY_919, 919.0f, 924.0f, 100, 0, 27, true, true, false),
 | |
| 
 | |
|     /*
 | |
|        2.4 GHZ WLAN Band equivalent. Only for SX128x chips.
 | |
|     */
 | |
|     RDEF(LORA_24, 2400.0f, 2483.5f, 100, 0, 10, true, false, true),
 | |
| 
 | |
|     /*
 | |
|         This needs to be last. Same as US.
 | |
|     */
 | |
|     RDEF(UNSET, 902.0f, 928.0f, 100, 0, 30, true, false, false)
 | |
| 
 | |
| };
 | |
| 
 | |
| void initRegion()
 | |
| {
 | |
|     MSG("[INF]Init region List ...");
 | |
|     const RegionInfo *r = regions;
 | |
|     for (; r->code != meshtastic_Config_LoRaConfig_RegionCode_UNSET && r->code != CC_MY_REGION; r++) ;
 | |
|     myRegion = r;
 | |
|     MSG(" done!\n");
 | |
| }
 | |
| 
 | |
| /** hash a string into an integer
 | |
|  *
 | |
|  * djb2 by Dan Bernstein.
 | |
|  * http://www.cse.yorku.ca/~oz/hash.html
 | |
|  */
 | |
| uint32_t hash(const char *str)
 | |
| {
 | |
|     uint32_t hash = 5381;
 | |
|     int c;
 | |
| 
 | |
|     while ((c = *str++) != 0)
 | |
|         hash = ((hash << 5) + hash) + (unsigned char)c; 
 | |
| 
 | |
|     return hash;
 | |
| }
 | |
| 
 | |
| /** A channel number (index into the channel table)
 | |
|  */
 | |
| typedef uint8_t ChannelIndex;
 | |
| 
 | |
| /** A low quality hash of the channel PSK and the channel name.  created by generateHash(chIndex)
 | |
|  * Used as a hint to limit which PSKs are considered for packet decoding.
 | |
|  */
 | |
| typedef uint8_t ChannelHash;
 | |
| 
 | |
| uint8_t xorHash(const uint8_t *p, size_t len)
 | |
| {
 | |
|     uint8_t code = 0;
 | |
|     for (size_t i = 0; i < len; i++)
 | |
|         code ^= p[i];
 | |
|     return code;
 | |
| }
 | |
| 
 | |
| /** Given a channel number, return the (0 to 255) hash for that channel.
 | |
|  * The hash is just an xor of the channel name followed by the channel PSK being used for encryption
 | |
|  * If no suitable channel could be found, return -1
 | |
|  */
 | |
| 
 | |
| int16_t generateHash(const char *name)
 | |
| {
 | |
|    /* auto k = getKey(channelNum);
 | |
|     if (k.length < 0)
 | |
|         return -1; // invalid
 | |
|     else {*/
 | |
|         //const char *name = getName(channelNum);
 | |
|         uint8_t h = xorHash((const uint8_t *)name, strlen(name));
 | |
|         h ^= xorHash(psk.bytes, psk.length);
 | |
|         return h;
 | |
|     //}
 | |
| }
 | |
| 
 | |
| uint32_t getPacketTime(uint32_t pl)
 | |
| {
 | |
|     float bandwidthHz = bw * 1000.0f;
 | |
|     bool  headDisable = false; // we currently always use the header
 | |
|     float tSym = (1 << sf) / bandwidthHz;
 | |
| 
 | |
|     bool lowDataOptEn = tSym > 16e-3 ? true : false; // Needed if symbol time is >16ms
 | |
| 
 | |
|     float tPreamble = (preambleLength + 4.25f) * tSym;
 | |
|     float numPayloadSym =
 | |
|         8 + max(ceilf(((8.0f * pl - 4 * sf + 28 + 16 - 20 * headDisable) / (4 * (sf - 2 * lowDataOptEn))) * cr), 0.0f);
 | |
|     float tPayload = numPayloadSym * tSym;
 | |
|     float tPacket = tPreamble + tPayload;
 | |
|   
 | |
|     return (uint32_t)(tPacket * 1000);
 | |
| }
 | |
| 
 | |
| void applyModemConfig()
 | |
| {
 | |
|     if (CC_LORA_USE_PRESET) {
 | |
| 
 | |
|         switch (CC_MY_LORA_PRESET) {
 | |
|         case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST:
 | |
|             bw = (myRegion->wideLora) ? 812.5 : 250;
 | |
|             cr = 5;
 | |
|             sf = 7;
 | |
|             break;
 | |
|         case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW:
 | |
|             bw = (myRegion->wideLora) ? 812.5 : 250;
 | |
|             cr = 5;
 | |
|             sf = 8;
 | |
|             break;
 | |
|         case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_FAST:
 | |
|             bw = (myRegion->wideLora) ? 812.5 : 250;
 | |
|             cr = 5;
 | |
|             sf = 9;
 | |
|             break;
 | |
|         case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW:
 | |
|             bw = (myRegion->wideLora) ? 812.5 : 250;
 | |
|             cr = 5;
 | |
|             sf = 10;
 | |
|             break;
 | |
|         default: // Config_LoRaConfig_ModemPreset_LONG_FAST is default.
 | |
|             bw = (myRegion->wideLora) ? 812.5 : 250;
 | |
|             cr = 5;
 | |
|             sf = 11;
 | |
|             break;
 | |
|         case meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE:
 | |
|             bw = (myRegion->wideLora) ? 406.25 : 125;
 | |
|             cr = 8;
 | |
|             sf = 11;
 | |
|             break;
 | |
|         case meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW:
 | |
|             bw = (myRegion->wideLora) ? 406.25 : 125;
 | |
|             cr = 8;
 | |
|             sf = 12;
 | |
|             break;
 | |
|         case meshtastic_Config_LoRaConfig_ModemPreset_VERY_LONG_SLOW:
 | |
|             bw = (myRegion->wideLora) ? 203.125 : 62.5;
 | |
|             cr = 8;
 | |
|             sf = 12;
 | |
|             break;
 | |
|         }
 | |
|     } else {
 | |
|         // Custom Settings:
 | |
|         sf = CC_MY_LORA_SF;
 | |
|         cr = CC_MY_LORA_CR;
 | |
|         bw = CC_MY_LORA_BW;
 | |
|         power = CC_MY_LORA_POWER;
 | |
|         // sanity check:
 | |
|         if (bw == 31) // This parameter is not an integer
 | |
|             bw = 31.25;
 | |
|         if (bw == 62) // Fix for 62.5Khz bandwidth
 | |
|             bw = 62.5;
 | |
|         if (bw == 200)
 | |
|             bw = 203.125;
 | |
|         if (bw == 400)
 | |
|             bw = 406.25;
 | |
|         if (bw == 800)
 | |
|             bw = 812.5;
 | |
|         if (bw == 1600)
 | |
|             bw = 1625.0;
 | |
|     }
 | |
| 
 | |
|     if (power == 0) 
 | |
|         power = (int8_t) myRegion->powerLimit;
 | |
| 
 | |
|     if (power == 0)
 | |
|         power = 17; // Default to this power level if we don't have a valid regional power limit (powerLimit of myRegion defaults
 | |
|                     // to 0, currently no region has an actual power limit of 0 [dBm] so we can assume regions which have this
 | |
|                     // variable set to 0 don't have a valid power limit)
 | |
|     
 | |
|     power = (power > CC_MAX_POWER)? CC_MAX_POWER : power;
 | |
| 
 | |
|     // Calculate the number of channels
 | |
|     uint32_t numChannels = floor((myRegion->freqEnd - myRegion->freqStart) / (myRegion->spacing + (bw / 1000)));
 | |
| 
 | |
|     const char *channelName = CC_CHANNEL_NAME; 
 | |
|     int channel_num;
 | |
| 
 | |
|     if (CC_CHANNEL_SLOT == 0) {
 | |
|         channel_num = hash(channelName) % numChannels;
 | |
|     } else {
 | |
|         channel_num = (CC_CHANNEL_SLOT-1) % numChannels;
 | |
|     }
 | |
| 
 | |
|     MSG("[INF]Channel name is \"%s\"\n\r", channelName);
 | |
|     MSG("[INF]Channel slot is %i (%i available frequency slots).\n\r", channel_num + 1, numChannels);
 | |
|     freq = myRegion->freqStart + (bw / 2000) + (channel_num * (bw / 1000));
 | |
| 
 | |
|     // override if we have a verbatim frequency
 | |
|     if (CC_MY_LORA_FREQ > 0.0) {
 | |
|         freq = CC_MY_LORA_FREQ;
 | |
|         channel_num = -1;
 | |
|     }
 | |
|     
 | |
|     MSG("[INF]Using Region %s : freq=%d bw=%i sf=%i cr=%i power=%i ... ",myRegion->name, lround(freq*1E6), lround(bw*1000), sf, cr, power);
 | |
|     
 | |
|     // Syncword is 0x2b, see RadioLibInterface.h
 | |
|     // preamble length is 16, see RadioInterface.h
 | |
|     
 | |
|     err = radio.begin(freq, bw, sf, cr, 0x2b, power, 16); 
 | |
| 
 | |
|     if (err == RADIOLIB_ERR_NONE) {
 | |
|         MSG("success!\n");
 | |
|     } else {
 | |
|         MSG("\n[ERROR] [SX1262} Init failed, code: %i\n\n\r ** Full Stop **", err);
 | |
|         while (true);
 | |
|     }
 | |
| 
 | |
|     // used to calculate wait time before repeating a packet
 | |
|     slotTimeMsec = 8.5 * pow(2, sf) / bw + 0.2 + 0.4 + 7;
 | |
|     preambleTimeMsec = getPacketTime((uint32_t)0);
 | |
|     maxPacketTimeMsec = getPacketTime(meshtastic_Constants_DATA_PAYLOAD_LEN + sizeof(PacketHeader));
 | |
|     MSG("[INF]SlotTime=%ims PreambleTime=%ims maxPacketTime=%ims\n\r", slotTimeMsec, preambleTimeMsec, maxPacketTimeMsec);
 | |
| }
 | |
| 
 | |
| /** The delay to use when we want to flood a message */
 | |
| uint32_t getTxDelayMsecWeighted(float snr) // RadioInterface.cpp
 | |
| {
 | |
|     // The minimum value for a LoRa SNR
 | |
|     const uint32_t SNR_MIN = -20;
 | |
| 
 | |
|     // The maximum value for a LoRa SNR
 | |
|     const uint32_t SNR_MAX = 15;
 | |
| 
 | |
|     //  high SNR = large CW size (Long Delay)
 | |
|     //  low SNR = small CW size (Short Delay)
 | |
|     uint32_t delay = 0;
 | |
|     uint8_t CWsize = map(snr, SNR_MIN, SNR_MAX, CWmin, CWmax);
 | |
| 
 | |
|     // our role is meshtastic_Config_DeviceConfig_Role_REPEATER
 | |
|     delay = random(0, 2 * CWsize) * slotTimeMsec;
 | |
|     return delay;
 | |
| }
 | |
| 
 | |
| bool isActivelyReceiving()
 | |
| {
 | |
|     // The IRQ status will be cleared when we start our read operation. Check if we've started a header, but haven't yet
 | |
|     // received and handled the interrupt for reading the packet/handling errors.
 | |
| 
 | |
|     uint16_t irq = radio.getIrqStatus();
 | |
|     bool detected = (irq & (RADIOLIB_SX126X_IRQ_HEADER_VALID | RADIOLIB_SX126X_IRQ_PREAMBLE_DETECTED));
 | |
|     // Handle false detections
 | |
|     if (detected) {
 | |
|         uint32_t now = millis();
 | |
|         if (!activeReceiveStart) {
 | |
|             activeReceiveStart = now;
 | |
|         } else if ((now - activeReceiveStart > 2 * preambleTimeMsec) && !(irq & RADIOLIB_SX126X_IRQ_HEADER_VALID)) {
 | |
|             // The HEADER_VALID flag should be set by now if it was really a packet, so ignore PREAMBLE_DETECTED flag
 | |
|             activeReceiveStart = 0;
 | |
|             MSG("Ignore false preamble detection.\n\r");
 | |
|             return false;
 | |
|         } else if (now - activeReceiveStart > maxPacketTimeMsec) {
 | |
|             // We should have gotten an RX_DONE IRQ by now if it was really a packet, so ignore HEADER_VALID flag
 | |
|             activeReceiveStart = 0;
 | |
|             MSG("Ignore false header detection.\n\r");
 | |
|             return false;
 | |
|         }
 | |
|     }
 | |
|     return detected;
 | |
| }
 |