Merge branch 'master' of https://github.com/cyberman54/ESP32-Paxcounter
This commit is contained in:
commit
760ddb8c84
23
README.md
23
README.md
@ -14,7 +14,7 @@ Paxcounter is a proof-of-concept device for metering passenger flows in realtime
|
|||||||
|
|
||||||
Intention of this project is to do this without intrusion in privacy: You don't need to track people owned devices, if you just want to count them. Therefore, Paxcounter does not persistenly store MAC adresses and does no kind of fingerprinting the scanned devices.
|
Intention of this project is to do this without intrusion in privacy: You don't need to track people owned devices, if you just want to count them. Therefore, Paxcounter does not persistenly store MAC adresses and does no kind of fingerprinting the scanned devices.
|
||||||
|
|
||||||
Metered counts are transferred to a server via a LoRaWAN network, and/or a local SPI cable interface.
|
Data is transferred to a server via a LoRaWAN network, and/or a wired SPI slave interface.
|
||||||
|
|
||||||
You can build this project battery powered and reach a full day uptime with a single 18650 Li-Ion cell.
|
You can build this project battery powered and reach a full day uptime with a single 18650 Li-Ion cell.
|
||||||
|
|
||||||
@ -33,19 +33,21 @@ This can all be done with a single small and cheap ESP32 board for less than $20
|
|||||||
LoLin32lite + [LoraNode32-Lite shield](https://github.com/hallard/LoLin32-Lite-Lora)
|
LoLin32lite + [LoraNode32-Lite shield](https://github.com/hallard/LoLin32-Lite-Lora)
|
||||||
- Adafruit ESP32 Feather + LoRa Wing + OLED Wing, #IoT Octopus32 (Octopus + ESP32 Feather)
|
- Adafruit ESP32 Feather + LoRa Wing + OLED Wing, #IoT Octopus32 (Octopus + ESP32 Feather)
|
||||||
|
|
||||||
*SPI only*: (code yet to come)
|
*SPI only*:
|
||||||
|
|
||||||
- Pyom: WiPy
|
- Pyom: WiPy
|
||||||
- WeMos: LoLin32, LoLin32 Lite, WeMos D32
|
- WeMos: LoLin32, LoLin32 Lite, WeMos D32
|
||||||
|
- Generic ESP32
|
||||||
|
|
||||||
Depending on board hardware following features are supported:
|
Depending on board hardware following features are supported:
|
||||||
- LED
|
- LED (power/status)
|
||||||
- OLED Display
|
- OLED Display (detailed status)
|
||||||
- RGB LED
|
- RGB LED (colorized status)
|
||||||
- Button
|
- Button
|
||||||
- Silicon unique ID
|
- Silicon unique ID
|
||||||
- Battery voltage monitoring
|
- Battery voltage monitoring
|
||||||
- GPS (Generic serial NMEA, or Quectel L76 I2C)
|
- GPS (Generic serial NMEA, or Quectel L76 I2C)
|
||||||
|
- MEMS sensor (Bosch BME680)
|
||||||
|
|
||||||
Target platform must be selected in [platformio.ini](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/platformio.ini).<br>
|
Target platform must be selected in [platformio.ini](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/platformio.ini).<br>
|
||||||
Hardware dependent settings (pinout etc.) are stored in board files in /hal directory. If you want to use a ESP32 board which is not yet supported, use hal file generic.h and tailor pin mappings to your needs. Pull requests for new boards welcome.<br>
|
Hardware dependent settings (pinout etc.) are stored in board files in /hal directory. If you want to use a ESP32 board which is not yet supported, use hal file generic.h and tailor pin mappings to your needs. Pull requests for new boards welcome.<br>
|
||||||
@ -197,6 +199,13 @@ Hereafter described is the default *plain* format, which uses MSB bit numbering.
|
|||||||
byte 1: Beacon RSSI reception level
|
byte 1: Beacon RSSI reception level
|
||||||
byte 2: Beacon identifier (0..255)
|
byte 2: Beacon identifier (0..255)
|
||||||
|
|
||||||
|
**Port #7:** BME680 query result
|
||||||
|
|
||||||
|
bytes 1-2: Temperature [°C]
|
||||||
|
bytes 3-4: Pressure [hPa]
|
||||||
|
bytes 5-6: Humidity [%]
|
||||||
|
bytes 7-8: Gas resistance [kOhm]
|
||||||
|
|
||||||
# Remote control
|
# Remote control
|
||||||
|
|
||||||
The device listenes for remote control commands on LoRaWAN Port 2. Multiple commands per downlink are possible by concatenating them.
|
The device listenes for remote control commands on LoRaWAN Port 2. Multiple commands per downlink are possible by concatenating them.
|
||||||
@ -309,6 +318,10 @@ Note: all settings are stored in NVRAM and will be reloaded when device starts.
|
|||||||
|
|
||||||
Device answers with it's current status on Port 4.
|
Device answers with it's current status on Port 4.
|
||||||
|
|
||||||
|
0x85 get BME680 sensor data
|
||||||
|
|
||||||
|
Device answers with BME680 sensor data set on Port 7.
|
||||||
|
|
||||||
|
|
||||||
# License
|
# License
|
||||||
|
|
||||||
|
15
include/bme680read.h
Normal file
15
include/bme680read.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#ifndef _HAS_BME
|
||||||
|
#define _HAS_BME
|
||||||
|
|
||||||
|
#include "globals.h"
|
||||||
|
#include <Wire.h>
|
||||||
|
#include <Adafruit_Sensor.h>
|
||||||
|
#include "Adafruit_BME680.h"
|
||||||
|
|
||||||
|
extern bmeStatus_t
|
||||||
|
bme_status; // Make struct for storing gps data globally available
|
||||||
|
|
||||||
|
void bme_init();
|
||||||
|
bool bme_read();
|
||||||
|
|
||||||
|
#endif
|
@ -3,8 +3,12 @@
|
|||||||
|
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
#include "senddata.h"
|
#include "senddata.h"
|
||||||
|
#include "rcommand.h"
|
||||||
|
#include "spislave.h"
|
||||||
|
#include <lmic.h>
|
||||||
|
|
||||||
void doHousekeeping(void);
|
void doHousekeeping(void);
|
||||||
|
void do_timesync(void);
|
||||||
uint64_t uptime(void);
|
uint64_t uptime(void);
|
||||||
void reset_counters(void);
|
void reset_counters(void);
|
||||||
int redirect_log(const char *fmt, va_list args);
|
int redirect_log(const char *fmt, va_list args);
|
||||||
|
@ -38,6 +38,21 @@ typedef struct {
|
|||||||
uint8_t Message[PAYLOAD_BUFFER_SIZE];
|
uint8_t Message[PAYLOAD_BUFFER_SIZE];
|
||||||
} MessageBuffer_t;
|
} MessageBuffer_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t latitude;
|
||||||
|
uint32_t longitude;
|
||||||
|
uint8_t satellites;
|
||||||
|
uint16_t hdop;
|
||||||
|
uint16_t altitude;
|
||||||
|
} gpsStatus_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
float temperature; // Temperature in degrees Centigrade
|
||||||
|
uint16_t pressure; // Barometic pressure in hecto pascals
|
||||||
|
float humidity; // Relative humidity in percent
|
||||||
|
uint16_t gas_resistance; // Resistance in MOhms
|
||||||
|
} bmeStatus_t;
|
||||||
|
|
||||||
// global variables
|
// global variables
|
||||||
extern configData_t cfg; // current device configuration
|
extern configData_t cfg; // current device configuration
|
||||||
extern char display_line6[], display_line7[]; // screen buffers
|
extern char display_line6[], display_line7[]; // screen buffers
|
||||||
@ -52,24 +67,21 @@ extern std::array<uint64_t, 0xff> beacons;
|
|||||||
|
|
||||||
extern TaskHandle_t irqHandlerTask, wifiSwitchTask;
|
extern TaskHandle_t irqHandlerTask, wifiSwitchTask;
|
||||||
|
|
||||||
|
#include "led.h"
|
||||||
|
#include "payload.h"
|
||||||
|
|
||||||
#ifdef HAS_GPS
|
#ifdef HAS_GPS
|
||||||
#include "gpsread.h"
|
#include "gpsread.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
|
#ifdef HAS_BME
|
||||||
#include "led.h"
|
#include "bme680read.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "payload.h"
|
|
||||||
|
|
||||||
#ifdef HAS_LORA
|
#ifdef HAS_LORA
|
||||||
#include "lorawan.h"
|
#include "lorawan.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAS_SPI
|
|
||||||
#include "spisend.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAS_DISPLAY
|
#ifdef HAS_DISPLAY
|
||||||
#include "display.h"
|
#include "display.h"
|
||||||
#endif
|
#endif
|
||||||
@ -90,8 +102,4 @@ extern TaskHandle_t irqHandlerTask, wifiSwitchTask;
|
|||||||
#include "antenna.h"
|
#include "antenna.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void reset_counters(void);
|
|
||||||
void blink_LED(uint16_t set_color, uint16_t set_blinkduration);
|
|
||||||
uint64_t uptime();
|
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -8,14 +8,6 @@
|
|||||||
#include <Wire.h>
|
#include <Wire.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint32_t latitude;
|
|
||||||
uint32_t longitude;
|
|
||||||
uint8_t satellites;
|
|
||||||
uint16_t hdop;
|
|
||||||
uint16_t altitude;
|
|
||||||
} gpsStatus_t;
|
|
||||||
|
|
||||||
extern TinyGPSPlus gps; // Make TinyGPS++ instance globally availabe
|
extern TinyGPSPlus gps; // Make TinyGPS++ instance globally availabe
|
||||||
extern gpsStatus_t
|
extern gpsStatus_t
|
||||||
gps_status; // Make struct for storing gps data globally available
|
gps_status; // Make struct for storing gps data globally available
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#define DISPLAY_IRQ 0x01
|
#define DISPLAY_IRQ 0x01
|
||||||
#define BUTTON_IRQ 0x02
|
#define BUTTON_IRQ 0x02
|
||||||
#define SENDPAYLOAD_IRQ 0x04
|
#define SENDCOUNTER_IRQ 0x04
|
||||||
#define CYCLIC_IRQ 0x08
|
#define CYCLIC_IRQ 0x08
|
||||||
|
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
|
@ -37,8 +37,6 @@ extern TaskHandle_t ledLoopTask;
|
|||||||
void rgb_set_color(uint16_t hue);
|
void rgb_set_color(uint16_t hue);
|
||||||
void blink_LED(uint16_t set_color, uint16_t set_blinkduration);
|
void blink_LED(uint16_t set_color, uint16_t set_blinkduration);
|
||||||
void ledLoop(void *parameter);
|
void ledLoop(void *parameter);
|
||||||
#if (HAS_LED != NOT_A_PIN)
|
|
||||||
void switch_LED(uint8_t state);
|
void switch_LED(uint8_t state);
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -26,5 +26,12 @@ void os_getDevEui(u1_t *buf);
|
|||||||
void showLoraKeys(void);
|
void showLoraKeys(void);
|
||||||
void switch_lora(uint8_t sf, uint8_t tx);
|
void switch_lora(uint8_t sf, uint8_t tx);
|
||||||
void lora_send(osjob_t *job);
|
void lora_send(osjob_t *job);
|
||||||
|
void lora_enqueuedata(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();
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -13,5 +13,7 @@
|
|||||||
#include "ota.h"
|
#include "ota.h"
|
||||||
#include "irqhandler.h"
|
#include "irqhandler.h"
|
||||||
#include "led.h"
|
#include "led.h"
|
||||||
|
#include "spislave.h"
|
||||||
|
#include "lorawan.h"
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -4,14 +4,15 @@
|
|||||||
#ifdef USE_OTA
|
#ifdef USE_OTA
|
||||||
|
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
#include "update.h"
|
|
||||||
#include "battery.h"
|
#include "battery.h"
|
||||||
|
#include "update.h"
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
#include <WiFiClientSecure.h>
|
#include <WiFiClientSecure.h>
|
||||||
#include <BintrayClient.h>
|
#include <BintrayClient.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
void do_ota_update();
|
int do_ota_update();
|
||||||
void start_ota_update();
|
void start_ota_update();
|
||||||
int version_compare(const String v1, const String v2);
|
int version_compare(const String v1, const String v2);
|
||||||
void display(const uint8_t row, const std::string status,
|
void display(const uint8_t row, const std::string status,
|
||||||
|
@ -10,19 +10,25 @@
|
|||||||
#define LPP_BATT_CHANNEL 23
|
#define LPP_BATT_CHANNEL 23
|
||||||
#define LPP_BUTTON_CHANNEL 24
|
#define LPP_BUTTON_CHANNEL 24
|
||||||
#define LPP_ADR_CHANNEL 25
|
#define LPP_ADR_CHANNEL 25
|
||||||
#define LPP_TEMP_CHANNEL 26
|
#define LPP_TEMPERATURE_CHANNEL 26
|
||||||
#define LPP_ALARM_CHANNEL 27
|
#define LPP_ALARM_CHANNEL 27
|
||||||
#define LPP_MSG_CHANNEL 28
|
#define LPP_MSG_CHANNEL 28
|
||||||
|
#define LPP_HUMIDITY_CHANNEL 29
|
||||||
|
#define LPP_BAROMETER_CHANNEL 30
|
||||||
|
#define LPP_GAS_CHANNEL 31
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// MyDevices CayenneLPP types
|
// MyDevices CayenneLPP types
|
||||||
#define LPP_GPS 136 // 3 byte lon/lat 0.0001 °, 3 bytes alt 0.01m
|
#define LPP_GPS 136 // 3 byte lon/lat 0.0001 °, 3 bytes alt 0.01m
|
||||||
#define LPP_TEMPERATURE 103 // 2 bytes, 0.1°C signed
|
#define LPP_TEMPERATURE 103 // 2 bytes, 0.1°C signed MSB
|
||||||
#define LPP_DIGITAL_INPUT 0 // 1 byte
|
#define LPP_DIGITAL_INPUT 0 // 1 byte
|
||||||
#define LPP_DIGITAL_OUTPUT 1 // 1 byte
|
#define LPP_DIGITAL_OUTPUT 1 // 1 byte
|
||||||
#define LPP_ANALOG_INPUT 2 // 2 bytes, 0.01 signed
|
#define LPP_ANALOG_INPUT 2 // 2 bytes, 0.01 signed
|
||||||
#define LPP_LUMINOSITY 101 // 2 bytes, 1 lux unsigned
|
#define LPP_LUMINOSITY 101 // 2 bytes, 1 lux unsigned
|
||||||
#define LPP_PRESENCE 102 // 1 byte
|
#define LPP_PRESENCE 102 // 1 byte
|
||||||
|
#define LPP_HUMIDITY 104 // 1 byte, 0.5 % unsigned
|
||||||
|
#define LPP_BAROMETER 115 // 2 bytes, hPa unsigned MSB
|
||||||
|
|
||||||
class PayloadConvert {
|
class PayloadConvert {
|
||||||
|
|
||||||
@ -38,12 +44,9 @@ public:
|
|||||||
void addStatus(uint16_t voltage, uint64_t uptime, float cputemp, uint32_t mem,
|
void addStatus(uint16_t voltage, uint64_t uptime, float cputemp, uint32_t mem,
|
||||||
uint8_t reset1, uint8_t reset2);
|
uint8_t reset1, uint8_t reset2);
|
||||||
void addAlarm(int8_t rssi, uint8_t message);
|
void addAlarm(int8_t rssi, uint8_t message);
|
||||||
#ifdef HAS_GPS
|
|
||||||
void addGPS(gpsStatus_t value);
|
void addGPS(gpsStatus_t value);
|
||||||
#endif
|
void addBME(bmeStatus_t value);
|
||||||
#ifdef HAS_BUTTON
|
|
||||||
void addButton(uint8_t value);
|
void addButton(uint8_t value);
|
||||||
#endif
|
|
||||||
|
|
||||||
#if PAYLOAD_ENCODER == 1 // format plain
|
#if PAYLOAD_ENCODER == 1 // format plain
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#define _RCOMMAND_H
|
#define _RCOMMAND_H
|
||||||
|
|
||||||
#include "senddata.h"
|
#include "senddata.h"
|
||||||
|
#include "cyclic.h"
|
||||||
#include "configmanager.h"
|
#include "configmanager.h"
|
||||||
#include "lorawan.h"
|
#include "lorawan.h"
|
||||||
#include "macsniff.h"
|
#include "macsniff.h"
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
#ifndef _SENDDATA_H
|
#ifndef _SENDDATA_H
|
||||||
#define _SENDDATA_H
|
#define _SENDDATA_H
|
||||||
|
|
||||||
void SendData(uint8_t port);
|
#include "spislave.h"
|
||||||
void sendPayload(void);
|
#include "lorawan.h"
|
||||||
|
#include "cyclic.h"
|
||||||
|
|
||||||
|
void SendPayload(uint8_t port);
|
||||||
|
void sendCounter(void);
|
||||||
void checkSendQueues(void);
|
void checkSendQueues(void);
|
||||||
void flushQueues();
|
void flushQueues();
|
||||||
|
|
||||||
|
@ -1,34 +0,0 @@
|
|||||||
#ifndef _SPISEND_H
|
|
||||||
#define _SPISEND_H
|
|
||||||
|
|
||||||
#include "globals.h"
|
|
||||||
#include "spi.h"
|
|
||||||
|
|
||||||
extern TaskHandle_t SpiTask;
|
|
||||||
extern QueueHandle_t SPISendQueue;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Process data in SPI send queue
|
|
||||||
*/
|
|
||||||
void spi_loop(void *pvParameters);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* initialize local SPI wire interface
|
|
||||||
*/
|
|
||||||
void hal_spi_init();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Perform SPI write transaction on local SPI wire interface
|
|
||||||
* - write the command byte 'cmd'
|
|
||||||
* - write 'len' bytes out of 'buf'
|
|
||||||
*/
|
|
||||||
void hal_spi_write(uint8_t cmd, const uint8_t* buf, int len);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Perform SPI read transaction on local SPI wire interface
|
|
||||||
* - read the command byte 'cmd'
|
|
||||||
* - read 'len' bytes into 'buf'
|
|
||||||
*/
|
|
||||||
void hal_spi_read(uint8_t cmd, uint8_t* buf, int len);
|
|
||||||
|
|
||||||
#endif
|
|
36
include/spislave.h
Normal file
36
include/spislave.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
//////////////////////// ESP32-Paxcounter \\\\\\\\\\\\\\\\\\\\\\\\\\
|
||||||
|
|
||||||
|
Copyright 2018 Christian Ambach <christian.ambach@deutschebahn.com>
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
NOTICE:
|
||||||
|
Parts of the source files in this repository are made available under different
|
||||||
|
licenses. Refer to LICENSE.txt file in repository for more details.
|
||||||
|
|
||||||
|
*/
|
||||||
|
#ifndef _SPISLAVE_H
|
||||||
|
#define _SPISLAVE_H
|
||||||
|
|
||||||
|
#include "globals.h"
|
||||||
|
|
||||||
|
esp_err_t spi_init();
|
||||||
|
|
||||||
|
void spi_enqueuedata(MessageBuffer_t *message);
|
||||||
|
void spi_queuereset();
|
||||||
|
|
||||||
|
void spi_housekeeping();
|
||||||
|
|
||||||
|
#endif // _SPISLAVE_H
|
@ -41,7 +41,7 @@ class UpdateClass {
|
|||||||
Call this to check the space needed for the update
|
Call this to check the space needed for the update
|
||||||
Will return false if there is not enough space
|
Will return false if there is not enough space
|
||||||
*/
|
*/
|
||||||
bool begin(size_t size=UPDATE_SIZE_UNKNOWN, int command = U_FLASH);
|
bool begin(size_t size=UPDATE_SIZE_UNKNOWN, int command = U_FLASH, int ledPin = -1, uint8_t ledOn = LOW);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Writes a buffer to the flash and increments the address
|
Writes a buffer to the flash and increments the address
|
||||||
@ -174,6 +174,9 @@ class UpdateClass {
|
|||||||
|
|
||||||
String _target_md5;
|
String _target_md5;
|
||||||
MD5Builder _md5;
|
MD5Builder _md5;
|
||||||
|
|
||||||
|
int _ledPin;
|
||||||
|
uint8_t _ledOn;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern UpdateClass Update;
|
extern UpdateClass Update;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
Arduino-LMIC library
|
# Arduino-LMIC library
|
||||||
====================
|
|
||||||
|
|
||||||
This repository contains the IBM LMIC (LoraMAC-in-C) library, slightly
|
This repository contains the IBM LMIC (LoraMAC-in-C) library, slightly
|
||||||
modified to run in the Arduino environment, allowing using the SX1272,
|
modified to run in the Arduino environment, allowing using the SX1272,
|
||||||
@ -25,7 +24,7 @@ requires C99 mode to be enabled by default.
|
|||||||
We strongly recommend updating using VS Code, the markdown-toc extension and the
|
We strongly recommend updating using VS Code, the markdown-toc extension and the
|
||||||
bierner.markdown-preview-github-styles extension.
|
bierner.markdown-preview-github-styles extension.
|
||||||
-->
|
-->
|
||||||
<!-- TOC depthFrom:1 -->
|
<!-- TOC depthFrom:2 -->
|
||||||
|
|
||||||
- [Installing](#installing)
|
- [Installing](#installing)
|
||||||
- [Features](#features)
|
- [Features](#features)
|
||||||
@ -86,12 +85,12 @@ requires C99 mode to be enabled by default.
|
|||||||
|
|
||||||
To install this library:
|
To install this library:
|
||||||
|
|
||||||
- install it using the Arduino Library manager ("Sketch" -> "Include
|
- install it using the Arduino Library manager ("Sketch" -> "Include
|
||||||
Library" -> "Manage Libraries..."), or
|
Library" -> "Manage Libraries..."), or
|
||||||
- download a zipfile from github using the "Download ZIP" button and
|
- download a zipfile from github using the "Download ZIP" button and
|
||||||
install it using the IDE ("Sketch" -> "Include Library" -> "Add .ZIP
|
install it using the IDE ("Sketch" -> "Include Library" -> "Add .ZIP
|
||||||
Library..."
|
Library..."
|
||||||
- clone this git repository into your sketchbook/libraries folder.
|
- clone this git repository into your sketchbook/libraries folder.
|
||||||
|
|
||||||
For more info, see https://www.arduino.cc/en/Guide/Libraries
|
For more info, see https://www.arduino.cc/en/Guide/Libraries
|
||||||
|
|
||||||
@ -105,19 +104,19 @@ The library has only been tested with LoRaWAN 1.0.2 networks and does not have t
|
|||||||
|
|
||||||
What certainly works:
|
What certainly works:
|
||||||
|
|
||||||
- Sending packets uplink, taking into account duty cycling.
|
- Sending packets uplink, taking into account duty cycling.
|
||||||
- Encryption and message integrity checking.
|
- Encryption and message integrity checking.
|
||||||
- Receiving downlink packets in the RX2 window.
|
- Receiving downlink packets in the RX2 window.
|
||||||
- Custom frequencies and datarate settings.
|
- Custom frequencies and datarate settings.
|
||||||
- Over-the-air activation (OTAA / joining).
|
- Over-the-air activation (OTAA / joining).
|
||||||
- Receiving downlink packets in the RX1 and RX2 windows.
|
- Receiving downlink packets in the RX1 and RX2 windows.
|
||||||
- Some MAC command processing.
|
- Some MAC command processing.
|
||||||
|
|
||||||
What has not been tested:
|
What has not been tested:
|
||||||
|
|
||||||
- Receiving and processing all MAC commands.
|
- Receiving and processing all MAC commands.
|
||||||
- Class B operation.
|
- Class B operation.
|
||||||
- FSK has not been extensively tested.
|
- FSK has not been extensively tested.
|
||||||
|
|
||||||
If you try one of these untested features and it works, be sure to let
|
If you try one of these untested features and it works, be sure to let
|
||||||
us know (creating a github issue is probably the best way for that).
|
us know (creating a github issue is probably the best way for that).
|
||||||
@ -131,7 +130,7 @@ directory is the only directory that contains files that you
|
|||||||
should edit to match your project; we organize things this way
|
should edit to match your project; we organize things this way
|
||||||
so that your local changes are more clearly separated from
|
so that your local changes are more clearly separated from
|
||||||
the distribution files. The Arduino environment doesn't give
|
the distribution files. The Arduino environment doesn't give
|
||||||
us a better way to do this.
|
us a better way to do this, unless you change `BOARDS.txt`.
|
||||||
|
|
||||||
Unlike other ports of the LMIC code, in this port, you should not edit `src/lmic/config.h` to configure this package.
|
Unlike other ports of the LMIC code, in this port, you should not edit `src/lmic/config.h` to configure this package.
|
||||||
|
|
||||||
@ -147,6 +146,7 @@ The library supports the following regions:
|
|||||||
`-D CFG_us915` | `LMIC_REGION_us915` | 2 | 2.2 | US 902-928 MHz ISM
|
`-D CFG_us915` | `LMIC_REGION_us915` | 2 | 2.2 | US 902-928 MHz ISM
|
||||||
`-D CFG_au921` | `LMIC_REGION_au921` | 5 | 2.5 | Australia 915-928 MHz ISM
|
`-D CFG_au921` | `LMIC_REGION_au921` | 5 | 2.5 | Australia 915-928 MHz ISM
|
||||||
`-D CFG_as923` | `LMIC_REGION_as923` | 7 | 2.7 | Asia 923 MHz ISM
|
`-D CFG_as923` | `LMIC_REGION_as923` | 7 | 2.7 | Asia 923 MHz ISM
|
||||||
|
`-D CFG_as923jp` | `LMIC_REGION_as923` and `LMIC_COUNTRY_CODE_JP` | 7 | 2.7 | Asia 923 MHz ISM with Japan listen-before-talk (LBT) rules
|
||||||
`-D CFG_in866` | `LMIC_REGION_in866` | 9 | 2.9 | India 865-867 MHz ISM
|
`-D CFG_in866` | `LMIC_REGION_in866` | 9 | 2.9 | India 865-867 MHz ISM
|
||||||
|
|
||||||
You should define exactly one of `CFG_...` variables. If you don't,
|
You should define exactly one of `CFG_...` variables. If you don't,
|
||||||
@ -205,8 +205,7 @@ By default, PING support is included in the library.
|
|||||||
If defined, removes all code needed for handling beacons. Removes the APIs `LMIC_enableTracking()` and `LMIC_disableTracking()`.
|
If defined, removes all code needed for handling beacons. Removes the APIs `LMIC_enableTracking()` and `LMIC_disableTracking()`.
|
||||||
Class A devices don't support beacons, so defining `DISABLE_BEACONS` might be a good idea.
|
Class A devices don't support beacons, so defining `DISABLE_BEACONS` might be a good idea.
|
||||||
|
|
||||||
|
### Rarely changed variables
|
||||||
### Rarely changed variables ###
|
|
||||||
|
|
||||||
The remaining variables are rarely used, but we list them here for completeness.
|
The remaining variables are rarely used, but we list them here for completeness.
|
||||||
|
|
||||||
@ -1022,6 +1021,10 @@ function uflt122f(rawUflt12)
|
|||||||
|
|
||||||
## Release History
|
## Release History
|
||||||
|
|
||||||
|
- Interim bug fixes: added a new API (`radio_irq_handler_v2()`), which allows the caller to provide the timestamp of the interrupt. This allows for more accurate timing, because the knowledge of interrupt overhead can be moved to a platform-specific layer ([#148](https://github.com/mcci-catena/arduino-lmic/issues/148)). Fixed compile issues on ESP32 ([#140](https://github.com/mcci-catena/arduino-lmic/issues/140) and [#153](https://github.com/mcci-catena/arduino-lmic/issues/150)). We added ESP32 and 32u4 as targets in CI testing. We switched CI testing to Arduino IDE 1.8.7.
|
||||||
|
Fixed issue [#161](https://github.com/mcci-catena/arduino-lmic/issues/161) selecting the Japan version of as923 using `CFG_as923jp` (selecting via `CFG_as923` and `LMIC_COUNTRY_CODE=LMIC_COUNTRY_CODE_JP` worked).
|
||||||
|
Fixed [#38](https://github.com/mcci-catena/arduino-lmic/issues/38) -- now any call to hal_init() will put the NSS line in the idle (high/inactive) state. As a side effect, RXTX is initialized, and RESET code changed to set value before transitioning state. Likely no net effect, but certainly more correct.
|
||||||
|
|
||||||
- V2.2.2 adds `ttn-abp-feather-us915-dht22.ino` example, and fixes some documentation typos. It also fixes encoding of the `Margin` field of the `DevStatusAns` MAC message ([#130](https://github.com/mcci-catena/arduino-lmic/issues/130)). This makes Arduino LMIC work with newtorks implemented with [LoraServer](https://www.loraserver.io/).
|
- V2.2.2 adds `ttn-abp-feather-us915-dht22.ino` example, and fixes some documentation typos. It also fixes encoding of the `Margin` field of the `DevStatusAns` MAC message ([#130](https://github.com/mcci-catena/arduino-lmic/issues/130)). This makes Arduino LMIC work with newtorks implemented with [LoraServer](https://www.loraserver.io/).
|
||||||
|
|
||||||
- V2.2.1 corrects the value of `ARDUINO_LMIC_VERSION` ([#123](https://github.com/mcci-catena/arduino-lmic/issues/123)), allows ttn-otaa-feather-us915 example to compile for the Feather 32u4 LoRa ([#116](https://github.com/mcci-catena/arduino-lmic/issues/116)), and addresses documentation issues ([#122](https://github.com/mcci-catena/arduino-lmic/issues/122), [#120](https://github.com/mcci-catena/arduino-lmic/issues/120)).
|
- V2.2.1 corrects the value of `ARDUINO_LMIC_VERSION` ([#123](https://github.com/mcci-catena/arduino-lmic/issues/123)), allows ttn-otaa-feather-us915 example to compile for the Feather 32u4 LoRa ([#116](https://github.com/mcci-catena/arduino-lmic/issues/116)), and addresses documentation issues ([#122](https://github.com/mcci-catena/arduino-lmic/issues/122), [#120](https://github.com/mcci-catena/arduino-lmic/issues/120)).
|
||||||
|
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_Shift_Rows();
|
||||||
static void AES_Mix_Collums();
|
static void AES_Mix_Collums();
|
||||||
static void AES_Calculate_Round_Key(unsigned char Round, unsigned char *Round_Key);
|
static void AES_Calculate_Round_Key(unsigned char Round, unsigned char *Round_Key);
|
||||||
static void Send_State();
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*****************************************************************************************
|
*****************************************************************************************
|
||||||
|
@ -33,11 +33,19 @@ static void hal_io_init () {
|
|||||||
// Serial.print("dio[1]: "); Serial.println(plmic_pins->dio[1]);
|
// Serial.print("dio[1]: "); Serial.println(plmic_pins->dio[1]);
|
||||||
// Serial.print("dio[2]: "); Serial.println(plmic_pins->dio[2]);
|
// Serial.print("dio[2]: "); Serial.println(plmic_pins->dio[2]);
|
||||||
|
|
||||||
|
// initialize SPI chip select to high (it's active low)
|
||||||
|
digitalWrite(plmic_pins->nss, HIGH);
|
||||||
pinMode(plmic_pins->nss, OUTPUT);
|
pinMode(plmic_pins->nss, OUTPUT);
|
||||||
if (plmic_pins->rxtx != LMIC_UNUSED_PIN)
|
|
||||||
|
if (plmic_pins->rxtx != LMIC_UNUSED_PIN) {
|
||||||
|
// initialize to RX
|
||||||
|
digitalWrite(plmic_pins->rxtx, LOW != plmic_pins->rxtx_rx_active);
|
||||||
pinMode(plmic_pins->rxtx, OUTPUT);
|
pinMode(plmic_pins->rxtx, OUTPUT);
|
||||||
if (plmic_pins->rst != LMIC_UNUSED_PIN)
|
}
|
||||||
pinMode(plmic_pins->rst, OUTPUT);
|
if (plmic_pins->rst != LMIC_UNUSED_PIN) {
|
||||||
|
// initialize RST to floating
|
||||||
|
pinMode(plmic_pins->rst, INPUT);
|
||||||
|
}
|
||||||
|
|
||||||
hal_interrupt_init();
|
hal_interrupt_init();
|
||||||
}
|
}
|
||||||
@ -54,8 +62,8 @@ void hal_pin_rst (u1_t val) {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if(val == 0 || val == 1) { // drive pin
|
if(val == 0 || val == 1) { // drive pin
|
||||||
pinMode(plmic_pins->rst, OUTPUT);
|
|
||||||
digitalWrite(plmic_pins->rst, val);
|
digitalWrite(plmic_pins->rst, val);
|
||||||
|
pinMode(plmic_pins->rst, OUTPUT);
|
||||||
} else { // keep pin floating
|
} else { // keep pin floating
|
||||||
pinMode(plmic_pins->rst, INPUT);
|
pinMode(plmic_pins->rst, INPUT);
|
||||||
}
|
}
|
||||||
|
@ -171,4 +171,11 @@
|
|||||||
# endif // defined(LMIC_DISABLE_DR_LEGACY)
|
# endif // defined(LMIC_DISABLE_DR_LEGACY)
|
||||||
#endif // LMIC_DR_LEGACY
|
#endif // LMIC_DR_LEGACY
|
||||||
|
|
||||||
|
// LMIC_ENABLE_DeviceTimeReq
|
||||||
|
// enable support for MCMD_DeviceTimeReq and MCMD_DeviceTimeAns
|
||||||
|
// this is always defined, and non-zero to enable it.
|
||||||
|
#if !defined(LMIC_ENABLE_DeviceTimeReq)
|
||||||
|
# define LMIC_ENABLE_DeviceTimeReq 0
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif // _lmic_config_h_
|
#endif // _lmic_config_h_
|
||||||
|
@ -47,8 +47,10 @@ static void startScan (void);
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
static inline void initTxrxFlags(const char *func, u1_t mask) {
|
static inline void initTxrxFlags(const char *func, u1_t mask) {
|
||||||
|
LMIC_DEBUG2_PARAMETER(func);
|
||||||
|
|
||||||
#if LMIC_DEBUG_LEVEL > 1
|
#if LMIC_DEBUG_LEVEL > 1
|
||||||
LMIC_DEBUG_PRINTF("%lu: %s txrxFlags %#02x --> %02x\n", os_getTime(), func, LMIC.txrxFlags, mask);
|
LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": %s txrxFlags %#02x --> %02x\n", os_getTime(), func, LMIC.txrxFlags, mask);
|
||||||
#endif
|
#endif
|
||||||
LMIC.txrxFlags = mask;
|
LMIC.txrxFlags = mask;
|
||||||
}
|
}
|
||||||
@ -272,29 +274,6 @@ ostime_t calcAirTime (rps_t rps, u1_t plen) {
|
|||||||
return (((ostime_t)tmp << sfx) * OSTICKS_PER_SEC + div/2) / div;
|
return (((ostime_t)tmp << sfx) * OSTICKS_PER_SEC + div/2) / div;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern inline rps_t updr2rps (dr_t dr);
|
|
||||||
extern inline rps_t dndr2rps (dr_t dr);
|
|
||||||
extern inline int isFasterDR (dr_t dr1, dr_t dr2);
|
|
||||||
extern inline int isSlowerDR (dr_t dr1, dr_t dr2);
|
|
||||||
extern inline dr_t incDR (dr_t dr);
|
|
||||||
extern inline dr_t decDR (dr_t dr);
|
|
||||||
extern inline dr_t assertDR (dr_t dr);
|
|
||||||
extern inline dr_t validDR (dr_t dr);
|
|
||||||
extern inline dr_t lowerDR (dr_t dr, u1_t n);
|
|
||||||
|
|
||||||
extern inline sf_t getSf (rps_t params);
|
|
||||||
extern inline rps_t setSf (rps_t params, sf_t sf);
|
|
||||||
extern inline bw_t getBw (rps_t params);
|
|
||||||
extern inline rps_t setBw (rps_t params, bw_t cr);
|
|
||||||
extern inline cr_t getCr (rps_t params);
|
|
||||||
extern inline rps_t setCr (rps_t params, cr_t cr);
|
|
||||||
extern inline int getNocrc (rps_t params);
|
|
||||||
extern inline rps_t setNocrc (rps_t params, int nocrc);
|
|
||||||
extern inline int getIh (rps_t params);
|
|
||||||
extern inline rps_t setIh (rps_t params, int ih);
|
|
||||||
extern inline rps_t makeRps (sf_t sf, bw_t bw, cr_t cr, int ih, int nocrc);
|
|
||||||
extern inline int sameSfBw (rps_t r1, rps_t r2);
|
|
||||||
|
|
||||||
// END LORA
|
// END LORA
|
||||||
// ================================================================================
|
// ================================================================================
|
||||||
|
|
||||||
@ -414,6 +393,8 @@ static void txDelay (ostime_t reftime, u1_t secSpan) {
|
|||||||
|
|
||||||
|
|
||||||
void LMICcore_setDrJoin (u1_t reason, u1_t dr) {
|
void LMICcore_setDrJoin (u1_t reason, u1_t dr) {
|
||||||
|
LMIC_EV_PARAMETER(reason);
|
||||||
|
|
||||||
EV(drChange, INFO, (e_.reason = reason,
|
EV(drChange, INFO, (e_.reason = reason,
|
||||||
e_.deveui = MAIN::CDEV->getEui(),
|
e_.deveui = MAIN::CDEV->getEui(),
|
||||||
e_.dr = dr|DR_PAGE,
|
e_.dr = dr|DR_PAGE,
|
||||||
@ -426,6 +407,8 @@ void LMICcore_setDrJoin (u1_t reason, u1_t dr) {
|
|||||||
|
|
||||||
|
|
||||||
static void setDrTxpow (u1_t reason, u1_t dr, s1_t pow) {
|
static void setDrTxpow (u1_t reason, u1_t dr, s1_t pow) {
|
||||||
|
LMIC_EV_PARAMETER(reason);
|
||||||
|
|
||||||
EV(drChange, INFO, (e_.reason = reason,
|
EV(drChange, INFO, (e_.reason = reason,
|
||||||
e_.deveui = MAIN::CDEV->getEui(),
|
e_.deveui = MAIN::CDEV->getEui(),
|
||||||
e_.dr = dr|DR_PAGE,
|
e_.dr = dr|DR_PAGE,
|
||||||
@ -462,6 +445,8 @@ void LMIC_setPingable (u1_t intvExp) {
|
|||||||
#endif // !DISABLE_PING
|
#endif // !DISABLE_PING
|
||||||
|
|
||||||
static void runEngineUpdate (xref2osjob_t osjob) {
|
static void runEngineUpdate (xref2osjob_t osjob) {
|
||||||
|
LMIC_API_PARAMETER(osjob);
|
||||||
|
|
||||||
engineUpdate();
|
engineUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -476,6 +461,8 @@ static void reportEvent (ev_t ev) {
|
|||||||
|
|
||||||
|
|
||||||
static void runReset (xref2osjob_t osjob) {
|
static void runReset (xref2osjob_t osjob) {
|
||||||
|
LMIC_API_PARAMETER(osjob);
|
||||||
|
|
||||||
// Disable session
|
// Disable session
|
||||||
LMIC_reset();
|
LMIC_reset();
|
||||||
#if !defined(DISABLE_JOIN)
|
#if !defined(DISABLE_JOIN)
|
||||||
@ -594,7 +581,7 @@ scan_mac_cmds(
|
|||||||
// of contiguous commands (whatever that means), and ignore the
|
// of contiguous commands (whatever that means), and ignore the
|
||||||
// data rate, NbTrans (uprpt) and txPow until the last one.
|
// data rate, NbTrans (uprpt) and txPow until the last one.
|
||||||
#if LMIC_DEBUG_LEVEL > 0
|
#if LMIC_DEBUG_LEVEL > 0
|
||||||
LMIC_DEBUG_PRINTF("%lu: LinkAdrReq: p1:%02x chmap:%04x chpage:%02x uprt:%02x ans:%02x\n",
|
LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": LinkAdrReq: p1:%02x chmap:%04x chpage:%02x uprt:%02x ans:%02x\n",
|
||||||
os_getTime(), p1, chmap, chpage, uprpt, LMIC.ladrAns
|
os_getTime(), p1, chmap, chpage, uprpt, LMIC.ladrAns
|
||||||
);
|
);
|
||||||
#endif /* LMIC_DEBUG_LEVEL */
|
#endif /* LMIC_DEBUG_LEVEL */
|
||||||
@ -726,6 +713,38 @@ scan_mac_cmds(
|
|||||||
oidx += 2;
|
oidx += 2;
|
||||||
continue;
|
continue;
|
||||||
} /* end case */
|
} /* end case */
|
||||||
|
case MCMD_DeviceTimeAns: {
|
||||||
|
#if LMIC_ENABLE_DeviceTimeReq
|
||||||
|
// don't process a spurious downlink.
|
||||||
|
if ( LMIC.txDeviceTimeReqState == lmic_RequestTimeState_rx ) {
|
||||||
|
// remember that it's time to notify the client.
|
||||||
|
LMIC.txDeviceTimeReqState = lmic_RequestTimeState_success;
|
||||||
|
|
||||||
|
// the network time is linked to the time of the last TX.
|
||||||
|
LMIC.localDeviceTime = LMIC.txend;
|
||||||
|
|
||||||
|
// save the network time.
|
||||||
|
// The first 4 bytes contain the seconds since the GPS epoch
|
||||||
|
// (i.e January the 6th 1980 at 00:00:00 UTC).
|
||||||
|
// Note: as per the LoRaWAN specs, the octet order for all
|
||||||
|
// multi-octet fields is little endian
|
||||||
|
// Note: the casts are necessary, because opts is an array of
|
||||||
|
// single byte values, and they might overflow when shifted
|
||||||
|
LMIC.netDeviceTime = ( (lmic_gpstime_t) opts[oidx + 1] ) |
|
||||||
|
(((lmic_gpstime_t) opts[oidx + 2]) << 8) |
|
||||||
|
(((lmic_gpstime_t) opts[oidx + 3]) << 16) |
|
||||||
|
(((lmic_gpstime_t) opts[oidx + 4]) << 24);
|
||||||
|
|
||||||
|
// The 5th byte contains the fractional seconds in 2^-8 second steps
|
||||||
|
LMIC.netDeviceTimeFrac = opts[oidx + 5];
|
||||||
|
#if LMIC_DEBUG_LEVEL > 0
|
||||||
|
LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": MAC command DeviceTimeAns received: seconds_since_gps_epoch=%"PRIu32", fractional_seconds=%d\n", os_getTime(), LMIC.netDeviceTime, LMIC.netDeviceTimeFrac);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif // LMIC_ENABLE_DeviceTimeReq
|
||||||
|
oidx += 6;
|
||||||
|
continue;
|
||||||
|
} /* end case */
|
||||||
} /* end switch */
|
} /* end switch */
|
||||||
/* unrecognized mac commands fall out of switch to here */
|
/* unrecognized mac commands fall out of switch to here */
|
||||||
EV(specCond, ERR, (e_.reason = EV::specCond_t::BAD_MAC_CMD,
|
EV(specCond, ERR, (e_.reason = EV::specCond_t::BAD_MAC_CMD,
|
||||||
@ -757,7 +776,7 @@ static bit_t decodeFrame (void) {
|
|||||||
e_.info2 = hdr + (dlen<<8)));
|
e_.info2 = hdr + (dlen<<8)));
|
||||||
norx:
|
norx:
|
||||||
#if LMIC_DEBUG_LEVEL > 0
|
#if LMIC_DEBUG_LEVEL > 0
|
||||||
LMIC_DEBUG_PRINTF("%lu: Invalid downlink, window=%s\n", os_getTime(), window);
|
LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": Invalid downlink, window=%s\n", os_getTime(), window);
|
||||||
#endif
|
#endif
|
||||||
LMIC.dataLen = 0;
|
LMIC.dataLen = 0;
|
||||||
return 0;
|
return 0;
|
||||||
@ -849,7 +868,7 @@ static bit_t decodeFrame (void) {
|
|||||||
|
|
||||||
#if LMIC_DEBUG_LEVEL > 0
|
#if LMIC_DEBUG_LEVEL > 0
|
||||||
// Process OPTS
|
// Process OPTS
|
||||||
LMIC_DEBUG_PRINTF("%lu: process options (olen=%#x)\n", os_getTime(), olen);
|
LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": process options (olen=%#x)\n", os_getTime(), olen);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
xref2u1_t opts = &d[OFF_DAT_OPTS];
|
xref2u1_t opts = &d[OFF_DAT_OPTS];
|
||||||
@ -868,13 +887,13 @@ static bit_t decodeFrame (void) {
|
|||||||
if (port == 0) {
|
if (port == 0) {
|
||||||
// this is a mac command. scan the options.
|
// this is a mac command. scan the options.
|
||||||
#if LMIC_DEBUG_LEVEL > 0
|
#if LMIC_DEBUG_LEVEL > 0
|
||||||
LMIC_DEBUG_PRINTF("%lu: process mac commands for port 0 (olen=%#x)\n", os_getTime(), pend-poff);
|
LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": process mac commands for port 0 (olen=%#x)\n", os_getTime(), pend-poff);
|
||||||
#endif
|
#endif
|
||||||
int optendindex = scan_mac_cmds(d+poff, pend-poff);
|
int optendindex = scan_mac_cmds(d+poff, pend-poff);
|
||||||
if (optendindex != pend-poff) {
|
if (optendindex != pend-poff) {
|
||||||
#if LMIC_DEBUG_LEVEL > 0
|
#if LMIC_DEBUG_LEVEL > 0
|
||||||
LMIC_DEBUG_PRINTF(
|
LMIC_DEBUG_PRINTF(
|
||||||
"%lu: error processing mac commands for port 0 "
|
"%"LMIC_PRId_ostime_t": error processing mac commands for port 0 "
|
||||||
"(len=%#x, optendindex=%#x)\n",
|
"(len=%#x, optendindex=%#x)\n",
|
||||||
os_getTime(), pend-poff, optendindex
|
os_getTime(), pend-poff, optendindex
|
||||||
);
|
);
|
||||||
@ -911,7 +930,7 @@ static bit_t decodeFrame (void) {
|
|||||||
e_.info = seqno,
|
e_.info = seqno,
|
||||||
e_.info2 = ackup));
|
e_.info2 = ackup));
|
||||||
#if LMIC_DEBUG_LEVEL > 1
|
#if LMIC_DEBUG_LEVEL > 1
|
||||||
LMIC_DEBUG_PRINTF("%lu: ??ack error ack=%d txCnt=%d\n", os_getTime(), ackup, LMIC.txCnt);
|
LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": ??ack error ack=%d txCnt=%d\n", os_getTime(), ackup, LMIC.txCnt);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -928,7 +947,7 @@ static bit_t decodeFrame (void) {
|
|||||||
LMIC.dataLen = pend-poff;
|
LMIC.dataLen = pend-poff;
|
||||||
}
|
}
|
||||||
#if LMIC_DEBUG_LEVEL > 0
|
#if LMIC_DEBUG_LEVEL > 0
|
||||||
LMIC_DEBUG_PRINTF("%lu: Received downlink, window=%s, port=%d, ack=%d, txrxFlags=%#x\n", os_getTime(), window, port, ackup, LMIC.txrxFlags);
|
LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": Received downlink, window=%s, port=%d, ack=%d, txrxFlags=%#x\n", os_getTime(), window, port, ackup, LMIC.txrxFlags);
|
||||||
#endif
|
#endif
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -977,7 +996,7 @@ static void schedRx12 (ostime_t delay, osjobcb_t func, u1_t dr) {
|
|||||||
// (again note that hsym is half a sumbol time, so no /2 needed)
|
// (again note that hsym is half a sumbol time, so no /2 needed)
|
||||||
LMIC.rxtime = LMIC.txend + delay + PAMBL_SYMS * hsym - LMIC.rxsyms * hsym;
|
LMIC.rxtime = LMIC.txend + delay + PAMBL_SYMS * hsym - LMIC.rxsyms * hsym;
|
||||||
|
|
||||||
LMIC_X_DEBUG_PRINTF("%lu: sched Rx12 %lu\n", os_getTime(), LMIC.rxtime - RX_RAMPUP);
|
LMIC_X_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": sched Rx12 %"LMIC_PRId_ostime_t"\n", os_getTime(), LMIC.rxtime - RX_RAMPUP);
|
||||||
os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - RX_RAMPUP, func);
|
os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - RX_RAMPUP, func);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1020,6 +1039,8 @@ static void txDone (ostime_t delay, osjobcb_t func) {
|
|||||||
|
|
||||||
#if !defined(DISABLE_JOIN)
|
#if !defined(DISABLE_JOIN)
|
||||||
static void onJoinFailed (xref2osjob_t osjob) {
|
static void onJoinFailed (xref2osjob_t osjob) {
|
||||||
|
LMIC_API_PARAMETER(osjob);
|
||||||
|
|
||||||
// Notify app - must call LMIC_reset() to stop joining
|
// Notify app - must call LMIC_reset() to stop joining
|
||||||
// otherwise join procedure continues.
|
// otherwise join procedure continues.
|
||||||
reportEvent(EV_JOIN_FAILED);
|
reportEvent(EV_JOIN_FAILED);
|
||||||
@ -1073,6 +1094,8 @@ static bit_t processJoinAccept (void) {
|
|||||||
u1_t hdr = LMIC.frame[0];
|
u1_t hdr = LMIC.frame[0];
|
||||||
u1_t dlen = LMIC.dataLen;
|
u1_t dlen = LMIC.dataLen;
|
||||||
u4_t mic = os_rlsbf4(&LMIC.frame[dlen-4]); // safe before modified by encrypt!
|
u4_t mic = os_rlsbf4(&LMIC.frame[dlen-4]); // safe before modified by encrypt!
|
||||||
|
LMIC_EV_VARIABLE(mic); // only used by EV().
|
||||||
|
|
||||||
if( (dlen != LEN_JA && dlen != LEN_JAEXT)
|
if( (dlen != LEN_JA && dlen != LEN_JAEXT)
|
||||||
|| (hdr & (HDR_FTYPE|HDR_MAJOR)) != (HDR_FTYPE_JACC|HDR_MAJOR_V1) ) {
|
|| (hdr & (HDR_FTYPE|HDR_MAJOR)) != (HDR_FTYPE_JACC|HDR_MAJOR_V1) ) {
|
||||||
EV(specCond, ERR, (e_.reason = EV::specCond_t::UNEXPECTED_FRAME,
|
EV(specCond, ERR, (e_.reason = EV::specCond_t::UNEXPECTED_FRAME,
|
||||||
@ -1109,7 +1132,7 @@ static bit_t processJoinAccept (void) {
|
|||||||
if( freq ) {
|
if( freq ) {
|
||||||
LMIC_setupChannel(chidx, freq, 0, -1);
|
LMIC_setupChannel(chidx, freq, 0, -1);
|
||||||
#if LMIC_DEBUG_LEVEL > 1
|
#if LMIC_DEBUG_LEVEL > 1
|
||||||
LMIC_DEBUG_PRINTF("%lu: Setup channel, idx=%d, freq=%lu\n", os_getTime(), chidx, (unsigned long)freq);
|
LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": Setup channel, idx=%d, freq=%"PRIu32"\n", os_getTime(), chidx, freq);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1161,6 +1184,8 @@ static bit_t processJoinAccept (void) {
|
|||||||
|
|
||||||
|
|
||||||
static void processRx2Jacc (xref2osjob_t osjob) {
|
static void processRx2Jacc (xref2osjob_t osjob) {
|
||||||
|
LMIC_API_PARAMETER(osjob);
|
||||||
|
|
||||||
if( LMIC.dataLen == 0 ) {
|
if( LMIC.dataLen == 0 ) {
|
||||||
initTxrxFlags(__func__, 0); // nothing in 1st/2nd DN slot
|
initTxrxFlags(__func__, 0); // nothing in 1st/2nd DN slot
|
||||||
}
|
}
|
||||||
@ -1169,23 +1194,31 @@ static void processRx2Jacc (xref2osjob_t osjob) {
|
|||||||
|
|
||||||
|
|
||||||
static void setupRx2Jacc (xref2osjob_t osjob) {
|
static void setupRx2Jacc (xref2osjob_t osjob) {
|
||||||
|
LMIC_API_PARAMETER(osjob);
|
||||||
|
|
||||||
LMIC.osjob.func = FUNC_ADDR(processRx2Jacc);
|
LMIC.osjob.func = FUNC_ADDR(processRx2Jacc);
|
||||||
setupRx2();
|
setupRx2();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void processRx1Jacc (xref2osjob_t osjob) {
|
static void processRx1Jacc (xref2osjob_t osjob) {
|
||||||
|
LMIC_API_PARAMETER(osjob);
|
||||||
|
|
||||||
if( LMIC.dataLen == 0 || !processJoinAccept() )
|
if( LMIC.dataLen == 0 || !processJoinAccept() )
|
||||||
schedRx12(DELAY_JACC2_osticks, FUNC_ADDR(setupRx2Jacc), LMIC.dn2Dr);
|
schedRx12(DELAY_JACC2_osticks, FUNC_ADDR(setupRx2Jacc), LMIC.dn2Dr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void setupRx1Jacc (xref2osjob_t osjob) {
|
static void setupRx1Jacc (xref2osjob_t osjob) {
|
||||||
|
LMIC_API_PARAMETER(osjob);
|
||||||
|
|
||||||
setupRx1(FUNC_ADDR(processRx1Jacc));
|
setupRx1(FUNC_ADDR(processRx1Jacc));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void jreqDone (xref2osjob_t osjob) {
|
static void jreqDone (xref2osjob_t osjob) {
|
||||||
|
LMIC_API_PARAMETER(osjob);
|
||||||
|
|
||||||
txDone(DELAY_JACC1_osticks, FUNC_ADDR(setupRx1Jacc));
|
txDone(DELAY_JACC1_osticks, FUNC_ADDR(setupRx1Jacc));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1197,10 +1230,14 @@ static void jreqDone (xref2osjob_t osjob) {
|
|||||||
static bit_t processDnData(void);
|
static bit_t processDnData(void);
|
||||||
|
|
||||||
static void processRx2DnDataDelay (xref2osjob_t osjob) {
|
static void processRx2DnDataDelay (xref2osjob_t osjob) {
|
||||||
|
LMIC_API_PARAMETER(osjob);
|
||||||
|
|
||||||
processDnData();
|
processDnData();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void processRx2DnData (xref2osjob_t osjob) {
|
static void processRx2DnData (xref2osjob_t osjob) {
|
||||||
|
LMIC_API_PARAMETER(osjob);
|
||||||
|
|
||||||
if( LMIC.dataLen == 0 ) {
|
if( LMIC.dataLen == 0 ) {
|
||||||
initTxrxFlags(__func__, 0); // nothing in 1st/2nd DN slot
|
initTxrxFlags(__func__, 0); // nothing in 1st/2nd DN slot
|
||||||
// Delay callback processing to avoid up TX while gateway is txing our missed frame!
|
// Delay callback processing to avoid up TX while gateway is txing our missed frame!
|
||||||
@ -1215,23 +1252,31 @@ static void processRx2DnData (xref2osjob_t osjob) {
|
|||||||
|
|
||||||
|
|
||||||
static void setupRx2DnData (xref2osjob_t osjob) {
|
static void setupRx2DnData (xref2osjob_t osjob) {
|
||||||
|
LMIC_API_PARAMETER(osjob);
|
||||||
|
|
||||||
LMIC.osjob.func = FUNC_ADDR(processRx2DnData);
|
LMIC.osjob.func = FUNC_ADDR(processRx2DnData);
|
||||||
setupRx2();
|
setupRx2();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void processRx1DnData (xref2osjob_t osjob) {
|
static void processRx1DnData (xref2osjob_t osjob) {
|
||||||
|
LMIC_API_PARAMETER(osjob);
|
||||||
|
|
||||||
if( LMIC.dataLen == 0 || !processDnData() )
|
if( LMIC.dataLen == 0 || !processDnData() )
|
||||||
schedRx12(sec2osticks(LMIC.rxDelay +(int)DELAY_EXTDNW2), FUNC_ADDR(setupRx2DnData), LMIC.dn2Dr);
|
schedRx12(sec2osticks(LMIC.rxDelay +(int)DELAY_EXTDNW2), FUNC_ADDR(setupRx2DnData), LMIC.dn2Dr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void setupRx1DnData (xref2osjob_t osjob) {
|
static void setupRx1DnData (xref2osjob_t osjob) {
|
||||||
|
LMIC_API_PARAMETER(osjob);
|
||||||
|
|
||||||
setupRx1(FUNC_ADDR(processRx1DnData));
|
setupRx1(FUNC_ADDR(processRx1DnData));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void updataDone (xref2osjob_t osjob) {
|
static void updataDone (xref2osjob_t osjob) {
|
||||||
|
LMIC_API_PARAMETER(osjob);
|
||||||
|
|
||||||
txDone(sec2osticks(LMIC.rxDelay), FUNC_ADDR(setupRx1DnData));
|
txDone(sec2osticks(LMIC.rxDelay), FUNC_ADDR(setupRx1DnData));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1315,6 +1360,13 @@ static void buildDataFrame (void) {
|
|||||||
LMIC.txParamSetupAns = 0;
|
LMIC.txParamSetupAns = 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#if LMIC_ENABLE_DeviceTimeReq
|
||||||
|
if ( LMIC.txDeviceTimeReqState == lmic_RequestTimeState_tx ) {
|
||||||
|
LMIC.frame[end+0] = MCMD_DeviceTimeReq;
|
||||||
|
end += 1;
|
||||||
|
LMIC.txDeviceTimeReqState = lmic_RequestTimeState_rx;
|
||||||
|
}
|
||||||
|
#endif // LMIC_ENABLE_DeviceTimeReq
|
||||||
ASSERT(end <= OFF_DAT_OPTS+16);
|
ASSERT(end <= OFF_DAT_OPTS+16);
|
||||||
|
|
||||||
u1_t flen = end + (txdata ? 5+dlen : 4);
|
u1_t flen = end + (txdata ? 5+dlen : 4);
|
||||||
@ -1376,7 +1428,9 @@ static void buildDataFrame (void) {
|
|||||||
|
|
||||||
#if !defined(DISABLE_BEACONS)
|
#if !defined(DISABLE_BEACONS)
|
||||||
// Callback from HAL during scan mode or when job timer expires.
|
// Callback from HAL during scan mode or when job timer expires.
|
||||||
static void onBcnRx (xref2osjob_t job) {
|
static void onBcnRx (xref2osjob_t osjob) {
|
||||||
|
LMIC_API_PARAMETER(osjob);
|
||||||
|
|
||||||
// If we arrive via job timer make sure to put radio to rest.
|
// If we arrive via job timer make sure to put radio to rest.
|
||||||
os_radio(RADIO_RST);
|
os_radio(RADIO_RST);
|
||||||
os_clearCallback(&LMIC.osjob);
|
os_clearCallback(&LMIC.osjob);
|
||||||
@ -1497,6 +1551,8 @@ static void buildJoinRequest (u1_t ftype) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void startJoining (xref2osjob_t osjob) {
|
static void startJoining (xref2osjob_t osjob) {
|
||||||
|
LMIC_API_PARAMETER(osjob);
|
||||||
|
|
||||||
reportEvent(EV_JOINING);
|
reportEvent(EV_JOINING);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1530,6 +1586,8 @@ bit_t LMIC_startJoining (void) {
|
|||||||
|
|
||||||
#if !defined(DISABLE_PING)
|
#if !defined(DISABLE_PING)
|
||||||
static void processPingRx (xref2osjob_t osjob) {
|
static void processPingRx (xref2osjob_t osjob) {
|
||||||
|
LMIC_API_PARAMETER(osjob);
|
||||||
|
|
||||||
if( LMIC.dataLen != 0 ) {
|
if( LMIC.dataLen != 0 ) {
|
||||||
initTxrxFlags(__func__, TXRX_PING);
|
initTxrxFlags(__func__, TXRX_PING);
|
||||||
if( decodeFrame() ) {
|
if( decodeFrame() ) {
|
||||||
@ -1568,6 +1626,24 @@ static bit_t processDnData (void) {
|
|||||||
LMIC.dataBeg = LMIC.dataLen = 0;
|
LMIC.dataBeg = LMIC.dataLen = 0;
|
||||||
txcomplete:
|
txcomplete:
|
||||||
LMIC.opmode &= ~(OP_TXDATA|OP_TXRXPEND);
|
LMIC.opmode &= ~(OP_TXDATA|OP_TXRXPEND);
|
||||||
|
|
||||||
|
#if LMIC_ENABLE_DeviceTimeReq
|
||||||
|
lmic_request_time_state_t const requestTimeState = LMIC.txDeviceTimeReqState;
|
||||||
|
if ( requestTimeState != lmic_RequestTimeState_idle ) {
|
||||||
|
lmic_request_network_time_cb_t * const pNetworkTimeCb = LMIC.pNetworkTimeCb;
|
||||||
|
int flagSuccess = (LMIC.txDeviceTimeReqState == lmic_RequestTimeState_success);
|
||||||
|
LMIC.txDeviceTimeReqState = lmic_RequestTimeState_idle;
|
||||||
|
if (pNetworkTimeCb != NULL) {
|
||||||
|
// reset the callback, so that the user's routine
|
||||||
|
// can post another request if desired.
|
||||||
|
LMIC.pNetworkTimeCb = NULL;
|
||||||
|
|
||||||
|
// call the user's notification routine.
|
||||||
|
(*pNetworkTimeCb)(LMIC.pNetworkTimeUserData, flagSuccess);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // LMIC_ENABLE_DeviceTimeReq
|
||||||
|
|
||||||
if( (LMIC.txrxFlags & (TXRX_DNW1|TXRX_DNW2|TXRX_PING)) != 0 && (LMIC.opmode & OP_LINKDEAD) != 0 ) {
|
if( (LMIC.txrxFlags & (TXRX_DNW1|TXRX_DNW2|TXRX_PING)) != 0 && (LMIC.opmode & OP_LINKDEAD) != 0 ) {
|
||||||
LMIC.opmode &= ~OP_LINKDEAD;
|
LMIC.opmode &= ~OP_LINKDEAD;
|
||||||
reportEvent(EV_LINK_ALIVE);
|
reportEvent(EV_LINK_ALIVE);
|
||||||
@ -1611,6 +1687,8 @@ static bit_t processDnData (void) {
|
|||||||
|
|
||||||
#if !defined(DISABLE_BEACONS)
|
#if !defined(DISABLE_BEACONS)
|
||||||
static void processBeacon (xref2osjob_t osjob) {
|
static void processBeacon (xref2osjob_t osjob) {
|
||||||
|
LMIC_API_PARAMETER(osjob);
|
||||||
|
|
||||||
ostime_t lasttx = LMIC.bcninfo.txtime; // save here - decodeBeacon might overwrite
|
ostime_t lasttx = LMIC.bcninfo.txtime; // save here - decodeBeacon might overwrite
|
||||||
u1_t flags = LMIC.bcninfo.flags;
|
u1_t flags = LMIC.bcninfo.flags;
|
||||||
ev_t ev;
|
ev_t ev;
|
||||||
@ -1672,6 +1750,8 @@ static void processBeacon (xref2osjob_t osjob) {
|
|||||||
|
|
||||||
|
|
||||||
static void startRxBcn (xref2osjob_t osjob) {
|
static void startRxBcn (xref2osjob_t osjob) {
|
||||||
|
LMIC_API_PARAMETER(osjob);
|
||||||
|
|
||||||
LMIC.osjob.func = FUNC_ADDR(processBeacon);
|
LMIC.osjob.func = FUNC_ADDR(processBeacon);
|
||||||
os_radio(RADIO_RX);
|
os_radio(RADIO_RX);
|
||||||
}
|
}
|
||||||
@ -1680,6 +1760,8 @@ static void startRxBcn (xref2osjob_t osjob) {
|
|||||||
|
|
||||||
#if !defined(DISABLE_PING)
|
#if !defined(DISABLE_PING)
|
||||||
static void startRxPing (xref2osjob_t osjob) {
|
static void startRxPing (xref2osjob_t osjob) {
|
||||||
|
LMIC_API_PARAMETER(osjob);
|
||||||
|
|
||||||
LMIC.osjob.func = FUNC_ADDR(processPingRx);
|
LMIC.osjob.func = FUNC_ADDR(processPingRx);
|
||||||
os_radio(RADIO_RX);
|
os_radio(RADIO_RX);
|
||||||
}
|
}
|
||||||
@ -1689,7 +1771,7 @@ static void startRxPing (xref2osjob_t osjob) {
|
|||||||
// Decide what to do next for the MAC layer of a device
|
// Decide what to do next for the MAC layer of a device
|
||||||
static void engineUpdate (void) {
|
static void engineUpdate (void) {
|
||||||
#if LMIC_DEBUG_LEVEL > 0
|
#if LMIC_DEBUG_LEVEL > 0
|
||||||
LMIC_DEBUG_PRINTF("%lu: engineUpdate, opmode=0x%x\n", os_getTime(), LMIC.opmode);
|
LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": engineUpdate, opmode=0x%x\n", os_getTime(), LMIC.opmode);
|
||||||
#endif
|
#endif
|
||||||
// Check for ongoing state: scan or TX/RX transaction
|
// Check for ongoing state: scan or TX/RX transaction
|
||||||
if( (LMIC.opmode & (OP_SCAN|OP_TXRXPEND|OP_SHUTDOWN)) != 0 )
|
if( (LMIC.opmode & (OP_SCAN|OP_TXRXPEND|OP_SHUTDOWN)) != 0 )
|
||||||
@ -1703,10 +1785,11 @@ static void engineUpdate (void) {
|
|||||||
#endif // !DISABLE_JOIN
|
#endif // !DISABLE_JOIN
|
||||||
|
|
||||||
ostime_t now = os_getTime();
|
ostime_t now = os_getTime();
|
||||||
ostime_t rxtime = 0;
|
|
||||||
ostime_t txbeg = 0;
|
ostime_t txbeg = 0;
|
||||||
|
|
||||||
#if !defined(DISABLE_BEACONS)
|
#if !defined(DISABLE_BEACONS)
|
||||||
|
ostime_t rxtime = 0;
|
||||||
|
|
||||||
if( (LMIC.opmode & OP_TRACK) != 0 ) {
|
if( (LMIC.opmode & OP_TRACK) != 0 ) {
|
||||||
// We are tracking a beacon
|
// We are tracking a beacon
|
||||||
ASSERT( now + RX_RAMPUP - LMIC.bcnRxtime <= 0 );
|
ASSERT( now + RX_RAMPUP - LMIC.bcnRxtime <= 0 );
|
||||||
@ -1848,7 +1931,7 @@ static void engineUpdate (void) {
|
|||||||
e_.eui = MAIN::CDEV->getEui(),
|
e_.eui = MAIN::CDEV->getEui(),
|
||||||
e_.info = osticks2ms(txbeg-now),
|
e_.info = osticks2ms(txbeg-now),
|
||||||
e_.info2 = LMIC.seqnoUp-1));
|
e_.info2 = LMIC.seqnoUp-1));
|
||||||
LMIC_X_DEBUG_PRINTF("%lu: next engine update in %lu\n", now, txbeg-TX_RAMPUP);
|
LMIC_X_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": next engine update in %"LMIC_PRId_ostime_t"\n", now, txbeg-TX_RAMPUP);
|
||||||
os_setTimedCallback(&LMIC.osjob, txbeg-TX_RAMPUP, FUNC_ADDR(runEngineUpdate));
|
os_setTimedCallback(&LMIC.osjob, txbeg-TX_RAMPUP, FUNC_ADDR(runEngineUpdate));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1902,6 +1985,11 @@ void LMIC_reset (void) {
|
|||||||
DO_DEVDB(LMIC.ping.dr, pingDr);
|
DO_DEVDB(LMIC.ping.dr, pingDr);
|
||||||
DO_DEVDB(LMIC.ping.intvExp, pingIntvExp);
|
DO_DEVDB(LMIC.ping.intvExp, pingIntvExp);
|
||||||
#endif // !DISABLE_PING
|
#endif // !DISABLE_PING
|
||||||
|
#if LMIC_ENABLE_DeviceTimeReq
|
||||||
|
LMIC.txDeviceTimeReqState = lmic_RequestTimeState_idle;
|
||||||
|
LMIC.netDeviceTime = 0; // the "invalid" time.
|
||||||
|
LMIC.netDeviceTimeFrac = 0;
|
||||||
|
#endif // LMIC_ENABLE_DeviceTimeReq
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1943,7 +2031,6 @@ int LMIC_setTxData2 (u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Send a payload-less message to signal device is alive
|
// Send a payload-less message to signal device is alive
|
||||||
void LMIC_sendAlive (void) {
|
void LMIC_sendAlive (void) {
|
||||||
LMIC.opmode |= OP_POLL;
|
LMIC.opmode |= OP_POLL;
|
||||||
@ -2041,3 +2128,36 @@ void LMIC_getSessionKeys (u4_t *netid, devaddr_t *devaddr, xref2u1_t nwkKey, xre
|
|||||||
memcpy(artKey, LMIC.artKey, sizeof(LMIC.artKey));
|
memcpy(artKey, LMIC.artKey, sizeof(LMIC.artKey));
|
||||||
memcpy(nwkKey, LMIC.nwkKey, sizeof(LMIC.nwkKey));
|
memcpy(nwkKey, LMIC.nwkKey, sizeof(LMIC.nwkKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// \brief post an asynchronous request for the network time.
|
||||||
|
void LMIC_requestNetworkTime(lmic_request_network_time_cb_t *pCallbackfn, void *pUserData) {
|
||||||
|
#if LMIC_ENABLE_DeviceTimeReq
|
||||||
|
if (LMIC.txDeviceTimeReqState == lmic_RequestTimeState_idle) {
|
||||||
|
LMIC.txDeviceTimeReqState = lmic_RequestTimeState_tx;
|
||||||
|
LMIC.pNetworkTimeCb = pCallbackfn;
|
||||||
|
LMIC.pNetworkTimeUserData = pUserData;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif // LMIC_ENABLE_DeviceTimeReq
|
||||||
|
// if no device time support, or if not in proper state,
|
||||||
|
// report a failure.
|
||||||
|
if (pCallbackfn != NULL)
|
||||||
|
(*pCallbackfn)(pUserData, /* false */ 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// \brief return local/remote time pair (if valid, and DeviceTimeReq enabled),
|
||||||
|
// return true for success, false for error. We adjust the sampled OS time
|
||||||
|
// back in time to the nearest second boundary.
|
||||||
|
int LMIC_getNetworkTimeReference(lmic_time_reference_t *pReference) {
|
||||||
|
#if LMIC_ENABLE_DeviceTimeReq
|
||||||
|
if (pReference != NULL && // valid parameter, and
|
||||||
|
LMIC.netDeviceTime != 0) { // ... we have a reasonable answer.
|
||||||
|
const ostime_t tAdjust = LMIC.netDeviceTimeFrac * ms2osticks(1000) / 256;
|
||||||
|
|
||||||
|
pReference->tLocal = LMIC.localDeviceTime - tAdjust;
|
||||||
|
pReference->tNetwork = LMIC.netDeviceTime;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#endif // LMIC_ENABLE_DeviceTimeReq
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
@ -105,7 +105,7 @@ extern "C"{
|
|||||||
#define ARDUINO_LMIC_VERSION_CALC(major, minor, patch, local) \
|
#define ARDUINO_LMIC_VERSION_CALC(major, minor, patch, local) \
|
||||||
(((major) << 24u) | ((minor) << 16u) | ((patch) << 8u) | (local))
|
(((major) << 24u) | ((minor) << 16u) | ((patch) << 8u) | (local))
|
||||||
|
|
||||||
#define ARDUINO_LMIC_VERSION ARDUINO_LMIC_VERSION_CALC(2, 2, 2, 0)
|
#define ARDUINO_LMIC_VERSION ARDUINO_LMIC_VERSION_CALC(2, 2, 2, 0) /* v2.2.2 */
|
||||||
|
|
||||||
#define ARDUINO_LMIC_VERSION_GET_MAJOR(v) \
|
#define ARDUINO_LMIC_VERSION_GET_MAJOR(v) \
|
||||||
(((v) >> 24u) & 0xFFu)
|
(((v) >> 24u) & 0xFFu)
|
||||||
@ -243,6 +243,35 @@ enum {
|
|||||||
MAX_CLOCK_ERROR = 65536,
|
MAX_CLOCK_ERROR = 65536,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// network time request callback function
|
||||||
|
// defined unconditionally, because APIs and types can't change based on config.
|
||||||
|
// This is called when a time-request succeeds or when we get a downlink
|
||||||
|
// without time request, "completing" the pending time request.
|
||||||
|
typedef void lmic_request_network_time_cb_t(void *pUserData, int flagSuccess);
|
||||||
|
|
||||||
|
// how the network represents time.
|
||||||
|
typedef u4_t lmic_gpstime_t;
|
||||||
|
|
||||||
|
// rather than deal with 1/256 second tick, we adjust ostime back
|
||||||
|
// (as it's high res) to match tNetwork.
|
||||||
|
typedef struct lmic_time_reference_s lmic_time_reference_t;
|
||||||
|
|
||||||
|
struct lmic_time_reference_s {
|
||||||
|
// our best idea of when we sent the uplink (end of packet).
|
||||||
|
ostime_t tLocal;
|
||||||
|
// the network's best idea of when we sent the uplink.
|
||||||
|
lmic_gpstime_t tNetwork;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum lmic_request_time_state_e {
|
||||||
|
lmic_RequestTimeState_idle = 0, // we're not doing anything
|
||||||
|
lmic_RequestTimeState_tx, // we want to tx a time request on next uplink
|
||||||
|
lmic_RequestTimeState_rx, // we have tx'ed, next downlink completes.
|
||||||
|
lmic_RequestTimeState_success // we sucessfully got time.
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef u1_t lmic_request_time_state_t;
|
||||||
|
|
||||||
struct lmic_t {
|
struct lmic_t {
|
||||||
// Radio settings TX/RX (also accessed by HAL)
|
// Radio settings TX/RX (also accessed by HAL)
|
||||||
ostime_t txend;
|
ostime_t txend;
|
||||||
@ -306,6 +335,14 @@ struct lmic_t {
|
|||||||
devaddr_t devaddr;
|
devaddr_t devaddr;
|
||||||
u4_t seqnoDn; // device level down stream seqno
|
u4_t seqnoDn; // device level down stream seqno
|
||||||
u4_t seqnoUp;
|
u4_t seqnoUp;
|
||||||
|
#if LMIC_ENABLE_DeviceTimeReq
|
||||||
|
// put here for alignment, to reduce RAM use.
|
||||||
|
ostime_t localDeviceTime; // the LMIC.txend value for last DeviceTimeAns
|
||||||
|
lmic_gpstime_t netDeviceTime; // the netDeviceTime for lastDeviceTimeAns
|
||||||
|
// zero ==> not valid.
|
||||||
|
lmic_request_network_time_cb_t *pNetworkTimeCb; // call-back routine
|
||||||
|
void *pNetworkTimeUserData; // call-back data
|
||||||
|
#endif // LMIC_ENABLE_DeviceTimeReq
|
||||||
|
|
||||||
u1_t dnConf; // dn frame confirm pending: LORA::FCT_ACK or 0
|
u1_t dnConf; // dn frame confirm pending: LORA::FCT_ACK or 0
|
||||||
s1_t adrAckReq; // counter until we reset data rate (0=off)
|
s1_t adrAckReq; // counter until we reset data rate (0=off)
|
||||||
@ -329,6 +366,10 @@ struct lmic_t {
|
|||||||
bit_t txParamSetupAns; // transmit setup answer pending.
|
bit_t txParamSetupAns; // transmit setup answer pending.
|
||||||
u1_t txParam; // the saved TX param byte.
|
u1_t txParam; // the saved TX param byte.
|
||||||
#endif
|
#endif
|
||||||
|
#if LMIC_ENABLE_DeviceTimeReq
|
||||||
|
lmic_request_time_state_t txDeviceTimeReqState; // current state, initially idle.
|
||||||
|
u1_t netDeviceTimeFrac; // updated on any DeviceTimeAns.
|
||||||
|
#endif
|
||||||
|
|
||||||
// rx1DrOffset is the offset from uplink to downlink datarate
|
// rx1DrOffset is the offset from uplink to downlink datarate
|
||||||
u1_t rx1DrOffset; // captured from join. zero by default.
|
u1_t rx1DrOffset; // captured from join. zero by default.
|
||||||
@ -368,6 +409,7 @@ struct lmic_t {
|
|||||||
|
|
||||||
u1_t noRXIQinversion;
|
u1_t noRXIQinversion;
|
||||||
};
|
};
|
||||||
|
|
||||||
//! \var struct lmic_t LMIC
|
//! \var struct lmic_t LMIC
|
||||||
//! The state of LMIC MAC layer is encapsulated in this variable.
|
//! The state of LMIC MAC layer is encapsulated in this variable.
|
||||||
DECLARE_LMIC; //!< \internal
|
DECLARE_LMIC; //!< \internal
|
||||||
@ -417,6 +459,9 @@ u4_t LMIC_getSeqnoUp (void);
|
|||||||
u4_t LMIC_setSeqnoUp (u4_t);
|
u4_t LMIC_setSeqnoUp (u4_t);
|
||||||
void LMIC_getSessionKeys (u4_t *netid, devaddr_t *devaddr, xref2u1_t nwkKey, xref2u1_t artKey);
|
void LMIC_getSessionKeys (u4_t *netid, devaddr_t *devaddr, xref2u1_t nwkKey, xref2u1_t artKey);
|
||||||
|
|
||||||
|
void LMIC_requestNetworkTime(lmic_request_network_time_cb_t *pCallbackfn, void *pUserData);
|
||||||
|
int LMIC_getNetworkTimeReference(lmic_time_reference_t *pReference);
|
||||||
|
|
||||||
// Declare onEvent() function, to make sure any definition will have the
|
// Declare onEvent() function, to make sure any definition will have the
|
||||||
// C conventions, even when in a C++ file.
|
// C conventions, even when in a C++ file.
|
||||||
DECL_ON_LMIC_EVENT;
|
DECL_ON_LMIC_EVENT;
|
||||||
|
@ -76,11 +76,15 @@ static CONST_TABLE(u1_t, maxFrameLens_dwell1)[] = {
|
|||||||
|
|
||||||
static uint8_t
|
static uint8_t
|
||||||
LMICas923_getUplinkDwellBit(uint8_t mcmd_txparam) {
|
LMICas923_getUplinkDwellBit(uint8_t mcmd_txparam) {
|
||||||
|
LMIC_API_PARAMETER(mcmd_txparam);
|
||||||
|
|
||||||
return (LMIC.txParam & MCMD_TxParam_TxDWELL_MASK) != 0;
|
return (LMIC.txParam & MCMD_TxParam_TxDWELL_MASK) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t
|
static uint8_t
|
||||||
LMICas923_getDownlinkDwellBit(uint8_t mcmd_txparam) {
|
LMICas923_getDownlinkDwellBit(uint8_t mcmd_txparam) {
|
||||||
|
LMIC_API_PARAMETER(mcmd_txparam);
|
||||||
|
|
||||||
return (LMIC.txParam & MCMD_TxParam_RxDWELL_MASK) != 0;
|
return (LMIC.txParam & MCMD_TxParam_RxDWELL_MASK) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,6 +168,8 @@ static CONST_TABLE(u4_t, iniChannelFreq)[NUM_DEFAULT_CHANNELS] = {
|
|||||||
|
|
||||||
// as923 ignores join, becuase the channel setup is the same either way.
|
// as923 ignores join, becuase the channel setup is the same either way.
|
||||||
void LMICas923_initDefaultChannels(bit_t join) {
|
void LMICas923_initDefaultChannels(bit_t join) {
|
||||||
|
LMIC_API_PARAMETER(join);
|
||||||
|
|
||||||
os_clearMem(&LMIC.channelFreq, sizeof(LMIC.channelFreq));
|
os_clearMem(&LMIC.channelFreq, sizeof(LMIC.channelFreq));
|
||||||
os_clearMem(&LMIC.channelDrMap, sizeof(LMIC.channelDrMap));
|
os_clearMem(&LMIC.channelDrMap, sizeof(LMIC.channelDrMap));
|
||||||
os_clearMem(&LMIC.bands, sizeof(LMIC.bands));
|
os_clearMem(&LMIC.bands, sizeof(LMIC.bands));
|
||||||
|
@ -101,6 +101,11 @@ u4_t LMICau921_convFreq(xref2cu1_t ptr) {
|
|||||||
|
|
||||||
// au921: no support for xchannels.
|
// au921: no support for xchannels.
|
||||||
bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) {
|
bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) {
|
||||||
|
LMIC_API_PARAMETER(chidx);
|
||||||
|
LMIC_API_PARAMETER(freq);
|
||||||
|
LMIC_API_PARAMETER(drmap);
|
||||||
|
LMIC_API_PARAMETER(band);
|
||||||
|
|
||||||
return 0; // all channels are hardwired.
|
return 0; // all channels are hardwired.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,6 +113,12 @@ Revision history:
|
|||||||
// following values. These are in order of the sections in the manual. Not all of the
|
// following values. These are in order of the sections in the manual. Not all of the
|
||||||
// below are supported yet.
|
// below are supported yet.
|
||||||
//
|
//
|
||||||
|
// CFG_as923jp is treated as a special case of CFG_as923, so it's not included in
|
||||||
|
// the below.
|
||||||
|
//
|
||||||
|
// TODO(tmm@mcci.com) consider moving this block to a central file as it's not
|
||||||
|
// user-editable.
|
||||||
|
//
|
||||||
# define CFG_LMIC_REGION_MASK \
|
# define CFG_LMIC_REGION_MASK \
|
||||||
((defined(CFG_eu868) << LMIC_REGION_eu868) | \
|
((defined(CFG_eu868) << LMIC_REGION_eu868) | \
|
||||||
(defined(CFG_us915) << LMIC_REGION_us915) | \
|
(defined(CFG_us915) << LMIC_REGION_us915) | \
|
||||||
@ -126,6 +132,8 @@ Revision history:
|
|||||||
0)
|
0)
|
||||||
|
|
||||||
// the selected region.
|
// the selected region.
|
||||||
|
// TODO(tmm@mcci.com) consider moving this block to a central file as it's not
|
||||||
|
// user-editable.
|
||||||
#if defined(CFG_eu868)
|
#if defined(CFG_eu868)
|
||||||
# define CFG_region LMIC_REGION_eu868
|
# define CFG_region LMIC_REGION_eu868
|
||||||
#elif defined(CFG_us915)
|
#elif defined(CFG_us915)
|
||||||
@ -138,6 +146,10 @@ Revision history:
|
|||||||
# define CFG_region LMIC_REGION_au921
|
# define CFG_region LMIC_REGION_au921
|
||||||
#elif defined(CFG_cn490)
|
#elif defined(CFG_cn490)
|
||||||
# define CFG_region LMIC_REGION_cn490
|
# define CFG_region LMIC_REGION_cn490
|
||||||
|
#elif defined(CFG_as923jp)
|
||||||
|
# define CFG_as923 1 /* CFG_as923jp implies CFG_as923 */
|
||||||
|
# define CFG_region LMIC_REGION_as923
|
||||||
|
# define LMIC_COUNTRY_CODE LMIC_COUNTRY_CODE_JP
|
||||||
#elif defined(CFG_as923)
|
#elif defined(CFG_as923)
|
||||||
# define CFG_region LMIC_REGION_as923
|
# define CFG_region LMIC_REGION_as923
|
||||||
#elif defined(CFG_kr921)
|
#elif defined(CFG_kr921)
|
||||||
@ -148,7 +160,11 @@ Revision history:
|
|||||||
# define CFG_region 0
|
# define CFG_region 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// finally the mask of` US-like and EU-like regions
|
// a bitmask of EU-like regions -- these are regions which have up to 16
|
||||||
|
// channels indidually programmable via downloink.
|
||||||
|
//
|
||||||
|
// TODO(tmm@mcci.com) consider moving this block to a central file as it's not
|
||||||
|
// user-editable.
|
||||||
#define CFG_LMIC_EU_like_MASK ( \
|
#define CFG_LMIC_EU_like_MASK ( \
|
||||||
(1 << LMIC_REGION_eu868) | \
|
(1 << LMIC_REGION_eu868) | \
|
||||||
/* (1 << LMIC_REGION_us915) | */ \
|
/* (1 << LMIC_REGION_us915) | */ \
|
||||||
@ -161,6 +177,12 @@ Revision history:
|
|||||||
(1 << LMIC_REGION_in866) | \
|
(1 << LMIC_REGION_in866) | \
|
||||||
0)
|
0)
|
||||||
|
|
||||||
|
// a bitmask of` US-like regions -- these are regions with 64 fixed 125 kHz channels
|
||||||
|
// overlaid by 8 500 kHz channels. The channel frequencies can't be changed, but
|
||||||
|
// subsets of channels can be selected via masks.
|
||||||
|
//
|
||||||
|
// TODO(tmm@mcci.com) consider moving this block to a central file as it's not
|
||||||
|
// user-editable.
|
||||||
#define CFG_LMIC_US_like_MASK ( \
|
#define CFG_LMIC_US_like_MASK ( \
|
||||||
/* (1 << LMIC_REGION_eu868) | */ \
|
/* (1 << LMIC_REGION_eu868) | */ \
|
||||||
(1 << LMIC_REGION_us915) | \
|
(1 << LMIC_REGION_us915) | \
|
||||||
@ -173,9 +195,12 @@ Revision history:
|
|||||||
/* (1 << LMIC_REGION_in866) | */ \
|
/* (1 << LMIC_REGION_in866) | */ \
|
||||||
0)
|
0)
|
||||||
|
|
||||||
|
//
|
||||||
|
// booleans that are true if the configured region is EU-like or US-like.
|
||||||
|
// TODO(tmm@mcci.com) consider moving this block to a central file as it's not
|
||||||
|
// user-editable.
|
||||||
|
//
|
||||||
#define CFG_LMIC_EU_like (!!(CFG_LMIC_REGION_MASK & CFG_LMIC_EU_like_MASK))
|
#define CFG_LMIC_EU_like (!!(CFG_LMIC_REGION_MASK & CFG_LMIC_EU_like_MASK))
|
||||||
#define CFG_LMIC_US_like (!!(CFG_LMIC_REGION_MASK & CFG_LMIC_US_like_MASK))
|
#define CFG_LMIC_US_like (!!(CFG_LMIC_REGION_MASK & CFG_LMIC_US_like_MASK))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* _LMIC_CONFIG_PRECONDITIONS_H_ */
|
#endif /* _LMIC_CONFIG_PRECONDITIONS_H_ */
|
||||||
|
@ -33,9 +33,11 @@
|
|||||||
#if CFG_LMIC_EU_like
|
#if CFG_LMIC_EU_like
|
||||||
|
|
||||||
void LMIC_enableSubBand(u1_t band) {
|
void LMIC_enableSubBand(u1_t band) {
|
||||||
|
LMIC_API_PARAMETER(band);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LMIC_disableSubBand(u1_t band) {
|
void LMIC_disableSubBand(u1_t band) {
|
||||||
|
LMIC_API_PARAMETER(band);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LMIC_disableChannel(u1_t channel) {
|
void LMIC_disableChannel(u1_t channel) {
|
||||||
@ -46,6 +48,7 @@ void LMIC_disableChannel(u1_t channel) {
|
|||||||
|
|
||||||
// this is a no-op provided for compatibilty
|
// this is a no-op provided for compatibilty
|
||||||
void LMIC_enableChannel(u1_t channel) {
|
void LMIC_enableChannel(u1_t channel) {
|
||||||
|
LMIC_API_PARAMETER(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
u1_t LMICeulike_mapChannels(u1_t chpage, u2_t chmap) {
|
u1_t LMICeulike_mapChannels(u1_t chpage, u2_t chmap) {
|
||||||
|
@ -95,6 +95,8 @@ static CONST_TABLE(u4_t, iniChannelFreq)[NUM_DEFAULT_CHANNELS] = {
|
|||||||
|
|
||||||
// india ignores join, becuase the channel setup is the same either way.
|
// india ignores join, becuase the channel setup is the same either way.
|
||||||
void LMICin866_initDefaultChannels(bit_t join) {
|
void LMICin866_initDefaultChannels(bit_t join) {
|
||||||
|
LMIC_API_PARAMETER(join);
|
||||||
|
|
||||||
os_clearMem(&LMIC.channelFreq, sizeof(LMIC.channelFreq));
|
os_clearMem(&LMIC.channelFreq, sizeof(LMIC.channelFreq));
|
||||||
os_clearMem(&LMIC.channelDrMap, sizeof(LMIC.channelDrMap));
|
os_clearMem(&LMIC.channelDrMap, sizeof(LMIC.channelDrMap));
|
||||||
os_clearMem(&LMIC.bands, sizeof(LMIC.bands));
|
os_clearMem(&LMIC.bands, sizeof(LMIC.bands));
|
||||||
|
@ -87,6 +87,8 @@ u4_t LMICus915_convFreq(xref2cu1_t ptr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) {
|
bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) {
|
||||||
|
LMIC_API_PARAMETER(band);
|
||||||
|
|
||||||
if (chidx < 72 || chidx >= 72 + MAX_XCHANNELS)
|
if (chidx < 72 || chidx >= 72 + MAX_XCHANNELS)
|
||||||
return 0; // channels 0..71 are hardwired
|
return 0; // channels 0..71 are hardwired
|
||||||
LMIC.xchFreq[chidx - 72] = freq;
|
LMIC.xchFreq[chidx - 72] = freq;
|
||||||
|
@ -71,12 +71,18 @@ static void setNextChannel(uint start, uint end, uint count) {
|
|||||||
|
|
||||||
|
|
||||||
bit_t LMIC_setupBand(u1_t bandidx, s1_t txpow, u2_t txcap) {
|
bit_t LMIC_setupBand(u1_t bandidx, s1_t txpow, u2_t txcap) {
|
||||||
|
LMIC_API_PARAMETER(bandidx);
|
||||||
|
LMIC_API_PARAMETER(txpow);
|
||||||
|
LMIC_API_PARAMETER(txcap);
|
||||||
|
|
||||||
// nothing; just succeed.
|
// nothing; just succeed.
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void LMICuslike_initDefaultChannels(bit_t fJoin) {
|
void LMICuslike_initDefaultChannels(bit_t fJoin) {
|
||||||
|
LMIC_API_PARAMETER(fJoin);
|
||||||
|
|
||||||
// things work the same for join as normal.
|
// things work the same for join as normal.
|
||||||
for (u1_t i = 0; i<4; i++)
|
for (u1_t i = 0; i<4; i++)
|
||||||
LMIC.channelMap[i] = 0xFFFF;
|
LMIC.channelMap[i] = 0xFFFF;
|
||||||
|
@ -449,6 +449,7 @@ enum {
|
|||||||
MCMD_RXTimingSetupAns = 0x08, // : -
|
MCMD_RXTimingSetupAns = 0x08, // : -
|
||||||
MCMD_TxParamSetupAns = 0x09, // : -
|
MCMD_TxParamSetupAns = 0x09, // : -
|
||||||
MCMD_DIChannelAns = 0x0A, // : u1: [7-2]:RFU 1:exists 0:OK
|
MCMD_DIChannelAns = 0x0A, // : u1: [7-2]:RFU 1:exists 0:OK
|
||||||
|
MCMD_DeviceTimeReq = 0x0D,
|
||||||
|
|
||||||
// Class B
|
// Class B
|
||||||
MCMD_PING_IND = 0x10, // - pingability indic : u1: 7=RFU, 6-4:interval, 3-0:datarate
|
MCMD_PING_IND = 0x10, // - pingability indic : u1: 7=RFU, 6-4:interval, 3-0:datarate
|
||||||
@ -468,6 +469,7 @@ enum {
|
|||||||
MCMD_RXTimingSetupReq = 0x08, // : u1: [7-4]:RFU [3-0]: Delay 1-15s (0 => 1)
|
MCMD_RXTimingSetupReq = 0x08, // : u1: [7-4]:RFU [3-0]: Delay 1-15s (0 => 1)
|
||||||
MCMD_TxParamSetupReq = 0x09, // : u1: [7-6]:RFU [5:4]: dl dwell/ul dwell [3:0] max EIRP
|
MCMD_TxParamSetupReq = 0x09, // : u1: [7-6]:RFU [5:4]: dl dwell/ul dwell [3:0] max EIRP
|
||||||
MCMD_DIChannelReq = 0x0A, // : u1: channel, u3: frequency
|
MCMD_DIChannelReq = 0x0A, // : u1: channel, u3: frequency
|
||||||
|
MCMD_DeviceTimeAns = 0x0D,
|
||||||
|
|
||||||
// Class B
|
// Class B
|
||||||
MCMD_PING_SET = 0x11, // set ping freq : u3: freq
|
MCMD_PING_SET = 0x11, // set ping freq : u3: freq
|
||||||
@ -583,33 +585,33 @@ typedef u4_t devaddr_t;
|
|||||||
// RX quality (device)
|
// RX quality (device)
|
||||||
enum { RSSI_OFF=64, SNR_SCALEUP=4 };
|
enum { RSSI_OFF=64, SNR_SCALEUP=4 };
|
||||||
|
|
||||||
inline sf_t getSf (rps_t params) { return (sf_t)(params & 0x7); }
|
static inline sf_t getSf (rps_t params) { return (sf_t)(params & 0x7); }
|
||||||
inline rps_t setSf (rps_t params, sf_t sf) { return (rps_t)((params & ~0x7) | sf); }
|
static inline rps_t setSf (rps_t params, sf_t sf) { return (rps_t)((params & ~0x7) | sf); }
|
||||||
inline bw_t getBw (rps_t params) { return (bw_t)((params >> 3) & 0x3); }
|
static inline bw_t getBw (rps_t params) { return (bw_t)((params >> 3) & 0x3); }
|
||||||
inline rps_t setBw (rps_t params, bw_t cr) { return (rps_t)((params & ~0x18) | (cr<<3)); }
|
static inline rps_t setBw (rps_t params, bw_t cr) { return (rps_t)((params & ~0x18) | (cr<<3)); }
|
||||||
inline cr_t getCr (rps_t params) { return (cr_t)((params >> 5) & 0x3); }
|
static inline cr_t getCr (rps_t params) { return (cr_t)((params >> 5) & 0x3); }
|
||||||
inline rps_t setCr (rps_t params, cr_t cr) { return (rps_t)((params & ~0x60) | (cr<<5)); }
|
static inline rps_t setCr (rps_t params, cr_t cr) { return (rps_t)((params & ~0x60) | (cr<<5)); }
|
||||||
inline int getNocrc(rps_t params) { return ((params >> 7) & 0x1); }
|
static inline int getNocrc(rps_t params) { return ((params >> 7) & 0x1); }
|
||||||
inline rps_t setNocrc(rps_t params, int nocrc) { return (rps_t)((params & ~0x80) | (nocrc<<7)); }
|
static inline rps_t setNocrc(rps_t params, int nocrc) { return (rps_t)((params & ~0x80) | (nocrc<<7)); }
|
||||||
inline int getIh (rps_t params) { return ((params >> 8) & 0xFF); }
|
static inline int getIh (rps_t params) { return ((params >> 8) & 0xFF); }
|
||||||
inline rps_t setIh (rps_t params, int ih) { return (rps_t)((params & ~0xFF00) | (ih<<8)); }
|
static inline rps_t setIh (rps_t params, int ih) { return (rps_t)((params & ~0xFF00) | (ih<<8)); }
|
||||||
inline rps_t makeRps (sf_t sf, bw_t bw, cr_t cr, int ih, int nocrc) {
|
static inline rps_t makeRps (sf_t sf, bw_t bw, cr_t cr, int ih, int nocrc) {
|
||||||
return sf | (bw<<3) | (cr<<5) | (nocrc?(1<<7):0) | ((ih&0xFF)<<8);
|
return sf | (bw<<3) | (cr<<5) | (nocrc?(1<<7):0) | ((ih&0xFF)<<8);
|
||||||
}
|
}
|
||||||
#define MAKERPS(sf,bw,cr,ih,nocrc) ((rps_t)((sf) | ((bw)<<3) | ((cr)<<5) | ((nocrc)?(1<<7):0) | ((ih&0xFF)<<8)))
|
#define MAKERPS(sf,bw,cr,ih,nocrc) ((rps_t)((sf) | ((bw)<<3) | ((cr)<<5) | ((nocrc)?(1<<7):0) | ((ih&0xFF)<<8)))
|
||||||
// Two frames with params r1/r2 would interfere on air: same SFx + BWx
|
// Two frames with params r1/r2 would interfere on air: same SFx + BWx
|
||||||
inline int sameSfBw(rps_t r1, rps_t r2) { return ((r1^r2)&0x1F) == 0; }
|
static inline int sameSfBw(rps_t r1, rps_t r2) { return ((r1^r2)&0x1F) == 0; }
|
||||||
|
|
||||||
extern CONST_TABLE(u1_t, _DR2RPS_CRC)[];
|
extern CONST_TABLE(u1_t, _DR2RPS_CRC)[];
|
||||||
inline rps_t updr2rps (dr_t dr) { return (rps_t)TABLE_GET_U1(_DR2RPS_CRC, dr+1); }
|
static inline rps_t updr2rps (dr_t dr) { return (rps_t)TABLE_GET_U1(_DR2RPS_CRC, dr+1); }
|
||||||
inline rps_t dndr2rps (dr_t dr) { return setNocrc(updr2rps(dr),1); }
|
static inline rps_t dndr2rps (dr_t dr) { return setNocrc(updr2rps(dr),1); }
|
||||||
inline int isFasterDR (dr_t dr1, dr_t dr2) { return dr1 > dr2; }
|
static inline int isFasterDR (dr_t dr1, dr_t dr2) { return dr1 > dr2; }
|
||||||
inline int isSlowerDR (dr_t dr1, dr_t dr2) { return dr1 < dr2; }
|
static inline int isSlowerDR (dr_t dr1, dr_t dr2) { return dr1 < dr2; }
|
||||||
inline dr_t incDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr+2)==ILLEGAL_RPS ? dr : (dr_t)(dr+1); } // increase data rate
|
static inline dr_t incDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr+2)==ILLEGAL_RPS ? dr : (dr_t)(dr+1); } // increase data rate
|
||||||
inline dr_t decDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr )==ILLEGAL_RPS ? dr : (dr_t)(dr-1); } // decrease data rate
|
static inline dr_t decDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr )==ILLEGAL_RPS ? dr : (dr_t)(dr-1); } // decrease data rate
|
||||||
inline dr_t assertDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr+1)==ILLEGAL_RPS ? DR_DFLTMIN : dr; } // force into a valid DR
|
static inline dr_t assertDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr+1)==ILLEGAL_RPS ? (dr_t)DR_DFLTMIN : dr; } // force into a valid DR
|
||||||
inline bit_t validDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr+1)!=ILLEGAL_RPS; } // in range
|
static inline bit_t validDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr+1)!=ILLEGAL_RPS; } // in range
|
||||||
inline dr_t lowerDR (dr_t dr, u1_t n) { while(n--){dr=decDR(dr);} return dr; } // decrease data rate by n steps
|
static inline dr_t lowerDR (dr_t dr, u1_t n) { while(n--){dr=decDR(dr);} return dr; } // decrease data rate by n steps
|
||||||
|
|
||||||
//
|
//
|
||||||
// BEG: Keep in sync with lorabase.hpp
|
// BEG: Keep in sync with lorabase.hpp
|
||||||
|
@ -71,7 +71,11 @@ static int unlinkjob (osjob_t** pnext, osjob_t* job) {
|
|||||||
// clear scheduled job
|
// clear scheduled job
|
||||||
void os_clearCallback (osjob_t* job) {
|
void os_clearCallback (osjob_t* job) {
|
||||||
hal_disableIRQs();
|
hal_disableIRQs();
|
||||||
unlinkjob(&OS.scheduledjobs, job) || unlinkjob(&OS.runnablejobs, job);
|
|
||||||
|
// if it's not in the scheduled jobs, look in the runnable...
|
||||||
|
if (! unlinkjob(&OS.scheduledjobs, job))
|
||||||
|
unlinkjob(&OS.runnablejobs, job);
|
||||||
|
|
||||||
hal_enableIRQs();
|
hal_enableIRQs();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,6 +75,14 @@ typedef const u1_t* xref2cu1_t;
|
|||||||
typedef u1_t* xref2u1_t;
|
typedef u1_t* xref2u1_t;
|
||||||
typedef s4_t ostime_t;
|
typedef s4_t ostime_t;
|
||||||
|
|
||||||
|
// int32_t == s4_t is long on some platforms; and someday
|
||||||
|
// we will want 64-bit ostime_t. So, we will use a macro for the
|
||||||
|
// print formatting of ostime_t.
|
||||||
|
#ifndef LMIC_PRId_ostime_t
|
||||||
|
# include <inttypes.h>
|
||||||
|
# define LMIC_PRId_ostime_t PRId32
|
||||||
|
#endif
|
||||||
|
|
||||||
#define TYPEDEF_xref2rps_t typedef rps_t* xref2rps_t
|
#define TYPEDEF_xref2rps_t typedef rps_t* xref2rps_t
|
||||||
#define TYPEDEF_xref2rxsched_t typedef rxsched_t* xref2rxsched_t
|
#define TYPEDEF_xref2rxsched_t typedef rxsched_t* xref2rxsched_t
|
||||||
#define TYPEDEF_xref2chnldef_t typedef chnldef_t* xref2chnldef_t
|
#define TYPEDEF_xref2chnldef_t typedef chnldef_t* xref2chnldef_t
|
||||||
@ -83,6 +91,105 @@ typedef s4_t ostime_t;
|
|||||||
|
|
||||||
#define SIZEOFEXPR(x) sizeof(x)
|
#define SIZEOFEXPR(x) sizeof(x)
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
// Annotations to avoid various "unused" warnings. These must appear as a
|
||||||
|
// statement in the function body; the macro annotates the variable to quiet
|
||||||
|
// compiler warnings. The way this is done is compiler-specific, and so these
|
||||||
|
// definitions are fall-backs, which might be overridden.
|
||||||
|
//
|
||||||
|
// Although these are all similar, we don't want extra macro expansions,
|
||||||
|
// so we define each one explicitly rather than relying on a common macro.
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// signal that a parameter is intentionally unused.
|
||||||
|
#ifndef LMIC_UNREFERENCED_PARAMETER
|
||||||
|
# define LMIC_UNREFERENCED_PARAMETER(v) do { (void) (v); } while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// an API parameter is a parameter that is required by an API definition, but
|
||||||
|
// happens to be unreferenced in this implementation. This is a stronger
|
||||||
|
// assertion than LMIC_UNREFERENCED_PARAMETER(): this parameter is here
|
||||||
|
// becuase of an API contract, but we have no use for it in this function.
|
||||||
|
#ifndef LMIC_API_PARAMETER
|
||||||
|
# define LMIC_API_PARAMETER(v) do { (void) (v); } while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// an intentionally-unreferenced variable.
|
||||||
|
#ifndef LMIC_UNREFERENCED_VARIABLE
|
||||||
|
# define LMIC_UNREFERENCED_VARIABLE(v) do { (void) (v); } while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// we have three (!) debug levels (LMIC_DEBUG_LEVEL > 0, LMIC_DEBUG_LEVEL > 1,
|
||||||
|
// and LMIC_X_DEBUG_LEVEL > 0. In each case we might have parameters or
|
||||||
|
// or varables that are only refereneced at the target debug level.
|
||||||
|
|
||||||
|
// Parameter referenced only if debugging at level > 0.
|
||||||
|
#ifndef LMIC_DEBUG1_PARAMETER
|
||||||
|
# if LMIC_DEBUG_LEVEL > 0
|
||||||
|
# define LMIC_DEBUG1_PARAMETER(v) do { ; } while (0)
|
||||||
|
# else
|
||||||
|
# define LMIC_DEBUG1_PARAMETER(v) do { (void) (v); } while (0)
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// variable referenced only if debugging at level > 0
|
||||||
|
#ifndef LMIC_DEBUG1_VARIABLE
|
||||||
|
# if LMIC_DEBUG_LEVEL > 0
|
||||||
|
# define LMIC_DEBUG1_VARIABLE(v) do { ; } while (0)
|
||||||
|
# else
|
||||||
|
# define LMIC_DEBUG1_VARIABLE(v) do { (void) (v); } while (0)
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// parameter referenced only if debugging at level > 1
|
||||||
|
#ifndef LMIC_DEBUG2_PARAMETER
|
||||||
|
# if LMIC_DEBUG_LEVEL > 1
|
||||||
|
# define LMIC_DEBUG2_PARAMETER(v) do { ; } while (0)
|
||||||
|
# else
|
||||||
|
# define LMIC_DEBUG2_PARAMETER(v) do { (void) (v); } while (0)
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// variable referenced only if debugging at level > 1
|
||||||
|
#ifndef LMIC_DEBUG2_VARIABLE
|
||||||
|
# if LMIC_DEBUG_LEVEL > 1
|
||||||
|
# define LMIC_DEBUG2_VARIABLE(v) do { ; } while (0)
|
||||||
|
# else
|
||||||
|
# define LMIC_DEBUG2_VARIABLE(v) do { (void) (v); } while (0)
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// parameter referenced only if LMIC_X_DEBUG_LEVEL > 0
|
||||||
|
#ifndef LMIC_X_DEBUG_PARAMETER
|
||||||
|
# if LMIC_X_DEBUG_LEVEL > 0
|
||||||
|
# define LMIC_X_DEBUG_PARAMETER(v) do { ; } while (0)
|
||||||
|
# else
|
||||||
|
# define LMIC_X_DEBUG_PARAMETER(v) do { (void) (v); } while (0)
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// variable referenced only if LMIC_X_DEBUG_LEVEL > 0
|
||||||
|
#ifndef LMIC_X_DEBUG_VARIABLE
|
||||||
|
# if LMIC_X_DEBUG_LEVEL > 0
|
||||||
|
# define LMIC_X_DEBUG_VARIABLE(v) do { ; } while (0)
|
||||||
|
# else
|
||||||
|
# define LMIC_X_DEBUG_VARIABLE(v) do { (void) (v); } while (0)
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// parameter referenced only if EV() macro is enabled (which it never is)
|
||||||
|
// TODO(tmm@mcci.com) take out the EV() framework as it reuqires C++, and
|
||||||
|
// this code is really C-99 to its bones.
|
||||||
|
#ifndef LMIC_EV_PARAMETER
|
||||||
|
# define LMIC_EV_PARAMETER(v) do { (void) (v); } while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// variable referenced only if EV() macro is defined.
|
||||||
|
#ifndef LMIC_EV_VARIABLE
|
||||||
|
# define LMIC_EV_VARIABLE(v) do { (void) (v); } while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#define ON_LMIC_EVENT(ev) onEvent(ev)
|
#define ON_LMIC_EVENT(ev) onEvent(ev)
|
||||||
#define DECL_ON_LMIC_EVENT void onEvent(ev_t e)
|
#define DECL_ON_LMIC_EVENT void onEvent(ev_t e)
|
||||||
|
|
||||||
@ -258,7 +365,7 @@ u2_t os_crc16 (xref2cu1_t d, uint len);
|
|||||||
// progmem using pgm_read_xx, or accesses memory directly when the
|
// progmem using pgm_read_xx, or accesses memory directly when the
|
||||||
// index is a constant so gcc can optimize it away;
|
// index is a constant so gcc can optimize it away;
|
||||||
#define TABLE_GETTER(postfix, type, pgm_type) \
|
#define TABLE_GETTER(postfix, type, pgm_type) \
|
||||||
inline type table_get ## postfix(const type *table, size_t index) { \
|
static inline type table_get ## postfix(const type *table, size_t index) { \
|
||||||
if (__builtin_constant_p(table[index])) \
|
if (__builtin_constant_p(table[index])) \
|
||||||
return table[index]; \
|
return table[index]; \
|
||||||
return pgm_read_ ## pgm_type(&table[index]); \
|
return pgm_read_ ## pgm_type(&table[index]); \
|
||||||
@ -278,13 +385,13 @@ u2_t os_crc16 (xref2cu1_t d, uint len);
|
|||||||
// For AVR, store constants in PROGMEM, saving on RAM usage
|
// For AVR, store constants in PROGMEM, saving on RAM usage
|
||||||
#define CONST_TABLE(type, name) const type PROGMEM RESOLVE_TABLE(name)
|
#define CONST_TABLE(type, name) const type PROGMEM RESOLVE_TABLE(name)
|
||||||
#else
|
#else
|
||||||
inline u1_t table_get_u1(const u1_t *table, size_t index) { return table[index]; }
|
static inline u1_t table_get_u1(const u1_t *table, size_t index) { return table[index]; }
|
||||||
inline s1_t table_get_s1(const s1_t *table, size_t index) { return table[index]; }
|
static inline s1_t table_get_s1(const s1_t *table, size_t index) { return table[index]; }
|
||||||
inline u2_t table_get_u2(const u2_t *table, size_t index) { return table[index]; }
|
static inline u2_t table_get_u2(const u2_t *table, size_t index) { return table[index]; }
|
||||||
inline s2_t table_get_s2(const s2_t *table, size_t index) { return table[index]; }
|
static inline s2_t table_get_s2(const s2_t *table, size_t index) { return table[index]; }
|
||||||
inline u4_t table_get_u4(const u4_t *table, size_t index) { return table[index]; }
|
static inline u4_t table_get_u4(const u4_t *table, size_t index) { return table[index]; }
|
||||||
inline s4_t table_get_s4(const s4_t *table, size_t index) { return table[index]; }
|
static inline s4_t table_get_s4(const s4_t *table, size_t index) { return table[index]; }
|
||||||
inline ostime_t table_get_ostime(const ostime_t *table, size_t index) { return table[index]; }
|
static inline ostime_t table_get_ostime(const ostime_t *table, size_t index) { return table[index]; }
|
||||||
|
|
||||||
// Declare a table
|
// Declare a table
|
||||||
#define CONST_TABLE(type, name) const type RESOLVE_TABLE(name)
|
#define CONST_TABLE(type, name) const type RESOLVE_TABLE(name)
|
||||||
|
@ -549,7 +549,7 @@ static void txlora () {
|
|||||||
u1_t sf = getSf(LMIC.rps) + 6; // 1 == SF7
|
u1_t sf = getSf(LMIC.rps) + 6; // 1 == SF7
|
||||||
u1_t bw = getBw(LMIC.rps);
|
u1_t bw = getBw(LMIC.rps);
|
||||||
u1_t cr = getCr(LMIC.rps);
|
u1_t cr = getCr(LMIC.rps);
|
||||||
LMIC_DEBUG_PRINTF("%lu: TXMODE, freq=%lu, len=%d, SF=%d, BW=%d, CR=4/%d, IH=%d\n",
|
LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": TXMODE, freq=%"PRIu32", len=%d, SF=%d, BW=%d, CR=4/%d, IH=%d\n",
|
||||||
os_getTime(), LMIC.freq, LMIC.dataLen, sf,
|
os_getTime(), LMIC.freq, LMIC.dataLen, sf,
|
||||||
bw == BW125 ? 125 : (bw == BW250 ? 250 : 500),
|
bw == BW125 ? 125 : (bw == BW250 ? 250 : 500),
|
||||||
cr == CR_4_5 ? 5 : (cr == CR_4_6 ? 6 : (cr == CR_4_7 ? 7 : 8)),
|
cr == CR_4_5 ? 5 : (cr == CR_4_6 ? 6 : (cr == CR_4_7 ? 7 : 8)),
|
||||||
@ -656,7 +656,7 @@ static void rxlora (u1_t rxmode) {
|
|||||||
opmode(OPMODE_RX_SINGLE);
|
opmode(OPMODE_RX_SINGLE);
|
||||||
#if LMIC_DEBUG_LEVEL > 0
|
#if LMIC_DEBUG_LEVEL > 0
|
||||||
ostime_t now = os_getTime();
|
ostime_t now = os_getTime();
|
||||||
LMIC_DEBUG_PRINTF("start single rx: now-rxtime: %lu\n", now - LMIC.rxtime);
|
LMIC_DEBUG_PRINTF("start single rx: now-rxtime: %"LMIC_PRId_ostime_t"\n", now - LMIC.rxtime);
|
||||||
#endif
|
#endif
|
||||||
} else { // continous rx (scan or rssi)
|
} else { // continous rx (scan or rssi)
|
||||||
opmode(OPMODE_RX);
|
opmode(OPMODE_RX);
|
||||||
@ -669,7 +669,7 @@ static void rxlora (u1_t rxmode) {
|
|||||||
u1_t sf = getSf(LMIC.rps) + 6; // 1 == SF7
|
u1_t sf = getSf(LMIC.rps) + 6; // 1 == SF7
|
||||||
u1_t bw = getBw(LMIC.rps);
|
u1_t bw = getBw(LMIC.rps);
|
||||||
u1_t cr = getCr(LMIC.rps);
|
u1_t cr = getCr(LMIC.rps);
|
||||||
LMIC_DEBUG_PRINTF("%lu: %s, freq=%lu, SF=%d, BW=%d, CR=4/%d, IH=%d\n",
|
LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": %s, freq=%"PRIu32", SF=%d, BW=%d, CR=4/%d, IH=%d\n",
|
||||||
os_getTime(),
|
os_getTime(),
|
||||||
rxmode == RXMODE_SINGLE ? "RXMODE_SINGLE" : (rxmode == RXMODE_SCAN ? "RXMODE_SCAN" : "UNKNOWN_RX"),
|
rxmode == RXMODE_SINGLE ? "RXMODE_SINGLE" : (rxmode == RXMODE_SCAN ? "RXMODE_SCAN" : "UNKNOWN_RX"),
|
||||||
LMIC.freq, sf,
|
LMIC.freq, sf,
|
||||||
@ -921,7 +921,12 @@ void radio_irq_handler (u1_t dio) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void radio_irq_handler_v2 (u1_t dio, ostime_t now) {
|
void radio_irq_handler_v2 (u1_t dio, ostime_t now) {
|
||||||
|
LMIC_API_PARAMETER(dio);
|
||||||
|
|
||||||
#if CFG_TxContinuousMode
|
#if CFG_TxContinuousMode
|
||||||
|
// in continuous mode, we don't use the now parameter.
|
||||||
|
LMIC_UNREFERENCED_PARAMETER(now);
|
||||||
|
|
||||||
// clear radio IRQ flags
|
// clear radio IRQ flags
|
||||||
writeReg(LORARegIrqFlags, 0xFF);
|
writeReg(LORARegIrqFlags, 0xFF);
|
||||||
u1_t p = readReg(LORARegFifoAddrPtr);
|
u1_t p = readReg(LORARegFifoAddrPtr);
|
||||||
@ -964,7 +969,8 @@ void radio_irq_handler_v2 (u1_t dio, ostime_t now) {
|
|||||||
LMIC.dataLen = 0;
|
LMIC.dataLen = 0;
|
||||||
#if LMIC_DEBUG_LEVEL > 0
|
#if LMIC_DEBUG_LEVEL > 0
|
||||||
ostime_t now2 = os_getTime();
|
ostime_t now2 = os_getTime();
|
||||||
LMIC_DEBUG_PRINTF("rxtimeout: entry: %lu rxtime: %lu entry-rxtime: %lu now-entry: %lu rxtime-txend: %lu\n", entry, LMIC.rxtime, entry - LMIC.rxtime, now2 - entry, LMIC.rxtime-LMIC.txend);
|
LMIC_DEBUG_PRINTF("rxtimeout: entry: %"LMIC_PRId_ostime_t" rxtime: %"LMIC_PRId_ostime_t" entry-rxtime: %"LMIC_PRId_ostime_t" now-entry: %"LMIC_PRId_ostime_t" rxtime-txend: %"LMIC_PRId_ostime_t"\n", entry,
|
||||||
|
LMIC.rxtime, entry - LMIC.rxtime, now2 - entry, LMIC.rxtime-LMIC.txend);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
// mask all radio IRQs
|
// mask all radio IRQs
|
||||||
|
@ -29,7 +29,7 @@ description = Paxcounter is a proof-of-concept ESP32 device for metering passeng
|
|||||||
|
|
||||||
[common]
|
[common]
|
||||||
; for release_version use max. 10 chars total, use any decimal format like "a.b.c"
|
; for release_version use max. 10 chars total, use any decimal format like "a.b.c"
|
||||||
release_version = 1.6.54
|
release_version = 1.6.82
|
||||||
; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running!
|
; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running!
|
||||||
; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose
|
; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose
|
||||||
debug_level = 0
|
debug_level = 0
|
||||||
@ -38,28 +38,40 @@ upload_protocol = esptool
|
|||||||
;upload_protocol = custom
|
;upload_protocol = custom
|
||||||
extra_scripts = pre:build.py
|
extra_scripts = pre:build.py
|
||||||
keyfile = ota.conf
|
keyfile = ota.conf
|
||||||
platform_espressif32 = espressif32@1.4.0
|
platform_espressif32 = espressif32@1.5.0
|
||||||
board_build.partitions = min_spiffs.csv
|
board_build.partitions = min_spiffs.csv
|
||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
lib_deps_all =
|
lib_deps_basic =
|
||||||
ArduinoJson@^5.13.1
|
ArduinoJson@^5.13.1
|
||||||
|
Time@>=1.5
|
||||||
lib_deps_lora =
|
lib_deps_lora =
|
||||||
; MCCI LoRaWAN LMIC library@^2.2.2
|
; MCCI LoRaWAN LMIC library@^2.2.2
|
||||||
lib_deps_display =
|
lib_deps_display =
|
||||||
U8g2@>=2.23.16
|
U8g2@>=2.25.0
|
||||||
lib_deps_rgbled =
|
lib_deps_rgbled =
|
||||||
SmartLeds@>=1.1.3
|
SmartLeds@>=1.1.3
|
||||||
lib_deps_gps =
|
lib_deps_gps =
|
||||||
TinyGPSPlus@>=1.0.2
|
TinyGPSPlus@>=1.0.2
|
||||||
Time@>=1.5
|
lib_deps_sensors =
|
||||||
|
Adafruit Unified Sensor@^1.0.2
|
||||||
|
Adafruit BME680 Library@^1.0.7
|
||||||
|
lib_deps_all =
|
||||||
|
${common.lib_deps_basic}
|
||||||
|
${common.lib_deps_lora}
|
||||||
|
${common.lib_deps_display}
|
||||||
|
${common.lib_deps_rgbled}
|
||||||
|
${common.lib_deps_gps}
|
||||||
|
${common.lib_deps_sensors}
|
||||||
build_flags =
|
build_flags =
|
||||||
-include "src/hal/${PIOENV}.h"
|
-include src/hal/${PIOENV}.h
|
||||||
-include "src/paxcounter.conf"
|
-include src/paxcounter.conf
|
||||||
-w
|
-w
|
||||||
'-DARDUINO_LMIC_PROJECT_CONFIG_H=../../../src/lmic_config.h'
|
;'-D ARDUINO_LMIC_PROJECT_CONFIG_H="/$PROJECTSRC_DIR/lmic_config.h"'
|
||||||
'-DCORE_DEBUG_LEVEL=${common.debug_level}'
|
'-D ARDUINO_LMIC_PROJECT_CONFIG_H=../../../src/lmic_config.h'
|
||||||
'-DBINTRAY_PACKAGE="${PIOENV}"'
|
'-D CORE_DEBUG_LEVEL=${common.debug_level}'
|
||||||
'-DPROGVERSION="${common.release_version}"'
|
'-D LOG_LOCAL_LEVEL=${common.debug_level}'
|
||||||
|
'-D BINTRAY_PACKAGE="${PIOENV}"'
|
||||||
|
'-D PROGVERSION="${common.release_version}"'
|
||||||
|
|
||||||
[env:ebox]
|
[env:ebox]
|
||||||
platform = ${common.platform_espressif32}
|
platform = ${common.platform_espressif32}
|
||||||
@ -68,7 +80,7 @@ board = heltec_wifi_lora_32
|
|||||||
board_build.partitions = ${common.board_build.partitions}
|
board_build.partitions = ${common.board_build.partitions}
|
||||||
upload_speed = 115200
|
upload_speed = 115200
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${common.lib_deps_all}
|
${common.lib_deps_basic}
|
||||||
${common.lib_deps_lora}
|
${common.lib_deps_lora}
|
||||||
build_flags =
|
build_flags =
|
||||||
${common.build_flags}
|
${common.build_flags}
|
||||||
@ -83,8 +95,9 @@ board = heltec_wifi_lora_32
|
|||||||
board_build.partitions = ${common.board_build.partitions}
|
board_build.partitions = ${common.board_build.partitions}
|
||||||
upload_speed = 115200
|
upload_speed = 115200
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${common.lib_deps_all}
|
${common.lib_deps_basic}
|
||||||
${common.lib_deps_lora}
|
${common.lib_deps_lora}
|
||||||
|
${common.lib_deps_rgbled}
|
||||||
build_flags =
|
build_flags =
|
||||||
${common.build_flags}
|
${common.build_flags}
|
||||||
upload_protocol = ${common.upload_protocol}
|
upload_protocol = ${common.upload_protocol}
|
||||||
@ -98,7 +111,7 @@ board = heltec_wifi_lora_32
|
|||||||
board_build.partitions = ${common.board_build.partitions}
|
board_build.partitions = ${common.board_build.partitions}
|
||||||
upload_speed = 921600
|
upload_speed = 921600
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${common.lib_deps_all}
|
${common.lib_deps_basic}
|
||||||
${common.lib_deps_lora}
|
${common.lib_deps_lora}
|
||||||
${common.lib_deps_display}
|
${common.lib_deps_display}
|
||||||
build_flags =
|
build_flags =
|
||||||
@ -114,7 +127,7 @@ board = heltec_wifi_lora_32
|
|||||||
board_build.partitions = ${common.board_build.partitions}
|
board_build.partitions = ${common.board_build.partitions}
|
||||||
upload_speed = 921600
|
upload_speed = 921600
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${common.lib_deps_all}
|
${common.lib_deps_basic}
|
||||||
${common.lib_deps_lora}
|
${common.lib_deps_lora}
|
||||||
${common.lib_deps_display}
|
${common.lib_deps_display}
|
||||||
build_flags =
|
build_flags =
|
||||||
@ -130,7 +143,7 @@ board = ttgo-lora32-v1
|
|||||||
board_build.partitions = ${common.board_build.partitions}
|
board_build.partitions = ${common.board_build.partitions}
|
||||||
upload_speed = 115200
|
upload_speed = 115200
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${common.lib_deps_all}
|
${common.lib_deps_basic}
|
||||||
${common.lib_deps_lora}
|
${common.lib_deps_lora}
|
||||||
${common.lib_deps_display}
|
${common.lib_deps_display}
|
||||||
build_flags =
|
build_flags =
|
||||||
@ -146,7 +159,7 @@ board = ttgo-lora32-v1
|
|||||||
board_build.partitions = ${common.board_build.partitions}
|
board_build.partitions = ${common.board_build.partitions}
|
||||||
upload_speed = 921600
|
upload_speed = 921600
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${common.lib_deps_all}
|
${common.lib_deps_basic}
|
||||||
${common.lib_deps_lora}
|
${common.lib_deps_lora}
|
||||||
${common.lib_deps_display}
|
${common.lib_deps_display}
|
||||||
build_flags =
|
build_flags =
|
||||||
@ -162,7 +175,7 @@ board = heltec_wifi_lora_32
|
|||||||
board_build.partitions = ${common.board_build.partitions}
|
board_build.partitions = ${common.board_build.partitions}
|
||||||
upload_speed = 921600
|
upload_speed = 921600
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${common.lib_deps_all}
|
${common.lib_deps_basic}
|
||||||
${common.lib_deps_lora}
|
${common.lib_deps_lora}
|
||||||
${common.lib_deps_display}
|
${common.lib_deps_display}
|
||||||
build_flags =
|
build_flags =
|
||||||
@ -178,7 +191,7 @@ board = heltec_wifi_lora_32
|
|||||||
board_build.partitions = ${common.board_build.partitions}
|
board_build.partitions = ${common.board_build.partitions}
|
||||||
upload_speed = 921600
|
upload_speed = 921600
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${common.lib_deps_all}
|
${common.lib_deps_basic}
|
||||||
${common.lib_deps_lora}
|
${common.lib_deps_lora}
|
||||||
${common.lib_deps_display}
|
${common.lib_deps_display}
|
||||||
build_flags =
|
build_flags =
|
||||||
@ -194,7 +207,7 @@ board = heltec_wifi_lora_32
|
|||||||
board_build.partitions = ${common.board_build.partitions}
|
board_build.partitions = ${common.board_build.partitions}
|
||||||
upload_speed = 921600
|
upload_speed = 921600
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${common.lib_deps_all}
|
${common.lib_deps_basic}
|
||||||
${common.lib_deps_lora}
|
${common.lib_deps_lora}
|
||||||
${common.lib_deps_gps}
|
${common.lib_deps_gps}
|
||||||
build_flags =
|
build_flags =
|
||||||
@ -211,7 +224,7 @@ board = heltec_wifi_lora_32
|
|||||||
board_build.partitions = ${common.board_build.partitions}
|
board_build.partitions = ${common.board_build.partitions}
|
||||||
upload_speed = 921600
|
upload_speed = 921600
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${common.lib_deps_all}
|
${common.lib_deps_basic}
|
||||||
${common.lib_deps_lora}
|
${common.lib_deps_lora}
|
||||||
${common.lib_deps_rgbled}
|
${common.lib_deps_rgbled}
|
||||||
build_flags =
|
build_flags =
|
||||||
@ -227,7 +240,7 @@ board = heltec_wifi_lora_32
|
|||||||
board_build.partitions = ${common.board_build.partitions}
|
board_build.partitions = ${common.board_build.partitions}
|
||||||
upload_speed = 921600
|
upload_speed = 921600
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${common.lib_deps_all}
|
${common.lib_deps_basic}
|
||||||
${common.lib_deps_lora}
|
${common.lib_deps_lora}
|
||||||
${common.lib_deps_rgbled}
|
${common.lib_deps_rgbled}
|
||||||
${common.lib_deps_gps}
|
${common.lib_deps_gps}
|
||||||
@ -244,7 +257,7 @@ board = heltec_wifi_lora_32
|
|||||||
board_build.partitions = ${common.board_build.partitions}
|
board_build.partitions = ${common.board_build.partitions}
|
||||||
upload_speed = 921600
|
upload_speed = 921600
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${common.lib_deps_all}
|
${common.lib_deps_basic}
|
||||||
${common.lib_deps_lora}
|
${common.lib_deps_lora}
|
||||||
${common.lib_deps_rgbled}
|
${common.lib_deps_rgbled}
|
||||||
${common.lib_deps_gps}
|
${common.lib_deps_gps}
|
||||||
@ -262,7 +275,7 @@ board = lolin32
|
|||||||
board_build.partitions = ${common.board_build.partitions}
|
board_build.partitions = ${common.board_build.partitions}
|
||||||
upload_speed = 921600
|
upload_speed = 921600
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${common.lib_deps_all}
|
${common.lib_deps_basic}
|
||||||
${common.lib_deps_lora}
|
${common.lib_deps_lora}
|
||||||
${common.lib_deps_rgbled}
|
${common.lib_deps_rgbled}
|
||||||
build_flags =
|
build_flags =
|
||||||
@ -278,7 +291,7 @@ board = lolin32
|
|||||||
board_build.partitions = ${common.board_build.partitions}
|
board_build.partitions = ${common.board_build.partitions}
|
||||||
upload_speed = 921600
|
upload_speed = 921600
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${common.lib_deps_all}
|
${common.lib_deps_basic}
|
||||||
${common.lib_deps_lora}
|
${common.lib_deps_lora}
|
||||||
${common.lib_deps_rgbled}
|
${common.lib_deps_rgbled}
|
||||||
build_flags =
|
build_flags =
|
||||||
@ -294,7 +307,7 @@ board = lolin32
|
|||||||
board_build.partitions = ${common.board_build.partitions}
|
board_build.partitions = ${common.board_build.partitions}
|
||||||
upload_speed = 921600
|
upload_speed = 921600
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${common.lib_deps_all}
|
${common.lib_deps_basic}
|
||||||
${common.lib_deps_rgbled}
|
${common.lib_deps_rgbled}
|
||||||
build_flags =
|
build_flags =
|
||||||
${common.build_flags}
|
${common.build_flags}
|
||||||
@ -308,13 +321,8 @@ framework = arduino
|
|||||||
board = featheresp32
|
board = featheresp32
|
||||||
board_build.partitions = ${common.board_build.partitions}
|
board_build.partitions = ${common.board_build.partitions}
|
||||||
upload_speed = 921600
|
upload_speed = 921600
|
||||||
lib_deps =
|
lib_deps = ${common.lib_deps_all}
|
||||||
${common.lib_deps_all}
|
build_flags = ${common.build_flags}
|
||||||
${common.lib_deps_lora}
|
|
||||||
${common.lib_deps_rgbled}
|
|
||||||
${common.lib_deps_display}
|
|
||||||
build_flags =
|
|
||||||
${common.build_flags}
|
|
||||||
upload_protocol = ${common.upload_protocol}
|
upload_protocol = ${common.upload_protocol}
|
||||||
extra_scripts = ${common.extra_scripts}
|
extra_scripts = ${common.extra_scripts}
|
||||||
monitor_speed = ${common.monitor_speed}
|
monitor_speed = ${common.monitor_speed}
|
||||||
@ -325,14 +333,8 @@ framework = arduino
|
|||||||
board = heltec_wifi_lora_32
|
board = heltec_wifi_lora_32
|
||||||
board_build.partitions = ${common.board_build.partitions}
|
board_build.partitions = ${common.board_build.partitions}
|
||||||
upload_speed = 921600
|
upload_speed = 921600
|
||||||
lib_deps =
|
lib_deps = ${common.lib_deps_all}
|
||||||
${common.lib_deps_all}
|
build_flags = ${common.build_flags}
|
||||||
${common.lib_deps_lora}
|
|
||||||
${common.lib_deps_rgbled}
|
|
||||||
${common.lib_deps_gps}
|
|
||||||
${common.lib_deps_display}
|
|
||||||
build_flags =
|
|
||||||
${common.build_flags}
|
|
||||||
upload_protocol = ${common.upload_protocol}
|
upload_protocol = ${common.upload_protocol}
|
||||||
extra_scripts = ${common.extra_scripts}
|
extra_scripts = ${common.extra_scripts}
|
||||||
monitor_speed = ${common.monitor_speed}
|
monitor_speed = ${common.monitor_speed}
|
@ -41,6 +41,11 @@ function Decoder(bytes, port) {
|
|||||||
return decode(bytes, [uint8, uint8], ['rssi', 'beacon']);
|
return decode(bytes, [uint8, uint8], ['rssi', 'beacon']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (port === 7) {
|
||||||
|
// BME680 sensor data
|
||||||
|
return decode(bytes, [temperature, uint16, humidity, uint16], ['temperature', 'pressure', 'humidity', 'air']);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -132,7 +137,7 @@ var temperature = function (bytes) {
|
|||||||
if (isNegative) {
|
if (isNegative) {
|
||||||
t = -t;
|
t = -t;
|
||||||
}
|
}
|
||||||
return t / 1e2;
|
return +(t / 100).toFixed(1);
|
||||||
};
|
};
|
||||||
temperature.BYTES = 2;
|
temperature.BYTES = 2;
|
||||||
|
|
||||||
@ -142,7 +147,7 @@ var humidity = function (bytes) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var h = bytesToInt(bytes);
|
var h = bytesToInt(bytes);
|
||||||
return h / 1e2;
|
return +(h / 100).toFixed(1);
|
||||||
};
|
};
|
||||||
humidity.BYTES = 2;
|
humidity.BYTES = 2;
|
||||||
|
|
||||||
|
@ -41,6 +41,14 @@ function Decoder(bytes, port) {
|
|||||||
decoded.beacon = bytes[i++];
|
decoded.beacon = bytes[i++];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (port === 7) {
|
||||||
|
var i = 0;
|
||||||
|
decoded.temperature = ((bytes[i++] << 8) | bytes[i++]);
|
||||||
|
decoded.pressure = ((bytes[i++] << 8) | bytes[i++]);
|
||||||
|
decoded.humidity = ((bytes[i++] << 8) | bytes[i++]);
|
||||||
|
decoded.air = ((bytes[i++] << 8) | bytes[i++]);
|
||||||
|
}
|
||||||
|
|
||||||
return decoded;
|
return decoded;
|
||||||
|
|
||||||
}
|
}
|
@ -1,10 +1,9 @@
|
|||||||
#ifdef HAS_BATTERY_PROBE
|
|
||||||
|
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
|
|
||||||
// Local logging tag
|
// Local logging tag
|
||||||
static const char TAG[] = "main";
|
static const char TAG[] = "main";
|
||||||
|
|
||||||
|
#ifdef HAS_BATTERY_PROBE
|
||||||
esp_adc_cal_characteristics_t *adc_characs =
|
esp_adc_cal_characteristics_t *adc_characs =
|
||||||
(esp_adc_cal_characteristics_t *)calloc(
|
(esp_adc_cal_characteristics_t *)calloc(
|
||||||
1, sizeof(esp_adc_cal_characteristics_t));
|
1, sizeof(esp_adc_cal_characteristics_t));
|
||||||
@ -12,8 +11,10 @@ esp_adc_cal_characteristics_t *adc_characs =
|
|||||||
static const adc1_channel_t adc_channel = HAS_BATTERY_PROBE;
|
static const adc1_channel_t adc_channel = HAS_BATTERY_PROBE;
|
||||||
static const adc_atten_t atten = ADC_ATTEN_DB_11;
|
static const adc_atten_t atten = ADC_ATTEN_DB_11;
|
||||||
static const adc_unit_t unit = ADC_UNIT_1;
|
static const adc_unit_t unit = ADC_UNIT_1;
|
||||||
|
#endif
|
||||||
|
|
||||||
void calibrate_voltage(void) {
|
void calibrate_voltage(void) {
|
||||||
|
#ifdef HAS_BATTERY_PROBE
|
||||||
// configure ADC
|
// configure ADC
|
||||||
ESP_ERROR_CHECK(adc1_config_width(ADC_WIDTH_BIT_12));
|
ESP_ERROR_CHECK(adc1_config_width(ADC_WIDTH_BIT_12));
|
||||||
ESP_ERROR_CHECK(adc1_config_channel_atten(adc_channel, atten));
|
ESP_ERROR_CHECK(adc1_config_channel_atten(adc_channel, atten));
|
||||||
@ -30,9 +31,11 @@ void calibrate_voltage(void) {
|
|||||||
} else {
|
} else {
|
||||||
ESP_LOGI(TAG, "ADC characterization based on default reference voltage");
|
ESP_LOGI(TAG, "ADC characterization based on default reference voltage");
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t read_voltage() {
|
uint16_t read_voltage() {
|
||||||
|
#ifdef HAS_BATTERY_PROBE
|
||||||
// multisample ADC
|
// multisample ADC
|
||||||
uint32_t adc_reading = 0;
|
uint32_t adc_reading = 0;
|
||||||
for (int i = 0; i < NO_OF_SAMPLES; i++) {
|
for (int i = 0; i < NO_OF_SAMPLES; i++) {
|
||||||
@ -47,11 +50,17 @@ uint16_t read_voltage() {
|
|||||||
#endif
|
#endif
|
||||||
ESP_LOGD(TAG, "Raw: %d / Voltage: %dmV", adc_reading, voltage);
|
ESP_LOGD(TAG, "Raw: %d / Voltage: %dmV", adc_reading, voltage);
|
||||||
return voltage;
|
return voltage;
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool batt_sufficient() {
|
bool batt_sufficient() {
|
||||||
|
#ifdef HAS_BATTERY_PROBE
|
||||||
uint16_t volts = read_voltage();
|
uint16_t volts = read_voltage();
|
||||||
return (( volts < 1000 ) || (volts > OTA_MIN_BATT)); // no battery or battery sufficient
|
return ((volts < 1000) ||
|
||||||
|
(volts > OTA_MIN_BATT)); // no battery or battery sufficient
|
||||||
|
#else
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // HAS_BATTERY_PROBE
|
|
44
src/bme680read.cpp
Normal file
44
src/bme680read.cpp
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#ifdef HAS_BME
|
||||||
|
|
||||||
|
#include "bme680read.h"
|
||||||
|
|
||||||
|
// Local logging tag
|
||||||
|
static const char TAG[] = "main";
|
||||||
|
|
||||||
|
#define SEALEVELPRESSURE_HPA (1013.25)
|
||||||
|
|
||||||
|
// I2C Bus interface
|
||||||
|
Adafruit_BME680 bme;
|
||||||
|
|
||||||
|
bmeStatus_t bme_status;
|
||||||
|
|
||||||
|
void bme_init(void) {
|
||||||
|
// initialize BME680 sensor using default i2c address 0x77
|
||||||
|
if (bme.begin(HAS_BME)) {
|
||||||
|
// 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
|
||||||
|
ESP_LOGI(TAG, "BME680 chip found and initialized");
|
||||||
|
} else
|
||||||
|
ESP_LOGE(TAG, "BME680 chip not found on i2c bus");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bme_read(void) {
|
||||||
|
bool ret = bme.performReading();
|
||||||
|
if (ret) {
|
||||||
|
// read current BME data and buffer in global struct
|
||||||
|
bme_status.temperature = bme.temperature;
|
||||||
|
bme_status.pressure = (uint16_t)(bme.pressure / 100.0); // convert Pa -> hPa
|
||||||
|
bme_status.humidity = bme.humidity;
|
||||||
|
bme_status.gas_resistance = (uint16_t)(bme.gas_resistance / 1000.0); // convert Ohm -> kOhm
|
||||||
|
ESP_LOGI(TAG, "BME680 sensor data read success");
|
||||||
|
} else {
|
||||||
|
ESP_LOGI(TAG, "BME680 sensor read error");
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // HAS_BME
|
@ -10,6 +10,6 @@ void readButton() {
|
|||||||
ESP_LOGI(TAG, "Button pressed");
|
ESP_LOGI(TAG, "Button pressed");
|
||||||
payload.reset();
|
payload.reset();
|
||||||
payload.addButton(0x01);
|
payload.addButton(0x01);
|
||||||
SendData(BUTTONPORT);
|
SendPayload(BUTTONPORT);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
@ -7,6 +7,9 @@
|
|||||||
// Local logging tag
|
// Local logging tag
|
||||||
static const char TAG[] = "main";
|
static const char TAG[] = "main";
|
||||||
|
|
||||||
|
uint32_t userUTCTime; // Seconds since the UTC epoch
|
||||||
|
unsigned long nextTimeSync = millis();
|
||||||
|
|
||||||
// do all housekeeping
|
// do all housekeeping
|
||||||
void doHousekeeping() {
|
void doHousekeeping() {
|
||||||
|
|
||||||
@ -17,7 +20,24 @@ void doHousekeeping() {
|
|||||||
if (cfg.runmode == 1)
|
if (cfg.runmode == 1)
|
||||||
do_reset();
|
do_reset();
|
||||||
|
|
||||||
// task storage debugging //
|
spi_housekeeping();
|
||||||
|
lora_housekeeping();
|
||||||
|
|
||||||
|
// time sync once per TIME_SYNC_INTERVAL
|
||||||
|
#ifdef TIME_SYNC_INTERVAL
|
||||||
|
if (millis() >= nextTimeSync) {
|
||||||
|
nextTimeSync = millis() + TIME_SYNC_INTERVAL *
|
||||||
|
60000; // set up next time sync period
|
||||||
|
do_timesync();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAS_BME
|
||||||
|
// read BME280 sensor if present
|
||||||
|
bme_read();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// task storage debugging //
|
||||||
ESP_LOGD(TAG, "Wifiloop %d bytes left",
|
ESP_LOGD(TAG, "Wifiloop %d bytes left",
|
||||||
uxTaskGetStackHighWaterMark(wifiSwitchTask));
|
uxTaskGetStackHighWaterMark(wifiSwitchTask));
|
||||||
ESP_LOGD(TAG, "IRQhandler %d bytes left",
|
ESP_LOGD(TAG, "IRQhandler %d bytes left",
|
||||||
@ -25,11 +45,10 @@ void doHousekeeping() {
|
|||||||
#ifdef HAS_GPS
|
#ifdef HAS_GPS
|
||||||
ESP_LOGD(TAG, "Gpsloop %d bytes left", uxTaskGetStackHighWaterMark(GpsTask));
|
ESP_LOGD(TAG, "Gpsloop %d bytes left", uxTaskGetStackHighWaterMark(GpsTask));
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAS_SPI
|
|
||||||
ESP_LOGD(TAG, "Spiloop %d bytes left", uxTaskGetStackHighWaterMark(SpiTask));
|
|
||||||
#endif
|
|
||||||
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
|
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
|
||||||
ESP_LOGD(TAG, "LEDloop %d bytes left", uxTaskGetStackHighWaterMark(ledLoopTask));
|
ESP_LOGD(TAG, "LEDloop %d bytes left",
|
||||||
|
uxTaskGetStackHighWaterMark(ledLoopTask));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// read battery voltage into global variable
|
// read battery voltage into global variable
|
||||||
@ -38,24 +57,13 @@ void doHousekeeping() {
|
|||||||
ESP_LOGI(TAG, "Measured Voltage: %dmV", batt_voltage);
|
ESP_LOGI(TAG, "Measured Voltage: %dmV", batt_voltage);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// sync time & date if we have valid gps time
|
|
||||||
#ifdef HAS_GPS
|
|
||||||
if (gps.time.isValid()) {
|
|
||||||
setTime(gps.time.hour(), gps.time.minute(), gps.time.second(),
|
|
||||||
gps.date.day(), gps.date.month(), gps.date.year());
|
|
||||||
ESP_LOGI(TAG, "Time synced to %02d:%02d:%02d", hour(), minute(), second());
|
|
||||||
} else {
|
|
||||||
ESP_LOGI(TAG, "No valid GPS time");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// check free memory
|
// check free memory
|
||||||
if (esp_get_minimum_free_heap_size() <= MEM_LOW) {
|
if (esp_get_minimum_free_heap_size() <= MEM_LOW) {
|
||||||
ESP_LOGI(TAG,
|
ESP_LOGI(TAG,
|
||||||
"Memory full, counter cleared (heap low water mark = %d Bytes / "
|
"Memory full, counter cleared (heap low water mark = %d Bytes / "
|
||||||
"free heap = %d bytes)",
|
"free heap = %d bytes)",
|
||||||
esp_get_minimum_free_heap_size(), ESP.getFreeHeap());
|
esp_get_minimum_free_heap_size(), ESP.getFreeHeap());
|
||||||
SendData(COUNTERPORT); // send data before clearing counters
|
SendPayload(COUNTERPORT); // send data before clearing counters
|
||||||
reset_counters(); // clear macs container and reset all counters
|
reset_counters(); // clear macs container and reset all counters
|
||||||
get_salt(); // get new salt for salting hashes
|
get_salt(); // get new salt for salting hashes
|
||||||
|
|
||||||
@ -81,6 +89,26 @@ void reset_counters() {
|
|||||||
macs_ble = 0;
|
macs_ble = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void do_timesync() {
|
||||||
|
#ifdef TIME_SYNC_INTERVAL
|
||||||
|
// 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_LOGI(TAG, "No valid GPS time");
|
||||||
|
}
|
||||||
|
#endif // HAS_GPS
|
||||||
|
// 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");
|
||||||
|
#endif // TIME_SYNC_INTERVAL
|
||||||
|
} // do_timesync()
|
||||||
|
|
||||||
#ifndef VERBOSE
|
#ifndef VERBOSE
|
||||||
int redirect_log(const char *fmt, va_list args) {
|
int redirect_log(const char *fmt, va_list args) {
|
||||||
// do nothing
|
// do nothing
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
// Hardware related definitions for ebox ESP32-bit with external connected RFM95 LoRa
|
// Hardware related definitions for ebox ESP32-bit with external connected RFM95 LoRa
|
||||||
|
|
||||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
|
||||||
#define CFG_sx1276_radio 1
|
#define CFG_sx1276_radio 1
|
||||||
|
|
||||||
#define HAS_LED (23) // blue LED on board
|
#define HAS_LED (23) // blue LED on board
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
// Hardware related definitions for ebox ESP32-bit with external connected RFM95 LoRa
|
// Hardware related definitions for ebox ESP32-bit with external connected RFM95 LoRa
|
||||||
|
|
||||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
|
||||||
#define CFG_sx1276_radio 1
|
#define CFG_sx1276_radio 1
|
||||||
|
|
||||||
#define HAS_LED (22) // Green LED on board
|
#define HAS_LED (22) // Green LED on board
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
// Hardware related definitions for Pycom FiPy Board
|
// Hardware related definitions for Pycom FiPy Board
|
||||||
|
|
||||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
|
||||||
|
|
||||||
#define CFG_sx1272_radio 1
|
#define CFG_sx1272_radio 1
|
||||||
#define HAS_LED NOT_A_PIN // FiPy has no on board LED, so we use RGB LED
|
#define HAS_LED NOT_A_PIN // FiPy has no on board LED, so we use RGB LED
|
||||||
|
@ -7,6 +7,13 @@
|
|||||||
|
|
||||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa or has no LoRa
|
#define HAS_LORA 1 // comment out if device shall not send data via LoRa or has no LoRa
|
||||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
||||||
|
// pin definitions for SPI slave interface
|
||||||
|
#define SPI_MOSI GPIO_NUM_23
|
||||||
|
#define SPI_MISO GPIO_NUM_19
|
||||||
|
#define SPI_SCLK GPIO_NUM_18
|
||||||
|
#define SPI_CS GPIO_NUM_5
|
||||||
|
|
||||||
|
#define HAS_BME 0x77 // BME680 sensor on I2C bus (SDA=4/SCL=15); comment out if not present
|
||||||
|
|
||||||
#define CFG_sx1276_radio 1 // select LoRa chip
|
#define CFG_sx1276_radio 1 // select LoRa chip
|
||||||
//#define CFG_sx1272_radio 1 // select LoRa chip
|
//#define CFG_sx1272_radio 1 // select LoRa chip
|
||||||
|
@ -3,12 +3,11 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
//#define HAS_BME 0x77 // BME680 sensor on I2C bus (SDI=21/SCL=22); comment out if not present
|
||||||
|
|
||||||
// Hardware related definitions for Heltec LoRa-32 Board
|
// Hardware related definitions for Heltec LoRa-32 Board
|
||||||
|
|
||||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
|
||||||
#define CFG_sx1276_radio 1
|
#define CFG_sx1276_radio 1
|
||||||
|
|
||||||
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C // OLED-Display on board
|
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C // OLED-Display on board
|
||||||
#define HAS_LED (25) // white LED on board
|
#define HAS_LED (25) // white LED on board
|
||||||
#define HAS_BUTTON (0) // button "PROG" on board
|
#define HAS_BUTTON (0) // button "PROG" on board
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
// Hardware related definitions for Heltec V2 LoRa-32 Board
|
// Hardware related definitions for Heltec V2 LoRa-32 Board
|
||||||
|
|
||||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
|
||||||
#define CFG_sx1276_radio 1
|
#define CFG_sx1276_radio 1
|
||||||
|
|
||||||
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C // OLED-Display on board
|
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C // OLED-Display on board
|
||||||
|
@ -7,9 +7,7 @@
|
|||||||
|
|
||||||
#define CFG_sx1272_radio 1 // dummy
|
#define CFG_sx1272_radio 1 // dummy
|
||||||
|
|
||||||
#define HAS_LED 22 // on board LED on GPIO22
|
#define HAS_LED (5) // on board LED on GPIO5
|
||||||
#define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW
|
#define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW
|
||||||
|
|
||||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -18,7 +18,6 @@
|
|||||||
#define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown
|
#define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown
|
||||||
|
|
||||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
|
||||||
#define CFG_sx1276_radio 1 // RFM95 module
|
#define CFG_sx1276_radio 1 // RFM95 module
|
||||||
|
|
||||||
// Pins for LORA chip SPI interface, reset line and interrupt lines
|
// Pins for LORA chip SPI interface, reset line and interrupt lines
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
#define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown
|
#define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown
|
||||||
|
|
||||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
|
||||||
#define CFG_sx1276_radio 1 // RFM95 module
|
#define CFG_sx1276_radio 1 // RFM95 module
|
||||||
|
|
||||||
// Pins for LORA chip SPI interface, reset line and interrupt lines
|
// Pins for LORA chip SPI interface, reset line and interrupt lines
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
// Hardware related definitions for Pycom LoPy Board (NOT LoPy4)
|
// Hardware related definitions for Pycom LoPy Board (NOT LoPy4)
|
||||||
|
|
||||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
|
||||||
#define CFG_sx1272_radio 1
|
#define CFG_sx1272_radio 1
|
||||||
#define HAS_LED NOT_A_PIN // LoPy4 has no on board mono LED, we use on board RGB LED
|
#define HAS_LED NOT_A_PIN // LoPy4 has no on board mono LED, we use on board RGB LED
|
||||||
#define HAS_RGB_LED (0) // WS2812B RGB LED on GPIO0
|
#define HAS_RGB_LED (0) // WS2812B RGB LED on GPIO0
|
||||||
@ -26,9 +25,9 @@
|
|||||||
#define WIFI_ANTENNA 0 // 0 = internal, 1 = external
|
#define WIFI_ANTENNA 0 // 0 = internal, 1 = external
|
||||||
|
|
||||||
// uncomment this only if your LoPy runs on a PYTRACK BOARD
|
// uncomment this only if your LoPy runs on a PYTRACK BOARD
|
||||||
//#define HAS_GPS 1
|
#define HAS_GPS 1
|
||||||
//#define GPS_I2C GPIO_NUM_25, GPIO_NUM_26 // SDA (P22), SCL (P21)
|
#define GPS_I2C GPIO_NUM_25, GPIO_NUM_26 // SDA (P22), SCL (P21)
|
||||||
//#define GPS_ADDR 0x10
|
#define GPS_ADDR 0x10
|
||||||
|
|
||||||
// uncomment this only if your LoPy runs on a EXPANSION BOARD
|
// uncomment this only if your LoPy runs on a EXPANSION BOARD
|
||||||
//#define HAS_LED (12) // use if LoPy is on Expansion Board, this has a user LED
|
//#define HAS_LED (12) // use if LoPy is on Expansion Board, this has a user LED
|
||||||
|
@ -6,7 +6,14 @@
|
|||||||
// Hardware related definitions for Pycom LoPy4 Board
|
// Hardware related definitions for Pycom LoPy4 Board
|
||||||
|
|
||||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||||
|
|
||||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
||||||
|
// pin definitions for local wired SPI slave interface
|
||||||
|
#define SPI_MOSI GPIO_NUM_22
|
||||||
|
#define SPI_MISO GPIO_NUM_33
|
||||||
|
#define SPI_SCLK GPIO_NUM_26
|
||||||
|
#define SPI_CS GPIO_NUM_36
|
||||||
|
|
||||||
#define CFG_sx1276_radio 1
|
#define CFG_sx1276_radio 1
|
||||||
//#define HAS_LED NOT_A_PIN // LoPy4 has no on board mono LED, we use on board RGB LED
|
//#define HAS_LED NOT_A_PIN // LoPy4 has no on board mono LED, we use on board RGB LED
|
||||||
#define HAS_RGB_LED (0) // WS2812B RGB LED on GPIO0 (P2)
|
#define HAS_RGB_LED (0) // WS2812B RGB LED on GPIO0 (P2)
|
||||||
|
@ -10,6 +10,8 @@
|
|||||||
// disable brownout detection (avoid unexpected reset on some boards)
|
// disable brownout detection (avoid unexpected reset on some boards)
|
||||||
#define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature
|
#define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature
|
||||||
|
|
||||||
|
#define HAS_BME 0x76 // BME680 sensor on I2C bus; comment out if not present
|
||||||
|
|
||||||
#define HAS_LED 13 // ESP32 GPIO12 (pin22) On Board LED
|
#define HAS_LED 13 // ESP32 GPIO12 (pin22) On Board LED
|
||||||
#define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW
|
#define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW
|
||||||
//#define HAS_RGB_LED 13 // ESP32 GPIO13 (pin13) On Board Shield WS2812B RGB LED
|
//#define HAS_RGB_LED 13 // ESP32 GPIO13 (pin13) On Board Shield WS2812B RGB LED
|
||||||
@ -17,7 +19,6 @@
|
|||||||
//#define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown
|
//#define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown
|
||||||
|
|
||||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
|
||||||
#define CFG_sx1276_radio 1 // RFM95 module
|
#define CFG_sx1276_radio 1 // RFM95 module
|
||||||
|
|
||||||
// Pins for LORA chip SPI interface, reset line and interrupt lines
|
// Pins for LORA chip SPI interface, reset line and interrupt lines
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
// Hardware related definitions for TTGO T-Beam board
|
// Hardware related definitions for TTGO T-Beam board
|
||||||
|
|
||||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
|
||||||
#define CFG_sx1276_radio 1 // HPD13A LoRa SoC
|
#define CFG_sx1276_radio 1 // HPD13A LoRa SoC
|
||||||
|
|
||||||
#define BOARD_HAS_PSRAM // use extra 4MB external RAM
|
#define BOARD_HAS_PSRAM // use extra 4MB external RAM
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
// Hardware related definitions for TTGOv1 board
|
// Hardware related definitions for TTGOv1 board
|
||||||
|
|
||||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
|
||||||
#define CFG_sx1276_radio 1
|
#define CFG_sx1276_radio 1
|
||||||
|
|
||||||
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C // OLED-Display on board
|
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C // OLED-Display on board
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
// Hardware related definitions for TTGO V2 Board
|
// Hardware related definitions for TTGO V2 Board
|
||||||
|
|
||||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
|
||||||
#define CFG_sx1276_radio 1 // HPD13A LoRa SoC
|
#define CFG_sx1276_radio 1 // HPD13A LoRa SoC
|
||||||
|
|
||||||
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C
|
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C
|
||||||
|
@ -8,8 +8,9 @@
|
|||||||
// This settings are for boards labeled v1.6 on pcb, NOT for v1.5 or older
|
// This settings are for boards labeled v1.6 on pcb, NOT for v1.5 or older
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define HAS_BME 0x77 // BME680 sensor on I2C bus (SDI=21/SCL=22); comment out if not present
|
||||||
|
|
||||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
|
||||||
#define CFG_sx1276_radio 1 // HPD13A LoRa SoC
|
#define CFG_sx1276_radio 1 // HPD13A LoRa SoC
|
||||||
|
|
||||||
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C
|
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C
|
||||||
|
@ -10,7 +10,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
|
||||||
#define CFG_sx1276_radio 1 // HPD13A LoRa SoC
|
#define CFG_sx1276_radio 1 // HPD13A LoRa SoC
|
||||||
#define HAS_LED NOT_A_PIN // no usable LED on board
|
#define HAS_LED NOT_A_PIN // no usable LED on board
|
||||||
#define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature
|
#define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature
|
||||||
|
@ -35,8 +35,8 @@ void irqHandler(void *pvParameters) {
|
|||||||
doHousekeeping();
|
doHousekeeping();
|
||||||
|
|
||||||
// is time to send the payload?
|
// is time to send the payload?
|
||||||
if (InterruptStatus & SENDPAYLOAD_IRQ)
|
if (InterruptStatus & SENDCOUNTER_IRQ)
|
||||||
sendPayload();
|
sendCounter();
|
||||||
}
|
}
|
||||||
vTaskDelete(NULL); // shoud never be reached
|
vTaskDelete(NULL); // shoud never be reached
|
||||||
}
|
}
|
||||||
@ -55,7 +55,7 @@ void IRAM_ATTR homeCycleIRQ() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void IRAM_ATTR SendCycleIRQ() {
|
void IRAM_ATTR SendCycleIRQ() {
|
||||||
xTaskNotifyFromISR(irqHandlerTask, SENDPAYLOAD_IRQ, eSetBits, NULL);
|
xTaskNotifyFromISR(irqHandlerTask, SENDCOUNTER_IRQ, eSetBits, NULL);
|
||||||
portYIELD_FROM_ISR();
|
portYIELD_FROM_ISR();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,9 +87,8 @@ void rgb_set_color(uint16_t hue) {}
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if (HAS_LED != NOT_A_PIN)
|
|
||||||
|
|
||||||
void switch_LED(uint8_t state) {
|
void switch_LED(uint8_t state) {
|
||||||
|
#if (HAS_LED != NOT_A_PIN)
|
||||||
if (state == LED_ON) {
|
if (state == LED_ON) {
|
||||||
// switch LED on
|
// switch LED on
|
||||||
#ifdef LED_ACTIVE_LOW
|
#ifdef LED_ACTIVE_LOW
|
||||||
@ -105,9 +104,8 @@ void switch_LED(uint8_t state) {
|
|||||||
digitalWrite(HAS_LED, LOW);
|
digitalWrite(HAS_LED, LOW);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
|
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
|
||||||
|
|
||||||
|
@ -21,6 +21,9 @@
|
|||||||
|
|
||||||
//#define LMIC_USE_INTERRUPTS
|
//#define LMIC_USE_INTERRUPTS
|
||||||
|
|
||||||
|
//time sync via LoRaWAN network, is not yet supported by TTN (LoRaWAN spec v1.0.3)
|
||||||
|
#define LMIC_ENABLE_DeviceTimeReq 1
|
||||||
|
|
||||||
// 16 μs per tick
|
// 16 μs per tick
|
||||||
// LMIC requires ticks to be 15.5μs - 100 μs long
|
// LMIC requires ticks to be 15.5μs - 100 μs long
|
||||||
#define US_PER_OSTICK_EXPONENT 4
|
#define US_PER_OSTICK_EXPONENT 4
|
||||||
@ -38,7 +41,7 @@
|
|||||||
// enable more verbose output. Make sure that printf is actually
|
// enable more verbose output. Make sure that printf is actually
|
||||||
// configured (e.g. on AVR it is not by default), otherwise using it can
|
// configured (e.g. on AVR it is not by default), otherwise using it can
|
||||||
// cause crashing.
|
// cause crashing.
|
||||||
//#define LMIC_DEBUG_LEVEL 1
|
#define LMIC_DEBUG_LEVEL 0
|
||||||
|
|
||||||
// Enable this to allow using printf() to print to the given serial port
|
// Enable this to allow using printf() to print to the given serial port
|
||||||
// (or any other Print object). This can be easy for debugging. The
|
// (or any other Print object). This can be easy for debugging. The
|
||||||
|
120
src/lorawan.cpp
120
src/lorawan.cpp
@ -1,11 +1,11 @@
|
|||||||
#ifdef HAS_LORA
|
|
||||||
|
|
||||||
// Basic Config
|
// Basic Config
|
||||||
#include "lorawan.h"
|
#include "lorawan.h"
|
||||||
|
|
||||||
// Local logging Tag
|
// Local logging Tag
|
||||||
static const char TAG[] = "lora";
|
static const char TAG[] = "lora";
|
||||||
|
|
||||||
|
#ifdef HAS_LORA
|
||||||
|
|
||||||
osjob_t sendjob;
|
osjob_t sendjob;
|
||||||
QueueHandle_t LoraSendQueue;
|
QueueHandle_t LoraSendQueue;
|
||||||
|
|
||||||
@ -328,10 +328,14 @@ void lora_send(osjob_t *job) {
|
|||||||
// waiting for LoRa getting ready
|
// waiting for LoRa getting ready
|
||||||
} else {
|
} else {
|
||||||
if (xQueueReceive(LoraSendQueue, &SendBuffer, (TickType_t)0) == pdTRUE) {
|
if (xQueueReceive(LoraSendQueue, &SendBuffer, (TickType_t)0) == pdTRUE) {
|
||||||
// SendBuffer gets struct MessageBuffer with next payload from queue
|
// SendBuffer now filled with next payload from queue
|
||||||
LMIC_setTxData2(SendBuffer.MessagePort, SendBuffer.Message,
|
if (!LMIC_setTxData2(SendBuffer.MessagePort, SendBuffer.Message,
|
||||||
SendBuffer.MessageSize, (cfg.countermode & 0x02));
|
SendBuffer.MessageSize, (cfg.countermode & 0x02))) {
|
||||||
ESP_LOGI(TAG, "%d bytes sent to LoRa", SendBuffer.MessageSize);
|
ESP_LOGI(TAG, "%d byte(s) sent to LoRa", SendBuffer.MessageSize);
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "could not send %d byte(s) to LoRa",
|
||||||
|
SendBuffer.MessageSize);
|
||||||
|
}
|
||||||
// sprintf(display_line7, "PACKET QUEUED");
|
// sprintf(display_line7, "PACKET QUEUED");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -342,3 +346,107 @@ void lora_send(osjob_t *job) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endif // HAS_LORA
|
#endif // HAS_LORA
|
||||||
|
|
||||||
|
esp_err_t lora_stack_init() {
|
||||||
|
#ifndef HAS_LORA
|
||||||
|
return ESP_OK; // continue main program
|
||||||
|
#else
|
||||||
|
LoraSendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t));
|
||||||
|
if (LoraSendQueue == 0) {
|
||||||
|
ESP_LOGE(TAG, "Could not create LORA send queue. Aborting.");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "LORA send queue created, size %d Bytes",
|
||||||
|
SEND_QUEUE_SIZE * PAYLOAD_BUFFER_SIZE);
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Starting LMIC...");
|
||||||
|
os_init(); // initialize lmic run-time environment on core 1
|
||||||
|
LMIC_reset(); // initialize lmic MAC
|
||||||
|
LMIC_setLinkCheckMode(0);
|
||||||
|
// This tells LMIC to make the receive windows bigger, in case your clock is
|
||||||
|
// faster or slower. This causes the transceiver to be earlier switched on,
|
||||||
|
// so consuming more power. You may sharpen (reduce) CLOCK_ERROR_PERCENTAGE
|
||||||
|
// in src/lmic_config.h if you are limited on battery.
|
||||||
|
LMIC_setClockError(MAX_CLOCK_ERROR * CLOCK_ERROR_PROCENTAGE / 100);
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
#if defined(CFG_US915) || defined(CFG_au921)
|
||||||
|
// 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);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!LMIC_startJoining()) { // start joining
|
||||||
|
ESP_LOGI(TAG, "Already joined");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK; // continue main program
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void lora_enqueuedata(MessageBuffer_t *message) {
|
||||||
|
// enqueue message in LORA send queue
|
||||||
|
#ifdef HAS_LORA
|
||||||
|
BaseType_t ret =
|
||||||
|
xQueueSendToBack(LoraSendQueue, (void *)message, (TickType_t)0);
|
||||||
|
if (ret == pdTRUE) {
|
||||||
|
ESP_LOGI(TAG, "%d bytes enqueued for LORA interface", message->MessageSize);
|
||||||
|
} else {
|
||||||
|
ESP_LOGW(TAG, "LORA sendqueue is full");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void lora_queuereset(void) {
|
||||||
|
#ifdef HAS_LORA
|
||||||
|
xQueueReset(LoraSendQueue);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void lora_housekeeping(void) {
|
||||||
|
#ifdef HAS_LORA
|
||||||
|
// ESP_LOGD(TAG, "loraloop %d bytes left",
|
||||||
|
// 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;
|
||||||
|
lmic_time_reference_t lmicTimeReference;
|
||||||
|
|
||||||
|
if (flagSuccess != 1) {
|
||||||
|
ESP_LOGW(TAG, "LoRaWAN network did not answer time request");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate lmic_time_reference
|
||||||
|
flagSuccess = LMIC_getNetworkTimeReference(&lmicTimeReference);
|
||||||
|
if (flagSuccess != 1) {
|
||||||
|
ESP_LOGW(TAG, "LoRaWAN time request failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update userUTCTime, considering the difference between the GPS and UTC
|
||||||
|
// epoch, and the leap seconds
|
||||||
|
*pUserUTCTime = lmicTimeReference.tNetwork + 315964800;
|
||||||
|
// Current time, in ticks
|
||||||
|
ostime_t ticksNow = os_getTime();
|
||||||
|
// Time when the request was sent, in ticks
|
||||||
|
ostime_t ticksRequestSent = lmicTimeReference.tLocal;
|
||||||
|
// Add the delay between the instant the time was transmitted and
|
||||||
|
// the current time
|
||||||
|
uint32_t requestDelaySec = osticks2ms(ticksNow - ticksRequestSent) / 1000;
|
||||||
|
*pUserUTCTime += requestDelaySec;
|
||||||
|
|
||||||
|
// Update system time with time read from the network
|
||||||
|
setTime(*pUserUTCTime);
|
||||||
|
ESP_LOGI(TAG, "Time synced by LoRa network to %02d:%02d:%02d", hour(),
|
||||||
|
minute(), second());
|
||||||
|
}
|
@ -107,7 +107,7 @@ bool mac_add(uint8_t *paddr, int8_t rssi, bool sniff_type) {
|
|||||||
#endif
|
#endif
|
||||||
payload.reset();
|
payload.reset();
|
||||||
payload.addAlarm(rssi, beaconID);
|
payload.addAlarm(rssi, beaconID);
|
||||||
SendData(BEACONPORT);
|
SendPayload(BEACONPORT);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
61
src/main.cpp
61
src/main.cpp
@ -55,7 +55,8 @@ uint8_t volatile channel = 0; // channel rotation counter
|
|||||||
uint16_t volatile macs_total = 0, macs_wifi = 0, macs_ble = 0,
|
uint16_t volatile macs_total = 0, macs_wifi = 0, macs_ble = 0,
|
||||||
batt_voltage = 0; // globals for display
|
batt_voltage = 0; // globals for display
|
||||||
|
|
||||||
hw_timer_t *channelSwitch, *sendCycle, *homeCycle, *displaytimer; // irq tasks
|
hw_timer_t *channelSwitch = NULL, *sendCycle = NULL, *homeCycle = NULL,
|
||||||
|
*displaytimer = NULL; // irq tasks
|
||||||
TaskHandle_t irqHandlerTask, wifiSwitchTask;
|
TaskHandle_t irqHandlerTask, wifiSwitchTask;
|
||||||
|
|
||||||
std::set<uint16_t> macs; // container holding unique MAC adress hashes
|
std::set<uint16_t> macs; // container holding unique MAC adress hashes
|
||||||
@ -157,54 +158,23 @@ void setup() {
|
|||||||
strcat_P(features, " GPS");
|
strcat_P(features, " GPS");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// initialize gps
|
||||||
|
#ifdef HAS_BME
|
||||||
|
strcat_P(features, " BME");
|
||||||
|
bme_init();
|
||||||
|
#endif
|
||||||
|
|
||||||
// initialize LoRa
|
// initialize LoRa
|
||||||
#ifdef HAS_LORA
|
#ifdef HAS_LORA
|
||||||
strcat_P(features, " LORA");
|
strcat_P(features, " LORA");
|
||||||
LoraSendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t));
|
|
||||||
if (LoraSendQueue == 0) {
|
|
||||||
ESP_LOGE(TAG, "Could not create LORA send queue. Aborting.");
|
|
||||||
exit(0);
|
|
||||||
} else
|
|
||||||
ESP_LOGI(TAG, "LORA send queue created, size %d Bytes",
|
|
||||||
SEND_QUEUE_SIZE * PAYLOAD_BUFFER_SIZE);
|
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Starting LMIC...");
|
|
||||||
os_init(); // initialize lmic run-time environment on core 1
|
|
||||||
LMIC_reset(); // initialize lmic MAC
|
|
||||||
LMIC_setLinkCheckMode(0);
|
|
||||||
// This tells LMIC to make the receive windows bigger, in case your clock is
|
|
||||||
// faster or slower. This causes the transceiver to be earlier switched on,
|
|
||||||
// so consuming more power. You may sharpen (reduce) CLOCK_ERROR_PERCENTAGE
|
|
||||||
// in src/lmic_config.h if you are limited on battery.
|
|
||||||
LMIC_setClockError(MAX_CLOCK_ERROR * CLOCK_ERROR_PROCENTAGE / 100);
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
#if defined(CFG_US915) || defined(CFG_au921)
|
|
||||||
// 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);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
LMIC_startJoining(); // start joining
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
assert(lora_stack_init() == ESP_OK);
|
||||||
|
|
||||||
// initialize SPI
|
// initialize SPI
|
||||||
#ifdef HAS_SPI
|
#ifdef HAS_SPI
|
||||||
strcat_P(features, " SPI");
|
strcat_P(features, " SPI");
|
||||||
SPISendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t));
|
|
||||||
if (SPISendQueue == 0) {
|
|
||||||
ESP_LOGE(TAG, "Could not create SPI send queue. Aborting.");
|
|
||||||
exit(0);
|
|
||||||
} else
|
|
||||||
ESP_LOGI(TAG, "SPI send queue created, size %d Bytes",
|
|
||||||
SEND_QUEUE_SIZE * PAYLOAD_BUFFER_SIZE);
|
|
||||||
#endif
|
#endif
|
||||||
|
assert(spi_init() == ESP_OK);
|
||||||
|
|
||||||
#ifdef VENDORFILTER
|
#ifdef VENDORFILTER
|
||||||
strcat_P(features, " OUIFLT");
|
strcat_P(features, " OUIFLT");
|
||||||
@ -312,17 +282,6 @@ void setup() {
|
|||||||
1); // CPU core
|
1); // CPU core
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAS_SPI
|
|
||||||
ESP_LOGI(TAG, "Starting SPIloop...");
|
|
||||||
xTaskCreatePinnedToCore(spi_loop, // task function
|
|
||||||
"spiloop", // name of task
|
|
||||||
2048, // stack size of task
|
|
||||||
(void *)1, // parameter of the task
|
|
||||||
2, // priority of the task
|
|
||||||
&SpiTask, // task handle
|
|
||||||
0); // CPU core
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// start state machine
|
// start state machine
|
||||||
ESP_LOGI(TAG, "Starting IRQ Handler...");
|
ESP_LOGI(TAG, "Starting IRQ Handler...");
|
||||||
xTaskCreatePinnedToCore(irqHandler, // task function
|
xTaskCreatePinnedToCore(irqHandler, // task function
|
||||||
|
241
src/ota.cpp
241
src/ota.cpp
@ -26,9 +26,6 @@ const BintrayClient bintray(BINTRAY_USER, BINTRAY_REPO, BINTRAY_PACKAGE);
|
|||||||
// Connection port (HTTPS)
|
// Connection port (HTTPS)
|
||||||
const int port = 443;
|
const int port = 443;
|
||||||
|
|
||||||
// Connection timeout
|
|
||||||
const uint32_t RESPONSE_TIMEOUT_MS = 5000;
|
|
||||||
|
|
||||||
// Variables to validate firmware content
|
// Variables to validate firmware content
|
||||||
int volatile contentLength = 0;
|
int volatile contentLength = 0;
|
||||||
bool volatile isValidContentType = false;
|
bool volatile isValidContentType = false;
|
||||||
@ -43,24 +40,13 @@ inline String getHeaderValue(String header, String headerName) {
|
|||||||
|
|
||||||
void start_ota_update() {
|
void start_ota_update() {
|
||||||
|
|
||||||
/*
|
// check battery status if we can before doing ota
|
||||||
// check battery status if we can before doing ota
|
|
||||||
#ifdef HAS_BATTERY_PROBE
|
|
||||||
if (!batt_sufficient()) {
|
if (!batt_sufficient()) {
|
||||||
ESP_LOGW(TAG, "Battery voltage %dmV too low for OTA", batt_voltage);
|
ESP_LOGE(TAG, "Battery voltage %dmV too low for OTA", batt_voltage);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
*/
|
|
||||||
|
|
||||||
// turn on LED
|
switch_LED(LED_ON);
|
||||||
#if (HAS_LED != NOT_A_PIN)
|
|
||||||
#ifdef LED_ACTIVE_LOW
|
|
||||||
digitalWrite(HAS_LED, LOW);
|
|
||||||
#else
|
|
||||||
digitalWrite(HAS_LED, HIGH);
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAS_DISPLAY
|
#ifdef HAS_DISPLAY
|
||||||
u8x8.begin();
|
u8x8.begin();
|
||||||
@ -82,86 +68,94 @@ void start_ota_update() {
|
|||||||
ESP_LOGI(TAG, "Starting Wifi OTA update");
|
ESP_LOGI(TAG, "Starting Wifi OTA update");
|
||||||
display(1, "**", WIFI_SSID);
|
display(1, "**", WIFI_SSID);
|
||||||
|
|
||||||
|
WiFi.mode(WIFI_AP_STA);
|
||||||
WiFi.begin(WIFI_SSID, WIFI_PASS);
|
WiFi.begin(WIFI_SSID, WIFI_PASS);
|
||||||
|
|
||||||
int i = WIFI_MAX_TRY;
|
int i = WIFI_MAX_TRY, j = OTA_MAX_TRY;
|
||||||
|
int ret = 1; // 0 = finished, 1 = retry, -1 = abort
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Trying to connect to %s", WIFI_SSID);
|
||||||
|
|
||||||
while (i--) {
|
while (i--) {
|
||||||
ESP_LOGI(TAG, "Trying to connect to %s", WIFI_SSID);
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
if (WiFi.status() == WL_CONNECTED)
|
// we now have wifi connection and try to do an OTA over wifi update
|
||||||
break;
|
|
||||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i >= 0) {
|
|
||||||
ESP_LOGI(TAG, "Connected to %s", WIFI_SSID);
|
ESP_LOGI(TAG, "Connected to %s", WIFI_SSID);
|
||||||
display(1, "OK", "WiFi connected");
|
display(1, "OK", "WiFi connected");
|
||||||
do_ota_update(); // gets and flashes new firmware
|
// do a number of tries to update firmware limited by OTA_MAX_TRY
|
||||||
} else {
|
while ((j--) && (ret > 0)) {
|
||||||
ESP_LOGI(TAG, "Could not connect to %s, rebooting.", WIFI_SSID);
|
ESP_LOGI(TAG, "Starting OTA update, attempt %u of %u", OTA_MAX_TRY - j,
|
||||||
display(1, " E", "no WiFi connect");
|
OTA_MAX_TRY);
|
||||||
|
ret = do_ota_update();
|
||||||
|
}
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||||
|
WiFi.reconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// wifi did not connect
|
||||||
|
ESP_LOGI(TAG, "Could not connect to %s", WIFI_SSID);
|
||||||
|
display(1, " E", "no WiFi connect");
|
||||||
|
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||||
|
|
||||||
|
end:
|
||||||
|
switch_LED(LED_OFF);
|
||||||
|
ESP_LOGI(TAG, "Rebooting to %s firmware", (ret == 0) ? "new" : "current");
|
||||||
display(5, "**", ""); // mark line rebooting
|
display(5, "**", ""); // mark line rebooting
|
||||||
|
|
||||||
// turn off LED
|
|
||||||
#if (HAS_LED != NOT_A_PIN)
|
|
||||||
#ifdef LED_ACTIVE_LOW
|
|
||||||
digitalWrite(HAS_LED, HIGH);
|
|
||||||
#else
|
|
||||||
digitalWrite(HAS_LED, LOW);
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||||
ESP.restart();
|
ESP.restart();
|
||||||
|
|
||||||
} // start_ota_update
|
} // start_ota_update
|
||||||
|
|
||||||
void do_ota_update() {
|
// Reads data vom wifi client and flashes it to ota partition
|
||||||
|
// returns: 0 = finished, 1 = retry, -1 = abort
|
||||||
|
int do_ota_update() {
|
||||||
|
|
||||||
char buf[17];
|
char buf[17];
|
||||||
|
bool redirect = true;
|
||||||
|
size_t written = 0;
|
||||||
|
|
||||||
// Fetch the latest firmware version
|
// Fetch the latest firmware version
|
||||||
ESP_LOGI(TAG, "Checking latest firmware version on server...");
|
ESP_LOGI(TAG, "Checking latest firmware version on server");
|
||||||
display(2, "**", "checking version");
|
display(2, "**", "checking version");
|
||||||
const String latest = bintray.getLatestVersion();
|
const String latest = bintray.getLatestVersion();
|
||||||
|
|
||||||
if (latest.length() == 0) {
|
if (latest.length() == 0) {
|
||||||
ESP_LOGI(
|
ESP_LOGI(TAG, "Could not fetch info on latest firmware");
|
||||||
TAG,
|
|
||||||
"Could not load info about the latest firmware. Rebooting to runmode.");
|
|
||||||
display(2, " E", "file not found");
|
display(2, " E", "file not found");
|
||||||
return;
|
return -1;
|
||||||
} else if (version_compare(latest, cfg.version) <= 0) {
|
} else if (version_compare(latest, cfg.version) <= 0) {
|
||||||
ESP_LOGI(TAG, "Current firmware is up to date. Rebooting to runmode.");
|
ESP_LOGI(TAG, "Current firmware is up to date");
|
||||||
display(2, "NO", "no update found");
|
display(2, "NO", "no update found");
|
||||||
return;
|
return -1;
|
||||||
}
|
}
|
||||||
ESP_LOGI(TAG, "New firmware version v%s available. Downloading...",
|
ESP_LOGI(TAG, "New firmware version v%s available", latest.c_str());
|
||||||
latest.c_str());
|
|
||||||
display(2, "OK", latest.c_str());
|
display(2, "OK", latest.c_str());
|
||||||
|
|
||||||
display(3, "**", "");
|
display(3, "**", "");
|
||||||
String firmwarePath = bintray.getBinaryPath(latest);
|
String firmwarePath = bintray.getBinaryPath(latest);
|
||||||
if (!firmwarePath.endsWith(".bin")) {
|
if (!firmwarePath.endsWith(".bin")) {
|
||||||
ESP_LOGI(TAG, "Unsupported binary format, OTA update cancelled.");
|
ESP_LOGI(TAG, "Unsupported binary format");
|
||||||
display(3, " E", "file type error");
|
display(3, " E", "file type error");
|
||||||
return;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
String currentHost = bintray.getStorageHost();
|
String currentHost = bintray.getStorageHost();
|
||||||
String prevHost = currentHost;
|
String prevHost = currentHost;
|
||||||
|
|
||||||
WiFiClientSecure client;
|
WiFiClientSecure client;
|
||||||
|
|
||||||
client.setCACert(bintray.getCertificate(currentHost));
|
client.setCACert(bintray.getCertificate(currentHost));
|
||||||
|
// client.setTimeout(RESPONSE_TIMEOUT_MS);
|
||||||
|
// --> causing error [E][WiFiClient.cpp:236] setSocketOption(): 1006 : 9
|
||||||
|
// so we unfortunately need patched update.cpp which sets the stream timeout
|
||||||
|
|
||||||
if (!client.connect(currentHost.c_str(), port)) {
|
if (!client.connect(currentHost.c_str(), port)) {
|
||||||
ESP_LOGI(TAG, "Cannot connect to %s", currentHost.c_str());
|
ESP_LOGI(TAG, "Cannot connect to %s", currentHost.c_str());
|
||||||
display(3, " E", "connection lost");
|
display(3, " E", "connection lost");
|
||||||
return;
|
goto abort;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool redirect = true;
|
|
||||||
while (redirect) {
|
while (redirect) {
|
||||||
if (currentHost != prevHost) {
|
if (currentHost != prevHost) {
|
||||||
client.stop();
|
client.stop();
|
||||||
@ -170,7 +164,7 @@ void do_ota_update() {
|
|||||||
ESP_LOGI(TAG, "Redirect detected, but cannot connect to %s",
|
ESP_LOGI(TAG, "Redirect detected, but cannot connect to %s",
|
||||||
currentHost.c_str());
|
currentHost.c_str());
|
||||||
display(3, " E", "server error");
|
display(3, " E", "server error");
|
||||||
return;
|
goto abort;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,11 +177,10 @@ void do_ota_update() {
|
|||||||
|
|
||||||
unsigned long timeout = millis();
|
unsigned long timeout = millis();
|
||||||
while (client.available() == 0) {
|
while (client.available() == 0) {
|
||||||
if (millis() - timeout > RESPONSE_TIMEOUT_MS) {
|
if ((millis() - timeout) > (RESPONSE_TIMEOUT_MS)) {
|
||||||
ESP_LOGI(TAG, "Client Timeout.");
|
ESP_LOGI(TAG, "Client timeout");
|
||||||
display(3, " E", "client timeout");
|
display(3, " E", "client timeout");
|
||||||
client.stop();
|
goto abort;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,13 +200,12 @@ void do_ota_update() {
|
|||||||
"firmware flashing");
|
"firmware flashing");
|
||||||
redirect = false;
|
redirect = false;
|
||||||
} else if (line.indexOf("302") > 0) {
|
} else if (line.indexOf("302") > 0) {
|
||||||
ESP_LOGI(TAG, "Got 302 status code from server. Redirecting to the "
|
ESP_LOGI(TAG, "Got 302 status code from server. Redirecting to "
|
||||||
"new address");
|
"new address");
|
||||||
redirect = true;
|
redirect = true;
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGI(TAG, "Could not get a valid firmware url.");
|
ESP_LOGI(TAG, "Could not get firmware download URL");
|
||||||
// Unexptected HTTP response. Retry or skip update?
|
goto retry;
|
||||||
redirect = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,76 +234,71 @@ void do_ota_update() {
|
|||||||
isValidContentType = true;
|
isValidContentType = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} // while (client.available())
|
||||||
}
|
} // while (redirect)
|
||||||
|
|
||||||
display(3, "OK", ""); // line download
|
display(3, "OK", ""); // line download
|
||||||
|
|
||||||
// check whether we have everything for OTA update
|
// check whether we have everything for OTA update
|
||||||
if (contentLength && isValidContentType) {
|
if (!(contentLength && isValidContentType)) {
|
||||||
|
ESP_LOGI(TAG, "Invalid OTA server response");
|
||||||
|
display(4, " E", "response error");
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
|
|
||||||
size_t written = 0;
|
#ifdef HAS_LED
|
||||||
|
#ifndef LED_ACTIVE_LOW
|
||||||
|
if (!Update.begin(contentLength, U_FLASH, HAS_LED, HIGH)) {
|
||||||
|
#else
|
||||||
|
if (!Update.begin(contentLength, U_FLASH, HAS_LED, LOW)) {
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
if (!Update.begin(contentLength)) {
|
||||||
|
#endif
|
||||||
|
ESP_LOGI(TAG, "Not enough space to start OTA update");
|
||||||
|
display(4, " E", "disk full");
|
||||||
|
goto abort;
|
||||||
|
}
|
||||||
|
|
||||||
if (Update.begin(contentLength)) {
|
|
||||||
#ifdef HAS_DISPLAY
|
#ifdef HAS_DISPLAY
|
||||||
// register callback function for showing progress while streaming data
|
// register callback function for showing progress while streaming data
|
||||||
Update.onProgress(&show_progress);
|
Update.onProgress(&show_progress);
|
||||||
#endif
|
#endif
|
||||||
int i = FLASH_MAX_TRY;
|
|
||||||
while ((i--) && (written != contentLength)) {
|
|
||||||
|
|
||||||
ESP_LOGI(TAG,
|
|
||||||
"Starting OTA update, attempt %u of %u. This will take some "
|
|
||||||
"time to complete...",
|
|
||||||
FLASH_MAX_TRY - i, FLASH_MAX_TRY);
|
|
||||||
display(4, "**", "writing...");
|
display(4, "**", "writing...");
|
||||||
|
written = Update.writeStream(client); // this is a blocking call
|
||||||
written = Update.writeStream(client);
|
|
||||||
|
|
||||||
if (written == contentLength) {
|
if (written == contentLength) {
|
||||||
ESP_LOGI(TAG, "Written %u bytes successfully", written);
|
ESP_LOGI(TAG, "Written %u bytes successfully", written);
|
||||||
snprintf(buf, 17, "%ukB Done!", (uint16_t)(written / 1024));
|
snprintf(buf, 17, "%ukB Done!", (uint16_t)(written / 1024));
|
||||||
display(4, "OK", buf);
|
display(4, "OK", buf);
|
||||||
break;
|
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGI(TAG,
|
ESP_LOGI(TAG, "Written only %u of %u bytes, OTA update attempt cancelled",
|
||||||
"Written only %u of %u bytes, OTA update attempt cancelled.",
|
|
||||||
written, contentLength);
|
written, contentLength);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (Update.end()) {
|
if (Update.end()) {
|
||||||
|
goto finished;
|
||||||
if (Update.isFinished()) {
|
|
||||||
ESP_LOGI(
|
|
||||||
TAG,
|
|
||||||
"OTA update completed. Rebooting to runmode with new version.");
|
|
||||||
client.stop();
|
|
||||||
return;
|
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGI(TAG, "Something went wrong! OTA update hasn't been finished "
|
ESP_LOGI(TAG, "An error occurred. Error#: %d", Update.getError());
|
||||||
"properly.");
|
snprintf(buf, 17, "Error#: %d", Update.getError());
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ESP_LOGI(TAG, "An error occurred. Error #: %d", Update.getError());
|
|
||||||
snprintf(buf, 17, "Error #: %d", Update.getError());
|
|
||||||
display(4, " E", buf);
|
display(4, " E", buf);
|
||||||
|
goto retry;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
finished:
|
||||||
ESP_LOGI(TAG, "There isn't enough space to start OTA update");
|
|
||||||
display(4, " E", "disk full");
|
|
||||||
client.flush();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ESP_LOGI(TAG,
|
|
||||||
"There was no valid content in the response from the OTA server!");
|
|
||||||
display(4, " E", "response error");
|
|
||||||
client.flush();
|
|
||||||
}
|
|
||||||
ESP_LOGI(TAG,
|
|
||||||
"OTA update failed. Rebooting to runmode with current version.");
|
|
||||||
client.stop();
|
client.stop();
|
||||||
|
ESP_LOGI(TAG, "OTA update finished");
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
abort:
|
||||||
|
client.stop();
|
||||||
|
ESP_LOGI(TAG, "OTA update failed");
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
retry:
|
||||||
|
return 1;
|
||||||
|
|
||||||
} // do_ota_update
|
} // do_ota_update
|
||||||
|
|
||||||
void display(const uint8_t row, const std::string status,
|
void display(const uint8_t row, const std::string status,
|
||||||
@ -329,45 +316,29 @@ void display(const uint8_t row, const std::string status,
|
|||||||
|
|
||||||
#ifdef HAS_DISPLAY
|
#ifdef HAS_DISPLAY
|
||||||
// callback function to show download progress while streaming data
|
// callback function to show download progress while streaming data
|
||||||
void show_progress (unsigned long current, unsigned long size) {
|
void show_progress(unsigned long current, unsigned long size) {
|
||||||
char buf[17];
|
char buf[17];
|
||||||
snprintf(buf, 17, "%-9lu (%3lu%%)", current, current * 100 / size);
|
snprintf(buf, 17, "%-9lu (%3lu%%)", current, current * 100 / size);
|
||||||
display(4, "**", buf);
|
display(4, "**", buf);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// helper function to compare two versions. Returns 1 if v2 is
|
// helper function to convert strings into lower case
|
||||||
|
bool comp(char s1, char s2) { return tolower(s1) < tolower(s2); }
|
||||||
|
|
||||||
|
// helper function to lexicographically compare two versions. Returns 1 if v2 is
|
||||||
// smaller, -1 if v1 is smaller, 0 if equal
|
// smaller, -1 if v1 is smaller, 0 if equal
|
||||||
|
|
||||||
int version_compare(const String v1, const String v2) {
|
int version_compare(const String v1, const String v2) {
|
||||||
// vnum stores each numeric part of version
|
|
||||||
int vnum1 = 0, vnum2 = 0;
|
|
||||||
|
|
||||||
// loop until both string are processed
|
if (v1 == v2)
|
||||||
for (int i = 0, j = 0; (i < v1.length() || j < v2.length());) {
|
|
||||||
// storing numeric part of version 1 in vnum1
|
|
||||||
while (i < v1.length() && v1[i] != '.') {
|
|
||||||
vnum1 = vnum1 * 10 + (v1[i] - '0');
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// storing numeric part of version 2 in vnum2
|
|
||||||
while (j < v2.length() && v2[j] != '.') {
|
|
||||||
vnum2 = vnum2 * 10 + (v2[j] - '0');
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vnum1 > vnum2)
|
|
||||||
return 1;
|
|
||||||
if (vnum2 > vnum1)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
// if equal, reset variables and go for next numeric
|
|
||||||
// part
|
|
||||||
vnum1 = vnum2 = 0;
|
|
||||||
i++;
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
const char *a1 = v1.c_str(), *a2 = v2.c_str();
|
||||||
|
|
||||||
|
if (lexicographical_compare(a1, a1 + strlen(a1), a2, a2 + strlen(a2), comp))
|
||||||
|
return -1;
|
||||||
|
else
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // USE_OTA
|
#endif // USE_OTA
|
@ -59,6 +59,7 @@
|
|||||||
#define LPP1PORT 1 // Port for Cayenne LPP 1.0 dynamic sensor encoding
|
#define LPP1PORT 1 // Port for Cayenne LPP 1.0 dynamic sensor encoding
|
||||||
#define LPP2PORT 2 // Port for Cayenne LPP 2.0 packed sensor encoding
|
#define LPP2PORT 2 // Port for Cayenne LPP 2.0 packed sensor encoding
|
||||||
#define BEACONPORT 6 // Port on which device sends beacon alarms
|
#define BEACONPORT 6 // Port on which device sends beacon alarms
|
||||||
|
#define BMEPORT 7 // Port on which device sends BME680 sensor data
|
||||||
|
|
||||||
// Some hardware settings
|
// Some hardware settings
|
||||||
#define RGBLUMINOSITY 30 // RGB LED luminosity [default = 30%]
|
#define RGBLUMINOSITY 30 // RGB LED luminosity [default = 30%]
|
||||||
@ -67,9 +68,13 @@
|
|||||||
|
|
||||||
// OTA settings
|
// OTA settings
|
||||||
#define USE_OTA 1 // Comment out to disable OTA update
|
#define USE_OTA 1 // Comment out to disable OTA update
|
||||||
#define WIFI_MAX_TRY 20 // maximum number of wifi connect attempts for OTA update [default = 20]
|
#define WIFI_MAX_TRY 5 // maximum number of wifi connect attempts for OTA update [default = 20]
|
||||||
#define FLASH_MAX_TRY 3 // maximum number of attempts for writing update binary to flash [default = 3]
|
#define OTA_MAX_TRY 5 // maximum number of attempts for OTA download and write to flash [default = 3]
|
||||||
#define OTA_MIN_BATT 3700 // minimum battery level vor OTA [millivolt]
|
#define OTA_MIN_BATT 3600 // minimum battery level for OTA [millivolt]
|
||||||
|
#define RESPONSE_TIMEOUT_MS 60000 // firmware binary server connection timeout [milliseconds]
|
||||||
|
|
||||||
|
// setting for syncing time of node
|
||||||
|
//#define TIME_SYNC_INTERVAL 60 // sync time each ... minutes [default = 60], comment out means off
|
||||||
|
|
||||||
// LMIC settings
|
// LMIC settings
|
||||||
// moved to src/lmic_config.h
|
// moved to src/lmic_config.h
|
115
src/payload.cpp
115
src/payload.cpp
@ -75,8 +75,8 @@ void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime, float cputemp,
|
|||||||
buffer[cursor++] = (byte)(reset2);
|
buffer[cursor++] = (byte)(reset2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAS_GPS
|
|
||||||
void PayloadConvert::addGPS(gpsStatus_t value) {
|
void PayloadConvert::addGPS(gpsStatus_t value) {
|
||||||
|
#ifdef HAS_GPS
|
||||||
buffer[cursor++] = (byte)((value.latitude & 0xFF000000) >> 24);
|
buffer[cursor++] = (byte)((value.latitude & 0xFF000000) >> 24);
|
||||||
buffer[cursor++] = (byte)((value.latitude & 0x00FF0000) >> 16);
|
buffer[cursor++] = (byte)((value.latitude & 0x00FF0000) >> 16);
|
||||||
buffer[cursor++] = (byte)((value.latitude & 0x0000FF00) >> 8);
|
buffer[cursor++] = (byte)((value.latitude & 0x0000FF00) >> 8);
|
||||||
@ -90,14 +90,32 @@ void PayloadConvert::addGPS(gpsStatus_t value) {
|
|||||||
buffer[cursor++] = lowByte(value.hdop);
|
buffer[cursor++] = lowByte(value.hdop);
|
||||||
buffer[cursor++] = highByte(value.altitude);
|
buffer[cursor++] = highByte(value.altitude);
|
||||||
buffer[cursor++] = lowByte(value.altitude);
|
buffer[cursor++] = lowByte(value.altitude);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
|
void PayloadConvert::addBME(bmeStatus_t value) {
|
||||||
|
#ifdef HAS_BME
|
||||||
|
int16_t temperature = (int16_t)(value.temperature); // float -> int
|
||||||
|
uint16_t humidity = (uint16_t)(value.humidity); // float -> int
|
||||||
|
buffer[cursor++] = highByte(temperature);
|
||||||
|
buffer[cursor++] = lowByte(temperature);
|
||||||
|
buffer[cursor++] = highByte(value.pressure);
|
||||||
|
buffer[cursor++] = lowByte(value.pressure);
|
||||||
|
buffer[cursor++] = highByte(humidity);
|
||||||
|
buffer[cursor++] = lowByte(humidity);
|
||||||
|
buffer[cursor++] = highByte(value.gas_resistance);
|
||||||
|
buffer[cursor++] = lowByte(value.gas_resistance);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void PayloadConvert::addButton(uint8_t value) {
|
||||||
#ifdef HAS_BUTTON
|
#ifdef HAS_BUTTON
|
||||||
void PayloadConvert::addButton(uint8_t value) { buffer[cursor++] = value; }
|
buffer[cursor++] = value;
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/* ---------------- packed format with LoRa serialization Encoder ---------- */
|
/* ---------------- packed format with LoRa serialization Encoder ----------
|
||||||
|
*/
|
||||||
// derived from
|
// derived from
|
||||||
// https://github.com/thesolarnomad/lora-serialization/blob/master/src/LoraEncoder.cpp
|
// https://github.com/thesolarnomad/lora-serialization/blob/master/src/LoraEncoder.cpp
|
||||||
|
|
||||||
@ -138,18 +156,29 @@ void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime, float cputemp,
|
|||||||
writeUint8(reset2);
|
writeUint8(reset2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAS_GPS
|
|
||||||
void PayloadConvert::addGPS(gpsStatus_t value) {
|
void PayloadConvert::addGPS(gpsStatus_t value) {
|
||||||
|
#ifdef HAS_GPS
|
||||||
writeLatLng(value.latitude, value.longitude);
|
writeLatLng(value.latitude, value.longitude);
|
||||||
writeUint8(value.satellites);
|
writeUint8(value.satellites);
|
||||||
writeUint16(value.hdop);
|
writeUint16(value.hdop);
|
||||||
writeUint16(value.altitude);
|
writeUint16(value.altitude);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAS_BUTTON
|
void PayloadConvert::addBME(bmeStatus_t value) {
|
||||||
void PayloadConvert::addButton(uint8_t value) { writeUint8(value); }
|
#ifdef HAS_BME
|
||||||
|
writeTemperature(value.temperature);
|
||||||
|
writeUint16(value.pressure);
|
||||||
|
writeHumidity(value.humidity);
|
||||||
|
writeUint16(value.gas_resistance);
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void PayloadConvert::addButton(uint8_t value) {
|
||||||
|
#ifdef HAS_BUTTON
|
||||||
|
writeUint8(value);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void PayloadConvert::intToBytes(uint8_t pos, int32_t i, uint8_t byteSize) {
|
void PayloadConvert::intToBytes(uint8_t pos, int32_t i, uint8_t byteSize) {
|
||||||
for (uint8_t x = 0; x < byteSize; x++) {
|
for (uint8_t x = 0; x < byteSize; x++) {
|
||||||
@ -162,7 +191,7 @@ void PayloadConvert::writeUptime(uint64_t uptime) {
|
|||||||
intToBytes(cursor, uptime, 8);
|
intToBytes(cursor, uptime, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PayloadConvert::writeVersion(char * version) {
|
void PayloadConvert::writeVersion(char *version) {
|
||||||
memcpy(buffer + cursor, version, 10);
|
memcpy(buffer + cursor, version, 10);
|
||||||
cursor += 10;
|
cursor += 10;
|
||||||
}
|
}
|
||||||
@ -212,8 +241,10 @@ void PayloadConvert::writeBitmap(bool a, bool b, bool c, bool d, bool e, bool f,
|
|||||||
writeUint8(bitmap);
|
writeUint8(bitmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---------------- Cayenne LPP format ---------- */
|
/* ---------------- Cayenne LPP 2.0 format ---------- */
|
||||||
// http://community.mydevices.com/t/cayenne-lpp-2-0/7510
|
// see specs http://community.mydevices.com/t/cayenne-lpp-2-0/7510
|
||||||
|
// PAYLOAD_ENCODER == 3 -> Dynamic Sensor Payload, using channels -> FPort 1
|
||||||
|
// PAYLOAD_ENCODER == 4 -> Packed Sensor Payload, not using channels -> FPort 2
|
||||||
|
|
||||||
#elif (PAYLOAD_ENCODER == 3 || PAYLOAD_ENCODER == 4)
|
#elif (PAYLOAD_ENCODER == 3 || PAYLOAD_ENCODER == 4)
|
||||||
|
|
||||||
@ -221,13 +252,15 @@ void PayloadConvert::addCount(uint16_t value1, uint16_t value2) {
|
|||||||
#if (PAYLOAD_ENCODER == 3)
|
#if (PAYLOAD_ENCODER == 3)
|
||||||
buffer[cursor++] = LPP_COUNT_WIFI_CHANNEL;
|
buffer[cursor++] = LPP_COUNT_WIFI_CHANNEL;
|
||||||
#endif
|
#endif
|
||||||
buffer[cursor++] = LPP_LUMINOSITY; // workaround, type meter not found?
|
buffer[cursor++] =
|
||||||
|
LPP_LUMINOSITY; // workaround since cayenne has no data type meter
|
||||||
buffer[cursor++] = highByte(value1);
|
buffer[cursor++] = highByte(value1);
|
||||||
buffer[cursor++] = lowByte(value1);
|
buffer[cursor++] = lowByte(value1);
|
||||||
#if (PAYLOAD_ENCODER == 3)
|
#if (PAYLOAD_ENCODER == 3)
|
||||||
buffer[cursor++] = LPP_COUNT_BLE_CHANNEL;
|
buffer[cursor++] = LPP_COUNT_BLE_CHANNEL;
|
||||||
#endif
|
#endif
|
||||||
buffer[cursor++] = LPP_LUMINOSITY; // workaround, type meter not found?
|
buffer[cursor++] =
|
||||||
|
LPP_LUMINOSITY; // workaround since cayenne has no data type meter
|
||||||
buffer[cursor++] = highByte(value2);
|
buffer[cursor++] = highByte(value2);
|
||||||
buffer[cursor++] = lowByte(value2);
|
buffer[cursor++] = lowByte(value2);
|
||||||
}
|
}
|
||||||
@ -264,17 +297,18 @@ void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime, float celsius,
|
|||||||
buffer[cursor++] = LPP_ANALOG_INPUT;
|
buffer[cursor++] = LPP_ANALOG_INPUT;
|
||||||
buffer[cursor++] = highByte(volt);
|
buffer[cursor++] = highByte(volt);
|
||||||
buffer[cursor++] = lowByte(volt);
|
buffer[cursor++] = lowByte(volt);
|
||||||
#endif
|
#endif // HAS_BATTERY_PROBE
|
||||||
|
|
||||||
#if (PAYLOAD_ENCODER == 3)
|
#if (PAYLOAD_ENCODER == 3)
|
||||||
buffer[cursor++] = LPP_TEMP_CHANNEL;
|
buffer[cursor++] = LPP_TEMPERATURE_CHANNEL;
|
||||||
#endif
|
#endif
|
||||||
buffer[cursor++] = LPP_TEMPERATURE;
|
buffer[cursor++] = LPP_TEMPERATURE;
|
||||||
buffer[cursor++] = highByte(temp);
|
buffer[cursor++] = highByte(temp);
|
||||||
buffer[cursor++] = lowByte(temp);
|
buffer[cursor++] = lowByte(temp);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAS_GPS
|
|
||||||
void PayloadConvert::addGPS(gpsStatus_t value) {
|
void PayloadConvert::addGPS(gpsStatus_t value) {
|
||||||
|
#ifdef HAS_GPS
|
||||||
int32_t lat = value.latitude / 100;
|
int32_t lat = value.latitude / 100;
|
||||||
int32_t lon = value.longitude / 100;
|
int32_t lon = value.longitude / 100;
|
||||||
int32_t alt = value.altitude * 100;
|
int32_t alt = value.altitude * 100;
|
||||||
@ -287,22 +321,61 @@ void PayloadConvert::addGPS(gpsStatus_t value) {
|
|||||||
buffer[cursor++] = (byte)((lat & 0x0000FF));
|
buffer[cursor++] = (byte)((lat & 0x0000FF));
|
||||||
buffer[cursor++] = (byte)((lon & 0xFF0000) >> 16);
|
buffer[cursor++] = (byte)((lon & 0xFF0000) >> 16);
|
||||||
buffer[cursor++] = (byte)((lon & 0x00FF00) >> 8);
|
buffer[cursor++] = (byte)((lon & 0x00FF00) >> 8);
|
||||||
buffer[cursor++] = (byte)((lon & 0x0000FF));
|
buffer[cursor++] = (byte)(lon & 0x0000FF);
|
||||||
buffer[cursor++] = (byte)((alt & 0xFF0000) >> 16);
|
buffer[cursor++] = (byte)((alt & 0xFF0000) >> 16);
|
||||||
buffer[cursor++] = (byte)((alt & 0x00FF00) >> 8);
|
buffer[cursor++] = (byte)((alt & 0x00FF00) >> 8);
|
||||||
buffer[cursor++] = (byte)((alt & 0x0000FF));
|
buffer[cursor++] = (byte)(alt & 0x0000FF);
|
||||||
|
#endif // HAS_GPS
|
||||||
|
}
|
||||||
|
|
||||||
|
void PayloadConvert::addBME(bmeStatus_t value) {
|
||||||
|
#ifdef HAS_BME
|
||||||
|
|
||||||
|
// data value conversions to meet cayenne data type definition
|
||||||
|
// 0.1°C per bit => -3276,7 .. +3276,7 °C
|
||||||
|
int16_t temperature = (int16_t)(value.temperature * 10.0);
|
||||||
|
// 0.1 hPa per bit => 0 .. 6553,6 hPa
|
||||||
|
uint16_t pressure = value.pressure * 10;
|
||||||
|
// 0.5% per bit => 0 .. 128 %C
|
||||||
|
uint8_t humidity = (uint8_t)(value.humidity * 2.0);
|
||||||
|
// 0.01 Ohm per bit => 0 .. 655,36 Ohm
|
||||||
|
uint16_t gas = value.gas_resistance * 100;
|
||||||
|
|
||||||
|
#if (PAYLOAD_ENCODER == 3)
|
||||||
|
buffer[cursor++] = LPP_TEMPERATURE_CHANNEL;
|
||||||
|
#endif
|
||||||
|
buffer[cursor++] = LPP_TEMPERATURE; // 2 bytes 0.1 °C Signed MSB
|
||||||
|
buffer[cursor++] = highByte(temperature);
|
||||||
|
buffer[cursor++] = lowByte(temperature);
|
||||||
|
#if (PAYLOAD_ENCODER == 3)
|
||||||
|
buffer[cursor++] = LPP_BAROMETER_CHANNEL;
|
||||||
|
#endif
|
||||||
|
buffer[cursor++] = LPP_BAROMETER; // 2 bytes 0.1 hPa Unsigned MSB
|
||||||
|
buffer[cursor++] = highByte(pressure);
|
||||||
|
buffer[cursor++] = lowByte(pressure);
|
||||||
|
#if (PAYLOAD_ENCODER == 3)
|
||||||
|
buffer[cursor++] = LPP_HUMIDITY_CHANNEL;
|
||||||
|
#endif
|
||||||
|
buffer[cursor++] = LPP_HUMIDITY; // 1 byte 0.5 % Unsigned
|
||||||
|
buffer[cursor++] = humidity;
|
||||||
|
#if (PAYLOAD_ENCODER == 3)
|
||||||
|
buffer[cursor++] = LPP_GAS_CHANNEL;
|
||||||
|
#endif
|
||||||
|
buffer[cursor++] = LPP_ANALOG_INPUT; // 2 bytes 0.01 Signed
|
||||||
|
buffer[cursor++] = highByte(gas);
|
||||||
|
buffer[cursor++] = lowByte(gas);
|
||||||
|
#endif // HAS_BME
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAS_BUTTON
|
|
||||||
void PayloadConvert::addButton(uint8_t value) {
|
void PayloadConvert::addButton(uint8_t value) {
|
||||||
|
#ifdef HAS_BUTTON
|
||||||
#if (PAYLOAD_ENCODER == 3)
|
#if (PAYLOAD_ENCODER == 3)
|
||||||
buffer[cursor++] = LPP_BUTTON_CHANNEL;
|
buffer[cursor++] = LPP_BUTTON_CHANNEL;
|
||||||
#endif
|
#endif
|
||||||
buffer[cursor++] = LPP_DIGITAL_INPUT;
|
buffer[cursor++] = LPP_DIGITAL_INPUT;
|
||||||
buffer[cursor++] = value;
|
buffer[cursor++] = value;
|
||||||
|
#endif // HAS_BUTTON
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#error "No valid payload converter defined"
|
#error "No valid payload converter defined"
|
||||||
|
@ -8,7 +8,10 @@ static const char TAG[] = "main";
|
|||||||
// helper function
|
// helper function
|
||||||
void do_reset() {
|
void do_reset() {
|
||||||
ESP_LOGI(TAG, "Remote command: restart device");
|
ESP_LOGI(TAG, "Remote command: restart device");
|
||||||
|
if (irqHandlerTask != NULL)
|
||||||
|
vTaskDelete(irqHandlerTask);
|
||||||
LMIC_shutdown();
|
LMIC_shutdown();
|
||||||
|
vTaskDelay(3000 / portTICK_PERIOD_MS);
|
||||||
esp_restart();
|
esp_restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,7 +211,7 @@ void get_config(uint8_t val[]) {
|
|||||||
ESP_LOGI(TAG, "Remote command: get device configuration");
|
ESP_LOGI(TAG, "Remote command: get device configuration");
|
||||||
payload.reset();
|
payload.reset();
|
||||||
payload.addConfig(cfg);
|
payload.addConfig(cfg);
|
||||||
SendData(CONFIGPORT);
|
SendPayload(CONFIGPORT);
|
||||||
};
|
};
|
||||||
|
|
||||||
void get_status(uint8_t val[]) {
|
void get_status(uint8_t val[]) {
|
||||||
@ -222,7 +225,7 @@ void get_status(uint8_t val[]) {
|
|||||||
payload.addStatus(voltage, uptime() / 1000, temperatureRead(),
|
payload.addStatus(voltage, uptime() / 1000, temperatureRead(),
|
||||||
ESP.getFreeHeap(), rtc_get_reset_reason(0),
|
ESP.getFreeHeap(), rtc_get_reset_reason(0),
|
||||||
rtc_get_reset_reason(1));
|
rtc_get_reset_reason(1));
|
||||||
SendData(STATUSPORT);
|
SendPayload(STATUSPORT);
|
||||||
};
|
};
|
||||||
|
|
||||||
void get_gps(uint8_t val[]) {
|
void get_gps(uint8_t val[]) {
|
||||||
@ -231,12 +234,23 @@ void get_gps(uint8_t val[]) {
|
|||||||
gps_read();
|
gps_read();
|
||||||
payload.reset();
|
payload.reset();
|
||||||
payload.addGPS(gps_status);
|
payload.addGPS(gps_status);
|
||||||
SendData(GPSPORT);
|
SendPayload(GPSPORT);
|
||||||
#else
|
#else
|
||||||
ESP_LOGW(TAG, "GPS function not supported");
|
ESP_LOGW(TAG, "GPS function not supported");
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void get_bme(uint8_t val[]) {
|
||||||
|
ESP_LOGI(TAG, "Remote command: get bme680 sensor data");
|
||||||
|
#ifdef HAS_BME
|
||||||
|
payload.reset();
|
||||||
|
payload.addBME(bme_status);
|
||||||
|
SendPayload(BMEPORT);
|
||||||
|
#else
|
||||||
|
ESP_LOGW(TAG, "BME680 sensor not supported");
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
// assign previously defined functions to set of numeric remote commands
|
// assign previously defined functions to set of numeric remote commands
|
||||||
// format: opcode, function, #bytes params,
|
// format: opcode, function, #bytes params,
|
||||||
// flag (true = do make settings persistent / false = don't)
|
// flag (true = do make settings persistent / false = don't)
|
||||||
@ -252,7 +266,8 @@ cmd_t table[] = {
|
|||||||
{0x0f, set_wifiant, 1, true}, {0x10, set_rgblum, 1, true},
|
{0x0f, set_wifiant, 1, true}, {0x10, set_rgblum, 1, true},
|
||||||
{0x11, set_monitor, 1, true}, {0x12, set_beacon, 7, false},
|
{0x11, set_monitor, 1, true}, {0x12, set_beacon, 7, false},
|
||||||
{0x80, get_config, 0, false}, {0x81, get_status, 0, false},
|
{0x80, get_config, 0, false}, {0x81, get_status, 0, false},
|
||||||
{0x84, get_gps, 0, false}};
|
{0x84, get_gps, 0, false}, {0x85, get_bme, 0, false},
|
||||||
|
};
|
||||||
|
|
||||||
const uint8_t cmdtablesize =
|
const uint8_t cmdtablesize =
|
||||||
sizeof(table) / sizeof(table[0]); // number of commands in command table
|
sizeof(table) / sizeof(table[0]); // number of commands in command table
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
// Basic Config
|
// Basic Config
|
||||||
#include "globals.h"
|
#include "senddata.h"
|
||||||
|
|
||||||
// put data to send in RTos Queues used for transmit over channels Lora and SPI
|
// put data to send in RTos Queues used for transmit over channels Lora and SPI
|
||||||
void SendData(uint8_t port) {
|
void SendPayload(uint8_t port) {
|
||||||
|
|
||||||
MessageBuffer_t SendBuffer;
|
MessageBuffer_t SendBuffer; // contains MessageSize, MessagePort, Message[]
|
||||||
|
|
||||||
SendBuffer.MessageSize = payload.getSize();
|
SendBuffer.MessageSize = payload.getSize();
|
||||||
SendBuffer.MessagePort = PAYLOAD_ENCODER <= 2
|
SendBuffer.MessagePort = PAYLOAD_ENCODER <= 2
|
||||||
@ -12,36 +12,27 @@ void SendData(uint8_t port) {
|
|||||||
: (PAYLOAD_ENCODER == 4 ? LPP2PORT : LPP1PORT);
|
: (PAYLOAD_ENCODER == 4 ? LPP2PORT : LPP1PORT);
|
||||||
memcpy(SendBuffer.Message, payload.getBuffer(), payload.getSize());
|
memcpy(SendBuffer.Message, payload.getBuffer(), payload.getSize());
|
||||||
|
|
||||||
// enqueue message in LoRa send queue
|
// enqueue message in device's send queues
|
||||||
#ifdef HAS_LORA
|
lora_enqueuedata(&SendBuffer);
|
||||||
if (xQueueSendToBack(LoraSendQueue, (void *)&SendBuffer, (TickType_t)0) ==
|
spi_enqueuedata(&SendBuffer);
|
||||||
pdTRUE)
|
|
||||||
ESP_LOGI(TAG, "%d bytes enqueued to send on LoRa", payload.getSize());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// enqueue message in SPI send queue
|
} // SendPayload
|
||||||
#ifdef HAS_SPI
|
|
||||||
if (xQueueSendToBack(SPISendQueue, (void *)&SendBuffer, (TickType_t)0) ==
|
|
||||||
pdTRUE)
|
|
||||||
ESP_LOGI(TAG, "%d bytes enqueued to send on SPI", payload.getSize());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// clear counter if not in cumulative counter mode
|
|
||||||
if ((port == COUNTERPORT) && (cfg.countermode != 1)) {
|
|
||||||
reset_counters(); // clear macs container and reset all counters
|
|
||||||
get_salt(); // get new salt for salting hashes
|
|
||||||
ESP_LOGI(TAG, "Counter cleared");
|
|
||||||
}
|
|
||||||
} // SendData
|
|
||||||
|
|
||||||
// interrupt triggered function to prepare payload to send
|
// interrupt triggered function to prepare payload to send
|
||||||
void sendPayload() {
|
void sendCounter() {
|
||||||
|
|
||||||
// append counter data to payload
|
// append counter data to payload
|
||||||
payload.reset();
|
payload.reset();
|
||||||
payload.addCount(macs_wifi, cfg.blescan ? macs_ble : 0);
|
payload.addCount(macs_wifi, cfg.blescan ? macs_ble : 0);
|
||||||
// append GPS data, if present
|
// append GPS data, if present
|
||||||
|
|
||||||
|
// clear counter if not in cumulative counter mode
|
||||||
|
if (cfg.countermode != 1) {
|
||||||
|
reset_counters(); // clear macs container and reset all counters
|
||||||
|
get_salt(); // get new salt for salting hashes
|
||||||
|
ESP_LOGI(TAG, "Counter cleared");
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef HAS_GPS
|
#ifdef HAS_GPS
|
||||||
// show NMEA data in debug mode, useful for debugging GPS on board
|
// show NMEA data in debug mode, useful for debugging GPS on board
|
||||||
// connection
|
// connection
|
||||||
@ -59,14 +50,18 @@ void sendPayload() {
|
|||||||
ESP_LOGD(TAG, "No valid GPS position or GPS data mode disabled");
|
ESP_LOGD(TAG, "No valid GPS position or GPS data mode disabled");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
SendData(COUNTERPORT);
|
SendPayload(COUNTERPORT);
|
||||||
} // sendpayload()
|
|
||||||
|
// if we have MEMS sensor, send sensor data in separate frame
|
||||||
|
#ifdef HAS_BME
|
||||||
|
payload.reset();
|
||||||
|
payload.addBME(bme_status);
|
||||||
|
SendPayload(BMEPORT);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // sendCounter()
|
||||||
|
|
||||||
void flushQueues() {
|
void flushQueues() {
|
||||||
#ifdef HAS_LORA
|
lora_queuereset();
|
||||||
xQueueReset(LoraSendQueue);
|
spi_queuereset();
|
||||||
#endif
|
|
||||||
#ifdef HAS_SPI
|
|
||||||
xQueueReset(SPISendQueue);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
@ -1,72 +0,0 @@
|
|||||||
#ifdef HAS_SPI
|
|
||||||
|
|
||||||
#include "spisend.h"
|
|
||||||
|
|
||||||
// Local logging tag
|
|
||||||
static const char TAG[] = "main";
|
|
||||||
|
|
||||||
MessageBuffer_t SendBuffer;
|
|
||||||
|
|
||||||
QueueHandle_t SPISendQueue;
|
|
||||||
TaskHandle_t SpiTask;
|
|
||||||
|
|
||||||
// SPI feed Task
|
|
||||||
void spi_loop(void *pvParameters) {
|
|
||||||
|
|
||||||
uint8_t buf[32];
|
|
||||||
|
|
||||||
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
// check if data to send on SPI interface
|
|
||||||
if (xQueueReceive(SPISendQueue, &SendBuffer, (TickType_t)0) == pdTRUE) {
|
|
||||||
hal_spi_write(SendBuffer.MessagePort, SendBuffer.Message,
|
|
||||||
SendBuffer.MessageSize);
|
|
||||||
ESP_LOGI(TAG, "%d bytes sent to SPI", SendBuffer.MessageSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if command is received on SPI command port, then call interpreter
|
|
||||||
hal_spi_read(RCMDPORT, buf, 32);
|
|
||||||
if (buf[0])
|
|
||||||
rcommand(buf, sizeof(buf));
|
|
||||||
|
|
||||||
vTaskDelay(2 / portTICK_PERIOD_MS); // yield to CPU
|
|
||||||
} // end of infinite loop
|
|
||||||
|
|
||||||
vTaskDelete(NULL); // shoud never be reached
|
|
||||||
|
|
||||||
} // spi_loop()
|
|
||||||
|
|
||||||
// SPI hardware abstraction layer
|
|
||||||
|
|
||||||
void hal_spi_init() { SPI.begin(SCK, MISO, MOSI, SS); }
|
|
||||||
|
|
||||||
void hal_spi_trx(uint8_t port, uint8_t *buf, int len, uint8_t is_read) {
|
|
||||||
|
|
||||||
SPISettings settings(1E6, MSBFIRST, SPI_MODE0);
|
|
||||||
SPI.beginTransaction(settings);
|
|
||||||
digitalWrite(SS, 0);
|
|
||||||
|
|
||||||
SPI.transfer(port);
|
|
||||||
|
|
||||||
for (uint8_t i = 0; i < len; i++) {
|
|
||||||
uint8_t *p = buf + i;
|
|
||||||
uint8_t data = is_read ? 0x00 : *p;
|
|
||||||
data = SPI.transfer(data);
|
|
||||||
if (is_read)
|
|
||||||
*p = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
digitalWrite(SS, 1);
|
|
||||||
SPI.endTransaction();
|
|
||||||
}
|
|
||||||
|
|
||||||
void hal_spi_write(uint8_t port, const uint8_t *buf, int len) {
|
|
||||||
hal_spi_trx(port, (uint8_t *)buf, len, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void hal_spi_read(uint8_t port, uint8_t *buf, int len) {
|
|
||||||
hal_spi_trx(port, buf, len, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // HAS_SPI
|
|
176
src/spislave.cpp
Normal file
176
src/spislave.cpp
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
//////////////////////// ESP32-Paxcounter \\\\\\\\\\\\\\\\\\\\\\\\\\
|
||||||
|
|
||||||
|
Copyright 2018 Christian Ambach <christian.ambach@deutschebahn.com>
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
NOTICE:
|
||||||
|
Parts of the source files in this repository are made available under different
|
||||||
|
licenses. Refer to LICENSE.txt file in repository for more details.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "spislave.h"
|
||||||
|
|
||||||
|
#include <driver/spi_slave.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <rom/crc.h>
|
||||||
|
|
||||||
|
static const char TAG[] = __FILE__;
|
||||||
|
|
||||||
|
#define HEADER_SIZE 4
|
||||||
|
// SPI transaction size needs to be at least 8 bytes and dividable by 4, see
|
||||||
|
// https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/spi_slave.html
|
||||||
|
#define BUFFER_SIZE \
|
||||||
|
(MAX(8, HEADER_SIZE + PAYLOAD_BUFFER_SIZE) + \
|
||||||
|
(PAYLOAD_BUFFER_SIZE % 4 == 0 \
|
||||||
|
? 0 \
|
||||||
|
: 4 - MAX(8, HEADER_SIZE + PAYLOAD_BUFFER_SIZE) % 4))
|
||||||
|
DMA_ATTR uint8_t txbuf[BUFFER_SIZE];
|
||||||
|
DMA_ATTR uint8_t rxbuf[BUFFER_SIZE];
|
||||||
|
|
||||||
|
QueueHandle_t SPISendQueue;
|
||||||
|
|
||||||
|
TaskHandle_t spiTask;
|
||||||
|
|
||||||
|
void spi_slave_task(void *param) {
|
||||||
|
while (1) {
|
||||||
|
MessageBuffer_t msg;
|
||||||
|
size_t transaction_size;
|
||||||
|
|
||||||
|
// clear rx + tx buffers
|
||||||
|
memset(txbuf, 0, sizeof(txbuf));
|
||||||
|
memset(rxbuf, 0, sizeof(rxbuf));
|
||||||
|
|
||||||
|
// wait until data to send arrivey
|
||||||
|
if (xQueueReceive(SPISendQueue, &msg, portMAX_DELAY) != pdTRUE) {
|
||||||
|
ESP_LOGE(TAG, "Premature return from xQueueReceive() with no data!");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill tx buffer with data to send from queue
|
||||||
|
uint8_t *messageType = txbuf + 2;
|
||||||
|
*messageType = msg.MessagePort;
|
||||||
|
uint8_t *messageSize = txbuf + 3;
|
||||||
|
*messageSize = msg.MessageSize;
|
||||||
|
memcpy(txbuf + HEADER_SIZE, &msg.Message, msg.MessageSize);
|
||||||
|
// calculate crc16 checksum over txbuf and insert checksum at pos 0+1 of txbuf
|
||||||
|
uint16_t *crc = (uint16_t *)txbuf;
|
||||||
|
*crc = crc16_be(0, messageType, msg.MessageSize + HEADER_SIZE - 2);
|
||||||
|
|
||||||
|
// set length for spi slave driver
|
||||||
|
transaction_size = HEADER_SIZE + msg.MessageSize;
|
||||||
|
// SPI transaction size needs to be at least 8 bytes and dividable by 4, see
|
||||||
|
// https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/spi_slave.html
|
||||||
|
if (transaction_size % 4 != 0) {
|
||||||
|
transaction_size += (4 - transaction_size % 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepare spi transaction
|
||||||
|
spi_slave_transaction_t spi_transaction = {0};
|
||||||
|
spi_transaction.length = transaction_size * 8;
|
||||||
|
spi_transaction.tx_buffer = txbuf;
|
||||||
|
spi_transaction.rx_buffer = rxbuf;
|
||||||
|
|
||||||
|
// wait until spi master clocks out the data, and read results in rx buffer
|
||||||
|
ESP_LOGI(TAG, "Prepared SPI transaction for %zu byte(s)", transaction_size);
|
||||||
|
ESP_LOG_BUFFER_HEXDUMP(TAG, txbuf, transaction_size, ESP_LOG_DEBUG);
|
||||||
|
esp_err_t ret =
|
||||||
|
spi_slave_transmit(HSPI_HOST, &spi_transaction, portMAX_DELAY);
|
||||||
|
ESP_LOG_BUFFER_HEXDUMP(TAG, rxbuf, transaction_size, ESP_LOG_DEBUG);
|
||||||
|
ESP_LOGI(TAG, "Transaction finished with size %zu bits",
|
||||||
|
spi_transaction.trans_len);
|
||||||
|
|
||||||
|
// check if command was received, then call interpreter with command payload
|
||||||
|
if ((spi_transaction.trans_len) && ((rxbuf[2]) == RCMDPORT)) {
|
||||||
|
rcommand(rxbuf + HEADER_SIZE, spi_transaction.trans_len - HEADER_SIZE);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t spi_init() {
|
||||||
|
#ifndef HAS_SPI
|
||||||
|
return ESP_OK;
|
||||||
|
#else
|
||||||
|
|
||||||
|
SPISendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t));
|
||||||
|
if (SPISendQueue == 0) {
|
||||||
|
ESP_LOGE(TAG, "Could not create SPI send queue. Aborting.");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "SPI send queue created, size %d Bytes",
|
||||||
|
SEND_QUEUE_SIZE * PAYLOAD_BUFFER_SIZE);
|
||||||
|
|
||||||
|
spi_bus_config_t spi_bus_cfg = {.mosi_io_num = SPI_MOSI,
|
||||||
|
.miso_io_num = SPI_MISO,
|
||||||
|
.sclk_io_num = SPI_SCLK,
|
||||||
|
.quadwp_io_num = -1,
|
||||||
|
.quadhd_io_num = -1,
|
||||||
|
.max_transfer_sz = 0,
|
||||||
|
.flags = 0};
|
||||||
|
|
||||||
|
spi_slave_interface_config_t spi_slv_cfg = {.spics_io_num = SPI_CS,
|
||||||
|
.flags = 0,
|
||||||
|
.queue_size = 1,
|
||||||
|
.mode = 0,
|
||||||
|
.post_setup_cb = NULL,
|
||||||
|
.post_trans_cb = NULL};
|
||||||
|
|
||||||
|
// Enable pull-ups on SPI lines so we don't detect rogue pulses when no master
|
||||||
|
// is connected
|
||||||
|
gpio_set_pull_mode(SPI_MOSI, GPIO_PULLUP_ONLY);
|
||||||
|
gpio_set_pull_mode(SPI_SCLK, GPIO_PULLUP_ONLY);
|
||||||
|
gpio_set_pull_mode(SPI_CS, GPIO_PULLUP_ONLY);
|
||||||
|
|
||||||
|
esp_err_t ret =
|
||||||
|
spi_slave_initialize(HSPI_HOST, &spi_bus_cfg, &spi_slv_cfg, 1);
|
||||||
|
|
||||||
|
if (ret == ESP_OK) {
|
||||||
|
ESP_LOGI(TAG, "Starting SPIloop...");
|
||||||
|
xTaskCreate(spi_slave_task, "spiloop", 4096, (void *)NULL, 2, &spiTask);
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "SPI interface initialization failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void spi_enqueuedata(MessageBuffer_t *message) {
|
||||||
|
// enqueue message in SPI send queue
|
||||||
|
#ifdef HAS_SPI
|
||||||
|
BaseType_t ret =
|
||||||
|
xQueueSendToBack(SPISendQueue, (void *)message, (TickType_t)0);
|
||||||
|
if (ret == pdTRUE) {
|
||||||
|
ESP_LOGI(TAG, "%d byte(s) enqueued for SPI interface",
|
||||||
|
message->MessageSize);
|
||||||
|
} else {
|
||||||
|
ESP_LOGW(TAG, "SPI sendqueue is full");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void spi_queuereset(void) {
|
||||||
|
#ifdef HAS_SPI
|
||||||
|
xQueueReset(SPISendQueue);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void spi_housekeeping(void) {
|
||||||
|
#ifdef HAS_SPI
|
||||||
|
ESP_LOGD(TAG, "spiloop %d bytes left", uxTaskGetStackHighWaterMark(spiTask));
|
||||||
|
#endif
|
||||||
|
}
|
@ -1,8 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
this file copied from esp32-arduino library and patched, see PR
|
this file copied from esp32-arduino library and patched, see PR
|
||||||
https://github.com/espressif/arduino-esp32/pull/1886
|
https://github.com/espressif/arduino-esp32/pull/1979
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "update.h"
|
#include "update.h"
|
||||||
@ -95,6 +93,10 @@ void UpdateClass::_reset() {
|
|||||||
_progress = 0;
|
_progress = 0;
|
||||||
_size = 0;
|
_size = 0;
|
||||||
_command = U_FLASH;
|
_command = U_FLASH;
|
||||||
|
|
||||||
|
if(_ledPin != -1) {
|
||||||
|
digitalWrite(_ledPin, !_ledOn); // off
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UpdateClass::canRollBack(){
|
bool UpdateClass::canRollBack(){
|
||||||
@ -113,12 +115,15 @@ bool UpdateClass::rollBack(){
|
|||||||
return _partitionIsBootable(partition) && !esp_ota_set_boot_partition(partition);
|
return _partitionIsBootable(partition) && !esp_ota_set_boot_partition(partition);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UpdateClass::begin(size_t size, int command) {
|
bool UpdateClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) {
|
||||||
if(_size > 0){
|
if(_size > 0){
|
||||||
log_w("already running");
|
log_w("already running");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_ledPin = ledPin;
|
||||||
|
_ledOn = !!ledOn; // 0(LOW) or 1(HIGH)
|
||||||
|
|
||||||
_reset();
|
_reset();
|
||||||
_error = 0;
|
_error = 0;
|
||||||
|
|
||||||
@ -310,7 +315,7 @@ size_t UpdateClass::write(uint8_t *data, size_t len) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t UpdateClass::writeStream(Stream &data) {
|
size_t UpdateClass::writeStream(Stream &data) {
|
||||||
data.setTimeout(20000);
|
data.setTimeout(RESPONSE_TIMEOUT_MS);
|
||||||
size_t written = 0;
|
size_t written = 0;
|
||||||
size_t toRead = 0;
|
size_t toRead = 0;
|
||||||
if(hasError() || !isRunning())
|
if(hasError() || !isRunning())
|
||||||
@ -323,16 +328,32 @@ size_t UpdateClass::writeStream(Stream &data) {
|
|||||||
if (_progress_callback) {
|
if (_progress_callback) {
|
||||||
_progress_callback(0, _size);
|
_progress_callback(0, _size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(_ledPin != -1) {
|
||||||
|
pinMode(_ledPin, OUTPUT);
|
||||||
|
}
|
||||||
|
|
||||||
while(remaining()) {
|
while(remaining()) {
|
||||||
toRead = data.readBytes(_buffer + _bufferLen, (SPI_FLASH_SEC_SIZE - _bufferLen));
|
if(_ledPin != -1) {
|
||||||
|
digitalWrite(_ledPin, _ledOn); // Switch LED on
|
||||||
|
}
|
||||||
|
size_t bytesToRead = SPI_FLASH_SEC_SIZE - _bufferLen;
|
||||||
|
if(bytesToRead > remaining()) {
|
||||||
|
bytesToRead = remaining();
|
||||||
|
}
|
||||||
|
|
||||||
|
toRead = data.readBytes(_buffer + _bufferLen, bytesToRead);
|
||||||
if(toRead == 0) { //Timeout
|
if(toRead == 0) { //Timeout
|
||||||
delay(100);
|
delay(100);
|
||||||
toRead = data.readBytes(_buffer + _bufferLen, (SPI_FLASH_SEC_SIZE - _bufferLen));
|
toRead = data.readBytes(_buffer + _bufferLen, bytesToRead);
|
||||||
if(toRead == 0) { //Timeout
|
if(toRead == 0) { //Timeout
|
||||||
_abort(UPDATE_ERROR_STREAM);
|
_abort(UPDATE_ERROR_STREAM);
|
||||||
return written;
|
return written;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(_ledPin != -1) {
|
||||||
|
digitalWrite(_ledPin, !_ledOn); // Switch LED off
|
||||||
|
}
|
||||||
_bufferLen += toRead;
|
_bufferLen += toRead;
|
||||||
if((_bufferLen == remaining() || _bufferLen == SPI_FLASH_SEC_SIZE) && !_writeBuffer())
|
if((_bufferLen == remaining() || _bufferLen == SPI_FLASH_SEC_SIZE) && !_writeBuffer())
|
||||||
return written;
|
return written;
|
||||||
|
Loading…
Reference in New Issue
Block a user