First commit: receiving LoRa APRS messages works.

This commit is contained in:
2022-04-29 17:38:15 +02:00
parent c3ea82cb9f
commit a04e4ca9ac
581 changed files with 67853 additions and 1 deletions

35
src/CMakeLists.txt Normal file
View File

@@ -0,0 +1,35 @@
cmake_minimum_required(VERSION 3.12)
# Pull in SDK (must be before project)
# project(LoRa_pico_lib)
add_library(LoRa_pico_lib LoRa-RP2040.cpp LoRa-RP2040.h)
add_library(LoRa_print Print.h Print.cpp)
target_link_libraries(LoRa_pico_lib pico_stdlib hardware_spi hardware_interp LoRa_print)
# enable usb output, disable uart output
pico_enable_stdio_usb(LoRa_pico_lib 1)
pico_enable_stdio_uart(LoRa_pico_lib 0)
add_executable(main
main.cpp
)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
target_include_directories(main PUBLIC ./)
target_link_directories(main PUBLIC ./)
target_link_libraries(main pico_stdlib LoRa_pico_lib LoRa_print)
# enable usb output, disable uart output
pico_enable_stdio_usb(main 1)
pico_enable_stdio_uart(main 0)
# create map/bin/hex/uf2 file in addition to ELF.
pico_add_extra_outputs(main)

60
src/Config.h Normal file
View File

@@ -0,0 +1,60 @@
#ifndef CONFIG_H
#define CONFIG_H
#define MCU_VARIANT RPI_PICO
#define MTU 255
#define CMD_L 4
int lastRssi = -292;
uint8_t lastRssiRaw = 0x00;
size_t readLength = 0;
#if MCU_VARIANT == RPI_PICO
const int pinNSS = 10;
const int pinNRST = 9;
const int pinDIO0 = 2;
// const int pinLedRx = 5;
// const int pinLedTx = 4;
#endif
const long serialBaudRate = 38400;
const int rssiOffset = 292;
const int loraRxTurnaround = 50;
// Default LoRa settings
int loraSpreadingFactor = 12;
int loraPreamble = 8;
int loraCodingRate = 5;
int loraTxPower = 17;
int LoRaPaSelect = 1;
uint32_t loraBandwidth = 125E3;
uint32_t loraFrequency = 433775000;
uint8_t txBuffer[MTU];
uint8_t rxBuffer[MTU];
uint32_t statRx = 0;
uint32_t statTx = 0;
bool outboundReady = false;
bool statSignalDetected = false;
bool statSignalSynced = false;
bool statRxOngoing = false;
bool dcd = false;
bool dcdLed = false;
bool dcdWaiting = false;
uint16_t dcdCount = 0;
uint16_t dcdThreshold = 15;
uint32_t statusIntervalms = 3;
uint32_t lastStatusUpdate = 0;
// Status flags
const uint8_t SIG_DETECT = 0x01;
const uint8_t SIG_SYNCED = 0x02;
const uint8_t RX_ONGOING = 0x04;
#endif

28
src/KISS.h Normal file
View File

@@ -0,0 +1,28 @@
#ifndef KISS_H
#define KISS_H
#define FEND 0xC0
#define FESC 0xDB
#define TFEND 0xDC
#define TFESC 0xDD
#define CMD_UNKNOWN 0xFE
#define CMD_DATA 0x00
#define CMD_HARDWARE 0x06
#define HW_RSSI 0x21
#define CMD_ERROR 0x90
#define ERROR_INITRADIO 0x01
#define ERROR_TXFAILED 0x02
#define ERROR_QUEUE_FULL 0x04
size_t frameLength;
bool inFrame = false;
bool escape = false;
bool SERIAL_READING = false;
uint8_t command = CMD_UNKNOWN;
uint32_t lastSerialRead = 0;
uint32_t serialReadTimeout = 25;
#endif

732
src/LoRa-RP2040.cpp Normal file
View File

