Merge pull request #16 from interfect/linux-pr

Allow building for Linux as a Linux binary
This commit is contained in:
markqvist 2022-02-24 10:06:18 +01:00 committed by GitHub
commit 475147711c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 844 additions and 177 deletions

View File

@ -1,18 +1,13 @@
#include "ROM.h" #include "ROM.h"
#include "Platform.h"
#ifndef CONFIG_H #ifndef CONFIG_H
#define CONFIG_H #define CONFIG_H
#define MAJ_VERS 0x01 #define MAJ_VERS 0x01
#define MIN_VERS 0x1B #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_RNODE 0x31
#define BOARD_HMBRW 0x32 #define BOARD_HMBRW 0x32
#define BOARD_TBEAM 0x33 #define BOARD_TBEAM 0x33
@ -21,22 +16,12 @@
#define BOARD_LORA32_V2_0 0x36 #define BOARD_LORA32_V2_0 0x36
#define BOARD_LORA32_V2_1 0x37 #define BOARD_LORA32_V2_1 0x37
#define SERIAL_INTERRUPT 0x1
#define SERIAL_POLLING 0x2
#define MODE_HOST 0x11 #define MODE_HOST 0x11
#define MODE_TNC 0x12 #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 MTU 500
#define SINGLE_MTU 255 #define SINGLE_MTU 255
#define HEADER_L 1 #define HEADER_L 1
@ -54,6 +39,7 @@
const int pin_led_tx = 13; const int pin_led_tx = 13;
#define BOARD_MODEL BOARD_RNODE #define BOARD_MODEL BOARD_RNODE
#define SERIAL_EVENTS SERIAL_INTERRUPT
#define CONFIG_UART_BUFFER_SIZE 6144 #define CONFIG_UART_BUFFER_SIZE 6144
#define CONFIG_QUEUE_SIZE 6144 #define CONFIG_QUEUE_SIZE 6144
@ -70,6 +56,7 @@
const int pin_led_tx = 13; const int pin_led_tx = 13;
#define BOARD_MODEL BOARD_HMBRW #define BOARD_MODEL BOARD_HMBRW
#define SERIAL_EVENTS SERIAL_INTERRUPT
#define CONFIG_UART_BUFFER_SIZE 768 #define CONFIG_UART_BUFFER_SIZE 768
#define CONFIG_QUEUE_SIZE 5120 #define CONFIG_QUEUE_SIZE 5120
@ -131,6 +118,8 @@
#error An unsupported board was selected. Cannot compile RNode firmware. #error An unsupported board was selected. Cannot compile RNode firmware.
#endif #endif
#define SERIAL_EVENTS SERIAL_POLLING
#define CONFIG_UART_BUFFER_SIZE 6144 #define CONFIG_UART_BUFFER_SIZE 6144
#define CONFIG_QUEUE_SIZE 6144 #define CONFIG_QUEUE_SIZE 6144
#define CONFIG_QUEUE_MAX_LENGTH 200 #define CONFIG_QUEUE_MAX_LENGTH 200
@ -141,6 +130,22 @@
#define GPS_BAUD_RATE 9600 #define GPS_BAUD_RATE 9600
#define PIN_GPS_TX 12 #define PIN_GPS_TX 12
#define PIN_GPS_RX 34 #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 #endif
#if BOARD_MODEL == BOARD_TBEAM #if BOARD_MODEL == BOARD_TBEAM
@ -149,6 +154,11 @@
#define PMU_IRQ 35 #define PMU_IRQ 35
#endif #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) #define eeprom_addr(a) (a+EEPROM_OFFSET)
// MCU independent configuration parameters // MCU independent configuration parameters

283
LoRa.cpp
View File

