terminal settings page and backend

pull/30/head
Ondřej Hruška 8 years ago
parent 3c202a1f50
commit 5f3cb6685d
  1. 4
      CMakeLists.txt
  2. 19
      html_orig/_debug_replacements.php
  3. 15
      html_orig/_pages.php
  4. 77
      html_orig/css/app.css
  5. 65
      html_orig/lang/en.php
  6. 71
      html_orig/pages/cfg_app.php
  7. 21
      html_orig/pages/cfg_network.php
  8. 2
      html_orig/pages/cfg_wifi_conn.php
  9. 10
      html_orig/pages/term.php
  10. 7
      html_orig/sass/form/_form_layout.scss
  11. 24
      html_orig/sass/layout/_box.scss
  12. 22
      include/helpers.h
  13. 162
      user/cgi_appcfg.c
  14. 251
      user/cgi_network.c
  15. 9
      user/cgi_network.h
  16. 337
      user/cgi_wifi.c
  17. 26
      user/cgi_wifi.h

@ -93,6 +93,8 @@ set(SOURCE_FILES
user/io.h user/io.h
user/cgi_wifi.c user/cgi_wifi.c
user/cgi_wifi.h user/cgi_wifi.h
user/cgi_network.c
user/cgi_network.h
user/cgi_appcfg.c user/cgi_appcfg.c
user/cgi_appcfg.h user/cgi_appcfg.h
user/cgi_ping.c user/cgi_ping.c
@ -119,7 +121,7 @@ set(SOURCE_FILES
user/wifimgr.c user/wifimgr.c
user/wifimgr.h user/wifimgr.h
user/persist.c user/persist.c
user/persist.h) user/persist.h include/helpers.h)
include_directories(include) include_directories(include)
include_directories(user) include_directories(user)