@@ -0,0 +1,732 @@
#include "LoRa-RP2040.h"
// registers
#define REG_FIFO 0x00
#define REG_OP_MODE 0x01
#define REG_FRF_MSB 0x06
#define REG_FRF_MID 0x07
#define REG_FRF_LSB 0x08
#define REG_PA_CONFIG 0x09
#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 0x12
#define REG_RX_NB_BYTES 0x13
#define REG_PKT_SNR_VALUE 0x19
#define REG_PKT_RSSI_VALUE 0x1a
#define REG_RSSI_VALUE 0x1b
#define REG_MODEM_CONFIG_1 0x1d
#define REG_MODEM_CONFIG_2 0x1e
#define REG_PREAMBLE_MSB 0x20
#define REG_PREAMBLE_LSB 0x21
#define REG_PAYLOAD_LENGTH 0x22
#define REG_MODEM_CONFIG_3 0x26
#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_DETECTION_OPTIMIZE 0x31
#define REG_INVERTIQ 0x33
#define REG_DETECTION_THRESHOLD 0x37
#define REG_SYNC_WORD 0x39
#define REG_INVERTIQ2 0x3b
#define REG_DIO_MAPPING_1 0x40
#define REG_VERSION 0x42
#define REG_PA_DAC 0x4d
// modes
#define MODE_LONG_RANGE_MODE 0x80
#define MODE_SLEEP 0x00
#define MODE_STDBY 0x01
#define MODE_TX 0x03
#define MODE_RX_CONTINUOUS 0x05
#define MODE_RX_SINGLE 0x06
// PA config
#define PA_BOOST 0x80
// IRQ masks
#define IRQ_TX_DONE_MASK 0x08
#define IRQ_PAYLOAD_CRC_ERROR_MASK 0x20
#define IRQ_RX_DONE_MASK 0x40
#define RF_MID_BAND_THRESHOLD 525E6
#define RSSI_OFFSET_HF_PORT 157
#define RSSI_OFFSET_LF_PORT 164
#define MAX_PKT_LENGTH 255
#if (ESP8266 || ESP32)
#define ISR_PREFIX ICACHE_RAM_ATTR
#else
#define ISR_PREFIX
#endif
LoRaClass::LoRaClass() :
_spi(SPI_PORT),
_ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN),
_frequency(0),
_packetIndex(0),
_implicitHeaderMode(0),
_onReceive(NULL),
_onTxDone(NULL)
{}
int LoRaClass::begin(long frequency)
{
// setup pins
gpio_init(_ss);
gpio_set_dir(_ss, GPIO_OUT);
// set SS high
gpio_put(_ss, 1);
if (_reset != -1) {
gpio_init(_reset);
gpio_set_dir(_reset, GPIO_OUT);
// perform reset
gpio_put(_reset, 0);
sleep_ms(10);
gpio_put(_reset, 1);
sleep_ms(10);
}
// start SPI
spi_init(SPI_PORT, 12500);
gpio_set_function(PIN_MISO, GPIO_FUNC_SPI);
gpio_set_function(PIN_SCK, GPIO_FUNC_SPI);
gpio_set_function(PIN_MOSI, GPIO_FUNC_SPI);
// Make the SPI pins available to picotool
bi_decl(bi_3pins_with_func(PIN_MISO, PIN_MOSI, PIN_SCK, GPIO_FUNC_SPI));
gpio_init(PIN_CS);
gpio_set_dir(PIN_CS, GPIO_OUT);
gpio_put(PIN_CS, 1);;
// Make the CS pin available to picotool
bi_decl(bi_1pin_with_name(PIN_CS, "SPI CS"));
// check version
uint8_t version = readRegister(REG_VERSION);
if (version != 0x12) {
return 0;
}
// put in sleep mode
sleep();
// set frequency
setFrequency(frequency);
// set base addresses
writeRegister(REG_FIFO_TX_BASE_ADDR, 0);
writeRegister(REG_FIFO_RX_BASE_ADDR, 0);
// set LNA boost
writeRegister(REG_LNA, readRegister(REG_LNA) | 0x03);
// set auto AGC
writeRegister(REG_MODEM_CONFIG_3, 0x04);
// set output power to 17 dBm
setTxPower(17);
// put in standby mode
idle();
return 1;
}
void LoRaClass::end()
{
// put in sleep mode
sleep();
// stop SPI
spi_deinit(SPI_PORT);
}
int LoRaClass::beginPacket(int implicitHeader)
{
if (isTransmitting()) {
return 0;
}
// put in standby mode
idle();
if (implicitHeader) {
implicitHeaderMode();
} else {
explicitHeaderMode();
}
// reset FIFO address and paload length
writeRegister(REG_FIFO_ADDR_PTR, 0);
writeRegister(REG_PAYLOAD_LENGTH, 0);
return 1;
}
int LoRaClass::endPacket(bool async)
{
if ((async) && (_onTxDone))
writeRegister(REG_DIO_MAPPING_1, 0x40); // DIO0 => TXDONE
// put in TX mode
writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_TX);
if (!async) {
// wait for TX done
while ((readRegister(REG_IRQ_FLAGS) & IRQ_TX_DONE_MASK) == 0) {
sleep_ms(0);
}
// clear IRQ's
writeRegister(REG_IRQ_FLAGS, IRQ_TX_DONE_MASK);
}
return 1;
}
bool LoRaClass::isTransmitting()
{
if ((readRegister(REG_OP_MODE) & MODE_TX) == MODE_TX) {
return true;
}
if (readRegister(REG_IRQ_FLAGS) & IRQ_TX_DONE_MASK) {
// clear IRQ's
writeRegister(REG_IRQ_FLAGS, IRQ_TX_DONE_MASK);
}
return false;
}
int LoRaClass::parsePacket(int size)
{
int packetLength = 0;
int irqFlags = readRegister(REG_IRQ_FLAGS);
if (size > 0) {
implicitHeaderMode();
writeRegister(REG_PAYLOAD_LENGTH, size & 0xff);
} else {
explicitHeaderMode();
}
// clear IRQ's
writeRegister(REG_IRQ_FLAGS, irqFlags);
writeRegister(REG_IRQ_FLAGS, irqFlags);
if ((irqFlags & IRQ_RX_DONE_MASK) && (irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) {
// received a packet
_packetIndex = 0;
// read packet length
if (_implicitHeaderMode) {
packetLength = readRegister(REG_PAYLOAD_LENGTH);
} else {
packetLength = readRegister(REG_RX_NB_BYTES);
}
// set FIFO address to current RX address
writeRegister(REG_FIFO_ADDR_PTR, readRegister(REG_FIFO_RX_CURRENT_ADDR));
// put in standby mode
idle();
} else if (readRegister(REG_OP_MODE) != (MODE_LONG_RANGE_MODE | MODE_RX_SINGLE)) {
// not currently in RX mode
// reset FIFO address
writeRegister(REG_FIFO_ADDR_PTR, 0);
// put in single RX mode
writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_SINGLE);
}
return packetLength;
}
int LoRaClass::packetRssi()
{
return (readRegister(REG_PKT_RSSI_VALUE) - (_frequency < RF_MID_BAND_THRESHOLD ? RSSI_OFFSET_LF_PORT : RSSI_OFFSET_HF_PORT));
}
float LoRaClass::packetSnr()
{
return ((int8_t)readRegister(REG_PKT_SNR_VALUE)) * 0.25;
}
long LoRaClass::packetFrequencyError()
{
int32_t freqError = 0;
freqError = static_cast<int32_t>(readRegister(REG_FREQ_ERROR_MSB) & 0x111);
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) & 0x1000) { // Sign bit is on
freqError -= 524288; // B1000'0000'0000'0000'0000
}
const float fXtal = 32E6; // FXOSC: crystal oscillator (XTAL) frequency (2.5. Chip Specification, p. 14)
const float fError = ((static_cast<float>(freqError) * (1L << 24)) / fXtal) * (getSignalBandwidth() / 500000.0f); // p. 37
return static_cast<long>(fError);
}
int LoRaClass::rssi()
{
return (readRegister(REG_RSSI_VALUE) - (_frequency < RF_MID_BAND_THRESHOLD ? RSSI_OFFSET_LF_PORT : RSSI_OFFSET_HF_PORT));
}
size_t LoRaClass::write(uint8_t byte)
{
return write(&byte, sizeof(byte));
}
size_t LoRaClass::write(const uint8_t *buffer, size_t size)
{
int currentLength = readRegister(REG_PAYLOAD_LENGTH);
// check size
if ((currentLength + size) > MAX_PKT_LENGTH) {
size = MAX_PKT_LENGTH - currentLength;
}
// write data
for (size_t i = 0; i < size; i++) {
writeRegister(REG_FIFO, buffer[i]);
}
// update length
writeRegister(REG_PAYLOAD_LENGTH, currentLength + size);
return size;
}
int LoRaClass::available()
{
return (readRegister(REG_RX_NB_BYTES) - _packetIndex);
}
int LoRaClass::read()
{
if (!available()) {
return -1;
}
_packetIndex++;
return readRegister(REG_FIFO);
}
int LoRaClass::peek()
{
if (!available()) {
return -1;
}
// store current FIFO address
int currentAddress = readRegister(REG_FIFO_ADDR_PTR);
// read
uint8_t b = readRegister(REG_FIFO);
// restore FIFO address
writeRegister(REG_FIFO_ADDR_PTR, currentAddress);
return b;
}
void LoRaClass::flush()
{
}
void LoRaClass::onReceive(void(*callback)(int))
{
_onReceive = callback;
if (callback) {
gpio_set_irq_enabled_with_callback(_dio0, GPIO_IRQ_EDGE_RISE, true, &LoRaClass::onDio0Rise);
} else {
gpio_set_irq_enabled(_dio0, GPIO_IRQ_EDGE_RISE, false);
}
}
void LoRaClass::onTxDone(void(*callback)())
{
_onTxDone = callback;
if (callback) {
gpio_set_irq_enabled_with_callback(_dio0, GPIO_IRQ_EDGE_RISE, true, &LoRaClass::onDio0Rise);
} else {
gpio_set_irq_enabled(_dio0, GPIO_IRQ_EDGE_RISE, false);
}
}
void LoRaClass::receive(int size)
{
writeRegister(REG_DIO_MAPPING_1, 0x00); // DIO0 => RXDONE
if (size > 0) {
implicitHeaderMode();
writeRegister(REG_PAYLOAD_LENGTH, size & 0xff);
} else {
explicitHeaderMode();
}
writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_CONTINUOUS);
}
void LoRaClass::idle()
{
writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_STDBY);
}
void LoRaClass::sleep()
{
writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_SLEEP);
}
void LoRaClass::setTxPower(int level, int outputPin)
{
if (PA_OUTPUT_RFO_PIN == outputPin) {
// RFO
if (level < 0) {
level = 0;
} else if (level > 14) {
level = 14;
}
writeRegister(REG_PA_CONFIG, 0x70 | level);
} else {
// PA BOOST
if (level > 17) {
if (level > 20) {
level = 20;
}
// subtract 3 from level, so 18 - 20 maps to 15 - 17
level -= 3;
// High Power +20 dBm Operation (Semtech SX1276/77/78/79 5.4.3.)
writeRegister(REG_PA_DAC, 0x87);
setOCP(140);
} else {
if (level < 2) {
level = 2;
}
//Default value PA_HF/LF or +17dBm
writeRegister(REG_PA_DAC, 0x84);
setOCP(100);
}
writeRegister(REG_PA_CONFIG, PA_BOOST | (level - 2));
}
}
void LoRaClass::setFrequency(long frequency)
{
_frequency = frequency;
uint64_t frf = ((uint64_t)frequency << 19) / 32000000;
writeRegister(REG_FRF_MSB, (uint8_t)(frf >> 16));
writeRegister(REG_FRF_MID, (uint8_t)(frf >> 8));
writeRegister(REG_FRF_LSB, (uint8_t)(frf >> 0));
}
int LoRaClass::getSpreadingFactor()
{
return readRegister(REG_MODEM_CONFIG_2) >> 4;
}
void LoRaClass::setSpreadingFactor(int sf)
{
if (sf < 6) {
sf = 6;
} else if (sf > 12) {
sf = 12;
}
if (sf == 6) {
writeRegister(REG_DETECTION_OPTIMIZE, 0xc5);
writeRegister(REG_DETECTION_THRESHOLD, 0x0c);
} else {
writeRegister(REG_DETECTION_OPTIMIZE, 0xc3);
writeRegister(REG_DETECTION_THRESHOLD, 0x0a);
}
writeRegister(REG_MODEM_CONFIG_2, (readRegister(REG_MODEM_CONFIG_2) & 0x0f) | ((sf << 4) & 0xf0));
setLdoFlag();
}
long LoRaClass::getSignalBandwidth()
{
uint8_t bw = (readRegister(REG_MODEM_CONFIG_1) >> 4);
switch (bw) {
case 0: return 7.8E3;
case 1: return 10.4E3;
case 2: return 15.6E3;
case 3: return 20.8E3;
case 4: return 31.25E3;
case 5: return 41.7E3;
case 6: return 62.5E3;
case 7: return 125E3;
case 8: return 250E3;
case 9: return 500E3;
}
return -1;
}
void LoRaClass::setSignalBandwidth(long sbw)
{
int bw;
if (sbw <= 7.8E3) {
bw = 0;
} else if (sbw <= 10.4E3) {
bw = 1;
} else if (sbw <= 15.6E3) {
bw = 2;
} else if (sbw <= 20.8E3) {
bw = 3;
} else if (sbw <= 31.25E3) {
bw = 4;
} else if (sbw <= 41.7E3) {
bw = 5;
} else if (sbw <= 62.5E3) {
bw = 6;
} else if (sbw <= 125E3) {
bw = 7;
} else if (sbw <= 250E3) {
bw = 8;
} else /*if (sbw <= 250E3)*/ {
bw = 9;
}
writeRegister(REG_MODEM_CONFIG_1, (readRegister(REG_MODEM_CONFIG_1) & 0x0f) | (bw << 4));
setLdoFlag();
}
void LoRaClass::setLdoFlag()
{
long symbolDuration = 1000 / ( getSignalBandwidth() / (1L << getSpreadingFactor()) ) ;
bool ldoOn = symbolDuration > 16;
uint8_t config3 = readRegister(REG_MODEM_CONFIG_3);
config3 = ldoOn ? config3 | (1 << 3) : config3 & ~(1 << 3);
writeRegister(REG_MODEM_CONFIG_3, config3);
}
void LoRaClass::setCodingRate4(int denominator)
{
if (denominator < 5) {
denominator = 5;
} else if (denominator > 8) {
denominator = 8;
}
int cr = denominator - 4;
writeRegister(REG_MODEM_CONFIG_1, (readRegister(REG_MODEM_CONFIG_1) & 0xf1) | (cr << 1));
}
void LoRaClass::setPreambleLength(long length)
{
writeRegister(REG_PREAMBLE_MSB, (uint8_t)(length >> 8));
writeRegister(REG_PREAMBLE_LSB, (uint8_t)(length >> 0));
}
void LoRaClass::setSyncWord(int sw)
{
writeRegister(REG_SYNC_WORD, sw);
}
void LoRaClass::enableCrc()
{
writeRegister(REG_MODEM_CONFIG_2, readRegister(REG_MODEM_CONFIG_2) | 0x04);
}
void LoRaClass::disableCrc()
{
writeRegister(REG_MODEM_CONFIG_2, readRegister(REG_MODEM_CONFIG_2) & 0xfb);
}
void LoRaClass::enableInvertIQ()
{
writeRegister(REG_INVERTIQ, 0x66);
writeRegister(REG_INVERTIQ2, 0x19);
}
void LoRaClass::disableInvertIQ()
{
writeRegister(REG_INVERTIQ, 0x27);
writeRegister(REG_INVERTIQ2, 0x1d);
}
void LoRaClass::setOCP(uint8_t mA)
{
uint8_t ocpTrim = 27;
if (mA <= 120) {
ocpTrim = (mA - 45) / 5;
} else if (mA <=240) {
ocpTrim = (mA + 30) / 10;
}
writeRegister(REG_OCP, 0x20 | (0x1F & ocpTrim));
}
void LoRaClass::setGain(uint8_t gain)
{
// check allowed range
if (gain > 6) {
gain = 6;
}
// set to standby
idle();
// set gain
if (gain == 0) {
// if gain = 0, enable AGC
writeRegister(REG_MODEM_CONFIG_3, 0x04);
} else {
// disable AGC
writeRegister(REG_MODEM_CONFIG_3, 0x00);
// clear Gain and set LNA boost
writeRegister(REG_LNA, 0x03);
// set gain
writeRegister(REG_LNA, readRegister(REG_LNA) | (gain << 5));
}
}
uint8_t LoRaClass::random()
{
return readRegister(REG_RSSI_WIDEBAND);
}
void LoRaClass::setPins(int ss, int reset, int dio0)
{
_ss = ss;
_reset = reset;
_dio0 = dio0;
}
void LoRaClass::setSPI(spi_inst_t& spi)
{
_spi = &spi;
}
void LoRaClass::setSPIFrequency(uint32_t frequency)
{
spi_set_baudrate(SPI_PORT, frequency);
}
void LoRaClass::dumpRegisters()
{
for (int i = 0; i < 128; i++) {
printf("0x%x: 0x%x",i,readRegister(i));
}
}
void LoRaClass::explicitHeaderMode()
{
_implicitHeaderMode = 0;
writeRegister(REG_MODEM_CONFIG_1, readRegister(REG_MODEM_CONFIG_1) & 0xfe);
}
void LoRaClass::implicitHeaderMode()
{
_implicitHeaderMode = 1;
writeRegister(REG_MODEM_CONFIG_1, readRegister(REG_MODEM_CONFIG_1) | 0x01);
}
void LoRaClass::handleDio0Rise()
{
int irqFlags = readRegister(REG_IRQ_FLAGS);
// clear IRQ's
writeRegister(REG_IRQ_FLAGS, irqFlags);
writeRegister(REG_IRQ_FLAGS, irqFlags);
if ((irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) {
if ((irqFlags & IRQ_RX_DONE_MASK) != 0) {
// received a packet
_packetIndex = 0;
// read packet length
int packetLength = _implicitHeaderMode ? readRegister(REG_PAYLOAD_LENGTH) : readRegister(REG_RX_NB_BYTES);
// set FIFO address to current RX address
writeRegister(REG_FIFO_ADDR_PTR, readRegister(REG_FIFO_RX_CURRENT_ADDR));
if (_onReceive) {
_onReceive(packetLength);
}
}
else if ((irqFlags & IRQ_TX_DONE_MASK) != 0) {
if (_onTxDone) {
_onTxDone();
}
}
}
}
uint8_t LoRaClass::readRegister(uint8_t address)
{
return singleTransfer(address & 0x7f, 0x00);
}
void LoRaClass::writeRegister(uint8_t address, uint8_t value)
{
singleTransfer(address | 0x80, value);
}
uint8_t LoRaClass::singleTransfer(uint8_t address, uint8_t value)
{
uint8_t response;
gpio_put(_ss, 0);
spi_write_blocking(SPI_PORT, &address, 1);
spi_write_read_blocking(SPI_PORT, &value, &response, 1);
gpio_put(_ss, 1);
return response;
}
void LoRaClass::onDio0Rise(uint gpio, uint32_t events)
{
gpio_acknowledge_irq(gpio, events);
LoRa.handleDio0Rise();
}
LoRaClass LoRa;

135
src/LoRa-RP2040.h Normal file
View File

@@ -0,0 +1,135 @@
#ifndef LORA_H
#define LORA_H
// // #include <Arduino.h>
// #include <SPI.h>
#include "pico/stdlib.h"
#include "pico/binary_info.h"
#include "hardware/gpio.h"
#include "hardware/spi.h"
#include "string.h"
#include "Print.h"
#define PIN_MISO 16
#define PIN_CS 8
#define PIN_SCK 18
#define PIN_MOSI 19
#define SPI_PORT spi0
#define READ_BIT 0x80
#define LORA_DEFAULT_SPI spi0
#define LORA_DEFAULT_SPI_FREQUENCY 8E6
#define LORA_DEFAULT_SS_PIN 8
#define LORA_DEFAULT_RESET_PIN 11
#define LORA_DEFAULT_DIO0_PIN 10
#define LORA_DEFAULT_DIO1_PIN 9
#endif
#define PA_OUTPUT_RFO_PIN 0
#define PA_OUTPUT_PA_BOOST_PIN 1
static void __empty();
//class LoRaClass : public Stream {
class LoRaClass : public Print {
public:
LoRaClass();
int begin(long frequency);
void end();
int beginPacket(int implicitHeader = false);
int endPacket(bool async = false);
int parsePacket(int size = 0);
int packetRssi();
float packetSnr();
long packetFrequencyError();
int rssi();
// from Print
virtual size_t write(uint8_t byte);
virtual size_t write(const uint8_t *buffer, size_t size);
// from Stream
virtual int available();
virtual int read();
virtual int peek();
virtual void flush();
void onReceive(void(*callback)(int));
void onTxDone(void(*callback)());
void receive(int size = 0);
void idle();
void sleep();
// size_t print(const char* c);
void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN);
void setFrequency(long frequency);
void setSpreadingFactor(int sf);
void setSignalBandwidth(long sbw);
void setCodingRate4(int denominator);
void setPreambleLength(long length);
void setSyncWord(int sw);
void enableCrc();
void disableCrc();
void enableInvertIQ();
void disableInvertIQ();
void setOCP(uint8_t mA); // Over Current Protection control
void setGain(uint8_t gain); // Set LNA gain
// deprecated
void crc() { enableCrc(); }
void noCrc() { disableCrc(); }
uint8_t random();
void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN);
void setSPI(spi_inst_t& spi);
void setSPIFrequency(uint32_t frequency);
void dumpRegisters();
private:
void explicitHeaderMode();
void implicitHeaderMode();
void handleDio0Rise();
bool isTransmitting();
int getSpreadingFactor();
long getSignalBandwidth();
void setLdoFlag();
uint8_t readRegister(uint8_t address);
void writeRegister(uint8_t address, uint8_t value);
uint8_t singleTransfer(uint8_t address, uint8_t value);
static void onDio0Rise(uint, uint32_t);
private:
// SPISettings _spiSettings;
spi_inst_t* _spi;
int _ss;
int _reset;
int _dio0;
long _frequency;
int _packetIndex;
int _implicitHeaderMode;
void (*_onReceive)(int);
void (*_onTxDone)();
};
extern LoRaClass LoRa;
// #endif

