mirror of
https://github.com/Genaker/LoraSA.git
synced 2026-06-30 06:51:54 +02:00
Merge pull request #101 from Genaker/testable
Add support for communication over LoRa
This commit is contained in:
+2
-1
@@ -50,6 +50,7 @@ def parse_line(line):
|
||||
|
||||
POLY = 0x1021
|
||||
def crc16(s, c):
|
||||
c = c ^ 0xffff
|
||||
for ch in s:
|
||||
c = c ^ (ord(ch) << 8)
|
||||
for i in range(8):
|
||||
@@ -58,7 +59,7 @@ def crc16(s, c):
|
||||
else:
|
||||
c = (c << 1) & 0xffff
|
||||
|
||||
return c
|
||||
return c ^ 0xffff
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(formatter_class=RawTextHelpFormatter, description='''\
|
||||
|
||||
+170
-27
@@ -1,4 +1,3 @@
|
||||
#ifdef SERIAL_OUT
|
||||
#include "comms.h"
|
||||
#include <config.h>
|
||||
|
||||
@@ -7,7 +6,22 @@
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
|
||||
Comms *Comms0;
|
||||
Comms *HostComms;
|
||||
Comms *Comms0 = NULL;
|
||||
Comms *Comms1 = NULL;
|
||||
|
||||
RadioComms *RxComms = NULL;
|
||||
RadioComms *TxComms = NULL;
|
||||
|
||||
void _onReceiveUsb(size_t len)
|
||||
{
|
||||
if (HostComms == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
HostComms->_onReceive();
|
||||
}
|
||||
|
||||
void _onReceive0()
|
||||
{
|
||||
@@ -19,54 +33,104 @@ void _onReceive0()
|
||||
Comms0->_onReceive();
|
||||
}
|
||||
|
||||
void _onReceive1()
|
||||
{
|
||||
if (Comms1 == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Comms1->_onReceive();
|
||||
}
|
||||
|
||||
#if ARDUINO_USB_MODE
|
||||
#define IF_CDC_EVENT(e, data) \
|
||||
arduino_hw_cdc_event_data_t *data = (arduino_hw_cdc_event_data_t *)event_data; \
|
||||
if (event_base == ARDUINO_HW_CDC_EVENTS && event_id == ARDUINO_HW_CDC_##e)
|
||||
#else
|
||||
#define IF_CDC_EVENT(e, data) \
|
||||
arduino_usb_cdc_event_data_t *data = (arduino_usb_cdc_event_data_t *)event_data; \
|
||||
if (event_base == ARDUINO_USB_CDC_EVENTS && event_id == ARDUINO_USB_CDC_##e)
|
||||
#endif
|
||||
|
||||
void _onUsbEvent0(void *arg, esp_event_base_t event_base, int32_t event_id,
|
||||
void *event_data)
|
||||
{
|
||||
if (event_base == ARDUINO_HW_CDC_EVENTS)
|
||||
{
|
||||
// arduino_hw_cdc_event_data_t *data = (arduino_hw_cdc_event_data_t *)event_data;
|
||||
if (event_id == ARDUINO_HW_CDC_RX_EVENT)
|
||||
{
|
||||
_onReceive0(/*data->rx.len*/);
|
||||
}
|
||||
}
|
||||
IF_CDC_EVENT(RX_EVENT, data) { _onReceiveUsb(data->rx.len); }
|
||||
}
|
||||
|
||||
bool Comms::initComms(Config &c)
|
||||
{
|
||||
bool fine = false;
|
||||
|
||||
#ifdef ARDUINO_USB_CDC_ON_BOOT
|
||||
if (c.listen_on_usb.equalsIgnoreCase("readline"))
|
||||
{
|
||||
// comms using readline plaintext protocol
|
||||
Comms0 = new ReadlineComms(Serial);
|
||||
HostComms = new ReadlineComms("Host", Serial);
|
||||
#if ARDUINO_USB_MODE
|
||||
// if Serial is HWCDC...
|
||||
Serial.onEvent(ARDUINO_HW_CDC_RX_EVENT, _onUsbEvent0);
|
||||
#else
|
||||
// if Serial is USBCDC...
|
||||
Serial.onEvent(ARDUINO_USB_CDC_RX_EVENT, _onUsbEvent0);
|
||||
#endif
|
||||
Serial.begin();
|
||||
|
||||
Serial.println("Initialized communications on Serial using readline protocol");
|
||||
|
||||
return true;
|
||||
fine = true;
|
||||
}
|
||||
else if (c.listen_on_serial0.equalsIgnoreCase("readline"))
|
||||
#endif
|
||||
|
||||
if (c.listen_on_serial0.equalsIgnoreCase("readline"))
|
||||
{
|
||||
// comms using readline plaintext protocol
|
||||
Comms0 = new ReadlineComms(Serial0);
|
||||
Serial0.onReceive(_onReceive0, false);
|
||||
Comms0 = new ReadlineComms("UART0", SERIAL0);
|
||||
SERIAL0.onReceive(_onReceive0, false);
|
||||
SERIAL0.begin(115200);
|
||||
|
||||
Serial.println("Initialized communications on Serial0 using readline protocol");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (c.listen_on_serial0.equalsIgnoreCase("none"))
|
||||
else
|
||||
{
|
||||
Comms0 = new NoopComms();
|
||||
|
||||
Serial.println("Configured none - Initialized no communications");
|
||||
return false;
|
||||
Serial.println("Configured none - Initialized no communications on Serial0");
|
||||
}
|
||||
|
||||
Comms0 = new NoopComms();
|
||||
Serial.println("Nothing is configured - initialized no communications");
|
||||
return false;
|
||||
if (c.listen_on_serial1.equalsIgnoreCase("readline"))
|
||||
{
|
||||
// comms using readline plaintext protocol
|
||||
Comms1 = new ReadlineComms("UART1", Serial1);
|
||||
Serial1.onReceive(_onReceive1, false);
|
||||
Serial1.begin(115200);
|
||||
|
||||
Serial.println("Initialized communications on Serial1 using readline protocol");
|
||||
}
|
||||
else
|
||||
{
|
||||
Comms1 = new NoopComms();
|
||||
|
||||
Serial.println("Configured none - Initialized no communications on Serial1");
|
||||
}
|
||||
|
||||
if (c.rx_lora != NULL)
|
||||
{
|
||||
RxComms = new RadioComms("RxComms", radio, *c.rx_lora);
|
||||
}
|
||||
|
||||
if (c.tx_lora != NULL)
|
||||
{
|
||||
TxComms = new RadioComms("TxComms", radio, *c.tx_lora);
|
||||
}
|
||||
|
||||
if (!fine)
|
||||
{
|
||||
HostComms = new NoopComms();
|
||||
Serial.println("Nothing is configured - initialized no communications");
|
||||
}
|
||||
return fine;
|
||||
}
|
||||
|
||||
size_t Comms::available() { return received_pos; }
|
||||
@@ -123,6 +187,7 @@ String _wrap_str(String);
|
||||
#define POLY 0x1021
|
||||
uint16_t crc16(String v, uint16_t c)
|
||||
{
|
||||
c ^= 0xffff;
|
||||
for (int i = 0; i < v.length(); i++)
|
||||
{
|
||||
uint16_t ch = v.charAt(i);
|
||||
@@ -140,7 +205,7 @@ uint16_t crc16(String v, uint16_t c)
|
||||
}
|
||||
}
|
||||
|
||||
return c;
|
||||
return c ^ 0xffff;
|
||||
}
|
||||
|
||||
void ReadlineComms::_onReceive()
|
||||
@@ -176,6 +241,10 @@ void ReadlineComms::_onReceive()
|
||||
delete m;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println(name + ": discarding > " + pack);
|
||||
}
|
||||
partialPacket = partialPacket.substring(i + 1);
|
||||
i = partialPacket.indexOf('\n');
|
||||
}
|
||||
@@ -190,16 +259,44 @@ bool ReadlineComms::send(Message &m)
|
||||
{
|
||||
case MessageType::SCAN:
|
||||
p = _scan_str(m.payload.scan);
|
||||
Serial.println(name + ": the message is: " + p);
|
||||
break;
|
||||
case MessageType::SCAN_RESULT:
|
||||
p = _scan_result_str(m.payload.dump);
|
||||
break;
|
||||
case MessageType::CONFIG_TASK:
|
||||
p = m.payload.config.is_set ? "SET " : "GET ";
|
||||
p += *m.payload.config.key;
|
||||
if (m.payload.config.is_set)
|
||||
{
|
||||
p += " " + *m.payload.config.value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
serial.print(_wrap_str(p));
|
||||
return true;
|
||||
}
|
||||
|
||||
String _stringParam(String &p, String default_v)
|
||||
{
|
||||
p.trim();
|
||||
int i = p.indexOf(' ');
|
||||
if (i < 0)
|
||||
{
|
||||
i = p.length();
|
||||
}
|
||||
|
||||
String v = p.substring(0, i);
|
||||
p = p.substring(i + 1);
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
v = default_v;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
int64_t _intParam(String &p, int64_t default_v)
|
||||
{
|
||||
p.trim();
|
||||
@@ -267,7 +364,27 @@ Message *_parsePacket(String p)
|
||||
return m;
|
||||
}
|
||||
|
||||
Serial.println("ignoring unknown message " + p);
|
||||
if (cmd.equalsIgnoreCase("get"))
|
||||
{
|
||||
Message *m = new Message();
|
||||
m->type = MessageType::CONFIG_TASK;
|
||||
m->payload.config.is_set = false;
|
||||
m->payload.config.key = new String(_stringParam(p, ""));
|
||||
m->payload.config.value = NULL;
|
||||
return m;
|
||||
}
|
||||
|
||||
if (cmd.equalsIgnoreCase("set"))
|
||||
{
|
||||
Message *m = new Message();
|
||||
m->type = MessageType::CONFIG_TASK;
|
||||
m->payload.config.is_set = true;
|
||||
m->payload.config.key = new String(_stringParam(p, ""));
|
||||
m->payload.config.value = new String(_stringParam(p, ""));
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -294,4 +411,30 @@ String _wrap_str(String v)
|
||||
String r = String(v.length()) + "\n" + v;
|
||||
return "WRAP " + String(crc16(r, 0), 16) + " " + r;
|
||||
}
|
||||
#endif
|
||||
|
||||
Message::~Message()
|
||||
{
|
||||
if (type == SCAN_RESULT)
|
||||
{
|
||||
if (payload.dump.sz > 0)
|
||||
{
|
||||
delete[] payload.dump.freqs_khz;
|
||||
delete[] payload.dump.rssis;
|
||||
payload.dump.sz = 0;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == CONFIG_TASK)
|
||||
{
|
||||
delete payload.config.key;
|
||||
|
||||
if (payload.config.is_set)
|
||||
{
|
||||
delete payload.config.value;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
+53
-7
@@ -1,16 +1,25 @@
|
||||
#ifndef __COMMS_H
|
||||
#define __COMMS_H
|
||||
|
||||
#ifdef SERIAL_OUT
|
||||
#include <HardwareSerial.h>
|
||||
#include <LoRaBoards.h>
|
||||
|
||||
#include <LiLyGo.h>
|
||||
#include <config.h>
|
||||
|
||||
#ifndef ARDUINO_USB_CDC_ON_BOOT
|
||||
#define SERIAL0 Serial
|
||||
#else
|
||||
#define SERIAL0 Serial0
|
||||
#endif
|
||||
|
||||
enum MessageType
|
||||
{
|
||||
WRAP = 0,
|
||||
SCAN,
|
||||
SCAN_RESULT,
|
||||
_MAX_MESSAGE_TYPE = SCAN_RESULT
|
||||
CONFIG_TASK,
|
||||
_MAX_MESSAGE_TYPE = CONFIG_TASK
|
||||
};
|
||||
|
||||
struct Wrapper
|
||||
@@ -32,19 +41,30 @@ struct ScanTaskResult
|
||||
int16_t *rssis;
|
||||
};
|
||||
|
||||
struct ConfigTask
|
||||
{
|
||||
String *key;
|
||||
String *value;
|
||||
bool is_set;
|
||||
};
|
||||
|
||||
struct Message
|
||||
{
|
||||
MessageType type;
|
||||
union
|
||||
{
|
||||
Wrapper wrap;
|
||||
ConfigTask config;
|
||||
ScanTask scan;
|
||||
ScanTaskResult dump;
|
||||
} payload;
|
||||
|
||||
~Message();
|
||||
};
|
||||
|
||||
struct Comms
|
||||
{
|
||||
String name;
|
||||
Stream &serial;
|
||||
Message **received;
|
||||
size_t received_sz;
|
||||
@@ -52,8 +72,9 @@ struct Comms
|
||||
|
||||
Message *wrap;
|
||||
|
||||
Comms(Stream &serial)
|
||||
: serial(serial), received(NULL), received_sz(0), received_pos(0), wrap(NULL) {};
|
||||
Comms(String name, Stream &serial)
|
||||
: name(name), serial(serial), received(NULL), received_sz(0), received_pos(0),
|
||||
wrap(NULL) {};
|
||||
|
||||
virtual size_t available();
|
||||
virtual bool send(Message &) = 0;
|
||||
@@ -67,7 +88,7 @@ struct Comms
|
||||
|
||||
struct NoopComms : Comms
|
||||
{
|
||||
NoopComms() : Comms(Serial0) {};
|
||||
NoopComms() : Comms("no-op", SERIAL0) {};
|
||||
|
||||
virtual bool send(Message &) { return true; };
|
||||
virtual void _onReceive() {};
|
||||
@@ -77,14 +98,39 @@ struct ReadlineComms : Comms
|
||||
{
|
||||
String partialPacket;
|
||||
|
||||
ReadlineComms(Stream &serial) : Comms(serial), partialPacket("") {};
|
||||
ReadlineComms(String name, Stream &serial)
|
||||
: Comms(name, serial), partialPacket("") {};
|
||||
|
||||
virtual bool send(Message &) override;
|
||||
|
||||
virtual void _onReceive() override;
|
||||
};
|
||||
|
||||
extern Comms *HostComms;
|
||||
|
||||
extern Comms *Comms0;
|
||||
|
||||
#endif
|
||||
extern Comms *Comms1;
|
||||
|
||||
struct RadioComms
|
||||
{
|
||||
String name;
|
||||
RADIO_TYPE &radio;
|
||||
LoRaConfig &loraCfg;
|
||||
|
||||
RadioComms(String name, RADIO_TYPE &radio, LoRaConfig &cfg)
|
||||
: name(name), radio(radio), loraCfg(cfg)
|
||||
{
|
||||
}
|
||||
|
||||
Message **received;
|
||||
|
||||
int16_t configureRadio();
|
||||
int16_t send(Message &);
|
||||
Message *receive(uint16_t timeout_ms);
|
||||
};
|
||||
|
||||
extern RadioComms *RxComms;
|
||||
extern RadioComms *TxComms;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
#include "comms.h"
|
||||
|
||||
int16_t RadioComms::configureRadio()
|
||||
{
|
||||
int16_t status =
|
||||
radio.begin(loraCfg.freq, loraCfg.bw, loraCfg.sf, loraCfg.cr, loraCfg.sync_word,
|
||||
loraCfg.tx_power, loraCfg.preamble_len);
|
||||
if (status != RADIOLIB_ERR_NONE)
|
||||
{
|
||||
Serial.printf("could not configure Lora: %d\n", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
if (!loraCfg.crc)
|
||||
{
|
||||
status = radio.setCRC(0);
|
||||
if (status != RADIOLIB_ERR_NONE)
|
||||
{
|
||||
Serial.printf("could not configure CRC for Lora: %d\n", status);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
size_t _write(uint8_t *m, size_t sz, size_t p, uint8_t v)
|
||||
{
|
||||
if (p >= sz)
|
||||
return sz;
|
||||
m[p] = v;
|
||||
return p + 1;
|
||||
}
|
||||
|
||||
size_t _write(uint8_t *m, size_t sz, size_t p, uint8_t *v, size_t v_sz)
|
||||
{
|
||||
while (v_sz > 0)
|
||||
{
|
||||
p = _write(m, sz, p, *v);
|
||||
v++;
|
||||
v_sz--;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
#define MAX_MSG 128
|
||||
#define RSSI_HI -80
|
||||
#define DETAIL_RSSIS 8
|
||||
#define RSSI_LO (RSSI_HI - 1 - DETAIL_RSSIS)
|
||||
int16_t RadioComms::send(Message &m)
|
||||
{
|
||||
if (m.type != SCAN_RESULT)
|
||||
{
|
||||
return RADIOLIB_ERR_INVALID_FUNCTION;
|
||||
}
|
||||
|
||||
uint8_t msg[MAX_MSG];
|
||||
size_t dump_sz = m.payload.dump.sz;
|
||||
size_t p = _write(msg, MAX_MSG, 0, (uint8_t)m.type);
|
||||
|
||||
// first cut: dump the RSSI as-is
|
||||
// optimize the message size later
|
||||
p = _write(msg, MAX_MSG, p, (uint8_t *)&m.payload.dump.freqs_khz[0], 4);
|
||||
p = _write(msg, MAX_MSG, p, (uint8_t *)&m.payload.dump.freqs_khz[dump_sz - 1], 4);
|
||||
p = _write(msg, MAX_MSG, p, (uint8_t *)&dump_sz, 2);
|
||||
|
||||
size_t rem = MAX_MSG - p;
|
||||
if (rem > dump_sz)
|
||||
rem = dump_sz;
|
||||
|
||||
uint8_t bits = 0;
|
||||
size_t pp = p;
|
||||
for (int i = 0; i < dump_sz; i++)
|
||||
{
|
||||
if (i * rem / dump_sz > p - pp)
|
||||
{
|
||||
p = _write(msg, MAX_MSG, p, bits);
|
||||
bits = 0;
|
||||
}
|
||||
int16_t v = m.payload.dump.rssis[i];
|
||||
if (v >= 0)
|
||||
{
|
||||
v = 255;
|
||||
}
|
||||
else
|
||||
{
|
||||
v += 255;
|
||||
if (v < 0)
|
||||
{
|
||||
v = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bits = max(bits, (uint8_t)v);
|
||||
}
|
||||
|
||||
if (dump_sz > 0)
|
||||
{
|
||||
p = _write(msg, MAX_MSG, p, bits);
|
||||
}
|
||||
|
||||
return radio.transmit(msg, p);
|
||||
}
|
||||
|
||||
size_t _read(uint8_t *buf, size_t sz, size_t p, uint8_t *v, size_t len)
|
||||
{
|
||||
for (; p < sz && len > 0; v++, p++, len--)
|
||||
{
|
||||
*v = buf[p];
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
size_t _read(uint8_t *buf, size_t sz, size_t p, uint8_t *v)
|
||||
{
|
||||
return _read(buf, sz, p, v, 1);
|
||||
}
|
||||
|
||||
volatile bool _received = false;
|
||||
void _rcv() { _received = true; }
|
||||
|
||||
Message *RadioComms::receive(uint16_t timeout_ms)
|
||||
{
|
||||
uint8_t msg[MAX_MSG];
|
||||
|
||||
#ifdef USING_LR1121
|
||||
Message *message = NULL;
|
||||
#warning Radio Comms not fully supported for LR1121
|
||||
#else
|
||||
// because of this, receive is single-threaded, single-device
|
||||
_received = false;
|
||||
radio.setDio1Action(_rcv);
|
||||
uint32_t timeout_ticks = (uint32_t)timeout_ms * (1000000 / 15625);
|
||||
|
||||
int16_t status = radio.startReceive(timeout_ticks);
|
||||
if (status != RADIOLIB_ERR_NONE)
|
||||
{
|
||||
radio.clearDio1Action();
|
||||
Serial.printf("Failed to start receive: %d\n", status);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// wait on a semaphore
|
||||
while (!_received)
|
||||
{
|
||||
yield();
|
||||
}
|
||||
radio.clearDio1Action();
|
||||
|
||||
size_t len = radio.getPacketLength(true);
|
||||
uint8_t *packet = msg;
|
||||
|
||||
if (len > MAX_MSG)
|
||||
{
|
||||
packet = new uint8_t[len];
|
||||
}
|
||||
status = radio.readData(packet, len);
|
||||
|
||||
if (status == RADIOLIB_ERR_RX_TIMEOUT)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (status != RADIOLIB_ERR_NONE || len == 0)
|
||||
{
|
||||
if (packet != msg)
|
||||
delete[] packet;
|
||||
|
||||
Serial.printf("Failed to read data: E(%d), len == %d\n", status, len);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (len > MAX_MSG)
|
||||
{
|
||||
Serial.printf("Packet is longer than expected: %d\n", len);
|
||||
}
|
||||
|
||||
size_t p;
|
||||
uint8_t b;
|
||||
p = _read(packet, len, 0, &b);
|
||||
|
||||
Message *message = NULL;
|
||||
if (b == SCAN_RESULT)
|
||||
{
|
||||
message = new Message();
|
||||
message->type = SCAN_RESULT;
|
||||
|
||||
uint32_t s, e;
|
||||
size_t dump_sz = 0;
|
||||
p = _read(packet, len, p, (uint8_t *)&s, 4);
|
||||
p = _read(packet, len, p, (uint8_t *)&e, 4);
|
||||
p = _read(packet, len, p, (uint8_t *)&dump_sz, 2);
|
||||
size_t rem = len - p;
|
||||
|
||||
message->payload.dump.sz = dump_sz;
|
||||
if (dump_sz > 0)
|
||||
{
|
||||
message->payload.dump.rssis = new int16_t[dump_sz];
|
||||
message->payload.dump.freqs_khz = new uint32_t[dump_sz];
|
||||
message->payload.dump.freqs_khz[0] = s;
|
||||
message->payload.dump.freqs_khz[dump_sz - 1] = e;
|
||||
|
||||
for (int i = 1; i < dump_sz - 1; i++)
|
||||
{
|
||||
uint32_t incr = (e - s) / (dump_sz - i);
|
||||
s += incr;
|
||||
message->payload.dump.freqs_khz[i] = s;
|
||||
}
|
||||
|
||||
for (int i = 0, k = 0; i < rem; i++)
|
||||
{
|
||||
int j = (i + 1) * dump_sz / rem;
|
||||
int16_t rssi = 0;
|
||||
p = _read(packet, len, p, (uint8_t *)&rssi);
|
||||
rssi -= 255;
|
||||
for (; k < j; k++)
|
||||
{
|
||||
message->payload.dump.rssis[k] = rssi;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.printf("Received message type %" PRIu8 ", length %d, but expecting %" PRIu8
|
||||
" - ignoring\n",
|
||||
b, len, (uint8_t)MessageType::SCAN_RESULT);
|
||||
}
|
||||
|
||||
if (packet != msg)
|
||||
{
|
||||
delete[] packet;
|
||||
}
|
||||
#endif
|
||||
return message;
|
||||
}
|
||||
+451
-27
@@ -83,38 +83,20 @@ Config Config::init()
|
||||
}
|
||||
|
||||
// do something with known keys and values
|
||||
|
||||
if (r.key.equalsIgnoreCase("print_profile_time"))
|
||||
if (c.updateConfig(r.key, r.value))
|
||||
{
|
||||
String v = r.value;
|
||||
bool p = v.equalsIgnoreCase("true");
|
||||
if (!p && !v.equalsIgnoreCase("false"))
|
||||
{
|
||||
Serial.printf("Expected bool for '%s', found '%s' - ignoring\n",
|
||||
r.key.c_str(), r.value.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
c.print_profile_time = p;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (r.key.equalsIgnoreCase("log_data_json_interval"))
|
||||
if (r.key.equalsIgnoreCase("rx_lora"))
|
||||
{
|
||||
c.log_data_json_interval = r.value.toInt();
|
||||
c.rx_lora = configureLora(r.value);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (r.key.equalsIgnoreCase("listen_on_serial0"))
|
||||
if (r.key.equalsIgnoreCase("tx_lora"))
|
||||
{
|
||||
c.listen_on_serial0 = r.value;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (r.key.equalsIgnoreCase("listen_on_usb"))
|
||||
{
|
||||
c.listen_on_serial0 = r.value;
|
||||
c.tx_lora = configureLora(r.value);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -125,6 +107,391 @@ Config Config::init()
|
||||
return c;
|
||||
}
|
||||
|
||||
bool Config::updateConfig(String key, String value)
|
||||
{
|
||||
if (key.equalsIgnoreCase("print_profile_time"))
|
||||
{
|
||||
String v = value;
|
||||
bool p = v.equalsIgnoreCase("true");
|
||||
if (!p && !v.equalsIgnoreCase("false"))
|
||||
{
|
||||
Serial.printf("Expected bool for '%s', found '%s' - ignoring\n", key.c_str(),
|
||||
value.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
print_profile_time = p;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (key.equalsIgnoreCase("log_data_json_interval"))
|
||||
{
|
||||
log_data_json_interval = value.toInt();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (key.equalsIgnoreCase("listen_on_serial0"))
|
||||
{
|
||||
listen_on_serial0 = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (key.equalsIgnoreCase("listen_on_serial1"))
|
||||
{
|
||||
listen_on_serial1 = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (key.equalsIgnoreCase("listen_on_usb"))
|
||||
{
|
||||
listen_on_serial0 = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (key.equalsIgnoreCase("detection_strategy"))
|
||||
{
|
||||
configureDetectionStrategy(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (key.equalsIgnoreCase("rx_lora"))
|
||||
{
|
||||
rx_lora = configureLora(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (key.equalsIgnoreCase("tx_lora"))
|
||||
{
|
||||
tx_lora = configureLora(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (key.equalsIgnoreCase("is_host"))
|
||||
{
|
||||
String v = value;
|
||||
bool p = v.equalsIgnoreCase("true");
|
||||
if (!p && !v.equalsIgnoreCase("false"))
|
||||
{
|
||||
Serial.printf("Expected bool for '%s', found '%s' - ignoring\n", key.c_str(),
|
||||
value.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
is_host = p;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
String loraConfigToStr(LoRaConfig *cfg)
|
||||
{
|
||||
if (cfg == NULL)
|
||||
{
|
||||
return String("none");
|
||||
}
|
||||
|
||||
return String("freq:") + String(cfg->freq) + String(",bw:") + String(cfg->bw) +
|
||||
String(",sf:") + String(cfg->sf) + String(",cr:") + String(cfg->cr) +
|
||||
String(",tx_power:") + String(cfg->tx_power) + String(",preamble_len:") +
|
||||
String(cfg->preamble_len) + String(",sync_word:") +
|
||||
String(cfg->sync_word, 16) + String(",crc:") + String(cfg->crc ? "1" : "0") +
|
||||
String(",implicit_header:") + String(cfg->implicit_header);
|
||||
}
|
||||
|
||||
String detectionStrategyToStr(Config &c)
|
||||
{
|
||||
String res = c.detection_strategy;
|
||||
if (c.scan_ranges_sz > 0)
|
||||
{
|
||||
res += ":";
|
||||
for (int i = 0; i < c.scan_ranges_sz; i++)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
res += ",";
|
||||
}
|
||||
|
||||
res += String(c.scan_ranges[i].start_khz);
|
||||
int s = c.scan_ranges[i].end_khz - c.scan_ranges[i].start_khz;
|
||||
if (s > 0)
|
||||
{
|
||||
res += ".." + String(c.scan_ranges[i].end_khz);
|
||||
if (c.scan_ranges[i].step_khz < s)
|
||||
{
|
||||
res += ":" + String(c.scan_ranges[i].step_khz);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// findSepa looks for a sepa in s from position begin, and does two things:
|
||||
// updates end with the index of the end of the string between begin and
|
||||
// separator or end of string, and returns index of the next search position
|
||||
// or -1 to stop the search.
|
||||
//
|
||||
// So you can do something like this:
|
||||
// for (int i = 0, j = 0, k = 0; (i = findSepa(s, sepa, j, k)) >= 0; j = i)
|
||||
// ...s.substring(j, k)
|
||||
int findSepa(String s, String sepa, int begin, int &end)
|
||||
{
|
||||
int i = s.indexOf(sepa, begin);
|
||||
if (i < 0)
|
||||
{
|
||||
end = s.length();
|
||||
return begin == end ? -1 : end;
|
||||
}
|
||||
|
||||
end = i;
|
||||
return i + sepa.length();
|
||||
}
|
||||
|
||||
uint64_t fromHex(String s)
|
||||
{
|
||||
uint64_t r = 0;
|
||||
for (char c : s)
|
||||
{
|
||||
uint64_t v;
|
||||
if (c >= 'A' && c <= 'F')
|
||||
{
|
||||
v = c - 'A' + 10;
|
||||
}
|
||||
else if (c >= 'a' && c <= 'f')
|
||||
{
|
||||
v = c - 'a' + 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
v = c - '0';
|
||||
}
|
||||
|
||||
r = (r << 4) + v;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
uint64_t toUint64(String s)
|
||||
{
|
||||
String v = s.substring(0, s.length() % 15);
|
||||
s = s.substring(v.length());
|
||||
uint64_t r = v.toInt();
|
||||
|
||||
while (s.length() > 0)
|
||||
{
|
||||
r = r * ((uint64_t)1000000000000000ll) + s.substring(0, 15).toInt();
|
||||
s = s.substring(15);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
ScanRange parseScanRange(String &cfg, int &begin)
|
||||
{
|
||||
ScanRange res;
|
||||
int end;
|
||||
int i = findSepa(cfg, ",", begin, end);
|
||||
|
||||
String r = cfg.substring(begin, end);
|
||||
begin = i;
|
||||
|
||||
if (i < 0)
|
||||
{
|
||||
res.start_khz = -1;
|
||||
res.end_khz = -1;
|
||||
res.step_khz = -1;
|
||||
return res;
|
||||
}
|
||||
|
||||
i = r.indexOf("..");
|
||||
if (i < 0)
|
||||
{
|
||||
i = r.length();
|
||||
}
|
||||
|
||||
res.start_khz = toUint64(r.substring(0, i));
|
||||
if (i == r.length())
|
||||
{
|
||||
res.end_khz = res.start_khz;
|
||||
res.step_khz = 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
r = r.substring(i + 2);
|
||||
i = r.indexOf("+");
|
||||
if (i < 0)
|
||||
{
|
||||
i = r.indexOf("/");
|
||||
if (i < 0)
|
||||
i = r.length();
|
||||
}
|
||||
|
||||
res.end_khz = toUint64(r.substring(0, i));
|
||||
if (i == r.length())
|
||||
{
|
||||
res.step_khz = res.end_khz - res.start_khz;
|
||||
}
|
||||
else
|
||||
{
|
||||
res.step_khz = toUint64(r.substring(i + 1));
|
||||
if (r.charAt(i) == '/')
|
||||
{
|
||||
// then it is not a literal increment, it is the number of steps
|
||||
if (res.step_khz == 0)
|
||||
res.step_khz = 1;
|
||||
res.step_khz = round(((double)(res.end_khz - res.start_khz)) / res.step_khz);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
LoRaConfig *configureLora(String cfg)
|
||||
{
|
||||
if (cfg.equalsIgnoreCase("none"))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LoRaConfig *lora = new LoRaConfig({
|
||||
freq : 0,
|
||||
bw : 500,
|
||||
sf : 7,
|
||||
cr : 5,
|
||||
tx_power : 1,
|
||||
preamble_len : 8,
|
||||
sync_word : 0x1e,
|
||||
crc : false,
|
||||
implicit_header : 0
|
||||
});
|
||||
|
||||
int begin = 0;
|
||||
int end, i;
|
||||
|
||||
while ((i = findSepa(cfg, ",", begin, end)) >= 0)
|
||||
{
|
||||
String param = cfg.substring(begin, end);
|
||||
begin = i;
|
||||
int j = param.indexOf(":");
|
||||
if (j < 0)
|
||||
{
|
||||
Serial.printf("Expected ':' to be present in '%s' - ignoring config\n",
|
||||
param);
|
||||
continue;
|
||||
}
|
||||
|
||||
String k = param.substring(0, j);
|
||||
param = param.substring(j + 1);
|
||||
|
||||
if (k.equalsIgnoreCase("sync_word"))
|
||||
{
|
||||
lora->sync_word = (uint8_t)fromHex(param);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (k.equalsIgnoreCase("freq"))
|
||||
{
|
||||
lora->freq = param.toFloat();
|
||||
continue;
|
||||
}
|
||||
|
||||
int v = param.toInt();
|
||||
|
||||
if (k.equalsIgnoreCase("bw"))
|
||||
{
|
||||
lora->bw = (uint16_t)v;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (k.equalsIgnoreCase("sf"))
|
||||
{
|
||||
lora->sf = (uint8_t)v;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (k.equalsIgnoreCase("cr"))
|
||||
{
|
||||
lora->cr = (uint8_t)v;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (k.equalsIgnoreCase("tx_power"))
|
||||
{
|
||||
lora->tx_power = (uint8_t)v;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (k.equalsIgnoreCase("preamble_len"))
|
||||
{
|
||||
lora->preamble_len = (uint8_t)v;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (k.equalsIgnoreCase("crc"))
|
||||
{
|
||||
lora->crc = v != 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (k.equalsIgnoreCase("implicit_header"))
|
||||
{
|
||||
lora->implicit_header = (uint8_t)v;
|
||||
continue;
|
||||
}
|
||||
|
||||
Serial.printf("Unknown key '%s' will be ignored\n", k);
|
||||
}
|
||||
|
||||
return lora;
|
||||
}
|
||||
|
||||
void Config::configureDetectionStrategy(String cfg)
|
||||
{
|
||||
if (scan_ranges_sz > 0)
|
||||
delete[] scan_ranges;
|
||||
scan_ranges = NULL;
|
||||
|
||||
String method = cfg;
|
||||
int i = cfg.indexOf(":");
|
||||
if (i >= 0)
|
||||
{
|
||||
method = cfg.substring(0, i);
|
||||
cfg = cfg.substring(i + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
cfg = "";
|
||||
}
|
||||
|
||||
samples = 0;
|
||||
i = method.indexOf(",");
|
||||
if (i >= 0)
|
||||
{
|
||||
samples = method.substring(i + 1).toInt();
|
||||
method = method.substring(0, i);
|
||||
}
|
||||
detection_strategy = method;
|
||||
|
||||
scan_ranges_sz = 0;
|
||||
for (int i = 0, k = 0; (i = findSepa(cfg, ",", i, k)) >= 0; scan_ranges_sz++)
|
||||
;
|
||||
|
||||
if (scan_ranges_sz == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
scan_ranges = new ScanRange[scan_ranges_sz];
|
||||
|
||||
for (int i = 0, j = 0; i < scan_ranges_sz; i++)
|
||||
{
|
||||
scan_ranges[i] = parseScanRange(cfg, j);
|
||||
}
|
||||
}
|
||||
|
||||
bool Config::write_config(const char *path)
|
||||
{
|
||||
File f = SD.open(path, FILE_WRITE, /*create = */ true);
|
||||
@@ -133,15 +500,72 @@ bool Config::write_config(const char *path)
|
||||
return false;
|
||||
}
|
||||
|
||||
f.println("print_profile_time = " + String(print_profile_time ? "true" : "false"));
|
||||
f.println("log_data_json_interval = " + String(log_data_json_interval));
|
||||
f.println("listen_on_serial0 = " + listen_on_serial0);
|
||||
f.println("listen_on_usb = " + listen_on_usb);
|
||||
f.println("print_profile_time = " + getConfig("print_profile_time"));
|
||||
f.println("log_data_json_interval = " + getConfig("log_data_json_interval"));
|
||||
f.println("listen_on_serial0 = " + getConfig("listen_on_serial0"));
|
||||
f.println("listen_on_serial1 = " + getConfig("listen_on_serial1"));
|
||||
f.println("listen_on_usb = " + getConfig("listen_on_usb"));
|
||||
|
||||
f.println("detection_strategy = " + getConfig("detection_strategy"));
|
||||
|
||||
f.println("rx_lora = " + getConfig("rx_lora"));
|
||||
f.println("tx_lora = " + getConfig("tx_lora"));
|
||||
f.println("is_host = " + getConfig("is_host"));
|
||||
|
||||
f.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
String Config::getConfig(String key)
|
||||
{
|
||||
if (key.equalsIgnoreCase("print_profile_time"))
|
||||
{
|
||||
return String(print_profile_time ? "true" : "false");
|
||||
}
|
||||
|
||||
if (key.equalsIgnoreCase("log_data_json_interval"))
|
||||
{
|
||||
return String(log_data_json_interval);
|
||||
}
|
||||
|
||||
if (key.equalsIgnoreCase("listen_on_serial0"))
|
||||
{
|
||||
return listen_on_serial0;
|
||||
}
|
||||
|
||||
if (key.equalsIgnoreCase("listen_on_serial1"))
|
||||
{
|
||||
return listen_on_serial1;
|
||||
}
|
||||
|
||||
if (key.equalsIgnoreCase("listen_on_usb"))
|
||||
{
|
||||
return listen_on_usb;
|
||||
}
|
||||
|
||||
if (key.equalsIgnoreCase("detection_strategy"))
|
||||
{
|
||||
return detectionStrategyToStr(*this);
|
||||
}
|
||||
|
||||
if (key.equalsIgnoreCase("rx_lora"))
|
||||
{
|
||||
return loraConfigToStr(rx_lora);
|
||||
}
|
||||
|
||||
if (key.equalsIgnoreCase("tx_lora"))
|
||||
{
|
||||
return loraConfigToStr(tx_lora);
|
||||
}
|
||||
|
||||
if (key.equalsIgnoreCase("is_host"))
|
||||
{
|
||||
return String(is_host ? "true" : "false");
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
ParseResult parse_config_line(String ln)
|
||||
{
|
||||
ln.trim();
|
||||
|
||||
+43
-2
@@ -3,22 +3,63 @@
|
||||
|
||||
#include <SD.h>
|
||||
|
||||
struct ScanRange
|
||||
{
|
||||
uint64_t start_khz;
|
||||
uint64_t end_khz;
|
||||
uint64_t step_khz;
|
||||
};
|
||||
|
||||
struct LoRaConfig
|
||||
{
|
||||
float freq;
|
||||
uint16_t bw;
|
||||
uint8_t sf;
|
||||
uint8_t cr;
|
||||
uint8_t tx_power;
|
||||
uint16_t preamble_len;
|
||||
uint8_t sync_word;
|
||||
bool crc;
|
||||
uint8_t implicit_header;
|
||||
};
|
||||
|
||||
LoRaConfig *configureLora(String cfg);
|
||||
|
||||
#define CREATE_MISSING_CONFIG true
|
||||
struct Config
|
||||
{
|
||||
bool create_missing_config;
|
||||
bool print_profile_time;
|
||||
String detection_strategy;
|
||||
int samples;
|
||||
size_t scan_ranges_sz;
|
||||
ScanRange *scan_ranges;
|
||||
int log_data_json_interval;
|
||||
String listen_on_serial0;
|
||||
String listen_on_serial1;
|
||||
String listen_on_usb;
|
||||
LoRaConfig *rx_lora;
|
||||
LoRaConfig *tx_lora;
|
||||
|
||||
bool is_host;
|
||||
|
||||
Config()
|
||||
: create_missing_config(CREATE_MISSING_CONFIG), print_profile_time(false),
|
||||
log_data_json_interval(1000), listen_on_serial0(String("none")),
|
||||
listen_on_usb("readline") {};
|
||||
detection_strategy(String("RSSI")), samples(0), scan_ranges_sz(0),
|
||||
scan_ranges(NULL), log_data_json_interval(1000),
|
||||
listen_on_serial0(String("none")), listen_on_serial1(String("readline")),
|
||||
listen_on_usb(String("readline")), rx_lora(NULL), tx_lora(NULL),
|
||||
// Enable Lora Send:
|
||||
// rx_lora(configureLora("freq:920")),tx_lora(configureLora("freq:916"))
|
||||
is_host(false) {};
|
||||
|
||||
bool write_config(const char *path);
|
||||
|
||||
static Config init();
|
||||
bool updateConfig(String key, String value);
|
||||
String getConfig(String key);
|
||||
|
||||
void configureDetectionStrategy(String cfg);
|
||||
};
|
||||
|
||||
struct ParseResult
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
#ifndef LORASA_EVENT_TYPES_H
|
||||
#define LORASA_EVENT_TYPES_H
|
||||
|
||||
struct Event;
|
||||
enum EventType
|
||||
{
|
||||
ALL_EVENTS = 0, // used only at registration time
|
||||
DETECTED,
|
||||
SCAN_TASK_COMPLETE,
|
||||
_MAX_EVENT_TYPE = SCAN_TASK_COMPLETE // unused as event type
|
||||
};
|
||||
struct Listener;
|
||||
#endif
|
||||
+1
-10
@@ -1,17 +1,8 @@
|
||||
#ifndef LORASA_EVENTS_H
|
||||
#define LORASA_EVENTS_H
|
||||
|
||||
struct Event;
|
||||
enum EventType
|
||||
{
|
||||
ALL_EVENTS = 0, // used only at registration time
|
||||
DETECTED,
|
||||
SCAN_TASK_COMPLETE,
|
||||
_MAX_EVENT_TYPE = SCAN_TASK_COMPLETE // unused as event type
|
||||
};
|
||||
struct Listener;
|
||||
|
||||
#include <cstdint>
|
||||
#include <event_types.h>
|
||||
#include <scan.h>
|
||||
|
||||
struct Event
|
||||
|
||||
@@ -1,4 +1,52 @@
|
||||
#ifndef __LILY_GO_H
|
||||
#define __LILY_GO_H
|
||||
|
||||
#include "RadioLib.h"
|
||||
|
||||
#ifdef HELTEC
|
||||
|
||||
#ifdef HELTEC_NO_DISPLAY
|
||||
#define HELTEC_NO_DISPLAY_INSTANCE
|
||||
#else
|
||||
#define DISPLAY_WIDTH 128
|
||||
#define DISPLAY_HEIGHT 64
|
||||
#include "SSD1306Wire.h"
|
||||
#endif
|
||||
|
||||
#define RADIO_TYPE SX1262
|
||||
|
||||
#ifndef HELTEC_NO_RADIO_INSTANCE
|
||||
#ifndef ARDUINO_heltec_wifi_32_lora_V3
|
||||
// Assume MISO and MOSI being wrong when not using Heltec's board definition
|
||||
// and use hspi to make it work anyway. See heltec_setup() for the actual SPI setup.
|
||||
#include <SPI.h>
|
||||
extern SPIClass *hspi;
|
||||
#define RADIO_MODULE_INIT() new Module(SS, DIO1, RST_LoRa, BUSY_LoRa, *hspi);
|
||||
#else // ARDUINO_heltec_wifi_32_lora_V3
|
||||
#endif // end ARDUINO_heltec_wifi_32_lora_V3
|
||||
#endif // end HELTEC_NO_RADIO_INSTANCE
|
||||
|
||||
extern RADIO_TYPE radio;
|
||||
|
||||
#ifndef HELTEC_NO_DISPLAY_INSTANCE
|
||||
#ifdef HELTEC_WIRELESS_STICK
|
||||
#define DISPLAY_GEOMETRY GEOMETRY_64_32
|
||||
#else
|
||||
#define DISPLAY_GEOMETRY GEOMETRY_128_64
|
||||
#endif
|
||||
#define SCREEN_ADDRESS 0x3C
|
||||
|
||||
#define DISPLAY_TYPE SSD1306Wire
|
||||
#define DISPLAY_INIT() SSD1306Wire(SCREEN_ADDRESS, I2C_SDA, I2C_SCL, DISPLAY_GEOMETRY)
|
||||
// SH1106Wire display(0x3c, I2C_SDA, I2C_SCL, DISPLAY_GEOMETRY);
|
||||
#else
|
||||
#define DISPLAY_TYPE void *
|
||||
#endif
|
||||
|
||||
// This file contains a binary patch for the SX1262
|
||||
#include "modules/SX126x/patches/SX126x_patch_scan.h"
|
||||
|
||||
#else
|
||||
// Define for our code
|
||||
#define RST_OLED UNUSED_PIN
|
||||
#define LED BOARD_LED
|
||||
@@ -8,9 +56,7 @@
|
||||
// (See RadioLib_convenience.h)
|
||||
#define RADIOLIB_DO_DURING_HALT heltec_delay(10)
|
||||
#include "RadioLib_convenience.h"
|
||||
#ifdef HELTEC_NO_DISPLAY
|
||||
#define HELTEC_NO_DISPLAY_INSTANCE
|
||||
#else
|
||||
|
||||
#define DISPLAY_WIDTH 128
|
||||
#define DISPLAY_HEIGHT 64
|
||||
// #include "OLEDDisplayUi.h"
|
||||
@@ -18,41 +64,38 @@
|
||||
// #include "SSD1306Brzo.h"
|
||||
#include "SSD1306Wire.h"
|
||||
|
||||
#endif
|
||||
#define ARDUINO_heltec_wifi_32_lora_V3
|
||||
#ifndef HELTEC_NO_RADIO_INSTANCE
|
||||
#ifndef ARDUINO_heltec_wifi_32_lora_V3
|
||||
// Assume MISO and MOSI being wrong when not using Heltec's board definition
|
||||
// and use hspi to make it work anyway. See heltec_setup() for the actual SPI setup.
|
||||
#include <SPI.h>
|
||||
SPIClass *hspi = new SPIClass(2);
|
||||
SX1262 radio = new Module(SS, DIO1, RST_LoRa, BUSY_LoRa, *hspi);
|
||||
#else // ARDUINO_heltec_wifi_32_lora_V3
|
||||
#ifdef USING_SX1280PA
|
||||
SX1280 radio = new Module(RADIO_CS_PIN, RADIO_DIO1_PIN, RADIO_RST_PIN, RADIO_BUSY_PIN);
|
||||
#define RADIO_TYPE SX1280
|
||||
#define RADIO_MODULE_INIT() \
|
||||
new Module(RADIO_CS_PIN, RADIO_DIO1_PIN, RADIO_RST_PIN, RADIO_BUSY_PIN);
|
||||
#endif // end USING_SX1280PA
|
||||
#ifdef USING_SX1262
|
||||
// Default SPI on pins from pins_arduino.h
|
||||
SX1262 radio = new Module(RADIO_CS_PIN, RADIO_DIO1_PIN, RADIO_RST_PIN, RADIO_BUSY_PIN);
|
||||
#define RADIO_TYPE SX1262
|
||||
#define RADIO_MODULE_INIT() \
|
||||
new Module(RADIO_CS_PIN, RADIO_DIO1_PIN, RADIO_RST_PIN, RADIO_BUSY_PIN)
|
||||
#endif // end USING_SX1262
|
||||
#ifdef USING_LR1121
|
||||
// Default SPI on pins from pins_arduino.h
|
||||
LR1121 radio = new Module(RADIO_CS_PIN, RADIO_DIO9_PIN, RADIO_RST_PIN, RADIO_BUSY_PIN);
|
||||
#define RADIO_TYPE LR1121
|
||||
#define RADIO_MODULE_INIT() \
|
||||
new Module(RADIO_CS_PIN, RADIO_DIO9_PIN, RADIO_RST_PIN, RADIO_BUSY_PIN);
|
||||
#endif // end USING_LR1121
|
||||
#ifdef USING_SX1276
|
||||
// Default SPI on pins from pins_arduino.h
|
||||
SX1276 radio = new Module(RADIO_CS_PIN, RADIO_DIO1_PIN, RADIO_RST_PIN, RADIO_BUSY_PIN);
|
||||
#define RADIO_TYPE SX1276
|
||||
#define RADIO_MODULE_INIT() \
|
||||
new Module(RADIO_CS_PIN, RADIO_DIO1_PIN, RADIO_RST_PIN, RADIO_BUSY_PIN);
|
||||
#endif // end USING_SX1276
|
||||
#endif // end ARDUINO_heltec_wifi_32_lora_V3
|
||||
#endif // end HELTEC_NO_RADIO_INSTANCE
|
||||
|
||||
void heltec_led(int led) {}
|
||||
extern RADIO_TYPE radio;
|
||||
|
||||
void heltec_deep_sleep() {}
|
||||
void heltec_led(int led);
|
||||
|
||||
void heltec_delay(int millisec) { delay(millisec); }
|
||||
void heltec_deep_sleep(int sleep_seconds = 0);
|
||||
|
||||
void heltec_delay(int millisec);
|
||||
|
||||
#ifndef HELTEC_NO_DISPLAY_INSTANCE
|
||||
/**
|
||||
* @class PrintSplitter
|
||||
* @brief A class that splits the output of the Print class to two different
|
||||
@@ -82,85 +125,35 @@ class PrintSplitter : public Print
|
||||
Print &b;
|
||||
};
|
||||
|
||||
#ifdef HELTEC_WIRELESS_STICK
|
||||
#define DISPLAY_GEOMETRY GEOMETRY_64_32
|
||||
#else
|
||||
#define DISPLAY_GEOMETRY GEOMETRY_128_64
|
||||
#endif
|
||||
#define SCREEN_ADDRESS 0x3C
|
||||
|
||||
SSD1306Wire display(SCREEN_ADDRESS, I2C_SDA, I2C_SCL, DISPLAY_GEOMETRY);
|
||||
#define DISPLAY_TYPE SSD1306Wire
|
||||
#define DISPLAY_INIT() SSD1306Wire(SCREEN_ADDRESS, I2C_SDA, I2C_SCL, DISPLAY_GEOMETRY)
|
||||
// SH1106Wire display(0x3c, I2C_SDA, I2C_SCL, DISPLAY_GEOMETRY);
|
||||
PrintSplitter both(Serial, display);
|
||||
#else
|
||||
Print &both = Serial;
|
||||
#endif
|
||||
|
||||
#define BOTH_TYPE PrintSplitter
|
||||
#define BOTH_INIT() PrintSplitter(Serial, display)
|
||||
|
||||
extern DISPLAY_TYPE display;
|
||||
extern BOTH_TYPE both;
|
||||
|
||||
// some fake pin
|
||||
#ifdef T3_V1_6_SX1276
|
||||
#define BUTTON_PIN 22
|
||||
#endif
|
||||
#define BUTTON BUTTON_PIN
|
||||
#include "HotButton.h"
|
||||
HotButton button(BUTTON);
|
||||
|
||||
void heltec_loop()
|
||||
{
|
||||
#ifndef DT3_V1_6_SX1276
|
||||
button.update();
|
||||
#endif
|
||||
}
|
||||
|
||||
// This file contains a binary patch for the SX1262
|
||||
#include "modules/SX126x/patches/SX126x_patch_scan.h"
|
||||
|
||||
void heltec_display_power(bool on)
|
||||
{
|
||||
#ifndef HELTEC_NO_DISPLAY_INSTANCE
|
||||
if (on)
|
||||
{
|
||||
#ifdef HELTEC_WIRELESS_STICK
|
||||
// They hooked the display to "external" power, and didn't tell anyone
|
||||
heltec_ve(true);
|
||||
delay(5);
|
||||
#endif
|
||||
pinMode(RST_OLED, OUTPUT);
|
||||
digitalWrite(RST_OLED, HIGH);
|
||||
delay(1);
|
||||
digitalWrite(RST_OLED, LOW);
|
||||
delay(20);
|
||||
digitalWrite(RST_OLED, HIGH);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef HELTEC_WIRELESS_STICK
|
||||
heltec_ve(false);
|
||||
#else
|
||||
display.displayOff();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
void heltec_setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
Serial.println("LILYGO BOARD");
|
||||
#define BUTTON BUTTON_PIN
|
||||
#include "HotButton.h"
|
||||
extern HotButton button;
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
SPI.begin(RADIO_SCLK_PIN, RADIO_MISO_PIN, RADIO_MOSI_PIN);
|
||||
#elif defined(ARDUINO_ARCH_STM32)
|
||||
SPI.setMISO(RADIO_MISO_PIN);
|
||||
SPI.setMOSI(RADIO_MOSI_PIN);
|
||||
SPI.setSCLK(RADIO_SCLK_PIN);
|
||||
SPI.begin();
|
||||
#endif
|
||||
void heltec_loop();
|
||||
|
||||
#ifndef ARDUINO_heltec_wifi_32_lora_V3
|
||||
hspi->begin(SCK, MISO, MOSI, SS);
|
||||
void heltec_display_power(bool on);
|
||||
|
||||
void heltec_setup();
|
||||
#endif
|
||||
#ifndef HELTEC_NO_DISPLAY_INSTANCE
|
||||
heltec_display_power(true);
|
||||
display.init();
|
||||
// display.setContrast(200);
|
||||
display.flipScreenVertically();
|
||||
#endif
|
||||
}
|
||||
@@ -12,6 +12,90 @@
|
||||
|
||||
#include "LoRaBoards.h"
|
||||
|
||||
#include "LiLyGo.h"
|
||||
|
||||
// Implement stubs for functions that exist on Heltec, but not on LilyGo
|
||||
void heltec_led(int led) {}
|
||||
|
||||
void heltec_deep_sleep(int sleep_seconds) {}
|
||||
|
||||
void heltec_delay(int millisec) { delay(millisec); }
|
||||
|
||||
DISPLAY_TYPE display = DISPLAY_INIT();
|
||||
BOTH_TYPE both = BOTH_INIT();
|
||||
|
||||
HotButton button(BUTTON);
|
||||
|
||||
void heltec_loop()
|
||||
{
|
||||
#ifndef DT3_V1_6_SX1276
|
||||
button.update();
|
||||
#endif
|
||||
}
|
||||
|
||||
void heltec_display_power(bool on)
|
||||
{
|
||||
#ifndef HELTEC_NO_DISPLAY_INSTANCE
|
||||
if (on)
|
||||
{
|
||||
#ifdef HELTEC_WIRELESS_STICK
|
||||
// They hooked the display to "external" power, and didn't tell anyone
|
||||
heltec_ve(true);
|
||||
delay(5);
|
||||
#endif
|
||||
pinMode(RST_OLED, OUTPUT);
|
||||
digitalWrite(RST_OLED, HIGH);
|
||||
delay(1);
|
||||
digitalWrite(RST_OLED, LOW);
|
||||
delay(20);
|
||||
digitalWrite(RST_OLED, HIGH);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef HELTEC_WIRELESS_STICK
|
||||
heltec_ve(false);
|
||||
#else
|
||||
display.displayOff();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void heltec_setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
Serial.println("LILYGO BOARD");
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
SPI.begin(RADIO_SCLK_PIN, RADIO_MISO_PIN, RADIO_MOSI_PIN);
|
||||
#elif defined(ARDUINO_ARCH_STM32)
|
||||
SPI.setMISO(RADIO_MISO_PIN);
|
||||
SPI.setMOSI(RADIO_MOSI_PIN);
|
||||
SPI.setSCLK(RADIO_SCLK_PIN);
|
||||
SPI.begin();
|
||||
#endif
|
||||
|
||||
#ifdef HELTEC
|
||||
#ifndef ARDUINO_heltec_wifi_32_lora_V3
|
||||
hspi->begin(SCK, MISO, MOSI, SS);
|
||||
#endif
|
||||
#endif
|
||||
#ifndef HELTEC_NO_DISPLAY_INSTANCE
|
||||
heltec_display_power(true);
|
||||
display.init();
|
||||
// display.setContrast(200);
|
||||
display.flipScreenVertically();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef HELTEC
|
||||
#ifndef ARDUINO_heltec_wifi_32_lora_V3
|
||||
SPIClass hspi = new SPIClass(2);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
RADIO_TYPE radio = RADIO_MODULE_INIT();
|
||||
|
||||
#if defined(HAS_SDCARD)
|
||||
SPIClass SDCardSPI(HSPI);
|
||||
#endif
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
* @last-update 2024-08-07
|
||||
*/
|
||||
|
||||
#ifdef LILYGO
|
||||
#pragma once
|
||||
|
||||
#include "utilities.h"
|
||||
@@ -95,3 +96,4 @@ extern SPIClass SDCardSPI;
|
||||
#elif defined(ARDUINO_ARCH_STM32)
|
||||
extern HardwareSerial SerialGPS;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
#include "models.h"
|
||||
#include <algorithm>
|
||||
using namespace std;
|
||||
|
||||
#include <cstring>
|
||||
|
||||
WaterfallModel::WaterfallModel(size_t w, uint64_t base_dt, size_t m_sz,
|
||||
@@ -67,13 +70,13 @@ void WaterfallModel::reset(uint64_t t0, size_t w)
|
||||
* and it gets added to incomplete minute. This gets repeated for incomplete
|
||||
* minutes, etc.
|
||||
*/
|
||||
size_t WaterfallModel::updateModel(uint16_t t, size_t x, uint16_t y)
|
||||
size_t WaterfallModel::updateModel(uint64_t t, size_t x, uint16_t y)
|
||||
{
|
||||
size_t changed = 1;
|
||||
|
||||
while (t > times[0])
|
||||
{
|
||||
changed = push();
|
||||
changed = max(changed, push());
|
||||
}
|
||||
|
||||
counts[0][x]++;
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ struct WaterfallModel
|
||||
WaterfallModel(size_t w, uint64_t base_dt, size_t m_sz, const size_t *multiples);
|
||||
|
||||
void reset(uint64_t t0, size_t width);
|
||||
size_t updateModel(uint16_t t, size_t x, uint16_t y);
|
||||
size_t updateModel(uint64_t t, size_t x, uint16_t y);
|
||||
size_t push();
|
||||
|
||||
char *toString();
|
||||
|
||||
+4
-2
@@ -4,9 +4,11 @@
|
||||
#include "scan.h"
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <events.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
uint16_t Scan::rssiMethod(size_t samples, uint16_t *result, size_t res_size)
|
||||
uint16_t Scan::rssiMethod(float (*getRSSI)(void *), void *param, size_t samples,
|
||||
uint16_t *result, size_t res_size)
|
||||
{
|
||||
float scale((float)res_size / (HI_RSSI_THRESHOLD - LO_RSSI_THRESHOLD + 0.1));
|
||||
|
||||
@@ -18,7 +20,7 @@ uint16_t Scan::rssiMethod(size_t samples, uint16_t *result, size_t res_size)
|
||||
// N of samples
|
||||
for (int r = 0; r < samples; r++)
|
||||
{
|
||||
float rssi = getRSSI();
|
||||
float rssi = getRSSI(param);
|
||||
if (rssi < -65535)
|
||||
rssi = -65535;
|
||||
|
||||
|
||||
+23
-8
@@ -1,11 +1,11 @@
|
||||
#ifndef LORASA_SCAN_H
|
||||
#define LORASA_SCAN_H
|
||||
|
||||
#include <config.h>
|
||||
#include <cstdint>
|
||||
#include <events.h>
|
||||
#include <event_types.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifndef LORASA_CORE_H
|
||||
|
||||
#define LORASA_CORE_H
|
||||
|
||||
#ifdef PRINT_DEBUG
|
||||
#define LOG(args...) Serial.printf(args...)
|
||||
#define LOG_IF(cond, args...) \
|
||||
@@ -34,6 +34,22 @@ constexpr float LO_RSSI_THRESHOLD = HI_RSSI_THRESHOLD - 66;
|
||||
#define SAMPLES_RSSI 20
|
||||
#endif
|
||||
|
||||
struct ScanPage
|
||||
{
|
||||
uint64_t start_mhz;
|
||||
uint64_t end_mhz;
|
||||
size_t page_sz;
|
||||
ScanRange *scan_ranges;
|
||||
|
||||
~ScanPage()
|
||||
{
|
||||
if (page_sz > 0)
|
||||
{
|
||||
delete[] scan_ranges;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct Scan
|
||||
{
|
||||
uint64_t epoch;
|
||||
@@ -61,11 +77,10 @@ struct Scan
|
||||
},
|
||||
comms_initialized(false) {};
|
||||
|
||||
virtual float getRSSI() = 0;
|
||||
|
||||
// rssiMethod gets the data similar to the scan method,
|
||||
// but uses getRSSI directly.
|
||||
uint16_t rssiMethod(size_t samples, uint16_t *result, size_t res_size);
|
||||
uint16_t rssiMethod(float (*getRSSI)(void *), void *param, size_t samples,
|
||||
uint16_t *result, size_t res_size);
|
||||
|
||||
// detect method analyses result, and produces filtered_result, marking
|
||||
// those values that represent a detection event.
|
||||
|
||||
@@ -34,6 +34,8 @@ lib_deps =
|
||||
build_flags =
|
||||
-DHELTEC_POWER_BUTTON
|
||||
-DHELTEC
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||
-DARDUINO_USB_MODE=1
|
||||
|
||||
[env:heltec_wifi_lora_32_V3-OSD]
|
||||
platform = espressif32
|
||||
|
||||
+4
-1
@@ -13,6 +13,7 @@ typedef struct {
|
||||
|
||||
#define POLY 0x1021
|
||||
uint16_t crc16(char *p, char *end, uint16_t c) {
|
||||
c ^= 0xffff;
|
||||
if (end == NULL) {
|
||||
end = strchr(p, 0);
|
||||
}
|
||||
@@ -29,7 +30,7 @@ uint16_t crc16(char *p, char *end, uint16_t c) {
|
||||
}
|
||||
}
|
||||
|
||||
return c;
|
||||
return c ^ 0xffff;
|
||||
}
|
||||
|
||||
#define BUFSIZE 102400
|
||||
@@ -132,6 +133,8 @@ int main(int argc, char** argv)
|
||||
lines--;
|
||||
write(1, buffer, pos);
|
||||
} else if (!is_wrap) {
|
||||
write(1, "> ", 2);
|
||||
write(1, buffer, pos);
|
||||
errors++;
|
||||
}
|
||||
|
||||
|
||||
+450
-178
@@ -55,10 +55,8 @@
|
||||
#define RADIOLIB_CHECK_PARAMS (0)
|
||||
|
||||
#include <charts.h>
|
||||
#ifdef SERIAL_OUT
|
||||
#include <comms.h>
|
||||
#include <config.h>
|
||||
#endif
|
||||
#include <events.h>
|
||||
#include <scan.h>
|
||||
#include <stdlib.h>
|
||||
@@ -79,7 +77,7 @@
|
||||
|
||||
// #include "utilities.h"
|
||||
// Our Code
|
||||
#include "LiLyGo.h"
|
||||
#include <LiLyGo.h>
|
||||
#endif // end LILYGO
|
||||
|
||||
#define BT_SCAN_DELAY 60 * 1 * 1000
|
||||
@@ -88,6 +86,8 @@ long noDevicesMillis = 0, cycleCnt = 0;
|
||||
bool present = false;
|
||||
bool scanFinished = true;
|
||||
|
||||
bool radioIsScan = false;
|
||||
|
||||
// time to scan BT
|
||||
#define BT_SCAN_TIME 10
|
||||
|
||||
@@ -153,8 +153,12 @@ typedef enum
|
||||
// constexpr int RSSI_OUTPUT_FORMULA = 2;
|
||||
|
||||
// Feature to scan diapasones. Other frequency settings will be ignored.
|
||||
// int SCAN_RANGES[] = {850890, 920950};
|
||||
int SCAN_RANGES[] = {};
|
||||
// String SCAN_RANGES = String("850..890,920..950");
|
||||
String SCAN_RANGES = "";
|
||||
|
||||
size_t scan_pages_sz = 0;
|
||||
ScanPage *scan_pages;
|
||||
size_t scan_page = 0;
|
||||
|
||||
// MHZ per page
|
||||
// to put everything into one page set RANGE_PER_PAGE = FREQ_END - 800
|
||||
@@ -213,8 +217,6 @@ bool detected_y[STEPS]; // 20 - ??? steps
|
||||
|
||||
// Used as a Led Light and Buzzer/count trigger
|
||||
bool first_run, new_pixel, detected_x = false;
|
||||
// drone detection flag
|
||||
bool detected = false;
|
||||
uint64_t drone_detection_level = DEFAULT_DRONE_DETECTION_LEVEL;
|
||||
#define TRIGGER_LEVEL -80.0
|
||||
uint64_t drone_detected_frequency_start = 0;
|
||||
@@ -243,9 +245,8 @@ HardwareSerial SerialPort(SERIAL_PORT);
|
||||
|
||||
// #define WEB_SERVER true
|
||||
|
||||
uint64_t x, y, range_item, w = WATERFALL_START, i = 0;
|
||||
uint64_t x, y, w = WATERFALL_START, i = 0;
|
||||
int osd_x = 1, osd_y = 2, col = 0, max_bin = 32;
|
||||
uint64_t ranges_count = 0;
|
||||
|
||||
int rssi = 0;
|
||||
int state = 0;
|
||||
@@ -482,17 +483,11 @@ void osdProcess()
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SERIAL_OUT
|
||||
Config config;
|
||||
#endif
|
||||
|
||||
struct RadioScan : Scan
|
||||
{
|
||||
float getRSSI() override;
|
||||
};
|
||||
|
||||
float RadioScan::getRSSI()
|
||||
float getRSSI(void *param)
|
||||
{
|
||||
Scan *r = (Scan *)param;
|
||||
#if defined(USING_SX1280PA)
|
||||
// radio.startReceive();
|
||||
// get instantaneous RSSI value
|
||||
@@ -512,7 +507,26 @@ float RadioScan::getRSSI()
|
||||
#endif
|
||||
}
|
||||
|
||||
RadioScan r;
|
||||
float getCAD(void *param)
|
||||
{
|
||||
Scan *r = (Scan *)param;
|
||||
|
||||
int16_t err = radio.scanChannel();
|
||||
if (err != RADIOLIB_ERR_NONE)
|
||||
{
|
||||
return -999;
|
||||
}
|
||||
|
||||
#ifdef USING_LR1121
|
||||
// LR1121 doesn't implement getRSSI(bool), getRSSI always
|
||||
// returns RSSI of the last packet
|
||||
return radio.getRSSI();
|
||||
#else
|
||||
return radio.getRSSI(true);
|
||||
#endif
|
||||
}
|
||||
|
||||
Scan r;
|
||||
|
||||
#define WATERFALL_SENSITIVITY 0.05
|
||||
DecoratedBarChart *bar;
|
||||
@@ -521,19 +535,68 @@ StackedChart stacked(display, 0, 0, 0, 0);
|
||||
|
||||
UptimeClock *uptime;
|
||||
|
||||
int16_t initForScan(float freq)
|
||||
{
|
||||
int16_t state;
|
||||
|
||||
#if defined(USING_SX1280PA)
|
||||
state = radio.beginGFSK(freq);
|
||||
#elif defined(USING_LR1121)
|
||||
state = radio.beginGFSK(freq, 4.8F, 5.0F, 156.2F, 10, 16U, 1.7F);
|
||||
#else
|
||||
state = radio.beginFSK(freq);
|
||||
#endif
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
bool setFrequency(float curr_freq)
|
||||
{
|
||||
r.current_frequency = curr_freq;
|
||||
LOG("setFrequency:%f\n", r.current_frequency);
|
||||
|
||||
int16_t state;
|
||||
#ifdef USING_SX1280PA
|
||||
int16_t state1 =
|
||||
radio.setFrequency(r.current_frequency); // 1280 doesn't have calibration
|
||||
|
||||
state = radio.startReceive(RADIOLIB_SX128X_RX_TIMEOUT_INF);
|
||||
if (state != RADIOLIB_ERR_NONE)
|
||||
{
|
||||
Serial.println("Error:startReceive:" + String(state));
|
||||
}
|
||||
|
||||
state = state1;
|
||||
#elif USING_SX1276
|
||||
state = radio.setFrequency(freq);
|
||||
#else
|
||||
state = radio.setFrequency(r.current_frequency,
|
||||
true); // false = calibration is needed here
|
||||
#endif
|
||||
if (state != RADIOLIB_ERR_NONE)
|
||||
{
|
||||
display.drawString(0, 64 - 10,
|
||||
"E(" + String(state) +
|
||||
"):setFrequency:" + String(r.current_frequency));
|
||||
Serial.println("E(" + String(state) +
|
||||
"):setFrequency:" + String(r.current_frequency));
|
||||
display.display();
|
||||
delay(2);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void init_radio()
|
||||
{
|
||||
// initialize SX1262 FSK modem at the initial frequency
|
||||
both.println("Init radio");
|
||||
#if defined(USING_SX1280PA)
|
||||
state = radio.beginGFSK(CONF_FREQ_BEGIN);
|
||||
#elif defined(USING_LR1121)
|
||||
state = radio.beginGFSK(CONF_FREQ_BEGIN, 4.8F, 5.0F, 156.2F, 10, 16U, 1.7F);
|
||||
#else
|
||||
state = radio.beginFSK(CONF_FREQ_BEGIN);
|
||||
#endif
|
||||
state = initForScan(CONF_FREQ_BEGIN);
|
||||
|
||||
if (state == RADIOLIB_ERR_NONE)
|
||||
{
|
||||
radioIsScan = true;
|
||||
Serial.println(F("success!"));
|
||||
}
|
||||
else
|
||||
@@ -579,30 +642,13 @@ void init_radio()
|
||||
}
|
||||
both.println("Starting scanning...");
|
||||
|
||||
// calibrate only once ,,, at startup
|
||||
// TODO: check documentation (9.2.1) if we must calibrate in certain ranges
|
||||
#ifdef USING_SX1280PA
|
||||
state = radio.setFrequency(CONF_FREQ_BEGIN);
|
||||
if (state != RADIOLIB_ERR_NONE)
|
||||
{
|
||||
Serial.println("Error:setFrequency:" + String(state));
|
||||
}
|
||||
state = radio.startReceive();
|
||||
if (state != RADIOLIB_ERR_NONE)
|
||||
{
|
||||
Serial.println("Error:startReceive:" + String(state));
|
||||
}
|
||||
#elif USING_SX1276
|
||||
// Sets carrier frequency. Allowed values range from 137.0 MHz to 1020.0 MHz.
|
||||
radio.setFrequency(CONF_FREQ_BEGIN);
|
||||
#else
|
||||
radio.setFrequency(CONF_FREQ_BEGIN, true);
|
||||
#endif
|
||||
// calibrate only once ,,, at startup
|
||||
// TODO: check documentation (9.2.1) if we must calibrate in certain ranges
|
||||
setFrequency(CONF_FREQ_BEGIN);
|
||||
|
||||
delay(50);
|
||||
}
|
||||
|
||||
#ifdef SERIAL_OUT
|
||||
struct frequency_scan_result
|
||||
{
|
||||
uint64_t begin;
|
||||
@@ -613,12 +659,10 @@ struct frequency_scan_result
|
||||
ScanTaskResult dump;
|
||||
size_t readings_sz;
|
||||
} frequency_scan_result;
|
||||
#endif
|
||||
|
||||
TaskHandle_t logToSerial = NULL;
|
||||
TaskHandle_t dumpToComms = NULL;
|
||||
|
||||
#ifdef SERIAL_OUT
|
||||
void eventListenerForReport(void *arg, Event &e)
|
||||
{
|
||||
if (e.type == EventType::DETECTED)
|
||||
@@ -686,6 +730,8 @@ ScanTask report_scans = ScanTask{
|
||||
delay : 0 // 0 => as and when it happens; > 0 => at least once that many ms
|
||||
};
|
||||
|
||||
bool requested_host = true;
|
||||
|
||||
void dumpToCommsTask(void *parameter)
|
||||
{
|
||||
uint64_t last_epoch = frequency_scan_result.last_epoch;
|
||||
@@ -712,10 +758,22 @@ void dumpToCommsTask(void *parameter)
|
||||
Message m;
|
||||
m.type = MessageType::SCAN_RESULT;
|
||||
m.payload.dump = frequency_scan_result.dump;
|
||||
Comms0->send(m);
|
||||
if (requested_host)
|
||||
{
|
||||
HostComms->send(m);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Comms0 != NULL)
|
||||
Comms0->send(m);
|
||||
if (Comms1 != NULL)
|
||||
Comms1->send(m);
|
||||
}
|
||||
|
||||
m.payload.dump.sz =
|
||||
0; // dump is shared, so should not delete arrays in destructor
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef LOG_DATA_JSON
|
||||
void logToSerialTask(void *parameter)
|
||||
@@ -752,6 +810,119 @@ void logToSerialTask(void *parameter)
|
||||
|
||||
void drone_sound_alarm(void *arg, Event &e);
|
||||
|
||||
void configurePages()
|
||||
{
|
||||
if (scan_pages_sz > 0)
|
||||
delete[] scan_pages;
|
||||
|
||||
if (single_page_scan)
|
||||
{
|
||||
scan_pages_sz = 1;
|
||||
ScanPage scan_page = {
|
||||
start_mhz : CONF_FREQ_BEGIN,
|
||||
end_mhz : CONF_FREQ_END,
|
||||
page_sz : config.scan_ranges_sz
|
||||
};
|
||||
if (scan_page.page_sz > 0)
|
||||
{
|
||||
scan_page.scan_ranges = new ScanRange[scan_page.page_sz];
|
||||
}
|
||||
for (int i = 0; i < scan_page.page_sz; i++)
|
||||
{
|
||||
scan_page.scan_ranges[i] = config.scan_ranges[i];
|
||||
}
|
||||
scan_pages = new ScanPage[1]{scan_page};
|
||||
scan_page.page_sz =
|
||||
0; // make sure it doesn't free up the Scanranges that were just constructed
|
||||
}
|
||||
else
|
||||
{
|
||||
scan_pages_sz =
|
||||
(CONF_FREQ_END - CONF_FREQ_BEGIN + RANGE_PER_PAGE - 1) / RANGE_PER_PAGE;
|
||||
scan_pages = new ScanPage[scan_pages_sz];
|
||||
for (int j = 0; j < scan_pages_sz; j++)
|
||||
{
|
||||
ScanPage scan_page = {
|
||||
start_mhz : CONF_FREQ_BEGIN + j * RANGE_PER_PAGE,
|
||||
end_mhz : CONF_FREQ_BEGIN + (j + 1) * RANGE_PER_PAGE,
|
||||
page_sz : 0
|
||||
};
|
||||
for (int i = 0; i < config.scan_ranges_sz; i++)
|
||||
{
|
||||
if (config.scan_ranges[i].start_khz > scan_page.end_mhz * 1000 ||
|
||||
config.scan_ranges[i].end_khz < scan_page.start_mhz * 1000)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
scan_page.page_sz++;
|
||||
}
|
||||
|
||||
if (scan_page.page_sz > 0)
|
||||
{
|
||||
scan_page.scan_ranges = new ScanRange[scan_page.page_sz];
|
||||
for (int i = 0, r = 0; i < config.scan_ranges_sz; i++)
|
||||
{
|
||||
if (config.scan_ranges[i].start_khz > scan_page.end_mhz * 1000 ||
|
||||
config.scan_ranges[i].end_khz < scan_page.start_mhz * 1000)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
scan_page.scan_ranges[r] = {
|
||||
start_khz : max(config.scan_ranges[i].start_khz,
|
||||
scan_page.start_mhz * 1000),
|
||||
end_khz :
|
||||
min(config.scan_ranges[i].end_khz, scan_page.end_mhz * 1000),
|
||||
step_khz : config.scan_ranges[i].step_khz
|
||||
};
|
||||
r++;
|
||||
}
|
||||
}
|
||||
scan_pages[j] = scan_page;
|
||||
scan_page.page_sz =
|
||||
0; // we copied over the values, make sure the array doesn't get freed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void configureDetection()
|
||||
{
|
||||
if (config.scan_ranges_sz == 0)
|
||||
{
|
||||
config.scan_ranges_sz = 1;
|
||||
config.scan_ranges = new ScanRange[1];
|
||||
config.scan_ranges[0].start_khz = FREQ_BEGIN * 1000;
|
||||
config.scan_ranges[0].end_khz = FREQ_END * 1000;
|
||||
config.scan_ranges[0].step_khz =
|
||||
(float)(FREQ_END - FREQ_BEGIN) * 1000 / (STEPS * SCAN_RBW_FACTOR);
|
||||
}
|
||||
|
||||
if (config.samples <= 0)
|
||||
{
|
||||
config.samples = SAMPLES_RSSI;
|
||||
}
|
||||
|
||||
CONF_SAMPLES = config.samples;
|
||||
|
||||
CONF_FREQ_BEGIN = config.scan_ranges[0].start_khz / 1000;
|
||||
CONF_FREQ_END = config.scan_ranges[0].end_khz / 1000;
|
||||
for (int i = 0; i < config.scan_ranges_sz; i++)
|
||||
{
|
||||
CONF_FREQ_BEGIN = min(CONF_FREQ_BEGIN, config.scan_ranges[i].start_khz / 1000);
|
||||
CONF_FREQ_END = max(CONF_FREQ_END, config.scan_ranges[i].end_khz / 1000);
|
||||
}
|
||||
|
||||
median_frequency = (CONF_FREQ_BEGIN + CONF_FREQ_END) / 2;
|
||||
|
||||
samples = CONF_SAMPLES;
|
||||
|
||||
RANGE_PER_PAGE = CONF_FREQ_END - CONF_FREQ_BEGIN; // FREQ_END - CONF_FREQ_BEGIN
|
||||
RANGE = (int)(CONF_FREQ_END - CONF_FREQ_BEGIN);
|
||||
range = RANGE;
|
||||
|
||||
configurePages();
|
||||
}
|
||||
|
||||
void readConfigFile()
|
||||
{
|
||||
// writeFile(LittleFS, "/text.txt", "{WIFI:{name:\"sdfsdf\",
|
||||
@@ -777,27 +948,22 @@ void readConfigFile()
|
||||
smpls = readParameterFromParameterFile("samples");
|
||||
Serial.println("SAMPLES: " + smpls);
|
||||
|
||||
CONF_SAMPLES = (smpls == "") ? samples : atoi(smpls.c_str());
|
||||
samples = CONF_SAMPLES;
|
||||
CONF_FREQ_BEGIN = (fstart == "") ? FREQ_BEGIN : atoi(fstart.c_str());
|
||||
CONF_FREQ_END = (fend == "") ? FREQ_END : atoi(fend.c_str());
|
||||
String detection = String("RSSI");
|
||||
if (smpls.length() > 0)
|
||||
detection += "," + smpls;
|
||||
if (fstart.length() == 0)
|
||||
fstart = String(FREQ_BEGIN * 1000);
|
||||
if (fend.length() == 0)
|
||||
fend = String(FREQ_END * 1000);
|
||||
|
||||
detection += ":" + fstart + ".." + fend + "/" + String(STEPS * SCAN_RBW_FACTOR);
|
||||
|
||||
config.configureDetectionStrategy(detection);
|
||||
configureDetection();
|
||||
|
||||
both.println("C FREQ BEGIN:" + String(CONF_FREQ_BEGIN));
|
||||
both.println("C FREQ END:" + String(CONF_FREQ_END));
|
||||
both.println("C SAMPLES:" + String(CONF_SAMPLES));
|
||||
|
||||
RANGE_PER_PAGE = CONF_FREQ_END - CONF_FREQ_BEGIN; // FREQ_END - CONF_FREQ_BEGIN
|
||||
|
||||
RANGE = (int)(CONF_FREQ_END - CONF_FREQ_BEGIN);
|
||||
|
||||
SINGLE_STEP = (float)(RANGE / (STEPS * SCAN_RBW_FACTOR));
|
||||
|
||||
range = (int)(CONF_FREQ_END - CONF_FREQ_BEGIN);
|
||||
|
||||
iterations = RANGE / RANGE_PER_PAGE;
|
||||
|
||||
// uint64_t range_frequency = FREQ_END - CONF_FREQ_BEGIN;
|
||||
median_frequency = (CONF_FREQ_BEGIN + CONF_FREQ_END) / 2;
|
||||
}
|
||||
|
||||
void setup(void)
|
||||
@@ -842,7 +1008,6 @@ void setup(void)
|
||||
bt_start = millis();
|
||||
wf_start = millis();
|
||||
|
||||
#ifdef SERIAL_OUT
|
||||
config = Config::init();
|
||||
r.comms_initialized = Comms::initComms(config);
|
||||
if (r.comms_initialized)
|
||||
@@ -853,7 +1018,6 @@ void setup(void)
|
||||
{
|
||||
Serial.println("Comms did not initialize");
|
||||
}
|
||||
#endif
|
||||
|
||||
pinMode(LED, OUTPUT);
|
||||
pinMode(BUZZER_PIN, OUTPUT);
|
||||
@@ -913,24 +1077,19 @@ void setup(void)
|
||||
initLittleFS();
|
||||
|
||||
readConfigFile();
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef WEB_SERVER
|
||||
CONF_SAMPLES = samples;
|
||||
CONF_FREQ_BEGIN = FREQ_BEGIN;
|
||||
CONF_FREQ_END = FREQ_END;
|
||||
if (config.scan_ranges_sz == 0 && SCAN_RANGES.length() > 0)
|
||||
{
|
||||
config.configureDetectionStrategy(config.detection_strategy + ":" + SCAN_RANGES);
|
||||
}
|
||||
|
||||
configureDetection();
|
||||
|
||||
both.println("FREQ BEGIN:" + String(CONF_FREQ_BEGIN));
|
||||
both.println("FREQ END:" + String(CONF_FREQ_END));
|
||||
both.println("SAMPLES:" + String(CONF_SAMPLES));
|
||||
|
||||
RANGE_PER_PAGE = CONF_FREQ_END - CONF_FREQ_BEGIN; // FREQ_END - CONF_FREQ_BEGIN
|
||||
RANGE = (int)(CONF_FREQ_END - CONF_FREQ_BEGIN);
|
||||
SINGLE_STEP = (float)(RANGE / (STEPS * SCAN_RBW_FACTOR));
|
||||
range = (int)(CONF_FREQ_END - CONF_FREQ_BEGIN);
|
||||
iterations = RANGE / RANGE_PER_PAGE;
|
||||
median_frequency = (CONF_FREQ_BEGIN + CONF_FREQ_END) / 2;
|
||||
#endif
|
||||
init_radio();
|
||||
|
||||
@@ -1007,6 +1166,8 @@ void setup(void)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configurePages();
|
||||
display.clear();
|
||||
Serial.println();
|
||||
|
||||
@@ -1036,9 +1197,7 @@ void setup(void)
|
||||
#ifdef LOG_DATA_JSON
|
||||
xTaskCreate(logToSerialTask, "LOG_DATA_JSON", 2048, NULL, 1, &logToSerial);
|
||||
#endif
|
||||
#ifdef SERIAL_OUT
|
||||
xTaskCreate(dumpToCommsTask, "DUMP_RESPONSE_PROCESS", 2048, NULL, 1, &dumpToComms);
|
||||
#endif
|
||||
|
||||
r.trigger_level = TRIGGER_LEVEL;
|
||||
stacked.reset(0, 0, display.width(), display.height());
|
||||
@@ -1076,12 +1235,10 @@ void setup(void)
|
||||
r.addEventListener(DETECTED, drone_sound_alarm, &r);
|
||||
r.addEventListener(SCAN_TASK_COMPLETE, stacked);
|
||||
|
||||
#ifdef SERIAL_OUT
|
||||
frequency_scan_result.readings_sz = 0;
|
||||
frequency_scan_result.dump.sz = 0;
|
||||
|
||||
r.addEventListener(ALL_EVENTS, eventListenerForReport, NULL);
|
||||
#endif
|
||||
|
||||
#ifdef UPTIME_CLOCK
|
||||
uptime = new UptimeClock(display, millis());
|
||||
@@ -1248,35 +1405,11 @@ bool is_new_x_pixel(int x)
|
||||
return false;
|
||||
}
|
||||
|
||||
void check_ranges()
|
||||
{
|
||||
if (RANGE_PER_PAGE == range)
|
||||
{
|
||||
single_page_scan = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
single_page_scan = false;
|
||||
}
|
||||
|
||||
for (int range : SCAN_RANGES)
|
||||
{
|
||||
ranges_count++;
|
||||
}
|
||||
|
||||
if (ranges_count > 0)
|
||||
{
|
||||
iterations = ranges_count;
|
||||
single_page_scan = false;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SERIAL_OUT
|
||||
void checkComms()
|
||||
{
|
||||
while (Comms0->available() > 0)
|
||||
while (HostComms->available() > 0)
|
||||
{
|
||||
Message *m = Comms0->receive();
|
||||
Message *m = HostComms->receive();
|
||||
if (m == NULL)
|
||||
continue;
|
||||
|
||||
@@ -1284,27 +1417,124 @@ void checkComms()
|
||||
{
|
||||
case MessageType::SCAN:
|
||||
report_scans = m->payload.scan;
|
||||
requested_host = true;
|
||||
Serial.println("Host: forwarding message SCAN to peer");
|
||||
Comms0->send(*m); // forward to peer
|
||||
Comms1->send(*m); // forward to peer
|
||||
break;
|
||||
case MessageType::CONFIG_TASK:
|
||||
if (m->payload.config.is_set)
|
||||
{
|
||||
String v = config.getConfig(*m->payload.config.key);
|
||||
bool r =
|
||||
config.updateConfig(*m->payload.config.key, *m->payload.config.value);
|
||||
Serial.printf("SET config (%s): %s = %s (was: %s)\n", r ? "OK" : "failed",
|
||||
m->payload.config.key->c_str(),
|
||||
m->payload.config.value->c_str(), v.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.printf("GET config: %s = %s\n", m->payload.config.key->c_str(),
|
||||
config.getConfig(*m->payload.config.key).c_str());
|
||||
}
|
||||
break;
|
||||
}
|
||||
delete m;
|
||||
}
|
||||
|
||||
while (Comms0->available() > 0)
|
||||
{
|
||||
Message *m = Comms0->receive();
|
||||
Serial.println("Comms0: was available, but didn't receive");
|
||||
if (m == NULL)
|
||||
continue;
|
||||
|
||||
switch (m->type)
|
||||
{
|
||||
case MessageType::SCAN:
|
||||
report_scans = m->payload.scan; // receive from peer
|
||||
requested_host = false;
|
||||
break;
|
||||
|
||||
case MessageType::SCAN_RESULT:
|
||||
HostComms->send(*m); // forward from peer
|
||||
break;
|
||||
}
|
||||
delete m;
|
||||
}
|
||||
|
||||
while (Comms1->available() > 0)
|
||||
{
|
||||
Message *m = Comms1->receive();
|
||||
Serial.println("Comms1: was available, but didn't receive");
|
||||
if (m == NULL)
|
||||
continue;
|
||||
|
||||
switch (m->type)
|
||||
{
|
||||
case MessageType::SCAN:
|
||||
report_scans = m->payload.scan; // receive from peer
|
||||
requested_host = false;
|
||||
break;
|
||||
|
||||
case MessageType::SCAN_RESULT:
|
||||
HostComms->send(*m); // forward from peer
|
||||
break;
|
||||
}
|
||||
delete m;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// MAX Frequency RSSI BIN value of the samples
|
||||
int max_rssi_x = 999;
|
||||
|
||||
void doScan();
|
||||
|
||||
void reportScan(RadioComms &c);
|
||||
|
||||
int16_t checkRadio(RadioComms &c);
|
||||
|
||||
void loop(void)
|
||||
{
|
||||
r.led_flag = false;
|
||||
|
||||
r.detection_count = 0;
|
||||
drone_detected_frequency_start = 0;
|
||||
ranges_count = 0;
|
||||
|
||||
#ifdef SERIAL_OUT
|
||||
checkComms();
|
||||
|
||||
if (config.is_host)
|
||||
{
|
||||
if (TxComms != NULL)
|
||||
{
|
||||
// NB: swapping the use of Tx and Rx comms, so a pair of modules
|
||||
// with identical rx/tx_lora config can talk
|
||||
int16_t status = checkRadio(*TxComms);
|
||||
if (status != RADIOLIB_ERR_NONE)
|
||||
{
|
||||
Serial.printf("Error getting a message: %d\n", status);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
doScan();
|
||||
if (TxComms != NULL)
|
||||
reportScan(*TxComms);
|
||||
if (RxComms != NULL)
|
||||
checkRadio(*RxComms);
|
||||
}
|
||||
}
|
||||
|
||||
void doScan()
|
||||
{
|
||||
if (!radioIsScan)
|
||||
{
|
||||
radioIsScan = true;
|
||||
initForScan(CONF_FREQ_BEGIN);
|
||||
state = radio.startReceive(RADIOLIB_SX126X_RX_TIMEOUT_NONE);
|
||||
}
|
||||
|
||||
// reset scan time
|
||||
if (config.print_profile_time)
|
||||
{
|
||||
@@ -1312,7 +1542,6 @@ void loop(void)
|
||||
loop_start = millis();
|
||||
}
|
||||
r.epoch++;
|
||||
#endif
|
||||
|
||||
if (!ANIMATED_RELOAD || !single_page_scan)
|
||||
{
|
||||
@@ -1331,35 +1560,13 @@ void loop(void)
|
||||
r.fr_begin = CONF_FREQ_BEGIN;
|
||||
r.fr_end = r.fr_begin;
|
||||
|
||||
// 50 is a single-screen range
|
||||
// TODO: Make 50 a variable with the option to show the full range
|
||||
iterations = range / RANGE_PER_PAGE;
|
||||
|
||||
#if 0 // disabled code
|
||||
if (range % RANGE_PER_PAGE != 0)
|
||||
for (scan_page = 0; scan_page < scan_pages_sz; scan_page++)
|
||||
{
|
||||
// add more scan
|
||||
//++;
|
||||
}
|
||||
#endif
|
||||
|
||||
check_ranges();
|
||||
|
||||
// Iterating by small ranges by 50 Mhz each pixel is 0.4 Mhz
|
||||
for (range_item = 0; range_item < iterations; range_item++)
|
||||
{
|
||||
range = RANGE_PER_PAGE;
|
||||
if (ranges_count == 0)
|
||||
{
|
||||
r.fr_begin = (range_item == 0) ? r.fr_begin : r.fr_begin + range;
|
||||
r.fr_end = r.fr_begin + RANGE_PER_PAGE;
|
||||
}
|
||||
else
|
||||
{
|
||||
r.fr_begin = SCAN_RANGES[range_item] / 1000;
|
||||
r.fr_end = SCAN_RANGES[range_item] % 1000;
|
||||
range = r.fr_end - r.fr_begin;
|
||||
}
|
||||
ScanPage &page = scan_pages[scan_page];
|
||||
r.fr_begin = page.start_mhz;
|
||||
r.fr_end = page.end_mhz;
|
||||
range = r.fr_end - r.fr_begin;
|
||||
median_frequency = (page.start_mhz + page.end_mhz) / 2;
|
||||
|
||||
#ifdef DISABLED_CODE
|
||||
if (!ANIMATED_RELOAD || !single_page_scan)
|
||||
@@ -1379,10 +1586,38 @@ void loop(void)
|
||||
|
||||
// horizontal (x axis) Frequency loop
|
||||
osd_x = 1, osd_y = 2, col = 0, max_bin = 0;
|
||||
|
||||
// x loop
|
||||
for (x = 0; x < STEPS * SCAN_RBW_FACTOR; x++)
|
||||
for (int range = 0, step = 0; range < page.page_sz; range += (step == 0))
|
||||
{
|
||||
new_pixel = is_new_x_pixel(x);
|
||||
// the logic is:
|
||||
// 1. go through each scan_range in the order that they are declared
|
||||
// in the page
|
||||
// 2. start with scan_range.start and always end with scan_range.end
|
||||
// if adding step lands us a little short of end, there will be
|
||||
// extra iteration to scan actual end frequency
|
||||
// 3. the next iteration after scanning end frequency will be next range
|
||||
// 4. x is derived from the frequency we are going to scan with respect to
|
||||
// the page size
|
||||
ScanRange scan_range = page.scan_ranges[range];
|
||||
uint64_t curr_freq = scan_range.start_khz + scan_range.step_khz * step;
|
||||
if (curr_freq > scan_range.end_khz)
|
||||
curr_freq = scan_range.end_khz;
|
||||
|
||||
// for now support legacy calculation of x that relies on SCAN_RBW_FACTOR
|
||||
x = (curr_freq - page.start_mhz * 1000) * STEPS * SCAN_RBW_FACTOR /
|
||||
((page.end_mhz - page.start_mhz) * 1000);
|
||||
|
||||
new_pixel = step == 0 || is_new_x_pixel(x);
|
||||
if (curr_freq == scan_range.end_khz)
|
||||
{
|
||||
step = 0; // trigger switch to the next range on the next iteration
|
||||
}
|
||||
else
|
||||
{
|
||||
step++;
|
||||
}
|
||||
|
||||
if (ANIMATED_RELOAD && SCAN_RBW_FACTOR == 1)
|
||||
{
|
||||
UI_drawCursor(x);
|
||||
@@ -1399,35 +1634,8 @@ void loop(void)
|
||||
// Because of the SCAN_RBW_FACTOR x is not a display coordinate anymore
|
||||
// x > STEPS on SCAN_RBW_FACTOR
|
||||
int display_x = x / SCAN_RBW_FACTOR;
|
||||
float step = (range * ((float)x / (STEPS * SCAN_RBW_FACTOR)));
|
||||
|
||||
r.current_frequency = r.fr_begin + step;
|
||||
LOG("setFrequency:%f\n", r.current_frequency);
|
||||
|
||||
#ifdef USING_SX1280PA
|
||||
state =
|
||||
radio.setFrequency(r.current_frequency); // 1280 doesn't have calibration
|
||||
radio.startReceive(RADIOLIB_SX128X_RX_TIMEOUT_INF);
|
||||
#elif USING_SX1276
|
||||
state = radio.setFrequency(freq);
|
||||
#else
|
||||
state = radio.setFrequency(r.current_frequency,
|
||||
true); // true = no calibration need here
|
||||
#endif
|
||||
int radio_error_count = 0;
|
||||
if (state != RADIOLIB_ERR_NONE)
|
||||
{
|
||||
display.drawString(0, 64 - 10,
|
||||
"E(" + String(state) +
|
||||
"):setFrequency:" + String(r.current_frequency));
|
||||
Serial.println("E(" + String(state) +
|
||||
"):setFrequency:" + String(r.current_frequency));
|
||||
display.display();
|
||||
delay(2);
|
||||
radio_error_count++;
|
||||
if (radio_error_count > 10)
|
||||
continue;
|
||||
}
|
||||
setFrequency(curr_freq / 1000.0);
|
||||
|
||||
LOG("Step:%d Freq: %f\n", x, r.current_frequency);
|
||||
// SpectralScan Method
|
||||
@@ -1461,7 +1669,23 @@ void loop(void)
|
||||
// Spectrum analyzer using getRSSI
|
||||
{
|
||||
LOG("METHOD RSSI");
|
||||
uint16_t max_rssi = r.rssiMethod(CONF_SAMPLES, result,
|
||||
|
||||
float (*g)(void *);
|
||||
samples = CONF_SAMPLES;
|
||||
|
||||
if (config.detection_strategy.equalsIgnoreCase("RSSI"))
|
||||
g = &getRSSI;
|
||||
else if (config.detection_strategy.equalsIgnoreCase("CAD"))
|
||||
{
|
||||
g = &getCAD;
|
||||
samples = min(
|
||||
1,
|
||||
CONF_SAMPLES); // TODO: do we need to support values other than 1
|
||||
}
|
||||
else
|
||||
g = &getRSSI;
|
||||
|
||||
uint16_t max_rssi = r.rssiMethod(g, &r, samples, result,
|
||||
RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE);
|
||||
|
||||
if (max_x_rssi[display_x] > max_rssi)
|
||||
@@ -1499,10 +1723,8 @@ void loop(void)
|
||||
max_rssi_x = detected_at;
|
||||
}
|
||||
|
||||
detected = event.detected.detected;
|
||||
detected_y[display_x] = false;
|
||||
|
||||
float rr = event.detected.rssi;
|
||||
r.drone_detection_level = drone_detection_level;
|
||||
|
||||
if (event.detected.trigger)
|
||||
@@ -1706,17 +1928,13 @@ void loop(void)
|
||||
|
||||
joy_btn_clicked = false;
|
||||
|
||||
#ifdef SERIAL_OUT
|
||||
if (config.print_profile_time)
|
||||
{
|
||||
#endif
|
||||
#ifdef PRINT_PROFILE_TIME
|
||||
loop_time = millis() - loop_start;
|
||||
Serial.printf("LOOP: %lld ms; SCAN: %lld ms;\n ", loop_time, scan_time);
|
||||
#endif
|
||||
#ifdef SERIAL_OUT
|
||||
}
|
||||
#endif
|
||||
|
||||
// No WiFi and BT Scan Without OSD
|
||||
#ifdef OSD_ENABLED
|
||||
@@ -1740,3 +1958,57 @@ void loop(void)
|
||||
sideBarCol = SIDEBAR_START_COL;
|
||||
#endif
|
||||
}
|
||||
|
||||
int16_t checkRadio(RadioComms &comms)
|
||||
{
|
||||
radioIsScan = false;
|
||||
int16_t status = comms.configureRadio();
|
||||
if (status != RADIOLIB_ERR_NONE)
|
||||
return status;
|
||||
|
||||
Message *msg = comms.receive(
|
||||
config.is_host
|
||||
? 2000
|
||||
: 200); // 200ms should be enough to receive 500 bytes at SF 7 and BW 500
|
||||
if (msg == NULL)
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
if (msg->type == SCAN_RESULT)
|
||||
{
|
||||
HostComms->send(*msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.printf("Received a message of unsupported type: %d\n", msg->type);
|
||||
}
|
||||
|
||||
delete msg;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
void reportScan(RadioComms &comms)
|
||||
{
|
||||
radioIsScan = false;
|
||||
int16_t status = comms.configureRadio();
|
||||
if (status != RADIOLIB_ERR_NONE)
|
||||
{
|
||||
Serial.printf("Failed to configure Radio: %d\n", status);
|
||||
return;
|
||||
}
|
||||
|
||||
Message m;
|
||||
m.type = SCAN_RESULT;
|
||||
m.payload.dump = frequency_scan_result.dump;
|
||||
status = comms.send(m);
|
||||
|
||||
m.payload.dump.sz = 0; // dump is shared, so should not delete underlying arrays
|
||||
|
||||
if (status != RADIOLIB_ERR_NONE)
|
||||
{
|
||||
Serial.printf("Failed to send scan result: %d\n", status);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
+10
-13
@@ -15,17 +15,14 @@
|
||||
#define SCALE_TEXT_TOP (HEIGHT + X_AXIS_WEIGHT + MAJOR_TICK_LENGTH)
|
||||
|
||||
// temporary dirty import ... to be solved durring upcoming refactoring
|
||||
extern unsigned int RANGE_PER_PAGE;
|
||||
extern uint64_t CONF_FREQ_BEGIN;
|
||||
extern uint64_t CONF_FREQ_END;
|
||||
extern unsigned int median_frequency;
|
||||
extern unsigned int drone_detected_frequency_start;
|
||||
extern unsigned int drone_detected_frequency_end;
|
||||
extern unsigned int ranges_count;
|
||||
extern int SCAN_RANGES[];
|
||||
extern unsigned int ranges_count;
|
||||
extern unsigned int iterations;
|
||||
extern unsigned int range_item;
|
||||
extern size_t scan_pages_sz;
|
||||
extern ScanPage *scan_pages;
|
||||
extern size_t scan_page;
|
||||
|
||||
extern uint64_t loop_time;
|
||||
|
||||
@@ -166,7 +163,7 @@ void StatusBar::draw()
|
||||
digitalWrite(LED, LOW);
|
||||
}
|
||||
|
||||
if (ranges_count == 0)
|
||||
if (scan_pages_sz == 1)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
display.setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
@@ -179,18 +176,18 @@ void StatusBar::draw()
|
||||
display.setTextAlignment(TEXT_ALIGN_RIGHT);
|
||||
display.drawString(pos_x + width, text_y, String(CONF_FREQ_END));
|
||||
}
|
||||
else if (ranges_count > 0)
|
||||
else if (scan_pages_sz > 1)
|
||||
{
|
||||
display.setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
display.drawString(pos_x, text_y,
|
||||
String(SCAN_RANGES[range_item] / 1000) + "-" +
|
||||
String(SCAN_RANGES[range_item] % 1000));
|
||||
if (range_item + 1 < iterations)
|
||||
String(scan_pages[scan_page].start_mhz) + "-" +
|
||||
String(scan_pages[scan_page].end_mhz));
|
||||
if (scan_page + 1 < scan_pages_sz)
|
||||
{
|
||||
display.setTextAlignment(TEXT_ALIGN_RIGHT);
|
||||
display.drawString(pos_x + width, text_y,
|
||||
String(SCAN_RANGES[range_item + 1] / 1000) + "-" +
|
||||
String(SCAN_RANGES[range_item + 1] % 1000));
|
||||
String(scan_pages[scan_page + 1].start_mhz) + "-" +
|
||||
String(scan_pages[scan_page + 1].end_mhz));
|
||||
}
|
||||
}
|
||||
ui_initialized = true;
|
||||
|
||||
+6
-6
@@ -7,21 +7,21 @@ struct TestScan : Scan
|
||||
{
|
||||
TestScan(float *ctx, int sz) : ctx(ctx), sz(sz), idx(0) {}
|
||||
|
||||
float getRSSI() override;
|
||||
|
||||
float *ctx;
|
||||
int sz;
|
||||
int idx;
|
||||
};
|
||||
|
||||
float TestScan::getRSSI()
|
||||
float getRSSI(void *param)
|
||||
{
|
||||
if (idx >= sz)
|
||||
TestScan *r = (TestScan *)param;
|
||||
|
||||
if (r->idx >= r->sz)
|
||||
{
|
||||
return -1000000;
|
||||
}
|
||||
|
||||
return ctx[idx++];
|
||||
return r->ctx[r->idx++];
|
||||
}
|
||||
|
||||
constexpr int test_sz = 13;
|
||||
@@ -35,7 +35,7 @@ void test_rssi(void)
|
||||
|
||||
TestScan t = TestScan(inputs, inputs_sz);
|
||||
|
||||
uint16_t r = t.rssiMethod(inputs_sz, samples, test_sz);
|
||||
uint16_t r = t.rssiMethod(getRSSI, &t, inputs_sz, samples, test_sz);
|
||||
|
||||
uint16_t expect[test_sz] = {20, 50, 55, 60, 0, 70, 75, 80, 0, 90, 0, 100, 110};
|
||||
|
||||
|
||||
+51
-39
@@ -50,8 +50,11 @@ void test_push()
|
||||
|
||||
m.reset(0, 1);
|
||||
uint64_t i = 0;
|
||||
size_t update_to = 0;
|
||||
for (; i < 10; i++)
|
||||
m.updateModel(i, 0, 1);
|
||||
update_to = max(update_to, m.updateModel(i, 0, 1));
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(6, update_to);
|
||||
|
||||
r = m.toString();
|
||||
TEST_ASSERT_EQUAL_STRING("w:1 b:34 "
|
||||
@@ -92,8 +95,11 @@ void test_push()
|
||||
r);
|
||||
delete r;
|
||||
|
||||
update_to = 0;
|
||||
for (; i < 100; i += 10)
|
||||
m.updateModel(i, 0, 1);
|
||||
update_to = max(update_to, m.updateModel(i, 0, 1));
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(13, update_to);
|
||||
|
||||
r = m.toString();
|
||||
TEST_ASSERT_EQUAL_STRING("w:1 b:34 "
|
||||
@@ -134,8 +140,11 @@ void test_push()
|
||||
r);
|
||||
delete r;
|
||||
|
||||
update_to = 0;
|
||||
for (; i < 10000; i++)
|
||||
m.updateModel(i, 0, 1);
|
||||
update_to = max(update_to, m.updateModel(i, 0, 1));
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(34, update_to);
|
||||
|
||||
r = m.toString();
|
||||
TEST_ASSERT_EQUAL_STRING("w:1 b:34 "
|
||||
@@ -176,45 +185,48 @@ void test_push()
|
||||
r);
|
||||
delete r;
|
||||
|
||||
for (; i < 5000; i++)
|
||||
m.updateModel(i, 0, 1);
|
||||
update_to = 0;
|
||||
for (; i < 15000; i++)
|
||||
update_to = max(update_to, m.updateModel(i, 0, 1));
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(34, update_to);
|
||||
|
||||
r = m.toString();
|
||||
TEST_ASSERT_EQUAL_STRING("w:1 b:34 "
|
||||
"[dt:1 t:9999 [ c:1 e:1 ]"
|
||||
"dt:1 t:9998 [ c:1 e:1 ]"
|
||||
"dt:1 t:9997 [ c:1 e:1 ]"
|
||||
"dt:1 t:9996 [ c:1 e:1 ]"
|
||||
"dt:1 t:9995 [ c:1 e:1 ]"
|
||||
"dt:5 t:9995 [ c:4 e:4 ]"
|
||||
"dt:5 t:9990 [ c:5 e:5 ]"
|
||||
"dt:5 t:9985 [ c:5 e:5 ]"
|
||||
"dt:15 t:9990 [ c:5 e:5 ]"
|
||||
"dt:15 t:9975 [ c:15 e:15 ]"
|
||||
"dt:15 t:9960 [ c:15 e:15 ]"
|
||||
"dt:15 t:9945 [ c:15 e:15 ]"
|
||||
"dt:60 t:9960 [ c:30 e:30 ]"
|
||||
"dt:60 t:9900 [ c:60 e:60 ]"
|
||||
"dt:60 t:9840 [ c:60 e:60 ]"
|
||||
"dt:60 t:9780 [ c:60 e:60 ]"
|
||||
"dt:60 t:9720 [ c:60 e:60 ]"
|
||||
"dt:60 t:9660 [ c:60 e:60 ]"
|
||||
"dt:60 t:9600 [ c:60 e:60 ]"
|
||||
"dt:60 t:9540 [ c:60 e:60 ]"
|
||||
"dt:60 t:9480 [ c:60 e:60 ]"
|
||||
"dt:60 t:9420 [ c:60 e:60 ]"
|
||||
"dt:60 t:9360 [ c:60 e:60 ]"
|
||||
"dt:60 t:9300 [ c:60 e:60 ]"
|
||||
"dt:60 t:9240 [ c:60 e:60 ]"
|
||||
"dt:60 t:9180 [ c:60 e:60 ]"
|
||||
"dt:60 t:9120 [ c:60 e:60 ]"
|
||||
"dt:900 t:9900 [ c:60 e:60 ]"
|
||||
"dt:900 t:9000 [ c:900 e:900 ]"
|
||||
"dt:900 t:8100 [ c:900 e:900 ]"
|
||||
"dt:900 t:7200 [ c:900 e:900 ]"
|
||||
"dt:3600 t:7200 [ c:2700 e:2700 ]"
|
||||
"dt:3600 t:3600 [ c:3520 e:3520 ]"
|
||||
"dt:3600 t:3600 [ c:0 e:0 ] ]",
|
||||
"[dt:1 t:14999 [ c:1 e:1 ]"
|
||||
"dt:1 t:14998 [ c:1 e:1 ]"
|
||||
"dt:1 t:14997 [ c:1 e:1 ]"
|
||||
"dt:1 t:14996 [ c:1 e:1 ]"
|
||||
"dt:1 t:14995 [ c:1 e:1 ]"
|
||||
"dt:5 t:14995 [ c:4 e:4 ]"
|
||||
"dt:5 t:14990 [ c:5 e:5 ]"
|
||||
"dt:5 t:14985 [ c:5 e:5 ]"
|
||||
"dt:15 t:14985 [ c:10 e:10 ]"
|
||||
"dt:15 t:14970 [ c:15 e:15 ]"
|
||||
"dt:15 t:14955 [ c:15 e:15 ]"
|
||||
"dt:15 t:14940 [ c:15 e:15 ]"
|
||||
"dt:60 t:14940 [ c:45 e:45 ]"
|
||||
"dt:60 t:14880 [ c:60 e:60 ]"
|
||||
"dt:60 t:14820 [ c:60 e:60 ]"
|
||||
"dt:60 t:14760 [ c:60 e:60 ]"
|
||||
"dt:60 t:14700 [ c:60 e:60 ]"
|
||||
"dt:60 t:14640 [ c:60 e:60 ]"
|
||||
"dt:60 t:14580 [ c:60 e:60 ]"
|
||||
"dt:60 t:14520 [ c:60 e:60 ]"
|
||||
"dt:60 t:14460 [ c:60 e:60 ]"
|
||||
"dt:60 t:14400 [ c:60 e:60 ]"
|
||||
"dt:60 t:14340 [ c:60 e:60 ]"
|
||||
"dt:60 t:14280 [ c:60 e:60 ]"
|
||||
"dt:60 t:14220 [ c:60 e:60 ]"
|
||||
"dt:60 t:14160 [ c:60 e:60 ]"
|
||||
"dt:60 t:14100 [ c:60 e:60 ]"
|
||||
"dt:900 t:14400 [ c:540 e:540 ]"
|
||||
"dt:900 t:13500 [ c:900 e:900 ]"
|
||||
"dt:900 t:12600 [ c:900 e:900 ]"
|
||||
"dt:900 t:11700 [ c:900 e:900 ]"
|
||||
"dt:3600 t:10800 [ c:3600 e:3600 ]"
|
||||
"dt:3600 t:7200 [ c:3600 e:3600 ]"
|
||||
"dt:3600 t:3600 [ c:3520 e:3520 ] ]",
|
||||
r);
|
||||
delete r;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user