@ -8,11 +8,12 @@
return [ return [
'%term_title%' => 'ESP8266 Wireless Terminal', '%term_title%' => 'ESP8266 Wireless Terminal',
'%b1%' => '1', '%btn1%' => '1',
'%b2%' => '2', '%btn2%' => '2',
'%b3%' => '3', '%btn3%' => '3',
'%b4%' => '4', '%btn4%' => '4',
'%b5%' => '5', '%btn5%' => '5',
'%screenData%' => '{ '%screenData%' => '{
"w": 26, "h": 10, "w": 26, "h": 10,
"x": 0, "y": 0, "x": 0, "y": 0,
@ -50,4 +51,12 @@ return [
'%sta_addr_ip%' => '192.168.0.33', '%sta_addr_ip%' => '192.168.0.33',
'%sta_addr_mask%' => '255.255.255.0', '%sta_addr_mask%' => '255.255.255.0',
'%sta_addr_gw%' => '192.168.0.1', '%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',
]; ];

@ -18,16 +18,21 @@ if (! function_exists('pg')) {
} }
pg('cfg_wifi', 'cfg', '/cfg/wifi'); pg('cfg_wifi', 'cfg', '/cfg/wifi');
pg('cfg_wifi_conn', '', '/wifi/connecting'); // page without menu that tries to show the connection progress pg('cfg_wifi_conn', '', '/cfg/wifi/connecting');
pg('wifi_connstatus', 'api', '/cfg/wifi/connstatus');
pg('wifi_set', 'api', '/cfg/wifi/set');
pg('wifi_scan', 'api', '/cfg/wifi/scan');
pg('cfg_network', 'cfg', '/cfg/network'); pg('cfg_network', 'cfg', '/cfg/network');
//pg('cfg_term', 'cfg', '/cfg/term'); pg('network_set', 'api', '/cfg/network/set');
pg('cfg_app', 'cfg', '/cfg/app');
pg('app_set', 'api', '/cfg/app/set');
pg('help', 'cfg page-help', '/help'); pg('help', 'cfg page-help', '/help');
pg('about', 'cfg page-about', '/about'); pg('about', 'cfg page-about', '/about');
pg('term', 'term', '/', 'title.term'); pg('term', 'term', '/', 'title.term');
// ajax API // ajax API
pg('wifi_set', 'api', '/wifi/set');//'/cfg/wifi/set');
pg('wifi_scan', 'api', '/wifi/scan');//'/cfg/wifi/scan');
pg('wifi_connstatus', 'api', '/wifi/connstatus');
return $pages; return $pages;

@ -489,14 +489,17 @@ ul > * {
margin-top: 1rem; margin-top: 1rem;
padding: 0.61805rem 1rem; padding: 0.61805rem 1rem;
border-radius: 3px; border-radius: 3px;
background-color: rgba(255, 255, 255, 0.07); } background-color: rgba(255, 255, 255, 0.07);
box-shadow: 0 0 4px black;
border: 1px solid #4f4f4f; }
@media screen and (max-width: 544px) { @media screen and (max-width: 544px) {
.Box { .Box {
margin-top: 0.61805rem; } } margin-top: 0.61805rem; } }
h1 + .Box { h1 + .Box {
margin-top: 0; } margin-top: 0; }
.Box h2 { .Box h2 {
margin-top: 0; } margin-top: 0;
margin-bottom: 0 !important; }
.Box.wide { .Box.wide {
width: initial; width: initial;
max-width: initial; } max-width: initial; }
@ -515,13 +518,26 @@ ul > * {
right: 0; right: 0;
top: 0; top: 0;
margin-top: 0.61805rem; } } margin-top: 0.61805rem; } }
.Box.str.mobopen .Row.buttons {
top: 0;
margin-top: 0.61805rem; }
.Box .Row.explain {
max-width: 600px;
margin-left: 0; }
@media screen and (max-width: 544px) {
.Box .Row.explain {
margin-top: 60px; } }
.Box.mobopen .Row.explain {
margin-top: 12px; }
@media screen and (max-width: 544px) {
.Box.mobopen .Row.explain {
margin-top: 18px; } }
@media screen and (max-width: 544px) { @media screen and (max-width: 544px) {
.Box.mobcol h2 { .Box.mobcol h2 {
position: relative; position: relative;
cursor: pointer; cursor: pointer;
padding-right: 1.3rem; padding-right: 1.3rem; }
margin-bottom: 0 !important; }
.Box.mobcol h2::after { .Box.mobcol h2::after {
position: absolute; position: absolute;
right: 0; right: 0;
@ -732,7 +748,7 @@ form {
input[type="number"], input[type="password"], input[type="text"], textarea, select, label.select-wrap { input[type="number"], input[type="password"], input[type="text"], textarea, select, label.select-wrap {
width: 250px; } width: 250px; }
input[type="number"] { input[type="number"], input.short {
width: 125px; } width: 125px; }
.Box.errors .list { .Box.errors .list {
@ -741,46 +757,46 @@ input[type="number"] {
.Box.errors .lead { .Box.errors .lead {
color: white; } color: white; }
form .Row { .Row {
vertical-align: middle; vertical-align: middle;
margin: 12px auto; margin: 12px auto;
text-align: left; text-align: left;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; } align-items: center; }
form .Row:first-child { .Row:first-child {
margin-top: 0; } margin-top: 0; }
form .Row:last-child { .Row:last-child {
margin-bottom: 0; } margin-bottom: 0; }
form .Row .spacer { .Row .spacer {
width: 160px; } width: 160px; }
@media screen and (max-width: 544px) { @media screen and (max-width: 544px) {
form .Row .spacer { .Row .spacer {
display: none; } } display: none; } }
form .Row.buttons { .Row.buttons {
margin: 16px auto; } margin: 16px auto; }
form .Row.buttons input, form .Row.buttons .button { .Row.buttons input, .Row.buttons .button {
margin-right: 0.61805rem; } margin-right: 0.61805rem; }
form .Row.centered { .Row.centered {
justify-content: center; } justify-content: center; }
form .Row.message { .Row.message {
font-size: 1em; font-size: 1em;
text-shadow: 1px 1px 3px black; text-shadow: 1px 1px 3px black;
text-align: center; } text-align: center; }
form .Row.message.error { .Row.message.error {
color: crimson; } color: crimson; }
form .Row.message.ok { .Row.message.ok {
color: #0fe851; } color: #0fe851; }
form .Row.separator { .Row.separator {
padding-top: 14px; padding-top: 14px;
border-top: 2px solid rgba(255, 255, 255, 0.1); } border-top: 2px solid rgba(255, 255, 255, 0.1); }
form .Row textarea { .Row textarea {
display: inline-block; display: inline-block;
vertical-align: top; vertical-align: top;
min-height: 10rem; min-height: 10rem;
flex-grow: 1; flex-grow: 1;
resize: vertical; } resize: vertical; }
form .Row label { .Row label {
font-weight: bold; font-weight: bold;
color: white; color: white;
display: inline-block; display: inline-block;
@ -795,32 +811,33 @@ form .Row {
user-select: none; user-select: none;
white-space: nowrap; white-space: nowrap;
word-wrap: normal; } word-wrap: normal; }
form .Row label.error { .Row label.error {
color: crimson; } color: crimson; }
form .Row input[type="range"] { .Row input[type="range"] {
width: 200px; } width: 200px; }
@media screen and (max-width: 544px) { @media screen and (max-width: 544px) {
form .Row { .Row {
flex-direction: column; } flex-direction: column;
form .Row.buttons, form .Row.centered, form .Row.checkbox { margin: 6px auto; }
.Row.buttons, .Row.centered, .Row.checkbox {
flex-direction: row; } flex-direction: row; }
form .Row.buttons { .Row.buttons {
justify-content: center; } justify-content: center; }
form .Row.buttons :last-child { .Row.buttons :last-child {
margin-right: 0; } margin-right: 0; }
form .Row label { .Row label {
padding-left: 0; padding-left: 0;
text-align: left; text-align: left;
width: auto; } width: auto; }
form .Row .checkbox-wrap { .Row .checkbox-wrap {
order: 1; order: 1;
text-align: left; text-align: left;
padding-bottom: 0; padding-bottom: 0;
border-radius: .4px; border-radius: .4px;
width: auto; } width: auto; }
form .Row .checkbox-wrap + label { .Row .checkbox-wrap + label {
width: auto; } width: auto; }
form .Row input[type="number"], form .Row input[type="password"], form .Row input[type="text"], form .Row textarea, form .Row input[type="range"], form .Row textarea { .Row input[type="number"], .Row input[type="password"], .Row input[type="text"], .Row textarea, .Row input[type="range"], .Row textarea, .Row select {
width: 100%; } } width: 100%; } }
form span.required { form span.required {

@ -5,7 +5,7 @@ return [
'menu.cfg_wifi' => 'WiFi Settings', 'menu.cfg_wifi' => 'WiFi Settings',
'menu.cfg_network' => 'Network Configuration', 'menu.cfg_network' => 'Network Configuration',
'menu.cfg_term' => 'Terminal Settings', 'menu.cfg_app' => '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',
@ -13,8 +13,46 @@ return [
'title.term' => 'Terminal', 'title.term' => 'Terminal',
'net.ap' => 'DHCP Server', 'net.ap' => 'DHCP Server (AP)',
'net.sta' => 'DHCP Client', 'net.sta' => 'DHCP Client (Station)',
'net.sta_mac' => 'Station MAC',
'net.ap_mac' => 'AP MAC',
'net.details' => 'MAC addresses',
'app.defaults' => 'Initial settings',
'app.explain_initials' => '
Those are the initial settings used after ESPTerm restarts, and they
will also be applied immediately after you submit this form.
They can be subsequently changed by ESC commands, but those changes
aren\'t persistent and will be lost when the device powers off.',
'app.term_title' => 'Header text',
'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',
'color.0' => 'Black',
'color.1' => 'Dark Red',
'color.2' => 'Dark Green',
'color.3' => 'Dim Yellow',
'color.4' => 'Deep Blue',
'color.5' => 'Dark Violet',
'color.6' => 'Dark Cyan',
'color.7' => 'Silver',
'color.8' => 'Gray',
'color.9' => 'Light Red',
'color.10' => 'Light Green',
'color.11' => 'Light Yellow',
'color.12' => 'Light Blue',
'color.13' => 'Light Violet',
'color.14' => 'Light Cyan',
'color.15' => 'White',
'net.explain_sta' => ' 'net.explain_sta' => '
Those settings affect the built-in DHCP client used for Those settings affect the built-in DHCP client used for
@ -42,16 +80,14 @@ return [
'wifi.ap' => 'Built-in Access Point', 'wifi.ap' => 'Built-in Access Point',
'wifi.sta' => 'Connect to External Network', 'wifi.sta' => 'Connect to External Network',
'wifi.enable' => 'Enabled:', 'wifi.enable' => 'Enabled',
'wifi.tpw' => 'Transmit Power:', 'wifi.tpw' => 'Transmit power',
'wifi.ap_channel' => 'Channel:', 'wifi.ap_channel' => 'Channel',
'wifi.ap_ssid' => 'AP SSID:', 'wifi.ap_ssid' => 'AP SSID',
'wifi.ap_password' => 'Password:', 'wifi.ap_password' => 'Password',
'wifi.ap_hidden' => 'Hide SSID:', 'wifi.ap_hidden' => 'Hide SSID',
'wifi.sta_info' => 'Selected:', 'wifi.sta_info' => 'Selected',
'wifi.sta_ssid' => 'Network SSID:',
'wifi.sta_password' => 'Password:',
'wifi.not_conn' => 'Not connected.', 'wifi.not_conn' => 'Not connected.',
'wifi.sta_none' => 'None', 'wifi.sta_none' => 'None',
'wifi.sta_active_pw' => '🔒', 'wifi.sta_active_pw' => '🔒',
@ -67,10 +103,11 @@ return [
'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. Try again...',
'wifi.conn.idle' =>"Preparing to connect", 'wifi.conn.disabled' =>"Station mode is disabled.",
'wifi.conn.idle' =>"Idle, not connected and with 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 your password and try again.", 'wifi.conn.fail' => "Connection failed, check settings & try again. Cause: ",
'apply' => 'Apply!', 'apply' => 'Apply!',
'enabled' => 'Enabled', 'enabled' => 'Enabled',

@ -0,0 +1,71 @@
<form class="Box mobopen str" action="<?= e(url('app_set')) ?>" method="GET">
<h2><?= tr('app.defaults') ?></h2>
<div class="Row buttons">
<input type="submit" value="<?= tr('apply') ?>">
</div>
<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>
</form>

@ -2,15 +2,14 @@
$ipmask='pattern="^([0-9]{1,3}\.){3}[0-9]{1,3}$"'; $ipmask='pattern="^([0-9]{1,3}\.){3}[0-9]{1,3}$"';
?> ?>
<form class="Box str mobcol" action="<?= e(url('wifi_set')) ?>" method="GET"> <form class="Box str mobcol" action="<?= e(url('network_set')) ?>" method="GET">
<h2><?= tr('net.ap') ?></h2> <h2><?= tr('net.ap') ?></h2>
<div class="Row buttons"> <div class="Row buttons">
<input type="submit" value="<?= tr('apply') ?>"> <input type="submit" value="<?= tr('apply') ?>">
</div> </div>
<div class="Row mq-phone" style="height: 35px"></div> <div class="Row explain">
<div class="Row" style="max-width: 600px; margin-left: 0">
<?= tr('net.explain_ap') ?> <?= tr('net.explain_ap') ?>
</div> </div>
@ -41,15 +40,14 @@ $ipmask='pattern="^([0-9]{1,3}\.){3}[0-9]{1,3}$"';
</div> </div>
</form> </form>
<form class="Box str mobcol" action="<?= e(url('wifi_set')) ?>" method="GET"> <form class="Box str mobcol" action="<?= e(url('network_set')) ?>" method="GET">
<h2><?= tr('net.sta') ?></h2> <h2><?= tr('net.sta') ?></h2>
<div class="Row buttons"> <div class="Row buttons">
<input type="submit" value="<?= tr('apply') ?>"> <input type="submit" value="<?= tr('apply') ?>">
</div> </div>
<div class="Row mq-phone" style="height: 35px"></div> <div class="Row explain">
<div class="Row" style="max-width: 600px; margin-left: 0">
<?= tr('net.explain_sta') ?> <?= tr('net.explain_sta') ?>
</div> </div>
@ -74,3 +72,14 @@ $ipmask='pattern="^([0-9]{1,3}\.){3}[0-9]{1,3}$"';
<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="%sta_addr_gw%" <?=$ipmask?> required>
</div> </div>
</form> </form>
<div class="Box mobcol">
<h2><?= tr('net.details') ?></h2>
<div class="Row">
<label><?= tr('net.sta_mac') ?></label><input type="text" readonly value="%sta_mac%">
</div>
<div class="Row">
<label><?= tr('net.ap_mac') ?></label><input type="text" readonly value="%ap_mac%">
</div>
</div>

@ -10,6 +10,7 @@
var abortTmeo; var abortTmeo;
var messages = <?= json_encode([ var messages = <?= json_encode([
'disabled' => tr('wifi.conn.disabled'),
'idle' => tr('wifi.conn.idle'), 'idle' => tr('wifi.conn.idle'),
'success' => tr('wifi.conn.success'), 'success' => tr('wifi.conn.success'),
'working' => tr('wifi.conn.working'), 'working' => tr('wifi.conn.working'),
@ -25,6 +26,7 @@
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 == 'success') msg += data.ip;
if (data.status == 'fail') msg += data.cause;
$("#status").html(msg); $("#status").html(msg);

@ -13,11 +13,11 @@
<div id="screen"></div> <div id="screen"></div>
<div id="buttons"> <div id="buttons">
<button data-n="1" class="btn-blue">%b1%</button><!-- <button data-n="1" class="btn-blue">%btn1%</button><!--
--><button data-n="2" class="btn-blue">%b2%</button><!-- --><button data-n="2" class="btn-blue">%btn2%</button><!--
--><button data-n="3" class="btn-blue">%b3%</button><!-- --><button data-n="3" class="btn-blue">%btn3%</button><!--
--><button data-n="4" class="btn-blue">%b4%</button><!-- --><button data-n="4" class="btn-blue">%btn4%</button><!--
--><button data-n="5" class="btn-blue">%b5%</button> --><button data-n="5" class="btn-blue">%btn5%</button>
</div> </div>
</div> </div>

@ -5,7 +5,7 @@ form { @include naked(); }
width: $form-field-w; width: $form-field-w;
} }
input[type="number"] { input[type="number"], input.short {
width: $form-field-w/2; width: $form-field-w/2;
} }
@ -20,7 +20,7 @@ input[type="number"] {
} }
} }
form .Row { .Row {
vertical-align: middle; vertical-align: middle;
margin: 12px auto; margin: 12px auto;
text-align: left; text-align: left;
@ -132,6 +132,7 @@ form .Row {
// special phone style // special phone style
@include media($phone) { @include media($phone) {
flex-direction: column; flex-direction: column;
margin: 6px auto;
&.buttons, &.centered, &.checkbox { &.buttons, &.centered, &.checkbox {
flex-direction: row; flex-direction: row;
@ -165,7 +166,7 @@ form .Row {
} }
} }
#{$all-text-inputs}, input[type="range"], textarea { #{$all-text-inputs}, input[type="range"], textarea, select {
width: 100%; width: 100%;
} }
} }

@ -15,10 +15,13 @@
h2 { h2 {
margin-top: 0; margin-top: 0;
margin-bottom: 0 !important;
} }
border-radius: 3px; border-radius: 3px;
background-color: rgba(white, .07); background-color: rgba(white, .07);
box-shadow: 0 0 4px black;
border: 1px solid #4f4f4f;
&.wide { &.wide {
width: initial; width: initial;
@ -52,6 +55,25 @@
} }
} }
} }
&.str.mobopen .Row.buttons {
top: 0;
margin-top: dist(-1);
}
.Row.explain {
max-width: 600px; margin-left: 0;
@include media($phone) {
margin-top: 60px;
}
}
&.mobopen .Row.explain {
margin-top: 12px; // default from .Row
@include media($phone) {
margin-top: 18px;
}
}
} }
@include media($phone) { @include media($phone) {
@ -71,8 +93,6 @@
font-weight: bold; font-weight: bold;
transform: translate(0,-50%) rotate(90deg); transform: translate(0,-50%) rotate(90deg);
} }
margin-bottom: 0 !important;
} }
&.expanded h2::after { &.expanded h2::after {

@ -0,0 +1,22 @@
//
// Created by MightyPork on 2017/07/23.
//
#ifndef ESP_VT100_FIRMWARE_HELPERS_H
#define ESP_VT100_FIRMWARE_HELPERS_H
// strcpy that adds 0 at the end of the buffer. Returns void.
#define strncpy_safe(dst, src, n) do { strncpy((char *)(dst), (char *)(src), (n)); (dst)[(n)-1]=0; } while (0)
/**
* Convert IP hex to arguments for printf.
* Library IP2STR(ip) does not work correctly due to unaligned memory access.
*/
#define GOOD_IP2STR(ip) ((ip)>>0)&0xff, ((ip)>>8)&0xff, ((ip)>>16)&0xff, ((ip)>>24)&0xff
/**
* 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)
#endif //ESP_VT100_FIRMWARE_HELPERS_H

@ -3,28 +3,24 @@ Cgi/template routines for configuring non-wifi settings
*/ */
#include <esp8266.h> #include <esp8266.h>
#include "cgi_wifi.h" #include "cgi_appcfg.h"
#include "wifimgr.h"
#include "persist.h" #include "persist.h"
#include "screen.h"
#include "helpers.h"
// strcpy that adds 0 at the end of the buffer. Returns void. #define SET_REDIR_SUC "/cfg/term"
#define strncpy_safe(dst, src, n) do { strncpy((char *)(dst), (char *)(src), (n)); (dst)[(n)-1]=0; } while (0) #define SET_REDIR_ERR SET_REDIR_SUC"?err="
/** /**
* Universal CGI endpoint to set WiFi params. * Universal CGI endpoint to set Terminal params.
* Note that some may cause a (delayed) restart.
*/ */
httpd_cgi_state ICACHE_FLASH_ATTR cgiAppCfgSet(HttpdConnData *connData) httpd_cgi_state ICACHE_FLASH_ATTR cgiAppCfgSet(HttpdConnData *connData)
{ {
static ETSTimer timer;
char buff[50]; char buff[50];
#define REDIR_BASE_URL "/wifi?err="
char redir_url_buf[300]; char redir_url_buf[300];
char *redir_url = redir_url_buf; char *redir_url = redir_url_buf;
redir_url += sprintf(redir_url, REDIR_BASE_URL); 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
if (connData->conn == NULL) { if (connData->conn == NULL) {
@ -32,40 +28,98 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiAppCfgSet(HttpdConnData *connData)
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }
#define GET_ARG(key) (httpdFindArg(connData->getArgs, key, buff, sizeof(buff)) > 0) // width and height must always go together so we can do max size validation
if (GET_ARG("term_width")) {
dbg("Default screen width: %s", buff);
int w = atoi(buff);
if (w > 1) {
if (GET_ARG("term_height")) {
dbg("Default screen height: %s", buff);
int h = atoi(buff);
if (h > 1) {
if (w * h <= MAX_SCREEN_SIZE) {
termconf->width = w;
termconf->height = h;
} else {
warn("Bad dimensions: %d x %d (total %d)", w, h, w*h);
redir_url += sprintf(redir_url, "term_width,term_height,");
}
} else {
warn("Bad height: \"%s\"", buff);
redir_url += sprintf(redir_url, "term_width,");
}
} else {
warn("Missing height arg", buff);
// this wont happen normally when the form is used
redir_url += sprintf(redir_url, "term_width,term_height,");
}
} else {
warn("Bad width: \"%s\"", buff);
redir_url += sprintf(redir_url, "term_width,");
}
}
if (GET_ARG("default_bg")) {
dbg("Screen default BG: %s", buff);
int color = atoi(buff);
if (color >= 0 && color < 16) {
termconf->default_bg = (u8) color;
} else {
warn("Bad color %s", buff);
redir_url += sprintf(redir_url, "default_bg,");
}
}
// TODO if (GET_ARG("default_fg")) {
if (GET_ARG("opmode")) { dbg("Screen default FG: %s", buff);
dbg("Setting WiFi opmode to: %s", buff); int color = atoi(buff);
int mode = atoi(buff); if (color >= 0 && color < 16) {
if (mode > NULL_MODE && mode < MAX_MODE) { termconf->default_fg = (u8) color;
wificonf->opmode = (WIFI_MODE) mode;
} else { } else {
warn("Bad opmode value \"%s\"", buff); warn("Bad color %s", buff);
redir_url += sprintf(redir_url, "opmode,"); redir_url += sprintf(redir_url, "default_fg,");
} }
} }
if (redir_url_buf[strlen(REDIR_BASE_URL)] == 0) { if (GET_ARG("term_title")) {
dbg("Terminal title default text: \"%s\"", buff);
strncpy_safe(termconf->title, buff, 64); // ATTN those must match the values in
}
if (GET_ARG("btn1")) {
dbg("Button1 default text: \"%s\"", buff);
strncpy_safe(termconf->btn1, buff, 10);
}
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) {
// All was OK // All was OK
info("Set WiFi params - success, applying in 1000 ms"); info("Set app params - success, saving...");
// Settings are applied only if all was OK
//
// This is so that options that consist of multiple keys sent together are not applied
// only partially if set wrong, which could lead to eg. user losing access and having
// to reset to defaults.
persist_store(); persist_store();
// Delayed settings apply, so the response page has a chance to load. httpdRedirect(connData, SET_REDIR_SUC);
// If user connects via the Station IF, they may not even notice the connection reset.
os_timer_disarm(&timer);
os_timer_setfn(&timer, applyWifiSettingsLaterCb, NULL);
os_timer_arm(&timer, 1000, false);
httpdRedirect(connData, "/wifi");
} else { } else {
warn("Some WiFi settings did not validate, asking for correction"); warn("Some settings did not validate, asking for correction");
// Some errors, appended to the URL as ?err= // Some errors, appended to the URL as ?err=
httpdRedirect(connData, redir_url_buf); httpdRedirect(connData, redir_url_buf);
} }
@ -73,12 +127,10 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiAppCfgSet(HttpdConnData *connData)
} }
//Template code for the WLAN page.
httpd_cgi_state ICACHE_FLASH_ATTR tplAppCfg(HttpdConnData *connData, char *token, void **arg) httpd_cgi_state ICACHE_FLASH_ATTR tplAppCfg(HttpdConnData *connData, char *token, void **arg)
{ {
char buff[100]; #define BUFLEN 100
int x; char buff[BUFLEN];
int connectStatus;
if (token == NULL) { if (token == NULL) {
// We're done // We're done
@ -87,9 +139,35 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplAppCfg(HttpdConnData *connData, char *token
strcpy(buff, ""); // fallback strcpy(buff, ""); // fallback
// TODO if (streq(token, "term_width")) {
if (streq(token, "opmode_name")) { sprintf(buff, "%d", termconf->width);
strcpy(buff, opmode2str(wificonf->opmode)); }
else if (streq(token, "term_height")) {
sprintf(buff, "%d", termconf->height);
}
else if (streq(token, "default_bg")) {
sprintf(buff, "%d", termconf->default_bg);
}
else if (streq(token, "default_fg")) {
sprintf(buff, "%d", termconf->default_bg);
}
else if (streq(token, "term_title")) {
strncpy_safe(buff, termconf->title, BUFLEN);
}
else if (streq(token, "btn1")) {
strncpy_safe(buff, termconf->btn1, BUFLEN);
}
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); httpdSend(connData, buff, -1);

@ -0,0 +1,251 @@
/*
configuring the network settings
*/
#include <esp8266.h>
#include "cgi_network.h"
#include "wifimgr.h"
#include "persist.h"
#include "helpers.h"
#define SET_REDIR_SUC "/cfg/network"
#define SET_REDIR_ERR SET_REDIR_SUC"?err="
/**
* Callback for async timer
*/
static void ICACHE_FLASH_ATTR applyNetSettingsLaterCb(void *arg)
{
wifimgr_apply_settings();
}
/**
* Universal CGI endpoint to set network params.
* Those affect DHCP etc, may cause a disconnection.
*/
httpd_cgi_state ICACHE_FLASH_ATTR cgiNetworkSetParams(HttpdConnData *connData)
{
static ETSTimer timer;
char buff[50];
char redir_url_buf[300];
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;
}
// ---- AP DHCP server lease time ----
if (GET_ARG("ap_dhcp_time")) {
dbg("Setting DHCP lease time to: %s min.", buff);
int min = atoi(buff);
if (min >= 1 && min <= 2880) {
if (wificonf->ap_dhcp_time != min) {
wificonf->ap_dhcp_time = (u16) min;
wifi_change_flags.ap = true;
}
} else {
warn("Lease time %s out of allowed range 1-2880.", buff);
redir_url += sprintf(redir_url, "ap_dhcp_time,");
}
}
// ---- AP DHCP start and end IP ----
if (GET_ARG("ap_dhcp_start")) {
dbg("Setting DHCP range start IP to: \"%s\"", buff);
u32 ip = ipaddr_addr(buff);
if (ip != 0) {
if (wificonf->ap_dhcp_range.start_ip.addr != ip) {
wificonf->ap_dhcp_range.start_ip.addr = ip;
wifi_change_flags.ap = true;
}
} else {
warn("Bad IP: %s", buff);
redir_url += sprintf(redir_url, "ap_dhcp_start,");
}
}
if (GET_ARG("ap_dhcp_end")) {
dbg("Setting DHCP range end IP to: \"%s\"", buff);
u32 ip = ipaddr_addr(buff);
if (ip != 0) {
if (wificonf->ap_dhcp_range.end_ip.addr != ip) {
wificonf->ap_dhcp_range.end_ip.addr = ip;
wifi_change_flags.ap = true;
}
} else {
warn("Bad IP: %s", buff);
redir_url += sprintf(redir_url, "ap_dhcp_end,");
}
}
// ---- AP local address & config ----
if (GET_ARG("ap_addr_ip")) {
dbg("Setting AP local IP to: \"%s\"", buff);
u32 ip = ipaddr_addr(buff);
if (ip != 0) {
if (wificonf->ap_addr.ip.addr != ip) {
wificonf->ap_addr.ip.addr = ip;
wificonf->ap_addr.gw.addr = ip; // always the same, we're the router here
wifi_change_flags.ap = true;
}
} else {
warn("Bad IP: %s", buff);
redir_url += sprintf(redir_url, "ap_addr_ip,");
}
}
if (GET_ARG("ap_addr_mask")) {
dbg("Setting AP local IP netmask to: \"%s\"", buff);
u32 ip = ipaddr_addr(buff);
if (ip != 0) {
if (wificonf->ap_addr.netmask.addr != ip) {
// ideally this should be checked to match the IP.
// Let's hope users know what they're doing
wificonf->ap_addr.netmask.addr = ip;
wifi_change_flags.ap = true;
}
} else {
warn("Bad IP mask: %s", buff);
redir_url += sprintf(redir_url, "ap_addr_mask,");
}
}
// ---- Station enable/disable DHCP ----
// DHCP enable / disable (disable means static IP is enabled)
if (GET_ARG("sta_dhcp_enable")) {
dbg("DHCP enable = %s", buff);
int enable = atoi(buff);
if (wificonf->sta_dhcp_enable != enable) {
wificonf->sta_dhcp_enable = (bool)enable;
wifi_change_flags.sta = true;
}
}
// ---- Station IP config (Static IP) ----
if (GET_ARG("sta_addr_ip")) {
dbg("Setting Station mode static IP to: \"%s\"", buff);
u32 ip = ipaddr_addr(buff);
if (ip != 0) {
if (wificonf->sta_addr.ip.addr != ip) {
wificonf->sta_addr.ip.addr = ip;
wifi_change_flags.sta = true;
}
} else {
warn("Bad IP: %s", buff);
redir_url += sprintf(redir_url, "sta_addr_ip,");
}
}
if (GET_ARG("sta_addr_mask")) {
dbg("Setting Station mode static IP netmask to: \"%s\"", buff);
u32 ip = ipaddr_addr(buff);
if (ip != 0 && ip != 0xFFFFFFFFUL) {
if (wificonf->sta_addr.netmask.addr != ip) {
wificonf->sta_addr.netmask.addr = ip;
wifi_change_flags.sta = true;
}
} else {
warn("Bad IP mask: %s", buff);
redir_url += sprintf(redir_url, "sta_addr_mask,");
}
}
if (GET_ARG("sta_addr_gw")) {
dbg("Setting Station mode static IP default gateway to: \"%s\"", buff);
u32 ip = ipaddr_addr(buff);
if (ip != 0) {
if (wificonf->sta_addr.gw.addr != ip) {
wificonf->sta_addr.gw.addr = ip;
wifi_change_flags.sta = true;
}
} else {
warn("Bad gw IP: %s", buff);
redir_url += sprintf(redir_url, "sta_addr_gw,");
}
}
if (redir_url_buf[strlen(SET_REDIR_ERR)] == 0) {
// All was OK
info("Set network params - success, applying in 1000 ms");
// Settings are applied only if all was OK
persist_store();
// Delayed settings apply, so the response page has a chance to load.
// If user connects via the Station IF, they may not even notice the connection reset.
os_timer_disarm(&timer);
os_timer_setfn(&timer, applyNetSettingsLaterCb, NULL);
os_timer_arm(&timer, 1000, false);
httpdRedirect(connData, SET_REDIR_SUC);
} else {
warn("Some WiFi settings did not validate, asking for correction");
// Some errors, appended to the URL as ?err=
httpdRedirect(connData, redir_url_buf);
}
return HTTPD_CGI_DONE;
}
//Template code for the WLAN page.
httpd_cgi_state ICACHE_FLASH_ATTR tplNetwork(HttpdConnData *connData, char *token, void **arg)
{
char buff[100];
u8 mac[6];
if (token == NULL) {
// We're done
return HTTPD_CGI_DONE;
}
strcpy(buff, ""); // fallback
if (streq(token, "ap_dhcp_time")) {
sprintf(buff, "%d", wificonf->ap_dhcp_time);
}
else if (streq(token, "ap_dhcp_start")) {
sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->ap_dhcp_range.start_ip.addr));
}
else if (streq(token, "ap_dhcp_end")) {
sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->ap_dhcp_range.end_ip.addr));
}
else if (streq(token, "ap_addr_ip")) {
sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->ap_addr.ip.addr));
}
else if (streq(token, "ap_addr_mask")) {
sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->ap_addr.netmask.addr));
}
else if (streq(token, "sta_dhcp_enable")) {
sprintf(buff, "%d", wificonf->sta_dhcp_enable);
}
else if (streq(token, "sta_addr_ip")) {
sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->sta_addr.ip.addr));
}
else if (streq(token, "ap_addr_mask")) {
sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->sta_addr.netmask.addr));
}
else if (streq(token, "ap_addr_gw")) {
sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->sta_addr.gw.addr));
}
else if (streq(token, "sta_mac")) {
wifi_get_macaddr(STATION_IF, mac);
sprintf(buff, MACSTR, MAC2STR(mac));
}
else if (streq(token, "ap_mac")) {
wifi_get_macaddr(SOFTAP_IF, mac);
sprintf(buff, MACSTR, MAC2STR(mac));
}
httpdSend(connData, buff, -1);
return HTTPD_CGI_DONE;
}