344
src/Print.cpp Normal file
View File

@@ -0,0 +1,344 @@
/*
Copyright (c) 2014 Arduino. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <string>
#include <math.h>
#include "Print.h"
using std::string;
// Public Methods //////////////////////////////////////////////////////////////
/* default implementation: may be overridden */
size_t Print::write(const uint8_t *buffer, size_t size)
{
size_t n = 0;
while (size--) {
if (write(*buffer++)) n++;
else break;
}
return n;
}
size_t Print::print(string str)
{
return write(str.c_str(), str.size());
}
// size_t Print::print(const char str[])
// {
// return write(str);
// }
size_t Print::print(char c)
{
return write(c);
}
size_t Print::print(const char* c){
return write(c);
}
size_t Print::print(unsigned char b, int base)
{
return print((unsigned long) b, base);
}
size_t Print::print(int n, int base)
{
return print((long) n, base);
}
size_t Print::print(unsigned int n, int base)
{
return print((unsigned long) n, base);
}
size_t Print::print(long n, int base)
{
if (base == 0) {
return write(n);
} else if (base == 10) {
if (n < 0) {
int t = print('-');
n = -n;
return printNumber(n, 10) + t;
}
return printNumber(n, 10);
} else {
return printNumber(n, base);
}
}
size_t Print::print(unsigned long n, int base)
{
if (base == 0) return write(n);
else return printNumber(n, base);
}
size_t Print::print(long long n, int base)
{
if (base == 0) {
return write(n);
} else if (base == 10) {
if (n < 0) {
int t = print('-');
n = -n;
return printULLNumber(n, 10) + t;
}
return printULLNumber(n, 10);
} else {
return printULLNumber(n, base);
}
}
size_t Print::print(unsigned long long n, int base)
{
if (base == 0) return write(n);
else return printULLNumber(n, base);
}
size_t Print::print(double n, int digits)
{
return printFloat(n, digits);
}
size_t Print::println(void)
{
return write("\r\n");
}
size_t Print::println(const char c[])
{
size_t n = print(c);
n += println();
return n;
}
size_t Print::println(char c)
{
size_t n = print(c);
n += println();
return n;
}
size_t Print::println(unsigned char b, int base)
{
size_t n = print(b, base);
n += println();
return n;
}
size_t Print::println(int num, int base)
{
size_t n = print(num, base);
n += println();
return n;
}
size_t Print::println(unsigned int num, int base)
{
size_t n = print(num, base);
n += println();
return n;
}
size_t Print::println(long num, int base)
{
size_t n = print(num, base);
n += println();
return n;
}
size_t Print::println(unsigned long num, int base)
{
size_t n = print(num, base);
n += println();
return n;
}
size_t Print::println(long long num, int base)
{
size_t n = print(num, base);
n += println();
return n;
}
size_t Print::println(unsigned long long num, int base)
{
size_t n = print(num, base);
n += println();
return n;
}
size_t Print::println(double num, int digits)
{
size_t n = print(num, digits);
n += println();
return n;
}
// Private Methods /////////////////////////////////////////////////////////////
size_t Print::printNumber(unsigned long n, uint8_t base)
{
char buf[8 * sizeof(long) + 1]; // Assumes 8-bit chars plus zero byte.
char *str = &buf[sizeof(buf) - 1];
*str = '\0';
// prevent crash if called with base == 1
if (base < 2) base = 10;
do {
char c = n % base;
n /= base;
*--str = c < 10 ? c + '0' : c + 'A' - 10;
} while(n);
return write(str);
}
// REFERENCE IMPLEMENTATION FOR ULL
// size_t Print::printULLNumber(unsigned long long n, uint8_t base)
// {
// // if limited to base 10 and 16 the bufsize can be smaller
// char buf[65];
// char *str = &buf[64];
// *str = '\0';
// // prevent crash if called with base == 1
// if (base < 2) base = 10;
// do {
// unsigned long long t = n / base;
// char c = n - t * base; // faster than c = n%base;
// n = t;
// *--str = c < 10 ? c + '0' : c + 'A' - 10;
// } while(n);
// return write(str);
// }
// FAST IMPLEMENTATION FOR ULL
size_t Print::printULLNumber(unsigned long long n64, uint8_t base)
{
// if limited to base 10 and 16 the bufsize can be 20
char buf[64];
uint8_t i = 0;
uint8_t innerLoops = 0;
// prevent crash if called with base == 1
if (base < 2) base = 10;
// process chunks that fit in "16 bit math".
uint16_t top = 0xFFFF / base;
uint16_t th16 = 1;
while (th16 < top)
{
th16 *= base;
innerLoops++;
}
while (n64 > th16)
{
// 64 bit math part
uint64_t q = n64 / th16;
uint16_t r = n64 - q*th16;
n64 = q;
// 16 bit math loop to do remainder. (note buffer is filled reverse)
for (uint8_t j=0; j < innerLoops; j++)
{
uint16_t qq = r/base;
buf[i++] = r - qq*base;
r = qq;
}
}
uint16_t n16 = n64;
while (n16 > 0)
{
uint16_t qq = n16/base;
buf[i++] = n16 - qq*base;
n16 = qq;
}
size_t bytes = i;
for (; i > 0; i--)
write((char) (buf[i - 1] < 10 ?
'0' + buf[i - 1] :
'A' + buf[i - 1] - 10));
return bytes;
}
size_t Print::printFloat(double number, int digits)
{
if (digits < 0)
digits = 2;
size_t n = 0;
if (isnan(number)) return print("nan");
if (isinf(number)) return print("inf");
if (number > 4294967040.0) return print ("ovf"); // constant determined empirically
if (number <-4294967040.0) return print ("ovf"); // constant determined empirically
// Handle negative numbers
if (number < 0.0)
{
n += print('-');
number = -number;
}
// Round correctly so that print(1.999, 2) prints as "2.00"
double rounding = 0.5;
for (uint8_t i=0; i<digits; ++i)
rounding /= 10.0;
number += rounding;
// Extract the integer part of the number and print it
unsigned long int_part = (unsigned long)number;
double remainder = number - (double)int_part;
n += print(int_part);
// Print the decimal point, but only if there are digits beyond
if (digits > 0) {
n += print(".");
}
// Extract digits from the remainder one at a time
while (digits-- > 0)
{
remainder *= 10.0;
unsigned int toPrint = (unsigned int)remainder;
n += print(toPrint);
remainder -= toPrint;
}
return n;
}

