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