LoRa APRS Tracker
Simple Code to use TTGO T-Beam as a LoRa APRS Tracker including OLED Display messages.pull/2/head
commit
ba25825309
|
@ -0,0 +1,5 @@
|
||||||
|
.pio
|
||||||
|
.pioenvs
|
||||||
|
.piolibdeps
|
||||||
|
.clang_complete
|
||||||
|
.gcc-flags.json
|
|
@ -0,0 +1,67 @@
|
||||||
|
# Continuous Integration (CI) is the practice, in software
|
||||||
|
# engineering, of merging all developer working copies with a shared mainline
|
||||||
|
# several times a day < https://docs.platformio.org/page/ci/index.html >
|
||||||
|
#
|
||||||
|
# Documentation:
|
||||||
|
#
|
||||||
|
# * Travis CI Embedded Builds with PlatformIO
|
||||||
|
# < https://docs.travis-ci.com/user/integration/platformio/ >
|
||||||
|
#
|
||||||
|
# * PlatformIO integration with Travis CI
|
||||||
|
# < https://docs.platformio.org/page/ci/travis.html >
|
||||||
|
#
|
||||||
|
# * User Guide for `platformio ci` command
|
||||||
|
# < https://docs.platformio.org/page/userguide/cmd_ci.html >
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Please choose one of the following templates (proposed below) and uncomment
|
||||||
|
# it (remove "# " before each line) or use own configuration according to the
|
||||||
|
# Travis CI documentation (see above).
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Template #1: General project. Test it using existing `platformio.ini`.
|
||||||
|
#
|
||||||
|
|
||||||
|
# language: python
|
||||||
|
# python:
|
||||||
|
# - "2.7"
|
||||||
|
#
|
||||||
|
# sudo: false
|
||||||
|
# cache:
|
||||||
|
# directories:
|
||||||
|
# - "~/.platformio"
|
||||||
|
#
|
||||||
|
# install:
|
||||||
|
# - pip install -U platformio
|
||||||
|
# - platformio update
|
||||||
|
#
|
||||||
|
# script:
|
||||||
|
# - platformio run
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Template #2: The project is intended to be used as a library with examples.
|
||||||
|
#
|
||||||
|
|
||||||
|
# language: python
|
||||||
|
# python:
|
||||||
|
# - "2.7"
|
||||||
|
#
|
||||||
|
# sudo: false
|
||||||
|
# cache:
|
||||||
|
# directories:
|
||||||
|
# - "~/.platformio"
|
||||||
|
#
|
||||||
|
# env:
|
||||||
|
# - PLATFORMIO_CI_SRC=path/to/test/file.c
|
||||||
|
# - PLATFORMIO_CI_SRC=examples/file.ino
|
||||||
|
# - PLATFORMIO_CI_SRC=path/to/test/directory
|
||||||
|
#
|
||||||
|
# install:
|
||||||
|
# - pip install -U platformio
|
||||||
|
# - platformio update
|
||||||
|
#
|
||||||
|
# script:
|
||||||
|
# - platformio ci --lib="." --board=ID_1 --board=ID_2 --board=ID_N
|
|
@ -0,0 +1,39 @@
|
||||||
|
|
||||||
|
This directory is intended for project header files.
|
||||||
|
|
||||||
|
A header file is a file containing C declarations and macro definitions
|
||||||
|
to be shared between several project source files. You request the use of a
|
||||||
|
header file in your project source file (C, C++, etc) located in `src` folder
|
||||||
|
by including it, with the C preprocessing directive `#include'.
|
||||||
|
|
||||||
|
```src/main.c
|
||||||
|
|
||||||
|
#include "header.h"
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Including a header file produces the same results as copying the header file
|
||||||
|
into each source file that needs it. Such copying would be time-consuming
|
||||||
|
and error-prone. With a header file, the related declarations appear
|
||||||
|
in only one place. If they need to be changed, they can be changed in one
|
||||||
|
place, and programs that include the header file will automatically use the
|
||||||
|
new version when next recompiled. The header file eliminates the labor of
|
||||||
|
finding and changing all the copies as well as the risk that a failure to
|
||||||
|
find one copy will result in inconsistencies within a program.
|
||||||
|
|
||||||
|
In C, the usual convention is to give header files names that end with `.h'.
|
||||||
|
It is most portable to use only letters, digits, dashes, and underscores in
|
||||||
|
header file names, and at most one dot.
|
||||||
|
|
||||||
|
Read more about using header files in official GCC documentation:
|
||||||
|
|
||||||
|
* Include Syntax
|
||||||
|
* Include Operation
|
||||||
|
* Once-Only Headers
|
||||||
|
* Computed Includes
|
||||||
|
|
||||||
|
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
|
@ -0,0 +1,46 @@
|
||||||
|
|
||||||
|
This directory is intended for project specific (private) libraries.
|
||||||
|
PlatformIO will compile them to static libraries and link into executable file.
|
||||||
|
|
||||||
|
The source code of each library should be placed in a an own separate directory
|
||||||
|
("lib/your_library_name/[here are source files]").
|
||||||
|
|
||||||
|
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
||||||
|
|
||||||
|
|--lib
|
||||||
|
| |
|
||||||
|
| |--Bar
|
||||||
|
| | |--docs
|
||||||
|
| | |--examples
|
||||||
|
| | |--src
|
||||||
|
| | |- Bar.c
|
||||||
|
| | |- Bar.h
|
||||||
|
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||||
|
| |
|
||||||
|
| |--Foo
|
||||||
|
| | |- Foo.c
|
||||||
|
| | |- Foo.h
|
||||||
|
| |
|
||||||
|
| |- README --> THIS FILE
|
||||||
|
|
|
||||||
|
|- platformio.ini
|
||||||
|
|--src
|
||||||
|
|- main.c
|
||||||
|
|
||||||
|
and a contents of `src/main.c`:
|
||||||
|
```
|
||||||
|
#include <Foo.h>
|
||||||
|
#include <Bar.h>
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
PlatformIO Library Dependency Finder will find automatically dependent
|
||||||
|
libraries scanning project source files.
|
||||||
|
|
||||||
|
More information about PlatformIO Library Dependency Finder
|
||||||
|
- https://docs.platformio.org/page/librarymanager/ldf.html
|
|
@ -0,0 +1,14 @@
|
||||||
|
; PlatformIO Project Configuration File
|
||||||
|
;
|
||||||
|
; Build options: build flags, source filter
|
||||||
|
; Upload options: custom upload port, speed and extra flags
|
||||||
|
; Library options: dependencies, extra library storages
|
||||||
|
; Advanced options: extra scripting
|
||||||
|
;
|
||||||
|
; Please visit documentation for the other options and examples
|
||||||
|
; https://docs.platformio.org/page/projectconf.html
|
||||||
|
|
||||||
|
[env:heltec_wifi_lora_32]
|
||||||
|
platform = espressif32
|
||||||
|
board = heltec_wifi_lora_32
|
||||||
|
framework = arduino
|
|
@ -0,0 +1,452 @@
|
||||||
|
// BG_RF95.cpp
|
||||||
|
//
|
||||||
|
// Copyright (C) 2011 Mike McCauley
|
||||||
|
// $Id: BG_RF95.cpp,v 1.11 2016/04/04 01:40:12 mikem Exp $
|
||||||
|
|
||||||
|
#include <BG_RF95.h>
|
||||||
|
|
||||||
|
byte _lastSNR = 0;
|
||||||
|
|
||||||
|
// Interrupt vectors for the 3 Arduino interrupt pins
|
||||||
|
// Each interrupt can be handled by a different instance of BG_RF95, allowing you to have
|
||||||
|
// 2 or more LORAs per Arduino
|
||||||
|
BG_RF95* BG_RF95::_deviceForInterrupt[BG_RF95_NUM_INTERRUPTS] = {0, 0, 0};
|
||||||
|
uint8_t BG_RF95::_interruptCount = 0; // Index into _deviceForInterrupt for next device
|
||||||
|
|
||||||
|
// These are indexed by the values of ModemConfigChoice
|
||||||
|
// Stored in flash (program) memory to save SRAM
|
||||||
|
PROGMEM static const BG_RF95::ModemConfig MODEM_CONFIG_TABLE[] =
|
||||||
|
{
|
||||||
|
// 1d, 1e, 26
|
||||||
|
{ 0x72, 0x74, 0x00}, // Bw125Cr45Sf128 (the chip default)
|
||||||
|
{ 0x92, 0x74, 0x00}, // Bw500Cr45Sf128
|
||||||
|
{ 0x48, 0x94, 0x00}, // Bw31_25Cr48Sf512
|
||||||
|
{ 0x78, 0xc4, 0x00}, // Bw125Cr48Sf4096
|
||||||
|
{ 0x72, 0xc7, 0x8}, // BG 125 cr45 sf12
|
||||||
|
};
|
||||||
|
|
||||||
|
BG_RF95::BG_RF95(uint8_t slaveSelectPin, uint8_t interruptPin, RHGenericSPI& spi)
|
||||||
|
:
|
||||||
|
RHSPIDriver(slaveSelectPin, spi),
|
||||||
|
_rxBufValid(0)
|
||||||
|
{
|
||||||
|
_interruptPin = interruptPin;
|
||||||
|
_myInterruptIndex = 0xff; // Not allocated yet
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BG_RF95::init()
|
||||||
|
{
|
||||||
|
if (!RHSPIDriver::init())
|
||||||
|
return false;
|
||||||
|
//Serial.println("RHSPIDriver::init completed");
|
||||||
|
// Determine the interrupt number that corresponds to the interruptPin
|
||||||
|
int interruptNumber = digitalPinToInterrupt(_interruptPin);
|
||||||
|
if (interruptNumber == NOT_AN_INTERRUPT)
|
||||||
|
return false;
|
||||||
|
#ifdef RH_ATTACHINTERRUPT_TAKES_PIN_NUMBER
|
||||||
|
interruptNumber = _interruptPin;
|
||||||
|
#endif
|
||||||
|
//Serial.println("Attach Interrupt completed");
|
||||||
|
|
||||||
|
// No way to check the device type :-(
|
||||||
|
|
||||||
|
// Set sleep mode, so we can also set LORA mode:
|
||||||
|
spiWrite(BG_RF95_REG_01_OP_MODE, BG_RF95_MODE_SLEEP | BG_RF95_LONG_RANGE_MODE);
|
||||||
|
delay(10); // Wait for sleep mode to take over from say, CAD
|
||||||
|
// Check we are in sleep mode, with LORA set
|
||||||
|
if (spiRead(BG_RF95_REG_01_OP_MODE) != (BG_RF95_MODE_SLEEP | BG_RF95_LONG_RANGE_MODE))
|
||||||
|
{
|
||||||
|
//Serial.println(spiRead(BG_RF95_REG_01_OP_MODE), HEX);
|
||||||
|
return false; // No device present?
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add by Adrien van den Bossche <vandenbo@univ-tlse2.fr> for Teensy
|
||||||
|
// ARM M4 requires the below. else pin interrupt doesn't work properly.
|
||||||
|
// On all other platforms, its innocuous, belt and braces
|
||||||
|
pinMode(_interruptPin, INPUT);
|
||||||
|
|
||||||
|
// Set up interrupt handler
|
||||||
|
// Since there are a limited number of interrupt glue functions isr*() available,
|
||||||
|
// we can only support a limited number of devices simultaneously
|
||||||
|
// ON some devices, notably most Arduinos, the interrupt pin passed in is actuallt the
|
||||||
|
// interrupt number. You have to figure out the interruptnumber-to-interruptpin mapping
|
||||||
|
// yourself based on knwledge of what Arduino board you are running on.
|
||||||
|
if (_myInterruptIndex == 0xff)
|
||||||
|
{
|
||||||
|
// First run, no interrupt allocated yet
|
||||||
|
if (_interruptCount <= BG_RF95_NUM_INTERRUPTS)
|
||||||
|
_myInterruptIndex = _interruptCount++;
|
||||||
|
else
|
||||||
|
return false; // Too many devices, not enough interrupt vectors
|
||||||
|
}
|
||||||
|
_deviceForInterrupt[_myInterruptIndex] = this;
|
||||||
|
if (_myInterruptIndex == 0)
|
||||||
|
attachInterrupt(interruptNumber, isr0, RISING);
|
||||||
|
else if (_myInterruptIndex == 1)
|
||||||
|
attachInterrupt(interruptNumber, isr1, RISING);
|
||||||
|
else if (_myInterruptIndex == 2)
|
||||||
|
attachInterrupt(interruptNumber, isr2, RISING);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//Serial.println("Interrupt vector too many vectors");
|
||||||
|
return false; // Too many devices, not enough interrupt vectors
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up FIFO
|
||||||
|
// We configure so that we can use the entire 256 byte FIFO for either receive
|
||||||
|
// or transmit, but not both at the same time
|
||||||
|
spiWrite(BG_RF95_REG_0E_FIFO_TX_BASE_ADDR, 0);
|
||||||
|
spiWrite(BG_RF95_REG_0F_FIFO_RX_BASE_ADDR, 0);
|
||||||
|
|
||||||
|
// Packet format is preamble + explicit-header + payload + crc
|
||||||
|
// Explicit Header Mode
|
||||||
|
// payload is TO + FROM + ID + FLAGS + message data
|
||||||
|
// RX mode is implmented with RXCONTINUOUS
|
||||||
|
// max message data length is 255 - 4 = 251 octets
|
||||||
|
|
||||||
|
setModeIdle();
|
||||||
|
|
||||||
|
// Set up default configuration
|
||||||
|
// No Sync Words in LORA mode.
|
||||||
|
setModemConfig(Bw125Cr45Sf128); // Radio default
|
||||||
|
// setModemConfig(Bw125Cr48Sf4096); // slow and reliable?
|
||||||
|
setPreambleLength(8); // Default is 8
|
||||||
|
// An innocuous ISM frequency, same as RF22's
|
||||||
|
setFrequency(433.800);
|
||||||
|
// Lowish power
|
||||||
|
setTxPower(20);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// C++ level interrupt handler for this instance
|
||||||
|
// LORA is unusual in that it has several interrupt lines, and not a single, combined one.
|
||||||
|
// On MiniWirelessLoRa, only one of the several interrupt lines (DI0) from the RFM95 is usefuly
|
||||||
|
// connnected to the processor.
|
||||||
|
// We use this to get RxDone and TxDone interrupts
|
||||||
|
void BG_RF95::handleInterrupt()
|
||||||
|
{
|
||||||
|
// Read the interrupt register
|
||||||
|
//Serial.println("HandleInterrupt");
|
||||||
|
uint8_t irq_flags = spiRead(BG_RF95_REG_12_IRQ_FLAGS);
|
||||||
|
if (_mode == RHModeRx && irq_flags & (BG_RF95_RX_TIMEOUT | BG_RF95_PAYLOAD_CRC_ERROR))
|
||||||
|
{
|
||||||
|
_rxBad++;
|
||||||
|
}
|
||||||
|
else if (_mode == RHModeRx && irq_flags & BG_RF95_RX_DONE)
|
||||||
|
{
|
||||||
|
// Have received a packet
|
||||||
|
uint8_t len = spiRead(BG_RF95_REG_13_RX_NB_BYTES);
|
||||||
|
|
||||||
|
// Reset the fifo read ptr to the beginning of the packet
|
||||||
|
spiWrite(BG_RF95_REG_0D_FIFO_ADDR_PTR, spiRead(BG_RF95_REG_10_FIFO_RX_CURRENT_ADDR));
|
||||||
|
spiBurstRead(BG_RF95_REG_00_FIFO, _buf, len);
|
||||||
|
_bufLen = len;
|
||||||
|
spiWrite(BG_RF95_REG_12_IRQ_FLAGS, 0xff); // Clear all IRQ flags
|
||||||
|
|
||||||
|
// Remember the RSSI of this packet
|
||||||
|
// this is according to the doc, but is it really correct?
|
||||||
|
// weakest receiveable signals are reported RSSI at about -66
|
||||||
|
_lastRssi = spiRead(BG_RF95_REG_1A_PKT_RSSI_VALUE) - 137;
|
||||||
|
|
||||||
|
_lastSNR = spiRead(BG_RF95_REG_19_PKT_SNR_VALUE);
|
||||||
|
|
||||||
|
// We have received a message.
|
||||||
|
validateRxBuf();
|
||||||
|
if (_rxBufValid)
|
||||||
|
setModeIdle(); // Got one
|
||||||
|
}
|
||||||
|
else if (_mode == RHModeTx && irq_flags & BG_RF95_TX_DONE)
|
||||||
|
{
|
||||||
|
_txGood++;
|
||||||
|
setModeIdle();
|
||||||
|
}
|
||||||
|
|
||||||
|
spiWrite(BG_RF95_REG_12_IRQ_FLAGS, 0xff); // Clear all IRQ flags
|
||||||
|
}
|
||||||
|
|
||||||
|
// These are low level functions that call the interrupt handler for the correct
|
||||||
|
// instance of BG_RF95.
|
||||||
|
// 3 interrupts allows us to have 3 different devices
|
||||||
|
void BG_RF95::isr0()
|
||||||
|
{
|
||||||
|
if (_deviceForInterrupt[0])
|
||||||
|
_deviceForInterrupt[0]->handleInterrupt();
|
||||||
|
}
|
||||||
|
void BG_RF95::isr1()
|
||||||
|
{
|
||||||
|
if (_deviceForInterrupt[1])
|
||||||
|
_deviceForInterrupt[1]->handleInterrupt();
|
||||||
|
}
|
||||||
|
void BG_RF95::isr2()
|
||||||
|
{
|
||||||
|
if (_deviceForInterrupt[2])
|
||||||
|
_deviceForInterrupt[2]->handleInterrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether the latest received message is complete and uncorrupted
|
||||||
|
void BG_RF95::validateRxBuf()
|
||||||
|
{
|
||||||
|
_promiscuous = 1;
|
||||||
|
if (_bufLen < 4)
|
||||||
|
return; // Too short to be a real message
|
||||||
|
// Extract the 4 headers
|
||||||
|
//Serial.println("validateRxBuf >= 4");
|
||||||
|
_rxHeaderTo = _buf[0];
|
||||||
|
_rxHeaderFrom = _buf[1];
|
||||||
|
_rxHeaderId = _buf[2];
|
||||||
|
_rxHeaderFlags = _buf[3];
|
||||||
|
if (_promiscuous ||
|
||||||
|
_rxHeaderTo == _thisAddress ||
|
||||||
|
_rxHeaderTo == RH_BROADCAST_ADDRESS)
|
||||||
|
{
|
||||||
|
_rxGood++;
|
||||||
|
_rxBufValid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BG_RF95::available()
|
||||||
|
{
|
||||||
|
if (_mode == RHModeTx)
|
||||||
|
return false;
|
||||||
|
setModeRx();
|
||||||
|
return _rxBufValid; // Will be set by the interrupt handler when a good message is received
|
||||||
|
}
|
||||||
|
|
||||||
|
void BG_RF95::clearRxBuf()
|
||||||
|
{
|
||||||
|
ATOMIC_BLOCK_START;
|
||||||
|
_rxBufValid = false;
|
||||||
|
_bufLen = 0;
|
||||||
|
ATOMIC_BLOCK_END;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// BG 3 Byte header
|
||||||
|
bool BG_RF95::recvAPRS(uint8_t* buf, uint8_t* len)
|
||||||
|
{
|
||||||
|
if (!available())
|
||||||
|
return false;
|
||||||
|
if (buf && len)
|
||||||
|
{
|
||||||
|
ATOMIC_BLOCK_START;
|
||||||
|
// Skip the 4 headers that are at the beginning of the rxBuf
|
||||||
|
if (*len > _bufLen-BG_RF95_HEADER_LEN)
|
||||||
|
*len = _bufLen-(BG_RF95_HEADER_LEN-1);
|
||||||
|
memcpy(buf, _buf+(BG_RF95_HEADER_LEN-1), *len); // BG only 3 Byte header (-1)
|
||||||
|
ATOMIC_BLOCK_END;
|
||||||
|
}
|
||||||
|
clearRxBuf(); // This message accepted and cleared
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BG_RF95::recv(uint8_t* buf, uint8_t* len)
|
||||||
|
{
|
||||||
|
if (!available())
|
||||||
|
return false;
|
||||||
|
if (buf && len)
|
||||||
|
{
|
||||||
|
ATOMIC_BLOCK_START;
|
||||||
|
// Skip the 4 headers that are at the beginning of the rxBuf
|
||||||
|
if (*len > _bufLen-BG_RF95_HEADER_LEN)
|
||||||
|
*len = _bufLen-BG_RF95_HEADER_LEN;
|
||||||
|
memcpy(buf, _buf+BG_RF95_HEADER_LEN, *len);
|
||||||
|
ATOMIC_BLOCK_END;
|
||||||
|
}
|
||||||
|
clearRxBuf(); // This message accepted and cleared
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t BG_RF95::lastSNR()
|
||||||
|
{
|
||||||
|
return(_lastSNR);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool BG_RF95::send(const uint8_t* data, uint8_t len)
|
||||||
|
{
|
||||||
|
if (len > BG_RF95_MAX_MESSAGE_LEN)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
waitPacketSent(); // Make sure we dont interrupt an outgoing message
|
||||||
|
setModeIdle();
|
||||||
|
|
||||||
|
// Position at the beginning of the FIFO
|
||||||
|
spiWrite(BG_RF95_REG_0D_FIFO_ADDR_PTR, 0);
|
||||||
|
// The headers
|
||||||
|
spiWrite(BG_RF95_REG_00_FIFO, _txHeaderTo);
|
||||||
|
spiWrite(BG_RF95_REG_00_FIFO, _txHeaderFrom);
|
||||||
|
spiWrite(BG_RF95_REG_00_FIFO, _txHeaderId);
|
||||||
|
spiWrite(BG_RF95_REG_00_FIFO, _txHeaderFlags);
|
||||||
|
// The message data
|
||||||
|
spiBurstWrite(BG_RF95_REG_00_FIFO, data, len);
|
||||||
|
spiWrite(BG_RF95_REG_22_PAYLOAD_LENGTH, len + BG_RF95_HEADER_LEN);
|
||||||
|
|
||||||
|
setModeTx(); // Start the transmitter
|
||||||
|
// when Tx is done, interruptHandler will fire and radio mode will return to STANDBY
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BG_RF95::sendAPRS(const uint8_t* data, uint8_t len)
|
||||||
|
{
|
||||||
|
if (len > BG_RF95_MAX_MESSAGE_LEN)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
waitPacketSent(); // Make sure we dont interrupt an outgoing message
|
||||||
|
setModeIdle();
|
||||||
|
|
||||||
|
// Position at the beginning of the FIFO
|
||||||
|
spiWrite(BG_RF95_REG_0D_FIFO_ADDR_PTR, 0);
|
||||||
|
// The headers for APRS
|
||||||
|
spiWrite(BG_RF95_REG_00_FIFO, '<');
|
||||||
|
spiWrite(BG_RF95_REG_00_FIFO, _txHeaderFrom);
|
||||||
|
spiWrite(BG_RF95_REG_00_FIFO, 0x1 );
|
||||||
|
//spiWrite(BG_RF95_REG_00_FIFO, _txHeaderFlags);
|
||||||
|
// The message data
|
||||||
|
spiBurstWrite(BG_RF95_REG_00_FIFO, data, len);
|
||||||
|
spiWrite(BG_RF95_REG_22_PAYLOAD_LENGTH, len + BG_RF95_HEADER_LEN -1 ); // only 3 Byte header BG
|
||||||
|
|
||||||
|
setModeTx(); // Start the transmitter
|
||||||
|
// when Tx is done, interruptHandler will fire and radio mode will return to STANDBY
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BG_RF95::printRegisters()
|
||||||
|
{
|
||||||
|
#ifdef RH_HAVE_SERIAL
|
||||||
|
uint8_t registers[] = { 0x01, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x014, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x4d };
|
||||||
|
|
||||||
|
uint8_t i;
|
||||||
|
for (i = 0; i < sizeof(registers); i++)
|
||||||
|
{
|
||||||
|
Serial.print(registers[i], HEX);
|
||||||
|
Serial.print(": ");
|
||||||
|
Serial.println(spiRead(registers[i]), HEX);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t BG_RF95::maxMessageLength()
|
||||||
|
{
|
||||||
|
return BG_RF95_MAX_MESSAGE_LEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BG_RF95::setFrequency(float centre)
|
||||||
|
{
|
||||||
|
// Frf = FRF / FSTEP
|
||||||
|
uint32_t frf = (centre * 1000000.0) / BG_RF95_FSTEP;
|
||||||
|
spiWrite(BG_RF95_REG_06_FRF_MSB, (frf >> 16) & 0xff);
|
||||||
|
spiWrite(BG_RF95_REG_07_FRF_MID, (frf >> 8) & 0xff);
|
||||||
|
spiWrite(BG_RF95_REG_08_FRF_LSB, frf & 0xff);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BG_RF95::setModeIdle()
|
||||||
|
{
|
||||||
|
if (_mode != RHModeIdle)
|
||||||
|
{
|
||||||
|
spiWrite(BG_RF95_REG_01_OP_MODE, BG_RF95_MODE_STDBY);
|
||||||
|
_mode = RHModeIdle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool BG_RF95::sleep()
|
||||||
|
{
|
||||||
|
if (_mode != RHModeSleep)
|
||||||
|
{
|
||||||
|
spiWrite(BG_RF95_REG_01_OP_MODE, BG_RF95_MODE_SLEEP);
|
||||||
|
_mode = RHModeSleep;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BG_RF95::setModeRx()
|
||||||
|
{
|
||||||
|
if (_mode != RHModeRx)
|
||||||
|
{
|
||||||
|
//Serial.println("SetModeRx");
|
||||||
|
_mode = RHModeRx;
|
||||||
|
spiWrite(BG_RF95_REG_01_OP_MODE, BG_RF95_MODE_RXCONTINUOUS);
|
||||||
|
spiWrite(BG_RF95_REG_40_DIO_MAPPING1, 0x00); // Interrupt on RxDone
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BG_RF95::setModeTx()
|
||||||
|
{
|
||||||
|
if (_mode != RHModeTx)
|
||||||
|
{
|
||||||
|
_mode = RHModeTx; // set first to avoid possible race condition
|
||||||
|
spiWrite(BG_RF95_REG_01_OP_MODE, BG_RF95_MODE_TX);
|
||||||
|
spiWrite(BG_RF95_REG_40_DIO_MAPPING1, 0x40); // Interrupt on TxDone
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BG_RF95::setTxPower(int8_t power, bool useRFO)
|
||||||
|
{
|
||||||
|
// Sigh, different behaviours depending on whther the module use PA_BOOST or the RFO pin
|
||||||
|
// for the transmitter output
|
||||||
|
if (useRFO)
|
||||||
|
{
|
||||||
|
if (power > 14) power = 14;
|
||||||
|
if (power < -1) power = -1;
|
||||||
|
spiWrite(BG_RF95_REG_09_PA_CONFIG, BG_RF95_MAX_POWER | (power + 1));
|
||||||
|
} else {
|
||||||
|
if (power > 23) power = 23;
|
||||||
|
if (power < 5) power = 5;
|
||||||
|
|
||||||
|
// For BG_RF95_PA_DAC_ENABLE, manual says '+20dBm on PA_BOOST when OutputPower=0xf'
|
||||||
|
// BG_RF95_PA_DAC_ENABLE actually adds about 3dBm to all power levels. We will us it
|
||||||
|
// for 21, 22 and 23dBm -= 3;
|
||||||
|
}
|
||||||
|
if (power > 20) {
|
||||||
|
spiWrite(BG_RF95_REG_0B_OCP, ( BG_RF95_OCP_ON | BG_RF95_OCP_TRIM ) ); // Trim max current tp 240mA
|
||||||
|
spiWrite(BG_RF95_REG_4D_PA_DAC, BG_RF95_PA_DAC_ENABLE);
|
||||||
|
//power -= 3;
|
||||||
|
power = 20; // and set PA_DAC_ENABLE
|
||||||
|
|
||||||
|
} else {
|
||||||
|
spiWrite(BG_RF95_REG_4D_PA_DAC, BG_RF95_PA_DAC_DISABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// RFM95/96/97/98 does not have RFO pins connected to anything. Only PA_BOOST
|
||||||
|
// pin is connected, so must use PA_BOOST
|
||||||
|
// Pout = 2 + OutputPower.
|
||||||
|
// The documentation is pretty confusing on this topic: PaSelect says the max power is 20dBm,
|
||||||
|
// but OutputPower claims it would be 17dBm.
|
||||||
|
// My measurements show 20dBm is correct
|
||||||
|
//spiWrite(BG_RF95_REG_09_PA_CONFIG, (BG_RF95_PA_SELECT | (power-5)) );
|
||||||
|
spiWrite(BG_RF95_REG_09_PA_CONFIG, (BG_RF95_PA_SELECT | BG_RF95_MAX_POWER | (power-5)) );
|
||||||
|
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets registers from a canned modem configuration structure
|
||||||
|
void BG_RF95::setModemRegisters(const ModemConfig* config)
|
||||||
|
{
|
||||||
|
spiWrite(BG_RF95_REG_1D_MODEM_CONFIG1, config->reg_1d);
|
||||||
|
spiWrite(BG_RF95_REG_1E_MODEM_CONFIG2, config->reg_1e);
|
||||||
|
spiWrite(BG_RF95_REG_26_MODEM_CONFIG3, config->reg_26);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set one of the canned FSK Modem configs
|
||||||
|
// Returns true if its a valid choice
|
||||||
|
bool BG_RF95::setModemConfig(ModemConfigChoice index)
|
||||||
|
{
|
||||||
|
if (index > (signed int)(sizeof(MODEM_CONFIG_TABLE) / sizeof(ModemConfig)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ModemConfig cfg;
|
||||||
|
memcpy_P(&cfg, &MODEM_CONFIG_TABLE[index], sizeof(BG_RF95::ModemConfig));
|
||||||
|
setModemRegisters(&cfg);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BG_RF95::setPreambleLength(uint16_t bytes)
|
||||||
|
{
|
||||||
|
spiWrite(BG_RF95_REG_20_PREAMBLE_MSB, bytes >> 8);
|
||||||
|
spiWrite(BG_RF95_REG_21_PREAMBLE_LSB, bytes & 0xff);
|
||||||
|
}
|
|
@ -0,0 +1,743 @@
|
||||||
|
// BG_RF95.h
|
||||||
|
//
|
||||||
|
// Definitions for HopeRF LoRa radios per:
|
||||||
|
// http://www.hoperf.com/upload/rf/RFM95_96_97_98W.pdf
|
||||||
|
// http://www.hoperf.cn/upload/rfchip/RF96_97_98.pdf
|
||||||
|
//
|
||||||
|
// Author: Mike McCauley (mikem@airspayce.com)
|
||||||
|
// Copyright (C) 2014 Mike McCauley
|
||||||
|
// $Id: BG_RF95.h,v 1.11 2016/07/07 00:02:53 mikem Exp mikem $
|
||||||
|
// modified for Lora APRS Bernd Gasser OE1ACM
|
||||||
|
|
||||||
|
#ifndef BG_RF95_h
|
||||||
|
#define BG_RF95_h
|
||||||
|
|
||||||
|
#include <RHSPIDriver.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// This is the maximum number of interrupts the driver can support
|
||||||
|
// Most Arduinos can handle 2, Megas can handle more
|
||||||
|
#define BG_RF95_NUM_INTERRUPTS 3
|
||||||
|
|
||||||
|
// Max number of octets the LORA Rx/Tx FIFO can hold
|
||||||
|
#define BG_RF95_FIFO_SIZE 255
|
||||||
|
|
||||||
|
// This is the maximum number of bytes that can be carried by the LORA.
|
||||||
|
// We use some for headers, keeping fewer for RadioHead messages
|
||||||
|
#define BG_RF95_MAX_PAYLOAD_LEN BG_RF95_FIFO_SIZE
|
||||||
|
|
||||||
|
// The length of the headers we add.
|
||||||
|
// The headers are inside the LORA's payload
|
||||||
|
#define BG_RF95_HEADER_LEN 4
|
||||||
|
|
||||||
|
// This is the maximum message length that can be supported by this driver.
|
||||||
|
// Can be pre-defined to a smaller size (to save SRAM) prior to including this header
|
||||||
|
// Here we allow for 1 byte message length, 4 bytes headers, user data and 2 bytes of FCS
|
||||||
|
#ifndef BG_RF95_MAX_MESSAGE_LEN
|
||||||
|
#define BG_RF95_MAX_MESSAGE_LEN (BG_RF95_MAX_PAYLOAD_LEN - BG_RF95_HEADER_LEN)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// The crystal oscillator frequency of the module
|
||||||
|
#define BG_RF95_FXOSC 32000000.0
|
||||||
|
|
||||||
|
// The Frequency Synthesizer step = BG_RF95_FXOSC / 2^^19
|
||||||
|
#define BG_RF95_FSTEP (BG_RF95_FXOSC / 524288)
|
||||||
|
|
||||||
|
|
||||||
|
// Register names (LoRa Mode, from table 85)
|
||||||
|
#define BG_RF95_REG_00_FIFO 0x00
|
||||||
|
#define BG_RF95_REG_01_OP_MODE 0x01
|
||||||
|
#define BG_RF95_REG_02_RESERVED 0x02
|
||||||
|
#define BG_RF95_REG_03_RESERVED 0x03
|
||||||
|
#define BG_RF95_REG_04_RESERVED 0x04
|
||||||
|
#define BG_RF95_REG_05_RESERVED 0x05
|
||||||
|
#define BG_RF95_REG_06_FRF_MSB 0x06
|
||||||
|
#define BG_RF95_REG_07_FRF_MID 0x07
|
||||||
|
#define BG_RF95_REG_08_FRF_LSB 0x08
|
||||||
|
#define BG_RF95_REG_09_PA_CONFIG 0x09
|
||||||
|
#define BG_RF95_REG_0A_PA_RAMP 0x0a
|
||||||
|
#define BG_RF95_REG_0B_OCP 0x0b
|
||||||
|
#define BG_RF95_REG_0C_LNA 0x0c
|
||||||
|
#define BG_RF95_REG_0D_FIFO_ADDR_PTR 0x0d
|
||||||
|
#define BG_RF95_REG_0E_FIFO_TX_BASE_ADDR 0x0e
|
||||||
|
#define BG_RF95_REG_0F_FIFO_RX_BASE_ADDR 0x0f
|
||||||
|
#define BG_RF95_REG_10_FIFO_RX_CURRENT_ADDR 0x10
|
||||||
|
#define BG_RF95_REG_11_IRQ_FLAGS_MASK 0x11
|
||||||
|
#define BG_RF95_REG_12_IRQ_FLAGS 0x12
|
||||||
|
#define BG_RF95_REG_13_RX_NB_BYTES 0x13
|
||||||
|
#define BG_RF95_REG_14_RX_HEADER_CNT_VALUE_MSB 0x14
|
||||||
|
#define BG_RF95_REG_15_RX_HEADER_CNT_VALUE_LSB 0x15
|
||||||
|
#define BG_RF95_REG_16_RX_PACKET_CNT_VALUE_MSB 0x16
|
||||||
|
#define BG_RF95_REG_17_RX_PACKET_CNT_VALUE_LSB 0x17
|
||||||
|
#define BG_RF95_REG_18_MODEM_STAT 0x18
|
||||||
|
#define BG_RF95_REG_19_PKT_SNR_VALUE 0x19
|
||||||
|
#define BG_RF95_REG_1A_PKT_RSSI_VALUE 0x1a
|
||||||
|
#define BG_RF95_REG_1B_RSSI_VALUE 0x1b
|
||||||
|
#define BG_RF95_REG_1C_HOP_CHANNEL 0x1c
|
||||||
|
#define BG_RF95_REG_1D_MODEM_CONFIG1 0x1d
|
||||||
|
#define BG_RF95_REG_1E_MODEM_CONFIG2 0x1e
|
||||||
|
#define BG_RF95_REG_1F_SYMB_TIMEOUT_LSB 0x1f
|
||||||
|
#define BG_RF95_REG_20_PREAMBLE_MSB 0x20
|
||||||
|
#define BG_RF95_REG_21_PREAMBLE_LSB 0x21
|
||||||
|
#define BG_RF95_REG_22_PAYLOAD_LENGTH 0x22
|
||||||
|
#define BG_RF95_REG_23_MAX_PAYLOAD_LENGTH 0x23
|
||||||
|
#define BG_RF95_REG_24_HOP_PERIOD 0x24
|
||||||
|
#define BG_RF95_REG_25_FIFO_RX_BYTE_ADDR 0x25
|
||||||
|
#define BG_RF95_REG_26_MODEM_CONFIG3 0x26
|
||||||
|
|
||||||
|
#define BG_RF95_REG_40_DIO_MAPPING1 0x40
|
||||||
|
#define BG_RF95_REG_41_DIO_MAPPING2 0x41
|
||||||
|
#define BG_RF95_REG_42_VERSION 0x42
|
||||||
|
|
||||||
|
#define BG_RF95_REG_4B_TCXO 0x4b
|
||||||
|
#define BG_RF95_REG_4D_PA_DAC 0x4d
|
||||||
|
#define BG_RF95_REG_5B_FORMER_TEMP 0x5b
|
||||||
|
#define BG_RF95_REG_61_AGC_REF 0x61
|
||||||
|
#define BG_RF95_REG_62_AGC_THRESH1 0x62
|
||||||
|
#define BG_RF95_REG_63_AGC_THRESH2 0x63
|
||||||
|
#define BG_RF95_REG_64_AGC_THRESH3 0x64
|
||||||
|
|
||||||
|
// BG_RF95_REG_01_OP_MODE 0x01
|
||||||
|
#define BG_RF95_LONG_RANGE_MODE 0x80
|
||||||
|
#define BG_RF95_ACCESS_SHARED_REG 0x40
|
||||||
|
#define BG_RF95_MODE 0x07
|
||||||
|
#define BG_RF95_MODE_SLEEP 0x00
|
||||||
|
#define BG_RF95_MODE_STDBY 0x01
|
||||||
|
#define BG_RF95_MODE_FSTX 0x02
|
||||||
|
#define BG_RF95_MODE_TX 0x03
|
||||||
|
#define BG_RF95_MODE_FSRX 0x04
|
||||||
|
#define BG_RF95_MODE_RXCONTINUOUS 0x05
|
||||||
|
#define BG_RF95_MODE_RXSINGLE 0x06
|
||||||
|
#define BG_RF95_MODE_CAD 0x07
|
||||||
|
|
||||||
|
// BG_RF95_REG_09_PA_CONFIG 0x09
|
||||||
|
#define BG_RF95_PA_SELECT 0x80
|
||||||
|
#define BG_RF95_MAX_POWER 0x70
|
||||||
|
#define BG_RF95_OUTPUT_POWER 0x0f
|
||||||
|
|
||||||
|
// BG_RF95_REG_0A_PA_RAMP 0x0a
|
||||||
|
#define BG_RF95_LOW_PN_TX_PLL_OFF 0x10
|
||||||
|
#define BG_RF95_PA_RAMP 0x0f
|
||||||
|
#define BG_RF95_PA_RAMP_3_4MS 0x00
|
||||||
|
#define BG_RF95_PA_RAMP_2MS 0x01
|
||||||
|
#define BG_RF95_PA_RAMP_1MS 0x02
|
||||||
|
#define BG_RF95_PA_RAMP_500US 0x03
|
||||||
|
#define BG_RF95_PA_RAMP_250US 0x0
|
||||||
|
#define BG_RF95_PA_RAMP_125US 0x05
|
||||||
|
#define BG_RF95_PA_RAMP_100US 0x06
|
||||||
|
#define BG_RF95_PA_RAMP_62US 0x07
|
||||||
|
#define BG_RF95_PA_RAMP_50US 0x08
|
||||||
|
#define BG_RF95_PA_RAMP_40US 0x09
|
||||||
|
#define BG_RF95_PA_RAMP_31US 0x0a
|
||||||
|
#define BG_RF95_PA_RAMP_25US 0x0b
|
||||||
|
#define BG_RF95_PA_RAMP_20US 0x0c
|
||||||
|
#define BG_RF95_PA_RAMP_15US 0x0d
|
||||||
|
#define BG_RF95_PA_RAMP_12US 0x0e
|
||||||
|
#define BG_RF95_PA_RAMP_10US 0x0f
|
||||||
|
|
||||||
|
// BG_RF95_REG_0B_OCP 0x0b
|
||||||
|
#define BG_RF95_OCP_ON 0x20
|
||||||
|
#define BG_RF95_OCP_TRIM 0x1f
|
||||||
|
|
||||||
|
// BG_RF95_REG_0C_LNA 0x0c
|
||||||
|
#define BG_RF95_LNA_GAIN 0xe0
|
||||||
|
#define BG_RF95_LNA_BOOST 0x03
|
||||||
|
#define BG_RF95_LNA_BOOST_DEFAULT 0x00
|
||||||
|
#define BG_RF95_LNA_BOOST_150PC 0x11
|
||||||
|
|
||||||
|
// BG_RF95_REG_11_IRQ_FLAGS_MASK 0x11
|
||||||
|
#define BG_RF95_RX_TIMEOUT_MASK 0x80
|
||||||
|
#define BG_RF95_RX_DONE_MASK 0x40
|
||||||
|
#define BG_RF95_PAYLOAD_CRC_ERROR_MASK 0x20
|
||||||
|
#define BG_RF95_VALID_HEADER_MASK 0x10
|
||||||
|
#define BG_RF95_TX_DONE_MASK 0x08
|
||||||
|
#define BG_RF95_CAD_DONE_MASK 0x04
|
||||||
|
#define BG_RF95_FHSS_CHANGE_CHANNEL_MASK 0x02
|
||||||
|
#define BG_RF95_CAD_DETECTED_MASK 0x01
|
||||||
|
|
||||||
|
// BG_RF95_REG_12_IRQ_FLAGS 0x12
|
||||||
|
#define BG_RF95_RX_TIMEOUT 0x80
|
||||||
|
#define BG_RF95_RX_DONE 0x40
|
||||||
|
#define BG_RF95_PAYLOAD_CRC_ERROR 0x20
|
||||||
|
#define BG_RF95_VALID_HEADER 0x10
|
||||||
|
#define BG_RF95_TX_DONE 0x08
|
||||||
|
#define BG_RF95_CAD_DONE 0x04
|
||||||
|
#define BG_RF95_FHSS_CHANGE_CHANNEL 0x02
|
||||||
|
#define BG_RF95_CAD_DETECTED 0x01
|
||||||
|
|
||||||
|
// BG_RF95_REG_18_MODEM_STAT 0x18
|
||||||
|
#define BG_RF95_RX_CODING_RATE 0xe0
|
||||||
|
#define BG_RF95_MODEM_STATUS_CLEAR 0x10
|
||||||
|
#define BG_RF95_MODEM_STATUS_HEADER_INFO_VALID 0x08
|
||||||
|
#define BG_RF95_MODEM_STATUS_RX_ONGOING 0x04
|
||||||
|
#define BG_RF95_MODEM_STATUS_SIGNAL_SYNCHRONIZED 0x02
|
||||||
|
#define BG_RF95_MODEM_STATUS_SIGNAL_DETECTED 0x01
|
||||||
|
|
||||||
|
// BG_RF95_REG_1C_HOP_CHANNEL 0x1c
|
||||||
|
#define BG_RF95_PLL_TIMEOUT 0x80
|
||||||
|
#define BG_RF95_RX_PAYLOAD_CRC_IS_ON 0x40
|
||||||
|
#define BG_RF95_FHSS_PRESENT_CHANNEL 0x3f
|
||||||
|
|
||||||
|
// BG_RF95_REG_1D_MODEM_CONFIG1 0x1d
|
||||||
|
#define BG_RF95_BW 0xc0
|
||||||
|
#define BG_RF95_BW_125KHZ 0x00
|
||||||
|
#define BG_RF95_BW_250KHZ 0x40
|
||||||
|
#define BG_RF95_BW_500KHZ 0x80
|
||||||
|
#define BG_RF95_BW_RESERVED 0xc0
|
||||||
|
#define BG_RF95_CODING_RATE 0x38
|
||||||
|
#define BG_RF95_CODING_RATE_4_5 0x00
|
||||||
|
#define BG_RF95_CODING_RATE_4_6 0x08
|
||||||
|
#define BG_RF95_CODING_RATE_4_7 0x10
|
||||||
|
#define BG_RF95_CODING_RATE_4_8 0x18
|
||||||
|
#define BG_RF95_IMPLICIT_HEADER_MODE_ON 0x04
|
||||||
|
#define BG_RF95_RX_PAYLOAD_CRC_ON 0x02
|
||||||
|
#define BG_RF95_LOW_DATA_RATE_OPTIMIZE 0x01
|
||||||
|
|
||||||
|
// BG_RF95_REG_1E_MODEM_CONFIG2 0x1e
|
||||||
|
#define BG_RF95_SPREADING_FACTOR 0xf0
|
||||||
|
#define BG_RF95_SPREADING_FACTOR_64CPS 0x60
|
||||||
|
#define BG_RF95_SPREADING_FACTOR_128CPS 0x70
|
||||||
|
#define BG_RF95_SPREADING_FACTOR_256CPS 0x80
|
||||||
|
#define BG_RF95_SPREADING_FACTOR_512CPS 0x90
|
||||||
|
#define BG_RF95_SPREADING_FACTOR_1024CPS 0xa0
|
||||||
|
#define BG_RF95_SPREADING_FACTOR_2048CPS 0xb0
|
||||||
|
#define BG_RF95_SPREADING_FACTOR_4096CPS 0xc0
|
||||||
|
#define BG_RF95_TX_CONTINUOUS_MOE 0x08
|
||||||
|
#define BG_RF95_AGC_AUTO_ON 0x04
|
||||||
|
#define BG_RF95_SYM_TIMEOUT_MSB 0x03
|
||||||
|
|
||||||
|
// BG_RF95_REG_4D_PA_DAC 0x4d
|
||||||
|
#define BG_RF95_PA_DAC_DISABLE 0x04
|
||||||
|
#define BG_RF95_PA_DAC_ENABLE 0x07
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
/// \class BG_RF95 BG_RF95.h <BG_RF95.h>
|
||||||
|
/// \brief Driver to send and receive unaddressed, unreliable datagrams via a LoRa
|
||||||
|
/// capable radio transceiver.
|
||||||
|
///
|
||||||
|
/// For Semtech SX1276/77/78/79 and HopeRF RF95/96/97/98 and other similar LoRa capable radios.
|
||||||
|
/// Based on http://www.hoperf.com/upload/rf/RFM95_96_97_98W.pdf
|
||||||
|
/// and http://www.hoperf.cn/upload/rfchip/RF96_97_98.pdf
|
||||||
|
/// and http://www.semtech.com/images/datasheet/LoraDesignGuide_STD.pdf
|
||||||
|
/// and http://www.semtech.com/images/datasheet/sx1276.pdf
|
||||||
|
/// and http://www.semtech.com/images/datasheet/sx1276_77_78_79.pdf
|
||||||
|
/// FSK/GFSK/OOK modes are not (yet) supported.
|
||||||
|
///
|
||||||
|
/// Works with
|
||||||
|
/// - the excellent MiniWirelessLoRa from Anarduino http://www.anarduino.com/miniwireless
|
||||||
|
/// - The excellent Modtronix inAir4 http://modtronix.com/inair4.html
|
||||||
|
/// and inAir9 modules http://modtronix.com/inair9.html.
|
||||||
|
/// - the excellent Rocket Scream Mini Ultra Pro with the RFM95W
|
||||||
|
/// http://www.rocketscream.com/blog/product/mini-ultra-pro-with-radio/
|
||||||
|
/// - Lora1276 module from NiceRF http://www.nicerf.com/product_view.aspx?id=99
|
||||||
|
/// - Adafruit Feather M0 with RFM95
|
||||||
|
///
|
||||||
|
/// \par Overview
|
||||||
|
///
|
||||||
|
/// This class provides basic functions for sending and receiving unaddressed,
|
||||||
|
/// unreliable datagrams of arbitrary length to 251 octets per packet.
|
||||||
|
///
|
||||||
|
/// Manager classes may use this class to implement reliable, addressed datagrams and streams,
|
||||||
|
/// mesh routers, repeaters, translators etc.
|
||||||
|
///
|
||||||
|
/// Naturally, for any 2 radios to communicate that must be configured to use the same frequency and
|
||||||
|
/// modulation scheme.
|
||||||
|
///
|
||||||
|
/// This Driver provides an object-oriented interface for sending and receiving data messages with Hope-RF
|
||||||
|
/// RFM95/96/97/98(W), Semtech SX1276/77/78/79 and compatible radio modules in LoRa mode.
|
||||||
|
///
|
||||||
|
/// The Hope-RF (http://www.hoperf.com) RFM95/96/97/98(W) and Semtech SX1276/77/78/79 is a low-cost ISM transceiver
|
||||||
|
/// chip. It supports FSK, GFSK, OOK over a wide range of frequencies and
|
||||||
|
/// programmable data rates, and it also supports the proprietary LoRA (Long Range) mode, which
|
||||||
|
/// is the only mode supported in this RadioHead driver.
|
||||||
|
///
|
||||||
|
/// This Driver provides functions for sending and receiving messages of up
|
||||||
|
/// to 251 octets on any frequency supported by the radio, in a range of
|
||||||
|
/// predefined Bandwidths, Spreading Factors and Coding Rates. Frequency can be set with
|
||||||
|
/// 61Hz precision to any frequency from 240.0MHz to 960.0MHz. Caution: most modules only support a more limited
|
||||||
|
/// range of frequencies due to antenna tuning.
|
||||||
|
///
|
||||||
|
/// Up to 2 modules can be connected to an Arduino (3 on a Mega),
|
||||||
|
/// permitting the construction of translators and frequency changers, etc.
|
||||||
|
///
|
||||||
|
/// Support for other features such as transmitter power control etc is
|
||||||
|
/// also provided.
|
||||||
|
///
|
||||||
|
/// Tested on MinWirelessLoRa with arduino-1.0.5
|
||||||
|
/// on OpenSuSE 13.1.
|
||||||
|
/// Also tested with Teensy3.1, Modtronix inAir4 and Arduino 1.6.5 on OpenSuSE 13.1
|
||||||
|
///
|
||||||
|
/// \par Packet Format
|
||||||
|
///
|
||||||
|
/// All messages sent and received by this BG_RF95 Driver conform to this packet format:
|
||||||
|
///
|
||||||
|
/// - LoRa mode:
|
||||||
|
/// - 8 symbol PREAMBLE
|
||||||
|
/// - Explicit header with header CRC (handled internally by the radio)
|
||||||
|
/// - 4 octets HEADER: (TO, FROM, ID, FLAGS)
|
||||||
|
/// - 0 to 251 octets DATA
|
||||||
|
/// - CRC (handled internally by the radio)
|
||||||
|
///
|
||||||
|
/// \par Connecting RFM95/96/97/98 and Semtech SX1276/77/78/79 to Arduino
|
||||||
|
///
|
||||||
|
/// We tested with Anarduino MiniWirelessLoRA, which is an Arduino Duemilanove compatible with a RFM96W
|
||||||
|
/// module on-board. Therefore it needs no connections other than the USB
|
||||||
|
/// programming connection and an antenna to make it work.
|
||||||
|
///
|
||||||
|
/// If you have a bare RFM95/96/97/98 that you want to connect to an Arduino, you
|
||||||
|
/// might use these connections (untested): CAUTION: you must use a 3.3V type
|
||||||
|
/// Arduino, otherwise you will also need voltage level shifters between the
|
||||||
|
/// Arduino and the RFM95. CAUTION, you must also ensure you connect an
|
||||||
|
/// antenna.
|
||||||
|
///
|
||||||
|
/// \code
|
||||||
|
/// Arduino RFM95/96/97/98
|
||||||
|
/// GND----------GND (ground in)
|
||||||
|
/// 3V3----------3.3V (3.3V in)
|
||||||
|
///
|
||||||
|
|
||||||
|
/// SS pin D10----------NSS (CS chip select in)
|
||||||
|
/// SCK pin D13----------SCK (SPI clock in)
|
||||||
|
/// MOSI pin D11----------MOSI (SPI Data in)
|
||||||
|
/// MISO pin D12----------MISO (SPI Data out)
|
||||||
|
/// \endcode
|
||||||
|
/// With these connections, you can then use the default constructor BG_RF95().
|
||||||
|
/// You can override the default settings for the SS pin and the interrupt in
|
||||||
|
/// the BG_RF95 constructor if you wish to connect the slave select SS to other
|
||||||
|
/// than the normal one for your Arduino (D10 for Diecimila, Uno etc and D53
|
||||||
|
/// for Mega) or the interrupt request to other than pin D2 (Caution,
|
||||||
|
/// different processors have different constraints as to the pins available
|
||||||
|
/// for interrupts).
|
||||||
|
///
|
||||||
|
/// You can connect a Modtronix inAir4 or inAir9 directly to a 3.3V part such as a Teensy 3.1 like
|
||||||
|
/// this (tested).
|
||||||
|
/// \code
|
||||||
|
/// Teensy inAir4 inAir9
|
||||||
|
/// GND----------GND (ground in)
|
||||||
|
/// 3V3----------3.3V (3.3V in)
|
||||||
|
/// interrupt 0 pin D2-----------D00 (interrupt request out)
|
||||||
|
/// SS pin D10----------CS (CS chip select in)
|
||||||
|
/// SCK pin D13----------CK (SPI clock in)
|
||||||
|
/// MOSI pin D11----------SI (SPI Data in)
|
||||||
|
/// MISO pin D12----------SO (SPI Data out)
|
||||||
|
/// \endcode
|
||||||
|
/// With these connections, you can then use the default constructor BG_RF95().
|
||||||
|
/// you must also set the transmitter power with useRFO:
|
||||||
|
/// driver.setTxPower(13, true);
|
||||||
|
///
|
||||||
|
/// Note that if you are using Modtronix inAir4 or inAir9,or any other module which uses the
|
||||||
|
/// transmitter RFO pins and not the PA_BOOST pins
|
||||||
|
/// that you must configure the power transmitter power for -1 to 14 dBm and with useRFO true.
|
||||||
|
/// Failure to do that will result in extremely low transmit powers.
|
||||||
|
///
|
||||||
|
/// If you have an Arduino M0 Pro from arduino.org,
|
||||||
|
/// you should note that you cannot use Pin 2 for the interrupt line
|
||||||
|
/// (Pin 2 is for the NMI only). The same comments apply to Pin 4 on Arduino Zero from arduino.cc.
|
||||||
|
/// Instead you can use any other pin (we use Pin 3) and initialise RH_RF69 like this:
|
||||||
|
/// \code
|
||||||
|
/// // Slave Select is pin 10, interrupt is Pin 3
|
||||||
|
/// BG_RF95 driver(10, 3);
|
||||||
|
/// \endcode
|
||||||
|
///
|
||||||
|
/// If you have a Rocket Scream Mini Ultra Pro with the RFM95W:
|
||||||
|
/// - Ensure you have Arduino SAMD board support 1.6.5 or later in Arduino IDE 1.6.8 or later.
|
||||||
|
/// - The radio SS is hardwired to pin D5 and the DIO0 interrupt to pin D2,
|
||||||
|
/// so you need to initialise the radio like this:
|
||||||
|
/// \code
|
||||||
|
/// BG_RF95 driver(5, 2);
|
||||||
|
/// \endcode
|
||||||
|
/// - The name of the serial port on that board is 'SerialUSB', not 'Serial', so this may be helpful at the top of our
|
||||||
|
/// sample sketches:
|
||||||
|
/// \code
|
||||||
|
/// #define Serial SerialUSB
|
||||||
|
/// \endcode
|
||||||
|
/// - You also need this in setup before radio initialisation
|
||||||
|
/// \code
|
||||||
|
/// // Ensure serial flash is not interfering with radio communication on SPI bus
|
||||||
|
/// pinMode(4, OUTPUT);
|
||||||
|
/// digitalWrite(4, HIGH);
|
||||||
|
/// \endcode
|
||||||
|
/// - and if you have a 915MHz part, you need this after driver/manager intitalisation:
|
||||||
|
/// \code
|
||||||
|
/// rf95.setFrequency(915.0);
|
||||||
|
/// \endcode
|
||||||
|
/// which adds up to modifying sample sketches something like:
|
||||||
|
/// \code
|
||||||
|
/// #include <SPI.h>
|
||||||
|
/// #include <BG_RF95.h>
|
||||||
|
/// BG_RF95 rf95(5, 2); // Rocket Scream Mini Ultra Pro with the RFM95W
|
||||||
|
/// #define Serial SerialUSB
|
||||||
|
///
|
||||||
|
/// void setup()
|
||||||
|
/// {
|
||||||
|
/// // Ensure serial flash is not interfering with radio communication on SPI bus
|
||||||
|
/// pinMode(4, OUTPUT);
|
||||||
|
/// digitalWrite(4, HIGH);
|
||||||
|
///
|
||||||
|
/// Serial.begin(9600);
|
||||||
|
/// while (!Serial) ; // Wait for serial port to be available
|
||||||
|
/// if (!rf95.init())
|
||||||
|
/// Serial.println("init failed");
|
||||||
|
/// rf95.setFrequency(915.0);
|
||||||
|
/// }
|
||||||
|
/// ...
|
||||||
|
/// \endcode
|
||||||
|
///
|
||||||
|
/// For Adafruit Feather M0 with RFM95, construct the driver like this:
|
||||||
|
/// \code
|
||||||
|
/// BG_RF95 rf95(8, 3);
|
||||||
|
/// \endcode
|
||||||
|
///
|
||||||
|
/// It is possible to have 2 or more radios connected to one Arduino, provided
|
||||||
|
/// each radio has its own SS and interrupt line (SCK, SDI and SDO are common
|
||||||
|
/// to all radios)
|
||||||
|
///
|
||||||
|
/// Caution: on some Arduinos such as the Mega 2560, if you set the slave
|
||||||
|
/// select pin to be other than the usual SS pin (D53 on Mega 2560), you may
|
||||||
|
/// need to set the usual SS pin to be an output to force the Arduino into SPI
|
||||||
|
/// master mode.
|
||||||
|
///
|
||||||
|
/// Caution: Power supply requirements of the RFM module may be relevant in some circumstances:
|
||||||
|
/// RFM95/96/97/98 modules are capable of pulling 120mA+ at full power, where Arduino's 3.3V line can
|
||||||
|
/// give 50mA. You may need to make provision for alternate power supply for
|
||||||
|
/// the RFM module, especially if you wish to use full transmit power, and/or you have
|
||||||
|
/// other shields demanding power. Inadequate power for the RFM is likely to cause symptoms such as:
|
||||||
|
/// - reset's/bootups terminate with "init failed" messages
|
||||||
|
/// - random termination of communication after 5-30 packets sent/received
|
||||||
|
/// - "fake ok" state, where initialization passes fluently, but communication doesn't happen
|
||||||
|
/// - shields hang Arduino boards, especially during the flashing
|
||||||
|
///
|
||||||
|
/// \par Interrupts
|
||||||
|
///
|
||||||
|
/// The BG_RF95 driver uses interrupts to react to events in the RFM module,
|
||||||
|
/// such as the reception of a new packet, or the completion of transmission
|
||||||
|
/// of a packet. The BG_RF95 driver interrupt service routine reads status from
|
||||||
|
/// and writes data to the the RFM module via the SPI interface. It is very
|
||||||
|
/// important therefore, that if you are using the BG_RF95 driver with another
|
||||||
|
/// SPI based deviced, that you disable interrupts while you transfer data to
|
||||||
|
/// and from that other device. Use cli() to disable interrupts and sei() to
|
||||||
|
/// reenable them.
|
||||||
|
///
|
||||||
|
/// \par Memory
|
||||||
|
///
|
||||||
|
/// The BG_RF95 driver requires non-trivial amounts of memory. The sample
|
||||||
|
/// programs all compile to about 8kbytes each, which will fit in the
|
||||||
|
/// flash proram memory of most Arduinos. However, the RAM requirements are
|
||||||
|
/// more critical. Therefore, you should be vary sparing with RAM use in
|
||||||
|
/// programs that use the BG_RF95 driver.
|
||||||
|
///
|
||||||
|
/// It is often hard to accurately identify when you are hitting RAM limits on Arduino.
|
||||||
|
/// The symptoms can include:
|
||||||
|
/// - Mysterious crashes and restarts
|
||||||
|
/// - Changes in behaviour when seemingly unrelated changes are made (such as adding print() statements)
|
||||||
|
/// - Hanging
|
||||||
|
/// - Output from Serial.print() not appearing
|
||||||
|
///
|
||||||
|
/// \par Range
|
||||||
|
///
|
||||||
|
/// We have made some simple range tests under the following conditions:
|
||||||
|
/// - rf95_client base station connected to a VHF discone antenna at 8m height above ground
|
||||||
|
/// - rf95_server mobile connected to 17.3cm 1/4 wavelength antenna at 1m height, no ground plane.
|
||||||
|
/// - Both configured for 13dBm, 434MHz, Bw = 125 kHz, Cr = 4/8, Sf = 4096chips/symbol, CRC on. Slow+long range
|
||||||
|
/// - Minimum reported RSSI seen for successful comms was about -91
|
||||||
|
/// - Range over flat ground through heavy trees and vegetation approx 2km.
|
||||||
|
/// - At 20dBm (100mW) otherwise identical conditions approx 3km.
|
||||||
|
/// - At 20dBm, along salt water flat sandy beach, 3.2km.
|
||||||
|
///
|
||||||
|
/// It should be noted that at this data rate, a 12 octet message takes 2 seconds to transmit.
|
||||||
|
///
|
||||||
|
/// At 20dBm (100mW) with Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on.
|
||||||
|
/// (Default medium range) in the conditions described above.
|
||||||
|
/// - Range over flat ground through heavy trees and vegetation approx 2km.
|
||||||
|
///
|
||||||
|
/// \par Transmitter Power
|
||||||
|
///
|
||||||
|
/// You can control the transmitter power on the RF transceiver
|
||||||
|
/// with the BG_RF95::setTxPower() function. The argument can be any of
|
||||||
|
/// +5 to +23 (for modules that use PA_BOOST)
|
||||||
|
/// -1 to +14 (for modules that use RFO transmitter pin)
|
||||||
|
/// The default is 13. Eg:
|
||||||
|
/// \code
|
||||||
|
/// driver.setTxPower(10); // use PA_BOOST transmitter pin
|
||||||
|
/// driver.setTxPower(10, true); // use PA_RFO pin transmitter pin
|
||||||
|
/// \endcode
|
||||||
|
///
|
||||||
|
/// We have made some actual power measurements against
|
||||||
|
/// programmed power for Anarduino MiniWirelessLoRa (which has RFM96W-433Mhz installed)
|
||||||
|
/// - MiniWirelessLoRa RFM96W-433Mhz, USB power
|
||||||
|
/// - 30cm RG316 soldered direct to RFM96W module ANT and GND
|
||||||
|
/// - SMA connector
|
||||||
|
/// - 12db attenuator
|
||||||
|
/// - SMA connector
|
||||||
|
/// - MiniKits AD8307 HF/VHF Power Head (calibrated against Rohde&Schwartz 806.2020 test set)
|
||||||
|
/// - Tektronix TDS220 scope to measure the Vout from power head
|
||||||
|
/// \code
|
||||||
|
/// Program power Measured Power
|
||||||
|
/// dBm dBm
|
||||||
|
/// 5 5
|
||||||
|
/// 7 7
|
||||||
|
/// 9 8
|
||||||
|
/// 11 11
|
||||||
|
/// 13 13
|
||||||
|
/// 15 15
|
||||||
|
/// 17 16
|
||||||
|
/// 19 18
|
||||||
|
/// 20 20
|
||||||
|
/// 21 21
|
||||||
|
/// 22 22
|
||||||
|
/// 23 23
|
||||||
|
/// \endcode
|
||||||
|
///
|
||||||
|
/// We have also measured the actual power output from a Modtronix inAir4 http://modtronix.com/inair4.html
|
||||||
|
/// connected to a Teensy 3.1:
|
||||||
|
/// Teensy 3.1 this is a 3.3V part, connected directly to:
|
||||||
|
/// Modtronix inAir4 with SMA antenna connector, connected as above:
|
||||||
|
/// 10cm SMA-SMA cable
|
||||||
|
/// - MiniKits AD8307 HF/VHF Power Head (calibrated against Rohde&Schwartz 806.2020 test set)
|
||||||
|
/// - Tektronix TDS220 scope to measure the Vout from power head
|
||||||
|
/// \code
|
||||||
|
/// Program power Measured Power
|
||||||
|
/// dBm dBm
|
||||||
|
/// -1 0
|
||||||
|
/// 1 2
|
||||||
|
/// 3 4
|
||||||
|
/// 5 7
|
||||||
|
/// 7 10
|
||||||
|
/// 9 13
|
||||||
|
/// 11 14.2
|
||||||
|
/// 13 15
|
||||||
|
/// 14 16
|
||||||
|
/// \endcode
|
||||||
|
/// (Caution: we dont claim laboratory accuracy for these power measurements)
|
||||||
|
/// You would not expect to get anywhere near these powers to air with a simple 1/4 wavelength wire antenna.
|
||||||
|
class BG_RF95 : public RHSPIDriver
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// \brief Defines register values for a set of modem configuration registers
|
||||||
|
///
|
||||||
|
/// Defines register values for a set of modem configuration registers
|
||||||
|
/// that can be passed to setModemRegisters() if none of the choices in
|
||||||
|
/// ModemConfigChoice suit your need setModemRegisters() writes the
|
||||||
|
/// register values from this structure to the appropriate registers
|
||||||
|
/// to set the desired spreading factor, coding rate and bandwidth
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t reg_1d; ///< Value for register BG_RF95_REG_1D_MODEM_CONFIG1
|
||||||
|
uint8_t reg_1e; ///< Value for register BG_RF95_REG_1E_MODEM_CONFIG2
|
||||||
|
uint8_t reg_26; ///< Value for register BG_RF95_REG_26_MODEM_CONFIG3
|
||||||
|
} ModemConfig;
|
||||||
|
|
||||||
|
/// Choices for setModemConfig() for a selected subset of common
|
||||||
|
/// data rates. If you need another configuration,
|
||||||
|
/// determine the necessary settings and call setModemRegisters() with your
|
||||||
|
/// desired settings. It might be helpful to use the LoRa calculator mentioned in
|
||||||
|
/// http://www.semtech.com/images/datasheet/LoraDesignGuide_STD.pdf
|
||||||
|
/// These are indexes into MODEM_CONFIG_TABLE. We strongly recommend you use these symbolic
|
||||||
|
/// definitions and not their integer equivalents: its possible that new values will be
|
||||||
|
/// introduced in later versions (though we will try to avoid it).
|
||||||
|
/// Caution: if you are using slow packet rates and long packets with RHReliableDatagram or subclasses
|
||||||
|
/// you may need to change the RHReliableDatagram timeout for reliable operations.
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
Bw125Cr45Sf128 = 0, ///< Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on. Default medium range
|
||||||
|
Bw500Cr45Sf128, ///< Bw = 500 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on. Fast+short range
|
||||||
|
Bw31_25Cr48Sf512, ///< Bw = 31.25 kHz, Cr = 4/8, Sf = 512chips/symbol, CRC on. Slow+long range
|
||||||
|
Bw125Cr48Sf4096, ///< Bw = 125 kHz, Cr = 4/8, Sf = 4096chips/symbol, CRC on. Slow+long range
|
||||||
|
Bw125Cr45Sf4096, ///< APRS
|
||||||
|
} ModemConfigChoice;
|
||||||
|
|
||||||
|
/// Constructor. You can have multiple instances, but each instance must have its own
|
||||||
|
/// interrupt and slave select pin. After constructing, you must call init() to initialise the interface
|
||||||
|
/// and the radio module. A maximum of 3 instances can co-exist on one processor, provided there are sufficient
|
||||||
|
/// distinct interrupt lines, one for each instance.
|
||||||
|
/// \param[in] slaveSelectPin the Arduino pin number of the output to use to select the RH_RF22 before
|
||||||
|
/// accessing it. Defaults to the normal SS pin for your Arduino (D10 for Diecimila, Uno etc, D53 for Mega, D10 for Maple)
|
||||||
|
/// \param[in] interruptPin The interrupt Pin number that is connected to the RFM DIO0 interrupt line.
|
||||||
|
/// Defaults to pin 2, as required by Anarduino MinWirelessLoRa module.
|
||||||
|
/// Caution: You must specify an interrupt capable pin.
|
||||||
|
/// On many Arduino boards, there are limitations as to which pins may be used as interrupts.
|
||||||
|
/// On Leonardo pins 0, 1, 2 or 3. On Mega2560 pins 2, 3, 18, 19, 20, 21. On Due and Teensy, any digital pin.
|
||||||
|
/// On Arduino Zero from arduino.cc, any digital pin other than 4.
|
||||||
|
/// On Arduino M0 Pro from arduino.org, any digital pin other than 2.
|
||||||
|
/// On other Arduinos pins 2 or 3.
|
||||||
|
/// See http://arduino.cc/en/Reference/attachInterrupt for more details.
|
||||||
|
/// On Chipkit Uno32, pins 38, 2, 7, 8, 35.
|
||||||
|
/// On other boards, any digital pin may be used.
|
||||||
|
/// \param[in] spi Pointer to the SPI interface object to use.
|
||||||
|
/// Defaults to the standard Arduino hardware SPI interface
|
||||||
|
BG_RF95(uint8_t slaveSelectPin = SS, uint8_t interruptPin = 2, RHGenericSPI& spi = hardware_spi);
|
||||||
|
|
||||||
|
/// Initialise the Driver transport hardware and software.
|
||||||
|
/// Make sure the Driver is properly configured before calling init().
|
||||||
|
/// \return true if initialisation succeeded.
|
||||||
|
virtual bool init();
|
||||||
|
|
||||||
|
/// Prints the value of all chip registers
|
||||||
|
/// to the Serial device if RH_HAVE_SERIAL is defined for the current platform
|
||||||
|
/// For debugging purposes only.
|
||||||
|
/// \return true on success
|
||||||
|
bool printRegisters();
|
||||||
|
|
||||||
|
/// Sets all the registered required to configure the data modem in the RF95/96/97/98, including the bandwidth,
|
||||||
|
/// spreading factor etc. You can use this to configure the modem with custom configurations if none of the
|
||||||
|
/// canned configurations in ModemConfigChoice suit you.
|
||||||
|
/// \param[in] config A ModemConfig structure containing values for the modem configuration registers.
|
||||||
|
void setModemRegisters(const ModemConfig* config);
|
||||||
|
|
||||||
|
/// Select one of the predefined modem configurations. If you need a modem configuration not provided
|
||||||
|
/// here, use setModemRegisters() with your own ModemConfig.
|
||||||
|
/// \param[in] index The configuration choice.
|
||||||
|
/// \return true if index is a valid choice.
|
||||||
|
bool setModemConfig(ModemConfigChoice index);
|
||||||
|
|
||||||
|
/// Tests whether a new message is available
|
||||||
|
/// from the Driver.
|
||||||
|
/// On most drivers, this will also put the Driver into RHModeRx mode until
|
||||||
|
/// a message is actually received by the transport, when it wil be returned to RHModeIdle.
|
||||||
|
/// This can be called multiple times in a timeout loop
|
||||||
|
/// \return true if a new, complete, error-free uncollected message is available to be retreived by recv()
|
||||||
|
virtual bool available();
|
||||||
|
|
||||||
|
/// Turns the receiver on if it not already on.
|
||||||
|
/// If there is a valid message available, copy it to buf and return true
|
||||||
|
/// else return false.
|
||||||
|
/// If a message is copied, *len is set to the length (Caution, 0 length messages are permitted).
|
||||||
|
/// You should be sure to call this function frequently enough to not miss any messages
|
||||||
|
/// It is recommended that you call it in your main loop.
|
||||||
|
/// \param[in] buf Location to copy the received message
|
||||||
|
/// \param[in,out] len Pointer to available space in buf. Set to the actual number of octets copied.
|
||||||
|
/// \return true if a valid message was copied to buf
|
||||||
|
virtual bool recv(uint8_t* buf, uint8_t* len);
|
||||||
|
|
||||||
|
// added BG APRS Packets are sent with 3-Byte header
|
||||||
|
// turn on promiscuous
|
||||||
|
virtual bool recvAPRS(uint8_t* buf, uint8_t* len);
|
||||||
|
|
||||||
|
/// Waits until any previous transmit packet is finished being transmitted with waitPacketSent().
|
||||||
|
/// Then loads a message into the transmitter and starts the transmitter. Note that a message length
|
||||||
|
/// of 0 is permitted.
|
||||||
|
/// \param[in] data Array of data to be sent
|
||||||
|
/// \param[in] len Number of bytes of data to send
|
||||||
|
/// \return true if the message length was valid and it was correctly queued for transmit
|
||||||
|
virtual bool send(const uint8_t* data, uint8_t len);
|
||||||
|
|
||||||
|
// Send APRS Header Format
|
||||||
|
virtual bool sendAPRS(const uint8_t* data, uint8_t len);
|
||||||
|
|
||||||
|
virtual uint8_t lastSNR();
|
||||||
|
|
||||||
|
/// Sets the length of the preamble
|
||||||
|
/// in bytes.
|
||||||
|
/// Caution: this should be set to the same
|
||||||
|
/// value on all nodes in your network. Default is 8.
|
||||||
|
/// Sets the message preamble length in BG_RF95_REG_??_PREAMBLE_?SB
|
||||||
|
/// \param[in] bytes Preamble length in bytes.
|
||||||
|
void setPreambleLength(uint16_t bytes);
|
||||||
|
|
||||||
|
/// Returns the maximum message length
|
||||||
|
/// available in this Driver.
|
||||||
|
/// \return The maximum legal message length
|
||||||
|
virtual uint8_t maxMessageLength();
|
||||||
|
|
||||||
|
/// Sets the transmitter and receiver
|
||||||
|
/// centre frequency.
|
||||||
|
/// \param[in] centre Frequency in MHz. 137.0 to 1020.0. Caution: RFM95/96/97/98 comes in several
|
||||||
|
/// different frequency ranges, and setting a frequency outside that range of your radio will probably not work
|
||||||
|
/// \return true if the selected frquency centre is within range
|
||||||
|
bool setFrequency(float centre);
|
||||||
|
|
||||||
|
/// If current mode is Rx or Tx changes it to Idle. If the transmitter or receiver is running,
|
||||||
|
/// disables them.
|
||||||
|
void setModeIdle();
|
||||||
|
|
||||||
|
/// If current mode is Tx or Idle, changes it to Rx.
|
||||||
|
/// Starts the receiver in the RF95/96/97/98.
|
||||||
|
void setModeRx();
|
||||||
|
|
||||||
|
/// If current mode is Rx or Idle, changes it to Rx. F
|
||||||
|
/// Starts the transmitter in the RF95/96/97/98.
|
||||||
|
void setModeTx();
|
||||||
|
|
||||||
|
/// Sets the transmitter power output level, and configures the transmitter pin.
|
||||||
|
/// Be a good neighbour and set the lowest power level you need.
|
||||||
|
/// Some SX1276/77/78/79 and compatible modules (such as RFM95/96/97/98)
|
||||||
|
/// use the PA_BOOST transmitter pin for high power output (and optionally the PA_DAC)
|
||||||
|
/// while some (such as the Modtronix inAir4 and inAir9)
|
||||||
|
/// use the RFO transmitter pin for lower power but higher efficiency.
|
||||||
|
/// You must set the appropriate power level and useRFO argument for your module.
|
||||||
|
/// Check with your module manufacturer which transmtter pin is used on your module
|
||||||
|
/// to ensure you are setting useRFO correctly.
|
||||||
|
/// Failure to do so will result in very low
|
||||||
|
/// transmitter power output.
|
||||||
|
/// Caution: legal power limits may apply in certain countries.
|
||||||
|
/// After init(), the power will be set to 13dBm, with useRFO false (ie PA_BOOST enabled).
|
||||||
|
/// \param[in] power Transmitter power level in dBm. For RFM95/96/97/98 LORA with useRFO false,
|
||||||
|
/// valid values are from +5 to +23.
|
||||||
|
/// For Modtronix inAir4 and inAir9 with useRFO true (ie RFO pins in use),
|
||||||
|
/// valid values are from -1 to 14.
|
||||||
|
/// \param[in] useRFO If true, enables the use of the RFO transmitter pins instead of
|
||||||
|
/// the PA_BOOST pin (false). Choose the correct setting for your module.
|
||||||
|
void setTxPower(int8_t power, bool useRFO = false);
|
||||||
|
|
||||||
|
/// Sets the radio into low-power sleep mode.
|
||||||
|
/// If successful, the transport will stay in sleep mode until woken by
|
||||||
|
/// changing mode it idle, transmit or receive (eg by calling send(), recv(), available() etc)
|
||||||
|
/// Caution: there is a time penalty as the radio takes a finite time to wake from sleep mode.
|
||||||
|
/// \return true if sleep mode was successfully entered.
|
||||||
|
virtual bool sleep();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// This is a low level function to handle the interrupts for one instance of BG_RF95.
|
||||||
|
/// Called automatically by isr*()
|
||||||
|
/// Should not need to be called by user code.
|
||||||
|
void handleInterrupt();
|
||||||
|
|
||||||
|
/// Examine the revceive buffer to determine whether the message is for this node
|
||||||
|
void validateRxBuf();
|
||||||
|
|
||||||
|
/// Clear our local receive buffer
|
||||||
|
void clearRxBuf();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Low level interrupt service routine for device connected to interrupt 0
|
||||||
|
static void isr0();
|
||||||
|
|
||||||
|
/// Low level interrupt service routine for device connected to interrupt 1
|
||||||
|
static void isr1();
|
||||||
|
|
||||||
|
/// Low level interrupt service routine for device connected to interrupt 1
|
||||||
|
static void isr2();
|
||||||
|
|
||||||
|
/// Array of instances connected to interrupts 0 and 1
|
||||||
|
static BG_RF95* _deviceForInterrupt[];
|
||||||
|
|
||||||
|
/// Index of next interrupt number to use in _deviceForInterrupt
|
||||||
|
static uint8_t _interruptCount;
|
||||||
|
|
||||||
|
/// The configured interrupt pin connected to this instance
|
||||||
|
uint8_t _interruptPin;
|
||||||
|
|
||||||
|
/// The index into _deviceForInterrupt[] for this device (if an interrupt is already allocated)
|
||||||
|
/// else 0xff
|
||||||
|
uint8_t _myInterruptIndex;
|
||||||
|
|
||||||
|
/// Number of octets in the buffer
|
||||||
|
volatile uint8_t _bufLen;
|
||||||
|
|
||||||
|
/// The receiver/transmitter buffer
|
||||||
|
uint8_t _buf[BG_RF95_MAX_PAYLOAD_LEN];
|
||||||
|
|
||||||
|
/// True when there is a valid message in the buffer
|
||||||
|
volatile bool _rxBufValid;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @example rf95_client.pde
|
||||||
|
/// @example rf95_server.pde
|
||||||
|
/// @example rf95_reliable_datagram_client.pde
|
||||||
|
/// @example rf95_reliable_datagram_server.pde
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -0,0 +1,603 @@
|
||||||
|
|
||||||
|
// Arduino Tracker for LoRA APRS
|
||||||
|
//
|
||||||
|
// TTGO T-Beam includes GPS module + optional DHT22 (not yet DONE)
|
||||||
|
//
|
||||||
|
// can be used as tracker only, tracker plus weather reports (temperature and humidity) or weather reports station only
|
||||||
|
//
|
||||||
|
// updated from OE1ACM sketch by OE3CJB to enable WX data to be sent via LoRa APRS.
|
||||||
|
// one package is with position and battery voltage
|
||||||
|
// the next is with weather data in APRS format
|
||||||
|
//
|
||||||
|
// licensed under CC BY-NC-SA
|
||||||
|
//
|
||||||
|
// last update: 24.11.2018
|
||||||
|
// modifications: select mode during compilation to select model
|
||||||
|
|
||||||
|
// USER DATA - USE THESE LINES TO MODIFY YOUR PREFERENCES
|
||||||
|
// Your Callsign
|
||||||
|
String Tcall="OE3CJB-7"; //your Call Sign for normal position reports
|
||||||
|
String wxTcall="OE3CJB-7"; //your Call Sign for weather reports
|
||||||
|
|
||||||
|
// Your symbol table and symbol for position reports incl. battery voltage
|
||||||
|
String sTable="/"; //Primer
|
||||||
|
//String sTable="\"; //Alternativ
|
||||||
|
|
||||||
|
// String sSymbol="_"; //symbol code Weather Station
|
||||||
|
// String sSymbol=">"; //symbol code CAR
|
||||||
|
String sSymbol="["; //symbol code RUNNER
|
||||||
|
// String sSymbol="b"; //symbol code BICYCLE
|
||||||
|
// String sSymbol="<"; //symbol code MOTORCYCLE
|
||||||
|
|
||||||
|
// SEND_WX - if true the tracker sends WX reports - needs DHT22 connected at Pin 10
|
||||||
|
// when FIXED_POSITION is false then it sends alternating normal position packets and weather report packets
|
||||||
|
#define SEND_WX false
|
||||||
|
|
||||||
|
// Your symbol table and symbol for weather reports
|
||||||
|
String wxTable="/"; //Primer
|
||||||
|
String wxSymbol="_"; //Symbol Code Weather Station
|
||||||
|
// String wxSymbol="W"; //Symbol Code Weather Station/
|
||||||
|
|
||||||
|
#define FIXED_POSITION false
|
||||||
|
// set to true if you want to use fixed position (position defined below) instead, or to false if you want to use GPS data
|
||||||
|
// also stops sending normal position reports when sending weather reports is active (SEND_WX true)
|
||||||
|
|
||||||
|
#define LATITUDE "4813.62N" // please in APRS notation DDMM.mmN or DDMM.mmS used for FIXED_POSITION
|
||||||
|
#define LONGITUDE "01539.85E" // please in APRS notation DDDMM.mmE or DDDMM.mmW used for FIXED_POSITION
|
||||||
|
// ^^^^^LATITUDE and LONGITUDE only used when FIXED_POSITION is true
|
||||||
|
|
||||||
|
// Tracker setting: use these lines to modify the tracker behaviour
|
||||||
|
#define TXFREQ 433.775 // Transmit frequency in MHz
|
||||||
|
#define TXdbmW 18 // Transmit power in dBm
|
||||||
|
#define TXenablePA 0 // switch internal power amplifier on (1) or off (0)
|
||||||
|
|
||||||
|
// Transmit intervall
|
||||||
|
unsigned long nextTX = 60000L; // Send every 60 secs
|
||||||
|
// unsigned long nextTX = 5000L; // Send every 5 secs - FOR TESTS ONLY - NO CONNECTION TO SERVER PLEASE!!!!
|
||||||
|
|
||||||
|
// STOP EDITING from here on - except you know what you do :-)
|
||||||
|
#define DEBUG false // used for debugging purposes , e.g. turning on special serial or display logging
|
||||||
|
|
||||||
|
//Hardware definitions
|
||||||
|
|
||||||
|
/* for feather32u4
|
||||||
|
#define RFM95_CS 8
|
||||||
|
#define RFM95_RST 4
|
||||||
|
#define RFM95_INT 7
|
||||||
|
*/
|
||||||
|
|
||||||
|
//Variables for DHT22 temperature and humidity sensor
|
||||||
|
int chk;
|
||||||
|
float hum; //Stores humidity value
|
||||||
|
float temp; //Stores temperature value
|
||||||
|
|
||||||
|
//other global Variables
|
||||||
|
String Textzeile1, Textzeile2;
|
||||||
|
|
||||||
|
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
//PINs used for HW extensions
|
||||||
|
|
||||||
|
// Pin for battery voltage -> bei T-Beam ADC1_CHANNEL_7
|
||||||
|
// #define ANALOG_PIN_0 35 // connected to battery
|
||||||
|
|
||||||
|
// Pins for GPS
|
||||||
|
static const int RXPin = 15, TXPin = 12; // changed BG A3 A2
|
||||||
|
static const uint32_t GPSBaud = 9600; //GPS
|
||||||
|
|
||||||
|
const byte TX_en = 0;
|
||||||
|
const byte RX_en = 0; //TX/RX enable 1W modul
|
||||||
|
|
||||||
|
const byte TXLED = 14; //pin number for LED on TX Tracker
|
||||||
|
// const byte GPSLED = 6; // pin gps & Heartbeat
|
||||||
|
// const byte GPSLED1 = 9; // pin gps & Heartbeat
|
||||||
|
|
||||||
|
// Pins for LoRa module
|
||||||
|
const byte lora_PReset = 23; //pin where LoRa device reset line is connected
|
||||||
|
const byte lora_PNSS = 18; //pin number where the NSS line for the LoRa device is connected.
|
||||||
|
// pin 11 MOSI
|
||||||
|
// pin 12 MISO
|
||||||
|
// pin 13 SCLK
|
||||||
|
|
||||||
|
// #define ModemConfig BG_RF95::Bw125Cr45Sf4096
|
||||||
|
|
||||||
|
#define DHTPIN 10 // what pin we're connected to
|
||||||
|
#define DHTTYPE DHT22 // DHT 22 (AM2302)
|
||||||
|
|
||||||
|
// Variables and Constants
|
||||||
|
|
||||||
|
String InputString = ""; //data on buff is copied to this string
|
||||||
|
String Outputstring = "";
|
||||||
|
String outString=""; //The new Output String with GPS Conversion RAW
|
||||||
|
float BattVolts;
|
||||||
|
|
||||||
|
#if (FIXED_POSITION)
|
||||||
|
boolean wx = true;
|
||||||
|
#else
|
||||||
|
boolean wx = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//byte arrays
|
||||||
|
byte lora_TXBUFF[128]; //buffer for packet to send
|
||||||
|
//byte Variables
|
||||||
|
byte lora_TXStart; //start of packet data in TXbuff
|
||||||
|
byte lora_TXEnd; //end of packet data in TXbuff
|
||||||
|
byte lora_FTXOK; //flag, set to 1 if TX OK
|
||||||
|
byte lora_TXPacketType; //type number of packet to send
|
||||||
|
byte lora_TXDestination; //destination address of packet to send
|
||||||
|
byte lora_TXSource; //source address of packet received
|
||||||
|
byte lora_FDeviceError; //flag, set to 1 if RFM98 device error
|
||||||
|
byte lora_TXPacketL; //length of packet to send, includes source, destination and packet type.
|
||||||
|
|
||||||
|
|
||||||
|
unsigned long lastTX = 0L;
|
||||||
|
|
||||||
|
// Includes
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <Adafruit_Sensor.h>
|
||||||
|
#include <SPI.h>
|
||||||
|
#include <BG_RF95.h>
|
||||||
|
// #include <string>
|
||||||
|
|
||||||
|
#include <TinyGPS++.h>
|
||||||
|
// #include <SoftwareSerial.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <DHT.h>
|
||||||
|
#include <driver/adc.h>
|
||||||
|
#include <Wire.h>
|
||||||
|
|
||||||
|
#include <Adafruit_SSD1306.h>
|
||||||
|
#include <splash.h>
|
||||||
|
|
||||||
|
#include "xtest_bw.h"
|
||||||
|
|
||||||
|
#include <Adafruit_GFX.h>
|
||||||
|
#include <Adafruit_SPITFT.h>
|
||||||
|
#include <Adafruit_SPITFT_Macros.h>
|
||||||
|
#include <gfxfont.h>
|
||||||
|
|
||||||
|
static void smartDelay(unsigned long);
|
||||||
|
void recalcGPS(void);
|
||||||
|
void sendpacket(void);
|
||||||
|
void loraSend(byte, byte, byte, byte, byte, long, byte, float);
|
||||||
|
void batt_read(void);
|
||||||
|
void writedisplaytext(String, String, String, int);
|
||||||
|
|
||||||
|
|
||||||
|
#if (SEND_WX)
|
||||||
|
DHT dht(DHTPIN, DHTTYPE); // Initialize DHT sensor for normal 16mhz Arduino
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// SoftwareSerial ss(RXPin, TXPin); // The serial connection to the GPS device
|
||||||
|
HardwareSerial ss(1); // TTGO has HW serial
|
||||||
|
TinyGPSPlus gps; // The TinyGPS++ object
|
||||||
|
|
||||||
|
// checkRX
|
||||||
|
uint8_t buf[BG_RF95_MAX_MESSAGE_LEN];
|
||||||
|
uint8_t len = sizeof(buf);
|
||||||
|
|
||||||
|
// Singleton instance of the radio driver
|
||||||
|
|
||||||
|
BG_RF95 rf95(18, 26); // TTGO T-Beam has NSS @ Pin 18 and Interrupt IO @ Pin26
|
||||||
|
|
||||||
|
// initialize OLED display
|
||||||
|
#define OLED_RESET 4 // not used
|
||||||
|
Adafruit_SSD1306 display(128, 64, &Wire, OLED_RESET);
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
|
||||||
|
pinMode(TXLED, OUTPUT);
|
||||||
|
digitalWrite(TXLED, LOW);
|
||||||
|
Serial.begin(115200);
|
||||||
|
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
|
||||||
|
for(;;); // Don't proceed, loop forever
|
||||||
|
}
|
||||||
|
digitalWrite(TXLED, HIGH);
|
||||||
|
writedisplaytext("Init:","Display OK!","",1000);
|
||||||
|
digitalWrite(TXLED, LOW);
|
||||||
|
Serial.println("Init: Display OK!");
|
||||||
|
if (!rf95.init()) {
|
||||||
|
// Serial.println("init failed");
|
||||||
|
|
||||||
|
writedisplaytext("Init:","RF95 FAILED!",":-(",1000);
|
||||||
|
Serial.println("Init: RF95 FAILED!");
|
||||||
|
for(;;); // Don't proceed, loop forever
|
||||||
|
}
|
||||||
|
|
||||||
|
digitalWrite(TXLED, HIGH);
|
||||||
|
writedisplaytext("Init:","RF95 OK!","",1000);
|
||||||
|
digitalWrite(TXLED, LOW);
|
||||||
|
Serial.println("Init: RF95 OK!");
|
||||||
|
|
||||||
|
#if !(FIXED_POSITION)
|
||||||
|
ss.begin(GPSBaud, SERIAL_8N1, 12, 15); //Startup HW serial for GPS
|
||||||
|
#endif // #if !(FIXED_POSITION)
|
||||||
|
digitalWrite(TXLED, HIGH);
|
||||||
|
writedisplaytext("Init:","GPS Serial OK!","",1000);
|
||||||
|
digitalWrite(TXLED, LOW);
|
||||||
|
Serial.println("Init: GPS Serial OK!");
|
||||||
|
|
||||||
|
adc1_config_width(ADC_WIDTH_BIT_12);
|
||||||
|
adc1_config_channel_atten(ADC1_CHANNEL_7,ADC_ATTEN_DB_6);
|
||||||
|
writedisplaytext("Init:","ADC OK!","",1000);
|
||||||
|
Serial.println("Init: ADC OK!");
|
||||||
|
|
||||||
|
rf95.setFrequency(433.775);
|
||||||
|
rf95.setModemConfig(BG_RF95::Bw125Cr45Sf4096); // hard coded because of double definition
|
||||||
|
// rf95.setModemConfig(ModemConfig); // das ist irgendwo doppelt definiert ???
|
||||||
|
rf95.setTxPower(5);
|
||||||
|
//rf95.printRegisters();
|
||||||
|
//rf95.setPromiscuousbg();
|
||||||
|
|
||||||
|
#if (SEND_WX)
|
||||||
|
dht.begin(); // DHT22 initialisieren
|
||||||
|
writedisplaytext("Init:","DHT OK!","",1000);
|
||||||
|
Serial.println("Init: DHT OK!");
|
||||||
|
#else //#if (SEND_WX)
|
||||||
|
writedisplaytext("Init:","no DHT configuration","",1000);
|
||||||
|
Serial.println("Init: no DHT configuration");
|
||||||
|
#endif //#if (SEND_WX)
|
||||||
|
|
||||||
|
digitalWrite(TXLED, HIGH);
|
||||||
|
writedisplaytext("Init:","All DONE OK!",":-D",1000);
|
||||||
|
digitalWrite(TXLED, LOW);
|
||||||
|
Serial.println("Init: ALL DONE OK! :-D");
|
||||||
|
writedisplaytext("","","",0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// LOOP
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
writedisplaytext("DEBUG","millis()",String(millis()),0);
|
||||||
|
#endif
|
||||||
|
//while(1) { if ( ss.available() ) Serial.write(ss.read());}
|
||||||
|
#if !(FIXED_POSITION)
|
||||||
|
// digitalWrite(GPSLED, HIGH);
|
||||||
|
|
||||||
|
while (ss.available() > 0) {
|
||||||
|
gps.encode(ss.read());
|
||||||
|
}
|
||||||
|
#endif // #if !(FIXED_POSITION)
|
||||||
|
|
||||||
|
if (rf95.waitAvailableTimeout(100))
|
||||||
|
{
|
||||||
|
// Should be a reply message for us now
|
||||||
|
if (rf95.recvAPRS(buf, &len))
|
||||||
|
{
|
||||||
|
// Serial.print("RX: ");
|
||||||
|
// Serial.println((char*)buf);
|
||||||
|
// Serial.print("RSSI: ");
|
||||||
|
// Serial.println(rf95.lastRssi(), DEC);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
display.clearDisplay();
|
||||||
|
display.setTextColor(WHITE);
|
||||||
|
display.setTextSize(2);
|
||||||
|
display.setCursor(0,0);
|
||||||
|
display.println("LoRa-APRS");
|
||||||
|
display.setTextSize(1);
|
||||||
|
display.setCursor(0,36);
|
||||||
|
display.print("LAT: ");
|
||||||
|
display.println(String(gps.location.lat(),5));
|
||||||
|
display.setCursor(0,46);
|
||||||
|
display.print("LON: ");
|
||||||
|
display.println(String(gps.location.lng(),5));
|
||||||
|
display.setCursor(0,56);
|
||||||
|
display.print("SPD: ");
|
||||||
|
display.print(String(gps.speed.kmph(),1));
|
||||||
|
display.print(" CRS: ");
|
||||||
|
display.println(String(gps.course.deg(),0));
|
||||||
|
display.display();
|
||||||
|
|
||||||
|
smartDelay(1000);
|
||||||
|
|
||||||
|
// digitalWrite(GPSLED, LOW);
|
||||||
|
#if (FIXED_POSITION)
|
||||||
|
// if (gps.location.isUpdated() || ( (lastTX+nextTX) <= millis() ) )
|
||||||
|
if ( (lastTX+nextTX) <= millis() )
|
||||||
|
#else
|
||||||
|
if (gps.location.isValid() && ( (lastTX+nextTX) <= millis() ) )
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
digitalWrite(TXLED, HIGH);
|
||||||
|
sendpacket();
|
||||||
|
writedisplaytext("State:","Packet sent!","",250);
|
||||||
|
Serial.println("State: Packet sent!");
|
||||||
|
digitalWrite(TXLED, LOW);
|
||||||
|
} else {
|
||||||
|
if ( (lastTX+nextTX*2) <= millis() )
|
||||||
|
{
|
||||||
|
digitalWrite(TXLED, HIGH);
|
||||||
|
sendpacket();
|
||||||
|
writedisplaytext("State:","Packet sent!","",250);
|
||||||
|
Serial.println("State: Packet sent!");
|
||||||
|
digitalWrite(TXLED, LOW);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
smartDelay(1000);
|
||||||
|
|
||||||
|
#if !(FIXED_POSITION)
|
||||||
|
if (millis() > 200000 && gps.charsProcessed() < 10)
|
||||||
|
{
|
||||||
|
writedisplaytext("Warning","No GPS Signal!","",1000);
|
||||||
|
Serial.println("Warning: No GPS Signal!");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// This custom version of delay() ensures that the gps object
|
||||||
|
// is being "fed".
|
||||||
|
static void smartDelay(unsigned long ms)
|
||||||
|
{
|
||||||
|
unsigned long start = millis();
|
||||||
|
do
|
||||||
|
{
|
||||||
|
#if !(FIXED_POSITION)
|
||||||
|
while (ss.available())
|
||||||
|
gps.encode(ss.read());
|
||||||
|
#endif
|
||||||
|
} while (millis() - start < ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//@APA Recalc GPS Position
|
||||||
|
void recalcGPS(){
|
||||||
|
|
||||||
|
String Ns, Ew, helper;
|
||||||
|
float Tlat, Tlon;
|
||||||
|
int Talt;
|
||||||
|
float Lat;
|
||||||
|
float Lon;
|
||||||
|
|
||||||
|
#if !(FIXED_POSITION)
|
||||||
|
Tlat=gps.location.lat();
|
||||||
|
Tlon=gps.location.lng();
|
||||||
|
Talt=gps.altitude.meters();
|
||||||
|
if(Tlat<0) { Ns = "S"; } else { Ns = "N"; }
|
||||||
|
if(Tlon<0) { Ew = "W"; } else { Ew = "E"; }
|
||||||
|
if(Tlat < 0) { Tlat= -Tlat; }
|
||||||
|
unsigned int Deg_Lat = Tlat;
|
||||||
|
Lat = 100*(Deg_Lat) + (Tlat - Deg_Lat)*60;
|
||||||
|
|
||||||
|
if(Tlon < 0) { Tlon= -Tlon; }
|
||||||
|
unsigned int Deg_Lon = Tlon;
|
||||||
|
Lon = 100*(Deg_Lon) + (Tlon - Deg_Lon)*60;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !(SEND_WX)
|
||||||
|
outString = "";
|
||||||
|
outString = (Tcall);
|
||||||
|
outString += ">APRS:!";
|
||||||
|
#if (FIXED_POSITION)
|
||||||
|
outString += LATITUDE;
|
||||||
|
#else
|
||||||
|
if(Tlat<10) {outString += "0"; }
|
||||||
|
outString += String(Lat,2);
|
||||||
|
outString += Ns;
|
||||||
|
#endif
|
||||||
|
outString += wxTable;
|
||||||
|
#if (FIXED_POSITION)
|
||||||
|
outString += LONGITUDE;
|
||||||
|
#else
|
||||||
|
if(Tlon<100) {outString += "0"; }
|
||||||
|
if(Tlon<10) {outString += "0"; }
|
||||||
|
outString += String(Lon,2);
|
||||||
|
outString += Ew;
|
||||||
|
#endif
|
||||||
|
outString += sSymbol;
|
||||||
|
outString += " /A=";
|
||||||
|
outString += Talt;
|
||||||
|
outString += "m Batt=";
|
||||||
|
outString += String(BattVolts,2);
|
||||||
|
outString += ("V");
|
||||||
|
#else
|
||||||
|
if ( !wx ) { // create standard position string
|
||||||
|
#if !(FIXED_POSITION)
|
||||||
|
outString = "";
|
||||||
|
outString = (Tcall);
|
||||||
|
outString += ">APRS:!";
|
||||||
|
if(Tlat<10) {outString += "0"; }
|
||||||
|
outString += String(Lat,2);
|
||||||
|
outString += Ns;
|
||||||
|
outString += sTable;
|
||||||
|
if(Tlon<100) {outString += "0"; }
|
||||||
|
if(Tlon<10) {outString += "0"; }
|
||||||
|
outString += String(Lon,2);
|
||||||
|
outString += Ew;
|
||||||
|
outString += sSymbol;
|
||||||
|
outString += " /A=";
|
||||||
|
outString += Talt;
|
||||||
|
outString += "m Batt=";
|
||||||
|
outString += String(BattVolts,2);
|
||||||
|
outString += ("V");
|
||||||
|
wx = true;
|
||||||
|
#endif
|
||||||
|
} else { // create weather report string
|
||||||
|
hum = dht.readHumidity();
|
||||||
|
// hum = 88.67;
|
||||||
|
// temp = 50.23;
|
||||||
|
temp = (dht.readTemperature() * 9/5) +32;
|
||||||
|
outString = "";
|
||||||
|
outString = (wxTcall);
|
||||||
|
outString += ">APRS:!";
|
||||||
|
#if (FIXED_POSITION)
|
||||||
|
outString += LATITUDE;
|
||||||
|
#else
|
||||||
|
if(Tlat<10) {outString += "0"; }
|
||||||
|
outString += String(Lat,2);
|
||||||
|
outString += Ns;
|
||||||
|
#endif
|
||||||
|
outString += wxTable;
|
||||||
|
#if (FIXED_POSITION)
|
||||||
|
outString += LONGITUDE;
|
||||||
|
#else
|
||||||
|
if(Tlon<100) {outString += "0"; }
|
||||||
|
if(Tlon<10) {outString += "0"; }
|
||||||
|
outString += String(Lon,2);
|
||||||
|
outString += Ew;
|
||||||
|
#endif
|
||||||
|
outString += wxSymbol;
|
||||||
|
outString += ".../...g...t";
|
||||||
|
if (temp < 0) { // negative Werte erstellen
|
||||||
|
outString += "-";
|
||||||
|
if(temp>-10) {outString += "0"; }
|
||||||
|
temp = abs(temp);
|
||||||
|
} else { // positive Werte erstellen
|
||||||
|
if(temp<100) {outString += "0"; }
|
||||||
|
if(temp<10) {outString += "0"; }
|
||||||
|
}
|
||||||
|
helper = String(temp,0);
|
||||||
|
helper.trim();
|
||||||
|
outString += helper;
|
||||||
|
outString += "r...p...P...h";
|
||||||
|
if(hum<10) {outString += "0"; }
|
||||||
|
helper = String(hum,0);
|
||||||
|
helper.trim();
|
||||||
|
outString += helper;
|
||||||
|
outString += "b......DHT22";
|
||||||
|
#if !(FIXED_POSITION)
|
||||||
|
wx = false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
void sendpacket()
|
||||||
|
{
|
||||||
|
|
||||||
|
batt_read();
|
||||||
|
Outputstring = "";
|
||||||
|
|
||||||
|
#if !(FIXED_POSITION)
|
||||||
|
if ( gps.location.isValid() || gps.location.isUpdated() )
|
||||||
|
{
|
||||||
|
// digitalWrite(GPSLED, HIGH);
|
||||||
|
//New System
|
||||||
|
//recalcEncodedGPS();
|
||||||
|
#endif
|
||||||
|
recalcGPS(); //
|
||||||
|
// digitalWrite(PLED1, HIGH);
|
||||||
|
Outputstring =outString;
|
||||||
|
|
||||||
|
loraSend(lora_TXStart, lora_TXEnd, 60, 255, 1, 10, TXdbmW, TXFREQ); //send the packet, data is in TXbuff from lora_TXStart to lora_TXEnd
|
||||||
|
#if !(FIXED_POSITION)
|
||||||
|
} else {
|
||||||
|
Outputstring = (Tcall);
|
||||||
|
Outputstring += " No GPS-Fix";
|
||||||
|
Outputstring += " Batt=";
|
||||||
|
Outputstring += String(BattVolts,2);
|
||||||
|
Outputstring += ("V ");
|
||||||
|
|
||||||
|
loraSend(lora_TXStart, lora_TXEnd, 60, 255, 1, 10, 5, TXFREQ); //send the packet, data is in TXbuff from lora_TXStart to lora_TXEnd
|
||||||
|
// digitalWrite(GPSLED, LOW);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// digitalWrite(PLED1, LOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
void loraSend(byte lora_LTXStart, byte lora_LTXEnd, byte lora_LTXPacketType, byte lora_LTXDestination, byte lora_LTXSource, long lora_LTXTimeout, byte lora_LTXPower, float lora_FREQ)
|
||||||
|
{
|
||||||
|
byte i;
|
||||||
|
byte ltemp;
|
||||||
|
|
||||||
|
if (rf95.waitAvailableTimeout(100))
|
||||||
|
{
|
||||||
|
if (rf95.recvAPRS(buf, &len))
|
||||||
|
{
|
||||||
|
// Serial.print("RX before TX: ");
|
||||||
|
// Serial.println((char*)buf);
|
||||||
|
// Serial.print("RSSI: ");
|
||||||
|
// Serial.println(rf95.lastRssi(), DEC);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// time of last TX
|
||||||
|
lastTX = millis();
|
||||||
|
|
||||||
|
ltemp = Outputstring.length();
|
||||||
|
for (i = 0; i <= ltemp; i++)
|
||||||
|
{
|
||||||
|
lora_TXBUFF[i] = Outputstring.charAt(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
i--;
|
||||||
|
lora_TXEnd = i;
|
||||||
|
lora_TXBUFF[i] ='\0';
|
||||||
|
|
||||||
|
// digitalWrite(PLED1, HIGH); //LED on during packet
|
||||||
|
|
||||||
|
rf95.setModemConfig(BG_RF95::Bw125Cr45Sf4096);
|
||||||
|
rf95.setFrequency(lora_FREQ);
|
||||||
|
rf95.setTxPower(lora_LTXPower);
|
||||||
|
|
||||||
|
// Serial.print(Outputstring);
|
||||||
|
// Serial.print(" len: ");
|
||||||
|
// Serial.println(strlen(lora_TXBUFF) );
|
||||||
|
|
||||||
|
//digitalWrite(RX_en, LOW); //RX lo
|
||||||
|
//digitalWrite(TX_en, HIGH); //TX HIGH
|
||||||
|
//rf95.sendAPRS(lora_TXBUFF, sizeof(lora_TXBUFF));
|
||||||
|
rf95.sendAPRS(lora_TXBUFF, Outputstring.length());
|
||||||
|
|
||||||
|
// rf95.sendAPRS(lora_TXBUFF, lora_TXBUFF.length());
|
||||||
|
rf95.waitPacketSent();
|
||||||
|
|
||||||
|
//digitalWrite(TX_en, LOW); //TX lo
|
||||||
|
//digitalWrite(RX_en,HIGH); //RX HIGH
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// digitalWrite(PLED1, LOW);
|
||||||
|
|
||||||
|
}
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
void batt_read()
|
||||||
|
{
|
||||||
|
//int BattRead = analogRead(ANALOG_PI);
|
||||||
|
int BattRead = adc1_get_raw(ADC1_CHANNEL_7);
|
||||||
|
//lora_TXBUFF[1] = (BattRead / 256); //MSB of battery volts
|
||||||
|
//lora_TXBUFF[0] = (BattRead - (lora_TXBUFF[1] * 256)); //LSB of battery volts
|
||||||
|
|
||||||
|
BattVolts = (BattRead * (2.2 / 4096.0));
|
||||||
|
|
||||||
|
//Serial.print("lora_TXBUFF[0] ");
|
||||||
|
//Serial.println(lora_TXBUFF[0]);
|
||||||
|
//Serial.print("lora_TXBUFF[1] ");
|
||||||
|
//Serial.println(lora_TXBUFF[1]);
|
||||||
|
//Serial.println("Battery ");
|
||||||
|
//Serial.print(BattVolts, 2);
|
||||||
|
//Serial.println("V");
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
void writedisplaytext(String Line1, String Line2, String Line3, int warten)
|
||||||
|
{
|
||||||
|
display.clearDisplay();
|
||||||
|
display.setTextColor(WHITE);
|
||||||
|
display.setTextSize(2);
|
||||||
|
display.setCursor(0,0);
|
||||||
|
display.println("LoRa-APRS");
|
||||||
|
display.setTextSize(1);
|
||||||
|
display.setCursor(0,36);
|
||||||
|
display.println(Line1);
|
||||||
|
display.setCursor(0,46);
|
||||||
|
display.println(Line2);
|
||||||
|
display.setCursor(0,56);
|
||||||
|
display.println(Line3);
|
||||||
|
display.display();
|
||||||
|
smartDelay(warten);
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// File generated by LCD Assistant
|
||||||
|
// http://en.radzio.dxp.pl/bitmap_converter/
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
const unsigned char xtest_bw [] PROGMEM = {
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x3E, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x7C, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x00, 0xF8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xF8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x01, 0xF0, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x10, 0x00, 0x3E, 0x00, 0x03, 0xE0, 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x18, 0x00, 0x1F, 0x00, 0x07, 0xC0, 0x01, 0x81, 0xFF, 0xFE, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x1C, 0x00, 0x1F, 0x80, 0x0F, 0x80, 0x01, 0x81, 0xED, 0xB6, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x1E, 0x00, 0x0F, 0x80, 0x1F, 0x00, 0x03, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x1F, 0x00, 0x07, 0xC0, 0x1F, 0x00, 0x07, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x0F, 0x00, 0x03, 0xE0, 0x3E, 0x00, 0x0F, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x0F, 0x80, 0x01, 0xF0, 0x7C, 0x00, 0x1F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x07, 0xC0, 0x00, 0xF8, 0xF8, 0x00, 0x3E, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x03, 0xE0, 0x00, 0xF9, 0xF0, 0x00, 0x3E, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x7F, 0xE0, 0x00, 0x7C, 0x00, 0x20, 0x78, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x3F, 0xE0, 0x00, 0xF8, 0x00, 0xE0, 0xCC, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x1F, 0xC0, 0x01, 0xF0, 0x00, 0x81, 0x86, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x0F, 0x80, 0x03, 0xE0, 0x01, 0x81, 0x02, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x07, 0x00, 0x07, 0xC0, 0x01, 0x01, 0x02, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x06, 0x00, 0x07, 0xC0, 0x01, 0x01, 0x02, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x00, 0x0F, 0x80, 0x01, 0x03, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, 0x1F, 0x00, 0x01, 0x02, 0x03, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, 0x3E, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x7C, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x00, 0xF8, 0x00, 0x01, 0x86, 0x02, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x8C, 0x04, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x01, 0xF0, 0x00, 0x00, 0xF8, 0x1C, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x07, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0xE0, 0x00, 0x01, 0x03, 0x02, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x01, 0xF0, 0x00, 0x01, 0x03, 0x02, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xF8, 0x00, 0x01, 0x03, 0x02, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x7C, 0x00, 0x01, 0x03, 0x02, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x7C, 0x00, 0x01, 0x03, 0x02, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x3E, 0x00, 0x01, 0x03, 0x02, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, 0x1F, 0x00, 0x01, 0x03, 0x02, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x00, 0x0F, 0x80, 0x01, 0x03, 0x02, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x02, 0x00, 0x07, 0xC0, 0x01, 0x03, 0x02, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x06, 0x00, 0x03, 0xE0, 0x01, 0x03, 0x02, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x0F, 0x00, 0x03, 0xE0, 0x01, 0x03, 0x02, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x0F, 0x80, 0x01, 0xF0, 0x01, 0x03, 0x02, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x1F, 0xC0, 0x00, 0xF8, 0x01, 0x03, 0x02, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x3F, 0xE0, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x7F, 0xF0, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x07, 0xC0, 0x00, 0xF9, 0xF0, 0x00, 0x1F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x07, 0xC0, 0x00, 0xF8, 0xF8, 0x00, 0x1F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x0F, 0x80, 0x01, 0xF0, 0x7C, 0x00, 0x0F, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x1F, 0x00, 0x03, 0xE0, 0x3E, 0x00, 0x07, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x1E, 0x00, 0x07, 0xC0, 0x1F, 0x00, 0x03, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x1C, 0x00, 0x0F, 0x80, 0x1F, 0x00, 0x01, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x18, 0x00, 0x1F, 0x00, 0x0F, 0x80, 0x01, 0x81, 0xFF, 0xFE, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x18, 0x00, 0x1F, 0x00, 0x07, 0xC0, 0x00, 0x81, 0xEA, 0xAA, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0xE0, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x01, 0xF0, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xF8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x00, 0xF8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x7C, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x3E, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||||
|
};
|
|
@ -0,0 +1,11 @@
|
||||||
|
|
||||||
|
This directory is intended for PIO Unit Testing and project tests.
|
||||||
|
|
||||||
|
Unit Testing is a software testing method by which individual units of
|
||||||
|
source code, sets of one or more MCU program modules together with associated
|
||||||
|
control data, usage procedures, and operating procedures, are tested to
|
||||||
|
determine whether they are fit for use. Unit testing finds problems early
|
||||||
|
in the development cycle.
|
||||||
|
|
||||||
|
More information about PIO Unit Testing:
|
||||||
|
- https://docs.platformio.org/page/plus/unit-testing.html
|
Loading…
Reference in New Issue