90
src/Print.h Normal file
View File

@@ -0,0 +1,90 @@
/*
Copyright (c) 2016 Arduino LLC. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
#include <inttypes.h>
#include <stdio.h> // for size_t
#include <string>
#define DEC 10
#define HEX 16
#define OCT 8
#define BIN 2
using std::string;
class Print
{
private:
int write_error;
size_t printNumber(unsigned long, uint8_t);
size_t printULLNumber(unsigned long long, uint8_t);
size_t printFloat(double, int);
protected:
void setWriteError(int err = 1) { write_error = err; }
public:
Print() : write_error(0) {}
int getWriteError() { return write_error; }
void clearWriteError() { setWriteError(0); }
virtual size_t write(uint8_t) = 0;
size_t write(const char *str) {
if (str == NULL) return 0;
return write((const uint8_t *)str, strlen(str));
}
virtual size_t write(const uint8_t *buffer, size_t size);
size_t write(const char *buffer, size_t size) {
return write((const uint8_t *)buffer, size);
}
// default to zero, meaning "a single write may block"
// should be overridden by subclasses with buffering
virtual int availableForWrite() { return 0; }
// size_t print(const char[]);
size_t print(char);
size_t print(const char*);
size_t print(string c);
size_t print(unsigned char, int = DEC);
size_t print(int, int = DEC);
size_t print(unsigned int, int = DEC);
size_t print(long, int = DEC);
size_t print(unsigned long, int = DEC);
size_t print(long long, int = DEC);
size_t print(unsigned long long, int = DEC);
size_t print(double, int = 2);
size_t println(const char[]);
size_t println(char);
size_t println(unsigned char, int = DEC);
size_t println(int, int = DEC);
size_t println(unsigned int, int = DEC);
size_t println(long, int = DEC);
size_t println(unsigned long, int = DEC);
size_t println(long long, int = DEC);
size_t println(unsigned long long, int = DEC);
size_t println(double, int = 2);
size_t println(void);
virtual void flush() { /* Empty implementation for backward compatibility */ }
};