@ -0,0 +1,9 @@
#ifndef CGINET_H
#define CGINET_H
#include "httpd.h"
httpd_cgi_state cgiNetworkSetParams(HttpdConnData *connData);
httpd_cgi_state tplNetwork(HttpdConnData *connData, char *token, void **arg);
#endif

@ -17,9 +17,10 @@ Cgi/template routines for the /wifi url.
#include "cgi_wifi.h" #include "cgi_wifi.h"
#include "wifimgr.h" #include "wifimgr.h"
#include "persist.h" #include "persist.h"
#include "helpers.h"
// strcpy that adds 0 at the end of the buffer. Returns void. #define SET_REDIR_SUC "/cfg/wifi"
#define strncpy_safe(dst, src, n) do { strncpy((char *)(dst), (char *)(src), (n)); (dst)[(n)-1]=0; } while (0) #define SET_REDIR_ERR SET_REDIR_SUC"?err="
/** WiFi access point data */ /** WiFi access point data */
typedef struct { typedef struct {
@ -40,17 +41,6 @@ typedef struct {
/** Static scan status storage. */ /** Static scan status storage. */
static ScanResultData cgiWifiAps; static ScanResultData cgiWifiAps;
/** Progress of connection to AP enum */
typedef enum {
CONNTRY_IDLE = 0,
CONNTRY_WORKING = 1,
CONNTRY_SUCCESS = 2,
CONNTRY_FAIL = 3,
} ConnTry;
/** Connection result var */
static ConnTry connTryStatus = CONNTRY_IDLE;
/** Connection to AP periodic check timer */ /** Connection to AP periodic check timer */
static os_timer_t staCheckTimer; static os_timer_t staCheckTimer;
@ -257,102 +247,6 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiScan(HttpdConnData *connData)
} }
} }
/**
* This routine is ran some time after a connection attempt to an access point. If
* the connect succeeds, this gets the module in STA-only mode.
*/
static void ICACHE_FLASH_ATTR staCheckConnStatus(void *arg)
{
int x = wifi_station_get_connect_status();
if (x == STATION_GOT_IP) {
info("Connected to AP.");
connTryStatus = CONNTRY_SUCCESS;
// This would enter STA only mode, but that kills the browser page if using STA+AP.
// Instead we stay in the current mode and let the user switch manually.
//wifi_set_opmode(STATION_MODE);
//system_restart();
}
else {
connTryStatus = CONNTRY_FAIL;
error("Connection failed.");
}
}
/**
* Delayed connect callback
*/
static void ICACHE_FLASH_ATTR cgiWiFiConnect_do(void *arg)
{
int x;
struct station_config cfg;
dbg("Try to connect to AP...");
strncpy_safe(cfg.password, wificonf->sta_password, PASSWORD_LEN);
strncpy_safe(cfg.ssid, wificonf->sta_ssid, SSID_LEN);
cfg.bssid_set = 0;
wifi_station_disconnect();
wifi_station_set_config(&cfg);
wifi_station_connect();
x = wifi_get_opmode();
connTryStatus = CONNTRY_WORKING;
// Assumption:
// if we're in station mode, no need to check: the browser will be disconnected
// and the user finds out whether it succeeded or not by checking if they can connect
if (x != STATION_MODE) {
os_timer_disarm(&staCheckTimer);
os_timer_setfn(&staCheckTimer, staCheckConnStatus, NULL);
os_timer_arm(&staCheckTimer, 15000, 0); //time out after 15 secs of trying to connect
}
}
/**
* This cgi uses the routines above to connect to a specific access point with the
* given ESSID using the given password.
*
* Args:
* - essid = SSID to connect to
* - passwd = password to connect with
*/
httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiConnect(HttpdConnData *connData)
{
char ssid[100];
char password[100];
static os_timer_t reassTimer;
if (connData->conn == NULL) {
//Connection aborted. Clean up.
return HTTPD_CGI_DONE;
}
int ssilen = httpdFindArg(connData->post->buff, "sta_ssid", ssid, sizeof(ssid));
int passlen = httpdFindArg(connData->post->buff, "sta_password", password, sizeof(password));
if (ssilen == -1 || passlen == -1) {
error("Did not receive the required arguments!");
httpdRedirect(connData, "/wifi");
}
else {
strncpy_safe(wificonf->sta_ssid, ssid, SSID_LEN);
strncpy_safe(wificonf->sta_password, password, PASSWORD_LEN);
info("Try to connect to AP \"%s\" pw \"%s\".", ssid, password);
//Schedule disconnect/connect
os_timer_disarm(&reassTimer);
os_timer_setfn(&reassTimer, cgiWiFiConnect_do, NULL);
// redirect & start connecting a little bit later
os_timer_arm(&reassTimer, 1000, 0); // was 500, increased so the connecting page has time to load
connTryStatus = CONNTRY_IDLE;
httpdRedirect(connData, "/wifi/connecting"); // this page is meant to show progress
}
return HTTPD_CGI_DONE;
}
/** /**
* Cgi to get connection status. * Cgi to get connection status.
* *
@ -363,45 +257,56 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiConnect(HttpdConnData *connData)
httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiConnStatus(HttpdConnData *connData) httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiConnStatus(HttpdConnData *connData)
{ {
char buff[100]; char buff[100];
int len;
struct ip_info info; struct ip_info info;
int st = wifi_station_get_connect_status();
httpdStartResponse(connData, 200); httpdStartResponse(connData, 200);
httpdHeader(connData, "Content-Type", "application/json"); httpdHeader(connData, "Content-Type", "application/json");
httpdEndHeaders(connData); httpdEndHeaders(connData);
if (connTryStatus == CONNTRY_IDLE) { // if bad opmode or no SSID configured, skip any checks
len = sprintf(buff, "{\"status\": \"idle\"}"); if (!(wificonf->opmode & STATION_MODE) || wificonf->sta_ssid[0] == 0) {
httpdSend(connData, "{\"status\": \"disabled\"}", -1);
return HTTPD_CGI_DONE;
} }
else if (connTryStatus == CONNTRY_WORKING || connTryStatus == CONNTRY_SUCCESS) {
if (st == STATION_GOT_IP) { STATION_STATUS st = wifi_station_get_connect_status();
switch(st) {
case STATION_IDLE:
sprintf(buff, "{\"status\": \"idle\"}"); // unclear when this is used
break;
case STATION_CONNECTING:
sprintf(buff, "{\"status\": \"working\"}");
break;
case STATION_WRONG_PASSWORD:
sprintf(buff, "{\"status\": \"fail\", \"cause\": \"WRONG_PASSWORD\"}");
break;
case STATION_NO_AP_FOUND:
sprintf(buff, "{\"status\": \"fail\", \"cause\": \"AP_NOT_FOUND\"}");
break;
case STATION_CONNECT_FAIL:
sprintf(buff, "{\"status\": \"fail\", \"cause\": \"CONNECTION_FAILED\"}");
break;
case STATION_GOT_IP:
wifi_get_ip_info(STATION_IF, &info); wifi_get_ip_info(STATION_IF, &info);
len = sprintf(buff, "{\"status\": \"success\", \"ip\": \"" sprintf(buff, "{\"status\": \"success\", \"ip\": \""IPSTR"\"}", GOOD_IP2STR(info.ip.addr));
IPSTR break;
"\"}", GOOD_IP2STR(info.ip.addr));
os_timer_disarm(&staCheckTimer);
os_timer_setfn(&staCheckTimer, staCheckConnStatus, NULL);
os_timer_arm(&staCheckTimer, 1000, 0);
} else {
len = sprintf(buff, "{\"status\": \"working\"}");
}
}
else {
len = sprintf(buff, "{\"status\": \"fail\"}");
} }
httpdSend(connData, buff, len); httpdSend(connData, buff, -1);
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }
/** reset_later() timer */
/** /**
* Callback for async timer * Callback for async timer
*/ */
static void ICACHE_FLASH_ATTR applyWifiSettingsLaterCb(void *arg) static void ICACHE_FLASH_ATTR applyWifiSettingsLaterCb(void *arg)
{ {
(void*)arg;
wifimgr_apply_settings(); wifimgr_apply_settings();
} }
@ -415,11 +320,9 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetParams(HttpdConnData *connData)
char buff[50]; char buff[50];
#define REDIR_BASE_URL "/wifi?err="
char redir_url_buf[300]; char redir_url_buf[300];
char *redir_url = redir_url_buf; char *redir_url = redir_url_buf;
redir_url += sprintf(redir_url, REDIR_BASE_URL); 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
if (connData->conn == NULL) { if (connData->conn == NULL) {
@ -427,8 +330,6 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetParams(HttpdConnData *connData)
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }
#define GET_ARG(key) (httpdFindArg(connData->getArgs, key, buff, sizeof(buff)) > 0)
// ---- WiFi opmode ---- // ---- WiFi opmode ----
if (GET_ARG("opmode")) { if (GET_ARG("opmode")) {
@ -548,85 +449,6 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetParams(HttpdConnData *connData)
} }
} }
// ---- AP DHCP server lease time ----
if (GET_ARG("ap_dhcp_time")) {
dbg("Setting DHCP lease time to: %s min.", buff);
int min = atoi(buff);
if (min >= 1 && min <= 2880) {
if (wificonf->ap_dhcp_time != min) {
wificonf->ap_dhcp_time = (u16) min;
wifi_change_flags.ap = true;
}
} else {
warn("Lease time %s out of allowed range 1-2880.", buff);
redir_url += sprintf(redir_url, "ap_dhcp_time,");
}
}
// ---- AP DHCP start and end IP ----
if (GET_ARG("ap_dhcp_start")) {
dbg("Setting DHCP range start IP to: \"%s\"", buff);
u32 ip = ipaddr_addr(buff);
if (ip != 0) {
if (wificonf->ap_dhcp_range.start_ip.addr != ip) {
wificonf->ap_dhcp_range.start_ip.addr = ip;
wifi_change_flags.ap = true;
}
} else {
warn("Bad IP: %s", buff);
redir_url += sprintf(redir_url, "ap_dhcp_start,");
}
}
if (GET_ARG("ap_dhcp_end")) {
dbg("Setting DHCP range end IP to: \"%s\"", buff);
u32 ip = ipaddr_addr(buff);
if (ip != 0) {
if (wificonf->ap_dhcp_range.end_ip.addr != ip) {
wificonf->ap_dhcp_range.end_ip.addr = ip;
wifi_change_flags.ap = true;
}
} else {
warn("Bad IP: %s", buff);
redir_url += sprintf(redir_url, "ap_dhcp_end,");
}
}
// ---- AP local address & config ----
if (GET_ARG("ap_addr_ip")) {
dbg("Setting AP local IP to: \"%s\"", buff);
u32 ip = ipaddr_addr(buff);
if (ip != 0) {
if (wificonf->ap_addr.ip.addr != ip) {
wificonf->ap_addr.ip.addr = ip;
wificonf->ap_addr.gw.addr = ip; // always the same, we're the router here
wifi_change_flags.ap = true;
}
} else {
warn("Bad IP: %s", buff);
redir_url += sprintf(redir_url, "ap_addr_ip,");
}
}
if (GET_ARG("ap_addr_mask")) {
dbg("Setting AP local IP netmask to: \"%s\"", buff);
u32 ip = ipaddr_addr(buff);
if (ip != 0) {
if (wificonf->ap_addr.netmask.addr != ip) {
// ideally this should be checked to match the IP.
// Let's hope users know what they're doing
wificonf->ap_addr.netmask.addr = ip;
wifi_change_flags.ap = true;
}
} else {
warn("Bad IP mask: %s", buff);
redir_url += sprintf(redir_url, "ap_addr_mask,");
}
}
// ---- Station SSID (to connect to) ---- // ---- Station SSID (to connect to) ----
if (GET_ARG("sta_ssid")) { if (GET_ARG("sta_ssid")) {
@ -649,63 +471,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetParams(HttpdConnData *connData)
} }
} }
// ---- Station enable/disable DHCP ---- if (redir_url_buf[strlen(SET_REDIR_ERR)] == 0) {
// DHCP enable / disable (disable means static IP is enabled)
if (GET_ARG("sta_dhcp_enable")) {
dbg("DHCP enable = %s", buff);
int enable = atoi(buff);
if (wificonf->sta_dhcp_enable != enable) {
wificonf->sta_dhcp_enable = (bool)enable;
wifi_change_flags.sta = true;
}
}
// ---- Station IP config (Static IP) ----
if (GET_ARG("sta_addr_ip")) {
dbg("Setting Station mode static IP to: \"%s\"", buff);
u32 ip = ipaddr_addr(buff);
if (ip != 0) {
if (wificonf->sta_addr.ip.addr != ip) {
wificonf->sta_addr.ip.addr = ip;
wifi_change_flags.sta = true;
}
} else {
warn("Bad IP: %s", buff);
redir_url += sprintf(redir_url, "sta_addr_ip,");
}
}
if (GET_ARG("sta_addr_mask")) {
dbg("Setting Station mode static IP netmask to: \"%s\"", buff);
u32 ip = ipaddr_addr(buff);
if (ip != 0 && ip != 0xFFFFFFFFUL) {
if (wificonf->sta_addr.netmask.addr != ip) {
wificonf->sta_addr.netmask.addr = ip;
wifi_change_flags.sta = true;
}
} else {
warn("Bad IP mask: %s", buff);
redir_url += sprintf(redir_url, "sta_addr_mask,");
}
}
if (GET_ARG("sta_addr_gw")) {
dbg("Setting Station mode static IP default gateway to: \"%s\"", buff);
u32 ip = ipaddr_addr(buff);
if (ip != 0) {
if (wificonf->sta_addr.gw.addr != ip) {
wificonf->sta_addr.gw.addr = ip;
wifi_change_flags.sta = true;
}
} else {
warn("Bad gw IP: %s", buff);
redir_url += sprintf(redir_url, "sta_addr_gw,");
}
}
if (redir_url_buf[strlen(REDIR_BASE_URL)] == 0) {
// All was OK // All was OK
info("Set WiFi params - success, applying in 1000 ms"); info("Set WiFi params - success, applying in 1000 ms");
@ -722,7 +488,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetParams(HttpdConnData *connData)
os_timer_setfn(&timer, applyWifiSettingsLaterCb, NULL); os_timer_setfn(&timer, applyWifiSettingsLaterCb, NULL);
os_timer_arm(&timer, 1000, false); os_timer_arm(&timer, 1000, false);
httpdRedirect(connData, "/wifi"); 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=
@ -773,39 +539,12 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplWlan(HttpdConnData *connData, char *token,
else if (streq(token, "ap_hidden")) { else if (streq(token, "ap_hidden")) {
sprintf(buff, "%d", wificonf->ap_hidden); sprintf(buff, "%d", wificonf->ap_hidden);
} }
else if (streq(token, "ap_dhcp_time")) {
sprintf(buff, "%d", wificonf->ap_dhcp_time);
}
else if (streq(token, "ap_dhcp_start")) {
sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->ap_dhcp_range.start_ip.addr));
}
else if (streq(token, "ap_dhcp_end")) {
sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->ap_dhcp_range.end_ip.addr));
}
else if (streq(token, "ap_addr_ip")) {
sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->ap_addr.ip.addr));
}
else if (streq(token, "ap_addr_mask")) {
sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->ap_addr.netmask.addr));
}
else if (streq(token, "sta_ssid")) { else if (streq(token, "sta_ssid")) {
sprintf(buff, "%s", wificonf->sta_ssid); sprintf(buff, "%s", wificonf->sta_ssid);
} }
else if (streq(token, "sta_password")) { else if (streq(token, "sta_password")) {
sprintf(buff, "%s", wificonf->sta_password); sprintf(buff, "%s", wificonf->sta_password);
} }
else if (streq(token, "sta_dhcp_enable")) {
sprintf(buff, "%d", wificonf->sta_dhcp_enable);
}
else if (streq(token, "sta_addr_ip")) {
sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->sta_addr.ip.addr));
}
else if (streq(token, "ap_addr_mask")) {
sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->sta_addr.netmask.addr));
}
else if (streq(token, "ap_addr_gw")) {
sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->sta_addr.gw.addr));
}
else if (streq(token, "sta_rssi")) { else if (streq(token, "sta_rssi")) {
sprintf(buff, "%d", wifi_station_get_rssi()); sprintf(buff, "%d", wifi_station_get_rssi());
} }

