Demo of DHT11->mDot->TTN
Dependencies: DHT11 libmDot mbed-rtos mbed
main.cpp
00001 /** mDot_TTN_DHT11 -- The Things Network Temperature & Humidity Sensor 00002 * 00003 * This is a rough demo of mDot+DHT11 on The Things Network. 00004 * This code is not indended as a reference design. 00005 * In particular, it lacks: 00006 * (1) power management 00007 * (2) reasonable transmission period 00008 * (3) adaptive data rate 00009 * 00010 * Uses MultiTech mDot developer board http://www.multitech.com/models/94558010LF 00011 * Requires a MultiTech MultiConnect Conduit http://www.multitech.com/models/94557203LF 00012 * http://www.multitech.net/developer/software/lora/conduit-mlinux-convert-to-basic-packet-forwarder/ 00013 * http://forum.thethingsnetwork.org/t/setting-up-multitech-conduit-gateway-for-ttn/216/35 00014 * 00015 * To receive and visualize this data, 00016 * consider using InitialState and the bridge code here: 00017 * https://github.com/things-nyc/initial-state-example 00018 */ 00019 00020 #include "mbed.h" 00021 #include "DHT11.h" 00022 #include "mDot.h" 00023 #include "MTSLog.h" 00024 #include "MTSText.h" 00025 #include <string> 00026 #include <vector> 00027 00028 using namespace mts; 00029 00030 #define MIN(a,b) (((a)<(b))?(a):(b)) 00031 #define MAX(a,b) (((a)>(b))?(a):(b)) 00032 00033 00034 /** ABP 00035 * Register your device and update these values: 00036 * https://account.thethingsnetwork.org/ 00037 */ 00038 uint8_t AppSKey[16]= { 0x11, 0x6F, 0xA9, 0x2A, 0x46, 0xDE, 0xE6, 0x1D, 0x11, 0xE3, 0x71, 0x37, 0x24, 0xBC, 0x44, 0x1A }; 00039 uint8_t NwkSKey[16]= { 0xF1, 0xA4, 0x78, 0x09, 0x75, 0xE2, 0x3C, 0x2B, 0x76, 0x8F, 0x9F, 0x8D, 0xE0, 0x5E, 0xAA, 0x64 }; 00040 uint8_t NetworkAddr[4]= { 0x68, 0x8E, 0x64, 0xE5 }; 00041 00042 00043 // Some defines for the LoRa configuration 00044 #define LORA_SF mDot::SF_7 00045 #define LORA_ACK 0 00046 #define LORA_TXPOWER 20 00047 static uint8_t config_frequency_sub_band = 2; 00048 00049 // functions for ensuring network endianness (little-endian) 00050 uint16_t hton16(const uint16_t x) 00051 { 00052 uint16_t t = x; 00053 uint8_t * a = (uint8_t*)&t; 00054 a[0] = x>>(8*1); 00055 a[1] = x>>(8*0); 00056 return t; 00057 } 00058 void hton16(uint16_t * x) 00059 { 00060 *x = hton16(*x); 00061 } 00062 00063 00064 00065 // build a transmit buffer (from https://raw.githubusercontent.com/mcci-catena/Catena4410-Sketches/master/catena4410_sensor1/catena4410_sensor1.ino) 00066 class TxBuffer_t 00067 { 00068 public: 00069 uint8_t buf[32]; // this sets the largest buffer size 00070 uint8_t *p; 00071 00072 TxBuffer_t() : p(buf) {}; 00073 void begin() 00074 { 00075 p = buf; 00076 } 00077 void put(uint8_t c) 00078 { 00079 if (p < buf + sizeof(buf)) 00080 *p++ = c; 00081 } 00082 void put1u(int32_t v) 00083 { 00084 if (v > 0xFF) 00085 v = 0xFF; 00086 else if (v < 0) 00087 v = 0; 00088 put((uint8_t) v); 00089 } 00090 void put2(uint32_t v) 00091 { 00092 if (v > 0xFFFF) 00093 v = 0xFFFF; 00094 00095 put((uint8_t) (v >> 8)); 00096 put((uint8_t) v); 00097 } 00098 void put2(int32_t v) 00099 { 00100 if (v < -0x8000) 00101 v = -0x8000; 00102 else if (v > 0x7FFF) 00103 v = 0x7FFF; 00104 00105 put2((uint32_t) v); 00106 } 00107 void put3(uint32_t v) 00108 { 00109 if (v > 0xFFFFFF) 00110 v = 0xFFFFFF; 00111 00112 put((uint8_t) (v >> 16)); 00113 put((uint8_t) (v >> 8)); 00114 put((uint8_t) v); 00115 } 00116 void put2u(int32_t v) 00117 { 00118 if (v < 0) 00119 v = 0; 00120 else if (v > 0xFFFF) 00121 v = 0xFFFF; 00122 put2((uint32_t) v); 00123 } 00124 void put3(int32_t v) 00125 { 00126 if (v < -0x800000) 00127 v = -0x800000; 00128 else if (v > 0x7FFFFF) 00129 v = 0x7FFFFF; 00130 put3((uint32_t) v); 00131 } 00132 uint8_t *getp(void) 00133 { 00134 return p; 00135 } 00136 size_t getn(void) 00137 { 00138 return p - buf; 00139 } 00140 uint8_t *getbase(void) 00141 { 00142 return buf; 00143 } 00144 void put2sf(float v) 00145 { 00146 int32_t iv; 00147 00148 if (v > 32766.5f) 00149 iv = 0x7fff; 00150 else if (v < -32767.5f) 00151 iv = -0x8000; 00152 else 00153 iv = (int32_t)(v + 0.5f); 00154 00155 put2(iv); 00156 } 00157 void put2uf(float v) 00158 { 00159 uint32_t iv; 00160 00161 if (v > 65535.5f) 00162 iv = 0xffff; 00163 else if (v < 0.5f) 00164 iv = 0; 00165 else 00166 iv = (uint32_t)(v + 0.5f); 00167 00168 put2(iv); 00169 } 00170 void put1uf(float v) 00171 { 00172 uint8_t c; 00173 00174 if (v > 254.5) 00175 c = 0xFF; 00176 else if (v < 0.5) 00177 c = 0; 00178 else 00179 c = (uint8_t) v; 00180 00181 put(c); 00182 } 00183 void putT(float T) 00184 { 00185 put2sf(T * 256.0f + 0.5f); 00186 } 00187 void putRH(float RH) 00188 { 00189 put1uf((RH / 0.390625f) + 0.5f); 00190 } 00191 void putV(float V) 00192 { 00193 put2sf(V * 4096.0f + 0.5f); 00194 } 00195 void putP(float P) 00196 { 00197 put2uf(P / 4.0f + 0.5f); 00198 } 00199 void putLux(float Lux) 00200 { 00201 put2uf(Lux); 00202 } 00203 }; 00204 00205 /* the magic byte at the front of the buffer */ 00206 enum { 00207 FormatSensor1 = 0x11, 00208 }; 00209 00210 /* the flags for the second byte of the buffer */ 00211 enum { 00212 FlagVbat = 1 << 0, 00213 FlagVcc = 1 << 1, 00214 FlagTPH = 1 << 2, 00215 FlagLux = 1 << 3, 00216 FlagWater = 1 << 4, 00217 FlagSoilTH = 1 << 5, 00218 }; 00219 00220 00221 // Temperature sensor object 00222 #define DHT_PIN PB_1 00223 DHT11 dht(DHT_PIN); 00224 00225 // Serial via USB for debugging only 00226 Serial pc(USBTX,USBRX); 00227 00228 int main() 00229 { 00230 TxBuffer_t b; 00231 00232 int32_t ret; 00233 mDot* dot; 00234 std::vector<uint8_t> send_data; 00235 std::vector<uint8_t> recv_data; 00236 std::vector<uint8_t> nwkSKey; 00237 std::vector<uint8_t> appSKey; 00238 std::vector<uint8_t> nodeAddr; 00239 std::vector<uint8_t> networkAddr; 00240 00241 float temperature = 0.0; 00242 00243 pc.baud(115200); 00244 pc.printf("TTN mDot LoRa Temperature & Humidity Sensor\n\r"); 00245 00246 // get a mDot handle 00247 dot = mDot::getInstance(); 00248 00249 // dot->setLogLevel(MTSLog::WARNING_LEVEL); 00250 dot->setLogLevel(MTSLog::TRACE_LEVEL); 00251 00252 logInfo("Checking Config"); 00253 00254 // Test if we've already saved the config 00255 std::string configNetworkName = dot->getNetworkName(); 00256 00257 uint8_t *it = NwkSKey; 00258 for (uint8_t i = 0; i<16; i++) 00259 nwkSKey.push_back((uint8_t) *it++); 00260 00261 it = AppSKey; 00262 for (uint8_t i = 0; i<16; i++) 00263 appSKey.push_back((uint8_t) *it++); 00264 00265 it = NetworkAddr; 00266 for (uint8_t i = 0; i<4; i++) 00267 networkAddr.push_back((uint8_t) *it++); 00268 00269 logInfo("Resetting Config"); 00270 // reset to default config so we know what state we're in 00271 dot->resetConfig(); 00272 00273 // Set byte order - AEP less than 1.0.30 00274 // dot->setJoinByteOrder(mDot::LSB); 00275 dot->setJoinByteOrder(mDot::MSB); // This is default for > 1.0.30 Conduit 00276 00277 00278 00279 logInfo("Set TxPower"); 00280 if((ret = dot->setTxPower( LORA_TXPOWER )) != mDot::MDOT_OK) { 00281 logError("Failed to set Tx Power %d:%s", ret, mDot::getReturnCodeString(ret).c_str()); 00282 } 00283 00284 logInfo("Set Public mode"); 00285 if((ret = dot->setPublicNetwork(true)) != mDot::MDOT_OK) { 00286 logError("failed to set Public Mode %d:%s", ret, mDot::getReturnCodeString(ret).c_str()); 00287 } 00288 00289 logInfo("Set MANUAL Join mode"); 00290 if((ret = dot->setJoinMode(mDot::MANUAL)) != mDot::MDOT_OK) { 00291 logError("Failed to set MANUAL Join Mode %d:%s", ret, mDot::getReturnCodeString(ret).c_str()); 00292 } 00293 00294 logInfo("Set Ack"); 00295 // 1 retries on Ack, 0 to disable 00296 if((ret = dot->setAck( LORA_ACK)) != mDot::MDOT_OK) { 00297 logError("Failed to set Ack %d:%s", ret, mDot::getReturnCodeString(ret).c_str()); 00298 } 00299 00300 //Not applicable for 868MHz in EU 00301 if ((ret = dot->setFrequencySubBand(config_frequency_sub_band)) != mDot::MDOT_OK) { 00302 logError("failed to set frequency sub band", ret); 00303 } 00304 00305 logInfo("Set Network Address"); 00306 if ((ret = dot->setNetworkAddress(networkAddr)) != mDot::MDOT_OK) { 00307 logError("Failed to set Network Address %d:%s", ret, mDot::getReturnCodeString(ret).c_str()); 00308 } 00309 00310 logInfo("Set Data Session Key"); 00311 if ((ret = dot->setDataSessionKey(appSKey)) != mDot::MDOT_OK) { 00312 logError("Failed to set Data Session Key %d:%s", ret, mDot::getReturnCodeString(ret).c_str()); 00313 } 00314 00315 logInfo("Set Network Session Key"); 00316 if ((ret = dot->setNetworkSessionKey(nwkSKey)) != mDot::MDOT_OK) { 00317 logError("Failed to set Network Session Key %d:%s", ret, mDot::getReturnCodeString(ret).c_str()); 00318 } 00319 00320 logInfo("Saving Config"); 00321 // Save config 00322 if (! dot->saveConfig()) { 00323 logError("failed to save configuration"); 00324 } 00325 00326 // Display what is set 00327 std::vector<uint8_t> tmp = dot->getNetworkSessionKey(); 00328 pc.printf("Network Session Key: "); 00329 pc.printf("%s\r\n", mts::Text::bin2hexString(tmp, " ").c_str()); 00330 00331 tmp = dot->getDataSessionKey(); 00332 pc.printf("Data Session Key: "); 00333 pc.printf("%s\r\n", mts::Text::bin2hexString(tmp, " ").c_str()); 00334 00335 pc.printf("Device ID "); 00336 std::vector<uint8_t> deviceId; 00337 deviceId = dot->getDeviceId(); 00338 for (std::vector<uint8_t>::iterator it = deviceId.begin() ; it != deviceId.end(); ++it) 00339 pc.printf("%2.2x",*it ); 00340 pc.printf("\r\n"); 00341 00342 std::vector<uint8_t> netAddress; 00343 00344 pc.printf("Network Address "); 00345 netAddress = dot->getNetworkAddress(); 00346 for (std::vector<uint8_t>::iterator it = netAddress.begin() ; it != netAddress.end(); ++it) 00347 pc.printf("%2.2x",*it ); 00348 00349 pc.printf("\r\n"); 00350 00351 // Display LoRa parameters 00352 // Display label and values in different colours, show pretty values not numeric values where applicable 00353 pc.printf("Public Network: %s\r\n", (char*)(dot->getPublicNetwork() ? "Yes" : "No") ); 00354 pc.printf("Frequency: %s\r\n", (char*)mDot::FrequencyBandStr(dot->getFrequencyBand()).c_str() ); 00355 pc.printf("Sub Band: %s\r\n", (char*)mDot::FrequencySubBandStr(dot->getFrequencySubBand()).c_str() ); 00356 pc.printf("Join Mode: %s\r\n", (char*)mDot::JoinModeStr(dot->getJoinMode()).c_str() ); 00357 pc.printf("Join Retries: %d\r\n", dot->getJoinRetries() ); 00358 pc.printf("Join Byte Order: %s\r\n", (char*)(dot->getJoinByteOrder() == 0 ? "LSB" : "MSB") ); 00359 pc.printf("Link Check Count: %d\r\n", dot->getLinkCheckCount() ); 00360 pc.printf("Link Check Thold: %d\r\n", dot->getLinkCheckThreshold() ); 00361 pc.printf("Tx Data Rate: %s\r\n", (char*)mDot::DataRateStr(dot->getTxDataRate()).c_str() ); 00362 pc.printf("Tx Power: %d\r\n", dot->getTxPower() ); 00363 pc.printf("TxWait: %s, ", (dot->getTxWait() ? "Y" : "N" )); 00364 pc.printf("CRC: %s, ", (dot->getCrc() ? "Y" : "N") ); 00365 pc.printf("Ack: %s\r\n", (dot->getAck() ? "Y" : "N") ); 00366 00367 logInfo("Joining Network"); 00368 00369 while ((ret = dot->joinNetwork()) != mDot::MDOT_OK) { 00370 logError("failed to join network [%d][%s]", ret, mDot::getReturnCodeString(ret).c_str()); 00371 wait_ms(dot->getNextTxMs() + 1); 00372 } 00373 00374 logInfo("Joined Network"); 00375 00376 char dataBuf[50]; 00377 uint16_t seq = 0; 00378 char * sf_str; 00379 while( 1 ) { 00380 00381 /* cycle through spreading factors */ 00382 uint8_t sf; 00383 switch (seq % 4) { 00384 case 0: 00385 sf = mDot::SF_7; 00386 sf_str = "SF7"; 00387 break; 00388 case 1: 00389 sf = mDot::SF_8; 00390 sf_str = "SF8"; 00391 break; 00392 case 2: 00393 sf = mDot::SF_9; 00394 sf_str = "SF9"; 00395 break; 00396 case 3: 00397 sf = mDot::SF_10; 00398 sf_str = "SF10"; 00399 break; 00400 } 00401 // Set Spreading Factor, higher is lower data rate, smaller packets but longer range 00402 // Lower is higher data rate, larger packets and shorter range. 00403 logInfo("Set SF: %s",sf_str); 00404 if((ret = dot->setTxDataRate( sf )) != mDot::MDOT_OK) { 00405 logError("Failed to set SF %d:%s", ret, mDot::getReturnCodeString(ret).c_str()); 00406 } 00407 00408 /* set default data values */ 00409 int temp = 0; 00410 int humid = -1; 00411 00412 /* read from sensor */ 00413 int r = dht.readData(); 00414 switch (r) { 00415 case DHT11::OK: 00416 { 00417 temp = dht.readTemperature(); 00418 humid = dht.readHumidity(); 00419 pc.printf("[DHT] T %d degC H %d %%\r\n",temp,humid); 00420 break; 00421 } 00422 default: 00423 { 00424 pc.printf("[DHT] ERROR %d\r\n",r); 00425 break; 00426 } 00427 }; 00428 00429 /* build packet */ 00430 b.begin(); 00431 uint8_t flag = 0; 00432 b.put(FormatSensor1); 00433 uint8_t * const pFlag = b.getp(); // save pointer to flag location 00434 b.put(0x00); // placeholder for flags 00435 00436 // TODO: read battery voltage 00437 b.putV(13.8); 00438 flag |= FlagVbat; 00439 00440 // TODO: read from Bme280 sensor: 00441 b.putT(27.0); // air temp 00442 b.putP(1010.0); // air pressure 00443 b.putRH(66.0); // air humidity 00444 flag |= FlagTPH; 00445 00446 // TODO: read from light sensor 00447 b.putLux(1234); // ambient light 00448 flag |= FlagLux; 00449 00450 // TODO: read water temperature 00451 b.putT(22.0); // water temperature 00452 flag |= FlagWater; 00453 00454 // TODO: read soil sensor 00455 b.putT(25.2); // soil temperature 00456 b.putRH(82.0); // soil humidity 00457 flag |= FlagSoilTH; 00458 00459 // write flag byte 00460 *pFlag = flag; 00461 00462 /* load vector */ 00463 send_data.clear(); 00464 uint8_t c; 00465 int n = b.getn(); 00466 for( int i=0; i< n; i++ ) { 00467 c = b.buf[i]; 00468 send_data.push_back( c ); 00469 } 00470 00471 /* send packet */ 00472 if ((ret = dot->send(send_data)) != mDot::MDOT_OK) { 00473 logError("failed to send: [%d][%s]", ret, mDot::getReturnCodeString(ret).c_str()); 00474 } else { 00475 logInfo("data len: %d, send data: %s", n, Text::bin2hexString(send_data).c_str()); 00476 } 00477 00478 /* sleep */ 00479 uint32_t sleep_time = MAX((dot->getNextTxMs() / 1000), 10 /* use 6000 for 10min */); 00480 logInfo("going to sleep for %d seconds", sleep_time); 00481 wait_ms(10*1000); 00482 00483 seq++; 00484 } 00485 00486 return 0; 00487 }
Generated on Sun Jul 17 2022 00:32:44 by 1.7.2