244
src/lora-kiss-tnc.ino Normal file
View File

@@ -0,0 +1,244 @@
#include <SPI.h>
#include "LoRa.h"
#include "Config.h"
#include "KISS.h"
void setup() {
// put your setup code here, to run once:
Serial.begin(serialBaudRate);
while (!Serial); // Waiting until LoRa32u4 is ready
// Buffers
memset(rxBuffer, 0, sizeof(rxBuffer));
memset(txBuffer, 0, sizeof(txBuffer));
LoRa.setPins(pinNSS, pinNRST, pinDIO0);
startRadio();
}
bool startRadio() {
if (!LoRa.begin(loraFrequency)) {
kissIndicateError(ERROR_INITRADIO);
Serial.println("FAIL");
while(1);
}
else {
Serial.println("SUCCESS");
LoRa.setSpreadingFactor(loraSpreadingFactor);
LoRa.setCodingRate4(loraCodingRate);
LoRa.enableCrc();
LoRa.onReceive(receiveCallback);
LoRa.receive();
}
}
void transmit(size_t size) {
size_t written = 0;
if (size > MTU) {
size = MTU;
}
LoRa.beginPacket();
for (size_t i; i < size; i++) {
LoRa.write(txBuffer[i]);
written++;
}
LoRa.endPacket();
LoRa.receive();
}
void serialCallback(uint8_t txByte) {
if (inFrame && txByte == FEND && command == CMD_DATA) {
inFrame = false;
//Serial.println("FULL_KISS");
if (outboundReady) {
kissIndicateError(ERROR_QUEUE_FULL);
}
else {
outboundReady = true;
//Serial.println("RDY_OUT");
}
}
else if (txByte == FEND) {
//Serial.println("KISS_FLAG");
inFrame = true;
command = CMD_UNKNOWN;
frameLength = 0;
}
else if (inFrame && frameLength < MTU) {
// Get command byte
if (frameLength == 0 && command == CMD_UNKNOWN) {
//Serial.println("ACQ_CMD");
command = txByte;
}
else if (command == CMD_DATA) {
if (txByte == FESC) {
escape = true;
}
else {
if (escape) {
if (txByte == TFEND) {
txByte = FEND;
}
if (txByte == TFESC) {
txByte = FESC;
}
escape = false;
}
else {
txBuffer[frameLength++] = txByte;
}
}
}
}
}
void updateModemStatus() {
uint8_t status = LoRa.modemStatus();
lastStatusUpdate = millis();
if (status & SIG_DETECT == 0x01) {
statSignalDetected = true;
}
else {
statSignalDetected = false;
}
if (status & SIG_SYNCED == 0x01) {
statSignalSynced = true;
}
else {
statSignalSynced = false;
}
if (status & RX_ONGOING == 0x01) {
statRxOngoing = true;
}
else {
statRxOngoing = false;
}
if (statSignalDetected || statSignalSynced || statRxOngoing) {
if (dcdCount < dcdThreshold) {
dcdCount++;
dcd = true;
}
else {
dcd = true;
dcdLed = true;
}
}
else {
if (dcdCount > 0) {
dcdCount--;
}
else {
dcdLed = false;
}
dcd = false;
}
}
bool isOutboundReady() {
return outboundReady;
}
void checkModemStatus() {
if (millis() - lastStatusUpdate >= statusIntervalms) {
updateModemStatus();
}
}
void receiveCallback(int packetSize) {
readLength = 0;
lastRssi = LoRa.packetRssi();
getPacketData(packetSize);
// Send RSSI
Serial.write(FEND);
Serial.write(HW_RSSI);
Serial.write((uint8_t)(lastRssi-rssiOffset));
Serial.write(FEND);
// And then write the entire packet
Serial.write(FEND);
Serial.write((uint8_t)CMD_DATA);
for (int i = 0; i < readLength; i++) {
uint8_t temp = rxBuffer[i];
if (temp == FEND) {
Serial.write(FESC);
temp = TFEND;
}
if (temp == FESC) {
Serial.write(FESC);
temp = TFESC;
}
Serial.write(temp);
}
Serial.write(FEND);
readLength = 0;
}
void escapedSerialWrite (uint8_t bufferByte) {
switch(bufferByte) {
case FEND:
Serial.write(FESC);
Serial.write(TFEND);
break;
case FESC:
Serial.write(FESC);
Serial.write(TFESC);
break;
default:
Serial.write(bufferByte);
}
}
void kissIndicateError(uint8_t errorCode) {
Serial.write(FEND);
Serial.write(CMD_ERROR);
Serial.write(errorCode);
Serial.write(FEND);
}
void getPacketData(int packetLength) {
while (packetLength--) {
rxBuffer[readLength++] = LoRa.read();
}
}
void loop() {
// put your main code here, to run repeatedly:
checkModemStatus();
if (isOutboundReady() && !SERIAL_READING) {
if (!dcdWaiting) {
updateModemStatus();
}
if (!dcd && !dcdLed) {
if (dcdWaiting)
delay(loraRxTurnaround);
updateModemStatus();
if (!dcd) {
dcdWaiting = false;
outboundReady = false;
//Serial.println("CLR_TRANSMIT");
transmit(frameLength);
}
else {
dcdWaiting = true;
}
}
}
if (Serial.available()) {
SERIAL_READING = true;
char txByte = Serial.read();
serialCallback(txByte);
lastSerialRead = millis();
}
else {
if (SERIAL_READING && millis() - lastSerialRead >= serialReadTimeout) {
SERIAL_READING = false;
}
}
}

