BME680 support (experimental); LoRa Time sync (experimental)

This commit is contained in:
Klaus K Wilting 2018-11-14 22:11:23 +01:00
parent dbc3d28d31
commit 4ae4633a15
41 changed files with 11540 additions and 169 deletions

18
include/bme680read.h Normal file
View 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

View File

@ -3,8 +3,12 @@
#include "globals.h" #include "globals.h"
#include "senddata.h" #include "senddata.h"
#include "rcommand.h"
#include "spislave.h"
#include <lmic.h>
void doHousekeeping(void); void doHousekeeping(void);
void do_timesync(void);
uint64_t uptime(void); uint64_t uptime(void);
void reset_counters(void); void reset_counters(void);
int redirect_log(const char *fmt, va_list args); int redirect_log(const char *fmt, va_list args);

View File

@ -46,6 +46,14 @@ typedef struct {
uint16_t altitude; uint16_t altitude;
} gpsStatus_t; } gpsStatus_t;
typedef struct {
uint16_t temperature;
uint16_t pressure;
uint16_t humidity;
uint16_t gas_resistance;
uint16_t altitude;
} bmeStatus_t;
// global variables // global variables
extern configData_t cfg; // current device configuration extern configData_t cfg; // current device configuration
extern char display_line6[], display_line7[]; // screen buffers extern char display_line6[], display_line7[]; // screen buffers
@ -67,6 +75,10 @@ extern TaskHandle_t irqHandlerTask, wifiSwitchTask;
#include "gpsread.h" #include "gpsread.h"
#endif #endif
#ifdef HAS_BME
#include "bme680read.h"
#endif
#ifdef HAS_LORA #ifdef HAS_LORA
#include "lorawan.h" #include "lorawan.h"
#endif #endif

View File

@ -29,6 +29,8 @@ void lora_send(osjob_t *job);
void lora_enqueuedata(uint8_t messageType, MessageBuffer_t *message); void lora_enqueuedata(uint8_t messageType, MessageBuffer_t *message);
void lora_queuereset(void); void lora_queuereset(void);
void lora_housekeeping(void); void lora_housekeeping(void);
void user_request_network_time_callback(void *pVoidUserUTCTime,
int flagSuccess);
esp_err_t lora_stack_init(); esp_err_t lora_stack_init();

View File

