Merge pull request #16 from interfect/linux-pr
Allow building for Linux as a Linux binary
This commit is contained in:
commit
475147711c
50
Config.h
50
Config.h
|
@ -1,18 +1,13 @@
|
|||
#include "ROM.h"
|
||||
|
||||
#include "Platform.h"
|
||||
|
||||
#ifndef CONFIG_H
|
||||
#define CONFIG_H
|
||||
|
||||
#define MAJ_VERS 0x01
|
||||
#define MIN_VERS 0x1B
|
||||
|
||||
#define PLATFORM_AVR 0x90
|
||||
#define PLATFORM_ESP32 0x80
|
||||
|
||||
#define MCU_1284P 0x91
|
||||
#define MCU_2560 0x92
|
||||
#define MCU_ESP32 0x81
|
||||
|
||||
#define BOARD_RNODE 0x31
|
||||
#define BOARD_HMBRW 0x32
|
||||
#define BOARD_TBEAM 0x33
|
||||
|
@ -21,22 +16,12 @@
|
|||
#define BOARD_LORA32_V2_0 0x36
|
||||
#define BOARD_LORA32_V2_1 0x37
|
||||
|
||||
#define SERIAL_INTERRUPT 0x1
|
||||
#define SERIAL_POLLING 0x2
|
||||
|
||||
#define MODE_HOST 0x11
|
||||
#define MODE_TNC 0x12
|
||||
|
||||
#if defined(__AVR_ATmega1284P__)
|
||||
#define PLATFORM PLATFORM_AVR
|
||||
#define MCU_VARIANT MCU_1284P
|
||||
#elif defined(__AVR_ATmega2560__)
|
||||
#define PLATFORM PLATFORM_AVR
|
||||
#define MCU_VARIANT MCU_2560
|
||||
#elif defined(ESP32)
|
||||
#define PLATFORM PLATFORM_ESP32
|
||||
#define MCU_VARIANT MCU_ESP32
|
||||
#else
|
||||
#error "The firmware cannot be compiled for the selected MCU variant"
|
||||
#endif
|
||||
|
||||
#define MTU 500
|
||||
#define SINGLE_MTU 255
|
||||
#define HEADER_L 1
|
||||
|
@ -54,6 +39,7 @@
|
|||
const int pin_led_tx = 13;
|
||||
|
||||
#define BOARD_MODEL BOARD_RNODE
|
||||
#define SERIAL_EVENTS SERIAL_INTERRUPT
|
||||
|
||||
#define CONFIG_UART_BUFFER_SIZE 6144
|
||||
#define CONFIG_QUEUE_SIZE 6144
|
||||
|
@ -70,6 +56,7 @@
|
|||
const int pin_led_tx = 13;
|
||||
|
||||
#define BOARD_MODEL BOARD_HMBRW
|
||||
#define SERIAL_EVENTS SERIAL_INTERRUPT
|
||||
|
||||
#define CONFIG_UART_BUFFER_SIZE 768
|
||||
#define CONFIG_QUEUE_SIZE 5120
|
||||
|
@ -131,6 +118,8 @@
|
|||
#error An unsupported board was selected. Cannot compile RNode firmware.
|
||||
#endif
|
||||
|
||||
#define SERIAL_EVENTS SERIAL_POLLING
|
||||
|
||||
#define CONFIG_UART_BUFFER_SIZE 6144
|
||||
#define CONFIG_QUEUE_SIZE 6144
|
||||
#define CONFIG_QUEUE_MAX_LENGTH 200
|
||||
|
@ -141,6 +130,22 @@
|
|||
#define GPS_BAUD_RATE 9600
|
||||
#define PIN_GPS_TX 12
|
||||
#define PIN_GPS_RX 34
|
||||
#elif MCU_VARIANT == MCU_LINUX
|
||||
const int pin_cs = -1;
|
||||
const int pin_reset = -1;
|
||||
const int pin_dio = -1;
|
||||
const int pin_led_rx = -1;
|
||||
const int pin_led_tx = -1;
|
||||
|
||||
#define BOARD_MODEL BOARD_HMBRW
|
||||
#define SERIAL_EVENTS SERIAL_POLLING
|
||||
|
||||
#define CONFIG_UART_BUFFER_SIZE 6144
|
||||
#define CONFIG_QUEUE_SIZE 6144
|
||||
#define CONFIG_QUEUE_MAX_LENGTH 200
|
||||
|
||||
#define EEPROM_SIZE 4096
|
||||
#define EEPROM_OFFSET EEPROM_SIZE-EEPROM_RESERVED
|
||||
#endif
|
||||
|
||||
#if BOARD_MODEL == BOARD_TBEAM
|
||||
|
@ -149,6 +154,11 @@
|
|||
#define PMU_IRQ 35
|
||||
#endif
|
||||
|
||||
#if LIBRARY_TYPE == LIBRARY_C
|
||||
// We need standard int types before we go any further
|
||||
#include <cstdint>
|
||||
#endif
|
||||
|
||||
#define eeprom_addr(a) (a+EEPROM_OFFSET)
|
||||
|
||||
// MCU independent configuration parameters
|
||||
|
|
283
LoRa.cpp
283
LoRa.cpp
|
@ -6,23 +6,23 @@
|
|||
|
||||
#include "LoRa.h"
|
||||
|
||||
#define MCU_1284P 0x91
|
||||
#define MCU_2560 0x92
|
||||
#define MCU_ESP32 0x81
|
||||
#if defined(__AVR_ATmega1284P__)
|
||||
#define PLATFORM PLATFORM_AVR
|
||||
#define MCU_VARIANT MCU_1284P
|
||||
#elif defined(__AVR_ATmega2560__)
|
||||
#define PLATFORM PLATFORM_AVR
|
||||
#define MCU_VARIANT MCU_2560
|
||||
#elif defined(ESP32)
|
||||
#define PLATFORM PLATFORM_ESP32
|
||||
#define MCU_VARIANT MCU_ESP32
|
||||
#if LIBRARY_TYPE == LIBRARY_C
|
||||
// We need sleep() to use instead of yield()
|
||||
#include <unistd.h>
|
||||
// And we need to use the filesystem and IOCTLs instead of an SPI global
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/spi/spidev.h>
|
||||
// And to have memset
|
||||
#include <cstring>
|
||||
// And we need to be able to report errors
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
// And we need IO formatting functions for the C++-stream dumpRegisters()
|
||||
#include <iomanip>
|
||||
#endif
|
||||
|
||||
#ifndef MCU_VARIANT
|
||||
#error No MCU variant defined, cannot compile
|
||||
#endif
|
||||
|
||||
|
||||
#if MCU_VARIANT == MCU_ESP32
|
||||
#include "soc/rtc_wdt.h"
|
||||
|
@ -38,11 +38,14 @@
|
|||
#define REG_FRF_MID 0x07
|
||||
#define REG_FRF_LSB 0x08
|
||||
#define REG_PA_CONFIG 0x09
|
||||
#define REG_PA_RAMP 0x0a
|
||||
#define REG_OCP 0x0b
|
||||
#define REG_LNA 0x0c
|
||||
#define REG_FIFO_ADDR_PTR 0x0d
|
||||
#define REG_FIFO_TX_BASE_ADDR 0x0e
|
||||
#define REG_FIFO_RX_BASE_ADDR 0x0f
|
||||
#define REG_FIFO_RX_CURRENT_ADDR 0x10
|
||||
#define REG_IRQ_FLAGS_MASK 0x11
|
||||
#define REG_IRQ_FLAGS 0x12
|
||||
#define REG_RX_NB_BYTES 0x13
|
||||
#define REG_MODEM_STAT 0x18
|
||||
|
@ -50,19 +53,38 @@
|
|||
#define REG_PKT_RSSI_VALUE 0x1a
|
||||
#define REG_MODEM_CONFIG_1 0x1d
|
||||
#define REG_MODEM_CONFIG_2 0x1e
|
||||
#define REG_SYMB_TIMEOUT_LSB 0x1f
|
||||
#define REG_PREAMBLE_MSB 0x20
|
||||
#define REG_PREAMBLE_LSB 0x21
|
||||
#define REG_PAYLOAD_LENGTH 0x22
|
||||
#define REG_PAYLOAD_MAX_LENGTH 0x23
|
||||
#define REG_HOP_PERIOD 0x24
|
||||
#define REG_MODEM_CONFIG_3 0x26
|
||||
#define REG_PPM_CORRECTION 0x27
|
||||
#define REG_FREQ_ERROR_MSB 0x28
|
||||
#define REG_FREQ_ERROR_MID 0x29
|
||||
#define REG_FREQ_ERROR_LSB 0x2a
|
||||
#define REG_RSSI_WIDEBAND 0x2c
|
||||
#define REG_IF_FREQ_2 0x2f
|
||||
#define REG_IF_FREQ_1 0x30
|
||||
#define REG_DETECTION_OPTIMIZE 0x31
|
||||
#define REG_INVERT_IQ 0x33
|
||||
#define REG_HIGH_BW_OPTIMIZE_1 0x36
|
||||
#define REG_DETECTION_THRESHOLD 0x37
|
||||
#define REG_SYNC_WORD 0x39
|
||||
#define REG_HIGH_BW_OPTIMIZE_2 0x3a
|
||||
#define REG_INVERT_IQ_2 0x3b
|
||||
#define REG_DIO_MAPPING_1 0x40
|
||||
#define REG_VERSION 0x42
|
||||
#define REG_TXCO 0x4B
|
||||
#define REG_PA_DAC 0x4D
|
||||
// These registers have different values in high and low frequency modes (flag 0x08 in mode)
|
||||
// We always stay in high frequency mode (flag is 0)
|
||||
#define REG_AGC_REF 0x61
|
||||
#define REG_AGC_THRESHOLD_1 0x62
|
||||
#define REG_AGC_THRESHOLD_2 0x63
|
||||
#define REG_AGC_THRESHOLD_3 0x64
|
||||
#define REG_PLL 0x70
|
||||
|
||||
// Modes
|
||||
#define MODE_LONG_RANGE_MODE 0x80
|
||||
|
@ -88,41 +110,50 @@ LoRaClass::LoRaClass() :
|
|||
_frequency(0),
|
||||
_packetIndex(0),
|
||||
_implicitHeaderMode(0),
|
||||
_onReceive(NULL)
|
||||
_onReceive(NULL),
|
||||
_spiBegun(false)
|
||||
{
|
||||
#if LIBRARY_TYPE == LIBRARY_ARDUINO
|
||||
// overide Stream timeout value
|
||||
setTimeout(0);
|
||||
#elif LIBRARY_TYPE == LIBRARY_C
|
||||
_fd = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int LoRaClass::begin(long frequency)
|
||||
{
|
||||
|
||||
#if LIBRARY_TYPE == LIBRARY_ARDUINO
|
||||
// setup pins
|
||||
pinMode(_ss, OUTPUT);
|
||||
// set SS high
|
||||
digitalWrite(_ss, HIGH);
|
||||
#endif
|
||||
|
||||
if (_reset != -1) {
|
||||
pinMode(_reset, OUTPUT);
|
||||
|
||||
// perform reset
|
||||
digitalWrite(_reset, LOW);
|
||||
delay(10);
|
||||
digitalWrite(_reset, HIGH);
|
||||
delay(10);
|
||||
}
|
||||
|
||||
#if LIBRARY_TYPE == LIBRARY_ARDUINO
|
||||
// start SPI
|
||||
SPI.begin();
|
||||
#elif LIBRARY_TYPE == LIBRARY_C
|
||||
const char* spi_filename = "/dev/spidev0.0";
|
||||
// We need to be re-entrant for restart
|
||||
if (_fd <= 0) {
|
||||
std::cerr << "Opening SPI device " << spi_filename << std::endl;
|
||||
_fd = open(spi_filename, O_RDWR);
|
||||
if (_fd <= 0) {
|
||||
perror("could not open SPI device");
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
std::cerr << "Skipping LoRa SPI reinitialization" << std::endl;
|
||||
}
|
||||
#endif
|
||||
_spiBegun = true;
|
||||
|
||||
// check version
|
||||
uint8_t version = readRegister(REG_VERSION);
|
||||
if (version != 0x12) {
|
||||
if (!resetModem()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// put in sleep mode
|
||||
sleep();
|
||||
|
||||
// set frequency
|
||||
setFrequency(frequency);
|
||||
|
||||
|
@ -147,11 +178,21 @@ int LoRaClass::begin(long frequency)
|
|||
|
||||
void LoRaClass::end()
|
||||
{
|
||||
// We need to be safe to call when the main loop is shutting down because
|
||||
// it's in a bad state, even if we ourselves haven't been begun yet. We can't
|
||||
// safely talk to the modem if the SPI link isn't begun, though.
|
||||
if (_spiBegun) {
|
||||
// put in sleep mode
|
||||
sleep();
|
||||
this->sleep();
|
||||
}
|
||||
|
||||
#if LIBRARY_TYPE == LIBRARY_ARDUINO
|
||||
// stop SPI
|
||||
SPI.end();
|
||||
#elif LIBRARY_TYPE == LIBRARY_C
|
||||
// Don't do anything. We need to keep things open for restart.
|
||||
#endif
|
||||
_spiBegun = false;
|
||||
}
|
||||
|
||||
int LoRaClass::beginPacket(int implicitHeader)
|
||||
|
@ -179,7 +220,11 @@ int LoRaClass::endPacket()
|
|||
|
||||
// wait for TX done
|
||||
while ((readRegister(REG_IRQ_FLAGS) & IRQ_TX_DONE_MASK) == 0) {
|
||||
#if LIBRARY_TYPE == LIBRARY_ARDUINO
|
||||
yield();
|
||||
#elif LIBRARY_TYPE == LIBRARY_C
|
||||
::sleep(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
// clear IRQ's
|
||||
|
@ -271,13 +316,13 @@ float ISR_VECT LoRaClass::packetSnr() {
|
|||
long LoRaClass::packetFrequencyError()
|
||||
{
|
||||
int32_t freqError = 0;
|
||||
freqError = static_cast<int32_t>(readRegister(REG_FREQ_ERROR_MSB) & B111);
|
||||
freqError = static_cast<int32_t>(readRegister(REG_FREQ_ERROR_MSB) & 0b111);
|
||||
freqError <<= 8L;
|
||||
freqError += static_cast<int32_t>(readRegister(REG_FREQ_ERROR_MID));
|
||||
freqError <<= 8L;
|
||||
freqError += static_cast<int32_t>(readRegister(REG_FREQ_ERROR_LSB));
|
||||
|
||||
if (readRegister(REG_FREQ_ERROR_MSB) & B1000) { // Sign bit is on
|
||||
if (readRegister(REG_FREQ_ERROR_MSB) & 0b1000) { // Sign bit is on
|
||||
freqError -= 524288; // B1000'0000'0000'0000'0000
|
||||
}
|
||||
|
||||
|
@ -350,22 +395,42 @@ void LoRaClass::flush()
|
|||
{
|
||||
}
|
||||
|
||||
void LoRaClass::pollReceive()
|
||||
{
|
||||
int irqFlags = readRegister(REG_IRQ_FLAGS);
|
||||
|
||||
// clear IRQ's
|
||||
writeRegister(REG_IRQ_FLAGS, irqFlags);
|
||||
|
||||
if ((irqFlags & IRQ_RX_DONE_MASK) && !(irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK)) {
|
||||
// received a packet
|
||||
handleRx();
|
||||
}
|
||||
}
|
||||
|
||||
void LoRaClass::onReceive(void(*callback)(int))
|
||||
{
|
||||
_onReceive = callback;
|
||||
|
||||
if (callback) {
|
||||
#if LIBRARY_TYPE == LIBRARY_ARDUINO
|
||||
pinMode(_dio0, INPUT);
|
||||
#endif
|
||||
|
||||
writeRegister(REG_DIO_MAPPING_1, 0x00);
|
||||
|
||||
#if MCU_VARIANT != MCU_LINUX && LIBRARY_TYPE == LIBRARY_ARDUINO
|
||||
#ifdef SPI_HAS_NOTUSINGINTERRUPT
|
||||
SPI.usingInterrupt(digitalPinToInterrupt(_dio0));
|
||||
#endif
|
||||
attachInterrupt(digitalPinToInterrupt(_dio0), LoRaClass::onDio0Rise, RISING);
|
||||
#endif
|
||||
} else {
|
||||
#if MCU_VARIANT != MCU_LINUX && LIBRARY_TYPE == LIBRARY_ARDUINO
|
||||
detachInterrupt(digitalPinToInterrupt(_dio0));
|
||||
#ifdef SPI_HAS_NOTUSINGINTERRUPT
|
||||
SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0));
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -572,6 +637,7 @@ void LoRaClass::setSPIFrequency(uint32_t frequency)
|
|||
_spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0);
|
||||
}
|
||||
|
||||
#if LIBRARY_TYPE == LIBRARY_ARDUINO
|
||||
void LoRaClass::dumpRegisters(Stream& out)
|
||||
{
|
||||
for (int i = 0; i < 128; i++) {
|
||||
|
@ -581,6 +647,94 @@ void LoRaClass::dumpRegisters(Stream& out)
|
|||
out.println(readRegister(i), HEX);
|
||||
}
|
||||
}
|
||||
#elif LIBRARY_TYPE == LIBRARY_C
|
||||
void LoRaClass::dumpRegisters(std::ostream& out)
|
||||
{
|
||||
for (int i = 0; i < 128; i++) {
|
||||
out << "0x" << std::hex << i << ": 0x" << std::hex << readRegister(i) << std::endl;
|
||||
}
|
||||
out << std::dec;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool LoRaClass::resetModem()
|
||||
{
|
||||
// Reset the modem to a known good default state and put it into sleep mode.
|
||||
// Returns false if the modem doesn't appear to be the right version.
|
||||
#if LIBRARY_TYPE == LIBRARY_ARDUINO
|
||||
if (_reset != -1) {
|
||||
pinMode(_reset, OUTPUT);
|
||||
|
||||
// perform reset
|
||||
digitalWrite(_reset, LOW);
|
||||
delay(10);
|
||||
digitalWrite(_reset, HIGH);
|
||||
delay(10);
|
||||
}
|
||||
#endif
|
||||
// check version
|
||||
uint8_t version = readRegister(REG_VERSION);
|
||||
if (version != 0x12) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this->sleep();
|
||||
|
||||
#if LIBRARY_TYPE == LIBRARY_C
|
||||
byte CLEAN_STATE[] = {
|
||||
REG_PA_RAMP, 0x09,
|
||||
REG_FRF_MSB, 0x6c,
|
||||
REG_FRF_MID, 0x80,
|
||||
REG_FRF_LSB, 0x00,
|
||||
REG_PA_CONFIG, 0x4f,
|
||||
REG_PA_RAMP, 0x09,
|
||||
REG_OCP, 0x2b,
|
||||
REG_LNA, 0x20,
|
||||
REG_FIFO_ADDR_PTR, 0x00,
|
||||
REG_FIFO_TX_BASE_ADDR, 0x80,
|
||||
REG_FIFO_RX_BASE_ADDR, 0x00,
|
||||
REG_FIFO_RX_CURRENT_ADDR, 0x00,
|
||||
REG_IRQ_FLAGS_MASK, 0x00,
|
||||
REG_MODEM_CONFIG_1, 0x72,
|
||||
REG_MODEM_CONFIG_2, 0x70,
|
||||
REG_SYMB_TIMEOUT_LSB, 0x64,
|
||||
REG_PREAMBLE_MSB, 0x00,
|
||||
REG_PREAMBLE_LSB, 0x08,
|
||||
REG_PAYLOAD_LENGTH, 0x01,
|
||||
REG_PAYLOAD_MAX_LENGTH, 0xff,
|
||||
REG_HOP_PERIOD, 0x00,
|
||||
REG_MODEM_CONFIG_3, 0x04,
|
||||
REG_PPM_CORRECTION, 0x00,
|
||||
REG_DETECTION_OPTIMIZE, 0xc3, // Errata says this needs to be set before REG_IF_FREQ_1 and REG_IF_FREQ_2
|
||||
REG_IF_FREQ_2, 0x45, // Datasheet says this defaults to 0x20, but dumping says 0x45.
|
||||
REG_IF_FREQ_1, 0x55, // Datasheet says this defaults to 0x00, but dumping says 0x55.
|
||||
REG_INVERT_IQ, 0x27,
|
||||
REG_HIGH_BW_OPTIMIZE_1, 0x03,
|
||||
REG_DETECTION_THRESHOLD, 0x0a,
|
||||
REG_SYNC_WORD, 0x12,
|
||||
REG_HIGH_BW_OPTIMIZE_2, 0x52, // Datasheet says this defaults to 0x20, but dumping says 0x52.
|
||||
REG_INVERT_IQ_2, 0x1d,
|
||||
REG_TXCO, 0x09,
|
||||
REG_PA_DAC, 0x84,
|
||||
// These are the high frequency mode (mode flag 0x08 is 0) values
|
||||
REG_AGC_REF, 0x1C,
|
||||
REG_AGC_THRESHOLD_1, 0x0e,
|
||||
REG_AGC_THRESHOLD_2, 0x5b,
|
||||
REG_AGC_THRESHOLD_3, 0xcc,
|
||||
REG_PLL, 0xd0,
|
||||
0, 0
|
||||
};
|
||||
|
||||
|
||||
// Manually set important registers to default values because we can't
|
||||
// reset.
|
||||
for (int i = 0; CLEAN_STATE[i] != 0; i += 2) {
|
||||
writeRegister(CLEAN_STATE[i], CLEAN_STATE[i + 1]);
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void LoRaClass::explicitHeaderMode()
|
||||
{
|
||||
|
@ -605,6 +759,13 @@ void ISR_VECT LoRaClass::handleDio0Rise()
|
|||
writeRegister(REG_IRQ_FLAGS, irqFlags);
|
||||
|
||||
if ((irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) {
|
||||
// received a packet
|
||||
handleRx();
|
||||
}
|
||||
}
|
||||
|
||||
void ISR_VECT LoRaClass::handleRx()
|
||||
{
|
||||
// received a packet
|
||||
_packetIndex = 0;
|
||||
|
||||
|
@ -621,7 +782,6 @@ void ISR_VECT LoRaClass::handleDio0Rise()
|
|||
// reset FIFO address
|
||||
writeRegister(REG_FIFO_ADDR_PTR, 0);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t ISR_VECT LoRaClass::readRegister(uint8_t address)
|
||||
{
|
||||
|
@ -637,6 +797,8 @@ uint8_t ISR_VECT LoRaClass::singleTransfer(uint8_t address, uint8_t value)
|
|||
{
|
||||
uint8_t response;
|
||||
|
||||
#if LIBRARY_TYPE == LIBRARY_ARDUINO
|
||||
// Select chip, send address, and send/read data, the Arduino way
|
||||
digitalWrite(_ss, LOW);
|
||||
|
||||
SPI.beginTransaction(_spiSettings);
|
||||
|
@ -645,6 +807,55 @@ uint8_t ISR_VECT LoRaClass::singleTransfer(uint8_t address, uint8_t value)
|
|||
SPI.endTransaction();
|
||||
|
||||
digitalWrite(_ss, HIGH);
|
||||
#elif LIBRARY_TYPE == LIBRARY_C
|
||||
// Select chip, send address, and send/read data, the Linux way
|
||||
|
||||
// In Linux, chip select is automatically turned off outside of transactions.
|
||||
|
||||
int status;
|
||||
|
||||
if (_fd <= 0) {
|
||||
throw std::runtime_error("Accessing SPI device without begin()!");
|
||||
}
|
||||
|
||||
// Configure SPI speed and mode to match settings
|
||||
status = ioctl(_fd, SPI_IOC_WR_MODE, &_spiSettings.mode);
|
||||
if (status < 0) {
|
||||
perror("ioctl SPI_IOC_WR_MODE failed");
|
||||
exit(1);
|
||||
}
|
||||
status = ioctl(_fd, SPI_IOC_WR_LSB_FIRST, &_spiSettings.bitness);
|
||||
if (status < 0) {
|
||||
perror("ioctl SPI_IOC_WR_LSB_FIRST failed");
|
||||
exit(1);
|
||||
}
|
||||
status = ioctl(_fd, SPI_IOC_WR_MAX_SPEED_HZ, &_spiSettings.frequency);
|
||||
if (status < 0) {
|
||||
perror("ioctl SPI_IOC_WR_MAX_SPEED_HZ failed");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// We have two transfers: one send-only to send the address, and one
|
||||
// send/receive, to send the value and get the response.
|
||||
struct spi_ioc_transfer xfer[2];
|
||||
memset(xfer, 0, sizeof xfer);
|
||||
|
||||
xfer[0].tx_buf = (unsigned long) &address;
|
||||
xfer[0].len = 1;
|
||||
|
||||
xfer[1].tx_buf = (unsigned long) &value;
|
||||
xfer[1].rx_buf = (unsigned long) &response;
|
||||
xfer[1].len = 1;
|
||||
|
||||
// Do the transaction
|
||||
status = ioctl(_fd, SPI_IOC_MESSAGE(2), xfer);
|
||||
if (status < 0) {
|
||||
perror("ioctl SPI_IOC_MESSAGE failed");
|
||||
exit(1);
|
||||
}
|
||||
#else
|
||||
#error "SPI transfer not implemented for library type"
|
||||
#endif
|
||||
|
||||
return response;
|
||||
}
|
||||
|
|
42
LoRa.h
42
LoRa.h
|
@ -7,8 +7,38 @@
|
|||
#ifndef LORA_H
|
||||
#define LORA_H
|
||||
|
||||
#include "Platform.h"
|
||||
|
||||
#if LIBRARY_TYPE == LIBRARY_ARDUINO
|
||||
#include <Arduino.h>
|
||||
#include <SPI.h>
|
||||
#elif LIBRARY_TYPE == LIBRARY_C
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
|
||||
// Arduino Stream is not available, but not actually needed.
|
||||
class Stream {};
|
||||
|
||||
typedef unsigned char byte;
|
||||
|
||||
// Arduino SPI is not available, so make a Linux-ish version of SPISettings
|
||||
#define MSBFIRST 0
|
||||
#define LSBFIRST 1
|
||||
#define SPI_MODE0 SPI_MODE_0
|
||||
#define SPI_MODE1 SPI_MODE_1
|
||||
#define SPI_MODE2 SPI_MODE_2
|
||||
#define SPI_MODE3 SPI_MODE_3
|
||||
class SPISettings {
|
||||
public:
|
||||
inline SPISettings(uint32_t frequency, byte bitness, byte mode) : frequency(frequency), bitness(bitness), mode(mode) {};
|
||||
SPISettings& operator=(const SPISettings& other) = default;
|
||||
uint32_t frequency;
|
||||
byte bitness;
|
||||
byte mode;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#define LORA_DEFAULT_SS_PIN 10
|
||||
#define LORA_DEFAULT_RESET_PIN 9
|
||||
|
@ -46,6 +76,7 @@ public:
|
|||
virtual int peek();
|
||||
virtual void flush();
|
||||
|
||||
void pollReceive();
|
||||
void onReceive(void(*callback)(int));
|
||||
|
||||
void receive(int size = 0);
|
||||
|
@ -74,13 +105,20 @@ public:
|
|||
void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN);
|
||||
void setSPIFrequency(uint32_t frequency);
|
||||
|
||||
#if LIBRARY_TYPE == LIBRARY_ARDUINO
|
||||
void dumpRegisters(Stream& out);
|
||||
#elif LIBRARY_TYPE == LIBRARY_C
|
||||
void dumpRegisters(std::ostream& out);
|
||||
#endif
|
||||
|
||||
private:
|
||||
bool resetModem();
|
||||
|
||||
void explicitHeaderMode();
|
||||
void implicitHeaderMode();
|
||||
|
||||
void handleDio0Rise();
|
||||
void handleRx();
|
||||
|
||||
uint8_t readRegister(uint8_t address);
|
||||
void writeRegister(uint8_t address, uint8_t value);
|
||||
|
@ -99,6 +137,10 @@ private:
|
|||
int _packetIndex;
|
||||
int _implicitHeaderMode;
|
||||
void (*_onReceive)(int);
|
||||
bool _spiBegun;
|
||||
#if LIBRARY_TYPE == LIBRARY_C
|
||||
int _fd;
|
||||
#endif
|
||||
};
|
||||
|
||||
extern LoRaClass LoRa;
|
||||
|
|
6
MD5.cpp
6
MD5.cpp
|
@ -1,5 +1,11 @@
|
|||
#include "MD5.h"
|
||||
|
||||
#if LIBRARY_TYPE == LIBRARY_ARDUINO
|
||||
#include <Arduino.h>
|
||||
#elif LIBRARY_TYPE == LIBRARY_C
|
||||
#include <cstdlib>
|
||||
#endif
|
||||
|
||||
MD5::MD5()
|
||||
{
|
||||
//nothing
|
||||
|
|
2
MD5.h
2
MD5.h
|
@ -1,7 +1,7 @@
|
|||
#ifndef MD5_h
|
||||
#define MD5_h
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "Platform.h"
|
||||
|
||||
/*
|
||||
* This is an OpenSSL-compatible implementation of the RSA Data Security,
|
||||
|
|
24
Makefile
24
Makefile
|
@ -10,8 +10,6 @@ prep-samd:
|
|||
arduino-cli core update-index --config-file arduino-cli.yaml
|
||||
arduino-cli core install adafruit:samd
|
||||
|
||||
|
||||
|
||||
firmware:
|
||||
arduino-cli compile --fqbn unsignedio:avr:rnode
|
||||
|
||||
|
@ -135,3 +133,25 @@ release-mega2560:
|
|||
arduino-cli compile --fqbn arduino:avr:mega -e
|
||||
cp build/arduino.avr.mega/RNode_Firmware.ino.hex Release/rnode_firmware_latest_m2560.hex
|
||||
rm -r build
|
||||
|
||||
clean:
|
||||
rm -Rf bin
|
||||
rm -Rf obj
|
||||
|
||||
CFLAGS += -g
|
||||
|
||||
obj/MD5.o: MD5.cpp MD5.h Platform.h
|
||||
mkdir -p obj
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
obj/LoRa.o: LoRa.cpp LoRa.h Platform.h
|
||||
mkdir -p obj
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
obj/RNode_Firmware.o: RNode_Firmware.ino Utilities.h Config.h LoRa.h ROM.h Framing.h MD5.h Platform.h
|
||||
mkdir -p obj
|
||||
$(CC) $(CFLAGS) -c -o $@ -x c++ $<
|
||||
|
||||
bin/rnode: obj/RNode_Firmware.o obj/LoRa.o obj/MD5.o
|
||||
mkdir -p bin
|
||||
$(CC) $(CFLAGS) -o $@ $^ -lstdc++ -lutil
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
#ifndef PLATFORM_H
|
||||
#define PLATFORM_H
|
||||
|
||||
// Determine the platform, MCU, and C library we are building for.
|
||||
|
||||
#define PLATFORM_AVR 0x90
|
||||
#define PLATFORM_ESP32 0x80
|
||||
#define PLATFORM_LINUX 0x70
|
||||
|
||||
#define MCU_1284P 0x91
|
||||
#define MCU_2560 0x92
|
||||
#define MCU_ESP32 0x81
|
||||
#define MCU_LINUX 0x71
|
||||
|
||||
#define LIBRARY_ARDUINO 0x1
|
||||
#define LIBRARY_C 0x2
|
||||
|
||||
#if defined(__AVR_ATmega1284P__)
|
||||
#define PLATFORM PLATFORM_AVR
|
||||
#define MCU_VARIANT MCU_1284P
|
||||
#define LIBRARY_TYPE LIBRARY_ARDUINO
|
||||
#elif defined(__AVR_ATmega2560__)
|
||||
#define PLATFORM PLATFORM_AVR
|
||||
#define MCU_VARIANT MCU_2560
|
||||
#define LIBRARY_TYPE LIBRARY_ARDUINO
|
||||
#elif defined(ESP32)
|
||||
#define PLATFORM PLATFORM_ESP32
|
||||
#define MCU_VARIANT MCU_ESP32
|
||||
#define LIBRARY_TYPE LIBRARY_ARDUINO
|
||||
#elif defined(__unix__)
|
||||
#define PLATFORM PLATFORM_LINUX
|
||||
#define MCU_VARIANT MCU_LINUX
|
||||
#define LIBRARY_TYPE LIBRARY_C
|
||||
#else
|
||||
#error "The firmware cannot be compiled for the selected MCU variant"
|
||||
#endif
|
||||
|
||||
#ifndef MCU_VARIANT
|
||||
#error No MCU variant defined, cannot compile
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -20,8 +20,12 @@
|
|||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include "Platform.h"
|
||||
|
||||
#if LIBRARY_TYPE == LIBRARY_ARDUINO
|
||||
#include <Arduino.h>
|
||||
#include <SPI.h>
|
||||
#endif
|
||||
#include "Utilities.h"
|
||||
|
||||
FIFOBuffer serialFIFO;
|
||||
|
@ -47,15 +51,26 @@ char sbuf[128];
|
|||
bool packet_ready = false;
|
||||
#endif
|
||||
|
||||
// Arduino C doesn't need pre-declarations to call functions that appear later,
|
||||
// but standard C does.
|
||||
void serial_interrupt_init();
|
||||
void validateStatus();
|
||||
void update_radio_lock();
|
||||
void transmit(uint16_t size);
|
||||
void buffer_serial();
|
||||
void serial_poll();
|
||||
|
||||
|
||||
void setup() {
|
||||
#if MCU_VARIANT == MCU_ESP32
|
||||
delay(500);
|
||||
EEPROM.begin(EEPROM_SIZE);
|
||||
Serial.setRxBufferSize(CONFIG_UART_BUFFER_SIZE);
|
||||
#endif
|
||||
eeprom_open(EEPROM_SIZE);
|
||||
|
||||
#if LIBRARY_TYPE == LIBRARY_ARDUINO
|
||||
// Seed the PRNG
|
||||
randomSeed(analogRead(0));
|
||||
#endif
|
||||
|
||||
// Initialise serial communication
|
||||
memset(serialBuffer, 0, sizeof(serialBuffer));
|
||||
|
@ -64,11 +79,17 @@ void setup() {
|
|||
Serial.begin(serial_baudrate);
|
||||
while (!Serial);
|
||||
|
||||
#if MCU_VARIANT == MCU_ESP32
|
||||
Serial.setRxBufferSize(CONFIG_UART_BUFFER_SIZE);
|
||||
#endif
|
||||
|
||||
serial_interrupt_init();
|
||||
|
||||
#if LIBRARY_TYPE == LIBRARY_ARDUINO
|
||||
// Configure input and output pins
|
||||
pinMode(pin_led_rx, OUTPUT);
|
||||
pinMode(pin_led_tx, OUTPUT);
|
||||
#endif
|
||||
|
||||
// Initialise buffers
|
||||
memset(pbuf, 0, sizeof(pbuf));
|
||||
|
@ -130,6 +151,9 @@ inline void getPacketData(uint16_t len) {
|
|||
}
|
||||
|
||||
void ISR_VECT receive_callback(int packet_size) {
|
||||
#if LIBRARY_TYPE == LIBRARY_C
|
||||
std::cerr << "Got packet of " << packet_size << " bytes" << std::endl;
|
||||
#endif
|
||||
if (!promisc) {
|
||||
// The standard operating mode allows large
|
||||
// packets with a payload up to 500 bytes,
|
||||
|
@ -144,6 +168,11 @@ void ISR_VECT receive_callback(int packet_size) {
|
|||
// This is the first part of a split
|
||||
// packet, so we set the seq variable
|
||||
// and add the data to the buffer
|
||||
|
||||
#if LIBRARY_TYPE == LIBRARY_C
|
||||
std::cerr << "\tIs first part of split packet" << std::endl;
|
||||
#endif
|
||||
|
||||
read_len = 0;
|
||||
seq = sequence;
|
||||
|
||||
|
@ -159,6 +188,10 @@ void ISR_VECT receive_callback(int packet_size) {
|
|||
// packet, so we add it to the buffer
|
||||
// and set the ready flag.
|
||||
|
||||
#if LIBRARY_TYPE == LIBRARY_C
|
||||
std::cerr << "\tIs second part of split packet" << std::endl;
|
||||
#endif
|
||||
|
||||
#if MCU_VARIANT != MCU_ESP32
|
||||
last_rssi = (last_rssi+LoRa.packetRssi())/2;
|
||||
last_snr_raw = (last_snr_raw+LoRa.packetSnrRaw())/2;
|
||||
|
@ -173,6 +206,11 @@ void ISR_VECT receive_callback(int packet_size) {
|
|||
// same sequence id, so we must assume
|
||||
// that we are seeing the first part of
|
||||
// a new split packet.
|
||||
|
||||
#if LIBRARY_TYPE == LIBRARY_C
|
||||
std::cerr << "\tIs first part of a different split packet" << std::endl;
|
||||
#endif
|
||||
|
||||
read_len = 0;
|
||||
seq = sequence;
|
||||
|
||||
|
@ -188,6 +226,10 @@ void ISR_VECT receive_callback(int packet_size) {
|
|||
// just read it and set the ready
|
||||
// flag to true.
|
||||
|
||||
#if LIBRARY_TYPE == LIBRARY_C
|
||||
std::cerr << "\tIs complete packet" << std::endl;
|
||||
#endif
|
||||
|
||||
if (seq != SEQ_UNSET) {
|
||||
// If we already had part of a split
|
||||
// packet in the buffer, we clear it.
|
||||
|
@ -318,7 +360,7 @@ void flushQueue(void) {
|
|||
|
||||
uint16_t processed = 0;
|
||||
|
||||
#if MCU_VARIANT == MCU_ESP32
|
||||
#if SERIAL_EVENTS == SERIAL_POLLING
|
||||
while (!fifo16_isempty(&packet_starts)) {
|
||||
#else
|
||||
while (!fifo16_isempty_locked(&packet_starts)) {
|
||||
|
@ -347,6 +389,9 @@ void flushQueue(void) {
|
|||
void transmit(uint16_t size) {
|
||||
if (radio_online) {
|
||||
if (!promisc) {
|
||||
#if LIBRARY_TYPE == LIBRARY_C
|
||||
std::cerr << "Sending RNode packet(s) of " << size << " bytes" << std::endl;
|
||||
#endif
|
||||
led_tx_on();
|
||||
uint16_t written = 0;
|
||||
uint8_t header = random(256) & 0xF0;
|
||||
|
@ -379,6 +424,11 @@ void transmit(uint16_t size) {
|
|||
// In promiscuous mode, we only send out
|
||||
// plain raw LoRa packets with a maximum
|
||||
// payload of 255 bytes
|
||||
|
||||
#if LIBRARY_TYPE == LIBRARY_C
|
||||
std::cerr << "Sending standard packet of " << size << " bytes" << std::endl;
|
||||
#endif
|
||||
|
||||
led_tx_on();
|
||||
uint16_t written = 0;
|
||||
|
||||
|
@ -683,13 +733,19 @@ void validateStatus() {
|
|||
uint8_t F_WDR = WDRF;
|
||||
#elif MCU_VARIANT == MCU_2560
|
||||
uint8_t boot_flags = OPTIBOOT_MCUSR;
|
||||
if (boot_flags == 0x00) boot_flags = 0x03;
|
||||
if (boot_flags == 0x00) boot_flags = START_FROM_BROWNOUT;
|
||||
uint8_t F_POR = PORF;
|
||||
uint8_t F_BOR = BORF;
|
||||
uint8_t F_WDR = WDRF;
|
||||
#elif MCU_VARIANT == MCU_ESP32
|
||||
// TODO: Get ESP32 boot flags
|
||||
uint8_t boot_flags = 0x02;
|
||||
uint8_t boot_flags = START_FROM_POWERON;
|
||||
uint8_t F_POR = 0x00;
|
||||
uint8_t F_BOR = 0x00;
|
||||
uint8_t F_WDR = 0x01;
|
||||
#elif MCU_VARIANT == MCU_LINUX
|
||||
// Linux build always works like a clean boot.
|
||||
uint8_t boot_flags = START_FROM_POWERON;
|
||||
uint8_t F_POR = 0x00;
|
||||
uint8_t F_BOR = 0x00;
|
||||
uint8_t F_WDR = 0x01;
|
||||
|
@ -702,12 +758,12 @@ void validateStatus() {
|
|||
} else if (boot_flags & (1<<F_WDR)) {
|
||||
boot_vector = START_FROM_BOOTLOADER;
|
||||
} else {
|
||||
Serial.write("Error, indeterminate boot vector\r\n");
|
||||
debug("Error, indeterminate boot vector\r\n");
|
||||
led_indicate_boot_error();
|
||||
}
|
||||
|
||||
if (boot_vector == START_FROM_BOOTLOADER || boot_vector == START_FROM_POWERON) {
|
||||
if (eeprom_lock_set()) {
|
||||
if (eeprom_info_locked()) {
|
||||
if (eeprom_product_valid() && eeprom_model_valid() && eeprom_hwrev_valid()) {
|
||||
if (eeprom_checksum_valid()) {
|
||||
hw_ready = true;
|
||||
|
@ -717,16 +773,21 @@ void validateStatus() {
|
|||
op_mode = MODE_TNC;
|
||||
startRadio();
|
||||
}
|
||||
} else {
|
||||
hw_ready = false;
|
||||
debug("Error, EEPROM checksum incorrect\r\n");
|
||||
}
|
||||
} else {
|
||||
hw_ready = false;
|
||||
debug("Error, EEPROM product, model, or revision not valid\r\n");
|
||||
}
|
||||
} else {
|
||||
hw_ready = false;
|
||||
debug("Error, EEPROM info not locked\r\n");
|
||||
}
|
||||
} else {
|
||||
hw_ready = false;
|
||||
Serial.write("Error, incorrect boot vector\r\n");
|
||||
debug("Error, incorrect boot vector\r\n");
|
||||
led_indicate_boot_error();
|
||||
}
|
||||
}
|
||||
|
@ -747,6 +808,12 @@ void loop() {
|
|||
}
|
||||
#endif
|
||||
|
||||
#if MCU_VARIANT == MCU_LINUX
|
||||
// We don't have interrupts, so we need to poll ofr received packets.
|
||||
// TODO: Is this fast enough? Or do we need threads or something?
|
||||
LoRa.pollReceive();
|
||||
#endif
|
||||
|
||||
if (queue_height > 0) {
|
||||
if (!dcd_waiting) updateModemStatus();
|
||||
|
||||
|
@ -775,7 +842,7 @@ void loop() {
|
|||
}
|
||||
}
|
||||
|
||||
#if MCU_VARIANT == MCU_ESP32
|
||||
#if SERIAL_EVENTS == SERIAL_POLLING
|
||||
buffer_serial();
|
||||
if (!fifo_isempty(&serialFIFO)) serial_poll();
|
||||
#else
|
||||
|
@ -787,7 +854,7 @@ volatile bool serial_polling = false;
|
|||
void serial_poll() {
|
||||
serial_polling = true;
|
||||
|
||||
#if MCU_VARIANT != MCU_ESP32
|
||||
#if SERIAL_EVENTS == SERIAL_INTERRUPT
|
||||
while (!fifo_isempty_locked(&serialFIFO)) {
|
||||
#else
|
||||
while (!fifo_isempty(&serialFIFO)) {
|
||||
|
@ -812,7 +879,7 @@ void buffer_serial() {
|
|||
while (c < MAX_CYCLES && Serial.available()) {
|
||||
c++;
|
||||
|
||||
#if MCU_VARIANT != MCU_ESP32
|
||||
#if SERIAL_EVENTS == SERIAL_INTERRUPT
|
||||
if (!fifo_isfull_locked(&serialFIFO)) {
|
||||
fifo_push_locked(&serialFIFO, Serial.read());
|
||||
}
|
||||
|
@ -853,8 +920,8 @@ void serial_interrupt_init() {
|
|||
|
||||
TIMSK3 = _BV(ICIE3);
|
||||
|
||||
#elif MCU_VARIANT == MCU_ESP32
|
||||
// No interrupt-based polling on ESP32
|
||||
#else
|
||||
// No interrupt-based polling on other MCUs.
|
||||
#endif
|
||||
|
||||
}
|
||||
|
@ -864,3 +931,12 @@ void serial_interrupt_init() {
|
|||
buffer_serial();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if PLATFORM == PLATFORM_LINUX
|
||||
int main(int argc, char** argv) {
|
||||
setup();
|
||||
while (true) {
|
||||
loop();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
370
Utilities.h
370
Utilities.h
|
@ -1,4 +1,6 @@
|
|||
#if LIBRARY_TYPE == LIBRARY_ARDUINO
|
||||
#include <EEPROM.h>
|
||||
#endif
|
||||
#include <stddef.h>
|
||||
#include "Config.h"
|
||||
#include "LoRa.h"
|
||||
|
@ -6,6 +8,143 @@
|
|||
#include "Framing.h"
|
||||
#include "MD5.h"
|
||||
|
||||
#if LIBRARY_TYPE == LIBRARY_C
|
||||
#include <time.h>
|
||||
#include <poll.h>
|
||||
#include <unistd.h>
|
||||
#include <pty.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
// We need a delay()
|
||||
void delay(int ms) {
|
||||
struct timespec interval;
|
||||
interval.tv_sec = ms / 1000;
|
||||
interval.tv_nsec = (ms % 1000) * 1000 * 1000;
|
||||
// TODO: handle signals interrupting sleep
|
||||
nanosleep(&interval, NULL);
|
||||
}
|
||||
|
||||
// And millis()
|
||||
struct timespec millis_base;
|
||||
uint32_t millis() {
|
||||
// Time since first call is close enough.
|
||||
static bool base_set(false);
|
||||
if (!base_set) {
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &millis_base)) {
|
||||
perror("Could not get time");
|
||||
exit(1);
|
||||
}
|
||||
base_set = true;
|
||||
}
|
||||
struct timespec now;
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &now)) {
|
||||
perror("Could not get time");
|
||||
exit(1);
|
||||
}
|
||||
return (now.tv_sec - millis_base.tv_sec) * 1000 + (now.tv_nsec - millis_base.tv_nsec)/(1000*1000);
|
||||
}
|
||||
|
||||
// Serial will want to poll the EEPROM a bit for help text
|
||||
bool eeprom_info_locked();
|
||||
|
||||
// We also need a Serial
|
||||
class SerialClass {
|
||||
public:
|
||||
void begin(int baud) {
|
||||
// Need to be rrentrant for restart
|
||||
if (_fd <= 0) {
|
||||
int other_end = 0;
|
||||
int status = openpty(&_fd, &other_end, NULL, NULL, NULL);
|
||||
if (status) {
|
||||
perror("could not open PTY");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
std::cerr << "Listening on " << ttyname(other_end) << std::endl;
|
||||
if (!eeprom_info_locked()) {
|
||||
std::cerr << "EEPROM configuration is not initialized. You will want to flash it with something like:" << std::endl;
|
||||
std::cerr << "\trnodeconf --key" << std::endl;
|
||||
std::cerr << "\trnodeconf --rom --platform " << std::hex << PLATFORM << " --product " << PRODUCT_HMBRW << " --model " << MODEL_FF << std::dec << " --hwrev 1 " << ttyname(other_end) << std::endl;
|
||||
}
|
||||
} else {
|
||||
std::cerr << "Skipping Serial reinitialization" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
operator bool() {
|
||||
return _fd > 0;
|
||||
}
|
||||
void write(int b) {
|
||||
uint8_t to_write = b;
|
||||
ssize_t written = ::write(_fd, &to_write, 1);
|
||||
while (written != 1) {
|
||||
if (written < 0) {
|
||||
perror("could not write to PTY");
|
||||
exit(1);
|
||||
}
|
||||
written = ::write(_fd, &to_write, 1);
|
||||
}
|
||||
}
|
||||
void write(const char* data) {
|
||||
while(*data) {
|
||||
write(*data);
|
||||
++data;
|
||||
}
|
||||
}
|
||||
bool available() {
|
||||
struct pollfd request;
|
||||
request.fd = _fd;
|
||||
request.events = POLLIN;
|
||||
request.revents = 0;
|
||||
|
||||
int result = poll(&request, 1, 0);
|
||||
|
||||
if (result == -1) {
|
||||
perror("could not poll");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return result > 0;
|
||||
}
|
||||
uint8_t read() {
|
||||
uint8_t buffer;
|
||||
ssize_t count = ::read(_fd, &buffer, 1);
|
||||
while (count != 1) {
|
||||
if (count < 0) {
|
||||
perror("could not read from PTY");
|
||||
exit(1);
|
||||
}
|
||||
count = ::read(_fd, &buffer, 1);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
protected:
|
||||
int _fd;
|
||||
};
|
||||
|
||||
|
||||
SerialClass Serial;
|
||||
|
||||
// And random(below);
|
||||
int random(int below) {
|
||||
return rand() % below;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
// Log a debug message. Message should have a \r to return the cursor, if
|
||||
// needed.
|
||||
void debug(const char* message) {
|
||||
#if LIBRARY_TYPE == LIBRARY_C
|
||||
std::cerr << message << std::endl;
|
||||
#endif
|
||||
if (Serial) {
|
||||
Serial.write(message);
|
||||
}
|
||||
}
|
||||
|
||||
#if MCU_VARIANT == MCU_ESP32
|
||||
#include "soc/rtc_wdt.h"
|
||||
#define ISR_VECT IRAM_ATTR
|
||||
|
@ -69,6 +208,17 @@ uint8_t boot_vector = 0x00;
|
|||
void led_tx_on() { digitalWrite(pin_led_tx, HIGH); }
|
||||
void led_tx_off() { digitalWrite(pin_led_tx, LOW); }
|
||||
#endif
|
||||
#elif MCU_VARIANT == MCU_LINUX
|
||||
// No LEDs on Linux, probably. SPI only.
|
||||
void led_rx_on() { }
|
||||
void led_rx_off() { }
|
||||
void led_tx_on() { }
|
||||
void led_tx_off() { }
|
||||
#endif
|
||||
|
||||
#if LIBRARY_TYPE == LIBRARY_C
|
||||
// hard_reset needs a declaration for main
|
||||
int main(int argc, char** argv);
|
||||
#endif
|
||||
|
||||
void hard_reset(void) {
|
||||
|
@ -79,18 +229,27 @@ void hard_reset(void) {
|
|||
}
|
||||
#elif MCU_VARIANT == MCU_ESP32
|
||||
ESP.restart();
|
||||
#elif MCU_VARIANT == MCU_LINUX
|
||||
// TODO: re-exec ourselves?
|
||||
#if LIBRARY_TYPE == LIBRARY_C
|
||||
std::cerr << "Restarting" << std::endl;
|
||||
exit(main(0, NULL));
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void led_indicate_error(int cycles) {
|
||||
#if LIBRARY_TYPE == LIBRARY_C
|
||||
std::cerr << "Indicating error" << std::endl;
|
||||
#endif
|
||||
bool forever = (cycles == 0) ? true : false;
|
||||
cycles = forever ? 1 : cycles;
|
||||
while(cycles > 0) {
|
||||
digitalWrite(pin_led_rx, HIGH);
|
||||
digitalWrite(pin_led_tx, LOW);
|
||||
led_rx_on();
|
||||
led_tx_off();
|
||||
delay(100);
|
||||
digitalWrite(pin_led_rx, LOW);
|
||||
digitalWrite(pin_led_tx, HIGH);
|
||||
led_rx_off();
|
||||
led_tx_on();
|
||||
delay(100);
|
||||
if (!forever) cycles--;
|
||||
}
|
||||
|
@ -99,6 +258,9 @@ void led_indicate_error(int cycles) {
|
|||
}
|
||||
|
||||
void led_indicate_boot_error() {
|
||||
#if LIBRARY_TYPE == LIBRARY_C
|
||||
std::cerr << "Indicating boot error" << std::endl;
|
||||
#endif
|
||||
while (true) {
|
||||
led_tx_on();
|
||||
led_rx_off();
|
||||
|
@ -110,9 +272,12 @@ void led_indicate_boot_error() {
|
|||
}
|
||||
|
||||
void led_indicate_warning(int cycles) {
|
||||
#if LIBRARY_TYPE == LIBRARY_C
|
||||
std::cerr << "Indicating warning" << std::endl;
|
||||
#endif
|
||||
bool forever = (cycles == 0) ? true : false;
|
||||
cycles = forever ? 1 : cycles;
|
||||
digitalWrite(pin_led_tx, HIGH);
|
||||
led_tx_on();
|
||||
while(cycles > 0) {
|
||||
led_tx_off();
|
||||
delay(100);
|
||||
|
@ -123,7 +288,7 @@ void led_indicate_warning(int cycles) {
|
|||
led_tx_off();
|
||||
}
|
||||
|
||||
#if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560
|
||||
#if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560 || MCU_VARIANT == MCU_LINUX
|
||||
void led_indicate_info(int cycles) {
|
||||
bool forever = (cycles == 0) ? true : false;
|
||||
cycles = forever ? 1 : cycles;
|
||||
|
@ -165,6 +330,9 @@ void led_indicate_warning(int cycles) {
|
|||
}
|
||||
#else
|
||||
void led_indicate_info(int cycles) {
|
||||
#if LIBRARY_TYPE == LIBRARY_C
|
||||
std::cerr << "Indicating info" << std::endl;
|
||||
#endif
|
||||
bool forever = (cycles == 0) ? true : false;
|
||||
cycles = forever ? 1 : cycles;
|
||||
while(cycles > 0) {
|
||||
|
@ -179,8 +347,9 @@ void led_indicate_warning(int cycles) {
|
|||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560 || MCU_VARIANT == MCU_ESP32
|
||||
unsigned long led_standby_ticks = 0;
|
||||
#endif
|
||||
#if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560
|
||||
uint8_t led_standby_min = 1;
|
||||
uint8_t led_standby_max = 40;
|
||||
|
@ -196,8 +365,10 @@ unsigned long led_standby_ticks = 0;
|
|||
unsigned long led_standby_wait = 1768;
|
||||
unsigned long led_notready_wait = 150;
|
||||
#endif
|
||||
#if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560 || MCU_VARIANT == MCU_ESP32
|
||||
uint8_t led_standby_value = led_standby_min;
|
||||
int8_t led_standby_direction = 0;
|
||||
#endif
|
||||
|
||||
#if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560
|
||||
void led_indicate_standby() {
|
||||
|
@ -243,6 +414,17 @@ int8_t led_standby_direction = 0;
|
|||
#endif
|
||||
}
|
||||
}
|
||||
#elif MCU_VARIANT == MCU_LINUX
|
||||
// No LEDs available.
|
||||
void led_indicate_standby() {
|
||||
#if LIBRARY_TYPE == LIBRARY_C
|
||||
static bool printed = false;
|
||||
if (!printed) {
|
||||
std::cerr << "Indicating standby" << std::endl;
|
||||
printed = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560
|
||||
|
@ -289,6 +471,17 @@ int8_t led_standby_direction = 0;
|
|||
#endif
|
||||
}
|
||||
}
|
||||
#elif MCU_VARIANT == MCU_LINUX
|
||||
// No LEDs available.
|
||||
void led_indicate_not_ready() {
|
||||
#if LIBRARY_TYPE == LIBRARY_C
|
||||
static bool printed = false;
|
||||
if (!printed) {
|
||||
std::cerr << "Indicating not ready" << std::endl;
|
||||
printed = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
void escapedSerialWrite(uint8_t byte) {
|
||||
|
@ -551,8 +744,68 @@ void promisc_disable() {
|
|||
promisc = false;
|
||||
}
|
||||
|
||||
#if MCU_VARIANT == MCU_LINUX
|
||||
// On Linux we always use memory-mapped EEPROM
|
||||
uint8_t* eeprom_mapping = NULL;
|
||||
#endif
|
||||
#if LIBRARY_TYPE == LIBRARY_C
|
||||
// And when using the C library we set it up from a file descriptor.
|
||||
int eeprom_fd = 0;
|
||||
#endif
|
||||
|
||||
void eeprom_open(int size) {
|
||||
#if MCU_VARIANT == MCU_ESP32
|
||||
// This MCU needs EEPROIM to be begun
|
||||
EEPROM.begin(size);
|
||||
#elif MCU_VARIANT == MCU_LINUX
|
||||
// We need to use file-backed EEPROM emulation
|
||||
#if LIBRARY_TYPE == LIBRARY_C
|
||||
const char* eeprom_filename = "eeprom.dat";
|
||||
// We need to be reentrant for restarts
|
||||
if (eeprom_fd <= 0) {
|
||||
eeprom_fd = open(eeprom_filename, O_RDWR | O_CREAT, 0644);
|
||||
if (eeprom_fd <= 0) {
|
||||
perror("Could not open EEPROM file");
|
||||
exit(1);
|
||||
}
|
||||
int status = ftruncate(eeprom_fd, size);
|
||||
if (status != 0) {
|
||||
perror("Could not set size of EEPROM file");
|
||||
exit(1);
|
||||
}
|
||||
// Map EEPROM into RAM
|
||||
eeprom_mapping = (uint8_t*) mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, eeprom_fd, 0);
|
||||
if (eeprom_mapping == NULL) {
|
||||
perror("Could not map EEPROM file");
|
||||
exit(1);
|
||||
}
|
||||
std::cerr << "Mapped " << eeprom_filename << " as FD " << eeprom_fd << " to address " << (void*)eeprom_mapping << " size " << size << std::endl;
|
||||
} else {
|
||||
std::cerr << "Skipping EEPROM reinitialization" << std::endl;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
uint8_t eeprom_read(uint8_t addr) {
|
||||
#if MCU_VARIANT == MCU_LINUX
|
||||
if (!eeprom_mapping) {
|
||||
throw std::runtime_error("Tried to read EEPROM before opening it!");
|
||||
}
|
||||
int mapped_address = eeprom_addr(addr);
|
||||
return eeprom_mapping[mapped_address];
|
||||
#else
|
||||
return EEPROM.read(eeprom_addr(addr));
|
||||
#endif
|
||||
}
|
||||
|
||||
bool eeprom_info_locked() {
|
||||
uint8_t lock_byte = EEPROM.read(eeprom_addr(ADDR_INFO_LOCK));
|
||||
#if MCU_VARIANT == MCU_LINUX
|
||||
if (!eeprom_mapping) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
uint8_t lock_byte = eeprom_read(ADDR_INFO_LOCK);
|
||||
if (lock_byte == INFO_LOCK_BYTE) {
|
||||
return true;
|
||||
} else {
|
||||
|
@ -560,34 +813,6 @@ bool eeprom_info_locked() {
|
|||
}
|
||||
}
|
||||
|
||||
void eeprom_dump_info() {
|
||||
for (int addr = ADDR_PRODUCT; addr <= ADDR_INFO_LOCK; addr++) {
|
||||
uint8_t byte = EEPROM.read(eeprom_addr(addr));
|
||||
escapedSerialWrite(byte);
|
||||
}
|
||||
}
|
||||
|
||||
void eeprom_dump_config() {
|
||||
for (int addr = ADDR_CONF_SF; addr <= ADDR_CONF_OK; addr++) {
|
||||
uint8_t byte = EEPROM.read(eeprom_addr(addr));
|
||||
escapedSerialWrite(byte);
|
||||
}
|
||||
}
|
||||
|
||||
void eeprom_dump_all() {
|
||||
for (int addr = 0; addr < EEPROM_RESERVED; addr++) {
|
||||
uint8_t byte = EEPROM.read(eeprom_addr(addr));
|
||||
escapedSerialWrite(byte);
|
||||
}
|
||||
}
|
||||
|
||||
void kiss_dump_eeprom() {
|
||||
Serial.write(FEND);
|
||||
Serial.write(CMD_ROM_READ);
|
||||
eeprom_dump_all();
|
||||
Serial.write(FEND);
|
||||
}
|
||||
|
||||
void eeprom_update(int mapped_addr, uint8_t byte) {
|
||||
#if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560
|
||||
EEPROM.update(mapped_addr, byte);
|
||||
|
@ -596,8 +821,12 @@ void eeprom_update(int mapped_addr, uint8_t byte) {
|
|||
EEPROM.write(mapped_addr, byte);
|
||||
EEPROM.commit();
|
||||
}
|
||||
#elif MCU_VARIANT == MCU_LINUX
|
||||
if (!eeprom_mapping) {
|
||||
throw std::runtime_error("Tried to write EEPROM before opening it!");
|
||||
}
|
||||
eeprom_mapping[mapped_addr] = byte;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void eeprom_write(uint8_t addr, uint8_t byte) {
|
||||
|
@ -615,32 +844,57 @@ void eeprom_erase() {
|
|||
hard_reset();
|
||||
}
|
||||
|
||||
bool eeprom_lock_set() {
|
||||
if (EEPROM.read(eeprom_addr(ADDR_INFO_LOCK)) == INFO_LOCK_BYTE) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
void eeprom_dump_info() {
|
||||
for (int addr = ADDR_PRODUCT; addr <= ADDR_INFO_LOCK; addr++) {
|
||||
uint8_t byte = eeprom_read(addr);
|
||||
escapedSerialWrite(byte);
|
||||
}
|
||||
}
|
||||
|
||||
void eeprom_dump_config() {
|
||||
for (int addr = ADDR_CONF_SF; addr <= ADDR_CONF_OK; addr++) {
|
||||
uint8_t byte = eeprom_read(addr);
|
||||
escapedSerialWrite(byte);
|
||||
}
|
||||
}
|
||||
|
||||
void eeprom_dump_all() {
|
||||
for (int addr = 0; addr < EEPROM_RESERVED; addr++) {
|
||||
uint8_t byte = eeprom_read(addr);
|
||||
escapedSerialWrite(byte);
|
||||
}
|
||||
}
|
||||
|
||||
void kiss_dump_eeprom() {
|
||||
Serial.write(FEND);
|
||||
Serial.write(CMD_ROM_READ);
|
||||
eeprom_dump_all();
|
||||
Serial.write(FEND);
|
||||
}
|
||||
|
||||
bool eeprom_product_valid() {
|
||||
uint8_t rval = EEPROM.read(eeprom_addr(ADDR_PRODUCT));
|
||||
uint8_t rval = eeprom_read(ADDR_PRODUCT);
|
||||
|
||||
#if PLATFORM == PLATFORM_AVR
|
||||
if (rval == PRODUCT_RNODE || rval == PRODUCT_HMBRW) {
|
||||
#elif PLATFORM == PLATFORM_ESP32
|
||||
if (rval == PRODUCT_RNODE || rval == PRODUCT_HMBRW || rval == PRODUCT_TBEAM || rval == PRODUCT_T32_20 || rval == PRODUCT_T32_21) {
|
||||
#elif PLATFORM == PLATFORM_LINUX
|
||||
if (rval == PRODUCT_HMBRW) {
|
||||
#else
|
||||
if (false) {
|
||||
#endif
|
||||
return true;
|
||||
} else {
|
||||
#if LIBRARY_TYPE == LIBRARY_C
|
||||
std::cerr << "Unacceptable platform: " << std::hex << "0x" << (int)rval << std::dec << std::endl;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool eeprom_model_valid() {
|
||||
model = EEPROM.read(eeprom_addr(ADDR_MODEL));
|
||||
model = eeprom_read(ADDR_MODEL);
|
||||
#if BOARD_MODEL == BOARD_RNODE
|
||||
if (model == MODEL_A4 || model == MODEL_A9) {
|
||||
#elif BOARD_MODEL == BOARD_HMBRW
|
||||
|
@ -660,15 +914,21 @@ bool eeprom_model_valid() {
|
|||
#endif
|
||||
return true;
|
||||
} else {
|
||||
#if LIBRARY_TYPE == LIBRARY_C
|
||||
std::cerr << "Unacceptable model: " << std::hex << "0x" << (int)model << std::dec << std::endl;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool eeprom_hwrev_valid() {
|
||||
hwrev = EEPROM.read(eeprom_addr(ADDR_HW_REV));
|
||||
hwrev = eeprom_read(ADDR_HW_REV);
|
||||
if (hwrev != 0x00 && hwrev != 0xFF) {
|
||||
return true;
|
||||
} else {
|
||||
#if LIBRARY_TYPE == LIBRARY_C
|
||||
std::cerr << "Unacceptable revision: " << std::hex << "0x" << (int)hwrev << std::dec << std::endl;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -676,14 +936,14 @@ bool eeprom_hwrev_valid() {
|
|||
bool eeprom_checksum_valid() {
|
||||
char *data = (char*)malloc(CHECKSUMMED_SIZE);
|
||||
for (uint8_t i = 0; i < CHECKSUMMED_SIZE; i++) {
|
||||
char byte = EEPROM.read(eeprom_addr(i));
|
||||
char byte = eeprom_read(i);
|
||||
data[i] = byte;
|
||||
}
|
||||
|
||||
unsigned char *hash = MD5::make_hash(data, CHECKSUMMED_SIZE);
|
||||
bool checksum_valid = true;
|
||||
for (uint8_t i = 0; i < 16; i++) {
|
||||
uint8_t stored_chk_byte = EEPROM.read(eeprom_addr(ADDR_CHKSUM+i));
|
||||
uint8_t stored_chk_byte = eeprom_read(ADDR_CHKSUM+i);
|
||||
uint8_t calced_chk_byte = (uint8_t)hash[i];
|
||||
if (stored_chk_byte != calced_chk_byte) {
|
||||
checksum_valid = false;
|
||||
|
@ -696,7 +956,7 @@ bool eeprom_checksum_valid() {
|
|||
}
|
||||
|
||||
bool eeprom_have_conf() {
|
||||
if (EEPROM.read(eeprom_addr(ADDR_CONF_OK)) == CONF_OK_BYTE) {
|
||||
if (eeprom_read(ADDR_CONF_OK) == CONF_OK_BYTE) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
@ -705,11 +965,11 @@ bool eeprom_have_conf() {
|
|||
|
||||
void eeprom_conf_load() {
|
||||
if (eeprom_have_conf()) {
|
||||
lora_sf = EEPROM.read(eeprom_addr(ADDR_CONF_SF));
|
||||
lora_cr = EEPROM.read(eeprom_addr(ADDR_CONF_CR));
|
||||
lora_txp = EEPROM.read(eeprom_addr(ADDR_CONF_TXP));
|
||||
lora_freq = (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_FREQ)+0x00) << 24 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_FREQ)+0x01) << 16 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_FREQ)+0x02) << 8 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_FREQ)+0x03);
|
||||
lora_bw = (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_BW)+0x00) << 24 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_BW)+0x01) << 16 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_BW)+0x02) << 8 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_BW)+0x03);
|
||||
lora_sf = eeprom_read(ADDR_CONF_SF);
|
||||
lora_cr = eeprom_read(ADDR_CONF_CR);
|
||||
lora_txp = eeprom_read(ADDR_CONF_TXP);
|
||||
lora_freq = (uint32_t)eeprom_read(ADDR_CONF_FREQ+0x00) << 24 | (uint32_t)eeprom_read(ADDR_CONF_FREQ+0x01) << 16 | (uint32_t)eeprom_read(ADDR_CONF_FREQ+0x02) << 8 | (uint32_t)eeprom_read(ADDR_CONF_FREQ+0x03);
|
||||
lora_bw = (uint32_t)eeprom_read(ADDR_CONF_BW+0x00) << 24 | (uint32_t)eeprom_read(ADDR_CONF_BW+0x01) << 16 | (uint32_t)eeprom_read(ADDR_CONF_BW+0x02) << 8 | (uint32_t)eeprom_read(ADDR_CONF_BW+0x03);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -784,7 +1044,7 @@ inline void fifo_flush(FIFOBuffer *f) {
|
|||
f->head = f->tail;
|
||||
}
|
||||
|
||||
#if MCU_VARIANT != MCU_ESP32
|
||||
#if SERIAL_EVENTS == SERIAL_INTERRUPT
|
||||
static inline bool fifo_isempty_locked(const FIFOBuffer *f) {
|
||||
bool result;
|
||||
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
|
||||
|
@ -866,7 +1126,7 @@ inline void fifo16_flush(FIFOBuffer16 *f) {
|
|||
f->head = f->tail;
|
||||
}
|
||||
|
||||
#if MCU_VARIANT != MCU_ESP32
|
||||
#if SERIAL_EVENTS == SERIAL_INTERRUPT
|
||||
static inline bool fifo16_isempty_locked(const FIFOBuffer16 *f) {
|
||||
bool result;
|
||||
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
|
||||
|
|
Loading…
Reference in New Issue