Fixed a ton of bugs and made almost everything work

pull/30/head
Ondřej Hruška 8 years ago
parent 314239842e
commit 28a170faa3
  1. 3
      CMakeLists.txt
  2. 2
      Makefile
  3. 5
      build_web.sh
  4. 3
      esphttpdconfig.mk
  5. 2
      html_orig/base.php
  6. 18
      html_orig/build_html.php
  7. 20
      html_orig/css/app.css
  8. 1284
      html_orig/css/app.css.bak
  9. 21
      html_orig/dump_js_lang.php
  10. 194
      html_orig/js/app.js
  11. 3
      html_orig/jssrc/appcommon.js
  12. 7
      html_orig/jssrc/lang.js
  13. 2
      html_orig/jssrc/term.js
  14. 226
      html_orig/jssrc/wifi.js
  15. 2
      html_orig/lang/en.php
  16. 2
      html_orig/packjs.sh
  17. 3
      html_orig/pages/_cfg_menu.php
  18. 16
      html_orig/pages/about.php
  19. 76
      html_orig/pages/cfg_network.php
  20. 190
      html_orig/pages/cfg_wifi.php
  21. 4
      html_orig/pages/cfg_wifi_conn.php
  22. 2
      html_orig/pages/term.php
  23. 11
      html_orig/sass/layout/_box.scss
  24. 1
      html_orig/sass/pages/_about.scss
  25. 9
      html_orig/sass/pages/_term.scss
  26. 1
      html_orig/sass/pages/_wifi.scss
  27. 3
      include/helpers.h
  28. 13
      user/cgi_appcfg.c
  29. 2
      user/cgi_appcfg.h
  30. 32
      user/cgi_main.c
  31. 4
      user/cgi_network.c
  32. 74
      user/cgi_persist.c
  33. 10
      user/cgi_persist.h
  34. 10
      user/cgi_wifi.c
  35. 16
      user/persist.c
  36. 22
      user/persist.h
  37. 37
      user/routes.c
  38. 2
      user/screen.c
  39. 5
      user/wifimgr.c

@ -93,6 +93,8 @@ set(SOURCE_FILES
user/io.h user/io.h
user/cgi_wifi.c user/cgi_wifi.c
user/cgi_wifi.h user/cgi_wifi.h
user/cgi_persist.c
user/cgi_persist.h
user/cgi_network.c user/cgi_network.c
user/cgi_network.h user/cgi_network.h
user/cgi_appcfg.c user/cgi_appcfg.c
@ -140,6 +142,7 @@ add_definitions(
-DICACHE_FLASH_ATTR= -DICACHE_FLASH_ATTR=
-DICACHE_RODATA_ATTR= -DICACHE_RODATA_ATTR=
-DFLAG_GZIP=2 -DFLAG_GZIP=2
-DADMIN_PASSWORD="asdf"
-DESPFS_HEATSHRINK) -DESPFS_HEATSHRINK)
add_executable(esp_vt100_firmware ${SOURCE_FILES}) add_executable(esp_vt100_firmware ${SOURCE_FILES})

@ -65,7 +65,7 @@ LIBS += esphttpd
# compiler flags using during compilation of source files # compiler flags using during compilation of source files
CFLAGS = -Os -ggdb -std=gnu99 -Werror -Wpointer-arith -Wundef -Wall -Wl,-EL -fno-inline-functions \ CFLAGS = -Os -ggdb -std=gnu99 -Werror -Wpointer-arith -Wundef -Wall -Wl,-EL -fno-inline-functions \
-nostdlib -mlongcalls -mtext-section-literals -D__ets__ -DICACHE_FLASH \ -nostdlib -mlongcalls -mtext-section-literals -D__ets__ -DICACHE_FLASH \
-Wno-address -Wno-unused -DHTTPD_MAX_BACKLOG_SIZE=8192 -Wno-address -Wno-unused -DHTTPD_MAX_BACKLOG_SIZE=8192 -DADMIN_PASSWORD=$(ADMIN_PASSWORD)
# linker flags used to generate the main object file # linker flags used to generate the main object file
LDFLAGS = -nostdlib -Wl,--no-check-sections -u call_user_start -Wl,-static LDFLAGS = -nostdlib -Wl,--no-check-sections -u call_user_start -Wl,-static

@ -14,7 +14,8 @@ cd ..
cp html_orig/js/app.js html/js/ cp html_orig/js/app.js html/js/
sass --sourcemap=none html_orig/sass/app.scss html/css/app.css sass html_orig/sass/app.scss html/css/app.css
rm html/css/app.css.map
cp html_orig/img/* html/img/ cp html_orig/img/* html/img/
cp html_orig/favicon.ico html/favicon.ico

@ -37,3 +37,6 @@ OUTPUT_TYPE = combined
# SPI flash size, in K # SPI flash size, in K
ESP_SPI_FLASH_SIZE_K = 1024 ESP_SPI_FLASH_SIZE_K = 1024
# Admin password, used to store settings to flash as defaults
ADMIN_PASSWORD = "19738426"

@ -20,7 +20,7 @@ require_once __DIR__ . '/_env.php';
$prod = defined('STDIN'); $prod = defined('STDIN');
define('DEBUG', !$prod); define('DEBUG', !$prod);
$root = DEBUG ? json_encode(ESP_IP) : 'window.location.href'; $root = DEBUG ? json_encode(ESP_IP) : 'location.host';
define('JS_WEB_ROOT', $root); define('JS_WEB_ROOT', $root);
define('LOCALE', isset($_GET['locale']) ? $_GET['locale'] : 'en'); define('LOCALE', isset($_GET['locale']) ? $_GET['locale'] : 'en');

@ -2,6 +2,18 @@
require_once __DIR__ . '/base.php'; require_once __DIR__ . '/base.php';
function process_html($s) {
$pattern = '/<!--(.*)-->/Uis';
$s = preg_replace($pattern, '', $s);
$pattern = '/(?:(?:\/\*(?:[^*]|(?:\*+[^*\/]))*\*+\/)|(?:(?<!\:|\\\|\'|\")\/\/.*))/';
$s = preg_replace($pattern, '', $s);
$pattern = '/\s+/s';
$s = preg_replace($pattern, ' ', $s);
return $s;
}
ob_start(); ob_start();
foreach($_pages as $_k => $p) { foreach($_pages as $_k => $p) {
if ($p->bodyclass == 'api') continue; if ($p->bodyclass == 'api') continue;
@ -11,6 +23,12 @@ foreach($_pages as $_k => $p) {
ob_clean(); // clean up ob_clean(); // clean up
include(__DIR__ . '/index.php'); include(__DIR__ . '/index.php');
$s = ob_get_contents(); // grab the output $s = ob_get_contents(); // grab the output
// remove newlines and comments
// as tests have shown, it saves just a couple kilobytes,
// making it not a very big improvement at the expense of ugly html.
// $s = process_html($s);
ob_clean(); // clean up ob_clean(); // clean up
$of = __DIR__ . '/../html/' . $_k . '.tpl'; $of = __DIR__ . '/../html/' . $_k . '.tpl';
file_put_contents($of, $s); // write to a file file_put_contents($of, $s); // write to a file

@ -576,6 +576,10 @@ ul > * {
background-color: rgba(255, 255, 255, 0.07); background-color: rgba(255, 255, 255, 0.07);
box-shadow: 0 0 4px black; box-shadow: 0 0 4px black;
border: 1px solid #4f4f4f; } border: 1px solid #4f4f4f; }
.Box::after {
content: '';
display: block;
clear: both; }
@media screen and (max-width: 544px) { @media screen and (max-width: 544px) {
.Box { .Box {
margin-top: 0.61805rem; } } margin-top: 0.61805rem; } }
@ -586,6 +590,8 @@ ul > * {
.Box h2 { .Box h2 {
margin-top: 0; margin-top: 0;
margin-bottom: 0 !important; } margin-bottom: 0 !important; }
.Box p:last-child {
margin-bottom: 0.5em; }
.Box.wide { .Box.wide {
width: initial; width: initial;
max-width: initial; } max-width: initial; }
@ -967,7 +973,8 @@ form span.required {
background: rgba(255, 255, 255, 0.1); background: rgba(255, 255, 255, 0.1);
border-radius: 5px; border-radius: 5px;
padding: 0.38198rem; padding: 0.38198rem;
margin-bottom: 0.38198rem; } margin-bottom: 0.38198rem;
margin-top: 0.38198rem; }
#ap-noscan { #ap-noscan {
font-weight: bold; } font-weight: bold; }
@ -1109,10 +1116,8 @@ body.term #buttons {
white-space: nowrap; } white-space: nowrap; }
body.term #buttons button { body.term #buttons button {
margin: 0 3px; margin: 0 3px;
padding: 10px 0; padding: 8px 5px;
width: 18%; min-width: 65px;
max-width: 65px;
min-width: initial;
cursor: pointer; cursor: pointer;
font-weight: bold; } font-weight: bold; }
body.term #botnav { body.term #botnav {
@ -1138,7 +1143,8 @@ body.term #botnav {
float: right; float: right;
height: 130px; } height: 130px; }
.page-about #logo2 { .page-about #logo2 {
max-width: 100%; } max-width: 100%;
margin: 1rem; }
.page-about td { .page-about td {
white-space: normal; } white-space: normal; }
@ -1280,3 +1286,5 @@ body.term #botnav {
@media screen and (max-width: 1000px) { @media screen and (max-width: 1000px) {
.mq-normal-min { .mq-normal-min {
display: none !important; } } display: none !important; } }
/*# sourceMappingURL=app.css.map */