@ -1,5 +1,4 @@
Arduino-LMIC library # Arduino-LMIC library
====================
This repository contains the IBM LMIC (LoraMAC-in-C) library, slightly This repository contains the IBM LMIC (LoraMAC-in-C) library, slightly
modified to run in the Arduino environment, allowing using the SX1272, modified to run in the Arduino environment, allowing using the SX1272,
@ -25,56 +24,56 @@ requires C99 mode to be enabled by default.
We strongly recommend updating using VS Code, the markdown-toc extension and the We strongly recommend updating using VS Code, the markdown-toc extension and the
bierner.markdown-preview-github-styles extension. bierner.markdown-preview-github-styles extension.
--> -->
<!-- TOC depthFrom:1 --> <!-- TOC depthFrom:2 -->
- [Installing](#installing) - [Installing](#installing)
- [Features](#features) - [Features](#features)
- [Configuration](#configuration) - [Configuration](#configuration)
- [Selecting the LoRaWAN Region Configuration](#selecting-the-lorawan-region-configuration) - [Selecting the LoRaWAN Region Configuration](#selecting-the-lorawan-region-configuration)
- [eu868, as923, in866](#eu868-as923-in866) - [eu868, as923, in866](#eu868-as923-in866)
- [us915, au921](#us915-au921) - [us915, au921](#us915-au921)
- [Selecting the target radio transceiver](#selecting-the-target-radio-transceiver) - [Selecting the target radio transceiver](#selecting-the-target-radio-transceiver)
- [Controlling use of interrupts](#controlling-use-of-interrupts) - [Controlling use of interrupts](#controlling-use-of-interrupts)
- [Disabling PING](#disabling-ping) - [Disabling PING](#disabling-ping)
- [Disabling Beacons](#disabling-beacons) - [Disabling Beacons](#disabling-beacons)
- [Rarely changed variables](#rarely-changed-variables) - [Rarely changed variables](#rarely-changed-variables)
- [Changing debug output](#changing-debug-output) - [Changing debug output](#changing-debug-output)
- [Getting debug from the RF library](#getting-debug-from-the-rf-library) - [Getting debug from the RF library](#getting-debug-from-the-rf-library)
- [Selecting the AES library](#selecting-the-aes-library) - [Selecting the AES library](#selecting-the-aes-library)
- [Defining the OS Tick Frequency](#defining-the-os-tick-frequency) - [Defining the OS Tick Frequency](#defining-the-os-tick-frequency)
- [Setting the SPI-bus frequency](#setting-the-spi-bus-frequency) - [Setting the SPI-bus frequency](#setting-the-spi-bus-frequency)
- [Changing handling of runtime assertion failures](#changing-handling-of-runtime-assertion-failures) - [Changing handling of runtime assertion failures](#changing-handling-of-runtime-assertion-failures)
- [Disabling JOIN](#disabling-join) - [Disabling JOIN](#disabling-join)
- [Disabling Class A MAC commands](#disabling-class-a-mac-commands) - [Disabling Class A MAC commands](#disabling-class-a-mac-commands)
- [Disabling Class B MAC commands](#disabling-class-b-mac-commands) - [Disabling Class B MAC commands](#disabling-class-b-mac-commands)
- [Special purpose](#special-purpose) - [Special purpose](#special-purpose)
- [Supported hardware](#supported-hardware) - [Supported hardware](#supported-hardware)
- [Connections](#connections) - [Connections](#connections)
- [Power](#power) - [Power](#power)
- [SPI](#spi) - [SPI](#spi)
- [DIO pins](#dio-pins) - [DIO pins](#dio-pins)
- [Reset](#reset) - [Reset](#reset)
- [RXTX](#rxtx) - [RXTX](#rxtx)
- [RXTX Polarity](#rxtx-polarity) - [RXTX Polarity](#rxtx-polarity)
- [Pin mapping](#pin-mapping) - [Pin mapping](#pin-mapping)
- [Adafruit Feather M0 LoRa](#adafruit-feather-m0-lora) - [Adafruit Feather M0 LoRa](#adafruit-feather-m0-lora)
- [Adafruit Feather 32u4 LoRa](#adafruit-feather-32u4-lora) - [Adafruit Feather 32u4 LoRa](#adafruit-feather-32u4-lora)
- [LoRa Nexus by Ideetron](#lora-nexus-by-ideetron) - [LoRa Nexus by Ideetron](#lora-nexus-by-ideetron)
- [MCCI Catena 4450/4460](#mcci-catena-44504460) - [MCCI Catena 4450/4460](#mcci-catena-44504460)
- [MCCI Catena 4551](#mcci-catena-4551) - [MCCI Catena 4551](#mcci-catena-4551)
- [Example Sketches](#example-sketches) - [Example Sketches](#example-sketches)
- [Timing](#timing) - [Timing](#timing)
- [`LMIC_setClockError()`](#lmic_setclockerror) - [`LMIC_setClockError()`](#lmic_setclockerror)
- [Downlink datarate](#downlink-datarate) - [Downlink datarate](#downlink-datarate)
- [Encoding Utilities](#encoding-utilities) - [Encoding Utilities](#encoding-utilities)
- [sflt16](#sflt16) - [sflt16](#sflt16)
- [JavaScript decoder](#javascript-decoder) - [JavaScript decoder](#javascript-decoder)
- [uflt16](#uflt16) - [uflt16](#uflt16)
- [JavaScript decoder](#javascript-decoder-1) - [JavaScript decoder](#javascript-decoder-1)
- [sflt12](#sflt12) - [sflt12](#sflt12)
- [JavaScript decoder](#javascript-decoder-2) - [JavaScript decoder](#javascript-decoder-2)
- [uflt12](#uflt12) - [uflt12](#uflt12)
- [JavaScript decoder](#javascript-decoder-3) - [JavaScript decoder](#javascript-decoder-3)
- [Release History](#release-history) - [Release History](#release-history)
- [Contributions](#contributions) - [Contributions](#contributions)
- [Trademark Acknowledgements](#trademark-acknowledgements) - [Trademark Acknowledgements](#trademark-acknowledgements)
@ -86,12 +85,12 @@ requires C99 mode to be enabled by default.
To install this library: 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 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 install it using the IDE ("Sketch" -> "Include Library" -> "Add .ZIP
Library..." 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 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: What certainly works:
- Sending packets uplink, taking into account duty cycling. - Sending packets uplink, taking into account duty cycling.
- Encryption and message integrity checking. - Encryption and message integrity checking.
- Receiving downlink packets in the RX2 window. - Receiving downlink packets in the RX2 window.
- Custom frequencies and datarate settings. - Custom frequencies and datarate settings.
- Over-the-air activation (OTAA / joining). - Over-the-air activation (OTAA / joining).
- Receiving downlink packets in the RX1 and RX2 windows. - Receiving downlink packets in the RX1 and RX2 windows.
- Some MAC command processing. - Some MAC command processing.
What has not been tested: What has not been tested:
- Receiving and processing all MAC commands. - Receiving and processing all MAC commands.
- Class B operation. - Class B operation.
- FSK has not been extensively tested. - FSK has not been extensively tested.
If you try one of these untested features and it works, be sure to let 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). 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 should edit to match your project; we organize things this way
so that your local changes are more clearly separated from so that your local changes are more clearly separated from
the distribution files. The Arduino environment doesn't give 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. 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_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_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_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 `-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, 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()`. 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. 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. The remaining variables are rarely used, but we list them here for completeness.
@ -1022,6 +1021,10 @@ function uflt122f(rawUflt12)
## Release History ## 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.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)). - 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)).

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 KiB

View File

@ -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()
{
}

View File

@ -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();
}

View 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();
}

View File

@ -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();
}

View 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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View 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();
}

View File

@ -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

View File

@ -85,7 +85,6 @@ static unsigned char AES_Sub_Byte(unsigned char Byte);
static void AES_Shift_Rows(); static void AES_Shift_Rows();
static void AES_Mix_Collums(); static void AES_Mix_Collums();
static void AES_Calculate_Round_Key(unsigned char Round, unsigned char *Round_Key); static void AES_Calculate_Round_Key(unsigned char Round, unsigned char *Round_Key);
static void Send_State();
/* /*
***************************************************************************************** *****************************************************************************************

View File

@ -33,11 +33,19 @@ static void hal_io_init () {
// Serial.print("dio[1]: "); Serial.println(plmic_pins->dio[1]); // Serial.print("dio[1]: "); Serial.println(plmic_pins->dio[1]);
// Serial.print("dio[2]: "); Serial.println(plmic_pins->dio[2]); // 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); 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); 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(); hal_interrupt_init();
} }
@ -54,8 +62,8 @@ void hal_pin_rst (u1_t val) {
return; return;
if(val == 0 || val == 1) { // drive pin if(val == 0 || val == 1) { // drive pin
pinMode(plmic_pins->rst, OUTPUT);
digitalWrite(plmic_pins->rst, val); digitalWrite(plmic_pins->rst, val);
pinMode(plmic_pins->rst, OUTPUT);
} else { // keep pin floating } else { // keep pin floating
pinMode(plmic_pins->rst, INPUT); pinMode(plmic_pins->rst, INPUT);
} }

View File

@ -171,4 +171,11 @@
# endif // defined(LMIC_DISABLE_DR_LEGACY) # endif // defined(LMIC_DISABLE_DR_LEGACY)
#endif // LMIC_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_ #endif // _lmic_config_h_

View File

@ -47,8 +47,10 @@ static void startScan (void);
#endif #endif
static inline void initTxrxFlags(const char *func, u1_t mask) { static inline void initTxrxFlags(const char *func, u1_t mask) {
LMIC_DEBUG2_PARAMETER(func);
#if LMIC_DEBUG_LEVEL > 1 #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 #endif
LMIC.txrxFlags = mask; 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; 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 // END LORA
// ================================================================================ // ================================================================================
@ -414,6 +393,8 @@ static void txDelay (ostime_t reftime, u1_t secSpan) {
void LMICcore_setDrJoin (u1_t reason, u1_t dr) { void LMICcore_setDrJoin (u1_t reason, u1_t dr) {
LMIC_EV_PARAMETER(reason);
EV(drChange, INFO, (e_.reason = reason, EV(drChange, INFO, (e_.reason = reason,
e_.deveui = MAIN::CDEV->getEui(), e_.deveui = MAIN::CDEV->getEui(),
e_.dr = dr|DR_PAGE, 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) { static void setDrTxpow (u1_t reason, u1_t dr, s1_t pow) {
LMIC_EV_PARAMETER(reason);
EV(drChange, INFO, (e_.reason = reason, EV(drChange, INFO, (e_.reason = reason,
e_.deveui = MAIN::CDEV->getEui(), e_.deveui = MAIN::CDEV->getEui(),
e_.dr = dr|DR_PAGE, e_.dr = dr|DR_PAGE,
@ -462,6 +445,8 @@ void LMIC_setPingable (u1_t intvExp) {
#endif // !DISABLE_PING #endif // !DISABLE_PING
static void runEngineUpdate (xref2osjob_t osjob) { static void runEngineUpdate (xref2osjob_t osjob) {
LMIC_API_PARAMETER(osjob);
engineUpdate(); engineUpdate();
} }
@ -476,6 +461,8 @@ static void reportEvent (ev_t ev) {
static void runReset (xref2osjob_t osjob) { static void runReset (xref2osjob_t osjob) {
LMIC_API_PARAMETER(osjob);
// Disable session // Disable session
LMIC_reset(); LMIC_reset();
#if !defined(DISABLE_JOIN) #if !defined(DISABLE_JOIN)
@ -594,7 +581,7 @@ scan_mac_cmds(
// of contiguous commands (whatever that means), and ignore the // of contiguous commands (whatever that means), and ignore the
// data rate, NbTrans (uprpt) and txPow until the last one. // data rate, NbTrans (uprpt) and txPow until the last one.
#if LMIC_DEBUG_LEVEL > 0 #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 os_getTime(), p1, chmap, chpage, uprpt, LMIC.ladrAns
); );
#endif /* LMIC_DEBUG_LEVEL */ #endif /* LMIC_DEBUG_LEVEL */
@ -726,6 +713,38 @@ scan_mac_cmds(
oidx += 2; oidx += 2;
continue; continue;
} /* end case */ } /* 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 */ } /* end switch */
/* unrecognized mac commands fall out of switch to here */ /* unrecognized mac commands fall out of switch to here */
EV(specCond, ERR, (e_.reason = EV::specCond_t::BAD_MAC_CMD, 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))); e_.info2 = hdr + (dlen<<8)));
norx: norx:
#if LMIC_DEBUG_LEVEL > 0 #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 #endif
LMIC.dataLen = 0; LMIC.dataLen = 0;
return 0; return 0;
@ -849,7 +868,7 @@ static bit_t decodeFrame (void) {
#if LMIC_DEBUG_LEVEL > 0 #if LMIC_DEBUG_LEVEL > 0
// Process OPTS // 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 #endif
xref2u1_t opts = &d[OFF_DAT_OPTS]; xref2u1_t opts = &d[OFF_DAT_OPTS];
@ -868,13 +887,13 @@ static bit_t decodeFrame (void) {
if (port == 0) { if (port == 0) {
// this is a mac command. scan the options. // this is a mac command. scan the options.
#if LMIC_DEBUG_LEVEL > 0 #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 #endif
int optendindex = scan_mac_cmds(d+poff, pend-poff); int optendindex = scan_mac_cmds(d+poff, pend-poff);
if (optendindex != pend-poff) { if (optendindex != pend-poff) {
#if LMIC_DEBUG_LEVEL > 0 #if LMIC_DEBUG_LEVEL > 0
LMIC_DEBUG_PRINTF( 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", "(len=%#x, optendindex=%#x)\n",
os_getTime(), pend-poff, optendindex os_getTime(), pend-poff, optendindex
); );
@ -911,7 +930,7 @@ static bit_t decodeFrame (void) {
e_.info = seqno, e_.info = seqno,
e_.info2 = ackup)); e_.info2 = ackup));
#if LMIC_DEBUG_LEVEL > 1 #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 #endif
} }
@ -928,7 +947,7 @@ static bit_t decodeFrame (void) {
LMIC.dataLen = pend-poff; LMIC.dataLen = pend-poff;
} }
#if LMIC_DEBUG_LEVEL > 0 #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 #endif
return 1; 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) // (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.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); 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) #if !defined(DISABLE_JOIN)
static void onJoinFailed (xref2osjob_t osjob) { static void onJoinFailed (xref2osjob_t osjob) {
LMIC_API_PARAMETER(osjob);
// Notify app - must call LMIC_reset() to stop joining // Notify app - must call LMIC_reset() to stop joining
// otherwise join procedure continues. // otherwise join procedure continues.
reportEvent(EV_JOIN_FAILED); reportEvent(EV_JOIN_FAILED);
@ -1073,6 +1094,8 @@ static bit_t processJoinAccept (void) {
u1_t hdr = LMIC.frame[0]; u1_t hdr = LMIC.frame[0];
u1_t dlen = LMIC.dataLen; u1_t dlen = LMIC.dataLen;
u4_t mic = os_rlsbf4(&LMIC.frame[dlen-4]); // safe before modified by encrypt! 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) if( (dlen != LEN_JA && dlen != LEN_JAEXT)
|| (hdr & (HDR_FTYPE|HDR_MAJOR)) != (HDR_FTYPE_JACC|HDR_MAJOR_V1) ) { || (hdr & (HDR_FTYPE|HDR_MAJOR)) != (HDR_FTYPE_JACC|HDR_MAJOR_V1) ) {
EV(specCond, ERR, (e_.reason = EV::specCond_t::UNEXPECTED_FRAME, EV(specCond, ERR, (e_.reason = EV::specCond_t::UNEXPECTED_FRAME,
@ -1109,7 +1132,7 @@ static bit_t processJoinAccept (void) {
if( freq ) { if( freq ) {
LMIC_setupChannel(chidx, freq, 0, -1); LMIC_setupChannel(chidx, freq, 0, -1);
#if LMIC_DEBUG_LEVEL > 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 #endif
} }
} }
@ -1161,6 +1184,8 @@ static bit_t processJoinAccept (void) {
static void processRx2Jacc (xref2osjob_t osjob) { static void processRx2Jacc (xref2osjob_t osjob) {
LMIC_API_PARAMETER(osjob);
if( LMIC.dataLen == 0 ) { if( LMIC.dataLen == 0 ) {
initTxrxFlags(__func__, 0); // nothing in 1st/2nd DN slot 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) { static void setupRx2Jacc (xref2osjob_t osjob) {
LMIC_API_PARAMETER(osjob);
LMIC.osjob.func = FUNC_ADDR(processRx2Jacc); LMIC.osjob.func = FUNC_ADDR(processRx2Jacc);
setupRx2(); setupRx2();
} }
static void processRx1Jacc (xref2osjob_t osjob) { static void processRx1Jacc (xref2osjob_t osjob) {
LMIC_API_PARAMETER(osjob);
if( LMIC.dataLen == 0 || !processJoinAccept() ) if( LMIC.dataLen == 0 || !processJoinAccept() )
schedRx12(DELAY_JACC2_osticks, FUNC_ADDR(setupRx2Jacc), LMIC.dn2Dr); schedRx12(DELAY_JACC2_osticks, FUNC_ADDR(setupRx2Jacc), LMIC.dn2Dr);
} }
static void setupRx1Jacc (xref2osjob_t osjob) { static void setupRx1Jacc (xref2osjob_t osjob) {
LMIC_API_PARAMETER(osjob);
setupRx1(FUNC_ADDR(processRx1Jacc)); setupRx1(FUNC_ADDR(processRx1Jacc));
} }
static void jreqDone (xref2osjob_t osjob) { static void jreqDone (xref2osjob_t osjob) {
LMIC_API_PARAMETER(osjob);
txDone(DELAY_JACC1_osticks, FUNC_ADDR(setupRx1Jacc)); txDone(DELAY_JACC1_osticks, FUNC_ADDR(setupRx1Jacc));
} }
@ -1197,10 +1230,14 @@ static void jreqDone (xref2osjob_t osjob) {
static bit_t processDnData(void); static bit_t processDnData(void);
static void processRx2DnDataDelay (xref2osjob_t osjob) { static void processRx2DnDataDelay (xref2osjob_t osjob) {
LMIC_API_PARAMETER(osjob);
processDnData(); processDnData();
} }
static void processRx2DnData (xref2osjob_t osjob) { static void processRx2DnData (xref2osjob_t osjob) {
LMIC_API_PARAMETER(osjob);
if( LMIC.dataLen == 0 ) { if( LMIC.dataLen == 0 ) {
initTxrxFlags(__func__, 0); // nothing in 1st/2nd DN slot initTxrxFlags(__func__, 0); // nothing in 1st/2nd DN slot
// Delay callback processing to avoid up TX while gateway is txing our missed frame! // 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) { static void setupRx2DnData (xref2osjob_t osjob) {
LMIC_API_PARAMETER(osjob);
LMIC.osjob.func = FUNC_ADDR(processRx2DnData); LMIC.osjob.func = FUNC_ADDR(processRx2DnData);
setupRx2(); setupRx2();
} }
static void processRx1DnData (xref2osjob_t osjob) { static void processRx1DnData (xref2osjob_t osjob) {
LMIC_API_PARAMETER(osjob);
if( LMIC.dataLen == 0 || !processDnData() ) if( LMIC.dataLen == 0 || !processDnData() )
schedRx12(sec2osticks(LMIC.rxDelay +(int)DELAY_EXTDNW2), FUNC_ADDR(setupRx2DnData), LMIC.dn2Dr); schedRx12(sec2osticks(LMIC.rxDelay +(int)DELAY_EXTDNW2), FUNC_ADDR(setupRx2DnData), LMIC.dn2Dr);
} }
static void setupRx1DnData (xref2osjob_t osjob) { static void setupRx1DnData (xref2osjob_t osjob) {
LMIC_API_PARAMETER(osjob);
setupRx1(FUNC_ADDR(processRx1DnData)); setupRx1(FUNC_ADDR(processRx1DnData));
} }
static void updataDone (xref2osjob_t osjob) { static void updataDone (xref2osjob_t osjob) {
LMIC_API_PARAMETER(osjob);
txDone(sec2osticks(LMIC.rxDelay), FUNC_ADDR(setupRx1DnData)); txDone(sec2osticks(LMIC.rxDelay), FUNC_ADDR(setupRx1DnData));
} }
@ -1315,6 +1360,13 @@ static void buildDataFrame (void) {
LMIC.txParamSetupAns = 0; LMIC.txParamSetupAns = 0;
} }
#endif #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); ASSERT(end <= OFF_DAT_OPTS+16);
u1_t flen = end + (txdata ? 5+dlen : 4); u1_t flen = end + (txdata ? 5+dlen : 4);
@ -1376,7 +1428,9 @@ static void buildDataFrame (void) {
#if !defined(DISABLE_BEACONS) #if !defined(DISABLE_BEACONS)
// Callback from HAL during scan mode or when job timer expires. // 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. // If we arrive via job timer make sure to put radio to rest.
os_radio(RADIO_RST); os_radio(RADIO_RST);
os_clearCallback(&LMIC.osjob); os_clearCallback(&LMIC.osjob);
@ -1497,6 +1551,8 @@ static void buildJoinRequest (u1_t ftype) {
} }
static void startJoining (xref2osjob_t osjob) { static void startJoining (xref2osjob_t osjob) {
LMIC_API_PARAMETER(osjob);
reportEvent(EV_JOINING); reportEvent(EV_JOINING);
} }
@ -1530,6 +1586,8 @@ bit_t LMIC_startJoining (void) {
#if !defined(DISABLE_PING) #if !defined(DISABLE_PING)
static void processPingRx (xref2osjob_t osjob) { static void processPingRx (xref2osjob_t osjob) {
LMIC_API_PARAMETER(osjob);
if( LMIC.dataLen != 0 ) { if( LMIC.dataLen != 0 ) {
initTxrxFlags(__func__, TXRX_PING); initTxrxFlags(__func__, TXRX_PING);
if( decodeFrame() ) { if( decodeFrame() ) {
@ -1568,6 +1626,24 @@ static bit_t processDnData (void) {
LMIC.dataBeg = LMIC.dataLen = 0; LMIC.dataBeg = LMIC.dataLen = 0;
txcomplete: txcomplete:
LMIC.opmode &= ~(OP_TXDATA|OP_TXRXPEND); 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 ) { if( (LMIC.txrxFlags & (TXRX_DNW1|TXRX_DNW2|TXRX_PING)) != 0 && (LMIC.opmode & OP_LINKDEAD) != 0 ) {
LMIC.opmode &= ~OP_LINKDEAD; LMIC.opmode &= ~OP_LINKDEAD;
reportEvent(EV_LINK_ALIVE); reportEvent(EV_LINK_ALIVE);
@ -1611,6 +1687,8 @@ static bit_t processDnData (void) {
#if !defined(DISABLE_BEACONS) #if !defined(DISABLE_BEACONS)
static void processBeacon (xref2osjob_t osjob) { static void processBeacon (xref2osjob_t osjob) {
LMIC_API_PARAMETER(osjob);
ostime_t lasttx = LMIC.bcninfo.txtime; // save here - decodeBeacon might overwrite ostime_t lasttx = LMIC.bcninfo.txtime; // save here - decodeBeacon might overwrite
u1_t flags = LMIC.bcninfo.flags; u1_t flags = LMIC.bcninfo.flags;
ev_t ev; ev_t ev;
@ -1672,6 +1750,8 @@ static void processBeacon (xref2osjob_t osjob) {
static void startRxBcn (xref2osjob_t osjob) { static void startRxBcn (xref2osjob_t osjob) {
LMIC_API_PARAMETER(osjob);
LMIC.osjob.func = FUNC_ADDR(processBeacon); LMIC.osjob.func = FUNC_ADDR(processBeacon);
os_radio(RADIO_RX); os_radio(RADIO_RX);
} }
@ -1680,6 +1760,8 @@ static void startRxBcn (xref2osjob_t osjob) {
#if !defined(DISABLE_PING) #if !defined(DISABLE_PING)
static void startRxPing (xref2osjob_t osjob) { static void startRxPing (xref2osjob_t osjob) {
LMIC_API_PARAMETER(osjob);
LMIC.osjob.func = FUNC_ADDR(processPingRx); LMIC.osjob.func = FUNC_ADDR(processPingRx);
os_radio(RADIO_RX); 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 // Decide what to do next for the MAC layer of a device
static void engineUpdate (void) { static void engineUpdate (void) {
#if LMIC_DEBUG_LEVEL > 0 #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 #endif
// Check for ongoing state: scan or TX/RX transaction // Check for ongoing state: scan or TX/RX transaction
if( (LMIC.opmode & (OP_SCAN|OP_TXRXPEND|OP_SHUTDOWN)) != 0 ) if( (LMIC.opmode & (OP_SCAN|OP_TXRXPEND|OP_SHUTDOWN)) != 0 )
@ -1703,10 +1785,11 @@ static void engineUpdate (void) {
#endif // !DISABLE_JOIN #endif // !DISABLE_JOIN
ostime_t now = os_getTime(); ostime_t now = os_getTime();
ostime_t rxtime = 0;
ostime_t txbeg = 0; ostime_t txbeg = 0;
#if !defined(DISABLE_BEACONS) #if !defined(DISABLE_BEACONS)
ostime_t rxtime = 0;
if( (LMIC.opmode & OP_TRACK) != 0 ) { if( (LMIC.opmode & OP_TRACK) != 0 ) {
// We are tracking a beacon // We are tracking a beacon
ASSERT( now + RX_RAMPUP - LMIC.bcnRxtime <= 0 ); ASSERT( now + RX_RAMPUP - LMIC.bcnRxtime <= 0 );
@ -1848,7 +1931,7 @@ static void engineUpdate (void) {
e_.eui = MAIN::CDEV->getEui(), e_.eui = MAIN::CDEV->getEui(),
e_.info = osticks2ms(txbeg-now), e_.info = osticks2ms(txbeg-now),
e_.info2 = LMIC.seqnoUp-1)); 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)); 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.dr, pingDr);
DO_DEVDB(LMIC.ping.intvExp, pingIntvExp); DO_DEVDB(LMIC.ping.intvExp, pingIntvExp);
#endif // !DISABLE_PING #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; return 0;
} }
// Send a payload-less message to signal device is alive // Send a payload-less message to signal device is alive
void LMIC_sendAlive (void) { void LMIC_sendAlive (void) {
LMIC.opmode |= OP_POLL; 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(artKey, LMIC.artKey, sizeof(LMIC.artKey));
memcpy(nwkKey, LMIC.nwkKey, sizeof(LMIC.nwkKey)); 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;
}

View File

@ -105,7 +105,7 @@ extern "C"{
#define ARDUINO_LMIC_VERSION_CALC(major, minor, patch, local) \ #define ARDUINO_LMIC_VERSION_CALC(major, minor, patch, local) \
(((major) << 24u) | ((minor) << 16u) | ((patch) << 8u) | (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) \ #define ARDUINO_LMIC_VERSION_GET_MAJOR(v) \
(((v) >> 24u) & 0xFFu) (((v) >> 24u) & 0xFFu)
@ -243,6 +243,35 @@ enum {
MAX_CLOCK_ERROR = 65536, 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 { struct lmic_t {
// Radio settings TX/RX (also accessed by HAL) // Radio settings TX/RX (also accessed by HAL)
ostime_t txend; ostime_t txend;
@ -306,6 +335,14 @@ struct lmic_t {
devaddr_t devaddr; devaddr_t devaddr;
u4_t seqnoDn; // device level down stream seqno u4_t seqnoDn; // device level down stream seqno
u4_t seqnoUp; 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 u1_t dnConf; // dn frame confirm pending: LORA::FCT_ACK or 0
s1_t adrAckReq; // counter until we reset data rate (0=off) 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. bit_t txParamSetupAns; // transmit setup answer pending.
u1_t txParam; // the saved TX param byte. u1_t txParam; // the saved TX param byte.
#endif #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 // rx1DrOffset is the offset from uplink to downlink datarate
u1_t rx1DrOffset; // captured from join. zero by default. u1_t rx1DrOffset; // captured from join. zero by default.
@ -368,6 +409,7 @@ struct lmic_t {
u1_t noRXIQinversion; u1_t noRXIQinversion;
}; };
//! \var struct lmic_t LMIC //! \var struct lmic_t LMIC
//! The state of LMIC MAC layer is encapsulated in this variable. //! The state of LMIC MAC layer is encapsulated in this variable.
DECLARE_LMIC; //!< \internal DECLARE_LMIC; //!< \internal
@ -417,6 +459,9 @@ u4_t LMIC_getSeqnoUp (void);
u4_t LMIC_setSeqnoUp (u4_t); u4_t LMIC_setSeqnoUp (u4_t);
void LMIC_getSessionKeys (u4_t *netid, devaddr_t *devaddr, xref2u1_t nwkKey, xref2u1_t artKey); 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 // Declare onEvent() function, to make sure any definition will have the
// C conventions, even when in a C++ file. // C conventions, even when in a C++ file.
DECL_ON_LMIC_EVENT; DECL_ON_LMIC_EVENT;

View File

@ -76,11 +76,15 @@ static CONST_TABLE(u1_t, maxFrameLens_dwell1)[] = {
static uint8_t static uint8_t
LMICas923_getUplinkDwellBit(uint8_t mcmd_txparam) { LMICas923_getUplinkDwellBit(uint8_t mcmd_txparam) {
LMIC_API_PARAMETER(mcmd_txparam);
return (LMIC.txParam & MCMD_TxParam_TxDWELL_MASK) != 0; return (LMIC.txParam & MCMD_TxParam_TxDWELL_MASK) != 0;
} }
static uint8_t static uint8_t
LMICas923_getDownlinkDwellBit(uint8_t mcmd_txparam) { LMICas923_getDownlinkDwellBit(uint8_t mcmd_txparam) {
LMIC_API_PARAMETER(mcmd_txparam);
return (LMIC.txParam & MCMD_TxParam_RxDWELL_MASK) != 0; 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. // as923 ignores join, becuase the channel setup is the same either way.
void LMICas923_initDefaultChannels(bit_t join) { void LMICas923_initDefaultChannels(bit_t join) {
LMIC_API_PARAMETER(join);
os_clearMem(&LMIC.channelFreq, sizeof(LMIC.channelFreq)); os_clearMem(&LMIC.channelFreq, sizeof(LMIC.channelFreq));
os_clearMem(&LMIC.channelDrMap, sizeof(LMIC.channelDrMap)); os_clearMem(&LMIC.channelDrMap, sizeof(LMIC.channelDrMap));
os_clearMem(&LMIC.bands, sizeof(LMIC.bands)); os_clearMem(&LMIC.bands, sizeof(LMIC.bands));

View File

@ -101,6 +101,11 @@ u4_t LMICau921_convFreq(xref2cu1_t ptr) {
// au921: no support for xchannels. // au921: no support for xchannels.
bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { 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. return 0; // all channels are hardwired.
} }

View File

@ -113,6 +113,12 @@ Revision history:
// following values. These are in order of the sections in the manual. Not all of the // following values. These are in order of the sections in the manual. Not all of the
// below are supported yet. // 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 \ # define CFG_LMIC_REGION_MASK \
((defined(CFG_eu868) << LMIC_REGION_eu868) | \ ((defined(CFG_eu868) << LMIC_REGION_eu868) | \
(defined(CFG_us915) << LMIC_REGION_us915) | \ (defined(CFG_us915) << LMIC_REGION_us915) | \
@ -126,6 +132,8 @@ Revision history:
0) 0)
// the selected region. // 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) #if defined(CFG_eu868)
# define CFG_region LMIC_REGION_eu868 # define CFG_region LMIC_REGION_eu868
#elif defined(CFG_us915) #elif defined(CFG_us915)
@ -138,6 +146,10 @@ Revision history:
# define CFG_region LMIC_REGION_au921 # define CFG_region LMIC_REGION_au921
#elif defined(CFG_cn490) #elif defined(CFG_cn490)
# define CFG_region LMIC_REGION_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) #elif defined(CFG_as923)
# define CFG_region LMIC_REGION_as923 # define CFG_region LMIC_REGION_as923
#elif defined(CFG_kr921) #elif defined(CFG_kr921)
@ -148,7 +160,11 @@ Revision history:
# define CFG_region 0 # define CFG_region 0
#endif #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 ( \ #define CFG_LMIC_EU_like_MASK ( \
(1 << LMIC_REGION_eu868) | \ (1 << LMIC_REGION_eu868) | \
/* (1 << LMIC_REGION_us915) | */ \ /* (1 << LMIC_REGION_us915) | */ \
@ -161,6 +177,12 @@ Revision history:
(1 << LMIC_REGION_in866) | \ (1 << LMIC_REGION_in866) | \
0) 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 ( \ #define CFG_LMIC_US_like_MASK ( \
/* (1 << LMIC_REGION_eu868) | */ \ /* (1 << LMIC_REGION_eu868) | */ \
(1 << LMIC_REGION_us915) | \ (1 << LMIC_REGION_us915) | \
@ -173,9 +195,12 @@ Revision history:
/* (1 << LMIC_REGION_in866) | */ \ /* (1 << LMIC_REGION_in866) | */ \
0) 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_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)) #define CFG_LMIC_US_like (!!(CFG_LMIC_REGION_MASK & CFG_LMIC_US_like_MASK))
#endif /* _LMIC_CONFIG_PRECONDITIONS_H_ */ #endif /* _LMIC_CONFIG_PRECONDITIONS_H_ */

View File

@ -33,9 +33,11 @@
#if CFG_LMIC_EU_like #if CFG_LMIC_EU_like
void LMIC_enableSubBand(u1_t band) { void LMIC_enableSubBand(u1_t band) {
LMIC_API_PARAMETER(band);
} }
void LMIC_disableSubBand(u1_t band) { void LMIC_disableSubBand(u1_t band) {
LMIC_API_PARAMETER(band);
} }
void LMIC_disableChannel(u1_t channel) { void LMIC_disableChannel(u1_t channel) {
@ -46,6 +48,7 @@ void LMIC_disableChannel(u1_t channel) {
// this is a no-op provided for compatibilty // this is a no-op provided for compatibilty
void LMIC_enableChannel(u1_t channel) { void LMIC_enableChannel(u1_t channel) {
LMIC_API_PARAMETER(channel);
} }
u1_t LMICeulike_mapChannels(u1_t chpage, u2_t chmap) { u1_t LMICeulike_mapChannels(u1_t chpage, u2_t chmap) {

View File

@ -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. // india ignores join, becuase the channel setup is the same either way.
void LMICin866_initDefaultChannels(bit_t join) { void LMICin866_initDefaultChannels(bit_t join) {
LMIC_API_PARAMETER(join);
os_clearMem(&LMIC.channelFreq, sizeof(LMIC.channelFreq)); os_clearMem(&LMIC.channelFreq, sizeof(LMIC.channelFreq));
os_clearMem(&LMIC.channelDrMap, sizeof(LMIC.channelDrMap)); os_clearMem(&LMIC.channelDrMap, sizeof(LMIC.channelDrMap));
os_clearMem(&LMIC.bands, sizeof(LMIC.bands)); os_clearMem(&LMIC.bands, sizeof(LMIC.bands));

View File

@ -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) { 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) if (chidx < 72 || chidx >= 72 + MAX_XCHANNELS)
return 0; // channels 0..71 are hardwired return 0; // channels 0..71 are hardwired
LMIC.xchFreq[chidx - 72] = freq; LMIC.xchFreq[chidx - 72] = freq;

View File

@ -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) { 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. // nothing; just succeed.
return 1; return 1;
} }
void LMICuslike_initDefaultChannels(bit_t fJoin) { void LMICuslike_initDefaultChannels(bit_t fJoin) {
LMIC_API_PARAMETER(fJoin);
// things work the same for join as normal. // things work the same for join as normal.
for (u1_t i = 0; i<4; i++) for (u1_t i = 0; i<4; i++)
LMIC.channelMap[i] = 0xFFFF; LMIC.channelMap[i] = 0xFFFF;

View File

@ -449,6 +449,7 @@ enum {
MCMD_RXTimingSetupAns = 0x08, // : - MCMD_RXTimingSetupAns = 0x08, // : -
MCMD_TxParamSetupAns = 0x09, // : - MCMD_TxParamSetupAns = 0x09, // : -
MCMD_DIChannelAns = 0x0A, // : u1: [7-2]:RFU 1:exists 0:OK MCMD_DIChannelAns = 0x0A, // : u1: [7-2]:RFU 1:exists 0:OK
MCMD_DeviceTimeReq = 0x0D,
// Class B // Class B
MCMD_PING_IND = 0x10, // - pingability indic : u1: 7=RFU, 6-4:interval, 3-0:datarate 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_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_TxParamSetupReq = 0x09, // : u1: [7-6]:RFU [5:4]: dl dwell/ul dwell [3:0] max EIRP
MCMD_DIChannelReq = 0x0A, // : u1: channel, u3: frequency MCMD_DIChannelReq = 0x0A, // : u1: channel, u3: frequency
MCMD_DeviceTimeAns = 0x0D,
// Class B // Class B
MCMD_PING_SET = 0x11, // set ping freq : u3: freq MCMD_PING_SET = 0x11, // set ping freq : u3: freq
@ -583,33 +585,33 @@ typedef u4_t devaddr_t;
// RX quality (device) // RX quality (device)
enum { RSSI_OFF=64, SNR_SCALEUP=4 }; enum { RSSI_OFF=64, SNR_SCALEUP=4 };
inline sf_t getSf (rps_t params) { return (sf_t)(params & 0x7); } static 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); } static 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); } static 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)); } static 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); } static 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)); } static 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); } static 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)); } static 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); } static 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)); } static 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 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); 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))) #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 // 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)[]; extern CONST_TABLE(u1_t, _DR2RPS_CRC)[];
inline rps_t updr2rps (dr_t dr) { return (rps_t)TABLE_GET_U1(_DR2RPS_CRC, dr+1); } static 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); } static inline rps_t dndr2rps (dr_t dr) { return setNocrc(updr2rps(dr),1); }
inline int isFasterDR (dr_t dr1, dr_t dr2) { return dr1 > dr2; } static inline int isFasterDR (dr_t dr1, dr_t dr2) { return dr1 > dr2; }
inline int isSlowerDR (dr_t dr1, dr_t dr2) { return dr1 < dr2; } static 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 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
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 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 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
inline bit_t validDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr+1)!=ILLEGAL_RPS; } // in range static 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 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 // BEG: Keep in sync with lorabase.hpp

View File

@ -71,7 +71,11 @@ static int unlinkjob (osjob_t** pnext, osjob_t* job) {
// clear scheduled job // clear scheduled job
void os_clearCallback (osjob_t* job) { void os_clearCallback (osjob_t* job) {
hal_disableIRQs(); 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(); hal_enableIRQs();
} }

View File

@ -75,6 +75,14 @@ typedef const u1_t* xref2cu1_t;
typedef u1_t* xref2u1_t; typedef u1_t* xref2u1_t;
typedef s4_t ostime_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_xref2rps_t typedef rps_t* xref2rps_t
#define TYPEDEF_xref2rxsched_t typedef rxsched_t* xref2rxsched_t #define TYPEDEF_xref2rxsched_t typedef rxsched_t* xref2rxsched_t
#define TYPEDEF_xref2chnldef_t typedef chnldef_t* xref2chnldef_t #define TYPEDEF_xref2chnldef_t typedef chnldef_t* xref2chnldef_t
@ -83,6 +91,105 @@ typedef s4_t ostime_t;
#define SIZEOFEXPR(x) sizeof(x) #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 ON_LMIC_EVENT(ev) onEvent(ev)
#define DECL_ON_LMIC_EVENT void onEvent(ev_t e) #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 // progmem using pgm_read_xx, or accesses memory directly when the
// index is a constant so gcc can optimize it away; // index is a constant so gcc can optimize it away;
#define TABLE_GETTER(postfix, type, pgm_type) \ #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])) \ if (__builtin_constant_p(table[index])) \
return table[index]; \ return table[index]; \
return pgm_read_ ## pgm_type(&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 // For AVR, store constants in PROGMEM, saving on RAM usage
#define CONST_TABLE(type, name) const type PROGMEM RESOLVE_TABLE(name) #define CONST_TABLE(type, name) const type PROGMEM RESOLVE_TABLE(name)
#else #else
inline u1_t table_get_u1(const u1_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]; }
inline s1_t table_get_s1(const s1_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]; }
inline u2_t table_get_u2(const u2_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]; }
inline s2_t table_get_s2(const s2_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]; }
inline u4_t table_get_u4(const u4_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]; }
inline s4_t table_get_s4(const s4_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]; }
inline ostime_t table_get_ostime(const ostime_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 // Declare a table
#define CONST_TABLE(type, name) const type RESOLVE_TABLE(name) #define CONST_TABLE(type, name) const type RESOLVE_TABLE(name)

View File

@ -549,7 +549,7 @@ static void txlora () {
u1_t sf = getSf(LMIC.rps) + 6; // 1 == SF7 u1_t sf = getSf(LMIC.rps) + 6; // 1 == SF7
u1_t bw = getBw(LMIC.rps); u1_t bw = getBw(LMIC.rps);
u1_t cr = getCr(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, os_getTime(), LMIC.freq, LMIC.dataLen, sf,
bw == BW125 ? 125 : (bw == BW250 ? 250 : 500), bw == BW125 ? 125 : (bw == BW250 ? 250 : 500),
cr == CR_4_5 ? 5 : (cr == CR_4_6 ? 6 : (cr == CR_4_7 ? 7 : 8)), 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); opmode(OPMODE_RX_SINGLE);
#if LMIC_DEBUG_LEVEL > 0 #if LMIC_DEBUG_LEVEL > 0
ostime_t now = os_getTime(); 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 #endif
} else { // continous rx (scan or rssi) } else { // continous rx (scan or rssi)
opmode(OPMODE_RX); opmode(OPMODE_RX);
@ -669,7 +669,7 @@ static void rxlora (u1_t rxmode) {
u1_t sf = getSf(LMIC.rps) + 6; // 1 == SF7 u1_t sf = getSf(LMIC.rps) + 6; // 1 == SF7
u1_t bw = getBw(LMIC.rps); u1_t bw = getBw(LMIC.rps);
u1_t cr = getCr(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(), os_getTime(),
rxmode == RXMODE_SINGLE ? "RXMODE_SINGLE" : (rxmode == RXMODE_SCAN ? "RXMODE_SCAN" : "UNKNOWN_RX"), rxmode == RXMODE_SINGLE ? "RXMODE_SINGLE" : (rxmode == RXMODE_SCAN ? "RXMODE_SCAN" : "UNKNOWN_RX"),
LMIC.freq, sf, 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) { void radio_irq_handler_v2 (u1_t dio, ostime_t now) {
LMIC_API_PARAMETER(dio);
#if CFG_TxContinuousMode #if CFG_TxContinuousMode
// in continuous mode, we don't use the now parameter.
LMIC_UNREFERENCED_PARAMETER(now);
// clear radio IRQ flags // clear radio IRQ flags
writeReg(LORARegIrqFlags, 0xFF); writeReg(LORARegIrqFlags, 0xFF);
u1_t p = readReg(LORARegFifoAddrPtr); u1_t p = readReg(LORARegFifoAddrPtr);
@ -964,7 +969,8 @@ void radio_irq_handler_v2 (u1_t dio, ostime_t now) {
LMIC.dataLen = 0; LMIC.dataLen = 0;
#if LMIC_DEBUG_LEVEL > 0 #if LMIC_DEBUG_LEVEL > 0
ostime_t now2 = os_getTime(); 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 #endif
} }
// mask all radio IRQs // mask all radio IRQs

View File

@ -6,10 +6,10 @@
; ---> SELECT TARGET PLATFORM HERE! <--- ; ---> SELECT TARGET PLATFORM HERE! <---
[platformio] [platformio]
env_default = generic ;env_default = generic
;env_default = ebox ;env_default = ebox
;env_default = eboxtube ;env_default = eboxtube
;env_default = heltec env_default = heltec
;env_default = heltecv2 ;env_default = heltecv2
;env_default = ttgov1 ;env_default = ttgov1
;env_default = ttgov2 ;env_default = ttgov2
@ -32,7 +32,7 @@ description = Paxcounter is a proof-of-concept ESP32 device for metering passeng
release_version = 1.6.63 release_version = 1.6.63
; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running! ; 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 ; 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 MODE: select esptool to flash via USB/UART, select custom to upload to cloud for OTA
upload_protocol = esptool upload_protocol = esptool
;upload_protocol = custom ;upload_protocol = custom
@ -52,15 +52,19 @@ lib_deps_rgbled =
lib_deps_gps = lib_deps_gps =
TinyGPSPlus@>=1.0.2 TinyGPSPlus@>=1.0.2
Time@>=1.5 Time@>=1.5
lib_deps_sensors =
Adafruit Unified Sensor@^1.0.2
Adafruit BME680 Library@^1.0.7
build_flags = build_flags =
-include "src/hal/${PIOENV}.h" -include $PROJECTSRC_DIR/hal/${PIOENV}.h
-include "src/paxcounter.conf" -include $PROJECTSRC_DIR/paxcounter.conf
-w -w
;'-D ARDUINO_LMIC_PROJECT_CONFIG_H="$PROJECTSRC_DIR/lmic_config.h"'
'-DARDUINO_LMIC_PROJECT_CONFIG_H=../../../src/lmic_config.h' '-DARDUINO_LMIC_PROJECT_CONFIG_H=../../../src/lmic_config.h'
'-DCORE_DEBUG_LEVEL=${common.debug_level}' '-D CORE_DEBUG_LEVEL=${common.debug_level}'
'-DLOG_LOCAL_LEVEL=${common.debug_level}' '-D LOG_LOCAL_LEVEL=${common.debug_level}'
'-DBINTRAY_PACKAGE="${PIOENV}"' '-D BINTRAY_PACKAGE="${PIOENV}"'
'-DPROGVERSION="${common.release_version}"' '-D PROGVERSION="${common.release_version}"'
[env:ebox] [env:ebox]
platform = ${common.platform_espressif32} platform = ${common.platform_espressif32}
@ -332,6 +336,7 @@ lib_deps =
${common.lib_deps_rgbled} ${common.lib_deps_rgbled}
${common.lib_deps_gps} ${common.lib_deps_gps}
${common.lib_deps_display} ${common.lib_deps_display}
${common.lib_deps_sensors}
build_flags = build_flags =
${common.build_flags} ${common.build_flags}
upload_protocol = ${common.upload_protocol} upload_protocol = ${common.upload_protocol}

65
src/bme680read.cpp Normal file
View 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

View File

@ -3,12 +3,12 @@
// Basic config // Basic config
#include "cyclic.h" #include "cyclic.h"
#include "rcommand.h"
#include "spislave.h"
// Local logging tag // Local logging tag
static const char TAG[] = "main"; static const char TAG[] = "main";
uint32_t userUTCTime; // Seconds since the UTC epoch
// do all housekeeping // do all housekeeping
void doHousekeeping() { void doHousekeeping() {
@ -19,7 +19,10 @@ void doHousekeeping() {
if (cfg.runmode == 1) if (cfg.runmode == 1)
do_reset(); do_reset();
// task storage debugging // spi_housekeeping();
do_timesync();
// task storage debugging //
ESP_LOGD(TAG, "Wifiloop %d bytes left", ESP_LOGD(TAG, "Wifiloop %d bytes left",
uxTaskGetStackHighWaterMark(wifiSwitchTask)); uxTaskGetStackHighWaterMark(wifiSwitchTask));
ESP_LOGD(TAG, "IRQhandler %d bytes left", ESP_LOGD(TAG, "IRQhandler %d bytes left",
@ -28,10 +31,9 @@ void doHousekeeping() {
ESP_LOGD(TAG, "Gpsloop %d bytes left", uxTaskGetStackHighWaterMark(GpsTask)); ESP_LOGD(TAG, "Gpsloop %d bytes left", uxTaskGetStackHighWaterMark(GpsTask));
#endif #endif
spi_housekeeping();
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED) #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 #endif
// read battery voltage into global variable // read battery voltage into global variable
@ -40,17 +42,6 @@ void doHousekeeping() {
ESP_LOGI(TAG, "Measured Voltage: %dmV", batt_voltage); ESP_LOGI(TAG, "Measured Voltage: %dmV", batt_voltage);
#endif #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 // check free memory
if (esp_get_minimum_free_heap_size() <= MEM_LOW) { if (esp_get_minimum_free_heap_size() <= MEM_LOW) {
ESP_LOGI(TAG, ESP_LOGI(TAG,
@ -83,6 +74,24 @@ void reset_counters() {
macs_ble = 0; 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 #ifndef VERBOSE
int redirect_log(const char *fmt, va_list args) { int redirect_log(const char *fmt, va_list args) {
// do nothing // do nothing

View File

@ -13,6 +13,8 @@
#define SPI_SCLK GPIO_NUM_18 #define SPI_SCLK GPIO_NUM_18
#define SPI_CS GPIO_NUM_5 #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_sx1276_radio 1 // select LoRa chip
//#define CFG_sx1272_radio 1 // select LoRa chip //#define CFG_sx1272_radio 1 // select LoRa chip
#define BOARD_HAS_PSRAM // use if board has external PSRAM #define BOARD_HAS_PSRAM // use if board has external PSRAM

View File

@ -3,6 +3,8 @@
#include <stdint.h> #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 // Hardware related definitions for Heltec LoRa-32 Board
#define HAS_LORA 1 // comment out if device shall not send data via LoRa #define HAS_LORA 1 // comment out if device shall not send data via LoRa

View File

@ -21,6 +21,8 @@
//#define LMIC_USE_INTERRUPTS //#define LMIC_USE_INTERRUPTS
#define LMIC_ENABLE_DeviceTimeReq 1
// 16 μs per tick // 16 μs per tick
// LMIC requires ticks to be 15.5μs - 100 μs long // LMIC requires ticks to be 15.5μs - 100 μs long
#define US_PER_OSTICK_EXPONENT 4 #define US_PER_OSTICK_EXPONENT 4
@ -38,7 +40,7 @@
// enable more verbose output. Make sure that printf is actually // enable more verbose output. Make sure that printf is actually
// configured (e.g. on AVR it is not by default), otherwise using it can // configured (e.g. on AVR it is not by default), otherwise using it can
// cause crashing. // 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 // 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 // (or any other Print object). This can be easy for debugging. The

View File

@ -328,9 +328,9 @@ void lora_send(osjob_t *job) {
// waiting for LoRa getting ready // waiting for LoRa getting ready
} else { } else {
if (xQueueReceive(LoraSendQueue, &SendBuffer, (TickType_t)0) == pdTRUE) { 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, if (!LMIC_setTxData2(SendBuffer.MessagePort, SendBuffer.Message,
SendBuffer.MessageSize, (cfg.countermode & 0x02))) { SendBuffer.MessageSize, (cfg.countermode & 0x02))) {
ESP_LOGI(TAG, "%d byte(s) sent to LoRa", SendBuffer.MessageSize); ESP_LOGI(TAG, "%d byte(s) sent to LoRa", SendBuffer.MessageSize);
} else { } else {
ESP_LOGE(TAG, "could not send %d byte(s) to LoRa", ESP_LOGE(TAG, "could not send %d byte(s) to LoRa",
@ -384,7 +384,7 @@ esp_err_t lora_stack_init() {
if (!LMIC_startJoining()) { // start joining if (!LMIC_startJoining()) { // start joining
ESP_LOGI(TAG, "Already joined"); ESP_LOGI(TAG, "Already joined");
} }
return ESP_OK; // continue main program return ESP_OK; // continue main program
#endif #endif
} }
@ -414,3 +414,48 @@ void lora_housekeeping(void) {
// uxTaskGetStackHighWaterMark(LoraTask)); // uxTaskGetStackHighWaterMark(LoraTask));
#endif #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());
}

View File

@ -35,6 +35,7 @@ IDLE 0 0 ESP32 arduino scheduler -> runs wifi sniffer
looptask 1 1 arduino core -> runs the LMIC LoRa stack looptask 1 1 arduino core -> runs the LMIC LoRa stack
irqhandler 1 1 executes tasks triggered by irq irqhandler 1 1 executes tasks triggered by irq
gpsloop 1 2 reads data from GPS over serial or i2c 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 IDLE 1 0 ESP32 arduino scheduler
ESP32 hardware timers ESP32 hardware timers
@ -157,6 +158,11 @@ void setup() {
strcat_P(features, " GPS"); strcat_P(features, " GPS");
#endif #endif
// initialize gps
#ifdef HAS_BME
strcat_P(features, " BME");
#endif
// initialize LoRa // initialize LoRa
#ifdef HAS_LORA #ifdef HAS_LORA
strcat_P(features, " LORA"); strcat_P(features, " LORA");
@ -275,6 +281,17 @@ void setup() {
1); // CPU core 1); // CPU core
#endif #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 // start state machine
ESP_LOGI(TAG, "Starting IRQ Handler..."); ESP_LOGI(TAG, "Starting IRQ Handler...");
xTaskCreatePinnedToCore(irqHandler, // task function xTaskCreatePinnedToCore(irqHandler, // task function