opencom_xl_firmware/opencom_xl_firmware.ino

1565 lines
47 KiB
Arduino
Raw Normal View History

// Copyright (C) 2024, Mark Qvist
2023-01-14 00:11:02 +01:00
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
2018-12-01 19:31:51 +01:00
#include <Arduino.h>
2018-04-05 18:10:42 +02:00
#include <SPI.h>
#include "Utilities.h"
2018-04-05 18:10:42 +02:00
#if MCU_VARIANT == MCU_NRF52
#define INTERFACE_SPI
2024-10-16 22:24:18 +02:00
#if BOARD_MODEL == BOARD_RAK4631 || BOARD_MODEL == BOARD_OPENCOM_XL
// Required because on RAK4631, non-default SPI pins must be initialised when class is declared.
SPIClass interface_spi[1] = {
// SX1262
SPIClass(
NRF_SPIM2,
interface_pins[0][3],
interface_pins[0][1],
interface_pins[0][2]
)
};
#elif BOARD_MODEL == BOARD_TECHO
SPIClass interface_spi[1] = {
// SX1262
SPIClass(
NRF_SPIM3,
interface_pins[0][3],
interface_pins[0][1],
interface_pins[0][2]
)
};
#endif
#endif
#ifndef INTERFACE_SPI
// INTERFACE_SPI is only required on NRF52 platforms, as the SPI pins are set in the class constructor and not by a setter method.
// Even if custom SPI interfaces are not needed, the array must exist to prevent compilation errors.
#define INTERFACE_SPI
SPIClass interface_spi[1];
#endif
2020-05-28 22:18:19 +02:00
FIFOBuffer serialFIFO;
uint8_t serialBuffer[CONFIG_UART_BUFFER_SIZE+1];
2020-05-28 22:18:19 +02:00
uint16_t packet_starts_buf[(CONFIG_QUEUE_MAX_LENGTH)+1];
2020-05-28 22:18:19 +02:00
uint16_t packet_lengths_buf[(CONFIG_QUEUE_MAX_LENGTH)+1];
2020-05-28 22:18:19 +02:00
2024-06-10 14:05:21 +02:00
FIFOBuffer16 packet_starts[INTERFACE_COUNT];
FIFOBuffer16 packet_lengths[INTERFACE_COUNT];
2020-05-28 22:18:19 +02:00
2024-06-10 14:05:21 +02:00
volatile uint8_t queue_height[INTERFACE_COUNT] = {0};
volatile uint16_t queued_bytes[INTERFACE_COUNT] = {0};
volatile uint16_t queue_cursor[INTERFACE_COUNT] = {0};
volatile uint16_t current_packet_start[INTERFACE_COUNT] = {0};
2020-05-29 14:58:10 +02:00
volatile bool serial_buffering = false;
#if HAS_BLUETOOTH || HAS_BLE == true
bool bt_init_ran = false;
#endif
2020-05-29 14:58:10 +02:00
2023-01-06 22:29:23 +01:00
#if HAS_CONSOLE
#include "Console.h"
#endif
#define MODEM_QUEUE_SIZE 4*INTERFACE_COUNT
typedef struct {
size_t len;
int rssi;
int snr_raw;
uint8_t interface;
2024-10-15 18:34:16 +02:00
uint8_t data[];
} modem_packet_t;
static xQueueHandle modem_packet_queue = NULL;
2020-05-29 14:58:10 +02:00
char sbuf[128];
2020-05-28 22:18:19 +02:00
uint8_t *packet_queue[INTERFACE_COUNT];
2022-01-10 22:14:30 +01:00
2018-04-05 18:10:42 +02:00
void setup() {
2022-01-09 23:40:30 +01:00
#if MCU_VARIANT == MCU_ESP32
boot_seq();
2022-01-09 23:40:30 +01:00
EEPROM.begin(EEPROM_SIZE);
2022-01-11 02:54:32 +01:00
Serial.setRxBufferSize(CONFIG_UART_BUFFER_SIZE);
#if BOARD_MODEL == BOARD_TDECK
pinMode(pin_poweron, OUTPUT);
digitalWrite(pin_poweron, HIGH);
pinMode(SD_CS, OUTPUT);
pinMode(DISPLAY_CS, OUTPUT);
digitalWrite(SD_CS, HIGH);
digitalWrite(DISPLAY_CS, HIGH);
pinMode(DISPLAY_BL_PIN, OUTPUT);
#endif
2022-01-09 23:40:30 +01:00
#endif
#if MCU_VARIANT == MCU_NRF52
if (!eeprom_begin()) {
Serial.write("EEPROM initialisation failed.\r\n");
}
#endif
// Seed the PRNG for CSMA R-value selection
# if MCU_VARIANT == MCU_ESP32
// On ESP32, get the seed value from the
// hardware RNG
int seed_val = (int)esp_random();
#else
// Otherwise, get a pseudo-random seed
// value from an unconnected analog pin
int seed_val = analogRead(0);
#endif
randomSeed(seed_val);
2018-04-05 18:10:42 +02:00
// Initialise serial communication
2020-05-28 22:18:19 +02:00
memset(serialBuffer, 0, sizeof(serialBuffer));
fifo_init(&serialFIFO, serialBuffer, CONFIG_UART_BUFFER_SIZE);
2020-05-28 22:18:19 +02:00
2018-04-05 18:10:42 +02:00
Serial.begin(serial_baudrate);
2024-05-18 10:26:10 +02:00
#if HAS_NP
led_init();
#endif
#if BOARD_MODEL != BOARD_RAK4631 && BOARD_MODEL != BOARD_RNODE_NG_22 && BOARD_MODEL != BOARD_TBEAM_S_V1 && BOARD_MODEL != BOARD_T3S3 && BOARD_MODEL != BOARD_TECHO && BOARD_MODEL != BOARD_OPENCOM_XL
// Some boards need to wait until the hardware UART is set up before booting
// the full firmware. In the case of the RAK4631/TECHO, the line below will wait
// until a serial connection is actually established with a master. Thus, it
// is disabled on this platform.
2024-05-18 01:07:38 +02:00
while (!Serial);
#endif
2018-04-05 18:10:42 +02:00
// Configure input and output pins
2024-05-02 01:20:33 +02:00
#if HAS_INPUT
input_init();
#endif
#if HAS_BUZZER
pinMode(PIN_BUZZER, OUTPUT);
#endif
2022-12-04 20:00:21 +01:00
#if HAS_NP == false
pinMode(pin_led_rx, OUTPUT);
pinMode(pin_led_tx, OUTPUT);
#endif
2018-04-05 18:10:42 +02:00
2024-06-10 14:05:21 +02:00
for (int i = 0; i < INTERFACE_COUNT; i++) {
if (interface_pins[i][9] != -1) {
pinMode(interface_pins[i][9], OUTPUT);
digitalWrite(interface_pins[i][9], HIGH);
2024-01-21 15:23:28 +01:00
}
2024-06-10 14:05:21 +02:00
}
2024-01-19 00:10:24 +01:00
2018-06-27 10:22:44 +02:00
// Initialise buffers
2018-04-05 18:10:42 +02:00
memset(pbuf, 0, sizeof(pbuf));
memset(cmdbuf, 0, sizeof(cmdbuf));
2018-06-27 10:22:44 +02:00
memset(packet_starts_buf, 0, sizeof(packet_starts_buf));
memset(packet_lengths_buf, 0, sizeof(packet_starts_buf));
2024-06-10 14:05:21 +02:00
memset(seq, 0xFF, sizeof(seq));
memset(read_len, 0, sizeof(read_len));
modem_packet_queue = xQueueCreate(MODEM_QUEUE_SIZE, sizeof(modem_packet_t*));
2024-06-10 14:05:21 +02:00
for (int i = 0; i < INTERFACE_COUNT; i++) {
2024-09-10 23:39:49 +02:00
fifo16_init(&packet_starts[i], packet_starts_buf, CONFIG_QUEUE_MAX_LENGTH+1);
fifo16_init(&packet_lengths[i], packet_lengths_buf, CONFIG_QUEUE_MAX_LENGTH+1);
2024-08-21 18:54:38 +02:00
packet_queue[i] = (uint8_t*)malloc(getQueueSize(i)+1);
2024-06-10 14:05:21 +02:00
}
memset(packet_rdy_interfaces_buf, 0, sizeof(packet_rdy_interfaces_buf));
fifo_init(&packet_rdy_interfaces, packet_rdy_interfaces_buf, MAX_INTERFACES);
// add call to init_channel_stats here? \todo
2024-06-10 14:05:21 +02:00
// Create and configure interface objects
for (uint8_t i = 0; i < INTERFACE_COUNT; i++) {
switch (interfaces[i]) {
case SX126X:
case SX1262:
{
sx126x* obj;
// if default spi enabled
if (interface_cfg[i][0]) {
obj = new sx126x(i, &SPI, interface_cfg[i][1],
2024-06-10 14:05:21 +02:00
interface_cfg[i][2], interface_pins[i][0], interface_pins[i][1],
interface_pins[i][2], interface_pins[i][3], interface_pins[i][6],
interface_pins[i][5], interface_pins[i][4], interface_pins[i][8]);
}
else {
obj = new sx126x(i, &interface_spi[i], interface_cfg[i][1],
2024-06-10 14:05:21 +02:00
interface_cfg[i][2], interface_pins[i][0], interface_pins[i][1],
interface_pins[i][2], interface_pins[i][3], interface_pins[i][6],
interface_pins[i][5], interface_pins[i][4], interface_pins[i][8]);
}
interface_obj[i] = obj;
interface_obj_sorted[i] = obj;
break;
2023-02-18 12:14:15 +01:00
}
2024-06-10 14:05:21 +02:00
case SX127X:
case SX1276:
case SX1278:
{
sx127x* obj;
// if default spi enabled
if (interface_cfg[i][0]) {
obj = new sx127x(i, &SPI, interface_pins[i][0],
2024-06-10 14:05:21 +02:00
interface_pins[i][1], interface_pins[i][2], interface_pins[i][3],
interface_pins[i][6], interface_pins[i][5], interface_pins[i][4]);
}
else {
obj = new sx127x(i, &interface_spi[i], interface_pins[i][0],
2024-06-10 14:05:21 +02:00
interface_pins[i][1], interface_pins[i][2], interface_pins[i][3],
interface_pins[i][6], interface_pins[i][5], interface_pins[i][4]);
}
interface_obj[i] = obj;
interface_obj_sorted[i] = obj;
break;
}
case SX128X:
case SX1280:
{
sx128x* obj;
// if default spi enabled
if (interface_cfg[i][0]) {
obj = new sx128x(i, &SPI, interface_cfg[i][1],
2024-06-10 14:05:21 +02:00
interface_pins[i][0], interface_pins[i][1], interface_pins[i][2],
interface_pins[i][3], interface_pins[i][6], interface_pins[i][5],
interface_pins[i][4], interface_pins[i][8], interface_pins[i][7]);
}
else {
obj = new sx128x(i, &interface_spi[i], interface_cfg[i][1],
2024-06-10 14:05:21 +02:00
interface_pins[i][0], interface_pins[i][1], interface_pins[i][2],
interface_pins[i][3], interface_pins[i][6], interface_pins[i][5],
interface_pins[i][4], interface_pins[i][8], interface_pins[i][7]);
}
interface_obj[i] = obj;
interface_obj_sorted[i] = obj;
break;
}
default:
break;
2023-02-18 12:14:15 +01:00
}
2024-06-10 14:05:21 +02:00
}
// Check installed transceiver chip(s) and probe boot parameters. If any of
// the configured modems cannot be initialised, do not boot
for (int i = 0; i < INTERFACE_COUNT; i++) {
switch (interfaces[i]) {
case SX126X:
case SX1262:
case SX127X:
case SX1276:
case SX1278:
case SX128X:
case SX1280:
selected_radio = interface_obj[i];
break;
default:
modems_installed = false;
break;
}
if (selected_radio->preInit()) {
modems_installed = true;
uint32_t lfr = selected_radio->getFrequency();
if (lfr == 0) {
// Normal boot
} else if (lfr == M_FRQ_R) {
// Quick reboot
#if HAS_CONSOLE
if (rtc_get_reset_reason(0) == POWERON_RESET) {
console_active = true;
}
#endif
} else {
// Unknown boot
}
selected_radio->setFrequency(M_FRQ_S);
} else {
modems_installed = false;
}
if (!modems_installed) {
break;
}
2023-01-07 16:35:07 +01:00
}
#if HAS_DISPLAY
2024-05-05 18:15:55 +02:00
#if HAS_EEPROM
2023-05-03 14:05:49 +02:00
if (EEPROM.read(eeprom_addr(ADDR_CONF_DSET)) != CONF_OK_BYTE) {
2024-05-05 18:15:55 +02:00
#elif MCU_VARIANT == MCU_NRF52
if (eeprom_read(eeprom_addr(ADDR_CONF_DSET)) != CONF_OK_BYTE) {
#endif
2023-05-03 14:05:49 +02:00
eeprom_update(eeprom_addr(ADDR_CONF_DSET), CONF_OK_BYTE);
eeprom_update(eeprom_addr(ADDR_CONF_DINT), 0xFF);
}
2024-05-20 21:14:16 +02:00
#if DISPLAY == EINK_BW || DISPLAY == EINK_3C
// Poll and process incoming serial commands whilst e-ink display is
// refreshing to make device still seem responsive
display_add_callback(process_serial);
#endif
disp_ready = display_init();
update_display();
#endif
#if HAS_PMU == true
pmu_ready = init_pmu();
#endif
#if HAS_BLUETOOTH || HAS_BLE == true
bt_init();
bt_init_ran = true;
#endif
2022-01-09 23:40:30 +01:00
2023-01-06 22:29:23 +01:00
if (console_active) {
2023-01-11 09:42:11 +01:00
#if HAS_CONSOLE
console_start();
#else
kiss_indicate_reset();
#endif
2023-01-07 16:35:07 +01:00
} else {
kiss_indicate_reset();
2023-01-06 22:29:23 +01:00
}
2022-10-29 16:44:49 +02:00
// Validate board health, EEPROM and config
validate_status();
2018-04-05 18:10:42 +02:00
}
2024-06-10 14:05:21 +02:00
void lora_receive(RadioInterface* radio) {
2021-03-12 18:48:50 +01:00
if (!implicit) {
2024-06-10 14:05:21 +02:00
radio->receive();
2021-03-12 18:48:50 +01:00
} else {
2024-06-10 14:05:21 +02:00
radio->receive(implicit_l);
2021-03-12 18:48:50 +01:00
}
}
2024-06-10 14:05:21 +02:00
inline void kiss_write_packet(int index) {
// We need to convert the interface index to the command byte representation
uint8_t cmd_byte = getInterfaceCommandByte(index);
2022-10-30 18:35:53 +01:00
serial_write(FEND);
2024-06-10 14:05:21 +02:00
// Add index of interface the packet came from
serial_write(cmd_byte);
for (uint16_t i = 0; i < read_len[index]; i++) {
#if MCU_VARIANT == MCU_NRF52
portENTER_CRITICAL();
uint8_t byte = pbuf[i];
portEXIT_CRITICAL();
#else
uint8_t byte = pbuf[i];
#endif
2022-10-30 18:35:53 +01:00
if (byte == FEND) { serial_write(FESC); byte = TFEND; }
if (byte == FESC) { serial_write(FESC); byte = TFESC; }
serial_write(byte);
2018-04-05 18:10:42 +02:00
}
2022-10-30 18:35:53 +01:00
serial_write(FEND);
read_len[index] = 0;
#if MCU_VARIANT == MCU_ESP32 && HAS_BLE
bt_flush();
#endif
2018-04-05 18:10:42 +02:00
}
2024-06-10 14:05:21 +02:00
inline void getPacketData(RadioInterface* radio, uint16_t len) {
uint8_t index = radio->getIndex();
#if MCU_VARIANT != MCU_NRF52
while (len-- && read_len[index] < MTU) {
pbuf[read_len[index]++] = radio->read();
}
#else
BaseType_t int_mask = taskENTER_CRITICAL_FROM_ISR();
while (len-- && read_len[index] < MTU) {
pbuf[read_len[index]++] = radio->read();
}
taskEXIT_CRITICAL_FROM_ISR(int_mask);
#endif
}
2024-10-15 18:34:16 +02:00
inline bool queue_packet(RadioInterface* radio, uint8_t index) {
// Allocate packet struct, but abort if there
// is not enough memory available.
modem_packet_t *modem_packet = (modem_packet_t*)malloc(sizeof(modem_packet_t) + read_len[index]);
if(!modem_packet) { memory_low = true; return false; }
// Get packet RSSI and SNR
modem_packet->snr_raw = radio->packetSnrRaw();
// Pass raw SNR to get RSSI as SX127X driver requires it for calculations
modem_packet->rssi = radio->packetRssi(modem_packet->snr_raw);
modem_packet->interface = index;
// Send packet to event queue, but free the
// allocated memory again if the queue is
// unable to receive the packet.
modem_packet->len = read_len[index];
memcpy(modem_packet->data, pbuf, read_len[index]);
if (!modem_packet_queue || xQueueSendFromISR(modem_packet_queue, &modem_packet, NULL) != pdPASS) {
free(modem_packet);
2024-10-15 18:34:16 +02:00
return false;
}
2024-10-15 18:34:16 +02:00
return true;
2022-01-11 02:54:32 +01:00
}
2022-01-10 22:14:30 +01:00
void ISR_VECT receive_callback(uint8_t index, int packet_size) {
selected_radio = interface_obj[index];
bool ready = false;
BaseType_t int_mask;
2022-01-14 21:12:33 +01:00
if (!promisc) {
// The standard operating mode allows large
// packets with a payload up to 500 bytes,
// by combining two raw LoRa packets.
// We read the 1-byte header and extract
// packet sequence number and split flags
2024-06-10 14:05:21 +02:00
uint8_t header = selected_radio->read(); packet_size--;
2022-01-14 21:12:33 +01:00
uint8_t sequence = packetSequence(header);
if (isSplitPacket(header) && seq[index] == SEQ_UNSET) {
2022-01-14 21:12:33 +01:00
// This is the first part of a split
// packet, so we set the seq variable
// and add the data to the buffer
#if MCU_VARIANT == MCU_NRF52
int_mask = taskENTER_CRITICAL_FROM_ISR(); read_len[index] = 0; taskEXIT_CRITICAL_FROM_ISR(int_mask);
#else
read_len[index] = 0;
#endif
seq[index] = sequence;
2022-01-10 22:14:30 +01:00
2024-06-10 14:05:21 +02:00
getPacketData(selected_radio, packet_size);
2022-01-11 02:54:32 +01:00
} else if (isSplitPacket(header) && seq[index] == sequence) {
2022-01-14 21:12:33 +01:00
// This is the second part of a split
// packet, so we add it to the buffer
// and set the ready flag.
2024-06-10 14:05:21 +02:00
getPacketData(selected_radio, packet_size);
2024-01-19 11:08:55 +01:00
seq[index] = SEQ_UNSET;
ready = true;
2022-01-14 21:12:33 +01:00
} else if (isSplitPacket(header) && seq[index] != sequence) {
2022-01-14 21:12:33 +01:00
// This split packet does not carry the
// same sequence id, so we must assume
// that we are seeing the first part of
// a new split packet.
#if MCU_VARIANT == MCU_NRF52
int_mask = taskENTER_CRITICAL_FROM_ISR(); read_len[index] = 0; taskEXIT_CRITICAL_FROM_ISR(int_mask);
#else
read_len[index] = 0;
#endif
seq[index] = sequence;
2022-01-11 02:54:32 +01:00
2024-06-10 14:05:21 +02:00
getPacketData(selected_radio, packet_size);
2022-01-11 02:54:32 +01:00
2022-01-14 21:12:33 +01:00
} else if (!isSplitPacket(header)) {
// This is not a split packet, so we
// just read it and set the ready
// flag to true.
2022-01-11 02:54:32 +01:00
if (seq[index] != SEQ_UNSET) {
2022-01-14 21:12:33 +01:00
// If we already had part of a split
// packet in the buffer, we clear it.
#if MCU_VARIANT == MCU_NRF52
int_mask = taskENTER_CRITICAL_FROM_ISR(); read_len[index] = 0; taskEXIT_CRITICAL_FROM_ISR(int_mask);
#else
read_len[index] = 0;
#endif
seq[index] = SEQ_UNSET;
2018-06-27 11:42:48 +02:00
}
2024-06-10 14:05:21 +02:00
getPacketData(selected_radio, packet_size);
2024-09-11 22:49:27 +02:00
ready = true;
2022-01-14 21:12:33 +01:00
}
} else {
2022-01-14 21:41:10 +01:00
// In promiscuous mode, raw packets are
// output directly to the host
read_len[index] = 0;
2022-01-14 21:41:10 +01:00
2024-06-10 14:05:21 +02:00
getPacketData(selected_radio, packet_size);
2024-09-11 22:49:27 +02:00
ready = true;
}
if (ready) {
2024-10-15 18:34:16 +02:00
queue_packet(selected_radio, index);
2022-01-10 22:14:30 +01:00
}
last_rx = millis();
2022-01-10 22:14:30 +01:00
}
2024-06-10 14:05:21 +02:00
bool startRadio(RadioInterface* radio) {
update_radio_lock(radio);
if (modems_installed && !console_active) {
if (!radio->getRadioLock() && hw_ready) {
if (!radio->begin()) {
2022-01-10 22:14:30 +01:00
// The radio could not be started.
// Indicate this failure over both the
// serial port and with the onboard LEDs
kiss_indicate_error(ERROR_INITRADIO);
led_indicate_error(0);
return false;
} else {
2024-06-10 14:05:21 +02:00
radio->enableCrc();
2024-06-10 14:05:21 +02:00
radio->onReceive(receive_callback);
2022-01-10 22:14:30 +01:00
2024-06-10 14:05:21 +02:00
radio->updateBitrate();
sort_interfaces();
kiss_indicate_phy_stats(radio);
2022-01-10 22:14:30 +01:00
2024-06-10 14:05:21 +02:00
lora_receive(radio);
2022-01-10 22:14:30 +01:00
// Flash an info pattern to indicate
// that the radio is now on
2024-06-10 14:05:21 +02:00
kiss_indicate_radiostate(radio);
2022-01-10 22:14:30 +01:00
led_indicate_info(3);
return true;
}
} else {
// Flash a warning pattern to indicate
// that the radio was locked, and thus
// not started
2024-06-10 14:05:21 +02:00
kiss_indicate_radiostate(radio);
2022-01-10 22:14:30 +01:00
led_indicate_warning(3);
return false;
2018-04-05 18:10:42 +02:00
}
2022-01-10 22:14:30 +01:00
} else {
// If radio is already on, we silently
// ignore the request.
2024-06-10 14:05:21 +02:00
kiss_indicate_radiostate(radio);
2022-01-10 22:14:30 +01:00
return true;
}
}
2024-06-10 14:05:21 +02:00
void stopRadio(RadioInterface* radio) {
radio->end();
sort_interfaces();
2024-08-24 16:12:50 +02:00
kiss_indicate_radiostate(radio);
2022-01-10 22:14:30 +01:00
}
2024-06-10 14:05:21 +02:00
void update_radio_lock(RadioInterface* radio) {
if (radio->getFrequency() != 0 && radio->getSignalBandwidth() != 0 && radio->getTxPower() != 0xFF && radio->getSpreadingFactor() != 0) {
radio->setRadioLock(false);
2022-01-10 22:14:30 +01:00
} else {
2024-06-10 14:05:21 +02:00
radio->setRadioLock(true);
2018-04-05 18:10:42 +02:00
}
}
2024-06-10 14:05:21 +02:00
// Check if the queue is full for the selected radio.
// Returns true if full, false if not
bool queueFull(RadioInterface* radio) {
return (queue_height[radio->getIndex()] >= (CONFIG_QUEUE_MAX_LENGTH) || queued_bytes[radio->getIndex()] >= (getQueueSize(radio->getIndex())));
2018-06-27 10:22:44 +02:00
}
2020-05-28 22:18:19 +02:00
volatile bool queue_flushing = false;
2024-06-10 14:05:21 +02:00
// Flushes all packets for the interface
void flushQueue(RadioInterface* radio) {
uint8_t index = radio->getIndex();
2020-05-28 22:18:19 +02:00
if (!queue_flushing) {
queue_flushing = true;
2018-06-27 10:22:44 +02:00
2023-09-14 22:32:26 +02:00
led_tx_on();
uint16_t processed = 0;
2024-06-10 14:05:21 +02:00
uint8_t data_byte;
2022-01-09 23:40:30 +01:00
2024-06-10 14:05:21 +02:00
while (!fifo16_isempty(&packet_starts[index])) {
#if HAS_BUZZER
update_buzzer_tone(TX_HI_TONE, TX_LO_TONE);
#endif
2024-06-10 14:05:21 +02:00
uint16_t start = fifo16_pop(&packet_starts[index]);
uint16_t length = fifo16_pop(&packet_lengths[index]);
2018-06-27 10:22:44 +02:00
2020-05-29 14:58:10 +02:00
if (length >= MIN_L && length <= MTU) {
for (uint16_t i = 0; i < length; i++) {
uint16_t pos = (start+i)%(getQueueSize(index));
2024-06-10 14:05:21 +02:00
tbuf[i] = packet_queue[index][pos];
2020-05-28 22:18:19 +02:00
}
2024-06-10 14:05:21 +02:00
transmit(radio, length);
2020-05-28 22:18:19 +02:00
processed++;
}
}
2023-09-14 22:32:26 +02:00
2024-06-10 14:05:21 +02:00
lora_receive(radio);
2023-09-14 22:32:26 +02:00
led_tx_off();
2024-06-10 14:05:21 +02:00
radio->setPostTxYieldTimeout(millis()+(lora_post_tx_yield_slots*selected_radio->getCSMASlotMS()));
2018-06-27 10:22:44 +02:00
}
2020-05-28 22:18:19 +02:00
2024-06-10 14:05:21 +02:00
queue_height[index] = 0;
queued_bytes[index] = 0;
selected_radio->updateAirtime();
2020-05-28 22:18:19 +02:00
queue_flushing = false;
#if HAS_DISPLAY
display_tx = true;
#endif
2018-06-27 10:22:44 +02:00
}
2024-06-10 14:05:21 +02:00
void transmit(RadioInterface* radio, uint16_t size) {
if (radio->getRadioOnline()) {
2018-06-27 11:42:48 +02:00
if (!promisc) {
uint16_t written = 0;
2018-06-27 11:42:48 +02:00
uint8_t header = random(256) & 0xF0;
2018-04-05 18:10:42 +02:00
2018-06-27 11:42:48 +02:00
if (size > SINGLE_MTU - HEADER_L) {
header = header | FLAG_SPLIT;
}
2018-04-05 18:10:42 +02:00
2024-06-10 14:05:21 +02:00
radio->beginPacket();
radio->write(header); written++;
2018-04-05 18:10:42 +02:00
2022-01-14 10:51:40 +01:00
for (uint16_t i=0; i < size; i++) {
2024-06-10 14:05:21 +02:00
radio->write(tbuf[i]);
2018-06-27 10:22:44 +02:00
2018-06-27 11:42:48 +02:00
written++;
2018-04-05 18:10:42 +02:00
2024-10-12 13:45:20 +02:00
// Only start a new packet if this is a split packet and it has
// exceeded the length of a single packet
if (written == 255 && header & 0x0F) {
2024-06-10 14:05:21 +02:00
radio->endPacket(); radio->addAirtime(written);
radio->beginPacket();
radio->write(header);
2018-06-27 11:42:48 +02:00
written = 1;
}
2018-04-05 18:10:42 +02:00
}
if (!radio->endPacket()) {
kiss_indicate_error(ERROR_MODEM_TIMEOUT);
kiss_indicate_error(ERROR_TXFAILED);
led_indicate_error(5);
hard_reset();
}
radio->addAirtime(written);
2018-06-27 11:42:48 +02:00
} else {
// In promiscuous mode, we only send out
// plain raw LoRa packets with a maximum
// payload of 255 bytes
led_tx_on();
uint16_t written = 0;
2018-06-27 11:42:48 +02:00
// Cap packets at 255 bytes
if (size > SINGLE_MTU) {
size = SINGLE_MTU;
}
2018-04-26 10:34:39 +02:00
2021-03-12 18:48:50 +01:00
// If implicit header mode has been set,
// set packet length to payload data length
if (!implicit) {
2024-06-10 14:05:21 +02:00
radio->beginPacket();
2021-03-12 18:48:50 +01:00
} else {
2024-06-10 14:05:21 +02:00
radio->beginPacket(size);
2021-03-12 18:48:50 +01:00
}
2022-01-14 10:51:40 +01:00
for (uint16_t i=0; i < size; i++) {
2024-06-10 14:05:21 +02:00
radio->write(tbuf[i]);
2018-06-27 11:42:48 +02:00
written++;
}
2024-06-10 14:05:21 +02:00
radio->endPacket(); radio->addAirtime(written);
2018-06-27 11:42:48 +02:00
}
last_tx = millis();
2018-04-05 18:10:42 +02:00
} else {
kiss_indicate_error(ERROR_TXFAILED);
led_indicate_error(5);
}
}
void serialCallback(uint8_t sbyte) {
2024-06-10 14:05:21 +02:00
if (IN_FRAME && sbyte == FEND &&
(command == CMD_INT0_DATA
|| command == CMD_INT1_DATA
|| command == CMD_INT2_DATA
|| command == CMD_INT3_DATA
|| command == CMD_INT4_DATA
|| command == CMD_INT5_DATA
|| command == CMD_INT6_DATA
|| command == CMD_INT7_DATA
|| command == CMD_INT8_DATA
|| command == CMD_INT9_DATA
|| command == CMD_INT10_DATA
|| command == CMD_INT11_DATA)) {
2018-04-05 18:10:42 +02:00
IN_FRAME = false;
2018-06-27 10:22:44 +02:00
2024-06-10 14:05:21 +02:00
if (getInterfaceIndex(command) < INTERFACE_COUNT) {
uint8_t index = getInterfaceIndex(command);
2024-08-21 18:54:38 +02:00
if (!fifo16_isfull(&packet_starts[index]) && (queued_bytes[index] < (getQueueSize(index)))) {
2024-06-10 14:05:21 +02:00
uint16_t s = current_packet_start[index];
2024-09-10 23:39:49 +02:00
int32_t e = queue_cursor[index]-1; if (e == -1) e = (getQueueSize(index))-1;
2024-06-10 14:05:21 +02:00
uint16_t l;
if (s != e) {
l = (s < e) ? e - s + 1: (getQueueSize(index)) - s + e + 1;
2024-06-10 14:05:21 +02:00
} else {
l = 1;
}
2020-05-28 22:18:19 +02:00
2024-06-10 14:05:21 +02:00
if (l >= MIN_L) {
queue_height[index]++;
2020-05-28 22:18:19 +02:00
2024-06-10 14:05:21 +02:00
fifo16_push(&packet_starts[index], s);
fifo16_push(&packet_lengths[index], l);
current_packet_start[index] = queue_cursor[index];
}
2020-05-28 22:18:19 +02:00
}
2018-06-27 10:22:44 +02:00
}
2020-05-28 22:18:19 +02:00
2018-04-05 18:10:42 +02:00
} else if (sbyte == FEND) {
IN_FRAME = true;
command = CMD_UNKNOWN;
frame_len = 0;
} else if (IN_FRAME && frame_len < MTU) {
// Have a look at the command byte first
if (frame_len == 0 && command == CMD_UNKNOWN) {
command = sbyte;
2024-06-10 14:05:21 +02:00
if (command == CMD_SEL_INT0
|| command == CMD_SEL_INT1
|| command == CMD_SEL_INT2
|| command == CMD_SEL_INT3
|| command == CMD_SEL_INT4
|| command == CMD_SEL_INT5
|| command == CMD_SEL_INT6
|| command == CMD_SEL_INT7
|| command == CMD_SEL_INT8
|| command == CMD_SEL_INT9
|| command == CMD_SEL_INT10
|| command == CMD_SEL_INT11) {
interface = getInterfaceIndex(command);
}
} else if (command == CMD_INT0_DATA
|| command == CMD_INT1_DATA
|| command == CMD_INT2_DATA
|| command == CMD_INT3_DATA
|| command == CMD_INT4_DATA
|| command == CMD_INT5_DATA
|| command == CMD_INT6_DATA
|| command == CMD_INT7_DATA
|| command == CMD_INT8_DATA
|| command == CMD_INT9_DATA
|| command == CMD_INT10_DATA
|| command == CMD_INT11_DATA) {
2022-10-30 18:35:53 +01:00
if (bt_state != BT_STATE_CONNECTED) cable_state = CABLE_STATE_CONNECTED;
2018-04-05 18:10:42 +02:00
if (sbyte == FESC) {
ESCAPE = true;
} else {
if (ESCAPE) {
if (sbyte == TFEND) sbyte = FEND;
if (sbyte == TFESC) sbyte = FESC;
ESCAPE = false;
}
2024-06-10 14:05:21 +02:00
if (getInterfaceIndex(command) < INTERFACE_COUNT) {
uint8_t index = getInterfaceIndex(command);
if (queue_height[index] < CONFIG_QUEUE_MAX_LENGTH && queued_bytes[index] < (getQueueSize(index))) {
2024-06-10 14:05:21 +02:00
queued_bytes[index]++;
packet_queue[index][queue_cursor[index]++] = sbyte;
if (queue_cursor[index] == (getQueueSize(index))) queue_cursor[index] = 0;
2024-06-10 14:05:21 +02:00
}
2020-05-28 22:18:19 +02:00
}
2018-04-05 18:10:42 +02:00
}
2024-06-10 14:05:21 +02:00
} else if (command == CMD_INTERFACES) {
for (int i = 0; i < INTERFACE_COUNT; i++) {
kiss_indicate_interface(i);
}
2018-04-05 18:10:42 +02:00
} else if (command == CMD_FREQUENCY) {
if (sbyte == FESC) {
ESCAPE = true;
} else {
if (ESCAPE) {
if (sbyte == TFEND) sbyte = FEND;
if (sbyte == TFESC) sbyte = FESC;
ESCAPE = false;
}
if (frame_len < CMD_L) cmdbuf[frame_len++] = sbyte;
2018-04-05 18:10:42 +02:00
}
if (frame_len == 4) {
uint32_t freq = (uint32_t)cmdbuf[0] << 24 | (uint32_t)cmdbuf[1] << 16 | (uint32_t)cmdbuf[2] << 8 | (uint32_t)cmdbuf[3];
2018-04-05 18:10:42 +02:00
2024-06-10 14:05:21 +02:00
selected_radio = interface_obj[interface];
2018-04-05 18:10:42 +02:00
if (freq == 0) {
2024-06-10 14:05:21 +02:00
kiss_indicate_frequency(selected_radio);
2018-04-05 18:10:42 +02:00
} else {
2024-06-10 14:05:21 +02:00
if (op_mode == MODE_HOST) selected_radio->setFrequency(freq);
kiss_indicate_frequency(selected_radio);
2018-04-05 18:10:42 +02:00
}
2024-06-10 14:05:21 +02:00
interface = 0;
2018-04-05 18:10:42 +02:00
}
} else if (command == CMD_BANDWIDTH) {
if (sbyte == FESC) {
ESCAPE = true;
} else {
if (ESCAPE) {
if (sbyte == TFEND) sbyte = FEND;
if (sbyte == TFESC) sbyte = FESC;
ESCAPE = false;
}
if (frame_len < CMD_L) cmdbuf[frame_len++] = sbyte;
2018-04-05 18:10:42 +02:00
}
if (frame_len == 4) {
uint32_t bw = (uint32_t)cmdbuf[0] << 24 | (uint32_t)cmdbuf[1] << 16 | (uint32_t)cmdbuf[2] << 8 | (uint32_t)cmdbuf[3];
2018-04-05 18:10:42 +02:00
2024-06-10 14:05:21 +02:00
selected_radio = interface_obj[interface];
2018-04-05 18:10:42 +02:00
if (bw == 0) {
2024-06-10 14:05:21 +02:00
kiss_indicate_bandwidth(selected_radio);
2018-04-05 18:10:42 +02:00
} else {
2024-06-10 14:05:21 +02:00
if (op_mode == MODE_HOST) selected_radio->setSignalBandwidth(bw);
selected_radio->updateBitrate();
sort_interfaces();
kiss_indicate_bandwidth(selected_radio);
kiss_indicate_phy_stats(selected_radio);
2018-04-05 18:10:42 +02:00
}
2024-06-10 14:05:21 +02:00
interface = 0;
2018-04-05 18:10:42 +02:00
}
} else if (command == CMD_TXPOWER) {
2024-06-10 14:05:21 +02:00
selected_radio = interface_obj[interface];
2018-04-05 18:10:42 +02:00
if (sbyte == 0xFF) {
2024-06-10 14:05:21 +02:00
kiss_indicate_txpower(selected_radio);
2018-04-05 18:10:42 +02:00
} else {
int8_t txp = (int8_t)sbyte;
2018-04-05 18:10:42 +02:00
2024-06-10 14:05:21 +02:00
if (op_mode == MODE_HOST) setTXPower(selected_radio, txp);
kiss_indicate_txpower(selected_radio);
2018-04-05 18:10:42 +02:00
}
2024-06-10 14:05:21 +02:00
interface = 0;
2018-04-05 18:10:42 +02:00
} else if (command == CMD_SF) {
2024-06-10 14:05:21 +02:00
selected_radio = interface_obj[interface];
2018-04-05 18:10:42 +02:00
if (sbyte == 0xFF) {
2024-06-10 14:05:21 +02:00
kiss_indicate_spreadingfactor(selected_radio);
2018-04-05 18:10:42 +02:00
} else {
int sf = sbyte;
if (sf < 5) sf = 5;
2018-04-05 18:10:42 +02:00
if (sf > 12) sf = 12;
2024-06-10 14:05:21 +02:00
if (op_mode == MODE_HOST) selected_radio->setSpreadingFactor(sf);
selected_radio->updateBitrate();
sort_interfaces();
kiss_indicate_spreadingfactor(selected_radio);
kiss_indicate_phy_stats(selected_radio);
2018-04-05 18:10:42 +02:00
}
2024-06-10 14:05:21 +02:00
interface = 0;
} else if (command == CMD_CR) {
2024-06-10 14:05:21 +02:00
selected_radio = interface_obj[interface];
if (sbyte == 0xFF) {
2024-06-10 14:05:21 +02:00
kiss_indicate_codingrate(selected_radio);
} else {
int cr = sbyte;
if (cr < 5) cr = 5;
if (cr > 8) cr = 8;
2024-06-10 14:05:21 +02:00
if (op_mode == MODE_HOST) selected_radio->setCodingRate4(cr);
selected_radio->updateBitrate();
sort_interfaces();
kiss_indicate_codingrate(selected_radio);
kiss_indicate_phy_stats(selected_radio);
}
2024-06-10 14:05:21 +02:00
interface = 0;
2021-03-12 18:48:50 +01:00
} else if (command == CMD_IMPLICIT) {
set_implicit_length(sbyte);
kiss_indicate_implicit_length();
2022-10-29 16:44:49 +02:00
} else if (command == CMD_LEAVE) {
if (sbyte == 0xFF) {
//display_unblank();
cable_state = CABLE_STATE_DISCONNECTED;
2024-06-10 14:05:21 +02:00
//current_rssi = -292;
last_rssi = -292;
last_rssi_raw = 0x00;
last_snr_raw = 0x80;
2022-10-29 16:44:49 +02:00
}
2018-04-05 18:10:42 +02:00
} else if (command == CMD_RADIO_STATE) {
2024-06-10 14:05:21 +02:00
selected_radio = interface_obj[interface];
2022-10-30 18:35:53 +01:00
if (bt_state != BT_STATE_CONNECTED) cable_state = CABLE_STATE_CONNECTED;
2018-04-05 18:10:42 +02:00
if (sbyte == 0xFF) {
2024-06-10 14:05:21 +02:00
kiss_indicate_radiostate(selected_radio);
2018-04-05 18:10:42 +02:00
} else if (sbyte == 0x00) {
2024-06-10 14:05:21 +02:00
stopRadio(selected_radio);
2018-04-05 18:10:42 +02:00
} else if (sbyte == 0x01) {
2024-06-10 14:05:21 +02:00
startRadio(selected_radio);
2018-04-05 18:10:42 +02:00
}
2024-06-10 14:05:21 +02:00
interface = 0;
} else if (command == CMD_ST_ALOCK) {
2024-06-10 14:05:21 +02:00
selected_radio = interface_obj[interface];
if (sbyte == FESC) {
ESCAPE = true;
} else {
if (ESCAPE) {
if (sbyte == TFEND) sbyte = FEND;
if (sbyte == TFESC) sbyte = FESC;
ESCAPE = false;
}
if (frame_len < CMD_L) cmdbuf[frame_len++] = sbyte;
}
if (frame_len == 2) {
uint16_t at = (uint16_t)cmdbuf[0] << 8 | (uint16_t)cmdbuf[1];
if (at == 0) {
2024-06-10 14:05:21 +02:00
selected_radio->setSTALock(0.0);
} else {
2024-06-10 14:05:21 +02:00
int st_airtime_limit = (float)at/(100.0*100.0);
if (st_airtime_limit >= 1.0) { st_airtime_limit = 0.0; }
2024-06-10 14:05:21 +02:00
selected_radio->setSTALock(st_airtime_limit);
}
2024-06-10 14:05:21 +02:00
kiss_indicate_st_alock(selected_radio);
}
2024-06-10 14:05:21 +02:00
interface = 0;
} else if (command == CMD_LT_ALOCK) {
2024-06-10 14:05:21 +02:00
selected_radio = interface_obj[interface];
if (sbyte == FESC) {
ESCAPE = true;
} else {
if (ESCAPE) {
if (sbyte == TFEND) sbyte = FEND;
if (sbyte == TFESC) sbyte = FESC;
ESCAPE = false;
}
if (frame_len < CMD_L) cmdbuf[frame_len++] = sbyte;
}
if (frame_len == 2) {
uint16_t at = (uint16_t)cmdbuf[0] << 8 | (uint16_t)cmdbuf[1];
if (at == 0) {
2024-06-10 14:05:21 +02:00
selected_radio->setLTALock(0.0);
} else {
2024-06-10 14:05:21 +02:00
int lt_airtime_limit = (float)at/(100.0*100.0);
if (lt_airtime_limit >= 1.0) { lt_airtime_limit = 0.0; }
2024-06-10 14:05:21 +02:00
selected_radio->setLTALock(lt_airtime_limit);
}
2024-06-10 14:05:21 +02:00
kiss_indicate_lt_alock(selected_radio);
}
2024-06-10 14:05:21 +02:00
interface = 0;
2018-04-05 18:10:42 +02:00
} else if (command == CMD_STAT_RX) {
kiss_indicate_stat_rx();
} else if (command == CMD_STAT_TX) {
kiss_indicate_stat_tx();
} else if (command == CMD_STAT_RSSI) {
kiss_indicate_stat_rssi();
} else if (command == CMD_RADIO_LOCK) {
2024-06-10 14:05:21 +02:00
selected_radio = interface_obj[interface];
update_radio_lock(selected_radio);
kiss_indicate_radio_lock(selected_radio);
interface = 0;
2018-04-05 18:10:42 +02:00
} else if (command == CMD_BLINK) {
led_indicate_info(sbyte);
} else if (command == CMD_RANDOM) {
2024-06-10 14:05:21 +02:00
// pick an interface at random to get data from
int int_index = random(INTERFACE_COUNT);
selected_radio = interface_obj[int_index];
kiss_indicate_random(getRandom(selected_radio));
interface = 0;
} else if (command == CMD_DETECT) {
if (sbyte == DETECT_REQ) {
2022-10-30 18:35:53 +01:00
if (bt_state != BT_STATE_CONNECTED) cable_state = CABLE_STATE_CONNECTED;
kiss_indicate_detect();
}
2018-06-27 11:42:48 +02:00
} else if (command == CMD_PROMISC) {
if (sbyte == 0x01) {
promisc_enable();
} else if (sbyte == 0x00) {
promisc_disable();
}
kiss_indicate_promisc();
} else if (command == CMD_READY) {
2024-06-10 14:05:21 +02:00
selected_radio = interface_obj[interface];
if (!queueFull(selected_radio)) {
kiss_indicate_ready();
} else {
kiss_indicate_not_ready();
}
} else if (command == CMD_UNLOCK_ROM) {
if (sbyte == ROM_UNLOCK_BYTE) {
unlock_rom();
}
2022-01-09 23:40:30 +01:00
} else if (command == CMD_RESET) {
if (sbyte == CMD_RESET_BYTE) {
hard_reset();
}
} else if (command == CMD_ROM_READ) {
kiss_dump_eeprom();
} else if (command == CMD_ROM_WRITE) {
if (sbyte == FESC) {
ESCAPE = true;
} else {
if (ESCAPE) {
if (sbyte == TFEND) sbyte = FEND;
if (sbyte == TFESC) sbyte = FESC;
ESCAPE = false;
}
if (frame_len < CMD_L) cmdbuf[frame_len++] = sbyte;
}
if (frame_len == 2) {
eeprom_write(cmdbuf[0], cmdbuf[1]);
}
} else if (command == CMD_FW_VERSION) {
kiss_indicate_version();
2022-01-09 23:40:30 +01:00
} else if (command == CMD_PLATFORM) {
kiss_indicate_platform();
} else if (command == CMD_MCU) {
kiss_indicate_mcu();
2022-01-22 22:34:03 +01:00
} else if (command == CMD_BOARD) {
kiss_indicate_board();
2018-06-20 16:32:30 +02:00
} else if (command == CMD_CONF_SAVE) {
2024-06-10 14:05:21 +02:00
// todo: add extra space in EEPROM so this isn't hardcoded
eeprom_conf_save(interface_obj[0]);
2018-06-20 16:32:30 +02:00
} else if (command == CMD_CONF_DELETE) {
eeprom_conf_delete();
} else if (command == CMD_FB_EXT) {
#if HAS_DISPLAY == true
if (sbyte == 0xFF) {
kiss_indicate_fbstate();
} else if (sbyte == 0x00) {
ext_fb_disable();
kiss_indicate_fbstate();
} else if (sbyte == 0x01) {
ext_fb_enable();
kiss_indicate_fbstate();
}
#endif
} else if (command == CMD_FB_WRITE) {
if (sbyte == FESC) {
ESCAPE = true;
} else {
if (ESCAPE) {
if (sbyte == TFEND) sbyte = FEND;
if (sbyte == TFESC) sbyte = FESC;
ESCAPE = false;
}
if (frame_len < CMD_L) cmdbuf[frame_len++] = sbyte;
}
2022-10-30 18:58:12 +01:00
#if HAS_DISPLAY
if (frame_len == 9) {
uint8_t line = cmdbuf[0];
2022-10-30 18:58:12 +01:00
if (line > 63) line = 63;
int fb_o = line*8;
memcpy(fb+fb_o, cmdbuf+1, 8);
2022-10-30 18:58:12 +01:00
}
#endif
} else if (command == CMD_FB_READ) {
if (sbyte != 0x00) {
kiss_indicate_fb();
}
} else if (command == CMD_DEV_HASH) {
if (sbyte != 0x00) {
kiss_indicate_device_hash();
}
} else if (command == CMD_DEV_SIG) {
if (sbyte == FESC) {
ESCAPE = true;
} else {
if (ESCAPE) {
if (sbyte == TFEND) sbyte = FEND;
if (sbyte == TFESC) sbyte = FESC;
ESCAPE = false;
}
if (frame_len < CMD_L) cmdbuf[frame_len++] = sbyte;
}
if (frame_len == DEV_SIG_LEN) {
memcpy(dev_sig, cmdbuf, DEV_SIG_LEN);
device_save_signature();
}
} else if (command == CMD_FW_UPD) {
if (sbyte == 0x01) {
firmware_update_mode = true;
} else {
firmware_update_mode = false;
}
2022-11-01 22:21:07 +01:00
} else if (command == CMD_HASHES) {
if (sbyte == 0x01) {
kiss_indicate_target_fw_hash();
} else if (sbyte == 0x02) {
kiss_indicate_fw_hash();
} else if (sbyte == 0x03) {
kiss_indicate_bootloader_hash();
} else if (sbyte == 0x04) {
kiss_indicate_partition_table_hash();
}
} else if (command == CMD_FW_HASH) {
if (sbyte == FESC) {
ESCAPE = true;
} else {
if (ESCAPE) {
if (sbyte == TFEND) sbyte = FEND;
if (sbyte == TFESC) sbyte = FESC;
ESCAPE = false;
}
if (frame_len < CMD_L) cmdbuf[frame_len++] = sbyte;
}
if (frame_len == DEV_HASH_LEN) {
2023-12-10 22:40:06 +01:00
memcpy(dev_firmware_hash_target, cmdbuf, DEV_HASH_LEN);
device_save_firmware_hash();
}
2022-10-30 13:51:36 +01:00
} else if (command == CMD_BT_CTRL) {
#if HAS_BLUETOOTH || HAS_BLE
2022-10-30 13:51:36 +01:00
if (sbyte == 0x00) {
bt_stop();
bt_conf_save(false);
2022-10-30 13:51:36 +01:00
} else if (sbyte == 0x01) {
bt_start();
bt_conf_save(true);
2022-10-30 13:51:36 +01:00
} else if (sbyte == 0x02) {
if (bt_state == BT_STATE_OFF) {
bt_start();
bt_conf_save(true);
}
if (bt_state != BT_STATE_CONNECTED) {
bt_enable_pairing();
}
2022-10-30 13:51:36 +01:00
}
#endif
} else if (command == CMD_DISP_INT) {
#if HAS_DISPLAY
if (sbyte == FESC) {
ESCAPE = true;
} else {
if (ESCAPE) {
if (sbyte == TFEND) sbyte = FEND;
if (sbyte == TFESC) sbyte = FESC;
ESCAPE = false;
}
display_intensity = sbyte;
2023-05-03 14:05:49 +02:00
di_conf_save(display_intensity);
//display_unblank();
}
#endif
} else if (command == CMD_DISP_ADDR) {
#if HAS_DISPLAY
if (sbyte == FESC) {
ESCAPE = true;
} else {
if (ESCAPE) {
if (sbyte == TFEND) sbyte = FEND;
if (sbyte == TFESC) sbyte = FESC;
ESCAPE = false;
}
display_addr = sbyte;
da_conf_save(display_addr);
}
#endif
} else if (command == CMD_FW_LENGTH) {
if (sbyte == FESC) {
ESCAPE = true;
} else {
if (ESCAPE) {
if (sbyte == TFEND) sbyte = FEND;
if (sbyte == TFESC) sbyte = FESC;
ESCAPE = false;
}
if (frame_len < CMD_L) cmdbuf[frame_len++] = sbyte;
}
if (frame_len == FW_LENGTH_LEN) {
set_fw_length(cmdbuf);
}
} else if (command == CMD_DISP_BLNK) {
#if HAS_DISPLAY
if (sbyte == FESC) {
ESCAPE = true;
} else {
if (ESCAPE) {
if (sbyte == TFEND) sbyte = FEND;
if (sbyte == TFESC) sbyte = FESC;
ESCAPE = false;
}
db_conf_save(sbyte);
//display_unblank();
}
#endif
} else if (command == CMD_NP_INT) {
#if HAS_NP
if (sbyte == FESC) {
ESCAPE = true;
} else {
if (ESCAPE) {
if (sbyte == TFEND) sbyte = FEND;
if (sbyte == TFESC) sbyte = FESC;
ESCAPE = false;
}
sbyte;
led_set_intensity(sbyte);
np_int_conf_save(sbyte);
}
#endif
2018-04-05 18:10:42 +02:00
}
}
}
2022-01-14 21:12:33 +01:00
#if MCU_VARIANT == MCU_ESP32
portMUX_TYPE update_lock = portMUX_INITIALIZER_UNLOCKED;
#endif
void validate_status() {
2024-06-10 14:05:21 +02:00
#if MCU_VARIANT == MCU_ESP32
2022-01-09 23:40:30 +01:00
// TODO: Get ESP32 boot flags
uint8_t boot_flags = 0x02;
uint8_t F_POR = 0x00;
uint8_t F_BOR = 0x00;
uint8_t F_WDR = 0x01;
2024-01-19 11:08:55 +01:00
#elif MCU_VARIANT == MCU_NRF52
// TODO: Get NRF52 boot flags
uint8_t boot_flags = 0x02;
uint8_t F_POR = 0x00;
uint8_t F_BOR = 0x00;
uint8_t F_WDR = 0x01;
2022-01-09 23:40:30 +01:00
#endif
if (hw_ready || device_init_done) {
hw_ready = false;
Serial.write("Error, invalid hardware check state\r\n");
#if HAS_DISPLAY
if (disp_ready) {
device_init_done = true;
update_display();
}
#endif
led_indicate_boot_error();
}
2022-01-09 23:40:30 +01:00
if (boot_flags & (1<<F_POR)) {
2021-12-26 11:27:32 +01:00
boot_vector = START_FROM_POWERON;
2022-01-09 23:40:30 +01:00
} else if (boot_flags & (1<<F_BOR)) {
2021-12-26 11:27:32 +01:00
boot_vector = START_FROM_BROWNOUT;
2022-01-09 23:40:30 +01:00
} else if (boot_flags & (1<<F_WDR)) {
2021-12-26 11:27:32 +01:00
boot_vector = START_FROM_BOOTLOADER;
} else {
Serial.write("Error, indeterminate boot vector\r\n");
2022-10-29 16:44:49 +02:00
#if HAS_DISPLAY
if (disp_ready) {
device_init_done = true;
update_display();
}
2022-10-29 16:44:49 +02:00
#endif
2021-12-26 11:27:32 +01:00
led_indicate_boot_error();
}
2022-01-09 23:40:30 +01:00
if (boot_vector == START_FROM_BOOTLOADER || boot_vector == START_FROM_POWERON) {
2021-12-26 11:27:32 +01:00
if (eeprom_lock_set()) {
if (eeprom_product_valid() && eeprom_model_valid() && eeprom_hwrev_valid()) {
if (eeprom_checksum_valid()) {
eeprom_ok = true;
2024-06-10 14:05:21 +02:00
if (modems_installed) {
if (device_init()) {
hw_ready = true;
} else {
hw_ready = false;
}
2023-01-07 16:35:07 +01:00
} else {
hw_ready = false;
Serial.write("No radio module found\r\n");
2023-01-07 16:35:07 +01:00
#if HAS_DISPLAY
if (disp_ready) {
device_init_done = true;
update_display();
}
#endif
}
} else {
hw_ready = false;
Serial.write("Invalid EEPROM checksum\r\n");
#if HAS_DISPLAY
if (disp_ready) {
device_init_done = true;
update_display();
}
#endif
2018-06-20 16:32:30 +02:00
}
2021-12-26 11:27:32 +01:00
} else {
hw_ready = false;
Serial.write("Invalid EEPROM configuration\r\n");
#if HAS_DISPLAY
if (disp_ready) {
device_init_done = true;
update_display();
}
#endif
}
} else {
hw_ready = false;
Serial.write("Device unprovisioned, no device configuration found in EEPROM\r\n");
#if HAS_DISPLAY
if (disp_ready) {
device_init_done = true;
update_display();
}
#endif
}
} else {
hw_ready = false;
2021-12-26 11:27:32 +01:00
Serial.write("Error, incorrect boot vector\r\n");
2022-10-29 16:44:49 +02:00
#if HAS_DISPLAY
if (disp_ready) {
device_init_done = true;
update_display();
}
2022-10-29 16:44:49 +02:00
#endif
2021-12-26 11:27:32 +01:00
led_indicate_boot_error();
}
Scheduler.startLoop(input_read);
}
2018-04-05 18:10:42 +02:00
void loop() {
#if MCU_VARIANT == MCU_ESP32
modem_packet_t *modem_packet = NULL;
if(modem_packet_queue && xQueueReceive(modem_packet_queue, &modem_packet, 0) == pdTRUE && modem_packet) {
packet_interface = modem_packet->interface;
read_len[packet_interface] = modem_packet->len;
last_rssi = modem_packet->rssi;
last_snr_raw = modem_packet->snr_raw;
memcpy(&pbuf, modem_packet->data, modem_packet->len);
free(modem_packet);
modem_packet = NULL;
kiss_indicate_stat_rssi();
kiss_indicate_stat_snr();
2024-09-11 22:49:27 +02:00
kiss_write_packet(packet_interface);
}
#elif MCU_VARIANT == MCU_NRF52
modem_packet_t *modem_packet = NULL;
if(modem_packet_queue && xQueueReceive(modem_packet_queue, &modem_packet, 0) == pdTRUE && modem_packet) {
packet_interface = modem_packet->interface;
read_len[packet_interface] = modem_packet->len;
last_rssi = modem_packet->rssi;
last_snr_raw = modem_packet->snr_raw;
memcpy(&pbuf, modem_packet->data, modem_packet->len);
free(modem_packet);
modem_packet = NULL;
kiss_indicate_stat_rssi();
kiss_indicate_stat_snr();
kiss_write_packet(packet_interface);
}
#endif
2024-06-10 14:05:21 +02:00
bool ready = false;
for (int i = 0; i < INTERFACE_COUNT; i++) {
selected_radio = interface_obj[i];
if (selected_radio->getRadioOnline()) {
selected_radio->checkModemStatus();
ready = true;
}
}
// If at least one radio is online then we can continue
2024-06-10 14:05:21 +02:00
if (ready) {
for (int i = 0; i < INTERFACE_COUNT; i++) {
selected_radio = interface_obj_sorted[i];
2020-05-28 22:18:19 +02:00
2024-06-10 14:05:21 +02:00
if (selected_radio->calculateALock() || !selected_radio->getRadioOnline()) {
// skip this interface
continue;
}
if (queue_height[selected_radio->getIndex()] > 0) {
2024-09-10 23:39:49 +02:00
uint32_t check_time = millis();
2024-06-10 14:05:21 +02:00
if (check_time > selected_radio->getPostTxYieldTimeout()) {
if (selected_radio->getDCDWaiting() && (check_time >= selected_radio->getDCDWaitUntil())) { selected_radio->setDCDWaiting(false); }
if (!selected_radio->getDCDWaiting()) {
// todo, will the delay here slow down transmission with
// multiple interfaces? needs investigation
for (uint8_t dcd_i = 0; dcd_i < DCD_THRESHOLD*2; dcd_i++) {
delay(STATUS_INTERVAL_MS); selected_radio->updateModemStatus();
}
if (!selected_radio->getDCD()) {
uint8_t csma_r = (uint8_t)random(256);
if (selected_radio->getCSMAp() >= csma_r) {
flushQueue(selected_radio);
} else {
selected_radio->setDCDWaiting(true);
selected_radio->setDCDWaitUntil(millis()+selected_radio->getCSMASlotMS());
}
}
}
2023-09-14 22:32:26 +02:00
}
2024-06-10 14:05:21 +02:00
}
2018-04-26 15:52:43 +02:00
}
2018-06-27 10:22:44 +02:00
2018-06-18 22:30:24 +02:00
} else {
if (hw_ready) {
2023-01-06 22:29:23 +01:00
if (console_active) {
2023-01-11 09:42:11 +01:00
#if HAS_CONSOLE
console_loop();
#endif
2023-01-06 22:29:23 +01:00
} else {
2023-01-10 14:27:03 +01:00
led_indicate_standby();
2023-01-06 22:29:23 +01:00
}
} else {
led_indicate_not_ready();
2024-06-10 14:05:21 +02:00
// shut down all radio interfaces
for (int i = 0; i < INTERFACE_COUNT; i++) {
stopRadio(interface_obj[i]);
}
}
2018-04-26 15:52:43 +02:00
}
2024-06-10 14:05:21 +02:00
buffer_serial();
if (!fifo_isempty(&serialFIFO)) serial_poll();
#if HAS_DISPLAY
#if DISPLAY == OLED
2022-10-29 16:44:49 +02:00
if (disp_ready) update_display();
#elif DISPLAY == EINK_BW || DISPLAY == EINK_3C
// Display refreshes take so long on e-paper displays that they can disrupt
// the regular operation of the device. To combat this the time it is
// chosen to do so must be strategically chosen. Particularly on the
// RAK4631, the display and the potentially installed SX1280 modem share
// the same SPI bus. Thus it is not possible to solve this by utilising the
// callback functionality to poll the modem in this case. todo, this may be
// able to be improved in the future.
if (disp_ready) {
if (millis() - last_tx >= 4000) {
if (millis() - last_rx >= 1000) {
update_display();
}
}
}
#endif
#endif
#if HAS_PMU
2022-10-29 16:44:49 +02:00
if (pmu_ready) update_pmu();
#endif
2022-10-30 13:51:36 +01:00
#if HAS_BLUETOOTH || HAS_BLE == true
2023-01-07 16:35:07 +01:00
if (!console_active && bt_ready) update_bt();
2022-10-30 13:51:36 +01:00
#endif
2024-05-02 01:20:33 +02:00
#if HAS_BUZZER
update_buzzer_notone();
#endif
if (memory_low) {
#if PLATFORM == PLATFORM_ESP32
if (esp_get_free_heap_size() < 8192) {
kiss_indicate_error(ERROR_MEMORY_LOW); memory_low = false;
} else {
memory_low = false;
}
#else
kiss_indicate_error(ERROR_MEMORY_LOW); memory_low = false;
#endif
}
2024-05-02 01:20:33 +02:00
}
2024-05-20 21:14:16 +02:00
void process_serial() {
buffer_serial();
if (!fifo_isempty(&serialFIFO)) serial_poll();
}
#if HAS_INPUT
2024-05-02 01:20:33 +02:00
void button_event(uint8_t event, unsigned long duration) {
if (duration > BUTTON_MIN_DURATION) {
2024-10-23 12:16:13 +02:00
if (duration > BUTTON_9S_DURATION) {
bt_bond_wipe();
} else if (duration > BUTTON_6S_DURATION) {
2024-09-17 17:19:14 +02:00
bt_stop();
bt_conf_save(false);
2024-09-17 17:19:14 +02:00
} else if (duration > BUTTON_3S_DURATION) {
bt_enable_pairing();
} else {
#if HAS_BUZZER_CTRL
toggle_buzzer_enable();
#endif
2024-09-17 17:19:14 +02:00
}
2024-05-02 01:20:33 +02:00
}
2020-05-28 22:18:19 +02:00
}
#endif
2020-05-28 22:18:19 +02:00
void poll_buffers() {
process_serial();
}
2020-05-29 14:58:10 +02:00
volatile bool serial_polling = false;
2020-05-28 22:18:19 +02:00
void serial_poll() {
2020-05-29 14:58:10 +02:00
serial_polling = true;
2022-01-09 23:40:30 +01:00
while (!fifo_isempty(&serialFIFO)) {
2020-05-29 14:58:10 +02:00
char sbyte = fifo_pop(&serialFIFO);
2018-04-05 18:10:42 +02:00
serialCallback(sbyte);
2020-05-28 22:18:19 +02:00
}
2020-05-29 14:58:10 +02:00
serial_polling = false;
2020-05-28 22:18:19 +02:00
}
2024-06-10 14:05:21 +02:00
#define MAX_CYCLES 20
2020-05-28 22:18:19 +02:00
void buffer_serial() {
2020-05-29 14:58:10 +02:00
if (!serial_buffering) {
serial_buffering = true;
uint8_t c = 0;
2022-10-30 18:35:53 +01:00
#if HAS_BLUETOOTH || HAS_BLE == true
2022-10-30 18:35:53 +01:00
while (
c < MAX_CYCLES &&
( (bt_state != BT_STATE_CONNECTED && Serial.available()) || (bt_state == BT_STATE_CONNECTED && SerialBT.available()) )
)
#else
while (c < MAX_CYCLES && Serial.available())
#endif
{
2020-05-29 14:58:10 +02:00
c++;
2024-06-10 14:05:21 +02:00
#if HAS_BLUETOOTH || HAS_BLE == true
2024-01-21 10:05:02 +01:00
if (bt_state == BT_STATE_CONNECTED) {
2022-10-30 18:35:53 +01:00
if (!fifo_isfull(&serialFIFO)) {
fifo_push(&serialFIFO, SerialBT.read());
}
} else {
if (!fifo_isfull(&serialFIFO)) {
fifo_push(&serialFIFO, Serial.read());
}
2022-01-09 23:40:30 +01:00
}
2024-01-21 10:05:02 +01:00
#else
if (!fifo_isfull(&serialFIFO)) {
fifo_push(&serialFIFO, Serial.read());
}
2022-01-09 23:40:30 +01:00
#endif
2018-06-27 10:22:44 +02:00
}
2020-05-29 14:58:10 +02:00
serial_buffering = false;
2018-04-05 18:10:42 +02:00
}
}