File diff suppressed because one or more lines are too long

@ -0,0 +1,21 @@
<?php
/* This script is run on demand to generate JS version of tr() */
require_once __DIR__ . '/base.php';
$selected = [
'wifi.connected_ip_is',
'wifi.not_conn',
];
$out = [];
foreach ($selected as $key) {
$out[$key] = $_messages[$key];
}
file_put_contents(__DIR__. '/jssrc/lang.js',
"// Generated from PHP locale file\n" .
'var _tr = ' . json_encode($out, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE) . ";\n\n" .
"function tr(key) { return _tr[key] || '?'+key+'?'; }\n"
);

@ -999,13 +999,203 @@ $.ready(function () {
x.removeAttribute('tabindex'); x.removeAttribute('tabindex');
}); });
qs('#brand').removeAttribute('tabindex'); var br = qs('#brand');
br && br.removeAttribute('tabindex');
} }
}); });
$._loader = function(vis) { $._loader = function(vis) {
$('#loader').toggleClass('show', vis); $('#loader').toggleClass('show', vis);
}; };
// Generated from PHP locale file
var _tr = {
"wifi.connected_ip_is": "Connected, IP is ",
"wifi.not_conn": "Not connected."
};
function tr(key) { return _tr[key] || '?'+key+'?'; }
(function(w) {
var authStr = ['Open', 'WEP', 'WPA', 'WPA2', 'WPA/WPA2'];
var curSSID;
// Get XX % for a slider input
function rangePt(inp) {
return Math.round(((inp.value / inp.max)*100)) + '%';
}
// Display selected STA SSID etc
function selectSta(name, password, ip) {
$('#sta_ssid').val(name);
$('#sta_password').val(password);
$('#sta-nw').toggleClass('hidden', name.length == 0);
$('#sta-nw-nil').toggleClass('hidden', name.length > 0);
$('#sta-nw .essid').html(e(name));
var nopw = undef(password) || password.length == 0;
$('#sta-nw .x-passwd').html(e(password));
$('#sta-nw .passwd').toggleClass('hidden', nopw);
$('#sta-nw .nopasswd').toggleClass('hidden', !nopw);
$('#sta-nw .ip').html(ip.length>0 ? tr('wifi.connected_ip_is')+ip : tr('wifi.not_conn'));
}
function submitPskModal(e, open) {
var passwd = $('#conn-passwd').val();
var ssid = $('#conn-ssid').val();
if (open || passwd.length) {
$('#sta_password').val(passwd);
$('#sta_ssid').val(ssid);
selectSta(ssid, passwd, '');
}
if (e) e.preventDefault();
Modal.hide('#psk-modal');
return false;
}
/** Update display for received response */
function onScan(resp, status) {
//var ap_json = {
// "result": {
// "inProgress": "0",
// "APs": [
// {"essid": "Chlivek", "bssid": "88:f7:c7:52:b3:99", "rssi": "204", "enc": "4", "channel": "1"},
// {"essid": "TyNikdy", "bssid": "5c:f4:ab:0d:f1:1b", "rssi": "164", "enc": "3", "channel": "1"},
// ]
// }
//};
if (status != 200) {
// bad response
rescan(5000); // wait 5sm then retry
return;
}
try {
resp = JSON.parse(resp);
} catch (e) {
console.log(e);
rescan(5000);
return;
}
var done = !bool(resp.result.inProgress) && (resp.result.APs.length > 0);
rescan(done ? 15000 : 1000);
if (!done) return; // no redraw yet
// clear the AP list
var $list = $('#ap-list');
// remove old APs
$('#ap-list .AP').remove();
$list.toggleClass('hidden', !done);
$('#ap-loader').toggleClass('hidden', done);
// scan done
resp.result.APs.sort(function (a, b) {
return b.rssi - a.rssi;
}).forEach(function (ap) {
ap.enc = parseInt(ap.enc);
if (ap.enc > 4) return; // hide unsupported auths
WiFi.scan_url = '/cfg/wifi/scan';
var item = mk('div');
var $item = $(item)
.data('ssid', ap.essid)
.data('pwd', ap.enc)
.attr('tabindex', 0)
.addClass('AP');
// mark current SSID
if (ap.essid == curSSID) {
$item.addClass('selected');
}
var inner = mk('div');
$(inner).addClass('inner')
.htmlAppend('<div class="rssi">{0}</div>'.format(ap.rssi_perc))
.htmlAppend('<div class="essid" title="{0}">{0}</div>'.format($.htmlEscape(ap.essid)))
.htmlAppend('<div class="auth">{0}</div>'.format(authStr[ap.enc]));
$item.on('click', function () {
var $th = $(this);
var ssid = $th.data('ssid');
$('#conn-ssid').val(ssid);
$('#conn-passwd').val('');
if (+$th.data('pwd')) {
// this AP needs a password
Modal.show('#psk-modal');
$('#conn-passwd')[0].focus();
} else {
//Modal.show('#reset-modal');
submitPskModal(null, true);
}
});
item.appendChild(inner);
$list[0].appendChild(item);
});
}
function startScanning() {
$('#ap-loader').removeClass('hidden');
$('#ap-scan').addClass('hidden');
$('#ap-loader .anim-dots').html('.');
scanAPs();
}
/** Ask the CGI what APs are visible (async) */
function scanAPs() {
$.get('http://'+_root+w.scan_url, onScan);
}
function rescan(time) {
setTimeout(scanAPs, time);
}
/** Set up the WiFi page */
function wifiInit(cfg) {
// Hide what should be hidden in this mode
cfg.mode = +cfg.mode;
$('#ap-noscan').toggleClass('hidden', cfg.mode != 2);
$('#ap-scan').toggleClass('hidden', cfg.mode == 2);
// Update slider value displays
$('.Row.range').forEach(function(x) {
var inp = x.querySelector('input');
var disp1 = x.querySelector('.x-disp1');
var disp2 = x.querySelector('.x-disp2');
var t = rangePt(inp);
$(disp1).html(t);
$(disp2).html(t);
$(inp).on('input', function() {
t = rangePt(inp);
$(disp1).html(t);
$(disp2).html(t);
});
});
// Forget STA credentials
$('#forget-sta').on('click', function() {
selectSta('', '', '');
return false;
});
selectSta(cfg.sta_ssid, cfg.sta_password, cfg.sta_active_ip);
curSSID = cfg.sta_active_ssid;
}
w.init = wifiInit;
w.startScanning = startScanning;
})(window.WiFi = {});
(function() { (function() {
/** /**
* Terminal module * Terminal module
@ -1307,5 +1497,5 @@ $._loader = function(vis) {
Term.init(obj); Term.init(obj);
Conn.init(); Conn.init();
Input.init(); Input.init();
} };
})(); })();

@ -100,7 +100,8 @@ $.ready(function () {
x.removeAttribute('tabindex'); x.removeAttribute('tabindex');
}); });
qs('#brand').removeAttribute('tabindex'); var br = qs('#brand');
br && br.removeAttribute('tabindex');
} }
}); });

@ -0,0 +1,7 @@
// Generated from PHP locale file
var _tr = {
"wifi.connected_ip_is": "Connected, IP is ",
"wifi.not_conn": "Not connected."
};
function tr(key) { return _tr[key] || '?'+key+'?'; }

@ -299,5 +299,5 @@
Term.init(obj); Term.init(obj);
Conn.init(); Conn.init();
Input.init(); Input.init();
} };
})(); })();

@ -1,19 +1,70 @@
/** Wifi page */ (function(w) {
(function () {
var authStr = ['Open', 'WEP', 'WPA', 'WPA2', 'WPA/WPA2']; var authStr = ['Open', 'WEP', 'WPA', 'WPA2', 'WPA/WPA2'];
var curSSID; var curSSID;
// Get XX % for a slider input
function rangePt(inp) {
return Math.round(((inp.value / inp.max)*100)) + '%';
}
// Display selected STA SSID etc
function selectSta(name, password, ip) {
$('#sta_ssid').val(name);
$('#sta_password').val(password);
$('#sta-nw').toggleClass('hidden', name.length == 0);
$('#sta-nw-nil').toggleClass('hidden', name.length > 0);
$('#sta-nw .essid').html(e(name));
var nopw = undef(password) || password.length == 0;
$('#sta-nw .x-passwd').html(e(password));
$('#sta-nw .passwd').toggleClass('hidden', nopw);
$('#sta-nw .nopasswd').toggleClass('hidden', !nopw);
$('#sta-nw .ip').html(ip.length>0 ? tr('wifi.connected_ip_is')+ip : tr('wifi.not_conn'));
}
function submitPskModal(e, open) {
var passwd = $('#conn-passwd').val();
var ssid = $('#conn-ssid').val();
if (open || passwd.length) {
$('#sta_password').val(passwd);
$('#sta_ssid').val(ssid);
selectSta(ssid, passwd, '');
}
if (e) e.preventDefault();
Modal.hide('#psk-modal');
return false;
}
/** Update display for received response */ /** Update display for received response */
function onScan(resp, status) { function onScan(resp, status) {
//var ap_json = {
// "result": {
// "inProgress": "0",
// "APs": [
// {"essid": "Chlivek", "bssid": "88:f7:c7:52:b3:99", "rssi": "204", "enc": "4", "channel": "1"},
// {"essid": "TyNikdy", "bssid": "5c:f4:ab:0d:f1:1b", "rssi": "164", "enc": "3", "channel": "1"},
// ]
// }
//};
if (status != 200) { if (status != 200) {
// bad response // bad response
rescan(5000); // wait 5sm then retry rescan(5000); // wait 5sm then retry
return; return;
} }
resp = jsp(resp); try {
resp = JSON.parse(resp);
} catch (e) {
console.log(e);
rescan(5000);
return;
}
var done = resp && !bool(resp.result.inProgress) && (resp.result.APs.length > 0); var done = !bool(resp.result.inProgress) && (resp.result.APs.length > 0);
rescan(done ? 15000 : 1000); rescan(done ? 15000 : 1000);
if (!done) return; // no redraw yet if (!done) return; // no redraw yet
@ -22,60 +73,71 @@
// remove old APs // remove old APs
$('#ap-list .AP').remove(); $('#ap-list .AP').remove();
$list.toggle(done); $list.toggleClass('hidden', !done);
$('#ap-loader').toggle(!done); $('#ap-loader').toggleClass('hidden', done);
// scan done // scan done
resp.result.APs.sort(function (a, b) { resp.result.APs.sort(function (a, b) {
return b.rssi - a.rssi; return b.rssi - a.rssi;
}).forEach(function (ap) { }).forEach(function (ap) {
ap.enc = parseInt(ap.enc); ap.enc = parseInt(ap.enc);
if (ap.enc > 4) return; // hide unsupported auths if (ap.enc > 4) return; // hide unsupported auths
WiFi.scan_url = '/cfg/wifi/scan';
var item = document.createElement('div');
var item = mk('div');
var $item = $(item)
.data('ssid', ap.essid) var $item = $(item)
.data('pwd', ap.enc != 0) .data('ssid', ap.essid)
.addClass('AP'); .data('pwd', ap.enc)
.attr('tabindex', 0)
// mark current SSID .addClass('AP');
if (ap.essid == curSSID) {
$item.addClass('selected'); // mark current SSID
if (ap.essid == curSSID) {
$item.addClass('selected');
}
var inner = mk('div');
$(inner).addClass('inner')
.htmlAppend('<div class="rssi">{0}</div>'.format(ap.rssi_perc))
.htmlAppend('<div class="essid" title="{0}">{0}</div>'.format($.htmlEscape(ap.essid)))
.htmlAppend('<div class="auth">{0}</div>'.format(authStr[ap.enc]));
$item.on('click', function () {
var $th = $(this);
var ssid = $th.data('ssid');
$('#conn-ssid').val(ssid);
$('#conn-passwd').val('');
if (+$th.data('pwd')) {
// this AP needs a password
Modal.show('#psk-modal');
$('#conn-passwd')[0].focus();
} else {
//Modal.show('#reset-modal');
submitPskModal(null, true);
} }
});
var inner = document.createElement('div');
$(inner).addClass('inner')
.htmlAppend('<div class="rssi">{0}</div>'.format(ap.rssi_perc))
.htmlAppend('<div class="essid" title="{0}">{0}</div>'.format($.htmlEscape(ap.essid)))
.htmlAppend('<div class="auth">{0}</div>'.format(authStr[ap.enc]));
$item.on('click', function () {
var $th = $(this);
// populate the form
$('#conn-essid').val($th.data('ssid'));
$('#conn-passwd').val(''); // clear
if ($th.data('pwd')) {
// this AP needs a password
Modal.show('#psk-modal');
} else {
Modal.show('#reset-modal');
$('#conn-form').submit();
}
});
item.appendChild(inner);
$list[0].appendChild(item);
});
}
item.appendChild(inner); function startScanning() {
$list[0].appendChild(item); $('#ap-loader').removeClass('hidden');
}); $('#ap-scan').addClass('hidden');
$('#ap-loader .anim-dots').html('.');
scanAPs();
} }
/** Ask the CGI what APs are visible (async) */ /** Ask the CGI what APs are visible (async) */
function scanAPs() { function scanAPs() {
$.get('http://'+_root+'/wifi/scan', onScan); $.get('http://'+_root+w.scan_url, onScan);
} }
function rescan(time) { function rescan(time) {
@ -83,46 +145,38 @@
} }
/** Set up the WiFi page */ /** Set up the WiFi page */
window.wifiInit = function (obj) { function wifiInit(cfg) {
//var ap_json = {
// "result": {
// "inProgress": "0",
// "APs": [
// {"essid": "Chlivek", "bssid": "88:f7:c7:52:b3:99", "rssi": "204", "enc": "4", "channel": "1"},
// {"essid": "TyNikdy", "bssid": "5c:f4:ab:0d:f1:1b", "rssi": "164", "enc": "3", "channel": "1"},
// ]
// }
//};
// Hide what should be hidden in this mode // Hide what should be hidden in this mode
$('.x-hide-'+obj.mode).addClass('hidden'); cfg.mode = +cfg.mode;
obj.mode = +obj.mode;
$('#ap-noscan').toggleClass('hidden', cfg.mode != 2);
// Channel writable only in AP mode $('#ap-scan').toggleClass('hidden', cfg.mode == 2);
if (obj.mode != 2) $('#channel').attr('readonly', 1);
// Update slider value displays
curSSID = obj.staSSID; $('.Row.range').forEach(function(x) {
var inp = x.querySelector('input');
// add SSID to the opmode field var disp1 = x.querySelector('.x-disp1');
if (curSSID) { var disp2 = x.querySelector('.x-disp2');
var box = $('#opmodebox'); var t = rangePt(inp);
box.html(box.html() + ' (' + curSSID + ')'); $(disp1).html(t);
} $(disp2).html(t);
$(inp).on('input', function() {
// hide IP if IP not received t = rangePt(inp);
if (!obj.staIP) $('.x-hide-noip').addClass('hidden'); $(disp1).html(t);
$(disp2).html(t);
// scan if not AP });
if (obj.mode != 2) { });
scanAPs();
}
$('#modeswitch').html([ // Forget STA credentials
'<a class="button" href="/wifi/set?opmode=3">Client+AP</a>&nbsp;<a class="button" href="/wifi/set?opmode=2">AP only</a>', $('#forget-sta').on('click', function() {
'<a class="button" href="/wifi/set?opmode=3">Client+AP</a>', selectSta('', '', '');
'<a class="button" href="/wifi/set?opmode=1">Client only</a>&nbsp;<a class="button" href="/wifi/set?opmode=2">AP only</a>' return false;
][obj.mode-1]); });
};
selectSta(cfg.sta_ssid, cfg.sta_password, cfg.sta_active_ip);
curSSID = cfg.sta_active_ssid;
}
})(); w.init = wifiInit;
w.startScanning = startScanning;
})(window.WiFi = {});

@ -12,6 +12,8 @@ return [
'menu.cfg_admin' => 'Reset & Restore', 'menu.cfg_admin' => 'Reset & Restore',
'menu.cfg_wifi_conn' => 'Connecting to External Network', 'menu.cfg_wifi_conn' => 'Connecting to External Network',
'menu.settings' => 'Settings',
'title.term' => 'Terminal', 'title.term' => 'Terminal',
'net.ap' => 'DHCP Server (AP)', 'net.ap' => 'DHCP Server (AP)',

@ -7,4 +7,6 @@ cat jssrc/chibi.js \
jssrc/modal.js \ jssrc/modal.js \
jssrc/notif.js \ jssrc/notif.js \
jssrc/appcommon.js \ jssrc/appcommon.js \
jssrc/lang.js \
jssrc/wifi.js \
jssrc/term.js > js/app.js jssrc/term.js > js/app.js

@ -1,5 +1,6 @@
<nav id="menu"> <nav id="menu">
<div id="brand" tabindex=0><?= tr('appname') ?></div> <div id="brand" tabindex=0><?= tr('appname') ?></div>
<a href="<?= e(url('term')) ?>" class="icn-back"><?= tr('menu.term') ?></a>
<?php <?php
// generate the menu // generate the menu
foreach ($_pages as $k => $page) { foreach ($_pages as $k => $page) {
@ -10,7 +11,7 @@
$url = e(url($k)); $url = e(url($k));
echo "<a href=\"$url\" class=\"$page->icon $sel\">$text</a>"; echo "<a href=\"$url\" class=\"$page->icon $sel\">$text</a>";
} }
?><a href="<?= e(url('term')) ?>" class="icn-back"><?= tr('menu.term') ?></a> ?>
</nav> </nav>
<script> <script>

@ -36,30 +36,28 @@
<div class="Box"> <div class="Box">
<h2>Issues</h2> <h2>Issues</h2>
<p> <p>
Please report any issues to the <a href="%githubrepo%/issues">bugtracker</a> or send them by e-mail. Please report any issues to the <a href="%githubrepo%/issues">bugtracker</a> or send them by e-mail (see above).
</p> </p>
<p> <p>
Firmware updates can be downloaded from the <a href="%githubrepo%/releases">releases page</a>. Firmware updates can be downloaded from the <a href="%githubrepo%/releases">releases page</a> and flashed
Flash the images using <a href="https://github.com/espressif/esptool">esptool</a>. with <a href="https://github.com/espressif/esptool">esptool.py</a>.
</p> </p>
</div> </div>
<div class="Box"> <div class="Box">
<h2>Contributing</h2> <h2>Contributing</h2>
<p> <p>
Submit your improvements and ideas to the project on <i class="icn-github"></i> You're welcome to submit your improvements and ideas to our <a href="%githubrepo%">GitHub repository</a>!
<a href="%githubrepo%">GitHub</a>.
</p> </p>
<p> <p>
You can donate on <a href="https://paypal.me/mightypork">PayPal</a> or <i class="icn-donate"></i> If you'd like to donate, please try <a href="https://paypal.me/mightypork">PayPal</a> or
<a href="https://liberapay.com/MightyPork/">LiberaPay</a> to help keep <a href="https://liberapay.com/MightyPork/">LiberaPay</a>.
the project going.
</p> </p>
</div> </div>
<div class="Box"> <div class="Box">
<h2>Acknowledgements</h2> <h2>Thanks</h2>
<p> <p>
The webserver is based on a <a href="https://github.com/MightyPork/libesphttpd">fork</a> of the The webserver is based on a <a href="https://github.com/MightyPork/libesphttpd">fork</a> of the
<a href="https://github.com/Spritetm/esphttpd">esphttpd</a> library by Jeroen Domburg (Sprite_tm). <a href="https://github.com/Spritetm/esphttpd">esphttpd</a> library by Jeroen Domburg (Sprite_tm).

@ -2,6 +2,39 @@
$ipmask='pattern="^([0-9]{1,3}\.){3}[0-9]{1,3}$"'; $ipmask='pattern="^([0-9]{1,3}\.){3}[0-9]{1,3}$"';
?> ?>
<form class="Box str mobcol" action="<?= e(url('network_set')) ?>" method="GET" id="form-2">
<h2 tabindex=0><?= tr('net.sta') ?></h2>
<div class="Row explain">
<?= tr('net.explain_sta') ?>
</div>
<div class="Row checkbox x-static-toggle" >
<label><?= tr('net.sta_dhcp_enable') ?></label><!--
--><span class="box" tabindex=0 role=checkbox></span>
<input type="hidden" name="sta_dhcp_enable" value="%sta_dhcp_enable%">
</div>
<div class="Row x-static">
<label for="sta_addr_ip"><?= tr('net.sta_addr_ip') ?></label>
<input type="text" name="sta_addr_ip" id="sta_addr_ip" value="%sta_addr_ip%" <?=$ipmask?> required>
</div>
<div class="Row x-static">
<label for="sta_addr_mask"><?= tr('net.sta_addr_mask') ?></label>
<input type="text" name="sta_addr_mask" id="sta_addr_mask" value="%sta_addr_mask%" <?=$ipmask?> required>
</div>
<div class="Row x-static">
<label for="sta_addr_gw"><?= tr('net.sta_addr_gw') ?></label>
<input type="text" name="sta_addr_gw" id="sta_addr_gw" value="%sta_addr_gw%" <?=$ipmask?> required>
</div>
<div class="Row buttons">
<a class="button icn-ok" href="#" onclick="qs('#form-2').submit()"><?= tr('apply') ?></a>
</div>
</form>
<form class="Box str mobcol" action="<?= e(url('network_set')) ?>" method="GET" id="form-1"> <form class="Box str mobcol" action="<?= e(url('network_set')) ?>" method="GET" id="form-1">
<h2 tabindex=0><?= tr('net.ap') ?></h2> <h2 tabindex=0><?= tr('net.ap') ?></h2>
@ -40,39 +73,6 @@ $ipmask='pattern="^([0-9]{1,3}\.){3}[0-9]{1,3}$"';
</div> </div>
</form> </form>
<form class="Box str mobcol" action="<?= e(url('network_set')) ?>" method="GET" id="form-2">
<h2 tabindex=0><?= tr('net.sta') ?></h2>
<div class="Row explain">
<?= tr('net.explain_sta') ?>
</div>
<div class="Row checkbox">
<label><?= tr('net.sta_dhcp_enable') ?></label><!--
--><span class="box" tabindex=0 role=checkbox></span>
<input type="hidden" name="sta_dhcp_enable" value="%sta_dhcp_enable%">
</div>
<div class="Row">
<label for="sta_addr_ip"><?= tr('net.sta_addr_ip') ?></label>
<input type="text" name="sta_addr_ip" id="sta_addr_ip" value="%sta_addr_ip%" <?=$ipmask?> required>
</div>
<div class="Row">
<label for="sta_addr_mask"><?= tr('net.sta_addr_mask') ?></label>
<input type="text" name="sta_addr_mask" id="sta_addr_mask" value="%sta_addr_mask%" <?=$ipmask?> required>
</div>
<div class="Row">
<label for="sta_addr_gw"><?= tr('net.sta_addr_gw') ?></label>
<input type="text" name="sta_addr_gw" id="sta_addr_gw" value="%sta_addr_gw%" <?=$ipmask?> required>
</div>
<div class="Row buttons">
<a class="button icn-ok" href="#" onclick="qs('#form-2').submit()"><?= tr('apply') ?></a>
</div>
</form>
<div class="Box mobcol"> <div class="Box mobcol">
<h2><?= tr('net.details') ?></h2> <h2><?= tr('net.details') ?></h2>
@ -83,3 +83,13 @@ $ipmask='pattern="^([0-9]{1,3}\.){3}[0-9]{1,3}$"';
<label><?= tr('net.ap_mac') ?></label><input type="text" readonly value="%ap_mac%"> <label><?= tr('net.ap_mac') ?></label><input type="text" readonly value="%ap_mac%">
</div> </div>
</div> </div>
<script>
function updateStaticDisp() {
$('.x-static').toggleClass('hidden', $('#sta_dhcp_enable').val());
}
$('.x-static-toggle').on('click', function() {
updateStaticDisp();
});
updateStaticDisp();
</script>

@ -74,7 +74,7 @@
<div id="ap-box"> <div id="ap-box">
<label><?= tr('wifi.select_ssid') ?></label> <label><?= tr('wifi.select_ssid') ?></label>
<div id="ap-scan"><a href="#" onclick="startScanning(); return false"><?= tr('wifi.scan_now') ?></a></div> <div id="ap-scan"><a href="#" onclick="WiFi.startScanning(); return false"><?= tr('wifi.scan_now') ?></a></div>
<div id="ap-loader" class="hidden"><?= tr('wifi.scanning') ?><span class="anim-dots">.</span></div> <div id="ap-loader" class="hidden"><?= tr('wifi.scanning') ?><span class="anim-dots">.</span></div>
<div id="ap-noscan" class="hidden"><?= tr('wifi.cant_scan_no_sta') ?></div> <div id="ap-noscan" class="hidden"><?= tr('wifi.cant_scan_no_sta') ?></div>
<div id="ap-list" class="hidden"></div> <div id="ap-list" class="hidden"></div>
@ -97,186 +97,12 @@
</div> </div>
<script> <script>
// Get XX % for a slider input WiFi.scan_url = '<?= url('wifi_scan', true) ?>';
function rangePt(inp) { WiFi.init({
return Math.round(((inp.value / inp.max)*100)) + '%'; mode: '%opmode%',
} sta_ssid: '%sta_ssid%',
sta_password: '%sta_password%',
// Update slider value displays sta_active_ip: '%sta_active_ip%',
$('.Row.range').forEach(function(x) { sta_active_ssid: '%sta_active_ssid%',
var inp = x.querySelector('input');
var disp1 = x.querySelector('.x-disp1');
var disp2 = x.querySelector('.x-disp2');
var t = rangePt(inp);
$(disp1).html(t);
$(disp2).html(t);
$(inp).on('input', function() {
t = rangePt(inp);
$(disp1).html(t);
$(disp2).html(t);
});
});
// Forget STA credentials
$('#forget-sta').on('click', function() {
selectSta('', '', '');
return false;
}); });
// Display selected STA SSID etc
function selectSta(name, password, ip) {
$('#sta_ssid').val(name);
$('#sta_password').val(password);
$('#sta-nw').toggleClass('hidden', name.length == 0);
$('#sta-nw-nil').toggleClass('hidden', name.length > 0);
$('#sta-nw .essid').html(e(name));
var nopw = undef(password) || password.length == 0;
$('#sta-nw .x-passwd').html(e(password));
$('#sta-nw .passwd').toggleClass('hidden', nopw);
$('#sta-nw .nopasswd').toggleClass('hidden', !nopw);
$('#sta-nw .ip').html(ip.length>0 ? '<?=tr('wifi.connected_ip_is')?>'+ip : '<?=tr('wifi.not_conn')?>');
}
selectSta('%sta_ssid%', '%sta_password%', '%sta_active_ip%');
var authStr = ['Open', 'WEP', 'WPA', 'WPA2', 'WPA/WPA2'];
var curSSID = '%sta_active_ssid%';
function submitPskModal(e, open) {
var passwd = $('#conn-passwd').val();
var ssid = $('#conn-ssid').val();
if (open || passwd.length) {
$('#sta_password').val(passwd);
$('#sta_ssid').val(ssid);
selectSta(ssid, passwd, '');
}
if (e) e.preventDefault();
Modal.hide('#psk-modal');
return false;
}
/** Update display for received response */
function onScan(resp, status) {
if (status != 200) {
// bad response
rescan(5000); // wait 5sm then retry
return;
}
try {
resp = JSON.parse(resp);
} catch (e) {
console.log(e);
rescan(5000);
return;
}
var done = !bool(resp.result.inProgress) && (resp.result.APs.length > 0);
rescan(done ? 15000 : 1000);
if (!done) return; // no redraw yet
// clear the AP list
var $list = $('#ap-list');
// remove old APs
$('#ap-list .AP').remove();
$list.toggleClass('hidden', !done);
$('#ap-loader').toggleClass('hidden', done);
// scan done
resp.result.APs.sort(function (a, b) {
return b.rssi - a.rssi;
}).forEach(function (ap) {
ap.enc = parseInt(ap.enc);
if (ap.enc > 4) return; // hide unsupported auths
var item = mk('div');
var $item = $(item)
.data('ssid', ap.essid)
.data('pwd', ap.enc)
.attr('tabindex', 0)
.addClass('AP');
// mark current SSID
if (ap.essid == curSSID) {
$item.addClass('selected');
}
var inner = mk('div');
$(inner).addClass('inner')
.htmlAppend('<div class="rssi">{0}</div>'.format(ap.rssi_perc))
.htmlAppend('<div class="essid" title="{0}">{0}</div>'.format($.htmlEscape(ap.essid)))
.htmlAppend('<div class="auth">{0}</div>'.format(authStr[ap.enc]));
$item.on('click', function () {
var $th = $(this);
var ssid = $th.data('ssid');
$('#conn-ssid').val(ssid);
$('#conn-passwd').val('');
if (+$th.data('pwd')) {
// this AP needs a password
Modal.show('#psk-modal');
$('#conn-passwd')[0].focus();
} else {
//Modal.show('#reset-modal');
submitPskModal(null, true);
}
});
item.appendChild(inner);
$list[0].appendChild(item);
});
}
function startScanning() {
$('#ap-loader').removeClass('hidden');
$('#ap-scan').addClass('hidden');
$('#ap-loader .anim-dots').html('.');
scanAPs();
}
/** Ask the CGI what APs are visible (async) */
function scanAPs() {
$.get('http://'+_root+'<?= url('wifi_scan', true) ?>', onScan);
}
function rescan(time) {
setTimeout(scanAPs, time);
}
/** Set up the WiFi page */
function wifiInit(obj) {
//var ap_json = {
// "result": {
// "inProgress": "0",
// "APs": [
// {"essid": "Chlivek", "bssid": "88:f7:c7:52:b3:99", "rssi": "204", "enc": "4", "channel": "1"},
// {"essid": "TyNikdy", "bssid": "5c:f4:ab:0d:f1:1b", "rssi": "164", "enc": "3", "channel": "1"},
// ]
// }
//};
// Hide what should be hidden in this mode
obj.mode = +obj.mode;
$('#ap-noscan').toggleClass('hidden', obj.mode != 2);
$('#ap-scan').toggleClass('hidden', obj.mode == 2);
// // scan if not AP
// if (obj.mode != 2) {
// scanAPs();
// }
}
wifiInit({mode: '%opmode%'});
</script> </script>

@ -31,7 +31,7 @@
$("#status").html(msg); $("#status").html(msg);
if (done) { if (done) {
// $('#backbtn').removeClass('hidden'); // $('#backbtn').removeClass('hidden');
$('.anim-dots').addClass('hidden'); $('.anim-dots').addClass('hidden');
} else { } else {
window.setTimeout(getStatus, 1000); window.setTimeout(getStatus, 1000);
@ -42,7 +42,7 @@
abortTmeo = setTimeout(function () { abortTmeo = setTimeout(function () {
xhr.abort(); xhr.abort();
$("#status").html(<?= json_encode(tr('wifi.conn.telemetry_lost')) ?>); $("#status").html(<?= json_encode(tr('wifi.conn.telemetry_lost')) ?>);
// $('#backbtn').removeClass('hidden'); // $('#backbtn').removeClass('hidden');
$('.anim-dots').addClass('hidden'); $('.anim-dots').addClass('hidden');
}, 4000); }, 4000);

@ -22,7 +22,7 @@
</div> </div>
<nav id="botnav"> <nav id="botnav">
<a href="<?= url('cfg_wifi') ?>"><?= tr('menu.cfg_wifi') ?></a><!-- <a href="<?= url('cfg_wifi') ?>"><?= tr('menu.settings') ?></a><!--
--><a href="<?= url('help') ?>">Help</a><!-- --><a href="<?= url('help') ?>">Help</a><!--
--><a href="<?= url('about') ?>">About</a> --><a href="<?= url('about') ?>">About</a>
</nav> </nav>

@ -5,6 +5,13 @@
margin-top: dist(0); margin-top: dist(0);
padding: dist(-1) dist(0); padding: dist(-1) dist(0);
// clear floats
&::after {
content: '';
display: block;
clear: both
}
@include media($phone) { @include media($phone) {
margin-top: dist(-1); margin-top: dist(-1);
} }
@ -22,6 +29,10 @@
margin-bottom: 0 !important; margin-bottom: 0 !important;
} }
p:last-child {
margin-bottom: 0.5em;
}
border-radius: 3px; border-radius: 3px;
background-color: rgba(white, .07); background-color: rgba(white, .07);
box-shadow: 0 0 4px black; box-shadow: 0 0 4px black;

@ -14,6 +14,7 @@
// mobile friendly // mobile friendly
#logo2 { #logo2 {
max-width: 100%; max-width: 100%;
margin: 1rem;
} }
td { td {

@ -34,10 +34,11 @@ body.term {
button { button {
margin: 0 3px; margin: 0 3px;
padding: 10px 0; padding: 8px 5px;
width: 18%; //width: 18%;
max-width: 65px; min-width: 65px;
min-width: initial; //max-width: 65px;
//min-width: initial;
cursor: pointer; cursor: pointer;
font-weight: bold; font-weight: bold;
} }

@ -18,6 +18,7 @@
border-radius: 5px; border-radius: 5px;
padding: dist(-2); padding: dist(-2);
margin-bottom: dist(-2); margin-bottom: dist(-2);
margin-top: dist(-2);
} }
#ap-noscan { #ap-noscan {

@ -19,4 +19,7 @@
*/ */
#define GET_ARG(key) (httpdFindArg(connData->getArgs, key, buff, sizeof(buff)) > 0) #define GET_ARG(key) (httpdFindArg(connData->getArgs, key, buff, sizeof(buff)) > 0)
#define STR_HELPER(x) #x
#define STR(x) STR_HELPER(x)
#endif //ESP_VT100_FIRMWARE_HELPERS_H #endif //ESP_VT100_FIRMWARE_HELPERS_H

@ -8,13 +8,14 @@ Cgi/template routines for configuring non-wifi settings
#include "screen.h" #include "screen.h"
#include "helpers.h" #include "helpers.h"
#define SET_REDIR_SUC "/cfg/term" #define SET_REDIR_SUC "/cfg/app"
#define SET_REDIR_ERR SET_REDIR_SUC"?err=" #define SET_REDIR_ERR SET_REDIR_SUC"?err="
/** /**
* Universal CGI endpoint to set Terminal params. * Universal CGI endpoint to set Terminal params.
*/ */
httpd_cgi_state ICACHE_FLASH_ATTR cgiAppCfgSet(HttpdConnData *connData) httpd_cgi_state ICACHE_FLASH_ATTR
cgiAppCfgSetParams(HttpdConnData *connData)
{ {
char buff[50]; char buff[50];
@ -49,7 +50,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiAppCfgSet(HttpdConnData *connData)
redir_url += sprintf(redir_url, "term_width,"); redir_url += sprintf(redir_url, "term_width,");
} }
} else { } else {
warn("Missing height arg", buff); warn("Missing height arg!");
// this wont happen normally when the form is used // this wont happen normally when the form is used
redir_url += sprintf(redir_url, "term_width,term_height,"); redir_url += sprintf(redir_url, "term_width,term_height,");
} }
@ -115,6 +116,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiAppCfgSet(HttpdConnData *connData)
// All was OK // All was OK
info("Set app params - success, saving..."); info("Set app params - success, saving...");
terminal_apply_settings();
persist_store(); persist_store();
httpdRedirect(connData, SET_REDIR_SUC); httpdRedirect(connData, SET_REDIR_SUC);
@ -127,7 +129,8 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiAppCfgSet(HttpdConnData *connData)
} }
httpd_cgi_state ICACHE_FLASH_ATTR tplAppCfg(HttpdConnData *connData, char *token, void **arg) httpd_cgi_state ICACHE_FLASH_ATTR
tplAppCfg(HttpdConnData *connData, char *token, void **arg)
{ {
#define BUFLEN 100 #define BUFLEN 100
char buff[BUFLEN]; char buff[BUFLEN];
@ -149,7 +152,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplAppCfg(HttpdConnData *connData, char *token
sprintf(buff, "%d", termconf->default_bg); sprintf(buff, "%d", termconf->default_bg);
} }
else if (streq(token, "default_fg")) { else if (streq(token, "default_fg")) {
sprintf(buff, "%d", termconf->default_bg); sprintf(buff, "%d", termconf->default_fg);
} }
else if (streq(token, "term_title")) { else if (streq(token, "term_title")) {
strncpy_safe(buff, termconf->title, BUFLEN); strncpy_safe(buff, termconf->title, BUFLEN);

@ -3,7 +3,7 @@
#include "httpd.h" #include "httpd.h"
httpd_cgi_state cgiAppCfgSet(HttpdConnData *connData); httpd_cgi_state cgiAppCfgSetParams(HttpdConnData *connData);
httpd_cgi_state tplAppCfg(HttpdConnData *connData, char *token, void **arg); httpd_cgi_state tplAppCfg(HttpdConnData *connData, char *token, void **arg);
#endif #endif

@ -5,9 +5,7 @@
#include "cgi_main.h" #include "cgi_main.h"
#include "screen.h" #include "screen.h"
#include "user_main.h" #include "user_main.h"
#include "helpers.h"
#define STR_HELPER(x) #x
#define STR(x) STR_HELPER(x)
/** /**
* Main page template substitution * Main page template substitution
@ -28,7 +26,33 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplScreen(HttpdConnData *connData, char *token
const int bufsiz = 512; const int bufsiz = 512;
char buff[bufsiz]; char buff[bufsiz];
if (streq(token, "screenData")) { if (streq(token, "term_title")) {
httpdSend(connData, termconf->title, -1);
}
else if (streq(token, "btn1")) {
httpdSend(connData, termconf->btn1, -1);
}
else if (streq(token, "btn2")) {
httpdSend(connData, termconf->btn2, -1);
}
else if (streq(token, "btn3")) {
httpdSend(connData, termconf->btn3, -1);
}
else if (streq(token, "btn4")) {
httpdSend(connData, termconf->btn4, -1);
}
else if (streq(token, "btn5")) {
httpdSend(connData, termconf->btn5, -1);
}
else if (streq(token, "default_bg")) {
sprintf(buff, "%d", termconf->default_bg);
httpdSend(connData, buff, -1);
}
else if (streq(token, "default_fg")) {
sprintf(buff, "%d", termconf->default_fg);
httpdSend(connData, buff, -1);
}
else if (streq(token, "screenData")) {
httpd_cgi_state cont = screenSerializeToBuffer(buff, bufsiz, arg); httpd_cgi_state cont = screenSerializeToBuffer(buff, bufsiz, arg);
httpdSend(connData, buff, -1); httpdSend(connData, buff, -1);
return cont; return cont;

@ -231,10 +231,10 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplNetwork(HttpdConnData *connData, char *toke
else if (streq(token, "sta_addr_ip")) { else if (streq(token, "sta_addr_ip")) {
sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->sta_addr.ip.addr)); sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->sta_addr.ip.addr));
} }
else if (streq(token, "ap_addr_mask")) { else if (streq(token, "sta_addr_mask")) {
sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->sta_addr.netmask.addr)); sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->sta_addr.netmask.addr));
} }
else if (streq(token, "ap_addr_gw")) { else if (streq(token, "sta_addr_gw")) {
sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->sta_addr.gw.addr)); sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->sta_addr.gw.addr));
} }
else if (streq(token, "sta_mac")) { else if (streq(token, "sta_mac")) {

@ -0,0 +1,74 @@
/*
Cgi/template routines for configuring non-wifi settings
*/
#include <esp8266.h>
#include "cgi_persist.h"
#include "persist.h"
#include "helpers.h"
#define SET_REDIR_SUC "/cfg/admin"
static bool ICACHE_FLASH_ATTR
verify_admin_pw(const char *pw)
{
// This is not really for security, but to prevent someone who
// shouldn't touch those settings from fucking it up.
return streq(pw, STR(ADMIN_PASSWORD)); // the PW comes from the makefile
}
httpd_cgi_state ICACHE_FLASH_ATTR
cgiPersistWriteDefaults(HttpdConnData *connData)
{
char buff[50];
if (connData->conn == NULL) {
//Connection aborted. Clean up.
return HTTPD_CGI_DONE;
}
// width and height must always go together so we can do max size validation
if (GET_ARG("pw")) {
dbg("Entered password for admin: %s", buff);
if (verify_admin_pw(buff)) {
dbg("pw is OK");
persist_set_as_default();
httpdRedirect(connData, SET_REDIR_SUC);
return HTTPD_CGI_DONE;
}
// if pw failed, show the same error as if it's wrong
}
httpdRedirect(connData, SET_REDIR_SUC "?err=Password"); // this will show in the "validation errors" box
return HTTPD_CGI_DONE;
}
httpd_cgi_state ICACHE_FLASH_ATTR
cgiPersistRestoreDefaults(HttpdConnData *connData)
{
if (connData->conn == NULL) {
//Connection aborted. Clean up.
return HTTPD_CGI_DONE;
}
persist_restore_default();
httpdRedirect(connData, SET_REDIR_SUC);
return HTTPD_CGI_DONE;
}
httpd_cgi_state ICACHE_FLASH_ATTR
cgiPersistRestoreHard(HttpdConnData *connData)
{
if (connData->conn == NULL) {
//Connection aborted. Clean up.
return HTTPD_CGI_DONE;
}
persist_restore_hard_default();
httpdRedirect(connData, SET_REDIR_SUC);
return HTTPD_CGI_DONE;
}

@ -0,0 +1,10 @@
#ifndef CGIPERSIST_H
#define CGIPERSIST_H
#include "httpd.h"
httpd_cgi_state cgiPersistWriteDefaults(HttpdConnData *connData);
httpd_cgi_state cgiPersistRestoreDefaults(HttpdConnData *connData);
httpd_cgi_state cgiPersistRestoreHard(HttpdConnData *connData);
#endif

@ -571,10 +571,12 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplWlan(HttpdConnData *connData, char *token,
else { else {
struct ip_info info; struct ip_info info;
wifi_get_ip_info(STATION_IF, &info); wifi_get_ip_info(STATION_IF, &info);
sprintf(buff, "ip: "IPSTR", mask: "IPSTR", gw: "IPSTR, sprintf(buff, IPSTR, GOOD_IP2STR(info.ip.addr));
GOOD_IP2STR(info.ip.addr),
GOOD_IP2STR(info.netmask.addr), // sprintf(buff, "ip: "IPSTR", mask: "IPSTR", gw: "IPSTR,
GOOD_IP2STR(info.gw.addr)); // GOOD_IP2STR(info.ip.addr),
// GOOD_IP2STR(info.netmask.addr),
// GOOD_IP2STR(info.gw.addr));
} }
} }

@ -11,9 +11,6 @@ PersistBlock persist;
#define PERSIST_SECTOR_ID 0x3D #define PERSIST_SECTOR_ID 0x3D
// This is used to force-erase the config area (when it's changed)
#define CHECKSUM_SALT 0x02
//region Persist and restore individual modules //region Persist and restore individual modules
static void ICACHE_FLASH_ATTR static void ICACHE_FLASH_ATTR
@ -81,6 +78,11 @@ persist_load(void)
{ {
info("[Persist] Loading stored settings from FLASH..."); info("[Persist] Loading stored settings from FLASH...");
dbg("sizeof(AppConfigBundle) = %d bytes", sizeof(AppConfigBundle));
dbg("sizeof(PersistBlock) = %d bytes", sizeof(PersistBlock));
dbg("sizeof(WiFiConfigBundle) = %d bytes", sizeof(WiFiConfigBundle));
dbg("sizeof(TerminalConfigBundle) = %d bytes", sizeof(TerminalConfigBundle));
bool hard_reset = false; bool hard_reset = false;
// Try to load // Try to load
@ -90,15 +92,15 @@ persist_load(void)
if (hard_reset || if (hard_reset ||
(compute_checksum(&persist.defaults) != persist.defaults.checksum) || (compute_checksum(&persist.defaults) != persist.defaults.checksum) ||
(compute_checksum(&persist.current) != persist.current.checksum)) { (compute_checksum(&persist.current) != persist.current.checksum)) {
dbg("[Persist] Checksum verification: FAILED"); error("[Persist] Checksum verification: FAILED");
hard_reset = true; hard_reset = true;
} else { } else {
dbg("[Persist] Checksum verification: PASSED"); info("[Persist] Checksum verification: PASSED");
} }
if (hard_reset) { if (hard_reset) {
persist_restore_hard_default(); persist_restore_hard_default();
// this also stores them to flash and applies to modues // this also stores them to flash and applies to modules
} else { } else {
apply_live_settings(); apply_live_settings();
} }
@ -161,8 +163,8 @@ persist_set_as_default(void)
{ {
info("[Persist] Storing live settings as defaults.."); info("[Persist] Storing live settings as defaults..");
// current -> defaults
memcpy(&persist.defaults, &persist.current, sizeof(AppConfigBundle)); memcpy(&persist.defaults, &persist.current, sizeof(AppConfigBundle));
persist_store(); persist_store();
info("[Persist] Default settings updated."); info("[Persist] Default settings updated.");

@ -13,15 +13,31 @@
#include "wifimgr.h" #include "wifimgr.h"
#include "screen.h" #include "screen.h"
// Changing this could be used to force-erase the config area
// after a firmware upgrade
#define CHECKSUM_SALT 0x5F5F5F5F
/** Struct for current or default settings */
typedef struct { typedef struct {
WiFiConfigBundle wificonf; WiFiConfigBundle wificonf;
TerminalConfigBundle termconf; TerminalConfigBundle termconf;
// ...
// other settings here // --- Space for future settings ---
// ... // Original size: 1024
//
// The size must be appropriately reduced each time something is added,
// and boolean flags defaulting to 0 should be used to detect unpopulated
// sections that must be restored to defaults on load.
//
// This ensures user settings are not lost each time they upgrade the firmware,
// which would lead to a checksum mismatch if the structure was changed and
// it grew to a different memory area.
uint8_t filler[1024];
uint32_t checksum; // computed before write and tested on load. If it doesn't match, values are reset to hard defaults. uint32_t checksum; // computed before write and tested on load. If it doesn't match, values are reset to hard defaults.
} AppConfigBundle; } AppConfigBundle;
/** This is the entire data block stored in FLASH */
typedef struct { typedef struct {
AppConfigBundle defaults; // defaults are stored here AppConfigBundle defaults; // defaults are stored here
AppConfigBundle current; // active settings adjusted by the user AppConfigBundle current; // active settings adjusted by the user

@ -10,6 +10,9 @@
#include "cgi_ping.h" #include "cgi_ping.h"
#include "cgi_main.h" #include "cgi_main.h"
#include "cgi_sockets.h" #include "cgi_sockets.h"
#include "cgi_network.h"
#include "cgi_appcfg.h"
#include "cgi_persist.h"
#define WIFI_PROTECT 0 #define WIFI_PROTECT 0
#define WIFI_AUTH_NAME "wifi" #define WIFI_AUTH_NAME "wifi"
@ -26,28 +29,38 @@ HttpdBuiltInUrl routes[] = {
// --- Web pages --- // --- Web pages ---
ROUTE_TPL_FILE("/", tplScreen, "/term.tpl"), ROUTE_TPL_FILE("/", tplScreen, "/term.tpl"),
ROUTE_TPL_FILE("/about", tplAbout, "/about.tpl"), ROUTE_TPL_FILE("/about/?", tplAbout, "/about.tpl"),
ROUTE_FILE("/help", "/help.tpl"), ROUTE_FILE("/help/?", "/help.tpl"),
// --- Sockets --- // --- Sockets ---
ROUTE_WS(URL_WS_UPDATE, updateSockConnect), ROUTE_WS(URL_WS_UPDATE, updateSockConnect),
// --- System control --- // --- System control ---
ROUTE_CGI("/system/reset", cgiResetDevice), ROUTE_CGI("/system/reset/?", cgiResetDevice),
ROUTE_CGI("/system/ping", cgiPing), ROUTE_CGI("/system/ping/?", cgiPing),
// --- WiFi config --- // --- WiFi config --- (TODO make this conditional and configurable)
#if WIFI_PROTECT #if WIFI_PROTECT
ROUTE_AUTH("/wifi*", wifiPassFn), ROUTE_AUTH("/wifi*", wifiPassFn),
#endif #endif
ROUTE_REDIRECT("/wifi/", "/wifi"), ROUTE_REDIRECT("/cfg/?", "/cfg/wifi"),
ROUTE_TPL_FILE("/wifi", tplWlan, "/wifi.tpl"),
ROUTE_CGI("/wifi/scan", cgiWiFiScan), ROUTE_TPL_FILE("/cfg/wifi/?", tplWlan, "/cfg_wifi.tpl"),
ROUTE_CGI("/wifi/connect", cgiWiFiConnect), ROUTE_FILE("/cfg/wifi/connecting/?", "/cfg_wifi_conn.tpl"),
ROUTE_CGI("/wifi/connstatus", cgiWiFiConnStatus), ROUTE_CGI("/cfg/wifi/scan", cgiWiFiScan),
ROUTE_FILE("/wifi/connecting", "/wifi_conn.tpl"), ROUTE_CGI("/cfg/wifi/connstatus", cgiWiFiConnStatus),
ROUTE_CGI("/wifi/set", cgiWiFiSetParams), ROUTE_CGI("/cfg/wifi/set", cgiWiFiSetParams),
ROUTE_TPL_FILE("/cfg/network/?", tplNetwork, "/cfg_network.tpl"),
ROUTE_CGI("/cfg/network/set", cgiNetworkSetParams),
ROUTE_TPL_FILE("/cfg/app/?", tplAppCfg, "/cfg_app.tpl"),
ROUTE_CGI("/cfg/app/set", cgiAppCfgSetParams),
ROUTE_FILE("/cfg/admin/?", "/cfg_admin.tpl"),
ROUTE_CGI("/cfg/admin/write_defaults", cgiPersistWriteDefaults),
ROUTE_CGI("/cfg/admin/restore_defaults", cgiPersistRestoreDefaults),
ROUTE_CGI("/cfg/admin/restore_hard", cgiPersistRestoreHard),
ROUTE_FILESYSTEM(), ROUTE_FILESYSTEM(),
ROUTE_END(), ROUTE_END(),

@ -17,7 +17,7 @@ void terminal_restore_defaults(void)
termconf->default_fg = 7; termconf->default_fg = 7;
termconf->width = 26; termconf->width = 26;
termconf->height = 10; termconf->height = 10;
sprintf(termconf->title, "ESP8266 Wireless Terminal"); sprintf(termconf->title, "ESPTerm");
sprintf(termconf->btn1, "1"); sprintf(termconf->btn1, "1");
sprintf(termconf->btn2, "2"); sprintf(termconf->btn2, "2");
sprintf(termconf->btn3, "3"); sprintf(termconf->btn3, "3");

@ -43,6 +43,11 @@ wifimgr_restore_defaults(void)
IP4_ADDR(&wificonf->sta_addr.ip, 192, 168, 0, (mac[5] == 1 ? 2 : mac[5])); // avoid being the same as "default gw" IP4_ADDR(&wificonf->sta_addr.ip, 192, 168, 0, (mac[5] == 1 ? 2 : mac[5])); // avoid being the same as "default gw"
IP4_ADDR(&wificonf->sta_addr.netmask, 255, 255, 255, 0); IP4_ADDR(&wificonf->sta_addr.netmask, 255, 255, 255, 0);
IP4_ADDR(&wificonf->sta_addr.gw, 192, 168, 0, 1); IP4_ADDR(&wificonf->sta_addr.gw, 192, 168, 0, 1);
// DEBUG ONLY - TODO remove for release
wificonf->opmode = STATION_MODE;
sprintf((char*)wificonf->sta_ssid, "Chlivek");
sprintf((char*)wificonf->sta_password, "prase chrochta");
} }
static void ICACHE_FLASH_ATTR static void ICACHE_FLASH_ATTR

Loading…
Cancel
Save