Update RadioLib
This commit is contained in:
parent
a8257c0cb2
commit
16bc3e2b42
46 changed files with 1102 additions and 97 deletions
46
lib/RadioLib/.github/workflows/static.yml
vendored
Normal file
46
lib/RadioLib/.github/workflows/static.yml
vendored
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
name: "Static Memory Management Test"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
branches: [master]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
static-memory:
|
||||
name: Build Arduino and non-Arduino with enabled static memory management
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install dependencies for unit test
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libboost-all-dev libfmt-dev lcov
|
||||
|
||||
- name: Run unit test for static memory management
|
||||
run: |
|
||||
cd extras/test/unit
|
||||
./test.sh -DRADIOLIB_STATIC_ONLY
|
||||
|
||||
- name: Install arduino-cli
|
||||
run:
|
||||
|
|
||||
mkdir -p ~/.local/bin
|
||||
echo "~/.local/bin" >> $GITHUB_PATH
|
||||
curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | BINDIR=~/.local/bin sh
|
||||
|
||||
- name: Install platform
|
||||
run:
|
||||
|
|
||||
arduino-cli core update-index
|
||||
arduino-cli core install arduino:avr
|
||||
|
||||
- name: Build Arduino example
|
||||
run:
|
||||
arduino-cli compile --libraries /home/runner/work/RadioLib --fqbn arduino:avr:mega $PWD/examples/SX123x/SX123x_Transmit_Blocking/SX123x_Transmit_Blocking.ino --warnings=all --build-property compiler.cpp.extra_flags="-DRADIOLIB_STATIC_ONLY"
|
||||
|
||||
|
||||
|
|
@ -16,6 +16,19 @@ Issues with generic titles (e.g. "not working", "lora", etc.) will be **CLOSED**
|
|||
4. **Issues deserve some attention too.**
|
||||
Issues that are left for 2 weeks without response by the original author when asked for further information will be closed due to inactivity. This is to keep track of important issues, the author is encouraged to reopen the issue at a later date.
|
||||
|
||||
## AI Use
|
||||
|
||||
**RadioLib was written by humans, for humans**. Its authors, contributors, maintainers and community members were not asked for permission or approval when billion-dollar companies decided to scrape our hard work for training data, to be sold back to the world at a profit. While we consider this approach to be bordering on violating our license, this seems to be the world we live in. Therefore, if you decide to contribute to RadioLib while using an AI-based tool, please follow the rules below.
|
||||
|
||||
1. **Mark the slop**
|
||||
The issue or pull request must be clearly marked as fully or partially AI-generated. If you don't do it, we'll do it for you by the means of a dedicated label - and then consider whether to process your input at all. Since people like that have not even bothered to read this statement, it would be unreasonable to expect they will read the code the machine generated and that is not code we want.
|
||||
2. **Less is more**
|
||||
If your AI has produced a novel-length wall of text to describe a miniscule change, we consider it somewhat rude to copy-paste that output into the issue/PR description. Not only are you ignoring the templates which are asking for generally useful info; this approach also has the added bonus of explaining our own codebase back to us, while trying to sound as simple as possible. In reality though, it sounds like a junior developer giving us a lecture on a project that is older then they are. So please, try to get the actual point across in your own, human-generated words.
|
||||
3. **Discuss first**
|
||||
It is usually not a great idea to hit us with a large, unsolicited pull request. Especially if it changes APIs or tries to work around something. It's a lot easier to discuss these things in advance and agree on an approach in an issue. Your AI can write the PR quickly; but then we need to spend our free time on reviewing something that we did not ask for.
|
||||
|
||||
RadioLib maintainers reserve the right to reject your contribution if it is not following these rules without further explanation other than directing you to this guideline document.
|
||||
|
||||
## Code style guidelines
|
||||
|
||||
I like pretty code! Or at least, I like *consistent* code style. When creating pull requests, please follow these style guidelines, they're in place to keep high code readability.
|
||||
|
|
|
|||
141
lib/RadioLib/extras/ADSB_Monitor/ADSBMonitorServer.py
Normal file
141
lib/RadioLib/extras/ADSB_Monitor/ADSBMonitorServer.py
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
#!/usr/bin/python3
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
import zmq
|
||||
import serial
|
||||
import threading
|
||||
import queue
|
||||
import time
|
||||
|
||||
from argparse import RawTextHelpFormatter
|
||||
|
||||
# default settings
|
||||
DEFAULT_BAUDRATE = 115200
|
||||
DEFAULT_SERVER_PORT = 30002
|
||||
|
||||
# marker to filter out ADS-B messages from the rest of the serial stream
|
||||
ADSB_MESSAGE_MARKER = '[ADS-B]'
|
||||
|
||||
class SerialToZMQBridge:
|
||||
def __init__(self, serial_port, baudrate, zmq_host="0.0.0.0", zmq_port=5555):
|
||||
self.serial_port = serial_port
|
||||
self.baudrate = baudrate
|
||||
self.zmq_host = zmq_host
|
||||
self.zmq_port = zmq_port
|
||||
|
||||
self.context = zmq.Context()
|
||||
self.socket = self.context.socket(zmq.STREAM)
|
||||
self.socket.setsockopt(zmq.LINGER, 0)
|
||||
self.socket.bind(f"tcp://{self.zmq_host}:{self.zmq_port}")
|
||||
|
||||
self.serial = serial.Serial(
|
||||
port=self.serial_port,
|
||||
baudrate=self.baudrate,
|
||||
timeout=1
|
||||
)
|
||||
|
||||
self.clients = set()
|
||||
self.serial_queue = queue.Queue()
|
||||
|
||||
self.running = True
|
||||
|
||||
def serial_reader(self):
|
||||
"""Continuously read from serial and queue messages."""
|
||||
while self.running:
|
||||
try:
|
||||
line = self.serial.readline()
|
||||
if line:
|
||||
line = line.decode(errors='ignore').strip()
|
||||
print(f"[SERIAL RX] {line}")
|
||||
|
||||
# read the ADS-B frames and add the markers expected by pyModeS
|
||||
if ADSB_MESSAGE_MARKER in line:
|
||||
msg = '*' + line.split(' ')[1].strip() + ';'
|
||||
self.serial_queue.put(msg.encode('utf-8'))
|
||||
|
||||
except Exception as e:
|
||||
print(f"Serial read error: {e}")
|
||||
time.sleep(1)
|
||||
|
||||
def run(self):
|
||||
print(f"ZMQ STREAM server listening on tcp://{self.zmq_host}:{self.zmq_port}")
|
||||
print(f"Listening to serial port {self.serial_port} @ {self.baudrate}")
|
||||
|
||||
threading.Thread(target=self.serial_reader, daemon=True).start()
|
||||
|
||||
poller = zmq.Poller()
|
||||
poller.register(self.socket, zmq.POLLIN)
|
||||
|
||||
try:
|
||||
while self.running:
|
||||
# Poll ZMQ socket
|
||||
events = dict(poller.poll(100))
|
||||
|
||||
if self.socket in events:
|
||||
identity, message = self.socket.recv_multipart()
|
||||
|
||||
if message == b'':
|
||||
# Connection event
|
||||
print(f"[ZMQ] Client connected/disconnected: {identity}")
|
||||
self.clients.add(identity)
|
||||
continue
|
||||
|
||||
print(f"[ZMQ RX] {identity}: {message.decode(errors='ignore')}")
|
||||
|
||||
# Send serial data to all connected clients
|
||||
while not self.serial_queue.empty():
|
||||
data = self.serial_queue.get()
|
||||
for client_id in list(self.clients):
|
||||
try:
|
||||
self.socket.send_multipart([client_id, data])
|
||||
print(f"[ZMQ TX] Sent to {client_id}")
|
||||
except zmq.ZMQError:
|
||||
self.clients.discard(client_id)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("Shutting down...")
|
||||
|
||||
finally:
|
||||
self.running = False
|
||||
self.serial.close()
|
||||
self.socket.close()
|
||||
self.context.term()
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(formatter_class=RawTextHelpFormatter, description='''
|
||||
RadioLib ADS-B Monitor script. Serves as server for "modeslive" live traffic decoder from pyModeS
|
||||
(https://github.com/junzis/pyModeS).
|
||||
|
||||
Depends on pyserial and pyModeS, install by:
|
||||
'python3 -m pip install pyserial pyModeS'
|
||||
|
||||
Step-by-step guide on how to use the script:
|
||||
1. Upload the ADSB_Monitor example to your Arduino board with LR2021 connected.
|
||||
2. Run the script with appropriate arguments.
|
||||
3. Run "modeslive --source net --connect localhost 30002 raw"
|
||||
''')
|
||||
parser.add_argument('port',
|
||||
type=str,
|
||||
help='COM port to connect to the device')
|
||||
parser.add_argument('--speed',
|
||||
default=DEFAULT_BAUDRATE,
|
||||
type=int,
|
||||
help=f'COM port baudrate (defaults to {DEFAULT_BAUDRATE})')
|
||||
parser.add_argument('--server-port',
|
||||
default=DEFAULT_SERVER_PORT,
|
||||
type=int,
|
||||
help=f'server port to be used by modeslive (defaults to {DEFAULT_SERVER_PORT})')
|
||||
args = parser.parse_args()
|
||||
|
||||
bridge = SerialToZMQBridge(
|
||||
serial_port=args.port,
|
||||
baudrate=args.speed,
|
||||
zmq_port=args.server_port
|
||||
)
|
||||
bridge.run()
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
50
lib/RadioLib/extras/SSTV_Image_Converter/ImageConverter.py
Normal file
50
lib/RadioLib/extras/SSTV_Image_Converter/ImageConverter.py
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
#!/usr/bin/python3
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
import argparse
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
from argparse import RawTextHelpFormatter
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(formatter_class=RawTextHelpFormatter, description='''
|
||||
RadioLib image to array conversion tool.
|
||||
|
||||
Input is a PNG image to be transmitted via RadioLib SSTV.
|
||||
The image must have correct size for the chose SSTV mode!
|
||||
|
||||
Output is a file (by default named "img.h") which can be included and transmitted.
|
||||
The resulting array will be very large (typically 320 kB),
|
||||
make sure your platform has sufficient Flash/RAM space.
|
||||
''')
|
||||
parser.add_argument('input',
|
||||
type=str,
|
||||
help='Input PNG file')
|
||||
parser.add_argument('output',
|
||||
type=str,
|
||||
nargs='?',
|
||||
default='img',
|
||||
help='Output header file')
|
||||
args = parser.parse_args()
|
||||
outfile = f'{args.output}.h'
|
||||
print(f'Converting "{args.input}" to "{outfile}"')
|
||||
|
||||
# open the image as numpy array
|
||||
img = Image.open(args.input)
|
||||
arr = np.array(img)
|
||||
|
||||
# open the output file
|
||||
with open(outfile, 'w') as f:
|
||||
print(f'const uint32_t img[{arr.shape[0]}][{arr.shape[1]}] = {{', file=f)
|
||||
for row in arr:
|
||||
print(' { ', end='', file=f)
|
||||
for pix in row:
|
||||
rgb = pix[0] << 16 | pix[1] << 8 | pix[2]
|
||||
print(hex(rgb), end=', ', file=f)
|
||||
print(' },', file=f)
|
||||
print('};', file=f)
|
||||
|
||||
print('Done!')
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
BIN
lib/RadioLib/extras/SSTV_Image_Converter/radiolib.png
Normal file
BIN
lib/RadioLib/extras/SSTV_Image_Converter/radiolib.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 62 KiB |
176
lib/RadioLib/extras/SX126x_Spectrum_Scan/SpectrumScan.py
Normal file
176
lib/RadioLib/extras/SX126x_Spectrum_Scan/SpectrumScan.py
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
#!/usr/bin/python3
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
import argparse
|
||||
import serial
|
||||
import sys
|
||||
import numpy as np
|
||||
import matplotlib as mpl
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from datetime import datetime
|
||||
from argparse import RawTextHelpFormatter
|
||||
|
||||
# number of samples in each scanline
|
||||
SCAN_WIDTH = 33
|
||||
|
||||
# scanline Serial start/end markers
|
||||
SCAN_MARK_START = 'SCAN '
|
||||
SCAN_MARK_FREQ = 'FREQ '
|
||||
SCAN_MARK_END = ' END'
|
||||
|
||||
# output path
|
||||
OUT_PATH = 'out'
|
||||
|
||||
# default settings
|
||||
DEFAULT_BAUDRATE = 115200
|
||||
DEFAULT_COLOR_MAP = 'viridis'
|
||||
DEFAULT_SCAN_LEN = 200
|
||||
DEFAULT_RSSI_OFFSET = -11
|
||||
|
||||
# Print iterations progress
|
||||
# from https://stackoverflow.com/questions/3173320/text-progress-bar-in-terminal-with-block-characters
|
||||
def printProgressBar (iteration, total, prefix = '', suffix = '', decimals = 1, length = 50, fill = '█', printEnd = "\r"):
|
||||
"""
|
||||
Call in a loop to create terminal progress bar
|
||||
@params:
|
||||
iteration - Required : current iteration (Int)
|
||||
total - Required : total iterations (Int)
|
||||
prefix - Optional : prefix string (Str)
|
||||
suffix - Optional : suffix string (Str)
|
||||
decimals - Optional : positive number of decimals in percent complete (Int)
|
||||
length - Optional : character length of bar (Int)
|
||||
fill - Optional : bar fill character (Str)
|
||||
printEnd - Optional : end character (e.g. "\r", "\r\n") (Str)
|
||||
"""
|
||||
percent = ("{0:." + str(decimals) + "f}").format(100 * (iteration / float(total)))
|
||||
filledLength = int(length * iteration // total)
|
||||
bar = fill * filledLength + '-' * (length - filledLength)
|
||||
print(f'\r{prefix} |{bar}| {percent}% {suffix}', end = printEnd)
|
||||
if iteration == total:
|
||||
print()
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(formatter_class=RawTextHelpFormatter, description='''
|
||||
RadioLib SX126x_Spectrum_Scan plotter script. Displays output from SX126x_Spectrum_Scan example
|
||||
as grayscale and
|
||||
|
||||
Depends on pyserial and matplotlib, install by:
|
||||
'python3 -m pip install pyserial matplotlib'
|
||||
|
||||
Step-by-step guide on how to use the script:
|
||||
1. Upload the SX126x_Spectrum_Scan example to your Arduino board with SX1262 connected.
|
||||
2. Run the script with appropriate arguments.
|
||||
3. Once the scan is complete, output files will be saved to out/
|
||||
''')
|
||||
parser.add_argument('port',
|
||||
type=str,
|
||||
help='COM port to connect to the device')
|
||||
parser.add_argument('--speed',
|
||||
default=DEFAULT_BAUDRATE,
|
||||
type=int,
|
||||
help=f'COM port baudrate (defaults to {DEFAULT_BAUDRATE})')
|
||||
parser.add_argument('--map',
|
||||
default=DEFAULT_COLOR_MAP,
|
||||
type=str,
|
||||
help=f'Matplotlib color map to use for the output (defaults to "{DEFAULT_COLOR_MAP}")')
|
||||
parser.add_argument('--len',
|
||||
default=DEFAULT_SCAN_LEN,
|
||||
type=int,
|
||||
help=f'Number of scanlines to record (defaults to {DEFAULT_SCAN_LEN})')
|
||||
parser.add_argument('--offset',
|
||||
default=DEFAULT_RSSI_OFFSET,
|
||||
type=int,
|
||||
help=f'Default RSSI offset in dBm (defaults to {DEFAULT_RSSI_OFFSET})')
|
||||
parser.add_argument('--freq',
|
||||
default=-1,
|
||||
type=float,
|
||||
help=f'Default starting frequency in MHz')
|
||||
args = parser.parse_args()
|
||||
|
||||
freq_mode = False
|
||||
scan_len = args.len
|
||||
if (args.freq != -1):
|
||||
freq_mode = True
|
||||
scan_len = 1000
|
||||
|
||||
# create the color map and the result array
|
||||
arr = np.zeros((SCAN_WIDTH, scan_len))
|
||||
|
||||
# scanline counter
|
||||
row = 0
|
||||
|
||||
# list of frequencies in frequency mode
|
||||
freq_list = []
|
||||
|
||||
# open the COM port
|
||||
with serial.Serial(args.port, args.speed, timeout=None) as com:
|
||||
while(True):
|
||||
# update the progress bar
|
||||
if not freq_mode:
|
||||
printProgressBar(row, scan_len)
|
||||
|
||||
# read a single line
|
||||
try:
|
||||
line = com.readline().decode('utf-8')
|
||||
except:
|
||||
continue
|
||||
|
||||
if SCAN_MARK_FREQ in line:
|
||||
new_freq = float(line.split(' ')[1])
|
||||
if (len(freq_list) > 1) and (new_freq < freq_list[-1]):
|
||||
break
|
||||
|
||||
freq_list.append(new_freq)
|
||||
print('{:.3f}'.format(new_freq), end = '\r')
|
||||
continue
|
||||
|
||||
# check the markers
|
||||
if (SCAN_MARK_START in line) and (SCAN_MARK_END in line):
|
||||
# get the values
|
||||
scanline = line[len(SCAN_MARK_START):-len(SCAN_MARK_END)].split(',')
|
||||
for col in range(SCAN_WIDTH):
|
||||
arr[col][row] = int(scanline[col])
|
||||
|
||||
# increment the row counter
|
||||
row = row + 1
|
||||
|
||||
# check if we're done
|
||||
if (not freq_mode) and (row >= scan_len):
|
||||
break
|
||||
|
||||
# scale to the number of scans (sum of any given scanline)
|
||||
num_samples = arr.sum(axis=0)[0]
|
||||
arr *= (num_samples/arr.max())
|
||||
|
||||
if freq_mode:
|
||||
scan_len = len(freq_list)
|
||||
|
||||
# create the figure
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
# display the result as heatmap
|
||||
extent = [0, scan_len, -4*(SCAN_WIDTH + 1), args.offset]
|
||||
if freq_mode:
|
||||
extent[0] = freq_list[0]
|
||||
extent[1] = freq_list[-1]
|
||||
im = ax.imshow(arr[:,:scan_len], cmap=args.map, extent=extent)
|
||||
fig.colorbar(im)
|
||||
|
||||
# set some properites and show
|
||||
timestamp = datetime.now().strftime('%y-%m-%d %H-%M-%S')
|
||||
title = f'RadioLib SX126x Spectral Scan {timestamp}'
|
||||
if freq_mode:
|
||||
plt.xlabel("Frequency [Hz]")
|
||||
else:
|
||||
plt.xlabel("Time [sample]")
|
||||
plt.ylabel("RSSI [dBm]")
|
||||
ax.set_aspect('auto')
|
||||
fig.suptitle(title)
|
||||
fig.canvas.manager.set_window_title(title)
|
||||
plt.savefig(f'{OUT_PATH}/{title.replace(" ", "_")}.png', dpi=300)
|
||||
plt.show()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
18
lib/RadioLib/extras/cppcheck/check_file.sh
Normal file
18
lib/RadioLib/extras/cppcheck/check_file.sh
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [[ $# -lt 1 ]]; then
|
||||
echo "Usage: $0 <path to check>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
path=$1
|
||||
|
||||
cppcheck --version
|
||||
cppcheck $path --enable=all \
|
||||
--force \
|
||||
--inline-suppr \
|
||||
--suppress=ConfigurationNotChecked \
|
||||
--suppress=unusedFunction \
|
||||
--suppress=missingIncludeSystem \
|
||||
--suppress=missingInclude \
|
||||
--quiet
|
||||
27
lib/RadioLib/extras/cppcheck/cppcheck.sh
Normal file
27
lib/RadioLib/extras/cppcheck/cppcheck.sh
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#! /bin/bash
|
||||
|
||||
file=cppcheck.txt
|
||||
cppcheck --version
|
||||
cppcheck src --enable=all \
|
||||
--force \
|
||||
--inline-suppr \
|
||||
--suppress=ConfigurationNotChecked \
|
||||
--suppress=unusedFunction \
|
||||
--suppress=missingIncludeSystem \
|
||||
--suppress=missingInclude \
|
||||
--quiet >> $file 2>&1
|
||||
echo "Cppcheck finished with exit code $?"
|
||||
|
||||
error=$(grep ": error:" $file | wc -l)
|
||||
warning=$(grep ": warning:" $file | wc -l)
|
||||
style=$(grep ": style:" $file | wc -l)
|
||||
performance=$(grep ": performance:" $file | wc -l)
|
||||
echo "found $error erros, $warning warnings, $style style and $performance performance issues"
|
||||
if [ $error -gt "0" ] || [ $warning -gt "0" ] || [ $style -gt "0" ] || [ $performance -gt "0" ]
|
||||
then
|
||||
cat $file
|
||||
exitcode=1
|
||||
fi
|
||||
|
||||
rm $file
|
||||
exit $exitcode
|
||||
22
lib/RadioLib/extras/template/ModuleTemplate.cpp
Normal file
22
lib/RadioLib/extras/template/ModuleTemplate.cpp
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#include "<module_name>.h"
|
||||
#if !defined(RADIOLIB_EXCLUDE_<module_name>)
|
||||
|
||||
<module_name>::<module_name>(Module* mod) {
|
||||
/*
|
||||
Constructor implementation MUST assign the provided "mod" pointer to the private "_mod" pointer.
|
||||
*/
|
||||
_mod = mod;
|
||||
}
|
||||
|
||||
int16_t <module_name>::begin() {
|
||||
/*
|
||||
"begin" method implementation MUST call the "init" method with appropriate settings.
|
||||
*/
|
||||
_mod->init();
|
||||
|
||||
/*
|
||||
"begin" method SHOULD implement some sort of mechanism to verify the connection between Arduino and the module.
|
||||
|
||||
For example, reading a version register
|
||||
*/
|
||||
}
|
||||
99
lib/RadioLib/extras/template/ModuleTemplate.h
Normal file
99
lib/RadioLib/extras/template/ModuleTemplate.h
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
RadioLib Module Template header file
|
||||
|
||||
Before opening pull request, please make sure that:
|
||||
1. All files MUST be compiled without errors using default Arduino IDE settings.
|
||||
2. All files SHOULD be compiled without warnings with compiler warnings set to "All".
|
||||
3. Example sketches MUST be working correctly and MUST be stable enough to run for prolonged periods of time.
|
||||
4. Writing style SHOULD be consistent.
|
||||
5. Comments SHOULD be in place for the most important chunks of code and SHOULD be free of typos.
|
||||
6. To indent, 2 spaces MUST be used.
|
||||
|
||||
If at any point you are unsure about the required style, please refer to the rest of the modules.
|
||||
*/
|
||||
|
||||
#if !defined(_RADIOLIB_<module_name>_H) && !defined(RADIOLIB_EXCLUDE_<module_name>)
|
||||
#if !defined(_RADIOLIB_<module_name>_H)
|
||||
#define _RADIOLIB_<module_name>_H
|
||||
|
||||
/*
|
||||
Header file for each module MUST include Module.h and TypeDef.h in the src folder.
|
||||
The header file MAY include additional header files.
|
||||
*/
|
||||
#include "../../Module.h"
|
||||
#include "../../TypeDef.h"
|
||||
|
||||
/*
|
||||
Only use the following include if the module implements methods for OSI physical layer control.
|
||||
This concerns only modules similar to SX127x/RF69/CC1101 etc.
|
||||
|
||||
In this case, your class MUST implement all virtual methods of PhysicalLayer class.
|
||||
*/
|
||||
//#include "../../protocols/PhysicalLayer/PhysicalLayer.h"
|
||||
|
||||
/*
|
||||
Register map
|
||||
Definition of SPI register map SHOULD be placed here. The register map SHOULD have two parts:
|
||||
|
||||
1 - Address map: only defines register names and addresses. Register names MUST match names in
|
||||
official documentation (datasheets etc.).
|
||||
2 - Variable map: defines variables inside register. This functions as a bit range map for a specific register.
|
||||
Bit range (MSB and LSB) as well as short description for each variable MUST be provided in a comment.
|
||||
|
||||
See RF69 and SX127x header files for examples of register maps.
|
||||
*/
|
||||
// <module_name> register map | spaces up to this point
|
||||
#define RADIOLIB_<module_name>_REG_<register_name> 0x00
|
||||
|
||||
// <module_name>_REG_<register_name> MSB LSB DESCRIPTION
|
||||
#define RADIOLIB_<module_name>_<register_variable> 0b00000000 // 7 0 <description>
|
||||
|
||||
|
||||
/*
|
||||
Module class definition
|
||||
|
||||
The module class MAY inherit from the following classes:
|
||||
|
||||
1 - PhysicalLayer: In case the module implements methods for OSI physical layer control (e.g. SX127x).
|
||||
2 - Common class: In case the module further specifies some more generic class (e.g. SX127x/SX1278)
|
||||
*/
|
||||
class <module_name> {
|
||||
public:
|
||||
/*
|
||||
Constructor MUST have only one parameter "Module* mod".
|
||||
The class MAY implement additional overloaded constructors.
|
||||
*/
|
||||
// constructor
|
||||
<module_name>(Module* mod);
|
||||
|
||||
/*
|
||||
The class MUST implement at least one basic method called "begin".
|
||||
The "begin" method MUST initialize the module and return the status as int16_t type.
|
||||
*/
|
||||
// basic methods
|
||||
int16_t begin();
|
||||
|
||||
/*
|
||||
The class MAY implement additional methods.
|
||||
All implemented methods SHOULD return the status as int16_t type.
|
||||
*/
|
||||
|
||||
#if !defined(RADIOLIB_GODMODE)
|
||||
private:
|
||||
#endif
|
||||
/*
|
||||
The class MUST contain private member "Module* _mod"
|
||||
*/
|
||||
Module* _mod;
|
||||
|
||||
/*
|
||||
The class MAY contain additional private variables and/or methods.
|
||||
Private member variables MUST have a name prefixed with "_" (underscore, ASCII 0x5F)
|
||||
|
||||
Usually, these are variables for saving module configuration, or methods that do not have to be exposed to the end user.
|
||||
*/
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -385,6 +385,7 @@ setDutyCycle KEYWORD2
|
|||
setDwellTime KEYWORD2
|
||||
setCSMA KEYWORD2
|
||||
setDeviceStatus KEYWORD2
|
||||
setActivityLeds KEYWORD2
|
||||
scheduleTransmission KEYWORD2
|
||||
getBand KEYWORD2
|
||||
getClass KEYWORD2
|
||||
|
|
|
|||
|
|
@ -106,6 +106,12 @@
|
|||
#define RADIOLIB_STATIC_ARRAY_SIZE (256)
|
||||
#endif
|
||||
|
||||
// allow user to set custom SPI buffer size
|
||||
// the default covers the maximum supported SPI command, address and status
|
||||
#if !defined(RADIOLIB_STATIC_SPI_ARRAY_SIZE)
|
||||
#define RADIOLIB_STATIC_SPI_ARRAY_SIZE (3*sizeof(uint32_t) + (RADIOLIB_STATIC_ARRAY_SIZE))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Uncomment on boards whose clock runs too slow or too fast
|
||||
* Set the value according to the following scheme:
|
||||
|
|
|
|||
|
|
@ -178,8 +178,8 @@ void Module::SPItransfer(uint16_t cmd, uint32_t reg, const uint8_t* dataOut, uin
|
|||
// prepare the buffers
|
||||
size_t buffLen = this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD]/8 + this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR]/8 + numBytes;
|
||||
#if RADIOLIB_STATIC_ONLY
|
||||
uint8_t buffOut[RADIOLIB_STATIC_ARRAY_SIZE];
|
||||
uint8_t buffIn[RADIOLIB_STATIC_ARRAY_SIZE];
|
||||
uint8_t buffOut[RADIOLIB_STATIC_SPI_ARRAY_SIZE];
|
||||
uint8_t buffIn[RADIOLIB_STATIC_SPI_ARRAY_SIZE];
|
||||
#else
|
||||
uint8_t* buffOut = new uint8_t[buffLen];
|
||||
uint8_t* buffIn = new uint8_t[buffLen];
|
||||
|
|
@ -323,7 +323,7 @@ int16_t Module::SPItransferStream(const uint8_t* cmd, uint8_t cmdLen, bool write
|
|||
buffLen += (this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_STATUS] / 8);
|
||||
}
|
||||
#if RADIOLIB_STATIC_ONLY
|
||||
uint8_t buffOut[RADIOLIB_STATIC_ARRAY_SIZE];
|
||||
uint8_t buffOut[RADIOLIB_STATIC_SPI_ARRAY_SIZE];
|
||||
#else
|
||||
uint8_t* buffOut = new uint8_t[buffLen];
|
||||
#endif
|
||||
|
|
@ -366,7 +366,7 @@ int16_t Module::SPItransferStream(const uint8_t* cmd, uint8_t cmdLen, bool write
|
|||
|
||||
// prepare the input buffer
|
||||
#if RADIOLIB_STATIC_ONLY
|
||||
uint8_t buffIn[RADIOLIB_STATIC_ARRAY_SIZE];
|
||||
uint8_t buffIn[RADIOLIB_STATIC_SPI_ARRAY_SIZE];
|
||||
#else
|
||||
uint8_t* buffIn = new uint8_t[buffLen];
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ void ArduinoHal::init() {
|
|||
if(initInterface) {
|
||||
spiBegin();
|
||||
}
|
||||
#if defined(ARDUINO_ARCH_STM32)
|
||||
#if defined(ARDUINO_ARCH_STM32) && defined(DWT_BASE)
|
||||
dwt_init();
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -266,8 +266,8 @@ int16_t CC1101::startTransmit(const uint8_t* data, size_t len, uint8_t addr) {
|
|||
|
||||
// optionally write packet length
|
||||
if(this->packetLengthConfig == RADIOLIB_CC1101_LENGTH_CONFIG_VARIABLE) {
|
||||
if (len > RADIOLIB_CC1101_MAX_PACKET_LENGTH - 1) {
|
||||
return(RADIOLIB_ERR_PACKET_TOO_LONG);
|
||||
if(len > RADIOLIB_CC1101_MAX_PACKET_LENGTH - 1) {
|
||||
return(RADIOLIB_ERR_PACKET_TOO_LONG);
|
||||
}
|
||||
SPIwriteRegister(RADIOLIB_CC1101_REG_FIFO, len + (filter != RADIOLIB_CC1101_ADR_CHK_NONE? 1:0));
|
||||
dataSent+= 1;
|
||||
|
|
@ -282,7 +282,9 @@ int16_t CC1101::startTransmit(const uint8_t* data, size_t len, uint8_t addr) {
|
|||
// fill the FIFO
|
||||
uint8_t initialWrite = RADIOLIB_MIN((uint8_t)len, (uint8_t)(RADIOLIB_CC1101_FIFO_SIZE - dataSent));
|
||||
SPIwriteRegisterBurst(RADIOLIB_CC1101_REG_FIFO, const_cast<uint8_t*>(data), initialWrite);
|
||||
dataSent += initialWrite;
|
||||
|
||||
// reset the data sent counter as it will be used later to calculate the remaining number of bytes to read from the user buffer
|
||||
dataSent = initialWrite;
|
||||
|
||||
// set RF switch (if present)
|
||||
this->mod->setRfSwitchState(Module::MODE_TX);
|
||||
|
|
@ -290,24 +292,40 @@ int16_t CC1101::startTransmit(const uint8_t* data, size_t len, uint8_t addr) {
|
|||
// set mode to transmit
|
||||
SPIsendCommand(RADIOLIB_CC1101_CMD_TX);
|
||||
|
||||
// Keep feeding the FIFO until the packet is done
|
||||
while (dataSent < len) {
|
||||
// keep feeding the FIFO until the packet is done
|
||||
// calculate timeout (5ms + 500 % of expected time-on-air)
|
||||
RadioLibTime_t timeout = 5 + (RadioLibTime_t)((((float)(len * 8)) / this->bitRate) * 5);
|
||||
start = this->mod->hal->millis();
|
||||
while(dataSent < len) {
|
||||
uint8_t fifoBytes = 0;
|
||||
uint8_t prevFifobytes = 0;
|
||||
|
||||
// Check number of bytes on FIFO twice due to the CC1101 errata. Block until two reads are equal.
|
||||
do{
|
||||
// check number of bytes on FIFO twice due to the CC1101 errata
|
||||
// block until two reads are equal
|
||||
do {
|
||||
fifoBytes = SPIgetRegValue(RADIOLIB_CC1101_REG_TXBYTES, 6, 0);
|
||||
prevFifobytes = SPIgetRegValue(RADIOLIB_CC1101_REG_TXBYTES, 6, 0);
|
||||
} while (fifoBytes != prevFifobytes);
|
||||
} while(fifoBytes != prevFifobytes);
|
||||
|
||||
//If there is room add more data to the FIFO
|
||||
if (fifoBytes < RADIOLIB_CC1101_FIFO_SIZE) {
|
||||
uint8_t bytesToWrite = RADIOLIB_MIN((uint8_t)(RADIOLIB_CC1101_FIFO_SIZE - fifoBytes), (uint8_t)(len - dataSent));
|
||||
SPIwriteRegisterBurst(RADIOLIB_CC1101_REG_FIFO, const_cast<uint8_t*>(&data[dataSent]), bytesToWrite);
|
||||
dataSent += bytesToWrite;
|
||||
// if there is room, add more data to the FIFO
|
||||
if(fifoBytes < RADIOLIB_CC1101_FIFO_SIZE) {
|
||||
uint8_t bytesToWrite = RADIOLIB_MIN((uint8_t)(RADIOLIB_CC1101_FIFO_SIZE - fifoBytes), (uint8_t)(len - dataSent));
|
||||
SPIwriteRegisterBurst(RADIOLIB_CC1101_REG_FIFO, const_cast<uint8_t*>(&data[dataSent]), bytesToWrite);
|
||||
dataSent += bytesToWrite;
|
||||
}
|
||||
|
||||
// check a timeout - this really shouldn't happen, but some packets can be quite long
|
||||
if(this->mod->hal->millis() - start > timeout) {
|
||||
(void)finishTransmit();
|
||||
return(RADIOLIB_ERR_TX_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
// enable interrupt for the final part of the packet
|
||||
if(len > RADIOLIB_CC1101_FIFO_SIZE) {
|
||||
state = SPIsetRegValue(RADIOLIB_CC1101_REG_IOCFG2, RADIOLIB_CC1101_GDOX_SYNC_WORD_SENT_OR_PKT_RECEIVED, 5, 0);
|
||||
}
|
||||
|
||||
return(state);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -900,7 +900,7 @@ class CC1101: public PhysicalLayer {
|
|||
int16_t setOOK(bool enableOOK);
|
||||
|
||||
/*!
|
||||
\brief Gets RSSI (Recorded Signal Strength Indicator) of the last received packet.
|
||||
\brief Gets RSSI (Received Signal Strength Indicator) of the last received packet.
|
||||
In direct or asynchronous direct mode, returns the current RSSI level.
|
||||
\returns RSSI in dBm.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1109,6 +1109,22 @@ float LR11x0::getRSSI() {
|
|||
return(val);
|
||||
}
|
||||
|
||||
float LR11x0::getRSSI(bool packet, bool skipReceive) {
|
||||
float val = 0;
|
||||
|
||||
// check if RSSI of packet is requested
|
||||
if (packet) {
|
||||
val = getRSSI();
|
||||
} else {
|
||||
if(!skipReceive) { (void)startReceive(); }
|
||||
int16_t state = getRssiInst(&val);
|
||||
if(!skipReceive) { (void)standby(); }
|
||||
if(state != RADIOLIB_ERR_NONE) { return(0); }
|
||||
}
|
||||
|
||||
return(val);
|
||||
}
|
||||
|
||||
float LR11x0::getSNR() {
|
||||
float val = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -424,11 +424,20 @@ class LR11x0: public LRxxxx {
|
|||
int16_t invertIQ(bool enable) override;
|
||||
|
||||
/*!
|
||||
\brief Gets RSSI (Recorded Signal Strength Indicator) of the last received packet. Only available for LoRa or GFSK modem.
|
||||
\brief Gets RSSI (Received Signal Strength Indicator) of the last received packet. Only available for LoRa or GFSK modem.
|
||||
\returns RSSI of the last received packet in dBm.
|
||||
*/
|
||||
float getRSSI() override;
|
||||
|
||||
/*!
|
||||
\brief Gets RSSI (Received Signal Strength Indicator).
|
||||
\param packet Whether to read last packet RSSI, or the current value.
|
||||
\param skipReceive Set to true to skip putting radio in receive mode for instantaneous RSSI measurement.
|
||||
If false, after the RSSI measurement, the radio will be in standby mode.
|
||||
\returns RSSI value in dBm.
|
||||
*/
|
||||
float getRSSI(bool packet, bool skipReceive = false);
|
||||
|
||||
/*!
|
||||
\brief Gets SNR (Signal to Noise Ratio) of the last received packet. Only available for LoRa modem.
|
||||
\returns SNR of the last received packet in dB.
|
||||
|
|
@ -922,7 +931,6 @@ class LR11x0: public LRxxxx {
|
|||
#endif
|
||||
uint8_t wifiScanMode = 0;
|
||||
bool gnss = false;
|
||||
|
||||
int16_t modSetup(float tcxoVoltage, uint8_t modem);
|
||||
bool findChip(uint8_t ver);
|
||||
int16_t config(uint8_t modem);
|
||||
|
|
|
|||
|
|
@ -550,7 +550,7 @@ int16_t LR2021::startChannelScan(const ChannelScanConfig_t &config) {
|
|||
RADIOLIB_ASSERT(state);
|
||||
|
||||
// set mode to CAD
|
||||
return(startCad(config.cad.symNum, config.cad.detPeak, config.cad.detMin, config.cad.exitMode, config.cad.timeout));
|
||||
return(startCad(config.cad.symNum, config.cad.detPeak, this->fastCad, config.cad.exitMode, config.cad.timeout));
|
||||
}
|
||||
|
||||
int16_t LR2021::getChannelScanResult() {
|
||||
|
|
@ -685,6 +685,11 @@ int16_t LR2021::config(uint8_t modem) {
|
|||
state = this->clearIrqState(RADIOLIB_LR2021_IRQ_ALL);
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
// Regulator / ramp resolution (datasheet SetRegMode); avoids relying on reset defaults.
|
||||
const uint8_t rampTimes[4] = { 0x00, 0x00, 0x00, 0x00 };
|
||||
state = this->setRegMode((uint8_t)(RADIOLIB_LR2021_REG_MODE_SIMO_NORMAL | RADIOLIB_LR2021_REG_MODE_RAMP_RES_4_US), rampTimes);
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
// validate DIO pin number
|
||||
if((this->irqDioNum < 5) || (this->irqDioNum > 11)) {
|
||||
return(RADIOLIB_ERR_INVALID_DIO_PIN);
|
||||
|
|
@ -721,7 +726,7 @@ int16_t LR2021::config(uint8_t modem) {
|
|||
return(state);
|
||||
}
|
||||
|
||||
int16_t LR2021::startCad(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin, uint8_t exitMode, RadioLibTime_t timeout) {
|
||||
int16_t LR2021::startCad(uint8_t symbolNum, uint8_t detPeak, bool fast, uint8_t exitMode, RadioLibTime_t timeout) {
|
||||
// check active modem
|
||||
uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
|
||||
int16_t state = getPacketType(&type);
|
||||
|
|
@ -731,22 +736,21 @@ int16_t LR2021::startCad(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin, uin
|
|||
}
|
||||
|
||||
// select CAD parameters
|
||||
//! \TODO: [LR2021] the magic numbers for CAD are based on Semtech examples, this is probably suboptimal
|
||||
uint8_t num = symbolNum;
|
||||
if(num == RADIOLIB_LR2021_CAD_PARAM_DEFAULT) {
|
||||
num = 2;
|
||||
}
|
||||
|
||||
const uint8_t detPeakValues[8] = { 48, 48, 50, 55, 55, 59, 61, 65 };
|
||||
// reference values from the datasheet for 2 symbols
|
||||
//! \TODO: [LR2021] allow CAD peak detection autoconfiguration
|
||||
const uint8_t detPeakValues[8] = { 56, 56, 56, 58, 58, 60, 64, 68 };
|
||||
uint8_t peak = detPeak;
|
||||
if(peak == RADIOLIB_LR2021_CAD_PARAM_DEFAULT) {
|
||||
peak = detPeakValues[this->spreadingFactor - 5];
|
||||
}
|
||||
|
||||
uint8_t min = detMin;
|
||||
if(min == RADIOLIB_LR2021_CAD_PARAM_DEFAULT) {
|
||||
min = 10;
|
||||
}
|
||||
// in Fast CAD mode enable acceleration
|
||||
uint8_t pnrDelta = fast ? RADIOLIB_LR2021_LORA_CAD_PNR_DELTA_FAST : RADIOLIB_LR2021_LORA_CAD_PNR_DELTA_STANDARD;
|
||||
|
||||
uint8_t mode = exitMode;
|
||||
if(mode == RADIOLIB_LR2021_CAD_PARAM_DEFAULT) {
|
||||
|
|
@ -755,17 +759,13 @@ int16_t LR2021::startCad(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin, uin
|
|||
|
||||
uint32_t timeout_raw = (float)timeout / 30.52f;
|
||||
|
||||
//! \TODO: [LR2021] The datasheet says this CAD is only based on RSSI, but the reference to the LoRa CAD is GetLoraRxStats ...?
|
||||
(void)peak;
|
||||
(void)min;
|
||||
|
||||
// set CAD parameters
|
||||
//! \TODO: [LR2021] add configurable exit mode and timeout
|
||||
state = setCadParams(timeout_raw, num, mode, timeout_raw);
|
||||
// set LoRa CAD parameters
|
||||
// preamble only mode is intentionally disabled, as it is unreliable according to the datasheet
|
||||
state = setLoRaCadParams(num, false, pnrDelta, mode, timeout_raw, peak);
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
// start CAD
|
||||
return(setCad());
|
||||
// start LoraCAD
|
||||
return(setLoRaCad());
|
||||
}
|
||||
|
||||
RadioLibTime_t LR2021::getTimeOnAir(size_t len) {
|
||||
|
|
@ -1021,6 +1021,8 @@ float LR2021::getRSSI(bool packet, bool skipReceive) {
|
|||
state = this->getLoRaPacketStatus(NULL, NULL, NULL, NULL, &rssi, NULL);
|
||||
} else if(modem == RADIOLIB_LR2021_PACKET_TYPE_GFSK) {
|
||||
state = this->getGfskPacketStatus(NULL, &rssi, NULL, NULL, NULL, NULL);
|
||||
} else if(modem == RADIOLIB_LR2021_PACKET_TYPE_OOK) {
|
||||
state = this->getOokPacketStatus(NULL, NULL, &rssi, NULL, NULL, NULL);
|
||||
} else {
|
||||
return(0);
|
||||
}
|
||||
|
|
@ -1046,4 +1048,8 @@ uint8_t LR2021::randomByte() {
|
|||
return((uint8_t)num);
|
||||
}
|
||||
|
||||
int16_t LR2021::getLoRaRxHeaderInfo(uint8_t* cr, bool* hasCRC){
|
||||
return(this->getLoRaPacketStatus(cr, hasCRC, NULL, NULL, NULL, NULL));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -42,6 +42,13 @@ class LR2021: public LRxxxx {
|
|||
*/
|
||||
uint32_t irqDioNum = 5;
|
||||
|
||||
/*!
|
||||
\brief Determines the type of Lora CAD to perform, either "standard" CAD
|
||||
(same as is implem,ented LR11x0, SX126x and others), or a "fast" CAD if set to true.
|
||||
If there is no signal to be detected, fast CAD should return faster than standard CAD.
|
||||
*/
|
||||
bool fastCad = false;
|
||||
|
||||
/*!
|
||||
\brief Custom operation modes for LR2021.
|
||||
Needed because LR2021 has several modems (sub-GHz, 2.4 GHz etc.) in one package
|
||||
|
|
@ -595,16 +602,18 @@ class LR2021: public LRxxxx {
|
|||
float getTemperature(uint8_t source, uint8_t bits = 13);
|
||||
|
||||
/*!
|
||||
\brief Gets recorded signal strength indicator.
|
||||
\brief Gets received signal strength indicator.
|
||||
Overload with packet mode enabled for PhysicalLayer compatibility.
|
||||
\returns RSSI value in dBm.
|
||||
*/
|
||||
float getRSSI() override;
|
||||
|
||||
/*!
|
||||
\brief Gets RSSI (Recorded Signal Strength Indicator).
|
||||
\brief Gets RSSI (Received Signal Strength Indicator).
|
||||
\param packet Whether to read last packet RSSI, or the current value.
|
||||
\param skipReceive Set to true to skip putting radio in receive mode for the RSSI measurement in FSK/OOK mode.
|
||||
NOTE: With OOK modem, the "packet" RSSI value is the received power level of the high bits (digital 1).
|
||||
\param skipReceive Set to true to skip putting radio in receive mode for instantaneous RSSI measurement.
|
||||
If false, after the RSSI measurement, the radio will be in standby mode.
|
||||
\returns RSSI value in dBm.
|
||||
*/
|
||||
float getRSSI(bool packet, bool skipReceive = false);
|
||||
|
|
@ -677,6 +686,27 @@ class LR2021: public LRxxxx {
|
|||
*/
|
||||
int16_t setGain(uint8_t gain);
|
||||
|
||||
/*!
|
||||
\brief Read status of the last received packet.
|
||||
Each parameter can be set to NULL if the caller is not intending to process it.
|
||||
\param cr Coding rate of the last received packet
|
||||
\param crc Will be set to true if the last packet had a CRC, false otherwise
|
||||
\param packetLen Length of the last received packet in bytes
|
||||
\param snrPacket SNR of the last received packet in dB
|
||||
\param rssiPacket RSSI of the last received packet in dBm
|
||||
\param rssiSignalPacket Estimation of the RSSI of LoRa signal after despreading in dBm
|
||||
\returns \ref status_codes
|
||||
*/
|
||||
int16_t getLoRaPacketStatus(uint8_t* cr, bool* crc, uint8_t* packetLen = NULL, float* snrPacket = NULL, float* rssiPacket = NULL, float* rssiSignalPacket = NULL);
|
||||
|
||||
/*!
|
||||
\brief Get LoRa header information from last received packet. Implementation based on getLoRaPacketStatus.
|
||||
\param cr Pointer to variable to store the coding rate.
|
||||
\param hasCRC Pointer to variable to store the CRC status.
|
||||
\returns \ref status_codes
|
||||
*/
|
||||
int16_t getLoRaRxHeaderInfo(uint8_t* cr, bool* hasCRC);
|
||||
|
||||
#if !RADIOLIB_GODMODE && !RADIOLIB_LOW_LEVEL
|
||||
protected:
|
||||
#endif
|
||||
|
|
@ -703,7 +733,7 @@ class LR2021: public LRxxxx {
|
|||
bool findChip(void);
|
||||
int16_t config(uint8_t modem);
|
||||
int16_t setPacketMode(uint8_t mode, uint8_t len);
|
||||
int16_t startCad(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin, uint8_t exitMode, RadioLibTime_t timeout);
|
||||
int16_t startCad(uint8_t symbolNum, uint8_t detPeak, bool fast, uint8_t exitMode, RadioLibTime_t timeout);
|
||||
|
||||
// chip control commands
|
||||
int16_t readRadioRxFifo(uint8_t* data, size_t len);
|
||||
|
|
@ -763,6 +793,8 @@ class LR2021: public LRxxxx {
|
|||
int16_t selPa(uint8_t pa);
|
||||
int16_t setPaConfig(uint8_t pa, uint8_t paLfMode, uint8_t paLfDutyCycle, uint8_t paLfSlices, uint8_t paHfDutyCycle);
|
||||
int16_t setTxParams(int8_t txPower, uint8_t rampTime);
|
||||
/*! \brief SetTxParams first byte as signed half-dBm steps (datasheet / lr20xx `power_half_dbm`). */
|
||||
int16_t setTxParamsHalfDbm(int8_t powerHalfDbm, uint8_t rampTime);
|
||||
|
||||
// modem configuration commands
|
||||
int16_t setPacketType(uint8_t packetType);
|
||||
|
|
@ -778,7 +810,6 @@ class LR2021: public LRxxxx {
|
|||
int16_t setLoRaCadParams(uint8_t numSymbols, bool preambleOnly, uint8_t pnrDelta, uint8_t cadExitMode, uint32_t timeout, uint8_t detPeak);
|
||||
int16_t setLoRaCad(void);
|
||||
int16_t getLoRaRxStats(uint16_t* pktRxTotal, uint16_t* pktCrcError, uint16_t* headerCrcError, uint16_t* falseSynch);
|
||||
int16_t getLoRaPacketStatus(uint8_t* crc, uint8_t* cr, uint8_t* packetLen, float* snrPacket, float* rssiPacket, float* rssiSignalPacket);
|
||||
int16_t setLoRaAddress(uint8_t addrLen, uint8_t addrPos, const uint8_t* addr);
|
||||
int16_t setLoRaHopping(uint8_t hopCtrl, uint16_t hopPeriod, const uint32_t* freqHops, size_t numFreqHops);
|
||||
int16_t setLoRaTxSync(uint8_t function, uint8_t dioNum);
|
||||
|
|
@ -834,7 +865,7 @@ class LR2021: public LRxxxx {
|
|||
int16_t setOokSyncword(const uint8_t* syncWord, size_t syncWordLen, bool msbFirst);
|
||||
int16_t setOokAddress(uint8_t addrNode, uint8_t addrBroadcast);
|
||||
int16_t getOokRxStats(uint16_t* packetRx, uint16_t* crcError, uint16_t* lenError);
|
||||
int16_t getOokPacketStatus(uint16_t* packetLen, float* rssiAvg, float* rssiSync, bool* addrMatchNode, bool* addrMatchBroadcast, float* lqi);
|
||||
int16_t getOokPacketStatus(uint16_t* packetLen, float* rssiAvg, float* rssiHigh, bool* addrMatchNode, bool* addrMatchBroadcast, float* lqi);
|
||||
int16_t setOokDetector(uint16_t preamblePattern, uint8_t patternLen, uint8_t patternNumRepeaters, bool syncWordRaw, bool sofDelimiterRising, uint8_t sofDelimiterLen);
|
||||
int16_t setOokWhiteningParams(uint8_t bitIdx, uint16_t poly, uint16_t init);
|
||||
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ int16_t LR2021::getLoRaRxStats(uint16_t* pktRxTotal, uint16_t* pktCrcError, uint
|
|||
return(state);
|
||||
}
|
||||
|
||||
int16_t LR2021::getLoRaPacketStatus(uint8_t* crc, uint8_t* cr, uint8_t* packetLen, float* snrPacket, float* rssiPacket, float* rssiSignalPacket) {
|
||||
int16_t LR2021::getLoRaPacketStatus(uint8_t* cr, bool* crc, uint8_t* packetLen, float* snrPacket, float* rssiPacket, float* rssiSignalPacket) {
|
||||
uint8_t buff[6] = { 0 };
|
||||
int16_t state = this->SPIcommand(RADIOLIB_LR2021_CMD_GET_LORA_PACKET_STATUS, false, buff, sizeof(buff));
|
||||
uint16_t raw;
|
||||
|
|
|
|||
|
|
@ -37,9 +37,14 @@ int16_t LR2021::setOokCrcParams(uint32_t poly, uint32_t init) {
|
|||
}
|
||||
|
||||
int16_t LR2021::setOokSyncword(const uint8_t* syncWord, size_t syncWordLen, bool msbFirst) {
|
||||
// OOK maximum sync word length is just 32-bits, unlike GFSK
|
||||
if(syncWordLen > RADIOLIB_LR2021_OOK_SYNC_WORD_LEN) {
|
||||
return(RADIOLIB_ERR_INVALID_SYNC_WORD);
|
||||
}
|
||||
|
||||
uint8_t buff[5] = { 0 };
|
||||
for(size_t i = 0; i < syncWordLen; i++) {
|
||||
buff[3 - i] = syncWord[i];
|
||||
for(int8_t i = 3; i >= (int8_t) (RADIOLIB_LR2021_OOK_SYNC_WORD_LEN - syncWordLen); i--) {
|
||||
buff[i] = syncWord[i - (int8_t) (RADIOLIB_LR2021_OOK_SYNC_WORD_LEN - syncWordLen)];
|
||||
}
|
||||
buff[4] = (uint8_t)msbFirst << 7 | ((syncWordLen*8) & 0x7F);
|
||||
return(this->SPIcommand(RADIOLIB_LR2021_CMD_SET_OOK_SYNCWORD, true, buff, sizeof(buff)));
|
||||
|
|
@ -59,7 +64,7 @@ int16_t LR2021::getOokRxStats(uint16_t* packetRx, uint16_t* crcError, uint16_t*
|
|||
return(state);
|
||||
}
|
||||
|
||||
int16_t LR2021::getOokPacketStatus(uint16_t* packetLen, float* rssiAvg, float* rssiSync, bool* addrMatchNode, bool* addrMatchBroadcast, float* lqi) {
|
||||
int16_t LR2021::getOokPacketStatus(uint16_t* packetLen, float* rssiAvg, float* rssiHigh, bool* addrMatchNode, bool* addrMatchBroadcast, float* lqi) {
|
||||
uint8_t buff[6] = { 0 };
|
||||
int16_t state = this->SPIcommand(RADIOLIB_LR2021_CMD_GET_OOK_PACKET_STATUS, false, buff, sizeof(buff));
|
||||
uint16_t raw;
|
||||
|
|
@ -69,10 +74,10 @@ int16_t LR2021::getOokPacketStatus(uint16_t* packetLen, float* rssiAvg, float* r
|
|||
raw |= (buff[4] & 0x04) >> 2;
|
||||
*rssiAvg = (float)raw / -2.0f;
|
||||
}
|
||||
if(rssiSync) {
|
||||
if(rssiHigh) {
|
||||
raw = (uint16_t)buff[3] << 1;
|
||||
raw |= (buff[4] & 0x01);
|
||||
*rssiSync = (float)raw / -2.0f;
|
||||
*rssiHigh = (float)raw / -2.0f;
|
||||
}
|
||||
if(addrMatchNode) { *addrMatchNode = (buff[4] & 0x10); }
|
||||
if(addrMatchBroadcast) { *addrMatchBroadcast = (buff[4] & 0x20); }
|
||||
|
|
|
|||
|
|
@ -94,17 +94,24 @@ int16_t LR2021::selPa(uint8_t pa) {
|
|||
}
|
||||
|
||||
int16_t LR2021::setPaConfig(uint8_t pa, uint8_t paLfMode, uint8_t paLfDutyCycle, uint8_t paLfSlices, uint8_t paHfDutyCycle) {
|
||||
// Matches Semtech lr20xx_radio_common_set_pa_cfg(): 3 payload bytes after opcode.
|
||||
uint8_t buff[] = {
|
||||
(uint8_t)(pa << 7), (uint8_t)(paLfMode & 0x03), (uint8_t)(paLfDutyCycle & 0xF0), (uint8_t)(paLfSlices & 0x0F), (uint8_t)(paHfDutyCycle & 0x1F),
|
||||
(uint8_t)(((pa & 0x01) << 7) | (paLfMode & 0x03)),
|
||||
(uint8_t)(((paLfDutyCycle & 0x0F) << 4) | (paLfSlices & 0x0F)),
|
||||
(uint8_t)(paHfDutyCycle & 0x1F),
|
||||
};
|
||||
return(this->SPIcommand(RADIOLIB_LR2021_CMD_SET_PA_CONFIG, true, buff, sizeof(buff)));
|
||||
}
|
||||
|
||||
int16_t LR2021::setTxParams(int8_t txPower, uint8_t rampTime) {
|
||||
uint8_t buff[] = { (uint8_t)(txPower * 2), rampTime };
|
||||
int16_t LR2021::setTxParamsHalfDbm(int8_t powerHalfDbm, uint8_t rampTime) {
|
||||
uint8_t buff[] = { (uint8_t)powerHalfDbm, rampTime };
|
||||
return(this->SPIcommand(RADIOLIB_LR2021_CMD_SET_TX_PARAMS, true, buff, sizeof(buff)));
|
||||
}
|
||||
|
||||
int16_t LR2021::setTxParams(int8_t txPower, uint8_t rampTime) {
|
||||
return(this->setTxParamsHalfDbm((int8_t)(txPower * 2), rampTime));
|
||||
}
|
||||
|
||||
int16_t LR2021::setPacketType(uint8_t packetType) {
|
||||
return(this->SPIcommand(RADIOLIB_LR2021_CMD_SET_PACKET_TYPE, true, &packetType, sizeof(packetType)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -345,7 +345,7 @@
|
|||
|
||||
// RADIOLIB_LR2021_CMD_SET_PA_CONFIG
|
||||
#define RADIOLIB_LR2021_PA_LF_MODE_FSM (0x00UL << 0) // 1 0 PA LF mode: full single-ended mode
|
||||
#define RADIOLIB_LR2021_PA_LF_DUTY_CYCLE_UNUSED (0x06UL << 4) // 7 4 PA LF duty cycle: PA not used
|
||||
#define RADIOLIB_LR2021_PA_LF_DUTY_CYCLE_UNUSED (0x06UL << 0) // 7 4 PA LF duty cycle: PA not used (nibble; packed in setPaConfig)
|
||||
#define RADIOLIB_LR2021_PA_LF_SLICES_UNUSED (0x07UL << 0) // 3 0 PA LF slices: PA not used
|
||||
#define RADIOLIB_LR2021_PA_HF_DUTY_CYCLE_UNUSED (0x10UL << 0) // 4 0 PA HF duty cycle: PA not used
|
||||
|
||||
|
|
@ -404,6 +404,10 @@
|
|||
#define RADIOLIB_LR2021_LORA_SYNC_WORD_PRIVATE (0x12UL << 0) // 7 0 LoRa sync word: 0x12 (private networks)
|
||||
#define RADIOLIB_LR2021_LORA_SYNC_WORD_LORAWAN (0x34UL << 0) // 7 0 0x34 (LoRaWAN reserved)
|
||||
|
||||
// RADIOLIB_LR2021_CMD_SET_LORA_CAD_PARAMS
|
||||
#define RADIOLIB_LR2021_LORA_CAD_PNR_DELTA_STANDARD (0x00UL << 0) // 7 0 LoRa CAD speed: normal
|
||||
#define RADIOLIB_LR2021_LORA_CAD_PNR_DELTA_FAST (0x08UL << 0) // 7 0 fast CAD
|
||||
|
||||
// RADIOLIB_LR2021_CMD_SET_LORA_HOPPING
|
||||
#define RADIOLIB_LR2021_LORA_HOPPING_DISABLED (0x00UL << 6) // 7 6 LoRa intra-packet hopping: disabled
|
||||
#define RADIOLIB_LR2021_LORA_HOPPING_ENABLED (0x01UL << 6) // 7 6 enabled
|
||||
|
|
@ -516,6 +520,9 @@
|
|||
#define RADIOLIB_LR2021_OOK_MANCHESTER_ON (0x01UL << 0) // 3 0 enabled
|
||||
#define RADIOLIB_LR2021_OOK_MANCHESTER_ON_INV (0x09UL << 0) // 3 0 enabled, inverted
|
||||
|
||||
// RADIOLIB_LR2021_CMD_SET_OOK_SYNCWORD
|
||||
#define RADIOLIB_LR2021_OOK_SYNC_WORD_LEN (4)
|
||||
|
||||
// RADIOLIB_LR2021_CMD_SET_TX_TEST_MODE
|
||||
#define RADIOLIB_LR2021_TX_TEST_MODE_NORMAL_TX (0x00UL << 0) // 7 0 Tx test mode: normal
|
||||
#define RADIOLIB_LR2021_TX_TEST_MODE_INF_PREAMBLE (0x01UL << 0) // 7 0 infinite preamble
|
||||
|
|
|
|||
|
|
@ -11,6 +11,98 @@
|
|||
// maximum number of allowed frontend calibration attempts
|
||||
#define RADIOLIB_LR2021_MAX_CAL_ATTEMPTS (10)
|
||||
|
||||
// Sub-GHz: below this RF frequency (MHz) use Table 7-17 (490 MHz ref.); at/above use Table 7-16 (915 MHz ref.).
|
||||
#define RADIOLIB_LR2021_LF_PA_TABLE_490_MHZ_MAX (700.0f)
|
||||
|
||||
// Table 7-16: Optimal Values for 915 MHz Semtech Reference Design (LF PA).
|
||||
// Integer targeted dBm 22..10 only: half-dBm rows (e.g. 21.5) need a future API that passes 0.5 dB steps.
|
||||
// SetTxParams first byte = `txHalfDbm` (signed half-dBm); matches datasheet TX_PARAM column * 2.
|
||||
static const struct {
|
||||
int8_t txHalfDbm;
|
||||
uint8_t paLfDutyCycle;
|
||||
uint8_t paLfSlices;
|
||||
} RADIOLIB_LR2021_TABLE_7_16_915_LF[] = {
|
||||
/* 22 */ { 44, 7, 6 },
|
||||
/* 21 */ { 42, 7, 7 },
|
||||
/* 20 */ { 41, 6, 6 },
|
||||
/* 19 */ { 39, 6, 6 },
|
||||
/* 18 */ { 38, 5, 6 },
|
||||
/* 17 */ { 36, 5, 6 },
|
||||
/* 16 */ { 36, 4, 4 },
|
||||
/* 15 */ { 33, 5, 4 },
|
||||
/* 14 */ { 34, 4, 2 },
|
||||
/* 13 */ { 31, 4, 3 },
|
||||
/* 12 */ { 30, 5, 1 },
|
||||
/* 11 */ { 32, 2, 2 },
|
||||
/* 10 */ { 32, 2, 1 },
|
||||
};
|
||||
|
||||
// `targetedDbm` integer 10..22 only (Table 7-16 rows).
|
||||
static void lr2021Table716LfRow(int8_t targetedDbm, int8_t* txHalfDbm, uint8_t* duty, uint8_t* slices) {
|
||||
if(targetedDbm < 10) { targetedDbm = 10; }
|
||||
if(targetedDbm > 22) { targetedDbm = 22; }
|
||||
const auto& e = RADIOLIB_LR2021_TABLE_7_16_915_LF[22 - targetedDbm];
|
||||
*txHalfDbm = e.txHalfDbm;
|
||||
*duty = e.paLfDutyCycle;
|
||||
*slices = e.paLfSlices;
|
||||
}
|
||||
|
||||
// Table 7-17: Optimal Values for 490 MHz Semtech Reference Design (LF PA). Integer targeted dBm 20..10.
|
||||
static const struct {
|
||||
int8_t txHalfDbm;
|
||||
uint8_t paLfDutyCycle;
|
||||
uint8_t paLfSlices;
|
||||
} RADIOLIB_LR2021_TABLE_7_17_490_LF[] = {
|
||||
/* 20 */ { 40, 7, 7 },
|
||||
/* 19 */ { 38, 7, 7 },
|
||||
/* 18 */ { 36, 7, 6 },
|
||||
/* 17 */ { 34, 7, 6 },
|
||||
/* 16 */ { 32, 7, 6 },
|
||||
/* 15 */ { 31, 7, 4 },
|
||||
/* 14 */ { 31, 6, 4 },
|
||||
/* 13 */ { 29, 7, 2 },
|
||||
/* 12 */ { 30, 5, 3 },
|
||||
/* 11 */ { 29, 5, 2 },
|
||||
/* 10 */ { 31, 4, 2 },
|
||||
};
|
||||
|
||||
static void lr2021Table717LfRow(int8_t targetedDbm, int8_t* txHalfDbm, uint8_t* duty, uint8_t* slices) {
|
||||
if(targetedDbm < 10) { targetedDbm = 10; }
|
||||
if(targetedDbm > 20) { targetedDbm = 20; }
|
||||
const auto& e = RADIOLIB_LR2021_TABLE_7_17_490_LF[20 - targetedDbm];
|
||||
*txHalfDbm = e.txHalfDbm;
|
||||
*duty = e.paLfDutyCycle;
|
||||
*slices = e.paLfSlices;
|
||||
}
|
||||
|
||||
// Table 7-18: Optimal Values for 2445 MHz Semtech Reference Design (HF PA). Integer targeted dBm 0..12.
|
||||
static const struct {
|
||||
int8_t txHalfDbm;
|
||||
uint8_t paHfDutyCycle;
|
||||
} RADIOLIB_LR2021_TABLE_7_18_2445_HF[] = {
|
||||
/* 12 */ { 24, 16 },
|
||||
/* 11 */ { 24, 26 },
|
||||
/* 10 */ { 24, 30 },
|
||||
/* 9 */ { 22, 30 },
|
||||
/* 8 */ { 21, 31 },
|
||||
/* 7 */ { 18, 30 },
|
||||
/* 6 */ { 16, 30 },
|
||||
/* 5 */ { 15, 31 },
|
||||
/* 4 */ { 10, 25 },
|
||||
/* 3 */ { 8, 25 },
|
||||
/* 2 */ { 7, 28 },
|
||||
/* 1 */ { 6, 30 },
|
||||
/* 0 */ { 4, 30 },
|
||||
};
|
||||
|
||||
static void lr2021Table718HfRow(int8_t targetedDbm, int8_t* txHalfDbm, uint8_t* hfDuty) {
|
||||
if(targetedDbm < 0) { targetedDbm = 0; }
|
||||
if(targetedDbm > 12) { targetedDbm = 12; }
|
||||
const auto& e = RADIOLIB_LR2021_TABLE_7_18_2445_HF[12 - targetedDbm];
|
||||
*txHalfDbm = e.txHalfDbm;
|
||||
*hfDuty = e.paHfDutyCycle;
|
||||
}
|
||||
|
||||
int16_t LR2021::setFrequency(float freq) {
|
||||
return(this->setFrequency(freq, false));
|
||||
}
|
||||
|
|
@ -76,22 +168,78 @@ int16_t LR2021::setOutputPower(int8_t power) {
|
|||
}
|
||||
|
||||
int16_t LR2021::setOutputPower(int8_t power, uint32_t rampTimeUs) {
|
||||
// check if power value is configurable
|
||||
int16_t state = this->checkOutputPower(power, NULL);
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
//! \TODO: [LR2021] how and when to configure OCP?
|
||||
//! \TODO: [LR2021] Determine the optimal PA configuration
|
||||
// update PA config
|
||||
state = setPaConfig(this->highFreq,
|
||||
RADIOLIB_LR2021_PA_LF_MODE_FSM,
|
||||
RADIOLIB_LR2021_PA_LF_DUTY_CYCLE_UNUSED,
|
||||
RADIOLIB_LR2021_PA_LF_SLICES_UNUSED,
|
||||
RADIOLIB_LR2021_PA_HF_DUTY_CYCLE_UNUSED);
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
// set output power
|
||||
state = setTxParams(power, roundRampTime(rampTimeUs));
|
||||
// pa_sel: 0 = LF (Sub-GHz), 1 = HF (1.9–2.5 GHz). Same encoding as lr20xx reference driver.
|
||||
uint8_t paSel = this->highFreq ? (uint8_t)1 : (uint8_t)0;
|
||||
uint8_t paLfMode = RADIOLIB_LR2021_PA_LF_MODE_FSM;
|
||||
uint8_t paLfDutyCycle = RADIOLIB_LR2021_PA_LF_DUTY_CYCLE_UNUSED;
|
||||
uint8_t paLfSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED;
|
||||
uint8_t paHfDutyCycle = RADIOLIB_LR2021_PA_HF_DUTY_CYCLE_UNUSED;
|
||||
// TX_PARAM from tables is signed half-dBm (not always 2 * targeted dBm).
|
||||
int8_t lfTxHalfDbm = (int8_t)(power * 2);
|
||||
int8_t hfTxHalfDbm = (int8_t)(power * 2);
|
||||
|
||||
if(this->highFreq) {
|
||||
// HF PA: Table 7-18 (2445 MHz); LF nibbles per Semtech reference (7/6).
|
||||
paLfDutyCycle = (uint8_t)0x07;
|
||||
paLfSlices = (uint8_t)0x06;
|
||||
if(power >= 0) {
|
||||
lr2021Table718HfRow(power, &hfTxHalfDbm, &paHfDutyCycle);
|
||||
} else {
|
||||
lr2021Table718HfRow(0, &hfTxHalfDbm, &paHfDutyCycle);
|
||||
hfTxHalfDbm = (int8_t)(power * 2);
|
||||
}
|
||||
} else if(this->freqMHz < RADIOLIB_LR2021_LF_PA_TABLE_490_MHZ_MAX) {
|
||||
// LF PA: Table 7-17 (490 MHz ref.), max +20 dBm in table; 21–22 use row 20 PA + requested TX half-dBm.
|
||||
if(power > 20) {
|
||||
lr2021Table717LfRow(20, &lfTxHalfDbm, &paLfDutyCycle, &paLfSlices);
|
||||
lfTxHalfDbm = (int8_t)(power * 2);
|
||||
} else if(power >= 10) {
|
||||
lr2021Table717LfRow(power, &lfTxHalfDbm, &paLfDutyCycle, &paLfSlices);
|
||||
} else {
|
||||
lr2021Table717LfRow(10, &lfTxHalfDbm, &paLfDutyCycle, &paLfSlices);
|
||||
lfTxHalfDbm = (int8_t)(power * 2);
|
||||
}
|
||||
} else {
|
||||
// LF PA: Table 7-16 (915 MHz ref.)
|
||||
if(power >= 10) {
|
||||
lr2021Table716LfRow(power, &lfTxHalfDbm, &paLfDutyCycle, &paLfSlices);
|
||||
} else {
|
||||
lr2021Table716LfRow(10, &lfTxHalfDbm, &paLfDutyCycle, &paLfSlices);
|
||||
lfTxHalfDbm = (int8_t)(power * 2);
|
||||
}
|
||||
}
|
||||
|
||||
(void)clearErrors();
|
||||
|
||||
state = setPaConfig(paSel, paLfMode, paLfDutyCycle, paLfSlices, paHfDutyCycle);
|
||||
RADIOLIB_ASSERT(state);
|
||||
#if RADIOLIB_DEBUG_BASIC
|
||||
RADIOLIB_DEBUG_BASIC_PRINTLN("LR2021 PA cfg: hf=%d lf_dc=0x%X lf_sl=0x%X hf_dc=0x%X",
|
||||
(int)this->highFreq, (int)paLfDutyCycle, (int)paLfSlices, (int)paHfDutyCycle);
|
||||
#endif
|
||||
|
||||
state = selPa(paSel);
|
||||
RADIOLIB_ASSERT(state);
|
||||
#if RADIOLIB_DEBUG_BASIC
|
||||
RADIOLIB_DEBUG_BASIC_PRINTLN("LR2021 PA sel: %s", paSel ? "HF" : "LF");
|
||||
#endif
|
||||
|
||||
if(this->highFreq) {
|
||||
state = setTxParamsHalfDbm(hfTxHalfDbm, roundRampTime(rampTimeUs));
|
||||
} else {
|
||||
state = setTxParamsHalfDbm(lfTxHalfDbm, roundRampTime(rampTimeUs));
|
||||
}
|
||||
RADIOLIB_ASSERT(state);
|
||||
#if RADIOLIB_DEBUG_BASIC
|
||||
uint16_t err = 0;
|
||||
if(getErrors(&err) == RADIOLIB_ERR_NONE) {
|
||||
RADIOLIB_DEBUG_BASIC_PRINTLN("LR2021 device errors after PA/TX: 0x%X", err);
|
||||
}
|
||||
#endif
|
||||
|
||||
return(state);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -957,7 +957,7 @@ class RF69: public PhysicalLayer {
|
|||
int16_t setLnaTestBoost(bool value);
|
||||
|
||||
/*!
|
||||
\brief Gets RSSI (Recorded Signal Strength Indicator) of the last received packet.
|
||||
\brief Gets RSSI (Received Signal Strength Indicator) of the last received packet.
|
||||
\returns Last packet RSSI in dBm.
|
||||
*/
|
||||
float getRSSI() override;
|
||||
|
|
|
|||
|
|
@ -184,6 +184,22 @@ class SX126x: public PhysicalLayer {
|
|||
*/
|
||||
int16_t scanChannel(const ChannelScanConfig_t &config) override;
|
||||
|
||||
/*!
|
||||
\brief Reset the AGC gain state by performing a warm sleep, recalibration, and
|
||||
image rejection calibration cycle. Re-applies DIO2 RF switch and RX boosted gain
|
||||
settings if previously configured. Leaves the radio in standby mode.
|
||||
\returns \ref status_codes
|
||||
*/
|
||||
int16_t resetAGC();
|
||||
|
||||
/*!
|
||||
\brief Perform calibration of the specified blocks.
|
||||
\param params Calibration parameters - bitfield of RADIOLIB_SX126X_CALIBRATE_* values.
|
||||
Use RADIOLIB_SX126X_CALIBRATE_ALL to calibrate all blocks.
|
||||
\returns \ref status_codes
|
||||
*/
|
||||
int16_t calibrate(uint8_t params);
|
||||
|
||||
/*!
|
||||
\brief Sets the module to sleep mode. To wake the device up, call standby().
|
||||
Overload with warm start enabled for PhysicalLayer compatibility.
|
||||
|
|
@ -528,14 +544,14 @@ class SX126x: public PhysicalLayer {
|
|||
int16_t setDio2AsRfSwitch(bool enable = true);
|
||||
|
||||
/*!
|
||||
\brief Gets recorded signal strength indicator.
|
||||
\brief Gets received signal strength indicator.
|
||||
Overload with packet mode enabled for PhysicalLayer compatibility.
|
||||
\returns RSSI value in dBm.
|
||||
*/
|
||||
float getRSSI() override;
|
||||
|
||||
/*!
|
||||
\brief Gets RSSI (Recorded Signal Strength Indicator).
|
||||
\brief Gets RSSI (Received Signal Strength Indicator).
|
||||
\param packet Whether to read last packet RSSI, or the current value.
|
||||
\returns RSSI value in dBm.
|
||||
*/
|
||||
|
|
@ -889,6 +905,8 @@ class SX126x: public PhysicalLayer {
|
|||
|
||||
uint32_t tcxoDelay = 0;
|
||||
uint8_t pwr = 0;
|
||||
bool dio2RfSwitch = false;
|
||||
bool rxBoostedGainMode = false;
|
||||
|
||||
size_t implicitLen = 0;
|
||||
uint8_t invertIQEnabled = RADIOLIB_SX126X_LORA_IQ_STANDARD;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,42 @@
|
|||
|
||||
#if !RADIOLIB_EXCLUDE_SX126X
|
||||
|
||||
int16_t SX126x::resetAGC() {
|
||||
// warm sleep to power down the analog frontend
|
||||
int16_t state = sleep(true);
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
// wake to RC standby
|
||||
state = standby(RADIOLIB_SX126X_STANDBY_RC, true);
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
// recalibrate all blocks
|
||||
state = calibrate(RADIOLIB_SX126X_CALIBRATE_ALL);
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
// re-calibrate image rejection for the operating frequency
|
||||
state = calibrateImage(this->freqMHz);
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
// re-apply DIO2 RF switch if it was configured
|
||||
if(this->dio2RfSwitch) {
|
||||
state = setDio2AsRfSwitch(true);
|
||||
RADIOLIB_ASSERT(state);
|
||||
}
|
||||
|
||||
// re-apply RX boosted gain if it was configured
|
||||
if(this->rxBoostedGainMode) {
|
||||
state = setRxBoostedGainMode(true);
|
||||
RADIOLIB_ASSERT(state);
|
||||
}
|
||||
|
||||
return(RADIOLIB_ERR_NONE);
|
||||
}
|
||||
|
||||
int16_t SX126x::calibrate(uint8_t params) {
|
||||
return(this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_CALIBRATE, ¶ms, 1, true, false));
|
||||
}
|
||||
|
||||
int16_t SX126x::sleep() {
|
||||
return(SX126x::sleep(true));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -394,6 +394,8 @@ int16_t SX126x::setRxBandwidth(float rxBw) {
|
|||
}
|
||||
|
||||
int16_t SX126x::setRxBoostedGainMode(bool rxbgm, bool persist) {
|
||||
this->rxBoostedGainMode = rxbgm;
|
||||
|
||||
// update RX gain setting register
|
||||
uint8_t rxGain = rxbgm ? RADIOLIB_SX126X_RX_GAIN_BOOSTED : RADIOLIB_SX126X_RX_GAIN_POWER_SAVING;
|
||||
int16_t state = writeRegister(RADIOLIB_SX126X_REG_RX_GAIN, &rxGain, 1);
|
||||
|
|
@ -708,6 +710,7 @@ int16_t SX126x::setTCXO(float voltage, uint32_t delay) {
|
|||
}
|
||||
|
||||
int16_t SX126x::setDio2AsRfSwitch(bool enable) {
|
||||
this->dio2RfSwitch = enable;
|
||||
uint8_t data = enable ? RADIOLIB_SX126X_DIO2_AS_RF_SWITCH : RADIOLIB_SX126X_DIO2_AS_IRQ;
|
||||
return(this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_DIO2_AS_RF_SWITCH_CTRL, &data, 1));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -258,14 +258,14 @@ class SX1272: public SX127x {
|
|||
int16_t setDataShapingOOK(uint8_t sh);
|
||||
|
||||
/*!
|
||||
\brief Gets recorded signal strength indicator.
|
||||
\brief Gets received signal strength indicator.
|
||||
Overload with packet mode enabled for PhysicalLayer compatibility.
|
||||
\returns RSSI value in dBm.
|
||||
*/
|
||||
float getRSSI() override;
|
||||
|
||||
/*!
|
||||
\brief Gets recorded signal strength indicator.
|
||||
\brief Gets received signal strength indicator.
|
||||
\param packet Whether to read last packet RSSI, or the current value. LoRa mode only, ignored for FSK.
|
||||
\param skipReceive Set to true to skip putting radio in receive mode for the RSSI measurement in FSK/OOK mode.
|
||||
\returns RSSI value in dBm.
|
||||
|
|
|
|||
|
|
@ -69,7 +69,15 @@ int16_t SX1276::beginFSK(float freq, float br, float freqDev, float rxBw, int8_t
|
|||
}
|
||||
|
||||
int16_t SX1276::setFrequency(float freq) {
|
||||
RADIOLIB_CHECK_RANGE(freq, 137.0f, 1020.0f, RADIOLIB_ERR_INVALID_FREQUENCY);
|
||||
// NOTE: The datasheet specifies Band 2 as 410-525 MHz, but the hardware has been
|
||||
// verified to work down to ~395 MHz. The lower bound is set here to 395 MHz to
|
||||
// accommodate real-world use cases (e.g. TinyGS satellites, radiosondes) while
|
||||
// adding a small margin below the 400 MHz practical limit.
|
||||
if(!(((freq >= 137.0f) && (freq <= 175.0f)) ||
|
||||
((freq >= 395.0f) && (freq <= 525.0f)) ||
|
||||
((freq >= 862.0f) && (freq <= 1020.0f)))) {
|
||||
return(RADIOLIB_ERR_INVALID_FREQUENCY);
|
||||
}
|
||||
|
||||
// set frequency and if successful, save the new setting
|
||||
int16_t state = SX127x::setFrequencyRaw(freq);
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ class SX1276: public SX1278 {
|
|||
// configuration methods
|
||||
|
||||
/*!
|
||||
\brief Sets carrier frequency. Allowed values range from 137.0 MHz to 1020.0 MHz.
|
||||
\brief Sets carrier frequency. Allowed values range from 137.0 MHz to 175.0 MHz, 395.0 to 525.0 MHz (datasheet minimum is 410.0 MHz, hardware works lower) and 862.0 to 1020 MHz.
|
||||
\param freq Carrier frequency to be set in MHz.
|
||||
\returns \ref status_codes
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -69,7 +69,15 @@ int16_t SX1277::beginFSK(float freq, float br, float freqDev, float rxBw, int8_t
|
|||
}
|
||||
|
||||
int16_t SX1277::setFrequency(float freq) {
|
||||
RADIOLIB_CHECK_RANGE(freq, 137.0f, 1020.0f, RADIOLIB_ERR_INVALID_FREQUENCY);
|
||||
// NOTE: The datasheet specifies Band 2 as 410-525 MHz, but the hardware has been
|
||||
// verified to work down to ~395 MHz. The lower bound is set here to 395 MHz to
|
||||
// accommodate real-world use cases (e.g. TinyGS satellites, radiosondes) while
|
||||
// adding a small margin below the 400 MHz practical limit.
|
||||
if(!(((freq >= 137.0f) && (freq <= 175.0f)) ||
|
||||
((freq >= 395.0f) && (freq <= 525.0f)) ||
|
||||
((freq >= 862.0f) && (freq <= 1020.0f)))) {
|
||||
return(RADIOLIB_ERR_INVALID_FREQUENCY);
|
||||
}
|
||||
|
||||
// set frequency and if successful, save the new setting
|
||||
int16_t state = SX127x::setFrequencyRaw(freq);
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ class SX1277: public SX1278 {
|
|||
// configuration methods
|
||||
|
||||
/*!
|
||||
\brief Sets carrier frequency. Allowed values range from 137.0 MHz to 1020.0 MHz.
|
||||
\brief Sets carrier frequency. Allowed values range from 137.0 MHz to 175.0 MHz, 395.0 to 525.0 MHz (datasheet minimum is 410.0 MHz, hardware works lower) and 862.0 to 1020 MHz.
|
||||
\param freq Carrier frequency to be set in MHz.
|
||||
\returns \ref status_codes
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -83,7 +83,14 @@ void SX1278::reset() {
|
|||
}
|
||||
|
||||
int16_t SX1278::setFrequency(float freq) {
|
||||
RADIOLIB_CHECK_RANGE(freq, 137.0f, 525.0f, RADIOLIB_ERR_INVALID_FREQUENCY);
|
||||
// NOTE: The datasheet specifies Band 2 as 410-525 MHz, but the hardware has been
|
||||
// verified to work down to ~395 MHz. The lower bound is set here to 395 MHz to
|
||||
// accommodate real-world use cases (e.g. TinyGS satellites, radiosondes) while
|
||||
// adding a small margin below the 400 MHz practical limit.
|
||||
if(!(((freq >= 137.0f) && (freq <= 175.0f)) ||
|
||||
((freq >= 395.0f) && (freq <= 525.0f)))) {
|
||||
return(RADIOLIB_ERR_INVALID_FREQUENCY);
|
||||
}
|
||||
|
||||
// set frequency and if successful, save the new setting
|
||||
int16_t state = SX127x::setFrequencyRaw(freq);
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ class SX1278: public SX127x {
|
|||
// configuration methods
|
||||
|
||||
/*!
|
||||
\brief Sets carrier frequency. Allowed values range from 137.0 MHz to 525.0 MHz.
|
||||
\brief Sets carrier frequency. Allowed values range from 137.0 MHz to 175.0 MHz and 395.0 to 525.0 MHz (datasheet minimum is 410.0 MHz, hardware works lower).
|
||||
\param freq Carrier frequency to be set in MHz.
|
||||
\returns \ref status_codes
|
||||
*/
|
||||
|
|
@ -270,14 +270,14 @@ class SX1278: public SX127x {
|
|||
int16_t setDataShapingOOK(uint8_t sh);
|
||||
|
||||
/*!
|
||||
\brief Gets recorded signal strength indicator.
|
||||
\brief Gets received signal strength indicator.
|
||||
Overload with packet mode enabled for PhysicalLayer compatibility.
|
||||
\returns RSSI value in dBm.
|
||||
*/
|
||||
float getRSSI() override;
|
||||
|
||||
/*!
|
||||
\brief Gets recorded signal strength indicator.
|
||||
\brief Gets received signal strength indicator.
|
||||
\param packet Whether to read last packet RSSI, or the current value. LoRa mode only, ignored for FSK.
|
||||
\param skipReceive Set to true to skip putting radio in receive mode for the RSSI measurement in FSK/OOK mode.
|
||||
\returns RSSI value in dBm.
|
||||
|
|
|
|||
|
|
@ -69,7 +69,15 @@ int16_t SX1279::beginFSK(float freq, float br, float freqDev, float rxBw, int8_t
|
|||
}
|
||||
|
||||
int16_t SX1279::setFrequency(float freq) {
|
||||
RADIOLIB_CHECK_RANGE(freq, 137.0f, 960.0f, RADIOLIB_ERR_INVALID_FREQUENCY);
|
||||
// NOTE: The datasheet specifies Band 2 as 410-480 MHz, but the hardware has been
|
||||
// verified to work down to ~395 MHz. The lower bound is set here to 395 MHz to
|
||||
// accommodate real-world use cases (e.g. TinyGS satellites, radiosondes) while
|
||||
// adding a small margin below the 400 MHz practical limit.
|
||||
if(!(((freq >= 137.0f) && (freq <= 160.0f)) ||
|
||||
((freq >= 395.0f) && (freq <= 480.0f)) ||
|
||||
((freq >= 779.0f) && (freq <= 960.0f)))) {
|
||||
return(RADIOLIB_ERR_INVALID_FREQUENCY);
|
||||
}
|
||||
|
||||
// set frequency and if successful, save the new setting
|
||||
int16_t state = SX127x::setFrequencyRaw(freq);
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ class SX1279: public SX1278 {
|
|||
// configuration methods
|
||||
|
||||
/*!
|
||||
\brief Sets carrier frequency. Allowed values range from 137.0 MHz to 960.0 MHz.
|
||||
\brief Sets carrier frequency. Allowed values range from 137.0 MHz to 160.0 MHz, 395.0 to 480.0 MHz (datasheet minimum is 410.0 MHz, hardware works lower) and 779.0 to 960 MHz.
|
||||
\param freq Carrier frequency to be set in MHz.
|
||||
\returns \ref status_codes
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -785,13 +785,13 @@ class SX128x: public PhysicalLayer {
|
|||
int16_t setGainControl(uint8_t gain = 0);
|
||||
|
||||
/*!
|
||||
\brief Gets RSSI (Recorded Signal Strength Indicator) of the last received packet.
|
||||
\brief Gets RSSI (Received Signal Strength Indicator) of the last received packet.
|
||||
\returns RSSI of the last received packet in dBm.
|
||||
*/
|
||||
float getRSSI() override;
|
||||
|
||||
/*!
|
||||
\brief Gets RSSI (Recorded Signal Strength Indicator).
|
||||
\brief Gets RSSI (Received Signal Strength Indicator).
|
||||
\param packet Whether to read last packet RSSI, or the current value.
|
||||
\returns RSSI value in dBm.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ int16_t APRSClient::sendPosition(char* destCallsign, uint8_t destSSID, const cha
|
|||
#if !RADIOLIB_STATIC_ONLY
|
||||
char* info = new char[len + 2];
|
||||
#else
|
||||
char info[RADIOLIB_STATIC_ARRAY_SIZE];
|
||||
char info[RADIOLIB_STATIC_ARRAY_SIZE + 2];
|
||||
#endif
|
||||
|
||||
// build the info field
|
||||
|
|
|
|||
|
|
@ -263,6 +263,11 @@ int16_t AX25Client::begin(const char* srcCallsign, uint8_t srcSSID, uint8_t preL
|
|||
return(phyLayer->startDirect());
|
||||
}
|
||||
|
||||
void AX25Client::setScrambler(uint32_t poly, uint32_t init) {
|
||||
this->scramblerPoly = poly;
|
||||
this->scramblerInit = init;
|
||||
}
|
||||
|
||||
#if defined(RADIOLIB_BUILD_ARDUINO)
|
||||
int16_t AX25Client::transmit(String& str, const char* destCallsign, uint8_t destSSID) {
|
||||
return(transmit(str.c_str(), destCallsign, destSSID));
|
||||
|
|
@ -307,7 +312,7 @@ int16_t AX25Client::sendFrame(AX25Frame* frame) {
|
|||
#if !RADIOLIB_STATIC_ONLY
|
||||
uint8_t* frameBuff = new uint8_t[frameBuffLen + 2];
|
||||
#else
|
||||
uint8_t frameBuff[RADIOLIB_STATIC_ARRAY_SIZE];
|
||||
uint8_t frameBuff[RADIOLIB_STATIC_ARRAY_SIZE + 2];
|
||||
#endif
|
||||
uint8_t* frameBuffPtr = frameBuff;
|
||||
|
||||
|
|
@ -390,7 +395,7 @@ int16_t AX25Client::sendFrame(AX25Frame* frame) {
|
|||
// worst-case scenario: sequence of 1s, will have 120% of the original length, stuffed frame also includes both flags
|
||||
uint8_t* stuffedFrameBuff = new uint8_t[preambleLen + 1 + (6*frameBuffLen)/5 + 2];
|
||||
#else
|
||||
uint8_t stuffedFrameBuff[RADIOLIB_STATIC_ARRAY_SIZE];
|
||||
uint8_t stuffedFrameBuff[1 + (6*RADIOLIB_STATIC_ARRAY_SIZE)/5 + 2];
|
||||
#endif
|
||||
|
||||
// initialize buffer to all zeros
|
||||
|
|
@ -474,6 +479,11 @@ int16_t AX25Client::sendFrame(AX25Frame* frame) {
|
|||
}
|
||||
}
|
||||
|
||||
// do the scrambling
|
||||
if(scramblerPoly) {
|
||||
rlb_scrambler(stuffedFrameBuff, stuffedFrameBuffLen, scramblerPoly, scramblerInit, true);
|
||||
}
|
||||
|
||||
// transmit
|
||||
int16_t state = RADIOLIB_ERR_NONE;
|
||||
#if !RADIOLIB_EXCLUDE_AFSK
|
||||
|
|
|
|||
|
|
@ -281,6 +281,13 @@ class AX25Client {
|
|||
*/
|
||||
int16_t begin(const char* srcCallsign, uint8_t srcSSID = 0x00, uint8_t preLen = 8);
|
||||
|
||||
/*!
|
||||
\brief Set scrambling polynomail and initial value.
|
||||
\param poly Scramling polynomial. Use RADIOLIB_SCRAMBLER_G3RUH_POLY for G3RUH coding.
|
||||
\param poly Initial scrambler value. Use RADIOLIB_SCRAMBLER_G3RUH_INIT for G3RUH coding.
|
||||
*/
|
||||
void setScrambler(uint32_t poly, uint32_t init = 0);
|
||||
|
||||
#if defined(RADIOLIB_BUILD_ARDUINO)
|
||||
/*!
|
||||
\brief Transmit unnumbered information (UI) frame.
|
||||
|
|
@ -324,6 +331,8 @@ class AX25Client {
|
|||
char sourceCallsign[RADIOLIB_AX25_MAX_CALLSIGN_LEN + 1] = { 0 };
|
||||
uint8_t sourceSSID = 0;
|
||||
uint16_t preambleLen = 0;
|
||||
uint32_t scramblerInit = 0;
|
||||
uint32_t scramblerPoly = 0;
|
||||
|
||||
void getCallsign(char* buff);
|
||||
uint8_t getSSID();
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ int16_t LoRaWANNode::sendReceive(const String& strUp, uint8_t fPort, String& str
|
|||
// build a temporary buffer
|
||||
// LoRaWAN downlinks can have 250 bytes at most with 1 extra byte for NULL
|
||||
size_t lenDown = 0;
|
||||
uint8_t dataDown[RADIOLIB_LORAWAN_MAX_DOWNLINK_SIZE + 1];
|
||||
uint8_t dataDown[RADIOLIB_LORAWAN_MAX_PAYLOAD_SIZE + 1];
|
||||
|
||||
state = this->sendReceive(reinterpret_cast<const uint8_t*>(dataUp), strlen(dataUp), fPort, dataDown, &lenDown, isConfirmed, eventUp, eventDown);
|
||||
|
||||
|
|
@ -45,7 +45,7 @@ int16_t LoRaWANNode::sendReceive(const char* strUp, uint8_t fPort, bool isConfir
|
|||
// build a temporary buffer
|
||||
// LoRaWAN downlinks can have 250 bytes at most with 1 extra byte for NULL
|
||||
size_t lenDown = 0;
|
||||
uint8_t dataDown[RADIOLIB_LORAWAN_MAX_DOWNLINK_SIZE + 1];
|
||||
uint8_t dataDown[RADIOLIB_LORAWAN_MAX_PAYLOAD_SIZE + 1];
|
||||
|
||||
return(this->sendReceive(reinterpret_cast<uint8_t*>(const_cast<char*>(strUp)), strlen(strUp), fPort, dataDown, &lenDown, isConfirmed, eventUp, eventDown));
|
||||
}
|
||||
|
|
@ -58,7 +58,7 @@ int16_t LoRaWANNode::sendReceive(const uint8_t* dataUp, size_t lenUp, uint8_t fP
|
|||
// build a temporary buffer
|
||||
// LoRaWAN downlinks can have 250 bytes at most with 1 extra byte for NULL
|
||||
size_t lenDown = 0;
|
||||
uint8_t dataDown[RADIOLIB_LORAWAN_MAX_DOWNLINK_SIZE + 1];
|
||||
uint8_t dataDown[RADIOLIB_LORAWAN_MAX_PAYLOAD_SIZE + 1];
|
||||
|
||||
return(this->sendReceive(dataUp, lenUp, fPort, dataDown, &lenDown, isConfirmed, eventUp, eventDown));
|
||||
}
|
||||
|
|
@ -1200,6 +1200,11 @@ void LoRaWANNode::stopMulticastSession() {
|
|||
|
||||
// stop any ongoing activity
|
||||
this->phyLayer->standby();
|
||||
|
||||
if(this->ledPins[RADIOLIB_LORAWAN_RX_BC] != RADIOLIB_NC) {
|
||||
Module *mod = this->phyLayer->getMod();
|
||||
mod->hal->digitalWrite(this->ledPins[RADIOLIB_LORAWAN_RX_BC], mod->hal->GpioLevelLow);
|
||||
}
|
||||
|
||||
// if in Class C, re-open RxC window with normal unicast configuration
|
||||
if(this->lwClass == RADIOLIB_LORAWAN_CLASS_C) {
|
||||
|
|
@ -1467,6 +1472,10 @@ int16_t LoRaWANNode::transmitUplink(const LoRaWANChannel_t* chnl, uint8_t* in, u
|
|||
}
|
||||
}
|
||||
|
||||
if(this->ledPins[0] != RADIOLIB_NC) {
|
||||
mod->hal->digitalWrite(this->ledPins[0], mod->hal->GpioLevelHigh);
|
||||
}
|
||||
|
||||
// start transmission, and time the duration of launchMode() to offset window timing
|
||||
RadioLibTime_t spiStart = mod->hal->millis();
|
||||
state = this->phyLayer->launchMode();
|
||||
|
|
@ -1491,6 +1500,11 @@ int16_t LoRaWANNode::transmitUplink(const LoRaWANChannel_t* chnl, uint8_t* in, u
|
|||
|
||||
// set the timestamp so that we can measure when to start receiving
|
||||
this->tUplinkEnd = mod->hal->millis();
|
||||
|
||||
if(this->ledPins[0] != RADIOLIB_NC) {
|
||||
mod->hal->digitalWrite(this->ledPins[0], mod->hal->GpioLevelLow);
|
||||
}
|
||||
|
||||
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Uplink sent (ToA = %d ms)", toa);
|
||||
|
||||
// increase Time on Air of the uplink sequence
|
||||
|
|
@ -1568,6 +1582,10 @@ int16_t LoRaWANNode::receiveClassA(uint8_t dir, const LoRaWANChannel_t* dlChanne
|
|||
this->sleepDelay(tWindow - tNow);
|
||||
}
|
||||
|
||||
if(window < 4 && this->ledPins[window] != RADIOLIB_NC) {
|
||||
mod->hal->digitalWrite(this->ledPins[window], mod->hal->GpioLevelHigh);
|
||||
}
|
||||
|
||||
// open Rx window by starting receive with specified timeout
|
||||
state = this->phyLayer->launchMode();
|
||||
RadioLibTime_t tOpen = mod->hal->millis();
|
||||
|
|
@ -1595,6 +1613,9 @@ int16_t LoRaWANNode::receiveClassA(uint8_t dir, const LoRaWANChannel_t* dlChanne
|
|||
this->phyLayer->clearPacketReceivedAction();
|
||||
this->phyLayer->clearIrq(1UL << RADIOLIB_IRQ_TIMEOUT);
|
||||
this->phyLayer->standby();
|
||||
if(window < 4 && this->ledPins[window] != RADIOLIB_NC) {
|
||||
mod->hal->digitalWrite(this->ledPins[window], mod->hal->GpioLevelLow);
|
||||
}
|
||||
return(0); // no downlink
|
||||
}
|
||||
|
||||
|
|
@ -1624,6 +1645,9 @@ int16_t LoRaWANNode::receiveClassA(uint8_t dir, const LoRaWANChannel_t* dlChanne
|
|||
// we have a message, clear actions, go to standby
|
||||
this->phyLayer->clearPacketReceivedAction();
|
||||
this->phyLayer->standby();
|
||||
if(window < 4 && this->ledPins[window] != RADIOLIB_NC) {
|
||||
mod->hal->digitalWrite(this->ledPins[window], mod->hal->GpioLevelLow);
|
||||
}
|
||||
|
||||
// if all windows passed without receiving anything, return 0 for no window
|
||||
if(!downlinkAction) {
|
||||
|
|
@ -1675,6 +1699,10 @@ int16_t LoRaWANNode::receiveClassC(RadioLibTime_t timeout) {
|
|||
state = this->phyLayer->stageMode(RADIOLIB_RADIO_MODE_RX, &modeCfg);
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
if(this->ledPins[RADIOLIB_LORAWAN_RX_BC] != RADIOLIB_NC) {
|
||||
mod->hal->digitalWrite(this->ledPins[RADIOLIB_LORAWAN_RX_BC], mod->hal->GpioLevelHigh);
|
||||
}
|
||||
|
||||
// open RxC window by starting receive with specified timeout
|
||||
state = this->phyLayer->launchMode();
|
||||
RadioLibTime_t tOpen = mod->hal->millis();
|
||||
|
|
@ -1683,8 +1711,7 @@ int16_t LoRaWANNode::receiveClassC(RadioLibTime_t timeout) {
|
|||
|
||||
if(timeout) {
|
||||
// wait for the DIO interrupt to fire (RxDone or RxTimeout)
|
||||
// use a small additional delay in case the RxTimeout interrupt is slow to fire
|
||||
while(!downlinkAction && mod->hal->millis() - tOpen <= timeout + this->scanGuard) {
|
||||
while(!downlinkAction && mod->hal->millis() - tOpen <= timeout) {
|
||||
mod->hal->yield();
|
||||
}
|
||||
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Closed RxC window");
|
||||
|
|
@ -1700,6 +1727,9 @@ int16_t LoRaWANNode::receiveClassC(RadioLibTime_t timeout) {
|
|||
this->phyLayer->clearPacketReceivedAction();
|
||||
this->phyLayer->clearIrq(1UL << RADIOLIB_IRQ_TIMEOUT);
|
||||
this->phyLayer->standby();
|
||||
if(this->ledPins[RADIOLIB_LORAWAN_RX_BC] != RADIOLIB_NC) {
|
||||
mod->hal->digitalWrite(this->ledPins[RADIOLIB_LORAWAN_RX_BC], mod->hal->GpioLevelLow);
|
||||
}
|
||||
return(0); // no downlink
|
||||
}
|
||||
|
||||
|
|
@ -1711,6 +1741,9 @@ int16_t LoRaWANNode::receiveClassC(RadioLibTime_t timeout) {
|
|||
// we have a message, clear actions, go to standby
|
||||
this->phyLayer->clearPacketReceivedAction();
|
||||
this->phyLayer->standby();
|
||||
if(this->ledPins[RADIOLIB_LORAWAN_RX_BC] != RADIOLIB_NC) {
|
||||
mod->hal->digitalWrite(this->ledPins[RADIOLIB_LORAWAN_RX_BC], mod->hal->GpioLevelLow);
|
||||
}
|
||||
|
||||
// if all windows passed without receiving anything, return 0 for no window
|
||||
if(!downlinkAction) {
|
||||
|
|
@ -1798,7 +1831,7 @@ int16_t LoRaWANNode::parseDownlink(uint8_t* data, size_t* len, uint8_t window, L
|
|||
#if !RADIOLIB_STATIC_ONLY
|
||||
uint8_t* downlinkMsg = new uint8_t[RADIOLIB_AES128_BLOCK_SIZE + downlinkMsgLen];
|
||||
#else
|
||||
uint8_t downlinkMsg[RADIOLIB_STATIC_ARRAY_SIZE];
|
||||
uint8_t downlinkMsg[RADIOLIB_AES128_BLOCK_SIZE + RADIOLIB_STATIC_ARRAY_SIZE];
|
||||
#endif
|
||||
|
||||
// read the data
|
||||
|
|
@ -2061,7 +2094,7 @@ int16_t LoRaWANNode::parseDownlink(uint8_t* data, size_t* len, uint8_t window, L
|
|||
if(fOptsLen > 0) {
|
||||
uint8_t* mPtr = fOptsPtr;
|
||||
uint8_t procLen = 0;
|
||||
uint8_t fOptsRe[RADIOLIB_LORAWAN_MAX_DOWNLINK_SIZE] = { 0 };
|
||||
uint8_t fOptsRe[RADIOLIB_LORAWAN_MAX_PAYLOAD_SIZE] = { 0 };
|
||||
uint8_t fOptsReLen = 0;
|
||||
|
||||
// indication whether LinkAdr MAC command has been processed
|
||||
|
|
@ -3216,6 +3249,17 @@ void LoRaWANNode::setDeviceStatus(uint8_t battLevel) {
|
|||
this->battLevel = battLevel;
|
||||
}
|
||||
|
||||
void LoRaWANNode::setActivityLeds(const uint32_t pins[4]) {
|
||||
Module *mod = this->phyLayer->getMod();
|
||||
// configure each provided pin and store in the ledPins array
|
||||
for(uint8_t i = 0; i < 4; i++) {
|
||||
if(pins[i] != RADIOLIB_NC) {
|
||||
mod->hal->pinMode(pins[i], mod->hal->GpioModeOutput);
|
||||
}
|
||||
this->ledPins[i] = pins[i];
|
||||
}
|
||||
}
|
||||
|
||||
void LoRaWANNode::scheduleTransmission(RadioLibTime_t tUplink) {
|
||||
this->tUplink = tUplink;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -207,7 +207,7 @@
|
|||
#define RADIOLIB_LORAWAN_MAX_MAC_COMMAND_LEN_UP (2)
|
||||
#define RADIOLIB_LORAWAN_MAX_NUM_ADR_COMMANDS (8)
|
||||
|
||||
#define RADIOLIB_LORAWAN_MAX_DOWNLINK_SIZE (250)
|
||||
#define RADIOLIB_LORAWAN_MAX_PAYLOAD_SIZE (242)
|
||||
|
||||
// session states
|
||||
#define RADIOLIB_LORAWAN_SESSION_NONE (0x00)
|
||||
|
|
@ -848,6 +848,13 @@ class LoRaWANNode {
|
|||
*/
|
||||
void setDeviceStatus(uint8_t battLevel);
|
||||
|
||||
/*!
|
||||
\brief Set pins for activity LEDs that will indicate when the radio is transmitting (Tx) or receiving (Rx).
|
||||
\param pins Array of 4 pin numbers: [Tx, Rx1, Rx2, RxBC].
|
||||
Use RADIOLIB_NC to disable individual indicators.
|
||||
*/
|
||||
void setActivityLeds(const uint32_t pins[4]);
|
||||
|
||||
/*!
|
||||
\brief Set the exact time a transmission should occur. Note: this is the internal clock time.
|
||||
On Arduino platforms, this is the usual time supplied by millis().
|
||||
|
|
@ -1093,6 +1100,8 @@ class LoRaWANNode {
|
|||
RADIOLIB_LORAWAN_RECEIVE_DELAY_2_MS,
|
||||
0 };
|
||||
|
||||
uint32_t ledPins[4] = { RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC };
|
||||
|
||||
// offset between Tx and Rx1 (such that Rx1 has equal or lower DR)
|
||||
uint8_t rx1DrOffset = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -547,7 +547,7 @@ class PhysicalLayer {
|
|||
virtual size_t getPacketLength(bool update = true);
|
||||
|
||||
/*!
|
||||
\brief Gets RSSI (Recorded Signal Strength Indicator) of the last received packet.
|
||||
\brief Gets RSSI (Received Signal Strength Indicator) of the last received packet.
|
||||
\returns RSSI of the last received packet in dBm.
|
||||
*/
|
||||
virtual float getRSSI();
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
// frequently used scrambling configurations
|
||||
// the final bit (x^0 term in polynomial notation) is assumed to always be present
|
||||
#define RADIOLIB_SCRAMBLER_G3RUH_POLY (0x00021001UL) // x^17 + x^12 + 1
|
||||
#define RADIOLIB_SCRAMBLER_G3RUH_POLY (0x00010800UL) // x^17 + x^12 + 1
|
||||
#define RADIOLIB_SCRAMBLER_G3RUH_INIT (0x00000000UL)
|
||||
|
||||
/*!
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue