ttn-featherm0-dht.ino
1 // SPDX-FileCopyrightText: 2015 Thomas Telkamp 2 // SPDX-FileCopyrightText: 2015 Matthijs Kooijman 3 // SPDX-FileCopyrightText: 2018 Terry Moore for MCCI 4 // SPDX-FileCopyrightText: 2018 Brent Rubell for Adafruit Industries 5 // 6 // SPDX-License-Identifier: MIT 7 8 /******************************************************************************* 9 * The Things Network - Sensor Data Example 10 * 11 * Example of sending a valid LoRaWAN packet with DHT22 temperature and 12 * humidity data to The Things Networ using a Feather M0 LoRa. 13 * 14 * Learn Guide: https://learn.adafruit.com/the-things-network-for-feather 15 * 16 * Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman 17 * Copyright (c) 2018 Terry Moore, MCCI 18 * Copyright (c) 2018 Brent Rubell, Adafruit Industries 19 * 20 * Permission is hereby granted, free of charge, to anyone 21 * obtaining a copy of this document and accompanying files, 22 * to do whatever they want with them without any restriction, 23 * including, but not limited to, copying, modification and redistribution. 24 * NO WARRANTY OF ANY KIND IS PROVIDED. 25 *******************************************************************************/ 26 #include <lmic.h> 27 #include <hal/hal.h> 28 #include <SPI.h> 29 30 // include the DHT22 Sensor Library 31 #include "DHT.h" 32 33 // DHT digital pin and sensor type 34 #define DHTPIN 10 35 #define DHTTYPE DHT22 36 37 // This EUI must be in little-endian format, so least-significant-byte 38 // first. When copying an EUI from ttnctl output, this means to reverse 39 // the bytes. For TTN issued EUIs the last bytes should be 0xD5, 0xB3, 40 // 0x70. 41 static const u1_t PROGMEM APPEUI[8] = { FILLMEIN }; 42 void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);} 43 44 // This should also be in little endian format, see above. 45 static const u1_t PROGMEM DEVEUI[8] = { FILLMEIN }; 46 void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);} 47 48 // This key should be in big endian format (or, since it is not really a 49 // number but a block of memory, endianness does not really apply). In 50 // practice, a key taken from the TTN console can be copied as-is. 51 static const u1_t PROGMEM APPKEY[16] = { FILLMEIN }; 52 void os_getDevKey (u1_t* buf) { memcpy_P(buf, APPKEY, 16);} 53 54 // payload to send to TTN gateway 55 static uint8_t payload[5]; 56 static osjob_t sendjob; 57 58 // Schedule TX every this many seconds (might become longer due to duty 59 // cycle limitations). 60 const unsigned TX_INTERVAL = 30; 61 62 // Pin mapping for Adafruit Feather M0 LoRa 63 const lmic_pinmap lmic_pins = { 64 .nss = 8, 65 .rxtx = LMIC_UNUSED_PIN, 66 .rst = 4, 67 .dio = {3, 6, LMIC_UNUSED_PIN}, 68 .rxtx_rx_active = 0, 69 .rssi_cal = 8, // LBT cal for the Adafruit Feather M0 LoRa, in dB 70 .spi_freq = 8000000, 71 }; 72 73 // init. DHT 74 DHT dht(DHTPIN, DHTTYPE); 75 76 void onEvent (ev_t ev) { 77 Serial.print(os_getTime()); 78 Serial.print(": "); 79 switch(ev) { 80 case EV_SCAN_TIMEOUT: 81 Serial.println(F("EV_SCAN_TIMEOUT")); 82 break; 83 case EV_BEACON_FOUND: 84 Serial.println(F("EV_BEACON_FOUND")); 85 break; 86 case EV_BEACON_MISSED: 87 Serial.println(F("EV_BEACON_MISSED")); 88 break; 89 case EV_BEACON_TRACKED: 90 Serial.println(F("EV_BEACON_TRACKED")); 91 break; 92 case EV_JOINING: 93 Serial.println(F("EV_JOINING")); 94 break; 95 case EV_JOINED: 96 Serial.println(F("EV_JOINED")); 97 { 98 u4_t netid = 0; 99 devaddr_t devaddr = 0; 100 u1_t nwkKey[16]; 101 u1_t artKey[16]; 102 LMIC_getSessionKeys(&netid, &devaddr, nwkKey, artKey); 103 Serial.print("netid: "); 104 Serial.println(netid, DEC); 105 Serial.print("devaddr: "); 106 Serial.println(devaddr, HEX); 107 Serial.print("artKey: "); 108 for (int i=0; i<sizeof(artKey); ++i) { 109 if (i != 0) 110 Serial.print("-"); 111 Serial.print(artKey[i], HEX); 112 } 113 Serial.println(""); 114 Serial.print("nwkKey: "); 115 for (int i=0; i<sizeof(nwkKey); ++i) { 116 if (i != 0) 117 Serial.print("-"); 118 Serial.print(nwkKey[i], HEX); 119 } 120 Serial.println(""); 121 } 122 // Disable link check validation (automatically enabled 123 // during join, but because slow data rates change max TX 124 // size, we don't use it in this example. 125 LMIC_setLinkCheckMode(0); 126 break; 127 /* 128 || This event is defined but not used in the code. No 129 || point in wasting codespace on it. 130 || 131 || case EV_RFU1: 132 || Serial.println(F("EV_RFU1")); 133 || break; 134 */ 135 case EV_JOIN_FAILED: 136 Serial.println(F("EV_JOIN_FAILED")); 137 break; 138 case EV_REJOIN_FAILED: 139 Serial.println(F("EV_REJOIN_FAILED")); 140 break; 141 break; 142 case EV_TXCOMPLETE: 143 Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)")); 144 digitalWrite(LED_BUILTIN, LOW); 145 if (LMIC.txrxFlags & TXRX_ACK) 146 Serial.println(F("Received ack")); 147 if (LMIC.dataLen) { 148 Serial.println(F("Received ")); 149 Serial.println(LMIC.dataLen); 150 Serial.println(F(" bytes of payload")); 151 } 152 // Schedule next transmission 153 os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send); 154 break; 155 case EV_LOST_TSYNC: 156 Serial.println(F("EV_LOST_TSYNC")); 157 break; 158 case EV_RESET: 159 Serial.println(F("EV_RESET")); 160 break; 161 case EV_RXCOMPLETE: 162 // data received in ping slot 163 Serial.println(F("EV_RXCOMPLETE")); 164 break; 165 case EV_LINK_DEAD: 166 Serial.println(F("EV_LINK_DEAD")); 167 break; 168 case EV_LINK_ALIVE: 169 Serial.println(F("EV_LINK_ALIVE")); 170 break; 171 /* 172 || This event is defined but not used in the code. No 173 || point in wasting codespace on it. 174 || 175 || case EV_SCAN_FOUND: 176 || Serial.println(F("EV_SCAN_FOUND")); 177 || break; 178 */ 179 case EV_TXSTART: 180 Serial.println(F("EV_TXSTART")); 181 digitalWrite(LED_BUILTIN, HIGH); 182 break; 183 default: 184 Serial.print(F("Unknown event: ")); 185 Serial.println((unsigned) ev); 186 break; 187 } 188 } 189 190 void do_send(osjob_t* j){ 191 // Check if there is not a current TX/RX job running 192 if (LMIC.opmode & OP_TXRXPEND) { 193 Serial.println(F("OP_TXRXPEND, not sending")); 194 } else { 195 // read the temperature from the DHT22 196 float temperature = dht.readTemperature(); 197 Serial.print("Temperature: "); Serial.print(temperature); 198 Serial.println(" *C"); 199 // adjust for the f2sflt16 range (-1 to 1) 200 temperature = temperature / 100; 201 202 // read the humidity from the DHT22 203 float rHumidity = dht.readHumidity(); 204 Serial.print("%RH "); 205 Serial.println(rHumidity); 206 // adjust for the f2sflt16 range (-1 to 1) 207 rHumidity = rHumidity / 100; 208 209 // float -> int 210 // note: this uses the sflt16 datum (https://github.com/mcci-catena/arduino-lmic#sflt16) 211 uint16_t payloadTemp = LMIC_f2sflt16(temperature); 212 // int -> bytes 213 byte tempLow = lowByte(payloadTemp); 214 byte tempHigh = highByte(payloadTemp); 215 // place the bytes into the payload 216 payload[0] = tempLow; 217 payload[1] = tempHigh; 218 219 // float -> int 220 uint16_t payloadHumid = LMIC_f2sflt16(rHumidity); 221 // int -> bytes 222 byte humidLow = lowByte(payloadHumid); 223 byte humidHigh = highByte(payloadHumid); 224 payload[2] = humidLow; 225 payload[3] = humidHigh; 226 227 // prepare upstream data transmission at the next possible time. 228 // transmit on port 1 (the first parameter); you can use any value from 1 to 223 (others are reserved). 229 // don't request an ack (the last parameter, if not zero, requests an ack from the network). 230 // Remember, acks consume a lot of network resources; don't ask for an ack unless you really need it. 231 LMIC_setTxData2(1, payload, sizeof(payload)-1, 0); 232 } 233 // Next TX is scheduled after TX_COMPLETE event. 234 } 235 236 void setup() { 237 delay(5000); 238 while (! Serial); 239 Serial.begin(9600); 240 Serial.println(F("Starting")); 241 242 pinMode(LED_BUILTIN, OUTPUT); 243 // dht init. 244 dht.begin(); 245 246 // LMIC init. 247 os_init(); 248 // Reset the MAC state. Session and pending data transfers will be discarded. 249 LMIC_reset(); 250 // Disable link-check mode and ADR, because ADR tends to complicate testing. 251 LMIC_setLinkCheckMode(0); 252 // Set the data rate to Spreading Factor 7. This is the fastest supported rate for 125 kHz channels, and it 253 // minimizes air time and battery power. Set the transmission power to 14 dBi (25 mW). 254 LMIC_setDrTxpow(DR_SF7,14); 255 // in the US, with TTN, it saves join time if we start on subband 1 (channels 8-15). This will 256 // get overridden after the join by parameters from the network. If working with other 257 // networks or in other regions, this will need to be changed. 258 LMIC_selectSubBand(1); 259 260 // Start job (sending automatically starts OTAA too) 261 do_send(&sendjob); 262 } 263 264 void loop() { 265 // we call the LMIC's runloop processor. This will cause things to happen based on events and time. One 266 // of the things that will happen is callbacks for transmission complete or received messages. We also 267 // use this loop to queue periodic data transmissions. You can put other things here in the `loop()` routine, 268 // but beware that LoRaWAN timing is pretty tight, so if you do more than a few milliseconds of work, you 269 // will want to call `os_runloop_once()` every so often, to keep the radio running. 270 os_runloop_once(); 271 }