_wwwServer.ino 54 KB

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