Compare commits

..
21 Commits
Author SHA1 Message Date
MightyPork 5564147f67 version bump 2017-08-06 19:08:12 +02:00
MightyPork 32ba7dc0d7 fixed a lot of bugs with wifi connecting 2017-08-06 19:07:43 +02:00
MightyPork 80721c1715 allow submitting empty get args - may solve some bugs with wifi config 2017-08-06 17:56:27 +02:00
MightyPork e22d3f26d9 Added Wifi conf link to the bottom navbar, auto-expand Station config box 2017-08-06 17:53:21 +02:00
MightyPork 24a6a0dfd3 added git hash to version string 2017-08-06 14:58:46 +02:00
MightyPork 8a182f072d removed mentions of " ' > < from help, as those characters are now allowed and properly escaped everywhere 2017-08-06 14:26:54 +02:00
MightyPork 76b447d03e throttle reload nad use textarea for mobile input bc it shows better symbol for the enter button 2017-08-06 14:20:34 +02:00
MightyPork 319427ba2e the onscreen android keyboard now works 2017-08-06 14:17:05 +02:00
MightyPork f8c60b0668 parser timeout, better checksum checking and struct stuffing, shaved some bytes off RAM 2017-08-06 11:22:45 +02:00
MightyPork 4eaee9389d use html and js escapes for tpl strings 2017-08-06 02:15:31 +02:00
MightyPork 0a731bd35e fix some bugs 2017-08-06 00:24:16 +02:00
MightyPork 0b3cfe0e74 use 0x01 for seps in labels string so users can use pipe if they want to 2017-08-05 23:56:35 +02:00
MightyPork 4551ad6dbe added info about the new commands to help. also e]FRa is removed bc it is no longer useful for anything 2017-08-05 23:46:11 +02:00
MightyPork 3f4b8a94f1 commands to set button or title 2017-08-05 21:40:25 +02:00
MightyPork 373affb582 parser formatting to look lkike a huge comment to C editors 2017-08-05 18:57:57 +02:00
MightyPork b48bb12067 help page update 2017-08-05 18:20:13 +02:00
MightyPork 110f461cf5 bold works !! 2017-07-31 02:59:13 +02:00
MightyPork 3ae1451821 working unicode!!! WTF!! and ajax initial load to avoid having to escape shit 2017-07-31 02:41:35 +02:00
MightyPork d711909812 w.i.p. Terminal Themes added, improved terminal color picker, new compression in front-end 2017-07-31 00:39:46 +02:00
MightyPork 76167b8014 WiFi sections hiding, STA auto enable on scan click, Terminal colors preview, better explanations, CONFIGURABLE UART 2017-07-29 20:55:16 +02:00
MightyPork e42407ad62 fontello unpack script & improved checkbox style 2017-07-29 12:33:55 +02:00
78 changed files with 3346 additions and 1757 deletions
+8 -6
View File
@@ -86,7 +86,6 @@ set(SOURCE_FILES
esp_iot_sdk_v1.5.2/include/json/jsonparse.h esp_iot_sdk_v1.5.2/include/json/jsonparse.h
esp_iot_sdk_v1.5.2/include/json/json.h esp_iot_sdk_v1.5.2/include/json/json.h
include/uart_hw.h
include/user_config.h include/user_config.h
include/ets_sys_extra.h include/ets_sys_extra.h
user/io.c user/io.c
@@ -97,10 +96,9 @@ set(SOURCE_FILES
user/cgi_persist.h 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_term_cfg.c
user/cgi_appcfg.h user/cgi_term_cfg.h
user/cgi_ping.c user/cgi_system.c
user/cgi_reset.c
user/uart_driver.c user/uart_driver.c
user/uart_handler.c user/uart_handler.c
user/ansi_parser.c user/ansi_parser.c
@@ -123,7 +121,10 @@ set(SOURCE_FILES
user/wifimgr.c user/wifimgr.c
user/wifimgr.h user/wifimgr.h
user/persist.c user/persist.c
user/persist.h include/helpers.h) user/persist.h
include/helpers.h
user/syscfg.c
user/syscfg.h)
include_directories(include) include_directories(include)
include_directories(user) include_directories(user)
@@ -143,6 +144,7 @@ add_definitions(
-DICACHE_RODATA_ATTR= -DICACHE_RODATA_ATTR=
-DFLAG_GZIP=2 -DFLAG_GZIP=2
-DADMIN_PASSWORD="asdf" -DADMIN_PASSWORD="asdf"
-DGIT_HASH="blabla"
-DESPFS_HEATSHRINK) -DESPFS_HEATSHRINK)
add_executable(esp_vt100_firmware ${SOURCE_FILES}) add_executable(esp_vt100_firmware ${SOURCE_FILES})
+3 -2
View File
@@ -65,7 +65,8 @@ 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 -DADMIN_PASSWORD=$(ADMIN_PASSWORD) -Wno-address -Wno-unused -DHTTPD_MAX_BACKLOG_SIZE=8192 -DADMIN_PASSWORD=$(ADMIN_PASSWORD) \
-DGIT_HASH='"$(shell git rev-parse --short HEAD)"'
# 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
@@ -196,7 +197,7 @@ libesphttpd/Makefile:
$(Q) [[ -e "libesphttpd/Makefile" ]] || echo -e "\e[31mlibesphttpd submodule missing.\nIf build fails, run \"git submodule init\" and \"git submodule update\".\e[0m" $(Q) [[ -e "libesphttpd/Makefile" ]] || echo -e "\e[31mlibesphttpd submodule missing.\nIf build fails, run \"git submodule init\" and \"git submodule update\".\e[0m"
libesphttpd: libesphttpd/Makefile libesphttpd: libesphttpd/Makefile
$(Q) make -C libesphttpd USE_OPENSDK=$(USE_OPENSDK) $(Q) make -C libesphttpd USE_OPENSDK=$(USE_OPENSDK) SERVERNAME_PREFIX="ESPTerm "
$(APP_AR): libesphttpd $(OBJ) $(APP_AR): libesphttpd $(OBJ)
$(vecho) "AR $@" $(vecho) "AR $@"
+57 -45
View File
@@ -3,60 +3,72 @@
/** /**
* Those replacements are done by the development server to test it locally * Those replacements are done by the development server to test it locally
* without esphttpd. This is needed mainly for places where the replacements * without esphttpd. This is needed mainly for places where the replacements
* are given to JavaScript, to avoid syntax errors with %% * are given to JavaScript, to avoid syntax errors with
*/ */
return [ return [
'%term_title%' => 'ESP8266 Wireless Terminal', 'term_title' => 'ESPTerm local debug',
'%btn1%' => '1', 'btn1' => '1',
'%btn2%' => '2', 'btn2' => '2',
'%btn3%' => '3', 'btn3' => '3',
'%btn4%' => '4', 'btn4' => '',
'%btn5%' => '5', 'btn5' => '5',
'labels_seq' => 'TESPTerm local debug1235',
'%screenData%' => '{ 'screenData' => '  HELLOx NRE3',//'\u000b\u0001\u001b\u0001\u0001\u0001\u0001\u0001\f\u0005\u0001\u0010\u0003HELLOx\u0002\u000b\u0001\u0001N\u0001RE\u00023\u0001', //,
/*'{
"w": 26, "h": 10, "w": 26, "h": 10,
"x": 0, "y": 0, "x": 10, "y": 5,
"cv": 1, "cv": 1, "fg": 10, "bg": 2,
"screen": "70 t259" "screen": ""
}', }',//"70 t259"*/
'%ap_enable%' => '1', 'parser_tout_ms' => 10,
'%tpw%' => '60',
'%ap_channel%' => '7',
'%ap_ssid%' => 'ESP-123456',
'%ap_password%' => 'Passw0rd!',
'%ap_hidden%' => '0',
'%sta_ssid%' => 'Chlivek',
'%sta_password%' => 'windows XP is The Best',
'%sta_active_ip%' => '1.2.3.4',
'%sta_active_ssid%' => 'Chlivek',
'%sta_enable%' => '1', 'opmode' => '2',
'%opmode%' => '3', 'sta_enable' => '0',
'%vers_fw%' => '1.2.3', 'ap_enable' => '1',
'%date%' => date('Y-m-d'),
'%time%' => date('G:i'),
'%vers_httpd%' => '4.5.6',
'%vers_sdk%' => '1.52',
'%githubrepo%' => 'https://github.com/MightyPork/esp-vt100-firmware',
'%ap_dhcp_time%' => '120', 'tpw' => '60',
'%ap_dhcp_start%' => '192.168.4.100', 'ap_channel' => '7',
'%ap_dhcp_end%' => '192.168.4.200', 'ap_ssid' => 'ESP-123456',
'%ap_addr_ip%' => '192.168.4.1', 'ap_password' => 'Passw0rd!',
'%ap_addr_mask%' => '255.255.255.0', 'ap_hidden' => '0',
'%sta_dhcp_enable%' => '1', 'sta_ssid' => 'Chlivek',
'%sta_addr_ip%' => '192.168.0.33', 'sta_password' => 'windows XP is The Best',
'%sta_addr_mask%' => '255.255.255.0', 'sta_active_ip' => '1.2.3.4',
'%sta_addr_gw%' => '192.168.0.1', 'sta_active_ssid' => 'Chlivek',
'%sta_mac%' => 'ab:cd:ef:01:23:45', 'vers_fw' => '1.2.3',
'%ap_mac%' => '01:23:45:ab:cd:ef', 'date' => date('Y-m-d'),
'time' => date('G:i'),
'vers_httpd' => '4.5.6',
'vers_sdk' => '1.52',
'githubrepo' => 'https://github.com/MightyPork/esp-vt100-firmware',
'%term_width%' => '26', 'ap_dhcp_time' => '120',
'%term_height%' => '10', 'ap_dhcp_start' => '192.168.4.100',
'%default_bg%' => '0', 'ap_dhcp_end' => '192.168.4.200',
'%default_fg%' => '7', 'ap_addr_ip' => '192.168.4.1',
'ap_addr_mask' => '255.255.255.0',
'sta_dhcp_enable' => '1',
'sta_addr_ip' => '192.168.0.33',
'sta_addr_mask' => '255.255.255.0',
'sta_addr_gw' => '192.168.0.1',
'sta_mac' => 'ab:cd:ef:01:23:45',
'ap_mac' => '01:23:45:ab:cd:ef',
'term_width' => '26',
'term_height' => '10',
'default_bg' => '0',
'default_fg' => '7',
'uart_baud' => 115200,
'uart_stopbits' => 1,
'uart_parity' => 2,
'theme' => 5,
]; ];
+8 -6
View File
@@ -18,6 +18,9 @@ if (! function_exists('pg')) {
} }
} }
pg('cfg_term', 'cfg', 'terminal', '/cfg/term');
pg('term_set', 'api', '', '/cfg/term/set');
pg('cfg_wifi', 'cfg', 'wifi', '/cfg/wifi'); pg('cfg_wifi', 'cfg', 'wifi', '/cfg/wifi');
pg('cfg_wifi_conn', '', '', '/cfg/wifi/connecting'); pg('cfg_wifi_conn', '', '', '/cfg/wifi/connecting');
pg('wifi_connstatus', 'api', '', '/cfg/wifi/connstatus'); pg('wifi_connstatus', 'api', '', '/cfg/wifi/connstatus');
@@ -27,13 +30,12 @@ pg('wifi_scan', 'api', '', '/cfg/wifi/scan');
pg('cfg_network', 'cfg', 'network', '/cfg/network'); pg('cfg_network', 'cfg', 'network', '/cfg/network');
pg('network_set', 'api', '', '/cfg/network/set'); pg('network_set', 'api', '', '/cfg/network/set');
pg('cfg_app', 'cfg', 'terminal', '/cfg/app'); pg('cfg_system', 'cfg', 'configure', '/cfg/system');
pg('app_set', 'api', '', '/cfg/app/set'); pg('system_set', 'api', '', '/cfg/system/set');
pg('cfg_admin', 'cfg', 'persist', '/cfg/admin'); pg('write_defaults', 'api', '', '/cfg/system/write_defaults');
pg('write_defaults', 'api', '', '/cfg/admin/write_defaults'); pg('restore_defaults', 'api', '', '/cfg/system/restore_defaults');
pg('restore_defaults', 'api', '', '/cfg/admin/restore_defaults'); pg('restore_hard', 'api', '', '/cfg/system/restore_hard');
pg('restore_hard', 'api', '', '/cfg/admin/restore_hard');
pg('help', 'cfg page-help', 'help', '/help'); pg('help', 'cfg page-help', 'help', '/help');
pg('about', 'cfg page-about', 'about', '/about'); pg('about', 'cfg page-about', 'about', '/about');
+498 -167
View File
File diff suppressed because one or more lines are too long
+1
View File
@@ -7,6 +7,7 @@ require_once __DIR__ . '/base.php';
$selected = [ $selected = [
'wifi.connected_ip_is', 'wifi.connected_ip_is',
'wifi.not_conn', 'wifi.not_conn',
'wifi.enter_passwd',
]; ];
$out = []; $out = [];
+1
View File
@@ -0,0 +1 @@
/out/
Binary file not shown.
Binary file not shown.
+90
View File
@@ -0,0 +1,90 @@
#!/bin/bash
# resolve current file's directory
DIR=$(dirname $(realpath $0))
OUTPUT_DIR="$DIR/out"
SASS_DIR="$DIR/../sass"
ICON_PREFIX='icn'
# list with full paths, sort from newest
NEWEST=$(ls -dt1 "$DIR"/*.zip | head -1)
if [[ -z "$NEWEST" ]]; then
echo "Fontello zip not found."
exit 1
fi
# Clean the output folder
rm -rf "$OUTPUT_DIR"
mkdir -p "$OUTPUT_DIR"
echo "Unpacking fontello..."
unzip -ju "$NEWEST" -d "$OUTPUT_DIR"
echo "Patching paths in the fontello CSS..."
# Fix bad relative paths in the CSS
sed -i "s|\.\./font/|/fonts/|g" "$OUTPUT_DIR/"*.css
echo "Generating SASS file with icon codes..."
SASSFILE="$SASS_DIR/_fontello.scss"
echo -e "@charset \"UTF-8\";\n\n/* Fontello data, processed by the unpack script. */\n" > "$SASSFILE"
# Extract the base font-face style
#grep -Pazo "(?s)@font-face.*?normal;\n\}" "$OUTPUT_DIR/fontello.css" \
# | sed 's/\x0//g' >> "$SASSFILE"
grep -Pazo "(?s)@font-face \{\n\s*font-family: 'fontello';\n\s*src: url\('data.*?truetype'\);\n\}" "$OUTPUT_DIR/fontello-embedded.css" \
| sed 's/\x0//g' >> "$SASSFILE"
grep -Pazo "(?s)$ICON_PREFIX-\"\]:before .*?\}" "$OUTPUT_DIR/fontello.css" \
| sed 's/\x0//g' \
| sed "s/$ICON_PREFIX-\"\]:before/\n\n%fontello-icon-base \{\n\&::before /g" \
>> "$SASSFILE"
echo -e "\n}" >> "$SASSFILE"
echo -e "\n\n/* Fontello icon codes */" >> "$SASSFILE"
echo -n "\$icon-codes: (" >> "$SASSFILE"
sed -r "s|\.$ICON_PREFIX-([a-z0-9-]+):before \{ content: ('.*?');.*?$|\t\1: \2,|g" "$OUTPUT_DIR/fontello-codes.css" \
| sed -r "s|@.*||g" >> "$SASSFILE"
echo -ne "\n);\n" >> "$SASSFILE"
echo -ne "\n/* Fontello classes */" >> "$SASSFILE"
cat "$OUTPUT_DIR/fontello-codes.css" \
| sed -r 's/\/\*.+\*\///g' \
| sed -r "s|@.*||g" \
| sed 's/:before/::before/g' >> "$SASSFILE"
TAIL=$(cat <<ASDF
[class^="$ICON_PREFIX-"], [class*=" $ICON_PREFIX-"] {
@extend %fontello-icon-base;
}
@mixin icon-base() {
@extend %fontello-icon-base;
}
@mixin icon-content(\$icon-name) {
&::before {
content: map-get(\$icon-codes, \$icon-name);
}
}
@mixin icon(\$icon-name) {
@include icon-base();
@include icon-content(\icon-name);
}
ASDF
)
echo "$TAIL" >> "$SASSFILE"
echo -e "\e[32mFontello ready\e[0m"
+34 -2
View File
@@ -7,12 +7,44 @@ if (!isset($_GET['page'])) $_GET['page'] = 'term';
$_GET['PAGE_TITLE'] = $_pages[$_GET['page']]->title . ' :: ' . APP_NAME; $_GET['PAGE_TITLE'] = $_pages[$_GET['page']]->title . ' :: ' . APP_NAME;
$_GET['BODYCLASS'] = $_pages[$_GET['page']]->bodyclass; $_GET['BODYCLASS'] = $_pages[$_GET['page']]->bodyclass;
if (!function_exists('tplSubs')) {
function tplSubs($str, $reps)
{
return preg_replace_callback('/%(j:|js:|h:|html:)?([a-z0-9-_.]+)%/i', function ($m) use ($reps) {
$key = $m[2];
if (array_key_exists($key, $reps)) {
$val = $reps[$key];
} else {
$val = '';
}
switch ($m[1]) {
case 'j:':
case 'js:':
$v = json_encode($val);
return substr($v, 1, strlen($v) - 2);
case 'h:':
case 'html:':
return htmlspecialchars($val);
default:
return $val;
}
}, $str);
}
}
require __DIR__ . '/pages/_head.php'; require __DIR__ . '/pages/_head.php';
$_pf = __DIR__ . '/pages/'.$_GET['page'].'.php'; $_pf = __DIR__ . '/pages/'.$_GET['page'].'.php';
if (file_exists($_pf)) { if (file_exists($_pf)) {
$f = file_get_contents($_pf); $f = file_get_contents($_pf);
$reps = DEBUG ? require(__DIR__ . '/_debug_replacements.php') : [];
$str = str_replace(array_keys($reps), array_values($reps), $f); if (DEBUG)
$str = tplSubs($f, require(__DIR__ . '/_debug_replacements.php'));
else $str = $f;
// special symbols
$str = str_replace('\,', '&#8239;', $str);
$str = preg_replace('/(?<=\w)~(?=\w)/', '&nbsp;', $str);
include_str($str); include_str($str);
} else { } else {
echo "404"; echo "404";
+207 -148
View File
@@ -488,7 +488,7 @@
// Multiple select // Multiple select
for (j = 0; j < value.length; j += 1) { for (j = 0; j < value.length; j += 1) {
elm[i].selected = ''; elm[i].selected = '';
if (elm[i].value === value[j]) { if (elm[i].value === ""+value[j]) {
elm[i].selected = 'selected'; elm[i].selected = 'selected';
break; break;
} }
@@ -999,18 +999,34 @@ $.ready(function () {
x.removeAttribute('tabindex'); x.removeAttribute('tabindex');
}); });
// brand works as a link back to term in widescreen mode
var br = qs('#brand'); var br = qs('#brand');
br && br.removeAttribute('tabindex'); br && br.addEventListener('click', function() {
location.href='/'; // go to terminal
});
} }
}); });
$._loader = function(vis) { $._loader = function(vis) {
$('#loader').toggleClass('show', vis); $('#loader').toggleClass('show', vis);
}; };
function showPage() {
$('#content').addClass('load');
}
$.ready(function() {
if (window.noAutoShow !== true) {
setTimeout(function () {
showPage();
}, 1);
}
});
// Generated from PHP locale file // Generated from PHP locale file
var _tr = { var _tr = {
"wifi.connected_ip_is": "Connected, IP is ", "wifi.connected_ip_is": "Connected, IP is ",
"wifi.not_conn": "Not connected." "wifi.not_conn": "Not connected.",
"wifi.enter_passwd": "Enter password for \":ssid:\""
}; };
function tr(key) { return _tr[key] || '?'+key+'?'; } function tr(key) { return _tr[key] || '?'+key+'?'; }
@@ -1039,21 +1055,6 @@ function tr(key) { return _tr[key] || '?'+key+'?'; }
$('#sta-nw .ip').html(ip.length>0 ? tr('wifi.connected_ip_is')+ip : tr('wifi.not_conn')); $('#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 = { //var ap_json = {
@@ -1099,7 +1100,6 @@ function tr(key) { return _tr[key] || '?'+key+'?'; }
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 = mk('div'); var item = mk('div');
@@ -1123,19 +1123,18 @@ function tr(key) { return _tr[key] || '?'+key+'?'; }
$item.on('click', function () { $item.on('click', function () {
var $th = $(this); var $th = $(this);
var ssid = $th.data('ssid'); var conn_ssid = $th.data('ssid');
var conn_pass = '';
$('#conn-ssid').val(ssid);
$('#conn-passwd').val('');
if (+$th.data('pwd')) { if (+$th.data('pwd')) {
// this AP needs a password // this AP needs a password
Modal.show('#psk-modal'); conn_pass = prompt(tr("wifi.enter_passwd").replace(":ssid:", conn_ssid));
$('#conn-passwd')[0].focus(); if (!conn_pass) return;
} else {
//Modal.show('#reset-modal');
submitPskModal(null, true);
} }
$('#sta_password').val(conn_pass);
$('#sta_ssid').val(conn_ssid);
selectSta(conn_ssid, conn_pass, '');
}); });
@@ -1148,12 +1147,13 @@ function tr(key) { return _tr[key] || '?'+key+'?'; }
$('#ap-loader').removeClass('hidden'); $('#ap-loader').removeClass('hidden');
$('#ap-scan').addClass('hidden'); $('#ap-scan').addClass('hidden');
$('#ap-loader .anim-dots').html('.'); $('#ap-loader .anim-dots').html('.');
scanAPs(); scanAPs();
} }
/** Ask the CGI what APs are visible (async) */ /** Ask the CGI what APs are visible (async) */
function scanAPs() { function scanAPs() {
$.get('http://'+_root+w.scan_url, onScan); $.get('http://'+_root+'/cfg/wifi/scan', onScan);
} }
function rescan(time) { function rescan(time) {
@@ -1162,12 +1162,6 @@ function tr(key) { return _tr[key] || '?'+key+'?'; }
/** Set up the WiFi page */ /** Set up the WiFi page */
function wifiInit(cfg) { 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 // Update slider value displays
$('.Row.range').forEach(function(x) { $('.Row.range').forEach(function(x) {
var inp = x.querySelector('input'); var inp = x.querySelector('input');
@@ -1196,141 +1190,90 @@ function tr(key) { return _tr[key] || '?'+key+'?'; }
w.init = wifiInit; w.init = wifiInit;
w.startScanning = startScanning; w.startScanning = startScanning;
})(window.WiFi = {}); })(window.WiFi = {});
(function() { var Screen = (function () {
/** var W = 0, H = 0; // dimensions
* Terminal module var inited = false;
*/
var Term = (function () { var cursor = {
var W, H; a: false, // active (blink state)
var cursor = {a: false, x: 0, y: 0, suppress: false, hidden: false}; x: 0, // 0-based coordinates
y: 0,
fg: 7, // colors 0-15
bg: 0,
bold: false,
suppress: false, // do not turn on in blink interval (for safe moving)
hidden: false // do not show
};
var screen = []; var screen = [];
var blinkIval; var blinkIval;
/* /** Clear screen */
/!** Clear screen *!/ function _clear() {
function cls() { for (var i = W*H-1; i>=0; i--) {
screen.forEach(function(cell, i) { var cell = screen[i];
cell.t = ' '; cell.t = ' ';
cell.fg = 7; cell.bg = cursor.bg;
cell.bg = 0; cell.fg = cursor.fg;
blit(cell); cell.bold = false;
}); _draw(cell);
}
} }
*/
/** Set text and color at XY */ /** Set text and color at XY */
function cellAt(y, x) { function _cellAt(y, x) {
return screen[y*W+x]; return screen[y*W+x];
} }
/** Get cell under cursor */ /** Get cell under cursor */
function cursorCell() { function _curCell() {
return cellAt(cursor.y, cursor.x); return screen[cursor.y*W + cursor.x];
} }
/* /** Enable or disable cursor visibility */
/!** Enable or disable cursor visibility *!/ function _cursorEnable(enable) {
function cursorEnable(enable) {
cursor.hidden = !enable; cursor.hidden = !enable;
cursor.a &= enable; cursor.a &= enable;
blit(cursorCell(), cursor.a); _draw(_curCell());
} }
/!** Safely move cursor *!/ /** Safely move cursor */
function cursorSet(y, x) { function cursorSet(y, x) {
// Hide and prevent from showing up during the move // Hide and prevent from showing up during the move
cursor.suppress = true; cursor.suppress = true;
blit(cursorCell(), false); _draw(_curCell(), false);
cursor.x = x; cursor.x = x;
cursor.y = y; cursor.y = y;
// Show again // Show again
cursor.suppress = false; cursor.suppress = false;
blit(cursorCell(), cursor.a); _draw(_curCell());
} }
*/
/** Update cell on display. inv = invert (for cursor) */ /** Update cell on display. inv = invert (for cursor) */
function blit(cell, inv) { function _draw(cell, inv) {
if (typeof inv == 'undefined') {
inv = cursor.a && cursor.x == cell.x && cursor.y == cell.y;
}
var e = cell.e, fg, bg; var e = cell.e, fg, bg;
// Colors // Colors
fg = inv ? cell.bg : cell.fg; fg = inv ? cell.bg : cell.fg;
bg = inv ? cell.fg : cell.bg; bg = inv ? cell.fg : cell.bg;
// Update // Update
e.innerText = (cell.t + ' ')[0]; e.innerText = (cell.t + ' ')[0];
e.className = 'fg'+fg+' bg'+bg; e.className = 'fg' + fg + ' bg' + bg + (cell.bold ? ' bold' : '');
} }
/** Show entire screen */ /** Show entire screen */
function blitAll() { function _drawAll() {
screen.forEach(function(cell, i) { for (var i = W*H-1; i>=0; i--) {
/* Invert if under cursor & cursor active */ _draw(screen[i]);
var inv = cursor.a && (i == cursor.y*W+cursor.x);
blit(cell, inv);
});
}
/** Load screen content from a 'binary' sequence */
function load(obj) {
cursor.x = obj.x;
cursor.y = obj.y;
cursor.hidden = !obj.cv;
// full re-init if size changed
if (obj.w != W || obj.h != H) {
Term.init(obj);
return;
}
// Simple compression - hexFG hexBG 'ASCII' (r/s/t/u NUM{1,2,3,4})?
// comma instead of both colors = same as before
var i = 0, ci = 0, str = obj.screen;
var fg = 7, bg = 0;
while(i < str.length && ci<W*H) {
var cell = screen[ci++];
var j = str[i];
if (j != ',') { // comma = repeat last colors
fg = cell.fg = parseInt(str[i++], 16);
bg = cell.bg = parseInt(str[i++], 16);
} else {
i++;
cell.fg = fg;
cell.bg = bg;
}
var t = cell.t = str[i++];
var repchars = 0;
switch(str[i]) {
case 'r': repchars = 1; break;
case 's': repchars = 2; break;
case 't': repchars = 3; break;
case 'u': repchars = 4; break;
default: repchars = 0;
}
if (repchars > 0) {
var rep = parseInt(str.substr(i+1,repchars));
i = i + repchars + 1;
for (; rep>0 && ci<W*H; rep--) {
cell = screen[ci++];
cell.fg = fg;
cell.bg = bg;
cell.t = t;
}
} }
} }
blitAll(); function _rebuild(rows, cols) {
} W = cols;
H = rows;
/** Init the terminal */
function init(obj) {
W = obj.w;
H = obj.h;
/* Build screen & show */ /* Build screen & show */
var e, cell, scr = qs('#screen'); var e, cell, scr = qs('#screen');
@@ -1358,11 +1301,21 @@ function tr(key) { return _tr[key] || '?'+key+'?'; }
/* The cell */ /* The cell */
scr.appendChild(e); scr.appendChild(e);
cell = {t: ' ', fg: 7, bg: 0, e: e}; cell = {
t: ' ',
fg: cursor.fg,
bg: cursor.bg,
e: e,
x: i % W,
y: Math.floor(i / W),
};
screen.push(cell); screen.push(cell);
blit(cell); _draw(cell);
}
} }
/** Init the terminal */
function _init() {
/* Cursor blinking */ /* Cursor blinking */
clearInterval(blinkIval); clearInterval(blinkIval);
blinkIval = setInterval(function () { blinkIval = setInterval(function () {
@@ -1372,17 +1325,116 @@ function tr(key) { return _tr[key] || '?'+key+'?'; }
} }
if (!cursor.suppress) { if (!cursor.suppress) {
blit(cursorCell(), cursor.a); _draw(_curCell(), cursor.a);
} }
}, 500); }, 500);
inited = true;
}
load(obj); /** Decode two-byte number */
function parse2B(s, i) {
return (s.charCodeAt(i++) - 1) + (s.charCodeAt(i) - 1) * 127;
}
var SEQ_SET_COLOR = 1;
var SEQ_REPEAT = 2;
function _load_content(str) {
var i = 0, ci = 0, j, jc, num, num2, t = ' ', fg, bg, bold, cell;
if (!inited) _init();
// Set size
num = parse2B(str, i); i += 2; // height
num2 = parse2B(str, i); i += 2; // width
if (num != H || num2 != W) {
_rebuild(num, num2);
}
// console.log("Size ",num, num2);
// Cursor position
num = parse2B(str, i); i += 2; // row
num2 = parse2B(str, i); i += 2; // col
cursorSet(num, num2);
// console.log("Cursor at ",num, num2);
// Attributes
num = parse2B(str, i); i += 2; // fg bg bold hidden
cursor.fg = num & 0x0F;
cursor.bg = (num & 0xF0) >> 4;
cursor.bold = !!(num & 0x100);
cursor.hidden = !(num & 0x200);
// console.log("FG ",cursor.fg, ", BG ", cursor.bg,", BOLD ", cursor.bold, ", HIDE ", cursor.hidden);
fg = cursor.fg;
bg = cursor.bg;
bold = cursor.bold;
// Here come the content
while(i < str.length && ci<W*H) {
j = str[i++];
jc = j.charCodeAt(0);
if (jc == SEQ_SET_COLOR) {
num = parse2B(str, i); i += 2;
fg = num & 0x0F;
bg = (num & 0xF0) >> 4;
bold = !!(num & 0x100);
// console.log("Switch to ",fg,bg,bold);
}
else if (jc == SEQ_REPEAT) {
num = parse2B(str, i); i += 2;
// console.log("Repeat x ",num);
for (; num>0 && ci<W*H; num--) {
cell = screen[ci++];
cell.fg = fg;
cell.bg = bg;
cell.t = t;
cell.bold = bold;
}
}
else {
cell = screen[ci++];
// Unique cell character
t = cell.t = j;
cell.fg = fg;
cell.bg = bg;
cell.bold = bold;
// console.log("Symbol ", j);
}
}
_drawAll();
}
function _load_labels(str) {
var pieces = str.split('\x01');
qs('h1').textContent = pieces[0];
qsa('#buttons button').forEach(function(x, i) {
var s = pieces[i+1].trim();
// if empty string, use the "dim" effect and put nbsp instead to stretch the btn vertically
x.innerHTML = s.length >0 ? e(s) : "&nbsp;";
x.style.opacity = s.length > 0 ? 1 : 0.2;
});
}
/** Load screen content from a binary sequence (new) */
function load(str) {
var content = str.substr(1);
switch(str.charAt(0)) {
case 'S':
_load_content(content);
break;
case 'T':
_load_labels(content);
break;
default:
console.warn("Bad data message type, ignoring.");
}
} }
// publish
return { return {
init: init, load: load, // full load (string)
load: load
}; };
})(); })();
@@ -1405,7 +1457,7 @@ function tr(key) { return _tr[key] || '?'+key+'?'; }
try { try {
console.log("RX: ", evt.data); console.log("RX: ", evt.data);
// Assume all our messages are screen updates // Assume all our messages are screen updates
Term.load(JSON.parse(evt.data)); Screen.load(evt.data);
} catch(e) { } catch(e) {
console.error(e); console.error(e);
} }
@@ -1413,6 +1465,7 @@ function tr(key) { return _tr[key] || '?'+key+'?'; }
function doSend(message) { function doSend(message) {
console.log("TX: ", message); console.log("TX: ", message);
if (!ws) return; // for dry testing
if (ws.readyState != 1) { if (ws.readyState != 1) {
console.error("Socket not ready"); console.error("Socket not ready");
return; return;
@@ -1424,12 +1477,21 @@ function tr(key) { return _tr[key] || '?'+key+'?'; }
} }
function init() { function init() {
ws = new WebSocket("ws://"+_root+"/ws/update.cgi"); ws = new WebSocket("ws://"+_root+"/term/update.ws");
ws.onopen = onOpen; ws.onopen = onOpen;
ws.onclose = onClose; ws.onclose = onClose;
ws.onmessage = onMessage; ws.onmessage = onMessage;
console.log("Opening socket."); console.log("Opening socket.");
// Ask for initial data
$.get('http://'+_root+'/term/init', function(resp, status) {
if (status !== 200) location.reload(true);
console.log("Data received!");
Screen.load(resp);
showPage();
});
} }
return { return {
@@ -1439,9 +1501,7 @@ function tr(key) { return _tr[key] || '?'+key+'?'; }
}; };
})(); })();
// /** User input */
// Keyboard (& mouse) input
//
var Input = (function() { var Input = (function() {
function sendStrMsg(str) { function sendStrMsg(str) {
Conn.send("STR:"+str); Conn.send("STR:"+str);
@@ -1470,6 +1530,7 @@ function tr(key) { return _tr[key] || '?'+key+'?'; }
//console.log("Down ", code, e); //console.log("Down ", code, e);
switch(code) { switch(code) {
case 8: sendStrMsg('\x08'); break; case 8: sendStrMsg('\x08'); break;
case 10:
case 13: sendStrMsg('\x0d\x0a'); break; case 13: sendStrMsg('\x0d\x0a'); break;
case 27: sendStrMsg('\x1b'); break; // this allows to directly enter control sequences case 27: sendStrMsg('\x1b'); break; // this allows to directly enter control sequences
case 37: sendStrMsg('\x1b[D'); break; case 37: sendStrMsg('\x1b[D'); break;
@@ -1488,14 +1549,12 @@ function tr(key) { return _tr[key] || '?'+key+'?'; }
return { return {
init: init, init: init,
onTap: sendPosMsg onTap: sendPosMsg,
sendString: sendStrMsg,
}; };
})(); })();
window.termInit = function () {
window.termInit = function (obj) {
Term.init(obj);
Conn.init(); Conn.init();
Input.init(); Input.init();
}; };
})();
+16 -1
View File
@@ -100,11 +100,26 @@ $.ready(function () {
x.removeAttribute('tabindex'); x.removeAttribute('tabindex');
}); });
// brand works as a link back to term in widescreen mode
var br = qs('#brand'); var br = qs('#brand');
br && br.removeAttribute('tabindex'); br && br.addEventListener('click', function() {
location.href='/'; // go to terminal
});
} }
}); });
$._loader = function(vis) { $._loader = function(vis) {
$('#loader').toggleClass('show', vis); $('#loader').toggleClass('show', vis);
}; };
function showPage() {
$('#content').addClass('load');
}
$.ready(function() {
if (window.noAutoShow !== true) {
setTimeout(function () {
showPage();
}, 1);
}
});
+1 -1
View File
@@ -488,7 +488,7 @@
// Multiple select // Multiple select
for (j = 0; j < value.length; j += 1) { for (j = 0; j < value.length; j += 1) {
elm[i].selected = ''; elm[i].selected = '';
if (elm[i].value === value[j]) { if (elm[i].value === ""+value[j]) {
elm[i].selected = 'selected'; elm[i].selected = 'selected';
break; break;
} }
+2 -1
View File
@@ -1,7 +1,8 @@
// Generated from PHP locale file // Generated from PHP locale file
var _tr = { var _tr = {
"wifi.connected_ip_is": "Connected, IP is ", "wifi.connected_ip_is": "Connected, IP is ",
"wifi.not_conn": "Not connected." "wifi.not_conn": "Not connected.",
"wifi.enter_passwd": "Enter password for \":ssid:\""
}; };
function tr(key) { return _tr[key] || '?'+key+'?'; } function tr(key) { return _tr[key] || '?'+key+'?'; }
+178 -113
View File
@@ -1,138 +1,87 @@
(function() { var Screen = (function () {
/** var W = 0, H = 0; // dimensions
* Terminal module var inited = false;
*/
var Term = (function () { var cursor = {
var W, H; a: false, // active (blink state)
var cursor = {a: false, x: 0, y: 0, suppress: false, hidden: false}; x: 0, // 0-based coordinates
y: 0,
fg: 7, // colors 0-15
bg: 0,
bold: false,
suppress: false, // do not turn on in blink interval (for safe moving)
hidden: false // do not show
};
var screen = []; var screen = [];
var blinkIval; var blinkIval;
/* /** Clear screen */
/!** Clear screen *!/ function _clear() {
function cls() { for (var i = W*H-1; i>=0; i--) {
screen.forEach(function(cell, i) { var cell = screen[i];
cell.t = ' '; cell.t = ' ';
cell.fg = 7; cell.bg = cursor.bg;
cell.bg = 0; cell.fg = cursor.fg;
blit(cell); cell.bold = false;
}); _draw(cell);
}
} }
*/
/** Set text and color at XY */ /** Set text and color at XY */
function cellAt(y, x) { function _cellAt(y, x) {
return screen[y*W+x]; return screen[y*W+x];
} }
/** Get cell under cursor */ /** Get cell under cursor */
function cursorCell() { function _curCell() {
return cellAt(cursor.y, cursor.x); return screen[cursor.y*W + cursor.x];
} }
/* /** Enable or disable cursor visibility */
/!** Enable or disable cursor visibility *!/ function _cursorEnable(enable) {
function cursorEnable(enable) {
cursor.hidden = !enable; cursor.hidden = !enable;
cursor.a &= enable; cursor.a &= enable;
blit(cursorCell(), cursor.a); _draw(_curCell());
} }
/!** Safely move cursor *!/ /** Safely move cursor */
function cursorSet(y, x) { function cursorSet(y, x) {
// Hide and prevent from showing up during the move // Hide and prevent from showing up during the move
cursor.suppress = true; cursor.suppress = true;
blit(cursorCell(), false); _draw(_curCell(), false);
cursor.x = x; cursor.x = x;
cursor.y = y; cursor.y = y;
// Show again // Show again
cursor.suppress = false; cursor.suppress = false;
blit(cursorCell(), cursor.a); _draw(_curCell());
} }
*/
/** Update cell on display. inv = invert (for cursor) */ /** Update cell on display. inv = invert (for cursor) */
function blit(cell, inv) { function _draw(cell, inv) {
if (typeof inv == 'undefined') {
inv = cursor.a && cursor.x == cell.x && cursor.y == cell.y;
}
var e = cell.e, fg, bg; var e = cell.e, fg, bg;
// Colors // Colors
fg = inv ? cell.bg : cell.fg; fg = inv ? cell.bg : cell.fg;
bg = inv ? cell.fg : cell.bg; bg = inv ? cell.fg : cell.bg;
// Update // Update
e.innerText = (cell.t + ' ')[0]; e.innerText = (cell.t + ' ')[0];
e.className = 'fg'+fg+' bg'+bg; e.className = 'fg' + fg + ' bg' + bg + (cell.bold ? ' bold' : '');
} }
/** Show entire screen */ /** Show entire screen */
function blitAll() { function _drawAll() {
screen.forEach(function(cell, i) { for (var i = W*H-1; i>=0; i--) {
/* Invert if under cursor & cursor active */ _draw(screen[i]);
var inv = cursor.a && (i == cursor.y*W+cursor.x);
blit(cell, inv);
});
}
/** Load screen content from a 'binary' sequence */
function load(obj) {
cursor.x = obj.x;
cursor.y = obj.y;
cursor.hidden = !obj.cv;
// full re-init if size changed
if (obj.w != W || obj.h != H) {
Term.init(obj);
return;
}
// Simple compression - hexFG hexBG 'ASCII' (r/s/t/u NUM{1,2,3,4})?
// comma instead of both colors = same as before
var i = 0, ci = 0, str = obj.screen;
var fg = 7, bg = 0;
while(i < str.length && ci<W*H) {
var cell = screen[ci++];
var j = str[i];
if (j != ',') { // comma = repeat last colors
fg = cell.fg = parseInt(str[i++], 16);
bg = cell.bg = parseInt(str[i++], 16);
} else {
i++;
cell.fg = fg;
cell.bg = bg;
}
var t = cell.t = str[i++];
var repchars = 0;
switch(str[i]) {
case 'r': repchars = 1; break;
case 's': repchars = 2; break;
case 't': repchars = 3; break;
case 'u': repchars = 4; break;
default: repchars = 0;
}
if (repchars > 0) {
var rep = parseInt(str.substr(i+1,repchars));
i = i + repchars + 1;
for (; rep>0 && ci<W*H; rep--) {
cell = screen[ci++];
cell.fg = fg;
cell.bg = bg;
cell.t = t;
}
} }
} }
blitAll(); function _rebuild(rows, cols) {
} W = cols;
H = rows;
/** Init the terminal */
function init(obj) {
W = obj.w;
H = obj.h;
/* Build screen & show */ /* Build screen & show */
var e, cell, scr = qs('#screen'); var e, cell, scr = qs('#screen');
@@ -160,11 +109,21 @@
/* The cell */ /* The cell */
scr.appendChild(e); scr.appendChild(e);
cell = {t: ' ', fg: 7, bg: 0, e: e}; cell = {
t: ' ',
fg: cursor.fg,
bg: cursor.bg,
e: e,
x: i % W,
y: Math.floor(i / W),
};
screen.push(cell); screen.push(cell);
blit(cell); _draw(cell);
}
} }
/** Init the terminal */
function _init() {
/* Cursor blinking */ /* Cursor blinking */
clearInterval(blinkIval); clearInterval(blinkIval);
blinkIval = setInterval(function () { blinkIval = setInterval(function () {
@@ -174,17 +133,116 @@
} }
if (!cursor.suppress) { if (!cursor.suppress) {
blit(cursorCell(), cursor.a); _draw(_curCell(), cursor.a);
} }
}, 500); }, 500);
inited = true;
}
load(obj); /** Decode two-byte number */
function parse2B(s, i) {
return (s.charCodeAt(i++) - 1) + (s.charCodeAt(i) - 1) * 127;
}
var SEQ_SET_COLOR = 1;
var SEQ_REPEAT = 2;
function _load_content(str) {
var i = 0, ci = 0, j, jc, num, num2, t = ' ', fg, bg, bold, cell;
if (!inited) _init();
// Set size
num = parse2B(str, i); i += 2; // height
num2 = parse2B(str, i); i += 2; // width
if (num != H || num2 != W) {
_rebuild(num, num2);
}
// console.log("Size ",num, num2);
// Cursor position
num = parse2B(str, i); i += 2; // row
num2 = parse2B(str, i); i += 2; // col
cursorSet(num, num2);
// console.log("Cursor at ",num, num2);
// Attributes
num = parse2B(str, i); i += 2; // fg bg bold hidden
cursor.fg = num & 0x0F;
cursor.bg = (num & 0xF0) >> 4;
cursor.bold = !!(num & 0x100);
cursor.hidden = !(num & 0x200);
// console.log("FG ",cursor.fg, ", BG ", cursor.bg,", BOLD ", cursor.bold, ", HIDE ", cursor.hidden);
fg = cursor.fg;
bg = cursor.bg;
bold = cursor.bold;
// Here come the content
while(i < str.length && ci<W*H) {
j = str[i++];
jc = j.charCodeAt(0);
if (jc == SEQ_SET_COLOR) {
num = parse2B(str, i); i += 2;
fg = num & 0x0F;
bg = (num & 0xF0) >> 4;
bold = !!(num & 0x100);
// console.log("Switch to ",fg,bg,bold);
}
else if (jc == SEQ_REPEAT) {
num = parse2B(str, i); i += 2;
// console.log("Repeat x ",num);
for (; num>0 && ci<W*H; num--) {
cell = screen[ci++];
cell.fg = fg;
cell.bg = bg;
cell.t = t;
cell.bold = bold;
}
}
else {
cell = screen[ci++];
// Unique cell character
t = cell.t = j;
cell.fg = fg;
cell.bg = bg;
cell.bold = bold;
// console.log("Symbol ", j);
}
}
_drawAll();
}
function _load_labels(str) {
var pieces = str.split('\x01');
qs('h1').textContent = pieces[0];
qsa('#buttons button').forEach(function(x, i) {
var s = pieces[i+1].trim();
// if empty string, use the "dim" effect and put nbsp instead to stretch the btn vertically
x.innerHTML = s.length >0 ? e(s) : "&nbsp;";
x.style.opacity = s.length > 0 ? 1 : 0.2;
});
}
/** Load screen content from a binary sequence (new) */
function load(str) {
var content = str.substr(1);
switch(str.charAt(0)) {
case 'S':
_load_content(content);
break;
case 'T':
_load_labels(content);
break;
default:
console.warn("Bad data message type, ignoring.");
}
} }
// publish
return { return {
init: init, load: load, // full load (string)
load: load
}; };
})(); })();
@@ -207,7 +265,7 @@
try { try {
console.log("RX: ", evt.data); console.log("RX: ", evt.data);
// Assume all our messages are screen updates // Assume all our messages are screen updates
Term.load(JSON.parse(evt.data)); Screen.load(evt.data);
} catch(e) { } catch(e) {
console.error(e); console.error(e);
} }
@@ -215,6 +273,7 @@
function doSend(message) { function doSend(message) {
console.log("TX: ", message); console.log("TX: ", message);
if (!ws) return; // for dry testing
if (ws.readyState != 1) { if (ws.readyState != 1) {
console.error("Socket not ready"); console.error("Socket not ready");
return; return;
@@ -226,12 +285,21 @@
} }
function init() { function init() {
ws = new WebSocket("ws://"+_root+"/ws/update.cgi"); ws = new WebSocket("ws://"+_root+"/term/update.ws");
ws.onopen = onOpen; ws.onopen = onOpen;
ws.onclose = onClose; ws.onclose = onClose;
ws.onmessage = onMessage; ws.onmessage = onMessage;
console.log("Opening socket."); console.log("Opening socket.");
// Ask for initial data
$.get('http://'+_root+'/term/init', function(resp, status) {
if (status !== 200) location.reload(true);
console.log("Data received!");
Screen.load(resp);
showPage();
});
} }
return { return {
@@ -241,9 +309,7 @@
}; };
})(); })();
// /** User input */
// Keyboard (& mouse) input
//
var Input = (function() { var Input = (function() {
function sendStrMsg(str) { function sendStrMsg(str) {
Conn.send("STR:"+str); Conn.send("STR:"+str);
@@ -272,6 +338,7 @@
//console.log("Down ", code, e); //console.log("Down ", code, e);
switch(code) { switch(code) {
case 8: sendStrMsg('\x08'); break; case 8: sendStrMsg('\x08'); break;
case 10:
case 13: sendStrMsg('\x0d\x0a'); break; case 13: sendStrMsg('\x0d\x0a'); break;
case 27: sendStrMsg('\x1b'); break; // this allows to directly enter control sequences case 27: sendStrMsg('\x1b'); break; // this allows to directly enter control sequences
case 37: sendStrMsg('\x1b[D'); break; case 37: sendStrMsg('\x1b[D'); break;
@@ -290,14 +357,12 @@
return { return {
init: init, init: init,
onTap: sendPosMsg onTap: sendPosMsg,
sendString: sendStrMsg,
}; };
})(); })();
window.termInit = function () {
window.termInit = function (obj) {
Term.init(obj);
Conn.init(); Conn.init();
Input.init(); Input.init();
}; };
})();
+10 -32
View File
@@ -23,21 +23,6 @@
$('#sta-nw .ip').html(ip.length>0 ? tr('wifi.connected_ip_is')+ip : tr('wifi.not_conn')); $('#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 = { //var ap_json = {
@@ -83,7 +68,6 @@
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 = mk('div'); var item = mk('div');
@@ -107,19 +91,18 @@
$item.on('click', function () { $item.on('click', function () {
var $th = $(this); var $th = $(this);
var ssid = $th.data('ssid'); var conn_ssid = $th.data('ssid');
var conn_pass = '';
$('#conn-ssid').val(ssid);
$('#conn-passwd').val('');
if (+$th.data('pwd')) { if (+$th.data('pwd')) {
// this AP needs a password // this AP needs a password
Modal.show('#psk-modal'); conn_pass = prompt(tr("wifi.enter_passwd").replace(":ssid:", conn_ssid));
$('#conn-passwd')[0].focus(); if (!conn_pass) return;
} else {
//Modal.show('#reset-modal');
submitPskModal(null, true);
} }
$('#sta_password').val(conn_pass);
$('#sta_ssid').val(conn_ssid);
selectSta(conn_ssid, conn_pass, '');
}); });
@@ -132,12 +115,13 @@
$('#ap-loader').removeClass('hidden'); $('#ap-loader').removeClass('hidden');
$('#ap-scan').addClass('hidden'); $('#ap-scan').addClass('hidden');
$('#ap-loader .anim-dots').html('.'); $('#ap-loader .anim-dots').html('.');
scanAPs(); scanAPs();
} }
/** Ask the CGI what APs are visible (async) */ /** Ask the CGI what APs are visible (async) */
function scanAPs() { function scanAPs() {
$.get('http://'+_root+w.scan_url, onScan); $.get('http://'+_root+'/cfg/wifi/scan', onScan);
} }
function rescan(time) { function rescan(time) {
@@ -146,12 +130,6 @@
/** Set up the WiFi page */ /** Set up the WiFi page */
function wifiInit(cfg) { 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 // Update slider value displays
$('.Row.range').forEach(function(x) { $('.Row.range').forEach(function(x) {
var inp = x.querySelector('input'); var inp = x.querySelector('input');
+73 -50
View File
@@ -4,70 +4,68 @@ return [
'appname' => 'ESPTerm', 'appname' => 'ESPTerm',
'menu.cfg_wifi' => 'WiFi Settings', 'menu.cfg_wifi' => 'WiFi Settings',
'menu.cfg_network' => 'Network Configuration', 'menu.cfg_network' => 'Network Settings',
'menu.cfg_app' => 'Terminal Settings', 'menu.cfg_term' => 'Terminal Settings',
'menu.about' => 'About ESPTerm', 'menu.about' => 'About ESPTerm',
'menu.help' => 'Quick Reference', 'menu.help' => 'Quick Reference',
'menu.term' => 'Back to Terminal', 'menu.term' => 'Back to Terminal',
'menu.cfg_admin' => 'Reset & Restore', 'menu.cfg_system' => 'System Settings',
'menu.cfg_wifi_conn' => 'Connecting to External Network', 'menu.cfg_wifi_conn' => 'Connecting to Network',
'menu.settings' => 'Settings', 'menu.settings' => 'Settings',
'title.term' => 'Terminal', 'title.term' => 'Terminal',
'term_nav.config' => 'Config',
'term_nav.wifi' => 'WiFi',
'term_nav.help' => 'Help',
'term_nav.about' => 'About',
'net.ap' => 'DHCP Server (AP)', 'net.ap' => 'DHCP Server (AP)',
'net.sta' => 'DHCP Client (Station)', 'net.sta' => 'DHCP Client (Station)',
'net.sta_mac' => 'Station MAC', 'net.sta_mac' => 'Station MAC',
'net.ap_mac' => 'AP MAC', 'net.ap_mac' => 'AP MAC',
'net.details' => 'MAC addresses', 'net.details' => 'MAC addresses',
'app.defaults' => 'Initial settings', 'term.defaults' => 'Initial Settings',
'app.explain_initials' => ' 'term.explain_initials' => '
Those are the initial settings used after ESPTerm restarts, and they Those are the initial settings used after ESPTerm powers on.
will also be applied immediately after you submit this form. The selected colors will also be used after receiving a
They can be subsequently changed by ESC commands, but those changes screen- or attributes-reset command. Those settings will be
aren\'t persistent and will be lost when the device powers off.', applied immediately after saving to preview the changes live.',
'app.term_title' => 'Header text', 'term.example' => 'Default colors preview',
'app.term_width' => 'Screen width',
'app.term_height' => 'Screen height',
'app.default_fg' => 'Base text color',
'app.default_bg' => 'Base background',
'app.btn1' => 'Button 1 text',
'app.btn2' => 'Button 2 text',
'app.btn3' => 'Button 3 text',
'app.btn4' => 'Button 4 text',
'app.btn5' => 'Button 5 text',
'term.term_title' => 'Header text',
'term.term_width' => 'Width / height',
'term.default_fg_bg' => 'Text / background',
'term.buttons' => 'Button labels',
'term.theme' => 'Color scheme',
'term.parser_tout_ms' => 'Parser timeout',
// terminal color labels
'color.0' => 'Black', 'color.0' => 'Black',
'color.1' => 'Dark Red', 'color.1' => 'Red',
'color.2' => 'Dark Green', 'color.2' => 'Green',
'color.3' => 'Dim Yellow', 'color.3' => 'Yellow',
'color.4' => 'Deep Blue', 'color.4' => 'Blue',
'color.5' => 'Dark Violet', 'color.5' => 'Purple',
'color.6' => 'Dark Cyan', 'color.6' => 'Cyan',
'color.7' => 'Silver', 'color.7' => 'Silver',
'color.8' => 'Gray', 'color.8' => 'Gray',
'color.9' => 'Light Red', 'color.9' => 'Light Red',
'color.10' => 'Light Green', 'color.10' => 'Light Green',
'color.11' => 'Light Yellow', 'color.11' => 'Light Yellow',
'color.12' => 'Light Blue', 'color.12' => 'Light Blue',
'color.13' => 'Light Violet', 'color.13' => 'Light Purple',
'color.14' => 'Light Cyan', 'color.14' => 'Light Cyan',
'color.15' => 'White', 'color.15' => 'White',
'net.explain_sta' => ' 'net.explain_sta' => '
Those settings affect the built-in DHCP client used for Switch off Dynamic IP to configure the static IP address.',
connecting to an external network. Switching DHCP (dynamic IP) off
makes ESPTerm use the configured static IP. Please double-check
those settings before submitting, setting them incorrectly may
make it hard to access ESPTerm via the external network.',
'net.explain_ap' => ' 'net.explain_ap' => '
Those settings affect the built-in DHCP server in AP mode. Those settings affect the built-in DHCP server in AP mode.',
Please double-check those settings before submitting, setting them
incorrectly may render ESPTerm inaccessible via the AP.',
'net.ap_dhcp_time' => 'Lease time', 'net.ap_dhcp_time' => 'Lease time',
'net.ap_dhcp_start' => 'Pool start IP', 'net.ap_dhcp_start' => 'Pool start IP',
@@ -81,7 +79,7 @@ return [
'net.sta_addr_gw' => 'Gateway IP', 'net.sta_addr_gw' => 'Gateway IP',
'wifi.ap' => 'Built-in Access Point', 'wifi.ap' => 'Built-in Access Point',
'wifi.sta' => 'Connect to External Network', 'wifi.sta' => 'Join Existing Network',
'wifi.enable' => 'Enabled', 'wifi.enable' => 'Enabled',
'wifi.tpw' => 'Transmit power', 'wifi.tpw' => 'Transmit power',
@@ -99,36 +97,61 @@ return [
'wifi.sta_password' => 'Password:', 'wifi.sta_password' => 'Password:',
'wifi.scanning' => 'Scanning', 'wifi.scanning' => 'Scanning',
'wifi.scan_now' => 'Start scanning!', 'wifi.scan_now' => 'Click here to start scanning!',
'wifi.cant_scan_no_sta' => 'Can\'t scan with Client mode disabled.', 'wifi.cant_scan_no_sta' => 'Click here to enable client mode and start scanning!',
'wifi.select_ssid' => 'Available networks:', 'wifi.select_ssid' => 'Available networks:',
'wifi.enter_passwd' => 'Enter password for ":ssid:"',
'wifi.sta_explain' => 'After selecting a network, press Apply to connect.',
'wifi.conn.status' => 'Status:', 'wifi.conn.status' => 'Status:',
'wifi.conn.back_to_config' => 'Back to WiFi config', 'wifi.conn.back_to_config' => 'Back to WiFi config',
'wifi.conn.telemetry_lost' => 'Telemetry lost, something went wrong. Try again...', 'wifi.conn.telemetry_lost' => 'Telemetry lost; something went wrong, or your device disconnected.',
'wifi.conn.explain_android_sucks' => '
If you\'re configuring ESPTerm via a smartphone, or were connected
from another external network, your device may lose connection and this
progress indicator won\'t work. Please wait a while (~ 15 seconds),
then check if the connection succeeded.',
'wifi.conn.explain_reset' => '
To force enable the built-in AP, hold the BOOT
button until the blue LED starts flashing. Hold the button longer (until the LED
flashes rapidly) for a "factory reset".',
'wifi.conn.disabled' =>"Station mode is disabled.", 'wifi.conn.disabled' =>"Station mode is disabled.",
'wifi.conn.idle' =>"Idle, not connected and with no IP.", 'wifi.conn.idle' =>"Idle, not connected and has no IP.",
'wifi.conn.success' => "Connected! Received IP ", 'wifi.conn.success' => "Connected! Received IP ",
'wifi.conn.working' => "Connecting to selected AP", 'wifi.conn.working' => "Connecting to selected AP",
'wifi.conn.fail' => "Connection failed, check settings & try again. Cause: ", 'wifi.conn.fail' => "Connection failed, check settings & try again. Cause: ",
'admin.confirm_restore' => 'Restore all settings to their default values?', 'system.save_restore' => 'Save & Restore',
'admin.confirm_restore_hard' => 'system.confirm_restore' => 'Restore all settings to their default values?',
'system.confirm_restore_hard' =>
'Restore to firmware default settings? This will reset ' . 'Restore to firmware default settings? This will reset ' .
'all active settings and switch to AP mode with the default SSID.', 'all active settings and switch to AP mode with the default SSID.',
'admin.confirm_store_defaults' => 'system.confirm_store_defaults' =>
'Enter admin password to confirm you want to store the current settings as defaults.', 'Enter admin password to confirm you want to store the current settings as defaults.',
'admin.password' => 'Admin password:', 'system.password' => 'Admin password:',
'admin.restore_defaults' => 'Reset to default settings', 'system.restore_defaults' => 'Reset active settings to defaults',
'admin.write_defaults' => 'Save current settings as default', 'system.write_defaults' => 'Save active settings as defaults',
'admin.restore_hard' => 'Reset to firmware default settings', 'system.restore_hard' => 'Reset active settings to firmware defaults',
'admin.explain' => ' 'system.explain_persist' => '
ESPTerm contains two persistent memory banks, one for default and ESPTerm contains two persistent memory banks, one for default and
one for active settings. Active settings can be stored as defaults one for active settings. Active settings can be stored as defaults
by the administrator. Use the following button to revert all by the administrator (password required).
active settings to their stored default values.
', ',
'system.uart' => 'Serial Port',
'system.explain_uart' => '
This form controls the primary, communication UART. The debug UART is fixed at 115.200 baud, one stop-bit and no parity.
',
'uart.baud' => 'Baud rate',
'uart.parity' => 'Parity',
'uart.parity.none' => 'None',
'uart.parity.odd' => 'Odd',
'uart.parity.even' => 'Even',
'uart.stop_bits' => 'Stop-bits',
'uart.stop_bits.one' => 'One',
'uart.stop_bits.one_and_half' => 'One and half',
'uart.stop_bits.two' => 'Two',
'apply' => 'Apply!', 'apply' => 'Apply!',
'enabled' => 'Enabled', 'enabled' => 'Enabled',
+1 -1
View File
@@ -20,7 +20,7 @@
<table> <table>
<tr> <tr>
<th>ESPTerm</th> <th>ESPTerm</th>
<td>v%vers_fw%, build %date% at %time%</td> <td>v%vers_fw%, built %date% at %time%</td>
</tr> </tr>
<tr> <tr>
<th>libesphttpd</th> <th>libesphttpd</th>
-34
View File
@@ -1,34 +0,0 @@
<div class="Box">
<div class="Row explain">
<?= tr('admin.explain') ?>
</div>
<div class="Row buttons">
<a class="button icn-restore"
onclick="return confirm('<?= tr('admin.confirm_restore') ?>');"
href="<?= e(url('restore_defaults')) ?>"
>
<?= tr('admin.restore_defaults') ?>
</a>
</div>
<div class="Row buttons">
<a onclick="writeDefaults(); return false;" href="#"><?= tr('admin.write_defaults') ?></a>
</div>
<div class="Row buttons">
<a onclick="return confirm('<?= tr('admin.confirm_restore_hard') ?>');"
href="<?= e(url('restore_hard')) ?>"
>
<?= tr('admin.restore_hard') ?>
</a>
</div>
</div>
<script>
function writeDefaults() {
var pw = prompt('<?= tr('admin.confirm_store_defaults') ?>');
if (!pw) return;
location.href = <?=json_encode(url('write_defaults')) ?> + '?pw=' + pw;
}
</script>
-71
View File
@@ -1,71 +0,0 @@
<form class="Box mobopen str" action="<?= e(url('app_set')) ?>" method="GET" id='form-1'>
<h2><?= tr('app.defaults') ?></h2>
<div class="Row explain">
<?= tr('app.explain_initials') ?>
</div>
<div class="Row">
<label for="term_width"><?= tr('app.term_width') ?></label>
<input type="number" step=1 min=1 max=255 name="term_width" id="term_width" value="%term_width%" required>
</div>
<div class="Row">
<label for="term_height"><?= tr('app.term_height') ?></label>
<input type="number" step=1 min=1 max=255 name="term_height" id="term_height" value="%term_height%" required>
</div>
<div class="Row">
<label for="default_fg"><?= tr("app.default_fg") ?></label>
<select name="default_fg" id="default_fg">
<?php for($i=0; $i<16; $i++): ?>
<option value="<?=$i?>"><?= tr("color.$i") ?></option>
<?php endfor; ?>
</select>
<script>qs('#default_fg').selectedIndex = %default_fg%;</script>
</div>
<div class="Row">
<label for="default_bg"><?= tr("app.default_bg") ?></label>
<select name="default_bg" id="default_bg">
<?php for($i=0; $i<16; $i++): ?>
<option value="<?=$i?>"><?= tr("color.$i") ?></option>
<?php endfor; ?>
</select>
<script>qs('#default_bg').selectedIndex = %default_bg%;</script>
</div>
<div class="Row">
<label for="term_title"><?= tr('app.term_title') ?></label>
<input type="text" name="term_title" id="term_title" value="%term_title%" required>
</div>
<div class="Row">
<label for="btn1"><?= tr("app.btn1") ?></label>
<input class="short" type="text" name="btn1" id="btn1" value="%btn1%">
</div>
<div class="Row">
<label for="btn2"><?= tr("app.btn2") ?></label>
<input class="short" type="text" name="btn2" id="btn2" value="%btn2%">
</div>
<div class="Row">
<label for="btn3"><?= tr("app.btn3") ?></label>
<input class="short" type="text" name="btn3" id="btn3" value="%btn3%">
</div>
<div class="Row">
<label for="btn4"><?= tr("app.btn4") ?></label>
<input class="short" type="text" name="btn4" id="btn4" value="%btn4%">
</div>
<div class="Row">
<label for="btn5"><?= tr("app.btn5") ?></label>
<input class="short" type="text" name="btn5" id="btn5" value="%btn5%">
</div>
<div class="Row buttons">
<a class="button icn-ok" href="#" onclick="qs('#form-1').submit()"><?= tr('apply') ?></a>
</div>
</form>
+24 -21
View File
@@ -12,22 +12,22 @@ $ipmask='pattern="^([0-9]{1,3}\.){3}[0-9]{1,3}$"';
<div class="Row checkbox x-static-toggle" > <div class="Row checkbox x-static-toggle" >
<label><?= tr('net.sta_dhcp_enable') ?></label><!-- <label><?= tr('net.sta_dhcp_enable') ?></label><!--
--><span class="box" tabindex=0 role=checkbox></span> --><span class="box" tabindex=0 role=checkbox></span>
<input type="hidden" name="sta_dhcp_enable" value="%sta_dhcp_enable%"> <input type="hidden" id="sta_dhcp_enable" name="sta_dhcp_enable" value="%sta_dhcp_enable%">
</div> </div>
<div class="Row x-static"> <div class="Row x-static">
<label for="sta_addr_ip"><?= tr('net.sta_addr_ip') ?></label> <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> <input type="text" name="sta_addr_ip" id="sta_addr_ip" value="%h:sta_addr_ip%" <?=$ipmask?> required>
</div> </div>
<div class="Row x-static"> <div class="Row x-static">
<label for="sta_addr_mask"><?= tr('net.sta_addr_mask') ?></label> <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> <input type="text" name="sta_addr_mask" id="sta_addr_mask" value="%h:sta_addr_mask%" <?=$ipmask?> required>
</div> </div>
<div class="Row x-static"> <div class="Row x-static">
<label for="sta_addr_gw"><?= tr('net.sta_addr_gw') ?></label> <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> <input type="text" name="sta_addr_gw" id="sta_addr_gw" value="%h:sta_addr_gw%" <?=$ipmask?> required>
</div> </div>
<div class="Row buttons"> <div class="Row buttons">
@@ -43,29 +43,29 @@ $ipmask='pattern="^([0-9]{1,3}\.){3}[0-9]{1,3}$"';
</div> </div>
<div class="Row"> <div class="Row">
<label for="ap_dhcp_time"><?= tr('net.ap_dhcp_time') ?><span class="mq-phone">&nbsp;(min)</span></label> <label for="ap_addr_mask"><?= tr('net.ap_addr_mask') ?></label>
<input type="number" step=1 min=1 max=2880 name="ap_dhcp_time" id="ap_dhcp_time" value="%ap_dhcp_time%" required> <input type="text" name="ap_addr_mask" id="ap_addr_mask" value="%h:ap_addr_mask%" <?=$ipmask?> required>
<span class="mq-no-phone">&nbsp;(min)</span>
</div>
<div class="Row">
<label for="ap_dhcp_start"><?= tr('net.ap_dhcp_start') ?></label>
<input type="text" name="ap_dhcp_start" id="ap_dhcp_start" value="%ap_dhcp_start%" <?=$ipmask?> required>
</div>
<div class="Row">
<label for="ap_dhcp_end"><?= tr('net.ap_dhcp_end') ?></label>
<input type="text" name="ap_dhcp_end" id="ap_dhcp_end" value="%ap_dhcp_end%" <?=$ipmask?> required>
</div> </div>
<div class="Row"> <div class="Row">
<label for="ap_addr_ip"><?= tr('net.ap_addr_ip') ?></label> <label for="ap_addr_ip"><?= tr('net.ap_addr_ip') ?></label>
<input type="text" name="ap_addr_ip" id="ap_addr_ip" value="%ap_addr_ip%" <?=$ipmask?> required> <input type="text" name="ap_addr_ip" id="ap_addr_ip" value="%h:ap_addr_ip%" <?=$ipmask?> required>
</div> </div>
<div class="Row"> <div class="Row">
<label for="ap_addr_mask"><?= tr('net.ap_addr_mask') ?></label> <label for="ap_dhcp_start"><?= tr('net.ap_dhcp_start') ?></label>
<input type="text" name="ap_addr_mask" id="ap_addr_mask" value="%ap_addr_mask%" <?=$ipmask?> required> <input type="text" name="ap_dhcp_start" id="ap_dhcp_start" value="%h:ap_dhcp_start%" <?=$ipmask?> required>
</div>
<div class="Row">
<label for="ap_dhcp_end"><?= tr('net.ap_dhcp_end') ?></label>
<input type="text" name="ap_dhcp_end" id="ap_dhcp_end" value="%h:ap_dhcp_end%" <?=$ipmask?> required>
</div>
<div class="Row">
<label for="ap_dhcp_time"><?= tr('net.ap_dhcp_time') ?><span class="mq-phone">&nbsp;(min)</span></label>
<input type="number" step=1 min=1 max=2880 name="ap_dhcp_time" id="ap_dhcp_time" value="%ap_dhcp_time%" required>
<span class="mq-no-phone">&nbsp;min</span>
</div> </div>
<div class="Row buttons"> <div class="Row buttons">
@@ -86,10 +86,13 @@ $ipmask='pattern="^([0-9]{1,3}\.){3}[0-9]{1,3}$"';
<script> <script>
function updateStaticDisp() { function updateStaticDisp() {
$('.x-static').toggleClass('hidden', $('#sta_dhcp_enable').val()); var sttc = !parseInt($('#sta_dhcp_enable').val());
$('.x-static').toggleClass('hidden', !sttc);
} }
$('.x-static-toggle').on('click', function() { $('.x-static-toggle').on('click', function() {
setTimeout(function() {
updateStaticDisp(); updateStaticDisp();
}, 0)
}); });
updateStaticDisp(); updateStaticDisp();
</script> </script>
+90
View File
@@ -0,0 +1,90 @@
<div class="Box str mobcol">
<h2 tabindex=0><?= tr('system.save_restore') ?></h2>
<div class="Row explain nomargintop">
<?= tr('system.explain_persist') ?>
</div>
<div class="Row buttons2">
<a class="button icn-restore"
onclick="return confirm('<?= tr('system.confirm_restore') ?>');"
href="<?= e(url('restore_defaults')) ?>">
<?= tr('system.restore_defaults') ?>
</a>
</div>
<div class="Row buttons2">
<a onclick="writeDefaults(); return false;" href="#"><?= tr('system.write_defaults') ?></a>
</div>
<div class="Row buttons2">
<a onclick="return confirm('<?= tr('system.confirm_restore_hard') ?>');"
href="<?= e(url('restore_hard')) ?>">
<?= tr('system.restore_hard') ?>
</a>
</div>
</div>
<form class="Box str mobcol" action="<?= e(url('system_set')) ?>" method="GET" id="form-1">
<h2 tabindex=0><?= tr('system.uart') ?></h2>
<div class="Row explain">
<?= tr('system.explain_uart') ?>
</div>
<div class="Row">
<label for="uart_baud"><?= tr('uart.baud') ?><span class="mq-phone">&nbsp;(bps)</span></label>
<select name="uart_baud" id="uart_baud" class="short">
<?php foreach([
300, 600, 1200, 2400, 4800, 9600, 19200, 38400,
57600, 74880, 115200, 230400, 460800, 921600, 1843200, 3686400,
] as $b):
?><option value="<?=$b?>"><?= number_format($b, 0, ',', '.') ?></option>
<?php endforeach; ?>
</select>
<span class="mq-no-phone">&nbsp;bps</span>
</div>
<div class="Row">
<label for="uart_parity"><?= tr('uart.parity') ?></label>
<select name="uart_parity" id="uart_parity" class="short">
<?php foreach([
2 => tr('uart.parity.none'),
1 => tr('uart.parity.odd'),
0 => tr('uart.parity.even'),
] as $k => $label):
?><option value="<?=$k?>"><?=$label?></option>
<?php endforeach; ?>
</select>
</div>
<div class="Row">
<label for="uart_stopbits"><?= tr('uart.stop_bits') ?></label>
<select name="uart_stopbits" id="uart_stopbits" class="short">
<?php foreach([
1 => tr('uart.stop_bits.one'),
2 => tr('uart.stop_bits.one_and_half'),
3 => tr('uart.stop_bits.two'),
] as $k => $label):
?><option value="<?=$k?>"><?=$label?></option>
<?php endforeach; ?>
</select>
</div>
<div class="Row buttons">
<a class="button icn-ok" href="#" onclick="qs('#form-1').submit()"><?= tr('apply') ?></a>
</div>
</form>
<script>
function writeDefaults() {
var pw = prompt('<?= tr('system.confirm_store_defaults') ?>');
if (!pw) return;
location.href = <?=json_encode(url('write_defaults')) ?> + '?pw=' + pw;
}
$('#uart_baud').val(%uart_baud%);
$('#uart_parity').val(%uart_parity%);
$('#uart_stopbits').val(%uart_stopbits%);
</script>
+142
View File
@@ -0,0 +1,142 @@
<form class="Box mobopen str" action="<?= e(url('term_set')) ?>" method="GET" id='form-1'>
<h2><?= tr('term.defaults') ?></h2>
<div class="Row explain">
<?= tr('term.explain_initials') ?>
</div>
<div class="Row">
<label for="theme"><?= tr("term.theme") ?></label>
<select name="theme" id="theme" class="short" onchange="showColor()">
<option value="0">Tango</option>
<option value="1">Linux</option>
<option value="2">XTerm</option>
<option value="3">Rxvt</option>
<option value="4">Ambience</option>
<option value="5">Solarized</option>
</select>
</div>
<div class="Row color-preview">
<div class="colorprev">
<span data-fg=0 class="bg0 fg0">30</span><!--
--><span data-fg=1 class="bg0 fg1">31</span><!--
--><span data-fg=2 class="bg0 fg2">32</span><!--
--><span data-fg=3 class="bg0 fg3">33</span><!--
--><span data-fg=4 class="bg0 fg4">34</span><!--
--><span data-fg=5 class="bg0 fg5">35</span><!--
--><span data-fg=6 class="bg0 fg6">36</span><!--
--><span data-fg=7 class="bg0 fg7">37</span>
</div>
<div class="colorprev">
<span data-fg=8 class="bg0 fg8">90</span><!--
--><span data-fg=9 class="bg0 fg9">91</span><!--
--><span data-fg=10 class="bg0 fg10">92</span><!--
--><span data-fg=11 class="bg0 fg11">93</span><!--
--><span data-fg=12 class="bg0 fg12">94</span><!--
--><span data-fg=13 class="bg0 fg13">95</span><!--
--><span data-fg=14 class="bg0 fg14">96</span><!--
--><span data-fg=15 class="bg0 fg15">97</span>
</div>
<div class="colorprev">
<span data-bg=0 class="bg0 fg15">40</span><!--
--><span data-bg=1 class="bg1 fg15">41</span><!--
--><span data-bg=2 class="bg2 fg15">42</span><!--
--><span data-bg=3 class="bg3 fg0">43</span><!--
--><span data-bg=4 class="bg4 fg15">44</span><!--
--><span data-bg=5 class="bg5 fg15">45</span><!--
--><span data-bg=6 class="bg6 fg15">46</span><!--
--><span data-bg=7 class="bg7 fg0">47</span>
</div>
<div class="colorprev">
<span data-bg=8 class="bg8 fg15">100</span><!--
--><span data-bg=9 class="bg9 fg0">101</span><!--
--><span data-bg=10 class="bg10 fg0">102</span><!--
--><span data-bg=11 class="bg11 fg0">103</span><!--
--><span data-bg=12 class="bg12 fg0">104</span><!--
--><span data-bg=13 class="bg13 fg0">105</span><!--
--><span data-bg=14 class="bg14 fg0">106</span><!--
--><span data-bg=15 class="bg15 fg0">107</span>
</div>
</div>
<div class="Row color-preview">
<div style="
" id="color-example">
<?= tr("term.example") ?>
</div>
</div>
<div class="Row">
<label><?= tr("term.default_fg_bg") ?></label>
<select name="default_fg" id="default_fg" class="short" onchange="showColor()">
<?php for($i=0; $i<16; $i++): ?>
<option value="<?=$i?>"><?= tr("color.$i") ?></option>
<?php endfor; ?>
</select>&nbsp;<!--
--><select name="default_bg" id="default_bg" class="short" onchange="showColor()">
<?php for($i=0; $i<16; $i++): ?>
<option value="<?=$i?>"><?= tr("color.$i") ?></option>
<?php endfor; ?>
</select>
</div>
<div class="Row">
<label for="term_width"><?= tr('term.term_width') ?></label>
<input type="number" step=1 min=1 max=255 name="term_width" id="term_width" value="%term_width%" required>&nbsp;<!--
--><input type="number" step=1 min=1 max=255 name="term_height" id="term_height" value="%term_height%" required>
</div>
<div class="Row">
<label for="term_title"><?= tr('term.term_title') ?></label>
<input type="text" name="term_title" id="term_title" value="%h:term_title%" required>
</div>
<div class="Row">
<label><?= tr("term.buttons") ?></label>
<input class="short" type="text" name="btn1" id="btn1" value="%h:btn1%">&nbsp;
<input class="short" type="text" name="btn2" id="btn2" value="%h:btn2%">&nbsp;
<input class="short" type="text" name="btn3" id="btn3" value="%h:btn3%">&nbsp;
<input class="short" type="text" name="btn4" id="btn4" value="%h:btn4%">&nbsp;
<input class="short" type="text" name="btn5" id="btn5" value="%h:btn5%">
</div>
<div class="Row">
<label for="parser_tout_ms"><?= tr('term.parser_tout_ms') ?><span class="mq-phone">&nbsp;(ms)</span></label>
<input type="number" step=1 min=0 name="parser_tout_ms" id="parser_tout_ms" value="%parser_tout_ms%" required>
<span class="mq-no-phone">&nbsp;ms</span>
</div>
<div class="Row buttons">
<a class="button icn-ok" href="#" onclick="qs('#form-1').submit()"><?= tr('apply') ?></a>
</div>
</form>
<script>
$('#default_fg').val(%default_fg%);
$('#default_bg').val(%default_bg%);
$('#theme').val(%theme%);
function showColor() {
var ex = qs('#color-example');
ex.className = '';
ex.classList.add('fg'+$('#default_fg').val());
ex.classList.add('bg'+$('#default_bg').val());
var th = $('#theme').val();
$('.color-preview').forEach(function(e) {
e.className = 'Row color-preview theme-'+th;
});
}
showColor();
$('.colorprev span').on('click', function() {
var fg = this.dataset.fg;
var bg = this.dataset.bg;
if (typeof fg != 'undefined') $('#default_fg').val(fg);
if (typeof bg != 'undefined') $('#default_bg').val(bg);
showColor()
});
</script>
+46 -31
View File
@@ -1,28 +1,28 @@
<form class="Box str mobcol" action="<?= e(url('wifi_set')) ?>" method="GET" id="form-1"> <form class="Box str mobcol" action="<?= e(url('wifi_set')) ?>" method="GET" id="form-1">
<h2 tabindex=0><?= tr('wifi.ap') ?></h2> <h2 tabindex=0><?= tr('wifi.ap') ?></h2>
<div class="Row checkbox"> <div class="Row checkbox x-ap-toggle">
<label><?= tr('wifi.enable') ?></label><!-- <label><?= tr('wifi.enable') ?></label><!--
--><span class="box" tabindex=0></span> --><span class="box" tabindex=0></span>
<input type="hidden" name="ap_enable" value="%ap_enable%"> <input type="hidden" id="ap_enabled" name="ap_enable" value="%ap_enable%">
</div> </div>
<div class="Row"> <div class="Row x-ap-on">
<label for="ap_ssid"><?= tr('wifi.ap_ssid') ?></label> <label for="ap_ssid"><?= tr('wifi.ap_ssid') ?></label>
<input type="text" name="ap_ssid" id="ap_ssid" value="%ap_ssid%" required> <input type="text" name="ap_ssid" id="ap_ssid" value="%h:ap_ssid%" required>
</div> </div>
<div class="Row"> <div class="Row x-ap-on">
<label for="ap_password"><?= tr('wifi.ap_password') ?></label> <label for="ap_password"><?= tr('wifi.ap_password') ?></label>
<input type="text" name="ap_password" id="ap_password" value="%ap_password%"> <input type="text" name="ap_password" id="ap_password" value="%h:ap_password%">
</div> </div>
<div class="Row"> <div class="Row x-ap-on">
<label for="ap_channel"><?= tr('wifi.ap_channel') ?></label> <label for="ap_channel"><?= tr('wifi.ap_channel') ?></label>
<input type="number" name="ap_channel" id="ap_channel" min=1 max=14 value="%ap_channel%" required> <input type="number" name="ap_channel" id="ap_channel" min=1 max=14 value="%ap_channel%" required>
</div> </div>
<div class="Row range"> <div class="Row range x-ap-on">
<label for="tpw"> <label for="tpw">
<?= tr('wifi.tpw') ?> <?= tr('wifi.tpw') ?>
<span class="display x-disp1 mq-phone"></span> <span class="display x-disp1 mq-phone"></span>
@@ -31,7 +31,7 @@
<span class="display x-disp2 mq-no-phone"></span> <span class="display x-disp2 mq-no-phone"></span>
</div> </div>
<div class="Row checkbox"> <div class="Row checkbox x-ap-on">
<label><?= tr('wifi.ap_hidden') ?></label><!-- <label><?= tr('wifi.ap_hidden') ?></label><!--
--><span class="box" tabindex=0></span> --><span class="box" tabindex=0></span>
<input type="hidden" name="ap_hidden" value="%ap_hidden%"> <input type="hidden" name="ap_hidden" value="%ap_hidden%">
@@ -42,19 +42,24 @@
</div> </div>
</form> </form>
<form class="Box str mobcol" action="<?= e(url('wifi_set')) ?>" method="GET" id="form-2"> <form class="Box str mobcol expanded" action="<?= e(url('wifi_set')) ?>" method="GET" id="form-2">
<h2 tabindex=0><?= tr('wifi.sta') ?></h2> <h2 tabindex=0><?= tr('wifi.sta') ?></h2>
<div class="Row checkbox"> <div class="Row checkbox x-sta-toggle">
<label><?= tr('wifi.enable') ?></label><!-- <label><?= tr('wifi.enable') ?></label><!--
--><span class="box" tabindex=0></span> --><span class="box" tabindex=0></span>
<input type="hidden" name="sta_enable" value="%sta_enable%"> <input type="hidden" id="sta_enabled" name="sta_enable" value="%sta_enable%">
</div>
<div class="Row explain nomargintop x-sta-on">
<span class="spacer"></span>
<?= tr("wifi.sta_explain") ?>
</div> </div>
<input type="hidden" name="sta_ssid" id="sta_ssid" value=""> <input type="hidden" name="sta_ssid" id="sta_ssid" value="">
<input type="hidden" name="sta_password" id="sta_password" value=""> <input type="hidden" name="sta_password" id="sta_password" value="">
<div class="Row sta-info"> <div class="Row sta-info x-sta-on">
<label><?= tr('wifi.sta_info') ?></label> <label><?= tr('wifi.sta_info') ?></label>
<div class="AP-preview hidden" id="sta-nw"> <div class="AP-preview hidden" id="sta-nw">
<div class="wrap"> <div class="wrap">
@@ -72,11 +77,10 @@
</div> </div>
</div> </div>
<div id="ap-box"> <div id="ap-box" class="x-sta-on">
<label><?= tr('wifi.select_ssid') ?></label> <label><?= tr('wifi.select_ssid') ?></label>
<div id="ap-scan"><a href="#" onclick="WiFi.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-list" class="hidden"></div> <div id="ap-list" class="hidden"></div>
</div> </div>
@@ -85,24 +89,35 @@
</div> </div>
</form> </form>
<div class="Modal hidden" id="psk-modal">
<div class="Dialog">
<form id="conn-form" onsubmit="submitPskModal(); return false;">
<input type="hidden" id="conn-ssid"><!--
--><label for="conn-passwd"><?= tr('wifi.sta_password') ?></label><!--
--><input type="text" id="conn-passwd"><!--
--><input type="submit" value="<?= tr('confirm') ?>">
</form>
</div>
</div>
<script> <script>
WiFi.scan_url = '<?= url('wifi_scan', true) ?>'; WiFi.scan_url = '<?= url('wifi_scan', true) ?>';
WiFi.init({ WiFi.init({
mode: '%opmode%', sta_ssid: '%j:sta_ssid%',
sta_ssid: '%sta_ssid%', sta_password: '%j:sta_password%',
sta_password: '%sta_password%', sta_active_ip: '%j:sta_active_ip%',
sta_active_ip: '%sta_active_ip%', sta_active_ssid: '%j:sta_active_ssid%',
sta_active_ssid: '%sta_active_ssid%',
}); });
function updateApDisp() {
var a = !!parseInt($('#ap_enabled').val());
$('.x-ap-on').toggleClass('hidden', !a);
}
$('.x-ap-toggle').on('click', function() {
setTimeout(function() {
updateApDisp();
}, 0)
});
function updateStaDisp() {
var a = !!parseInt($('#sta_enabled').val());
$('.x-sta-on').toggleClass('hidden', !a);
}
$('.x-sta-toggle').on('click', function() {
setTimeout(function() {
updateStaDisp();
}, 0)
});
updateApDisp();
updateStaDisp();
</script> </script>
+42 -6
View File
@@ -5,9 +5,15 @@
<a href="<?= e(url('cfg_wifi')) ?>" id="backbtn" class="button"><?= tr('wifi.conn.back_to_config') ?></a> <a href="<?= e(url('cfg_wifi')) ?>" id="backbtn" class="button"><?= tr('wifi.conn.back_to_config') ?></a>
</div> </div>
<div class="Box">
<p><?= tr('wifi.conn.explain_android_sucks') ?></p>
<p><?= tr('wifi.conn.explain_reset') ?></p>
</div>
<script> <script>
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
var abortTmeo; var abortTmeo;
var failCounter = 0;
var messages = <?= json_encode([ var messages = <?= json_encode([
'disabled' => tr('wifi.conn.disabled'), 'disabled' => tr('wifi.conn.disabled'),
@@ -17,16 +23,32 @@
'fail' => tr('wifi.conn.fail'), 'fail' => tr('wifi.conn.fail'),
]) ?>; ]) ?>;
function onFail() {
$("#status").html(<?= json_encode(tr('wifi.conn.telemetry_lost')) ?>);
$('.anim-dots').addClass('hidden');
}
function getStatus() { function getStatus() {
xhr.open("GET", 'http://'+_root+'<?= url('wifi_connstatus', true) ?>'); xhr.open("GET", 'http://'+_root+'<?= url('wifi_connstatus', true) ?>');
xhr.onreadystatechange = function () { xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status >= 200 && xhr.status < 300) { if (xhr.readyState == 4) {
if (xhr.status == 200) {
clearTimeout(abortTmeo); clearTimeout(abortTmeo);
try {
var data = JSON.parse(xhr.responseText); var data = JSON.parse(xhr.responseText);
var done = false; var done = false;
var msg = messages[data.status] || '...'; var msg = messages[data.status] || '...';
if (data.status == 'success') msg += data.ip;
if (data.status == 'fail') msg += data.cause; if (data.status == 'success') {
msg += data.ip;
done = true;
}
if (data.status == 'fail') {
msg += data.cause;
done = true;
}
$("#status").html(msg); $("#status").html(msg);
@@ -34,16 +56,30 @@
// $('#backbtn').removeClass('hidden'); // $('#backbtn').removeClass('hidden');
$('.anim-dots').addClass('hidden'); $('.anim-dots').addClass('hidden');
} else { } else {
// ask again after a short delay
window.setTimeout(getStatus, 1000); window.setTimeout(getStatus, 1000);
} }
} catch(e) {
failCounter++;
console.log(e);
// repeat
if (failCounter > 5) {
onFail();
}
else {
window.setTimeout(getStatus, 1000);
}
}
} else {
onFail();
}
} }
}; };
// XHR timeout
abortTmeo = setTimeout(function () { abortTmeo = setTimeout(function () {
xhr.abort(); xhr.abort();
$("#status").html(<?= json_encode(tr('wifi.conn.telemetry_lost')) ?>); onFail();
// $('#backbtn').removeClass('hidden');
$('.anim-dots').addClass('hidden');
}, 4000); }, 4000);
xhr.send(); xhr.send();
+81 -36
View File
@@ -1,15 +1,34 @@
<div class="Box"> <div class="Box">
<h2>Wiring</h2> <h2>Troubleshooting</h2>
<ul> <ul>
<li>Communication UART is on pins <b>Rx, Tx</b> at 115200-8-1-N. The baud rate can be changed in Terminal Settings. <li>ESPTerm communicates with external µC via UART on pins <b>Rx</b> and <b>Tx</b>.
<li>Debug log is on pin <b>GPIO2</b> (P2) at 115200-8-1-N. This baud rate is fixed. It can be configured in <a href="<?= url('cfg_system') ?>">System Settings</a>.
<li>Compatible with 3.3&nbsp;V and 5&nbsp;V logic. For 5&nbsp;V, 470&nbsp;R protection resistors are recommended. Make sure you're using the correct baud rate and other settings on the other side.
<li>If the "LVD" LED on the ESPTerm module lights up, it doesn't get enough power to run correctly. Check your connections.
<li>Connect Rx and Tx with a piece of wire to test the terminal alone, you should see what you type in the browser. <li>Boot log and debug messages are available on pin <b>GPIO2</b> (P2) at 115200\,bps, 1 stop bit, no parity.
<i>NOTE: This won't work if your ESP8266 board has a built-in USB-serial converter (like NodeMCU).</i>
<li>For best performance, use the module in Client mode (connected to external network). <li>ESPTerm uses 3.3\,V logic. For communication with 5\,V logic (eg. Aurdino), 470\,R
<li>In AP mode, check that the channel used is clear; interference may cause a flaky connection. protection resistors are recommended. The ESPTerm breakout board already includes those resistors.
<li>If the Low Voltage Detector ("LVD") LED on the ESPTerm breakout lights up, your power connections have too high
resistance or the power supply is insufficient. ESP8266 can consume in short spikes up to 500\,A and
the supply voltage on the module must not drop below about 2.7\,V. Avoid needless breadboard jumpers and too thin wires.
<li>Connect Rx and Tx with a piece of wire to verify that the terminal works correctly; you should then immediately
see what you type in the browser. <i>This won't work if your ESP8266 board has a built-in USB-serial
converter connected to those pins.</i>
<li>For best performance, use the module in Client mode (connected to external network). Enabling AP mode consumes
extra RAM and cycles, since the captive portal DNS server and DHCP server are be loaded.
<li>In AP mode, check that the channel used by ESPTerm is clear; interference may cause flaky connection. A good mobile app to use for this is <a href="https://play.google.com/store/apps/details?id=com.farproc.wifi.analyzer">WiFi Analyzer (Google Play)</a>
<li>The terminal browser display needs WebSockets for the real-time updating to work.
It will not work with some old browsers (Android < 4.4, IE < 10, Opera Mini)
- see the <a href="https://caniuse.com/websockets/embed/">compatibility chart</a>.
<li>At most 4 clients can be connected to the AP at the same time. This is a limitation of the ESP8266 SDK.
</ul> </ul>
</div> </div>
@@ -17,19 +36,22 @@
<h2>Screen</h2> <h2>Screen</h2>
<ul> <ul>
<li>Most ANSI escape sequences are supported.</li> <li>Most ANSI escape sequences are supported.
<li>The max screen size is 2000 characters (eg. <b>25x80</b>), default is <b>10x26</b>. Set using <code>\e]W&lt;rows&gt;;&lt;cols&gt;\a</code>.</li>
<li>The screen will automatically scroll, can be used for log output.</li> <li>The initial screen size and other terminal options can be configured in
<li>Display update is sent <b>after 20 ms of inactivity</b>.</li> <a href="<?= url('cfg_term') ?>">Terminal Settings</a>
<li>At most 4 clients can be connected at the same time.</li>
<li>The browser display needs WebSockets for the real-time updating. It may not work on really old phones / browsers.</li> <li>Display updates are sent to the browser after 20 ms of inactivity on the communication UART.
This is to avoid hogging the server with tiny updates during large screen repaints.
</ul> </ul>
</div> </div>
<div class="Box"> <div class="Box theme-0">
<h2>Color Reference</h2> <h2>Color Reference</h2>
<p>Color is set using <code>\e[&lt;c&gt;m</code> or <code>\e[&lt;c&gt;;&lt;c&gt;m</code> where "&lt;c&gt;" are numbers from the following tables:</p> <p>Color is set using <code>\e[&lt;c&gt;m</code> where <code>&lt;c&gt;</code> are numbers separated by semicolons
(and <code>\e</code> is ESC, code 27). The actual color representation depends on a color theme which
can be selected in <a href="<?= url('cfg_term') ?>">Terminal Settings</a>.</p>
<b>Foreground</b> <b>Foreground</b>
@@ -45,7 +67,7 @@
</div> </div>
<div class="colorprev"> <div class="colorprev">
<span class="bg7 fg8">90</span> <span class="bg0 fg8">90</span>
<span class="bg0 fg9">91</span> <span class="bg0 fg9">91</span>
<span class="bg0 fg10">92</span> <span class="bg0 fg10">92</span>
<span class="bg0 fg11">93</span> <span class="bg0 fg11">93</span>
@@ -58,18 +80,18 @@
<b>Background</b> <b>Background</b>
<div class="colorprev"> <div class="colorprev">
<span class="bg0 fg15 nb">40</span> <span class="bg0 fg15">40</span>
<span class="bg1 fg15 nb">41</span> <span class="bg1 fg15">41</span>
<span class="bg2 fg15 nb">42</span> <span class="bg2 fg15">42</span>
<span class="bg3 fg0">43</span> <span class="bg3 fg0">43</span>
<span class="bg4 fg15 nb">44</span> <span class="bg4 fg15">44</span>
<span class="bg5 fg15 nb">45</span> <span class="bg5 fg15">45</span>
<span class="bg6 fg15 nb">46</span> <span class="bg6 fg15">46</span>
<span class="bg7 fg0">47</span> <span class="bg7 fg0">47</span>
</div> </div>
<div class="colorprev"> <div class="colorprev">
<span class="bg8 fg15 nb">100</span> <span class="bg8 fg15">100</span>
<span class="bg9 fg0">101</span> <span class="bg9 fg0">101</span>
<span class="bg10 fg0">102</span> <span class="bg10 fg0">102</span>
<span class="bg11 fg0">103</span> <span class="bg11 fg0">103</span>
@@ -79,7 +101,10 @@
<span class="bg15 fg0">107</span> <span class="bg15 fg0">107</span>
</div> </div>
<p>Bright foreground can also be set using the "bold" attribute 1 (eg. <code>\e[31;1m</code>). For more details, see the ANSI code reference below.</p> <p>To make the text bold, use the "bold" attribute (eg. <code>\e[31;40;1m</code> for
<span style="padding: 2px;" class="bg0 fg1 bold">bold red on black</span>).</p>
<p>The colors are reset to default using <code>\e[0m</code>. For more info, see the tables below.</p>
</div> </div>
<div class="Box"> <div class="Box">
@@ -87,13 +112,12 @@
<p> <p>
All the user types on their keyboard is sent as-is to the UART, including ESC, ANSI sequences for arrow keys and CR-LF for Enter. All the user types on their keyboard is sent as-is to the UART, including ESC, ANSI sequences for arrow keys and CR-LF for Enter.
The input is limited to ASCII codes 32-126, backspace 8 and tab 9.
</p> </p>
<p>The buttons under the screen send ASCII codes 1, 2, 3, 4, 5.</p> <p>The buttons under the screen send ASCII codes 1, 2, 3, 4, 5. This choice was made to make their parsing as simple as possible.</p>
<p> <p>
<b>Mouse input</b> (click/tap) is sent as <code>\e[&lt;y&gt;;&lt;x&gt;M</code>. You can use this for on-screen buttons, menu navigation etc. Mouse input (click/tap) is sent as <code>\e[&lt;y&gt;;&lt;x&gt;M</code>. You can use this for virtual on-screen buttons.
</p> </p>
</div> </div>
@@ -123,6 +147,14 @@
<td>0</td> <td>0</td>
<td>Reset text attributes to default</td> <td>Reset text attributes to default</td>
</tr> </tr>
<tr>
<td>1</td>
<td>Bold</td>
</tr>
<tr>
<td>21 or 22</td>
<td>Bold off</td>
</tr>
<tr> <tr>
<td>7</td> <td>7</td>
<td>Inverse (fg/bg swap when printing)</td> <td>Inverse (fg/bg swap when printing)</td>
@@ -178,7 +210,7 @@
<tr> <tr>
<td>\e[&lt;n&gt;E</td> <td>\e[&lt;n&gt;E</td>
<td>[count]</td> <td>[count]</td>
<td>Go N line down, start of line</td> <td>Go N lines down, start of line</td>
</tr> </tr>
<tr> <tr>
<td>\e[&lt;n&gt;F</td> <td>\e[&lt;n&gt;F</td>
@@ -277,7 +309,7 @@
<tr> <tr>
<td>\e]W&lt;r&gt;;&lt;c&gt;\a</td> <td>\e]W&lt;r&gt;;&lt;c&gt;\a</td>
<td>rows;cols</td> <td>rows;cols</td>
<td>Set screen size, maximum 25x80 (resp. total 2000 characters). This also clears the screen.</td> <td>Set screen size (max ~ 80x30). This also clears the screen. This is a custom ESPTerm OSC command.</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@@ -301,16 +333,29 @@
The screen size and WiFi settings stay unchanged. The screen size and WiFi settings stay unchanged.
</td> </td>
</tr> </tr>
<tr>
<td>\e]FR\a</td>
<td>--</td>
<td>"Factory Reset", emergency code when you mess up the WiFi, restores SSID to unique default, clears stored credentials & enters Client+AP mode.</td>
</tr>
<tr> <tr>
<td>\e[5n</td> <td>\e[5n</td>
<td>--</td> <td>--</td>
<td>Query device status, replies with <code>\e[0n</code> "device is OK". Can be used to check if the UART works.</td> <td>Query device status, replies with <code>\e[0n</code> "device is OK". Can be used to check if the UART works.</td>
</tr> </tr>
<tr>
<td>\e]TITLE=…\a</td>
<td>Title text</td>
<td>Set terminal title. This is a custom ESPTerm OSC command.</td>
</tr>
<tr>
<td>\e]BTNn=…\a</td>
<td>1-5, label</td>
<td>Set button label. This is a custom ESPTerm OSC command.</td>
</tr>
<tr>
<td>ASCII 24 (18h)</td>
<td></td>
<td>This symbol is sent by ESPTerm when it becomes ready to receive commands. It can be used to wait before it becomes
ready on power-up, or to detect a spontaneous ESPTerm restart - that could happen due to RAM exhaustion due
to a memory leak, or perhaps a power outage.
</td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>
+37 -12
View File
@@ -2,36 +2,61 @@
// Workaround for badly loaded page // Workaround for badly loaded page
setTimeout(function() { setTimeout(function() {
if (typeof termInit == 'undefined' || typeof $ == 'undefined') { if (typeof termInit == 'undefined' || typeof $ == 'undefined') {
console.error("Page load failed, refreshing…");
location.reload(true); location.reload(true);
} }
}, 2000); }, 2000);
</script> </script>
<h1>%term_title%</h1> <h1></h1>
<div id="termwrap"> <div id="termwrap">
<div id="screen"></div> <div id="screen" class="theme-%theme%"></div>
<div id="buttons"> <div id="buttons">
<button data-n="1" class="btn-blue">%btn1%</button><!-- <button data-n="1" class="btn-blue"></button><!--
--><button data-n="2" class="btn-blue">%btn2%</button><!-- --><button data-n="2" class="btn-blue"></button><!--
--><button data-n="3" class="btn-blue">%btn3%</button><!-- --><button data-n="3" class="btn-blue"></button><!--
--><button data-n="4" class="btn-blue">%btn4%</button><!-- --><button data-n="4" class="btn-blue"></button><!--
--><button data-n="5" class="btn-blue">%btn5%</button> --><button data-n="5" class="btn-blue"></button>
</div> </div>
</div> </div>
<textarea id="softkb-input" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"></textarea>
<nav id="botnav"> <nav id="botnav">
<a href="<?= url('cfg_wifi') ?>"><?= tr('menu.settings') ?></a><!-- <a href="#" onclick="toggleSoftKb(true); return false" class="icn-keyboard mq-tablet-max"></a><!--
--><a href="<?= url('help') ?>">Help</a><!-- --><a href="<?= url('cfg_term') ?>"><?= tr('term_nav.config') ?></a><!--
--><a href="<?= url('about') ?>">About</a> --><a href="<?= url('cfg_wifi') ?>"><?= tr('term_nav.wifi') ?></a><!--
--><a href="<?= url('help') ?>"><?= tr('term_nav.help') ?></a><!--
--><a href="<?= url('about') ?>"><?= tr('term_nav.about') ?></a>
</nav> </nav>
<script> <script>
try { try {
termInit(%screenData%); window.noAutoShow = true;
termInit(); // the screen will be loaded via ajax
Screen.load('%j:labels_seq%');
// auto-clear the input box
$('#softkb-input').on('input', function(e) {
setTimeout(function(){
var str = $('#softkb-input').val();
$('#softkb-input').val('');
Input.sendString(str);
}, 1);
});
} catch(e) { } catch(e) {
console.error("Fail, reloading..."); console.error(e);
console.error("Fail, reloading in 1s…");
setTimeout(function() {
location.reload(true); location.reload(true);
}, 1000);
}
function toggleSoftKb(yes) {
var i = qs('#softkb-input');
if (yes) i.focus();
else i.blur();
} }
</script> </script>
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-24
View File
@@ -1,24 +0,0 @@
$term-colors:
// 0 black, 1 red, 2 green, 3 yellow
// 4 blue, 5 mag, 6 cyan, 7 white
#111213, #CC0000, #4E9A06, #C4A000,
#3465A4, #75507B, #06989A, #D3D7CF,
// BRIGHT
// 8 black, 9 red, 10 green, 11 yellow
// 12 blue, 13 mag, 14 cyan, 15 white
#555753, #EF2929, #8AE234, #FCE94F,
#729FCF, #AD7FA8, #34E2E2, #EEEEEC;
@for $i from 1 through length($term-colors) {
$c: nth($term-colors, $i);
.fg#{$i - 1} { color: $c; }
.bg#{$i - 1} { background-color: $c; }
}
.fg8, .fg9, .fg10, .fg11, .fg12, .fg13, .fg14, .fg15 {
font-weight: bold;
}
.nb {
font-weight: normal !important;
}
+1 -2
View File
@@ -1,5 +1,5 @@
@import "normalize"; @import "normalize";
@import "fontello-embedded"; @import "fontello";
@import "lib/bourbon/bourbon"; @import "lib/bourbon/bourbon";
@import "grid-settings"; @import "grid-settings";
@@ -33,7 +33,6 @@ $c-form-highlight-a: #2ea1f9;
@import "pages/wifi"; @import "pages/wifi";
@import "pages/term"; @import "pages/term";
@import "pages/about"; @import "pages/about";
@import "term-colors";
// media queries // media queries
+15 -4
View File
@@ -1,5 +1,13 @@
@import "buttons"; @import "buttons";
#{$all-text-inputs}, select, label.select-wrap {
width: $form-field-w;
}
input[type="number"], input.short, select.short {
width: $form-field-w/2;
}
#{$all-text-inputs}, select { #{$all-text-inputs}, select {
border: 0 none; border: 0 none;
border-bottom: 2px solid $c-form-highlight; border-bottom: 2px solid $c-form-highlight;
@@ -33,17 +41,20 @@
color: $c-form-highlight-a; color: $c-form-highlight-a;
&::before { &::before {
font-weight: bold; font-family: "fontello";
position: absolute; position: absolute;
content: '×'; //content: '×';
//content: '';
left: 0; top: 0; right: 0; bottom: 0; left: 0; top: 0; right: 0; bottom: 0;
line-height: $h - 1px; line-height: 25px;
text-align: center; text-align: center;
font-size: $h; font-size: 20px;
vertical-align: middle; vertical-align: middle;
display: none; display: none;
} }
@include icon-content('ok');
&.checked::before { &.checked::before {
display: block; display: block;
} }
+2 -9
View File
@@ -1,14 +1,6 @@
// Unified Form wrapper // Unified Form wrapper
form { @include naked(); } form { @include naked(); }
#{$all-text-inputs}, select, label.select-wrap {
width: $form-field-w;
}
input[type="number"], input.short {
width: $form-field-w/2;
}
.Box.errors { .Box.errors {
.list { .list {
color: crimson; color: crimson;
@@ -45,7 +37,8 @@ input[type="number"], input.short {
} }
} }
&.buttons { // buttons2 is the same style, but different selector for use in the admin page
&.buttons, &.buttons2 {
margin: 16px auto; margin: 16px auto;
input, .button { input, .button {
margin-right: dist(-1); margin-right: dist(-1);
+14 -2
View File
@@ -78,10 +78,21 @@
} }
.Row.explain { .Row.explain {
display: block; // this stops flex messing up text with inline tags
max-width: 600px; margin-left: 0; max-width: 600px; margin-left: 0;
line-height: 1.2;
@include media($phone) { @include media($phone) {
margin-top: 60px; margin-top: 60px;
} }
&.nomargintop {
margin-top: 12px !important;
}
&.padleft {
}
} }
&.mobopen .Row.explain { &.mobopen .Row.explain {
margin-top: 12px; // default from .Row margin-top: 12px; // default from .Row
@@ -97,11 +108,12 @@
h2 { h2 {
position: relative; position: relative;
cursor: pointer; cursor: pointer;
padding-right: 1.3rem; padding: 2px 1.3rem 2px 5px;
margin: 0 -5px 0 -5px;
&::after { &::after {
position: absolute; position: absolute;
right: 0; right: 4px;
content: ''; content: '';
top:50%; top:50%;
+10
View File
@@ -48,3 +48,13 @@
color: $c-form-label-fg; color: $c-form-label-fg;
} }
} }
#content {
// fade in effect
opacity: 0;
transition: opacity 0.15s ease-in;
}
#content.load {
opacity: 1;
}
+1
View File
@@ -16,6 +16,7 @@
} }
#brand { #brand {
cursor: pointer;
color: $menu-fg; color: $menu-fg;
background: darken($menu-bg, 10%); background: darken($menu-bg, 10%);
font-size: 120%; font-size: 120%;
+127 -2
View File
@@ -1,4 +1,8 @@
body.term { body.term {
#content {
padding-left: 0;
padding-right: 0;
h1 { h1 {
font-size: fsize(5); font-size: fsize(5);
@include media($phone) { @include media($phone) {
@@ -6,6 +10,10 @@ body.term {
} }
} }
// longer duration to load everything in background nicely
transition: opacity 0.25s ease-in;
}
#screen { #screen {
@include noselect(); @include noselect();
font-family: monospace; font-family: monospace;
@@ -36,7 +44,7 @@ body.term {
margin: 0 3px; margin: 0 3px;
padding: 8px 5px; padding: 8px 5px;
//width: 18%; //width: 18%;
min-width: 65px; min-width: 62px;
//max-width: 65px; //max-width: 65px;
//min-width: initial; //min-width: initial;
cursor: pointer; cursor: pointer;
@@ -53,16 +61,133 @@ body.term {
text-decoration: underline; text-decoration: underline;
&, &:visited, &:link { &, &:visited, &:link {
color: #2e4d6e; color: #336085;
} }
&:hover { &:hover {
color: #5abfff; color: #5abfff;
} }
} }
.icn-keyboard {
text-decoration: none;
font-size: 150%;
vertical-align: middle;
}
} }
} }
#termwrap { #termwrap {
text-align: center; text-align: center;
} }
#softkb-input {
position: absolute;
top: -9999px;
}
// "non-bold"
.nb {
font-weight: normal !important;
}
// Tango
.theme-0 {
$term-colors:
#111213, #CC0000, #4E9A06, #C4A000, #3465A4, #75507B, #06989A, #D3D7CF,
#555753, #EF2929, #8AE234, #FCE94F, #729FCF, #AD7FA8, #34E2E2, #EEEEEC;
@for $i from 1 through length($term-colors) {
$c: nth($term-colors, $i);
.fg#{$i - 1} { color: $c; }
.bg#{$i - 1} { background-color: $c; }
}
}
// Linux
.theme-1 {
$term-colors:
#000000, #aa0000, #00aa00, #aa5500, #0000aa, #aa00aa, #00aaaa, #aaaaaa,
#555555, #ff5555, #55ff55, #ffff55, #5555ff, #ff55ff, #55ffff, #ffffff;
@for $i from 1 through length($term-colors) {
$c: nth($term-colors, $i);
.fg#{$i - 1} { color: $c; }
.bg#{$i - 1} { background-color: $c; }
}
}
// xterm
.theme-2 {
$term-colors:
#000000, #cd0000, #00cd00, #cdcd00, #0000ee, #cd00cd, #00cdcd, #e5e5e5,
#7f7f7f, #ff0000, #00ff00, #ffff00, #5c5cff, #ff00ff, #00ffff, #ffffff;
@for $i from 1 through length($term-colors) {
$c: nth($term-colors, $i);
.fg#{$i - 1} { color: $c; }
.bg#{$i - 1} { background-color: $c; }
}
}
// rxvt
.theme-3 {
$term-colors:
#000000, #cd0000, #00cd00, #cdcd00, #0000cd, #cd00cd, #00cdcd, #faebd7,
#404040, #ff0000, #00ff00, #ffff00, #0000ff, #ff00ff, #00ffff, #ffffff;
@for $i from 1 through length($term-colors) {
$c: nth($term-colors, $i);
.fg#{$i - 1} { color: $c; }
.bg#{$i - 1} { background-color: $c; }
}
}
// Ambience
.theme-4 {
$term-colors:
#2e3436, #cc0000, #4e9a06, #c4a000, #3465a4, #75507b, #06989a, #d3d7cf,
#555753, #ef2929, #8ae234, #fce94f, #729fcf, #ad7fa8, #34e2e2, #eeeeec;
@for $i from 1 through length($term-colors) {
$c: nth($term-colors, $i);
.fg#{$i - 1} { color: $c; }
.bg#{$i - 1} { background-color: $c; }
}
}
// Solarized
.theme-5 {
$term-colors:
#073642, #dc322f, #859900, #b58900, #268bd2, #d33682, #2aa198, #eee8d5,
#002b36, #cb4b16, #586e75, #657b83, #839496, #6c71c4, #93a1a1, #fdf6e3;
@for $i from 1 through length($term-colors) {
$c: nth($term-colors, $i);
.fg#{$i - 1} { color: $c; }
.bg#{$i - 1} { background-color: $c; }
}
}
.bold {
font-weight: bold !important;
}
.Row.color-preview {
font-family: monospace;
font-size: 16pt;
display: block;
margin-bottom: 0;
padding-left: $form-label-w;
@include media($phone) {
padding-left: 0;
font-size: 14pt;
}
.colorprev {
display:block;
margin: 0;
cursor: pointer;
}
}
#color-example {
display: inline-block;
padding: 5px;
}
+1
View File
@@ -19,6 +19,7 @@
padding: dist(-2); padding: dist(-2);
margin-bottom: dist(-2); margin-bottom: dist(-2);
margin-top: dist(-2); margin-top: dist(-2);
font-size: 110%;
} }
#ap-noscan { #ap-noscan {
+1 -1
View File
@@ -17,7 +17,7 @@
/** /**
* Helper that retrieves an arg from `connData->getArgs` and stores it in `buff`. Returns 1 on success * Helper that retrieves an arg from `connData->getArgs` and stores it in `buff`. Returns 1 on success
*/ */
#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_HELPER(x) #x
#define STR(x) STR_HELPER(x) #define STR(x) STR_HELPER(x)
+205 -84
View File
@@ -2,25 +2,28 @@
/* #line 1 "user/ansi_parser.rl" */ /* #line 1 "user/ansi_parser.rl" */
#include <esp8266.h> #include <esp8266.h>
#include "ansi_parser.h" #include "ansi_parser.h"
#include "screen.h"
/* Ragel constants block */ /* Ragel constants block */
/* #line 9 "user/ansi_parser.c" */ /* #line 10 "user/ansi_parser.c" */
static const char _ansi_actions[] = { static const char _ansi_actions[] = {
0, 1, 0, 1, 1, 1, 2, 1, 0, 1, 0, 1, 1, 1, 2, 1,
3, 1, 4, 1, 5, 1, 6, 1, 3, 1, 4, 1, 5, 1, 6, 1,
7, 1, 8, 1, 9, 1, 10, 1, 7, 1, 8, 1, 9, 1, 10, 1,
11, 1, 12 11, 1, 12, 1, 13, 1, 14, 2,
9, 10, 2, 9, 11
}; };
static const char _ansi_eof_actions[] = { static const char _ansi_eof_actions[] = {
0, 13, 13, 13, 13, 13, 13, 13, 0, 13, 13, 13, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 0, 0, 13, 13, 13, 13, 13, 13, 13, 13,
0 13, 13, 13, 13, 13, 13, 13, 13,
13, 13, 0, 0, 0, 0, 0
}; };
static const int ansi_start = 1; static const int ansi_start = 1;
static const int ansi_first_final = 14; static const int ansi_first_final = 26;
static const int ansi_error = 0; static const int ansi_error = 0;
static const int ansi_en_CSI_body = 3; static const int ansi_en_CSI_body = 3;
@@ -28,9 +31,21 @@ static const int ansi_en_OSC_body = 5;
static const int ansi_en_main = 1; static const int ansi_en_main = 1;
/* #line 8 "user/ansi_parser.rl" */ /* #line 9 "user/ansi_parser.rl" */
static volatile int cs = -1;
static ETSTimer resetTim;
static void ICACHE_FLASH_ATTR
resetParserCb(void *arg) {
if (cs != ansi_start) {
cs = ansi_start;
warn("Parser timeout, state reset");
}
}
/** /**
* \brief Linear ANSI chars stream parser * \brief Linear ANSI chars stream parser
* *
@@ -47,13 +62,13 @@ static const int ansi_en_main = 1;
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
ansi_parser(const char *newdata, size_t len) ansi_parser(const char *newdata, size_t len)
{ {
static int cs = -1;
// The CSI code is built here // The CSI code is built here
static char csi_leading; //!< Leading char, 0 if none static char csi_leading; //!< Leading char, 0 if none
static int csi_ni; //!< Number of the active digit static int csi_ni; //!< Number of the active digit
static int csi_n[CSI_N_MAX]; //!< Param digits static int csi_n[CSI_N_MAX]; //!< Param digits
static char csi_char; //!< CSI action char (end) static char csi_char; //!< CSI action char (end)
static char osc_buffer[OSC_CHAR_MAX];
static int osc_bi;
if (len == 0) len = strlen(newdata); if (len == 0) len = strlen(newdata);
@@ -65,17 +80,24 @@ ansi_parser(const char *newdata, size_t len)
// Init Ragel on the first run // Init Ragel on the first run
if (cs == -1) { if (cs == -1) {
/* #line 69 "user/ansi_parser.c" */ /* #line 84 "user/ansi_parser.c" */
{ {
cs = ansi_start; cs = ansi_start;
} }
/* #line 44 "user/ansi_parser.rl" */ /* #line 57 "user/ansi_parser.rl" */
}
// schedule state reset
if (termconf->parser_tout_ms > 0) {
os_timer_disarm(&resetTim);
os_timer_setfn(&resetTim, resetParserCb, NULL);
os_timer_arm(&resetTim, termconf->parser_tout_ms, 0);
} }
// The parser // The parser
/* #line 79 "user/ansi_parser.c" */ /* #line 101 "user/ansi_parser.c" */
{ {
const char *_acts; const char *_acts;
unsigned int _nacts; unsigned int _nacts;
@@ -101,7 +123,7 @@ case 2:
goto tr2; goto tr2;
case 0: case 0:
goto _out; goto _out;
case 14: case 26:
if ( (*p) == 27 ) if ( (*p) == 27 )
goto tr1; goto tr1;
goto tr0; goto tr0;
@@ -135,55 +157,118 @@ case 4:
} else } else
goto tr11; goto tr11;
goto tr2; goto tr2;
case 15: case 27:
goto tr2; goto tr2;
case 5: case 5:
switch( (*p) ) { switch( (*p) ) {
case 70: goto tr12; case 66: goto tr12;
case 87: goto tr13; case 84: goto tr13;
case 87: goto tr14;
} }
goto tr2; goto tr2;
case 6: case 6:
if ( (*p) == 82 ) if ( (*p) == 84 )
goto tr14;
goto tr2;
case 7:
switch( (*p) ) {
case 7: goto tr15;
case 27: goto tr16;
}
goto tr2;
case 16:
goto tr2;
case 8:
if ( (*p) == 92 )
goto tr15; goto tr15;
goto tr2; goto tr2;
case 9: case 7:
if ( (*p) == 78 )
goto tr16;
goto tr2;
case 8:
if ( 48 <= (*p) && (*p) <= 57 ) if ( 48 <= (*p) && (*p) <= 57 )
goto tr17; goto tr17;
goto tr2; goto tr2;
case 9:
if ( (*p) == 61 )
goto tr18;
goto tr2;
case 10: case 10:
if ( (*p) == 59 ) if ( (*p) == 27 )
goto tr18;
if ( 48 <= (*p) && (*p) <= 57 )
goto tr17;
goto tr2; goto tr2;
case 11:
if ( 48 <= (*p) && (*p) <= 57 )
goto tr19; goto tr19;
goto tr2; case 11:
case 12:
switch( (*p) ) { switch( (*p) ) {
case 7: goto tr20; case 7: goto tr20;
case 27: goto tr21; case 27: goto tr21;
} }
if ( 48 <= (*p) && (*p) <= 57 )
goto tr19; goto tr19;
case 28:
switch( (*p) ) {
case 7: goto tr20;
case 27: goto tr21;
}
goto tr19;
case 12:
if ( (*p) == 92 )
goto tr22;
goto tr2;
case 29:
goto tr2; goto tr2;
case 13: case 13:
if ( (*p) == 73 )
goto tr23;
goto tr2;
case 14:
if ( (*p) == 84 )
goto tr24;
goto tr2;
case 15:
if ( (*p) == 76 )
goto tr25;
goto tr2;
case 16:
if ( (*p) == 69 )
goto tr26;
goto tr2;
case 17:
if ( (*p) == 61 )
goto tr27;
goto tr2;
case 18:
if ( (*p) == 27 )
goto tr2;
goto tr28;
case 19:
switch( (*p) ) {
case 7: goto tr29;
case 27: goto tr30;
}
goto tr28;
case 30:
switch( (*p) ) {
case 7: goto tr29;
case 27: goto tr30;
}
goto tr28;
case 20:
if ( (*p) == 92 ) if ( (*p) == 92 )
goto tr20; goto tr31;
goto tr2;
case 21:
if ( 48 <= (*p) && (*p) <= 57 )
goto tr32;
goto tr2;
case 22:
if ( (*p) == 59 )
goto tr33;
if ( 48 <= (*p) && (*p) <= 57 )
goto tr32;
goto tr2;
case 23:
if ( 48 <= (*p) && (*p) <= 57 )
goto tr34;
goto tr2;
case 24:
switch( (*p) ) {
case 7: goto tr35;
case 27: goto tr36;
}
if ( 48 <= (*p) && (*p) <= 57 )
goto tr34;
goto tr2;
case 25:
if ( (*p) == 92 )
goto tr35;
goto tr2; goto tr2;
} }
@@ -194,21 +279,36 @@ case 13:
tr9: cs = 4; goto f8; tr9: cs = 4; goto f8;
tr10: cs = 4; goto f9; tr10: cs = 4; goto f9;
tr12: cs = 6; goto _again; tr12: cs = 6; goto _again;
tr14: cs = 7; goto _again; tr15: cs = 7; goto _again;
tr16: cs = 8; goto _again; tr16: cs = 8; goto _again;
tr13: cs = 9; goto _again; tr17: cs = 9; goto f8;
tr17: cs = 10; goto f8; tr18: cs = 10; goto _again;
tr18: cs = 11; goto f9; tr19: cs = 11; goto f11;
tr19: cs = 12; goto f8; tr21: cs = 12; goto _again;
tr21: cs = 13; goto _again; tr13: cs = 13; goto _again;
tr3: cs = 14; goto f2; tr23: cs = 14; goto _again;
tr4: cs = 14; goto f3; tr24: cs = 15; goto _again;
tr5: cs = 14; goto f4; tr25: cs = 16; goto _again;
tr6: cs = 14; goto f5; tr26: cs = 17; goto _again;
tr7: cs = 14; goto f6; tr27: cs = 18; goto _again;
tr11: cs = 15; goto f10; tr28: cs = 19; goto f11;
tr15: cs = 16; goto f11; tr30: cs = 20; goto _again;
tr20: cs = 16; goto f12; tr14: cs = 21; goto _again;
tr32: cs = 22; goto f8;
tr33: cs = 23; goto f9;
tr34: cs = 24; goto f8;
tr36: cs = 25; goto _again;
tr3: cs = 26; goto f2;
tr4: cs = 26; goto f3;
tr5: cs = 26; goto f4;
tr6: cs = 26; goto f5;
tr7: cs = 26; goto f6;
tr11: cs = 27; goto f10;
tr20: cs = 28; goto f12;
tr22: cs = 29; goto f13;
tr31: cs = 29; goto f15;
tr35: cs = 29; goto f16;
tr29: cs = 30; goto f14;
f1: _acts = _ansi_actions + 1; goto execFuncs; f1: _acts = _ansi_actions + 1; goto execFuncs;
f4: _acts = _ansi_actions + 3; goto execFuncs; f4: _acts = _ansi_actions + 3; goto execFuncs;
@@ -218,30 +318,34 @@ case 13:
f10: _acts = _ansi_actions + 11; goto execFuncs; f10: _acts = _ansi_actions + 11; goto execFuncs;
f0: _acts = _ansi_actions + 13; goto execFuncs; f0: _acts = _ansi_actions + 13; goto execFuncs;
f5: _acts = _ansi_actions + 15; goto execFuncs; f5: _acts = _ansi_actions + 15; goto execFuncs;
f11: _acts = _ansi_actions + 17; goto execFuncs; f16: _acts = _ansi_actions + 17; goto execFuncs;
f12: _acts = _ansi_actions + 19; goto execFuncs; f11: _acts = _ansi_actions + 19; goto execFuncs;
f6: _acts = _ansi_actions + 21; goto execFuncs; f15: _acts = _ansi_actions + 21; goto execFuncs;
f2: _acts = _ansi_actions + 23; goto execFuncs; f13: _acts = _ansi_actions + 23; goto execFuncs;
f3: _acts = _ansi_actions + 25; goto execFuncs; f6: _acts = _ansi_actions + 25; goto execFuncs;
f2: _acts = _ansi_actions + 27; goto execFuncs;
f3: _acts = _ansi_actions + 29; goto execFuncs;
f14: _acts = _ansi_actions + 31; goto execFuncs;
f12: _acts = _ansi_actions + 34; goto execFuncs;
execFuncs: execFuncs:
_nacts = *_acts++; _nacts = *_acts++;
while ( _nacts-- > 0 ) { while ( _nacts-- > 0 ) {
switch ( *_acts++ ) { switch ( *_acts++ ) {
case 0: case 0:
/* #line 54 "user/ansi_parser.rl" */ /* #line 76 "user/ansi_parser.rl" */
{ {
apars_handle_plainchar((*p)); apars_handle_plainchar((*p));
} }
break; break;
case 1: case 1:
/* #line 61 "user/ansi_parser.rl" */ /* #line 83 "user/ansi_parser.rl" */
{ {
/* Reset the CSI builder */ // Reset the CSI builder
csi_leading = csi_char = 0; csi_leading = csi_char = 0;
csi_ni = 0; csi_ni = 0;
/* Zero out digits */ // Zero out digits
for(int i = 0; i < CSI_N_MAX; i++) { for(int i = 0; i < CSI_N_MAX; i++) {
csi_n[i] = 0; csi_n[i] = 0;
} }
@@ -250,28 +354,28 @@ execFuncs:
} }
break; break;
case 2: case 2:
/* #line 74 "user/ansi_parser.rl" */ /* #line 96 "user/ansi_parser.rl" */
{ {
csi_leading = (*p); csi_leading = (*p);
} }
break; break;
case 3: case 3:
/* #line 78 "user/ansi_parser.rl" */ /* #line 100 "user/ansi_parser.rl" */
{ {
/* x10 + digit */ // x10 + digit
if (csi_ni < CSI_N_MAX) { if (csi_ni < CSI_N_MAX) {
csi_n[csi_ni] = csi_n[csi_ni]*10 + ((*p) - '0'); csi_n[csi_ni] = csi_n[csi_ni]*10 + ((*p) - '0');
} }
} }
break; break;
case 4: case 4:
/* #line 85 "user/ansi_parser.rl" */ /* #line 107 "user/ansi_parser.rl" */
{ {
csi_ni++; csi_ni++;
} }
break; break;
case 5: case 5:
/* #line 89 "user/ansi_parser.rl" */ /* #line 111 "user/ansi_parser.rl" */
{ {
csi_char = (*p); csi_char = (*p);
@@ -281,14 +385,14 @@ execFuncs:
} }
break; break;
case 6: case 6:
/* #line 97 "user/ansi_parser.rl" */ /* #line 119 "user/ansi_parser.rl" */
{ {
apars_handle_badseq(); apars_handle_badseq();
{cs = 1;goto _again;} {cs = 1;goto _again;}
} }
break; break;
case 7: case 7:
/* #line 114 "user/ansi_parser.rl" */ /* #line 136 "user/ansi_parser.rl" */
{ {
csi_ni = 0; csi_ni = 0;
@@ -297,47 +401,64 @@ execFuncs:
csi_n[i] = 0; csi_n[i] = 0;
} }
osc_bi = 0;
osc_buffer[0] = '\0';
{cs = 5;goto _again;} {cs = 5;goto _again;}
} }
break; break;
case 8: case 8:
/* #line 125 "user/ansi_parser.rl" */ /* #line 150 "user/ansi_parser.rl" */
{ {
apars_handle_OSC_FactoryReset(); apars_handle_OSC_SetScreenSize(csi_n[0], csi_n[1]);
{cs = 1;goto _again;} {cs = 1;goto _again;}
} }
break; break;
case 9: case 9:
/* #line 130 "user/ansi_parser.rl" */ /* #line 155 "user/ansi_parser.rl" */
{ {
apars_handle_OSC_SetScreenSize(csi_n[0], csi_n[1]); osc_buffer[osc_bi++] = (*p);
{cs = 1;goto _again;}
} }
break; break;
case 10: case 10:
/* #line 141 "user/ansi_parser.rl" */ /* #line 159 "user/ansi_parser.rl" */
{
osc_buffer[osc_bi++] = '\0';
apars_handle_OSC_SetTitle(osc_buffer);
{cs = 1;goto _again;}
}
break;
case 11:
/* #line 165 "user/ansi_parser.rl" */
{
osc_buffer[osc_bi++] = '\0';
apars_handle_OSC_SetButton(csi_n[0], osc_buffer);
{cs = 1;goto _again;}
}
break;
case 12:
/* #line 177 "user/ansi_parser.rl" */
{ {
// Reset screen // Reset screen
apars_handle_RESET_cmd(); apars_handle_RESET_cmd();
{cs = 1;goto _again;} {cs = 1;goto _again;}
} }
break; break;
case 11: case 13:
/* #line 147 "user/ansi_parser.rl" */ /* #line 183 "user/ansi_parser.rl" */
{ {
apars_handle_saveCursorAttrs(); apars_handle_saveCursorAttrs();
{cs = 1;goto _again;} {cs = 1;goto _again;}
} }
break; break;
case 12: case 14:
/* #line 152 "user/ansi_parser.rl" */ /* #line 188 "user/ansi_parser.rl" */
{ {
apars_handle_restoreCursorAttrs(); apars_handle_restoreCursorAttrs();
{cs = 1;goto _again;} {cs = 1;goto _again;}
} }
break; break;
/* #line 341 "user/ansi_parser.c" */ /* #line 462 "user/ansi_parser.c" */
} }
} }
goto _again; goto _again;
@@ -355,7 +476,7 @@ _again:
while ( __nacts-- > 0 ) { while ( __nacts-- > 0 ) {
switch ( *__acts++ ) { switch ( *__acts++ ) {
case 6: case 6:
/* #line 97 "user/ansi_parser.rl" */ /* #line 119 "user/ansi_parser.rl" */
{ {
apars_handle_badseq(); apars_handle_badseq();
{cs = 1; if ( p == pe ) {cs = 1; if ( p == pe )
@@ -363,7 +484,7 @@ _again:
goto _again;} goto _again;}
} }
break; break;
/* #line 367 "user/ansi_parser.c" */ /* #line 488 "user/ansi_parser.c" */
} }
} }
} }
@@ -371,6 +492,6 @@ goto _again;}
_out: {} _out: {}
} }
/* #line 171 "user/ansi_parser.rl" */ /* #line 208 "user/ansi_parser.rl" */
} }
+4 -1
View File
@@ -2,18 +2,21 @@
#define ANSI_PARSER_H #define ANSI_PARSER_H
#include <stdlib.h> #include <stdlib.h>
#include <screen.h>
// Max nr of CSI parameters // Max nr of CSI parameters
#define CSI_N_MAX 3 #define CSI_N_MAX 3
#define OSC_CHAR_MAX TERM_TITLE_LEN
extern void apars_handle_badseq(void); extern void apars_handle_badseq(void);
extern void apars_handle_CSI(char leadchar, int *params, char keychar); extern void apars_handle_CSI(char leadchar, int *params, char keychar);
extern void apars_handle_RESET_cmd(void); extern void apars_handle_RESET_cmd(void);
extern void apars_handle_plainchar(char c); extern void apars_handle_plainchar(char c);
extern void apars_handle_OSC_FactoryReset(void);
extern void apars_handle_OSC_SetScreenSize(int rows, int cols); extern void apars_handle_OSC_SetScreenSize(int rows, int cols);
extern void apars_handle_saveCursorAttrs(void); extern void apars_handle_saveCursorAttrs(void);
extern void apars_handle_restoreCursorAttrs(void); extern void apars_handle_restoreCursorAttrs(void);
extern void apars_handle_OSC_SetButton(int num, const char *buffer);
extern void apars_handle_OSC_SetTitle(const char *buffer);
/** /**
* \brief Linear ANSI chars stream parser * \brief Linear ANSI chars stream parser
+49 -12
View File
@@ -1,5 +1,6 @@
#include <esp8266.h> #include <esp8266.h>
#include "ansi_parser.h" #include "ansi_parser.h"
#include "screen.h"
/* Ragel constants block */ /* Ragel constants block */
%%{ %%{
@@ -7,6 +8,18 @@
write data; write data;
}%% }%%
static volatile int cs = -1;
static ETSTimer resetTim;
static void ICACHE_FLASH_ATTR
resetParserCb(void *arg) {
if (cs != ansi_start) {
cs = ansi_start;
warn("Parser timeout, state reset");
}
}
/** /**
* \brief Linear ANSI chars stream parser * \brief Linear ANSI chars stream parser
* *
@@ -23,13 +36,13 @@
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
ansi_parser(const char *newdata, size_t len) ansi_parser(const char *newdata, size_t len)
{ {
static int cs = -1;
// The CSI code is built here // The CSI code is built here
static char csi_leading; //!< Leading char, 0 if none static char csi_leading; //!< Leading char, 0 if none
static int csi_ni; //!< Number of the active digit static int csi_ni; //!< Number of the active digit
static int csi_n[CSI_N_MAX]; //!< Param digits static int csi_n[CSI_N_MAX]; //!< Param digits
static char csi_char; //!< CSI action char (end) static char csi_char; //!< CSI action char (end)
static char osc_buffer[OSC_CHAR_MAX];
static int osc_bi;
if (len == 0) len = strlen(newdata); if (len == 0) len = strlen(newdata);
@@ -43,11 +56,20 @@ ansi_parser(const char *newdata, size_t len)
%% write init; %% write init;
} }
// schedule state reset
if (termconf->parser_tout_ms > 0) {
os_timer_disarm(&resetTim);
os_timer_setfn(&resetTim, resetParserCb, NULL);
os_timer_arm(&resetTim, termconf->parser_tout_ms, 0);
}
// The parser // The parser
%%{ %%{
#/*
ESC = 27; ESC = 27;
NOESC = (any - ESC); NOESC = (any - ESC);
TOK_ST = ESC '\\'; # String terminator - used for OSC commands TOK_ST = ESC '\\'; # String terminator - used for OSC commands
OSC_END = ('\a' | ESC '\\');
# --- Regular characters to be printed --- # --- Regular characters to be printed ---
@@ -59,11 +81,11 @@ ansi_parser(const char *newdata, size_t len)
# Text color & style # Text color & style
action CSI_start { action CSI_start {
/* Reset the CSI builder */ // Reset the CSI builder
csi_leading = csi_char = 0; csi_leading = csi_char = 0;
csi_ni = 0; csi_ni = 0;
/* Zero out digits */ // Zero out digits
for(int i = 0; i < CSI_N_MAX; i++) { for(int i = 0; i < CSI_N_MAX; i++) {
csi_n[i] = 0; csi_n[i] = 0;
} }
@@ -76,7 +98,7 @@ ansi_parser(const char *newdata, size_t len)
} }
action CSI_digit { action CSI_digit {
/* x10 + digit */ // x10 + digit
if (csi_ni < CSI_N_MAX) { if (csi_ni < CSI_N_MAX) {
csi_n[csi_ni] = csi_n[csi_ni]*10 + (fc - '0'); csi_n[csi_ni] = csi_n[csi_ni]*10 + (fc - '0');
} }
@@ -119,23 +141,37 @@ ansi_parser(const char *newdata, size_t len)
csi_n[i] = 0; csi_n[i] = 0;
} }
fgoto OSC_body; osc_bi = 0;
} osc_buffer[0] = '\0';
action OSC_fr { fgoto OSC_body;
apars_handle_OSC_FactoryReset();
fgoto main;
} }
action OSC_resize { action OSC_resize {
apars_handle_OSC_SetScreenSize(csi_n[0], csi_n[1]); apars_handle_OSC_SetScreenSize(csi_n[0], csi_n[1]);
fgoto main;
}
action OSC_text_char {
osc_buffer[osc_bi++] = fc;
}
action OSC_title {
osc_buffer[osc_bi++] = '\0';
apars_handle_OSC_SetTitle(osc_buffer);
fgoto main;
}
action OSC_button {
osc_buffer[osc_bi++] = '\0';
apars_handle_OSC_SetButton(csi_n[0], osc_buffer);
fgoto main; fgoto main;
} }
OSC_body := ( OSC_body := (
("FR" ('\a' | ESC '\\') @OSC_fr) | ("BTN" digit @CSI_digit '=' (NOESC @OSC_text_char)+ OSC_END @OSC_button) |
('W' (digit @CSI_digit)+ ';' @CSI_semi (digit @CSI_digit)+ ('\a' | ESC '\\') @OSC_resize) ("TITLE=" (NOESC @OSC_text_char)+ OSC_END @OSC_title) |
('W' (digit @CSI_digit)+ ';' @CSI_semi (digit @CSI_digit)+ OSC_END @OSC_resize)
) $!errBadSeq; ) $!errBadSeq;
action RESET_cmd { action RESET_cmd {
@@ -168,5 +204,6 @@ ansi_parser(const char *newdata, size_t len)
)+ $!errBadSeq; )+ $!errBadSeq;
write exec; write exec;
#*/
}%% }%%
} }
+59 -71
View File
@@ -9,6 +9,11 @@
#include "screen.h" #include "screen.h"
#include "ansi_parser.h" #include "ansi_parser.h"
#include "uart_driver.h" #include "uart_driver.h"
#include "persist.h"
static char utf_collect[4];
static int utf_i = 0;
static int utf_j = 0;
/** /**
* Handle a received plain character * Handle a received plain character
@@ -16,7 +21,37 @@
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
apars_handle_plainchar(char c) apars_handle_plainchar(char c)
{ {
screen_putchar(c); // collecting unicode glyphs...
if (c & 0x80) {
if (utf_i == 0) {
if ((c & 0xE0) == 0xC0) {
utf_i = 2;
}
else if ((c & 0xF0) == 0xE0) {
utf_i = 3;
}
else if ((c & 0xF8) == 0xF0) {
utf_i = 4;
}
utf_collect[0] = c;
utf_j = 1;
}
else {
utf_collect[utf_j++] = c;
if (utf_j >= utf_i) {
screen_putchar(utf_collect);
utf_i = 0;
utf_j = 0;
memset(utf_collect, 0, 4);
}
}
}
else {
utf_collect[0] = c;
utf_collect[1] = 0; // just to make sure it's closed...
screen_putchar(utf_collect);
}
} }
/** /**
@@ -188,17 +223,17 @@ apars_handle_CSI(char leadchar, int *params, char keychar)
int n = params[i]; int n = params[i];
if (i == 0 && n == 0) { // reset SGR if (i == 0 && n == 0) { // reset SGR
screen_set_fg(7); screen_reset_cursor(); // resets colors, inverse and bold.
screen_set_bg(0);
break; // cannot combine reset with others break; // cannot combine reset with others
} }
else if (n >= 30 && n <= 37) screen_set_fg(n-30); // ANSI normal fg else if (n >= 30 && n <= 37) screen_set_fg(n-30); // ANSI normal fg
else if (n >= 40 && n <= 47) screen_set_bg(n-40); // ANSI normal bg else if (n >= 40 && n <= 47) screen_set_bg(n-40); // ANSI normal bg
else if (n == 39) screen_set_fg(7); // default fg else if (n == 39) screen_set_fg(7); // default fg
else if (n == 49) screen_set_bg(0); // default bg else if (n == 49) screen_set_bg(false); // default bg
else if (n == 7) screen_inverse(1); // inverse else if (n == 7) screen_inverse(true); // inverse
else if (n == 27) screen_inverse(0); // positive else if (n == 27) screen_inverse(false); // positive
else if (n == 1) screen_set_bright_fg(); // ANSI bold = bright fg else if (n == 1) screen_set_bold(true); // bold
else if (n == 21 || n == 22) screen_set_bold(false); // bold off
else if (n >= 90 && n <= 97) screen_set_fg(n-90+8); // AIX bright fg else if (n >= 90 && n <= 97) screen_set_fg(n-90+8); // AIX bright fg
else if (n >= 100 && n <= 107) screen_set_bg(n-100+8); // AIX bright bg else if (n >= 100 && n <= 107) screen_set_bg(n-100+8); // AIX bright bg
} }
@@ -222,73 +257,9 @@ void ICACHE_FLASH_ATTR apars_handle_restoreCursorAttrs(void)
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
apars_handle_RESET_cmd(void) apars_handle_RESET_cmd(void)
{ {
// XXX maybe user wanted to reset the module instead?
screen_reset(); screen_reset();
} }
/**
* Handle a factory reset request
*/
void ICACHE_FLASH_ATTR
apars_handle_OSC_FactoryReset(void)
{
warn("-------- Factory reset --------");
dbg("Switching to Client+AP mode");
// Send acknowledgement message to UART0
// User is performing this manually, so we can just print it as string
UART_WriteString(UART0, "\r\nFACTORY RESET\r\n", UART_TIMEOUT_US);
// Disconnect from AP if connected
int opmode = wifi_get_opmode();
if (opmode != SOFTAP_MODE) {
wifi_station_disconnect();
}
// Both must be enabled so we can manipulate their settings
wifi_set_opmode(STATIONAP_MODE);
// --- AP config ---
dbg("AP WiFi channel: 6");
struct softap_config apconf;
wifi_softap_get_config(&apconf);
apconf.authmode=AUTH_OPEN; // Disable access protection
apconf.channel=6; // Reset channel; user may have set bad channel in the UI
// generate unique AP name
u8 mac[6];
wifi_get_macaddr(SOFTAP_IF, mac);
sprintf((char*)apconf.ssid, "TERM-%02X%02X%02X", mac[3], mac[4], mac[5]);
apconf.ssid_len = (u8)strlen((char*)apconf.ssid);
info("New AP name: %s", (char*)apconf.ssid);
// --- Station ---
dbg("Erasing stored WiFi credentials...");
struct station_config staconf;
wifi_station_get_config(&staconf);
// clear info about SSID
staconf.ssid[0]=0;
staconf.bssid_set=0;
staconf.password[0]=0;
dbg("Commiting changes...");
wifi_softap_set_config(&apconf);
wifi_station_set_config(&staconf);
UART_WriteString(UART0, "Factory Reset complete, device reset.\r\n\r\n", UART_TIMEOUT_US);
info("*** FACTORY RESET COMPLETE ***");
dbg("Device reset...");
// Reboot to clean STA+AP mode with Channel X & reset AP SSID.
system_restart();
}
/** /**
* Handle a screen resize request * Handle a screen resize request
*/ */
@@ -299,3 +270,20 @@ apars_handle_OSC_SetScreenSize(int rows, int cols)
screen_resize(rows, cols); screen_resize(rows, cols);
} }
void ICACHE_FLASH_ATTR
apars_handle_OSC_SetButton(int num, const char *buffer)
{
strncpy(termconf_scratch.btn[num-1], buffer, TERM_BTN_LEN);
info("OSC: Set BTN%d = %s", num, buffer);
screen_notifyChange(CHANGE_LABELS);
}
void ICACHE_FLASH_ATTR
apars_handle_OSC_SetTitle(const char *buffer)
{
strncpy(termconf_scratch.title, buffer, TERM_TITLE_LEN);
info("OSC: Set TITLE = %s", buffer);
screen_notifyChange(CHANGE_LABELS);
}
+2 -1
View File
@@ -5,9 +5,10 @@ void apars_handle_badseq(void);
void apars_handle_CSI(char leadchar, int *params, char keychar); void apars_handle_CSI(char leadchar, int *params, char keychar);
void apars_handle_RESET_cmd(void); void apars_handle_RESET_cmd(void);
void apars_handle_plainchar(char c); void apars_handle_plainchar(char c);
void apars_handle_OSC_FactoryReset(void);
void apars_handle_OSC_SetScreenSize(int rows, int cols); void apars_handle_OSC_SetScreenSize(int rows, int cols);
void apars_handle_saveCursorAttrs(void); void apars_handle_saveCursorAttrs(void);
void apars_handle_restoreCursorAttrs(void); void apars_handle_restoreCursorAttrs(void);
void apars_handle_OSC_SetButton(int num, const char *buffer);
void apars_handle_OSC_SetTitle(const char *buffer);
#endif //ESP_VT100_FIRMWARE_ANSI_PARSER_CALLBACKS_H #endif //ESP_VT100_FIRMWARE_ANSI_PARSER_CALLBACKS_H
-9
View File
@@ -1,9 +0,0 @@
#ifndef CGIAPPCFG_H
#define CGIAPPCFG_H
#include "httpd.h"
httpd_cgi_state cgiAppCfgSetParams(HttpdConnData *connData);
httpd_cgi_state tplAppCfg(HttpdConnData *connData, char *token, void **arg);
#endif
+41 -40
View File
@@ -1,6 +1,7 @@
#include <esp8266.h> #include <esp8266.h>
#include <httpd.h> #include <httpd.h>
#include <esp_sdk_ver.h> #include <esp_sdk_ver.h>
#include <httpdespfs.h>
#include "cgi_main.h" #include "cgi_main.h"
#include "screen.h" #include "screen.h"
@@ -19,70 +20,70 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplScreen(HttpdConnData *connData, char *token
{ {
if (token == NULL) { if (token == NULL) {
// Release data object // Release data object
screenSerializeToBuffer(NULL, 0, arg);
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }
char buff[150];
if (streq(token, "labels_seq")) {
screenSerializeLabelsToBuffer(buff, 150);
tplSend(connData, buff, -1);
}
else if (streq(token, "theme")) {
sprintf(buff, "%d", termconf->theme);
tplSend(connData, buff, -1);
}
return HTTPD_CGI_DONE;
}
httpd_cgi_state ICACHE_FLASH_ATTR
cgiTermInitialImage(HttpdConnData *connData)
{
const int bufsiz = 512; const int bufsiz = 512;
char buff[bufsiz]; char buff[bufsiz];
if (streq(token, "term_title")) { if (connData->conn == NULL) {
httpdSend(connData, termconf->title, -1); //Connection aborted. Clean up.
} // Release data object
else if (streq(token, "btn1")) { screenSerializeToBuffer(NULL, 0, &connData->cgiData);
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);
httpdSend(connData, buff, -1);
return cont;
}
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }
if (connData->cgiData == NULL) {
httpdStartResponse(connData, 200);
httpdHeader(connData, "Content-Type", "application/octet-stream");
httpdEndHeaders(connData);
}
httpd_cgi_state cont = screenSerializeToBuffer(buff, bufsiz, &connData->cgiData);
httpdSend(connData, buff, -1); // no encode
return cont;
}
/** "About" page */ /** "About" page */
httpd_cgi_state ICACHE_FLASH_ATTR tplAbout(HttpdConnData *connData, char *token, void **arg) httpd_cgi_state ICACHE_FLASH_ATTR
tplAbout(HttpdConnData *connData, char *token, void **arg)
{ {
if (token == NULL) return HTTPD_CGI_DONE; if (token == NULL) return HTTPD_CGI_DONE;
if (streq(token, "vers_fw")) { if (streq(token, "vers_fw")) {
httpdSend(connData, FIRMWARE_VERSION, -1); tplSend(connData, FIRMWARE_VERSION, -1);
} }
else if (streq(token, "date")) { else if (streq(token, "date")) {
httpdSend(connData, __DATE__, -1); tplSend(connData, __DATE__, -1);
} }
else if (streq(token, "time")) { else if (streq(token, "time")) {
httpdSend(connData, __TIME__, -1); tplSend(connData, __TIME__, -1);
} }
else if (streq(token, "vers_httpd")) { else if (streq(token, "vers_httpd")) {
httpdSend(connData, HTTPDVER, -1); tplSend(connData, httpdGetVersion(), -1);
} }
else if (streq(token, "vers_sdk")) { else if (streq(token, "vers_sdk")) {
httpdSend(connData, STR(ESP_SDK_VERSION), -1); tplSend(connData, STR(ESP_SDK_VERSION), -1);
} }
else if (streq(token, "githubrepo")) { else if (streq(token, "githubrepo")) {
httpdSend(connData, TERMINAL_GITHUB_REPO, -1); tplSend(connData, TERMINAL_GITHUB_REPO, -1);
} }
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
+1
View File
@@ -3,5 +3,6 @@
httpd_cgi_state tplScreen(HttpdConnData *connData, char *token, void **arg); httpd_cgi_state tplScreen(HttpdConnData *connData, char *token, void **arg);
httpd_cgi_state tplAbout(HttpdConnData *connData, char *token, void **arg); httpd_cgi_state tplAbout(HttpdConnData *connData, char *token, void **arg);
httpd_cgi_state cgiTermInitialImage(HttpdConnData *connData);
#endif // CGI_MAIN_H #endif // CGI_MAIN_H
+5 -4
View File
@@ -3,6 +3,7 @@ configuring the network settings
*/ */
#include <esp8266.h> #include <esp8266.h>
#include <httpdespfs.h>
#include "cgi_network.h" #include "cgi_network.h"
#include "wifimgr.h" #include "wifimgr.h"
#include "persist.h" #include "persist.h"
@@ -27,9 +28,9 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiNetworkSetParams(HttpdConnData *connData)
{ {
static ETSTimer timer; static ETSTimer timer;
char buff[50]; char buff[20];
char redir_url_buf[100];
char redir_url_buf[300];
char *redir_url = redir_url_buf; char *redir_url = redir_url_buf;
redir_url += sprintf(redir_url, SET_REDIR_ERR); redir_url += sprintf(redir_url, SET_REDIR_ERR);
// we'll test if anything was printed by looking for \0 in failed_keys_buf // we'll test if anything was printed by looking for \0 in failed_keys_buf
@@ -200,7 +201,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiNetworkSetParams(HttpdConnData *connData)
//Template code for the WLAN page. //Template code for the WLAN page.
httpd_cgi_state ICACHE_FLASH_ATTR tplNetwork(HttpdConnData *connData, char *token, void **arg) httpd_cgi_state ICACHE_FLASH_ATTR tplNetwork(HttpdConnData *connData, char *token, void **arg)
{ {
char buff[100]; char buff[20];
u8 mac[6]; u8 mac[6];
if (token == NULL) { if (token == NULL) {
@@ -246,6 +247,6 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplNetwork(HttpdConnData *connData, char *toke
sprintf(buff, MACSTR, MAC2STR(mac)); sprintf(buff, MACSTR, MAC2STR(mac));
} }
httpdSend(connData, buff, -1); tplSend(connData, buff, -1);
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }
+1 -1
View File
@@ -20,7 +20,7 @@ verify_admin_pw(const char *pw)
httpd_cgi_state ICACHE_FLASH_ATTR httpd_cgi_state ICACHE_FLASH_ATTR
cgiPersistWriteDefaults(HttpdConnData *connData) cgiPersistWriteDefaults(HttpdConnData *connData)
{ {
char buff[50]; char buff[PASSWORD_LEN];
if (connData->conn == NULL) { if (connData->conn == NULL) {
//Connection aborted. Clean up. //Connection aborted. Clean up.
-20
View File
@@ -1,20 +0,0 @@
#include <esp8266.h>
#include <httpd.h>
#include "cgi_ping.h"
httpd_cgi_state ICACHE_FLASH_ATTR cgiPing(HttpdConnData *connData)
{
if (connData->conn==NULL) {
//Connection aborted. Clean up.
return HTTPD_CGI_DONE;
}
httpdStartResponse(connData, 200);
httpdHeader(connData, "Content-Type", "text/plain");
httpdEndHeaders(connData);
httpdSend(connData, "pong\n", -1);
return HTTPD_CGI_DONE;
}
-11
View File
@@ -1,11 +0,0 @@
#ifndef CGI_PING_H
#define CGI_PING_H
#include <esp8266.h>
#include <httpd.h>
// this is used by the UI to check if server is already restarted and working again.
httpd_cgi_state cgiPing(HttpdConnData *connData);
#endif // CGI_PING_H
-31
View File
@@ -1,31 +0,0 @@
#include <esp8266.h>
#include <httpd.h>
#include "cgi_reset.h"
static ETSTimer tmr;
static void ICACHE_FLASH_ATTR tmrCb(void *arg)
{
system_restart();
}
httpd_cgi_state ICACHE_FLASH_ATTR cgiResetDevice(HttpdConnData *connData)
{
if (connData->conn==NULL) {
//Connection aborted. Clean up.
return HTTPD_CGI_DONE;
}
httpdStartResponse(connData, 200);
httpdHeader(connData, "Content-Type", "text/plain");
httpdEndHeaders(connData);
os_timer_disarm(&tmr);
os_timer_setfn(&tmr, tmrCb, NULL);
os_timer_arm(&tmr, 100, false);
httpdSend(connData, "system reset\n", -1);
return HTTPD_CGI_DONE;
}
-9
View File
@@ -1,9 +0,0 @@
#ifndef CGI_RESET_H
#define CGI_RESET_H
#include <esp8266.h>
#include <httpd.h>
httpd_cgi_state cgiResetDevice(HttpdConnData *connData);
#endif // CGI_RESET_H
+58 -5
View File
@@ -6,12 +6,31 @@
#include "uart_driver.h" #include "uart_driver.h"
#include "screen.h" #include "screen.h"
#define SOCK_BUF_LEN 2048 #define SOCK_BUF_LEN 1024
static char sock_buff[SOCK_BUF_LEN]; static char sock_buff[SOCK_BUF_LEN];
static void notifyTimCb(void *arg) { volatile bool notify_available = true;
static ETSTimer notifyTim;
static ETSTimer notifyTim2;
// we're trying to do a kind of mutex here, without the actual primitives
// this might glitch, very rarely.
// it's recommended to put some delay between setting labels and updating the screen.
static void ICACHE_FLASH_ATTR
notifyTimCb(void *arg) {
void *data = NULL; void *data = NULL;
if (!notify_available) {
// postpone a little
os_timer_disarm(&notifyTim);
os_timer_setfn(&notifyTim, notifyTimCb, NULL);
os_timer_arm(&notifyTim, 1, 0);
return;
}
notify_available = false;
for (int i = 0; i < 20; i++) { for (int i = 0; i < 20; i++) {
httpd_cgi_state cont = screenSerializeToBuffer(sock_buff, SOCK_BUF_LEN, &data); httpd_cgi_state cont = screenSerializeToBuffer(sock_buff, SOCK_BUF_LEN, &data);
int flg = 0; int flg = 0;
@@ -21,21 +40,53 @@ static void notifyTimCb(void *arg) {
if (cont == HTTPD_CGI_DONE) break; if (cont == HTTPD_CGI_DONE) break;
} }
// cleanup
screenSerializeToBuffer(NULL, SOCK_BUF_LEN, &data); screenSerializeToBuffer(NULL, SOCK_BUF_LEN, &data);
notify_available = true;
}
static void ICACHE_FLASH_ATTR
notifyLabelsTimCb(void *arg)
{
if (!notify_available) {
// postpone a little
os_timer_disarm(&notifyTim2);
os_timer_setfn(&notifyTim2, notifyLabelsTimCb, NULL);
os_timer_arm(&notifyTim2, 1, 0);
return;
}
notify_available = false;
screenSerializeLabelsToBuffer(sock_buff, SOCK_BUF_LEN);
cgiWebsockBroadcast(URL_WS_UPDATE, sock_buff, (int) strlen(sock_buff), 0);
notify_available = true;
} }
static ETSTimer notifyTim;
/** /**
* Broadcast screen state to sockets. * Broadcast screen state to sockets.
* This is a callback for the Screen module, * This is a callback for the Screen module,
* called after each visible screen modification. * called after each visible screen modification.
*/ */
void ICACHE_FLASH_ATTR screen_notifyChange(void) void ICACHE_FLASH_ATTR screen_notifyChange(ScreenNotifyChangeTopic topic)
{ {
// this is not the most ideal/cleanest implementation
// PRs are welcome for a nicer update "queue" solution
if (topic == CHANGE_LABELS) {
// separate timer from content change timer, to avoid losing that update
os_timer_disarm(&notifyTim2);
os_timer_setfn(&notifyTim2, notifyLabelsTimCb, NULL);
os_timer_arm(&notifyTim2, SCREEN_NOTIFY_DELAY_MS, 0);
}
else if (topic == CHANGE_CONTENT) {
// throttle delay
os_timer_disarm(&notifyTim); os_timer_disarm(&notifyTim);
os_timer_setfn(&notifyTim, notifyTimCb, NULL); os_timer_setfn(&notifyTim, notifyTimCb, NULL);
os_timer_arm(&notifyTim, 20, 0); os_timer_arm(&notifyTim, SCREEN_NOTIFY_DELAY_MS, 0);
}
} }
/** Socket received a message */ /** Socket received a message */
@@ -45,6 +96,8 @@ void ICACHE_FLASH_ATTR updateSockRx(Websock *ws, char *data, int len, int flags)
// Add terminator if missing (seems to randomly happen) // Add terminator if missing (seems to randomly happen)
data[len] = 0; data[len] = 0;
// TODO re-implement those, use single byte markers and B2 encoding
dbg("Sock RX str: %s, len %d", data, len); dbg("Sock RX str: %s, len %d", data, len);
if (strstarts(data, "STR:")) { if (strstarts(data, "STR:")) {
+4 -1
View File
@@ -1,9 +1,12 @@
#ifndef CGI_SOCKETS_H #ifndef CGI_SOCKETS_H
#define CGI_SOCKETS_H #define CGI_SOCKETS_H
#define URL_WS_UPDATE "/ws/update.cgi" #define URL_WS_UPDATE "/term/update.ws"
#include "screen.h"
/** Update websocket connect callback */ /** Update websocket connect callback */
void updateSockConnect(Websock *ws); void updateSockConnect(Websock *ws);
void screen_notifyChange(ScreenNotifyChangeTopic topic);
#endif //CGI_SOCKETS_H #endif //CGI_SOCKETS_H
+165
View File
@@ -0,0 +1,165 @@
#include <esp8266.h>
#include <httpd.h>
#include <helpers.h>
#include <httpdespfs.h>
#include "cgi_system.h"
#include "persist.h"
#include "syscfg.h"
#include "uart_driver.h"
#define SET_REDIR_SUC "/cfg/system"
#define SET_REDIR_ERR SET_REDIR_SUC"?err="
static ETSTimer tmr;
static void ICACHE_FLASH_ATTR tmrCb(void *arg)
{
system_restart();
}
httpd_cgi_state ICACHE_FLASH_ATTR cgiResetDevice(HttpdConnData *connData)
{
if (connData->conn==NULL) {
//Connection aborted. Clean up.
return HTTPD_CGI_DONE;
}
httpdStartResponse(connData, 200);
httpdHeader(connData, "Content-Type", "text/plain");
httpdEndHeaders(connData);
os_timer_disarm(&tmr);
os_timer_setfn(&tmr, tmrCb, NULL);
os_timer_arm(&tmr, 100, false);
httpdSend(connData, "system reset\n", -1);
return HTTPD_CGI_DONE;
}
httpd_cgi_state ICACHE_FLASH_ATTR cgiPing(HttpdConnData *connData)
{
if (connData->conn==NULL) {
//Connection aborted. Clean up.
return HTTPD_CGI_DONE;
}
httpdStartResponse(connData, 200);
httpdHeader(connData, "Content-Type", "text/plain");
httpdEndHeaders(connData);
httpdSend(connData, "pong\n", -1);
return HTTPD_CGI_DONE;
}
/**
* Universal CGI endpoint to set Terminal params.
*/
httpd_cgi_state ICACHE_FLASH_ATTR
cgiSystemCfgSetParams(HttpdConnData *connData)
{
char buff[50];
char redir_url_buf[100];
char *redir_url = redir_url_buf;
redir_url += sprintf(redir_url, SET_REDIR_ERR);
// we'll test if anything was printed by looking for \0 in failed_keys_buf
if (connData->conn == NULL) {
//Connection aborted. Clean up.
return HTTPD_CGI_DONE;
}
if (GET_ARG("uart_baud")) {
dbg("Baud rate: %s", buff);
int baud = atoi(buff);
if (baud == BIT_RATE_300 ||
baud == BIT_RATE_600 ||
baud == BIT_RATE_1200 ||
baud == BIT_RATE_2400 ||
baud == BIT_RATE_4800 ||
baud == BIT_RATE_9600 ||
baud == BIT_RATE_19200 ||
baud == BIT_RATE_38400 ||
baud == BIT_RATE_57600 ||
baud == BIT_RATE_74880 ||
baud == BIT_RATE_115200 ||
baud == BIT_RATE_230400 ||
baud == BIT_RATE_460800 ||
baud == BIT_RATE_921600 ||
baud == BIT_RATE_1843200 ||
baud == BIT_RATE_3686400) {
sysconf->uart_baudrate = (u32) baud;
} else {
warn("Bad baud rate %s", buff);
redir_url += sprintf(redir_url, "uart_baud,");
}
}
if (GET_ARG("uart_parity")) {
dbg("Parity: %s", buff);
int parity = atoi(buff);
if (parity >= 0 && parity <= 2) {
sysconf->uart_parity = (UartParityMode) parity;
} else {
warn("Bad parity %s", buff);
redir_url += sprintf(redir_url, "uart_parity,");
}
}
if (GET_ARG("uart_stopbits")) {
dbg("Stop bits: %s", buff);
int stopbits = atoi(buff);
if (stopbits >= 1 && stopbits <= 3) {
sysconf->uart_stopbits = (UartStopBitsNum) stopbits;
} else {
warn("Bad stopbits %s", buff);
redir_url += sprintf(redir_url, "uart_stopbits,");
}
}
if (redir_url_buf[strlen(SET_REDIR_ERR)] == 0) {
// All was OK
info("Set system params - success, saving...");
sysconf_apply_settings();
persist_store();
httpdRedirect(connData, SET_REDIR_SUC);
} else {
warn("Some settings did not validate, asking for correction");
// Some errors, appended to the URL as ?err=
httpdRedirect(connData, redir_url_buf);
}
return HTTPD_CGI_DONE;
}
httpd_cgi_state ICACHE_FLASH_ATTR
tplSystemCfg(HttpdConnData *connData, char *token, void **arg)
{
#define BUFLEN 100
char buff[BUFLEN];
if (token == NULL) {
// We're done
return HTTPD_CGI_DONE;
}
strcpy(buff, ""); // fallback
if (streq(token, "uart_baud")) {
sprintf(buff, "%d", sysconf->uart_baudrate);
}
else if (streq(token, "uart_parity")) {
sprintf(buff, "%d", sysconf->uart_parity);
}
else if (streq(token, "uart_stopbits")) {
sprintf(buff, "%d", sysconf->uart_stopbits);
}
tplSend(connData, buff, -1);
return HTTPD_CGI_DONE;
}
+12
View File
@@ -0,0 +1,12 @@
#ifndef CGI_PING_H
#define CGI_PING_H
#include <esp8266.h>
#include <httpd.h>
httpd_cgi_state cgiPing(HttpdConnData *connData);
httpd_cgi_state cgiResetDevice(HttpdConnData *connData);
httpd_cgi_state cgiSystemCfgSetParams(HttpdConnData *connData);
httpd_cgi_state tplSystemCfg(HttpdConnData *connData, char *token, void **arg);
#endif // CGI_PING_H
+58 -50
View File
@@ -3,23 +3,25 @@ Cgi/template routines for configuring non-wifi settings
*/ */
#include <esp8266.h> #include <esp8266.h>
#include "cgi_appcfg.h" #include <httpdespfs.h>
#include "cgi_term_cfg.h"
#include "persist.h" #include "persist.h"
#include "screen.h" #include "screen.h"
#include "helpers.h" #include "helpers.h"
#define SET_REDIR_SUC "/cfg/app" #define SET_REDIR_SUC "/cfg/term"
#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 httpd_cgi_state ICACHE_FLASH_ATTR
cgiAppCfgSetParams(HttpdConnData *connData) cgiTermCfgSetParams(HttpdConnData *connData)
{ {
char buff[50]; char buff[50];
char redir_url_buf[100];
int n, w, h;
char redir_url_buf[300];
char *redir_url = redir_url_buf; char *redir_url = redir_url_buf;
redir_url += sprintf(redir_url, SET_REDIR_ERR); redir_url += sprintf(redir_url, SET_REDIR_ERR);
// we'll test if anything was printed by looking for \0 in failed_keys_buf // we'll test if anything was printed by looking for \0 in failed_keys_buf
@@ -32,11 +34,11 @@ cgiAppCfgSetParams(HttpdConnData *connData)
// width and height must always go together so we can do max size validation // width and height must always go together so we can do max size validation
if (GET_ARG("term_width")) { if (GET_ARG("term_width")) {
dbg("Default screen width: %s", buff); dbg("Default screen width: %s", buff);
int w = atoi(buff); w = atoi(buff);
if (w > 1) { if (w > 1) {
if (GET_ARG("term_height")) { if (GET_ARG("term_height")) {
dbg("Default screen height: %s", buff); dbg("Default screen height: %s", buff);
int h = atoi(buff); h = atoi(buff);
if (h > 1) { if (h > 1) {
if (w * h <= MAX_SCREEN_SIZE) { if (w * h <= MAX_SCREEN_SIZE) {
termconf->width = w; termconf->width = w;
@@ -62,59 +64,64 @@ cgiAppCfgSetParams(HttpdConnData *connData)
if (GET_ARG("default_bg")) { if (GET_ARG("default_bg")) {
dbg("Screen default BG: %s", buff); dbg("Screen default BG: %s", buff);
int color = atoi(buff); n = atoi(buff);
if (color >= 0 && color < 16) { if (n >= 0 && n < 16) {
termconf->default_bg = (u8) color; termconf->default_bg = (u8) n;
} else { } else {
warn("Bad color %s", buff); warn("Bad color %s", buff);
redir_url += sprintf(redir_url, "default_bg,"); redir_url += sprintf(redir_url, "default_bg,");
} }
} }
if (GET_ARG("parser_tout_ms")) {
dbg("Parser timeout: %s ms", buff);
n = atoi(buff);
if (n >= 0) {
termconf->parser_tout_ms = (u8) n;
} else {
warn("Bad timeout %s", buff);
redir_url += sprintf(redir_url, "parser_tout_ms,");
}
}
if (GET_ARG("default_fg")) { if (GET_ARG("default_fg")) {
dbg("Screen default FG: %s", buff); dbg("Screen default FG: %s", buff);
int color = atoi(buff); n = atoi(buff);
if (color >= 0 && color < 16) { if (n >= 0 && n < 16) {
termconf->default_fg = (u8) color; termconf->default_fg = (u8) n;
} else { } else {
warn("Bad color %s", buff); warn("Bad color %s", buff);
redir_url += sprintf(redir_url, "default_fg,"); redir_url += sprintf(redir_url, "default_fg,");
} }
} }
if (GET_ARG("theme")) {
dbg("Screen color theme: %s", buff);
n = atoi(buff);
if (n >= 0 && n <= 5) { // ALWAYS ADJUST WHEN ADDING NEW THEME!
termconf->theme = (u8) n;
} else {
warn("Bad theme num: %s", buff);
redir_url += sprintf(redir_url, "theme,");
}
}
if (GET_ARG("term_title")) { if (GET_ARG("term_title")) {
dbg("Terminal title default text: \"%s\"", buff); dbg("Terminal title default text: \"%s\"", buff);
strncpy_safe(termconf->title, buff, 64); // ATTN those must match the values in strncpy_safe(termconf->title, buff, 64); // ATTN those must match the values in
} }
if (GET_ARG("btn1")) { for (int i = 1; i <= 5; i++) {
dbg("Button1 default text: \"%s\"", buff); sprintf(buff, "btn%d", i);
strncpy_safe(termconf->btn1, buff, 10); if (GET_ARG(buff)) {
dbg("Button%d default text: \"%s\"", i, buff);
strncpy_safe(termconf->btn[i-1], buff, TERM_BTN_LEN);
} }
if (GET_ARG("btn2")) {
dbg("Button1 default text: \"%s\"", buff);
strncpy_safe(termconf->btn2, buff, 10);
}
if (GET_ARG("btn3")) {
dbg("Button1 default text: \"%s\"", buff);
strncpy_safe(termconf->btn3, buff, 10);
}
if (GET_ARG("btn4")) {
dbg("Button1 default text: \"%s\"", buff);
strncpy_safe(termconf->btn4, buff, 10);
}
if (GET_ARG("btn5")) {
dbg("Button1 default text: \"%s\"", buff);
strncpy_safe(termconf->btn5, buff, 10);
} }
if (redir_url_buf[strlen(SET_REDIR_ERR)] == 0) { if (redir_url_buf[strlen(SET_REDIR_ERR)] == 0) {
// All was OK // All was OK
info("Set app params - success, saving..."); info("Set term params - success, saving...");
terminal_apply_settings(); terminal_apply_settings();
persist_store(); persist_store();
@@ -130,10 +137,11 @@ cgiAppCfgSetParams(HttpdConnData *connData)
httpd_cgi_state ICACHE_FLASH_ATTR httpd_cgi_state ICACHE_FLASH_ATTR
tplAppCfg(HttpdConnData *connData, char *token, void **arg) tplTermCfg(HttpdConnData *connData, char *token, void **arg)
{ {
#define BUFLEN 100 #define BUFLEN TERM_TITLE_LEN
char buff[BUFLEN]; char buff[BUFLEN];
char buff2[10];
if (token == NULL) { if (token == NULL) {
// We're done // We're done
@@ -148,6 +156,12 @@ tplAppCfg(HttpdConnData *connData, char *token, void **arg)
else if (streq(token, "term_height")) { else if (streq(token, "term_height")) {
sprintf(buff, "%d", termconf->height); sprintf(buff, "%d", termconf->height);
} }
else if (streq(token, "parser_tout_ms")) {
sprintf(buff, "%d", termconf->parser_tout_ms);
}
else if (streq(token, "theme")) {
sprintf(buff, "%d", termconf->theme);
}
else if (streq(token, "default_bg")) { else if (streq(token, "default_bg")) {
sprintf(buff, "%d", termconf->default_bg); sprintf(buff, "%d", termconf->default_bg);
} }
@@ -157,22 +171,16 @@ tplAppCfg(HttpdConnData *connData, char *token, void **arg)
else if (streq(token, "term_title")) { else if (streq(token, "term_title")) {
strncpy_safe(buff, termconf->title, BUFLEN); strncpy_safe(buff, termconf->title, BUFLEN);
} }
else if (streq(token, "btn1")) { else {
strncpy_safe(buff, termconf->btn1, BUFLEN); for (int i = 1; i <= 5; i++) {
sprintf(buff2, "btn%d", i);
if (streq(token, buff2)) {
strncpy_safe(buff, termconf->btn[i-1], BUFLEN);
break;
} }
else if (streq(token, "btn2")) {
strncpy_safe(buff, termconf->btn2, BUFLEN);
} }
else if (streq(token, "btn3")) {
strncpy_safe(buff, termconf->btn3, BUFLEN);
}
else if (streq(token, "btn4")) {
strncpy_safe(buff, termconf->btn4, BUFLEN);
}
else if (streq(token, "btn5")) {
strncpy_safe(buff, termconf->btn5, BUFLEN);
} }
httpdSend(connData, buff, -1); tplSend(connData, buff, -1);
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }
+9
View File
@@ -0,0 +1,9 @@
#ifndef CGIAPPCFG_H
#define CGIAPPCFG_H
#include "httpd.h"
httpd_cgi_state cgiTermCfgSetParams(HttpdConnData *connData);
httpd_cgi_state tplTermCfg(HttpdConnData *connData, char *token, void **arg);
#endif
+50 -10
View File
@@ -14,6 +14,7 @@ Cgi/template routines for the /wifi url.
*/ */
#include <esp8266.h> #include <esp8266.h>
#include <httpdespfs.h>
#include "cgi_wifi.h" #include "cgi_wifi.h"
#include "wifimgr.h" #include "wifimgr.h"
#include "persist.h" #include "persist.h"
@@ -183,9 +184,8 @@ static void ICACHE_FLASH_ATTR wifiStartScan(void)
} }
/** /**
* This CGI is called from the bit of AJAX-code in wifi.tpl. It will initiate a * Start a scan and return a result of an earlier scan, if available.
* scan for access points and if available will return the result of an earlier scan. * The STA is switched ON if disabled.
* The result is embedded in a bit of JSON parsed by the javascript in wifi.tpl.
*/ */
httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiScan(HttpdConnData *connData) httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiScan(HttpdConnData *connData)
{ {
@@ -193,6 +193,17 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiScan(HttpdConnData *connData)
int len; int len;
char buff[256]; char buff[256];
if (connData->conn == NULL) {
//Connection aborted. Clean up.
return HTTPD_CGI_DONE;
}
// auto-turn on STA
if ((wificonf->opmode & STATION_MODE) == 0) {
wificonf->opmode |= STATION_MODE;
wifimgr_apply_settings();
}
// 2nd and following runs of the function via MORE: // 2nd and following runs of the function via MORE:
if (!cgiWifiAps.scanInProgress && pos != 0) { if (!cgiWifiAps.scanInProgress && pos != 0) {
// Fill in json code for an access point // Fill in json code for an access point
@@ -259,6 +270,13 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiConnStatus(HttpdConnData *connData)
char buff[100]; char buff[100];
struct ip_info info; struct ip_info info;
buff[0] = 0; // avoid unitialized read
if (connData->conn == NULL) {
//Connection aborted. Clean up.
return HTTPD_CGI_DONE;
}
httpdStartResponse(connData, 200); httpdStartResponse(connData, 200);
httpdHeader(connData, "Content-Type", "application/json"); httpdHeader(connData, "Content-Type", "application/json");
httpdEndHeaders(connData); httpdEndHeaders(connData);
@@ -270,6 +288,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiConnStatus(HttpdConnData *connData)
} }
STATION_STATUS st = wifi_station_get_connect_status(); STATION_STATUS st = wifi_station_get_connect_status();
dbg("CONN STATE = %d", st);
switch(st) { switch(st) {
case STATION_IDLE: case STATION_IDLE:
sprintf(buff, "{\"status\": \"idle\"}"); // unclear when this is used sprintf(buff, "{\"status\": \"idle\"}"); // unclear when this is used
@@ -295,6 +314,11 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiConnStatus(HttpdConnData *connData)
wifi_get_ip_info(STATION_IF, &info); wifi_get_ip_info(STATION_IF, &info);
sprintf(buff, "{\"status\": \"success\", \"ip\": \""IPSTR"\"}", GOOD_IP2STR(info.ip.addr)); sprintf(buff, "{\"status\": \"success\", \"ip\": \""IPSTR"\"}", GOOD_IP2STR(info.ip.addr));
break; break;
default:
sprintf(buff, "{\"status\": \"working\", \"wtf\": \"state = %d\"}", st);
break;
} }
httpdSend(connData, buff, -1); httpdSend(connData, buff, -1);
@@ -319,8 +343,8 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetParams(HttpdConnData *connData)
static ETSTimer timer; static ETSTimer timer;
char buff[50]; char buff[50];
char redir_url_buf[100]; // this is just barely enough - but it's split into two forms, so we never have error in all fields
char redir_url_buf[300];
char *redir_url = redir_url_buf; char *redir_url = redir_url_buf;
redir_url += sprintf(redir_url, SET_REDIR_ERR); redir_url += sprintf(redir_url, SET_REDIR_ERR);
// we'll test if anything was printed by looking for \0 in failed_keys_buf // we'll test if anything was printed by looking for \0 in failed_keys_buf
@@ -330,6 +354,9 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetParams(HttpdConnData *connData)
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }
bool sta_turned_on = false;
bool sta_ssid_pw_changed = false;
// ---- WiFi opmode ---- // ---- WiFi opmode ----
if (GET_ARG("opmode")) { if (GET_ARG("opmode")) {
@@ -360,6 +387,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetParams(HttpdConnData *connData)
if (enable) { if (enable) {
wificonf->opmode |= STATION_MODE; wificonf->opmode |= STATION_MODE;
sta_turned_on = true;
} else { } else {
wificonf->opmode &= ~STATION_MODE; wificonf->opmode &= ~STATION_MODE;
} }
@@ -457,6 +485,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetParams(HttpdConnData *connData)
info("Setting station SSID to: \"%s\"", buff); info("Setting station SSID to: \"%s\"", buff);
strncpy_safe(wificonf->sta_ssid, buff, SSID_LEN); strncpy_safe(wificonf->sta_ssid, buff, SSID_LEN);
wifi_change_flags.sta = true; wifi_change_flags.sta = true;
sta_ssid_pw_changed = true;
} }
} }
@@ -468,12 +497,13 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetParams(HttpdConnData *connData)
info("Setting station password to: \"%s\"", buff); info("Setting station password to: \"%s\"", buff);
strncpy_safe(wificonf->sta_password, buff, PASSWORD_LEN); strncpy_safe(wificonf->sta_password, buff, PASSWORD_LEN);
wifi_change_flags.sta = true; wifi_change_flags.sta = true;
sta_ssid_pw_changed = true;
} }
} }
if (redir_url_buf[strlen(SET_REDIR_ERR)] == 0) { if (redir_url_buf[strlen(SET_REDIR_ERR)] == 0) {
// All was OK // All was OK
info("Set WiFi params - success, applying in 1000 ms"); info("Set WiFi params - success, applying in 2000 ms");
// Settings are applied only if all was OK // Settings are applied only if all was OK
// //
@@ -486,9 +516,19 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetParams(HttpdConnData *connData)
// If user connects via the Station IF, they may not even notice the connection reset. // If user connects via the Station IF, they may not even notice the connection reset.
os_timer_disarm(&timer); os_timer_disarm(&timer);
os_timer_setfn(&timer, applyWifiSettingsLaterCb, NULL); os_timer_setfn(&timer, applyWifiSettingsLaterCb, NULL);
os_timer_arm(&timer, 1000, false); os_timer_arm(&timer, 2000, false);
if ((sta_ssid_pw_changed || sta_turned_on)
&& wificonf->opmode != SOFTAP_MODE
&& wificonf->sta_ssid[0] != 0) {
// User wants to connect
info("User wants to connect to SSID, redirecting to ConnStatus page.");
httpdRedirect(connData, "/cfg/wifi/connecting");
}
else {
httpdRedirect(connData, SET_REDIR_SUC); httpdRedirect(connData, SET_REDIR_SUC);
}
} else { } else {
warn("Some WiFi settings did not validate, asking for correction"); warn("Some WiFi settings did not validate, asking for correction");
// Some errors, appended to the URL as ?err= // Some errors, appended to the URL as ?err=
@@ -501,7 +541,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetParams(HttpdConnData *connData)
//Template code for the WLAN page. //Template code for the WLAN page.
httpd_cgi_state ICACHE_FLASH_ATTR tplWlan(HttpdConnData *connData, char *token, void **arg) httpd_cgi_state ICACHE_FLASH_ATTR tplWlan(HttpdConnData *connData, char *token, void **arg)
{ {
char buff[100]; char buff[PASSWORD_LEN];
int x; int x;
int connectStatus; int connectStatus;
@@ -552,7 +592,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplWlan(HttpdConnData *connData, char *token,
// For display of our current SSID // For display of our current SSID
connectStatus = wifi_station_get_connect_status(); connectStatus = wifi_station_get_connect_status();
x = wifi_get_opmode(); x = wifi_get_opmode();
if (x == SOFTAP_MODE || connectStatus != STATION_GOT_IP) { if (x == SOFTAP_MODE || connectStatus != STATION_GOT_IP || wificonf->opmode == SOFTAP_MODE) {
strcpy(buff, ""); strcpy(buff, "");
} }
else { else {
@@ -565,7 +605,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplWlan(HttpdConnData *connData, char *token,
x = wifi_get_opmode(); x = wifi_get_opmode();
connectStatus = wifi_station_get_connect_status(); connectStatus = wifi_station_get_connect_status();
if (x == SOFTAP_MODE || connectStatus != STATION_GOT_IP) { if (x == SOFTAP_MODE || connectStatus != STATION_GOT_IP || wificonf->opmode == SOFTAP_MODE) {
strcpy(buff, ""); strcpy(buff, "");
} }
else { else {
@@ -580,6 +620,6 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplWlan(HttpdConnData *connData, char *token,
} }
} }
httpdSend(connData, buff, -1); tplSend(connData, buff, -1);
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }
+44 -10
View File
@@ -17,21 +17,45 @@ static void ICACHE_FLASH_ATTR
apply_live_settings(void) apply_live_settings(void)
{ {
dbg("[Persist] Applying live settings..."); dbg("[Persist] Applying live settings...");
terminal_apply_settings();
dbg("[Persist] > system");
sysconf_apply_settings();
dbg("[Persist] > wifi");
wifimgr_apply_settings(); wifimgr_apply_settings();
dbg("[Persist] > terminal");
terminal_apply_settings();
dbg("[Persist] Live settings applied.");
// ... // ...
} }
static void ICACHE_FLASH_ATTR static void ICACHE_FLASH_ATTR
restore_live_settings_to_hard_defaults(void) restore_live_settings_to_hard_defaults(void)
{ {
dbg("[Persist] Restore to hard defaults...");
dbg("[Persist] > system");
sysconf_restore_defaults();
dbg("[Persist] > wifi");
wifimgr_restore_defaults(); wifimgr_restore_defaults();
dbg("[Persist] > terminal");
terminal_restore_defaults(); terminal_restore_defaults();
dbg("[Persist] Restored to hard defaults.");
// ... // ...
} }
//endregion //endregion
const u32 wconf_at = (u32)&persist.defaults.wificonf - (u32)&persist.defaults;
const u32 tconf_at = (u32)&persist.defaults.termconf - (u32)&persist.defaults;
const u32 sconf_at = (u32)&persist.defaults.sysconf - (u32)&persist.defaults;
const u32 cksum_at = (u32)&persist.defaults.checksum - (u32)&persist.defaults;
/** /**
* Compute CRC32. Adapted from https://github.com/esp8266/Arduino * Compute CRC32. Adapted from https://github.com/esp8266/Arduino
* @param data * @param data
@@ -41,7 +65,9 @@ restore_live_settings_to_hard_defaults(void)
static uint32_t ICACHE_FLASH_ATTR static uint32_t ICACHE_FLASH_ATTR
calculateCRC32(const uint8_t *data, size_t length) calculateCRC32(const uint8_t *data, size_t length)
{ {
uint32_t crc = 0xffffffff; // the salt here should ensure settings are wiped when the structure changes
// CHECKSUM_SALT can be adjusted manually to force a reset.
uint32_t crc = 0xffffffff + CHECKSUM_SALT + ((wconf_at << 16) ^ (tconf_at << 10) ^ (sconf_at << 5));
while (length--) { while (length--) {
uint8_t c = *data++; uint8_t c = *data++;
for (uint32_t i = 0x80; i > 0; i >>= 1) { for (uint32_t i = 0x80; i > 0; i >>= 1) {
@@ -67,7 +93,8 @@ calculateCRC32(const uint8_t *data, size_t length)
static uint32_t ICACHE_FLASH_ATTR static uint32_t ICACHE_FLASH_ATTR
compute_checksum(AppConfigBundle *bundle) compute_checksum(AppConfigBundle *bundle)
{ {
return calculateCRC32((uint8_t *) bundle, sizeof(AppConfigBundle) - 4) ^ CHECKSUM_SALT; // this should be the bundle without the checksum
return calculateCRC32((uint8_t *) bundle, sizeof(AppConfigBundle) - 4);
} }
/** /**
@@ -78,10 +105,12 @@ 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("AppConfigBundle memory map:");
dbg("sizeof(PersistBlock) = %d bytes", sizeof(PersistBlock)); dbg("> WiFiConfigBundle at %4d (error %2d)", wconf_at, wconf_at - 0);
dbg("sizeof(WiFiConfigBundle) = %d bytes", sizeof(WiFiConfigBundle)); dbg("> SystemConfigBundle at %4d (error %2d)", sconf_at, sconf_at - WIFICONF_SIZE);
dbg("sizeof(TerminalConfigBundle) = %d bytes", sizeof(TerminalConfigBundle)); dbg("> TerminalConfigBundle at %4d (error %2d)", tconf_at, tconf_at - WIFICONF_SIZE - SYSCONF_SIZE);
dbg("> Checksum at %4d (error %2d)", cksum_at, cksum_at - (APPCONF_SIZE - 4));
dbg("> Total size = %d bytes (error %d)", sizeof(AppConfigBundle), APPCONF_SIZE - sizeof(AppConfigBundle));
bool hard_reset = false; bool hard_reset = false;
@@ -99,7 +128,15 @@ persist_load(void)
} }
if (hard_reset) { if (hard_reset) {
// Zero all out
memset(&persist, 0, sizeof(PersistBlock));
persist_load_hard_default(); persist_load_hard_default();
// write them also as defaults
memcpy(&persist.defaults, &persist.current, sizeof(AppConfigBundle));
persist_store();
// this also stores them to flash and applies to modules // this also stores them to flash and applies to modules
} else { } else {
apply_live_settings(); apply_live_settings();
@@ -135,9 +172,6 @@ persist_load_hard_default(void)
restore_live_settings_to_hard_defaults(); restore_live_settings_to_hard_defaults();
persist_store(); persist_store();
// // Store current -> default
// memcpy(&persist.defaults, &persist.current, sizeof(AppConfigBundle));
info("[Persist] Settings restored to hard defaults."); info("[Persist] Settings restored to hard defaults.");
apply_live_settings(); // apply apply_live_settings(); // apply
+18 -5
View File
@@ -12,19 +12,26 @@
#include "wifimgr.h" #include "wifimgr.h"
#include "screen.h" #include "screen.h"
#include "syscfg.h"
// Changing this could be used to force-erase the config area // Changing this could be used to force-erase the config area
// after a firmware upgrade // after a firmware upgrade
#define CHECKSUM_SALT 0x5F5F5F5F #define CHECKSUM_SALT 3
#define APPCONF_SIZE 2048
/** Struct for current or default settings */ /** Struct for current or default settings */
typedef struct { typedef struct { // the entire block should be 1024 bytes long (for compatibility across upgrades)
WiFiConfigBundle wificonf; WiFiConfigBundle wificonf;
uint8_t _filler1[WIFICONF_SIZE - sizeof(WiFiConfigBundle)];
SystemConfigBundle sysconf;
uint8_t _filler3[SYSCONF_SIZE - sizeof(SystemConfigBundle)];
TerminalConfigBundle termconf; TerminalConfigBundle termconf;
uint8_t _filler2[TERMCONF_SIZE - sizeof(TerminalConfigBundle)];
// --- Space for future settings --- // --- Space for future settings ---
// Original size: 1024
//
// The size must be appropriately reduced each time something is added, // The size must be appropriately reduced each time something is added,
// and boolean flags defaulting to 0 should be used to detect unpopulated // and boolean flags defaulting to 0 should be used to detect unpopulated
// sections that must be restored to defaults on load. // sections that must be restored to defaults on load.
@@ -32,7 +39,13 @@ typedef struct {
// This ensures user settings are not lost each time they upgrade the firmware, // 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 // which would lead to a checksum mismatch if the structure was changed and
// it grew to a different memory area. // it grew to a different memory area.
uint8_t filler[1024]; uint8_t _filler_end[
APPCONF_SIZE
- sizeof(uint32_t) // checksum
- WIFICONF_SIZE
- SYSCONF_SIZE
- TERMCONF_SIZE
];
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;
+10 -9
View File
@@ -6,12 +6,11 @@
#include "routes.h" #include "routes.h"
#include "cgi_wifi.h" #include "cgi_wifi.h"
#include "cgi_reset.h" #include "cgi_system.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_network.h"
#include "cgi_appcfg.h" #include "cgi_term_cfg.h"
#include "cgi_persist.h" #include "cgi_persist.h"
#define WIFI_PROTECT 0 #define WIFI_PROTECT 0
@@ -33,6 +32,7 @@ HttpdBuiltInUrl routes[] = {
ROUTE_FILE("/help/?", "/help.tpl"), ROUTE_FILE("/help/?", "/help.tpl"),
// --- Sockets --- // --- Sockets ---
ROUTE_CGI("/term/init", cgiTermInitialImage),
ROUTE_WS(URL_WS_UPDATE, updateSockConnect), ROUTE_WS(URL_WS_UPDATE, updateSockConnect),
// --- System control --- // --- System control ---
@@ -54,13 +54,14 @@ HttpdBuiltInUrl routes[] = {
ROUTE_TPL_FILE("/cfg/network/?", tplNetwork, "/cfg_network.tpl"), ROUTE_TPL_FILE("/cfg/network/?", tplNetwork, "/cfg_network.tpl"),
ROUTE_CGI("/cfg/network/set", cgiNetworkSetParams), ROUTE_CGI("/cfg/network/set", cgiNetworkSetParams),
ROUTE_TPL_FILE("/cfg/app/?", tplAppCfg, "/cfg_app.tpl"), ROUTE_TPL_FILE("/cfg/term/?", tplTermCfg, "/cfg_term.tpl"),
ROUTE_CGI("/cfg/app/set", cgiAppCfgSetParams), ROUTE_CGI("/cfg/term/set", cgiTermCfgSetParams),
ROUTE_FILE("/cfg/admin/?", "/cfg_admin.tpl"), ROUTE_TPL_FILE("/cfg/system/?", tplSystemCfg, "/cfg_system.tpl"),
ROUTE_CGI("/cfg/admin/write_defaults", cgiPersistWriteDefaults), ROUTE_CGI("/cfg/system/set", cgiSystemCfgSetParams),
ROUTE_CGI("/cfg/admin/restore_defaults", cgiPersistRestoreDefaults), ROUTE_CGI("/cfg/system/write_defaults", cgiPersistWriteDefaults),
ROUTE_CGI("/cfg/admin/restore_hard", cgiPersistRestoreHard), ROUTE_CGI("/cfg/system/restore_defaults", cgiPersistRestoreDefaults),
ROUTE_CGI("/cfg/system/restore_hard", cgiPersistRestoreHard),
ROUTE_FILESYSTEM(), ROUTE_FILESYSTEM(),
ROUTE_END(), ROUTE_END(),
+129 -60
View File
@@ -8,6 +8,9 @@
TerminalConfigBundle * const termconf = &persist.current.termconf; TerminalConfigBundle * const termconf = &persist.current.termconf;
TerminalConfigBundle termconf_scratch; TerminalConfigBundle termconf_scratch;
#define W termconf_scratch.width
#define H termconf_scratch.height
/** /**
* Restore hard defaults * Restore hard defaults
*/ */
@@ -17,12 +20,11 @@ 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;
termconf->parser_tout_ms = 10;
sprintf(termconf->title, "ESPTerm"); sprintf(termconf->title, "ESPTerm");
sprintf(termconf->btn1, "1"); for(int i=1; i <= 5; i++) {
sprintf(termconf->btn2, "2"); sprintf(termconf->btn[i-1], "%d", i);
sprintf(termconf->btn3, "3"); }
sprintf(termconf->btn4, "4");
sprintf(termconf->btn5, "5");
} }
/** /**
@@ -31,12 +33,16 @@ void terminal_restore_defaults(void)
void terminal_apply_settings(void) void terminal_apply_settings(void)
{ {
memcpy(&termconf_scratch, termconf, sizeof(TerminalConfigBundle)); memcpy(&termconf_scratch, termconf, sizeof(TerminalConfigBundle));
if (W*H >= MAX_SCREEN_SIZE) {
error("BAD SCREEN SIZE: %d rows x %d cols", H, W);
error("reverting terminal settings to default");
terminal_restore_defaults();
persist_store();
memcpy(&termconf_scratch, termconf, sizeof(TerminalConfigBundle));
}
screen_init(); screen_init();
} }
#define W termconf_scratch.width
#define H termconf_scratch.height
/** /**
* Highest permissible value of the color attribute * Highest permissible value of the color attribute
*/ */
@@ -46,9 +52,10 @@ void terminal_apply_settings(void)
* Screen cell data type (16 bits) * Screen cell data type (16 bits)
*/ */
typedef struct __attribute__((packed)){ typedef struct __attribute__((packed)){
char c : 8; char c[4]; // space for a full unicode character
Color fg : 4; Color fg : 4;
Color bg : 4; Color bg : 4;
bool bold : 1;
} Cell; } Cell;
/** /**
@@ -65,6 +72,7 @@ static struct {
bool visible; //!< Visible bool visible; //!< Visible
bool inverse; //!< Inverse colors bool inverse; //!< Inverse colors
bool autowrap; //!< Wrapping when EOL bool autowrap; //!< Wrapping when EOL
bool bold; //!< Bold style
Color fg; //!< Foreground color for writing Color fg; //!< Foreground color for writing
Color bg; //!< Background color for writing Color bg; //!< Background color for writing
} cursor; } cursor;
@@ -96,7 +104,7 @@ static volatile int notifyLock = 0;
#define NOTIFY_DONE() do { \ #define NOTIFY_DONE() do { \
if (notifyLock > 0) notifyLock--; \ if (notifyLock > 0) notifyLock--; \
if (notifyLock == 0) screen_notifyChange(); \ if (notifyLock == 0) screen_notifyChange(CHANGE_CONTENT); \
} while(0) } while(0)
/** /**
@@ -105,12 +113,17 @@ static volatile int notifyLock = 0;
static inline void static inline void
clear_range(unsigned int from, unsigned int to) clear_range(unsigned int from, unsigned int to)
{ {
if (to >= W*H) to = W*H-1;
Color fg = cursor.inverse ? cursor.bg : cursor.fg; Color fg = cursor.inverse ? cursor.bg : cursor.fg;
Color bg = cursor.inverse ? cursor.fg : cursor.bg; Color bg = cursor.inverse ? cursor.fg : cursor.bg;
for (unsigned int i = from; i <= to; i++) { for (unsigned int i = from; i <= to; i++) {
screen[i].c = ' '; screen[i].c[0] = ' ';
screen[i].c[1] = 0;
screen[i].c[2] = 0;
screen[i].c[3] = 0;
screen[i].fg = fg; screen[i].fg = fg;
screen[i].bg = bg; screen[i].bg = bg;
screen[i].bold = false;
} }
} }
@@ -127,6 +140,7 @@ cursor_reset(void)
cursor.visible = 1; cursor.visible = 1;
cursor.inverse = 0; cursor.inverse = 0;
cursor.autowrap = 1; cursor.autowrap = 1;
cursor.bold = 0;
} }
//endregion //endregion
@@ -156,6 +170,20 @@ screen_reset(void)
NOTIFY_DONE(); NOTIFY_DONE();
} }
/**
* Reset the cursor
*/
void ICACHE_FLASH_ATTR
screen_reset_cursor(void)
{
NOTIFY_LOCK();
cursor.fg = termconf_scratch.default_fg;
cursor.bg = termconf_scratch.default_bg;
cursor.inverse = 0;
cursor.bold = 0;
NOTIFY_DONE();
}
/** /**
* Clear screen area * Clear screen area
*/ */
@@ -352,8 +380,8 @@ screen_cursor_move(int dy, int dx)
cursor.x += dx; cursor.x += dx;
cursor.y += dy; cursor.y += dy;
if (cursor.x >= W) cursor.x = W - 1; if (cursor.x >= (int)W) cursor.x = W - 1;
if (cursor.x < 0) cursor.x = 0; if (cursor.x < (int)0) cursor.x = 0;
if (cursor.y < 0) { if (cursor.y < 0) {
move = -cursor.y; move = -cursor.y;
@@ -482,11 +510,18 @@ screen_inverse(bool inverse)
* This relates to the '1' SGR command which originally means * This relates to the '1' SGR command which originally means
* "bold font". We interpret that as "Bright", similar to other * "bold font". We interpret that as "Bright", similar to other
* terminal emulators. * terminal emulators.
*
* Note that the bright colors can be used without bold using the 90+ codes
*/ */
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
screen_set_bright_fg(void) screen_set_bold(bool bold)
{ {
cursor.fg = (cursor.fg % 8) + 8; if (!bold) {
cursor.fg = (Color) (cursor.fg % 8);
} else {
cursor.fg = (Color) ((cursor.fg % 8) + 8); // change anything to the bright colors
}
cursor.bold = bold;
} }
//endregion //endregion
@@ -507,14 +542,14 @@ bool ICACHE_FLASH_ATTR screen_isCoordValid(int y, int x)
* Set a character in the cursor color, move to right with wrap. * Set a character in the cursor color, move to right with wrap.
*/ */
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
screen_putchar(char ch) screen_putchar(const char *ch)
{ {
NOTIFY_LOCK(); NOTIFY_LOCK();
Cell *c = &screen[cursor.x + cursor.y * W]; Cell *c = &screen[cursor.x + cursor.y * W];
// Special treatment for CRLF // Special treatment for CRLF
switch (ch) { switch (ch[0]) {
case '\r': case '\r':
screen_cursor_set_x(0); screen_cursor_set_x(0);
goto done; goto done;
@@ -535,27 +570,34 @@ screen_putchar(char ch)
} }
// erase target cell // erase target cell
c = &screen[cursor.x + cursor.y * W]; c = &screen[cursor.x + cursor.y * W];
c->c = ' '; c->c[0] = ' ';
c->c[1] = 0;
c->c[2] = 0;
c->c[3] = 0;
goto done; goto done;
case 9: // TAB case 9: // TAB
if (cursor.x<((W-1)-(W-1)%4)) { if (cursor.x<((W-1)-(W-1)%4)) {
c->c = ' '; c->c[0] = ' ';
c->c[1] = 0;
c->c[2] = 0;
c->c[3] = 0;
do { do {
screen_putchar(' '); screen_putchar(" ");
} while(cursor.x%4!=0); } while(cursor.x%4!=0);
} }
goto done; goto done;
default: default:
if (ch < ' ') { if (ch[0] < ' ') {
// Discard // Discard
warn("Ignoring control char %d", (int)ch); warn("Ignoring control char %d", (int)ch);
goto done; goto done;
} }
} }
c->c = ch; // copy unicode char
strncpy(c->c, ch, 4);
if (cursor.inverse) { if (cursor.inverse) {
c->fg = cursor.bg; c->fg = cursor.bg;
@@ -564,6 +606,7 @@ screen_putchar(char ch)
c->fg = cursor.fg; c->fg = cursor.fg;
c->bg = cursor.bg; c->bg = cursor.bg;
} }
c->bold = cursor.bold;
cursor.x++; cursor.x++;
// X wrap // X wrap
@@ -627,10 +670,38 @@ void screen_dd(void)
struct ScreenSerializeState { struct ScreenSerializeState {
Color lastFg; Color lastFg;
Color lastBg; Color lastBg;
char lastChar; bool lastBold;
char lastChar[4];
int index; int index;
}; };
void ICACHE_FLASH_ATTR
encode2B(u16 number, WordB2 *stru)
{
stru->lsb = (u8) (number % 127);
stru->msb = (u8) ((number - stru->lsb) / 127 + 1);
stru->lsb += 1;
}
/**
* buffer should be at least 64+5*10+6 long (title + buttons + 6), ie. 120
* @param buffer
* @param buf_len
*/
void ICACHE_FLASH_ATTR
screenSerializeLabelsToBuffer(char *buffer, size_t buf_len)
{
// let's just assume it's long enough - called with the huge websocket buffer
sprintf(buffer, "T%s\x01%s\x01%s\x01%s\x01%s\x01%s", // use 0x01 as separator
termconf_scratch.title,
termconf_scratch.btn[0],
termconf_scratch.btn[1],
termconf_scratch.btn[2],
termconf_scratch.btn[3],
termconf_scratch.btn[4]
);
}
/** /**
* Serialize the screen to a data buffer. May need multiple calls if the buffer is insufficient in size. * Serialize the screen to a data buffer. May need multiple calls if the buffer is insufficient in size.
* *
@@ -654,6 +725,7 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, void **data)
} }
Cell *cell, *cell0; Cell *cell, *cell0;
WordB2 w1, w2, w3, w4, w5;
size_t remain = buf_len; int used = 0; size_t remain = buf_len; int used = 0;
char *bb = buffer; char *bb = buffer;
@@ -669,9 +741,22 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, void **data)
ss->index = 0; ss->index = 0;
ss->lastBg = 0; ss->lastBg = 0;
ss->lastFg = 0; ss->lastFg = 0;
ss->lastChar = '\0'; ss->lastBold = false;
memset(ss->lastChar, 0, 4); // this ensures the first char is never "repeat"
bufprint("{\n \"w\": %d, \"h\": %d,\n \"x\": %d, \"y\": %d,\n \"cv\": %d,\n \"screen\": \"", W, H, cursor.x, cursor.y, cursor.visible); encode2B((u16) H, &w1);
encode2B((u16) W, &w2);
encode2B((u16) cursor.y, &w3);
encode2B((u16) cursor.x, &w4);
encode2B((u16) (
cursor.fg |
(cursor.bg<<4) |
(cursor.bold?0x100:0) |
(cursor.visible?0x200:0))
, &w5);
// H W X Y Attribs
bufprint("S%c%c%c%c%c%c%c%c%c%c", w1.lsb, w1.msb, w2.lsb, w2.msb, w3.lsb, w3.msb, w4.lsb, w4.msb, w5.lsb, w5.msb);
} }
int i = ss->index; int i = ss->index;
@@ -683,52 +768,41 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, void **data)
while (i < W*H while (i < W*H
&& cell->fg == ss->lastFg && cell->fg == ss->lastFg
&& cell->bg == ss->lastBg && cell->bg == ss->lastBg
&& cell->c == ss->lastChar) { && cell->bold == ss->lastBold
&& strneq(cell->c, ss->lastChar, 4)) {
// Repeat // Repeat
repCnt++; repCnt++;
cell = &screen[++i]; cell = &screen[++i];
} }
if (repCnt == 0) { if (repCnt == 0) {
// All this crap is needed because it's JSON and also // No repeat
// embedded in HTML (hence the angle brackets) if (cell0->bold != ss->lastBold || cell0->fg != ss->lastFg || cell0->bg != ss->lastBg) {
encode2B((u16) (
if (cell0->fg == ss->lastFg && cell0->bg == ss->lastBg) { cell0->fg |
// same colors as previous (cell0->bg<<4) |
bufprint(","); (cell0->bold?0x100:0))
} else { , &w1);
bufprint("%X%X", cell0->fg, cell0->bg); bufprint("\x01%c%c", w1.lsb, w1.msb);
} }
char c = cell0->c; // copy the symbol, until first 0 or reached 4 bytes
if (c == '"' || c == '\\') { char c;
bufprint("\\%c", c); int j = 0;
} while ((c = cell->c[j++]) != 0 && j < 4) {
else if (c == '<' || c == '>' || c == '\'' || c == '/' || c == '&') {
bufprint("\\u00%02X", (int)c);
}
else {
bufprint("%c", c); bufprint("%c", c);
} }
ss->lastFg = cell0->fg; ss->lastFg = cell0->fg;
ss->lastBg = cell0->bg; ss->lastBg = cell0->bg;
ss->lastChar = cell0->c; ss->lastBold = cell0->bold;
memcpy(ss->lastChar, cell0->c, 4);
i++; i++;
} else { } else {
char code; // Repeat count
if(repCnt<10) { encode2B((u16) repCnt, &w1);
code = 'r'; bufprint("\x02%c%c", w1.lsb, w1.msb);
} else if(repCnt<100) {
code = 's';
} else if(repCnt<1000) {
code = 't';
} else {
code = 'u';
}
bufprint("%c%d", code, repCnt);
} }
} }
@@ -738,12 +812,7 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, void **data)
return HTTPD_CGI_MORE; return HTTPD_CGI_MORE;
} }
if (remain >= 3) {
bufprint("\"\n}");
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} else {
return HTTPD_CGI_MORE;
}
} }
//endregion //endregion
+41 -11
View File
@@ -34,17 +34,29 @@
* *
*/ */
// Size designed for the wifi config structure
// Must be constant to avoid corrupting user config after upgrade
#define TERMCONF_SIZE 200
#define TERM_BTN_LEN 10
#define TERM_TITLE_LEN 64
typedef enum {
CHANGE_CONTENT,
CHANGE_LABELS,
} ScreenNotifyChangeTopic;
#define SCREEN_NOTIFY_DELAY_MS 20
typedef struct { typedef struct {
u32 width; u32 width;
u32 height; u32 height;
u8 default_bg; u8 default_bg;
u8 default_fg; u8 default_fg;
char title[64]; char title[TERM_TITLE_LEN];
char btn1[10]; char btn[5][TERM_BTN_LEN];
char btn2[10]; u8 theme;
char btn3[10]; u32 parser_tout_ms;
char btn4[10];
char btn5[10];
} TerminalConfigBundle; } TerminalConfigBundle;
// Live config // Live config
@@ -65,7 +77,7 @@ void terminal_apply_settings(void);
* TODO May need adjusting if there are size problems when flashing the ESP. * TODO May need adjusting if there are size problems when flashing the ESP.
* We could also try to pack the Cell struct to a single 32bit word. * We could also try to pack the Cell struct to a single 32bit word.
*/ */
#define MAX_SCREEN_SIZE (80*25) #define MAX_SCREEN_SIZE (80*30)
typedef enum { typedef enum {
CLEAR_TO_CURSOR=0, CLEAR_FROM_CURSOR=1, CLEAR_ALL=2 CLEAR_TO_CURSOR=0, CLEAR_FROM_CURSOR=1, CLEAR_ALL=2
@@ -75,6 +87,17 @@ typedef uint8_t Color;
httpd_cgi_state screenSerializeToBuffer(char *buffer, size_t buf_len, void **data); httpd_cgi_state screenSerializeToBuffer(char *buffer, size_t buf_len, void **data);
void screenSerializeLabelsToBuffer(char *buffer, size_t buf_len);
typedef struct {
u8 lsb;
u8 msb;
} WordB2;
/** Encode number to two nice ASCII bytes */
void encode2B(u16 number, WordB2 *stru);
/** Init the screen */ /** Init the screen */
void screen_init(void); void screen_init(void);
@@ -115,6 +138,9 @@ void screen_cursor_set_x(int x);
/** Set cursor Y position */ /** Set cursor Y position */
void screen_cursor_set_y(int y); void screen_cursor_set_y(int y);
/** Reset cursor attribs */
void screen_reset_cursor(void);
/** Relative cursor move */ /** Relative cursor move */
void screen_cursor_move(int dy, int dx); void screen_cursor_move(int dy, int dx);
@@ -139,7 +165,7 @@ void screen_set_fg(Color color);
void screen_set_bg(Color color); void screen_set_bg(Color color);
/** make foreground bright */ /** make foreground bright */
void screen_set_bright_fg(void); void screen_set_bold(bool bold);
/** Set cursor foreground and background color */ /** Set cursor foreground and background color */
void screen_set_colors(Color fg, Color bg); void screen_set_colors(Color fg, Color bg);
@@ -148,14 +174,18 @@ void screen_set_colors(Color fg, Color bg);
void screen_inverse(bool inverse); void screen_inverse(bool inverse);
/** Set a character in the cursor color, move to right with wrap. */ /**
void screen_putchar(char c); * Set a character in the cursor color, move to right with wrap.
* The character may be ASCII (then only one char is used), or
* unicode (then it can be 4 chars, or terminated by a zero)
*/
void screen_putchar(const char *ch);
#if 0 #if 0
/** Debug dump */ /** Debug dump */
void screen_dd(void); void screen_dd(void);
#endif #endif
extern void screen_notifyChange(void); extern void screen_notifyChange(ScreenNotifyChangeTopic topic);
#endif // SCREEN_H #endif // SCREEN_H
+23 -11
View File
@@ -2,19 +2,36 @@
#include "uart_driver.h" #include "uart_driver.h"
#include "uart_handler.h" #include "uart_handler.h"
#include "ansi_parser.h" #include "ansi_parser.h"
#include "syscfg.h"
// Here the bitrates are defined /**
#define UART0_BAUD BIT_RATE_115200 * Init the serial ports
#define UART1_BAUD BIT_RATE_115200 */
void ICACHE_FLASH_ATTR serialInitBase(void)
{
UART_Init();
// debug port
UART_SetParity(UART1, PARITY_NONE);
UART_SetStopBits(UART1, ONE_STOP_BIT);
UART_SetBaudrate(UART1, BIT_RATE_115200);
UART_SetPrintPort(UART1);
UART_SetupAsyncReceiver();
}
/** /**
* Init the serial ports * Init the serial ports
*/ */
void ICACHE_FLASH_ATTR serialInit(void) void ICACHE_FLASH_ATTR serialInit(void)
{ {
UART_Init(UART0_BAUD, UART1_BAUD); UART_SetParity(UART0, (UartParityMode) sysconf->uart_parity);
UART_SetPrintPort(UART1); UART_SetStopBits(UART0, (UartStopBitsNum) sysconf->uart_stopbits);
UART_SetupAsyncReceiver(); UART_SetBaudrate(UART0, sysconf->uart_baudrate);
info("COM SERIAL: %d baud, %s parity, %s stopbit(s)",
sysconf->uart_baudrate,
(sysconf->uart_parity == PARITY_NONE ? "NONE" : (sysconf->uart_parity == PARITY_ODD ? "ODD" : "EVEN")),
(sysconf->uart_stopbits == ONE_STOP_BIT ? "1" : (sysconf->uart_stopbits == ONE_HALF_STOP_BIT ? "1.5" : "2"))
);
} }
/** /**
@@ -25,10 +42,5 @@ void ICACHE_FLASH_ATTR serialInit(void)
*/ */
void ICACHE_FLASH_ATTR UART_HandleRxByte(char c) void ICACHE_FLASH_ATTR UART_HandleRxByte(char c)
{ {
if (c > 0 && c < 127) {
// TODO buffering, do not run parser after just 1 char
ansi_parser(&c, 1); ansi_parser(&c, 1);
} else {
warn("Bad char %d ('%c')", (unsigned char)c, c);
}
} }
+3
View File
@@ -1,6 +1,9 @@
#ifndef SERIAL_H #ifndef SERIAL_H
#define SERIAL_H #define SERIAL_H
/** Initial uart init (before sysconf is loaded) */
void serialInitBase(void);
/** Init the uarts */ /** Init the uarts */
void serialInit(void); void serialInit(void);
+24
View File
@@ -0,0 +1,24 @@
//
// Created by MightyPork on 2017/07/29.
//
#include "syscfg.h"
#include "persist.h"
#include "uart_driver.h"
#include "serial.h"
SystemConfigBundle * const sysconf = &persist.current.sysconf;
void ICACHE_FLASH_ATTR
sysconf_apply_settings(void)
{
serialInit();
}
void ICACHE_FLASH_ATTR
sysconf_restore_defaults(void)
{
sysconf->uart_parity = PARITY_NONE;
sysconf->uart_baudrate = BIT_RATE_115200;
sysconf->uart_stopbits = ONE_STOP_BIT;
}
+26
View File
@@ -0,0 +1,26 @@
//
// Created by MightyPork on 2017/07/29.
//
#ifndef ESP_VT100_FIRMWARE_SYSCFG_H
#define ESP_VT100_FIRMWARE_SYSCFG_H
#include <esp8266.h>
// Size designed for the wifi config structure
// Must be constant to avoid corrupting user config after upgrade
#define SYSCONF_SIZE 200
typedef struct {
u32 uart_baudrate;
u8 uart_parity;
u8 uart_stopbits;
} SystemConfigBundle;
extern SystemConfigBundle * const sysconf;
void sysconf_apply_settings(void);
void sysconf_restore_defaults(void);
#endif //ESP_VT100_FIRMWARE_SYSCFG_H
+5 -17
View File
@@ -32,22 +32,8 @@ void ICACHE_FLASH_ATTR clear_rxtx(int uart_no)
} }
/**
* @brief Configure UART 115200-8-N-1
* @param uart_no
*/
static void ICACHE_FLASH_ATTR my_uart_init(UARTn uart_no, uint32 baud)
{
UART_SetParity(uart_no, PARITY_NONE);
UART_SetStopBits(uart_no, ONE_STOP_BIT);
UART_SetWordLength(uart_no, EIGHT_BITS);
UART_SetBaudrate(uart_no, baud);
UART_ResetFifo(uart_no);
}
/** Configure basic UART func and pins */ /** Configure basic UART func and pins */
void ICACHE_FLASH_ATTR UART_Init(uint32_t baud0, uint32_t baud1) void ICACHE_FLASH_ATTR UART_Init(void)
{ {
// U0TXD // U0TXD
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD); PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD);
@@ -60,8 +46,10 @@ void ICACHE_FLASH_ATTR UART_Init(uint32_t baud0, uint32_t baud1)
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_U1TXD_BK); PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_U1TXD_BK);
// Configure the UART peripherals // Configure the UART peripherals
my_uart_init(UART0, baud0); // main UART_SetWordLength(UART0, EIGHT_BITS); // main
my_uart_init(UART1, baud1); // debug (output only) UART_ResetFifo(UART0);
UART_SetWordLength(UART1, EIGHT_BITS); // debug (output only)
UART_ResetFifo(UART1);
} }
/** Configure Rx on UART0 */ /** Configure Rx on UART0 */
+2 -2
View File
@@ -12,8 +12,8 @@
#include <esp8266.h> #include <esp8266.h>
/** Configure UART periphs and enable pins */ /** Configure UART periphs and enable pins - does not set baud rate, parity and stopbits */
void UART_Init(uint32_t baud0, uint32_t baud1); void UART_Init(void);
/** Configure async Rx on UART0 */ /** Configure async Rx on UART0 */
void UART_SetupAsyncReceiver(void); void UART_SetupAsyncReceiver(void);
+4 -18
View File
@@ -86,12 +86,12 @@ void ICACHE_FLASH_ATTR user_init(void)
static ETSTimer userStartTimer; static ETSTimer userStartTimer;
static ETSTimer prHeapTimer; static ETSTimer prHeapTimer;
serialInit(); serialInitBase();
// Prevent WiFi starting and connecting by default // Prevent WiFi starting and connecting by default
// let wifi manager handle it // let wifi manager handle it
wifi_station_set_auto_connect(false); wifi_station_set_auto_connect(false);
wifi_set_opmode(NULL_MODE); // save to flash if changed - this might avoid the current spike on startup? wifi_set_opmode(NULL_MODE); // saves to flash if changed - this might avoid the current spike on startup?
printf("\r\n"); printf("\r\n");
banner("====== ESP8266 Remote Terminal ======"); banner("====== ESP8266 Remote Terminal ======");
@@ -126,30 +126,16 @@ void ICACHE_FLASH_ATTR user_init(void)
static void user_start(void *unused) static void user_start(void *unused)
{ {
// Change AP name if AI-THINKER found (means un-initialized device)
// struct softap_config apconf;
// wifi_softap_get_config(&apconf);
// if (strstarts((char *) apconf.ssid, "AI-THINKER")) {
// warn("Un-initialized device, performing factory reset.");
// apars_handle_OSC_FactoryReset();
// return;
// }
// Load and apply stored settings, or defaults if stored settings are invalid // Load and apply stored settings, or defaults if stored settings are invalid
persist_load(); persist_load();
// Captive portal (DNS redirector)
captdnsInit();
// Server
httpdInit(routes, 80);
// The terminal screen captdnsInit();
httpdInit(routes, 80);
screen_init(); screen_init();
// Print the CANCEL character to indicate the module has restarted // Print the CANCEL character to indicate the module has restarted
// Critically important for client application if any kind of screen persistence / content re-use is needed // Critically important for client application if any kind of screen persistence / content re-use is needed
UART_WriteChar(UART0, 24, UART_TIMEOUT_US); // 0x18 - 24 - CAN UART_WriteChar(UART0, 24, UART_TIMEOUT_US); // 0x18 - 24 - CAN
info("Listening on UART0, 115200-8-N-1!");
} }
// ---- unused funcs removed from sdk to save space --- // ---- unused funcs removed from sdk to save space ---
+1 -1
View File
@@ -1,7 +1,7 @@
#ifndef USER_MAIN_H_H #ifndef USER_MAIN_H_H
#define USER_MAIN_H_H #define USER_MAIN_H_H
#define FIRMWARE_VERSION "0.6.0-beta" #define FIRMWARE_VERSION "0.6.2+" GIT_HASH
#define TERMINAL_GITHUB_REPO "https://github.com/MightyPork/esp-vt100-firmware" #define TERMINAL_GITHUB_REPO "https://github.com/MightyPork/esp-vt100-firmware"
#endif //USER_MAIN_H_H #endif //USER_MAIN_H_H
+1 -1
View File
@@ -19,7 +19,7 @@ wifimgr_restore_defaults(void)
u8 mac[6]; u8 mac[6];
wifi_get_macaddr(SOFTAP_IF, mac); wifi_get_macaddr(SOFTAP_IF, mac);
wificonf->opmode = SOFTAP_MODE; wificonf->opmode = STATIONAP_MODE; // Client+AP, so we can scan without having to enable Station
wificonf->tpw = 20; wificonf->tpw = 20;
wificonf->ap_channel = 1; wificonf->ap_channel = 1;
sprintf((char *) wificonf->ap_ssid, "TERM-%02X%02X%02X", mac[3], mac[4], mac[5]); sprintf((char *) wificonf->ap_ssid, "TERM-%02X%02X%02X", mac[3], mac[4], mac[5]);
+7 -4
View File
@@ -13,6 +13,10 @@
#define SSID_LEN 32 #define SSID_LEN 32
#define PASSWORD_LEN 64 #define PASSWORD_LEN 64
// Size designed for the wifi config structure
// Must be constant to avoid corrupting user config after upgrade
#define WIFICONF_SIZE 340
/** /**
* A structure holding all configured WiFi parameters * A structure holding all configured WiFi parameters
* and the active state. * and the active state.
@@ -23,21 +27,20 @@ typedef struct {
WIFI_MODE opmode : 8; WIFI_MODE opmode : 8;
u8 tpw; u8 tpw;
// --- AP config --- // AP config
u8 ap_channel; u8 ap_channel;
u8 ap_ssid[SSID_LEN]; u8 ap_ssid[SSID_LEN];
u8 ap_password[PASSWORD_LEN]; u8 ap_password[PASSWORD_LEN];
bool ap_hidden; bool ap_hidden;
//
u16 ap_dhcp_time; // in minutes u16 ap_dhcp_time; // in minutes
struct dhcps_lease ap_dhcp_range; struct dhcps_lease ap_dhcp_range;
struct ip_info ap_addr; struct ip_info ap_addr;
// --- Client config --- // Client config
u8 sta_ssid[SSID_LEN]; u8 sta_ssid[SSID_LEN];
u8 sta_password[PASSWORD_LEN]; u8 sta_password[PASSWORD_LEN];
bool sta_dhcp_enable; bool sta_dhcp_enable;
struct ip_info sta_addr; struct ip_info sta_addr;
} WiFiConfigBundle; } WiFiConfigBundle;