First commit: receiving LoRa APRS messages works.
This commit is contained in:
35
src/CMakeLists.txt
Normal file
35
src/CMakeLists.txt
Normal 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
60
src/Config.h
Normal 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
28
src/KISS.h
Normal 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
732
src/LoRa-RP2040.cpp
Normal 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
135
src/LoRa-RP2040.h
Normal 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
344
src/Print.cpp
Normal 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
90
src/Print.h
Normal 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
244
src/lora-kiss-tnc.ino
Normal 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
75
src/main.cpp
Normal 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");
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user