_wwwServer.ino 54 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572
  1. // 1-channel LoRa Gateway for ESP8266
  2. // Copyright (c) 2016, 2017, 2018, 2019 Maarten Westenberg version for ESP8266
  3. // Version 6.1.1
  4. // Date: 2019-11-06
  5. //
  6. // based on work done by many people and making use of several libraries.
  7. //
  8. // All rights reserved. This program and the accompanying materials
  9. // are made available under the terms of the MIT License
  10. // which accompanies this distribution, and is available at
  11. // https://opensource.org/licenses/mit-license.php
  12. //
  13. // NO WARRANTY OF ANY KIND IS PROVIDED
  14. //
  15. // Author: Maarten Westenberg (mw12554@hotmail.com)
  16. //
  17. // This file contains the webserver code for the ESP Single Channel Gateway.
  18. // Note:
  19. // The ESP Webserver works with Strings to display HTML content.
  20. // Care must be taken that not all data is output to the webserver in one string
  21. // as this will use a LOT of memory and possibly kill the heap (cause system
  22. // crash or other unreliable behaviour.
  23. // Instead, output of the various tables on the webpage should be displayed in
  24. // chunks so that strings are limited in size.
  25. // Be aware that using no strings but only sendContent() calls has its own
  26. // disadvantage that these calls take a lot of time and cause the page to be
  27. // displayed like an old typewriter.
  28. // So, the trick is to make chucks that are sent to the website by using
  29. // a response String but not make those Strings too big.
  30. //
  31. // Also, selecting too many options for Statistics, display, Hopping channels
  32. // etc makes the gateway more sluggish and may impact the available memory and
  33. // thus its performance and reliability. It's up to the user to select wisely!
  34. //
  35. // --------------------------------------------------------------------------------
  36. // PRINT IP
  37. // Output the 4-byte IP address for easy printing.
  38. // As this function is also used by _otaServer.ino do not put in #define
  39. // --------------------------------------------------------------------------------
  40. static void printIP(IPAddress ipa, const char sep, String& response)
  41. {
  42. response+=(String)ipa[0]; response+=sep;
  43. response+=(String)ipa[1]; response+=sep;
  44. response+=(String)ipa[2]; response+=sep;
  45. response+=(String)ipa[3];
  46. }
  47. //
  48. // The remainder of the file ONLY works is A_SERVER=1 is set.
  49. //
  50. #if A_SERVER==1
  51. // ================================================================================
  52. // WEBSERVER DECLARATIONS
  53. // ================================================================================
  54. // None at the moment
  55. // ================================================================================
  56. // WEBSERVER FUNCTIONS
  57. // ================================================================================
  58. // --------------------------------------------------------------------------------
  59. // Used by all functions requiring user confirmation
  60. // Displays a menu by user and two buttons "OK" and "CANCEL"
  61. // The function returns true for OK and false for CANCEL
  62. // Usage: Call this function ONCE during startup to declare and init
  63. // the ynDialog JavaScript function, and call the function
  64. // from the program when needed.
  65. // Paramters of the JavaScript function:
  66. // s: Th strig contining the question to be answered
  67. // o: The OK tab for the webpage where to go to
  68. // c: The Cancel string (optional)
  69. // --------------------------------------------------------------------------------
  70. boolean YesNo()
  71. {
  72. boolean ret = false;
  73. String response = "";
  74. response += "<script>";
  75. response += "var ch = \"\"; "; // Init choice
  76. response += "function ynDialog(s,y) {";
  77. response += " try { adddlert(s); }";
  78. response += " catch(err) {";
  79. response += " ch = \" \" + s + \".\\n\\n\"; ";
  80. response += " ch += \"Click OK to continue,\\n\"; ";
  81. response += " ch += \"or Cancel\\n\\n\"; ";
  82. response += " if(!confirm(ch)) { ";
  83. response += " javascript:window.location.reload(true);";
  84. response += " } ";
  85. response += " else { ";
  86. response += " document.location.href = '/'+y; ";
  87. response += " } ";
  88. response += " }";
  89. response += "}";
  90. response += "</script>";
  91. server.sendContent(response);
  92. // Put something like this in the ESP program
  93. // response += "<input type=\"button\" value=\"YesNo\" onclick=\"ynDialog()\" />";
  94. return(ret);
  95. }
  96. // --------------------------------------------------------------------------------
  97. // WWWFILE
  98. // This function will open a pop-up in the browser and then
  99. // display the contents of a file in that window
  100. // Output is sent to server.sendContent()
  101. // Note: The output is not in a variable, its size would be too large
  102. // Parameters:
  103. // fn; String with filename
  104. // Returns:
  105. // <none>
  106. // --------------------------------------------------------------------------------
  107. void wwwFile(String fn) {
  108. if (!SPIFFS.exists(fn)) {
  109. #if _DUSB>=1
  110. Serial.print(F("wwwFile:: ERROR: file not found="));
  111. Serial.println(fn);
  112. #endif
  113. return;
  114. }
  115. #if _DUSB>=2
  116. else {
  117. Serial.print(F("wwwFile:: File existist= "));
  118. Serial.println(fn);
  119. }
  120. #endif
  121. #if _DUSB>=1
  122. File f = SPIFFS.open(fn, "r"); // Open the file for reading
  123. int j;
  124. for (j=0; j<LOGFILEREC; j++) {
  125. String s=f.readStringUntil('\n');
  126. if (s.length() == 0) {
  127. Serial.print(F("wwwFile:: String length 0"));
  128. break;
  129. }
  130. server.sendContent(s.substring(12)); // Skip the first 12 Gateway specific binary characters
  131. server.sendContent("\n");
  132. yield();
  133. }
  134. f.close();
  135. #endif
  136. }
  137. // --------------------------------------------------------------------------------
  138. // Button function Docu, display the documentation pages.
  139. // This is a button on the top of the GUI screen.
  140. // --------------------------------------------------------------------------------
  141. void buttonDocu()
  142. {
  143. String response = "";
  144. response += "<script>";
  145. response += "var txt = \"\";";
  146. response += "function showDocu() {";
  147. response += " try { adddlert(\"Welcome,\"); }";
  148. response += " catch(err) {";
  149. response += " txt = \"Do you want the documentation page.\\n\\n\"; ";
  150. response += " txt += \"Click OK to continue viewing documentation,\\n\"; ";
  151. response += " txt += \"or Cancel to return to the home page.\\n\\n\"; ";
  152. response += " if(confirm(txt)) { ";
  153. response += " document.location.href = \"https://things4u.github.io/Projects/SingleChannelGateway/UserGuide/Introduction%206.html\"; ";
  154. response += " }";
  155. response += " }";
  156. response += "}";
  157. response += "</script>";
  158. server.sendContent(response);
  159. }
  160. // --------------------------------------------------------------------------------
  161. // Button function Log displays logfiles.
  162. // This is a button on the top of the GUI screen
  163. // --------------------------------------------------------------------------------
  164. void buttonLog()
  165. {
  166. // String response = "";
  167. String fn = "";
  168. int i = 0;
  169. while (i< LOGFILEMAX ) {
  170. fn = "/log-" + String(gwayConfig.logFileNo - i);
  171. wwwFile(fn); // Display the file contents in the browser
  172. i++;
  173. }
  174. // server.sendContent(response);
  175. }
  176. // --------------------------------------------------------------------------------
  177. // BUTTONSEEN
  178. // List the listSeen array.
  179. // Read the logfiles and display info about nodes (last seend, SF used etc).
  180. // This is a button on the top of the GUI screen
  181. // --------------------------------------------------------------------------------
  182. void buttonSeen()
  183. {
  184. String fn = "";
  185. int i = 0;
  186. printSeen(listSeen);
  187. #if _DUSB>=1
  188. if (( debug>=1 ) && ( pdebug & P_MAIN )) {
  189. Serial.println(F("buttonSeen:: printSeen called"));
  190. }
  191. #endif
  192. }
  193. // --------------------------------------------------------------------------------
  194. // Navigate webpage by buttons. This method has some advantages:
  195. // - Less time/cpu usage
  196. // - Less memory usage <a href=\"SPEED=160\">
  197. // --------------------------------------------------------------------------------
  198. static void wwwButtons()
  199. {
  200. String response = "";
  201. String mode = (gwayConfig.expert ? "Basic Mode" : "Expert Mode");
  202. YesNo(); // Init the Yes/No function
  203. buttonDocu();
  204. response += "<input type=\"button\" value=\"Documentation\" onclick=\"showDocu()\" >";
  205. response += "<a href=\"EXPERT\" download><button type=\"button\">" + mode + "</button></a>";
  206. response += "<a href=\"SEEN\" download><button type=\"button\">Nodes Seen</button></a>";
  207. response += "<a href=\"LOG\" download><button type=\"button\">Log Files</button></a>";
  208. server.sendContent(response); // Send to the screen
  209. }
  210. // --------------------------------------------------------------------------------
  211. // SET ESP8266 WEB SERVER VARIABLES
  212. //
  213. // This funtion implements the WiFi Webserver (very simple one). The purpose
  214. // of this server is to receive simple admin commands, and execute these
  215. // results which are sent back to the web client.
  216. // Commands: DEBUG, ADDRESS, IP, CONFIG, GETTIME, SETTIME
  217. // The webpage is completely built response and then printed on screen.
  218. //
  219. // Parameters:
  220. // cmd: Contains a character array with the command to execute
  221. // arg: Contains the parameter value of that command
  222. // Returns:
  223. // <none>
  224. // --------------------------------------------------------------------------------
  225. static void setVariables(const char *cmd, const char *arg) {
  226. // DEBUG settings; These can be used as a single argument
  227. if (strcmp(cmd, "DEBUG")==0) { // Set debug level 0-2
  228. if (atoi(arg) == 1) {
  229. debug = (debug+1)%4;
  230. }
  231. else if (atoi(arg) == -1) {
  232. debug = (debug+3)%4;
  233. }
  234. writeGwayCfg(CONFIGFILE); // Save configuration to file
  235. }
  236. if (strcmp(cmd, "CAD")==0) { // Set -cad on=1 or off=0
  237. _cad=(bool)atoi(arg);
  238. writeGwayCfg(CONFIGFILE); // Save configuration to file
  239. }
  240. if (strcmp(cmd, "HOP")==0) { // Set -hop on=1 or off=0
  241. _hop=(bool)atoi(arg);
  242. if (! _hop) {
  243. ifreq=0;
  244. setFreq(freqs[ifreq].upFreq);
  245. rxLoraModem();
  246. sf = SF7;
  247. cadScanner();
  248. }
  249. writeGwayCfg(CONFIGFILE); // Save configuration to file
  250. }
  251. if (strcmp(cmd, "DELAY")==0) { // Set delay usecs
  252. txDelay+=atoi(arg)*1000;
  253. }
  254. // SF; Handle Spreading Factor Settings
  255. if (strcmp(cmd, "SF")==0) {
  256. uint8_t sfi = sf;
  257. if (atoi(arg) == 1) {
  258. if (sf>=SF12) sf=SF7; else sf= (sf_t)((int)sf+1);
  259. }
  260. else if (atoi(arg) == -1) {
  261. if (sf<=SF7) sf=SF12; else sf= (sf_t)((int)sf-1);
  262. }
  263. rxLoraModem(); // Reset the radio with the new spreading factor
  264. writeGwayCfg(CONFIGFILE); // Save configuration to file
  265. }
  266. // FREQ; Handle Frequency Settings
  267. if (strcmp(cmd, "FREQ")==0) {
  268. uint8_t nf = sizeof(freqs)/ sizeof(freqs[0]); // Number of frequency elements in array
  269. // Compute frequency index
  270. if (atoi(arg) == 1) {
  271. if (ifreq==(nf-1)) ifreq=0; else ifreq++;
  272. }
  273. else if (atoi(arg) == -1) {
  274. Serial.println("down");
  275. if (ifreq==0) ifreq=(nf-1); else ifreq--;
  276. }
  277. setFreq(freqs[ifreq].upFreq);
  278. rxLoraModem(); // Reset the radio with the new frequency
  279. writeGwayCfg(CONFIGFILE); // Save configuration to file
  280. }
  281. //if (strcmp(cmd, "GETTIME")==0) { Serial.println(F("gettime tbd")); } // Get the local time
  282. //if (strcmp(cmd, "SETTIME")==0) { Serial.println(F("settime tbd")); } // Set the local time
  283. if (strcmp(cmd, "HELP")==0) { Serial.println(F("Display Help Topics")); }
  284. #if GATEWAYNODE==1
  285. if (strcmp(cmd, "NODE")==0) { // Set node on=1 or off=0
  286. gwayConfig.isNode =(bool)atoi(arg);
  287. writeGwayCfg(CONFIGFILE); // Save configuration to file
  288. }
  289. if (strcmp(cmd, "FCNT")==0) {
  290. frameCount=0;
  291. rxLoraModem(); // Reset the radio with the new frequency
  292. writeGwayCfg(CONFIGFILE);
  293. }
  294. #endif
  295. #if _WIFIMANAGER==1
  296. if (strcmp(cmd, "NEWSSID")==0) {
  297. WiFiManager wifiManager;
  298. strcpy(wpa[0].login,"");
  299. strcpy(wpa[0].passw,"");
  300. WiFi.disconnect();
  301. wifiManager.autoConnect(AP_NAME, AP_PASSWD );
  302. }
  303. #endif
  304. #if A_OTA==1
  305. if (strcmp(cmd, "UPDATE")==0) {
  306. if (atoi(arg) == 1) {
  307. updateOtaa();
  308. }
  309. }
  310. #endif
  311. #if A_REFRESH==1
  312. if (strcmp(cmd, "REFR")==0) { // Set refresh on=1 or off=0
  313. gwayConfig.refresh =(bool)atoi(arg);
  314. writeGwayCfg(CONFIGFILE); // Save configuration to file
  315. }
  316. #endif
  317. }
  318. // --------------------------------------------------------------------------------
  319. // OPEN WEB PAGE
  320. // This is the init function for opening the webpage
  321. //
  322. // --------------------------------------------------------------------------------
  323. static void openWebPage()
  324. {
  325. ++gwayConfig.views; // increment number of views
  326. #if A_REFRESH==1
  327. //server.client().stop(); // Experimental, stop webserver in case something is still running!
  328. #endif
  329. String response="";
  330. server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
  331. server.sendHeader("Pragma", "no-cache");
  332. server.sendHeader("Expires", "-1");
  333. // init webserver, fill the webpage
  334. // NOTE: The page is renewed every _WWW_INTERVAL seconds, please adjust in configGway.h
  335. //
  336. server.setContentLength(CONTENT_LENGTH_UNKNOWN);
  337. server.send(200, "text/html", "");
  338. #if A_REFRESH==1
  339. if (gwayConfig.refresh) {
  340. response += String() + "<!DOCTYPE HTML><HTML><HEAD><meta http-equiv='refresh' content='"+_WWW_INTERVAL+";http://";
  341. printIP((IPAddress)WiFi.localIP(),'.',response);
  342. response += "'><TITLE>ESP8266 1ch Gateway</TITLE>";
  343. }
  344. else {
  345. response += String() + "<!DOCTYPE HTML><HTML><HEAD><TITLE>ESP8266 1ch Gateway</TITLE>";
  346. }
  347. #else
  348. response += String() + "<!DOCTYPE HTML><HTML><HEAD><TITLE>ESP8266 1ch Gateway</TITLE>";
  349. #endif
  350. response += "<META HTTP-EQUIV='CONTENT-TYPE' CONTENT='text/html; charset=UTF-8'>";
  351. response += "<META NAME='AUTHOR' CONTENT='M. Westenberg (mw1554@hotmail.com)'>";
  352. response += "<style>.thead {background-color:green; color:white;} ";
  353. response += ".cell {border: 1px solid black;}";
  354. response += ".config_table {max_width:100%; min-width:400px; width:98%; border:1px solid black; border-collapse:collapse;}";
  355. response += "</style></HEAD><BODY>";
  356. response +="<h1>ESP Gateway Config</h1>";
  357. response +="<p style='font-size:10px;'>";
  358. response +="Version: "; response+=VERSION;
  359. response +="<br>ESP alive since "; // STARTED ON
  360. stringTime(startTime, response);
  361. response +=", Uptime: "; // UPTIME
  362. uint32_t secs = millis()/1000;
  363. uint16_t days = secs / 86400; // Determine number of days
  364. uint8_t _hour = hour(secs);
  365. uint8_t _minute = minute(secs);
  366. uint8_t _second = second(secs);
  367. response += String() + days + "-";
  368. if (_hour < 10) response += "0";
  369. response += String() + _hour + ":";
  370. if (_minute < 10) response += "0";
  371. response += String() + _minute + ":";
  372. if (_second < 10) response += "0";
  373. response += String() + _second;
  374. response +="<br>Current time "; // CURRENT TIME
  375. stringTime(now(), response);
  376. response +="<br>";
  377. response +="</p>";
  378. server.sendContent(response);
  379. }
  380. // --------------------------------------------------------------------------------
  381. // H2 Gateway Settings
  382. //
  383. // Display the configuration and settings data. This is an interactive setting
  384. // allowing the user to set CAD, HOP, Debug and several other operating parameters
  385. //
  386. // --------------------------------------------------------------------------------
  387. static void gatewaySettings()
  388. {
  389. String response="";
  390. String bg="";
  391. response +="<h2>Gateway Settings</h2>";
  392. response +="<table class=\"config_table\">";
  393. response +="<tr>";
  394. response +="<th class=\"thead\">Setting</th>";
  395. response +="<th colspan=\"2\" style=\"background-color: green; color: white; width:120px;\">Value</th>";
  396. response +="<th colspan=\"4\" style=\"background-color: green; color: white; width:100px;\">Set</th>";
  397. response +="</tr>";
  398. bg = " background-color: ";
  399. bg += ( _cad ? "LightGreen" : "orange" );
  400. response +="<tr><td class=\"cell\">CAD</td>";
  401. response +="<td colspan=\"2\" style=\"border: 1px solid black;"; response += bg; response += "\">";
  402. response += ( _cad ? "ON" : "OFF" );
  403. response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"CAD=1\"><button>ON</button></a></td>";
  404. response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"CAD=0\"><button>OFF</button></a></td>";
  405. response +="</tr>";
  406. bg = " background-color: ";
  407. bg += ( _hop ? "LightGreen" : "orange" );
  408. response +="<tr><td class=\"cell\">HOP</td>";
  409. response +="<td colspan=\"2\" style=\"border: 1px solid black;"; response += bg; response += "\">";
  410. response += ( _hop ? "ON" : "OFF" );
  411. response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"HOP=1\"><button>ON</button></a></td>";
  412. response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"HOP=0\"><button>OFF</button></a></td>";
  413. response +="</tr>";
  414. response +="<tr><td class=\"cell\">SF Setting</td><td class=\"cell\" colspan=\"2\">";
  415. if (_cad) {
  416. response += "AUTO</td>";
  417. }
  418. else {
  419. response += sf;
  420. response +="<td class=\"cell\"><a href=\"SF=-1\"><button>-</button></a></td>";
  421. response +="<td class=\"cell\"><a href=\"SF=1\"><button>+</button></a></td>";
  422. }
  423. response +="</tr>";
  424. // Channel
  425. response +="<tr><td class=\"cell\">Channel</td>";
  426. response +="<td class=\"cell\" colspan=\"2\">";
  427. if (_hop) {
  428. response += "AUTO</td>";
  429. }
  430. else {
  431. response += String() + ifreq;
  432. response +="</td>";
  433. response +="<td class=\"cell\"><a href=\"FREQ=-1\"><button>-</button></a></td>";
  434. response +="<td class=\"cell\"><a href=\"FREQ=1\"><button>+</button></a></td>";
  435. }
  436. response +="</tr>";
  437. // Debugging options, only when _DUSB is set, otherwise no
  438. // serial activity
  439. #if _DUSB>=1
  440. response +="<tr><td class=\"cell\">Debug Level</td><td class=\"cell\" colspan=\"2\">";
  441. response +=debug;
  442. response +="</td>";
  443. response +="<td class=\"cell\"><a href=\"DEBUG=-1\"><button>-</button></a></td>";
  444. response +="<td class=\"cell\"><a href=\"DEBUG=1\"><button>+</button></a></td>";
  445. response +="</tr>";
  446. // Time Correction
  447. if (gwayConfig.expert) {
  448. response +="<tr><td class=\"cell\">Time Correction (uSec)</td><td class=\"cell\" colspan=\"2\">";
  449. response += txDelay;
  450. response +="</td>";
  451. response +="<td class=\"cell\"><a href=\"DELAY=-1\"><button>-</button></a></td>";
  452. response +="<td class=\"cell\"><a href=\"DELAY=1\"><button>+</button></a></td>";
  453. response +="</tr>";
  454. }
  455. // Debug Pattern
  456. response +="<tr><td class=\"cell\">Debug pattern</td>";
  457. bg = ( (pdebug & P_SCAN) ? "LightGreen" : "orange" );
  458. response +="<td class=\"cell\" style=\"border: 1px solid black; width:20px; background-color: ";
  459. response += bg; response += "\">";
  460. response +="<a href=\"PDEBUG=SCAN\">";
  461. response +="<button>SCN</button></a></td>";
  462. bg = ( (pdebug & P_CAD) ? "LightGreen" : "orange" );
  463. response +="<td class=\"cell\" style=\"border: 1px solid black; width:20px; background-color: ";
  464. response += bg; response += "\">";
  465. response +="<a href=\"PDEBUG=CAD\">";
  466. response +="<button>CAD</button></a></td>";
  467. bg = ( (pdebug & P_RX) ? "LightGreen" : "orange" );
  468. response +="<td class=\"cell\" style=\"border: 1px solid black; width:20px; background-color: ";
  469. response += bg; response += "\">";
  470. response +="<a href=\"PDEBUG=RX\">";
  471. response +="<button>RX</button></a></td>";
  472. bg = ( (pdebug & P_TX) ? "LightGreen" : "orange" );
  473. response +="<td class=\"cell\" style=\"border: 1px solid black; width:20px; background-color: ";
  474. response += bg; response += "\">";
  475. response +="<a href=\"PDEBUG=TX\">";
  476. response +="<button>TX</button></a></td>";
  477. response += "</tr>";
  478. // Use a second Line
  479. response +="<tr><td class=\"cell\"></td>";
  480. bg = ( (pdebug & P_PRE) ? "LightGreen" : "orange" );
  481. response +="<td class=\"cell\" style=\"border: 1px solid black; width:20px; background-color: ";
  482. response += bg; response += "\">";
  483. response +="<a href=\"PDEBUG=PRE\">";
  484. response +="<button>PRE</button></a></td>";
  485. bg = ( (pdebug & P_MAIN) ? "LightGreen" : "orange" );
  486. response +="<td class=\"cell\" style=\"border: 1px solid black; width:20px; background-color: ";
  487. response += bg; response += "\">";
  488. response +="<a href=\"PDEBUG=MAIN\">";
  489. response +="<button>MAI</button></a></td>";
  490. bg = ( (pdebug & P_GUI) ? "LightGreen" : "orange" );
  491. response +="<td class=\"cell\" style=\"border: 1px solid black; width:20px; background-color: ";
  492. response += bg; response += "\">";
  493. response +="<a href=\"PDEBUG=GUI\">";
  494. response +="<button>GUI</button></a></td>";
  495. bg = ( (pdebug & P_RADIO) ? "LightGreen" : "orange" );
  496. response +="<td class=\"cell\" style=\"border: 1px solid black; width:20px; background-color: ";
  497. response += bg; response += "\">";
  498. response +="<a href=\"PDEBUG=RADIO\">";
  499. response +="<button>RDIO</button></a></td>";
  500. response +="</tr>";
  501. #endif
  502. // USB Debug, Serial Debugging
  503. response +="<tr><td class=\"cell\">Usb Debug</td><td class=\"cell\" colspan=\"2\">";
  504. response += _DUSB;
  505. response +="</td>";
  506. //response +="<td class=\"cell\"> </td>";
  507. //response +="<td class=\"cell\"> </td>";
  508. response +="</tr>";
  509. #if GATEWAYNODE==1
  510. response +="<tr><td class=\"cell\">Framecounter Internal Sensor</td>";
  511. response +="<td class=\"cell\" colspan=\"2\">";
  512. response +=frameCount;
  513. response +="</td><td colspan=\"2\" style=\"border: 1px solid black;\">";
  514. response +="<button><a href=\"/FCNT\">RESET</a></button></td>";
  515. response +="</tr>";
  516. bg = " background-color: ";
  517. bg += ( (gwayConfig.isNode == 1) ? "LightGreen" : "orange" );
  518. response +="<tr><td class=\"cell\">Gateway Node</td>";
  519. response +="<td class=\"cell\" style=\"border: 1px solid black;" + bg + "\">";
  520. response += ( (gwayConfig.isNode == true) ? "ON" : "OFF" );
  521. response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"NODE=1\"><button>ON</button></a></td>";
  522. response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"NODE=0\"><button>OFF</button></a></td>";
  523. response +="</tr>";
  524. #endif
  525. /// WWW Refresh
  526. #if A_REFRESH==1
  527. bg = " background-color: ";
  528. bg += ( (gwayConfig.refresh == 1) ? "LightGreen" : "orange" );
  529. response +="<tr><td class=\"cell\">WWW Refresh</td>";
  530. response +="<td class=\"cell\" colspan=\"2\" style=\"border: 1px solid black; " + bg + "\">";
  531. response += ( (gwayConfig.refresh == 1) ? "ON" : "OFF" );
  532. response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"REFR=1\"><button>ON</button></a></td>";
  533. response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"REFR=0\"><button>OFF</button></a></td>";
  534. response +="</tr>";
  535. #endif
  536. // Reset Accesspoint
  537. #if _WIFIMANAGER==1
  538. response +="<tr><td><tr><td>";
  539. response +="Click <a href=\"/NEWSSID\">here</a> to reset accesspoint<br>";
  540. response +="</td><td></td></tr>";
  541. #endif
  542. // Update Firmware all statistics
  543. response +="<tr><td class=\"cell\">Update Firmware</td><td colspan=\"2\"></td>";
  544. response +="<td class=\"cell\" colspan=\"2\" class=\"cell\"><a href=\"/UPDATE=1\"><button>UPDATE</button></a></td></tr>";
  545. // Format the Filesystem
  546. response +="<tr><td class=\"cell\">Format SPIFFS</td>";
  547. response +=String() + "<td class=\"cell\" colspan=\"2\" >"+""+"</td>";
  548. response +="<td colspan=\"2\" class=\"cell\"><input type=\"button\" value=\"FORMAT\" onclick=\"ynDialog(\'Do you really want to format?\',\'FORMAT\')\" /></td></tr>";
  549. // Reset all statistics
  550. #if _STATISTICS >= 1
  551. response +="<tr><td class=\"cell\">Statistics</td>";
  552. response +=String() + "<td class=\"cell\" colspan=\"2\" >"+statc.resets+"</td>";
  553. response +="<td colspan=\"2\" class=\"cell\"><input type=\"button\" value=\"RESET\" onclick=\"ynDialog(\'Do you really want to reset statistics?\',\'RESET\')\" /></td></tr>";
  554. // Reset
  555. response +="<tr><td class=\"cell\">Boots and Resets</td>";
  556. response +=String() + "<td class=\"cell\" colspan=\"2\" >"+gwayConfig.boots+"</td>";
  557. response +="<td colspan=\"2\" class=\"cell\"><input type=\"button\" value=\"RESET\" onclick=\"ynDialog(\'Do you want to reset boots?\',\'BOOT\')\" /></td></tr>";
  558. #endif
  559. response +="</table>";
  560. server.sendContent(response);
  561. }
  562. // --------------------------------------------------------------------------------
  563. // H2 Package Statistics
  564. //
  565. // This section display a matrix on the screen where everay channel and spreading
  566. // factor is displayed.
  567. // --------------------------------------------------------------------------------
  568. static void statisticsData()
  569. {
  570. String response="";
  571. // Header
  572. response +="<h2>Package Statistics</h2>";
  573. response +="<table class=\"config_table\">";
  574. response +="<tr><th class=\"thead\">Counter</th>";
  575. #if _STATISTICS == 3
  576. response +="<th class=\"thead\">C 0</th>";
  577. response +="<th class=\"thead\">C 1</th>";
  578. response +="<th class=\"thead\">C 2</th>";
  579. #endif
  580. response +="<th class=\"thead\">Pkgs</th>";
  581. response +="<th class=\"thead\">Pkgs/hr</th>";
  582. response +="</tr>";
  583. //
  584. // Table rows
  585. //
  586. response +="<tr><td class=\"cell\">Packages Downlink</td>";
  587. #if _STATISTICS == 3
  588. response +="<td class=\"cell\">" + String(statc.msg_down_0) + "</td>";
  589. response +="<td class=\"cell\">" + String(statc.msg_down_1) + "</td>";
  590. response +="<td class=\"cell\">" + String(statc.msg_down_2) + "</td>";
  591. #endif
  592. response += "<td class=\"cell\">" + String(statc.msg_down) + "</td>";
  593. response +="<td class=\"cell\"></td></tr>";
  594. response +="<tr><td class=\"cell\">Packages Uplink Total</td>";
  595. #if _STATISTICS == 3
  596. response +="<td class=\"cell\">" + String(statc.msg_ttl_0) + "</td>";
  597. response +="<td class=\"cell\">" + String(statc.msg_ttl_1) + "</td>";
  598. response +="<td class=\"cell\">" + String(statc.msg_ttl_2) + "</td>";
  599. #endif
  600. response +="<td class=\"cell\">" + String(statc.msg_ttl) + "</td>";
  601. response +="<td class=\"cell\">" + String((statc.msg_ttl*3600)/(now() - startTime)) + "</td></tr>";
  602. response +="<tr><td class=\"cell\">Packages Uplink OK </td>";
  603. #if _STATISTICS == 3
  604. response +="<td class=\"cell\">" + String(statc.msg_ok_0) + "</td>";
  605. response +="<td class=\"cell\">" + String(statc.msg_ok_1) + "</td>";
  606. response +="<td class=\"cell\">" + String(statc.msg_ok_2) + "</td>";
  607. #endif
  608. response +="<td class=\"cell\">" + String(statc.msg_ok) + "</td>";
  609. response +="<td class=\"cell\"></td></tr>";
  610. // Provide a table with all the SF data including percentage of messsages
  611. #if _STATISTICS == 2
  612. response +="<tr><td class=\"cell\">SF7 rcvd</td>";
  613. response +="<td class=\"cell\">"; response +=statc.sf7;
  614. response +="<td class=\"cell\">"; response += String(statc.msg_ttl>0 ? 100*statc.sf7/statc.msg_ttl : 0)+" %";
  615. response +="</td></tr>";
  616. response +="<tr><td class=\"cell\">SF8 rcvd</td>";
  617. response +="<td class=\"cell\">"; response +=statc.sf8;
  618. response +="<td class=\"cell\">"; response += String(statc.msg_ttl>0 ? 100*statc.sf8/statc.msg_ttl : 0)+" %";
  619. response +="</td></tr>";
  620. response +="<tr><td class=\"cell\">SF9 rcvd</td>";
  621. response +="<td class=\"cell\">"; response +=statc.sf9;
  622. response +="<td class=\"cell\">"; response += String(statc.msg_ttl>0 ? 100*statc.sf9/statc.msg_ttl : 0)+" %";
  623. response +="</td></tr>";
  624. response +="<tr><td class=\"cell\">SF10 rcvd</td>";
  625. response +="<td class=\"cell\">"; response +=statc.sf10;
  626. response +="<td class=\"cell\">"; response += String(statc.msg_ttl>0 ? 100*statc.sf10/statc.msg_ttl : 0)+" %";
  627. response +="</td></tr>";
  628. response +="<tr><td class=\"cell\">SF11 rcvd</td>";
  629. response +="<td class=\"cell\">"; response +=statc.sf11;
  630. response +="<td class=\"cell\">"; response += String(statc.msg_ttl>0 ? 100*statc.sf11/statc.msg_ttl : 0)+" %";
  631. response +="</td></tr>";
  632. response +="<tr><td class=\"cell\">SF12 rcvd</td>";
  633. response +="<td class=\"cell\">"; response +=statc.sf12;
  634. response +="<td class=\"cell\">"; response += String(statc.msg_ttl>0 ? 100*statc.sf12/statc.msg_ttl : 0)+" %";
  635. response +="</td></tr>";
  636. #endif
  637. #if _STATISTICS == 3
  638. response +="<tr><td class=\"cell\">SF7 rcvd</td>";
  639. response +="<td class=\"cell\">"; response +=statc.sf7_0;
  640. response +="<td class=\"cell\">"; response +=statc.sf7_1;
  641. response +="<td class=\"cell\">"; response +=statc.sf7_2;
  642. response +="<td class=\"cell\">"; response +=statc.sf7;
  643. response +="<td class=\"cell\">"; response += String(statc.msg_ttl>0 ? 100*statc.sf7/statc.msg_ttl : 0)+" %";
  644. response +="</td></tr>";
  645. response +="<tr><td class=\"cell\">SF8 rcvd</td>";
  646. response +="<td class=\"cell\">"; response +=statc.sf8_0;
  647. response +="<td class=\"cell\">"; response +=statc.sf8_1;
  648. response +="<td class=\"cell\">"; response +=statc.sf8_2;
  649. response +="<td class=\"cell\">"; response +=statc.sf8;
  650. response +="<td class=\"cell\">"; response += String(statc.msg_ttl>0 ? 100*statc.sf8/statc.msg_ttl : 0)+" %";
  651. response +="</td></tr>";
  652. response +="<tr><td class=\"cell\">SF9 rcvd</td>";
  653. response +="<td class=\"cell\">"; response +=statc.sf9_0;
  654. response +="<td class=\"cell\">"; response +=statc.sf9_1;
  655. response +="<td class=\"cell\">"; response +=statc.sf9_2;
  656. response +="<td class=\"cell\">"; response +=statc.sf9;
  657. response +="<td class=\"cell\">"; response += String(statc.msg_ttl>0 ? 100*statc.sf9/statc.msg_ttl : 0)+" %";
  658. response +="</td></tr>";
  659. response +="<tr><td class=\"cell\">SF10 rcvd</td>";
  660. response +="<td class=\"cell\">"; response +=statc.sf10_0;
  661. response +="<td class=\"cell\">"; response +=statc.sf10_1;
  662. response +="<td class=\"cell\">"; response +=statc.sf10_2;
  663. response +="<td class=\"cell\">"; response +=statc.sf10;
  664. response +="<td class=\"cell\">"; response += String(statc.msg_ttl>0 ? 100*statc.sf10/statc.msg_ttl : 0)+" %";
  665. response +="</td></tr>";
  666. response +="<tr><td class=\"cell\">SF11 rcvd</td>";
  667. response +="<td class=\"cell\">"; response +=statc.sf11_0;
  668. response +="<td class=\"cell\">"; response +=statc.sf11_1;
  669. response +="<td class=\"cell\">"; response +=statc.sf11_2;
  670. response +="<td class=\"cell\">"; response +=statc.sf11;
  671. response +="<td class=\"cell\">"; response += String(statc.msg_ttl>0 ? 100*statc.sf11/statc.msg_ttl : 0)+" %";
  672. response +="</td></tr>";
  673. response +="<tr><td class=\"cell\">SF12 rcvd</td>";
  674. response +="<td class=\"cell\">"; response +=statc.sf12_0;
  675. response +="<td class=\"cell\">"; response +=statc.sf12_1;
  676. response +="<td class=\"cell\">"; response +=statc.sf12_1;
  677. response +="<td class=\"cell\">"; response +=statc.sf12;
  678. response +="<td class=\"cell\">"; response += String(statc.msg_ttl>0 ? 100*statc.sf12/statc.msg_ttl : 0)+" %";
  679. response +="</td></tr>";
  680. #endif
  681. response +="</table>";
  682. server.sendContent(response);
  683. }
  684. // --------------------------------------------------------------------------------
  685. // Message History
  686. // If enabled, display the sensor messageHistory on the current webserver Page.
  687. // In this GUI section a number of statr[x] records are displayed such as:
  688. //
  689. // Time, The time the sensor message was received
  690. // Node, the DevAddr or even Node name for Trusted nodes,
  691. // Data (Localserver), when _LOCALSERVER is enabled contains decoded data
  692. // C, Channel frequency on which the sensor was received
  693. // Freq, The frequency of the channel
  694. // SF, Spreading Factor
  695. // pRSSI, Packet RSSI
  696. //
  697. // Parameters:
  698. // - <none>
  699. // Returns:
  700. // - <none>
  701. // --------------------------------------------------------------------------------
  702. static void messageHistory()
  703. {
  704. #if _STATISTICS >= 1
  705. String response="";
  706. response += "<h2>Message History</h2>";
  707. response += "<table class=\"config_table\">";
  708. response += "<tr>";
  709. response += "<th class=\"thead\">Time</th>";
  710. response += "<th class=\"thead\">Node</th>";
  711. #if _LOCALSERVER==1
  712. response += "<th class=\"thead\">Data</th>";
  713. #endif
  714. response += "<th class=\"thead\" style=\"width: 20px;\">C</th>";
  715. response += "<th class=\"thead\">Freq</th>";
  716. response += "<th class=\"thead\" style=\"width: 40px;\">SF</th>";
  717. response += "<th class=\"thead\" style=\"width: 50px;\">pRSSI</th>";
  718. #if RSSI==1
  719. if (debug > 1) {
  720. response += "<th class=\"thead\" style=\"width: 50px;\">RSSI</th>";
  721. }
  722. #endif
  723. response += "</tr>";
  724. server.sendContent(response);
  725. for (int i=0; i<MAX_STAT; i++) {
  726. if (statr[i].sf == 0) break;
  727. response = "";
  728. response += String() + "<tr><td class=\"cell\">"; // Tmst
  729. stringTime((statr[i].tmst), response); // XXX Change tmst not to be millis() dependent
  730. response += "</td>";
  731. response += String() + "<td class=\"cell\">"; // Node
  732. if (SerialName((char *)(& (statr[i].node)), response) < 0) { // works with TRUSTED_NODES >= 1
  733. printHEX((char *)(& (statr[i].node)),' ',response); // else
  734. }
  735. response += "</td>";
  736. #if _LOCALSERVER==1
  737. response += String() + "<td class=\"cell\">"; // Data
  738. for (int j=0; j<statr[i].datal; j++) {
  739. if (statr[i].data[j] <0x10) response+= "0";
  740. response += String(statr[i].data[j],HEX) + " ";
  741. }
  742. response += "</td>";
  743. #endif
  744. response += String() + "<td class=\"cell\">" + statr[i].ch + "</td>";
  745. response += String() + "<td class=\"cell\">" + freqs[statr[i].ch].upFreq + "</td>";
  746. response += String() + "<td class=\"cell\">" + statr[i].sf + "</td>";
  747. response += String() + "<td class=\"cell\">" + statr[i].prssi + "</td>";
  748. #if RSSI==1
  749. if (debug >= 2) {
  750. response += String() + "<td class=\"cell\">" + statr[i].rssi + "</td>";
  751. }
  752. #endif
  753. response += "</tr>";
  754. server.sendContent(response);
  755. }
  756. server.sendContent("</table>");
  757. #endif
  758. }
  759. // --------------------------------------------------------------------------------
  760. // H2 NODE SEEN HISTORY
  761. // If enabled, display the sensor last Seen history.
  762. // This setting ,pves togetjer with the "expert" mode.
  763. // If that mode is enabled than the node seen intory is displayed
  764. //
  765. // Parameters:
  766. // - <none>
  767. // Returns:
  768. // - <none>
  769. // --------------------------------------------------------------------------------
  770. static void nodeHistory()
  771. {
  772. #if _SEENMAX > 0
  773. if (gwayConfig.expert) {
  774. // First draw the headers
  775. String response="";
  776. response += "<h2>Node Last Seen History</h2>";
  777. response += "<table class=\"config_table\">";
  778. response += "<tr>";
  779. response += "<th class=\"thead\" style=\"width: 220px;\">Time</th>";
  780. response += "<th class=\"thead\">Node</th>";
  781. //#if _LOCALSERVER==1
  782. // response += "<th class=\"thead\">Data</th>";
  783. //#endif
  784. response += "<th class=\"thead\" style=\"width: 20px;\">C</th>";
  785. response += "<th class=\"thead\" style=\"width: 40px;\">SF</th>";
  786. response += "</tr>";
  787. server.sendContent(response);
  788. // Now start the contents
  789. int i;
  790. for (i=0; i<_SEENMAX; i++) {
  791. if (listSeen[i].idSeen == 0) break;
  792. response = "";
  793. response += String() + "<tr><td class=\"cell\">"; // Tmst
  794. stringTime((listSeen[i].timSeen), response);
  795. response += "</td>";
  796. response += String() + "<td class=\"cell\">"; // Node
  797. if (SerialName((char *)(& (listSeen[i].idSeen)), response) < 0) { // works with TRUSTED_NODES >= 1
  798. printHEX((char *)(& (listSeen[i].idSeen)),' ',response); // else
  799. }
  800. response += "</td>";
  801. response += String() + "<td class=\"cell\">" + 0 + "</td>"; // Channel
  802. response += String() + "<td class=\"cell\">" + listSeen[i].sfSeen + "</td>";
  803. server.sendContent(response);
  804. }
  805. server.sendContent("</table>");
  806. }
  807. #endif
  808. }
  809. // --------------------------------------------------------------------------------
  810. // SEND WEB PAGE()
  811. // Call the webserver and send the standard content and the content that is
  812. // passed by the parameter. Each time a variable is changed, this function is
  813. // called to display the webpage again/
  814. //
  815. // NOTE: This is the only place where yield() or delay() calls are used.
  816. //
  817. // --------------------------------------------------------------------------------
  818. void sendWebPage(const char *cmd, const char *arg)
  819. {
  820. openWebPage(); yield(); // Do the initial website setup
  821. wwwButtons(); // Display buttons such as Documentation, Mode, Logfiles
  822. setVariables(cmd,arg); yield(); // Read Webserver commands from line
  823. statisticsData(); yield(); // Node statistics
  824. messageHistory(); yield(); // Display the sensor history, message statistics
  825. nodeHistory(); yield(); // Display the lastSeen array
  826. gatewaySettings(); yield(); // Display web configuration
  827. wifiConfig(); yield(); // WiFi specific parameters
  828. systemStatus(); yield(); // System statistics such as heap etc.
  829. interruptData(); yield(); // Display interrupts only when debug >= 2
  830. websiteFooter(); yield();
  831. server.client().stop();
  832. }
  833. // --------------------------------------------------------------------------------
  834. // setupWWW is the main function for webserver functions/
  835. // SetupWWW function called by main setup() program to setup webserver
  836. // It does actually not much more than installing all the callback handlers
  837. // for messages sent to the webserver
  838. //
  839. // Implemented is an interface like:
  840. // http://<server>/<Variable>=<value>
  841. //
  842. // --------------------------------------------------------------------------------
  843. void setupWWW()
  844. {
  845. server.begin(); // Start the webserver
  846. // -----------------
  847. // BUTTONS, define what should happen with the buttons we press on the homepage
  848. server.on("/", []() {
  849. sendWebPage("",""); // Send the webPage string
  850. server.sendHeader("Location", String("/"), true);
  851. server.send ( 302, "text/plain", "");
  852. });
  853. server.on("/HELP", []() {
  854. sendWebPage("HELP",""); // Send the webPage string
  855. server.sendHeader("Location", String("/"), true);
  856. server.send ( 302, "text/plain", "");
  857. });
  858. // Format the filesystem
  859. server.on("/FORMAT", []() {
  860. Serial.print(F("FORMAT ..."));
  861. SPIFFS.format(); // Normally disabled. Enable only when SPIFFS corrupt
  862. initConfig(&gwayConfig);
  863. writeConfig( CONFIGFILE, &gwayConfig);
  864. #if _DUSB>=1
  865. Serial.println(F("DONE"));
  866. #endif
  867. server.sendHeader("Location", String("/"), true);
  868. server.send ( 302, "text/plain", "");
  869. });
  870. // Reset the statistics
  871. server.on("/RESET", []() {
  872. Serial.println(F("RESET"));
  873. startTime= now() - 1; // Reset all timers too
  874. statc.msg_ttl = 0; // Reset package statistics
  875. statc.msg_ok = 0;
  876. statc.msg_down = 0;
  877. #if _STATISTICS >= 3
  878. statc.msg_ttl_0 = 0;
  879. statc.msg_ttl_1 = 0;
  880. statc.msg_ttl_2 = 0;
  881. statc.msg_ok_0 = 0;
  882. statc.msg_ok_1 = 0;
  883. statc.msg_ok_2 = 0;
  884. statc.msg_down_0 = 0;
  885. statc.msg_down_1 = 0;
  886. statc.msg_down_2 = 0;
  887. #endif
  888. #if _STATISTICS >= 1
  889. for (int i=0; i<MAX_STAT; i++) { statr[i].sf = 0; }
  890. #if _STATISTICS >= 2
  891. statc.sf7 = 0;
  892. statc.sf8 = 0;
  893. statc.sf9 = 0;
  894. statc.sf10= 0;
  895. statc.sf11= 0;
  896. statc.sf12= 0;
  897. statc.resets= 0;
  898. writeGwayCfg(CONFIGFILE);
  899. #if _STATISTICS >= 3
  900. statc.sf7_0 = 0; statc.sf7_1 = 0; statc.sf7_2 = 0;
  901. statc.sf8_0 = 0; statc.sf8_1 = 0; statc.sf8_2 = 0;
  902. statc.sf9_0 = 0; statc.sf9_1 = 0; statc.sf9_2 = 0;
  903. statc.sf10_0= 0; statc.sf10_1= 0; statc.sf10_2= 0;
  904. statc.sf11_0= 0; statc.sf11_1= 0; statc.sf11_2= 0;
  905. statc.sf12_0= 0; statc.sf12_1= 0; statc.sf12_2= 0;
  906. #endif
  907. #endif
  908. #endif
  909. server.sendHeader("Location", String("/"), true);
  910. server.send ( 302, "text/plain", "");
  911. });
  912. // Reset the boot counter
  913. server.on("/BOOT", []() {
  914. #if _STATISTICS >= 2
  915. gwayConfig.boots = 0;
  916. gwayConfig.wifis = 0;
  917. gwayConfig.views = 0;
  918. gwayConfig.ntpErr = 0; // NTP errors
  919. gwayConfig.ntpErrTime = 0; // NTP last error time
  920. gwayConfig.ntps = 0; // Number of NTP calls
  921. #endif
  922. gwayConfig.reents = 0; // Re-entrance
  923. writeGwayCfg(CONFIGFILE);
  924. #if _DUSB>=1
  925. Serial.println(F("BOOT, config written"));
  926. #endif
  927. server.sendHeader("Location", String("/"), true);
  928. server.send ( 302, "text/plain", "");
  929. });
  930. server.on("/NEWSSID", []() {
  931. sendWebPage("NEWSSID",""); // Send the webPage string
  932. server.sendHeader("Location", String("/"), true);
  933. server.send ( 302, "text/plain", "");
  934. });
  935. // Set debug parameter
  936. server.on("/DEBUG=-1", []() { // Set debug level 0-2
  937. debug = (debug+3)%4;
  938. writeGwayCfg(CONFIGFILE); // Save configuration to file
  939. #if _DUSB>=1
  940. Serial.println(F("DEBUG -1: config written"));
  941. #endif
  942. server.sendHeader("Location", String("/"), true);
  943. server.send ( 302, "text/plain", "");
  944. });
  945. server.on("/DEBUG=1", []() {
  946. debug = (debug+1)%4;
  947. writeGwayCfg(CONFIGFILE); // Save configuration to file
  948. #if _DUSB>=1
  949. Serial.println(F("DEBUG +1: config written"));
  950. #endif
  951. server.sendHeader("Location", String("/"), true);
  952. server.send ( 302, "text/plain", "");
  953. });
  954. // Set PDEBUG parameter
  955. //
  956. server.on("/PDEBUG=SCAN", []() { // Set debug level 0-2
  957. pdebug ^= P_SCAN;
  958. writeGwayCfg(CONFIGFILE); // Save configuration to file
  959. server.sendHeader("Location", String("/"), true);
  960. server.send ( 302, "text/plain", "");
  961. });
  962. server.on("/PDEBUG=CAD", []() { // Set debug level 0-2
  963. pdebug ^= P_CAD;
  964. writeGwayCfg(CONFIGFILE); // Save configuration to file
  965. server.sendHeader("Location", String("/"), true);
  966. server.send ( 302, "text/plain", "");
  967. });
  968. server.on("/PDEBUG=RX", []() { // Set debug level 0-2
  969. pdebug ^= P_RX;
  970. writeGwayCfg(CONFIGFILE); // Save configuration to file
  971. server.sendHeader("Location", String("/"), true);
  972. server.send ( 302, "text/plain", "");
  973. });
  974. server.on("/PDEBUG=TX", []() { // Set debug level 0-2
  975. pdebug ^= P_TX;
  976. writeGwayCfg(CONFIGFILE); // Save configuration to file
  977. server.sendHeader("Location", String("/"), true);
  978. server.send ( 302, "text/plain", "");
  979. });
  980. server.on("/PDEBUG=PRE", []() { // Set debug level 0-2
  981. pdebug ^= P_PRE;
  982. writeGwayCfg(CONFIGFILE); // Save configuration to file
  983. server.sendHeader("Location", String("/"), true);
  984. server.send ( 302, "text/plain", "");
  985. });
  986. server.on("/PDEBUG=MAIN", []() { // Set debug level 0-2
  987. pdebug ^= P_MAIN;
  988. writeGwayCfg(CONFIGFILE); // Save configuration to file
  989. server.sendHeader("Location", String("/"), true);
  990. server.send ( 302, "text/plain", "");
  991. });
  992. server.on("/PDEBUG=GUI", []() { // Set debug level 0-2
  993. pdebug ^= P_GUI;
  994. writeGwayCfg(CONFIGFILE); // Save configuration to file
  995. server.sendHeader("Location", String("/"), true);
  996. server.send ( 302, "text/plain", "");
  997. });
  998. server.on("/PDEBUG=RADIO", []() { // Set debug level 0-2
  999. pdebug ^= P_RADIO;
  1000. writeGwayCfg(CONFIGFILE); // Save configuration to file
  1001. server.sendHeader("Location", String("/"), true);
  1002. server.send ( 302, "text/plain", "");
  1003. });
  1004. // Set delay in microseconds
  1005. server.on("/DELAY=1", []() {
  1006. txDelay+=5000;
  1007. writeGwayCfg(CONFIGFILE); // Save configuration to file
  1008. #if _DUSB>=1
  1009. Serial.println(F("DELAY +, config written"));
  1010. #endif
  1011. server.sendHeader("Location", String("/"), true);
  1012. server.send ( 302, "text/plain", "");
  1013. });
  1014. server.on("/DELAY=-1", []() {
  1015. txDelay-=5000;
  1016. writeGwayCfg(CONFIGFILE); // Save configuration to file
  1017. #if _DUSB>=1
  1018. Serial.println(F("DELAY +, config written"));
  1019. #endif
  1020. server.sendHeader("Location", String("/"), true);
  1021. server.send ( 302, "text/plain", "");
  1022. });
  1023. // Spreading Factor setting
  1024. server.on("/SF=1", []() {
  1025. if (sf>=SF12) sf=SF7; else sf= (sf_t)((int)sf+1);
  1026. server.sendHeader("Location", String("/"), true);
  1027. server.send ( 302, "text/plain", "");
  1028. });
  1029. server.on("/SF=-1", []() {
  1030. if (sf<=SF7) sf=SF12; else sf= (sf_t)((int)sf-1);
  1031. server.sendHeader("Location", String("/"), true);
  1032. server.send ( 302, "text/plain", "");
  1033. });
  1034. // Set Frequency of the GateWay node
  1035. server.on("/FREQ=1", []() {
  1036. uint8_t nf = sizeof(freqs)/sizeof(freqs[0]); // Number of elements in array
  1037. #if _DUSB==2
  1038. Serial.print("FREQ==1:: For freq[0] sizeof vector=");
  1039. Serial.print(sizeof(freqs[0]));
  1040. Serial.println();
  1041. #endif
  1042. if (ifreq==(nf-1)) ifreq=0; else ifreq++;
  1043. server.sendHeader("Location", String("/"), true);
  1044. server.send ( 302, "text/plain", "");
  1045. });
  1046. server.on("/FREQ=-1", []() {
  1047. uint8_t nf = sizeof(freqs)/sizeof(freqs[0]); // Number of elements in array
  1048. if (ifreq==0) ifreq=(nf-1); else ifreq--;
  1049. server.sendHeader("Location", String("/"), true);
  1050. server.send ( 302, "text/plain", "");
  1051. });
  1052. // Set CAD function off/on
  1053. server.on("/CAD=1", []() {
  1054. _cad=(bool)1;
  1055. writeGwayCfg(CONFIGFILE); // Save configuration to file
  1056. server.sendHeader("Location", String("/"), true);
  1057. server.send ( 302, "text/plain", "");
  1058. });
  1059. server.on("/CAD=0", []() {
  1060. _cad=(bool)0;
  1061. writeGwayCfg(CONFIGFILE); // Save configuration to file
  1062. server.sendHeader("Location", String("/"), true);
  1063. server.send ( 302, "text/plain", "");
  1064. });
  1065. // GatewayNode
  1066. server.on("/NODE=1", []() {
  1067. #if GATEWAYNODE==1
  1068. gwayConfig.isNode =(bool)1;
  1069. writeGwayCfg(CONFIGFILE); // Save configuration to file
  1070. #endif
  1071. server.sendHeader("Location", String("/"), true);
  1072. server.send ( 302, "text/plain", "");
  1073. });
  1074. server.on("/NODE=0", []() {
  1075. #if GATEWAYNODE==1
  1076. gwayConfig.isNode =(bool)0;
  1077. writeGwayCfg(CONFIGFILE); // Save configuration to file
  1078. #endif
  1079. server.sendHeader("Location", String("/"), true);
  1080. server.send ( 302, "text/plain", "");
  1081. });
  1082. #if GATEWAYNODE==1
  1083. // Framecounter of the Gateway node
  1084. server.on("/FCNT", []() {
  1085. frameCount=0;
  1086. rxLoraModem(); // Reset the radio with the new frequency
  1087. writeGwayCfg(CONFIGFILE);
  1088. //sendWebPage("",""); // Send the webPage string
  1089. server.sendHeader("Location", String("/"), true);
  1090. server.send ( 302, "text/plain", "");
  1091. });
  1092. #endif
  1093. // WWW Page refresh function
  1094. server.on("/REFR=1", []() { // WWW page auto refresh ON
  1095. #if A_REFRESH==1
  1096. gwayConfig.refresh =1;
  1097. writeGwayCfg(CONFIGFILE); // Save configuration to file
  1098. #endif
  1099. server.sendHeader("Location", String("/"), true);
  1100. server.send ( 302, "text/plain", "");
  1101. });
  1102. server.on("/REFR=0", []() { // WWW page auto refresh OFF
  1103. #if A_REFRESH==1
  1104. gwayConfig.refresh =0;
  1105. writeGwayCfg(CONFIGFILE); // Save configuration to file
  1106. #endif
  1107. server.sendHeader("Location", String("/"), true);
  1108. server.send ( 302, "text/plain", "");
  1109. });
  1110. // Switch off/on the HOP functions
  1111. server.on("/HOP=1", []() {
  1112. _hop=true;
  1113. server.sendHeader("Location", String("/"), true);
  1114. server.send ( 302, "text/plain", "");
  1115. });
  1116. server.on("/HOP=0", []() {
  1117. _hop=false;
  1118. ifreq=0;
  1119. setFreq(freqs[ifreq].upFreq);
  1120. rxLoraModem();
  1121. server.sendHeader("Location", String("/"), true);
  1122. server.send ( 302, "text/plain", "");
  1123. });
  1124. #if !defined ESP32_ARCH
  1125. // Change speed to 160 MHz
  1126. server.on("/SPEED=80", []() {
  1127. system_update_cpu_freq(80);
  1128. server.sendHeader("Location", String("/"), true);
  1129. server.send ( 302, "text/plain", "");
  1130. });
  1131. server.on("/SPEED=160", []() {
  1132. system_update_cpu_freq(160);
  1133. server.sendHeader("Location", String("/"), true);
  1134. server.send ( 302, "text/plain", "");
  1135. });
  1136. #endif
  1137. // Display Documentation pages
  1138. server.on("/DOCU", []() {
  1139. server.sendHeader("Location", String("/"), true);
  1140. buttonDocu();
  1141. server.send ( 302, "text/plain", "");
  1142. });
  1143. // Display LOGging information
  1144. server.on("/LOG", []() {
  1145. server.sendHeader("Location", String("/"), true);
  1146. #if _DUSB>=2
  1147. Serial.println(F("LOG button"));
  1148. #endif
  1149. buttonLog();
  1150. server.send ( 302, "text/plain", "");
  1151. });
  1152. // Display Expert mode or Simple mode
  1153. server.on("/EXPERT", []() {
  1154. server.sendHeader("Location", String("/"), true);
  1155. gwayConfig.expert = bool(1 - (int) gwayConfig.expert) ;
  1156. server.send ( 302, "text/plain", "");
  1157. });
  1158. // Display the SEEN statistics
  1159. server.on("/SEEN", []() {
  1160. server.sendHeader("Location", String("/"), true);
  1161. #if _DUSB>=2
  1162. Serial.println(F("SEEN button"));
  1163. #endif
  1164. buttonSeen();
  1165. server.send ( 302, "text/plain", "");
  1166. });
  1167. // Update the sketch. Not yet implemented
  1168. server.on("/UPDATE=1", []() {
  1169. #if A_OTA==1
  1170. updateOtaa();
  1171. #endif
  1172. server.sendHeader("Location", String("/"), true);
  1173. server.send ( 302, "text/plain", "");
  1174. });
  1175. // -----------
  1176. // This section from version 4.0.7 defines what PART of the
  1177. // webpage is shown based on the buttons pressed by the user
  1178. // Maybe not all information should be put on the screen since it
  1179. // may take too much time to serve all information before a next
  1180. // package interrupt arrives at the gateway
  1181. Serial.print(F("WWW Server started on port "));
  1182. Serial.println(A_SERVERPORT);
  1183. return;
  1184. } // setupWWW
  1185. // --------------------------------------------------------------------------------
  1186. // WIFI CONFIG
  1187. // wifiConfig() displays the most important Wifi parameters gathered
  1188. //
  1189. // --------------------------------------------------------------------------------
  1190. static void wifiConfig()
  1191. {
  1192. if (gwayConfig.expert) {
  1193. String response="";
  1194. response +="<h2>WiFi Config</h2>";
  1195. response +="<table class=\"config_table\">";
  1196. response +="<tr><th class=\"thead\">Parameter</th><th class=\"thead\">Value</th></tr>";
  1197. response +="<tr><td class=\"cell\">WiFi host</td><td class=\"cell\">";
  1198. #if ESP32_ARCH==1
  1199. response +=WiFi.getHostname(); response+="</tr>";
  1200. #else
  1201. response +=wifi_station_get_hostname(); response+="</tr>";
  1202. #endif
  1203. response +="<tr><td class=\"cell\">WiFi SSID</td><td class=\"cell\">";
  1204. response +=WiFi.SSID(); response+="</tr>";
  1205. response +="<tr><td class=\"cell\">IP Address</td><td class=\"cell\">";
  1206. printIP((IPAddress)WiFi.localIP(),'.',response);
  1207. response +="</tr>";
  1208. response +="<tr><td class=\"cell\">IP Gateway</td><td class=\"cell\">";
  1209. printIP((IPAddress)WiFi.gatewayIP(),'.',response);
  1210. response +="</tr>";
  1211. response +="<tr><td class=\"cell\">NTP Server</td><td class=\"cell\">"; response+=NTP_TIMESERVER; response+="</tr>";
  1212. response +="<tr><td class=\"cell\">LoRa Router</td><td class=\"cell\">"; response+=_TTNSERVER; response+="</tr>";
  1213. response +="<tr><td class=\"cell\">LoRa Router IP</td><td class=\"cell\">";
  1214. printIP((IPAddress)ttnServer,'.',response);
  1215. response +="</tr>";
  1216. #ifdef _THINGSERVER
  1217. response +="<tr><td class=\"cell\">LoRa Router 2</td><td class=\"cell\">"; response+=_THINGSERVER;
  1218. response += String() + ":" + _THINGPORT + "</tr>";
  1219. response +="<tr><td class=\"cell\">LoRa Router 2 IP</td><td class=\"cell\">";
  1220. printIP((IPAddress)thingServer,'.',response);
  1221. response +="</tr>";
  1222. #endif
  1223. response +="</table>";
  1224. server.sendContent(response);
  1225. } // gwayConfig.expert
  1226. } // wifiConfig
  1227. // --------------------------------------------------------------------------------
  1228. // H2 systemStatus
  1229. // systemStatus is additional and only available in the expert mode.
  1230. // It provides a number of system specific data such as heap size etc.
  1231. // --------------------------------------------------------------------------------
  1232. static void systemStatus()
  1233. {
  1234. if (gwayConfig.expert) {
  1235. String response="";
  1236. response +="<h2>System Status</h2>";
  1237. response +="<table class=\"config_table\">";
  1238. response +="<tr>";
  1239. response +="<th class=\"thead\">Parameter</th>";
  1240. response +="<th class=\"thead\">Value</th>";
  1241. response +="<th colspan=\"2\" class=\"thead\">Set</th>";
  1242. response +="</tr>";
  1243. response +="<tr><td style=\"border: 1px solid black; width:120px;\">Gateway ID</td>";
  1244. response +="<td class=\"cell\">";
  1245. if (MAC_array[0]< 0x10) response +='0'; response +=String(MAC_array[0],HEX); // The MAC array is always returned in lowercase
  1246. if (MAC_array[1]< 0x10) response +='0'; response +=String(MAC_array[1],HEX);
  1247. if (MAC_array[2]< 0x10) response +='0'; response +=String(MAC_array[2],HEX);
  1248. response +="FFFF";
  1249. if (MAC_array[3]< 0x10) response +='0'; response +=String(MAC_array[3],HEX);
  1250. if (MAC_array[4]< 0x10) response +='0'; response +=String(MAC_array[4],HEX);
  1251. if (MAC_array[5]< 0x10) response +='0'; response +=String(MAC_array[5],HEX);
  1252. response+="</tr>";
  1253. response +="<tr><td class=\"cell\">Free heap</td><td class=\"cell\">"; response+=ESP.getFreeHeap(); response+="</tr>";
  1254. // XXX We Shoudl find an ESP32 alternative
  1255. #if !defined ESP32_ARCH
  1256. response +="<tr><td class=\"cell\">ESP speed</td><td class=\"cell\">"; response+=ESP.getCpuFreqMHz();
  1257. response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"SPEED=80\"><button>80</button></a></td>";
  1258. response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"SPEED=160\"><button>160</button></a></td>";
  1259. response+="</tr>";
  1260. response +="<tr><td class=\"cell\">ESP Chip ID</td><td class=\"cell\">"; response+=ESP.getChipId(); response+="</tr>";
  1261. #endif
  1262. response +="<tr><td class=\"cell\">OLED</td><td class=\"cell\">"; response+=OLED; response+="</tr>";
  1263. #if _STATISTICS >= 1
  1264. response +="<tr><td class=\"cell\">WiFi Setups</td><td class=\"cell\">"; response+=gwayConfig.wifis; response+="</tr>";
  1265. response +="<tr><td class=\"cell\">WWW Views</td><td class=\"cell\">"; response+=gwayConfig.views; response+="</tr>";
  1266. #endif
  1267. response +="</table>";
  1268. server.sendContent(response);
  1269. } // gwayConfig.expert
  1270. } // systemStatus
  1271. // --------------------------------------------------------------------------------
  1272. // H2 System State and Interrupt
  1273. // Display interrupt data, but only for debug >= 2
  1274. //
  1275. // --------------------------------------------------------------------------------
  1276. static void interruptData()
  1277. {
  1278. if (gwayConfig.expert) {
  1279. uint8_t flags = readRegister(REG_IRQ_FLAGS);
  1280. uint8_t mask = readRegister(REG_IRQ_FLAGS_MASK);
  1281. String response="";
  1282. response +="<h2>System State and Interrupt</h2>";
  1283. response +="<table class=\"config_table\">";
  1284. response +="<tr>";
  1285. response +="<th class=\"thead\">Parameter</th>";
  1286. response +="<th class=\"thead\">Value</th>";
  1287. response +="<th colspan=\"2\" class=\"thead\">Set</th>";
  1288. response +="</tr>";
  1289. response +="<tr><td class=\"cell\">_state</td>";
  1290. response +="<td class=\"cell\">";
  1291. switch (_state) { // See loraModem.h
  1292. case S_INIT: response +="INIT"; break;
  1293. case S_SCAN: response +="SCAN"; break;
  1294. case S_CAD: response +="CAD"; break;
  1295. case S_RX: response +="RX"; break;
  1296. case S_TX: response +="TX"; break;
  1297. default: response +="unknown"; break;
  1298. }
  1299. response +="</td></tr>";
  1300. response +="<tr><td class=\"cell\">_STRICT_1CH</td>";
  1301. response +="<td class=\"cell\">" ;
  1302. response += String() + _STRICT_1CH;
  1303. response +="</td></tr>";
  1304. response +="<tr><td class=\"cell\">flags (8 bits)</td>";
  1305. response +="<td class=\"cell\">0x";
  1306. if (flags <16) response += "0";
  1307. response +=String(flags,HEX); response+="</td></tr>";
  1308. response +="<tr><td class=\"cell\">mask (8 bits)</td>";
  1309. response +="<td class=\"cell\">0x";
  1310. if (mask <16) response += "0";
  1311. response +=String(mask,HEX); response+="</td></tr>";
  1312. response +="<tr><td class=\"cell\">Re-entrant cntr</td>";
  1313. response +="<td class=\"cell\">";
  1314. response += String() + gwayConfig.reents;
  1315. response +="</td></tr>";
  1316. response +="<tr><td class=\"cell\">ntp call cntr</td>";
  1317. response +="<td class=\"cell\">";
  1318. response += String() + gwayConfig.ntps;
  1319. response+="</td></tr>";
  1320. response +="<tr><td class=\"cell\">ntpErr cntr</td>";
  1321. response +="<td class=\"cell\">";
  1322. response += String() + gwayConfig.ntpErr;
  1323. response +="</td>";
  1324. response +="<td colspan=\"2\" style=\"border: 1px solid black;\">";
  1325. stringTime(gwayConfig.ntpErrTime, response);
  1326. response +="</td>";
  1327. response +="</tr>";
  1328. response +="</table>";
  1329. server.sendContent(response);
  1330. }// if gwayConfig.expert
  1331. } // interruptData
  1332. // --------------------------------------------------------------------------------
  1333. // websiteFooter
  1334. //
  1335. // Thi function displays the last messages without header on the webpage and then
  1336. // closes the webpage.
  1337. // --------------------------------------------------------------------------------
  1338. static void websiteFooter()
  1339. {
  1340. // Close the client connection to server
  1341. server.sendContent(String() + "<br><br /><p style='font-size:10px'>Click <a href=\"/HELP\">here</a> to explain Help and REST options</p><br>");
  1342. server.sendContent(String() + "</BODY></HTML>");
  1343. server.sendContent(""); yield();
  1344. }
  1345. #endif // A_SERVER==1