_utils.ino 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. // 1-channel LoRa Gateway for ESP8266
  2. // Copyright (c) 2016-2020 Maarten Westenberg version for ESP8266
  3. //
  4. // based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
  5. // and many others.
  6. //
  7. // All rights reserved. This program and the accompanying materials
  8. // are made available under the terms of the MIT License
  9. // which accompanies this distribution, and is available at
  10. // https://opensource.org/licenses/mit-license.php
  11. //
  12. // NO WARRANTY OF ANY KIND IS PROVIDED
  13. //
  14. // Author: Maarten Westenberg (mw12554@hotmail.com)
  15. //
  16. // This file contains the utilities for time and other functions
  17. // ========================================================================================
  18. // ==================== STRING STRING STRING ==============================================
  19. // --------------------------------------------------------------------------------
  20. // PRINT IP
  21. // Output the 4-byte IP address for easy printing.
  22. // As this function is also used by _otaServer.ino do not put in #define
  23. // Parameters:
  24. // ipa: The Ip Address (input)
  25. // sep: Separator character (input)
  26. // response: The response string (output)
  27. // Return:
  28. // <none>
  29. // --------------------------------------------------------------------------------
  30. void printIP(IPAddress ipa, const char sep, String & response)
  31. {
  32. response+=(String)ipa[0]; response+=sep;
  33. response+=(String)ipa[1]; response+=sep;
  34. response+=(String)ipa[2]; response+=sep;
  35. response+=(String)ipa[3];
  36. }
  37. // ----------------------------------------------------------------------------------------
  38. // Fill a HEXadecimal String from a 4-byte char array (uint32_t)
  39. //
  40. // ----------------------------------------------------------------------------------------
  41. void printHex(uint32_t hexa, const char sep, String & response)
  42. {
  43. # if _MONITOR>=1
  44. if ((debug>=0) && (hexa==0)) {
  45. mPrint("printHex:: hexa amount to convert is 0");
  46. }
  47. # endif
  48. uint8_t * h = (uint8_t *)(& hexa);
  49. if (h[0]<016) response+='0'; response += String(h[0], HEX); response+=sep;
  50. if (h[1]<016) response+='0'; response += String(h[1], HEX); response+=sep;
  51. if (h[2]<016) response+='0'; response += String(h[2], HEX); response+=sep;
  52. if (h[3]<016) response+='0'; response += String(h[3], HEX); response+=sep;
  53. }
  54. // ----------------------------------------------------------------------------
  55. // Print uint8_t values in HEX with leading 0 when necessary
  56. // ----------------------------------------------------------------------------
  57. void printHexDigit(uint8_t digit, String & response)
  58. {
  59. // utility function for printing Hex Values with leading 0
  60. if(digit < 0x10)
  61. response += '0';
  62. response += String(digit,HEX);
  63. }
  64. // ----------------------------------------------------------------------------------------
  65. // Print to the monitor console.
  66. // This function is used all over the gateway code as a substite for USB debug code.
  67. // It allows webserver users to view printed/debugging code.
  68. // With initMonitor() we init the index iMoni=0;
  69. //
  70. // Parameters:
  71. // txt: The text to be printed.
  72. // return:
  73. // <None>
  74. // ----------------------------------------------------------------------------------------
  75. void mPrint(String txt)
  76. {
  77. # if _DUSB>=1
  78. Serial.println(txt);
  79. if (debug>=2) Serial.flush();
  80. # endif //_DUSB
  81. # if _MONITOR>=1
  82. time_t tt = now();
  83. monitor[iMoni].txt = String(day(tt)) + "-";
  84. monitor[iMoni].txt += String(month(tt)) + "-";
  85. monitor[iMoni].txt += String(year(tt)) + " ";
  86. uint8_t _hour = hour(tt);
  87. uint8_t _minute = minute(tt);
  88. uint8_t _second = second(tt);
  89. if (_hour < 10) monitor[iMoni].txt += "0"; monitor[iMoni].txt += String( _hour )+ ":";
  90. if (_minute < 10) monitor[iMoni].txt += "0"; monitor[iMoni].txt += String(_minute) + ":";
  91. if (_second < 10) monitor[iMoni].txt += "0"; monitor[iMoni].txt += String(_second) + "- ";
  92. monitor[iMoni].txt += String(txt);
  93. // Use the circular buffer to increment the index
  94. iMoni = (iMoni+1) % _MAXMONITOR ; // And goto 0 when skipping over _MAXMONITOR
  95. # endif //_MONITOR
  96. return;
  97. }
  98. // ----------------------------------------------------------------------------
  99. // mStat
  100. // Print the statistics on Serial (USB) port and/or Monitor
  101. // Depending on setting of _DUSB and _MONITOR.
  102. // Note: This function does not initialise the response var, will only append.
  103. // Parameters:
  104. // Interrupt: 8-bit
  105. // Response: String
  106. // Return:
  107. // 1: If successful
  108. // 0: No Success
  109. // ----------------------------------------------------------------------------
  110. int mStat(uint8_t intr, String & response)
  111. {
  112. #if _MONITOR>=1
  113. if (debug>=0) {
  114. response += "I=";
  115. if (intr & IRQ_LORA_RXTOUT_MASK) response += String("RXTOUT "); // 0x80
  116. if (intr & IRQ_LORA_RXDONE_MASK) response += String("RXDONE "); // 0x40
  117. if (intr & IRQ_LORA_CRCERR_MASK) response += "CRCERR "; // 0x20
  118. if (intr & IRQ_LORA_HEADER_MASK) response += "HEADER "; // 0x10
  119. if (intr & IRQ_LORA_TXDONE_MASK) response += "TXDONE "; // 0x08
  120. if (intr & IRQ_LORA_CDDONE_MASK) response += String("CDDONE "); // 0x04
  121. if (intr & IRQ_LORA_FHSSCH_MASK) response += "FHSSCH "; // 0x02
  122. if (intr & IRQ_LORA_CDDETD_MASK) response += "CDDETD "; // 0x01
  123. if (intr == 0x00) response += " -- ";
  124. response += ", F=" + String(gwayConfig.ch);
  125. response += ", SF=" + String(sf);
  126. response += ", E=" + String(_event);
  127. response += ", S=";
  128. switch (_state) {
  129. case S_INIT:
  130. response += "INIT ";
  131. break;
  132. case S_SCAN:
  133. response += "SCAN ";
  134. break;
  135. case S_CAD:
  136. response += "CAD ";
  137. break;
  138. case S_RX:
  139. response += String("RX ");
  140. break;
  141. case S_TX:
  142. response += "TX ";
  143. break;
  144. case S_TXDONE:
  145. response += "TXDONE";
  146. break;
  147. default:
  148. response += " -- ";
  149. }
  150. response += ", eT=";
  151. response += String( micros() - eventTime );
  152. response += ", dT=";
  153. response += String( micros() - doneTime );
  154. //mPrint(response); // Do actual printing
  155. }
  156. #endif //_MONITOR
  157. return(1);
  158. }
  159. // ============== NUMBER FUNCTIONS ============================================
  160. // ----------------------------------------------------------------------------
  161. // Convert a float to string for printing
  162. // Parameters:
  163. // f is float value to convert
  164. // p is precision in decimal digits
  165. // val is character array for results
  166. // ----------------------------------------------------------------------------
  167. void ftoa(float f, char *val, int p)
  168. {
  169. int j=1;
  170. int ival, fval;
  171. char b[7] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
  172. for (int i=0; i< p; i++) { j= j*10; }
  173. ival = (int) f; // Make integer part
  174. fval = (int) ((f- ival)*j); // Make fraction. Has same sign as integer part
  175. if (fval<0) fval = -fval; // So if it is negative make fraction positive again.
  176. // sprintf does NOT fit in memory
  177. if ((f<0) && (ival == 0)) strcat(val, "-");
  178. strcat(val,itoa(ival,b,10)); // Copy integer part first, base 10, null terminated
  179. strcat(val,"."); // Copy decimal point
  180. itoa(fval,b,10); // Copy fraction part base 10
  181. for (int i=0; i<(p-strlen(b)); i++) {
  182. strcat(val,"0"); // first number of 0 of faction?
  183. }
  184. // Fraction can be anything from 0 to 10^p , so can have less digits
  185. strcat(val,b);
  186. }
  187. // ============== SERIAL SERIAL SERIAL ========================================
  188. // ----------------------------------------------------------------------------
  189. // Print leading '0' digits for hours(0) and second(0) when
  190. // printing values less than 10
  191. // ----------------------------------------------------------------------------
  192. void printDigits(uint32_t digits)
  193. {
  194. // utility function for digital clock display: prints leading 0
  195. if(digits < 10)
  196. Serial.print(F("0"));
  197. Serial.print(digits);
  198. }
  199. // ============================================================================
  200. // NTP TIME functions
  201. // These helper function deal with the Network Time Protool(NTP) functions.
  202. // ============================================================================
  203. // ----------------------------------------------------------------------------------------
  204. // stringTime
  205. // Print the time t into the String reponse. t is of type time_t in seconds.
  206. // Only when RTC is present we print real time values
  207. // t contains number of seconds since system started that the event happened.
  208. // So a value of 100 would mean that the event took place 1 minute and 40 seconds ago
  209. // ----------------------------------------------------------------------------------------
  210. static void stringTime(time_t t, String & response)
  211. {
  212. if (t==0) { response += "--"; return; }
  213. // now() gives seconds since 1970
  214. // as millis() does rotate every 50 days
  215. // So we need another timing parameter
  216. time_t eTime = t;
  217. // Rest is standard
  218. byte _hour = hour(eTime);
  219. byte _minute = minute(eTime);
  220. byte _second = second(eTime);
  221. switch(weekday(eTime)) {
  222. case 1: response += "Sunday "; break;
  223. case 2: response += "Monday "; break;
  224. case 3: response += "Tuesday "; break;
  225. case 4: response += "Wednesday "; break;
  226. case 5: response += "Thursday "; break;
  227. case 6: response += "Friday "; break;
  228. case 7: response += "Saturday "; break;
  229. }
  230. response += String(day(eTime)) + "-";
  231. response += String(month(eTime)) + "-";
  232. response += String(year(eTime)) + " ";
  233. if (_hour < 10) response += "0"; response += String(_hour) + ":";
  234. if (_minute < 10) response += "0"; response += String(_minute) + ":";
  235. if (_second < 10) response += "0"; response += String(_second);
  236. }
  237. // ----------------------------------------------------------------------------
  238. // Send the time request packet to the NTP server.
  239. //
  240. // ----------------------------------------------------------------------------
  241. int sendNtpRequest(IPAddress timeServerIP)
  242. {
  243. const int NTP_PACKET_SIZE = 48; // Fixed size of NTP record
  244. byte packetBuffer[NTP_PACKET_SIZE];
  245. memset(packetBuffer, 0, NTP_PACKET_SIZE); // Zero the buffer.
  246. packetBuffer[0] = 0b11100011; // LI, Version, Mode
  247. packetBuffer[1] = 0; // Stratum, or type of clock
  248. packetBuffer[2] = 6; // Polling Interval
  249. packetBuffer[3] = 0xEC; // Peer Clock Precision
  250. // 8 bytes of zero for Root Delay & Root Dispersion
  251. packetBuffer[12] = 49;
  252. packetBuffer[13] = 0x4E;
  253. packetBuffer[14] = 49;
  254. packetBuffer[15] = 52;
  255. if (!sendUdp( (IPAddress) timeServerIP, (int) 123, packetBuffer, NTP_PACKET_SIZE)) {
  256. gwayConfig.ntpErr++;
  257. gwayConfig.ntpErrTime = now();
  258. return(0);
  259. }
  260. return(1);
  261. }
  262. // ----------------------------------------------------------------------------
  263. // Get the NTP time from one of the time servers
  264. // Note: As this function is called from SyncInterval in the background
  265. // make sure we have no blocking calls in this function
  266. // parameters:
  267. // t: the resulting time_t
  268. // return:
  269. // 0: when fail
  270. // >=1: when success
  271. // ----------------------------------------------------------------------------
  272. int getNtpTime(time_t *t)
  273. {
  274. gwayConfig.ntps++;
  275. if (!sendNtpRequest(ntpServer)) // Send the request for new time
  276. {
  277. # if _MONITOR>=1
  278. if (debug>=0) {
  279. mPrint("utils:: ERROR getNtpTime: sendNtpRequest failed");
  280. }
  281. # endif //_MONITOR
  282. return(0);
  283. }
  284. const int NTP_PACKET_SIZE = 48; // Fixed size of NTP record
  285. byte packetBuffer[NTP_PACKET_SIZE];
  286. memset(packetBuffer, 0, NTP_PACKET_SIZE); // Set buffer contents to zero
  287. uint32_t beginWait = millis();
  288. delay(10);
  289. while (millis() - beginWait < 1500) // Wait for 1500 millisecs
  290. {
  291. int size = Udp.parsePacket();
  292. if ( size >= NTP_PACKET_SIZE ) {
  293. if (Udp.read(packetBuffer, NTP_PACKET_SIZE) < NTP_PACKET_SIZE) {
  294. # if _MONITOR>=1
  295. if (debug>=0) {
  296. mPrint("getNtpTime:: ERROR packetsize too low");
  297. }
  298. # endif //_MONITOR
  299. break; // Error, or should we use continue
  300. }
  301. else {
  302. // Extract seconds portion.
  303. uint32_t secs;
  304. secs = packetBuffer[40] << 24;
  305. secs |= packetBuffer[41] << 16;
  306. secs |= packetBuffer[42] << 8;
  307. secs |= packetBuffer[43];
  308. // in NL UTC is 1 TimeZone correction when no daylight saving time
  309. *t = (time_t)(secs - 2208988800UL + NTP_TIMEZONES * SECS_IN_HOUR);
  310. Udp.flush();
  311. return(1);
  312. }
  313. Udp.flush();
  314. }
  315. delay(100); // Wait 100 millisecs, allow kernel to act when necessary
  316. }
  317. Udp.flush();
  318. // If we are here, we could not read the time from internet
  319. // So increase the counter
  320. gwayConfig.ntpErr++;
  321. gwayConfig.ntpErrTime = now();
  322. # if _MONITOR>=1
  323. if ((debug>=3) && (pdebug & P_MAIN)) {
  324. mPrint("getNtpTime:: WARNING read time failed"); // but we return 0 to indicate this to caller
  325. }
  326. # endif //_MONITOR
  327. return(0); // return 0 if unable to get the time
  328. } //getNtpTime
  329. // ----------------------------------------------------------------------------
  330. // Set up regular synchronization of NTP server and the local time.
  331. // ----------------------------------------------------------------------------
  332. #if NTP_INTR==1
  333. void setupTime()
  334. {
  335. time_t t;
  336. getNtpTime(&t);
  337. setSyncProvider(t);
  338. setSyncInterval(_NTP_INTERVAL);
  339. }
  340. #endif //NTP_INTR
  341. // ----------------------------------------------------------------------------
  342. // SerialName(id, response)
  343. // Check whether for address a (4 bytes in uint32_t) there is a
  344. // Trusted Node name. It will return the index of that name in nodex struct.
  345. // Otherwise it returns -1.
  346. // This function only works if _TRUSTED_NODES is set.
  347. // ----------------------------------------------------------------------------
  348. int SerialName(uint32_t a, String & response)
  349. {
  350. #if _TRUSTED_NODES>=1
  351. uint8_t * in = (uint8_t *)(& a);
  352. uint32_t id = ((in[0]<<24) | (in[1]<<16) | (in[2]<<8) | in[3]);
  353. for (int i=0; i< (sizeof(nodes)/sizeof(nodex)); i++) {
  354. if (id == nodes[i].id) {
  355. # if _MONITOR>=1
  356. if ((debug>=3) && (pdebug & P_MAIN )) {
  357. mPrint("SerialName:: i="+String(i)+", Name="+String(nodes[i].nm)+". for node=0x"+String(nodes[i].id,HEX));
  358. }
  359. # endif //_MONITOR
  360. response += nodes[i].nm;
  361. return(i);
  362. }
  363. }
  364. #endif // _TRUSTED_NODES
  365. return(-1); // If no success OR is TRUSTED NODES not defined
  366. } //SerialName
  367. #if _LOCALSERVER==1
  368. // ----------------------------------------------------------------------------
  369. // inDecodes(id)
  370. // Find the id in Decodes array, and return the index of the item
  371. // Parameters:
  372. // id: The first field in the array (normally DevAddr id). Must be char[4]
  373. // Returns:
  374. // The index of the ID in the Array. Returns -1 if not found
  375. // ----------------------------------------------------------------------------
  376. int inDecodes(char * id) {
  377. uint32_t ident = ((id[3]<<24) | (id[2]<<16) | (id[1]<<8) | id[0]);
  378. for (int i=0; i< (sizeof(decodes)/sizeof(codex)); i++) {
  379. if (ident == decodes[i].id) {
  380. return(i);
  381. }
  382. }
  383. return(-1);
  384. }
  385. #endif
  386. // ============================= GENERAL SKETCH ===============================
  387. // ----------------------------------------------------------------------------
  388. // DIE is not used actively in the source code apart from resolveHost().
  389. // It is replaced by a Serial.print command so we know that we have a problem
  390. // somewhere.
  391. // There are at least 3 other ways to restart the ESP. Pick one if you want.
  392. // ----------------------------------------------------------------------------
  393. void die(String s)
  394. {
  395. # if _MONITOR>=1
  396. mPrint(s);
  397. # endif //_MONITOR
  398. # if _DUSB>=1
  399. Serial.println(s);
  400. if (debug>=2) Serial.flush();
  401. # endif //_DUSB
  402. delay(50);
  403. abort(); // Within a second
  404. }
  405. // ----------------------------------------------------------------------------
  406. // gway_failed is a function called by ASSERT in configGway.h
  407. //
  408. // ----------------------------------------------------------------------------
  409. void gway_failed(const char *file, uint16_t line) {
  410. #if _MONITOR>=1
  411. String response = "Program failed in file: ";
  412. response += String(file);
  413. response += ", line: ";
  414. response += String(line);
  415. mPrint(response);
  416. #endif //_MONITOR
  417. }