Adafruit M0 LoRa¶
Hardware¶
Microcontroller¶
The Adafruit Feather M0 LoRa board is operated by the 32bit ATSAMD21G18 ARM Cortex M0 microcontroller running at 48MHz. It has 256 KB flash memory (to store the program code) and 32 KB of RAM (to store variables, status information, and buffers). The operating voltage of the board is 3.3V (this is important when attaching sensors and other peripherals; they also must operate on 3.3V). The board offers 20 general purpose digital input/output pins (20 GPIOs) with 10 analog input pins (with 12bit analog digital converters (ADC)), one analog output pin, one serial port (programmable Universal Asynchronous Receiver and Transmitter, UART), one I2C port, one SPI port, one USB port. The board comes with an embedded Lithium polymer battery management chip and status indicator led, which allows to directly connect a 3.7V LiPo rechargeable battery that will be automatically recharged when the board is powered over its USB connector. The Adafruit Feather M0 LoRa board is available in German shops from around 37 € to 45 €.
The LoRa transmitter and receiver is encapsulated within an RFM95 module from the company HopeRF. This module uses the LoRa chip SX1276 from the company Semtech and is dedicated to the 868 MHz frequency band. The RFM95 module is connected via SPI interface to the microcontroller. Most of the required connections of the LoRa transceiver pins with the microcontroller are already built-in on the Adafruit Feather M0 LoRa board. However, Digital Pin 6 of the microcontroller must be connected to DIO1 of the LoRa transceiver module in addition using a simple wire. Since the module only implements the LoRa physical layer, the LoRaWAN protocol stack must be implemented in software on the microcontroller. We are using the Arduino library LMIC for that purpose (see below). The implemented LoRaWAN functionality is compatible with LoRaWAN Class A/C.
Sensor¶
We have attached a DHT22 sensor to the microcontroller board, which measures air temperature and humidity. The minimal time interval between two measurements is 2 seconds. All data transfers between the DHT22 and the microcontroller use a single digital line. The sensor data pin is attached to a GPIO pin (here: Digital Pin 12) of the microcontroller. In addition, a so-called pull-up resistor of 4.7k to 10k Ohm must be connected between the data line and VCC (+3.3V). The DHT22 datasheet can be accessed here. A tutorial on how to use the DHT22 sensor with Arduino microcontrollers is provided on this page. The sensor is available in German shops for around 4 € to 10 €.
Software¶
The sensor node has been programmed using the Arduino IDE. Please note, that in the Arduino framework a program is called a ‘Sketch’.
Now download and run the Arduino Sketch for Adafruit M0 LoRa sensor node file in the Arduino IDE. After the sketch has successfully established a connection to The Things Network it reports the air temperature, humidity, and the voltage of a (possibly) attached LiPo battery every 5 minutes. All three values are being encoded in two byte integer values each and then sent as a 6 bytes data packet to the respective TTN application using LoRaWAN port 7. Please note, that LoRaWAN messages can be addressed to ports 1-255 (port 0 is reserved); these ports are similar to port numbers 0-65535 when using the Internet TCP/IP protocol. Voltage and humidity values are always greater or equal to 0, but the temperature value can also become negative. Negative values are represented as a two’s complement; this must be considered in the Payload Decoding Function used in The Things Network (see below).
In between two sensor readings the microcontroller is going into deep sleep mode to save battery power. With a 2000 mAh LiPo battery and the current version of the sketch the system can run for at least 3 months. (Further optimizations would be possible, for example, not switching on the LED on the microcontroller board during LoRa data transmissions.)
The employed RFM95 LoRa module does not provide built-in support of the LoRaWAN protocol. Thus, it has to be implemented on the ARM Cortex M0 microcontroller. We use the IBM LMIC (LoraMAC-in-C) library for Arduino, which can be downloaded from this repository. The ARM Cortex M0 microcontroller has 256 KB of flash memory, which is plenty enough for the LMIC library, the code dealing with the sensors, and even some sophisticated analysis tasks (if required). The source code is given in the following listing:
Note, that the source code is very similar to the source code for the Adafruit Feather 32u4 LoRa board given on the Wiki page LoRaWAN Node - Adafruit 32u4 LoRa. The source code for the Adafruit Feather 32u4 LoRa board has also more detailed comments. It is planned to merge them into a single source code that can be used and compiled for both types of microcontrollers (ATmega32u4 and ARM Cortex M0). The merged source code is already available from LoRaWAN Node - Adafruit 32u4 LoRa, but was not tested with the M0 microcontroller board yet.
Note also, that there is an open issue regarding the deep sleep mode on the ARM Cortex M0 microcontroller in the source code above. During deep sleep mode the (software) timers of the LMIC library are not incremented and after wake-up the library does not recognize that enough time has passed to allow sending another data packet. This built-in mechanism of the LMIC library should ensure that the sensor node does not exceed the maximum duty cycle for LoRaWAN of 1%. This somehow also affects the waiting time for a possible downlink data packet coming from the gateway. As a consequence, the sensor node is not only active for around 2.5 seconds (0.5 seconds to submit the most recent datapacket to the gateway (uplink) and 2 seconds to wait for possible downlink data packets), but sometimes for about 5-6 seconds before it goes back into deep sleep mode (this can be seen from the duration the red LED is activated on the board). These extra seconds awake (with the LED and the LoRa transceiver module switched on) reduce battery lifetime significantly. The ATmega32u4 microcontroller does not have these problems and can go faster back to deep sleep mode. As a result the Adafruit Feather 32u4 LoRa board can run with a 1000 mAh LiPo battery for 5 months and the Adafruit Feather M0 LoRa board with a 2000 mAh LiPo battery for only 3 months.
Services¶
The services used for this sensor-node are:
- TheThingsNetwork service for LoRaWAN network service.
- TheThingsNetwork - OGC SensorWeb integration for uploading LoRaWAN sensor data into OGC infrastructure.
Registration of the sensor node with The Things Network (TTN)¶
The LoRaWAN protocol makes use of a number of different identifiers, addresses, keys, etc. These are required to unambiguously identify devices, applications, as well as to encrypt and decrypt messages. The names and meanings are nicely explained on a dedicated TTN web page.
The sketch given above connects the sensor node with The Things Network (TTN) using the Activation-by-Personalisation (ABP) mode. In this mode, the required keys for data encryption and session management are created manually using the TTN console window and must be pasted into the source code of the sketch provided in software section . In order to get this running, you will need to create a new device <https://www.thethingsnetwork.org/docs/devices/registration.html>`_ in the TTN console window. This assumes that you already have a TTN user account (which needs to be created otherwise). In the settings menu of the newly created device the ABP mode must be selected and the settings must be saved. Then copy the DevAddr, the NwkSKey, and the AppSKey from the TTN console web page of the newly registered device and paste them into the proper places in the sketch above. Please make sure that you choose for each of the three keys the correct byte ordering (MSB for all three keys). A detailed explanation of these steps is given here. Then the sketch can be compiled and uploaded to the Adafruit Feather M0 LoRa microcontroller.
Important hint: everytime the sensor node is reset or being started again, make sure to reset the frame counter of the registered sensor in the TTN console web page of the registered device. The reason is that in LoRaWAN all transmitted data packets have a frame counter, which is incremented after each data frame being sent. This way a LoRaWAN application can avoid receiving and using the same packet again (replay attack). When TTN receives a data packet, it checks if the frame number is higher than the last one received before. If not, the received packet is considered to be old or a replay attack and is discarded. When the sensor node is reset or being started again, its frame counter is also reset to 0, hence, the TTN application assumes that all new packages are old, because their frame counter is lower than the last frame received (before the reset). A manual frame counter reset is only necessary when registering the node using ABP mode. In OTAA mode the frame counter is automatically reset in the sensor node and the TTN network server.
TTN Payload Decoding¶
Everytime a data packet is received by a TTN application a dedicated Javascript function is being called (Payload Decoder Function). This function can be used to decode the received byte string and to create proper Javascript objects or values that can directly be read by humans when looking at the incoming data packet. This is also useful to format the data in a specific way that can then be forwarded to an external application (e.g. a sensor data platform like MyDevices or Thingspeak ). Such a forwarding can be configured in the TTN console in the “Integrations” tab. TTN payload decoder for Adafruit M0 LoRa sensor node given here checks if a packet was received on LoRaWAN port 7 and then assumes that it consists of the 6 bytes encoded as described above. It creates the three Javascript objects ‘temperature’, ‘humidity’, and ‘vbattery’. Each object has two fields: ‘value’ holds the value and ‘uom’ gives the unit of measure. The source code can simply be copied and pasted into the ‘decoder’ tab in the TTN console after having selected the application. Choose the option ‘Custom’ in the ‘Payload Format’ field. Note that when you also want to handle other sensor nodes sending packets on different LoRaWAN ports, then the Payload Decoder Function can be extended after the end of the if (port==7) {…} statement by adding else if (port==8) {…} else if (port==9) {…} etc.
The Things Network - OGC SensorWeb Integration¶
The presented Payload Decoder Function works also with the TTN-OGC SWE Integration for the 52° North Sensor Observation Service (SOS). This software component can be downloaded from this repository. It connects a TTN application with a running transactional Sensor Observation Service 2.0.0 (SOS). Data packets received from TTN are imported into the SOS. The SOS persistently stores sensor data from an arbitrary number of sensor nodes and can be queried for the most recent as well as for historic sensor data readings. The 52° North SOS comes with its own REST API and a nice web client allowing to browse the stored sensor data in a convenient way.
We are running an instance of the 52° North SOS and the TTN-OGC SWE Integration. The web client for this LoRaWAN sensor node can be accessed on this page. Here is a screenshot showing the webclient: (Note that the sensor node was wrongly registered with TTN using the name adafruit-feather-32u4-lora - it should have been adafruit-feather-m0-lora. Hence, while the legend says it is a 32u4 microcontroller in fact it is the M0)
Code files¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 | /*******************************************************************************
* Copyright (c) 2015 Thomas Telkamp and 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 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 config.h.
*
*******************************************************************************/
// #define SERIALDEBUG
#ifdef SERIALDEBUG
#define SERIALDEBUG_PRINT(...) Serial.print(__VA_ARGS__)
#define SERIALDEBUG_PRINTLN(...) Serial.println(__VA_ARGS__)
#else
#define SERIALDEBUG_PRINT(...)
#define SERIALDEBUG_PRINTLN(...)
#endif
#include <lmic.h>
#include <hal/hal.h>
#include <SPI.h>
#include <Adafruit_SleepyDog.h>
// #include <Adafruit_Sensor.h>
#include <DHT.h>
// #include <DHT_U.h>
#define DHTPIN 12 // Pin which is connected to the DHT sensor.
#define DHTTYPE DHT22 // DHT 22 (AM2302)
// DHT_Unified dht(DHTPIN, DHTTYPE);
DHT dht(DHTPIN, DHTTYPE);
#define VBATPIN A7
// LoRaWAN NwkSKey, network session key
// This should be in big-endian (aka msb).
static const PROGMEM u1_t NWKSKEY[16] = {NETWORK_SESSION_KEY_HERE_IN_MSB_FORMAT};
// LoRaWAN AppSKey, application session key
// This should also be in big-endian (aka msb).
static const u1_t PROGMEM APPSKEY[16] = {APPLICATION_SESSION_KEY_HERE_IN_MSB_FORMAT};
// LoRaWAN end-device address (DevAddr)
// See http://thethingsnetwork.org/wiki/AddressSpace
// The library converts the address to network byte order as needed, so this should be in big-endian (aka msb) too.
static const u4_t DEVADDR = 0x260XXXXX ; // <-- 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 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[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA};
static osjob_t sendjob;
// Schedule TX every this many seconds (might become longer due to duty
// cycle limitations).
const unsigned TX_INTERVAL = 1; // seconds transmit cycle plus ...
const unsigned SLEEP_TIME = 60*4+55; // seconds sleep time plus ...
const unsigned MEASURE_TIME = 2; // seconds measuring time should lead to ...
// 5 minute(s) total cycle time
// Pin mapping
const lmic_pinmap lmic_pins = {
.nss = 8,
.rxtx = LMIC_UNUSED_PIN,
.rst = 4,
.dio = {3, 6, LMIC_UNUSED_PIN},
};
void onEvent (ev_t ev) {
// Serial.print(os_getTime());
// Serial.print(": ");
SERIALDEBUG_PRINT(os_getTime());
SERIALDEBUG_PRINT(": ");
switch(ev) {
case EV_SCAN_TIMEOUT:
SERIALDEBUG_PRINTLN(F("EV_SCAN_TIMEOUT"));
break;
case EV_BEACON_FOUND:
SERIALDEBUG_PRINTLN(F("EV_BEACON_FOUND"));
break;
case EV_BEACON_MISSED:
SERIALDEBUG_PRINTLN(F("EV_BEACON_MISSED"));
break;
case EV_BEACON_TRACKED:
SERIALDEBUG_PRINTLN(F("EV_BEACON_TRACKED"));
break;
case EV_JOINING:
SERIALDEBUG_PRINTLN(F("EV_JOINING"));
break;
case EV_JOINED:
SERIALDEBUG_PRINTLN(F("EV_JOINED"));
break;
case EV_RFU1:
SERIALDEBUG_PRINTLN(F("EV_RFU1"));
break;
case EV_JOIN_FAILED:
SERIALDEBUG_PRINTLN(F("EV_JOIN_FAILED"));
break;
case EV_REJOIN_FAILED:
SERIALDEBUG_PRINTLN(F("EV_REJOIN_FAILED"));
break;
case EV_TXCOMPLETE:
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
SERIALDEBUG_PRINTLN(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
if (LMIC.txrxFlags & TXRX_ACK)
SERIALDEBUG_PRINTLN(F("Received ack"));
if (LMIC.dataLen) {
SERIALDEBUG_PRINT(F("Received "));
SERIALDEBUG_PRINT(LMIC.dataLen);
SERIALDEBUG_PRINTLN(F(" bytes of payload"));
}
// Schedule next transmission
os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send);
SERIALDEBUG_PRINTLN("going to sleep now ... ");
// lmic library sleeps automatically after transmission has been completed
for(int i= 0; i < SLEEP_TIME / 16; i++) {
Watchdog.sleep(16000); // maximum seems to be 16 seconds
SERIALDEBUG_PRINT('.');
}
if (SLEEP_TIME % 16) {
Watchdog.sleep((SLEEP_TIME % 16)*1000);
SERIALDEBUG_PRINT('*');
}
SERIALDEBUG_PRINTLN("... woke up again");
break;
case EV_LOST_TSYNC:
SERIALDEBUG_PRINTLN(F("EV_LOST_TSYNC"));
break;
case EV_RESET:
SERIALDEBUG_PRINTLN(F("EV_RESET"));
break;
case EV_RXCOMPLETE:
// data received in ping slot
SERIALDEBUG_PRINTLN(F("EV_RXCOMPLETE"));
break;
case EV_LINK_DEAD:
SERIALDEBUG_PRINTLN(F("EV_LINK_DEAD"));
break;
case EV_LINK_ALIVE:
SERIALDEBUG_PRINTLN(F("EV_LINK_ALIVE"));
break;
default:
SERIALDEBUG_PRINTLN(F("Unknown event"));
break;
}
}
void do_send(osjob_t* j){
// Check if there is not a current TX/RX job running
if (LMIC.opmode & OP_TXRXPEND) {
SERIALDEBUG_PRINTLN(F("OP_TXRXPEND, not sending"));
} else {
// Prepare upstream data transmission at the next possible time.
float temperature, humidity, measuredvbat;
int16_t int16_temperature, int16_humidity, int16_vbat;
// Start a measurement to update the sensor's internal temperature & humidity reading
SERIALDEBUG_PRINTLN("Start measurement...");
temperature = dht.readTemperature();
// delay(2000);
Watchdog.sleep(2000);
// Now read the recently measured temperature (2 secs ago) as Celsius (the default)
temperature = dht.readTemperature();
// Read the recently measured humidity (2 secs ago)
humidity = dht.readHumidity();
SERIALDEBUG_PRINTLN("... finished!");
// Check if any reads failed and exit early (to try again).
if (isnan(humidity) || isnan(temperature)) {
SERIALDEBUG_PRINTLN("Failed to read from DHT sensor!");
for (int i=0; i<5; i++) {
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on by making the voltage HIGH
delay(150);
digitalWrite(LED_BUILTIN, LOW); // turn the LED on by making the voltage HIGH
delay(150);
}
// ok, then wait for another period and try it again
os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send);
} else {
SERIALDEBUG_PRINT("Humidity: ");
SERIALDEBUG_PRINT(humidity);
SERIALDEBUG_PRINT(" %\t");
SERIALDEBUG_PRINT("Temperature: ");
SERIALDEBUG_PRINT(temperature);
SERIALDEBUG_PRINT(" *C ");
int16_temperature = 100*temperature;
int16_humidity = 100*humidity;
mydata[0] = (byte) (int16_temperature >> 8);
mydata[1] = (byte) (int16_temperature & 0x00FF);
mydata[2] = (byte) (int16_humidity >> 8);
mydata[3] = (byte) (int16_humidity & 0x00FF);
measuredvbat = analogRead(VBATPIN);
measuredvbat *= 2; // we divided by 2, so multiply back
measuredvbat *= 3.3; // Multiply by 3.3V, our reference voltage
measuredvbat /= 1023; // convert to voltage
int16_vbat = round(measuredvbat * 100);
mydata[4] = (byte) (int16_vbat >> 8);
mydata[5] = (byte) (int16_vbat & 0x00FF);
SERIALDEBUG_PRINT(" %\t");
SERIALDEBUG_PRINT("Battery Voltage: ");
SERIALDEBUG_PRINTLN(measuredvbat);
// LMIC_setTxData2(1, mydata, sizeof(mydata)-1, 0);
// send the 6 bytes payload to LoRaWAN port 7
LMIC_setTxData2(7, mydata, 6, 0);
SERIALDEBUG_PRINTLN(F("Packet queued"));
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on by making the voltage HIGH
}
// 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);
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
#ifdef SERIALDEBUG
Serial.begin(115200);
// while (!Serial);
#endif
dht.begin();
SERIALDEBUG_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_setClockError(MAX_CLOCK_ERROR * 1 / 100);
// Set static session parameters. Instead of dynamically establishing a session
// by joining the network, precomputed session parameters are be provided.
#ifdef PROGMEM
// On AVR, these values are stored in flash and only copied to RAM
// once. Copy them to a temporary buffer here, LMIC_setSession will
// copy them into a buffer of its own again.
uint8_t appskey[sizeof(APPSKEY)];
uint8_t nwkskey[sizeof(NWKSKEY)];
memcpy_P(appskey, APPSKEY, sizeof(APPSKEY));
memcpy_P(nwkskey, NWKSKEY, sizeof(NWKSKEY));
LMIC_setSession (0x1, DEVADDR, nwkskey, appskey);
#else
// If not running an AVR with PROGMEM, just use the arrays directly
LMIC_setSession (0x1, DEVADDR, NWKSKEY, APPSKEY);
#endif
#if defined(CFG_eu868)
// Set up the channels used by the Things Network, which corresponds
// to the defaults of most gateways. Without this, only three base
// channels from the LoRaWAN specification are used, which certainly
// works, so it is good for debugging, but can overload those
// frequencies, so be sure to configure the full frequency range of
// your network here (unless your network autoconfigures them).
// Setting up channels should happen after LMIC_setSession, as that
// configures the minimal channel set.
// NA-US channels 0-71 are configured automatically
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() {
os_runloop_once();
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | function Decoder (bytes, port) {
var result = {};
var transformers = {};
if (port==7) {
transformers = {
'temperature': function transform (bytes) {
value=bytes[0]*256 + bytes[1];
if (value>=32768) value=value-65536;
return value/100.0;
},
'humidity': function transform (bytes) {
return (bytes[0]*256 + bytes[1])/100.0;
},
'vbattery': function transform (bytes) {
return (bytes[0]*256 + bytes[1])/100.0;
},
}
result['temperature'] = {
value: transformers['temperature'](bytes.slice(0, 2)),
uom: 'Celsius',
}
result['humidity'] = {
value: transformers['humidity'](bytes.slice(2, 4)),
uom: 'Percent',
}
result['vbattery'] = {
value: transformers['vbattery'](bytes.slice(4, 6)),
uom: 'Volt',
}
}
return result;
}
|
References¶
- Adafruit Feather M0 LoRa microntroller
- Adafruit Feather M0 LoRa tutorial
- IBM LMIC (LoraMAC-in-C) library for Arduino
- Adafruit feather m0 lora 900 end-to-end instructions - End Devices (Nodes) - The Things Network
- Getting Started with AdaFruit Feather M0 LoRa - Telenor Start IoT
- GitHub - mcci-catena/arduino-lorawan: User-friendly library for using Feather M0 LoRa with The Things Network and LoRaWAN™
- GitHub - marcuscbehrens/loralife: source code associated with https://www.meetup.com/Internet-of-Things-IoT-LoRaWan-Infrastruktur-4-RheinNeckar/
- Workshop — LoRaTAS
- mikenz/Feather_M0_LoRa: Example Arduino code of using an Adafruit Feather M0 LoRa module to send sensor data
- TTN Ulm - LoRaWAN und LoRa in Ulm | Verkehrszählung mit LoRaWAN und TTN
On battery saving / using the deep sleep mode