First Half
#include "LMIC-node.h"
#include <Adafruit_ADS1X15.h>
#include "TinyGPS++.h"
TinyGPSPlus gps;
Adafruit_ADS1115 ads;
// β β βββ βββ βββ βββ βββ βββ βββ βββ βββ βββ βββ βββ
// β β βββ βββ βββ β β β β β βββ βββ βββ β β β β β
// βββ βββ βββ β β βββ βββ ββ βββ ββ βββ βββ βββ β β
const uint8_t payloadBufferLength = 4; // Adjust to fit max payload length
const int LED_PIN = 12;
int16_t SensorRead;
float LatRead;
float LonRead;
float AltRead;
int HourRead;
int MinRead;
int SecRead;
double LatRAW;
double LonRAW;
double AltRAW;
double HourRAW;
double MinRAW;
double SecRAW;
// β β βββ βββ βββ βββ βββ βββ βββ βββ βββ βββ
// β β βββ βββ βββ β β β β β βββ βββ β β β β
// βββ βββ βββ β β βββ βββ ββ βββ βββ β β ββ
uint8_t payloadBuffer[payloadBufferLength];
static osjob_t doWorkJob;
uint32_t doWorkIntervalSeconds = DO_WORK_INTERVAL_SECONDS; // Change value in platformio.ini
// Note: LoRa module pin mappings are defined in the Board Support Files.
// Set LoRaWAN keys defined in lorawan-keys.h.
#ifdef OTAA_ACTIVATION
static const u1_t PROGMEM DEVEUI[8] = { OTAA_DEVEUI } ;
static const u1_t PROGMEM APPEUI[8] = { OTAA_APPEUI };
static const u1_t PROGMEM APPKEY[16] = { OTAA_APPKEY };
// Below callbacks are used by LMIC for reading above values.
void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8); }
void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8); }
void os_getDevKey (u1_t* buf) { memcpy_P(buf, APPKEY, 16); }
#else
// ABP activation
static const u4_t DEVADDR = ABP_DEVADDR ;
static const PROGMEM u1_t NWKSKEY[16] = { ABP_NWKSKEY };
static const u1_t PROGMEM APPSKEY[16] = { ABP_APPSKEY };
// Below callbacks are not used be they must be defined.
void os_getDevEui (u1_t* buf) { }
void os_getArtEui (u1_t* buf) { }
void os_getDevKey (u1_t* buf) { }
#endif
int16_t getSnrTenfold()
{
// Returns ten times the SNR (dB) value of the last received packet.
// Ten times to prevent the use of float but keep 1 decimal digit accuracy.
// Calculation per SX1276 datasheet rev.7 Β§6.4, SX1276 datasheet rev.4 Β§6.4.
// LMIC.snr contains value of PacketSnr, which is 4 times the actual SNR value.
return (LMIC.snr * 10) / 4;
}
int16_t getRssi(int8_t snr)
{
// Returns correct RSSI (dBm) value of the last received packet.
// Calculation per SX1276 datasheet rev.7 Β§5.5.5, SX1272 datasheet rev.4 Β§5.5.5.
#define RSSI_OFFSET 64
#define SX1276_FREQ_LF_MAX 525000000 // per datasheet 6.3
#define SX1272_RSSI_ADJUST -139
#define SX1276_RSSI_ADJUST_LF -164
#define SX1276_RSSI_ADJUST_HF -157
int16_t rssi;
#ifdef MCCI_LMIC
rssi = LMIC.rssi - RSSI_OFFSET;
#else
int16_t rssiAdjust;
#ifdef CFG_sx1276_radio
if (LMIC.freq > SX1276_FREQ_LF_MAX)
{
rssiAdjust = SX1276_RSSI_ADJUST_HF;
}
else
{
rssiAdjust = SX1276_RSSI_ADJUST_LF;
}
#else
// CFG_sx1272_radio
rssiAdjust = SX1272_RSSI_ADJUST;
#endif
// Revert modification (applied in lmic/radio.c) to get PacketRssi.
int16_t packetRssi = LMIC.rssi + 125 - RSSI_OFFSET;
if (snr < 0)
{
rssi = rssiAdjust + packetRssi + snr;
}
else
{
rssi = rssiAdjust + (16 * packetRssi) / 15;
}
#endif
return rssi;
}
void printEvent(ostime_t timestamp,
const char * const message,
PrintTarget target = PrintTarget::All,
bool clearDisplayStatusRow = true,
bool eventLabel = false)
{
#ifdef USE_DISPLAY
if (target == PrintTarget::All || target == PrintTarget::Display)
{
display.clearLine(TIME_ROW);
display.setCursor(COL_0, TIME_ROW);
display.print(F("Time:"));
display.print(timestamp);
display.clearLine(EVENT_ROW);
if (clearDisplayStatusRow)
{
display.clearLine(STATUS_ROW);
}
display.setCursor(COL_0, EVENT_ROW);
display.print(message);
}
#endif
#ifdef USE_SERIAL
// Create padded/indented output without using printf().
// printf() is not default supported/enabled in each Arduino core.
// Not using printf() will save memory for memory constrainted devices.
String timeString(timestamp);
uint8_t len = timeString.length();
uint8_t zerosCount = TIMESTAMP_WIDTH > len ? TIMESTAMP_WIDTH - len : 0;
if (target == PrintTarget::All || target == PrintTarget::Serial)
{
printChars(serial, '0', zerosCount);
serial.print(timeString);
serial.print(": ");
if (eventLabel)
{
serial.print(F("Event: "));
}
serial.println(message);
}
#endif
}
void printEvent(ostime_t timestamp,
ev_t ev,
PrintTarget target = PrintTarget::All,
bool clearDisplayStatusRow = true)
{
#if defined(USE_DISPLAY) || defined(USE_SERIAL)
printEvent(timestamp, lmicEventNames[ev], target, clearDisplayStatusRow, true);
#endif
}
void printFrameCounters(PrintTarget target = PrintTarget::All)
{
#ifdef USE_DISPLAY
if (target == PrintTarget::Display || target == PrintTarget::All)
{
display.clearLine(FRMCNTRS_ROW);
display.setCursor(COL_0, FRMCNTRS_ROW);
display.print(F("Up:"));
display.print(LMIC.seqnoUp);
display.print(F(" Dn:"));
display.print(LMIC.seqnoDn);
}
#endif
#ifdef USE_SERIAL
if (target == PrintTarget::Serial || target == PrintTarget::All)
{
printSpaces(serial, MESSAGE_INDENT);
serial.print(F("Up: "));
serial.print(LMIC.seqnoUp);
serial.print(F(", Down: "));
serial.println(LMIC.seqnoDn);
}
#endif
}
void printSessionKeys()
{
#if defined(USE_SERIAL) && defined(MCCI_LMIC)
u4_t networkId = 0;
devaddr_t deviceAddress = 0;
u1_t networkSessionKey[16];
u1_t applicationSessionKey[16];
LMIC_getSessionKeys(&networkId, &deviceAddress,
networkSessionKey, applicationSessionKey);
printSpaces(serial, MESSAGE_INDENT);
serial.print(F("Network Id: "));
serial.println(networkId, DEC);
printSpaces(serial, MESSAGE_INDENT);
serial.print(F("Device Address: "));
serial.println(deviceAddress, HEX);
printSpaces(serial, MESSAGE_INDENT);
serial.print(F("Application Session Key: "));
printHex(serial, applicationSessionKey, 16, true, '-');
printSpaces(serial, MESSAGE_INDENT);
serial.print(F("Network Session Key: "));
printHex(serial, networkSessionKey, 16, true, '-');
#endif
}
void printDownlinkInfo(void)
{
#if defined(USE_SERIAL) || defined(USE_DISPLAY)
uint8_t dataLength = LMIC.dataLen;
// bool ackReceived = LMIC.txrxFlags & TXRX_ACK;
int16_t snrTenfold = getSnrTenfold();
int8_t snr = snrTenfold / 10;
int8_t snrDecimalFraction = snrTenfold % 10;
int16_t rssi = getRssi(snr);
uint8_t fPort = 0;
if (LMIC.txrxFlags & TXRX_PORT)
{
fPort = LMIC.frame[LMIC.dataBeg -1];
}
#ifdef USE_DISPLAY
display.clearLine(EVENT_ROW);
display.setCursor(COL_0, EVENT_ROW);
display.print(F("RX P:"));
display.print(fPort);
if (dataLength != 0)
{
display.print(" Len:");
display.print(LMIC.dataLen);
}
display.clearLine(STATUS_ROW);
display.setCursor(COL_0, STATUS_ROW);
display.print(F("RSSI"));
display.print(rssi);
display.print(F(" SNR"));
display.print(snr);
display.print(".");
display.print(snrDecimalFraction);
#endif
#ifdef USE_SERIAL
printSpaces(serial, MESSAGE_INDENT);
serial.println(F("Downlink received"));
printSpaces(serial, MESSAGE_INDENT);
serial.print(F("RSSI: "));
serial.print(rssi);
serial.print(F(" dBm, SNR: "));
serial.print(snr);
serial.print(".");
serial.print(snrDecimalFraction);
serial.println(F(" dB"));
printSpaces(serial, MESSAGE_INDENT);
serial.print(F("Port: "));
serial.println(fPort);
if (dataLength != 0)
{
printSpaces(serial, MESSAGE_INDENT);
serial.print(F("Length: "));
serial.println(LMIC.dataLen);
printSpaces(serial, MESSAGE_INDENT);
serial.print(F("Data: "));
printHex(serial, LMIC.frame+LMIC.dataBeg, LMIC.dataLen, true, ' ');
}
#endif
#endif
}
void printHeader(void)
{
#ifdef USE_DISPLAY
display.clear();
display.setCursor(COL_0, HEADER_ROW);
display.print(F("LMIC-node"));
#ifdef ABP_ACTIVATION
display.drawString(ABPMODE_COL, HEADER_ROW, "ABP");
#endif
#ifdef CLASSIC_LMIC
display.drawString(CLMICSYMBOL_COL, HEADER_ROW, "*");
#endif
display.drawString(COL_0, DEVICEID_ROW, deviceId);
display.setCursor(COL_0, INTERVAL_ROW);
display.print(F("Interval:"));
display.print(doWorkIntervalSeconds);
display.print("s");
#endif
#ifdef USE_SERIAL
serial.println(F("\n\nLMIC-node\n"));
serial.print(F("Device-id: "));
serial.println(deviceId);
serial.print(F("LMIC library: "));
#ifdef MCCI_LMIC
serial.println(F("MCCI"));
#else
serial.println(F("Classic [Deprecated]"));
#endif
serial.print(F("Activation: "));
#ifdef OTAA_ACTIVATION
serial.println(F("OTAA"));
#else
serial.println(F("ABP"));
#endif
#if defined(LMIC_DEBUG_LEVEL) && LMIC_DEBUG_LEVEL > 0
serial.print(F("LMIC debug: "));
serial.println(LMIC_DEBUG_LEVEL);
#endif
serial.print(F("Interval: "));
serial.print(doWorkIntervalSeconds);
serial.println(F(" seconds"));
if (activationMode == ActivationMode::OTAA)
{
serial.println();
}
#endif
}
#ifdef ABP_ACTIVATION
void setAbpParameters(dr_t dataRate = DefaultABPDataRate, s1_t txPower = DefaultABPTxPower)
{
// Set static session parameters. Instead of dynamically establishing a session
// by joining the network, precomputed session parameters are be provided.
#ifdef PROGMEM
// On AVR, these values are stored in flash and only copied to RAM
// once. Copy them to a temporary buffer here, LMIC_setSession will
// copy them into a buffer of its own again.
uint8_t appskey[sizeof(APPSKEY)];
uint8_t nwkskey[sizeof(NWKSKEY)];
memcpy_P(appskey, APPSKEY, sizeof(APPSKEY));
memcpy_P(nwkskey, NWKSKEY, sizeof(NWKSKEY));
LMIC_setSession (0x1, DEVADDR, nwkskey, appskey);
#else
// If not running an AVR with PROGMEM, just use the arrays directly
LMIC_setSession (0x1, DEVADDR, NWKSKEY, APPSKEY);
#endif
#if defined(CFG_eu868)
// Set up the channels used by the Things Network, which corresponds
// to the defaults of most gateways. Without this, only three base
// channels from the LoRaWAN specification are used, which certainly
// works, so it is good for debugging, but can overload those
// frequencies, so be sure to configure the full frequency range of
// your network here (unless your network autoconfigures them).
// Setting up channels should happen after LMIC_setSession, as that
// configures the minimal channel set. The LMIC doesn't let you change
// the three basic settings, but we show them here.
LMIC_setupChannel(0, 868100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band
LMIC_setupChannel(1, 868300000, DR_RANGE_MAP(DR_SF12, DR_SF7B), BAND_CENTI); // g-band
LMIC_setupChannel(2, 868500000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band
LMIC_setupChannel(3, 867100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band
LMIC_setupChannel(4, 867300000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band
LMIC_setupChannel(5, 867500000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band
LMIC_setupChannel(6, 867700000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band
LMIC_setupChannel(7, 867900000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band
LMIC_setupChannel(8, 868800000, DR_RANGE_MAP(DR_FSK, DR_FSK), BAND_MILLI); // g2-band
// TTN defines an additional channel at 869.525Mhz using SF9 for class B
// devices' ping slots. LMIC does not have an easy way to define set this
// frequency and support for class B is spotty and untested, so this
// frequency is not configured here.
#elif defined(CFG_us915) || defined(CFG_au915)
// NA-US and AU channels 0-71 are configured automatically
// but only one group of 8 should (a subband) should be active
// TTN recommends the second sub band, 1 in a zero based count.
// https://github.com/TheThingsNetwork/gateway-conf/blob/master/US-global_conf.json
LMIC_selectSubBand(1);
#elif defined(CFG_as923)
// Set up the channels used in your country. Only two are defined by default,
// and they cannot be changed. Use BAND_CENTI to indicate 1% duty cycle.
// LMIC_setupChannel(0, 923200000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI);
// LMIC_setupChannel(1, 923400000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI);
// ... extra definitions for channels 2..n here
#elif defined(CFG_kr920)
// Set up the channels used in your country. Three are defined by default,
// and they cannot be changed. Duty cycle doesn't matter, but is conventionally
// BAND_MILLI.
// LMIC_setupChannel(0, 922100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_MILLI);
// LMIC_setupChannel(1, 922300000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_MILLI);
// LMIC_setupChannel(2, 922500000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_MILLI);
// ... extra definitions for channels 3..n here.
#elif defined(CFG_in866)
// Set up the channels used in your country. Three are defined by default,
// and they cannot be changed. Duty cycle doesn't matter, but is conventionally
// BAND_MILLI.
// LMIC_setupChannel(0, 865062500, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_MILLI);
// LMIC_setupChannel(1, 865402500, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_MILLI);
// LMIC_setupChannel(2, 865985000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_MILLI);
// ... extra definitions for channels 3..n here.
#endif
// Disable link check validation
LMIC_setLinkCheckMode(0);
// TTN uses SF9 for its RX2 window.
LMIC.dn2Dr = DR_SF9;
// Set data rate and transmit power (note: txpow is possibly ignored by the library)
LMIC_setDrTxpow(dataRate, txPower);
}
#endif //ABP_ACTIVATION