|
|
@ -2,27 +2,49 @@ |
|
|
|
#include <SPI.h> |
|
|
|
#include <SPI.h> |
|
|
|
#include "Utilities.h" |
|
|
|
#include "Utilities.h" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FIFOBuffer serialFIFO; |
|
|
|
|
|
|
|
uint8_t serialBuffer[CONFIG_UART_BUFFER_SIZE]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FIFOBuffer16 packet_starts; |
|
|
|
|
|
|
|
size_t packet_starts_buf[CONFIG_QUEUE_MAX_LENGTH+1]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FIFOBuffer16 packet_lengths; |
|
|
|
|
|
|
|
size_t packet_lengths_buf[CONFIG_QUEUE_MAX_LENGTH+1]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint8_t packet_queue[CONFIG_QUEUE_SIZE]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
volatile uint8_t queue_height = 0; |
|
|
|
|
|
|
|
volatile size_t queued_bytes = 0; |
|
|
|
|
|
|
|
volatile size_t queue_cursor = 0; |
|
|
|
|
|
|
|
volatile size_t current_packet_start = 0; |
|
|
|
|
|
|
|
|
|
|
|
void setup() { |
|
|
|
void setup() { |
|
|
|
// Seed the PRNG
|
|
|
|
// Seed the PRNG
|
|
|
|
randomSeed(analogRead(0)); |
|
|
|
randomSeed(analogRead(0)); |
|
|
|
|
|
|
|
|
|
|
|
// Initialise serial communication
|
|
|
|
// Initialise serial communication
|
|
|
|
|
|
|
|
memset(serialBuffer, 0, sizeof(serialBuffer)); |
|
|
|
|
|
|
|
fifo_init(&serialFIFO, serialBuffer, sizeof(serialBuffer)); |
|
|
|
|
|
|
|
|
|
|
|
Serial.begin(serial_baudrate); |
|
|
|
Serial.begin(serial_baudrate); |
|
|
|
while (!Serial); |
|
|
|
while (!Serial); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
serial_timer_init(); |
|
|
|
|
|
|
|
|
|
|
|
// Configure input and output pins
|
|
|
|
// Configure input and output pins
|
|
|
|
pinMode(pin_led_rx, OUTPUT); |
|
|
|
pinMode(pin_led_rx, OUTPUT); |
|
|
|
pinMode(pin_led_tx, OUTPUT); |
|
|
|
pinMode(pin_led_tx, OUTPUT); |
|
|
|
|
|
|
|
|
|
|
|
// Initialise buffers
|
|
|
|
// Initialise buffers
|
|
|
|
memset(pbuf, 0, sizeof(pbuf)); |
|
|
|
memset(pbuf, 0, sizeof(pbuf)); |
|
|
|
memset(sbuf, 0, sizeof(sbuf)); |
|
|
|
|
|
|
|
memset(cbuf, 0, sizeof(cbuf)); |
|
|
|
memset(cbuf, 0, sizeof(cbuf)); |
|
|
|
|
|
|
|
|
|
|
|
#if QUEUE_SIZE > 0 |
|
|
|
memset(packet_queue, 0, sizeof(packet_queue)); |
|
|
|
memset(qbuf, 0, sizeof(qbuf)); |
|
|
|
memset(packet_starts_buf, 0, sizeof(packet_starts)); |
|
|
|
memset(queued_lengths, 0, sizeof(queued_lengths)); |
|
|
|
memset(packet_lengths_buf, 0, sizeof(packet_lengths)); |
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
fifo16_init(&packet_starts, packet_starts_buf, sizeof(packet_starts_buf)); |
|
|
|
|
|
|
|
fifo16_init(&packet_lengths, packet_lengths_buf, sizeof(packet_lengths_buf)); |
|
|
|
|
|
|
|
|
|
|
|
// Set chip select, reset and interrupt
|
|
|
|
// Set chip select, reset and interrupt
|
|
|
|
// pins for the LoRa module
|
|
|
|
// pins for the LoRa module
|
|
|
@ -188,66 +210,37 @@ void receiveCallback(int packet_size) { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool outboundReady() { |
|
|
|
|
|
|
|
#if QUEUE_SIZE > 0 |
|
|
|
|
|
|
|
if (queue_head != queue_tail) { |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
#else |
|
|
|
|
|
|
|
return outbound_ready; |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool queueFull() { |
|
|
|
bool queueFull() { |
|
|
|
size_t new_queue_head = (queue_head+1)%QUEUE_BUF_SIZE; |
|
|
|
return (queue_height < CONFIG_QUEUE_MAX_LENGTH && queued_bytes < CONFIG_QUEUE_SIZE); |
|
|
|
if (new_queue_head == queue_tail) { |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void enqueuePacket(size_t length) { |
|
|
|
volatile bool queue_flushing = false; |
|
|
|
size_t new_queue_head = (queue_head+1)%QUEUE_BUF_SIZE; |
|
|
|
void flushQueue(void) { |
|
|
|
if (new_queue_head != queue_tail) { |
|
|
|
if (!queue_flushing) { |
|
|
|
queued_lengths[queue_head] = length; |
|
|
|
queue_flushing = true; |
|
|
|
size_t insert_addr = queue_head * MTU; |
|
|
|
|
|
|
|
for (int i = 0; i < length; i++) { |
|
|
|
|
|
|
|
qbuf[insert_addr+i] = sbuf[i]; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
queue_head = new_queue_head; |
|
|
|
|
|
|
|
if (!queueFull()) { |
|
|
|
|
|
|
|
kiss_indicate_ready(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
kiss_indicate_error(ERROR_QUEUE_FULL); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#if QUEUE_SIZE > 0 |
|
|
|
size_t processed = 0; |
|
|
|
void processQueue() { |
|
|
|
for (size_t n = 0; n < queue_height; n++) { |
|
|
|
size_t fetch_address = queue_tail*MTU; |
|
|
|
size_t start = fifo16_pop_locked(&packet_starts); |
|
|
|
size_t fetch_length = queued_lengths[queue_tail]; |
|
|
|
size_t length = fifo16_pop_locked(&packet_lengths); |
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < fetch_length; i++) { |
|
|
|
if (length >= MIN_L) { |
|
|
|
tbuf[i] = qbuf[fetch_address+i]; |
|
|
|
for (size_t i = 0; i < length; i++) { |
|
|
|
qbuf[fetch_address+i] = 0x00; |
|
|
|
size_t pos = (start+i)%CONFIG_QUEUE_SIZE; |
|
|
|
|
|
|
|
tbuf[i] = packet_queue[pos]; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
queued_lengths[queue_tail] = 0; |
|
|
|
transmit(length); |
|
|
|
|
|
|
|
processed++; |
|
|
|
queue_tail = ++queue_tail%QUEUE_BUF_SIZE; |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
transmit(fetch_length); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (!queueFull()) { |
|
|
|
queue_height = 0; |
|
|
|
|
|
|
|
queued_bytes = 0; |
|
|
|
|
|
|
|
queue_flushing = false; |
|
|
|
kiss_indicate_ready(); |
|
|
|
kiss_indicate_ready(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void transmit(size_t size) { |
|
|
|
void transmit(size_t size) { |
|
|
|
if (radio_online) { |
|
|
|
if (radio_online) { |
|
|
@ -264,11 +257,7 @@ void transmit(size_t size) { |
|
|
|
LoRa.write(header); written++; |
|
|
|
LoRa.write(header); written++; |
|
|
|
|
|
|
|
|
|
|
|
for (size_t i; i < size; i++) { |
|
|
|
for (size_t i; i < size; i++) { |
|
|
|
#if QUEUE_SIZE > 0 |
|
|
|
|
|
|
|
LoRa.write(tbuf[i]);
|
|
|
|
LoRa.write(tbuf[i]);
|
|
|
|
#else |
|
|
|
|
|
|
|
LoRa.write(sbuf[i]); |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
written++; |
|
|
|
written++; |
|
|
|
|
|
|
|
|
|
|
@ -298,11 +287,7 @@ void transmit(size_t size) { |
|
|
|
|
|
|
|
|
|
|
|
LoRa.beginPacket(); |
|
|
|
LoRa.beginPacket(); |
|
|
|
for (size_t i; i < size; i++) { |
|
|
|
for (size_t i; i < size; i++) { |
|
|
|
#if QUEUE_SIZE > 0 |
|
|
|
|
|
|
|
LoRa.write(tbuf[i]); |
|
|
|
LoRa.write(tbuf[i]); |
|
|
|
#else |
|
|
|
|
|
|
|
LoRa.write(sbuf[i]); |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
written++; |
|
|
|
written++; |
|
|
|
} |
|
|
|
} |
|
|
@ -315,26 +300,35 @@ void transmit(size_t size) { |
|
|
|
kiss_indicate_error(ERROR_TXFAILED); |
|
|
|
kiss_indicate_error(ERROR_TXFAILED); |
|
|
|
led_indicate_error(5); |
|
|
|
led_indicate_error(5); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#if QUEUE_SIZE == 0 |
|
|
|
|
|
|
|
if (FLOW_CONTROL_ENABLED) |
|
|
|
|
|
|
|
kiss_indicate_ready(); |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void serialCallback(uint8_t sbyte) { |
|
|
|
void serialCallback(uint8_t sbyte) { |
|
|
|
if (IN_FRAME && sbyte == FEND && command == CMD_DATA) { |
|
|
|
if (IN_FRAME && sbyte == FEND && command == CMD_DATA) { |
|
|
|
IN_FRAME = false; |
|
|
|
IN_FRAME = false; |
|
|
|
|
|
|
|
|
|
|
|
if (QUEUE_SIZE == 0) { |
|
|
|
if (queue_height < CONFIG_QUEUE_MAX_LENGTH && queued_bytes < CONFIG_QUEUE_SIZE) { |
|
|
|
if (outbound_ready) { |
|
|
|
size_t s = current_packet_start; |
|
|
|
kiss_indicate_error(ERROR_QUEUE_FULL); |
|
|
|
size_t e = queue_cursor-1; if (e == -1) e = CONFIG_QUEUE_SIZE-1; |
|
|
|
|
|
|
|
size_t l; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (s != e) { |
|
|
|
|
|
|
|
l = (s < e) ? e - s + 1 : CONFIG_QUEUE_SIZE - s + e + 1; |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
outbound_ready = true; |
|
|
|
l = 1; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (l >= MIN_L) { |
|
|
|
|
|
|
|
queue_height++; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fifo16_push_locked(&packet_starts, s); |
|
|
|
|
|
|
|
fifo16_push_locked(&packet_lengths, l); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
current_packet_start = queue_cursor; |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
|
|
|
|
enqueuePacket(frame_len); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!queueFull()) kiss_indicate_ready();
|
|
|
|
|
|
|
|
|
|
|
|
} else if (sbyte == FEND) { |
|
|
|
} else if (sbyte == FEND) { |
|
|
|
IN_FRAME = true; |
|
|
|
IN_FRAME = true; |
|
|
|
command = CMD_UNKNOWN; |
|
|
|
command = CMD_UNKNOWN; |
|
|
@ -352,7 +346,11 @@ void serialCallback(uint8_t sbyte) { |
|
|
|
if (sbyte == TFESC) sbyte = FESC; |
|
|
|
if (sbyte == TFESC) sbyte = FESC; |
|
|
|
ESCAPE = false; |
|
|
|
ESCAPE = false; |
|
|
|
} |
|
|
|
} |
|
|
|
sbuf[frame_len++] = sbyte; |
|
|
|
if (queue_height < CONFIG_QUEUE_MAX_LENGTH && queued_bytes < CONFIG_QUEUE_SIZE) { |
|
|
|
|
|
|
|
queued_bytes++; |
|
|
|
|
|
|
|
packet_queue[queue_cursor++] = sbyte; |
|
|
|
|
|
|
|
if (queue_cursor == CONFIG_QUEUE_SIZE) queue_cursor = 0; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} else if (command == CMD_FREQUENCY) { |
|
|
|
} else if (command == CMD_FREQUENCY) { |
|
|
|
if (sbyte == FESC) { |
|
|
|
if (sbyte == FESC) { |
|
|
@ -566,19 +564,20 @@ void validateStatus() { |
|
|
|
void loop() { |
|
|
|
void loop() { |
|
|
|
if (radio_online) { |
|
|
|
if (radio_online) { |
|
|
|
checkModemStatus(); |
|
|
|
checkModemStatus(); |
|
|
|
if (outboundReady() && !SERIAL_READING) { |
|
|
|
|
|
|
|
|
|
|
|
if (queue_height > 0) { |
|
|
|
if (!dcd_waiting) updateModemStatus(); |
|
|
|
if (!dcd_waiting) updateModemStatus(); |
|
|
|
|
|
|
|
|
|
|
|
if (!dcd && !dcd_led) { |
|
|
|
if (!dcd && !dcd_led) { |
|
|
|
if (dcd_waiting) delay(lora_rx_turnaround_ms); |
|
|
|
if (dcd_waiting) delay(lora_rx_turnaround_ms); |
|
|
|
|
|
|
|
|
|
|
|
updateModemStatus(); |
|
|
|
updateModemStatus(); |
|
|
|
|
|
|
|
|
|
|
|
if (!dcd) { |
|
|
|
if (!dcd) { |
|
|
|
dcd_waiting = false; |
|
|
|
dcd_waiting = false; |
|
|
|
#if QUEUE_SIZE > 0 |
|
|
|
|
|
|
|
processQueue(); |
|
|
|
flushQueue(); |
|
|
|
#else |
|
|
|
|
|
|
|
outbound_ready = false; |
|
|
|
|
|
|
|
transmit(frame_len); |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
dcd_waiting = true; |
|
|
|
dcd_waiting = true; |
|
|
@ -594,14 +593,36 @@ void loop() { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (Serial.available()) { |
|
|
|
serial_poll(); |
|
|
|
SERIAL_READING = true; |
|
|
|
} |
|
|
|
char sbyte = Serial.read(); |
|
|
|
|
|
|
|
|
|
|
|
void serial_poll() { |
|
|
|
|
|
|
|
while (!fifo_isempty_locked(&serialFIFO)) { |
|
|
|
|
|
|
|
char sbyte = fifo_pop_locked(&serialFIFO); |
|
|
|
serialCallback(sbyte); |
|
|
|
serialCallback(sbyte); |
|
|
|
last_serial_read = millis(); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
if (SERIAL_READING && millis()-last_serial_read >= serial_read_timeout_ms) { |
|
|
|
|
|
|
|
SERIAL_READING = false; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void buffer_serial() { |
|
|
|
|
|
|
|
while (Serial.available()) { |
|
|
|
|
|
|
|
char c = Serial.read(); |
|
|
|
|
|
|
|
if (!fifo_isfull_locked(&serialFIFO)) { |
|
|
|
|
|
|
|
fifo_push_locked(&serialFIFO, c); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void serial_timer_init() { |
|
|
|
|
|
|
|
TCCR3A = 0; |
|
|
|
|
|
|
|
TCCR3B = _BV(CS10) | |
|
|
|
|
|
|
|
_BV(WGM33)| |
|
|
|
|
|
|
|
_BV(WGM32); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ICR3 = 23704; // Approximation of 16Mhz / 675
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TIMSK3 = _BV(ICIE3); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ISR(TIMER3_CAPT_vect) { |
|
|
|
|
|
|
|
buffer_serial(); |
|
|
|
} |
|
|
|
} |