mirror of
https://github.com/Genaker/LoraSA.git
synced 2026-08-05 16:32:51 +02:00
Add interaction over Serial0
This commit is contained in:
@@ -0,0 +1,226 @@
|
||||
#include "comms.h"
|
||||
#include <config.h>
|
||||
|
||||
#include <HardwareSerial.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
|
||||
Comms *Comms0;
|
||||
|
||||
TaskHandle_t monitorSerial = NULL;
|
||||
|
||||
void _onReceive0();
|
||||
|
||||
void monitorSerialTask(void *)
|
||||
{
|
||||
Serial.println("Spawned a task to monitor Serial.available()");
|
||||
for (;;)
|
||||
{
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
_onReceive0();
|
||||
}
|
||||
}
|
||||
|
||||
void _onReceive0()
|
||||
{
|
||||
if (Comms0 == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Comms0->_onReceive();
|
||||
}
|
||||
|
||||
bool Comms::initComms(Config &c)
|
||||
{
|
||||
if (c.listen_on_usb.equalsIgnoreCase("readline"))
|
||||
{
|
||||
// comms using readline plaintext protocol
|
||||
Comms0 = new ReadlineComms(Serial);
|
||||
// Serial.onEvent(_onUsbEvent0);
|
||||
// Serial.begin();
|
||||
xTaskCreate(monitorSerialTask, "CHECK_SERIAL_PROCESS", 2048, NULL, 1,
|
||||
&monitorSerial);
|
||||
|
||||
Serial.println("Initialized communications on Serial using readline protocol");
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (c.listen_on_serial0.equalsIgnoreCase("readline"))
|
||||
{
|
||||
// comms using readline plaintext protocol
|
||||
Comms0 = new ReadlineComms(Serial0);
|
||||
Serial0.onReceive(_onReceive0, false);
|
||||
|
||||
Serial.println("Initialized communications on Serial0 using readline protocol");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (c.listen_on_serial0.equalsIgnoreCase("none"))
|
||||
{
|
||||
Comms0 = new NoopComms();
|
||||
|
||||
Serial.println("Configured none - Initialized no communications");
|
||||
return false;
|
||||
}
|
||||
|
||||
Comms0 = new NoopComms();
|
||||
Serial.println("Nothing is configured - initialized no communications");
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t Comms::available() { return received_pos; }
|
||||
|
||||
#define _RECEIVED_BUF_INCREMENT 10
|
||||
#define _MAX_RECEIVED_SZ 100
|
||||
bool Comms::_messageArrived(Message &m)
|
||||
{
|
||||
if (received_pos == received_sz)
|
||||
{
|
||||
// TODO: if received_sz exceeds a configurable bound, complain and drop the
|
||||
// message on the floor
|
||||
if (received_sz >= _MAX_RECEIVED_SZ)
|
||||
{
|
||||
Serial.println("Receive buffer backlog too large; dropping the message");
|
||||
return false;
|
||||
}
|
||||
Message **m = new Message *[received_sz + _RECEIVED_BUF_INCREMENT];
|
||||
if (received_sz > 0)
|
||||
{
|
||||
memcpy(m, received, received_sz * sizeof(Message *));
|
||||
delete[] received;
|
||||
}
|
||||
received = m;
|
||||
received_sz += _RECEIVED_BUF_INCREMENT;
|
||||
}
|
||||
|
||||
received[received_pos] = &m;
|
||||
received_pos++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Message *Comms::receive()
|
||||
{
|
||||
if (received_pos == 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Message *m = received[0];
|
||||
received_pos--;
|
||||
|
||||
memmove(received, received + 1, received_pos * sizeof(Message *));
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
Message *_parsePacket(String);
|
||||
String _scan_str(ScanTask &);
|
||||
String _scan_result_str(ScanTaskResult &);
|
||||
|
||||
void ReadlineComms::_onReceive()
|
||||
{
|
||||
while (serial.available() > 0)
|
||||
{
|
||||
partialPacket = partialPacket + serial.readString();
|
||||
int i = partialPacket.indexOf('\n');
|
||||
while (i >= 0)
|
||||
{
|
||||
Message *m = _parsePacket(partialPacket.substring(0, i));
|
||||
if (m != NULL)
|
||||
{
|
||||
if (!_messageArrived(*m))
|
||||
{
|
||||
delete m;
|
||||
}
|
||||
}
|
||||
partialPacket = partialPacket.substring(i + 1);
|
||||
i = partialPacket.indexOf('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ReadlineComms::send(Message &m)
|
||||
{
|
||||
String p;
|
||||
|
||||
switch (m.type)
|
||||
{
|
||||
case MessageType::SCAN:
|
||||
p = _scan_str(m.payload.scan);
|
||||
break;
|
||||
case MessageType::SCAN_RESULT:
|
||||
p = _scan_result_str(m.payload.dump);
|
||||
break;
|
||||
}
|
||||
serial.println(p);
|
||||
return true;
|
||||
}
|
||||
|
||||
int64_t _intParam(String &p, int64_t default_v)
|
||||
{
|
||||
p.trim();
|
||||
int i = p.indexOf(' ');
|
||||
if (i < 0)
|
||||
{
|
||||
i = p.length();
|
||||
}
|
||||
|
||||
int64_t v = p.substring(0, i).toInt();
|
||||
p = p.substring(i + 1);
|
||||
return v;
|
||||
}
|
||||
|
||||
Message *_parsePacket(String p)
|
||||
{
|
||||
p.trim();
|
||||
if (p.length() == 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
String cmd = p;
|
||||
int i = p.indexOf(' ');
|
||||
if (i < 0)
|
||||
{
|
||||
p = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
cmd = p.substring(0, i);
|
||||
p = p.substring(i + 1);
|
||||
p.trim();
|
||||
}
|
||||
|
||||
if (cmd.equalsIgnoreCase("scan"))
|
||||
{
|
||||
Message *m = new Message();
|
||||
m->type = MessageType::SCAN;
|
||||
m->payload.scan.count = _intParam(p, 1);
|
||||
m->payload.scan.delay = _intParam(p, -1);
|
||||
return m;
|
||||
}
|
||||
|
||||
Serial.println("ignoring unknown message " + p);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
String _scan_str(ScanTask &t)
|
||||
{
|
||||
return "SCAN " + String(t.count) + " " + String(t.delay);
|
||||
}
|
||||
|
||||
String _scan_result_str(ScanTaskResult &r)
|
||||
{
|
||||
String p = "SCAN_RESULT " + String(r.sz) + " [";
|
||||
|
||||
for (int i = 0; i < r.sz; i++)
|
||||
{
|
||||
p += (i == 0 ? "(" : ", (") + String(r.freqs_khz[i]) + ", " + String(r.rssis[i]) +
|
||||
")";
|
||||
}
|
||||
|
||||
return p + " ]";
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
#ifndef __COMMS_H
|
||||
#define __COMMS_H
|
||||
|
||||
#include <HardwareSerial.h>
|
||||
#include <config.h>
|
||||
|
||||
enum MessageType
|
||||
{
|
||||
SCAN = 0,
|
||||
SCAN_RESULT,
|
||||
_MAX_MESSAGE_TYPE = SCAN_RESULT
|
||||
};
|
||||
|
||||
struct ScanTask
|
||||
{
|
||||
int64_t count;
|
||||
int64_t delay;
|
||||
};
|
||||
|
||||
struct ScanTaskResult
|
||||
{
|
||||
size_t sz;
|
||||
uint32_t *freqs_khz;
|
||||
int16_t *rssis;
|
||||
};
|
||||
|
||||
struct Message
|
||||
{
|
||||
MessageType type;
|
||||
union
|
||||
{
|
||||
ScanTask scan;
|
||||
ScanTaskResult dump;
|
||||
} payload;
|
||||
};
|
||||
|
||||
struct Comms
|
||||
{
|
||||
Stream &serial;
|
||||
Message **received;
|
||||
size_t received_sz;
|
||||
size_t received_pos;
|
||||
|
||||
Comms(Stream &serial)
|
||||
: serial(serial), received(NULL), received_sz(0), received_pos(0) {};
|
||||
|
||||
virtual size_t available();
|
||||
virtual bool send(Message &) = 0;
|
||||
virtual Message *receive();
|
||||
|
||||
virtual void _onReceive() = 0;
|
||||
virtual bool _messageArrived(Message &);
|
||||
|
||||
static bool initComms(Config &);
|
||||
};
|
||||
|
||||
struct NoopComms : Comms
|
||||
{
|
||||
NoopComms() : Comms(Serial0) {};
|
||||
|
||||
virtual bool send(Message &) { return true; };
|
||||
virtual void _onReceive() {};
|
||||
};
|
||||
|
||||
struct ReadlineComms : Comms
|
||||
{
|
||||
String partialPacket;
|
||||
|
||||
ReadlineComms(Stream &serial) : Comms(serial), partialPacket("") {};
|
||||
|
||||
virtual bool send(Message &) override;
|
||||
|
||||
virtual void _onReceive() override;
|
||||
};
|
||||
|
||||
extern Comms *Comms0;
|
||||
|
||||
#endif
|
||||
@@ -103,6 +103,19 @@ Config Config::init()
|
||||
if (r.key.equalsIgnoreCase("log_data_json_interval"))
|
||||
{
|
||||
c.log_data_json_interval = r.value.toInt();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (r.key.equalsIgnoreCase("listen_on_serial0"))
|
||||
{
|
||||
c.listen_on_serial0 = r.value;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (r.key.equalsIgnoreCase("listen_on_usb"))
|
||||
{
|
||||
c.listen_on_serial0 = r.value;
|
||||
continue;
|
||||
}
|
||||
|
||||
Serial.printf("Unknown key '%s' will be ignored\n", r.key);
|
||||
@@ -122,6 +135,8 @@ bool Config::write_config(const char *path)
|
||||
|
||||
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.close();
|
||||
return true;
|
||||
|
||||
+4
-1
@@ -9,10 +9,13 @@ struct Config
|
||||
bool create_missing_config;
|
||||
bool print_profile_time;
|
||||
int log_data_json_interval;
|
||||
String listen_on_serial0;
|
||||
String listen_on_usb;
|
||||
|
||||
Config()
|
||||
: create_missing_config(CREATE_MISSING_CONFIG), print_profile_time(false),
|
||||
log_data_json_interval(1000) {};
|
||||
log_data_json_interval(1000), listen_on_serial0(String("none")),
|
||||
listen_on_usb("readline") {};
|
||||
bool write_config(const char *path);
|
||||
|
||||
static Config init();
|
||||
|
||||
+7
-3
@@ -45,15 +45,19 @@ struct Scan
|
||||
bool animated;
|
||||
float trigger_level;
|
||||
|
||||
bool comms_initialized;
|
||||
|
||||
Listener **eventListeners[(size_t)EventType::_MAX_EVENT_TYPE + 1];
|
||||
size_t listener_count[(size_t)EventType::_MAX_EVENT_TYPE + 1];
|
||||
|
||||
Scan()
|
||||
: epoch(0), current_frequency(0), fr_begin(0), fr_end(0),
|
||||
drone_detection_level(0), sound_on(false), led_flag(false), detection_count(0),
|
||||
animated(false), trigger_level(0), listener_count{
|
||||
0,
|
||||
} {};
|
||||
animated(false), trigger_level(0),
|
||||
listener_count{
|
||||
0,
|
||||
},
|
||||
comms_initialized(false) {};
|
||||
|
||||
virtual float getRSSI() = 0;
|
||||
|
||||
|
||||
+111
-2
@@ -42,6 +42,7 @@
|
||||
#define RADIOLIB_GODMODE (1)
|
||||
|
||||
#include <charts.h>
|
||||
#include <comms.h>
|
||||
#include <config.h>
|
||||
#include <events.h>
|
||||
#include <scan.h>
|
||||
@@ -475,14 +476,50 @@ struct frequency_scan_result
|
||||
uint64_t end;
|
||||
uint64_t last_epoch;
|
||||
int16_t rssi; // deliberately not a float; floats can pin task to wrong core forever
|
||||
|
||||
ScanTaskResult dump;
|
||||
size_t readings_sz;
|
||||
} frequency_scan_result;
|
||||
|
||||
TaskHandle_t logToSerial = NULL;
|
||||
TaskHandle_t dumpToComms = NULL;
|
||||
|
||||
void eventListenerForMSP(void *arg, Event &e)
|
||||
void eventListenerForReport(void *arg, Event &e)
|
||||
{
|
||||
if (e.type == EventType::DETECTED)
|
||||
{
|
||||
if (e.epoch != frequency_scan_result.last_epoch)
|
||||
{
|
||||
frequency_scan_result.dump.sz = 0;
|
||||
}
|
||||
|
||||
if (frequency_scan_result.dump.sz >= frequency_scan_result.readings_sz)
|
||||
{
|
||||
size_t old_sz = frequency_scan_result.readings_sz;
|
||||
frequency_scan_result.readings_sz = frequency_scan_result.dump.sz + 1;
|
||||
uint32_t *f = new uint32_t[frequency_scan_result.readings_sz];
|
||||
int16_t *r = new int16_t[frequency_scan_result.readings_sz];
|
||||
|
||||
if (old_sz > 0)
|
||||
{
|
||||
memcpy(f, frequency_scan_result.dump.freqs_khz,
|
||||
old_sz * sizeof(uint32_t));
|
||||
memcpy(r, frequency_scan_result.dump.rssis, old_sz * sizeof(int16_t));
|
||||
|
||||
delete[] frequency_scan_result.dump.freqs_khz;
|
||||
delete[] frequency_scan_result.dump.rssis;
|
||||
}
|
||||
|
||||
frequency_scan_result.dump.freqs_khz = f;
|
||||
frequency_scan_result.dump.rssis = r;
|
||||
}
|
||||
|
||||
frequency_scan_result.dump.freqs_khz[frequency_scan_result.dump.sz] =
|
||||
e.detected.freq * 1000; // convert to kHz
|
||||
frequency_scan_result.dump.rssis[frequency_scan_result.dump.sz] =
|
||||
max(e.detected.rssi, -999.0f);
|
||||
frequency_scan_result.dump.sz++;
|
||||
|
||||
if (e.epoch != frequency_scan_result.last_epoch ||
|
||||
e.detected.rssi > frequency_scan_result.rssi)
|
||||
{
|
||||
@@ -500,10 +537,50 @@ void eventListenerForMSP(void *arg, Event &e)
|
||||
{
|
||||
xTaskNotifyGive(logToSerial);
|
||||
}
|
||||
|
||||
if (dumpToComms != NULL)
|
||||
{
|
||||
xTaskNotifyGive(dumpToComms);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ScanTask report_scans = ScanTask{
|
||||
count : 0, // 0 => report none; < 0 => report forever; > 0 => report that many
|
||||
delay : 0 // 0 => as and when it happens; > 0 => at least once that many ms
|
||||
};
|
||||
|
||||
void dumpToCommsTask(void *parameter)
|
||||
{
|
||||
uint64_t last_epoch = frequency_scan_result.last_epoch;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
int64_t delay = report_scans.delay;
|
||||
if (delay == 0)
|
||||
{
|
||||
delay = (1 << 63) - 1;
|
||||
}
|
||||
|
||||
ulTaskNotifyTake(true, pdMS_TO_TICKS(delay));
|
||||
if (report_scans.count == 0 || frequency_scan_result.last_epoch == last_epoch)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (report_scans.count > 0)
|
||||
{
|
||||
report_scans.count--;
|
||||
}
|
||||
|
||||
Message m;
|
||||
m.type = MessageType::SCAN_RESULT;
|
||||
m.payload.dump = frequency_scan_result.dump;
|
||||
Comms0->send(m);
|
||||
}
|
||||
}
|
||||
|
||||
void logToSerialTask(void *parameter)
|
||||
{
|
||||
#ifdef HELTEC
|
||||
@@ -579,6 +656,15 @@ void setup(void)
|
||||
wf_start = millis();
|
||||
|
||||
config = Config::init();
|
||||
r.comms_initialized = Comms::initComms(config);
|
||||
if (r.comms_initialized)
|
||||
{
|
||||
Serial.println("Comms initialized fine");
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println("Comms did not initialize");
|
||||
}
|
||||
|
||||
pinMode(LED, OUTPUT);
|
||||
pinMode(BUZZER_PIN, OUTPUT);
|
||||
@@ -705,6 +791,7 @@ void setup(void)
|
||||
#ifdef LOG_DATA_JSON
|
||||
xTaskCreate(logToSerialTask, "LOG_DATA_JSON", 2048, NULL, 1, &logToSerial);
|
||||
#endif
|
||||
xTaskCreate(dumpToCommsTask, "DUMP_RESPONSE_PROCESS", 2048, NULL, 1, &dumpToComms);
|
||||
|
||||
r.trigger_level = TRIGGER_LEVEL;
|
||||
stacked.reset(0, 0, display.width(), display.height());
|
||||
@@ -741,7 +828,9 @@ void setup(void)
|
||||
r.addEventListener(DETECTED, drone_sound_alarm, &r);
|
||||
r.addEventListener(SCAN_TASK_COMPLETE, stacked);
|
||||
|
||||
r.addEventListener(ALL_EVENTS, eventListenerForMSP, NULL);
|
||||
frequency_scan_result.readings_sz = 0;
|
||||
frequency_scan_result.dump.sz = 0;
|
||||
r.addEventListener(ALL_EVENTS, eventListenerForReport, NULL);
|
||||
|
||||
#ifdef UPTIME_CLOCK
|
||||
uptime = new UptimeClock(display, millis());
|
||||
@@ -931,6 +1020,24 @@ void check_ranges()
|
||||
}
|
||||
}
|
||||
|
||||
void checkComms()
|
||||
{
|
||||
while (Comms0->available() > 0)
|
||||
{
|
||||
Message *m = Comms0->receive();
|
||||
if (m == NULL)
|
||||
continue;
|
||||
|
||||
switch (m->type)
|
||||
{
|
||||
case MessageType::SCAN:
|
||||
report_scans = m->payload.scan;
|
||||
break;
|
||||
}
|
||||
delete m;
|
||||
}
|
||||
}
|
||||
|
||||
// MAX Frequency RSSI BIN value of the samples
|
||||
int max_rssi_x = 999;
|
||||
|
||||
@@ -942,6 +1049,8 @@ void loop(void)
|
||||
drone_detected_frequency_start = 0;
|
||||
ranges_count = 0;
|
||||
|
||||
checkComms();
|
||||
|
||||
// reset scan time
|
||||
if (config.print_profile_time)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user