@ -6,23 +6,23 @@
#include "LoRa.h" #include "LoRa.h"
#define MCU_1284P 0x91 #if LIBRARY_TYPE == LIBRARY_C
#define MCU_2560 0x92 // We need sleep() to use instead of yield()
#define MCU_ESP32 0x81 #include <unistd.h>
#if defined(__AVR_ATmega1284P__) // And we need to use the filesystem and IOCTLs instead of an SPI global
#define PLATFORM PLATFORM_AVR #include <fcntl.h>
#define MCU_VARIANT MCU_1284P #include <sys/ioctl.h>
#elif defined(__AVR_ATmega2560__) #include <linux/spi/spidev.h>
#define PLATFORM PLATFORM_AVR // And to have memset
#define MCU_VARIANT MCU_2560 #include <cstring>
#elif defined(ESP32) // And we need to be able to report errors
#define PLATFORM PLATFORM_ESP32 #include <stdio.h>
#define MCU_VARIANT MCU_ESP32 #include <errno.h>
// And we need IO formatting functions for the C++-stream dumpRegisters()
#include <iomanip>
#endif #endif
#ifndef MCU_VARIANT
#error No MCU variant defined, cannot compile
#endif
#if MCU_VARIANT == MCU_ESP32 #if MCU_VARIANT == MCU_ESP32
#include "soc/rtc_wdt.h" #include "soc/rtc_wdt.h"
@ -38,11 +38,14 @@
#define REG_FRF_MID 0x07 #define REG_FRF_MID 0x07
#define REG_FRF_LSB 0x08 #define REG_FRF_LSB 0x08
#define REG_PA_CONFIG 0x09 #define REG_PA_CONFIG 0x09
#define REG_PA_RAMP 0x0a
#define REG_OCP 0x0b
#define REG_LNA 0x0c #define REG_LNA 0x0c
#define REG_FIFO_ADDR_PTR 0x0d #define REG_FIFO_ADDR_PTR 0x0d
#define REG_FIFO_TX_BASE_ADDR 0x0e #define REG_FIFO_TX_BASE_ADDR 0x0e
#define REG_FIFO_RX_BASE_ADDR 0x0f #define REG_FIFO_RX_BASE_ADDR 0x0f
#define REG_FIFO_RX_CURRENT_ADDR 0x10 #define REG_FIFO_RX_CURRENT_ADDR 0x10
#define REG_IRQ_FLAGS_MASK 0x11
#define REG_IRQ_FLAGS 0x12 #define REG_IRQ_FLAGS 0x12
#define REG_RX_NB_BYTES 0x13 #define REG_RX_NB_BYTES 0x13
#define REG_MODEM_STAT 0x18 #define REG_MODEM_STAT 0x18
@ -50,19 +53,38 @@
#define REG_PKT_RSSI_VALUE 0x1a #define REG_PKT_RSSI_VALUE 0x1a
#define REG_MODEM_CONFIG_1 0x1d #define REG_MODEM_CONFIG_1 0x1d
#define REG_MODEM_CONFIG_2 0x1e #define REG_MODEM_CONFIG_2 0x1e
#define REG_SYMB_TIMEOUT_LSB 0x1f
#define REG_PREAMBLE_MSB 0x20 #define REG_PREAMBLE_MSB 0x20
#define REG_PREAMBLE_LSB 0x21 #define REG_PREAMBLE_LSB 0x21
#define REG_PAYLOAD_LENGTH 0x22 #define REG_PAYLOAD_LENGTH 0x22
#define REG_PAYLOAD_MAX_LENGTH 0x23
#define REG_HOP_PERIOD 0x24
#define REG_MODEM_CONFIG_3 0x26 #define REG_MODEM_CONFIG_3 0x26
#define REG_PPM_CORRECTION 0x27
#define REG_FREQ_ERROR_MSB 0x28 #define REG_FREQ_ERROR_MSB 0x28
#define REG_FREQ_ERROR_MID 0x29 #define REG_FREQ_ERROR_MID 0x29
#define REG_FREQ_ERROR_LSB 0x2a #define REG_FREQ_ERROR_LSB 0x2a
#define REG_RSSI_WIDEBAND 0x2c #define REG_RSSI_WIDEBAND 0x2c
#define REG_IF_FREQ_2 0x2f
#define REG_IF_FREQ_1 0x30
#define REG_DETECTION_OPTIMIZE 0x31 #define REG_DETECTION_OPTIMIZE 0x31
#define REG_INVERT_IQ 0x33
#define REG_HIGH_BW_OPTIMIZE_1 0x36
#define REG_DETECTION_THRESHOLD 0x37 #define REG_DETECTION_THRESHOLD 0x37
#define REG_SYNC_WORD 0x39 #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_DIO_MAPPING_1 0x40
#define REG_VERSION 0x42 #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 // Modes
#define MODE_LONG_RANGE_MODE 0x80 #define MODE_LONG_RANGE_MODE 0x80
@ -88,41 +110,50 @@ LoRaClass::LoRaClass() :
_frequency(0), _frequency(0),
_packetIndex(0), _packetIndex(0),
_implicitHeaderMode(0), _implicitHeaderMode(0),
_onReceive(NULL) _onReceive(NULL),
_spiBegun(false)
{ {
#if LIBRARY_TYPE == LIBRARY_ARDUINO
// overide Stream timeout value // overide Stream timeout value
setTimeout(0); setTimeout(0);
#elif LIBRARY_TYPE == LIBRARY_C
_fd = 0;
#endif
} }
int LoRaClass::begin(long frequency) int LoRaClass::begin(long frequency)
{ {
#if LIBRARY_TYPE == LIBRARY_ARDUINO
// setup pins // setup pins
pinMode(_ss, OUTPUT); pinMode(_ss, OUTPUT);
// set SS high // set SS high
digitalWrite(_ss, HIGH); digitalWrite(_ss, HIGH);
#endif
if (_reset != -1) { #if LIBRARY_TYPE == LIBRARY_ARDUINO
pinMode(_reset, OUTPUT);
// perform reset
digitalWrite(_reset, LOW);
delay(10);
digitalWrite(_reset, HIGH);
delay(10);
}
// start SPI // start SPI
SPI.begin(); 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 if (!resetModem()) {
uint8_t version = readRegister(REG_VERSION);
if (version != 0x12) {
return 0; return 0;
} }
// put in sleep mode
sleep();
// set frequency // set frequency
setFrequency(frequency); setFrequency(frequency);
@ -147,11 +178,21 @@ int LoRaClass::begin(long frequency)
void LoRaClass::end() 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 // put in sleep mode
sleep(); this->sleep();
}
#if LIBRARY_TYPE == LIBRARY_ARDUINO
// stop SPI // stop SPI
SPI.end(); 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) int LoRaClass::beginPacket(int implicitHeader)
@ -179,7 +220,11 @@ int LoRaClass::endPacket()
// wait for TX done // wait for TX done
while ((readRegister(REG_IRQ_FLAGS) & IRQ_TX_DONE_MASK) == 0) { while ((readRegister(REG_IRQ_FLAGS) & IRQ_TX_DONE_MASK) == 0) {
#if LIBRARY_TYPE == LIBRARY_ARDUINO
yield(); yield();
#elif LIBRARY_TYPE == LIBRARY_C
::sleep(0);
#endif
} }
// clear IRQ's // clear IRQ's
@ -271,13 +316,13 @@ float ISR_VECT LoRaClass::packetSnr() {
long LoRaClass::packetFrequencyError() long LoRaClass::packetFrequencyError()
{ {
int32_t freqError = 0; 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 <<= 8L;
freqError += static_cast<int32_t>(readRegister(REG_FREQ_ERROR_MID)); freqError += static_cast<int32_t>(readRegister(REG_FREQ_ERROR_MID));
freqError <<= 8L; freqError <<= 8L;
freqError += static_cast<int32_t>(readRegister(REG_FREQ_ERROR_LSB)); 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 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)) void LoRaClass::onReceive(void(*callback)(int))
{ {
_onReceive = callback; _onReceive = callback;
if (callback) { if (callback) {
#if LIBRARY_TYPE == LIBRARY_ARDUINO
pinMode(_dio0, INPUT); pinMode(_dio0, INPUT);
#endif
writeRegister(REG_DIO_MAPPING_1, 0x00); writeRegister(REG_DIO_MAPPING_1, 0x00);
#if MCU_VARIANT != MCU_LINUX && LIBRARY_TYPE == LIBRARY_ARDUINO
#ifdef SPI_HAS_NOTUSINGINTERRUPT #ifdef SPI_HAS_NOTUSINGINTERRUPT
SPI.usingInterrupt(digitalPinToInterrupt(_dio0)); SPI.usingInterrupt(digitalPinToInterrupt(_dio0));
#endif #endif
attachInterrupt(digitalPinToInterrupt(_dio0), LoRaClass::onDio0Rise, RISING); attachInterrupt(digitalPinToInterrupt(_dio0), LoRaClass::onDio0Rise, RISING);
#endif
} else { } else {
#if MCU_VARIANT != MCU_LINUX && LIBRARY_TYPE == LIBRARY_ARDUINO
detachInterrupt(digitalPinToInterrupt(_dio0)); detachInterrupt(digitalPinToInterrupt(_dio0));
#ifdef SPI_HAS_NOTUSINGINTERRUPT #ifdef SPI_HAS_NOTUSINGINTERRUPT
SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0)); SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0));
#endif
#endif #endif
} }
} }
@ -572,6 +637,7 @@ void LoRaClass::setSPIFrequency(uint32_t frequency)
_spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0); _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0);
} }
#if LIBRARY_TYPE == LIBRARY_ARDUINO
void LoRaClass::dumpRegisters(Stream& out) void LoRaClass::dumpRegisters(Stream& out)
{ {
for (int i = 0; i < 128; i++) { for (int i = 0; i < 128; i++) {
@ -581,6 +647,94 @@ void LoRaClass::dumpRegisters(Stream& out)
out.println(readRegister(i), HEX); 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() void LoRaClass::explicitHeaderMode()
{ {
@ -605,6 +759,13 @@ void ISR_VECT LoRaClass::handleDio0Rise()
writeRegister(REG_IRQ_FLAGS, irqFlags); writeRegister(REG_IRQ_FLAGS, irqFlags);
if ((irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) { if ((irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) {
// received a packet
handleRx();
}
}
void ISR_VECT LoRaClass::handleRx()
{
// received a packet // received a packet
_packetIndex = 0; _packetIndex = 0;
@ -621,7 +782,6 @@ void ISR_VECT LoRaClass::handleDio0Rise()
// reset FIFO address // reset FIFO address
writeRegister(REG_FIFO_ADDR_PTR, 0); writeRegister(REG_FIFO_ADDR_PTR, 0);
} }
}
uint8_t ISR_VECT LoRaClass::readRegister(uint8_t address) 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; uint8_t response;
#if LIBRARY_TYPE == LIBRARY_ARDUINO
// Select chip, send address, and send/read data, the Arduino way
digitalWrite(_ss, LOW); digitalWrite(_ss, LOW);
SPI.beginTransaction(_spiSettings); SPI.beginTransaction(_spiSettings);
@ -645,6 +807,55 @@ uint8_t ISR_VECT LoRaClass::singleTransfer(uint8_t address, uint8_t value)
SPI.endTransaction(); SPI.endTransaction();
digitalWrite(_ss, HIGH); 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; return response;
} }

42
LoRa.h
View File

@ -7,8 +7,38 @@
#ifndef LORA_H #ifndef LORA_H
#define LORA_H #define LORA_H
#include "Platform.h"
#if LIBRARY_TYPE == LIBRARY_ARDUINO
#include <Arduino.h> #include <Arduino.h>
#include <SPI.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_SS_PIN 10
#define LORA_DEFAULT_RESET_PIN 9 #define LORA_DEFAULT_RESET_PIN 9
@ -46,6 +76,7 @@ public:
virtual int peek(); virtual int peek();
virtual void flush(); virtual void flush();
void pollReceive();
void onReceive(void(*callback)(int)); void onReceive(void(*callback)(int));
void receive(int size = 0); 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 setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN);
void setSPIFrequency(uint32_t frequency); void setSPIFrequency(uint32_t frequency);
#if LIBRARY_TYPE == LIBRARY_ARDUINO
void dumpRegisters(Stream& out); void dumpRegisters(Stream& out);
#elif LIBRARY_TYPE == LIBRARY_C
void dumpRegisters(std::ostream& out);
#endif
private: private:
bool resetModem();
void explicitHeaderMode(); void explicitHeaderMode();
void implicitHeaderMode(); void implicitHeaderMode();
void handleDio0Rise(); void handleDio0Rise();
void handleRx();
uint8_t readRegister(uint8_t address); uint8_t readRegister(uint8_t address);
void writeRegister(uint8_t address, uint8_t value); void writeRegister(uint8_t address, uint8_t value);
@ -99,6 +137,10 @@ private:
int _packetIndex; int _packetIndex;
int _implicitHeaderMode; int _implicitHeaderMode;
void (*_onReceive)(int); void (*_onReceive)(int);
bool _spiBegun;
#if LIBRARY_TYPE == LIBRARY_C
int _fd;
#endif
}; };
extern LoRaClass LoRa; extern LoRaClass LoRa;

View File

@ -1,5 +1,11 @@
#include "MD5.h" #include "MD5.h"
#if LIBRARY_TYPE == LIBRARY_ARDUINO
#include <Arduino.h>
#elif LIBRARY_TYPE == LIBRARY_C
#include <cstdlib>
#endif
MD5::MD5() MD5::MD5()
{ {
//nothing //nothing

2
MD5.h
View File

@ -1,7 +1,7 @@
#ifndef MD5_h #ifndef MD5_h
#define MD5_h #define MD5_h
#include "Arduino.h" #include "Platform.h"
/* /*
* This is an OpenSSL-compatible implementation of the RSA Data Security, * This is an OpenSSL-compatible implementation of the RSA Data Security,

View File

@ -10,8 +10,6 @@ prep-samd:
arduino-cli core update-index --config-file arduino-cli.yaml arduino-cli core update-index --config-file arduino-cli.yaml
arduino-cli core install adafruit:samd arduino-cli core install adafruit:samd
firmware: firmware:
arduino-cli compile --fqbn unsignedio:avr:rnode arduino-cli compile --fqbn unsignedio:avr:rnode
@ -135,3 +133,25 @@ release-mega2560:
arduino-cli compile --fqbn arduino:avr:mega -e arduino-cli compile --fqbn arduino:avr:mega -e
cp build/arduino.avr.mega/RNode_Firmware.ino.hex Release/rnode_firmware_latest_m2560.hex cp build/arduino.avr.mega/RNode_Firmware.ino.hex Release/rnode_firmware_latest_m2560.hex
rm -r build 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

42
Platform.h Normal file
View File

@ -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

View File

@ -20,8 +20,12 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
#include "Platform.h"
#if LIBRARY_TYPE == LIBRARY_ARDUINO
#include <Arduino.h> #include <Arduino.h>
#include <SPI.h> #include <SPI.h>
#endif
#include "Utilities.h" #include "Utilities.h"
FIFOBuffer serialFIFO; FIFOBuffer serialFIFO;
@ -47,15 +51,26 @@ char sbuf[128];
bool packet_ready = false; bool packet_ready = false;
#endif #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() { void setup() {
#if MCU_VARIANT == MCU_ESP32 #if MCU_VARIANT == MCU_ESP32
delay(500); delay(500);
EEPROM.begin(EEPROM_SIZE);
Serial.setRxBufferSize(CONFIG_UART_BUFFER_SIZE);
#endif #endif
eeprom_open(EEPROM_SIZE);
#if LIBRARY_TYPE == LIBRARY_ARDUINO
// Seed the PRNG // Seed the PRNG
randomSeed(analogRead(0)); randomSeed(analogRead(0));
#endif
// Initialise serial communication // Initialise serial communication
memset(serialBuffer, 0, sizeof(serialBuffer)); memset(serialBuffer, 0, sizeof(serialBuffer));
@ -64,11 +79,17 @@ void setup() {
Serial.begin(serial_baudrate); Serial.begin(serial_baudrate);
while (!Serial); while (!Serial);
#if MCU_VARIANT == MCU_ESP32
Serial.setRxBufferSize(CONFIG_UART_BUFFER_SIZE);
#endif
serial_interrupt_init(); serial_interrupt_init();
#if LIBRARY_TYPE == LIBRARY_ARDUINO
// Configure input and output pins // Configure input and output pins
pinMode(pin_led_rx, OUTPUT); pinMode(pin_led_rx, OUTPUT);
pinMode(pin_led_tx, OUTPUT); pinMode(pin_led_tx, OUTPUT);
#endif
// Initialise buffers // Initialise buffers
memset(pbuf, 0, sizeof(pbuf)); memset(pbuf, 0, sizeof(pbuf));
@ -130,6 +151,9 @@ inline void getPacketData(uint16_t len) {
} }
void ISR_VECT receive_callback(int packet_size) { 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) { if (!promisc) {
// The standard operating mode allows large // The standard operating mode allows large
// packets with a payload up to 500 bytes, // 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 // This is the first part of a split
// packet, so we set the seq variable // packet, so we set the seq variable
// and add the data to the buffer // 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; read_len = 0;
seq = sequence; seq = sequence;
@ -159,6 +188,10 @@ void ISR_VECT receive_callback(int packet_size) {
// packet, so we add it to the buffer // packet, so we add it to the buffer
// and set the ready flag. // 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 #if MCU_VARIANT != MCU_ESP32
last_rssi = (last_rssi+LoRa.packetRssi())/2; last_rssi = (last_rssi+LoRa.packetRssi())/2;
last_snr_raw = (last_snr_raw+LoRa.packetSnrRaw())/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 // same sequence id, so we must assume
// that we are seeing the first part of // that we are seeing the first part of
// a new split packet. // 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; read_len = 0;
seq = sequence; seq = sequence;
@ -188,6 +226,10 @@ void ISR_VECT receive_callback(int packet_size) {
// just read it and set the ready // just read it and set the ready
// flag to true. // flag to true.
#if LIBRARY_TYPE == LIBRARY_C
std::cerr << "\tIs complete packet" << std::endl;
#endif
if (seq != SEQ_UNSET) { if (seq != SEQ_UNSET) {
// If we already had part of a split // If we already had part of a split
// packet in the buffer, we clear it. // packet in the buffer, we clear it.
@ -318,7 +360,7 @@ void flushQueue(void) {
uint16_t processed = 0; uint16_t processed = 0;
#if MCU_VARIANT == MCU_ESP32 #if SERIAL_EVENTS == SERIAL_POLLING
while (!fifo16_isempty(&packet_starts)) { while (!fifo16_isempty(&packet_starts)) {
#else #else
while (!fifo16_isempty_locked(&packet_starts)) { while (!fifo16_isempty_locked(&packet_starts)) {
@ -347,6 +389,9 @@ void flushQueue(void) {
void transmit(uint16_t size) { void transmit(uint16_t size) {
if (radio_online) { if (radio_online) {
if (!promisc) { if (!promisc) {
#if LIBRARY_TYPE == LIBRARY_C
std::cerr << "Sending RNode packet(s) of " << size << " bytes" << std::endl;
#endif
led_tx_on(); led_tx_on();
uint16_t written = 0; uint16_t written = 0;
uint8_t header = random(256) & 0xF0; uint8_t header = random(256) & 0xF0;
@ -379,6 +424,11 @@ void transmit(uint16_t size) {
// In promiscuous mode, we only send out // In promiscuous mode, we only send out
// plain raw LoRa packets with a maximum // plain raw LoRa packets with a maximum
// payload of 255 bytes // payload of 255 bytes
#if LIBRARY_TYPE == LIBRARY_C
std::cerr << "Sending standard packet of " << size << " bytes" << std::endl;
#endif
led_tx_on(); led_tx_on();
uint16_t written = 0; uint16_t written = 0;
@ -683,13 +733,19 @@ void validateStatus() {
uint8_t F_WDR = WDRF; uint8_t F_WDR = WDRF;
#elif MCU_VARIANT == MCU_2560 #elif MCU_VARIANT == MCU_2560
uint8_t boot_flags = OPTIBOOT_MCUSR; 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_POR = PORF;
uint8_t F_BOR = BORF; uint8_t F_BOR = BORF;
uint8_t F_WDR = WDRF; uint8_t F_WDR = WDRF;
#elif MCU_VARIANT == MCU_ESP32 #elif MCU_VARIANT == MCU_ESP32
// TODO: Get ESP32 boot flags // 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_POR = 0x00;
uint8_t F_BOR = 0x00; uint8_t F_BOR = 0x00;
uint8_t F_WDR = 0x01; uint8_t F_WDR = 0x01;
@ -702,12 +758,12 @@ void validateStatus() {
} else if (boot_flags & (1<<F_WDR)) { } else if (boot_flags & (1<<F_WDR)) {
boot_vector = START_FROM_BOOTLOADER; boot_vector = START_FROM_BOOTLOADER;
} else { } else {
Serial.write("Error, indeterminate boot vector\r\n"); debug("Error, indeterminate boot vector\r\n");
led_indicate_boot_error(); led_indicate_boot_error();
} }
if (boot_vector == START_FROM_BOOTLOADER || boot_vector == START_FROM_POWERON) { 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_product_valid() && eeprom_model_valid() && eeprom_hwrev_valid()) {
if (eeprom_checksum_valid()) { if (eeprom_checksum_valid()) {
hw_ready = true; hw_ready = true;
@ -717,16 +773,21 @@ void validateStatus() {
op_mode = MODE_TNC; op_mode = MODE_TNC;
startRadio(); startRadio();
} }
} else {
hw_ready = false;
debug("Error, EEPROM checksum incorrect\r\n");
} }
} else { } else {
hw_ready = false; hw_ready = false;
debug("Error, EEPROM product, model, or revision not valid\r\n");
} }
} else { } else {
hw_ready = false; hw_ready = false;
debug("Error, EEPROM info not locked\r\n");
} }
} else { } else {
hw_ready = false; hw_ready = false;
Serial.write("Error, incorrect boot vector\r\n"); debug("Error, incorrect boot vector\r\n");
led_indicate_boot_error(); led_indicate_boot_error();
} }
} }
@ -747,6 +808,12 @@ void loop() {
} }
#endif #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 (queue_height > 0) {
if (!dcd_waiting) updateModemStatus(); if (!dcd_waiting) updateModemStatus();
@ -775,7 +842,7 @@ void loop() {
} }
} }
#if MCU_VARIANT == MCU_ESP32 #if SERIAL_EVENTS == SERIAL_POLLING
buffer_serial(); buffer_serial();
if (!fifo_isempty(&serialFIFO)) serial_poll(); if (!fifo_isempty(&serialFIFO)) serial_poll();
#else #else
@ -787,7 +854,7 @@ volatile bool serial_polling = false;
void serial_poll() { void serial_poll() {
serial_polling = true; serial_polling = true;
#if MCU_VARIANT != MCU_ESP32 #if SERIAL_EVENTS == SERIAL_INTERRUPT
while (!fifo_isempty_locked(&serialFIFO)) { while (!fifo_isempty_locked(&serialFIFO)) {
#else #else
while (!fifo_isempty(&serialFIFO)) { while (!fifo_isempty(&serialFIFO)) {
@ -812,7 +879,7 @@ void buffer_serial() {
while (c < MAX_CYCLES && Serial.available()) { while (c < MAX_CYCLES && Serial.available()) {
c++; c++;
#if MCU_VARIANT != MCU_ESP32 #if SERIAL_EVENTS == SERIAL_INTERRUPT
if (!fifo_isfull_locked(&serialFIFO)) { if (!fifo_isfull_locked(&serialFIFO)) {
fifo_push_locked(&serialFIFO, Serial.read()); fifo_push_locked(&serialFIFO, Serial.read());
} }
@ -853,8 +920,8 @@ void serial_interrupt_init() {
TIMSK3 = _BV(ICIE3); TIMSK3 = _BV(ICIE3);
#elif MCU_VARIANT == MCU_ESP32 #else
// No interrupt-based polling on ESP32 // No interrupt-based polling on other MCUs.
#endif #endif
} }
@ -864,3 +931,12 @@ void serial_interrupt_init() {
buffer_serial(); buffer_serial();
} }
#endif #endif
#if PLATFORM == PLATFORM_LINUX
int main(int argc, char** argv) {
setup();
while (true) {
loop();
}
}
#endif

View File

@ -1,4 +1,6 @@
#if LIBRARY_TYPE == LIBRARY_ARDUINO
#include <EEPROM.h> #include <EEPROM.h>
#endif
#include <stddef.h> #include <stddef.h>
#include "Config.h" #include "Config.h"
#include "LoRa.h" #include "LoRa.h"
@ -6,6 +8,143 @@
#include "Framing.h" #include "Framing.h"
#include "MD5.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 #if MCU_VARIANT == MCU_ESP32
#include "soc/rtc_wdt.h" #include "soc/rtc_wdt.h"
#define ISR_VECT IRAM_ATTR #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_on() { digitalWrite(pin_led_tx, HIGH); }
void led_tx_off() { digitalWrite(pin_led_tx, LOW); } void led_tx_off() { digitalWrite(pin_led_tx, LOW); }
#endif #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 #endif
void hard_reset(void) { void hard_reset(void) {
@ -79,18 +229,27 @@ void hard_reset(void) {
} }
#elif MCU_VARIANT == MCU_ESP32 #elif MCU_VARIANT == MCU_ESP32
ESP.restart(); 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 #endif
} }
void led_indicate_error(int cycles) { void led_indicate_error(int cycles) {
#if LIBRARY_TYPE == LIBRARY_C
std::cerr << "Indicating error" << std::endl;
#endif
bool forever = (cycles == 0) ? true : false; bool forever = (cycles == 0) ? true : false;
cycles = forever ? 1 : cycles; cycles = forever ? 1 : cycles;
while(cycles > 0) { while(cycles > 0) {
digitalWrite(pin_led_rx, HIGH); led_rx_on();
digitalWrite(pin_led_tx, LOW); led_tx_off();
delay(100); delay(100);
digitalWrite(pin_led_rx, LOW); led_rx_off();
digitalWrite(pin_led_tx, HIGH); led_tx_on();
delay(100); delay(100);
if (!forever) cycles--; if (!forever) cycles--;
} }
@ -99,6 +258,9 @@ void led_indicate_error(int cycles) {
} }
void led_indicate_boot_error() { void led_indicate_boot_error() {
#if LIBRARY_TYPE == LIBRARY_C
std::cerr << "Indicating boot error" << std::endl;
#endif
while (true) { while (true) {
led_tx_on(); led_tx_on();
led_rx_off(); led_rx_off();
@ -110,9 +272,12 @@ void led_indicate_boot_error() {
} }
void led_indicate_warning(int cycles) { void led_indicate_warning(int cycles) {
#if LIBRARY_TYPE == LIBRARY_C
std::cerr << "Indicating warning" << std::endl;
#endif
bool forever = (cycles == 0) ? true : false; bool forever = (cycles == 0) ? true : false;
cycles = forever ? 1 : cycles; cycles = forever ? 1 : cycles;
digitalWrite(pin_led_tx, HIGH); led_tx_on();
while(cycles > 0) { while(cycles > 0) {
led_tx_off(); led_tx_off();
delay(100); delay(100);
@ -123,7 +288,7 @@ void led_indicate_warning(int cycles) {
led_tx_off(); 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) { void led_indicate_info(int cycles) {
bool forever = (cycles == 0) ? true : false; bool forever = (cycles == 0) ? true : false;
cycles = forever ? 1 : cycles; cycles = forever ? 1 : cycles;
@ -165,6 +330,9 @@ void led_indicate_warning(int cycles) {
} }
#else #else
void led_indicate_info(int cycles) { void led_indicate_info(int cycles) {
#if LIBRARY_TYPE == LIBRARY_C
std::cerr << "Indicating info" << std::endl;
#endif
bool forever = (cycles == 0) ? true : false; bool forever = (cycles == 0) ? true : false;
cycles = forever ? 1 : cycles; cycles = forever ? 1 : cycles;
while(cycles > 0) { while(cycles > 0) {
@ -179,8 +347,9 @@ void led_indicate_warning(int cycles) {
#endif #endif
#endif #endif
#if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560 || MCU_VARIANT == MCU_ESP32
unsigned long led_standby_ticks = 0; unsigned long led_standby_ticks = 0;
#endif
#if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560 #if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560
uint8_t led_standby_min = 1; uint8_t led_standby_min = 1;
uint8_t led_standby_max = 40; 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_standby_wait = 1768;
unsigned long led_notready_wait = 150; unsigned long led_notready_wait = 150;
#endif #endif
#if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560 || MCU_VARIANT == MCU_ESP32
uint8_t led_standby_value = led_standby_min; uint8_t led_standby_value = led_standby_min;
int8_t led_standby_direction = 0; int8_t led_standby_direction = 0;
#endif
#if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560 #if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560
void led_indicate_standby() { void led_indicate_standby() {
@ -243,6 +414,17 @@ int8_t led_standby_direction = 0;
#endif #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 #endif
#if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560 #if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560
@ -289,6 +471,17 @@ int8_t led_standby_direction = 0;
#endif #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 #endif
void escapedSerialWrite(uint8_t byte) { void escapedSerialWrite(uint8_t byte) {
@ -551,8 +744,68 @@ void promisc_disable() {
promisc = false; 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() { 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) { if (lock_byte == INFO_LOCK_BYTE) {
return true; return true;
} else { } 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) { void eeprom_update(int mapped_addr, uint8_t byte) {
#if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560 #if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560
EEPROM.update(mapped_addr, byte); EEPROM.update(mapped_addr, byte);
@ -596,8 +821,12 @@ void eeprom_update(int mapped_addr, uint8_t byte) {
EEPROM.write(mapped_addr, byte); EEPROM.write(mapped_addr, byte);
EEPROM.commit(); 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 #endif
} }
void eeprom_write(uint8_t addr, uint8_t byte) { void eeprom_write(uint8_t addr, uint8_t byte) {
@ -615,32 +844,57 @@ void eeprom_erase() {
hard_reset(); hard_reset();
} }
bool eeprom_lock_set() { void eeprom_dump_info() {
if (EEPROM.read(eeprom_addr(ADDR_INFO_LOCK)) == INFO_LOCK_BYTE) { for (int addr = ADDR_PRODUCT; addr <= ADDR_INFO_LOCK; addr++) {
return true; uint8_t byte = eeprom_read(addr);
} else { escapedSerialWrite(byte);
return false;
} }
} }
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() { 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 PLATFORM == PLATFORM_AVR
if (rval == PRODUCT_RNODE || rval == PRODUCT_HMBRW) { if (rval == PRODUCT_RNODE || rval == PRODUCT_HMBRW) {
#elif PLATFORM == PLATFORM_ESP32 #elif PLATFORM == PLATFORM_ESP32
if (rval == PRODUCT_RNODE || rval == PRODUCT_HMBRW || rval == PRODUCT_TBEAM || rval == PRODUCT_T32_20 || rval == PRODUCT_T32_21) { 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 #else
if (false) { if (false) {
#endif #endif
return true; return true;
} else { } else {
#if LIBRARY_TYPE == LIBRARY_C
std::cerr << "Unacceptable platform: " << std::hex << "0x" << (int)rval << std::dec << std::endl;
#endif
return false; return false;
} }
} }
bool eeprom_model_valid() { bool eeprom_model_valid() {
model = EEPROM.read(eeprom_addr(ADDR_MODEL)); model = eeprom_read(ADDR_MODEL);
#if BOARD_MODEL == BOARD_RNODE #if BOARD_MODEL == BOARD_RNODE
if (model == MODEL_A4 || model == MODEL_A9) { if (model == MODEL_A4 || model == MODEL_A9) {
#elif BOARD_MODEL == BOARD_HMBRW #elif BOARD_MODEL == BOARD_HMBRW
@ -660,15 +914,21 @@ bool eeprom_model_valid() {
#endif #endif
return true; return true;
} else { } else {
#if LIBRARY_TYPE == LIBRARY_C
std::cerr << "Unacceptable model: " << std::hex << "0x" << (int)model << std::dec << std::endl;
#endif
return false; return false;
} }
} }
bool eeprom_hwrev_valid() { bool eeprom_hwrev_valid() {
hwrev = EEPROM.read(eeprom_addr(ADDR_HW_REV)); hwrev = eeprom_read(ADDR_HW_REV);
if (hwrev != 0x00 && hwrev != 0xFF) { if (hwrev != 0x00 && hwrev != 0xFF) {
return true; return true;
} else { } else {
#if LIBRARY_TYPE == LIBRARY_C
std::cerr << "Unacceptable revision: " << std::hex << "0x" << (int)hwrev << std::dec << std::endl;
#endif
return false; return false;
} }
} }
@ -676,14 +936,14 @@ bool eeprom_hwrev_valid() {
bool eeprom_checksum_valid() { bool eeprom_checksum_valid() {
char *data = (char*)malloc(CHECKSUMMED_SIZE); char *data = (char*)malloc(CHECKSUMMED_SIZE);
for (uint8_t i = 0; i < CHECKSUMMED_SIZE; i++) { for (uint8_t i = 0; i < CHECKSUMMED_SIZE; i++) {
char byte = EEPROM.read(eeprom_addr(i)); char byte = eeprom_read(i);
data[i] = byte; data[i] = byte;
} }
unsigned char *hash = MD5::make_hash(data, CHECKSUMMED_SIZE); unsigned char *hash = MD5::make_hash(data, CHECKSUMMED_SIZE);
bool checksum_valid = true; bool checksum_valid = true;
for (uint8_t i = 0; i < 16; i++) { 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]; uint8_t calced_chk_byte = (uint8_t)hash[i];
if (stored_chk_byte != calced_chk_byte) { if (stored_chk_byte != calced_chk_byte) {
checksum_valid = false; checksum_valid = false;
@ -696,7 +956,7 @@ bool eeprom_checksum_valid() {
} }
bool eeprom_have_conf() { 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; return true;
} else { } else {
return false; return false;
@ -705,11 +965,11 @@ bool eeprom_have_conf() {
void eeprom_conf_load() { void eeprom_conf_load() {
if (eeprom_have_conf()) { if (eeprom_have_conf()) {
lora_sf = EEPROM.read(eeprom_addr(ADDR_CONF_SF)); lora_sf = eeprom_read(ADDR_CONF_SF);
lora_cr = EEPROM.read(eeprom_addr(ADDR_CONF_CR)); lora_cr = eeprom_read(ADDR_CONF_CR);
lora_txp = EEPROM.read(eeprom_addr(ADDR_CONF_TXP)); lora_txp = eeprom_read(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_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(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_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; f->head = f->tail;
} }
#if MCU_VARIANT != MCU_ESP32 #if SERIAL_EVENTS == SERIAL_INTERRUPT
static inline bool fifo_isempty_locked(const FIFOBuffer *f) { static inline bool fifo_isempty_locked(const FIFOBuffer *f) {
bool result; bool result;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
@ -866,7 +1126,7 @@ inline void fifo16_flush(FIFOBuffer16 *f) {
f->head = f->tail; f->head = f->tail;
} }
#if MCU_VARIANT != MCU_ESP32 #if SERIAL_EVENTS == SERIAL_INTERRUPT
static inline bool fifo16_isempty_locked(const FIFOBuffer16 *f) { static inline bool fifo16_isempty_locked(const FIFOBuffer16 *f) {
bool result; bool result;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {