BME680 support (experimental); LoRa Time sync (experimental)
This commit is contained in:
parent
dbc3d28d31
commit
4ae4633a15
18
include/bme680read.h
Normal file
18
include/bme680read.h
Normal file
@ -0,0 +1,18 @@
|
||||
#ifndef _HAS_BME
|
||||
#define _HAS_BME
|
||||
|
||||
#include "globals.h"
|
||||
|
||||
#include <Wire.h>
|
||||
#include <SPI.h>
|
||||
#include <Adafruit_Sensor.h>
|
||||
#include "Adafruit_BME680.h"
|
||||
|
||||
extern Adafruit_BME680 bme; // Make TinyGPS++ instance globally availabe
|
||||
extern bmeStatus_t
|
||||
bme_status; // Make struct for storing gps data globally available
|
||||
extern TaskHandle_t BmeTask;
|
||||
|
||||
void bme_loop(void *pvParameters);
|
||||
|
||||
#endif
|
@ -3,8 +3,12 @@
|
||||
|
||||
#include "globals.h"
|
||||
#include "senddata.h"
|
||||
#include "rcommand.h"
|
||||
#include "spislave.h"
|
||||
#include <lmic.h>
|
||||
|
||||
void doHousekeeping(void);
|
||||
void do_timesync(void);
|
||||
uint64_t uptime(void);
|
||||
void reset_counters(void);
|
||||
int redirect_log(const char *fmt, va_list args);
|
||||
|
@ -46,6 +46,14 @@ typedef struct {
|
||||
uint16_t altitude;
|
||||
} gpsStatus_t;
|
||||
|
||||
typedef struct {
|
||||
uint16_t temperature;
|
||||
uint16_t pressure;
|
||||
uint16_t humidity;
|
||||
uint16_t gas_resistance;
|
||||
uint16_t altitude;
|
||||
} bmeStatus_t;
|
||||
|
||||
// global variables
|
||||
extern configData_t cfg; // current device configuration
|
||||
extern char display_line6[], display_line7[]; // screen buffers
|
||||
@ -67,6 +75,10 @@ extern TaskHandle_t irqHandlerTask, wifiSwitchTask;
|
||||
#include "gpsread.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAS_BME
|
||||
#include "bme680read.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAS_LORA
|
||||
#include "lorawan.h"
|
||||
#endif
|
||||
|
@ -29,6 +29,8 @@ void lora_send(osjob_t *job);
|
||||
void lora_enqueuedata(uint8_t messageType, MessageBuffer_t *message);
|
||||
void lora_queuereset(void);
|
||||
void lora_housekeeping(void);
|
||||
void user_request_network_time_callback(void *pVoidUserUTCTime,
|
||||
int flagSuccess);
|
||||
|
||||
esp_err_t lora_stack_init();
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
Arduino-LMIC library
|
||||
====================
|
||||
# Arduino-LMIC library
|
||||
|
||||
This repository contains the IBM LMIC (LoraMAC-in-C) library, slightly
|
||||
modified to run in the Arduino environment, allowing using the SX1272,
|
||||
@ -25,7 +24,7 @@ requires C99 mode to be enabled by default.
|
||||
We strongly recommend updating using VS Code, the markdown-toc extension and the
|
||||
bierner.markdown-preview-github-styles extension.
|
||||
-->
|
||||
<!-- TOC depthFrom:1 -->
|
||||
<!-- TOC depthFrom:2 -->
|
||||
|
||||
- [Installing](#installing)
|
||||
- [Features](#features)
|
||||
@ -86,12 +85,12 @@ requires C99 mode to be enabled by default.
|
||||
|
||||
To install this library:
|
||||
|
||||
- install it using the Arduino Library manager ("Sketch" -> "Include
|
||||
- install it using the Arduino Library manager ("Sketch" -> "Include
|
||||
Library" -> "Manage Libraries..."), or
|
||||
- download a zipfile from github using the "Download ZIP" button and
|
||||
- download a zipfile from github using the "Download ZIP" button and
|
||||
install it using the IDE ("Sketch" -> "Include Library" -> "Add .ZIP
|
||||
Library..."
|
||||
- clone this git repository into your sketchbook/libraries folder.
|
||||
- clone this git repository into your sketchbook/libraries folder.
|
||||
|
||||
For more info, see https://www.arduino.cc/en/Guide/Libraries
|
||||
|
||||
@ -105,19 +104,19 @@ The library has only been tested with LoRaWAN 1.0.2 networks and does not have t
|
||||
|
||||
What certainly works:
|
||||
|
||||
- Sending packets uplink, taking into account duty cycling.
|
||||
- Encryption and message integrity checking.
|
||||
- Receiving downlink packets in the RX2 window.
|
||||
- Custom frequencies and datarate settings.
|
||||
- Over-the-air activation (OTAA / joining).
|
||||
- Receiving downlink packets in the RX1 and RX2 windows.
|
||||
- Some MAC command processing.
|
||||
- Sending packets uplink, taking into account duty cycling.
|
||||
- Encryption and message integrity checking.
|
||||
- Receiving downlink packets in the RX2 window.
|
||||
- Custom frequencies and datarate settings.
|
||||
- Over-the-air activation (OTAA / joining).
|
||||
- Receiving downlink packets in the RX1 and RX2 windows.
|
||||
- Some MAC command processing.
|
||||
|
||||
What has not been tested:
|
||||
|
||||
- Receiving and processing all MAC commands.
|
||||
- Class B operation.
|
||||
- FSK has not been extensively tested.
|
||||
- Receiving and processing all MAC commands.
|
||||
- Class B operation.
|
||||
- FSK has not been extensively tested.
|
||||
|
||||
If you try one of these untested features and it works, be sure to let
|
||||
us know (creating a github issue is probably the best way for that).
|
||||
@ -131,7 +130,7 @@ directory is the only directory that contains files that you
|
||||
should edit to match your project; we organize things this way
|
||||
so that your local changes are more clearly separated from
|
||||
the distribution files. The Arduino environment doesn't give
|
||||
us a better way to do this.
|
||||
us a better way to do this, unless you change `BOARDS.txt`.
|
||||
|
||||
Unlike other ports of the LMIC code, in this port, you should not edit `src/lmic/config.h` to configure this package.
|
||||
|
||||
@ -147,6 +146,7 @@ The library supports the following regions:
|
||||
`-D CFG_us915` | `LMIC_REGION_us915` | 2 | 2.2 | US 902-928 MHz ISM
|
||||
`-D CFG_au921` | `LMIC_REGION_au921` | 5 | 2.5 | Australia 915-928 MHz ISM
|
||||
`-D CFG_as923` | `LMIC_REGION_as923` | 7 | 2.7 | Asia 923 MHz ISM
|
||||
`-D CFG_as923jp` | `LMIC_REGION_as923` and `LMIC_COUNTRY_CODE_JP` | 7 | 2.7 | Asia 923 MHz ISM with Japan listen-before-talk (LBT) rules
|
||||
`-D CFG_in866` | `LMIC_REGION_in866` | 9 | 2.9 | India 865-867 MHz ISM
|
||||
|
||||
You should define exactly one of `CFG_...` variables. If you don't,
|
||||
@ -205,8 +205,7 @@ By default, PING support is included in the library.
|
||||
If defined, removes all code needed for handling beacons. Removes the APIs `LMIC_enableTracking()` and `LMIC_disableTracking()`.
|
||||
Class A devices don't support beacons, so defining `DISABLE_BEACONS` might be a good idea.
|
||||
|
||||
|
||||
### Rarely changed variables ###
|
||||
### Rarely changed variables
|
||||
|
||||
The remaining variables are rarely used, but we list them here for completeness.
|
||||
|
||||
@ -1022,6 +1021,10 @@ function uflt122f(rawUflt12)
|
||||
|
||||
## Release History
|
||||
|
||||
- Interim bug fixes: added a new API (`radio_irq_handler_v2()`), which allows the caller to provide the timestamp of the interrupt. This allows for more accurate timing, because the knowledge of interrupt overhead can be moved to a platform-specific layer ([#148](https://github.com/mcci-catena/arduino-lmic/issues/148)). Fixed compile issues on ESP32 ([#140](https://github.com/mcci-catena/arduino-lmic/issues/140) and [#153](https://github.com/mcci-catena/arduino-lmic/issues/150)). We added ESP32 and 32u4 as targets in CI testing. We switched CI testing to Arduino IDE 1.8.7.
|
||||
Fixed issue [#161](https://github.com/mcci-catena/arduino-lmic/issues/161) selecting the Japan version of as923 using `CFG_as923jp` (selecting via `CFG_as923` and `LMIC_COUNTRY_CODE=LMIC_COUNTRY_CODE_JP` worked).
|
||||
Fixed [#38](https://github.com/mcci-catena/arduino-lmic/issues/38) -- now any call to hal_init() will put the NSS line in the idle (high/inactive) state. As a side effect, RXTX is initialized, and RESET code changed to set value before transitioning state. Likely no net effect, but certainly more correct.
|
||||
|
||||
- V2.2.2 adds `ttn-abp-feather-us915-dht22.ino` example, and fixes some documentation typos. It also fixes encoding of the `Margin` field of the `DevStatusAns` MAC message ([#130](https://github.com/mcci-catena/arduino-lmic/issues/130)). This makes Arduino LMIC work with newtorks implemented with [LoraServer](https://www.loraserver.io/).
|
||||
|
||||
- V2.2.1 corrects the value of `ARDUINO_LMIC_VERSION` ([#123](https://github.com/mcci-catena/arduino-lmic/issues/123)), allows ttn-otaa-feather-us915 example to compile for the Feather 32u4 LoRa ([#116](https://github.com/mcci-catena/arduino-lmic/issues/116)), and addresses documentation issues ([#122](https://github.com/mcci-catena/arduino-lmic/issues/122), [#120](https://github.com/mcci-catena/arduino-lmic/issues/120)).
|
||||
|
8569
lib/arduino-lmic-mcci-v2.2.2/assets/Feather-M0-LoRa-Wire.ai
Normal file
8569
lib/arduino-lmic-mcci-v2.2.2/assets/Feather-M0-LoRa-Wire.ai
Normal file
File diff suppressed because one or more lines are too long
BIN
lib/arduino-lmic-mcci-v2.2.2/assets/Feather-M0-LoRa-Wire.png
Normal file
BIN
lib/arduino-lmic-mcci-v2.2.2/assets/Feather-M0-LoRa-Wire.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 269 KiB |
@ -0,0 +1,36 @@
|
||||
/*
|
||||
|
||||
Module: header_test.ino
|
||||
|
||||
Function:
|
||||
Simple hello-world (and compile-test) app
|
||||
|
||||
Copyright notice and License:
|
||||
See LICENSE file accompanying this project.
|
||||
|
||||
Author:
|
||||
Terry Moore, MCCI Corporation April 2018
|
||||
|
||||
*/
|
||||
|
||||
#include <lmic.h>
|
||||
|
||||
# define STATIC_ASSERT(e) \
|
||||
void STATIC_ASSERT__(int MCCIADK_C_ASSERT_x[(e) ? 1: -1])
|
||||
|
||||
STATIC_ASSERT(ARDUINO_LMIC_VERSION >= ARDUINO_LMIC_VERSION_CALC(2,1,5,0));
|
||||
|
||||
STATIC_ASSERT(ARDUINO_LMIC_VERSION_CALC(1,2,3,4) == 0x01020304);
|
||||
|
||||
STATIC_ASSERT(ARDUINO_LMIC_VERSION_GET_MAJOR(ARDUINO_LMIC_VERSION_CALC(1,2,3,4)) == 1);
|
||||
STATIC_ASSERT(ARDUINO_LMIC_VERSION_GET_MINOR(ARDUINO_LMIC_VERSION_CALC(1,2,3,4)) == 2);
|
||||
STATIC_ASSERT(ARDUINO_LMIC_VERSION_GET_PATCH(ARDUINO_LMIC_VERSION_CALC(1,2,3,4)) == 3);
|
||||
STATIC_ASSERT(ARDUINO_LMIC_VERSION_GET_LOCAL(ARDUINO_LMIC_VERSION_CALC(1,2,3,4)) == 4);
|
||||
|
||||
void setup()
|
||||
{
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
}
|
@ -0,0 +1,441 @@
|
||||
/*
|
||||
|
||||
Module: raw-feather.ino
|
||||
|
||||
Function:
|
||||
Slightly improved Raw test example, for Adafruit Feather M0 LoRa
|
||||
|
||||
Copyright notice and License:
|
||||
See LICENSE file accompanying this project.
|
||||
|
||||
Author:
|
||||
Matthijs Kooijman 2015
|
||||
Terry Moore, MCCI Corporation April 2017
|
||||
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2015 Matthijs Kooijman
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to anyone
|
||||
* obtaining a copy of this document and accompanying files,
|
||||
* to do whatever they want with them without any restriction,
|
||||
* including, but not limited to, copying, modification and redistribution.
|
||||
* NO WARRANTY OF ANY KIND IS PROVIDED.
|
||||
*
|
||||
* This example transmits data on hardcoded channel and receives data
|
||||
* when not transmitting. Running this sketch on two nodes should allow
|
||||
* them to communicate.
|
||||
*******************************************************************************/
|
||||
|
||||
#include <lmic.h>
|
||||
#include <hal/hal.h>
|
||||
#include <SPI.h>
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// we formerly would check this configuration; but now there is a flag,
|
||||
// in the LMIC, LMIC.noRXIQinversion;
|
||||
// if we set that during init, we get the same effect. If
|
||||
// DISABLE_INVERT_IQ_ON_RX is defined, it means that LMIC.noRXIQinversion is
|
||||
// treated as always set.
|
||||
//
|
||||
// #if !defined(DISABLE_INVERT_IQ_ON_RX)
|
||||
// #error This example requires DISABLE_INVERT_IQ_ON_RX to be set. Update \
|
||||
// lmic_project_config.h in arduino-lmic/project_config to set it.
|
||||
// #endif
|
||||
|
||||
// How often to send a packet. Note that this sketch bypasses the normal
|
||||
// LMIC duty cycle limiting, so when you change anything in this sketch
|
||||
// (payload length, frequency, spreading factor), be sure to check if
|
||||
// this interval should not also be increased.
|
||||
// See this spreadsheet for an easy airtime and duty cycle calculator:
|
||||
// https://docs.google.com/spreadsheets/d/1voGAtQAjC1qBmaVuP1ApNKs1ekgUjavHuVQIXyYSvNc
|
||||
|
||||
#define TX_INTERVAL 2000 // milliseconds
|
||||
#define RX_RSSI_INTERVAL 100 // milliseconds
|
||||
|
||||
// Pin mapping for Adafruit Feather M0 LoRa, etc.
|
||||
#if defined(ARDUINO_SAMD_FEATHER_M0)
|
||||
const lmic_pinmap lmic_pins = {
|
||||
.nss = 8,
|
||||
.rxtx = LMIC_UNUSED_PIN,
|
||||
.rst = 4,
|
||||
.dio = {3, 6, LMIC_UNUSED_PIN},
|
||||
.rxtx_rx_active = 0,
|
||||
.rssi_cal = 8, // LBT cal for the Adafruit Feather M0 LoRa, in dB
|
||||
.spi_freq = 8000000,
|
||||
};
|
||||
#elif defined(ARDUINO_AVR_FEATHER32U4)
|
||||
// Pin mapping for Adafruit Feather 32u4 LoRa, etc.
|
||||
// Just like Feather M0 LoRa, but uses SPI at 1MHz; and that's only
|
||||
// because MCCI doesn't have a test board; probably higher frequencies
|
||||
// will work.
|
||||
const lmic_pinmap lmic_pins = {
|
||||
.nss = 8,
|
||||
.rxtx = LMIC_UNUSED_PIN,
|
||||
.rst = 4,
|
||||
.dio = {3, 6, LMIC_UNUSED_PIN},
|
||||
.rxtx_rx_active = 0,
|
||||
.rssi_cal = 8, // LBT cal for the Adafruit Feather M0 LoRa, in dB
|
||||
.spi_freq = 1000000,
|
||||
};
|
||||
#elif defined(ARDUINO_CATENA_4551)
|
||||
const lmic_pinmap lmic_pins = {
|
||||
.nss = 7,
|
||||
.rxtx = 29,
|
||||
.rst = 8,
|
||||
.dio = { 25, // DIO0 (IRQ) is D25
|
||||
26, // DIO1 is D26
|
||||
27, // DIO2 is D27
|
||||
},
|
||||
.rxtx_rx_active = 1,
|
||||
.rssi_cal = 10,
|
||||
.spi_freq = 8000000 // 8MHz
|
||||
};
|
||||
#else
|
||||
# error "Unknown target"
|
||||
#endif
|
||||
|
||||
// These callbacks are only used in over-the-air activation, so they are
|
||||
// left empty here (we cannot leave them out completely unless
|
||||
// DISABLE_JOIN is set in arduino-lmoc/project_config/lmic_project_config.h,
|
||||
// otherwise the linker will complain).
|
||||
void os_getArtEui (u1_t* buf) { }
|
||||
void os_getDevEui (u1_t* buf) { }
|
||||
void os_getDevKey (u1_t* buf) { }
|
||||
|
||||
// this gets callled by the library but we choose not to display any info;
|
||||
// and no action is required.
|
||||
void onEvent (ev_t ev) {
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
void lmic_printf(const char *fmt, ...);
|
||||
};
|
||||
|
||||
void lmic_printf(const char *fmt, ...) {
|
||||
if (! Serial.dtr())
|
||||
return;
|
||||
|
||||
char buf[256];
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
(void) vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
// in case we overflowed:
|
||||
buf[sizeof(buf) - 1] = '\0';
|
||||
if (Serial.dtr()) Serial.print(buf);
|
||||
}
|
||||
|
||||
osjob_t txjob;
|
||||
osjob_t timeoutjob;
|
||||
static void tx_func (osjob_t* job);
|
||||
|
||||
// Transmit the given string and call the given function afterwards
|
||||
void tx(const char *str, osjobcb_t func) {
|
||||
// the radio is probably in RX mode; stop it.
|
||||
os_radio(RADIO_RST);
|
||||
// wait a bit so the radio can come out of RX mode
|
||||
delay(1);
|
||||
|
||||
// prepare data
|
||||
LMIC.dataLen = 0;
|
||||
while (*str)
|
||||
LMIC.frame[LMIC.dataLen++] = *str++;
|
||||
|
||||
// set completion function.
|
||||
LMIC.osjob.func = func;
|
||||
|
||||
// start the transmission
|
||||
os_radio(RADIO_TX);
|
||||
Serial.println("TX");
|
||||
}
|
||||
|
||||
// Enable rx mode and call func when a packet is received
|
||||
void rx(osjobcb_t func) {
|
||||
LMIC.osjob.func = func;
|
||||
LMIC.rxtime = os_getTime(); // RX _now_
|
||||
// Enable "continuous" RX (e.g. without a timeout, still stops after
|
||||
// receiving a packet)
|
||||
os_radio(RADIO_RXON);
|
||||
Serial.println("RX");
|
||||
}
|
||||
|
||||
static void rxtimeout_func(osjob_t *job) {
|
||||
digitalWrite(LED_BUILTIN, LOW); // off
|
||||
}
|
||||
|
||||
static void rx_func (osjob_t* job) {
|
||||
// Blink once to confirm reception and then keep the led on
|
||||
digitalWrite(LED_BUILTIN, LOW); // off
|
||||
delay(10);
|
||||
digitalWrite(LED_BUILTIN, HIGH); // on
|
||||
|
||||
// Timeout RX (i.e. update led status) after 3 periods without RX
|
||||
os_setTimedCallback(&timeoutjob, os_getTime() + ms2osticks(3*TX_INTERVAL), rxtimeout_func);
|
||||
|
||||
// Reschedule TX so that it should not collide with the other side's
|
||||
// next TX
|
||||
os_setTimedCallback(&txjob, os_getTime() + ms2osticks(TX_INTERVAL/2), tx_func);
|
||||
|
||||
Serial.print("Got ");
|
||||
Serial.print(LMIC.dataLen);
|
||||
Serial.println(" bytes");
|
||||
Serial.write(LMIC.frame, LMIC.dataLen);
|
||||
Serial.println();
|
||||
|
||||
// Restart RX
|
||||
rx(rx_func);
|
||||
}
|
||||
|
||||
static void txdone_func (osjob_t* job) {
|
||||
rx(rx_func);
|
||||
}
|
||||
|
||||
// log text to USART and toggle LED
|
||||
static void tx_func (osjob_t* job) {
|
||||
// say hello
|
||||
tx("Hello, world!", txdone_func);
|
||||
// reschedule job every TX_INTERVAL (plus a bit of random to prevent
|
||||
// systematic collisions), unless packets are received, then rx_func
|
||||
// will reschedule at half this time.
|
||||
os_setTimedCallback(job, os_getTime() + ms2osticks(TX_INTERVAL + random(500)), tx_func);
|
||||
}
|
||||
|
||||
// application entry point
|
||||
void setup() {
|
||||
// delay(3000) makes recovery from botched images much easier, as it
|
||||
// gives the host time to break in to start a download. Without it,
|
||||
// you get to the crash before the host can break in.
|
||||
delay(3000);
|
||||
|
||||
// even after the delay, we wait for the host to open the port. operator
|
||||
// bool(Serial) just checks dtr(), and it tosses in a 10ms delay.
|
||||
while(! Serial.dtr())
|
||||
/* wait for the PC */;
|
||||
|
||||
Serial.begin(115200);
|
||||
Serial.println("Starting");
|
||||
|
||||
#ifdef VCC_ENABLE
|
||||
// For Pinoccio Scout boards
|
||||
pinMode(VCC_ENABLE, OUTPUT);
|
||||
digitalWrite(VCC_ENABLE, HIGH);
|
||||
delay(1000);
|
||||
#endif
|
||||
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
|
||||
// initialize runtime env
|
||||
os_init();
|
||||
|
||||
// Set up these settings once, and use them for both TX and RX
|
||||
#ifdef ARDUINO_ARCH_STM32
|
||||
LMIC_setClockError(10*65536/100);
|
||||
#endif
|
||||
|
||||
#if defined(CFG_eu868)
|
||||
// Use a frequency in the g3 which allows 10% duty cycling.
|
||||
LMIC.freq = 869525000;
|
||||
// Use a medium spread factor. This can be increased up to SF12 for
|
||||
// better range, but then, the interval should be (significantly)
|
||||
// raised to comply with duty cycle limits as well.
|
||||
LMIC.datarate = DR_SF9;
|
||||
// Maximum TX power
|
||||
LMIC.txpow = 27;
|
||||
#elif defined(CFG_us915)
|
||||
// make it easier for test, by pull the parameters up to the top of the
|
||||
// block. Ideally, we'd use the serial port to drive this; or have
|
||||
// a voting protocol where one side is elected the controller and
|
||||
// guides the responder through all the channels, powers, ramps
|
||||
// the transmit power from min to max, and measures the RSSI and SNR.
|
||||
// Even more amazing would be a scheme where the controller could
|
||||
// handle multiple nodes; in that case we'd have a way to do
|
||||
// production test and qualification. However, using an RWC5020A
|
||||
// is a much better use of development time.
|
||||
|
||||
// set fDownlink true to use a downlink channel; false
|
||||
// to use an uplink channel. Generally speaking, uplink
|
||||
// is more interesting, because you can prove that gateways
|
||||
// *should* be able to hear you.
|
||||
const static bool fDownlink = false;
|
||||
|
||||
// the downlink channel to be used.
|
||||
const static uint8_t kDownlinkChannel = 3;
|
||||
|
||||
// the uplink channel to be used.
|
||||
const static uint8_t kUplinkChannel = 8 + 3;
|
||||
|
||||
// this is automatically set to the proper bandwidth in kHz,
|
||||
// based on the selected channel.
|
||||
uint32_t uBandwidth;
|
||||
|
||||
if (! fDownlink)
|
||||
{
|
||||
if (kUplinkChannel < 64)
|
||||
{
|
||||
LMIC.freq = US915_125kHz_UPFBASE +
|
||||
kUplinkChannel * US915_125kHz_UPFSTEP;
|
||||
uBandwidth = 125;
|
||||
}
|
||||
else
|
||||
{
|
||||
LMIC.freq = US915_500kHz_UPFBASE +
|
||||
(kUplinkChannel - 64) * US915_500kHz_UPFSTEP;
|
||||
uBandwidth = 500;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// downlink channel
|
||||
LMIC.freq = US915_500kHz_DNFBASE +
|
||||
kDownlinkChannel * US915_500kHz_DNFSTEP;
|
||||
uBandwidth = 500;
|
||||
}
|
||||
|
||||
// Use a suitable spreading factor
|
||||
if (uBandwidth < 500)
|
||||
LMIC.datarate = US915_DR_SF7; // DR4
|
||||
else
|
||||
LMIC.datarate = US915_DR_SF12CR; // DR8
|
||||
|
||||
// default tx power for US: 21 dBm
|
||||
LMIC.txpow = 21;
|
||||
#elif defined(CFG_au921)
|
||||
// make it easier for test, by pull the parameters up to the top of the
|
||||
// block. Ideally, we'd use the serial port to drive this; or have
|
||||
// a voting protocol where one side is elected the controller and
|
||||
// guides the responder through all the channels, powers, ramps
|
||||
// the transmit power from min to max, and measures the RSSI and SNR.
|
||||
// Even more amazing would be a scheme where the controller could
|
||||
// handle multiple nodes; in that case we'd have a way to do
|
||||
// production test and qualification. However, using an RWC5020A
|
||||
// is a much better use of development time.
|
||||
|
||||
// set fDownlink true to use a downlink channel; false
|
||||
// to use an uplink channel. Generally speaking, uplink
|
||||
// is more interesting, because you can prove that gateways
|
||||
// *should* be able to hear you.
|
||||
const static bool fDownlink = false;
|
||||
|
||||
// the downlink channel to be used.
|
||||
const static uint8_t kDownlinkChannel = 3;
|
||||
|
||||
// the uplink channel to be used.
|
||||
const static uint8_t kUplinkChannel = 8 + 3;
|
||||
|
||||
// this is automatically set to the proper bandwidth in kHz,
|
||||
// based on the selected channel.
|
||||
uint32_t uBandwidth;
|
||||
|
||||
if (! fDownlink)
|
||||
{
|
||||
if (kUplinkChannel < 64)
|
||||
{
|
||||
LMIC.freq = AU921_125kHz_UPFBASE +
|
||||
kUplinkChannel * AU921_125kHz_UPFSTEP;
|
||||
uBandwidth = 125;
|
||||
}
|
||||
else
|
||||
{
|
||||
LMIC.freq = AU921_500kHz_UPFBASE +
|
||||
(kUplinkChannel - 64) * AU921_500kHz_UPFSTEP;
|
||||
uBandwidth = 500;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// downlink channel
|
||||
LMIC.freq = AU921_500kHz_DNFBASE +
|
||||
kDownlinkChannel * AU921_500kHz_DNFSTEP;
|
||||
uBandwidth = 500;
|
||||
}
|
||||
|
||||
// Use a suitable spreading factor
|
||||
if (uBandwidth < 500)
|
||||
LMIC.datarate = AU921_DR_SF7; // DR4
|
||||
else
|
||||
LMIC.datarate = AU921_DR_SF12CR; // DR8
|
||||
|
||||
// default tx power for AU: 30 dBm
|
||||
LMIC.txpow = 30;
|
||||
#elif defined(CFG_as923)
|
||||
// make it easier for test, by pull the parameters up to the top of the
|
||||
// block. Ideally, we'd use the serial port to drive this; or have
|
||||
// a voting protocol where one side is elected the controller and
|
||||
// guides the responder through all the channels, powers, ramps
|
||||
// the transmit power from min to max, and measures the RSSI and SNR.
|
||||
// Even more amazing would be a scheme where the controller could
|
||||
// handle multiple nodes; in that case we'd have a way to do
|
||||
// production test and qualification. However, using an RWC5020A
|
||||
// is a much better use of development time.
|
||||
const static uint8_t kChannel = 0;
|
||||
uint32_t uBandwidth;
|
||||
|
||||
LMIC.freq = AS923_F1 + kChannel * 200000;
|
||||
uBandwidth = 125;
|
||||
|
||||
// Use a suitable spreading factor
|
||||
if (uBandwidth == 125)
|
||||
LMIC.datarate = AS923_DR_SF7; // DR7
|
||||
else
|
||||
LMIC.datarate = AS923_DR_SF7B; // DR8
|
||||
|
||||
// default tx power for AS: 21 dBm
|
||||
LMIC.txpow = 16;
|
||||
|
||||
if (LMIC_COUNTRY_CODE == LMIC_COUNTRY_CODE_JP)
|
||||
{
|
||||
LMIC.lbt_ticks = us2osticks(AS923JP_LBT_US);
|
||||
LMIC.lbt_dbmax = AS923JP_LBT_DB_MAX;
|
||||
}
|
||||
#elif defined(CFG_in866)
|
||||
// make it easier for test, by pull the parameters up to the top of the
|
||||
// block. Ideally, we'd use the serial port to drive this; or have
|
||||
// a voting protocol where one side is elected the controller and
|
||||
// guides the responder through all the channels, powers, ramps
|
||||
// the transmit power from min to max, and measures the RSSI and SNR.
|
||||
// Even more amazing would be a scheme where the controller could
|
||||
// handle multiple nodes; in that case we'd have a way to do
|
||||
// production test and qualification. However, using an RWC5020A
|
||||
// is a much better use of development time.
|
||||
const static uint8_t kChannel = 0;
|
||||
uint32_t uBandwidth;
|
||||
|
||||
LMIC.freq = IN866_F1 + kChannel * 200000;
|
||||
uBandwidth = 125;
|
||||
|
||||
LMIC.datarate = IN866_DR_SF7; // DR7
|
||||
// default tx power for IN: 30 dBm
|
||||
LMIC.txpow = IN866_TX_EIRP_MAX_DBM;
|
||||
#else
|
||||
# error Unsupported LMIC regional configuration.
|
||||
#endif
|
||||
|
||||
|
||||
// disable RX IQ inversion
|
||||
LMIC.noRXIQinversion = true;
|
||||
|
||||
// This sets CR 4/5, BW125 (except for EU/AS923 DR_SF7B, which uses BW250)
|
||||
LMIC.rps = updr2rps(LMIC.datarate);
|
||||
|
||||
Serial.print("Frequency: "); Serial.print(LMIC.freq / 1000000);
|
||||
Serial.print("."); Serial.print((LMIC.freq / 100000) % 10);
|
||||
Serial.print("MHz");
|
||||
Serial.print(" LMIC.datarate: "); Serial.print(LMIC.datarate);
|
||||
Serial.print(" LMIC.txpow: "); Serial.println(LMIC.txpow);
|
||||
Serial.println("Started");
|
||||
Serial.flush();
|
||||
|
||||
// setup initial job
|
||||
os_setCallback(&txjob, tx_func);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// execute scheduled jobs and events
|
||||
os_runloop_once();
|
||||
}
|
175
lib/arduino-lmic-mcci-v2.2.2/examples/raw/raw.ino
Normal file
175
lib/arduino-lmic-mcci-v2.2.2/examples/raw/raw.ino
Normal file
@ -0,0 +1,175 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2015 Matthijs Kooijman
|
||||
* Copyright (c) 2018 Terry Moore, MCCI Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to anyone
|
||||
* obtaining a copy of this document and accompanying files,
|
||||
* to do whatever they want with them without any restriction,
|
||||
* including, but not limited to, copying, modification and redistribution.
|
||||
* NO WARRANTY OF ANY KIND IS PROVIDED.
|
||||
*
|
||||
* This example transmits data on hardcoded channel and receives data
|
||||
* when not transmitting. Running this sketch on two nodes should allow
|
||||
* them to communicate.
|
||||
*******************************************************************************/
|
||||
|
||||
#include <lmic.h>
|
||||
#include <hal/hal.h>
|
||||
#include <SPI.h>
|
||||
|
||||
// we formerly would check this configuration; but now there is a flag,
|
||||
// in the LMIC, LMIC.noRXIQinversion;
|
||||
// if we set that during init, we get the same effect. If
|
||||
// DISABLE_INVERT_IQ_ON_RX is defined, it means that LMIC.noRXIQinversion is
|
||||
// treated as always set.
|
||||
//
|
||||
// #if !defined(DISABLE_INVERT_IQ_ON_RX)
|
||||
// #error This example requires DISABLE_INVERT_IQ_ON_RX to be set. Update \
|
||||
// lmic_project_config.h in arduino-lmic/project_config to set it.
|
||||
// #endif
|
||||
|
||||
// How often to send a packet. Note that this sketch bypasses the normal
|
||||
// LMIC duty cycle limiting, so when you change anything in this sketch
|
||||
// (payload length, frequency, spreading factor), be sure to check if
|
||||
// this interval should not also be increased.
|
||||
// See this spreadsheet for an easy airtime and duty cycle calculator:
|
||||
// https://docs.google.com/spreadsheets/d/1voGAtQAjC1qBmaVuP1ApNKs1ekgUjavHuVQIXyYSvNc
|
||||
#define TX_INTERVAL 2000
|
||||
|
||||
// Pin mapping
|
||||
const lmic_pinmap lmic_pins = {
|
||||
.nss = 6,
|
||||
.rxtx = LMIC_UNUSED_PIN,
|
||||
.rst = 5,
|
||||
.dio = {2, 3, 4},
|
||||
};
|
||||
|
||||
|
||||
// These callbacks are only used in over-the-air activation, so they are
|
||||
// left empty here (we cannot leave them out completely unless
|
||||
// DISABLE_JOIN is set in arduino-lmoc/project_config/lmic_project_config.h,
|
||||
// otherwise the linker will complain).
|
||||
void os_getArtEui (u1_t* buf) { }
|
||||
void os_getDevEui (u1_t* buf) { }
|
||||
void os_getDevKey (u1_t* buf) { }
|
||||
|
||||
void onEvent (ev_t ev) {
|
||||
}
|
||||
|
||||
osjob_t txjob;
|
||||
osjob_t timeoutjob;
|
||||
static void tx_func (osjob_t* job);
|
||||
|
||||
// Transmit the given string and call the given function afterwards
|
||||
void tx(const char *str, osjobcb_t func) {
|
||||
os_radio(RADIO_RST); // Stop RX first
|
||||
delay(1); // Wait a bit, without this os_radio below asserts, apparently because the state hasn't changed yet
|
||||
LMIC.dataLen = 0;
|
||||
while (*str)
|
||||
LMIC.frame[LMIC.dataLen++] = *str++;
|
||||
LMIC.osjob.func = func;
|
||||
os_radio(RADIO_TX);
|
||||
Serial.println("TX");
|
||||
}
|
||||
|
||||
// Enable rx mode and call func when a packet is received
|
||||
void rx(osjobcb_t func) {
|
||||
LMIC.osjob.func = func;
|
||||
LMIC.rxtime = os_getTime(); // RX _now_
|
||||
// Enable "continuous" RX (e.g. without a timeout, still stops after
|
||||
// receiving a packet)
|
||||
os_radio(RADIO_RXON);
|
||||
Serial.println("RX");
|
||||
}
|
||||
|
||||
static void rxtimeout_func(osjob_t *job) {
|
||||
digitalWrite(LED_BUILTIN, LOW); // off
|
||||
}
|
||||
|
||||
static void rx_func (osjob_t* job) {
|
||||
// Blink once to confirm reception and then keep the led on
|
||||
digitalWrite(LED_BUILTIN, LOW); // off
|
||||
delay(10);
|
||||
digitalWrite(LED_BUILTIN, HIGH); // on
|
||||
|
||||
// Timeout RX (i.e. update led status) after 3 periods without RX
|
||||
os_setTimedCallback(&timeoutjob, os_getTime() + ms2osticks(3*TX_INTERVAL), rxtimeout_func);
|
||||
|
||||
// Reschedule TX so that it should not collide with the other side's
|
||||
// next TX
|
||||
os_setTimedCallback(&txjob, os_getTime() + ms2osticks(TX_INTERVAL/2), tx_func);
|
||||
|
||||
Serial.print("Got ");
|
||||
Serial.print(LMIC.dataLen);
|
||||
Serial.println(" bytes");
|
||||
Serial.write(LMIC.frame, LMIC.dataLen);
|
||||
Serial.println();
|
||||
|
||||
// Restart RX
|
||||
rx(rx_func);
|
||||
}
|
||||
|
||||
static void txdone_func (osjob_t* job) {
|
||||
rx(rx_func);
|
||||
}
|
||||
|
||||
// log text to USART and toggle LED
|
||||
static void tx_func (osjob_t* job) {
|
||||
// say hello
|
||||
tx("Hello, world!", txdone_func);
|
||||
// reschedule job every TX_INTERVAL (plus a bit of random to prevent
|
||||
// systematic collisions), unless packets are received, then rx_func
|
||||
// will reschedule at half this time.
|
||||
os_setTimedCallback(job, os_getTime() + ms2osticks(TX_INTERVAL + random(500)), tx_func);
|
||||
}
|
||||
|
||||
// application entry point
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Starting");
|
||||
#ifdef VCC_ENABLE
|
||||
// For Pinoccio Scout boards
|
||||
pinMode(VCC_ENABLE, OUTPUT);
|
||||
digitalWrite(VCC_ENABLE, HIGH);
|
||||
delay(1000);
|
||||
#endif
|
||||
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
|
||||
// initialize runtime env
|
||||
os_init();
|
||||
|
||||
// Set up these settings once, and use them for both TX and RX
|
||||
|
||||
#if defined(CFG_eu868)
|
||||
// Use a frequency in the g3 which allows 10% duty cycling.
|
||||
LMIC.freq = 869525000;
|
||||
#elif defined(CFG_us915)
|
||||
LMIC.freq = 902300000;
|
||||
#else
|
||||
error Region not supported!
|
||||
#endif
|
||||
|
||||
// Maximum TX power
|
||||
LMIC.txpow = 27;
|
||||
// Use a medium spread factor. This can be increased up to SF12 for
|
||||
// better range, but then the interval should be (significantly)
|
||||
// lowered to comply with duty cycle limits as well.
|
||||
LMIC.datarate = DR_SF9;
|
||||
// This sets CR 4/5, BW125 (except for DR_SF7B, which uses BW250)
|
||||
LMIC.rps = updr2rps(LMIC.datarate);
|
||||
|
||||
// disable RX IQ inversion
|
||||
LMIC.noRXIQinversion = true;
|
||||
|
||||
Serial.println("Started");
|
||||
Serial.flush();
|
||||
|
||||
// setup initial job
|
||||
os_setCallback(&txjob, tx_func);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// execute scheduled jobs and events
|
||||
os_runloop_once();
|
||||
}
|
@ -0,0 +1,266 @@
|
||||
/*******************************************************************************
|
||||
* The Things Network - ABP Feather
|
||||
*
|
||||
* Example of using an Adafruit Feather M0 and DHT22 with a
|
||||
* single-channel TheThingsNetwork gateway.
|
||||
*
|
||||
* This uses ABP (Activation by Personalization), where session keys for
|
||||
* communication would be assigned/generated by TTN and hard-coded on the device.
|
||||
*
|
||||
* Learn Guide: https://learn.adafruit.com/lora-pi
|
||||
*
|
||||
* Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman
|
||||
* Copyright (c) 2018 Terry Moore, MCCI
|
||||
* Copyright (c) 2018 Brent Rubell, Adafruit Industries
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to anyone
|
||||
* obtaining a copy of this document and accompanying files,
|
||||
* to do whatever they want with them without any restriction,
|
||||
* including, but not limited to, copying, modification and redistribution.
|
||||
* NO WARRANTY OF ANY KIND IS PROVIDED.
|
||||
*******************************************************************************/
|
||||
#include <lmic.h>
|
||||
#include <hal/hal.h>
|
||||
#include <SPI.h>
|
||||
|
||||
// include the DHT22 Sensor Library
|
||||
#include "DHT.h"
|
||||
|
||||
// DHT digital pin and sensor type
|
||||
#define DHTPIN 10
|
||||
#define DHTTYPE DHT22
|
||||
|
||||
//
|
||||
// For normal use, we require that you edit the sketch to replace FILLMEIN
|
||||
// with values assigned by the TTN console. However, for regression tests,
|
||||
// we want to be able to compile these scripts. The regression tests define
|
||||
// COMPILE_REGRESSION_TEST, and in that case we define FILLMEIN to a non-
|
||||
// working but innocuous value.
|
||||
//
|
||||
#ifdef COMPILE_REGRESSION_TEST
|
||||
# define FILLMEIN 0
|
||||
#else
|
||||
# warning "You must replace the values marked FILLMEIN with real values from the TTN control panel!"
|
||||
# define FILLMEIN (#dont edit this, edit the lines that use FILLMEIN)
|
||||
#endif
|
||||
|
||||
// LoRaWAN NwkSKey, network session key
|
||||
static const PROGMEM u1_t NWKSKEY[16] = { FILLMEIN };
|
||||
|
||||
// LoRaWAN AppSKey, application session key
|
||||
static const u1_t PROGMEM APPSKEY[16] = { FILLMEIN };
|
||||
|
||||
// LoRaWAN end-device address (DevAddr)
|
||||
// See http://thethingsnetwork.org/wiki/AddressSpace
|
||||
// The library converts the address to network byte order as needed.
|
||||
#ifndef COMPILE_REGRESSION_TEST
|
||||
static const u4_t DEVADDR = 0xFILLMEIN;
|
||||
#else
|
||||
static const u4_t DEVADDR = 0;
|
||||
#endif
|
||||
|
||||
// These callbacks are only used in over-the-air activation, so they are
|
||||
// left empty here (we cannot leave them out completely unless
|
||||
// DISABLE_JOIN is set in arduino-lmic/project_config/lmic_project_config.h,
|
||||
// otherwise the linker will complain).
|
||||
void os_getArtEui (u1_t* buf) { }
|
||||
void os_getDevEui (u1_t* buf) { }
|
||||
void os_getDevKey (u1_t* buf) { }
|
||||
|
||||
// payload to send to TTN gateway
|
||||
static uint8_t payload[5];
|
||||
static osjob_t sendjob;
|
||||
|
||||
// Schedule TX every this many seconds (might become longer due to duty
|
||||
// cycle limitations).
|
||||
const unsigned TX_INTERVAL = 30;
|
||||
|
||||
// Pin mapping for Adafruit Feather M0 LoRa
|
||||
const lmic_pinmap lmic_pins = {
|
||||
.nss = 8,
|
||||
.rxtx = LMIC_UNUSED_PIN,
|
||||
.rst = 4,
|
||||
.dio = {3, 6, LMIC_UNUSED_PIN},
|
||||
.rxtx_rx_active = 0,
|
||||
.rssi_cal = 8, // LBT cal for the Adafruit Feather M0 LoRa, in dB
|
||||
.spi_freq = 8000000,
|
||||
};
|
||||
|
||||
// init. DHT
|
||||
DHT dht(DHTPIN, DHTTYPE);
|
||||
|
||||
void onEvent (ev_t ev) {
|
||||
Serial.print(os_getTime());
|
||||
Serial.print(": ");
|
||||
switch(ev) {
|
||||
case EV_SCAN_TIMEOUT:
|
||||
Serial.println(F("EV_SCAN_TIMEOUT"));
|
||||
break;
|
||||
case EV_BEACON_FOUND:
|
||||
Serial.println(F("EV_BEACON_FOUND"));
|
||||
break;
|
||||
case EV_BEACON_MISSED:
|
||||
Serial.println(F("EV_BEACON_MISSED"));
|
||||
break;
|
||||
case EV_BEACON_TRACKED:
|
||||
Serial.println(F("EV_BEACON_TRACKED"));
|
||||
break;
|
||||
case EV_JOINING:
|
||||
Serial.println(F("EV_JOINING"));
|
||||
break;
|
||||
case EV_JOINED:
|
||||
Serial.println(F("EV_JOINED"));
|
||||
break;
|
||||
/*
|
||||
|| This event is defined but not used in the code. No
|
||||
|| point in wasting codespace on it.
|
||||
||
|
||||
|| case EV_RFU1:
|
||||
|| Serial.println(F("EV_RFU1"));
|
||||
|| break;
|
||||
*/
|
||||
case EV_JOIN_FAILED:
|
||||
Serial.println(F("EV_JOIN_FAILED"));
|
||||
break;
|
||||
case EV_REJOIN_FAILED:
|
||||
Serial.println(F("EV_REJOIN_FAILED"));
|
||||
break;
|
||||
case EV_TXCOMPLETE:
|
||||
Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
|
||||
if (LMIC.txrxFlags & TXRX_ACK)
|
||||
Serial.println(F("Received ack"));
|
||||
if (LMIC.dataLen) {
|
||||
Serial.println(F("Received "));
|
||||
Serial.println(LMIC.dataLen);
|
||||
Serial.println(F(" bytes of payload"));
|
||||
}
|
||||
// Schedule next transmission
|
||||
os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send);
|
||||
break;
|
||||
case EV_LOST_TSYNC:
|
||||
Serial.println(F("EV_LOST_TSYNC"));
|
||||
break;
|
||||
case EV_RESET:
|
||||
Serial.println(F("EV_RESET"));
|
||||
break;
|
||||
case EV_RXCOMPLETE:
|
||||
// data received in ping slot
|
||||
Serial.println(F("EV_RXCOMPLETE"));
|
||||
break;
|
||||
case EV_LINK_DEAD:
|
||||
Serial.println(F("EV_LINK_DEAD"));
|
||||
break;
|
||||
case EV_LINK_ALIVE:
|
||||
Serial.println(F("EV_LINK_ALIVE"));
|
||||
break;
|
||||
/*
|
||||
|| This event is defined but not used in the code. No
|
||||
|| point in wasting codespace on it.
|
||||
||
|
||||
|| case EV_SCAN_FOUND:
|
||||
|| Serial.println(F("EV_SCAN_FOUND"));
|
||||
|| break;
|
||||
*/
|
||||
case EV_TXSTART:
|
||||
Serial.println(F("EV_TXSTART"));
|
||||
break;
|
||||
default:
|
||||
Serial.print(F("Unknown event: "));
|
||||
Serial.println((unsigned) ev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void do_send(osjob_t* j){
|
||||
// Check if there is not a current TX/RX job running
|
||||
if (LMIC.opmode & OP_TXRXPEND) {
|
||||
Serial.println(F("OP_TXRXPEND, not sending"));
|
||||
} else {
|
||||
// read the temperature from the DHT22
|
||||
float temperature = dht.readTemperature();
|
||||
Serial.print("Temperature: "); Serial.print(temperature);
|
||||
Serial.println(" *C");
|
||||
// adjust for the f2sflt16 range (-1 to 1)
|
||||
temperature = temperature / 100;
|
||||
|
||||
// read the humidity from the DHT22
|
||||
float rHumidity = dht.readHumidity();
|
||||
Serial.print("%RH ");
|
||||
Serial.println(rHumidity);
|
||||
// adjust for the f2sflt16 range (-1 to 1)
|
||||
rHumidity = rHumidity / 100;
|
||||
|
||||
// float -> int
|
||||
// note: this uses the sflt16 datum (https://github.com/mcci-catena/arduino-lmic#sflt16)
|
||||
uint16_t payloadTemp = LMIC_f2sflt16(temperature);
|
||||
// int -> bytes
|
||||
byte tempLow = lowByte(payloadTemp);
|
||||
byte tempHigh = highByte(payloadTemp);
|
||||
// place the bytes into the payload
|
||||
payload[0] = tempLow;
|
||||
payload[1] = tempHigh;
|
||||
|
||||
// float -> int
|
||||
uint16_t payloadHumid = LMIC_f2sflt16(rHumidity);
|
||||
// int -> bytes
|
||||
byte humidLow = lowByte(payloadHumid);
|
||||
byte humidHigh = highByte(payloadHumid);
|
||||
payload[2] = humidLow;
|
||||
payload[3] = humidHigh;
|
||||
|
||||
// prepare upstream data transmission at the next possible time.
|
||||
// transmit on port 1 (the first parameter); you can use any value from 1 to 223 (others are reserved).
|
||||
// don't request an ack (the last parameter, if not zero, requests an ack from the network).
|
||||
// Remember, acks consume a lot of network resources; don't ask for an ack unless you really need it.
|
||||
LMIC_setTxData2(1, payload, sizeof(payload)-1, 0);
|
||||
}
|
||||
// Next TX is scheduled after TX_COMPLETE event.
|
||||
}
|
||||
|
||||
void setup() {
|
||||
delay(5000);
|
||||
while (!Serial);
|
||||
Serial.begin(115200);
|
||||
delay(100);
|
||||
Serial.println(F("Starting"));
|
||||
|
||||
// LMIC init
|
||||
os_init();
|
||||
// Reset the MAC state. Session and pending data transfers will be discarded.
|
||||
LMIC_reset();
|
||||
|
||||
// Set static session parameters. Instead of dynamically establishing a session
|
||||
// by joining the network, precomputed session parameters are be provided.
|
||||
// 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 (0x13, DEVADDR, nwkskey, appskey);
|
||||
|
||||
// We'll disable all 72 channels used by TTN
|
||||
for (int c = 0; c < 72; c++){
|
||||
LMIC_disableChannel(c);
|
||||
}
|
||||
|
||||
// We'll only enable Channel 16 (905.5Mhz) since we're transmitting on a single-channel
|
||||
LMIC_enableChannel(16);
|
||||
|
||||
// Disable link check validation
|
||||
LMIC_setLinkCheckMode(0);
|
||||
|
||||
// TTN uses SF9 for its RX2 window.
|
||||
LMIC.dn2Dr = DR_SF9;
|
||||
|
||||
// Set data rate and transmit power for uplink (note: txpow seems to be ignored by the library)
|
||||
LMIC_setDrTxpow(DR_SF7,14);
|
||||
|
||||
// Start job
|
||||
do_send(&sendjob);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
os_runloop_once();
|
||||
}
|
276
lib/arduino-lmic-mcci-v2.2.2/examples/ttn-abp/ttn-abp.ino
Normal file
276
lib/arduino-lmic-mcci-v2.2.2/examples/ttn-abp/ttn-abp.ino
Normal file
@ -0,0 +1,276 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman
|
||||
* Copyright (c) 2018 Terry Moore, MCCI
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to anyone
|
||||
* obtaining a copy of this document and accompanying files,
|
||||
* to do whatever they want with them without any restriction,
|
||||
* including, but not limited to, copying, modification and redistribution.
|
||||
* NO WARRANTY OF ANY KIND IS PROVIDED.
|
||||
*
|
||||
* This example sends a valid LoRaWAN packet with payload "Hello,
|
||||
* world!", using frequency and encryption settings matching those of
|
||||
* the The Things Network.
|
||||
*
|
||||
* This uses ABP (Activation-by-personalisation), where a DevAddr and
|
||||
* Session keys are preconfigured (unlike OTAA, where a DevEUI and
|
||||
* application key is configured, while the DevAddr and session keys are
|
||||
* assigned/generated in the over-the-air-activation procedure).
|
||||
*
|
||||
* Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in
|
||||
* g1, 0.1% in g2), but not the TTN fair usage policy (which is probably
|
||||
* violated by this sketch when left running for longer)!
|
||||
*
|
||||
* To use this sketch, first register your application and device with
|
||||
* the things network, to set or generate a DevAddr, NwkSKey and
|
||||
* AppSKey. Each device should have their own unique values for these
|
||||
* fields.
|
||||
*
|
||||
* Do not forget to define the radio type correctly in
|
||||
* arduino-lmic/project_config/lmic_project_config.h or from your BOARDS.txt.
|
||||
*
|
||||
*******************************************************************************/
|
||||
|
||||
// References:
|
||||
// [feather] adafruit-feather-m0-radio-with-lora-module.pdf
|
||||
|
||||
#include <lmic.h>
|
||||
#include <hal/hal.h>
|
||||
#include <SPI.h>
|
||||
|
||||
//
|
||||
// For normal use, we require that you edit the sketch to replace FILLMEIN
|
||||
// with values assigned by the TTN console. However, for regression tests,
|
||||
// we want to be able to compile these scripts. The regression tests define
|
||||
// COMPILE_REGRESSION_TEST, and in that case we define FILLMEIN to a non-
|
||||
// working but innocuous value.
|
||||
//
|
||||
#ifdef COMPILE_REGRESSION_TEST
|
||||
# define FILLMEIN 0
|
||||
#else
|
||||
# warning "You must replace the values marked FILLMEIN with real values from the TTN control panel!"
|
||||
# define FILLMEIN (#dont edit this, edit the lines that use FILLMEIN)
|
||||
#endif
|
||||
|
||||
// LoRaWAN NwkSKey, network session key
|
||||
static const PROGMEM u1_t NWKSKEY[16] = { FILLMEIN };
|
||||
|
||||
// LoRaWAN AppSKey, application session key
|
||||
static const u1_t PROGMEM APPSKEY[16] = { FILLMEIN };
|
||||
|
||||
// LoRaWAN end-device address (DevAddr)
|
||||
// See http://thethingsnetwork.org/wiki/AddressSpace
|
||||
// The library converts the address to network byte order as needed.
|
||||
static const u4_t DEVADDR = FILLMEIN ; // <-- Change this address for every node!
|
||||
|
||||
// These callbacks are only used in over-the-air activation, so they are
|
||||
// left empty here (we cannot leave them out completely unless
|
||||
// DISABLE_JOIN is set in arduino-lmic/project_config/lmic_project_config.h,
|
||||
// otherwise the linker will complain).
|
||||
void os_getArtEui (u1_t* buf) { }
|
||||
void os_getDevEui (u1_t* buf) { }
|
||||
void os_getDevKey (u1_t* buf) { }
|
||||
|
||||
static uint8_t mydata[] = "Hello, world!";
|
||||
static osjob_t sendjob;
|
||||
|
||||
// Schedule TX every this many seconds (might become longer due to duty
|
||||
// cycle limitations).
|
||||
const unsigned TX_INTERVAL = 60;
|
||||
|
||||
// Pin mapping
|
||||
// Adapted for Feather M0 per p.10 of [feather]
|
||||
const lmic_pinmap lmic_pins = {
|
||||
.nss = 8, // chip select on feather (rf95module) CS
|
||||
.rxtx = LMIC_UNUSED_PIN,
|
||||
.rst = 4, // reset pin
|
||||
.dio = {6, 5, LMIC_UNUSED_PIN}, // assumes external jumpers [feather_lora_jumper]
|
||||
// DIO1 is on JP1-1: is io1 - we connect to GPO6
|
||||
// DIO1 is on JP5-3: is D2 - we connect to GPO5
|
||||
};
|
||||
|
||||
void onEvent (ev_t ev) {
|
||||
Serial.print(os_getTime());
|
||||
Serial.print(": ");
|
||||
switch(ev) {
|
||||
case EV_SCAN_TIMEOUT:
|
||||
Serial.println(F("EV_SCAN_TIMEOUT"));
|
||||
break;
|
||||
case EV_BEACON_FOUND:
|
||||
Serial.println(F("EV_BEACON_FOUND"));
|
||||
break;
|
||||
case EV_BEACON_MISSED:
|
||||
Serial.println(F("EV_BEACON_MISSED"));
|
||||
break;
|
||||
case EV_BEACON_TRACKED:
|
||||
Serial.println(F("EV_BEACON_TRACKED"));
|
||||
break;
|
||||
case EV_JOINING:
|
||||
Serial.println(F("EV_JOINING"));
|
||||
break;
|
||||
case EV_JOINED:
|
||||
Serial.println(F("EV_JOINED"));
|
||||
break;
|
||||
/*
|
||||
|| This event is defined but not used in the code. No
|
||||
|| point in wasting codespace on it.
|
||||
||
|
||||
|| case EV_RFU1:
|
||||
|| Serial.println(F("EV_RFU1"));
|
||||
|| break;
|
||||
*/
|
||||
case EV_JOIN_FAILED:
|
||||
Serial.println(F("EV_JOIN_FAILED"));
|
||||
break;
|
||||
case EV_REJOIN_FAILED:
|
||||
Serial.println(F("EV_REJOIN_FAILED"));
|
||||
break;
|
||||
case EV_TXCOMPLETE:
|
||||
Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
|
||||
if (LMIC.txrxFlags & TXRX_ACK)
|
||||
Serial.println(F("Received ack"));
|
||||
if (LMIC.dataLen) {
|
||||
Serial.println(F("Received "));
|
||||
Serial.println(LMIC.dataLen);
|
||||
Serial.println(F(" bytes of payload"));
|
||||
}
|
||||
// Schedule next transmission
|
||||
os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send);
|
||||
break;
|
||||
case EV_LOST_TSYNC:
|
||||
Serial.println(F("EV_LOST_TSYNC"));
|
||||
break;
|
||||
case EV_RESET:
|
||||
Serial.println(F("EV_RESET"));
|
||||
break;
|
||||
case EV_RXCOMPLETE:
|
||||
// data received in ping slot
|
||||
Serial.println(F("EV_RXCOMPLETE"));
|
||||
break;
|
||||
case EV_LINK_DEAD:
|
||||
Serial.println(F("EV_LINK_DEAD"));
|
||||
break;
|
||||
case EV_LINK_ALIVE:
|
||||
Serial.println(F("EV_LINK_ALIVE"));
|
||||
break;
|
||||
/*
|
||||
|| This event is defined but not used in the code. No
|
||||
|| point in wasting codespace on it.
|
||||
||
|
||||
|| case EV_SCAN_FOUND:
|
||||
|| Serial.println(F("EV_SCAN_FOUND"));
|
||||
|| break;
|
||||
*/
|
||||
case EV_TXSTART:
|
||||
Serial.println(F("EV_TXSTART"));
|
||||
break;
|
||||
default:
|
||||
Serial.print(F("Unknown event: "));
|
||||
Serial.println((unsigned) ev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void do_send(osjob_t* j){
|
||||
// Check if there is not a current TX/RX job running
|
||||
if (LMIC.opmode & OP_TXRXPEND) {
|
||||
Serial.println(F("OP_TXRXPEND, not sending"));
|
||||
} else {
|
||||
// Prepare upstream data transmission at the next possible time.
|
||||
LMIC_setTxData2(1, mydata, sizeof(mydata)-1, 0);
|
||||
Serial.println(F("Packet queued"));
|
||||
}
|
||||
// Next TX is scheduled after TX_COMPLETE event.
|
||||
}
|
||||
|
||||
void setup() {
|
||||
// pinMode(13, OUTPUT);
|
||||
while (!Serial); // wait for Serial to be initialized
|
||||
Serial.begin(115200);
|
||||
delay(100); // per sample code on RF_95 test
|
||||
Serial.println(F("Starting"));
|
||||
|
||||
#ifdef VCC_ENABLE
|
||||
// For Pinoccio Scout boards
|
||||
pinMode(VCC_ENABLE, OUTPUT);
|
||||
digitalWrite(VCC_ENABLE, HIGH);
|
||||
delay(1000);
|
||||
#endif
|
||||
|
||||
// LMIC init
|
||||
os_init();
|
||||
// Reset the MAC state. Session and pending data transfers will be discarded.
|
||||
LMIC_reset();
|
||||
|
||||
// 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 (0x13, DEVADDR, nwkskey, appskey);
|
||||
#else
|
||||
// If not running an AVR with PROGMEM, just use the arrays directly
|
||||
LMIC_setSession (0x13, 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.
|
||||
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)
|
||||
// NA-US 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);
|
||||
#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 for uplink (note: txpow seems to be ignored by the library)
|
||||
LMIC_setDrTxpow(DR_SF7,14);
|
||||
|
||||
// Start job
|
||||
do_send(&sendjob);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
unsigned long now;
|
||||
now = millis();
|
||||
if ((now & 512) != 0) {
|
||||
digitalWrite(13, HIGH);
|
||||
}
|
||||
else {
|
||||
digitalWrite(13, LOW);
|
||||
}
|
||||
|
||||
os_runloop_once();
|
||||
|
||||
}
|
@ -0,0 +1,274 @@
|
||||
/*******************************************************************************
|
||||
* The Things Network - Sensor Data Example
|
||||
*
|
||||
* Example of sending a valid LoRaWAN packet with DHT22 temperature and
|
||||
* humidity data to The Things Networ using a Feather M0 LoRa.
|
||||
*
|
||||
* Learn Guide: https://learn.adafruit.com/the-things-network-for-feather
|
||||
*
|
||||
* Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman
|
||||
* Copyright (c) 2018 Terry Moore, MCCI
|
||||
* Copyright (c) 2018 Brent Rubell, Adafruit Industries
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to anyone
|
||||
* obtaining a copy of this document and accompanying files,
|
||||
* to do whatever they want with them without any restriction,
|
||||
* including, but not limited to, copying, modification and redistribution.
|
||||
* NO WARRANTY OF ANY KIND IS PROVIDED.
|
||||
*******************************************************************************/
|
||||
#include <lmic.h>
|
||||
#include <hal/hal.h>
|
||||
#include <SPI.h>
|
||||
|
||||
// include the DHT22 Sensor Library
|
||||
#include "DHT.h"
|
||||
|
||||
// DHT digital pin and sensor type
|
||||
#define DHTPIN 10
|
||||
#define DHTTYPE DHT22
|
||||
|
||||
//
|
||||
// For normal use, we require that you edit the sketch to replace FILLMEIN
|
||||
// with values assigned by the TTN console. However, for regression tests,
|
||||
// we want to be able to compile these scripts. The regression tests define
|
||||
// COMPILE_REGRESSION_TEST, and in that case we define FILLMEIN to a non-
|
||||
// working but innocuous value.
|
||||
//
|
||||
#ifdef COMPILE_REGRESSION_TEST
|
||||
#define FILLMEIN 0
|
||||
#else
|
||||
#warning "You must replace the values marked FILLMEIN with real values from the TTN control panel!"
|
||||
#define FILLMEIN (#dont edit this, edit the lines that use FILLMEIN)
|
||||
#endif
|
||||
|
||||
// This EUI must be in little-endian format, so least-significant-byte
|
||||
// first. When copying an EUI from ttnctl output, this means to reverse
|
||||
// the bytes. For TTN issued EUIs the last bytes should be 0xD5, 0xB3,
|
||||
// 0x70.
|
||||
static const u1_t PROGMEM APPEUI[8] = { FILLMEIN };
|
||||
void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);}
|
||||
|
||||
// This should also be in little endian format, see above.
|
||||
static const u1_t PROGMEM DEVEUI[8] = { FILLMEIN };
|
||||
void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);}
|
||||
|
||||
// This key should be in big endian format (or, since it is not really a
|
||||
// number but a block of memory, endianness does not really apply). In
|
||||
// practice, a key taken from the TTN console can be copied as-is.
|
||||
static const u1_t PROGMEM APPKEY[16] = { FILLMEIN };
|
||||
void os_getDevKey (u1_t* buf) { memcpy_P(buf, APPKEY, 16);}
|
||||
|
||||
// payload to send to TTN gateway
|
||||
static uint8_t payload[5];
|
||||
static osjob_t sendjob;
|
||||
|
||||
// Schedule TX every this many seconds (might become longer due to duty
|
||||
// cycle limitations).
|
||||
const unsigned TX_INTERVAL = 30;
|
||||
|
||||
// Pin mapping for Adafruit Feather M0 LoRa
|
||||
const lmic_pinmap lmic_pins = {
|
||||
.nss = 8,
|
||||
.rxtx = LMIC_UNUSED_PIN,
|
||||
.rst = 4,
|
||||
.dio = {3, 6, LMIC_UNUSED_PIN},
|
||||
.rxtx_rx_active = 0,
|
||||
.rssi_cal = 8, // LBT cal for the Adafruit Feather M0 LoRa, in dB
|
||||
.spi_freq = 8000000,
|
||||
};
|
||||
|
||||
// init. DHT
|
||||
DHT dht(DHTPIN, DHTTYPE);
|
||||
|
||||
void onEvent (ev_t ev) {
|
||||
Serial.print(os_getTime());
|
||||
Serial.print(": ");
|
||||
switch(ev) {
|
||||
case EV_SCAN_TIMEOUT:
|
||||
Serial.println(F("EV_SCAN_TIMEOUT"));
|
||||
break;
|
||||
case EV_BEACON_FOUND:
|
||||
Serial.println(F("EV_BEACON_FOUND"));
|
||||
break;
|
||||
case EV_BEACON_MISSED:
|
||||
Serial.println(F("EV_BEACON_MISSED"));
|
||||
break;
|
||||
case EV_BEACON_TRACKED:
|
||||
Serial.println(F("EV_BEACON_TRACKED"));
|
||||
break;
|
||||
case EV_JOINING:
|
||||
Serial.println(F("EV_JOINING"));
|
||||
break;
|
||||
case EV_JOINED:
|
||||
Serial.println(F("EV_JOINED"));
|
||||
{
|
||||
u4_t netid = 0;
|
||||
devaddr_t devaddr = 0;
|
||||
u1_t nwkKey[16];
|
||||
u1_t artKey[16];
|
||||
LMIC_getSessionKeys(&netid, &devaddr, nwkKey, artKey);
|
||||
Serial.print("netid: ");
|
||||
Serial.println(netid, DEC);
|
||||
Serial.print("devaddr: ");
|
||||
Serial.println(devaddr, HEX);
|
||||
Serial.print("artKey: ");
|
||||
for (int i=0; i<sizeof(artKey); ++i) {
|
||||
if (i != 0)
|
||||
Serial.print("-");
|
||||
Serial.print(artKey[i], HEX);
|
||||
}
|
||||
Serial.println("");
|
||||
Serial.print("nwkKey: ");
|
||||
for (int i=0; i<sizeof(nwkKey); ++i) {
|
||||
if (i != 0)
|
||||
Serial.print("-");
|
||||
Serial.print(nwkKey[i], HEX);
|
||||
}
|
||||
Serial.println("");
|
||||
}
|
||||
// Disable link check validation (automatically enabled
|
||||
// during join, but because slow data rates change max TX
|
||||
// size, we don't use it in this example.
|
||||
LMIC_setLinkCheckMode(0);
|
||||
break;
|
||||
/*
|
||||
|| This event is defined but not used in the code. No
|
||||
|| point in wasting codespace on it.
|
||||
||
|
||||
|| case EV_RFU1:
|
||||
|| Serial.println(F("EV_RFU1"));
|
||||
|| break;
|
||||
*/
|
||||
case EV_JOIN_FAILED:
|
||||
Serial.println(F("EV_JOIN_FAILED"));
|
||||
break;
|
||||
case EV_REJOIN_FAILED:
|
||||
Serial.println(F("EV_REJOIN_FAILED"));
|
||||
break;
|
||||
break;
|
||||
case EV_TXCOMPLETE:
|
||||
Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
|
||||
if (LMIC.txrxFlags & TXRX_ACK)
|
||||
Serial.println(F("Received ack"));
|
||||
if (LMIC.dataLen) {
|
||||
Serial.println(F("Received "));
|
||||
Serial.println(LMIC.dataLen);
|
||||
Serial.println(F(" bytes of payload"));
|
||||
}
|
||||
// Schedule next transmission
|
||||
os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send);
|
||||
break;
|
||||
case EV_LOST_TSYNC:
|
||||
Serial.println(F("EV_LOST_TSYNC"));
|
||||
break;
|
||||
case EV_RESET:
|
||||
Serial.println(F("EV_RESET"));
|
||||
break;
|
||||
case EV_RXCOMPLETE:
|
||||
// data received in ping slot
|
||||
Serial.println(F("EV_RXCOMPLETE"));
|
||||
break;
|
||||
case EV_LINK_DEAD:
|
||||
Serial.println(F("EV_LINK_DEAD"));
|
||||
break;
|
||||
case EV_LINK_ALIVE:
|
||||
Serial.println(F("EV_LINK_ALIVE"));
|
||||
break;
|
||||
/*
|
||||
|| This event is defined but not used in the code. No
|
||||
|| point in wasting codespace on it.
|
||||
||
|
||||
|| case EV_SCAN_FOUND:
|
||||
|| Serial.println(F("EV_SCAN_FOUND"));
|
||||
|| break;
|
||||
*/
|
||||
case EV_TXSTART:
|
||||
Serial.println(F("EV_TXSTART"));
|
||||
break;
|
||||
default:
|
||||
Serial.print(F("Unknown event: "));
|
||||
Serial.println((unsigned) ev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void do_send(osjob_t* j){
|
||||
// Check if there is not a current TX/RX job running
|
||||
if (LMIC.opmode & OP_TXRXPEND) {
|
||||
Serial.println(F("OP_TXRXPEND, not sending"));
|
||||
} else {
|
||||
// read the temperature from the DHT22
|
||||
float temperature = dht.readTemperature();
|
||||
Serial.print("Temperature: "); Serial.print(temperature);
|
||||
Serial.println(" *C");
|
||||
// adjust for the f2sflt16 range (-1 to 1)
|
||||
temperature = temperature / 100;
|
||||
|
||||
// read the humidity from the DHT22
|
||||
float rHumidity = dht.readHumidity();
|
||||
Serial.print("%RH ");
|
||||
Serial.println(rHumidity);
|
||||
// adjust for the f2sflt16 range (-1 to 1)
|
||||
rHumidity = rHumidity / 100;
|
||||
|
||||
// float -> int
|
||||
// note: this uses the sflt16 datum (https://github.com/mcci-catena/arduino-lmic#sflt16)
|
||||
uint16_t payloadTemp = LMIC_f2sflt16(temperature);
|
||||
// int -> bytes
|
||||
byte tempLow = lowByte(payloadTemp);
|
||||
byte tempHigh = highByte(payloadTemp);
|
||||
// place the bytes into the payload
|
||||
payload[0] = tempLow;
|
||||
payload[1] = tempHigh;
|
||||
|
||||
// float -> int
|
||||
uint16_t payloadHumid = LMIC_f2sflt16(rHumidity);
|
||||
// int -> bytes
|
||||
byte humidLow = lowByte(payloadHumid);
|
||||
byte humidHigh = highByte(payloadHumid);
|
||||
payload[2] = humidLow;
|
||||
payload[3] = humidHigh;
|
||||
|
||||
// prepare upstream data transmission at the next possible time.
|
||||
// transmit on port 1 (the first parameter); you can use any value from 1 to 223 (others are reserved).
|
||||
// don't request an ack (the last parameter, if not zero, requests an ack from the network).
|
||||
// Remember, acks consume a lot of network resources; don't ask for an ack unless you really need it.
|
||||
LMIC_setTxData2(1, payload, sizeof(payload)-1, 0);
|
||||
}
|
||||
// Next TX is scheduled after TX_COMPLETE event.
|
||||
}
|
||||
|
||||
void setup() {
|
||||
delay(5000);
|
||||
while (! Serial);
|
||||
Serial.begin(9600);
|
||||
Serial.println(F("Starting"));
|
||||
|
||||
dht.begin();
|
||||
|
||||
// LMIC init
|
||||
os_init();
|
||||
// Reset the MAC state. Session and pending data transfers will be discarded.
|
||||
LMIC_reset();
|
||||
// Disable link-check mode and ADR, because ADR tends to complicate testing.
|
||||
LMIC_setLinkCheckMode(0);
|
||||
// Set the data rate to Spreading Factor 7. This is the fastest supported rate for 125 kHz channels, and it
|
||||
// minimizes air time and battery power. Set the transmission power to 14 dBi (25 mW).
|
||||
LMIC_setDrTxpow(DR_SF7,14);
|
||||
// in the US, with TTN, it saves join time if we start on subband 1 (channels 8-15). This will
|
||||
// get overridden after the join by parameters from the network. If working with other
|
||||
// networks or in other regions, this will need to be changed.
|
||||
LMIC_selectSubBand(1);
|
||||
|
||||
// Start job (sending automatically starts OTAA too)
|
||||
do_send(&sendjob);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// we call the LMIC's runloop processor. This will cause things to happen based on events and time. One
|
||||
// of the things that will happen is callbacks for transmission complete or received messages. We also
|
||||
// use this loop to queue periodic data transmissions. You can put other things here in the `loop()` routine,
|
||||
// but beware that LoRaWAN timing is pretty tight, so if you do more than a few milliseconds of work, you
|
||||
// will want to call `os_runloop_once()` every so often, to keep the radio running.
|
||||
os_runloop_once();
|
||||
}
|
@ -0,0 +1,274 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman
|
||||
* Copyright (c) 2018 Terry Moore, MCCI
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to anyone
|
||||
* obtaining a copy of this document and accompanying files,
|
||||
* to do whatever they want with them without any restriction,
|
||||
* including, but not limited to, copying, modification and redistribution.
|
||||
* NO WARRANTY OF ANY KIND IS PROVIDED.
|
||||
*
|
||||
* This example sends a valid LoRaWAN packet with payload "Hello,
|
||||
* world!", using frequency and encryption settings matching those of
|
||||
* the The Things Network. It's pre-configured for the Adafruit
|
||||
* Feather M0 LoRa.
|
||||
*
|
||||
* This uses OTAA (Over-the-air activation), where where a DevEUI and
|
||||
* application key is configured, which are used in an over-the-air
|
||||
* activation procedure where a DevAddr and session keys are
|
||||
* assigned/generated for use with all further communication.
|
||||
*
|
||||
* Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in
|
||||
* g1, 0.1% in g2), but not the TTN fair usage policy (which is probably
|
||||
* violated by this sketch when left running for longer)!
|
||||
|
||||
* To use this sketch, first register your application and device with
|
||||
* the things network, to set or generate an AppEUI, DevEUI and AppKey.
|
||||
* Multiple devices can use the same AppEUI, but each device has its own
|
||||
* DevEUI and AppKey.
|
||||
*
|
||||
* Do not forget to define the radio type correctly in
|
||||
* arduino-lmic/project_config/lmic_project_config.h or from your BOARDS.txt.
|
||||
*
|
||||
*******************************************************************************/
|
||||
|
||||
#include <lmic.h>
|
||||
#include <hal/hal.h>
|
||||
#include <SPI.h>
|
||||
|
||||
//
|
||||
// For normal use, we require that you edit the sketch to replace FILLMEIN
|
||||
// with values assigned by the TTN console. However, for regression tests,
|
||||
// we want to be able to compile these scripts. The regression tests define
|
||||
// COMPILE_REGRESSION_TEST, and in that case we define FILLMEIN to a non-
|
||||
// working but innocuous value.
|
||||
//
|
||||
#ifdef COMPILE_REGRESSION_TEST
|
||||
# define FILLMEIN 0
|
||||
#else
|
||||
# warning "You must replace the values marked FILLMEIN with real values from the TTN control panel!"
|
||||
# define FILLMEIN (#dont edit this, edit the lines that use FILLMEIN)
|
||||
#endif
|
||||
|
||||
// This EUI must be in little-endian format, so least-significant-byte
|
||||
// first. When copying an EUI from ttnctl output, this means to reverse
|
||||
// the bytes. For TTN issued EUIs the last bytes should be 0xD5, 0xB3,
|
||||
// 0x70.
|
||||
static const u1_t PROGMEM APPEUI[8]= { FILLMEIN };
|
||||
void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);}
|
||||
|
||||
// This should also be in little endian format, see above.
|
||||
static const u1_t PROGMEM DEVEUI[8]= { FILLMEIN };
|
||||
void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);}
|
||||
|
||||
// This key should be in big endian format (or, since it is not really a
|
||||
// number but a block of memory, endianness does not really apply). In
|
||||
// practice, a key taken from the TTN console can be copied as-is.
|
||||
static const u1_t PROGMEM APPKEY[16] = { FILLMEIN };
|
||||
void os_getDevKey (u1_t* buf) { memcpy_P(buf, APPKEY, 16);}
|
||||
|
||||
static uint8_t mydata[] = "Hello, world!";
|
||||
static osjob_t sendjob;
|
||||
|
||||
// Schedule TX every this many seconds (might become longer due to duty
|
||||
// cycle limitations).
|
||||
const unsigned TX_INTERVAL = 60;
|
||||
|
||||
// Pin mapping
|
||||
#if defined(ARDUINO_SAMD_FEATHER_M0)
|
||||
// Pin mapping for Adafruit Feather M0 LoRa, etc.
|
||||
const lmic_pinmap lmic_pins = {
|
||||
.nss = 8,
|
||||
.rxtx = LMIC_UNUSED_PIN,
|
||||
.rst = 4,
|
||||
.dio = {3, 6, LMIC_UNUSED_PIN},
|
||||
.rxtx_rx_active = 0,
|
||||
.rssi_cal = 8, // LBT cal for the Adafruit Feather M0 LoRa, in dB
|
||||
.spi_freq = 8000000,
|
||||
};
|
||||
#elif defined(ARDUINO_AVR_FEATHER32U4)
|
||||
// Pin mapping for Adafruit Feather 32u4 LoRa, etc.
|
||||
// Just like Feather M0 LoRa, but uses SPI at 1MHz; and that's only
|
||||
// because MCCI doesn't have a test board; probably higher frequencies
|
||||
// will work.
|
||||
const lmic_pinmap lmic_pins = {
|
||||
.nss = 8,
|
||||
.rxtx = LMIC_UNUSED_PIN,
|
||||
.rst = 4,
|
||||
.dio = {3, 6, LMIC_UNUSED_PIN},
|
||||
.rxtx_rx_active = 0,
|
||||
.rssi_cal = 8, // LBT cal for the Adafruit Feather M0 LoRa, in dB
|
||||
.spi_freq = 1000000,
|
||||
};
|
||||
#elif defined(ARDUINO_CATENA_4551)
|
||||
// Pin mapping for Murata module / Catena 4551
|
||||
const lmic_pinmap lmic_pins = {
|
||||
.nss = 7,
|
||||
.rxtx = 29,
|
||||
.rst = 8,
|
||||
.dio = { 25, // DIO0 (IRQ) is D25
|
||||
26, // DIO1 is D26
|
||||
27, // DIO2 is D27
|
||||
},
|
||||
.rxtx_rx_active = 1,
|
||||
.rssi_cal = 10,
|
||||
.spi_freq = 8000000 // 8MHz
|
||||
};
|
||||
#else
|
||||
# error "Unknown target"
|
||||
#endif
|
||||
|
||||
void onEvent (ev_t ev) {
|
||||
Serial.print(os_getTime());
|
||||
Serial.print(": ");
|
||||
switch(ev) {
|
||||
case EV_SCAN_TIMEOUT:
|
||||
Serial.println(F("EV_SCAN_TIMEOUT"));
|
||||
break;
|
||||
case EV_BEACON_FOUND:
|
||||
Serial.println(F("EV_BEACON_FOUND"));
|
||||
break;
|
||||
case EV_BEACON_MISSED:
|
||||
Serial.println(F("EV_BEACON_MISSED"));
|
||||
break;
|
||||
case EV_BEACON_TRACKED:
|
||||
Serial.println(F("EV_BEACON_TRACKED"));
|
||||
break;
|
||||
case EV_JOINING:
|
||||
Serial.println(F("EV_JOINING"));
|
||||
break;
|
||||
case EV_JOINED:
|
||||
Serial.println(F("EV_JOINED"));
|
||||
{
|
||||
u4_t netid = 0;
|
||||
devaddr_t devaddr = 0;
|
||||
u1_t nwkKey[16];
|
||||
u1_t artKey[16];
|
||||
LMIC_getSessionKeys(&netid, &devaddr, nwkKey, artKey);
|
||||
Serial.print("netid: ");
|
||||
Serial.println(netid, DEC);
|
||||
Serial.print("devaddr: ");
|
||||
Serial.println(devaddr, HEX);
|
||||
Serial.print("artKey: ");
|
||||
for (int i=0; i<sizeof(artKey); ++i) {
|
||||
if (i != 0)
|
||||
Serial.print("-");
|
||||
Serial.print(artKey[i], HEX);
|
||||
}
|
||||
Serial.println("");
|
||||
Serial.print("nwkKey: ");
|
||||
for (int i=0; i<sizeof(nwkKey); ++i) {
|
||||
if (i != 0)
|
||||
Serial.print("-");
|
||||
Serial.print(nwkKey[i], HEX);
|
||||
}
|
||||
Serial.println("");
|
||||
}
|
||||
// Disable link check validation (automatically enabled
|
||||
// during join, but because slow data rates change max TX
|
||||
// size, we don't use it in this example.
|
||||
LMIC_setLinkCheckMode(0);
|
||||
break;
|
||||
/*
|
||||
|| This event is defined but not used in the code. No
|
||||
|| point in wasting codespace on it.
|
||||
||
|
||||
|| case EV_RFU1:
|
||||
|| Serial.println(F("EV_RFU1"));
|
||||
|| break;
|
||||
*/
|
||||
case EV_JOIN_FAILED:
|
||||
Serial.println(F("EV_JOIN_FAILED"));
|
||||
break;
|
||||
case EV_REJOIN_FAILED:
|
||||
Serial.println(F("EV_REJOIN_FAILED"));
|
||||
break;
|
||||
break;
|
||||
case EV_TXCOMPLETE:
|
||||
Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
|
||||
if (LMIC.txrxFlags & TXRX_ACK)
|
||||
Serial.println(F("Received ack"));
|
||||
if (LMIC.dataLen) {
|
||||
Serial.println(F("Received "));
|
||||
Serial.println(LMIC.dataLen);
|
||||
Serial.println(F(" bytes of payload"));
|
||||
}
|
||||
// Schedule next transmission
|
||||
os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send);
|
||||
break;
|
||||
case EV_LOST_TSYNC:
|
||||
Serial.println(F("EV_LOST_TSYNC"));
|
||||
break;
|
||||
case EV_RESET:
|
||||
Serial.println(F("EV_RESET"));
|
||||
break;
|
||||
case EV_RXCOMPLETE:
|
||||
// data received in ping slot
|
||||
Serial.println(F("EV_RXCOMPLETE"));
|
||||
break;
|
||||
case EV_LINK_DEAD:
|
||||
Serial.println(F("EV_LINK_DEAD"));
|
||||
break;
|
||||
case EV_LINK_ALIVE:
|
||||
Serial.println(F("EV_LINK_ALIVE"));
|
||||
break;
|
||||
/*
|
||||
|| This event is defined but not used in the code. No
|
||||
|| point in wasting codespace on it.
|
||||
||
|
||||
|| case EV_SCAN_FOUND:
|
||||
|| Serial.println(F("EV_SCAN_FOUND"));
|
||||
|| break;
|
||||
*/
|
||||
case EV_TXSTART:
|
||||
Serial.println(F("EV_TXSTART"));
|
||||
break;
|
||||
default:
|
||||
Serial.print(F("Unknown event: "));
|
||||
Serial.println((unsigned) ev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void do_send(osjob_t* j){
|
||||
// Check if there is not a current TX/RX job running
|
||||
if (LMIC.opmode & OP_TXRXPEND) {
|
||||
Serial.println(F("OP_TXRXPEND, not sending"));
|
||||
} else {
|
||||
// Prepare upstream data transmission at the next possible time.
|
||||
LMIC_setTxData2(1, mydata, sizeof(mydata)-1, 0);
|
||||
Serial.println(F("Packet queued"));
|
||||
}
|
||||
// Next TX is scheduled after TX_COMPLETE event.
|
||||
}
|
||||
|
||||
void setup() {
|
||||
delay(5000);
|
||||
while (! Serial)
|
||||
;
|
||||
Serial.begin(9600);
|
||||
Serial.println(F("Starting"));
|
||||
|
||||
#ifdef VCC_ENABLE
|
||||
// For Pinoccio Scout boards
|
||||
pinMode(VCC_ENABLE, OUTPUT);
|
||||
digitalWrite(VCC_ENABLE, HIGH);
|
||||
delay(1000);
|
||||
#endif
|
||||
|
||||
// LMIC init
|
||||
os_init();
|
||||
// Reset the MAC state. Session and pending data transfers will be discarded.
|
||||
LMIC_reset();
|
||||
|
||||
LMIC_setLinkCheckMode(0);
|
||||
LMIC_setDrTxpow(DR_SF7,14);
|
||||
LMIC_selectSubBand(1);
|
||||
|
||||
// Start job (sending automatically starts OTAA too)
|
||||
do_send(&sendjob);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
os_runloop_once();
|
||||
}
|
@ -0,0 +1,293 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman
|
||||
* Copyright (c) 2018 Terry Moore, MCCI
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to anyone
|
||||
* obtaining a copy of this document and accompanying files,
|
||||
* to do whatever they want with them without any restriction,
|
||||
* including, but not limited to, copying, modification and redistribution.
|
||||
* NO WARRANTY OF ANY KIND IS PROVIDED.
|
||||
*
|
||||
* This example sends a valid LoRaWAN packet with payload "Hello,
|
||||
* world!", using frequency and encryption settings matching those of
|
||||
* the The Things Network.
|
||||
*
|
||||
* This uses OTAA (Over-the-air activation), where where a DevEUI and
|
||||
* application key is configured, which are used in an over-the-air
|
||||
* activation procedure where a DevAddr and session keys are
|
||||
* assigned/generated for use with all further communication.
|
||||
*
|
||||
* Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in
|
||||
* g1, 0.1% in g2), but not the TTN fair usage policy (which is probably
|
||||
* violated by this sketch when left running for longer)!
|
||||
|
||||
* To use this sketch, first register your application and device with
|
||||
* the things network, to set or generate an AppEUI, DevEUI and AppKey.
|
||||
* Multiple devices can use the same AppEUI, but each device has its own
|
||||
* DevEUI and AppKey.
|
||||
*
|
||||
* Do not forget to define the radio type correctly in config.h.
|
||||
*
|
||||
*******************************************************************************/
|
||||
|
||||
#include <Time.h>
|
||||
#include <lmic.h>
|
||||
#include <hal/hal.h>
|
||||
#include <SPI.h>
|
||||
|
||||
//
|
||||
// For normal use, we require that you edit the sketch to replace FILLMEIN
|
||||
// with values assigned by the TTN console. However, for regression tests,
|
||||
// we want to be able to compile these scripts. The regression tests define
|
||||
// COMPILE_REGRESSION_TEST, and in that case we define FILLMEIN to a non-
|
||||
// working but innocuous value.
|
||||
//
|
||||
#ifdef COMPILE_REGRESSION_TEST
|
||||
# define FILLMEIN 0
|
||||
#else
|
||||
# warning "You must replace the values marked FILLMEIN with real values from the TTN control panel!"
|
||||
# define FILLMEIN (#dont edit this, edit the lines that use FILLMEIN)
|
||||
#endif
|
||||
|
||||
|
||||
// This EUI must be in little-endian format, so least-significant-byte
|
||||
// first. When copying an EUI from ttnctl output, this means to reverse
|
||||
// the bytes. For TTN issued EUIs the last bytes should be 0xD5, 0xB3,
|
||||
// 0x70.
|
||||
static const u1_t PROGMEM APPEUI[8]={ FILLMEIN };
|
||||
void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);}
|
||||
|
||||
// This should also be in little endian format, see above.
|
||||
static const u1_t PROGMEM DEVEUI[8]={ FILLMEIN };
|
||||
void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);}
|
||||
|
||||
// This key should be in big endian format (or, since it is not really a
|
||||
// number but a block of memory, endianness does not really apply). In
|
||||
// practice, a key taken from ttnctl can be copied as-is.
|
||||
static const u1_t PROGMEM APPKEY[16] = { FILLMEIN };
|
||||
void os_getDevKey (u1_t* buf) { memcpy_P(buf, APPKEY, 16);}
|
||||
|
||||
static uint8_t mydata[] = "Hello, world!";
|
||||
static osjob_t sendjob;
|
||||
|
||||
// Schedule TX every this many seconds (might become longer due to duty
|
||||
// cycle limitations).
|
||||
const unsigned TX_INTERVAL = 60;
|
||||
|
||||
// Pin mapping
|
||||
const lmic_pinmap lmic_pins = {
|
||||
.nss = 6,
|
||||
.rxtx = LMIC_UNUSED_PIN,
|
||||
.rst = 5,
|
||||
.dio = {2, 3, 4},
|
||||
};
|
||||
|
||||
void onEvent (ev_t ev) {
|
||||
Serial.print(os_getTime());
|
||||
Serial.print(": ");
|
||||
switch(ev) {
|
||||
case EV_SCAN_TIMEOUT:
|
||||
Serial.println(F("EV_SCAN_TIMEOUT"));
|
||||
break;
|
||||
case EV_BEACON_FOUND:
|
||||
Serial.println(F("EV_BEACON_FOUND"));
|
||||
break;
|
||||
case EV_BEACON_MISSED:
|
||||
Serial.println(F("EV_BEACON_MISSED"));
|
||||
break;
|
||||
case EV_BEACON_TRACKED:
|
||||
Serial.println(F("EV_BEACON_TRACKED"));
|
||||
break;
|
||||
case EV_JOINING:
|
||||
Serial.println(F("EV_JOINING"));
|
||||
break;
|
||||
case EV_JOINED:
|
||||
Serial.println(F("EV_JOINED"));
|
||||
{
|
||||
u4_t netid = 0;
|
||||
devaddr_t devaddr = 0;
|
||||
u1_t nwkKey[16];
|
||||
u1_t artKey[16];
|
||||
LMIC_getSessionKeys(&netid, &devaddr, nwkKey, artKey);
|
||||
Serial.print("netid: ");
|
||||
Serial.println(netid, DEC);
|
||||
Serial.print("devaddr: ");
|
||||
Serial.println(devaddr, HEX);
|
||||
Serial.print("artKey: ");
|
||||
for (int i=0; i<sizeof(artKey); ++i) {
|
||||
Serial.print(artKey[i], HEX);
|
||||
}
|
||||
Serial.println("");
|
||||
Serial.print("nwkKey: ");
|
||||
for (int i=0; i<sizeof(nwkKey); ++i) {
|
||||
Serial.print(nwkKey[i], HEX);
|
||||
}
|
||||
Serial.println("");
|
||||
}
|
||||
// Disable link check validation (automatically enabled
|
||||
// during join, but because slow data rates change max TX
|
||||
// size, we don't use it in this example.
|
||||
LMIC_setLinkCheckMode(0);
|
||||
break;
|
||||
/*
|
||||
|| This event is defined but not used in the code. No
|
||||
|| point in wasting codespace on it.
|
||||
||
|
||||
|| case EV_RFU1:
|
||||
|| Serial.println(F("EV_RFU1"));
|
||||
|| break;
|
||||
*/
|
||||
case EV_JOIN_FAILED:
|
||||
Serial.println(F("EV_JOIN_FAILED"));
|
||||
break;
|
||||
case EV_REJOIN_FAILED:
|
||||
Serial.println(F("EV_REJOIN_FAILED"));
|
||||
break;
|
||||
case EV_TXCOMPLETE:
|
||||
Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
|
||||
if (LMIC.txrxFlags & TXRX_ACK)
|
||||
Serial.println(F("Received ack"));
|
||||
if (LMIC.dataLen) {
|
||||
Serial.print(F("Received "));
|
||||
Serial.print(LMIC.dataLen);
|
||||
Serial.println(F(" bytes of payload"));
|
||||
}
|
||||
// Schedule next transmission
|
||||
os_setTimedCallback(&sendjob, os_getTime() + sec2osticks(TX_INTERVAL), do_send);
|
||||
break;
|
||||
case EV_LOST_TSYNC:
|
||||
Serial.println(F("EV_LOST_TSYNC"));
|
||||
break;
|
||||
case EV_RESET:
|
||||
Serial.println(F("EV_RESET"));
|
||||
break;
|
||||
case EV_RXCOMPLETE:
|
||||
// data received in ping slot
|
||||
Serial.println(F("EV_RXCOMPLETE"));
|
||||
break;
|
||||
case EV_LINK_DEAD:
|
||||
Serial.println(F("EV_LINK_DEAD"));
|
||||
break;
|
||||
case EV_LINK_ALIVE:
|
||||
Serial.println(F("EV_LINK_ALIVE"));
|
||||
break;
|
||||
/*
|
||||
|| This event is defined but not used in the code. No
|
||||
|| point in wasting codespace on it.
|
||||
||
|
||||
|| case EV_SCAN_FOUND:
|
||||
|| Serial.println(F("EV_SCAN_FOUND"));
|
||||
|| break;
|
||||
*/
|
||||
case EV_TXSTART:
|
||||
Serial.println(F("EV_TXSTART"));
|
||||
break;
|
||||
default:
|
||||
Serial.print(F("Unknown event: "));
|
||||
Serial.println((unsigned) ev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t userUTCTime; // Seconds since the UTC epoch
|
||||
|
||||
// Utility function for digital clock display: prints preceding colon and
|
||||
// leading 0
|
||||
void printDigits(int digits) {
|
||||
Serial.print(':');
|
||||
if (digits < 10) Serial.print('0');
|
||||
Serial.print(digits);
|
||||
}
|
||||
|
||||
void user_request_network_time_callback(void *pVoidUserUTCTime, int flagSuccess) {
|
||||
// Explicit conversion from void* to uint32_t* to avoid compiler errors
|
||||
uint32_t *pUserUTCTime = (uint32_t *) pVoidUserUTCTime;
|
||||
|
||||
// A struct that will be populated by LMIC_getNetworkTimeReference.
|
||||
// It contains the following fields:
|
||||
// - tLocal: the value returned by os_GetTime() when the time
|
||||
// request was sent to the gateway, and
|
||||
// - tNetwork: the seconds between the GPS epoch and the time
|
||||
// the gateway received the time request
|
||||
lmic_time_reference_t lmicTimeReference;
|
||||
|
||||
if (flagSuccess != 1) {
|
||||
Serial.println(F("USER CALLBACK: Not a success"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Populate "lmic_time_reference"
|
||||
flagSuccess = LMIC_getNetworkTimeReference(&lmicTimeReference);
|
||||
if (flagSuccess != 1) {
|
||||
Serial.println(F("USER CALLBACK: LMIC_getNetworkTimeReference didn't succeed"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Update userUTCTime, considering the difference between the GPS and UTC
|
||||
// epoch, and the leap seconds
|
||||
*pUserUTCTime = lmicTimeReference.tNetwork + 315964800;
|
||||
|
||||
// Add the delay between the instant the time was transmitted and
|
||||
// the current time
|
||||
|
||||
// Current time, in ticks
|
||||
ostime_t ticksNow = os_getTime();
|
||||
// Time when the request was sent, in ticks
|
||||
ostime_t ticksRequestSent = lmicTimeReference.tLocal;
|
||||
uint32_t requestDelaySec = osticks2ms(ticksNow - ticksRequestSent) / 1000;
|
||||
*pUserUTCTime += requestDelaySec;
|
||||
|
||||
// Update the system time with the time read from the network
|
||||
setTime(*pUserUTCTime);
|
||||
|
||||
Serial.print(F("The current UTC time is: "));
|
||||
Serial.print(hour());
|
||||
printDigits(minute());
|
||||
printDigits(second());
|
||||
Serial.print(' ');
|
||||
Serial.print(day());
|
||||
Serial.print('/');
|
||||
Serial.print(month());
|
||||
Serial.print('/');
|
||||
Serial.print(year());
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
void do_send(osjob_t* j) {
|
||||
// Check if there is not a current TX/RX job running
|
||||
if (LMIC.opmode & OP_TXRXPEND) {
|
||||
Serial.println(F("OP_TXRXPEND, not sending"));
|
||||
} else {
|
||||
// Schedule a network time request at the next possible time
|
||||
LMIC_requestNetworkTime(user_request_network_time_callback, &userUTCTime);
|
||||
|
||||
// Prepare upstream data transmission at the next possible time.
|
||||
LMIC_setTxData2(1, mydata, sizeof(mydata)-1, 0);
|
||||
Serial.println(F("Packet queued"));
|
||||
}
|
||||
// Next TX is scheduled after TX_COMPLETE event.
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
Serial.println(F("Starting"));
|
||||
|
||||
#ifdef VCC_ENABLE
|
||||
// For Pinoccio Scout boards
|
||||
pinMode(VCC_ENABLE, OUTPUT);
|
||||
digitalWrite(VCC_ENABLE, HIGH);
|
||||
delay(1000);
|
||||
#endif
|
||||
|
||||
// LMIC init
|
||||
os_init();
|
||||
// Reset the MAC state. Session and pending data transfers will be discarded.
|
||||
LMIC_reset();
|
||||
|
||||
// Start job (sending automatically starts OTAA too)
|
||||
do_send(&sendjob);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
os_runloop_once();
|
||||
}
|
225
lib/arduino-lmic-mcci-v2.2.2/examples/ttn-otaa/ttn-otaa.ino
Normal file
225
lib/arduino-lmic-mcci-v2.2.2/examples/ttn-otaa/ttn-otaa.ino
Normal file
@ -0,0 +1,225 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman
|
||||
* Copyright (c) 2018 Terry Moore, MCCI
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to anyone
|
||||
* obtaining a copy of this document and accompanying files,
|
||||
* to do whatever they want with them without any restriction,
|
||||
* including, but not limited to, copying, modification and redistribution.
|
||||
* NO WARRANTY OF ANY KIND IS PROVIDED.
|
||||
*
|
||||
* This example sends a valid LoRaWAN packet with payload "Hello,
|
||||
* world!", using frequency and encryption settings matching those of
|
||||
* the The Things Network.
|
||||
*
|
||||
* This uses OTAA (Over-the-air activation), where where a DevEUI and
|
||||
* application key is configured, which are used in an over-the-air
|
||||
* activation procedure where a DevAddr and session keys are
|
||||
* assigned/generated for use with all further communication.
|
||||
*
|
||||
* Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in
|
||||
* g1, 0.1% in g2), but not the TTN fair usage policy (which is probably
|
||||
* violated by this sketch when left running for longer)!
|
||||
|
||||
* To use this sketch, first register your application and device with
|
||||
* the things network, to set or generate an AppEUI, DevEUI and AppKey.
|
||||
* Multiple devices can use the same AppEUI, but each device has its own
|
||||
* DevEUI and AppKey.
|
||||
*
|
||||
* Do not forget to define the radio type correctly in
|
||||
* arduino-lmic/project_config/lmic_project_config.h or from your BOARDS.txt.
|
||||
*
|
||||
*******************************************************************************/
|
||||
|
||||
#include <lmic.h>
|
||||
#include <hal/hal.h>
|
||||
#include <SPI.h>
|
||||
|
||||
//
|
||||
// For normal use, we require that you edit the sketch to replace FILLMEIN
|
||||
// with values assigned by the TTN console. However, for regression tests,
|
||||
// we want to be able to compile these scripts. The regression tests define
|
||||
// COMPILE_REGRESSION_TEST, and in that case we define FILLMEIN to a non-
|
||||
// working but innocuous value.
|
||||
//
|
||||
#ifdef COMPILE_REGRESSION_TEST
|
||||
# define FILLMEIN 0
|
||||
#else
|
||||
# warning "You must replace the values marked FILLMEIN with real values from the TTN control panel!"
|
||||
# define FILLMEIN (#dont edit this, edit the lines that use FILLMEIN)
|
||||
#endif
|
||||
|
||||
// This EUI must be in little-endian format, so least-significant-byte
|
||||
// first. When copying an EUI from ttnctl output, this means to reverse
|
||||
// the bytes. For TTN issued EUIs the last bytes should be 0xD5, 0xB3,
|
||||
// 0x70.
|
||||
static const u1_t PROGMEM APPEUI[8]={ FILLMEIN };
|
||||
void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);}
|
||||
|
||||
// This should also be in little endian format, see above.
|
||||
static const u1_t PROGMEM DEVEUI[8]={ FILLMEIN };
|
||||
void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);}
|
||||
|
||||
// This key should be in big endian format (or, since it is not really a
|
||||
// number but a block of memory, endianness does not really apply). In
|
||||
// practice, a key taken from ttnctl can be copied as-is.
|
||||
static const u1_t PROGMEM APPKEY[16] = { FILLMEIN };
|
||||
void os_getDevKey (u1_t* buf) { memcpy_P(buf, APPKEY, 16);}
|
||||
|
||||
static uint8_t mydata[] = "Hello, world!";
|
||||
static osjob_t sendjob;
|
||||
|
||||
// Schedule TX every this many seconds (might become longer due to duty
|
||||
// cycle limitations).
|
||||
const unsigned TX_INTERVAL = 60;
|
||||
|
||||
// Pin mapping
|
||||
const lmic_pinmap lmic_pins = {
|
||||
.nss = 6,
|
||||
.rxtx = LMIC_UNUSED_PIN,
|
||||
.rst = 5,
|
||||
.dio = {2, 3, 4},
|
||||
};
|
||||
|
||||
void onEvent (ev_t ev) {
|
||||
Serial.print(os_getTime());
|
||||
Serial.print(": ");
|
||||
switch(ev) {
|
||||
case EV_SCAN_TIMEOUT:
|
||||
Serial.println(F("EV_SCAN_TIMEOUT"));
|
||||
break;
|
||||
case EV_BEACON_FOUND:
|
||||
Serial.println(F("EV_BEACON_FOUND"));
|
||||
break;
|
||||
case EV_BEACON_MISSED:
|
||||
Serial.println(F("EV_BEACON_MISSED"));
|
||||
break;
|
||||
case EV_BEACON_TRACKED:
|
||||
Serial.println(F("EV_BEACON_TRACKED"));
|
||||
break;
|
||||
case EV_JOINING:
|
||||
Serial.println(F("EV_JOINING"));
|
||||
break;
|
||||
case EV_JOINED:
|
||||
Serial.println(F("EV_JOINED"));
|
||||
{
|
||||
u4_t netid = 0;
|
||||
devaddr_t devaddr = 0;
|
||||
u1_t nwkKey[16];
|
||||
u1_t artKey[16];
|
||||
LMIC_getSessionKeys(&netid, &devaddr, nwkKey, artKey);
|
||||
Serial.print("netid: ");
|
||||
Serial.println(netid, DEC);
|
||||
Serial.print("devaddr: ");
|
||||
Serial.println(devaddr, HEX);
|
||||
Serial.print("artKey: ");
|
||||
for (int i=0; i<sizeof(artKey); ++i) {
|
||||
Serial.print(artKey[i], HEX);
|
||||
}
|
||||
Serial.println("");
|
||||
Serial.print("nwkKey: ");
|
||||
for (int i=0; i<sizeof(nwkKey); ++i) {
|
||||
Serial.print(nwkKey[i], HEX);
|
||||
}
|
||||
Serial.println("");
|
||||
}
|
||||
// Disable link check validation (automatically enabled
|
||||
// during join, but because slow data rates change max TX
|
||||
// size, we don't use it in this example.
|
||||
LMIC_setLinkCheckMode(0);
|
||||
break;
|
||||
/*
|
||||
|| This event is defined but not used in the code. No
|
||||
|| point in wasting codespace on it.
|
||||
||
|
||||
|| case EV_RFU1:
|
||||
|| Serial.println(F("EV_RFU1"));
|
||||
|| break;
|
||||
*/
|
||||
case EV_JOIN_FAILED:
|
||||
Serial.println(F("EV_JOIN_FAILED"));
|
||||
break;
|
||||
case EV_REJOIN_FAILED:
|
||||
Serial.println(F("EV_REJOIN_FAILED"));
|
||||
break;
|
||||
case EV_TXCOMPLETE:
|
||||
Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
|
||||
if (LMIC.txrxFlags & TXRX_ACK)
|
||||
Serial.println(F("Received ack"));
|
||||
if (LMIC.dataLen) {
|
||||
Serial.print(F("Received "));
|
||||
Serial.print(LMIC.dataLen);
|
||||
Serial.println(F(" bytes of payload"));
|
||||
}
|
||||
// Schedule next transmission
|
||||
os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send);
|
||||
break;
|
||||
case EV_LOST_TSYNC:
|
||||
Serial.println(F("EV_LOST_TSYNC"));
|
||||
break;
|
||||
case EV_RESET:
|
||||
Serial.println(F("EV_RESET"));
|
||||
break;
|
||||
case EV_RXCOMPLETE:
|
||||
// data received in ping slot
|
||||
Serial.println(F("EV_RXCOMPLETE"));
|
||||
break;
|
||||
case EV_LINK_DEAD:
|
||||
Serial.println(F("EV_LINK_DEAD"));
|
||||
break;
|
||||
case EV_LINK_ALIVE:
|
||||
Serial.println(F("EV_LINK_ALIVE"));
|
||||
break;
|
||||
/*
|
||||
|| This event is defined but not used in the code. No
|
||||
|| point in wasting codespace on it.
|
||||
||
|
||||
|| case EV_SCAN_FOUND:
|
||||
|| Serial.println(F("EV_SCAN_FOUND"));
|
||||
|| break;
|
||||
*/
|
||||
case EV_TXSTART:
|
||||
Serial.println(F("EV_TXSTART"));
|
||||
break;
|
||||
default:
|
||||
Serial.print(F("Unknown event: "));
|
||||
Serial.println((unsigned) ev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void do_send(osjob_t* j){
|
||||
// Check if there is not a current TX/RX job running
|
||||
if (LMIC.opmode & OP_TXRXPEND) {
|
||||
Serial.println(F("OP_TXRXPEND, not sending"));
|
||||
} else {
|
||||
// Prepare upstream data transmission at the next possible time.
|
||||
LMIC_setTxData2(1, mydata, sizeof(mydata)-1, 0);
|
||||
Serial.println(F("Packet queued"));
|
||||
}
|
||||
// Next TX is scheduled after TX_COMPLETE event.
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
Serial.println(F("Starting"));
|
||||
|
||||
#ifdef VCC_ENABLE
|
||||
// For Pinoccio Scout boards
|
||||
pinMode(VCC_ENABLE, OUTPUT);
|
||||
digitalWrite(VCC_ENABLE, HIGH);
|
||||
delay(1000);
|
||||
#endif
|
||||
|
||||
// LMIC init
|
||||
os_init();
|
||||
// Reset the MAC state. Session and pending data transfers will be discarded.
|
||||
LMIC_reset();
|
||||
|
||||
// Start job (sending automatically starts OTAA too)
|
||||
do_send(&sendjob);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
os_runloop_once();
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
// project-specific definitions
|
||||
//#define CFG_eu868 1
|
||||
#define CFG_us915 1
|
||||
//#define CFG_au921 1
|
||||
//#define CFG_as923 1
|
||||
// #define LMIC_COUNTRY_CODE LMIC_COUNTRY_CODE_JP /* for as923-JP */
|
||||
//#define CFG_in866 1
|
||||
#define CFG_sx1276_radio 1
|
||||
//#define LMIC_USE_INTERRUPTS
|
@ -85,7 +85,6 @@ static unsigned char AES_Sub_Byte(unsigned char Byte);
|
||||
static void AES_Shift_Rows();
|
||||
static void AES_Mix_Collums();
|
||||
static void AES_Calculate_Round_Key(unsigned char Round, unsigned char *Round_Key);
|
||||
static void Send_State();
|
||||
|
||||
/*
|
||||
*****************************************************************************************
|
||||
|
@ -33,11 +33,19 @@ static void hal_io_init () {
|
||||
// Serial.print("dio[1]: "); Serial.println(plmic_pins->dio[1]);
|
||||
// Serial.print("dio[2]: "); Serial.println(plmic_pins->dio[2]);
|
||||
|
||||
// initialize SPI chip select to high (it's active low)
|
||||
digitalWrite(plmic_pins->nss, HIGH);
|
||||
pinMode(plmic_pins->nss, OUTPUT);
|
||||
if (plmic_pins->rxtx != LMIC_UNUSED_PIN)
|
||||
|
||||
if (plmic_pins->rxtx != LMIC_UNUSED_PIN) {
|
||||
// initialize to RX
|
||||
digitalWrite(plmic_pins->rxtx, LOW != plmic_pins->rxtx_rx_active);
|
||||
pinMode(plmic_pins->rxtx, OUTPUT);
|
||||
if (plmic_pins->rst != LMIC_UNUSED_PIN)
|
||||
pinMode(plmic_pins->rst, OUTPUT);
|
||||
}
|
||||
if (plmic_pins->rst != LMIC_UNUSED_PIN) {
|
||||
// initialize RST to floating
|
||||
pinMode(plmic_pins->rst, INPUT);
|
||||
}
|
||||
|
||||
hal_interrupt_init();
|
||||
}
|
||||
@ -54,8 +62,8 @@ void hal_pin_rst (u1_t val) {
|
||||
return;
|
||||
|
||||
if(val == 0 || val == 1) { // drive pin
|
||||
pinMode(plmic_pins->rst, OUTPUT);
|
||||
digitalWrite(plmic_pins->rst, val);
|
||||
pinMode(plmic_pins->rst, OUTPUT);
|
||||
} else { // keep pin floating
|
||||
pinMode(plmic_pins->rst, INPUT);
|
||||
}
|
||||
|
@ -171,4 +171,11 @@
|
||||
# endif // defined(LMIC_DISABLE_DR_LEGACY)
|
||||
#endif // LMIC_DR_LEGACY
|
||||
|
||||
// LMIC_ENABLE_DeviceTimeReq
|
||||
// enable support for MCMD_DeviceTimeReq and MCMD_DeviceTimeAns
|
||||
// this is always defined, and non-zero to enable it.
|
||||
#if !defined(LMIC_ENABLE_DeviceTimeReq)
|
||||
# define LMIC_ENABLE_DeviceTimeReq 0
|
||||
#endif
|
||||
|
||||
#endif // _lmic_config_h_
|
||||
|
@ -47,8 +47,10 @@ static void startScan (void);
|
||||
#endif
|
||||
|
||||
static inline void initTxrxFlags(const char *func, u1_t mask) {
|
||||
LMIC_DEBUG2_PARAMETER(func);
|
||||
|
||||
#if LMIC_DEBUG_LEVEL > 1
|
||||
LMIC_DEBUG_PRINTF("%lu: %s txrxFlags %#02x --> %02x\n", os_getTime(), func, LMIC.txrxFlags, mask);
|
||||
LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": %s txrxFlags %#02x --> %02x\n", os_getTime(), func, LMIC.txrxFlags, mask);
|
||||
#endif
|
||||
LMIC.txrxFlags = mask;
|
||||
}
|
||||
@ -272,29 +274,6 @@ ostime_t calcAirTime (rps_t rps, u1_t plen) {
|
||||
return (((ostime_t)tmp << sfx) * OSTICKS_PER_SEC + div/2) / div;
|
||||
}
|
||||
|
||||
extern inline rps_t updr2rps (dr_t dr);
|
||||
extern inline rps_t dndr2rps (dr_t dr);
|
||||
extern inline int isFasterDR (dr_t dr1, dr_t dr2);
|
||||
extern inline int isSlowerDR (dr_t dr1, dr_t dr2);
|
||||
extern inline dr_t incDR (dr_t dr);
|
||||
extern inline dr_t decDR (dr_t dr);
|
||||
extern inline dr_t assertDR (dr_t dr);
|
||||
extern inline dr_t validDR (dr_t dr);
|
||||
extern inline dr_t lowerDR (dr_t dr, u1_t n);
|
||||
|
||||
extern inline sf_t getSf (rps_t params);
|
||||
extern inline rps_t setSf (rps_t params, sf_t sf);
|
||||
extern inline bw_t getBw (rps_t params);
|
||||
extern inline rps_t setBw (rps_t params, bw_t cr);
|
||||
extern inline cr_t getCr (rps_t params);
|
||||
extern inline rps_t setCr (rps_t params, cr_t cr);
|
||||
extern inline int getNocrc (rps_t params);
|
||||
extern inline rps_t setNocrc (rps_t params, int nocrc);
|
||||
extern inline int getIh (rps_t params);
|
||||
extern inline rps_t setIh (rps_t params, int ih);
|
||||
extern inline rps_t makeRps (sf_t sf, bw_t bw, cr_t cr, int ih, int nocrc);
|
||||
extern inline int sameSfBw (rps_t r1, rps_t r2);
|
||||
|
||||
// END LORA
|
||||
// ================================================================================
|
||||
|
||||
@ -414,6 +393,8 @@ static void txDelay (ostime_t reftime, u1_t secSpan) {
|
||||
|
||||
|
||||
void LMICcore_setDrJoin (u1_t reason, u1_t dr) {
|
||||
LMIC_EV_PARAMETER(reason);
|
||||
|
||||
EV(drChange, INFO, (e_.reason = reason,
|
||||
e_.deveui = MAIN::CDEV->getEui(),
|
||||
e_.dr = dr|DR_PAGE,
|
||||
@ -426,6 +407,8 @@ void LMICcore_setDrJoin (u1_t reason, u1_t dr) {
|
||||
|
||||
|
||||
static void setDrTxpow (u1_t reason, u1_t dr, s1_t pow) {
|
||||
LMIC_EV_PARAMETER(reason);
|
||||
|
||||
EV(drChange, INFO, (e_.reason = reason,
|
||||
e_.deveui = MAIN::CDEV->getEui(),
|
||||
e_.dr = dr|DR_PAGE,
|
||||
@ -462,6 +445,8 @@ void LMIC_setPingable (u1_t intvExp) {
|
||||
#endif // !DISABLE_PING
|
||||
|
||||
static void runEngineUpdate (xref2osjob_t osjob) {
|
||||
LMIC_API_PARAMETER(osjob);
|
||||
|
||||
engineUpdate();
|
||||
}
|
||||
|
||||
@ -476,6 +461,8 @@ static void reportEvent (ev_t ev) {
|
||||
|
||||
|
||||
static void runReset (xref2osjob_t osjob) {
|
||||
LMIC_API_PARAMETER(osjob);
|
||||
|
||||
// Disable session
|
||||
LMIC_reset();
|
||||
#if !defined(DISABLE_JOIN)
|
||||
@ -594,7 +581,7 @@ scan_mac_cmds(
|
||||
// of contiguous commands (whatever that means), and ignore the
|
||||
// data rate, NbTrans (uprpt) and txPow until the last one.
|
||||
#if LMIC_DEBUG_LEVEL > 0
|
||||
LMIC_DEBUG_PRINTF("%lu: LinkAdrReq: p1:%02x chmap:%04x chpage:%02x uprt:%02x ans:%02x\n",
|
||||
LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": LinkAdrReq: p1:%02x chmap:%04x chpage:%02x uprt:%02x ans:%02x\n",
|
||||
os_getTime(), p1, chmap, chpage, uprpt, LMIC.ladrAns
|
||||
);
|
||||
#endif /* LMIC_DEBUG_LEVEL */
|
||||
@ -726,6 +713,38 @@ scan_mac_cmds(
|
||||
oidx += 2;
|
||||
continue;
|
||||
} /* end case */
|
||||
case MCMD_DeviceTimeAns: {
|
||||
#if LMIC_ENABLE_DeviceTimeReq
|
||||
// don't process a spurious downlink.
|
||||
if ( LMIC.txDeviceTimeReqState == lmic_RequestTimeState_rx ) {
|
||||
// remember that it's time to notify the client.
|
||||
LMIC.txDeviceTimeReqState = lmic_RequestTimeState_success;
|
||||
|
||||
// the network time is linked to the time of the last TX.
|
||||
LMIC.localDeviceTime = LMIC.txend;
|
||||
|
||||
// save the network time.
|
||||
// The first 4 bytes contain the seconds since the GPS epoch
|
||||
// (i.e January the 6th 1980 at 00:00:00 UTC).
|
||||
// Note: as per the LoRaWAN specs, the octet order for all
|
||||
// multi-octet fields is little endian
|
||||
// Note: the casts are necessary, because opts is an array of
|
||||
// single byte values, and they might overflow when shifted
|
||||
LMIC.netDeviceTime = ( (lmic_gpstime_t) opts[oidx + 1] ) |
|
||||
(((lmic_gpstime_t) opts[oidx + 2]) << 8) |
|
||||
(((lmic_gpstime_t) opts[oidx + 3]) << 16) |
|
||||
(((lmic_gpstime_t) opts[oidx + 4]) << 24);
|
||||
|
||||
// The 5th byte contains the fractional seconds in 2^-8 second steps
|
||||
LMIC.netDeviceTimeFrac = opts[oidx + 5];
|
||||
#if LMIC_DEBUG_LEVEL > 0
|
||||
LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": MAC command DeviceTimeAns received: seconds_since_gps_epoch=%"PRIu32", fractional_seconds=%d\n", os_getTime(), LMIC.netDeviceTime, LMIC.netDeviceTimeFrac);
|
||||
#endif
|
||||
}
|
||||
#endif // LMIC_ENABLE_DeviceTimeReq
|
||||
oidx += 6;
|
||||
continue;
|
||||
} /* end case */
|
||||
} /* end switch */
|
||||
/* unrecognized mac commands fall out of switch to here */
|
||||
EV(specCond, ERR, (e_.reason = EV::specCond_t::BAD_MAC_CMD,
|
||||
@ -757,7 +776,7 @@ static bit_t decodeFrame (void) {
|
||||
e_.info2 = hdr + (dlen<<8)));
|
||||
norx:
|
||||
#if LMIC_DEBUG_LEVEL > 0
|
||||
LMIC_DEBUG_PRINTF("%lu: Invalid downlink, window=%s\n", os_getTime(), window);
|
||||
LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": Invalid downlink, window=%s\n", os_getTime(), window);
|
||||
#endif
|
||||
LMIC.dataLen = 0;
|
||||
return 0;
|
||||
@ -849,7 +868,7 @@ static bit_t decodeFrame (void) {
|
||||
|
||||
#if LMIC_DEBUG_LEVEL > 0
|
||||
// Process OPTS
|
||||
LMIC_DEBUG_PRINTF("%lu: process options (olen=%#x)\n", os_getTime(), olen);
|
||||
LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": process options (olen=%#x)\n", os_getTime(), olen);
|
||||
#endif
|
||||
|
||||
xref2u1_t opts = &d[OFF_DAT_OPTS];
|
||||
@ -868,13 +887,13 @@ static bit_t decodeFrame (void) {
|
||||
if (port == 0) {
|
||||
// this is a mac command. scan the options.
|
||||
#if LMIC_DEBUG_LEVEL > 0
|
||||
LMIC_DEBUG_PRINTF("%lu: process mac commands for port 0 (olen=%#x)\n", os_getTime(), pend-poff);
|
||||
LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": process mac commands for port 0 (olen=%#x)\n", os_getTime(), pend-poff);
|
||||
#endif
|
||||
int optendindex = scan_mac_cmds(d+poff, pend-poff);
|
||||
if (optendindex != pend-poff) {
|
||||
#if LMIC_DEBUG_LEVEL > 0
|
||||
LMIC_DEBUG_PRINTF(
|
||||
"%lu: error processing mac commands for port 0 "
|
||||
"%"LMIC_PRId_ostime_t": error processing mac commands for port 0 "
|
||||
"(len=%#x, optendindex=%#x)\n",
|
||||
os_getTime(), pend-poff, optendindex
|
||||
);
|
||||
@ -911,7 +930,7 @@ static bit_t decodeFrame (void) {
|
||||
e_.info = seqno,
|
||||
e_.info2 = ackup));
|
||||
#if LMIC_DEBUG_LEVEL > 1
|
||||
LMIC_DEBUG_PRINTF("%lu: ??ack error ack=%d txCnt=%d\n", os_getTime(), ackup, LMIC.txCnt);
|
||||
LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": ??ack error ack=%d txCnt=%d\n", os_getTime(), ackup, LMIC.txCnt);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -928,7 +947,7 @@ static bit_t decodeFrame (void) {
|
||||
LMIC.dataLen = pend-poff;
|
||||
}
|
||||
#if LMIC_DEBUG_LEVEL > 0
|
||||
LMIC_DEBUG_PRINTF("%lu: Received downlink, window=%s, port=%d, ack=%d, txrxFlags=%#x\n", os_getTime(), window, port, ackup, LMIC.txrxFlags);
|
||||
LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": Received downlink, window=%s, port=%d, ack=%d, txrxFlags=%#x\n", os_getTime(), window, port, ackup, LMIC.txrxFlags);
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
@ -977,7 +996,7 @@ static void schedRx12 (ostime_t delay, osjobcb_t func, u1_t dr) {
|
||||
// (again note that hsym is half a sumbol time, so no /2 needed)
|
||||
LMIC.rxtime = LMIC.txend + delay + PAMBL_SYMS * hsym - LMIC.rxsyms * hsym;
|
||||
|
||||
LMIC_X_DEBUG_PRINTF("%lu: sched Rx12 %lu\n", os_getTime(), LMIC.rxtime - RX_RAMPUP);
|
||||
LMIC_X_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": sched Rx12 %"LMIC_PRId_ostime_t"\n", os_getTime(), LMIC.rxtime - RX_RAMPUP);
|
||||
os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - RX_RAMPUP, func);
|
||||
}
|
||||
|
||||
@ -1020,6 +1039,8 @@ static void txDone (ostime_t delay, osjobcb_t func) {
|
||||
|
||||
#if !defined(DISABLE_JOIN)
|
||||
static void onJoinFailed (xref2osjob_t osjob) {
|
||||
LMIC_API_PARAMETER(osjob);
|
||||
|
||||
// Notify app - must call LMIC_reset() to stop joining
|
||||
// otherwise join procedure continues.
|
||||
reportEvent(EV_JOIN_FAILED);
|
||||
@ -1073,6 +1094,8 @@ static bit_t processJoinAccept (void) {
|
||||
u1_t hdr = LMIC.frame[0];
|
||||
u1_t dlen = LMIC.dataLen;
|
||||
u4_t mic = os_rlsbf4(&LMIC.frame[dlen-4]); // safe before modified by encrypt!
|
||||
LMIC_EV_VARIABLE(mic); // only used by EV().
|
||||
|
||||
if( (dlen != LEN_JA && dlen != LEN_JAEXT)
|
||||
|| (hdr & (HDR_FTYPE|HDR_MAJOR)) != (HDR_FTYPE_JACC|HDR_MAJOR_V1) ) {
|
||||
EV(specCond, ERR, (e_.reason = EV::specCond_t::UNEXPECTED_FRAME,
|
||||
@ -1109,7 +1132,7 @@ static bit_t processJoinAccept (void) {
|
||||
if( freq ) {
|
||||
LMIC_setupChannel(chidx, freq, 0, -1);
|
||||
#if LMIC_DEBUG_LEVEL > 1
|
||||
LMIC_DEBUG_PRINTF("%lu: Setup channel, idx=%d, freq=%lu\n", os_getTime(), chidx, (unsigned long)freq);
|
||||
LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": Setup channel, idx=%d, freq=%"PRIu32"\n", os_getTime(), chidx, freq);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@ -1161,6 +1184,8 @@ static bit_t processJoinAccept (void) {
|
||||
|
||||
|
||||
static void processRx2Jacc (xref2osjob_t osjob) {
|
||||
LMIC_API_PARAMETER(osjob);
|
||||
|
||||
if( LMIC.dataLen == 0 ) {
|
||||
initTxrxFlags(__func__, 0); // nothing in 1st/2nd DN slot
|
||||
}
|
||||
@ -1169,23 +1194,31 @@ static void processRx2Jacc (xref2osjob_t osjob) {
|
||||
|
||||
|
||||
static void setupRx2Jacc (xref2osjob_t osjob) {
|
||||
LMIC_API_PARAMETER(osjob);
|
||||
|
||||
LMIC.osjob.func = FUNC_ADDR(processRx2Jacc);
|
||||
setupRx2();
|
||||
}
|
||||
|
||||
|
||||
static void processRx1Jacc (xref2osjob_t osjob) {
|
||||
LMIC_API_PARAMETER(osjob);
|
||||
|
||||
if( LMIC.dataLen == 0 || !processJoinAccept() )
|
||||
schedRx12(DELAY_JACC2_osticks, FUNC_ADDR(setupRx2Jacc), LMIC.dn2Dr);
|
||||
}
|
||||
|
||||
|
||||
static void setupRx1Jacc (xref2osjob_t osjob) {
|
||||
LMIC_API_PARAMETER(osjob);
|
||||
|
||||
setupRx1(FUNC_ADDR(processRx1Jacc));
|
||||
}
|
||||
|
||||
|
||||
static void jreqDone (xref2osjob_t osjob) {
|
||||
LMIC_API_PARAMETER(osjob);
|
||||
|
||||
txDone(DELAY_JACC1_osticks, FUNC_ADDR(setupRx1Jacc));
|
||||
}
|
||||
|
||||
@ -1197,10 +1230,14 @@ static void jreqDone (xref2osjob_t osjob) {
|
||||
static bit_t processDnData(void);
|
||||
|
||||
static void processRx2DnDataDelay (xref2osjob_t osjob) {
|
||||
LMIC_API_PARAMETER(osjob);
|
||||
|
||||
processDnData();
|
||||
}
|
||||
|
||||
static void processRx2DnData (xref2osjob_t osjob) {
|
||||
LMIC_API_PARAMETER(osjob);
|
||||
|
||||
if( LMIC.dataLen == 0 ) {
|
||||
initTxrxFlags(__func__, 0); // nothing in 1st/2nd DN slot
|
||||
// Delay callback processing to avoid up TX while gateway is txing our missed frame!
|
||||
@ -1215,23 +1252,31 @@ static void processRx2DnData (xref2osjob_t osjob) {
|
||||
|
||||
|
||||
static void setupRx2DnData (xref2osjob_t osjob) {
|
||||
LMIC_API_PARAMETER(osjob);
|
||||
|
||||
LMIC.osjob.func = FUNC_ADDR(processRx2DnData);
|
||||
setupRx2();
|
||||
}
|
||||
|
||||
|
||||
static void processRx1DnData (xref2osjob_t osjob) {
|
||||
LMIC_API_PARAMETER(osjob);
|
||||
|
||||
if( LMIC.dataLen == 0 || !processDnData() )
|
||||
schedRx12(sec2osticks(LMIC.rxDelay +(int)DELAY_EXTDNW2), FUNC_ADDR(setupRx2DnData), LMIC.dn2Dr);
|
||||
}
|
||||
|
||||
|
||||
static void setupRx1DnData (xref2osjob_t osjob) {
|
||||
LMIC_API_PARAMETER(osjob);
|
||||
|
||||
setupRx1(FUNC_ADDR(processRx1DnData));
|
||||
}
|
||||
|
||||
|
||||
static void updataDone (xref2osjob_t osjob) {
|
||||
LMIC_API_PARAMETER(osjob);
|
||||
|
||||
txDone(sec2osticks(LMIC.rxDelay), FUNC_ADDR(setupRx1DnData));
|
||||
}
|
||||
|
||||
@ -1315,6 +1360,13 @@ static void buildDataFrame (void) {
|
||||
LMIC.txParamSetupAns = 0;
|
||||
}
|
||||
#endif
|
||||
#if LMIC_ENABLE_DeviceTimeReq
|
||||
if ( LMIC.txDeviceTimeReqState == lmic_RequestTimeState_tx ) {
|
||||
LMIC.frame[end+0] = MCMD_DeviceTimeReq;
|
||||
end += 1;
|
||||
LMIC.txDeviceTimeReqState = lmic_RequestTimeState_rx;
|
||||
}
|
||||
#endif // LMIC_ENABLE_DeviceTimeReq
|
||||
ASSERT(end <= OFF_DAT_OPTS+16);
|
||||
|
||||
u1_t flen = end + (txdata ? 5+dlen : 4);
|
||||
@ -1376,7 +1428,9 @@ static void buildDataFrame (void) {
|
||||
|
||||
#if !defined(DISABLE_BEACONS)
|
||||
// Callback from HAL during scan mode or when job timer expires.
|
||||
static void onBcnRx (xref2osjob_t job) {
|
||||
static void onBcnRx (xref2osjob_t osjob) {
|
||||
LMIC_API_PARAMETER(osjob);
|
||||
|
||||
// If we arrive via job timer make sure to put radio to rest.
|
||||
os_radio(RADIO_RST);
|
||||
os_clearCallback(&LMIC.osjob);
|
||||
@ -1497,6 +1551,8 @@ static void buildJoinRequest (u1_t ftype) {
|
||||
}
|
||||
|
||||
static void startJoining (xref2osjob_t osjob) {
|
||||
LMIC_API_PARAMETER(osjob);
|
||||
|
||||
reportEvent(EV_JOINING);
|
||||
}
|
||||
|
||||
@ -1530,6 +1586,8 @@ bit_t LMIC_startJoining (void) {
|
||||
|
||||
#if !defined(DISABLE_PING)
|
||||
static void processPingRx (xref2osjob_t osjob) {
|
||||
LMIC_API_PARAMETER(osjob);
|
||||
|
||||
if( LMIC.dataLen != 0 ) {
|
||||
initTxrxFlags(__func__, TXRX_PING);
|
||||
if( decodeFrame() ) {
|
||||
@ -1568,6 +1626,24 @@ static bit_t processDnData (void) {
|
||||
LMIC.dataBeg = LMIC.dataLen = 0;
|
||||
txcomplete:
|
||||
LMIC.opmode &= ~(OP_TXDATA|OP_TXRXPEND);
|
||||
|
||||
#if LMIC_ENABLE_DeviceTimeReq
|
||||
lmic_request_time_state_t const requestTimeState = LMIC.txDeviceTimeReqState;
|
||||
if ( requestTimeState != lmic_RequestTimeState_idle ) {
|
||||
lmic_request_network_time_cb_t * const pNetworkTimeCb = LMIC.pNetworkTimeCb;
|
||||
int flagSuccess = (LMIC.txDeviceTimeReqState == lmic_RequestTimeState_success);
|
||||
LMIC.txDeviceTimeReqState = lmic_RequestTimeState_idle;
|
||||
if (pNetworkTimeCb != NULL) {
|
||||
// reset the callback, so that the user's routine
|
||||
// can post another request if desired.
|
||||
LMIC.pNetworkTimeCb = NULL;
|
||||
|
||||
// call the user's notification routine.
|
||||
(*pNetworkTimeCb)(LMIC.pNetworkTimeUserData, flagSuccess);
|
||||
}
|
||||
}
|
||||
#endif // LMIC_ENABLE_DeviceTimeReq
|
||||
|
||||
if( (LMIC.txrxFlags & (TXRX_DNW1|TXRX_DNW2|TXRX_PING)) != 0 && (LMIC.opmode & OP_LINKDEAD) != 0 ) {
|
||||
LMIC.opmode &= ~OP_LINKDEAD;
|
||||
reportEvent(EV_LINK_ALIVE);
|
||||
@ -1611,6 +1687,8 @@ static bit_t processDnData (void) {
|
||||
|
||||
#if !defined(DISABLE_BEACONS)
|
||||
static void processBeacon (xref2osjob_t osjob) {
|
||||
LMIC_API_PARAMETER(osjob);
|
||||
|
||||
ostime_t lasttx = LMIC.bcninfo.txtime; // save here - decodeBeacon might overwrite
|
||||
u1_t flags = LMIC.bcninfo.flags;
|
||||
ev_t ev;
|
||||
@ -1672,6 +1750,8 @@ static void processBeacon (xref2osjob_t osjob) {
|
||||
|
||||
|
||||
static void startRxBcn (xref2osjob_t osjob) {
|
||||
LMIC_API_PARAMETER(osjob);
|
||||
|
||||
LMIC.osjob.func = FUNC_ADDR(processBeacon);
|
||||
os_radio(RADIO_RX);
|
||||
}
|
||||
@ -1680,6 +1760,8 @@ static void startRxBcn (xref2osjob_t osjob) {
|
||||
|
||||
#if !defined(DISABLE_PING)
|
||||
static void startRxPing (xref2osjob_t osjob) {
|
||||
LMIC_API_PARAMETER(osjob);
|
||||
|
||||
LMIC.osjob.func = FUNC_ADDR(processPingRx);
|
||||
os_radio(RADIO_RX);
|
||||
}
|
||||
@ -1689,7 +1771,7 @@ static void startRxPing (xref2osjob_t osjob) {
|
||||
// Decide what to do next for the MAC layer of a device
|
||||
static void engineUpdate (void) {
|
||||
#if LMIC_DEBUG_LEVEL > 0
|
||||
LMIC_DEBUG_PRINTF("%lu: engineUpdate, opmode=0x%x\n", os_getTime(), LMIC.opmode);
|
||||
LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": engineUpdate, opmode=0x%x\n", os_getTime(), LMIC.opmode);
|
||||
#endif
|
||||
// Check for ongoing state: scan or TX/RX transaction
|
||||
if( (LMIC.opmode & (OP_SCAN|OP_TXRXPEND|OP_SHUTDOWN)) != 0 )
|
||||
@ -1703,10 +1785,11 @@ static void engineUpdate (void) {
|
||||
#endif // !DISABLE_JOIN
|
||||
|
||||
ostime_t now = os_getTime();
|
||||
ostime_t rxtime = 0;
|
||||
ostime_t txbeg = 0;
|
||||
|
||||
#if !defined(DISABLE_BEACONS)
|
||||
ostime_t rxtime = 0;
|
||||
|
||||
if( (LMIC.opmode & OP_TRACK) != 0 ) {
|
||||
// We are tracking a beacon
|
||||
ASSERT( now + RX_RAMPUP - LMIC.bcnRxtime <= 0 );
|
||||
@ -1848,7 +1931,7 @@ static void engineUpdate (void) {
|
||||
e_.eui = MAIN::CDEV->getEui(),
|
||||
e_.info = osticks2ms(txbeg-now),
|
||||
e_.info2 = LMIC.seqnoUp-1));
|
||||
LMIC_X_DEBUG_PRINTF("%lu: next engine update in %lu\n", now, txbeg-TX_RAMPUP);
|
||||
LMIC_X_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": next engine update in %"LMIC_PRId_ostime_t"\n", now, txbeg-TX_RAMPUP);
|
||||
os_setTimedCallback(&LMIC.osjob, txbeg-TX_RAMPUP, FUNC_ADDR(runEngineUpdate));
|
||||
}
|
||||
|
||||
@ -1902,6 +1985,11 @@ void LMIC_reset (void) {
|
||||
DO_DEVDB(LMIC.ping.dr, pingDr);
|
||||
DO_DEVDB(LMIC.ping.intvExp, pingIntvExp);
|
||||
#endif // !DISABLE_PING
|
||||
#if LMIC_ENABLE_DeviceTimeReq
|
||||
LMIC.txDeviceTimeReqState = lmic_RequestTimeState_idle;
|
||||
LMIC.netDeviceTime = 0; // the "invalid" time.
|
||||
LMIC.netDeviceTimeFrac = 0;
|
||||
#endif // LMIC_ENABLE_DeviceTimeReq
|
||||
}
|
||||
|
||||
|
||||
@ -1943,7 +2031,6 @@ int LMIC_setTxData2 (u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Send a payload-less message to signal device is alive
|
||||
void LMIC_sendAlive (void) {
|
||||
LMIC.opmode |= OP_POLL;
|
||||
@ -2041,3 +2128,36 @@ void LMIC_getSessionKeys (u4_t *netid, devaddr_t *devaddr, xref2u1_t nwkKey, xre
|
||||
memcpy(artKey, LMIC.artKey, sizeof(LMIC.artKey));
|
||||
memcpy(nwkKey, LMIC.nwkKey, sizeof(LMIC.nwkKey));
|
||||
}
|
||||
|
||||
// \brief post an asynchronous request for the network time.
|
||||
void LMIC_requestNetworkTime(lmic_request_network_time_cb_t *pCallbackfn, void *pUserData) {
|
||||
#if LMIC_ENABLE_DeviceTimeReq
|
||||
if (LMIC.txDeviceTimeReqState == lmic_RequestTimeState_idle) {
|
||||
LMIC.txDeviceTimeReqState = lmic_RequestTimeState_tx;
|
||||
LMIC.pNetworkTimeCb = pCallbackfn;
|
||||
LMIC.pNetworkTimeUserData = pUserData;
|
||||
return;
|
||||
}
|
||||
#endif // LMIC_ENABLE_DeviceTimeReq
|
||||
// if no device time support, or if not in proper state,
|
||||
// report a failure.
|
||||
if (pCallbackfn != NULL)
|
||||
(*pCallbackfn)(pUserData, /* false */ 0);
|
||||
}
|
||||
|
||||
// \brief return local/remote time pair (if valid, and DeviceTimeReq enabled),
|
||||
// return true for success, false for error. We adjust the sampled OS time
|
||||
// back in time to the nearest second boundary.
|
||||
int LMIC_getNetworkTimeReference(lmic_time_reference_t *pReference) {
|
||||
#if LMIC_ENABLE_DeviceTimeReq
|
||||
if (pReference != NULL && // valid parameter, and
|
||||
LMIC.netDeviceTime != 0) { // ... we have a reasonable answer.
|
||||
const ostime_t tAdjust = LMIC.netDeviceTimeFrac * ms2osticks(1000) / 256;
|
||||
|
||||
pReference->tLocal = LMIC.localDeviceTime - tAdjust;
|
||||
pReference->tNetwork = LMIC.netDeviceTime;
|
||||
return 1;
|
||||
}
|
||||
#endif // LMIC_ENABLE_DeviceTimeReq
|
||||
return 0;
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ extern "C"{
|
||||
#define ARDUINO_LMIC_VERSION_CALC(major, minor, patch, local) \
|
||||
(((major) << 24u) | ((minor) << 16u) | ((patch) << 8u) | (local))
|
||||
|
||||
#define ARDUINO_LMIC_VERSION ARDUINO_LMIC_VERSION_CALC(2, 2, 2, 0)
|
||||
#define ARDUINO_LMIC_VERSION ARDUINO_LMIC_VERSION_CALC(2, 2, 2, 0) /* v2.2.2 */
|
||||
|
||||
#define ARDUINO_LMIC_VERSION_GET_MAJOR(v) \
|
||||
(((v) >> 24u) & 0xFFu)
|
||||
@ -243,6 +243,35 @@ enum {
|
||||
MAX_CLOCK_ERROR = 65536,
|
||||
};
|
||||
|
||||
// network time request callback function
|
||||
// defined unconditionally, because APIs and types can't change based on config.
|
||||
// This is called when a time-request succeeds or when we get a downlink
|
||||
// without time request, "completing" the pending time request.
|
||||
typedef void lmic_request_network_time_cb_t(void *pUserData, int flagSuccess);
|
||||
|
||||
// how the network represents time.
|
||||
typedef u4_t lmic_gpstime_t;
|
||||
|
||||
// rather than deal with 1/256 second tick, we adjust ostime back
|
||||
// (as it's high res) to match tNetwork.
|
||||
typedef struct lmic_time_reference_s lmic_time_reference_t;
|
||||
|
||||
struct lmic_time_reference_s {
|
||||
// our best idea of when we sent the uplink (end of packet).
|
||||
ostime_t tLocal;
|
||||
// the network's best idea of when we sent the uplink.
|
||||
lmic_gpstime_t tNetwork;
|
||||
};
|
||||
|
||||
enum lmic_request_time_state_e {
|
||||
lmic_RequestTimeState_idle = 0, // we're not doing anything
|
||||
lmic_RequestTimeState_tx, // we want to tx a time request on next uplink
|
||||
lmic_RequestTimeState_rx, // we have tx'ed, next downlink completes.
|
||||
lmic_RequestTimeState_success // we sucessfully got time.
|
||||
};
|
||||
|
||||
typedef u1_t lmic_request_time_state_t;
|
||||
|
||||
struct lmic_t {
|
||||
// Radio settings TX/RX (also accessed by HAL)
|
||||
ostime_t txend;
|
||||
@ -306,6 +335,14 @@ struct lmic_t {
|
||||
devaddr_t devaddr;
|
||||
u4_t seqnoDn; // device level down stream seqno
|
||||
u4_t seqnoUp;
|
||||
#if LMIC_ENABLE_DeviceTimeReq
|
||||
// put here for alignment, to reduce RAM use.
|
||||
ostime_t localDeviceTime; // the LMIC.txend value for last DeviceTimeAns
|
||||
lmic_gpstime_t netDeviceTime; // the netDeviceTime for lastDeviceTimeAns
|
||||
// zero ==> not valid.
|
||||
lmic_request_network_time_cb_t *pNetworkTimeCb; // call-back routine
|
||||
void *pNetworkTimeUserData; // call-back data
|
||||
#endif // LMIC_ENABLE_DeviceTimeReq
|
||||
|
||||
u1_t dnConf; // dn frame confirm pending: LORA::FCT_ACK or 0
|
||||
s1_t adrAckReq; // counter until we reset data rate (0=off)
|
||||
@ -329,6 +366,10 @@ struct lmic_t {
|
||||
bit_t txParamSetupAns; // transmit setup answer pending.
|
||||
u1_t txParam; // the saved TX param byte.
|
||||
#endif
|
||||
#if LMIC_ENABLE_DeviceTimeReq
|
||||
lmic_request_time_state_t txDeviceTimeReqState; // current state, initially idle.
|
||||
u1_t netDeviceTimeFrac; // updated on any DeviceTimeAns.
|
||||
#endif
|
||||
|
||||
// rx1DrOffset is the offset from uplink to downlink datarate
|
||||
u1_t rx1DrOffset; // captured from join. zero by default.
|
||||
@ -368,6 +409,7 @@ struct lmic_t {
|
||||
|
||||
u1_t noRXIQinversion;
|
||||
};
|
||||
|
||||
//! \var struct lmic_t LMIC
|
||||
//! The state of LMIC MAC layer is encapsulated in this variable.
|
||||
DECLARE_LMIC; //!< \internal
|
||||
@ -417,6 +459,9 @@ u4_t LMIC_getSeqnoUp (void);
|
||||
u4_t LMIC_setSeqnoUp (u4_t);
|
||||
void LMIC_getSessionKeys (u4_t *netid, devaddr_t *devaddr, xref2u1_t nwkKey, xref2u1_t artKey);
|
||||
|
||||
void LMIC_requestNetworkTime(lmic_request_network_time_cb_t *pCallbackfn, void *pUserData);
|
||||
int LMIC_getNetworkTimeReference(lmic_time_reference_t *pReference);
|
||||
|
||||
// Declare onEvent() function, to make sure any definition will have the
|
||||
// C conventions, even when in a C++ file.
|
||||
DECL_ON_LMIC_EVENT;
|
||||
|
@ -76,11 +76,15 @@ static CONST_TABLE(u1_t, maxFrameLens_dwell1)[] = {
|
||||
|
||||
static uint8_t
|
||||
LMICas923_getUplinkDwellBit(uint8_t mcmd_txparam) {
|
||||
LMIC_API_PARAMETER(mcmd_txparam);
|
||||
|
||||
return (LMIC.txParam & MCMD_TxParam_TxDWELL_MASK) != 0;
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
LMICas923_getDownlinkDwellBit(uint8_t mcmd_txparam) {
|
||||
LMIC_API_PARAMETER(mcmd_txparam);
|
||||
|
||||
return (LMIC.txParam & MCMD_TxParam_RxDWELL_MASK) != 0;
|
||||
}
|
||||
|
||||
@ -164,6 +168,8 @@ static CONST_TABLE(u4_t, iniChannelFreq)[NUM_DEFAULT_CHANNELS] = {
|
||||
|
||||
// as923 ignores join, becuase the channel setup is the same either way.
|
||||
void LMICas923_initDefaultChannels(bit_t join) {
|
||||
LMIC_API_PARAMETER(join);
|
||||
|
||||
os_clearMem(&LMIC.channelFreq, sizeof(LMIC.channelFreq));
|
||||
os_clearMem(&LMIC.channelDrMap, sizeof(LMIC.channelDrMap));
|
||||
os_clearMem(&LMIC.bands, sizeof(LMIC.bands));
|
||||
|
@ -101,6 +101,11 @@ u4_t LMICau921_convFreq(xref2cu1_t ptr) {
|
||||
|
||||
// au921: no support for xchannels.
|
||||
bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) {
|
||||
LMIC_API_PARAMETER(chidx);
|
||||
LMIC_API_PARAMETER(freq);
|
||||
LMIC_API_PARAMETER(drmap);
|
||||
LMIC_API_PARAMETER(band);
|
||||
|
||||
return 0; // all channels are hardwired.
|
||||
}
|
||||
|
||||
|
@ -113,6 +113,12 @@ Revision history:
|
||||
// following values. These are in order of the sections in the manual. Not all of the
|
||||
// below are supported yet.
|
||||
//
|
||||
// CFG_as923jp is treated as a special case of CFG_as923, so it's not included in
|
||||
// the below.
|
||||
//
|
||||
// TODO(tmm@mcci.com) consider moving this block to a central file as it's not
|
||||
// user-editable.
|
||||
//
|
||||
# define CFG_LMIC_REGION_MASK \
|
||||
((defined(CFG_eu868) << LMIC_REGION_eu868) | \
|
||||
(defined(CFG_us915) << LMIC_REGION_us915) | \
|
||||
@ -126,6 +132,8 @@ Revision history:
|
||||
0)
|
||||
|
||||
// the selected region.
|
||||
// TODO(tmm@mcci.com) consider moving this block to a central file as it's not
|
||||
// user-editable.
|
||||
#if defined(CFG_eu868)
|
||||
# define CFG_region LMIC_REGION_eu868
|
||||
#elif defined(CFG_us915)
|
||||
@ -138,6 +146,10 @@ Revision history:
|
||||
# define CFG_region LMIC_REGION_au921
|
||||
#elif defined(CFG_cn490)
|
||||
# define CFG_region LMIC_REGION_cn490
|
||||
#elif defined(CFG_as923jp)
|
||||
# define CFG_as923 1 /* CFG_as923jp implies CFG_as923 */
|
||||
# define CFG_region LMIC_REGION_as923
|
||||
# define LMIC_COUNTRY_CODE LMIC_COUNTRY_CODE_JP
|
||||
#elif defined(CFG_as923)
|
||||
# define CFG_region LMIC_REGION_as923
|
||||
#elif defined(CFG_kr921)
|
||||
@ -148,7 +160,11 @@ Revision history:
|
||||
# define CFG_region 0
|
||||
#endif
|
||||
|
||||
// finally the mask of` US-like and EU-like regions
|
||||
// a bitmask of EU-like regions -- these are regions which have up to 16
|
||||
// channels indidually programmable via downloink.
|
||||
//
|
||||
// TODO(tmm@mcci.com) consider moving this block to a central file as it's not
|
||||
// user-editable.
|
||||
#define CFG_LMIC_EU_like_MASK ( \
|
||||
(1 << LMIC_REGION_eu868) | \
|
||||
/* (1 << LMIC_REGION_us915) | */ \
|
||||
@ -161,6 +177,12 @@ Revision history:
|
||||
(1 << LMIC_REGION_in866) | \
|
||||
0)
|
||||
|
||||
// a bitmask of` US-like regions -- these are regions with 64 fixed 125 kHz channels
|
||||
// overlaid by 8 500 kHz channels. The channel frequencies can't be changed, but
|
||||
// subsets of channels can be selected via masks.
|
||||
//
|
||||
// TODO(tmm@mcci.com) consider moving this block to a central file as it's not
|
||||
// user-editable.
|
||||
#define CFG_LMIC_US_like_MASK ( \
|
||||
/* (1 << LMIC_REGION_eu868) | */ \
|
||||
(1 << LMIC_REGION_us915) | \
|
||||
@ -173,9 +195,12 @@ Revision history:
|
||||
/* (1 << LMIC_REGION_in866) | */ \
|
||||
0)
|
||||
|
||||
//
|
||||
// booleans that are true if the configured region is EU-like or US-like.
|
||||
// TODO(tmm@mcci.com) consider moving this block to a central file as it's not
|
||||
// user-editable.
|
||||
//
|
||||
#define CFG_LMIC_EU_like (!!(CFG_LMIC_REGION_MASK & CFG_LMIC_EU_like_MASK))
|
||||
#define CFG_LMIC_US_like (!!(CFG_LMIC_REGION_MASK & CFG_LMIC_US_like_MASK))
|
||||
|
||||
|
||||
|
||||
#endif /* _LMIC_CONFIG_PRECONDITIONS_H_ */
|
||||
|
@ -33,9 +33,11 @@
|
||||
#if CFG_LMIC_EU_like
|
||||
|
||||
void LMIC_enableSubBand(u1_t band) {
|
||||
LMIC_API_PARAMETER(band);
|
||||
}
|
||||
|
||||
void LMIC_disableSubBand(u1_t band) {
|
||||
LMIC_API_PARAMETER(band);
|
||||
}
|
||||
|
||||
void LMIC_disableChannel(u1_t channel) {
|
||||
@ -46,6 +48,7 @@ void LMIC_disableChannel(u1_t channel) {
|
||||
|
||||
// this is a no-op provided for compatibilty
|
||||
void LMIC_enableChannel(u1_t channel) {
|
||||
LMIC_API_PARAMETER(channel);
|
||||
}
|
||||
|
||||
u1_t LMICeulike_mapChannels(u1_t chpage, u2_t chmap) {
|
||||
|
@ -95,6 +95,8 @@ static CONST_TABLE(u4_t, iniChannelFreq)[NUM_DEFAULT_CHANNELS] = {
|
||||
|
||||
// india ignores join, becuase the channel setup is the same either way.
|
||||
void LMICin866_initDefaultChannels(bit_t join) {
|
||||
LMIC_API_PARAMETER(join);
|
||||
|
||||
os_clearMem(&LMIC.channelFreq, sizeof(LMIC.channelFreq));
|
||||
os_clearMem(&LMIC.channelDrMap, sizeof(LMIC.channelDrMap));
|
||||
os_clearMem(&LMIC.bands, sizeof(LMIC.bands));
|
||||
|
@ -87,6 +87,8 @@ u4_t LMICus915_convFreq(xref2cu1_t ptr) {
|
||||
}
|
||||
|
||||
bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) {
|
||||
LMIC_API_PARAMETER(band);
|
||||
|
||||
if (chidx < 72 || chidx >= 72 + MAX_XCHANNELS)
|
||||
return 0; // channels 0..71 are hardwired
|
||||
LMIC.xchFreq[chidx - 72] = freq;
|
||||
|
@ -71,12 +71,18 @@ static void setNextChannel(uint start, uint end, uint count) {
|
||||
|
||||
|
||||
bit_t LMIC_setupBand(u1_t bandidx, s1_t txpow, u2_t txcap) {
|
||||
LMIC_API_PARAMETER(bandidx);
|
||||
LMIC_API_PARAMETER(txpow);
|
||||
LMIC_API_PARAMETER(txcap);
|
||||
|
||||
// nothing; just succeed.
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
void LMICuslike_initDefaultChannels(bit_t fJoin) {
|
||||
LMIC_API_PARAMETER(fJoin);
|
||||
|
||||
// things work the same for join as normal.
|
||||
for (u1_t i = 0; i<4; i++)
|
||||
LMIC.channelMap[i] = 0xFFFF;
|
||||
|
@ -449,6 +449,7 @@ enum {
|
||||
MCMD_RXTimingSetupAns = 0x08, // : -
|
||||
MCMD_TxParamSetupAns = 0x09, // : -
|
||||
MCMD_DIChannelAns = 0x0A, // : u1: [7-2]:RFU 1:exists 0:OK
|
||||
MCMD_DeviceTimeReq = 0x0D,
|
||||
|
||||
// Class B
|
||||
MCMD_PING_IND = 0x10, // - pingability indic : u1: 7=RFU, 6-4:interval, 3-0:datarate
|
||||
@ -468,6 +469,7 @@ enum {
|
||||
MCMD_RXTimingSetupReq = 0x08, // : u1: [7-4]:RFU [3-0]: Delay 1-15s (0 => 1)
|
||||
MCMD_TxParamSetupReq = 0x09, // : u1: [7-6]:RFU [5:4]: dl dwell/ul dwell [3:0] max EIRP
|
||||
MCMD_DIChannelReq = 0x0A, // : u1: channel, u3: frequency
|
||||
MCMD_DeviceTimeAns = 0x0D,
|
||||
|
||||
// Class B
|
||||
MCMD_PING_SET = 0x11, // set ping freq : u3: freq
|
||||
@ -583,33 +585,33 @@ typedef u4_t devaddr_t;
|
||||
// RX quality (device)
|
||||
enum { RSSI_OFF=64, SNR_SCALEUP=4 };
|
||||
|
||||
inline sf_t getSf (rps_t params) { return (sf_t)(params & 0x7); }
|
||||
inline rps_t setSf (rps_t params, sf_t sf) { return (rps_t)((params & ~0x7) | sf); }
|
||||
inline bw_t getBw (rps_t params) { return (bw_t)((params >> 3) & 0x3); }
|
||||
inline rps_t setBw (rps_t params, bw_t cr) { return (rps_t)((params & ~0x18) | (cr<<3)); }
|
||||
inline cr_t getCr (rps_t params) { return (cr_t)((params >> 5) & 0x3); }
|
||||
inline rps_t setCr (rps_t params, cr_t cr) { return (rps_t)((params & ~0x60) | (cr<<5)); }
|
||||
inline int getNocrc(rps_t params) { return ((params >> 7) & 0x1); }
|
||||
inline rps_t setNocrc(rps_t params, int nocrc) { return (rps_t)((params & ~0x80) | (nocrc<<7)); }
|
||||
inline int getIh (rps_t params) { return ((params >> 8) & 0xFF); }
|
||||
inline rps_t setIh (rps_t params, int ih) { return (rps_t)((params & ~0xFF00) | (ih<<8)); }
|
||||
inline rps_t makeRps (sf_t sf, bw_t bw, cr_t cr, int ih, int nocrc) {
|
||||
static inline sf_t getSf (rps_t params) { return (sf_t)(params & 0x7); }
|
||||
static inline rps_t setSf (rps_t params, sf_t sf) { return (rps_t)((params & ~0x7) | sf); }
|
||||
static inline bw_t getBw (rps_t params) { return (bw_t)((params >> 3) & 0x3); }
|
||||
static inline rps_t setBw (rps_t params, bw_t cr) { return (rps_t)((params & ~0x18) | (cr<<3)); }
|
||||
static inline cr_t getCr (rps_t params) { return (cr_t)((params >> 5) & 0x3); }
|
||||
static inline rps_t setCr (rps_t params, cr_t cr) { return (rps_t)((params & ~0x60) | (cr<<5)); }
|
||||
static inline int getNocrc(rps_t params) { return ((params >> 7) & 0x1); }
|
||||
static inline rps_t setNocrc(rps_t params, int nocrc) { return (rps_t)((params & ~0x80) | (nocrc<<7)); }
|
||||
static inline int getIh (rps_t params) { return ((params >> 8) & 0xFF); }
|
||||
static inline rps_t setIh (rps_t params, int ih) { return (rps_t)((params & ~0xFF00) | (ih<<8)); }
|
||||
static inline rps_t makeRps (sf_t sf, bw_t bw, cr_t cr, int ih, int nocrc) {
|
||||
return sf | (bw<<3) | (cr<<5) | (nocrc?(1<<7):0) | ((ih&0xFF)<<8);
|
||||
}
|
||||
#define MAKERPS(sf,bw,cr,ih,nocrc) ((rps_t)((sf) | ((bw)<<3) | ((cr)<<5) | ((nocrc)?(1<<7):0) | ((ih&0xFF)<<8)))
|
||||
// Two frames with params r1/r2 would interfere on air: same SFx + BWx
|
||||
inline int sameSfBw(rps_t r1, rps_t r2) { return ((r1^r2)&0x1F) == 0; }
|
||||
static inline int sameSfBw(rps_t r1, rps_t r2) { return ((r1^r2)&0x1F) == 0; }
|
||||
|
||||
extern CONST_TABLE(u1_t, _DR2RPS_CRC)[];
|
||||
inline rps_t updr2rps (dr_t dr) { return (rps_t)TABLE_GET_U1(_DR2RPS_CRC, dr+1); }
|
||||
inline rps_t dndr2rps (dr_t dr) { return setNocrc(updr2rps(dr),1); }
|
||||
inline int isFasterDR (dr_t dr1, dr_t dr2) { return dr1 > dr2; }
|
||||
inline int isSlowerDR (dr_t dr1, dr_t dr2) { return dr1 < dr2; }
|
||||
inline dr_t incDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr+2)==ILLEGAL_RPS ? dr : (dr_t)(dr+1); } // increase data rate
|
||||
inline dr_t decDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr )==ILLEGAL_RPS ? dr : (dr_t)(dr-1); } // decrease data rate
|
||||
inline dr_t assertDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr+1)==ILLEGAL_RPS ? DR_DFLTMIN : dr; } // force into a valid DR
|
||||
inline bit_t validDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr+1)!=ILLEGAL_RPS; } // in range
|
||||
inline dr_t lowerDR (dr_t dr, u1_t n) { while(n--){dr=decDR(dr);} return dr; } // decrease data rate by n steps
|
||||
static inline rps_t updr2rps (dr_t dr) { return (rps_t)TABLE_GET_U1(_DR2RPS_CRC, dr+1); }
|
||||
static inline rps_t dndr2rps (dr_t dr) { return setNocrc(updr2rps(dr),1); }
|
||||
static inline int isFasterDR (dr_t dr1, dr_t dr2) { return dr1 > dr2; }
|
||||
static inline int isSlowerDR (dr_t dr1, dr_t dr2) { return dr1 < dr2; }
|
||||
static inline dr_t incDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr+2)==ILLEGAL_RPS ? dr : (dr_t)(dr+1); } // increase data rate
|
||||
static inline dr_t decDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr )==ILLEGAL_RPS ? dr : (dr_t)(dr-1); } // decrease data rate
|
||||
static inline dr_t assertDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr+1)==ILLEGAL_RPS ? (dr_t)DR_DFLTMIN : dr; } // force into a valid DR
|
||||
static inline bit_t validDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr+1)!=ILLEGAL_RPS; } // in range
|
||||
static inline dr_t lowerDR (dr_t dr, u1_t n) { while(n--){dr=decDR(dr);} return dr; } // decrease data rate by n steps
|
||||
|
||||
//
|
||||
// BEG: Keep in sync with lorabase.hpp
|
||||
|
@ -71,7 +71,11 @@ static int unlinkjob (osjob_t** pnext, osjob_t* job) {
|
||||
// clear scheduled job
|
||||
void os_clearCallback (osjob_t* job) {
|
||||
hal_disableIRQs();
|
||||
unlinkjob(&OS.scheduledjobs, job) || unlinkjob(&OS.runnablejobs, job);
|
||||
|
||||
// if it's not in the scheduled jobs, look in the runnable...
|
||||
if (! unlinkjob(&OS.scheduledjobs, job))
|
||||
unlinkjob(&OS.runnablejobs, job);
|
||||
|
||||
hal_enableIRQs();
|
||||
}
|
||||
|
||||
|
@ -75,6 +75,14 @@ typedef const u1_t* xref2cu1_t;
|
||||
typedef u1_t* xref2u1_t;
|
||||
typedef s4_t ostime_t;
|
||||
|
||||
// int32_t == s4_t is long on some platforms; and someday
|
||||
// we will want 64-bit ostime_t. So, we will use a macro for the
|
||||
// print formatting of ostime_t.
|
||||
#ifndef LMIC_PRId_ostime_t
|
||||
# include <inttypes.h>
|
||||
# define LMIC_PRId_ostime_t PRId32
|
||||
#endif
|
||||
|
||||
#define TYPEDEF_xref2rps_t typedef rps_t* xref2rps_t
|
||||
#define TYPEDEF_xref2rxsched_t typedef rxsched_t* xref2rxsched_t
|
||||
#define TYPEDEF_xref2chnldef_t typedef chnldef_t* xref2chnldef_t
|
||||
@ -83,6 +91,105 @@ typedef s4_t ostime_t;
|
||||
|
||||
#define SIZEOFEXPR(x) sizeof(x)
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Annotations to avoid various "unused" warnings. These must appear as a
|
||||
// statement in the function body; the macro annotates the variable to quiet
|
||||
// compiler warnings. The way this is done is compiler-specific, and so these
|
||||
// definitions are fall-backs, which might be overridden.
|
||||
//
|
||||
// Although these are all similar, we don't want extra macro expansions,
|
||||
// so we define each one explicitly rather than relying on a common macro.
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
// signal that a parameter is intentionally unused.
|
||||
#ifndef LMIC_UNREFERENCED_PARAMETER
|
||||
# define LMIC_UNREFERENCED_PARAMETER(v) do { (void) (v); } while (0)
|
||||
#endif
|
||||
|
||||
// an API parameter is a parameter that is required by an API definition, but
|
||||
// happens to be unreferenced in this implementation. This is a stronger
|
||||
// assertion than LMIC_UNREFERENCED_PARAMETER(): this parameter is here
|
||||
// becuase of an API contract, but we have no use for it in this function.
|
||||
#ifndef LMIC_API_PARAMETER
|
||||
# define LMIC_API_PARAMETER(v) do { (void) (v); } while (0)
|
||||
#endif
|
||||
|
||||
// an intentionally-unreferenced variable.
|
||||
#ifndef LMIC_UNREFERENCED_VARIABLE
|
||||
# define LMIC_UNREFERENCED_VARIABLE(v) do { (void) (v); } while (0)
|
||||
#endif
|
||||
|
||||
// we have three (!) debug levels (LMIC_DEBUG_LEVEL > 0, LMIC_DEBUG_LEVEL > 1,
|
||||
// and LMIC_X_DEBUG_LEVEL > 0. In each case we might have parameters or
|
||||
// or varables that are only refereneced at the target debug level.
|
||||
|
||||
// Parameter referenced only if debugging at level > 0.
|
||||
#ifndef LMIC_DEBUG1_PARAMETER
|
||||
# if LMIC_DEBUG_LEVEL > 0
|
||||
# define LMIC_DEBUG1_PARAMETER(v) do { ; } while (0)
|
||||
# else
|
||||
# define LMIC_DEBUG1_PARAMETER(v) do { (void) (v); } while (0)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// variable referenced only if debugging at level > 0
|
||||
#ifndef LMIC_DEBUG1_VARIABLE
|
||||
# if LMIC_DEBUG_LEVEL > 0
|
||||
# define LMIC_DEBUG1_VARIABLE(v) do { ; } while (0)
|
||||
# else
|
||||
# define LMIC_DEBUG1_VARIABLE(v) do { (void) (v); } while (0)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// parameter referenced only if debugging at level > 1
|
||||
#ifndef LMIC_DEBUG2_PARAMETER
|
||||
# if LMIC_DEBUG_LEVEL > 1
|
||||
# define LMIC_DEBUG2_PARAMETER(v) do { ; } while (0)
|
||||
# else
|
||||
# define LMIC_DEBUG2_PARAMETER(v) do { (void) (v); } while (0)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// variable referenced only if debugging at level > 1
|
||||
#ifndef LMIC_DEBUG2_VARIABLE
|
||||
# if LMIC_DEBUG_LEVEL > 1
|
||||
# define LMIC_DEBUG2_VARIABLE(v) do { ; } while (0)
|
||||
# else
|
||||
# define LMIC_DEBUG2_VARIABLE(v) do { (void) (v); } while (0)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// parameter referenced only if LMIC_X_DEBUG_LEVEL > 0
|
||||
#ifndef LMIC_X_DEBUG_PARAMETER
|
||||
# if LMIC_X_DEBUG_LEVEL > 0
|
||||
# define LMIC_X_DEBUG_PARAMETER(v) do { ; } while (0)
|
||||
# else
|
||||
# define LMIC_X_DEBUG_PARAMETER(v) do { (void) (v); } while (0)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// variable referenced only if LMIC_X_DEBUG_LEVEL > 0
|
||||
#ifndef LMIC_X_DEBUG_VARIABLE
|
||||
# if LMIC_X_DEBUG_LEVEL > 0
|
||||
# define LMIC_X_DEBUG_VARIABLE(v) do { ; } while (0)
|
||||
# else
|
||||
# define LMIC_X_DEBUG_VARIABLE(v) do { (void) (v); } while (0)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// parameter referenced only if EV() macro is enabled (which it never is)
|
||||
// TODO(tmm@mcci.com) take out the EV() framework as it reuqires C++, and
|
||||
// this code is really C-99 to its bones.
|
||||
#ifndef LMIC_EV_PARAMETER
|
||||
# define LMIC_EV_PARAMETER(v) do { (void) (v); } while (0)
|
||||
#endif
|
||||
|
||||
// variable referenced only if EV() macro is defined.
|
||||
#ifndef LMIC_EV_VARIABLE
|
||||
# define LMIC_EV_VARIABLE(v) do { (void) (v); } while (0)
|
||||
#endif
|
||||
|
||||
|
||||
#define ON_LMIC_EVENT(ev) onEvent(ev)
|
||||
#define DECL_ON_LMIC_EVENT void onEvent(ev_t e)
|
||||
|
||||
@ -258,7 +365,7 @@ u2_t os_crc16 (xref2cu1_t d, uint len);
|
||||
// progmem using pgm_read_xx, or accesses memory directly when the
|
||||
// index is a constant so gcc can optimize it away;
|
||||
#define TABLE_GETTER(postfix, type, pgm_type) \
|
||||
inline type table_get ## postfix(const type *table, size_t index) { \
|
||||
static inline type table_get ## postfix(const type *table, size_t index) { \
|
||||
if (__builtin_constant_p(table[index])) \
|
||||
return table[index]; \
|
||||
return pgm_read_ ## pgm_type(&table[index]); \
|
||||
@ -278,13 +385,13 @@ u2_t os_crc16 (xref2cu1_t d, uint len);
|
||||
// For AVR, store constants in PROGMEM, saving on RAM usage
|
||||
#define CONST_TABLE(type, name) const type PROGMEM RESOLVE_TABLE(name)
|
||||
#else
|
||||
inline u1_t table_get_u1(const u1_t *table, size_t index) { return table[index]; }
|
||||
inline s1_t table_get_s1(const s1_t *table, size_t index) { return table[index]; }
|
||||
inline u2_t table_get_u2(const u2_t *table, size_t index) { return table[index]; }
|
||||
inline s2_t table_get_s2(const s2_t *table, size_t index) { return table[index]; }
|
||||
inline u4_t table_get_u4(const u4_t *table, size_t index) { return table[index]; }
|
||||
inline s4_t table_get_s4(const s4_t *table, size_t index) { return table[index]; }
|
||||
inline ostime_t table_get_ostime(const ostime_t *table, size_t index) { return table[index]; }
|
||||
static inline u1_t table_get_u1(const u1_t *table, size_t index) { return table[index]; }
|
||||
static inline s1_t table_get_s1(const s1_t *table, size_t index) { return table[index]; }
|
||||
static inline u2_t table_get_u2(const u2_t *table, size_t index) { return table[index]; }
|
||||
static inline s2_t table_get_s2(const s2_t *table, size_t index) { return table[index]; }
|
||||
static inline u4_t table_get_u4(const u4_t *table, size_t index) { return table[index]; }
|
||||
static inline s4_t table_get_s4(const s4_t *table, size_t index) { return table[index]; }
|
||||
static inline ostime_t table_get_ostime(const ostime_t *table, size_t index) { return table[index]; }
|
||||
|
||||
// Declare a table
|
||||
#define CONST_TABLE(type, name) const type RESOLVE_TABLE(name)
|
||||
|
@ -549,7 +549,7 @@ static void txlora () {
|
||||
u1_t sf = getSf(LMIC.rps) + 6; // 1 == SF7
|
||||
u1_t bw = getBw(LMIC.rps);
|
||||
u1_t cr = getCr(LMIC.rps);
|
||||
LMIC_DEBUG_PRINTF("%lu: TXMODE, freq=%lu, len=%d, SF=%d, BW=%d, CR=4/%d, IH=%d\n",
|
||||
LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": TXMODE, freq=%"PRIu32", len=%d, SF=%d, BW=%d, CR=4/%d, IH=%d\n",
|
||||
os_getTime(), LMIC.freq, LMIC.dataLen, sf,
|
||||
bw == BW125 ? 125 : (bw == BW250 ? 250 : 500),
|
||||
cr == CR_4_5 ? 5 : (cr == CR_4_6 ? 6 : (cr == CR_4_7 ? 7 : 8)),
|
||||
@ -656,7 +656,7 @@ static void rxlora (u1_t rxmode) {
|
||||
opmode(OPMODE_RX_SINGLE);
|
||||
#if LMIC_DEBUG_LEVEL > 0
|
||||
ostime_t now = os_getTime();
|
||||
LMIC_DEBUG_PRINTF("start single rx: now-rxtime: %lu\n", now - LMIC.rxtime);
|
||||
LMIC_DEBUG_PRINTF("start single rx: now-rxtime: %"LMIC_PRId_ostime_t"\n", now - LMIC.rxtime);
|
||||
#endif
|
||||
} else { // continous rx (scan or rssi)
|
||||
opmode(OPMODE_RX);
|
||||
@ -669,7 +669,7 @@ static void rxlora (u1_t rxmode) {
|
||||
u1_t sf = getSf(LMIC.rps) + 6; // 1 == SF7
|
||||
u1_t bw = getBw(LMIC.rps);
|
||||
u1_t cr = getCr(LMIC.rps);
|
||||
LMIC_DEBUG_PRINTF("%lu: %s, freq=%lu, SF=%d, BW=%d, CR=4/%d, IH=%d\n",
|
||||
LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": %s, freq=%"PRIu32", SF=%d, BW=%d, CR=4/%d, IH=%d\n",
|
||||
os_getTime(),
|
||||
rxmode == RXMODE_SINGLE ? "RXMODE_SINGLE" : (rxmode == RXMODE_SCAN ? "RXMODE_SCAN" : "UNKNOWN_RX"),
|
||||
LMIC.freq, sf,
|
||||
@ -921,7 +921,12 @@ void radio_irq_handler (u1_t dio) {
|
||||
}
|
||||
|
||||
void radio_irq_handler_v2 (u1_t dio, ostime_t now) {
|
||||
LMIC_API_PARAMETER(dio);
|
||||
|
||||
#if CFG_TxContinuousMode
|
||||
// in continuous mode, we don't use the now parameter.
|
||||
LMIC_UNREFERENCED_PARAMETER(now);
|
||||
|
||||
// clear radio IRQ flags
|
||||
writeReg(LORARegIrqFlags, 0xFF);
|
||||
u1_t p = readReg(LORARegFifoAddrPtr);
|
||||
@ -964,7 +969,8 @@ void radio_irq_handler_v2 (u1_t dio, ostime_t now) {
|
||||
LMIC.dataLen = 0;
|
||||
#if LMIC_DEBUG_LEVEL > 0
|
||||
ostime_t now2 = os_getTime();
|
||||
LMIC_DEBUG_PRINTF("rxtimeout: entry: %lu rxtime: %lu entry-rxtime: %lu now-entry: %lu rxtime-txend: %lu\n", entry, LMIC.rxtime, entry - LMIC.rxtime, now2 - entry, LMIC.rxtime-LMIC.txend);
|
||||
LMIC_DEBUG_PRINTF("rxtimeout: entry: %"LMIC_PRId_ostime_t" rxtime: %"LMIC_PRId_ostime_t" entry-rxtime: %"LMIC_PRId_ostime_t" now-entry: %"LMIC_PRId_ostime_t" rxtime-txend: %"LMIC_PRId_ostime_t"\n", entry,
|
||||
LMIC.rxtime, entry - LMIC.rxtime, now2 - entry, LMIC.rxtime-LMIC.txend);
|
||||
#endif
|
||||
}
|
||||
// mask all radio IRQs
|
||||
|
@ -6,10 +6,10 @@
|
||||
|
||||
; ---> SELECT TARGET PLATFORM HERE! <---
|
||||
[platformio]
|
||||
env_default = generic
|
||||
;env_default = generic
|
||||
;env_default = ebox
|
||||
;env_default = eboxtube
|
||||
;env_default = heltec
|
||||
env_default = heltec
|
||||
;env_default = heltecv2
|
||||
;env_default = ttgov1
|
||||
;env_default = ttgov2
|
||||
@ -32,7 +32,7 @@ description = Paxcounter is a proof-of-concept ESP32 device for metering passeng
|
||||
release_version = 1.6.63
|
||||
; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running!
|
||||
; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose
|
||||
debug_level = 0
|
||||
debug_level = 3
|
||||
; UPLOAD MODE: select esptool to flash via USB/UART, select custom to upload to cloud for OTA
|
||||
upload_protocol = esptool
|
||||
;upload_protocol = custom
|
||||
@ -52,15 +52,19 @@ lib_deps_rgbled =
|
||||
lib_deps_gps =
|
||||
TinyGPSPlus@>=1.0.2
|
||||
Time@>=1.5
|
||||
lib_deps_sensors =
|
||||
Adafruit Unified Sensor@^1.0.2
|
||||
Adafruit BME680 Library@^1.0.7
|
||||
build_flags =
|
||||
-include "src/hal/${PIOENV}.h"
|
||||
-include "src/paxcounter.conf"
|
||||
-include $PROJECTSRC_DIR/hal/${PIOENV}.h
|
||||
-include $PROJECTSRC_DIR/paxcounter.conf
|
||||
-w
|
||||
;'-D ARDUINO_LMIC_PROJECT_CONFIG_H="$PROJECTSRC_DIR/lmic_config.h"'
|
||||
'-DARDUINO_LMIC_PROJECT_CONFIG_H=../../../src/lmic_config.h'
|
||||
'-DCORE_DEBUG_LEVEL=${common.debug_level}'
|
||||
'-DLOG_LOCAL_LEVEL=${common.debug_level}'
|
||||
'-DBINTRAY_PACKAGE="${PIOENV}"'
|
||||
'-DPROGVERSION="${common.release_version}"'
|
||||
'-D CORE_DEBUG_LEVEL=${common.debug_level}'
|
||||
'-D LOG_LOCAL_LEVEL=${common.debug_level}'
|
||||
'-D BINTRAY_PACKAGE="${PIOENV}"'
|
||||
'-D PROGVERSION="${common.release_version}"'
|
||||
|
||||
[env:ebox]
|
||||
platform = ${common.platform_espressif32}
|
||||
@ -332,6 +336,7 @@ lib_deps =
|
||||
${common.lib_deps_rgbled}
|
||||
${common.lib_deps_gps}
|
||||
${common.lib_deps_display}
|
||||
${common.lib_deps_sensors}
|
||||
build_flags =
|
||||
${common.build_flags}
|
||||
upload_protocol = ${common.upload_protocol}
|
||||
|
65
src/bme680read.cpp
Normal file
65
src/bme680read.cpp
Normal file
@ -0,0 +1,65 @@
|
||||
#ifdef HAS_BME
|
||||
|
||||
#include "globals.h"
|
||||
|
||||
// Local logging tag
|
||||
static const char TAG[] = "main";
|
||||
|
||||
#include <Wire.h>
|
||||
#include <SPI.h>
|
||||
#include <Adafruit_Sensor.h>
|
||||
#include "Adafruit_BME680.h"
|
||||
|
||||
#define SEALEVELPRESSURE_HPA (1013.25)
|
||||
|
||||
// I2C Bus interface
|
||||
Adafruit_BME680 bme;
|
||||
|
||||
bmeStatus_t bme_status;
|
||||
TaskHandle_t BmeTask;
|
||||
|
||||
// BME680 read loop Task
|
||||
void bme_loop(void *pvParameters) {
|
||||
|
||||
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check
|
||||
|
||||
// initialize BME680 sensor
|
||||
if (!bme.begin()) {
|
||||
ESP_LOGE(TAG, "BME680 chip not found on i2c bus, bus error %d. "
|
||||
"Stopping BME task.");
|
||||
vTaskDelete(BmeTask);
|
||||
} else {
|
||||
ESP_LOGI(TAG, "BME680 chip found.");
|
||||
}
|
||||
|
||||
// Set up oversampling and filter initialization
|
||||
bme.setTemperatureOversampling(BME680_OS_8X);
|
||||
bme.setHumidityOversampling(BME680_OS_2X);
|
||||
bme.setPressureOversampling(BME680_OS_4X);
|
||||
bme.setIIRFilterSize(BME680_FILTER_SIZE_3);
|
||||
bme.setGasHeater(320, 150); // 320*C for 150 ms
|
||||
|
||||
// read loop
|
||||
while (1) {
|
||||
|
||||
vTaskDelay(10000 / portTICK_PERIOD_MS);
|
||||
|
||||
if (!bme.performReading()) {
|
||||
ESP_LOGE(TAG, "BME680 read error");
|
||||
continue;
|
||||
}
|
||||
|
||||
// read BME data and cast to global struct
|
||||
bme_status.temperature = (uint16_t)bme.temperature;
|
||||
bme_status.pressure = (uint16_t)(bme.pressure / 100.0);
|
||||
bme_status.humidity = (uint16_t)bme.humidity;
|
||||
bme_status.gas_resistance = (uint16_t)(bme.gas_resistance / 1000.0);
|
||||
bme_status.altitude = (uint16_t)bme.readAltitude(SEALEVELPRESSURE_HPA);
|
||||
|
||||
} // end of infinite loop
|
||||
|
||||
vTaskDelete(NULL); // shoud never be reached
|
||||
|
||||
} // bme_loop()
|
||||
|
||||
#endif // HAS_BME
|
@ -3,12 +3,12 @@
|
||||
|
||||
// Basic config
|
||||
#include "cyclic.h"
|
||||
#include "rcommand.h"
|
||||
#include "spislave.h"
|
||||
|
||||
// Local logging tag
|
||||
static const char TAG[] = "main";
|
||||
|
||||
uint32_t userUTCTime; // Seconds since the UTC epoch
|
||||
|
||||
// do all housekeeping
|
||||
void doHousekeeping() {
|
||||
|
||||
@ -19,7 +19,10 @@ void doHousekeeping() {
|
||||
if (cfg.runmode == 1)
|
||||
do_reset();
|
||||
|
||||
// task storage debugging //
|
||||
spi_housekeeping();
|
||||
do_timesync();
|
||||
|
||||
// task storage debugging //
|
||||
ESP_LOGD(TAG, "Wifiloop %d bytes left",
|
||||
uxTaskGetStackHighWaterMark(wifiSwitchTask));
|
||||
ESP_LOGD(TAG, "IRQhandler %d bytes left",
|
||||
@ -28,10 +31,9 @@ void doHousekeeping() {
|
||||
ESP_LOGD(TAG, "Gpsloop %d bytes left", uxTaskGetStackHighWaterMark(GpsTask));
|
||||
#endif
|
||||
|
||||
spi_housekeeping();
|
||||
|
||||
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
|
||||
ESP_LOGD(TAG, "LEDloop %d bytes left", uxTaskGetStackHighWaterMark(ledLoopTask));
|
||||
ESP_LOGD(TAG, "LEDloop %d bytes left",
|
||||
uxTaskGetStackHighWaterMark(ledLoopTask));
|
||||
#endif
|
||||
|
||||
// read battery voltage into global variable
|
||||
@ -40,17 +42,6 @@ void doHousekeeping() {
|
||||
ESP_LOGI(TAG, "Measured Voltage: %dmV", batt_voltage);
|
||||
#endif
|
||||
|
||||
// sync time & date if we have valid gps time
|
||||
#ifdef HAS_GPS
|
||||
if (gps.time.isValid()) {
|
||||
setTime(gps.time.hour(), gps.time.minute(), gps.time.second(),
|
||||
gps.date.day(), gps.date.month(), gps.date.year());
|
||||
ESP_LOGI(TAG, "Time synced to %02d:%02d:%02d", hour(), minute(), second());
|
||||
} else {
|
||||
ESP_LOGI(TAG, "No valid GPS time");
|
||||
}
|
||||
#endif
|
||||
|
||||
// check free memory
|
||||
if (esp_get_minimum_free_heap_size() <= MEM_LOW) {
|
||||
ESP_LOGI(TAG,
|
||||
@ -83,6 +74,24 @@ void reset_counters() {
|
||||
macs_ble = 0;
|
||||
}
|
||||
|
||||
void do_timesync() {
|
||||
// sync time & date if we have valid gps time
|
||||
#ifdef HAS_GPS
|
||||
if (gps.time.isValid()) {
|
||||
setTime(gps.time.hour(), gps.time.minute(), gps.time.second(),
|
||||
gps.date.day(), gps.date.month(), gps.date.year());
|
||||
ESP_LOGI(TAG, "Time synced by GPS to %02d:%02d:%02d", hour(), minute(),
|
||||
second());
|
||||
return;
|
||||
} else {
|
||||
ESP_LOGW(TAG, "No valid GPS time");
|
||||
}
|
||||
#endif
|
||||
// Schedule a network time request at the next possible time
|
||||
LMIC_requestNetworkTime(user_request_network_time_callback, &userUTCTime);
|
||||
ESP_LOGI(TAG, "Network time request scheduled");
|
||||
}
|
||||
|
||||
#ifndef VERBOSE
|
||||
int redirect_log(const char *fmt, va_list args) {
|
||||
// do nothing
|
||||
|
@ -13,6 +13,8 @@
|
||||
#define SPI_SCLK GPIO_NUM_18
|
||||
#define SPI_CS GPIO_NUM_5
|
||||
|
||||
#define HAS_BME 1 // BME680 sensor on I2C bus (SDA=4/SCL=15); comment out if not present
|
||||
|
||||
#define CFG_sx1276_radio 1 // select LoRa chip
|
||||
//#define CFG_sx1272_radio 1 // select LoRa chip
|
||||
#define BOARD_HAS_PSRAM // use if board has external PSRAM
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define HAS_BME 1 // BME680 sensor on I2C bus (SDA=4/SCL=15); comment out if not present
|
||||
|
||||
// Hardware related definitions for Heltec LoRa-32 Board
|
||||
|
||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||
|
@ -21,6 +21,8 @@
|
||||
|
||||
//#define LMIC_USE_INTERRUPTS
|
||||
|
||||
#define LMIC_ENABLE_DeviceTimeReq 1
|
||||
|
||||
// 16 μs per tick
|
||||
// LMIC requires ticks to be 15.5μs - 100 μs long
|
||||
#define US_PER_OSTICK_EXPONENT 4
|
||||
@ -38,7 +40,7 @@
|
||||
// enable more verbose output. Make sure that printf is actually
|
||||
// configured (e.g. on AVR it is not by default), otherwise using it can
|
||||
// cause crashing.
|
||||
//#define LMIC_DEBUG_LEVEL 1
|
||||
#define LMIC_DEBUG_LEVEL 2
|
||||
|
||||
// Enable this to allow using printf() to print to the given serial port
|
||||
// (or any other Print object). This can be easy for debugging. The
|
||||
|
@ -328,7 +328,7 @@ void lora_send(osjob_t *job) {
|
||||
// waiting for LoRa getting ready
|
||||
} else {
|
||||
if (xQueueReceive(LoraSendQueue, &SendBuffer, (TickType_t)0) == pdTRUE) {
|
||||
// SendBuffer gets struct MessageBuffer with next payload from queue
|
||||
// SendBuffer now filled with next payload from queue
|
||||
if (!LMIC_setTxData2(SendBuffer.MessagePort, SendBuffer.Message,
|
||||
SendBuffer.MessageSize, (cfg.countermode & 0x02))) {
|
||||
ESP_LOGI(TAG, "%d byte(s) sent to LoRa", SendBuffer.MessageSize);
|
||||
@ -414,3 +414,48 @@ void lora_housekeeping(void) {
|
||||
// uxTaskGetStackHighWaterMark(LoraTask));
|
||||
#endif
|
||||
}
|
||||
|
||||
void user_request_network_time_callback(void *pVoidUserUTCTime,
|
||||
int flagSuccess) {
|
||||
// Explicit conversion from void* to uint32_t* to avoid compiler errors
|
||||
uint32_t *pUserUTCTime = (uint32_t *)pVoidUserUTCTime;
|
||||
|
||||
// A struct that will be populated by LMIC_getNetworkTimeReference.
|
||||
// It contains the following fields:
|
||||
// - tLocal: the value returned by os_GetTime() when the time
|
||||
// request was sent to the gateway, and
|
||||
// - tNetwork: the seconds between the GPS epoch and the time
|
||||
// the gateway received the time request
|
||||
lmic_time_reference_t lmicTimeReference;
|
||||
|
||||
if (flagSuccess != 1) {
|
||||
ESP_LOGW(TAG, "Network time request callback error");
|
||||
return;
|
||||
}
|
||||
|
||||
// Populate "lmic_time_reference"
|
||||
flagSuccess = LMIC_getNetworkTimeReference(&lmicTimeReference);
|
||||
if (flagSuccess != 1) {
|
||||
ESP_LOGW(TAG, "Network time request failed");
|
||||
return;
|
||||
}
|
||||
|
||||
// Update userUTCTime, considering the difference between the GPS and UTC
|
||||
// epoch, and the leap seconds
|
||||
*pUserUTCTime = lmicTimeReference.tNetwork + 315964800;
|
||||
|
||||
// Add the delay between the instant the time was transmitted and
|
||||
// the current time
|
||||
|
||||
// Current time, in ticks
|
||||
ostime_t ticksNow = os_getTime();
|
||||
// Time when the request was sent, in ticks
|
||||
ostime_t ticksRequestSent = lmicTimeReference.tLocal;
|
||||
uint32_t requestDelaySec = osticks2ms(ticksNow - ticksRequestSent) / 1000;
|
||||
*pUserUTCTime += requestDelaySec;
|
||||
|
||||
// Update the system time with the time read from the network
|
||||
setTime(*pUserUTCTime);
|
||||
ESP_LOGI(TAG, "Time synced by LoRa network to %02d:%02d:%02d", hour(),
|
||||
minute(), second());
|
||||
}
|
||||
|
17
src/main.cpp
17
src/main.cpp
@ -35,6 +35,7 @@ IDLE 0 0 ESP32 arduino scheduler -> runs wifi sniffer
|
||||
looptask 1 1 arduino core -> runs the LMIC LoRa stack
|
||||
irqhandler 1 1 executes tasks triggered by irq
|
||||
gpsloop 1 2 reads data from GPS over serial or i2c
|
||||
bmeloop 1 2 reads data from BME680 over i2c
|
||||
IDLE 1 0 ESP32 arduino scheduler
|
||||
|
||||
ESP32 hardware timers
|
||||
@ -157,6 +158,11 @@ void setup() {
|
||||
strcat_P(features, " GPS");
|
||||
#endif
|
||||
|
||||
// initialize gps
|
||||
#ifdef HAS_BME
|
||||
strcat_P(features, " BME");
|
||||
#endif
|
||||
|
||||
// initialize LoRa
|
||||
#ifdef HAS_LORA
|
||||
strcat_P(features, " LORA");
|
||||
@ -275,6 +281,17 @@ void setup() {
|
||||
1); // CPU core
|
||||
#endif
|
||||
|
||||
#ifdef HAS_BME
|
||||
ESP_LOGI(TAG, "Starting BMEloop...");
|
||||
xTaskCreatePinnedToCore(bme_loop, // task function
|
||||
"bmeloop", // name of task
|
||||
2048, // stack size of task
|
||||
(void *)1, // parameter of the task
|
||||
2, // priority of the task
|
||||
&BmeTask, // task handle
|
||||
1); // CPU core
|
||||
#endif
|
||||
|
||||
// start state machine
|
||||
ESP_LOGI(TAG, "Starting IRQ Handler...");
|
||||
xTaskCreatePinnedToCore(irqHandler, // task function
|
||||
|
Loading…
Reference in New Issue
Block a user