75
src/main.cpp Normal file
View File

@@ -0,0 +1,75 @@
#include <stdio.h>
#include <string.h>
#include "pico/stdlib.h"
#include "pico/binary_info.h"
#include "LoRa-RP2040.h"
#include "Config.h"
#include "KISS.h"
bool startRadio();
int main() {
/* Among others, this initializes the USB-serial port at 115200bps 8N1 */
stdio_init_all();
printf("Hello, world!\n");
/* while (1) {
printf("1");
sleep_ms(1000);
}
*/
// override the default CS, reset, and IRQ pins (optional)
// LoRa.setPins(7, 6, 1); // set CS, reset, IRQ pin
sleep_ms(5000);
printf("loraFrequency = %u\n", loraFrequency);
startRadio();
int counter = 0;
while (1) {
int packetSize = LoRa.parsePacket();
if (packetSize) {
// received a packet
printf("Received packet \n");
// TODO: read packet into array and check if APRS header is correct (header = 0x3C 0xFF 0x01)
// If it all checks out, remove the header from the string
while (LoRa.available()) {
printf("%c", LoRa.read());
}
// print RSSI of packet
printf(" (RSSI = %idBm)\n",LoRa.packetRssi());
}
}
return 0;
}
/*
* Initializes the LoRa module with the parameters set in config.h
*/
bool startRadio() {
printf("Starting LoRa radio");
if (!LoRa.begin(loraFrequency)) {
//kissIndicateError(ERROR_INITRADIO);
printf(" [ FAILED ]\n");
while(1);
}
else {
LoRa.setPreambleLength(loraPreamble);
LoRa.setSignalBandwidth(loraBandwidth);
LoRa.setTxPower(loraTxPower,LoRaPaSelect);
LoRa.setSpreadingFactor(loraSpreadingFactor);
LoRa.setCodingRate4(loraCodingRate);
LoRa.enableCrc();
//LoRa.onReceive(receiveCallback);
//LoRa.receive();
printf(" [ DONE ]\n");
}
}