_wwwServer.ino 53 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549
  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+=(IPAddress)ipa[0]; response+=sep;
  43. response+=(IPAddress)ipa[1]; response+=sep;
  44. response+=(IPAddress)ipa[2]; response+=sep;
  45. response+=(IPAddress)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. response +="<tr><td class=\"cell\">Debug pattern</td>";
  447. bg = ( (pdebug & P_SCAN) ? "LightGreen" : "orange" );
  448. response +="<td class=\"cell\" style=\"border: 1px solid black; width:20px; background-color: ";
  449. response += bg; response += "\">";
  450. response +="<a href=\"PDEBUG=SCAN\">";
  451. response +="<button>SCN</button></a></td>";
  452. bg = ( (pdebug & P_CAD) ? "LightGreen" : "orange" );
  453. response +="<td class=\"cell\" style=\"border: 1px solid black; width:20px; background-color: ";
  454. response += bg; response += "\">";
  455. response +="<a href=\"PDEBUG=CAD\">";
  456. response +="<button>CAD</button></a></td>";
  457. bg = ( (pdebug & P_RX) ? "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=RX\">";
  461. response +="<button>RX</button></a></td>";
  462. bg = ( (pdebug & P_TX) ? "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=TX\">";
  466. response +="<button>TX</button></a></td>";
  467. response += "</tr>";
  468. // Use a second Line
  469. response +="<tr><td class=\"cell\"></td>";
  470. bg = ( (pdebug & P_PRE) ? "LightGreen" : "orange" );
  471. response +="<td class=\"cell\" style=\"border: 1px solid black; width:20px; background-color: ";
  472. response += bg; response += "\">";
  473. response +="<a href=\"PDEBUG=PRE\">";
  474. response +="<button>PRE</button></a></td>";
  475. bg = ( (pdebug & P_MAIN) ? "LightGreen" : "orange" );
  476. response +="<td class=\"cell\" style=\"border: 1px solid black; width:20px; background-color: ";
  477. response += bg; response += "\">";
  478. response +="<a href=\"PDEBUG=MAIN\">";
  479. response +="<button>MAI</button></a></td>";
  480. bg = ( (pdebug & P_GUI) ? "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=GUI\">";
  484. response +="<button>GUI</button></a></td>";
  485. bg = ( (pdebug & P_RADIO) ? "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=RADIO\">";
  489. response +="<button>RDIO</button></a></td>";
  490. response +="</tr>";
  491. #endif
  492. // Serial Debugging
  493. response +="<tr><td class=\"cell\">Usb Debug</td><td class=\"cell\" colspan=\"2\">";
  494. response += _DUSB;
  495. response +="</td>";
  496. //response +="<td class=\"cell\"> </td>";
  497. //response +="<td class=\"cell\"> </td>";
  498. response +="</tr>";
  499. #if GATEWAYNODE==1
  500. response +="<tr><td class=\"cell\">Framecounter Internal Sensor</td>";
  501. response +="<td class=\"cell\" colspan=\"2\">";
  502. response +=frameCount;
  503. response +="</td><td colspan=\"2\" style=\"border: 1px solid black;\">";
  504. response +="<button><a href=\"/FCNT\">RESET</a></button></td>";
  505. response +="</tr>";
  506. bg = " background-color: ";
  507. bg += ( (gwayConfig.isNode == 1) ? "LightGreen" : "orange" );
  508. response +="<tr><td class=\"cell\">Gateway Node</td>";
  509. response +="<td class=\"cell\" style=\"border: 1px solid black;" + bg + "\">";
  510. response += ( (gwayConfig.isNode == true) ? "ON" : "OFF" );
  511. response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"NODE=1\"><button>ON</button></a></td>";
  512. response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"NODE=0\"><button>OFF</button></a></td>";
  513. response +="</tr>";
  514. #endif
  515. #if A_REFRESH==1
  516. bg = " background-color: ";
  517. bg += ( (gwayConfig.refresh == 1) ? "LightGreen" : "orange" );
  518. response +="<tr><td class=\"cell\">WWW Refresh</td>";
  519. response +="<td class=\"cell\" colspan=\"2\" style=\"border: 1px solid black; " + bg + "\">";
  520. response += ( (gwayConfig.refresh == 1) ? "ON" : "OFF" );
  521. response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"REFR=1\"><button>ON</button></a></td>";
  522. response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"REFR=0\"><button>OFF</button></a></td>";
  523. response +="</tr>";
  524. #endif
  525. // Reset Accesspoint
  526. #if _WIFIMANAGER==1
  527. response +="<tr><td><tr><td>";
  528. response +="Click <a href=\"/NEWSSID\">here</a> to reset accesspoint<br>";
  529. response +="</td><td></td></tr>";
  530. #endif
  531. // Update Firmware all statistics
  532. response +="<tr><td class=\"cell\">Update Firmware</td><td colspan=\"2\"></td>";
  533. response +="<td class=\"cell\" colspan=\"2\" class=\"cell\"><a href=\"/UPDATE=1\"><button>UPDATE</button></a></td></tr>";
  534. // Format the Filesystem
  535. response +="<tr><td class=\"cell\">Format SPIFFS</td>";
  536. response +=String() + "<td class=\"cell\" colspan=\"2\" >"+""+"</td>";
  537. response +="<td colspan=\"2\" class=\"cell\"><input type=\"button\" value=\"FORMAT\" onclick=\"ynDialog(\'Do you really want to format?\',\'FORMAT\')\" /></td></tr>";
  538. // Reset all statistics
  539. #if _STATISTICS >= 1
  540. response +="<tr><td class=\"cell\">Statistics</td>";
  541. response +=String() + "<td class=\"cell\" colspan=\"2\" >"+statc.resets+"</td>";
  542. response +="<td colspan=\"2\" class=\"cell\"><input type=\"button\" value=\"RESET\" onclick=\"ynDialog(\'Do you really want to reset statistics?\',\'RESET\')\" /></td></tr>";
  543. // Reset
  544. response +="<tr><td class=\"cell\">Boots and Resets</td>";
  545. response +=String() + "<td class=\"cell\" colspan=\"2\" >"+gwayConfig.boots+"</td>";
  546. response +="<td colspan=\"2\" class=\"cell\"><input type=\"button\" value=\"RESET\" onclick=\"ynDialog(\'Do you want to reset boots?\',\'BOOT\')\" /></td></tr>";
  547. #endif
  548. response +="</table>";
  549. server.sendContent(response);
  550. }
  551. // --------------------------------------------------------------------------------
  552. // H2 Package Statistics
  553. //
  554. // This section display a matrix on the screen where everay channel and spreading
  555. // factor is displayed.
  556. // --------------------------------------------------------------------------------
  557. static void statisticsData()
  558. {
  559. String response="";
  560. // Header
  561. response +="<h2>Package Statistics</h2>";
  562. response +="<table class=\"config_table\">";
  563. response +="<tr><th class=\"thead\">Counter</th>";
  564. #if _STATISTICS == 3
  565. response +="<th class=\"thead\">C 0</th>";
  566. response +="<th class=\"thead\">C 1</th>";
  567. response +="<th class=\"thead\">C 2</th>";
  568. #endif
  569. response +="<th class=\"thead\">Pkgs</th>";
  570. response +="<th class=\"thead\">Pkgs/hr</th>";
  571. response +="</tr>";
  572. //
  573. // Table rows
  574. //
  575. response +="<tr><td class=\"cell\">Packages Downlink</td>";
  576. #if _STATISTICS == 3
  577. response +="<td class=\"cell\">" + String(statc.msg_down_0) + "</td>";
  578. response +="<td class=\"cell\">" + String(statc.msg_down_1) + "</td>";
  579. response +="<td class=\"cell\">" + String(statc.msg_down_2) + "</td>";
  580. #endif
  581. response += "<td class=\"cell\">" + String(statc.msg_down) + "</td>";
  582. response +="<td class=\"cell\"></td></tr>";
  583. response +="<tr><td class=\"cell\">Packages Uplink Total</td>";
  584. #if _STATISTICS == 3
  585. response +="<td class=\"cell\">" + String(statc.msg_ttl_0) + "</td>";
  586. response +="<td class=\"cell\">" + String(statc.msg_ttl_1) + "</td>";
  587. response +="<td class=\"cell\">" + String(statc.msg_ttl_2) + "</td>";
  588. #endif
  589. response +="<td class=\"cell\">" + String(statc.msg_ttl) + "</td>";
  590. response +="<td class=\"cell\">" + String((statc.msg_ttl*3600)/(now() - startTime)) + "</td></tr>";
  591. response +="<tr><td class=\"cell\">Packages Uplink OK </td>";
  592. #if _STATISTICS == 3
  593. response +="<td class=\"cell\">" + String(statc.msg_ok_0) + "</td>";
  594. response +="<td class=\"cell\">" + String(statc.msg_ok_1) + "</td>";
  595. response +="<td class=\"cell\">" + String(statc.msg_ok_2) + "</td>";
  596. #endif
  597. response +="<td class=\"cell\">" + String(statc.msg_ok) + "</td>";
  598. response +="<td class=\"cell\"></td></tr>";
  599. // Provide a table with all the SF data including percentage of messsages
  600. #if _STATISTICS == 2
  601. response +="<tr><td class=\"cell\">SF7 rcvd</td>";
  602. response +="<td class=\"cell\">"; response +=statc.sf7;
  603. response +="<td class=\"cell\">"; response += String(statc.msg_ttl>0 ? 100*statc.sf7/statc.msg_ttl : 0)+" %";
  604. response +="</td></tr>";
  605. response +="<tr><td class=\"cell\">SF8 rcvd</td>";
  606. response +="<td class=\"cell\">"; response +=statc.sf8;
  607. response +="<td class=\"cell\">"; response += String(statc.msg_ttl>0 ? 100*statc.sf8/statc.msg_ttl : 0)+" %";
  608. response +="</td></tr>";
  609. response +="<tr><td class=\"cell\">SF9 rcvd</td>";
  610. response +="<td class=\"cell\">"; response +=statc.sf9;
  611. response +="<td class=\"cell\">"; response += String(statc.msg_ttl>0 ? 100*statc.sf9/statc.msg_ttl : 0)+" %";
  612. response +="</td></tr>";
  613. response +="<tr><td class=\"cell\">SF10 rcvd</td>";
  614. response +="<td class=\"cell\">"; response +=statc.sf10;
  615. response +="<td class=\"cell\">"; response += String(statc.msg_ttl>0 ? 100*statc.sf10/statc.msg_ttl : 0)+" %";
  616. response +="</td></tr>";
  617. response +="<tr><td class=\"cell\">SF11 rcvd</td>";
  618. response +="<td class=\"cell\">"; response +=statc.sf11;
  619. response +="<td class=\"cell\">"; response += String(statc.msg_ttl>0 ? 100*statc.sf11/statc.msg_ttl : 0)+" %";
  620. response +="</td></tr>";
  621. response +="<tr><td class=\"cell\">SF12 rcvd</td>";
  622. response +="<td class=\"cell\">"; response +=statc.sf12;
  623. response +="<td class=\"cell\">"; response += String(statc.msg_ttl>0 ? 100*statc.sf12/statc.msg_ttl : 0)+" %";
  624. response +="</td></tr>";
  625. #endif
  626. #if _STATISTICS == 3
  627. response +="<tr><td class=\"cell\">SF7 rcvd</td>";
  628. response +="<td class=\"cell\">"; response +=statc.sf7_0;
  629. response +="<td class=\"cell\">"; response +=statc.sf7_1;
  630. response +="<td class=\"cell\">"; response +=statc.sf7_2;
  631. response +="<td class=\"cell\">"; response +=statc.sf7;
  632. response +="<td class=\"cell\">"; response += String(statc.msg_ttl>0 ? 100*statc.sf7/statc.msg_ttl : 0)+" %";
  633. response +="</td></tr>";
  634. response +="<tr><td class=\"cell\">SF8 rcvd</td>";
  635. response +="<td class=\"cell\">"; response +=statc.sf8_0;
  636. response +="<td class=\"cell\">"; response +=statc.sf8_1;
  637. response +="<td class=\"cell\">"; response +=statc.sf8_2;
  638. response +="<td class=\"cell\">"; response +=statc.sf8;
  639. response +="<td class=\"cell\">"; response += String(statc.msg_ttl>0 ? 100*statc.sf8/statc.msg_ttl : 0)+" %";
  640. response +="</td></tr>";
  641. response +="<tr><td class=\"cell\">SF9 rcvd</td>";
  642. response +="<td class=\"cell\">"; response +=statc.sf9_0;
  643. response +="<td class=\"cell\">"; response +=statc.sf9_1;
  644. response +="<td class=\"cell\">"; response +=statc.sf9_2;
  645. response +="<td class=\"cell\">"; response +=statc.sf9;
  646. response +="<td class=\"cell\">"; response += String(statc.msg_ttl>0 ? 100*statc.sf9/statc.msg_ttl : 0)+" %";
  647. response +="</td></tr>";
  648. response +="<tr><td class=\"cell\">SF10 rcvd</td>";
  649. response +="<td class=\"cell\">"; response +=statc.sf10_0;
  650. response +="<td class=\"cell\">"; response +=statc.sf10_1;
  651. response +="<td class=\"cell\">"; response +=statc.sf10_2;
  652. response +="<td class=\"cell\">"; response +=statc.sf10;
  653. response +="<td class=\"cell\">"; response += String(statc.msg_ttl>0 ? 100*statc.sf10/statc.msg_ttl : 0)+" %";
  654. response +="</td></tr>";
  655. response +="<tr><td class=\"cell\">SF11 rcvd</td>";
  656. response +="<td class=\"cell\">"; response +=statc.sf11_0;
  657. response +="<td class=\"cell\">"; response +=statc.sf11_1;
  658. response +="<td class=\"cell\">"; response +=statc.sf11_2;
  659. response +="<td class=\"cell\">"; response +=statc.sf11;
  660. response +="<td class=\"cell\">"; response += String(statc.msg_ttl>0 ? 100*statc.sf11/statc.msg_ttl : 0)+" %";
  661. response +="</td></tr>";
  662. response +="<tr><td class=\"cell\">SF12 rcvd</td>";
  663. response +="<td class=\"cell\">"; response +=statc.sf12_0;
  664. response +="<td class=\"cell\">"; response +=statc.sf12_1;
  665. response +="<td class=\"cell\">"; response +=statc.sf12_1;
  666. response +="<td class=\"cell\">"; response +=statc.sf12;
  667. response +="<td class=\"cell\">"; response += String(statc.msg_ttl>0 ? 100*statc.sf12/statc.msg_ttl : 0)+" %";
  668. response +="</td></tr>";
  669. #endif
  670. response +="</table>";
  671. server.sendContent(response);
  672. }
  673. // --------------------------------------------------------------------------------
  674. // Message History
  675. // If enabled, display the sensor messageHistory on the current webserver Page.
  676. // In this GUI section a number of statr[x] records are displayed such as:
  677. //
  678. // Time, The time the sensor message was received
  679. // Node, the DevAddr or even Node name for Trusted nodes,
  680. // Data (Localserver), when _LOCALSERVER is enabled contains decoded data
  681. // C, Channel frequency on which the sensor was received
  682. // Freq, The frequency of the channel
  683. // SF, Spreading Factor
  684. // pRSSI, Packet RSSI
  685. //
  686. // Parameters:
  687. // - <none>
  688. // Returns:
  689. // - <none>
  690. // --------------------------------------------------------------------------------
  691. static void messageHistory()
  692. {
  693. #if _STATISTICS >= 1
  694. String response="";
  695. response += "<h2>Message History</h2>";
  696. response += "<table class=\"config_table\">";
  697. response += "<tr>";
  698. response += "<th class=\"thead\">Time</th>";
  699. response += "<th class=\"thead\">Node</th>";
  700. #if _LOCALSERVER==1
  701. response += "<th class=\"thead\">Data</th>";
  702. #endif
  703. response += "<th class=\"thead\" style=\"width: 20px;\">C</th>";
  704. response += "<th class=\"thead\">Freq</th>";
  705. response += "<th class=\"thead\" style=\"width: 40px;\">SF</th>";
  706. response += "<th class=\"thead\" style=\"width: 50px;\">pRSSI</th>";
  707. #if RSSI==1
  708. if (debug > 1) {
  709. response += "<th class=\"thead\" style=\"width: 50px;\">RSSI</th>";
  710. }
  711. #endif
  712. response += "</tr>";
  713. server.sendContent(response);
  714. for (int i=0; i<MAX_STAT; i++) {
  715. if (statr[i].sf == 0) break;
  716. response = "";
  717. response += String() + "<tr><td class=\"cell\">"; // Tmst
  718. stringTime((statr[i].tmst), response); // XXX Change tmst not to be millis() dependent
  719. response += "</td>";
  720. response += String() + "<td class=\"cell\">"; // Node
  721. if (SerialName((char *)(& (statr[i].node)), response) < 0) { // works with TRUSTED_NODES >= 1
  722. printHEX((char *)(& (statr[i].node)),' ',response); // else
  723. }
  724. response += "</td>";
  725. #if _LOCALSERVER==1
  726. response += String() + "<td class=\"cell\">"; // Data
  727. for (int j=0; j<statr[i].datal; j++) {
  728. if (statr[i].data[j] <0x10) response+= "0";
  729. response += String(statr[i].data[j],HEX) + " ";
  730. }
  731. response += "</td>";
  732. #endif
  733. response += String() + "<td class=\"cell\">" + statr[i].ch + "</td>";
  734. response += String() + "<td class=\"cell\">" + freqs[statr[i].ch].upFreq + "</td>";
  735. response += String() + "<td class=\"cell\">" + statr[i].sf + "</td>";
  736. response += String() + "<td class=\"cell\">" + statr[i].prssi + "</td>";
  737. #if RSSI==1
  738. if (debug >= 2) {
  739. response += String() + "<td class=\"cell\">" + statr[i].rssi + "</td>";
  740. }
  741. #endif
  742. response += "</tr>";
  743. server.sendContent(response);
  744. }
  745. server.sendContent("</table>");
  746. #endif
  747. }
  748. // --------------------------------------------------------------------------------
  749. // H2 NODE SEEN HISTORY
  750. // If enabled, display the sensor last Seen history.
  751. // This setting ,pves togetjer with the "expert" mode.
  752. // If that mode is enabled than the node seen intory is displayed
  753. //
  754. // Parameters:
  755. // - <none>
  756. // Returns:
  757. // - <none>
  758. // --------------------------------------------------------------------------------
  759. static void nodeHistory()
  760. {
  761. #if _SEENMAX > 0
  762. if (gwayConfig.expert) {
  763. // First draw the headers
  764. String response="";
  765. response += "<h2>Node Last Seen History</h2>";
  766. response += "<table class=\"config_table\">";
  767. response += "<tr>";
  768. response += "<th class=\"thead\" style=\"width: 220px;\">Time</th>";
  769. response += "<th class=\"thead\">Node</th>";
  770. //#if _LOCALSERVER==1
  771. // response += "<th class=\"thead\">Data</th>";
  772. //#endif
  773. response += "<th class=\"thead\" style=\"width: 20px;\">C</th>";
  774. response += "<th class=\"thead\" style=\"width: 40px;\">SF</th>";
  775. response += "</tr>";
  776. server.sendContent(response);
  777. // Now start the contents
  778. int i;
  779. for (i=0; i<_SEENMAX; i++) {
  780. if (listSeen[i].idSeen == 0) break;
  781. response = "";
  782. response += String() + "<tr><td class=\"cell\">"; // Tmst
  783. stringTime((listSeen[i].timSeen), response);
  784. response += "</td>";
  785. response += String() + "<td class=\"cell\">"; // Node
  786. if (SerialName((char *)(& (listSeen[i].idSeen)), response) < 0) { // works with TRUSTED_NODES >= 1
  787. printHEX((char *)(& (listSeen[i].idSeen)),' ',response); // else
  788. }
  789. response += "</td>";
  790. response += String() + "<td class=\"cell\">" + 0 + "</td>"; // Channel
  791. response += String() + "<td class=\"cell\">" + listSeen[i].sfSeen + "</td>";
  792. server.sendContent(response);
  793. }
  794. server.sendContent("</table>");
  795. }
  796. #endif
  797. }
  798. // --------------------------------------------------------------------------------
  799. // SEND WEB PAGE()
  800. // Call the webserver and send the standard content and the content that is
  801. // passed by the parameter. Each time a variable is changed, this function is
  802. // called to display the webpage again/
  803. //
  804. // NOTE: This is the only place where yield() or delay() calls are used.
  805. //
  806. // --------------------------------------------------------------------------------
  807. void sendWebPage(const char *cmd, const char *arg)
  808. {
  809. openWebPage(); yield(); // Do the initial website setup
  810. wwwButtons(); // Display buttons such as Documentation, Mode, Logfiles
  811. setVariables(cmd,arg); yield(); // Read Webserver commands from line
  812. statisticsData(); yield(); // Node statistics
  813. messageHistory(); yield(); // Display the sensor history, message statistics
  814. nodeHistory(); yield(); // Display the lastSeen array
  815. gatewaySettings(); yield(); // Display web configuration
  816. wifiConfig(); yield(); // WiFi specific parameters
  817. systemStatus(); yield(); // System statistics such as heap etc.
  818. interruptData(); yield(); // Display interrupts only when debug >= 2
  819. websiteFooter(); yield();
  820. server.client().stop();
  821. }
  822. // --------------------------------------------------------------------------------
  823. // setupWWW is the main function for webserver functions/
  824. // SetupWWW function called by main setup() program to setup webserver
  825. // It does actually not much more than installing all the callback handlers
  826. // for messages sent to the webserver
  827. //
  828. // Implemented is an interface like:
  829. // http://<server>/<Variable>=<value>
  830. //
  831. // --------------------------------------------------------------------------------
  832. void setupWWW()
  833. {
  834. server.begin(); // Start the webserver
  835. // -----------------
  836. // BUTTONS, define what should happen with the buttons we press on the homepage
  837. server.on("/", []() {
  838. sendWebPage("",""); // Send the webPage string
  839. server.sendHeader("Location", String("/"), true);
  840. server.send ( 302, "text/plain", "");
  841. });
  842. server.on("/HELP", []() {
  843. sendWebPage("HELP",""); // Send the webPage string
  844. server.sendHeader("Location", String("/"), true);
  845. server.send ( 302, "text/plain", "");
  846. });
  847. // Format the filesystem
  848. server.on("/FORMAT", []() {
  849. Serial.print(F("FORMAT ..."));
  850. SPIFFS.format(); // Normally disabled. Enable only when SPIFFS corrupt
  851. initConfig(&gwayConfig);
  852. writeConfig( CONFIGFILE, &gwayConfig);
  853. #if _DUSB>=1
  854. Serial.println(F("DONE"));
  855. #endif
  856. server.sendHeader("Location", String("/"), true);
  857. server.send ( 302, "text/plain", "");
  858. });
  859. // Reset the statistics
  860. server.on("/RESET", []() {
  861. Serial.println(F("RESET"));
  862. startTime= now() - 1; // Reset all timers too
  863. statc.msg_ttl = 0; // Reset package statistics
  864. statc.msg_ok = 0;
  865. statc.msg_down = 0;
  866. #if _STATISTICS >= 3
  867. statc.msg_ttl_0 = 0;
  868. statc.msg_ttl_1 = 0;
  869. statc.msg_ttl_2 = 0;
  870. statc.msg_ok_0 = 0;
  871. statc.msg_ok_1 = 0;
  872. statc.msg_ok_2 = 0;
  873. statc.msg_down_0 = 0;
  874. statc.msg_down_1 = 0;
  875. statc.msg_down_2 = 0;
  876. #endif
  877. #if _STATISTICS >= 1
  878. for (int i=0; i<MAX_STAT; i++) { statr[i].sf = 0; }
  879. #if _STATISTICS >= 2
  880. statc.sf7 = 0;
  881. statc.sf8 = 0;
  882. statc.sf9 = 0;
  883. statc.sf10= 0;
  884. statc.sf11= 0;
  885. statc.sf12= 0;
  886. statc.resets= 0;
  887. writeGwayCfg(CONFIGFILE);
  888. #if _STATISTICS >= 3
  889. statc.sf7_0 = 0; statc.sf7_1 = 0; statc.sf7_2 = 0;
  890. statc.sf8_0 = 0; statc.sf8_1 = 0; statc.sf8_2 = 0;
  891. statc.sf9_0 = 0; statc.sf9_1 = 0; statc.sf9_2 = 0;
  892. statc.sf10_0= 0; statc.sf10_1= 0; statc.sf10_2= 0;
  893. statc.sf11_0= 0; statc.sf11_1= 0; statc.sf11_2= 0;
  894. statc.sf12_0= 0; statc.sf12_1= 0; statc.sf12_2= 0;
  895. #endif
  896. #endif
  897. #endif
  898. server.sendHeader("Location", String("/"), true);
  899. server.send ( 302, "text/plain", "");
  900. });
  901. // Reset the boot counter
  902. server.on("/BOOT", []() {
  903. Serial.println(F("BOOT"));
  904. #if _STATISTICS >= 2
  905. gwayConfig.boots = 0;
  906. gwayConfig.wifis = 0;
  907. gwayConfig.views = 0;
  908. gwayConfig.ntpErr = 0; // NTP errors
  909. gwayConfig.ntpErrTime = 0; // NTP last error time
  910. gwayConfig.ntps = 0; // Number of NTP calls
  911. #endif
  912. gwayConfig.reents = 0; // Re-entrance
  913. writeGwayCfg(CONFIGFILE);
  914. server.sendHeader("Location", String("/"), true);
  915. server.send ( 302, "text/plain", "");
  916. });
  917. server.on("/NEWSSID", []() {
  918. sendWebPage("NEWSSID",""); // Send the webPage string
  919. server.sendHeader("Location", String("/"), true);
  920. server.send ( 302, "text/plain", "");
  921. });
  922. // Set debug parameter
  923. server.on("/DEBUG=-1", []() { // Set debug level 0-2
  924. debug = (debug+3)%4;
  925. writeGwayCfg(CONFIGFILE); // Save configuration to file
  926. server.sendHeader("Location", String("/"), true);
  927. server.send ( 302, "text/plain", "");
  928. });
  929. server.on("/DEBUG=1", []() {
  930. debug = (debug+1)%4;
  931. writeGwayCfg(CONFIGFILE); // Save configuration to file
  932. server.sendHeader("Location", String("/"), true);
  933. server.send ( 302, "text/plain", "");
  934. });
  935. // Set PDEBUG parameter
  936. //
  937. server.on("/PDEBUG=SCAN", []() { // Set debug level 0-2
  938. pdebug ^= P_SCAN;
  939. writeGwayCfg(CONFIGFILE); // Save configuration to file
  940. server.sendHeader("Location", String("/"), true);
  941. server.send ( 302, "text/plain", "");
  942. });
  943. server.on("/PDEBUG=CAD", []() { // Set debug level 0-2
  944. pdebug ^= P_CAD;
  945. writeGwayCfg(CONFIGFILE); // Save configuration to file
  946. server.sendHeader("Location", String("/"), true);
  947. server.send ( 302, "text/plain", "");
  948. });
  949. server.on("/PDEBUG=RX", []() { // Set debug level 0-2
  950. pdebug ^= P_RX;
  951. writeGwayCfg(CONFIGFILE); // Save configuration to file
  952. server.sendHeader("Location", String("/"), true);
  953. server.send ( 302, "text/plain", "");
  954. });
  955. server.on("/PDEBUG=TX", []() { // Set debug level 0-2
  956. pdebug ^= P_TX;
  957. writeGwayCfg(CONFIGFILE); // Save configuration to file
  958. server.sendHeader("Location", String("/"), true);
  959. server.send ( 302, "text/plain", "");
  960. });
  961. server.on("/PDEBUG=PRE", []() { // Set debug level 0-2
  962. pdebug ^= P_PRE;
  963. writeGwayCfg(CONFIGFILE); // Save configuration to file
  964. server.sendHeader("Location", String("/"), true);
  965. server.send ( 302, "text/plain", "");
  966. });
  967. server.on("/PDEBUG=MAIN", []() { // Set debug level 0-2
  968. pdebug ^= P_MAIN;
  969. writeGwayCfg(CONFIGFILE); // Save configuration to file
  970. server.sendHeader("Location", String("/"), true);
  971. server.send ( 302, "text/plain", "");
  972. });
  973. server.on("/PDEBUG=GUI", []() { // Set debug level 0-2
  974. pdebug ^= P_GUI;
  975. writeGwayCfg(CONFIGFILE); // Save configuration to file
  976. server.sendHeader("Location", String("/"), true);
  977. server.send ( 302, "text/plain", "");
  978. });
  979. server.on("/PDEBUG=RADIO", []() { // Set debug level 0-2
  980. pdebug ^= P_RADIO;
  981. writeGwayCfg(CONFIGFILE); // Save configuration to file
  982. server.sendHeader("Location", String("/"), true);
  983. server.send ( 302, "text/plain", "");
  984. });
  985. // Set delay in microseconds
  986. server.on("/DELAY=1", []() {
  987. txDelay+=5000;
  988. server.sendHeader("Location", String("/"), true);
  989. server.send ( 302, "text/plain", "");
  990. });
  991. server.on("/DELAY=-1", []() {
  992. txDelay-=5000;
  993. server.sendHeader("Location", String("/"), true);
  994. server.send ( 302, "text/plain", "");
  995. });
  996. // Spreading Factor setting
  997. server.on("/SF=1", []() {
  998. if (sf>=SF12) sf=SF7; else sf= (sf_t)((int)sf+1);
  999. server.sendHeader("Location", String("/"), true);
  1000. server.send ( 302, "text/plain", "");
  1001. });
  1002. server.on("/SF=-1", []() {
  1003. if (sf<=SF7) sf=SF12; else sf= (sf_t)((int)sf-1);
  1004. server.sendHeader("Location", String("/"), true);
  1005. server.send ( 302, "text/plain", "");
  1006. });
  1007. // Set Frequency of the GateWay node
  1008. server.on("/FREQ=1", []() {
  1009. uint8_t nf = sizeof(freqs)/sizeof(freqs[0]); // Number of elements in array
  1010. #if _DUSB==2
  1011. Serial.print("FREQ==1:: For freq[0] sizeof vector=");
  1012. Serial.print(sizeof(freqs[0]));
  1013. Serial.println();
  1014. #endif
  1015. if (ifreq==(nf-1)) ifreq=0; else ifreq++;
  1016. server.sendHeader("Location", String("/"), true);
  1017. server.send ( 302, "text/plain", "");
  1018. });
  1019. server.on("/FREQ=-1", []() {
  1020. uint8_t nf = sizeof(freqs)/sizeof(freqs[0]); // Number of elements in array
  1021. if (ifreq==0) ifreq=(nf-1); else ifreq--;
  1022. server.sendHeader("Location", String("/"), true);
  1023. server.send ( 302, "text/plain", "");
  1024. });
  1025. // Set CAD function off/on
  1026. server.on("/CAD=1", []() {
  1027. _cad=(bool)1;
  1028. writeGwayCfg(CONFIGFILE); // Save configuration to file
  1029. server.sendHeader("Location", String("/"), true);
  1030. server.send ( 302, "text/plain", "");
  1031. });
  1032. server.on("/CAD=0", []() {
  1033. _cad=(bool)0;
  1034. writeGwayCfg(CONFIGFILE); // Save configuration to file
  1035. server.sendHeader("Location", String("/"), true);
  1036. server.send ( 302, "text/plain", "");
  1037. });
  1038. // GatewayNode
  1039. server.on("/NODE=1", []() {
  1040. #if GATEWAYNODE==1
  1041. gwayConfig.isNode =(bool)1;
  1042. writeGwayCfg(CONFIGFILE); // Save configuration to file
  1043. #endif
  1044. server.sendHeader("Location", String("/"), true);
  1045. server.send ( 302, "text/plain", "");
  1046. });
  1047. server.on("/NODE=0", []() {
  1048. #if GATEWAYNODE==1
  1049. gwayConfig.isNode =(bool)0;
  1050. writeGwayCfg(CONFIGFILE); // Save configuration to file
  1051. #endif
  1052. server.sendHeader("Location", String("/"), true);
  1053. server.send ( 302, "text/plain", "");
  1054. });
  1055. #if GATEWAYNODE==1
  1056. // Framecounter of the Gateway node
  1057. server.on("/FCNT", []() {
  1058. frameCount=0;
  1059. rxLoraModem(); // Reset the radio with the new frequency
  1060. writeGwayCfg(CONFIGFILE);
  1061. //sendWebPage("",""); // Send the webPage string
  1062. server.sendHeader("Location", String("/"), true);
  1063. server.send ( 302, "text/plain", "");
  1064. });
  1065. #endif
  1066. // WWW Page refresh function
  1067. server.on("/REFR=1", []() { // WWW page auto refresh ON
  1068. #if A_REFRESH==1
  1069. gwayConfig.refresh =1;
  1070. writeGwayCfg(CONFIGFILE); // Save configuration to file
  1071. #endif
  1072. server.sendHeader("Location", String("/"), true);
  1073. server.send ( 302, "text/plain", "");
  1074. });
  1075. server.on("/REFR=0", []() { // WWW page auto refresh OFF
  1076. #if A_REFRESH==1
  1077. gwayConfig.refresh =0;
  1078. writeGwayCfg(CONFIGFILE); // Save configuration to file
  1079. #endif
  1080. server.sendHeader("Location", String("/"), true);
  1081. server.send ( 302, "text/plain", "");
  1082. });
  1083. // Switch off/on the HOP functions
  1084. server.on("/HOP=1", []() {
  1085. _hop=true;
  1086. server.sendHeader("Location", String("/"), true);
  1087. server.send ( 302, "text/plain", "");
  1088. });
  1089. server.on("/HOP=0", []() {
  1090. _hop=false;
  1091. ifreq=0;
  1092. setFreq(freqs[ifreq].upFreq);
  1093. rxLoraModem();
  1094. server.sendHeader("Location", String("/"), true);
  1095. server.send ( 302, "text/plain", "");
  1096. });
  1097. #if !defined ESP32_ARCH
  1098. // Change speed to 160 MHz
  1099. server.on("/SPEED=80", []() {
  1100. system_update_cpu_freq(80);
  1101. server.sendHeader("Location", String("/"), true);
  1102. server.send ( 302, "text/plain", "");
  1103. });
  1104. server.on("/SPEED=160", []() {
  1105. system_update_cpu_freq(160);
  1106. server.sendHeader("Location", String("/"), true);
  1107. server.send ( 302, "text/plain", "");
  1108. });
  1109. #endif
  1110. // Display Documentation pages
  1111. server.on("/DOCU", []() {
  1112. server.sendHeader("Location", String("/"), true);
  1113. buttonDocu();
  1114. server.send ( 302, "text/plain", "");
  1115. });
  1116. // Display LOGging information
  1117. server.on("/LOG", []() {
  1118. server.sendHeader("Location", String("/"), true);
  1119. #if _DUSB>=2
  1120. Serial.println(F("LOG button"));
  1121. #endif
  1122. buttonLog();
  1123. server.send ( 302, "text/plain", "");
  1124. });
  1125. // Display Expert mode or Simple mode
  1126. server.on("/EXPERT", []() {
  1127. server.sendHeader("Location", String("/"), true);
  1128. gwayConfig.expert = bool(1 - (int) gwayConfig.expert) ;
  1129. server.send ( 302, "text/plain", "");
  1130. });
  1131. // Display the SEEN statistics
  1132. server.on("/SEEN", []() {
  1133. server.sendHeader("Location", String("/"), true);
  1134. #if _DUSB>=2
  1135. Serial.println(F("SEEN button"));
  1136. #endif
  1137. buttonSeen();
  1138. server.send ( 302, "text/plain", "");
  1139. });
  1140. // Update the sketch. Not yet implemented
  1141. server.on("/UPDATE=1", []() {
  1142. #if A_OTA==1
  1143. updateOtaa();
  1144. #endif
  1145. server.sendHeader("Location", String("/"), true);
  1146. server.send ( 302, "text/plain", "");
  1147. });
  1148. // -----------
  1149. // This section from version 4.0.7 defines what PART of the
  1150. // webpage is shown based on the buttons pressed by the user
  1151. // Maybe not all information should be put on the screen since it
  1152. // may take too much time to serve all information before a next
  1153. // package interrupt arrives at the gateway
  1154. Serial.print(F("WWW Server started on port "));
  1155. Serial.println(A_SERVERPORT);
  1156. return;
  1157. } // setupWWW
  1158. // --------------------------------------------------------------------------------
  1159. // WIFI CONFIG
  1160. // wifiConfig() displays the most important Wifi parameters gathered
  1161. //
  1162. // --------------------------------------------------------------------------------
  1163. static void wifiConfig()
  1164. {
  1165. if (gwayConfig.expert) {
  1166. String response="";
  1167. response +="<h2>WiFi Config</h2>";
  1168. response +="<table class=\"config_table\">";
  1169. response +="<tr><th class=\"thead\">Parameter</th><th class=\"thead\">Value</th></tr>";
  1170. response +="<tr><td class=\"cell\">WiFi host</td><td class=\"cell\">";
  1171. #if ESP32_ARCH==1
  1172. response +=WiFi.getHostname(); response+="</tr>";
  1173. #else
  1174. response +=wifi_station_get_hostname(); response+="</tr>";
  1175. #endif
  1176. response +="<tr><td class=\"cell\">WiFi SSID</td><td class=\"cell\">";
  1177. response +=WiFi.SSID(); response+="</tr>";
  1178. response +="<tr><td class=\"cell\">IP Address</td><td class=\"cell\">";
  1179. printIP((IPAddress)WiFi.localIP(),'.',response);
  1180. response +="</tr>";
  1181. response +="<tr><td class=\"cell\">IP Gateway</td><td class=\"cell\">";
  1182. printIP((IPAddress)WiFi.gatewayIP(),'.',response);
  1183. response +="</tr>";
  1184. response +="<tr><td class=\"cell\">NTP Server</td><td class=\"cell\">"; response+=NTP_TIMESERVER; response+="</tr>";
  1185. response +="<tr><td class=\"cell\">LoRa Router</td><td class=\"cell\">"; response+=_TTNSERVER; response+="</tr>";
  1186. response +="<tr><td class=\"cell\">LoRa Router IP</td><td class=\"cell\">";
  1187. printIP((IPAddress)ttnServer,'.',response);
  1188. response +="</tr>";
  1189. #ifdef _THINGSERVER
  1190. response +="<tr><td class=\"cell\">LoRa Router 2</td><td class=\"cell\">"; response+=_THINGSERVER;
  1191. response += String() + ":" + _THINGPORT + "</tr>";
  1192. response +="<tr><td class=\"cell\">LoRa Router 2 IP</td><td class=\"cell\">";
  1193. printIP((IPAddress)thingServer,'.',response);
  1194. response +="</tr>";
  1195. #endif
  1196. response +="</table>";
  1197. server.sendContent(response);
  1198. } // gwayConfig.expert
  1199. } // wifiConfig
  1200. // --------------------------------------------------------------------------------
  1201. // H2 systemStatus
  1202. // systemStatus is additional and only available in the expert mode.
  1203. // It provides a number of system specific data such as heap size etc.
  1204. // --------------------------------------------------------------------------------
  1205. static void systemStatus()
  1206. {
  1207. if (gwayConfig.expert) {
  1208. String response="";
  1209. response +="<h2>System Status</h2>";
  1210. response +="<table class=\"config_table\">";
  1211. response +="<tr>";
  1212. response +="<th class=\"thead\">Parameter</th>";
  1213. response +="<th class=\"thead\">Value</th>";
  1214. response +="<th colspan=\"2\" class=\"thead\">Set</th>";
  1215. response +="</tr>";
  1216. response +="<tr><td style=\"border: 1px solid black; width:120px;\">Gateway ID</td>";
  1217. response +="<td class=\"cell\">";
  1218. if (MAC_array[0]< 0x10) response +='0'; response +=String(MAC_array[0],HEX); // The MAC array is always returned in lowercase
  1219. if (MAC_array[1]< 0x10) response +='0'; response +=String(MAC_array[1],HEX);
  1220. if (MAC_array[2]< 0x10) response +='0'; response +=String(MAC_array[2],HEX);
  1221. response +="FFFF";
  1222. if (MAC_array[3]< 0x10) response +='0'; response +=String(MAC_array[3],HEX);
  1223. if (MAC_array[4]< 0x10) response +='0'; response +=String(MAC_array[4],HEX);
  1224. if (MAC_array[5]< 0x10) response +='0'; response +=String(MAC_array[5],HEX);
  1225. response+="</tr>";
  1226. response +="<tr><td class=\"cell\">Free heap</td><td class=\"cell\">"; response+=ESP.getFreeHeap(); response+="</tr>";
  1227. // XXX We Shoudl find an ESP32 alternative
  1228. #if !defined ESP32_ARCH
  1229. response +="<tr><td class=\"cell\">ESP speed</td><td class=\"cell\">"; response+=ESP.getCpuFreqMHz();
  1230. response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"SPEED=80\"><button>80</button></a></td>";
  1231. response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"SPEED=160\"><button>160</button></a></td>";
  1232. response+="</tr>";
  1233. response +="<tr><td class=\"cell\">ESP Chip ID</td><td class=\"cell\">"; response+=ESP.getChipId(); response+="</tr>";
  1234. #endif
  1235. response +="<tr><td class=\"cell\">OLED</td><td class=\"cell\">"; response+=OLED; response+="</tr>";
  1236. #if _STATISTICS >= 1
  1237. response +="<tr><td class=\"cell\">WiFi Setups</td><td class=\"cell\">"; response+=gwayConfig.wifis; response+="</tr>";
  1238. response +="<tr><td class=\"cell\">WWW Views</td><td class=\"cell\">"; response+=gwayConfig.views; response+="</tr>";
  1239. #endif
  1240. response +="</table>";
  1241. server.sendContent(response);
  1242. } // gwayConfig.expert
  1243. } // systemStatus
  1244. // --------------------------------------------------------------------------------
  1245. // H2 System State and Interrupt
  1246. // Display interrupt data, but only for debug >= 2
  1247. //
  1248. // --------------------------------------------------------------------------------
  1249. static void interruptData()
  1250. {
  1251. if (gwayConfig.expert) {
  1252. uint8_t flags = readRegister(REG_IRQ_FLAGS);
  1253. uint8_t mask = readRegister(REG_IRQ_FLAGS_MASK);
  1254. String response="";
  1255. response +="<h2>System State and Interrupt</h2>";
  1256. response +="<table class=\"config_table\">";
  1257. response +="<tr>";
  1258. response +="<th class=\"thead\">Parameter</th>";
  1259. response +="<th class=\"thead\">Value</th>";
  1260. response +="<th colspan=\"2\" class=\"thead\">Set</th>";
  1261. response +="</tr>";
  1262. response +="<tr><td class=\"cell\">_state</td>";
  1263. response +="<td class=\"cell\">";
  1264. switch (_state) { // See loraModem.h
  1265. case S_INIT: response +="INIT"; break;
  1266. case S_SCAN: response +="SCAN"; break;
  1267. case S_CAD: response +="CAD"; break;
  1268. case S_RX: response +="RX"; break;
  1269. case S_TX: response +="TX"; break;
  1270. default: response +="unknown"; break;
  1271. }
  1272. response +="</td></tr>";
  1273. response +="<tr><td class=\"cell\">_STRICT_1CH</td>";
  1274. response +="<td class=\"cell\">" ;
  1275. response += String() + _STRICT_1CH;
  1276. response +="</td></tr>";
  1277. response +="<tr><td class=\"cell\">flags (8 bits)</td>";
  1278. response +="<td class=\"cell\">0x";
  1279. if (flags <16) response += "0";
  1280. response +=String(flags,HEX); response+="</td></tr>";
  1281. response +="<tr><td class=\"cell\">mask (8 bits)</td>";
  1282. response +="<td class=\"cell\">0x";
  1283. if (mask <16) response += "0";
  1284. response +=String(mask,HEX); response+="</td></tr>";
  1285. response +="<tr><td class=\"cell\">Re-entrant cntr</td>";
  1286. response +="<td class=\"cell\">";
  1287. response += String() + gwayConfig.reents;
  1288. response +="</td></tr>";
  1289. response +="<tr><td class=\"cell\">ntp call cntr</td>";
  1290. response +="<td class=\"cell\">";
  1291. response += String() + gwayConfig.ntps;
  1292. response+="</td></tr>";
  1293. response +="<tr><td class=\"cell\">ntpErr cntr</td>";
  1294. response +="<td class=\"cell\">";
  1295. response += String() + gwayConfig.ntpErr;
  1296. response +="</td>";
  1297. response +="<td colspan=\"2\" style=\"border: 1px solid black;\">";
  1298. stringTime(gwayConfig.ntpErrTime, response);
  1299. response +="</td>";
  1300. response +="</tr>";
  1301. response +="<tr><td class=\"cell\">Time Correction (uSec)</td><td class=\"cell\">";
  1302. response += txDelay;
  1303. response +="</td>";
  1304. response +="<td class=\"cell\"><a href=\"DELAY=-1\"><button>-</button></a></td>";
  1305. response +="<td class=\"cell\"><a href=\"DELAY=1\"><button>+</button></a></td>";
  1306. response +="</tr>";
  1307. response +="</table>";
  1308. server.sendContent(response);
  1309. }// if gwayConfig.expert
  1310. } // interruptData
  1311. // --------------------------------------------------------------------------------
  1312. // websiteFooter
  1313. //
  1314. // Thi function displays the last messages without header on the webpage and then
  1315. // closes the webpage.
  1316. // --------------------------------------------------------------------------------
  1317. static void websiteFooter()
  1318. {
  1319. // Close the client connection to server
  1320. server.sendContent(String() + "<br><br /><p style='font-size:10px'>Click <a href=\"/HELP\">here</a> to explain Help and REST options</p><br>");
  1321. server.sendContent(String() + "</BODY></HTML>");
  1322. server.sendContent(""); yield();
  1323. }
  1324. #endif // A_SERVER==1