1564 lines
46 KiB
C++
1564 lines
46 KiB
C++
// Copyright (C) 2024, Mark Qvist
|
|
|
|
// 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/>.
|
|
|
|
#include <Arduino.h>
|
|
#include <SPI.h>
|
|
#include "Utilities.h"
|
|
|
|
#if MCU_VARIANT == MCU_NRF52
|
|
#define INTERFACE_SPI
|
|
#if 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
|
|
|
|
FIFOBuffer serialFIFO;
|
|
uint8_t serialBuffer[CONFIG_UART_BUFFER_SIZE+1];
|
|
|
|
uint16_t packet_starts_buf[(CONFIG_QUEUE_MAX_LENGTH)+1];
|
|
|
|
uint16_t packet_lengths_buf[(CONFIG_QUEUE_MAX_LENGTH)+1];
|
|
|
|
FIFOBuffer16 packet_starts[INTERFACE_COUNT];
|
|
FIFOBuffer16 packet_lengths[INTERFACE_COUNT];
|
|
|
|
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};
|
|
volatile bool serial_buffering = false;
|
|
#if HAS_BLUETOOTH || HAS_BLE == true
|
|
bool bt_init_ran = false;
|
|
#endif
|
|
|
|
#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;
|
|
uint8_t data[];
|
|
} modem_packet_t;
|
|
static xQueueHandle modem_packet_queue = NULL;
|
|
|
|
char sbuf[128];
|
|
|
|
uint8_t *packet_queue[INTERFACE_COUNT];
|
|
|
|
void setup() {
|
|
#if MCU_VARIANT == MCU_ESP32
|
|
boot_seq();
|
|
EEPROM.begin(EEPROM_SIZE);
|
|
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
|
|
#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);
|
|
|
|
// Initialise serial communication
|
|
memset(serialBuffer, 0, sizeof(serialBuffer));
|
|
fifo_init(&serialFIFO, serialBuffer, CONFIG_UART_BUFFER_SIZE);
|
|
|
|
Serial.begin(serial_baudrate);
|
|
|
|
#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.
|
|
while (!Serial);
|
|
#endif
|
|
|
|
// Configure input and output pins
|
|
#if HAS_INPUT
|
|
input_init();
|
|
#endif
|
|
|
|
#if HAS_BUZZER
|
|
pinMode(PIN_BUZZER, OUTPUT);
|
|
#endif
|
|
|
|
#if HAS_NP == false
|
|
pinMode(pin_led_rx, OUTPUT);
|
|
pinMode(pin_led_tx, OUTPUT);
|
|
#endif
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
// Initialise buffers
|
|
memset(pbuf, 0, sizeof(pbuf));
|
|
memset(cmdbuf, 0, sizeof(cmdbuf));
|
|
|
|
memset(packet_starts_buf, 0, sizeof(packet_starts_buf));
|
|
memset(packet_lengths_buf, 0, sizeof(packet_starts_buf));
|
|
|
|
memset(seq, 0xFF, sizeof(seq));
|
|
|
|
modem_packet_queue = xQueueCreate(MODEM_QUEUE_SIZE, sizeof(modem_packet_t*));
|
|
|
|
for (int i = 0; i < INTERFACE_COUNT; i++) {
|
|
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);
|
|
packet_queue[i] = (uint8_t*)malloc(getQueueSize(i)+1);
|
|
}
|
|
|
|
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
|
|
|
|
// 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],
|
|
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],
|
|
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;
|
|
}
|
|
|
|
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],
|
|
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],
|
|
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],
|
|
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],
|
|
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;
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
#if HAS_DISPLAY
|
|
#if HAS_EEPROM
|
|
if (EEPROM.read(eeprom_addr(ADDR_CONF_DSET)) != CONF_OK_BYTE) {
|
|
#elif MCU_VARIANT == MCU_NRF52
|
|
if (eeprom_read(eeprom_addr(ADDR_CONF_DSET)) != CONF_OK_BYTE) {
|
|
#endif
|
|
eeprom_update(eeprom_addr(ADDR_CONF_DSET), CONF_OK_BYTE);
|
|
eeprom_update(eeprom_addr(ADDR_CONF_DINT), 0xFF);
|
|
}
|
|
#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
|
|
|
|
if (console_active) {
|
|
#if HAS_CONSOLE
|
|
console_start();
|
|
#else
|
|
kiss_indicate_reset();
|
|
#endif
|
|
} else {
|
|
kiss_indicate_reset();
|
|
}
|
|
|
|
// Validate board health, EEPROM and config
|
|
validate_status();
|
|
}
|
|
|
|
void lora_receive(RadioInterface* radio) {
|
|
if (!implicit) {
|
|
radio->receive();
|
|
} else {
|
|
radio->receive(implicit_l);
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
serial_write(FEND);
|
|
// Add index of interface the packet came from
|
|
serial_write(cmd_byte);
|
|
|
|
for (uint16_t i = 0; i < read_len; i++) {
|
|
#if MCU_VARIANT == MCU_NRF52
|
|
portENTER_CRITICAL();
|
|
uint8_t byte = pbuf[i];
|
|
portEXIT_CRITICAL();
|
|
#else
|
|
uint8_t byte = pbuf[i];
|
|
#endif
|
|
|
|
if (byte == FEND) { serial_write(FESC); byte = TFEND; }
|
|
if (byte == FESC) { serial_write(FESC); byte = TFESC; }
|
|
serial_write(byte);
|
|
}
|
|
|
|
serial_write(FEND);
|
|
read_len = 0;
|
|
|
|
#if MCU_VARIANT == MCU_ESP32 && HAS_BLE
|
|
bt_flush();
|
|
#endif
|
|
}
|
|
|
|
inline void getPacketData(RadioInterface* radio, uint16_t len) {
|
|
#if MCU_VARIANT != MCU_NRF52
|
|
while (len-- && read_len < MTU) {
|
|
pbuf[read_len++] = radio->read();
|
|
}
|
|
#else
|
|
BaseType_t int_mask = taskENTER_CRITICAL_FROM_ISR();
|
|
while (len-- && read_len < MTU) {
|
|
pbuf[read_len++] = radio->read();
|
|
}
|
|
taskEXIT_CRITICAL_FROM_ISR(int_mask);
|
|
#endif
|
|
}
|
|
|
|
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);
|
|
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;
|
|
memcpy(modem_packet->data, pbuf, read_len);
|
|
if (!modem_packet_queue || xQueueSendFromISR(modem_packet_queue, &modem_packet, NULL) != pdPASS) {
|
|
free(modem_packet);
|
|
return false;
|
|