_loraFiles.ino 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689
  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 LoRa filesystem specific code
  17. #if _MONITOR>=1
  18. // ----------------------------------------------------------------------------
  19. // LoRa Monitor logging code.
  20. // Define one print function and depending on the logging parameter output
  21. // to _USB of to the www screen function
  22. // ----------------------------------------------------------------------------
  23. int initMonitor(struct moniLine *monitor)
  24. {
  25. for (int i=0; i< _MAXMONITOR; i++) {
  26. monitor[i].txt= "-"; // Make all lines empty
  27. }
  28. iMoni=0; // Init the index
  29. return(1);
  30. }
  31. #endif //_MONITOR
  32. // ============================================================================
  33. // LORA SPIFFS FILESYSTEM FUNCTIONS
  34. //
  35. // The LoRa supporting functions are in the section below
  36. // ----------------------------------------------------------------------------
  37. // Supporting function to readConfig
  38. // ----------------------------------------------------------------------------
  39. void id_print (String id, String val)
  40. {
  41. #if _MONITOR>=1
  42. if (( debug>=0 ) && ( pdebug & P_MAIN )) {
  43. Serial.print(id);
  44. Serial.print(F("=\t"));
  45. Serial.println(val);
  46. }
  47. #endif //_MONITOR
  48. }
  49. // ============================================================================
  50. // config functions
  51. //
  52. // ----------------------------------------------------------------------------
  53. // INITCONFIG; Init the gateway configuration file
  54. // Espcecially when calling SPIFFS.format() the gateway is left in an init state
  55. // which is not very well defined. This function will init some of the settings
  56. // to well known settings.
  57. // ----------------------------------------------------------------------------
  58. void initConfig(struct espGwayConfig *c)
  59. {
  60. (*c).ch = 0;
  61. (*c).sf = _SPREADING;
  62. (*c).debug = 1; // debug level is 1
  63. (*c).pdebug = P_GUI | P_MAIN;
  64. (*c).cad = _CAD;
  65. (*c).hop = false;
  66. (*c).seen = true; // Seen interface is ON
  67. (*c).expert = false; // Expert interface is OFF
  68. (*c).monitor = true; // Monitoring is ON
  69. (*c).trusted = 1;
  70. (*c).txDelay = 0; // First Value without saving is 0;
  71. (*c).dusbStat = true;
  72. } // initConfig()
  73. // ----------------------------------------------------------------------------
  74. // Read the config file and fill the (copied) variables
  75. // ----------------------------------------------------------------------------
  76. int readGwayCfg(const char *fn, struct espGwayConfig *c)
  77. {
  78. if (readConfig(fn, c)<0) {
  79. # if _MONITOR>=1
  80. mPrint("readConfig:: Error reading config file");
  81. return 0;
  82. # endif //_MONITOR
  83. }
  84. if (gwayConfig.sf != (uint8_t) 0) {
  85. sf = (sf_t) gwayConfig.sf;
  86. }
  87. debug = (*c).debug;
  88. pdebug = (*c).pdebug;
  89. (*c).boots++; // Increment Boot Counter
  90. # if _GATEWAYNODE==1
  91. if (gwayConfig.fcnt != (uint8_t) 0) {
  92. frameCount = gwayConfig.fcnt+10;
  93. }
  94. # endif
  95. writeGwayCfg(CONFIGFILE, &gwayConfig ); // And writeback the configuration, not to miss a boot
  96. return 1;
  97. } // readGwayCfg()
  98. // ----------------------------------------------------------------------------
  99. // Read the gateway configuration file
  100. // ----------------------------------------------------------------------------
  101. int readConfig(const char *fn, struct espGwayConfig *c)
  102. {
  103. int tries = 0;
  104. if (!SPIFFS.exists(fn)) {
  105. # if _MONITOR>=1
  106. mPrint("readConfig ERR:: file="+String(fn)+" does not exist ..");
  107. # endif //_MONITOR
  108. initConfig(c); // If we cannot read the config, at least init known values
  109. return(-1);
  110. }
  111. File f = SPIFFS.open(fn, "r");
  112. if (!f) {
  113. # if _MONITOR>=1
  114. Serial.println(F("ERROR:: SPIFFS open failed"));
  115. # endif //_MONITOR
  116. return(-1);
  117. }
  118. while (f.available()) {
  119. # if _MONITOR>=1
  120. if (( debug>=0 ) && ( pdebug & P_MAIN )) {
  121. Serial.print('.');
  122. }
  123. # endif //_MONITOR
  124. // If we wait for more than 15 times, reformat the filesystem
  125. // We do this so that the system will be responsive (over OTA for example).
  126. //
  127. if (tries >= 15) {
  128. f.close();
  129. # if _MONITOR>=1
  130. if (debug>=0) {
  131. mPrint("readConfig:: Formatting");
  132. }
  133. # endif //_MONITOR
  134. SPIFFS.format();
  135. f = SPIFFS.open(fn, "r");
  136. tries = 0;
  137. initSeen(listSeen);
  138. }
  139. initConfig(c); // Even if we do not read a value, give a default
  140. String id =f.readStringUntil('='); // Read keyword until '=', C++ thing
  141. String val=f.readStringUntil('\n'); // Read value until End of Line (EOL)
  142. if (id == "MONITOR") { // MONITOR button setting
  143. id_print(id, val);
  144. (*c).monitor = (bool) val.toInt();
  145. }
  146. else if (id == "CH") { // Frequency Channel
  147. id_print(id,val);
  148. (*c).ch = (uint8_t) val.toInt();
  149. }
  150. else if (id == "SF") { // Spreading Factor
  151. id_print(id, val);
  152. (*c).sf = (uint8_t) val.toInt();
  153. }
  154. else if (id == "FCNT") { // Frame Counter
  155. id_print(id, val);
  156. (*c).fcnt = (uint16_t) val.toInt();
  157. }
  158. else if (id == "DEBUG") { // Debug Level
  159. id_print(id, val);
  160. (*c).debug = (uint8_t) val.toInt();
  161. }
  162. else if (id == "PDEBUG") { // pDebug Pattern
  163. id_print(id, val);
  164. (*c).pdebug = (uint8_t) val.toInt();
  165. }
  166. else if (id == "CAD") { // CAD setting
  167. id_print(id, val);
  168. (*c).cad = (bool) val.toInt();
  169. }
  170. else if (id == "HOP") { // HOP setting
  171. id_print(id, val);
  172. (*c).hop = (bool) val.toInt();
  173. }
  174. else if (id == "BOOTS") { // BOOTS setting
  175. id_print(id, val);
  176. (*c).boots = (uint16_t) val.toInt();
  177. }
  178. else if (id == "RESETS") { // RESET setting
  179. id_print(id, val);
  180. (*c).resets = (uint16_t) val.toInt();
  181. }
  182. else if (id == "WIFIS") { // WIFIS setting
  183. id_print(id, val);
  184. (*c).wifis = (uint16_t) val.toInt();
  185. }
  186. else if (id == "VIEWS") { // VIEWS setting
  187. id_print(id, val);
  188. (*c).views = (uint16_t) val.toInt();
  189. }
  190. else if (id == "NODE") { // NODE setting
  191. id_print(id, val);
  192. (*c).isNode = (bool) val.toInt();
  193. }
  194. else if (id == "REFR") { // REFR setting
  195. id_print(id, val);
  196. (*c).refresh = (bool) val.toInt();
  197. }
  198. else if (id == "REENTS") { // REENTS setting
  199. id_print(id, val);
  200. (*c).reents = (uint16_t) val.toInt();
  201. }
  202. else if (id == "NTPERR") { // NTPERR setting
  203. id_print(id, val);
  204. (*c).ntpErr = (uint16_t) val.toInt();
  205. }
  206. else if (id == "WAITERR") { // WAITERR setting
  207. id_print(id, val);
  208. (*c).waitErr = (uint16_t) val.toInt();
  209. }
  210. else if (id == "WAITOK") { // WAITOK setting
  211. id_print(id, val);
  212. (*c).waitOk = (uint16_t) val.toInt();
  213. }
  214. else if (id == "NTPETIM") { // NTPERR setting
  215. id_print(id, val);
  216. (*c).ntpErrTime = (uint32_t) val.toInt();
  217. }
  218. else if (id == "NTPS") { // NTPS setting
  219. id_print(id, val);
  220. (*c).ntps = (uint16_t) val.toInt();
  221. }
  222. else if (id == "FILENO") { // FILENO setting
  223. id_print(id, val);
  224. (*c).logFileNo = (uint16_t) val.toInt();
  225. }
  226. else if (id == "FILEREC") { // FILEREC setting
  227. id_print(id, val);
  228. (*c).logFileRec = (uint16_t) val.toInt();
  229. }
  230. else if (id == "FILENUM") { // FILEREC setting
  231. id_print(id, val);
  232. (*c).logFileNum = (uint16_t) val.toInt();
  233. }
  234. else if (id == "EXPERT") { // EXPERT button setting
  235. id_print(id, val);
  236. (*c).expert = (bool) val.toInt();
  237. }
  238. else if (id == "SEEN") { // SEEN button setting
  239. id_print(id, val);
  240. (*c).seen = (bool) val.toInt();
  241. }
  242. else if (id == "DELAY") { // DELAY setting
  243. id_print(id, val);
  244. (*c).txDelay = (int32_t) val.toInt();
  245. }
  246. else if (id == "TRUSTED") { // TRUSTED setting
  247. id_print(id, val);
  248. (*c).trusted= (int8_t) val.toInt();
  249. }
  250. else if (id == "FORMAT") { // TRUSTED setting
  251. id_print(id, val);
  252. (*c).formatCntr= (int8_t) val.toInt();
  253. }
  254. else {
  255. # if _MONITOR>=1
  256. mPrint(F("readConfig:: tries++"));
  257. # endif //_MONITOR
  258. tries++;
  259. }
  260. }
  261. f.close();
  262. return(1);
  263. } // readConfig()
  264. // ----------------------------------------------------------------------------
  265. // Write the current gateway configuration to SPIFFS. First copy all the
  266. // separate data items to the gwayConfig structure
  267. //
  268. // Note: gwayConfig.expert contains the expert setting already
  269. // gwayConfig.txDelay
  270. // ----------------------------------------------------------------------------
  271. int writeGwayCfg(const char *fn, struct espGwayConfig *c)
  272. {
  273. (*c).sf = (uint8_t) sf; // Spreading Factor
  274. (*c).debug = debug;
  275. (*c).pdebug = pdebug;
  276. # if _GATEWAYNODE==1
  277. (*c).fcnt = frameCount;
  278. # endif //_GATEWAYNODE
  279. return(writeConfig(fn, c));
  280. } // writeGwayCfg
  281. // ----------------------------------------------------------------------------
  282. // Write the configuration as found in the espGwayConfig structure
  283. // to SPIFFS
  284. // Parameters:
  285. // fn; Filename
  286. // c; struct config
  287. // Returns:
  288. // 1 when successful, -1 on error
  289. // ----------------------------------------------------------------------------
  290. int writeConfig(const char *fn, struct espGwayConfig *c)
  291. {
  292. // Assuming the config file is the first we write...
  293. File f = SPIFFS.open(fn, "w");
  294. if (!f) {
  295. #if _MONITOR>=1
  296. mPrint("writeConfig: ERROR open file="+String(fn));
  297. #endif //_MONITOR
  298. return(-1);
  299. }
  300. f.print("CH"); f.print('='); f.print((*c).ch); f.print('\n');
  301. f.print("SF"); f.print('='); f.print((*c).sf); f.print('\n');
  302. f.print("FCNT"); f.print('='); f.print((*c).fcnt); f.print('\n');
  303. f.print("DEBUG"); f.print('='); f.print((*c).debug); f.print('\n');
  304. f.print("PDEBUG"); f.print('='); f.print((*c).pdebug); f.print('\n');
  305. f.print("CAD"); f.print('='); f.print((*c).cad); f.print('\n');
  306. f.print("HOP"); f.print('='); f.print((*c).hop); f.print('\n');
  307. f.print("NODE"); f.print('='); f.print((*c).isNode); f.print('\n');
  308. f.print("BOOTS"); f.print('='); f.print((*c).boots); f.print('\n');
  309. f.print("RESETS"); f.print('='); f.print((*c).resets); f.print('\n');
  310. f.print("WIFIS"); f.print('='); f.print((*c).wifis); f.print('\n');
  311. f.print("VIEWS"); f.print('='); f.print((*c).views); f.print('\n');
  312. f.print("REFR"); f.print('='); f.print((*c).refresh); f.print('\n');
  313. f.print("REENTS"); f.print('='); f.print((*c).reents); f.print('\n');
  314. f.print("NTPETIM"); f.print('='); f.print((*c).ntpErrTime); f.print('\n');
  315. f.print("NTPERR"); f.print('='); f.print((*c).ntpErr); f.print('\n');
  316. f.print("WAITERR"); f.print('='); f.print((*c).waitErr); f.print('\n');
  317. f.print("WAITOK"); f.print('='); f.print((*c).waitOk); f.print('\n');
  318. f.print("NTPS"); f.print('='); f.print((*c).ntps); f.print('\n');
  319. f.print("FILEREC"); f.print('='); f.print((*c).logFileRec); f.print('\n');
  320. f.print("FILENO"); f.print('='); f.print((*c).logFileNo); f.print('\n');
  321. f.print("FILENUM"); f.print('='); f.print((*c).logFileNum); f.print('\n');
  322. f.print("FORMAT"); f.print('='); f.print((*c).formatCntr); f.print('\n');
  323. f.print("DELAY"); f.print('='); f.print((*c).txDelay); f.print('\n');
  324. f.print("TRUSTED"); f.print('='); f.print((*c).trusted); f.print('\n');
  325. f.print("EXPERT"); f.print('='); f.print((*c).expert); f.print('\n');
  326. f.print("SEEN"); f.print('='); f.print((*c).seen); f.print('\n');
  327. f.print("MONITOR"); f.print('='); f.print((*c).monitor); f.print('\n');
  328. f.close();
  329. return(1);
  330. } // writeConfig()
  331. // ----------------------------------------------------------------------------
  332. // Add a line with statistics to the log.
  333. //
  334. // We put the check in the function to protect against calling
  335. // the function without _STAT_LOG being proper defined
  336. // ToDo: Store the fileNo and the fileRec in the status file to save for
  337. // restarts
  338. //
  339. // Parameters:
  340. // line; char array with characters to write to log
  341. // cnt;
  342. // Returns:
  343. // <none>
  344. // ----------------------------------------------------------------------------
  345. int addLog(const unsigned char * line, int cnt)
  346. {
  347. # if _STAT_LOG==1
  348. char fn[16];
  349. if (gwayConfig.logFileRec > LOGFILEREC) { // Have to make define for this
  350. gwayConfig.logFileRec = 0; // In new logFile start with record 0
  351. gwayConfig.logFileNo++; // Increase file ID
  352. gwayConfig.logFileNum++; // Increase number of log files
  353. }
  354. gwayConfig.logFileRec++;
  355. // If we have too many logfies, delete the oldest
  356. //
  357. if (gwayConfig.logFileNum > LOGFILEMAX){
  358. sprintf(fn,"/log-%d", gwayConfig.logFileNo - LOGFILEMAX);
  359. # if _MONITOR>=1
  360. if (( debug>=2 ) && ( pdebug & P_GUI )) {
  361. mPrint("addLog:: Too many logfiles, deleting="+String(fn));
  362. }
  363. # endif //_MONITOR
  364. SPIFFS.remove(fn);
  365. gwayConfig.logFileNum--;
  366. }
  367. // Make sure we have the right fileno
  368. sprintf(fn,"/log-%d", gwayConfig.logFileNo);
  369. // If there is no SPIFFS, Error
  370. // Make sure to write the config record/line also
  371. if (!SPIFFS.exists(fn)) {
  372. # if _MONITOR>=1
  373. if (( debug >= 2 ) && ( pdebug & P_GUI )) {
  374. mPrint("addLog:: WARNING file="+String(fn)+" does not exist .. rec="+String(gwayConfig.logFileRec) );
  375. }
  376. # endif //_MONITOR
  377. }
  378. File f = SPIFFS.open(fn, "a");
  379. if (!f) {
  380. # if _MONITOR>=1
  381. if (( debug>=1 ) && ( pdebug & P_GUI )) {
  382. mPrint("addLOG:: ERROR file open failed="+String(fn));
  383. }
  384. # endif //_MONITOR
  385. return(0); // If file open failed, return
  386. }
  387. int i=0;
  388. # if _MONITOR>=1
  389. if (( debug>=2 ) && ( pdebug & P_GUI )) {
  390. Serial.print(F("addLog:: fileno="));
  391. Serial.print(gwayConfig.logFileNo);
  392. Serial.print(F(", rec="));
  393. Serial.print(gwayConfig.logFileRec);
  394. Serial.print(F(": "));
  395. # if _MONITOR>=2
  396. {
  397. for (i=0; i< 12; i++) { // The first 12 bytes contain non printable characters
  398. Serial.print(line[i],HEX);
  399. Serial.print(' ');
  400. }
  401. }
  402. # else //_MONITOR>=2
  403. i+=12;
  404. # endif //_DUSB>=2
  405. Serial.print((char *) &line[i]); // The rest if the buffer contains ascii
  406. Serial.println();
  407. }
  408. # endif //_MONITOR
  409. for (i=0; i< 12; i++) { // The first 12 bytes contain non printable characters
  410. // f.print(line[i],HEX);
  411. f.print('*');
  412. }
  413. f.write(&(line[i]), cnt-12); // write/append the line to the file
  414. f.print('\n');
  415. f.close(); // Close the file after appending to it
  416. # endif //_STAT_LOG
  417. return(1);
  418. } //addLog()
  419. // ============================================================================
  420. // Below are the xxxSeen() functions. These functions keep track of the kast
  421. // time a device was seen bij the gateway.
  422. // These functions are not round-robin and they do not need to be.
  423. // ----------------------------------------------------------------------------
  424. // initSeen
  425. // Init the lisrScreen array
  426. // Return:
  427. // 1: Success
  428. // Parameters:
  429. // listSeen: array of Seen data
  430. // ----------------------------------------------------------------------------
  431. int initSeen(struct nodeSeen *listSeen)
  432. {
  433. #if _MAXSEEN >= 1
  434. for (int i=0; i< _MAXSEEN; i++) {
  435. listSeen[i].idSeen=0;
  436. listSeen[i].sfSeen=0;
  437. listSeen[i].cntSeen=0;
  438. listSeen[i].chnSeen=0;
  439. listSeen[i].timSeen=(time_t) 0; // 1 jan 1970 0:00:00 hrs
  440. }
  441. iSeen= 0; // Init index to 0
  442. #endif // _MAXSEEN
  443. return(1);
  444. } // initSeen()
  445. // ----------------------------------------------------------------------------
  446. // readSeen
  447. // This function read the information stored by writeSeen from the file.
  448. // The file is read as String() values and converted to int after.
  449. // Parameters:
  450. // fn: Filename
  451. // listSeen: Array of all last seen nodes on the LoRa network
  452. // Return:
  453. // 1: When successful
  454. // ----------------------------------------------------------------------------
  455. int readSeen(const char *fn, struct nodeSeen *listSeen)
  456. {
  457. #if _MAXSEEN >= 1
  458. int i;
  459. iSeen= 0; // Init the index at 0
  460. if (!SPIFFS.exists(fn)) { // Does listSeen file exist
  461. # if _MONITOR>=1
  462. mPrint("WARNING:: readSeen, history file not exists "+String(fn) );
  463. # endif //_MONITOR
  464. initSeen(listSeen); // XXX make all initial declarations here if config vars need to have a value
  465. return(-1);
  466. }
  467. File f = SPIFFS.open(fn, "r");
  468. if (!f) {
  469. # if _MONITOR>=1
  470. mPrint("readSeen:: ERROR open file=" + String(fn));
  471. # endif //_MONITOR
  472. return(-1);
  473. }
  474. delay(1000);
  475. for (i=0; i<_MAXSEEN; i++) {
  476. delay(200);
  477. String val="";
  478. if (!f.available()) {
  479. # if _MONITOR>=2
  480. mPrint("readSeen:: No more info left in file, i=" + String(i));
  481. # endif //_MONITOR
  482. break;
  483. }
  484. val=f.readStringUntil('\t'); listSeen[i].timSeen = (time_t) val.toInt();
  485. val=f.readStringUntil('\t'); listSeen[i].idSeen = (int64_t) val.toInt();
  486. val=f.readStringUntil('\t'); listSeen[i].cntSeen = (uint32_t) val.toInt();
  487. val=f.readStringUntil('\t'); listSeen[i].chnSeen = (uint8_t) val.toInt();
  488. val=f.readStringUntil('\n'); listSeen[i].sfSeen = (uint8_t) val.toInt();
  489. # if _MONITOR>=1
  490. if ((debug>=2) && (pdebug & P_MAIN)) {
  491. mPrint("readSeen:: idSeen ="+String(listSeen[i].idSeen,HEX)+", i="+String(i));
  492. }
  493. # endif
  494. iSeen++; // Increase index, new record read
  495. }
  496. f.close();
  497. #endif // _MAXSEEN
  498. // So we read iSeen records
  499. return 1;
  500. } // readSeen()
  501. // ----------------------------------------------------------------------------
  502. // writeSeen
  503. // Once every few messages, update the SPIFFS file and write the array.
  504. // Parameters:
  505. // - fn contains the filename to write
  506. // - listSeen contains the _MAXSEEN array of list structures
  507. // Return values:
  508. // - return 1 on success
  509. // ----------------------------------------------------------------------------
  510. int writeSeen(const char *fn, struct nodeSeen *listSeen)
  511. {
  512. #if _MAXSEEN >= 1
  513. int i;
  514. if (!SPIFFS.exists(fn)) {
  515. # if _MONITOR>=1
  516. mPrint("WARNING:: writeSeen, file not exists="+String(fn));
  517. # endif //_MONITOR
  518. //initSeen(listSeen); // XXX make all initial declarations here if config vars need to have a value
  519. }
  520. File f = SPIFFS.open(fn, "w");
  521. if (!f) {
  522. # if _MONITOR>=1
  523. mPrint("writeSeen:: ERROR open file="+String(fn)+" for writing");
  524. # endif //_MONITOR
  525. return(-1);
  526. }
  527. delay(500);
  528. for (i=0; i<iSeen; i++) { // For all records indexed
  529. f.print((time_t)listSeen[i].timSeen); f.print('\t');
  530. f.print((int32_t)listSeen[i].idSeen); f.print('\t'); // Typecast to avoid errors in unsigned conversion!
  531. f.print((uint32_t)listSeen[i].cntSeen); f.print('\t');
  532. f.print((uint8_t)listSeen[i].chnSeen); f.print('\t');
  533. f.print((uint8_t)listSeen[i].sfSeen); f.print('\n');
  534. }
  535. f.close();
  536. #endif // _MAXSEEN
  537. return(1);
  538. }
  539. // ----------------------------------------------------------------------------
  540. // addSeen
  541. // With every message received:
  542. // - Look whether message is already in the array, if so update existing message.
  543. // - If not, create new record.
  544. // - With this record, update the SF settings
  545. //
  546. // Parameters:
  547. // listSeen: The array of records of nodes we have seen
  548. // stat: one record
  549. // Returns:
  550. // 1 when successful
  551. // ----------------------------------------------------------------------------
  552. int addSeen(struct nodeSeen *listSeen, struct stat_t stat)
  553. {
  554. #if _MAXSEEN >= 1
  555. int i;
  556. for (i=0; i<iSeen; i++) { // For all known records
  557. // If the record node is equal, we found the record already.
  558. // So increment cntSeen
  559. if (listSeen[i].idSeen==stat.node) {
  560. listSeen[i].timSeen = (time_t)stat.time;
  561. listSeen[i].cntSeen++; // Not included on function para
  562. //listSeen[i].idSeen = stat.node; // Not necessary, is the same
  563. listSeen[i].chnSeen = stat.ch;
  564. listSeen[i].sfSeen = stat.sf; // The SF argument
  565. // writeSeen(_SEENFILE, listSeen);
  566. # if _MONITOR>=2
  567. if ((debug>=1) && (pdebug & P_MAIN)) {
  568. mPrint("addSeen:: adding i="+String(i)+", node="+String(stat.node,HEX));
  569. }
  570. # endif
  571. return 1;
  572. }
  573. }
  574. // else: We did not find the current record so make a new Seen entry
  575. if ((i>=iSeen) && (i<_MAXSEEN)) {
  576. listSeen[i].idSeen = stat.node;
  577. listSeen[i].chnSeen = stat.ch;
  578. listSeen[i].sfSeen = stat.sf; // The SF argument
  579. listSeen[i].timSeen = (time_t)stat.time; // Timestamp correctly
  580. listSeen[i].cntSeen = 1; // We see this for the first time
  581. iSeen++;
  582. }
  583. # if _MONITOR>=1
  584. if ((debug>=2) && (pdebug & P_MAIN)) {
  585. String response= "addSeen:: i=";
  586. response += i;
  587. response += ", tim=";
  588. stringTime(stat.time, response);
  589. response += ", iSeen=";
  590. response += String(iSeen);
  591. response += ", node=";
  592. response += String(stat.node,HEX);
  593. response += ", listSeen[0]=";
  594. printHex(listSeen[0].idSeen,':',response);
  595. mPrint(response);
  596. }
  597. # endif // _MONITOR
  598. #endif //_MAXSEEN>=1
  599. return 1;
  600. } // addSeen()
  601. // End of File