@ -2,32 +2,12 @@
#define CGIWIFI_H #define CGIWIFI_H
#include "httpd.h" #include "httpd.h"
#include "helpers.h"
/**
* Convert IP hex to arguments for printf.
* Library IP2STR(ip) does not work correctly due to unaligned memory access.
*/
#define GOOD_IP2STR(ip) ((ip)>>0)&0xff, ((ip)>>8)&0xff, ((ip)>>16)&0xff, ((ip)>>24)&0xff
httpd_cgi_state cgiWiFiScan(HttpdConnData *connData); httpd_cgi_state cgiWiFiScan(HttpdConnData *connData);
httpd_cgi_state cgiWiFiConnect(HttpdConnData *connData);
httpd_cgi_state cgiWiFiConnStatus(HttpdConnData *connData);
httpd_cgi_state cgiWiFiSetParams(HttpdConnData *connData); httpd_cgi_state cgiWiFiSetParams(HttpdConnData *connData);
httpd_cgi_state tplWlan(HttpdConnData *connData, char *token, void **arg); httpd_cgi_state tplWlan(HttpdConnData *connData, char *token, void **arg);
httpd_cgi_state cgiWiFiConnStatus(HttpdConnData *connData);
// WiFi config options:
// - Persistent
// - channel
// - AP ssid
// - opmode
// - AP to connect to
// - Temporary
// - sta_hostname (sta)
// - tpw (ap, sta+ap?)
// - dhcp_lt (ap, sta+ap)
// - static IP
// - static mask
// - static gw
// - dhcp enable or disable
#endif #endif

Loading…
Cancel
Save