Merge Development version to master

This commit is contained in:
luc lebosse 2017-11-13 16:17:07 +01:00
parent 628436cb9c
commit 13ee5e19e8
86 changed files with 10000 additions and 6357 deletions

View File

@ -11,9 +11,9 @@ before_install:
before_script:
- "export DISPLAY=:99.0"
- sleep 3 # give xvfb some time to start
- wget http://downloads.arduino.cc/arduino-1.8.0-linux64.tar.xz
- tar xf arduino-1.8.0-linux64.tar.xz
- mv arduino-1.8.0 $HOME/arduino_ide
- wget http://downloads.arduino.cc/arduino-1.8.4-linux64.tar.xz
- tar xf arduino-1.8.4-linux64.tar.xz
- mv arduino-1.8.4 $HOME/arduino_ide
- cd $HOME/arduino_ide/hardware
- mkdir esp8266com
- cd esp8266com
@ -21,13 +21,22 @@ before_script:
- cd esp8266/tools
- python get.py
- cd ..
- touch pt.txt
- echo 'build.flash_ld=eagle.flash.4m.ld' >> pt.txt
- echo 'build.flash_freq=40' >> pt.txt
- echo 'build.flash_size=4M' >> pt.txt
- cat ./platform.txt >> pt.txt
- rm -fr platform.txt
- mv pt.txt platform.txt
- echo 'build.flash_ld=eagle.flash.4m.ld' >> platform.txt
- echo 'build.flash_freq=40' >> platform.txt
- echo 'build.flash_size=4M' >> platform.txt
- echo 'build.flash_mode=dio' >> platform.txt
- echo 'build.f_cpu=160000000L' >> platform.txt
- sed -i "s/generic.build.f_cpu=80000000L/generic.build.f_cpu=160000000L/g" ./boards.txt
- cd $HOME/arduino_ide/hardware
- mkdir esp32
- cd esp32
- git clone https://github.com/espressif/arduino-esp32.git esp32
- cd esp32/tools
- python get.py
- cd ..
- echo 'build.flash_freq=40m' >> platform.txt
- mv $TRAVIS_BUILD_DIR/libraries/WebServer $HOME/arduino_ide/hardware/esp32/esp32/libraries/
- mv $TRAVIS_BUILD_DIR/libraries/DNSServer $HOME/arduino_ide/hardware/esp32/esp32/libraries/
script:
@ -37,6 +46,8 @@ script:
- arduino --board esp8266com:esp8266:generic --save-prefs
- arduino --get-pref sketchbook.path
- build_sketch esp3d/esp3d.ino
- arduino --board esp32:esp32:esp32 --save-prefs
- build_sketch esp3d/esp3d.ino
notifications:
email:

190
README.md
View File

@ -1,29 +1,32 @@
# ESP3D[![Code Climate](https://codeclimate.com/github/luc-github/ESP3D/badges/gpa.svg)](https://codeclimate.com/github/luc-github/ESP3D)
Firmware for ESP8266/ESP8285 used with 3D printer using [arduino core version](https://github.com/esp8266/Arduino)
Firmware for ESP8266/ESP8285 and ESP32 used with 3D printer using [ESP8266 core version](https://github.com/esp8266/Arduino) and [ESP32 core version](https://github.com/espressif/arduino-esp32)
This firmware allows not only to have a cheap bridge between Wifi and serial, but also to have a web UI to configure wifi, to monitor 3D printer and even control it, and to make things easy,
UI is fully customizable without reflashing FW.
Firmware should work with any 3D printer firmware (repetier/marlin/etc..) if serial connection has correct setup.
I currently use it with my personnal flavor of [repetier for Due based boards](https://github.com/luc-github/Repetier-Firmware-0.92).
Please use ESP with at least 1M flash, for ESP with 512K there is limited version [here](https://github.com/luc-github/ESP3D/tree/ESP-512K-64KSPIFFS)
<u>Stable version:</u>
Firmware should work with any 3D printer firmware (repetier/marlin/smoothieware using GCODE) if serial connection has a correct setup.
I currently use it with my personnal flavor of [repetier for Due based boards](https://github.com/luc-github/Repetier-Firmware-0.92).
The web interface files are present in data directory but UI has it's own repository [ESP3D-WEBUI](https://github.com/luc-github/ESP3D-WEBUI).
* be aware ESP3D-WEBUI is for firmware 0.9.99 minimum - previous released version use tpl files which are no more used.
<u>Stable version (ESP8266 only):</u>
Arduino ide 1.6.5 with stable [2.0.0](http://arduino.esp8266.com/versions/2.0.0/package_esp8266com_index.json) from ESP8266, please use https://github.com/luc-github/ESP3D/releases/tag/v0.5.1
Arduino ide 1.6.8 with stable [2.2.0](http://arduino.esp8266.com/versions/2.2.0/package_esp8266com_index.json) from ESP8266, please use https://github.com/luc-github/ESP3D/releases/tag/v0.6.2
<u>Development version:</u>
Arduino ide 1.8.0 with git from ESP8266 : [![Build Status](https://travis-ci.org/luc-github/ESP3D.svg?branch=master)](https://travis-ci.org/luc-github/ESP3D)
<u>RC version for 1.0(master branch) very stable but waiting for 2.4 release of ESP8266:</u>
Arduino ide 1.8.4 with git version from ESP8266 or ESP32 : [![Build Status](https://travis-ci.org/luc-github/ESP3D.svg?branch=master)](https://travis-ci.org/luc-github/ESP3D)
<u>Development version for 2.0 (asyncwebserver branch) pre alpha :</u>
Arduino ide 1.8.4 with git version from ESP8266 or ESP32 : [![Build Status](https://travis-ci.org/luc-github/ESP3D.svg?branch=asyncwebserver)](https://travis-ci.org/luc-github/ESP3D)
[All releases](https://github.com/luc-github/ESP3D/wiki)
:question:Any question ? check [Wiki](https://github.com/luc-github/ESP3D/wiki/Install-Instructions) or [![Join the chat at https://gitter.im/luc-github/ESP3D](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/luc-github/ESP3D?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
:exclamation:Any issue ? check [Wiki](https://github.com/luc-github/ESP3D/wiki/Install-Instructions) and [FAQ](https://github.com/luc-github/ESP3D/issues?utf8=%E2%9C%93&q=label%3AFAQ+) or [submit ticket](https://github.com/luc-github/ESP3D/issues)
:+1:Thanks
* to @disneysw for bringing this module idea
* to @lkarlslund for suggestion about independent reset using GPIO2
* to all contributors (treepleks, j0hnlittle, openhardwarecoza, TRoager, all feedbacks owners and donations)
* to all contributors, feedbacks owners and donations.
Every support is welcome: [<img src="https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG_global.gif" border="0" alt="PayPal The safer, easier way to pay online.">](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=Y8FFE7NA4LJWQ)
Especially if need to buy new modules for testing.
@ -31,7 +34,7 @@ Especially if need to buy new modules for testing.
##Features
* Serial/Wifi bridge using configurable port 8888, here to enable/disable [TCP_IP_DATA_FEATURE](https://github.com/luc-github/ESP3D/blob/master/esp3d/config.h)
* Use GPIO2 to ground to reset all settings in hard way - 2-6 sec after boot / not before!! Set GPIO2 to ground before boot change boot mode and go to special boot that do not reach FW. Currently boot take 10 sec - giving 8 seconds to connect GPIO2 to GND and do an hard recovery for settings, here to enable/disable [RECOVERY_FEATURE](https://github.com/luc-github/ESP8266/blob/master/esp8266/config.h)
* Wifi configuration by web browser (Station or Access point)
* Complete configuration by web browser (Station or Access point) or by Serial commands
* Authentication for sensitive pages, here to enable/disable [AUTHENTICATION_FEATURE](https://github.com/luc-github/ESP3D/blob/master/esp3d/config.h)
* Update firmware by web browser, here to enable/disable [WEB_UPDATE_FEATURE](https://github.com/luc-github/ESP3D/blob/master/esp3d/config.h)
* Control ESP module using commands on serial or data port, here to enable/disable [SERIAL_COMMAND_FEATURE](https://github.com/luc-github/ESP3D/blob/master/esp3d/config.h)
@ -41,6 +44,8 @@ Especially if need to buy new modules for testing.
* SSDP, this feature is a discovery protocol, supported on Windows out of the box, here to enable/disable [SSDP_FEATURE](https://github.com/luc-github/ESP3D/blob/master/esp3d/config.h)
* Printer monitoring / control (temperatures/speed/jog/list SDCard content/launch,pause or stop a print/etc...), here to enable/disable [MONITORING_FEATURE/INFO_MSG_FEATURE/ERROR_MSG_FEATURE/STATUS_MSG_FEATURE](https://github.com/luc-github/ESP3D/blob/master/esp3d/config.h)
* Fail safe mode (Access point)is enabled if cannot connect to defined station at boot.
* The web ui add even more feature : https://github.com/luc-github/ESP3D-WEBUI/blob/master/README.md#features
##Web configuration
*Wifi Mode : Access point / Client station
@ -63,7 +68,7 @@ IP Mode: Static IP
IP: 192.168.0.1
Mask: 255.255.255.0
GW:192.168.0.1
Baud rate: 9600
Baud rate: 115200
Web port:80
Data port: 8888
Web Page refresh: 3 secondes
@ -72,33 +77,7 @@ Password: admin
User:user
Password: user
These are the pages defined using template:
Home page :
<img src=https://raw.githubusercontent.com/luc-github/ESP3D/master/images/UI/Page1.png><br>
System Configuration Page:
<img src=https://raw.githubusercontent.com/luc-github/ESP3D/master/images/UI/Page2.png><br>
Access Point Configuration Page:
<img src=https://raw.githubusercontent.com/luc-github/ESP3D/master/images/UI/Page3.png><br>
Client Configuration Page:
<img src=https://raw.githubusercontent.com/luc-github/ESP3D/master/images/UI/Page4.png><br>
Printer Status Page for 64K SPIFFS, due to limited space available no fancy:
<img src=https://raw.githubusercontent.com/luc-github/ESP3D/master/images/UI/Page5-2.png><br>
Printer Status Page for more than 64K SPIFFS, fancy one:
<img src=https://raw.githubusercontent.com/luc-github/ESP3D/master/images/UI/page5.png><br>
Extra Settings Page, for web UI and for printer:
<img src=https://raw.githubusercontent.com/luc-github/ESP3D/master/images/UI/Page6.png><br>
Change password Page:
<img src=https://raw.githubusercontent.com/luc-github/ESP3D/master/images/UI/Page7.png><br>
Login Page:
<img src=https://raw.githubusercontent.com/luc-github/ESP3D/master/images/UI/Page8.png><br>
the template files are stored on SPIFFS:
<img src=https://raw.githubusercontent.com/luc-github/ESP3D/master/images/UI/files.png><br>
and uploaded using [pluggin IDE](http://esp8266.github.io/Arduino/versions/2.1.0/doc/filesystem.html#uploading-files-to-file-system)
Any files on SPIFFS can be called on web interface without having the path hard coded, this give more flexibility, favicon.ico is a good example of it.
So UI is kind of separated from FW which allow easier modifications. For this a light file manager is available in extra settings page, it allows to upload/download/delete files.
Because SPIFFS is flat filesystem, no directory management is necessary, so it is very simple.
Additionally 404.tpl (the page not found) and restart.tpl(restart page when applying changes) are not mandatory, a fail safe version is embedded if they are not present.
##Direct commands:
```
@ -142,12 +121,50 @@ if authentication is on, need admin password
if authentication is on, need admin password
[ESP107]<mode>pwd=<admin password>
* Set wifi on/off
[ESP110]<state>
state can be ON, OFF, RESTART
if authentication is on, need admin password
[ESP110]<state>pwd=<admin password>
* Get current IP
[ESP111]<header answer>
* Get hostname
[ESP112]<header answer>
*Get/Set pin value
[ESP201]P<pin> V<value> [PULLUP=YES RAW=YES]
if no V<value> get P<pin> value
if V<value> 0/1 set INPUT_PULLUP value, but for GPIO16 INPUT_PULLDOWN_16
GPIO1 and GPIO3 cannot be used as they are used for serial
if PULLUP=YES set input pull up, if not set input
if RAW=YES do not set pinmode just read value
*Save data string
[ESP300]<data> pwd=<user/admin password>
*Get data string
[ESP301] pwd=<user/admin password>
*Get full EEPROM settings content
but do not give any passwords
can filter if only need wifi or printer
[ESP400]<network/printer>
*Set EEPROM setting
position in EEPROM, type: B(byte), I(integer/long), S(string), A(IP address / mask)
[ESP401]P=<position> T=<type> V=<value> pwd=<user/admin password>
*Get available AP list (limited to 30)
output is JSON or plain text according parameter
[ESP410]<plain>
*Get current settings of ESP3D
output is JSON or plain text according parameter
[ESP420]<plain>
* Get/Set ESP mode
cmd can be RESET, SAFEMODE, CONFIG, RESTART
[ESP444]<cmd>
@ -161,79 +178,62 @@ if no password set it use default one
* Read SPIFFS file and send each line to serial
[ESP700]<filename>
* Get fw version
* Format SPIFFS
[ESP710]FORMAT pwd=<admin password>
* SPIFFS total size and used size
[ESP720]<header answer>
* Get fw version and basic information
[ESP800]<header answer>
* Get fw target
[ESP801]<header answer>
* Clear status/error/info list
cmd can be ALL, ERROR, INFO, STATUS
[ESP999]<cmd>
```
##Installation
* For stable:
Please use [Arduino IDE 1.6.5](http://arduino.cc/en/Main/Software) with the esp8266 module from board manager use 2.0.0 stable version by adding in your preferences http://arduino.esp8266.com/version/2.0.0/package_esp8266com_index.json
with https://github.com/luc-github/ESP3D/releases/tag/v0.5.1
or
use [Arduino IDE 1.6.8](http://arduino.cc/en/Main/Software) with the esp8266 module from board manager use 2.2.0 stable version by adding in your preferences http://arduino.esp8266.com/stable/package_esp8266com_index.json
with https://github.com/luc-github/ESP3D/releases/tag/v0.6.2
1. Please follow installation of the ESP core you want to use : [ESP8266 core version](https://github.com/esp8266/Arduino) or [ESP32 core version](https://github.com/espressif/arduino-esp32)
2. Add missing libraries if you target ESP32 present in libraries directory
* DNSServer (from https://github.com/bbx10/DNSServer_tng)
* WebServer (from https://github.com/bbx10/WebServer_tng)
* NetBIOS and SSDP are currently disabled for ESP32 as not yet supported
3. Compile project (ESP3D.ino) according target: ESP8266 board or ESP32 board, please review config.h to enable disable a feature, by default athenticatio is disabled and all others are enabled.
* for ESP8266 set CPU freq to 160MHz for better (https://github.com/luc-github/ESP3D/wiki/Install-Instructions)
4. Upload the data content on ESP3D file system
* Using SPIFFS uploader, this plugin and install instructions is available on each ESP core - please refere to it
* Using embedded uploader (you may need to format SPIFFS using : [ESP710]FORMAT on ESP8266 first)
if embedded uploader does not show up you can force it ti display using : http://your_IP_address?forcefallback=yes
<img src=https://raw.githubusercontent.com/luc-github/ESP3D/master/images/docs/embedded.png><br>
* For development:
Please use [Arduino IDE 1.6.8](http://arduino.cc/en/Main/Software) and [git version of esp8266 module](http://esp8266.github.io/Arduino/versions/2.2.0/doc/installing.html#using-git-version)
##Update
* Generate a binary using the export binary menu from Arduino IDE and upload it using ESP-WEBUI or embedded interface
* To flash the module :
You must define the targeted FW, can be REPETIER (Original Repetier)/ REPETIER4DV (Repetier for Davinci) / MARLIN (Marlin)/ SMOOTHIEWARE (Smoothieware) in config.h and enable/disable any others features
```
//MDNS_FEATURE: this feature allow type the name defined
//in web browser by default: http:\\esp8266.local and connect
//#define MDNS_FEATURE
//SSDD_FEATURE: this feature is a discovery protocol, supported on Windows out of the box
#define SSDP_FEATURE
//CAPTIVE_PORTAL_FEATURE: In SoftAP redirect all unknow call to main page
#define CAPTIVE_PORTAL_FEATURE
//AUTHENTICATION_FEATURE: protect pages by login password
#define AUTHENTICATION_FEATURE
//WEB_UPDATE_FEATURE: allow to flash fw using web UI
#define WEB_UPDATE_FEATURE
//SERIAL_COMMAND_FEATURE: allow to send command by serial
#define SERIAL_COMMAND_FEATURE
//TCP_IP_DATA_FEATURE: allow to connect serial from TCP/IP
#define TCP_IP_DATA_FEATURE
//RECOVERY_FEATURE: allow to use GPIO2 pin as hardware reset for EEPROM, add 8s to boot time to let user to jump GPIO2 to GND
#define RECOVERY_FEATURE
//FIRMWARE_TARGET: the targeted FW, can be REPETIER (Original Repetier)/ REPETIER4DV (Repetier for Davinci) / MARLIN (Marlin)/ SMOOTHIEWARE (Smoothieware)
#define FIRMWARE_TARGET REPETIER4DV
//DEBUG Flag
//#define DEBUG_ESP3D
```
For better performance select CPU Frequency to be 160MHz instead of default 80MHz
Use IDE to upload directly (latest version of board manager module generate one binary)
* To flash the html files present in data directory you need to use another tool, installation and usage is explained [here](https://github.com/esp8266/Arduino/blob/master/doc/filesystem.md#uploading-files-to-file-system)
Once flashed you also can use the web updater to flash new FW in System Configuration Page or go to settings to change html files
<H3>:warning:Do not flash Printer fw with ESP connected - it bring troubles, at least on DaVinci</H3>
<H3>:warning:Do not flash your Printer fw with ESP connected - it bring troubles, at least on DaVinci</H3>
##Contribution/customization
To modifying and Testing tpl files a local tool has been created by [j0hnlittle](https://github.com/j0hnlittle) to avoid to upload every time your tpl files just to see the results of your modifications. It is a python script (2.7+) located in tools directory, launch it: python server.py, then open browser: http://localhost:8080
It will display the web ui and allow some navigation
To style the code before pushing PR please use [astyle --style=otbs *.h *.cpp *.ino](http://astyle.sourceforge.net/)
* To style the code before pushing PR please use [astyle --style=otbs *.h *.cpp *.ino](http://astyle.sourceforge.net/)
* The embedded page is created using nodejs then gulp to generate a compressed html page (tool.html.gz), all necessary modules can be installed using the install.bat file content, then it is included using bin2c (https://sourceforge.net/projects/bin2c/) to generate the h file used to create the file nofile.h, update the array and size according new out.h.
* The current UI is located [here](https://github.com/luc-github/ESP3D-WEBUI)
* An optional UI is under development using old repetier UI - check [UI\repetier\testui.htm] (https://github.com/luc-github/ESP3D/blob/master/UI/repetier/testui.htm) file
Feedback/suggestion/discussions are always welcome
##Need more information about board or wiring ?
##Need more information about supported boards or wiring ?
[Check the wiki](https://github.com/luc-github/ESP3D/wiki)
##:question:Any question ?
Check [Wiki](https://github.com/luc-github/ESP3D/wiki/Install-Instructions) or [![Join the chat at https://gitter.im/luc-github/ESP3D](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/luc-github/ESP3D?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
##:exclamation:Any issue/feedback ?
Check [Wiki](https://github.com/luc-github/ESP3D/wiki/Install-Instructions) and [FAQ](https://github.com/luc-github/ESP3D/issues?utf8=%E2%9C%93&q=label%3AFAQ+) or [submit ticket](https://github.com/luc-github/ESP3D/issues)
##TODO
-- Close open topics
-- Do testing (a lot)
-- UI Improvement
-- ESP3D V2

View File

@ -1,150 +0,0 @@
<html>
<head>
</head>
<body>
<style>
.sliderthumb{
position : absolute;
cursor : pointer;
}
.sliderrange{
cursor : pointer;
}
</style>
<script>
var offset = 0;
var isDown = false;
var active_control = null;
document.addEventListener('mouseup', function() {
isDown = false;
}, true);
//position is based on screen position value
function movethumb(pos)
{
var p = pos + offset;
var minpos =0;
var maxpos = 0;
var minvalue =0;
var maxvalue = 100;
var value=0;
var rangeelement =null;
if (active_control.hasAttribute('customrange')){
rangeelement = document.getElementById(active_control.getAttribute('customrange'));
var r = rangeelement.getBoundingClientRect();
minpos = r.left;
maxpos = r.right;
if (rangeelement.hasAttribute('min')){
minvalue = parseFloat(rangeelement.getAttribute('min'))
}
if (rangeelement.hasAttribute('max')){
maxvalue = parseFloat(rangeelement.getAttribute('max'))
}
}
var r2 = active_control.getBoundingClientRect();
if ( p >maxpos - (r2.right-r2.left)) p = maxpos - (r2.right-r2.left);
if ( p <minpos) p =minpos ;
active_control.style.left = p+ 'px';
if (active_control.hasAttribute('render')){
value = (((p-minpos) * (maxvalue-minvalue))/((maxpos-minpos)- (r2.right-r2.left)))
document.getElementById(active_control.getAttribute('render')).value = Math.round(value + minvalue) ;
active_control.style.cursor='pointer';
}
}
//position is based on range value
function movethumbat(thumbcursor, value){
var minvalue =0;
var maxvalue = 100;
var minpos =0;
var maxpos = 0;
var rangeelement =null;
if (thumbcursor.hasAttribute('customrange')){
rangeelement = document.getElementById(thumbcursor.getAttribute('customrange'));
var r = rangeelement.getBoundingClientRect();
minpos = r.left;
maxpos = r.right;
if (rangeelement.hasAttribute('min')){
minvalue = parseFloat(rangeelement.getAttribute('min'))
}
if (rangeelement.hasAttribute('max')){
maxvalue = parseFloat(rangeelement.getAttribute('max'))
}
}
if (value > maxvalue) value = maxvalue;
if (value < minvalue) value = minvalue;
var r2 = thumbcursor.getBoundingClientRect();
var pos=0;
pos = ((value-minvalue)*((maxpos-minpos)-(r2.right-r2.left))/(maxvalue-minvalue))+minpos;
thumbcursor.style.left = pos+ 'px';
if (thumbcursor.hasAttribute('render')){
document.getElementById(thumbcursor.getAttribute('render')).value =value;
}
}
document.addEventListener('mousemove', function(event) {
event.preventDefault();
if (isDown) {
movethumb(event.clientX);
}
}, true);
function rangeclicked(event,element)
{
if (element.hasAttribute('customthumb')){
active_control = document.getElementById(element.getAttribute('customthumb'));
offset = 0;
movethumb(event.clientX);
}
}
function initthumb( element)
{
element.addEventListener('mousedown', function(e) {
isDown = true;
offset = element.offsetLeft - e.clientX;
active_control = this;
}, true);
if (element.hasAttribute('bgimage')){
element.innerHTML = element.getAttribute('bgimage');
}
if (element.hasAttribute('customrange')){
var r = document.getElementById(element.getAttribute('customrange')).getBoundingClientRect();
element.style.top = r.top;
element.style.left = r.left;
}
}
function initrange( element)
{
if (element.hasAttribute('bgimage')){
element.innerHTML = element.getAttribute('bgimage');
}
}
</script>
<table border=1>
<tr>
<td><div id='range1' min="-10" max="270" class='sliderrange' onclick ="rangeclicked(event,this);"bgimage="<svg width='310' height='10' viewbox='0 0 310 10'><rect width='310' height='10' style='fill:rgb(255,0,255);stroke-width:3;stroke:rgb(0,0,0)' />" customthumb='thumb1'></div>
<div id='thumb1' class='sliderthumb' render='v1' bgimage="<svg width='10' height='10' viewbox='0 0 10 10'><circle cx='5' cy='5' r='5'/></svg>" customrange='range1'></div>
</td><td><input id='v1' type='number' value='0'/></td></tr>
<tr>
<td><div id='range2' min="50" max="300" onclick ="rangeclicked(event,this);" bgimage="<svg width='310' height='10' viewbox='0 0 310 10'><rect width='310' height='10' style='fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)' />" customthumb='thumb2'></div>
<div id='thumb2' class='sliderthumb' render='v2' bgimage="<svg width='10' height='10' viewbox='0 0 10 10'><circle cx='5' cy='5' r='5'/></svg>" customrange='range2'></div>
</td><td><input id='v2' type='number' value='0'/></td></tr>
</table>
<input type='button' onclick='movethumbat(thumb1, inputtest.value)' value='push me' > <input id='inputtest' type='number' />
<script>
initrange( range1);
initrange( range2);
initthumb( thumb1);
initthumb( thumb2);
</script>
</body>
</html>

View File

@ -522,7 +522,7 @@ function initrange( element)
<g id=''centerze">
<circle class="bg" cx="354" cy="122" r="34" filter="url(#shadow)" />
<circle class="bg" cx="354" cy="122" r="34" class="bg" opacity='0.7' filter="url(#light)" />
<text id="centeretext" class="centerjog" x="353" y="132" opacity="0">Z</text>
<text id="centeretext" class="centerjog" x="353" y="132" opacity="0"></text>
<g id="extruder">
<path class='sign' d="M350,108 L360,108 L360,128 L355,136 L350,128 z"/>
<path class='sign' d="M335,110 L338,109 L340,111 L341,111 L343,109 L346,110 L346,113 L349,113 L350,116 L348,118 L348,119 L350,121 L349,124 L346,124 L346,127 L343,128 L341,126 L340,126 L338,128 L335,127 L335,124 L332,124 L331,121 L333,119 L333,118 L331,116 L332,113 L335,113 z"/>

View File

@ -38,12 +38,20 @@ if authentication is on, need admin password
if authentication is on, need admin password
[ESP107]<mode>pwd=<admin password>
* Set wifi on/off
[ESP110]<state>
state can be ON, OFF, RESTART
if authentication is on, need admin password
[ESP110]<state>pwd=<admin password>
* Get current IP
[ESP111]<header answer>
* Get hostname
[ESP112]<header answer>
* Restart time client
[ESP114]
*Get/Set pin value
[ESP201]P<pin> V<value> [PULLUP=YES RAW=YES]
@ -53,6 +61,29 @@ GPIO1 and GPIO3 cannot be used as they are used for serial
if PULLUP=YES set input pull up, if not set input
if RAW=YES do not set pinmode just read value
*Save data string
[ESP300]<data> pwd=<user/admin password>
*Get data string
[ESP301] pwd=<user/admin password>
*Get full EEPROM settings content
but do not give any passwords
can filter if only need wifi or printer
[ESP400]<network/printer>
*Set EEPROM setting
position in EEPROM, type: B(byte), I(integer/long), S(string), A(IP address / mask)
[ESP401]P=<position> T=<type> V=<value> pwd=<user/admin password>
*Get available AP list (limited to 30)
output is JSON or plain text according parameter
[ESP410]<plain>
*Get current settings of ESP3D
output is JSON or plain text according parameter
[ESP420]<plain>
* Get/Set ESP mode
cmd can be RESET, SAFEMODE, CONFIG, RESTART
[ESP444]<cmd>
@ -66,7 +97,13 @@ if no password set it use default one
* Read SPIFFS file and send each line to serial
[ESP700]<filename>
* Get fw version
* Format SPIFFS
[ESP710]FORMAT pwd=<admin password>
* SPIFFS total size and used size
[ESP720]<header answer>
* Get fw version and basic information
[ESP800]<header answer>
* Get fw target

View File

@ -1,122 +0,0 @@
EPR:0 1028 7 Language
EPR:2 75 230400 Baudrate
EPR:0 1125 1 Display Mode:
EPR:0 1119 1 Light On:
EPR:0 1127 1 Keep Light On:
EPR:0 1126 0 Filament Sensor On:
EPR:0 1176 0 Top Sensor On:
EPR:0 1120 1 Sound On:
EPR:0 1177 1 Wifi On:
EPR:3 129 0.000 Filament printed [m]
EPR:2 125 0 Printer active [s]
EPR:2 79 0 Max. inactive time [ms,0=off]
EPR:2 83 360000 Stop stepper after inactivity [ms,0=off]
EPR:2 1121 0 Powersave after [ms,0=off]:
EPR:3 1160 180.000 Temp Ext PLA:
EPR:3 1164 230.000 Temp Ext ABS:
EPR:3 1168 60.000 Temp Bed PLA:
EPR:3 1172 90.000 Temp Bed ABS:
EPR:3 1179 2.000 Load Feed Rate:
EPR:3 1183 4.000 Unload Feed Rate:
EPR:3 1187 60.000 Unload/Load Distance:
EPR:3 3 80.0000 X-axis steps per mm
EPR:3 7 80.0000 Y-axis steps per mm
EPR:3 11 2560.0000 Z-axis steps per mm
EPR:3 15 200.000 X-axis max. feedrate [mm/s]
EPR:3 19 200.000 Y-axis max. feedrate [mm/s]
EPR:3 23 5.000 Z-axis max. feedrate [mm/s]
EPR:3 27 40.000 X-axis homing feedrate [mm/s]
EPR:3 31 40.000 Y-axis homing feedrate [mm/s]
EPR:3 35 4.000 Z-axis homing feedrate [mm/s]
EPR:3 39 20.000 Max. jerk [mm/s]
EPR:3 47 0.342 Max. Z-jerk [mm/s]
EPR:3 133 0.000 X min pos [mm]
EPR:3 137 0.000 Y min pos [mm]
EPR:3 141 0.000 Z min pos [mm]
EPR:3 145 199.000 X max length [mm]
EPR:3 149 204.000 Y max length [mm]
EPR:3 153 200.000 Z max length [mm]
EPR:3 51 1000.000 X-axis acceleration [mm/s^2]
EPR:3 55 1000.000 Y-axis acceleration [mm/s^2]
EPR:3 59 100.000 Z-axis acceleration [mm/s^2]
EPR:3 63 1000.000 X-axis travel acceleration [mm/s^2]
EPR:3 67 1000.000 Y-axis travel acceleration [mm/s^2]
EPR:3 71 150.000 Z-axis travel acceleration [mm/s^2]
EPR:3 1024 0.000 Coating thickness [mm]
EPR:3 1128 100.000 Manual-probe X1 [mm]
EPR:3 1132 180.000 Manual-probe Y1 [mm]
EPR:3 1136 100.000 Manual-probe X2 [mm]
EPR:3 1140 10.000 Manual-probe Y2 [mm]
EPR:3 1144 50.000 Manual-probe X3 [mm]
EPR:3 1148 95.000 Manual-probe Y3 [mm]
EPR:3 1152 150.000 Manual-probe X4 [mm]
EPR:3 1156 95.000 Manual-probe Y4 [mm]
EPR:3 808 0.280 Z-probe height [mm]
EPR:3 929 5.000 Max. z-probe - bed dist. [mm]
EPR:3 812 1.000 Z-probe speed [mm/s]
EPR:3 840 30.000 Z-probe x-y-speed [mm/s]
EPR:3 800 0.000 Z-probe offset x [mm]
EPR:3 804 0.000 Z-probe offset y [mm]
EPR:3 816 36.000 Z-probe X1 [mm]
EPR:3 820 -7.000 Z-probe Y1 [mm]
EPR:3 824 36.000 Z-probe X2 [mm]
EPR:3 828 203.000 Z-probe Y2 [mm]
EPR:3 832 171.000 Z-probe X3 [mm]
EPR:3 836 203.000 Z-probe Y3 [mm]
EPR:3 1036 0.000 Z-probe bending correction A [mm]
EPR:3 1040 0.000 Z-probe bending correction B [mm]
EPR:3 1044 0.000 Z-probe bending correction C [mm]
EPR:0 880 0 Autolevel active (1/0)
EPR:0 106 2 Bed Heat Manager [0-3]
EPR:0 107 255 Bed PID drive max
EPR:0 124 80 Bed PID drive min
EPR:3 108 196.000 Bed PID P-gain
EPR:3 112 33.000 Bed PID I-gain
EPR:3 116 290.000 Bed PID D-gain
EPR:0 120 255 Bed PID max value [0-255]
EPR:0 1020 0 Enable retraction conversion [0/1]
EPR:3 992 3.000 Retraction length [mm]
EPR:3 996 13.000 Retraction length extruder switch [mm]
EPR:3 1000 40.000 Retraction speed [mm/s]
EPR:3 1004 0.000 Retraction z-lift [mm]
EPR:3 1008 0.000 Extra extrusion on undo retract [mm]
EPR:3 1012 0.000 Extra extrusion on undo switch retract [mm]
EPR:3 1016 20.000 Retraction undo speed
EPR:3 200 99.000 Extr.1 steps per mm
EPR:3 204 50.000 Extr.1 max. feedrate [mm/s]
EPR:3 208 20.000 Extr.1 start feedrate [mm/s]
EPR:3 212 5000.000 Extr.1 acceleration [mm/s^2]
EPR:0 216 3 Extr.1 heat manager [0-3]
EPR:0 217 230 Extr.1 PID drive max
EPR:0 245 40 Extr.1 PID drive min
EPR:3 218 3.0000 Extr.1 PID P-gain/dead-time
EPR:3 222 2.0000 Extr.1 PID I-gain
EPR:3 226 40.0000 Extr.1 PID D-gain
EPR:0 230 255 Extr.1 PID max value [0-255]
EPR:2 231 0 Extr.1 X-offset [steps]
EPR:2 235 0 Extr.1 Y-offset [steps]
EPR:2 290 0 Extr.1 Z-offset [steps]
EPR:1 239 1 Extr.1 temp. stabilize time [s]
EPR:1 250 150 Extr.1 temp. for retraction when heating [C]
EPR:1 252 0 Extr.1 distance to retract when heating [mm]
EPR:0 254 255 Extr.1 extruder cooler speed [0-255]
EPR:3 246 0.000 Extr.1 advance L [0=off]
EPR:3 300 99.000 Extr.2 steps per mm
EPR:3 304 50.000 Extr.2 max. feedrate [mm/s]
EPR:3 308 20.000 Extr.2 start feedrate [mm/s]
EPR:3 312 5000.000 Extr.2 acceleration [mm/s^2]
EPR:0 316 3 Extr.2 heat manager [0-3]
EPR:0 317 230 Extr.2 PID drive max
EPR:0 345 40 Extr.2 PID drive min
EPR:3 318 3.0000 Extr.2 PID P-gain/dead-time
EPR:3 322 2.0000 Extr.2 PID I-gain
EPR:3 326 40.0000 Extr.2 PID D-gain
EPR:0 330 255 Extr.2 PID max value [0-255]
EPR:2 331 -2852 Extr.2 X-offset [steps]
EPR:2 335 12 Extr.2 Y-offset [steps]
EPR:2 390 0 Extr.2 Z-offset [steps]
EPR:1 339 1 Extr.2 temp. stabilize time [s]
EPR:1 350 150 Extr.2 temp. for retraction when heating [C]
EPR:1 352 0 Extr.2 distance to retract when heating [mm]
EPR:0 354 255 Extr.2 extruder cooler speed [0-255]
EPR:3 346 0.000 Extr.2 advance L [0=off]

View File

@ -1,150 +0,0 @@
[COMMON]
$INCLUDE[<filename>] : only one per line and alone, others will be ignored
$IP$ : current active ip
$WEB_ADDRESS$: current active ip , if port is not 80 it will add port like xxx.xxx.xxxx.xxx:XX
$MENU_HOME$/$MENU_SYSTEM$/$MENU_AP$/$MENU_STA$/$MENU_PRINTER$/$MENU_SETTINGS$: to highlight menu of active page (css use class active for active menu)
$SERVICE_PAGE$ : to add extra code like redirection or anything not managed by page
$PAGE_TILE$ : page title
$FILENAME$ : file name of tpl
$SHORT_FILENAME$ : file name of tpl without extension
$MODE$ : the mode when displaying page AP/STA/AP_STA
$FW_VER$ : Firmware version
[HOME]
$HOSTNAME$: Hostname
$HOSTNAME_VISIBLE$ : if AP mode then hostname is not applicable so set to hidden, if in STA mode set to visible
$CHIP_ID$ : Chip ID
$CPU_FREQ$ : CPU Frequency
$FREE_MEM$ : Free memory on heap
$SDK_VER$ : SDK version
$MDNS_VISIBLE$: set to hidden if no MDNS and visible if present
$MDNS_NAME$ : mDNS name if enabled or "Not enabled" if not enabled
$SSDP_VISIBLE$ : set to hidden if no MDNS and visible if present
$SSDP_STATUS$ : set to Enabled / Not enabled according compilation settings
$CAPTIVE_PORTAL_VISIBLE$: set to hidden if no Captive portal and visible if present
$CAPTIVE_PORTAL_STATUS$: set to Enabled / Not enabled according compilation settings
$NET_PHY$ : Network type (b/g/n)
$SLEEP_MODE$ : Sleep Mode
$BOOT_VER$ : Boot version
$BAUD_RATE$ : Baud rates for serial communication
$WEB_PORT$ : Port for web access
$DATA_PORT$ : Port for tcp ip connection
$DATA_PORT_VISIBILITY$ : set to hidden if no enabled and visible if enabled
$AP_STATUS_ENABLED$ : is Access Point enabled or disabled
$AP_VISIBILITY$ : if Access Point is enabled set visible, else set to hidden
$AP_MAC$ : Mac address of AP
$AP_SSID$ : SSID of AP
$AP_IS_VISIBLE$ : Is AP visible
$AP_CHANNEL$ : Channel
$AP_AUTH$ : Autehntification mode
$AP_MAX_CON$ : Maximum connections allowed
$AP_DHCP_STATUS$ : AP DHCP server status (started/stopped)
$AP_IP$ : AP IP address
$AP_GW$ : AP gateway address
$AP_SUBNET$ : AP mask of subnet
$CONNECTED_STATIONS[ Repetive template using $ROW_NUMBER$ $MAC_CONNECTED$ $IP_CONNECTED$ ]$
$CONNECTED_STATIONS_NB_ITEMS$ : number of item for connected stations list
or can get variables in direct access:
$ROW_NUMBER[0]$
$MAC_CONNECTED[0]$
$IP_CONNECTED[0]$
until
$ROW_NUMBER[$CONNECTED_STATIONS_NB_ITEMS$ -1]$
$MAC_CONNECTED[$CONNECTED_STATIONS_NB_ITEMS$ -1]$
$IP_CONNECTED[$CONNECTED_STATIONS_NB_ITEMS$ -1]$
$STA_STATUS_ENABLED$ : is Station enabled or disabled
$STA_VISIBILITY$ : if Station is enabled set visible, else set to hidden
$STA_MAC$ : Mac address of Station
$STA_SSID$ : SSID used by STA
$STA_CHANNEL$ : Channel
$STA_STATUS$ : Connection Status
$STA_SIGNAL$ : Signal strength
$STA_DHCP_STATUS$ : DHCP Client status
$STA_IP$ : STA IP
$STA_GW$ : STA Gateway
$STA_SUBNET$ : STA Mask
[SYSTEM CONFIGURATION]
$BAUD_RATE_OPTIONS_LIST$ : Baud rate list
$SLEEP_MODE_OPTIONS_LIST$ : Sleep mode list
$POLLING_OPTIONS_LIST$ : Refresh delay list
$WEB_PORT$ : Port for web access
$DATA_PORT$ : Port for tcp ip connection
$DATA_PORT_VISIBILITY$ : set to hidden if no enabled and visible if enabled
$ERROR_MSG$ : Error message if any
$SUCCESS_MSG$ : Success message announcing restart
$ERROR_MSG_VISIBILITY$ : Show/Hide Error message
$SUCCESS_MSG_VISIBILITY$ : Show/Hide Success message
$SUBMIT_BUTTON_VISIBILITY$ : Show if no submission or any error, hide if submission is successful
$BAUD_RATE_STATUS$ : change to has-error if issue, or has-success if ok
$SLEEP_MODE_STATUS$ : change to has-error if issue, or has-success if ok
$WEB_PORT_STATUS$ : change to has-error if issue, or has-success if ok
$DATA_PORT_STATUS$ : change to has-error if issue, or has-success if ok
[AP CONFIGURATION]
$AP_SSID$ : Access point SSID
$AP_PASSWORD$ Access point password
$IS_SSID_VISIBLE$ : set to checked if SSID is visible
$NETWORK_OPTION_LIST$ : Option list for network for AP
$CHANNEL_OPTION_LIST$ : Channel list
$AUTH_OPTION_LIST$ Authentification methods list
$IS_STATIC_IP$ : set to checked if use static address
$AP_IP$ : Access point IP if static address
$AP_GW$ : Access point Gaieway if static address
$AP_SUBNET$ : : Access point network mask if static address
$ERROR_MSG$ : Error message if any
$SUCCESS_MSG$ : Success message announcing restart
$ERROR_MSG_VISIBILITY$ : Show/Hide Error message
$SUCCESS_MSG_VISIBILITY$ : Show/Hide Success message
$SUBMIT_BUTTON_VISIBILITY$ : Show if no submission or any error, hide if submission is successful
$AP_SSID_STATUS$ : change to has-error if issue, or has-success if ok
$AP_PASSWORD_STATUS$ : change to has-error if issue, or has-success if ok
$IS_SSID_VISIBLE_STATUS$ : change to has-error if issue, or has-success if ok
$NETWORK_OPTION_LIST_STATUS$ : change to has-error if issue, or has-success if ok
$CHANNEL_OPTION_LIST_STATUS$ : change to has-error if issue, or has-success if ok
$AUTH_OPTION_LIST_STATUS$ : change to has-error if issue, or has-success if ok
$AP_STATIC_IP_STATUS$ : change to has-error if issue, or has-success if ok
$AP_IP_STATUS$ : change to has-error if issue, or has-success if ok
$AP_GW_STATUS$ : change to has-error if issue, or has-success if ok
$AP_SUBNET_STATUS$ : change to has-error if issue, or has-success if ok
[STATION CONFIGURATION]
$STA_SSID$ : Access point SSID
$STA_PASSWORD$ Access point password
$NETWORK_OPTION_LIST$ : Option list for network for AP
$IS_STATIC_IP$ : set to checked if use static address
$STA_IP$ : Access point IP if static address
$STA_GW$ : Access point Gaieway if static address
$STA_SUBNET$ : : Access point network mask if static address
$ERROR_MSG$ : Error message if any
$SUCCESS_MSG$ : Success message announcing restart
$ERROR_MSG_VISIBILITY$ : Show/Hide Error message
$SUCCESS_MSG_VISIBILITY$ : Show/Hide Success message
$SUBMIT_BUTTON_VISIBILITY$ : Show if no submission or any error, hide if submission is successful
$STA_SSID_STATUS$ : change to has-error if issue, or has-success if ok
$STA_PASSWORD_STATUS$ : change to has-error if issue, or has-success if ok
$NETWORK_OPTION_LIST_STATUS$ : change to has-error if issue, or has-success if ok
$STA_STATIC_IP_STATUS$ : change to has-error if issue, or has-success if ok
$STA_IP_STATUS$ : change to has-error if issue, or has-success if ok
$STA_GW_STATUS$ : change to has-error if issue, or has-success if ok
$STA_SUBNET_STATUS$ : change to has-error if issue, or has-success if ok
$AP_SCAN_VISIBILITY$ : Hide scan table which is not executed when saving to EEPROM
[PRINTER STATUS]
$REFRESH_PAGE$ : Delay for refreshing pages
$XY_FEEDRATE$ : XY axis feedrate
$Z_FEEDRATE$ : Z axis feedrate
$E_FEEDRATE$ : Extruder feedrate
[PRINTER SETTINGS]
$REFRESH_PAGE$ : Delay for refreshing pages
$XY_FEEDRATE$ : XY axis feedrate
$Z_FEEDRATE$ : Z axis feedrate
$E_FEEDRATE$ : Extruder feedrate
$REFRESH_PAGE_STATUS$ : change to has-error if issue, or has-success if ok
$XY_FEEDRATE_STATUS$ : change to has-error if issue, or has-success if ok
$Z_FEEDRATE_STATUS$ : change to has-error if issue, or has-success if ok
$E_FEEDRATE_STATUS$ : change to has-error if issue, or has-success if ok

1
embedded/build.bat Normal file
View File

@ -0,0 +1 @@
bin2c /infile tool.html.gz /outfile out.h /targetfolder .

125
embedded/gulpfile.js Normal file
View File

@ -0,0 +1,125 @@
var gulp = require('gulp'),
jshint = require('gulp-jshint'),
gulpif = require('gulp-if'),
concat = require('gulp-concat'),
uglify = require('gulp-uglify'),
cleanCSS = require('gulp-clean-css'),
removeCode = require('gulp-remove-code'),
merge = require('merge-stream'),
del = require('del'),
zip = require('gulp-zip'),
gzip = require('gulp-gzip'),
htmlmin = require('gulp-htmlmin'),
replace = require('gulp-replace'),
fs = require('fs'),
smoosher = require('gulp-smoosher');
var demoMode = false;
var testMode = false;
function clean() {
return del(['dist']);
}
function clean2() {
return del(['dist/js', 'dist/css']);
}
function lint() {
return gulp.src('www/js/**/script.js')
.pipe(jshint())
.pipe(jshint.reporter('default'));
}
function Copytest() {
return merge(
gulp.src(['www/tool.html'])
.pipe(removeCode({production: false}))
.pipe(gulp.dest('dist')),
gulp.src(['www/images/**/*.*'])
.pipe(gulp.dest('dist/images'))
)
}
function Copy() {
return merge(
gulp.src(['www/tool.html'])
.pipe(removeCode({production: true}))
.pipe(gulp.dest('dist')),
gulp.src(['www/images/**/*.*'])
.pipe(gulp.dest('dist/images'))
)
}
function concatApptest() {
return merge(
gulp.src([ 'www/js/**/*.js'])
.pipe(concat('script.js'))
.pipe(removeCode({production: false}))
.pipe(gulp.dest('./dist/js')),
gulp.src([ 'www/css/**/*.css'])
.pipe(concat('style.css'))
.pipe(gulp.dest('./dist/css/'))
)
}
function concatApp() {
return merge(
gulp.src([ 'www/js/**/*.js'])
.pipe(concat('script.js'))
.pipe(removeCode({production: true}))
.pipe(gulp.dest('./dist/js')),
gulp.src([ 'www/css/**/*.css'])
.pipe(concat('style.css'))
.pipe(gulp.dest('./dist/css/'))
)
}
function minifyApp() {
return merge(
gulp.src(['dist/js/script.js'])
.pipe(uglify({mangle: true}))
.pipe(gulp.dest('./dist/js/')),
gulp.src('dist/css/style.css')
.pipe(cleanCSS({debug: true}, function(details) {
console.log(details.name + ': ' + details.stats.originalSize);
console.log(details.name + ': ' + details.stats.minifiedSize);
}))
.pipe(gulp.dest('./dist/css/')),
gulp.src('dist/tool.html')
.pipe(htmlmin({collapseWhitespace: true, minifyCSS: true}))
.pipe(gulp.dest('dist'))
)
}
function smoosh() {
return gulp.src('dist/tool.html')
.pipe(smoosher())
.pipe(gulp.dest('dist'))
}
function compress() {
return gulp.src('dist/tool.html')
.pipe(gzip())
.pipe(gulp.dest('.'));
}
gulp.task(clean);
gulp.task(lint);
gulp.task(Copy);
gulp.task(Copytest);
gulp.task(concatApp);
gulp.task(concatApptest);
gulp.task(minifyApp);
gulp.task(smoosh);
gulp.task(clean2);
var defaultSeries = gulp.series(clean, lint, Copy, concatApp, smoosh);
var packageSeries = gulp.series(clean, lint, Copy, concatApp,minifyApp, smoosh, compress, clean2);
gulp.task('default', defaultSeries);
gulp.task('package', packageSeries);

15
embedded/install.bat Normal file
View File

@ -0,0 +1,15 @@
npm install --global gulp-cli
npm install --save-dev gulp
npm install --save-dev gulp-jshint
npm install --save-dev jshint
npm install --save-dev gulp-if
npm install --save-dev gulp-concat
npm install --save-dev gulp-uglify
npm install --save-dev gulp-clean-css
npm install --save-dev gulp-remove-code
npm install --save-dev del
npm install --save-dev gulp-zip
npm install --save-dev gulp-gzip
npm install --save-dev gulp-htmlmin
npm install --save-dev gulp-replace
npm install --save-dev gulp-smoosher

30
embedded/package.json Normal file
View File

@ -0,0 +1,30 @@
{
"name": "embedded4ESP3D",
"description": "Embedded files for ESP3D",
"devDependencies": {
"del": "^2.2.2",
"deprecated": "0.0.1",
"fs": "0.0.1-security",
"gulp": "github:gulpjs/gulp#4.0",
"gulp-bytediff": "^1.0.0",
"gulp-cdnizer": "^1.1.7",
"gulp-clean-css": "^2.4.0",
"gulp-concat": "^2.6.1",
"gulp-gzip": "^1.4.0",
"gulp-htmlmin": "^3.0.0",
"gulp-if": "^2.0.2",
"gulp-jshint": "^2.0.4",
"gulp-ng-annotate": "^2.0.0",
"gulp-remove-code": "^1.0.2",
"gulp-replace": "^0.5.4",
"gulp-smoosher": "0.0.9",
"gulp-uglify": "^2.1.2",
"gulp-util": "^3.0.1",
"gulp-zip": "^3.2.0",
"jshint": "^2.9.5",
"merge-stream": "^1.0.1"
},
"repository": "https://github.com/luc-github/ESP3D",
"author": "Luc LEBOSSE",
"license": "(ISC OR GPL-3.0)"
}

BIN
embedded/tool.html.gz Normal file

Binary file not shown.

View File

@ -50,3 +50,90 @@ caption{padding-top:8px;padding-bottom:8px;color:#777777;text-align:left;}
.btnroundimg:hover{background-color:#F0F0F0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483;}
.blacklink {color:#000000;}
.blacklink:hover, .filelink:focus {color:#0094FF;}
input[type="file"]::-webkit-file-upload-button{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;-ms-touch-action:manipulation; touch-action:manipulation;cursor:pointer;
background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;
* -webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none; color: #ffffff;background-color: #5bc0de;border-color: #46b8da;}
input[type="file"]::-webkit-file-upload-button:focus{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;-ms-touch-action:manipulation; touch-action:manipulation;cursor:pointer;
background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;
* -webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none; color: #ffffff;background-color: #31b0d5;border-color: #1b6d85;}
input[type="file"]::-webkit-file-upload-button:hover{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;-ms-touch-action:manipulation; touch-action:manipulation;cursor:pointer;
background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;
* -webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none; color: #ffffff;background-color: #31b0d5;border-color: #269abc;}
.filelink {color:#000000;}
.filelink:hover, .filelink:focus {color:#0094FF;}
.panel-footer{padding:10px 15px;color:#31708f;background-color:#f5f5f5;border-color:#dddddd;border-top:1px solid #dddddd;}
.loader {
border: 4px solid #f3f3f3; /* Light grey */
border-top: 4px solid #3498db; /* Blue */
border-radius: 50%;
width: 12px;
height: 12px;
animation: spin 2s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.panel-footer{padding:10px 15px;color:#31708f;background-color:#f5f5f5;border-color:#dddddd;border-top:1px solid #dddddd;}
.modal {
display: none; /* Hidden by default */
position: fixed; /* Stay in place */
z-index: 10000; /* Sit on top */
padding-top: 100px; /* Location of the box */
left: 0;
top: 0;
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: auto; /* Enable scroll if needed */
background-color: rgb(0,0,0); /* Fallback color */
background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
}
/* Modal Content */
.modal-content {
border-top-left-radius: 10px;
border-top-right-radius: 10px;
border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px;
border: 2px solid #337AB7;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
position: relative;
margin: auto;
padding: 0;
background-color: #fefefe;
}
.modal-header {
padding: 2px 16px;
color: #0f0f0f;
background-color: #f2f2f2;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
border-bottom: 1px solid #cfcfcf;
}
.modal-body {padding: 10px 16px;}
.modal-footer {
padding: 16px 16px;
height: 4.5em;
color: #0f0f0f;
background-color: #f2f2f2;
border-top: 1px solid #cfcfcf;
border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px;
}
@media (min-width: 768px) {
.modal-content {
width: 580px;
}
}
@media screen and (max-width: 767px) {
.modal-content {
width: 100%;
}
}

View File

@ -1,52 +1,3 @@
$INCLUDE[header.inc]$
$INCLUDE[css2.inc]$
<style>
.panel-footer{padding:10px 15px;color:#31708f;background-color:#f5f5f5;border-color:#dddddd;border-top:1px solid #dddddd;}
</style>
<div class="panel">
<div class="panel-heading">Extra Settings</div>
<div class="panel-body">
<form method="POST">
<div class="form-group $REFRESH_PAGE_STATUS$"><label class="control-label" for="CONFIG1">Refresh page time: </label><br>
<input type="number" class="form-control" id="CONFIG1" name="REFRESH_PAGE" placeholder="Time in minutes 0~120, 0 = disabled " value="$REFRESH_PAGE$" min="0"max="120" step="1"style="width: auto;"></div>
<div class="form-group $XY_FEEDRATE_STATUS$"><label class="control-label" for="CONFIG2">XY axis feedrate: </label><br>
<input type="number" class="form-control" id="CONFIG2" name="XY_FEEDRATE" placeholder="1~9999 " value="$XY_FEEDRATE$" min="1"max="9999" step="1"style="width: auto;"></div>
<div class="form-group $Z_FEEDRATE_STATUS$"><label class="control-label" for="CONFIG3">Z axis feedrate: </label><br>
<input type="number" class="form-control" id="CONFIG3" name="Z_FEEDRATE" placeholder="1~9999 " value="$Z_FEEDRATE$" min="1"max="9999" step="1"style="width: auto;"></div>
<div class="form-group $E_FEEDRATE_STATUS$"><label class="control-label" for="CONFIG4">Extruder feedrate: </label><br>
<input type="number" class="form-control" id="CONFIG4" name="E_FEEDRATE" placeholder="1~9999 " value="$E_FEEDRATE$" min="1"max="9999" step="1"style="width: auto;"></div>
<div class="alert alert-danger" role="alert" style="$ERROR_MSG_VISIBILITY$" >
$ERROR_MSG$
</div>
<hr><input style="$SUBMIT_BUTTON_VISIBILITY$" type="submit" class="btn btn-primary" name="SUBMIT" value="Apply">
</form>
<div class="alert alert-success" role="alert" style="$SUCCESS_MSG_VISIBILITY$" >
$SUCCESS_MSG$
</div>
</div>
</div>
<div class="panel">
<div class="panel-heading">Flash Filesystem</div>
<div class="panel-body">
<input type="file" id="file-select" name="myfiles[]" multiple />
<input class="btn btn-primary" type="button" id="upload-button" onclick="Sendfile();" value="Upload"/>&nbsp;&nbsp;<progress style="visibility:hidden;" name='prg' id='prg' max='100'></progress>
<br><br><div class="panel">
<div class="panel-body">
<table><tr><td width="0%">
<div onclick="Createdir()" class="btnimg"><svg width="40" height="40" viewBox="0 0 40 40"><rect x="5" y="10" width="30" height="20" rx="2" ry="2" fill="#31b0d5" />
<rect x="20" y="5" width="15" height="15" rx="2" ry="2" fill="#31b0d5" /><text x="15" y="25" font-size="18" font-weight="800" fill="white">+</text></svg>
</div>
</td><td><div id="loader" class="loader"></div></td><td width="100%"><div id="path" class="info" >&nbsp</div>
</td>
</tr></table>
<table class="table table-striped" style="border:1px;solid #dddddd;margin-bottom:20px;" ><thead><tr><th width='0%'>Type</th><th>Name</th><th>Size</th><th width='0%'></th><th width='100%'></th></tr></thead><tbody id="file_list"><tbody></table>
</div>
<div class="panel-footer " id="status"></div>
</div>
</div>
</div>
<script>
var currentpath = "/";
function navbar(){
var content="<table><tr>";
@ -101,39 +52,39 @@ document.getElementById('status').innerHTML=content;
content ="";
if (currentpath!="/")
{
var pos = currentpath.lastIndexOf("/",currentpath.length-2)
var pos = currentpath.lastIndexOf("/",currentpath.length-2);
var previouspath = currentpath.slice(0,pos+1);
content +="<tr style='cursor:hand;' onclick=\"currentpath='"+previouspath+"'; SendCommand('list','all');\"><td >"+back_icon()+"</td><td colspan='4'> Up..</td></tr>";
}
jsonresponse.files.sort(function(a, b) {
return compareStrings(a.name, b.name);
});
for (var i=0;i <jsonresponse.files.length;i++){
for (var i1=0;i1 <jsonresponse.files.length;i1++){
//first display files
if (String(jsonresponse.files[i].size) != "-1")
if (String(jsonresponse.files[i1].size) != "-1")
{
content +="<TR>";
content +="<td><svg height='24' width='24' viewBox='0 0 24 24' > <path d='M1,2 L1,21 L2,22 L16,22 L17,21 L17,6 L12,6 L12,1 L2,1 z' stroke='black' fill='white' /><line x1='12' y1='1' x2='17' y2='6' stroke='black' stroke-width='1'/>";
content +="<line x1='5' y1='10' x2='13' y2='10' stroke='black' stroke-width='1'/> <line x1='5' y1='14' x2='13' y2='14' stroke='black' stroke-width='1'/> <line x1='5' y1='18' x2='13' y2='18' stroke='black' stroke-width='1'/></svg></td>";
content +="<TD class='btnimg' style=\"padding:0px;\"><a href=\""+jsonresponse.path+jsonresponse.files[i].name+"\" target=_blank><div class=\"blacklink\">";
content +=jsonresponse.files[i].name;
content +="<TD class='btnimg' style=\"padding:0px;\"><a href=\""+jsonresponse.path+jsonresponse.files[i1].name+"\" target=_blank><div class=\"blacklink\">";
content +=jsonresponse.files[i1].name;
content +="</div></a></TD><TD>";
content +=jsonresponse.files[i].size;
content +="</TD><TD width='0%'><div class=\"btnimg\" onclick=\"Delete('"+jsonresponse.files[i].name+"')\">";
content +=jsonresponse.files[i1].size;
content +="</TD><TD width='0%'><div class=\"btnimg\" onclick=\"Delete('"+jsonresponse.files[i1].name+"')\">";
content +=trash_icon();
content +="</div></TD><td></td></TR>";
}
}
//then display directories
for (var i=0;i <jsonresponse.files.length;i++){
if (String(jsonresponse.files[i].size) == "-1")
for (var i2=0;i2 <jsonresponse.files.length;i2++){
if (String(jsonresponse.files[i2].size) == "-1")
{
content +="<TR>";
content+="<td><svg height='24' width='24' viewBox='0 0 24 24' ><path d='M19,11 L19,8 L18,7 L8,7 L8,5 L7,4 L2,4 L1,5 L1,22 L19,22 L20,21 L23,11 L5,11 L2,21 L1,22' stroke='black' fill='white' /></svg></td>";
content +="<TD class='btnimg blacklink' style='padding:10px 15px;' onclick=\"select_dir('" + jsonresponse.files[i].name+"');\">";
content +=jsonresponse.files[i].name;
content +="<TD class='btnimg blacklink' style='padding:10px 15px;' onclick=\"select_dir('" + jsonresponse.files[i2].name+"');\">";
content +=jsonresponse.files[i2].name;
content +="</TD><TD>";
content +="</TD><TD width='0%'><div class=\"btnimg\" onclick=\"Deletedir('"+jsonresponse.files[i].name+"')\">";
content +="</TD><TD width='0%'><div class=\"btnimg\" onclick=\"Deletedir('"+jsonresponse.files[i2].name+"')\">";
content +=trash_icon();
content +="</div></TD><td></td></TR>";
}
@ -155,16 +106,19 @@ if (filename != null) {
}
function SendCommand(action,filename){
var xmlhttp = new XMLHttpRequest();
var url = "/FILES?action="+action;
var url = "/files?action="+action;
url += "&filename="+encodeURI(filename);
url += "&path="+encodeURI(currentpath);
document.getElementById('loader').style.visibility="visible";
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
if (xmlhttp.readyState == 4 ) {
if(xmlhttp.status == 200) {
var jsonresponse = JSON.parse(xmlhttp.responseText);
document.getElementById('loader').style.visibility="hidden";
dispatchfilestatus(jsonresponse);}
else SubmitRequest ()
}
};
xmlhttp.open("GET", url, true);
xmlhttp.send();
}
@ -175,11 +129,11 @@ document.getElementById('upload-button').value = "Uploading...";
document.getElementById('prg').style.visibility = "visible";
var formData = new FormData();
formData.append('path', currentpath);
for (var i = 0; i < files.length; i++) {
var file = files[i];
for (var i3 = 0; i3 < files.length; i3++) {
var file = files[i3];
formData.append('myfiles[]', file, currentpath+file.name);}
var xmlhttp = new XMLHttpRequest();
xmlhttp.open('POST', '/FILES', true);
xmlhttp.open('POST', '/files', true);
//progress upload event
xmlhttp.upload.addEventListener("progress", updateProgress, false);
//progress function
@ -201,15 +155,95 @@ document.getElementById('file-select').value="";
var jsonresponse = JSON.parse(xmlhttp.responseText);
dispatchfilestatus(jsonresponse);
} else alert('An error occurred!');
}
};
xmlhttp.send(formData);
}
window.onload = function() {
SendCommand('list','all');
};
function Uploadfile(){
if (!confirm("Confirm Firmware Update ?"))return;
var files = document.getElementById('fw-select').files;
if (files.length==0)return;
document.getElementById('uploadfw-button').style.visibility = 'hidden';
document.getElementById('fw-select').style.visibility = 'hidden';
document.getElementById('msg').style.visibility = "visible";
document.getElementById('msg').innerHTML="";
document.getElementById('SPIFFS').style.display = "none";
document.getElementById('prgfw').style.visibility = "visible";
var formData = new FormData();
for (var i4 = 0; i4 < files.length; i4++) {
var file = files[i4];
formData.append('myfile[]', file, "/"+file.name);}
var xmlhttp = new XMLHttpRequest();
xmlhttp.open('POST', '/updatefw', true);
//progress upload event
xmlhttp.upload.addEventListener("progress", updateProgress, false);
//progress function
function updateProgress (oEvent) {
if (oEvent.lengthComputable) {
var percentComplete = (oEvent.loaded / oEvent.total)*100;
document.getElementById('prgfw').value=percentComplete;
document.getElementById('msg').innerHTML = "Uploading ..." + percentComplete.toFixed(0)+"%" ;
} else {
// Impossible because size is unknown
}
}
xmlhttp.onload = function () {
if (xmlhttp.status === 200) {
document.getElementById('uploadfw-button').value = 'Upload';
document.getElementById('msg').innerHTML="Restarting, please wait....";
document.getElementById('counter').style.visibility = "visible";
document.getElementById('uploadfw-button').style.visibility = 'hidden';
document.getElementById('uploadfw-button').style.width = '0px';
document.getElementById('fw-select').value="";
document.getElementById('fw-select').style.visibility = 'hidden';
document.getElementById('fw-select').style.width = '0px';
var jsonresponse = JSON.parse(xmlhttp.responseText);
if (jsonresponse.status=='1' || jsonresponse.status=='4' || jsonresponse.status=='1')alert("Update failed");
if (jsonresponse.status=='2')alert('Update canceled!');
else if (jsonresponse.status=='3')
{
var i5 = 0;
var interval;
var x = document.getElementById("prgfw");
x.max=40;
interval = setInterval(function(){
i5=i5+1;
var x = document.getElementById("prgfw");
x.value=i5;
document.getElementById('counter').innerHTML=41-i5;
if (i5>40)
{
clearInterval(interval);
location.reload();
}
},1000);
}
else alert('Update failed!');
} else alert('An error occurred!');
};
xmlhttp.send(formData);
}
</script>
$INCLUDE[footer.inc]$
function RequestLogin(){
document.getElementById('loader').style.visibility="hidden";
document.getElementById('loginpage').style.display='block';
}
function SubmitRequest (){
document.getElementById('loginpage').style.display='none';
var user = document.getElementById('login_user_text').value.trim();
var password = document.getElementById('login_password_text').value.trim();
var url = "/login?USER="+encodeURIComponent(user) + "&PASSWORD=" + encodeURIComponent(password) + "&SUBMIT=yes" ;
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status != 200) {
RequestLogin();
}
};
xmlhttp.open("GET", url, true);
xmlhttp.send();
}

100
embedded/www/tool.html Normal file
View File

@ -0,0 +1,100 @@
<html>
<head>
<!-- smoosh -->
<link href="css/style.css" rel="stylesheet">
<!-- endsmoosh -->
</head>
<body>
<div style="position:absolute;top:0;right:0;">
V1.1
&nbsp;&nbsp;
</div>
<center>
<h2>It seems you do not have any index.html neither index.html.gz, please upload it or update your firmware if necessary.</h2>
</center>
<div class="panel" id="SPIFFS">
<div class="panel-heading">Flash Filesystem</div>
<div class="panel-body">
<input type="file" id="file-select" name="myfiles[]" multiple />
<input class="btn btn-primary" type="button" id="upload-button" onclick="Sendfile();" value="Upload"/>&nbsp;&nbsp;<progress style="visibility:hidden;" name='prg' id='prg' max='100'></progress>
<br><br>
<div class="panel">
<div class="panel-body">
<table>
<tr>
<td width="0%">
<input class="btn btn-primary" type="button" onclick="SendCommand('list','all');" value="Refresh"/>
</td>
<td width="0%">
<div onclick="Createdir()" class="btnimg"><svg width="40" height="40" viewBox="0 0 40 40"><rect x="5" y="10" width="30" height="20" rx="2" ry="2" fill="#31b0d5" />
<rect x="20" y="5" width="15" height="15" rx="2" ry="2" fill="#31b0d5" /><text x="15" y="25" font-size="18" font-weight="800" fill="white">+</text></svg>
</div>
</td>
<td>
<div id="loader" class="loader"></div>
</td>
<td width="100%">
<div id="path" class="info" >&nbsp;</div>
</td>
</tr>
</table>
<table class="table table-striped" style="border:1px;solid #dddddd;margin-bottom:20px;" >
<thead>
<tr>
<th width='0%'>Type</th>
<th>Name</th>
<th>Size</th>
<th width='0%'></th>
<th width='100%'></th>
</tr>
</thead>
<tbody id="file_list"></tbody>
</table>
</div>
<div class="panel-footer " id="status"></div>
</div>
</div>
</div>
<div class="panel">
<div class="panel-heading">Firmware Update</div>
<div class="panel-body">
<table>
<tr>
<td><input type="file" id="fw-select" name="myfiles[]" /></td>
<td><input class="btn btn-primary" type="button" id="uploadfw-button" onclick="Uploadfile();" value="Update"/></td>
<td><progress style="visibility:hidden;" name='prgfw' id='prgfw' max='100'></progress></td>
<td><span id='msg' style='visibility:hidden;'>Restarting, please wait....</span><span id='counter'></span></td>
</tr>
</table>
</div>
</div>
<div id="loginpage"class="modal">
<div class="modal-content" >
<div class="modal-header">
<h3>Identification</h3>
</div>
<div class="modal-body">
<span>
<span class="modal-text" translate>User:</span>
<input class='form-control' type="text" id="login_user_text" style="width:auto"/>
</span>
<hr>
<span>
<span class="modal-text" translate>Password:</span>
<input class='form-control' type="password" id="login_password_text" style="width:auto"/>
</span>
<br>
</div>
<div class="modal-footer">
<input type="button" class="btn btn-primary" onclick="SubmitRequest()" value="Submit"/>
</div>
</div>
</div>
<!-- smoosh -->
<script src="js/script.js"></script>
<!-- endsmoosh -->
</body>
</html>

View File

@ -18,6 +18,7 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "config.h"
#include "bridge.h"
#include "command.h"
#include "webinterface.h"
@ -29,22 +30,21 @@ WiFiClient serverClients[MAX_SRV_CLIENTS];
bool BRIDGE::header_sent = false;
String BRIDGE::buffer_web = "";
void BRIDGE::print (const __FlashStringHelper *data, tpipe output){
void BRIDGE::print (const __FlashStringHelper *data, tpipe output)
{
String tmp = data;
BRIDGE::print(tmp.c_str(), output);
}
void BRIDGE::print (String & data, tpipe output){
void BRIDGE::print (String & data, tpipe output)
{
BRIDGE::print(data.c_str(), output);
}
void BRIDGE::print (const char * data, tpipe output){
void BRIDGE::print (const char * data, tpipe output)
{
switch(output) {
case SERIAL_PIPE:
header_sent = false;
Serial.print(data);
break;
case SERIAL1_PIPE:
header_sent = false;
Serial1.print(data);
ESP_SERIAL_OUT.print(data);
break;
#ifdef TCP_IP_DATA_FEATURE
case TCP_PIPE:
@ -54,17 +54,16 @@ void BRIDGE::print (const char * data, tpipe output){
#endif
case WEB_PIPE:
if (!header_sent) {
web_interface->WebServer.setContentLength(CONTENT_LENGTH_UNKNOWN);
web_interface->WebServer.sendHeader("Content-Type","text/html");
web_interface->WebServer.sendHeader("Cache-Control","no-cache");
web_interface->WebServer.send(200);
web_interface->web_server.setContentLength(CONTENT_LENGTH_UNKNOWN);
web_interface->web_server.sendHeader("Content-Type","text/html");
web_interface->web_server.sendHeader("Cache-Control","no-cache");
web_interface->web_server.send(200);
header_sent = true;
}
buffer_web+=data;
if (buffer_web.length() > 1200)
{
if (buffer_web.length() > 1200) {
//send data
web_interface->WebServer.sendContent(buffer_web);
web_interface->web_server.sendContent(buffer_web);
//reset buffer
buffer_web="";
}
@ -73,34 +72,35 @@ void BRIDGE::print (const char * data, tpipe output){
break;
}
}
void BRIDGE::println (const __FlashStringHelper *data, tpipe output){
void BRIDGE::println (const __FlashStringHelper *data, tpipe output)
{
BRIDGE::print(data,output);
#ifdef TCP_IP_DATA_FEATURE
BRIDGE::print("\r",output);
#endif
BRIDGE::print("\n",output);
}
void BRIDGE::println (String & data, tpipe output){
void BRIDGE::println (String & data, tpipe output)
{
BRIDGE::print(data,output);
#ifdef TCP_IP_DATA_FEATURE
BRIDGE::print("\r",output);
#endif
BRIDGE::print("\n",output);
}
void BRIDGE::println (const char * data, tpipe output){
void BRIDGE::println (const char * data, tpipe output)
{
BRIDGE::print(data,output);
#ifdef TCP_IP_DATA_FEATURE
BRIDGE::print("\r",output);
#endif
BRIDGE::print("\n",output);
}
void BRIDGE::flush (tpipe output){
void BRIDGE::flush (tpipe output)
{
switch(output) {
case SERIAL_PIPE:
Serial.flush();
break;
case SERIAL1_PIPE:
Serial1.flush();
ESP_SERIAL_OUT.flush();
break;
#ifdef TCP_IP_DATA_FEATURE
case TCP_PIPE:
@ -109,9 +109,9 @@ void BRIDGE::flush (tpipe output){
case WEB_PIPE:
if(header_sent) {
//send data
web_interface->WebServer.sendContent(buffer_web);
web_interface->web_server.sendContent(buffer_web);
//close line
web_interface->WebServer.sendContent("");
web_interface->web_server.sendContent("");
}
break;
default:
@ -123,14 +123,17 @@ void BRIDGE::flush (tpipe output){
#ifdef TCP_IP_DATA_FEATURE
void BRIDGE::send2TCP(const __FlashStringHelper *data){
void BRIDGE::send2TCP(const __FlashStringHelper *data)
{
String tmp = data;
BRIDGE::send2TCP(tmp.c_str());
}
void BRIDGE::send2TCP(String data){
void BRIDGE::send2TCP(String data)
{
BRIDGE::send2TCP(data.c_str());
}
void BRIDGE::send2TCP(const char * data){
void BRIDGE::send2TCP(const char * data)
{
for(uint8_t i = 0; i < MAX_SRV_CLIENTS; i++) {
if (serverClients[i] && serverClients[i].connected()) {
serverClients[i].write(data, strlen(data));
@ -139,15 +142,17 @@ void BRIDGE::send2TCP(const char * data){
}
}
#endif
bool BRIDGE::processFromSerial2TCP()
{
uint8_t i;
//check UART for data
if(Serial.available()) {
size_t len = Serial.available();
if(ESP_SERIAL_OUT.available()) {
size_t len = ESP_SERIAL_OUT.available();
uint8_t sbuf[len];
Serial.readBytes(sbuf, len);
ESP_SERIAL_OUT.readBytes(sbuf, len);
#ifdef TCP_IP_DATA_FEATURE
if (WiFi.getMode()!=WIFI_OFF ) {
//push UART data to all connected tcp clients
for(i = 0; i < MAX_SRV_CLIENTS; i++) {
if (serverClients[i] && serverClients[i].connected()) {
@ -155,6 +160,7 @@ bool BRIDGE::processFromSerial2TCP()
delay(0);
}
}
}
#endif
//process data if any
COMMAND::read_buffer_serial(sbuf, len);
@ -192,7 +198,7 @@ void BRIDGE::processFromTCP2Serial()
//get data from the tcp client and push it to the UART
while(serverClients[i].available()) {
data = serverClients[i].read();
Serial.write(data);
ESP_SERIAL_OUT.write(data);
COMMAND::read_buffer_tcp(data);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -34,10 +34,10 @@ public:
static void read_buffer_tcp(uint8_t b);
#endif
static bool check_command(String buffer, tpipe output, bool handlelockserial = true);
static void execute_command(int cmd,String cmd_params, tpipe output);
static bool execute_command(int cmd,String cmd_params, tpipe output, level_authenticate_type auth_level = LEVEL_GUEST);
static String get_param(String & cmd_params, const char * id, bool withspace = false);
static bool isadmin(String & cmd_params);
static bool isuser(String & cmd_params);
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -19,15 +19,47 @@
*/
//definition
#define REPETIER 0
#define UNKNOWN_FW 0
#define REPETIER4DV 1
#define MARLIN 2
#define MARLINKIMBRA 3
#define SMOOTHIEWARE 4
#define REPETIER 5
//FIRMWARE_TARGET: the targeted FW, can be REPETIER (Original Repetier)/ REPETIER4DV (Repetier for Davinci) / MARLIN (Marlin)/ SMOOTHIEWARE (Smoothieware)
#define FIRMWARE_TARGET MARLIN
#ifdef ARDUINO_ARCH_ESP32
#include "FS.h"
#include "SPIFFS.h"
#define WIFI_NONE_SLEEP WIFI_PS_NONE
#define WIFI_MODEM_SLEEP WIFI_PS_MODEM
#define WIFI_PHY_MODE_11B WIFI_PROTOCOL_11B
#define WIFI_PHY_MODE_11G WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G
#define WIFI_PHY_MODE_11N WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N
#define AUTH_OPEN WIFI_AUTH_OPEN
#define AUTH_WEP WIFI_AUTH_WEP
#define AUTH_WPA_PSK WIFI_AUTH_WPA_PSK
#define AUTH_WPA2_PSK WIFI_AUTH_WPA2_PSK
#define AUTH_WPA_WPA2_PSK WIFI_AUTH_WPA_WPA2_PSK
#define ENC_TYPE_NONE AUTH_OPEN
#define FS_FILE File
#define FS_DIR File
#define ESP_SERIAL_OUT Serial
#define SD_FILE_READ FILE_READ
#define SPIFFS_FILE_READ FILE_READ
#define SD_FILE_WRITE FILE_WRITE
#define SPIFFS_FILE_WRITE FILE_WRITE
extern HardwareSerial Serial2;
#else
#define FS_DIR fs::Dir
#define FS_FILE fs::File
#define ESP_SERIAL_OUT Serial
#define SD_FILE_READ FILE_READ
#define SPIFFS_FILE_READ "r"
#define SD_FILE_WRITE FILE_WRITE
#define SPIFFS_FILE_WRITE "w"
#endif
#define MAX_FW_ID REPETIER
//number of clients allowed to use data port at once
#define MAX_SRV_CLIENTS 1
@ -41,7 +73,16 @@
#define SSDP_FEATURE
//NETBIOS_FEATURE: this feature is a discovery protocol, supported on Windows out of the box
//#define NETBIOS_FEATURE
#define NETBIOS_FEATURE
#ifdef ARDUINO_ARCH_ESP32
#ifdef SSDP_FEATURE
#undef SSDP_FEATURE
#endif
#ifdef NETBIOS_FEATURE
#undef NETBIOS_FEATURE
#endif
#endif
//CAPTIVE_PORTAL_FEATURE: In SoftAP redirect all unknow call to main page
#define CAPTIVE_PORTAL_FEATURE
@ -78,43 +119,48 @@
//STATUS_MSG_FEATURE: catch the status msg and filter it to specific table
#define STATUS_MSG_FEATURE
//TEMP_MONITORING_FEATURE : catch the specific answer and store it to variable
#define TEMP_MONITORING_FEATURE
//SPEED_MONITORING_FEATURE : catch the specific answer and store it to variable
#define SPEED_MONITORING_FEATURE
//POS_MONITORING_FEATURE : catch the specific answer and store it to variable
#define POS_MONITORING_FEATURE
//FLOW_MONITORING_FEATURE : catch the specific answer and store it to variable
#define FLOW_MONITORING_FEATURE
//Serial rx buffer size is 256 but can be extended
#define SERIAL_RX_BUFFER_SIZE 512
//DEBUG Flag do not do this when connected to printer unless you know what you are doing!!!
#ifdef ARDUINO_ARCH_ESP32
#ifdef SSDP_FEATURE
#undef SSDP_FEATURE
#endif
#ifdef NETBIOS_FEATURE
#undef NETBIOS_FEATURE
#endif
#endif
//DEBUG Flag do not do this when connected to printer !!!
//be noted all upload may failed if enabled
//#define DEBUG_ESP3D
//#define DEBUG_OUTPUT_SPIFFS
//#define DEBUG_OUTPUT_SD
//#define DEBUG_OUTPUT_SERIAL
//#define DEBUG_OUTPUT_TCP
//store performance result in storestring variable : info_msg / status_msg
//#define DEBUG_PERFORMANCE
#define DEBUG_PERF_VARIABLE (web_interface->info_msg)
#ifdef DEBUG_ESP3D
#ifdef DEBUG_OUTPUT_SPIFFS
/*#ifdef SDCARD_FEATURE
/*
#ifndef FS_NO_GLOBALS
#define FS_NO_GLOBALS
#endif
#include <FS.h>
#define DEBUG_ESP3D(string) { FS_FILE logfile = SPIFFS.open("/log.txt", "a+");logfile.print(string);logfile.close();}
*/
#ifdef DEBUG_ESP3D
#ifdef DEBUG_OUTPUT_SPIFFS
#ifndef FS_NO_GLOBALS
#define FS_NO_GLOBALS
#endif
#endif*/
#include <FS.h>
#define DEBUG_PIPE NO_PIPE
#define LOG(string) {FSFILE logfile = SPIFFS.open("/log.txt", "a+");logfile.print(string);logfile.close();}
#define LOG(string) { FS_FILE logfile = SPIFFS.open("/log.txt", "a+");logfile.print(string);logfile.close();}
#endif
#ifdef DEBUG_OUTPUT_SERIAL
#define LOG(string) {Serial.print(string);}
#define LOG(string) {ESP_SERIAL_OUT.print(string);}
#define DEBUG_PIPE SERIAL_PIPE
#endif
#ifdef DEBUG_OUTPUT_TCP
@ -127,26 +173,20 @@
#define DEBUG_PIPE NO_PIPE
#endif
#ifdef SDCARD_FEATURE
#define FSFILE fs::File
#define FSDIR fs::Dir
#define FSINFO fs::FSInfo
#else
#define FSFILE File
#define FSDIR fs::Dir
#define FSINFO FSInfo
#endif
#ifndef CONFIG_h
#define CONFIG_h
#include <Arduino.h>
#ifdef ARDUINO_ARCH_ESP8266
extern "C" {
#include "user_interface.h"
}
#include "wifi.h"
#else
//Nothing here
#endif
#include "wificonf.h"
//version and sources location
#define FW_VERSION "0.9.78"
#define FW_VERSION "0.9.99"
#define REPOSITORY "https://github.com/luc-github/ESP3D"
typedef enum {
@ -159,6 +199,18 @@ typedef enum {
WEB_PIPE = 5
} tpipe;
typedef enum {
LEVEL_GUEST = 0,
LEVEL_USER = 1,
LEVEL_ADMIN = 2
} level_authenticate_type;
#define NO_SD 0
#define SD_DIRECTORY 1
#define EXT_DIRECTORY 2
//flags
#define AP_MODE 1
#define CLIENT_MODE 2
@ -196,14 +248,29 @@ typedef enum {
#define EP_AP_GATEWAY_VALUE 324 //4 bytes xxx.xxx.xxx.xxx
#define EP_AP_IP_MODE 329 //1 byte = flag
#define EP_AP_PHY_MODE 330 //1 byte = flag
//next available is 330
//space left 512 - 330 = 18
#define EP_DATA_STRING 331 //129 bytes 128+1 = string ; warning does not support multibyte char like chinese
#define EP_REFRESH_PAGE_TIME2 460 //1 bytes = flag
#define EP_TARGET_FW 461 //1 bytes = flag
#define EP_TIMEZONE 462//1 bytes = flag
#define EP_TIME_ISDST 463//1 bytes = flag
#define EP_TIME_SERVER1 464//129 bytes 128+1 = string ; warning does not support multibyte char like chinese
#define EP_TIME_SERVER2 593 //129 bytes 128+1 = string ; warning does not support multibyte char like chinese
#define EP_TIME_SERVER3 722 //129 bytes 128+1 = string ; warning does not support multibyte char like chinese
#define EP_IS_DIRECT_SD 850//1 bytes = flag
#define EP_PRIMARY_SD 851//1 bytes = flag
#define EP_SECONDARY_SD 852//1 bytes = flag
#define EP_DIRECT_SD_CHECK 853//1 bytes = flag
#define EP_SD_CHECK_UPDATE_AT_BOOT 854//1 bytes = flag
#define LAST_EEPROM_ADDRESS 855
//next available is 855
//space left 1024 - 855 = 169
//default values
#define DEFAULT_WIFI_MODE AP_MODE
const char DEFAULT_AP_SSID [] PROGMEM = "ESP8266";
const char DEFAULT_AP_SSID [] PROGMEM = "ESP3D";
const char DEFAULT_AP_PASSWORD [] PROGMEM = "12345678";
const char DEFAULT_STA_SSID [] PROGMEM = "ESP8266";
const char DEFAULT_STA_SSID [] PROGMEM = "ESP3D";
const char DEFAULT_STA_PASSWORD [] PROGMEM = "12345678";
const byte DEFAULT_STA_IP_MODE = DHCP_MODE;
const byte DEFAULT_AP_IP_MODE = STATIC_IP_MODE;
@ -229,24 +296,105 @@ const char DEFAULT_ADMIN_PWD [] PROGMEM = "admin";
const char DEFAULT_USER_PWD [] PROGMEM = "user";
const char DEFAULT_ADMIN_LOGIN [] PROGMEM = "admin";
const char DEFAULT_USER_LOGIN [] PROGMEM = "user";
const char DEFAULT_TIME_SERVER1 [] PROGMEM = "time.nist.gov";
const char DEFAULT_TIME_SERVER2 [] PROGMEM = "0.pool.ntp.org";
const char DEFAULT_TIME_SERVER3 [] PROGMEM = "1.pool.ntp.org";
#define DEFAULT_TIME_ZONE 1
#define DEFAULT_TIME_DST 0
#define DEFAULT_PRIMARY_SD 1
#define DEFAULT_SECONDARY_SD 2
#define DEFAULT_DIRECT_SD_CHECK 0
#define DEFAULT_SD_CHECK_UPDATE_AT_BOOT 1
#define DEFAULT_IS_DIRECT_SD 0
const uint16_t Setting[][2] = {
{EP_WIFI_MODE, LEVEL_ADMIN},//0
{EP_STA_SSID, LEVEL_ADMIN},//1
{EP_STA_PASSWORD, LEVEL_ADMIN},//2
{EP_STA_IP_MODE, LEVEL_ADMIN},//3
{EP_STA_IP_VALUE, LEVEL_ADMIN},//4
{EP_STA_MASK_VALUE, LEVEL_ADMIN},//5
{EP_STA_GATEWAY_VALUE, LEVEL_ADMIN},//6
{EP_BAUD_RATE, LEVEL_ADMIN},//7
{EP_STA_PHY_MODE, LEVEL_ADMIN},//8
{EP_SLEEP_MODE, LEVEL_ADMIN},//9
{EP_CHANNEL, LEVEL_ADMIN},//10
{EP_AUTH_TYPE, LEVEL_ADMIN},//11
{EP_SSID_VISIBLE, LEVEL_ADMIN},//12
{EP_WEB_PORT, LEVEL_ADMIN},//13
{EP_DATA_PORT, LEVEL_ADMIN},//14
{EP_REFRESH_PAGE_TIME, LEVEL_USER},//15
{EP_HOSTNAME, LEVEL_ADMIN},//16
{EP_XY_FEEDRATE, LEVEL_USER},//17
{EP_Z_FEEDRATE, LEVEL_USER},//18
{EP_E_FEEDRATE, LEVEL_USER},//19
{EP_ADMIN_PWD, LEVEL_ADMIN},//20
{EP_USER_PWD, LEVEL_USER},//21
{EP_AP_SSID, LEVEL_ADMIN},//22
{EP_AP_PASSWORD, LEVEL_ADMIN},//23
{EP_AP_IP_VALUE, LEVEL_ADMIN},//24
{EP_AP_MASK_VALUE, LEVEL_ADMIN},//25
{EP_AP_GATEWAY_VALUE, LEVEL_ADMIN},//26
{EP_AP_IP_MODE, LEVEL_ADMIN},//27
{EP_AP_PHY_MODE, LEVEL_ADMIN},//28
{EP_DATA_STRING, LEVEL_USER},//29
{EP_REFRESH_PAGE_TIME2, LEVEL_USER},//30
{EP_TARGET_FW, LEVEL_USER},//31
{EP_TIMEZONE, LEVEL_USER},//32
{EP_TIME_ISDST, LEVEL_USER},//33
{EP_TIME_SERVER1, LEVEL_USER},//34
{EP_TIME_SERVER2, LEVEL_USER},//35
{EP_TIME_SERVER3, LEVEL_USER},//36
{EP_IS_DIRECT_SD, LEVEL_USER},//37
{EP_PRIMARY_SD, LEVEL_USER},//38
{EP_SECONDARY_SD, LEVEL_USER},//39
{EP_DIRECT_SD_CHECK, LEVEL_USER}, //40
{EP_SD_CHECK_UPDATE_AT_BOOT, LEVEL_USER} //41
};
#define AUTH_ENTRY_NB 42
//values
#define DEFAULT_MAX_REFRESH 120
#define DEFAULT_MIN_REFRESH 0
#define DEFAULT_MAX_XY_FEEDRATE 9999
#define DEFAULT_MIN_XY_FEEDRATE 1
#define DEFAULT_MAX_Z_FEEDRATE 9999
#define DEFAULT_MIN_Z_FEEDRATE 1
#define DEFAULT_MAX_E_FEEDRATE 9999
#define DEFAULT_MIN_E_FEEDRATE 1
#define DEFAULT_MAX_WEB_PORT 65001
#define DEFAULT_MIN_WEB_PORT 1
#define DEFAULT_MAX_DATA_PORT 65001
#define DEFAULT_MIN_DATA_PORT 1
#define MAX_TRY 2000
//sizes
#define EEPROM_SIZE 512 //max is 512
#define EEPROM_SIZE 1024 //max is 1024
#define MAX_SSID_LENGTH 32
#define MIN_SSID_LENGTH 1
#define MAX_PASSWORD_LENGTH 64
#define MIN_PASSWORD_LENGTH 8
//min size of password is 0 or upper than 8 char
//so let set min is 0
#define MIN_PASSWORD_LENGTH 0
#define MAX_LOCAL_PASSWORD_LENGTH 16
#define MIN_LOCAL_PASSWORD_LENGTH 1
#define MAX_DATA_LENGTH 128
#define MIN_DATA_LENGTH 0
#define IP_LENGTH 4
#define INTEGER_LENGTH 4
#define MAX_HOSTNAME_LENGTH 32
#define MIN_HOSTNAME_LENGTH 1
#define WL_MAC_ADDR_LENGTH 6
class CONFIG
{
public:
static bool is_direct_sd;
static bool read_string(int pos, char byte_buffer[], int size_max);
static bool read_string(int pos, String & sbuffer, int size_max);
static bool read_buffer(int pos, byte byte_buffer[], int size_buffer);
@ -256,18 +404,30 @@ public:
static bool write_buffer(int pos, const byte * byte_buffer, int size_buffer);
static bool write_byte(int pos, const byte value);
static bool reset_config();
static void print_config(tpipe output);
static void print_config(tpipe output, bool plaintext);
static bool SetFirmwareTarget(uint8_t fw);
static void InitFirmwareTarget();
static void InitDirectSD();
static void InitPins();
static bool InitBaudrate();
static bool InitExternalPorts();
static bool check_update_presence();
static uint8_t GetFirmwareTarget();
static const char* GetFirmwareTargetName();
static const char* GetFirmwareTargetShortName();
static bool isHostnameValid(const char * hostname);
static bool isSSIDValid(const char * ssid);
static bool isPasswordValid(const char * password);
static bool isLocalPasswordValid(const char * password);
static bool isIPValid(const char * IP);
static char * intTostr(int value);
static String formatBytes(size_t bytes);
static String formatBytes(uint32_t bytes);
static char * mac2str(uint8_t mac [WL_MAC_ADDR_LENGTH]);
static byte split_ip (const char * ptr,byte * part);
static void esp_restart();
static void flashfromSD(const char * Filename, int flashtype);
private:
static uint8_t FirmwareTarget;
};
#endif

18
esp3d/data/404.htm Normal file
View File

@ -0,0 +1,18 @@
<HTML>
<HEAD>
<title>404 page</title>
</HEAD>
<BODY STYLE="background-color: lightblue;">
<CENTER>
<H1>
<svg width='24' height='24' viewBox='0 0 1300 1200'>
<g transform='translate(50,1200) scale(1, -1)'>
<path fill='red' d='M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5q0 -120 65 -225 l587 587q-105 65 -225 65zM965 819l-584 -584q104 -62 219 -62q116 0 214.5 57t155.5 155.5t57 214.5q0 115 -62 219z' />
</g>
</svg>404 Error, unknown page!</H1>
<H2>What are you doing here ?</H2>
You are not supposed to find this location...
</CENTER>
</BODY>
</HTML>

View File

@ -1,30 +0,0 @@
<HTML>
<HEAD>
<title>Redirecting...</title>
</HEAD>
<BODY>
<CENTER>Unknown page - you will be redirected...
<BR><BR>
if not redirected, <a href='http://$WEB_ADDRESS$'>click here</a>
<BR><BR>
<PROGRESS name='prg' id='prg'></PROGRESS>
<script>
var i = 0;
var x = document.getElementById("prg");
x.max=5;
var interval=setInterval(function(){
i=i+1;
var x = document.getElementById("prg");
x.value=i;
if (i>5)
{
clearInterval(interval);
window.location.href='/';
}
},1000);
</script>
</CENTER>
</BODY>
</HTML>

View File

@ -1,65 +0,0 @@
$INCLUDE[header.inc]$
<div class="panel">
<div class="panel-heading">Access Point</div>
<div class="panel-body">
<form method="POST">
<div class="checkbox"><label class="control-label"><input type="checkbox" name="DEFAULT_MODE" $IS_DEFAULT_MODE$>Default Mode</label></div><br>
<div class="form-group $AP_SSID_STATUS$">
<label class="control-label" for="CONFIG1">SSID: </label><br>
<input type="text" class="form-control" id="CONFIG1" name="SSID" placeholder="SSID (8~32)" max="32" value="$AP_SSID$" style="width: auto;"></div>
<div class="form-group $AP_PASSWORD_STATUS$"><label class="control-label" for="CONFIG2">Password:</label><br>
<input type="password" class="form-control" id="CONFIG2" name="PASSWORD" placeholder="Password (0~64)" max="64" value="$AP_PASSWORD$" style="width: auto;"></div>
<div class="checkbox $IS_SSID_VISIBLE_STATUS$"><label class="control-label"><input type="checkbox" name="SSID_VISIBLE" $IS_SSID_VISIBLE$>Visible</label></div>
<div class="form-group $NETWORK_OPTION_LIST_STATUS$"><label class="control-label" for="CONFIG3">Network: </label><br>
<select name="NETWORK" id="CONFIG3" class="form-control" style="width:auto;">
$NETWORK_OPTION_LIST$
</select></div>
<div class="form-group $CHANNEL_OPTION_LIST_STATUS$"><label class="control-label" for="CONFIG4">Channel: </label><br>
<select name="CHANNEL" id="CONFIG4" class="form-control" style="width:auto;">
$CHANNEL_OPTION_LIST$
</select></div>
<div class="form-group $AUTH_OPTION_LIST_STATUS$"><label class="control-label" for="CONFIG5">Authentification: </label><br>
<select name="AUTHENTIFICATION" id="CONFIG5" class="form-control" style="width:auto;">
$AUTH_OPTION_LIST$
</select></div>
<script type="text/javascript">
function update_ip_set()
{
if (document.getElementById("STATIC_IP").checked)
{
document.getElementById("IP_SET").style.visibility="visible";
document.getElementById("IP_SET").style.width="auto";
document.getElementById("IP_SET").style.height="auto";
}
else
{
document.getElementById("IP_SET").style.visibility="hidden";
document.getElementById("IP_SET").style.width="0px";
document.getElementById("IP_SET").style.height="0px";
}
}
</script>
<div class="checkbox $AP_STATIC_IP_STATUS$"><label class="control-label"><input type="checkbox" id="STATIC_IP" name="STATIC_IP" onclick="update_ip_set();" $IS_STATIC_IP$>Static IP</label></div>
<div id="IP_SET" name="IP_SET">
<div class="form-group $AP_IP_STATUS$"><label class="control-label" for="CONFIG6">IP: </label><br>
<input type="text" class="form-control" id="CONFIG6" name="IP" placeholder="IP" value="$AP_IP$" max="15" style="width: auto;"></div>
<div class="form-group $AP_GW_STATUS$"><label class="control-label" for="CONFIG7">Gateway: </label><br>
<input type="text" class="form-control" id="CONFIG7" name="GATEWAY" placeholder="Gateway" value="$AP_GW$" max="15" style="width: auto;"></div>
<div class="form-group $AP_SUBNET_STATUS$"><label class="control-label" for="CONFIG8">Subnet: </label><br>
<input type="text" class="form-control" id="CONFIG8" name="SUBNET" placeholder="Subnet" value="$AP_SUBNET$" max="15" style="width: auto;"></div>
</div>
<div class="alert alert-danger" role="alert" style="$ERROR_MSG_VISIBILITY$" >
$ERROR_MSG$
</div>
<hr><input style="$SUBMIT_BUTTON_VISIBILITY$" type="submit" class="btn btn-primary" name="SUBMIT" value="Apply">
</form>
<div class="alert alert-success" role="alert" style="$SUCCESS_MSG_VISIBILITY$" >
$SUCCESS_MSG$
</div>
</div>
</div>
<script type="text/javascript">
update_ip_set();
</script>
$INCLUDE[footer.inc]$

View File

@ -1,66 +0,0 @@
$INCLUDE[header.inc]$
<div class="panel">
<div class="panel-heading">Station</div>
<div class="panel-body">
<form method="POST">
<DIV style="$AP_SCAN_VISIBILITY$">
<table class="table table-bordered table-striped" >
<caption>$AVAILABLE_AP_NB_ITEMS$ AP(s) available</caption>
<thead><tr><th>#</th><th>SSID</th><th>Signal</th><th>Protected</th></tr></thead>
<tbody>$AVAILABLE_AP[<tr><th>#$ROW_NUMBER$</th><td style="cursor:hand;" onclick="document.getElementById('CONFIG1').value='$AP_SSID$';">$AP_SSID$</td><td>$AP_SIGNAL$</td><td>$IS_PROTECTED$</td></tr>]$</tbody>
</table>
</DIV>
<div class="checkbox"><label class="control-label"><input type="checkbox" name="DEFAULT_MODE" $IS_DEFAULT_MODE$>Default Mode</label></div><br>
<div class="form-group $STA_SSID_STATUS$" ><label class="control-label" for="CONFIG1">SSID: </label><br>
<input type="text" class="form-control" id="CONFIG1" name="SSID" placeholder="SSID (8~32)" value="$STA_SSID$" max="32" style="width: auto;"></div>
<div class="form-group $STA_PASSWORD_STATUS$"><label class="control-label"for="CONFIG2">Password:</label><br>
<input type="password" class="form-control" id="CONFIG2" name="PASSWORD" placeholder="Password (0~64)" max="64" value="$STA_PASSWORD$" style="width: auto;"></div>
<div class="form-group $HOSTNAME_STATUS$" ><label class="control-label" for="CONFIG7">Hostname: </label><br>
<input type="text" class="form-control" id="CONFIG7" name="HOSTNAME" placeholder="Hostname (1~32)" value="$HOSTNAME$" max="32" style="width: auto;"></div>
<div class="form-group $NETWORK_OPTION_LIST_STATUS$"><label class="control-label"for="CONFIG3">Network: </label><br>
<select name="NETWORK" id="CONFIG3" class="form-control control-label" style="width:auto;">
$NETWORK_OPTION_LIST$
</select></div>
<script type="text/javascript">
function update_ip_set()
{
if (document.getElementById("STATIC_IP").checked)
{
document.getElementById("IP_SET").style.visibility="visible";
document.getElementById("IP_SET").style.width="auto";
document.getElementById("IP_SET").style.height="auto";
}
else
{
document.getElementById("IP_SET").style.visibility="hidden";
document.getElementById("IP_SET").style.width="0px";
document.getElementById("IP_SET").style.height="0px";
}
}
</script>
<div class="checkbox $STA_STATIC_IP_STATUS$"><label class="control-label">
<input type="checkbox" id="STATIC_IP" name="STATIC_IP" onclick="update_ip_set();" $IS_STATIC_IP$ >Static IP
</label>
</div>
<div id="IP_SET" name="IP_SET">
<div class="form-group $STA_IP_STATUS$"><label class="control-label" for="CONFIG4">IP: </label><br>
<input type="text" class="form-control" id="CONFIG4" name="IP" placeholder="IP" value="$STA_IP$" max="15" style="width: auto;"></div>
<div class="form-group $STA_GW_STATUS$"><label class="control-label"for="CONFIG5">Gateway: </label><br>
<input type="text" class="form-control" id="CONFIG5" name="GATEWAY" placeholder="Gateway" value="$STA_GW$" max="15" style="width: auto;"></div>
<div class="form-group $STA_SUBNET_STATUS$"><label class="control-label" for="CONFIG6">Subnet: </label><br>
<input type="text" class="form-control" id="CONFIG6" name="SUBNET" placeholder="Subnet" value="$STA_SUBNET$" max="15" style="width: auto;"></div>
</div>
<div class="alert alert-danger" role="alert" style="$ERROR_MSG_VISIBILITY$" >
$ERROR_MSG$
</div>
<hr><input style="$SUBMIT_BUTTON_VISIBILITY$" type="submit" class="btn btn-primary" name="SUBMIT" value="Apply">
</form>
<div class="alert alert-success" role="alert" style="$SUCCESS_MSG_VISIBILITY$" >
$SUCCESS_MSG$
</div>
</div>
</div>
<script type="text/javascript">
update_ip_set();
</script>
$INCLUDE[footer.inc]$

View File

@ -1,27 +0,0 @@
<STYLE>
input[type="file"]::-webkit-file-upload-button{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;-ms-touch-action:manipulation; touch-action:manipulation;cursor:pointer;
background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;
* -webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none; color: #ffffff;background-color: #5bc0de;border-color: #46b8da;}
input[type="file"]::-webkit-file-upload-button:focus{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;-ms-touch-action:manipulation; touch-action:manipulation;cursor:pointer;
background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;
* -webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none; color: #ffffff;background-color: #31b0d5;border-color: #1b6d85;}
input[type="file"]::-webkit-file-upload-button:hover{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;-ms-touch-action:manipulation; touch-action:manipulation;cursor:pointer;
background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;
* -webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none; color: #ffffff;background-color: #31b0d5;border-color: #269abc;}
.filelink {color:#000000;}
.filelink:hover, .filelink:focus {color:#0094FF;}
.panel-footer{padding:10px 15px;color:#31708f;background-color:#f5f5f5;border-color:#dddddd;border-top:1px solid #dddddd;}
.loader {
border: 4px solid #f3f3f3; /* Light grey */
border-top: 4px solid #3498db; /* Blue */
border-radius: 50%;
width: 12px;
height: 12px;
animation: spin 2s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</STYLE>

View File

@ -1,6 +0,0 @@
$SERVICE_PAGE$
<hr><br>
<div class="panel">
<center><table><tr><td><a class='blacklink' href='https://github.com/luc-github/ESP3D'>ESP3D powered</a></td><td>-</td><td><a class='blacklink' href='http://www.gnu.org/licenses/gpl-2.0.html'>GPL licensed</a></td></center></div>
</body>
</html>

View File

@ -1,30 +0,0 @@
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
$INCLUDE[css.inc]$
</style>
<title>$PAGE_TITLE$</title> </head>
<body>
<div class="container"><table class="nav">
<tr width=100%>
<td class="$MENU_HOME$"><a href="http://$WEB_ADDRESS$/HOME">Home</a></td>
<td class="$MENU_SYSTEM$"><a href="http://$WEB_ADDRESS$/CONFIGSYS">System</a></td>
<td class="$MENU_AP$"><a href="http://$WEB_ADDRESS$/CONFIGAP">Access Point</a></td>
<td class="$MENU_STA$"><a href="http://$WEB_ADDRESS$/CONFIGSTA">Station</a></td>
<td class="$MENU_PRINTER$"><a href="http://$WEB_ADDRESS$/PRINTER">3D Printer</a></td>
<td class="$MENU_SETTINGS$"><a href="http://$WEB_ADDRESS$/SETTINGS">Extra Settings</a></td>
<td width=100%>&nbsp;</td>
<td class="$MENU_ADMIN$"><a href="/PASSWORD" style="$DISCONNECT_VISIBILITY$">$LOGIN_ID$</a></td>
<td style="padding:0px;"><a href="/LOGIN?DISCONNECT=YES"><div class="btnroundimg" style="$DISCONNECT_VISIBILITY$"><svg width="30" height="30" viewBox="0 0 40 40">
<circle style="fill:white" cx="20" cy="20" r="20"/>
<circle style="fill:white;stroke:black;stroke-width:4" cx="20" cy="15" r="10"/>
<rect style="fill:black;stroke-width:4;stroke:black" width="20" height="15" x="10" y="17" />
<circle style="fill:white" cx="20" cy="22" r="2.5"/>
<polygon points="20,23 16,30 24,30" style="fill:white"/></svg></div></a></td>
<td>FW: V$FW_VER$</td>
<td><a href="https://github.com/luc-github/ESP3D">ESP3D</a></td>
</tr>
</table>

View File

@ -1,55 +0,0 @@
$INCLUDE[header.inc]$
<div class="panel">
<div class="panel-heading">System</div>
<div class="panel-body"><label>Chip ID: </label><label class="text-info">$CHIP_ID$</label><BR>
<label>CPU Frequency: </label><label class="text-info">$CPU_FREQ$ MHz</label><BR>
<label>Free Memory: </label><label class="text-info">$FREE_MEM$ bytes</label><BR>
<label>SDK Version: </label><label class="text-info">$SDK_VER$</label><BR>
<DIV style ="$HOSTNAME_VISIBLE$"><label>Hostname: </label><label class="text-info">$HOSTNAME$</label><BR></DIV>
<DIV style ="$MDNS_VISIBLE$;"><label>mDNS name: </label><label class="text-info">$MDNS_NAME$</label><BR></DIV>
<DIV style ="$SSDP_VISIBLE$;"><label>SSDP Protocol: </label><label class="text-info">$SSDP_STATUS$</label><BR></DIV>
<DIV style ="$CAPTIVE_PORTAL_VISIBLE$;"><label>Captive Portal: </label><label class="text-info">$CAPTIVE_PORTAL_STATUS$</label><BR></DIV>
<label>Network: </label><label class="text-info">$NET_PHY$</label><BR>
<label>Sleep mode: </label><label class="text-info">$SLEEP_MODE$</label><BR>
<label>Boot version: </label><label class="text-info">$BOOT_VER$</label><BR>
<label>Baud rate: </label><label class="text-info">$BAUD_RATE$</label><BR>
<label>Web port:</label><label class="text-info">$WEB_PORT$</label><BR>
<label>Data port:</label><label class="text-info">$DATA_PORT$</label><BR>
</div></div>
<div class="panel"><div class="panel-heading">Access Point ($AP_STATUS_ENABLED$)</div>
<div class="panel-body"><label>Mac address: </label><label class="text-info">$AP_MAC$</label><BR>
<div style="$AP_VISIBILITY$;">
<label>SSID: </label><label class="text-info">$AP_SSID$</label><BR>
<label>Visible: </label><label class="text-info">$AP_IS_VISIBLE$</label><BR>
<label>Channel: </label><label class="text-info">$AP_CHANNEL$</label><BR>
<label>Authentification: </label><label class="text-info">$AP_AUTH$</label><BR>
<label>Maximum connections: </label><label class="text-info">$AP_MAX_CON$</label><BR>
<label>DHCP Server: </label><label class="text-info">$AP_DHCP_STATUS$</label><BR>
<label>IP: </label><label class="text-info">$AP_IP$</label><BR>
<label>Gateway: </label><label class="text-info">$AP_GW$</label><BR>
<label>Subnet: </label><label class="text-info">$AP_SUBNET$</label><BR>
<table class="table table-bordered table-striped">
<caption>$CONNECTED_STATIONS_NB_ITEMS$ connected station(s)</caption>
<thead><tr><th>#</th><th>Mac</th><th>IP</th></tr></thead>
<tbody>$CONNECTED_STATIONS[<TR><th>#$ROW_NUMBER$</th><td>$MAC_CONNECTED$</td><td>$IP_CONNECTED$</td></TR>]$</tbody>
</table>
</div>
</div>
</div>
<div class="panel">
<div class="panel-heading">Station ($STA_STATUS_ENABLED$)</div>
<div class="panel-body"><label>Mac address: </label><label class="text-info">$STA_MAC$</label><BR>
<div style="$STA_VISIBILITY$;">
<label>Connection to: </label><label class="text-info">$STA_SSID$</label><BR>
<label>Channel: </label><label class="text-info">$STA_CHANNEL$</label><BR>
<label>Status: </label><label class="text-info">$STA_STATUS$</label><BR>
<label>Signal strength: </label><label class="text-info">$STA_SIGNAL$%</label><BR>
<label>DHCP Client: </label><label class="text-info">$STA_DHCP_STATUS$</label><BR>
<label>IP: </label><label class="text-info">$STA_IP$</label><BR>
<label>Gateway: </label><label class="text-info">$STA_GW$</label><BR>
<label>Subnet: </label><label class="text-info">$STA_SUBNET$</label><BR>
</div>
</div>
</div>
$INCLUDE[footer.inc]$

BIN
esp3d/data/index.html.gz Normal file

Binary file not shown.

View File

@ -1,21 +0,0 @@
$INCLUDE[header.inc]$
<div class="panel">
<div class="panel-heading">Log in</div>
<div class="panel-body">
<form method="POST">
<div class="form-group $USER_STATUS$"><label class="control-label" for="USER">User :</label><br>
<input type="hidden" name="return" value="$RETURN$">
<input type="text" class="form-control" id="USER" name="USER" placeholder="User (1~16)" min="1" max="16" value="$USER$" style="width: auto;"></div>
<div class="form-group $USER_PASSWORD_STATUS$"><label class="control-label" for="PASSWORD">Password :</label><br>
<input type="password" class="form-control" id="PASSWORD" name="PASSWORD" placeholder="Password (1~16)" min="1" max="16" value="$USER_PASSWORD$" style="width: auto;"></div>
<div class="alert alert-danger" role="alert" style="$ERROR_MSG_VISIBILITY$" >
$ERROR_MSG$
</div>
<hr><input style="$SUBMIT_BUTTON_VISIBILITY$" type="submit" class="btn btn-primary" name="SUBMIT" value="Apply">
</form>
<div class="alert alert-success" role="alert" style="$SUCCESS_MSG_VISIBILITY$" >
$SUCCESS_MSG$
</div>
</div>
</div>
$INCLUDE[footer.inc]$

74
esp3d/data/macrocfg.json Normal file
View File

@ -0,0 +1,74 @@
[
{
"name": "",
"glyph": "",
"filename": "",
"target": "",
"class": "",
"index": 0
},
{
"name": "",
"glyph": "",
"filename": "",
"target": "",
"class": "",
"index": 1
},
{
"name": "",
"glyph": "",
"filename": "",
"target": "",
"class": "",
"index": 2
},
{
"name": "",
"glyph": "",
"filename": "",
"target": "",
"class": "",
"index": 3
},
{
"name": "",
"glyph": "",
"filename": "",
"target": "",
"class": "",
"index": 4
},
{
"name": "",
"glyph": "",
"filename": "",
"target": "",
"class": "",
"index": 5
},
{
"name": "",
"glyph": "",
"filename": "",
"target": "",
"class": "",
"index": 6
},
{
"name": "",
"glyph": "",
"filename": "",
"target": "",
"class": "",
"index": 7
},
{
"name": "",
"glyph": "",
"filename": "",
"target": "",
"class": "",
"index": 8
}
]

View File

@ -1,111 +0,0 @@
$INCLUDE[header.inc]$
<div class="panel">
<div class="panel-heading">Change Password</div>
<div class="panel-body">
<form method="POST">
<div id="divpassword1" class="form-group $USER_PASSWORD_STATUS$" ><label class="control-label" for="PASSWORD1">Password: </label><br>
<input type="password" class="form-control" onkeyup="checkpassword()" id="PASSWORD1" name="PASSWORD" placeholder="Password (1~16)" value="$USER_PASSWORD$" max="16" m1n="1" style="width: auto;"></div>
<div id="divpassword2" class="form-group $USER_PASSWORD_STATUS2$"><label class="control-label"for="PASSWORD2">Confirm Password :</label><br>
<input type="password" class="form-control" onkeyup="checkpassword()" id="PASSWORD2" name="PASSWORD2" placeholder="Password (1~16)" max="16" minn="1" value="$USER_PASSWORD2$" style="width: auto;"></div>
<div class="alert alert-danger" role="alert" id="alerterror" style="$ERROR_MSG_VISIBILITY$" >
$ERROR_MSG$
</div>
<hr><input style="$SUBMIT_BUTTON_VISIBILITY$" type="submit" class="btn btn-primary" id="BTNSUBMIT" name="SUBMIT" value="Apply">
</form>
<div class="alert alert-success" id="alertsuccess" role="alert" style="$SUCCESS_MSG_VISIBILITY$" >
$SUCCESS_MSG$
</div>
</div>
</div>
<script>
function checkpassword()
{
var msg="";
var haserror=false;
var hassuccess=false;
var password1 = document.getElementById('PASSWORD1').value;
var password2 = document.getElementById('PASSWORD2').value;
if (password1.length<1)
{
msg+="Password too short<br>";
document.getElementById("divpassword1").className = "form-group has-error";
haserror=true;
hassuccess=false;
}
if (password1.length>16)
{
msg+="Password too long<br>";
document.getElementById("divpassword1").className = "form-group has-error";
haserror=true;
hassuccess=false;
}
if (password2.length<1)
{
msg+="Confirmation Password too short<br>";
document.getElementById("divpassword1").className = "form-group has-error";
haserror=true;
hassuccess=false;
}
if (password2.length>16)
{
msg+="Confirmation Password too long<br>";
document.getElementById("divpassword1").className = "form-group has-error";
haserror=true;
hassuccess=false;
}
if (password2!=password1)
{
msg+="Passwords do not matches<br>";
document.getElementById("divpassword2").className = "form-group has-error";
haserror=true;
hassuccess=false;
}
if (password1.length>0 && password1.length<17)
{
document.getElementById("divpassword1").className = "form-group has-success";
}
if (password2==password1 && password1.length>0 && password1.length<17)
{
haserror=false;
hassuccess=true;
document.getElementById("divpassword1").className = "form-group has-success";
document.getElementById("divpassword2").className = "form-group has-success";
msg="Passwords matches"
}
if (haserror)
{
document.getElementById("alerterror").style.visibility="visible";
document.getElementById("alerterror").style.width="auto";
document.getElementById("alerterror").style.height="auto";
document.getElementById("alerterror").style.padding="15px";
document.getElementById("alerterror").style.marginBottom="20px";
document.getElementById("alertsuccess").style.visibility="hidden";
document.getElementById("alertsuccess").style.width="0px";
document.getElementById("alertsuccess").style.height="0px";
document.getElementById("alertsuccess").style.padding="0px";
document.getElementById("alertsuccess").style.margin="0px";
document.getElementById("BTNSUBMIT").style.visibility="hidden";
document.getElementById("alerterror").innerHTML=msg;
}
if (hassuccess)
{
document.getElementById("alertsuccess").style.visibility="visible";
document.getElementById("alertsuccess").style.width="auto";
document.getElementById("alertsuccess").style.height="auto";
document.getElementById("alertsuccess").style.padding="15px";
document.getElementById("alertsuccess").style.marginBottom="20px";
document.getElementById("alerterror").style.visibility="hidden";
document.getElementById("alerterror").style.width="0px";
document.getElementById("alerterror").style.height="0px";
document.getElementById("alerterror").style.padding="0px";
document.getElementById("alerterror").style.margin="0px";
document.getElementById("BTNSUBMIT").style.visibility="visible";
document.getElementById("alertsuccess").innerHTML=msg;
}
}
</script>
$INCLUDE[footer.inc]$

View File

@ -1,870 +0,0 @@
$INCLUDE[header.inc]$
$INCLUDE[css2.inc]$
<div class="panel">
<div class="panel-heading" ><div class="btnimg" style="float:left;" onclick="control_expanded = expand_collapse(control_expanded,'pin_control','control')" id="pin_control">&#9660;</div>&nbsp;&nbsp;Control</div>
<div class="panel-body" id="control">
<table>
<tr><td style="padding:0px;">
<div id="Extruder1" style="visibility:hidden;height:0px;">
<table><tr><td><label>E1:&nbsp;</label></td>
<td id="data_extruder1" style="overflow: hidden;"></td><td>0<input id="rangeinput1" type="range" min=0 max=270 onchange="Updatenumber('1');">270</td>
<td><input class="form-control" id="numberinput1" type="number" min=0 max=270 step=1 value=0 onchange="Updaterange('1');"></td><td> &#176;C
<td><input type="button" class="btn btn-primary" value="Set" onclick="SendValue( 'M104 T0 S', '1');"></td></tr>
</table>
</div>
</td></tr>
<tr ><td style="padding:0px;">
<div id="Extruder2" style="visibility:hidden;height:0px;">
<table><tr><td><label>E2:&nbsp;</label></td>
<td id="data_extruder2" style="overflow: hidden;"></td><td>0<input id="rangeinput2" type="range" min=0 max=270 onchange="Updatenumber('2');">270</td>
<td><input class="form-control" id="numberinput2" type="number" min=0 max=270 step=1 value=0 onchange="Updaterange('2');"></td><td>&#176;C
<input type="button" class="btn btn-primary" value="Set" onclick="SendValue( 'M104 T1 S', '2');">
</td></tr>
</table>
</div>
</td></tr>
<tr><td style="padding:0px;">
<div id="Bed" style="visibility:hidden;height:0px;">
<table><tr><td><label>Bed:</label></td>
<td id="data_bed" style="overflow: hidden;"></td><td>0<input id="rangeinputbed" type="range" min=0 max=130 onchange="Updatenumber('bed');">130</td>
<td><input class="form-control" id="numberinputbed"type="number" min=0 max=270 step=1 value=0 onchange="Updaterange('bed');"></td><td>&#176;C
<input type="button" class="btn btn-primary" value="Set" onclick="SendValue( 'M140 S', 'bed');">
</td></tr>
</table>
</div>
</td></tr>
<tr><td id="speed">
<table>
<tr>
<td><label>Speed:</label></td><td><label class="text-info" id="currentspeed"></label></td>
<td>0<input id="rangeinputspeed" type="range" min=0 max=300 onchange="Updatenumber('speed');">300</td>
<td><input class="form-control" id="numberinputspeed" type="number" size="3" min=0 max=300 step=1 value=0 onchange="Updaterange('speed');"></td><td>%
<input type="button" class="btn btn-primary" class="btn btn-primary" value="Set" onclick="SendValue( 'M220 S', 'speed');"></td>
<td>&nbsp;&nbsp;</td>
<td colspan=3>
<div id="autostatus">
<table>
<tr>
<td>Status:</td>
<td id="status" align="center" valign="middle"><svg width="20" height="20"><circle cx="10" cy="10" r="8" stroke="black" stroke-width="2" fill="white"></circle></svg></td>
<td id="status-text" style="width:100px;"></td>
</tr>
</table>
</div>
<div id="manualstatus"><input type="button" class="btn btn-primary" value="Refresh" onclick="getstatus();" ></div>
</td>
<td>&nbsp;&nbsp;</td><td class="btnimg" onclick="OnclickEmergency();">
<svg width="40" height="40" viewBox="0 0 40 40"><circle cx="20" cy="20" r="18" stroke="black" stroke-width="2" fill="red" />
<circle cx="20" cy="20" r="10" stroke="black" stroke-width="4" fill="red" /><rect x="15" y="8" width="10" height="10" style="fill:red;stroke-width:1;stroke:red" />
<rect x="18" y="6" rx="1" ry="1" width="4" height="14" style="fill:black;stroke-width:1;stroke:black" /></svg></td>
</tr>
</table>
</td></tr>
<tr><td id="flow">
<table>
<tr>
<td><label>Flow:</label></td><td><label class="text-info" id="currentflow"></label></td>
<td>0<input id="rangeinputflow" type="range" min=0 max=300 onchange="Updatenumber('flow');">300</td>
<td><input class="form-control" id="numberinputflow" size="3" type="number" min=0 max=300 step=1 value=0 onchange="Updaterange('flow');"></td><td>%
<input type="button" class="btn btn-primary" value="Set" onclick="SendValue( 'M221 S', 'flow');"></td><td>&nbsp;&nbsp;</td>
<td><label>X:</label></td><td><label class="text-info" id="posx"></label></td><td>&nbsp;&nbsp;</td>
<td><label>Y:</label></td><td><label class="text-info" id="posy"></label></td><td>&nbsp;&nbsp;</td>
<td><label>Z:</label></td><td><label class="text-info" id="posz" ></label></td>
</tr>
</table>
</td></tr>
</table>
</div>
</div>
<div class="panel">
<div class="panel-heading"><div class="btnimg" style="float:left;" onclick="command_expanded = expand_collapse(command_expanded,'pin_command','command')" id="pin_command">&#9660;</div>&nbsp;&nbsp;Command</div>
<div class="panel-body" id="command">
<table width="100%"><tr>
<td width="100%"><input class="form-control" id="cmd" type="text" style="width: 100%;"></td>
<td width="auto"><input type="button" class="btn btn-primary" value="Send" onclick="Sendcustomcommand();"></td></tr></table>
<textarea style="overflow: scroll;width: 100%" rows="10" id="logwindow" readonly></textarea>
</div>
</div>
<div class="panel">
<div class="panel-heading"><div class="btnimg" style="float:left;" onclick="information_expanded = expand_collapse(information_expanded,'pin_information','information')" id="pin_information">&#9660;</div>&nbsp;&nbsp;Information</div>
<div class="panel-body" id="information">
<table><tr><td>
<center><table><tr><td><div class="btnimg" onclick="if(confirm('Clear Info log ?'))Sendcommand('[ESP999]INFO');">
<svg height="20" width="20" viewBox="0 0 40 40" >";
<circle cx="20" cy="20" r="17" stroke="black" stroke-width="1" fill="red" />
<line x1="11" y1="11" x2="29" y2="29" style="stroke:white;stroke-width:6" />
<line x1="29" y1="11" x2="11" y2="29" style="stroke:white;stroke-width:6" /></svg></div></td></tr></table></center>
</td><td width=100% id="infomsg" class="text-info"></td></tr></table>
</div>
</div>
<div class="panel">
<div class="panel-heading"><div class="btnimg" style="float:left;" onclick="error_expanded = expand_collapse(error_expanded,'pin_error','error')" id="pin_error">&#9660;</div>&nbsp;&nbsp;Error</div>
<div class="panel-body" id="error">
<table><tr><td>
<center><table><tr><td><div class="btnimg" onclick="if(confirm('Clear Error log ?'))Sendcommand('[ESP999]ERROR');">
<svg height="20" width="20" viewBox="0 0 40 40" >";
<circle cx="20" cy="20" r="17" stroke="black" stroke-width="1" fill="red" />
<line x1="11" y1="11" x2="29" y2="29" style="stroke:white;stroke-width:6" />
<line x1="29" y1="11" x2="11" y2="29" style="stroke:white;stroke-width:6" /></svg></div></td></tr></table></center>
</td><td width=100% id="errormsg" class="text-info"></td></tr></table>
</div>
</div>
<div class="panel">
<div class="panel-heading"><div class="btnimg" style="float:left;" onclick="status_expanded = expand_collapse(status_expanded,'pin_status','statusdisplay')" id="pin_status">&#9660;</div>&nbsp;&nbsp;Status</div>
<div class="panel-body" id="statusdisplay">
<table><tr><td>
<center><table><tr><td><div class="btnimg" onclick="if(confirm('Clear Status log ?'))Sendcommand('[ESP999]STATUS');">
<svg height="20" width="20" viewBox="0 0 40 40" >";
<circle cx="20" cy="20" r="17" stroke="black" stroke-width="1" fill="red" />
<line x1="11" y1="11" x2="29" y2="29" style="stroke:white;stroke-width:6" />
<line x1="29" y1="11" x2="11" y2="29" style="stroke:white;stroke-width:6" /></svg></div></td></tr></table></center>
</td><td width=100% id="statusmsg" class="text-info"></td></tr></table>
</div>
</div>
<div class="panel">
<div class="panel-heading"><div class="btnimg" style="float:left;" onclick="printcommands_expanded = expand_collapse(printcommands_expanded,'pin_printcommands','printcommands')" id="pin_printcommands">&#9660;</div>&nbsp;&nbsp;Print</div>
<div class="panel-body" id="printcommands">
<table>
<tr>
<td class="btnimg" onclick="Sendcommand('M24');">
<svg width="40" height="40" viewBox="0 0 40 40"><circle cx="20" cy="20" r="18" stroke="black" stroke-width="2" fill="white" /><polygon points="15,10 30,20 15,30" fill:"white" stroke:"white" stroke-width:"1" /></svg></td>
<td class="btnimg" onclick="Sendcommand('M25');"><svg width="40" height="40" viewBox="0 0 40 40"> <circle cx="20" cy="20" r="18" stroke="black" stroke-width="2" fill="white" />
<rect x="10" y="10" width="7" height="20" rx="2" ry="2" style="fill:rgb(0,0,0);stroke-width:1;stroke:rgb(0,0,0)" /> <rect x="23" y="10" width="7" height="20" rx="2" ry="2" style="fill:rgb(0,0,0);stroke-width:1;stroke:rgb(0,0,0)" /></svg></td>
<td class="btnimg" onclick="Sendcommand('M50');"><svg width="40" height="40" viewBox="0 0 40 40"><circle cx="20" cy="20" r="18" stroke="black" stroke-width="2" fill="white" />
<rect x="10" y="10" width="20" height="20" rx="2" ry="2" style="fill:rgb(0,0,0);stroke-width:1;stroke:rgb(0,0,0)" /></svg></td>
<td>&nbsp;&nbsp;</td>
<td id="SDLIST"></td>
</tr>
</table>
</div>
</div>
<div class="panel">
<div class="panel-heading"><div class="btnimg" style="float:left;" onclick="jog_expanded = expand_collapse(jog_expanded,'pin_jog','jogcommands')" id="pin_jog">&#9660;</div>&nbsp;&nbsp;Jog</div>
<div class="panel-body" id="jogcommands">
<table>
<tr align="center" valign="middle">
<td class="btnimg" onclick=" Sendcommand('G28 X');">
<svg width="40" height="40" viewBox="0 0 40 40" ><polygon points="7,40 7,25 4,28 0,24 20,4 26,10 26,6 32,6 32,16 40,24 36,28 33,25 33,40" fill="black" stroke-width:"1" stroke:"black" />
<line x1="25" y1="8" x2="33" y2="16" style="stroke:white;stroke-width:1" /><polyline points="4,28 20,12 36,28" style="fill:none;stroke:white;stroke-width:1" />
<text x="15" y="35" font-family="Verdana" font-size="14" fill="white">X</text></svg></td><td>
<table>
<tr>
<td class="btnimg" onclick="SendJogcommand( 'Y-10',XYfeedrate);"><svg width="40" height="20" viewBox="0 0 40 20">
<polyline points="5,18 20,5 35,18" style="fill:none;stroke:blue;stroke-width:7"/><text x="13" y="20" font-family="Verdana" font-size="7" fill="black">-10</text></svg></td>
</tr>
<tr>
<td class="btnimg" onclick="SendJogcommand( 'Y-1',XYfeedrate);"><svg width="40" height="20" viewBox="0 2 40 20">
<polyline points="5,18 20,5 35,18" style="fill:none;stroke:blue;stroke-width:5"/><text x="15" y="20" font-family="Verdana" font-size="7" fill="black">-1</text></svg></td>
</tr>
<tr>
<td class="btnimg" onclick="SendJogcommand( 'Y-0.1',XYfeedrate);"><svg width="40" height="20" viewBox="0 4 40 20">
<polyline points="5,18 20,5 35,18" style="fill:none;stroke:blue;stroke-width:2"/><text x="12" y="20" font-family="Verdana" font-size="7" fill="black">-0.1</text></svg></td>
</tr>
</table>
</td>
<td class="btnimg" onclick=" Sendcommand('G28 Y');"><svg width="40" height="40" viewBox="0 0 40 40">
<polygon points="7,40 7,25 4,28 0,24 20,4 26,10 26,6 32,6 32,16 40,24 36,28 33,25 33,40" fill="blue" stroke-width:"1" stroke:"black" /><line x1="25" y1="8" x2="33" y2="16" style="stroke:white;stroke-width:1" />
<polyline points="4,28 20,12 36,28" style="fill:none;stroke:white;stroke-width:1" /><text x="15" y="35" font-family="Verdana" font-size="14" fill="white">Y</text></svg>
</td>
<td></td>
<td>
<table>
<tr>
<td class="btnimg" onclick="SendJogcommand( 'Z-10',Zfeedrate);"><svg width="40" height="20" viewBox="0 0 40 20">
<polyline points="5,18 20,5 35,18" style="fill:none;stroke:green;stroke-width:7"/><text x="13" y="20" font-family="Verdana" font-size="7" fill="black">-10</text></svg>
</td>
</tr>
<tr>
<td class="btnimg" onclick="SendJogcommand( 'Z-1',Zfeedrate);"><svg width="40" height="20" viewBox="0 2 40 20">
<polyline points="5,18 20,5 35,18" style="fill:none;stroke:green;stroke-width:5"/><text x="15" y="20" font-family="Verdana" font-size="7" fill="black">-1</text></svg>
</td>
</tr>
<tr>
<td class="btnimg" onclick="SendJogcommand( 'Z-0.1',Zfeedrate);"><svg width="40" height="20" viewBox="0 4 40 20"><polyline points="5,18 20,5 35,18" style="fill:none;stroke:green;stroke-width:2"/>
<text x="12" y="20" font-family="Verdana" font-size="7" fill="black">-0.1</text></svg>
</td>
</tr>
</table>
</td>
<td></td>
<td id="JogExtruder1-1" style="visibility:hidden;">
<table>
<tr>
<td class="btnimg" onclick="SendJogcommand( 'E-50',Efeedrate,'T0');"><svg width="40" height="20" viewBox="0 0 40 20">
<polyline points="5,18 20,5 35,18" style="fill:none;stroke:orange;stroke-width:7"/><text x="13" y="20" font-family="Verdana" font-size="7" fill="black">-50</text></svg>
</td>
</tr>
<tr>
<td class="btnimg" onclick="SendJogcommand( 'E-10',Efeedrate,'T0');"><svg width="40" height="20" viewBox="0 2 40 20"><polyline points="5,18 20,5 35,18" style="fill:none;stroke:orange;stroke-width:5"/>
<text x="14" y="20" font-family="Verdana" font-size="7" fill="black">-10</text></svg>
</td>
</tr>
<tr>
<td class="btnimg" onclick="SendJogcommand( 'E-1',Efeedrate,'T0');"><svg width="40" height="20" viewBox="0 4 40 20"><polyline points="5,18 20,5 35,18" style="fill:none;stroke:orange;stroke-width:2"/>
<text x="14" y="20" font-family="Verdana" font-size="7" fill="black">-1</text></svg>
</td>
</tr>
</table>
</td>
<td></td>
<td id="JogExtruder2-1" style="visibility:hidden;">
<table>
<tr>
<td class="btnimg" onclick="SendJogcommand( 'E-50',Efeedrate,'T1');"><svg width="40" height="20" viewBox="0 0 40 20">
<polyline points="5,18 20,5 35,18" style="fill:none;stroke:pink;stroke-width:7"/><text x="13" y="20" font-family="Verdana" font-size="7" fill="black">-50</text></svg>
</td>
</tr>
<tr>
<td class="btnimg" onclick="SendJogcommand( 'E-10',Efeedrate,'T1');"><svg width="40" height="20" viewBox="0 2 40 20"><polyline points="5,18 20,5 35,18" style="fill:none;stroke:pink;stroke-width:5"/>
<text x="14" y="20" font-family="Verdana" font-size="7" fill="black">-10</text></svg>
</td>
</tr>
<tr>
<td class="btnimg" onclick="SendJogcommand( 'E-1',Efeedrate,'T1');"><svg width="40" height="20" viewBox="0 4 40 20"><polyline points="5,18 20,5 35,18" style="fill:none;stroke:pink;stroke-width:2"/>
<text x="15" y="20" font-family="Verdana" font-size="7" fill="black">-1</text></svg>
</td>
</tr>
</table>
</td>
</tr>
<tr align="center" valign="middle">
<td>
<table>
<tr>
<td class="btnimg" onclick="SendJogcommand( 'X10',XYfeedrate);"><svg width="20" height="40" viewBox="12 -10 20 40">
<polyline points="5,18 20,5 35,18" style="fill:none;stroke:black;stroke-width:7" transform="rotate(-90 20 10)"/><text x="22" y="13" font-family="Verdana" font-size="7" fill="black">10</text></svg></td>
<td class="btnimg" onclick="SendJogcommand( 'X1',XYfeedrate);"><svg width="20" height="40" viewBox="10 -10 20 40"><polyline points="5,18 20,5 35,18" style="fill:none;stroke:black;stroke-width:5" transform="rotate(-90 20 10)"/>
<text x="21" y="13" font-family="Verdana" font-size="7" fill="black">1</text></svg></td>
<td class="btnimg" onclick="SendJogcommand( 'X0.1',XYfeedrate);"><svg width="20" height="40" viewBox="14 -10 20 40">
<polyline points="5,18 20,5 35,18" style="fill:none;stroke:black;stroke-width:2" transform="rotate(-90 20 10)"/><text x="19" y="13" font-family="Verdana" font-size="7" fill="black">0.1</text></svg>
</td>
</tr>
</table>
</td>
<td></td>
<td>
<table>
<tr>
<td class="btnimg" onclick="SendJogcommand( 'X-0.1',XYfeedrate);"><svg width="20" height="40" viewBox="6 -10 20 40">
<polyline points="5,18 20,5 35,18" style="fill:none;stroke:black;stroke-width:3" transform="rotate(90 20 10)"/><text x="7" y="12" font-family="Verdana" font-size="7" fill="black">-0.1</text></svg></td>
<td class="btnimg" onclick="SendJogcommand( 'X-1',XYfeedrate);"><svg width="20" height="40" viewBox="8 -10 20 40"><polyline points="5,18 20,5 35,18" style="fill:none;stroke:black;stroke-width:5" transform="rotate(90 20 10)"/>
<text x="11" y="13" font-family="Verdana" font-size="7" fill="black">-1</text></svg></td><td class="btnimg" onclick="SendJogcommand( 'X-10',XYfeedrate);">
<svg width="20" height="40" viewBox="8 -10 20 40"><polyline points="5,18 20,5 35,18" style="fill:none;stroke:black;stroke-width:7" transform="rotate(90 20 10)"/>
<text x="7" y="12" font-size="7" fill="black">-10</text></svg>
</td>
</tr>
</table>
</td>
<td></td>
<td><svg width="20" height="20" viewBox="0 0 20 20"><text x="1" y="18" font-family="Verdana" font-size="22" fill="green">Z</text></svg></td>
<td></td>
<td id="JogExtruder1-2" style="visibility:hidden;"><svg width="20" height="20" viewBox="0 0 20 20"><text x="1" y="18" font-family="Verdana" font-size="22" fill="orange">1</text></svg></td>
<td></td>
<td id="JogExtruder2-2" style="visibility:hidden;"><svg width="20" height="20" viewBox="0 0 20 20"><text x="1" y="18" font-family="Verdana" font-size="22" fill="pink">2</text></svg></td>
</tr>
<tr align="center" valign="middle">
<td class="btnimg" onclick=" Sendcommand('G28');"><svg width="40" height="40" viewBox="0 0 40 40"><polygon points="7,40 7,25 4,28 0,24 20,4 26,10 26,6 32,6 32,16 40,24 36,28 33,25 33,40" fill="purple" stroke-width:"1" stroke:"black" />
<line x1="25" y1="8" x2="33" y2="16" style="stroke:white;stroke-width:1" /><polyline points="4,28 20,12 36,28" style="fill:none;stroke:white;stroke-width:1" /></svg></td>
<td>
<table>
<tr>
<td class="btnimg" onclick="SendJogcommand( 'Y0.1',XYfeedrate);"><svg width="40" height="20" viewBox="0 -4 40 20">
<polyline points="5,18 20,5 35,18" style="fill:none;stroke:blue;stroke-width:3" transform="rotate(180 20 10)"/><text x="15" y="6" font-family="Verdana" font-size="7" fill="black">0.1</text></svg></td>
</tr>
<tr>
<td class="btnimg" onclick="SendJogcommand( 'Y1',XYfeedrate);"><svg width="40" height="20" viewBox="0 -2 40 20"><polyline points="5,18 20,5 35,18" style="fill:none;stroke:blue;stroke-width:5" transform="rotate(180 20 10)"/>
<text x="17" y="7" font-family="Verdana" font-size="7" fill="black">1</text></svg></td>
</tr>
<tr>
<td class="btnimg" onclick="SendJogcommand( 'Y10',XYfeedrate);"><svg width="40" height="20" viewBox="0 0 40 20"><polyline points="5,18 20,5 35,18" style="fill:none;stroke:blue;stroke-width:7" transform="rotate(180 20 10)"/>
<text x="15" y="6" font-family="Verdana" font-size="7" fill="black">10</text></svg></td>
</tr>
</table>
</td>
<td class="btnimg" onclick=" Sendcommand('G28 Z');"><svg width="40" height="40" viewBox="0 0 40 40"><polygon points="7,40 7,25 4,28 0,24 20,4 26,10 26,6 32,6 32,16 40,24 36,28 33,25 33,40" fill="green" stroke-width:"1" stroke:"black" />
<line x1="25" y1="8" x2="33" y2="16" style="stroke:white;stroke-width:1" /><polyline points="4,28 20,12 36,28" style="fill:none;stroke:white;stroke-width:1" /><text x="15" y="35" font-family="Verdana" font-size="14" fill="white">Z</text></svg></td>
<td></td>
<td>
<table>
<tr>
<td class="btnimg" onclick="SendJogcommand( 'Z0.1',Zfeedrate);"><svg width="40" height="20" viewBox="0 -4 40 20">
<polyline points="5,18 20,5 35,18" style="fill:none;stroke:green;stroke-width:3" transform="rotate(180 20 10)"/><text x="14" y="6" font-family="Verdana" font-size="7" fill="black">0.1</text></svg></td>
</tr>
<tr>
<td class="btnimg" onclick="SendJogcommand( 'Z1',Zfeedrate);"><svg width="40" height="20" viewBox="0 -2 40 20"><polyline points="5,18 20,5 35,18" style="fill:none;stroke:green;stroke-width:5" transform="rotate(180 20 10)"/>
<text x="18" y="7" font-family="Verdana" font-size="7" fill="black">1</text></svg></td>
</tr>
<tr>
<td class="btnimg" onclick="SendJogcommand( 'Z10',Zfeedrate);"><svg width="40" height="20" viewBox="0 0 40 20">
<polyline points="5,18 20,5 35,18" style="fill:none;stroke:green;stroke-width:7" transform="rotate(180 20 10)"/><text x="15" y="6" font-family="Verdana" font-size="7" fill="black">10</text></svg></td>
</tr>
</table>
</td>
<td></td>
<td id="JogExtruder1-3" style="visibility:hidden;">
<table>
<tr>
<td class="btnimg" onclick="SendJogcommand( 'E1',Efeedrate,'T0');"><svg width="40" height="20" viewBox="0 -4 40 20">
<polyline points="5,18 20,5 35,18" style="fill:none;stroke:orange;stroke-width:3" transform="rotate(180 20 10)"/><text x="18" y="6" font-family="Verdana" font-size="7" fill="black">1</text></svg></td>
</tr>
<tr>
<td class="btnimg" onclick="SendJogcommand( 'E10',Efeedrate,'T0');"><svg width="40" height="20" viewBox="0 -2 40 20"><polyline points="5,18 20,5 35,18" style="fill:none;stroke:orange;stroke-width:5" transform="rotate(180 20 10)"/>
<text x="14" y="7" font-family="Verdana" font-size="7" fill="black">10</text></svg></td>
</tr>
<tr>
<td class="btnimg" onclick="SendJogcommand( 'E50',Efeedrate,'T0');"><svg width="40" height="20" viewBox="0 0 40 20">
<polyline points="5,18 20,5 35,18" style="fill:none;stroke:orange;stroke-width:7" transform="rotate(180 20 10)"/><text x="15" y="6" font-family="Verdana" font-size="7" fill="black">50</text></svg></td>
</tr>
</table>
</td>
<td></td>
<td id="JogExtruder2-3" style="visibility:hidden;">
<table>
<tr>
<td class="btnimg" onclick="SendJogcommand( 'E1',Efeedrate,'T1');"><svg width="40" height="20" viewBox="0 -4 40 20">
<polyline points="5,18 20,5 35,18" style="fill:none;stroke:pink;stroke-width:3" transform="rotate(180 20 10)"/><text x="18" y="6" font-family="Verdana" font-size="7" fill="black">1</text></svg></td>
</tr>
<tr>
<td class="btnimg" onclick="SendJogcommand( 'E10',Efeedrate,'T1');"><svg width="40" height="20" viewBox="0 -2 40 20"><polyline points="5,18 20,5 35,18" style="fill:none;stroke:pink;stroke-width:5" transform="rotate(180 20 10)"/>
<text x="14" y="7" font-family="Verdana" font-size="7" fill="black">10</text></svg></td>
</tr>
<tr>
<td class="btnimg" onclick="SendJogcommand( 'E50',Efeedrate,'T1');"><svg width="40" height="20" viewBox="0 0 40 20" ><polyline points="5,18 20,5 35,18" style="fill:none;stroke:pink;stroke-width:7" transform="rotate(180 20 10)"/>
<text x="15" y="6" font-family="Verdana" font-size="7" fill="black">50</text></svg></td>
</tr>
</table>
</td>
</tr>
</table>
</div>
</div>
<div class="panel">
<div class="panel-heading"><div class="btnimg" style="float:left;" onclick="filemanager_expanded = expand_collapse(filemanager_expanded,'pin_filemanager','filemanager')" id="pin_filemanager">&#9660;</div>&nbsp;&nbsp;SD Files</div>
<div class="panel-body" id="filemanager">
<input type="file" id="file-select" name="myfiles[]" multiple/>
<input class="btn btn-primary" type="button" id="upload-button" onclick="Sendfile();" value="Upload"/>&nbsp;&nbsp;<progress style="visibility:hidden;" name='prg' id='prg' max='100'></progress>
<br><br><div class="panel">
<div class="panel-body">
<table><tr><td width="0%">
<div onclick="Createdir()" class="btnimg" id="iconcreatedir" style="display:none;"><svg width="40" height="40" viewBox="0 0 40 40"><rect x="5" y="10" width="30" height="20" rx="2" ry="2" fill="#31b0d5" />
<rect x="20" y="5" width="15" height="15" rx="2" ry="2" fill="#31b0d5" /><text x="15" y="25" font-size="18" font-weight="800" fill="white">+</text></svg>
</div>
</td><td><div id="loader" class="loader"></div></td><td width="100%"><div id="path" class="info" >&nbsp</div>
</td>
</tr></table>
<table class="table table-striped" style="border:1px;solid #dddddd;margin-bottom:20px;" ><thead><tr><th width='0%'>Type</th><th>Name</th><th>Size</th><th width='0%'></th><th width='0%'></th><th width='100%'></th></tr></thead><tbody id="file_list"><tbody></table>
</div>
<div class="panel-footer " id="filestatus"></div>
</div>
</div>
</div>
<script type="text/javascript">
var control_expanded = true;
var command_expanded = true;
var information_expanded = true;
var status_expanded = true;
var error_expanded = true;
var filemanager_expanded = true;
var printcommands_expanded = true;
var jog_expanded = true;
function expand_collapse(flag, targetpin,targetdiv){
if (flag) {
document.getElementById(targetpin).innerHTML = '&#9658;';
document.getElementById(targetdiv).style.display = 'none';
return false;
} else {
document.getElementById(targetpin).innerHTML = '&#9660;';
document.getElementById(targetdiv).style.display = 'block';
return true;
}
}
var XYfeedrate=$XY_FEEDRATE$;
var Zfeedrate=$Z_FEEDRATE$;
var Efeedrate=$E_FEEDRATE$;
function Sendcommand(commandtxt, showresult){
if (typeof showresult === 'undefined')showresult=false
var xmlhttp = new XMLHttpRequest();
var url = "/command?plain="+encodeURIComponent(commandtxt);
if (!showresult)url = "/command_silent?plain="+encodeURIComponent(commandtxt);
if (showresult){
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status === 200) {
var textarea = document.getElementById("logwindow");
textarea.innerHTML = textarea.innerHTML + xmlhttp.responseText;
textarea.scrollTop = textarea.scrollHeight;
}
}
}
xmlhttp.open("GET", url, true);
xmlhttp.send();
}
function delay(ms) {
ms += new Date().getTime();
while (new Date() < ms){}
}
function SendJogcommand( cmd, feedrate, extra){
if (typeof extra === 'undefined')extra=""
if (extra != ""){
Sendcommand(extra);
delay(100);
}
Sendcommand("G91");
delay(100);
Sendcommand("G1 "+cmd + " F"+feedrate);
delay(100);
Sendcommand("G90");
}
function SendValue( cmd, item){
Sendcommand(cmd + document.getElementById("numberinput"+item).value);
}
function Sendcustomcommand(){
var cmd = document.getElementById("cmd").value;
if (cmd.trim().length > 0) Sendcommand(cmd,true);
document.getElementById("cmd").value="";
}
function OnclickEmergency(){
Sendcommand("M112");
}
function Updatenumber(item){
document.getElementById("numberinput"+item).value=document.getElementById("rangeinput"+item).value;
}
function Updaterange(item){
document.getElementById("rangeinput"+item).value=document.getElementById("numberinput"+item).value;
}
Updaterange('1');
Updaterange('2');
Updaterange('bed');
Updaterange('speed');
Updaterange('flow');
var pulse=true;
var initialization_done = false;
var pos=0;
function displaytemp(temperature, target,item,factor){
var displaypicture = "<svg width='300' height='30' viewBox='0 0 300 30'>\n";
if (temperature.length ==0)temperature=0;
if (target.length ==0)target=0;
var description = String (temperature) + "/";
if (target>0)description += String (target);
else description += "Off ";
displaypicture+="<defs><linearGradient id='grad1' x1='0%' y1='0%' x2='100%' y2='0%'><stop offset='0%' style='stop-color:#0007FE;stop-opacity:1' />\n";
displaypicture+="<stop offset='100%' style='stop-color:#00FAFE;stop-opacity:1' /></linearGradient>/n";
displaypicture+="<linearGradient id='grad2' x1='0%' y1='0%' x2='100%' y2='0%'><stop offset='0%' style='stop-color:#00FAFE;stop-opacity:1' />\n";
displaypicture+="<stop offset='100%' style='stop-color:#00FF00;stop-opacity:1' /></linearGradient>\n";
displaypicture+="<linearGradient id='grad3' x1='0%' y1='0%' x2='100%' y2='0%'><stop offset='0%' style='stop-color:#00FF00;stop-opacity:1' />\n"
displaypicture+="<stop offset='100%' style='stop-color:#FAFD00;stop-opacity:1' /></linearGradient>\n";
displaypicture+="<linearGradient id='grad4' x1='0%' y1='0%' x2='100%' y2='0%'><stop offset='0%' style='stop-color:#FAFD00;stop-opacity:1' />\n";
displaypicture+="<stop offset='100%' style='stop-color:#FE0700;stop-opacity:1' /></linearGradient></defs>\n";
displaypicture+="<rect x='10' y='4' width='70' height='21' fill='url(#grad1)' /><rect x='80' y='4' width='70' height='21' fill='url(#grad2)' />\n";
displaypicture+="<rect x='150' y='4' width='70' height='21' fill='url(#grad3)' /><rect x='220' y='4' width='70' height='21' fill='url(#grad4)' />\n";
displaypicture+="<rect x='10' y='4' width='280' height='21' fill='none' stroke-width='1' stroke='#C3BDB5' />\n";
displaypicture+="<line x1=\"";
displaypicture+=String(parseFloat(target)*factor+10);
displaypicture+="\" y1=\"4\" x2=\"";
displaypicture+=String(parseFloat(target)*factor+10);
displaypicture+="\" y2=\"25\" style=\"stroke:rgb(255,255,255);stroke-width:1\" />\n<path d=\"M";
displaypicture+=String(parseFloat(temperature)*factor+5);
displaypicture+=" 0 L";
displaypicture+=String(parseFloat(temperature)*factor+15);
displaypicture+=" 0 L";
displaypicture+=String(parseFloat(temperature)*factor+10);
displaypicture+=" 8 Z\" stroke=\"white\" stroke-width=\"1\" />\n<path d=\"M";
displaypicture+=String(parseFloat(temperature)*factor+5);
displaypicture+=" 30 L";
displaypicture+=String(parseFloat(temperature)*factor+15);
displaypicture+=" 30 L";
displaypicture+=String(parseFloat(temperature)*factor+10);
displaypicture+=" 22 Z\" stroke=\"white\" stroke-width=\"1\"/>\n<text x=\"30\" y=\"19\" fill=\"black\" style=\"font-family: calibri; font-size:10pt;\">\n";
displaypicture+=description;
displaypicture+=" &#176;C</text>\n";
displaypicture+=" </svg>\n";
document.getElementById(item).innerHTML=displaypicture;
}
function displaystatus(status){
var content ="<svg width=\"20\" height=\"20\"><circle cx=\"10\" cy=\"10\" r=\"8\" stroke=\"black\" stroke-width=\"2\" fill=\"";
if (status=="Connected"){
if (pulse)content +="#00FF00";
else content +="#007F0E";}
else if (status=="Busy"){
if (pulse)content +="#FFD800";
else content +="#7F6A00";}
else{
if (pulse)content +="#FF0000";
else content +="#7F0000";}
content +="\"></circle></svg>";
pulse=!pulse;
document.getElementById("status").innerHTML=content;
document.getElementById("status-text").innerHTML=status;
}
function dispatchstatus(jsonresponse){
var temp=0;
if(jsonresponse.heater[0].active==1){
document.getElementById("Extruder1").style.visibility="visible";
document.getElementById("Extruder1").style.height="auto";
document.getElementById("JogExtruder1-1").style.visibility="visible";
document.getElementById("JogExtruder1-2").style.visibility="visible";
document.getElementById("JogExtruder1-3").style.visibility="visible";
displaytemp(jsonresponse.heater[0].temperature, jsonresponse.heater[0].target,"data_extruder1",1.03);
Updaterange('1');}
else {
document.getElementById("Extruder1").style.visibility="hidden";
document.getElementById("Extruder1").style.height="0px";
document.getElementById("JogExtruder1-1").style.visibility="hidden";
document.getElementById("JogExtruder1-2").style.visibility="hidden";
document.getElementById("JogExtruder1-3").style.visibility="hidden";}
if(jsonresponse.heater[1].active==1){
document.getElementById("Extruder2").style.visibility="visible";
document.getElementById("Extruder2").style.height="auto";
document.getElementById("JogExtruder2-1").style.visibility="visible";
document.getElementById("JogExtruder2-2").style.visibility="visible";
document.getElementById("JogExtruder2-3").style.visibility="visible";
displaytemp(jsonresponse.heater[1].temperature, jsonresponse.heater[1].target,"data_extruder2",1.03);
Updaterange('2');}
else {
document.getElementById("Extruder2").style.visibility="hidden";
document.getElementById("Extruder2").style.height="0px";
document.getElementById("JogExtruder2-1").style.visibility="hidden";
document.getElementById("JogExtruder2-2").style.visibility="hidden";
document.getElementById("JogExtruder2-3").style.visibility="hidden";}
if(jsonresponse.heater[2].active==1){
document.getElementById("Bed").style.visibility="visible";
document.getElementById("Bed").style.height="auto";
displaytemp(jsonresponse.heater[2].temperature, jsonresponse.heater[2].target,"data_bed",2.15);
Updaterange('bed');}
else {
document.getElementById("Bed").style.visibility="hidden";
document.getElementById("Bed").style.height="0px";}
document.getElementById("posx").innerHTML=jsonresponse.Xpos;
document.getElementById("posy").innerHTML=jsonresponse.Ypos;
document.getElementById("posz").innerHTML=jsonresponse.Zpos;
displaystatus(jsonresponse.status);
var content="";
for (i = 0; i < jsonresponse.InformationMsg.length; i++) {
if (i==jsonresponse.InformationMsg.length-1)content +="<li style='list-style-type: disc;'><b>" +jsonresponse.InformationMsg[i].line+ "</b>";
else content +="<li style='list-style-type: circle;'>"+jsonresponse.InformationMsg[i].line;
content += "</li>";}
document.getElementById("infomsg").innerHTML=content;
content="";
for (i = 0; i < jsonresponse.ErrorMsg.length; i++){
if (i==jsonresponse.ErrorMsg.length-1)content +="<li style='list-style-type: disc;'><b>" +jsonresponse.ErrorMsg[i].line+ "</b>";
else content +="<li style='list-style-type: circle;'>"+jsonresponse.ErrorMsg[i].line;
content +="</li>";}
document.getElementById("errormsg").innerHTML=content;
content="";
for (i = 0; i < jsonresponse.StatusMsg.length; i++)
{
if (i==jsonresponse.StatusMsg.length-1)content +="<li style='list-style-type: disc;'><b>" +jsonresponse.StatusMsg[i].line+ "</b>";
else content +="<li style='list-style-type: circle;'>"+jsonresponse.StatusMsg[i].line;
content +="</li>";
}
document.getElementById("statusmsg").innerHTML=content;
if (!initialization_done){
document.getElementById("numberinputspeed").value=jsonresponse.speed;
Updaterange('speed');
document.getElementById("numberinputflow").value=jsonresponse.flow;
Updaterange('flow');
if(jsonresponse.heater[0].active==1){
if (jsonresponse.heater[0].target.length==0)temp=0;
else temp = parseInt(jsonresponse.heater[0].target);
document.getElementById("numberinput1").value= temp;
Updaterange('1');}
if(jsonresponse.heater[1].active==1){
if (jsonresponse.heater[1].target.length==0)temp=0;
else temp = parseInt(jsonresponse.heater[1].target);
document.getElementById("numberinput2").value=temp;
Updaterange('2');}
if(jsonresponse.heater[2].active==1){
if (jsonresponse.heater[2].target.length==0)temp=0;
else temp = parseInt(jsonresponse.heater[2].target);
document.getElementById("numberinputbed").value=temp;
Updaterange('bed');}
initialization_done=true;}
document.getElementById("currentspeed").innerHTML=jsonresponse.speed + "%";
document.getElementById("currentflow").innerHTML=jsonresponse.flow + "%";
}
var canrefresh = true;
function getstatus(){
if (canrefresh){
var xmlhttp = new XMLHttpRequest();
var url = "http://$WEB_ADDRESS$/STATUS";
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
var jsonresponse = JSON.parse(xmlhttp.responseText);
dispatchstatus(jsonresponse);}
}
xmlhttp.open("GET", url, true);
xmlhttp.send();
}
}
var currentpath = "/";
function navbar(){
var content="<table><tr>";
var tlist = currentpath.split("/");
var path="/";
var nb = 1;
content+="<td class='btnimg' onclick=\"currentpath='/'; refreshSDfiles();\">/</td>";
while (nb < (tlist.length-1))
{
path+=tlist[nb] + "/";
content+="<td class='btnimg' onclick=\"currentpath='"+path+"'; SendFileCommand('list','all');\">"+tlist[nb] +"</td><td>/</td>";
nb++;
}
content+="</tr></table>";
return content;
}
function print_icon(){
var content ="<svg width='24' height='24' viewBox='-10 -10 138 138'>";
content +="<rect x='20' y='0' rx='10' ry='10' width='88' height='127' style='fill:black;' />";
content +="<rect x='0' y='40' rx='10' ry='10' width='127' height='58' style='fill:black;' />";
content +="<rect x='29' y='9' rx='10' ry='10' width='70' height='109' style='fill:white;' />";
content +="<rect x='29' y='40' width='88' height='32' style='fill:black;' />";
content +="<line x1='20' y1='72' x2='20' y2='98' style='stroke:white;stroke-width:1' />";
content +="<line x1='108' y1='72' x2='108' y2='98' style='stroke:white;stroke-width:1' />";
content +="<circle cx='105' cy='56' r='7' fill='white' />";
content +="<rect x='38' y='82' width='51' height='10' style='fill:black;' />";
content +="<rect x='38' y='98' width='32' height='10' style='fill:black;' />";
content +="</svg>";
return content;
}
function trash_icon(){
var content ="<svg width='24' height='24' viewBox='0 0 128 128'>";
content +="<rect x='52' y='12' rx='6' ry='6' width='25' height='7' style='fill:red;' />";
content +="<rect x='52' y='16' width='25' height='2' style='fill:white;' />";
content +="<rect x='30' y='18' rx='6' ry='6' width='67' height='100' style='fill:red;' />";
content +="<rect x='20' y='18' rx='10' ry='10' width='87' height='14' style='fill:red;' />";
content +="<rect x='20' y='29' width='87' height='3' style='fill:white;' />";
content +="<rect x='40' y='43' rx='7' ry='7' width='7' height='63' style='fill:white;' />";
content +="<rect x='60' y='43' rx='7' ry='7' width='7' height='63' style='fill:white;' />";
content +="<rect x='80' y='43' rx='7' ry='7' width='7' height='63' style='fill:white;' /></svg>";
return content;
}
function back_icon(){
var content ="<svg width='24' height='24' viewBox='0 0 24 24'><path d='M7,3 L2,8 L7,13 L7,10 L17,10 L18,11 L18,15 L17,16 L10,16 L9,17 L9,19 L10,20 L20,20 L22,18 L22,8 L20,6 L7,6 z' stroke='black' fill='white' /></svg>";
return content;
}
function select_dir(directoryname){
currentpath+=directoryname + "/";
SendFileCommand('list','all');
}
function compareStrings(a, b) {
// case-insensitive comparison
a = a.toLowerCase();
b = b.toLowerCase();
return (a < b) ? -1 : (a > b) ? 1 : 0;
}
var retry = 0;
var serialsdmode = true;
function refreshSDfiles(){
document.getElementById("SDLIST").innerHTML="";
if (serialsdmode){
Sendcommand("M20");
delay(1000);
}
retry = 0;
SendFileCommand("list","all");
}
function dispatchfilestatus(jsonresponse)
{
var content ="";
if (!jsonresponse.status) {
document.getElementById('filestatus').innerHTML="&nbsp;&nbsp;Status: Error!";
return;
}
if (jsonresponse.status == "processing"){
document.getElementById('filestatus').innerHTML="&nbsp;&nbsp;Status: Processing";
delay(1000);
retry = retry +1;
if (retry < 6) SendFileCommand("list","all");
else document.getElementById('filestatus').innerHTML="&nbsp;&nbsp;Status: No answer";
return;
}
if (jsonresponse.mode){
if (jsonresponse.mode == "direct") {
serialsdmode=false;
document.getElementById('iconcreatedir').style.display='block';
}
}
content ="&nbsp;&nbsp;Status: "+jsonresponse.status;
retry = 0;
if (jsonresponse.files){
if (jsonresponse.total && !serialsdmode){
content +="&nbsp;&nbsp;|&nbsp;&nbsp;Total space: "+jsonresponse.total;
content +="&nbsp;&nbsp;|&nbsp;&nbsp;Used space: "+jsonresponse.used;
content +="&nbsp;&nbsp;|&nbsp;&nbsp;Occupation: ";
content +="<meter min='0' max='100' high='90' value='"+jsonresponse.occupation +"'></meter>&nbsp;"+jsonresponse.occupation +"%";
}
}
document.getElementById('filestatus').innerHTML=content;
content ="";
if (serialsdmode)currentpath="/";
if (currentpath!="/")
{
var pos = currentpath.lastIndexOf("/",currentpath.length-2)
var previouspath = currentpath.slice(0,pos+1);
content +="<tr style='cursor:hand;' onclick=\"currentpath='"+previouspath+"'; SendFileCommand('list','all');\"><td >"+back_icon()+"</td><td colspan='4'> Up..</td></tr>";
}
if (jsonresponse.files){
jsonresponse.files.sort(function(a, b) {
return compareStrings(a.name, b.name);
});
var linenumber=1;
for (var i=0;i <jsonresponse.files.length;i++){
//first display files
if (String(jsonresponse.files[i].size) != "-1")
{
content +="<TR>";
content +="<td id='line"+linenumber+"'><svg height='24' width='24' viewBox='0 0 24 24' > <path d='M1,2 L1,21 L2,22 L16,22 L17,21 L17,6 L12,6 L12,1 L2,1 z' stroke='black' fill='white' /><line x1='12' y1='1' x2='17' y2='6' stroke='black' stroke-width='1'/>";
content +="<line x1='5' y1='10' x2='13' y2='10' stroke='black' stroke-width='1'/> <line x1='5' y1='14' x2='13' y2='14' stroke='black' stroke-width='1'/> <line x1='5' y1='18' x2='13' y2='18' stroke='black' stroke-width='1'/></svg></td>";
if (serialsdmode) content +="<TD>";
else content +="<TD class='btnimg' style=\"padding:0px;\"><a href=\"/SD"+jsonresponse.path+jsonresponse.files[i].name+"\" target=_blank><div class=\"blacklink\">";
content +=jsonresponse.files[i].name;
if (serialsdmode) content +="</TD>";
else content +="</div></a></TD>";
content +="<TD>";
content +=jsonresponse.files[i].size;
content +="</TD><TD width='0%'><div class=\"btnimg\" onclick=\"Delete('"+jsonresponse.files[i].name+"','line"+linenumber+"')\">";
content +=trash_icon();
content +="</div></TD><td><div class=\"btnimg\" onclick=\"if(confirm('Print "+jsonresponse.files[i].name+"?'))printfile('"+jsonresponse.files[i].name+"')\">";
content +=print_icon();
content +="</div></td><td></td></TR>";
linenumber++;
}
}
//then display directories
for (var i=0;i <jsonresponse.files.length;i++){
if (String(jsonresponse.files[i].size) == "-1")
{
content +="<TR>";
content+="<td id='line"+linenumber+"'><svg height='24' width='24' viewBox='0 0 24 24' ><path d='M19,11 L19,8 L18,7 L8,7 L8,5 L7,4 L2,4 L1,5 L1,22 L19,22 L20,21 L23,11 L5,11 L2,21 L1,22' stroke='black' fill='white' /></svg></td>";
if (serialsdmode) content +="<TD>";
else content +="<TD class='btnimg blacklink' style='padding:10px 15px;' onclick=\"select_dir('" + jsonresponse.files[i].name+"');\">";
content +=jsonresponse.files[i].name;
content +="</TD><TD></TD>";
if (serialsdmode) content +="<TD></TD>";
else {
content +="<TD width='0%'><div class=\"btnimg\" onclick=\"Deletedir('"+jsonresponse.files[i].name+"','line"+linenumber+"')\">";
content +=trash_icon();
content +="</div></TD>";
}
content +="<td></td><td></td></TR>";
linenumber++;
}
}
}
document.getElementById('file_list').innerHTML=content;
document.getElementById('path').innerHTML=navbar();}
function Delete(filename,icon){
if (confirm("Confirm deletion of file: " + filename)) {
document.getElementById(icon).innerHTML = "<div id=\"loader\" class=\"loader\"></div>";
if (serialsdmode)
{
Sendcommand("M30 " + filename);
refreshSDfiles();
}
else{
SendFileCommand("delete",filename);
}
}
}
function Deletedir(filename,icon){
if (confirm("Confirm deletion of directory: " + filename)){
document.getElementById(icon).innerHTML = "<div id=\"loader\" class=\"loader\"></div>";
SendFileCommand("deletedir",filename);
}
}
function Createdir(){
var filename = prompt("Please enter directory name", "");
if (filename != null) {
SendFileCommand("createdir",filename.trim());
}
}
function SendFileCommand(action,filename){
canrefresh = false;
var xmlhttp = new XMLHttpRequest();
var url = "/SDFILES?action="+action;
url += "&filename="+encodeURI(filename);
url += "&path="+encodeURI(currentpath);
document.getElementById('loader').style.visibility="visible";
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
var jsonresponse = JSON.parse(xmlhttp.responseText);
dispatchfilestatus(jsonresponse);document.getElementById('loader').style.visibility="hidden";
canrefresh = true;}
}
xmlhttp.open("GET", url, true);
xmlhttp.send();
}
function Sendfile(){
var files = document.getElementById('file-select').files;
if (files.length==0)return;
canrefresh = false;
document.getElementById('upload-button').value = "Uploading...";
document.getElementById('prg').style.visibility = "visible";
var formData = new FormData();
formData.append('path', currentpath);
for (var i = 0; i < files.length; i++) {
var file = files[i];
formData.append('myfiles[]', file, currentpath+file.name);}
var xmlhttp = new XMLHttpRequest();
xmlhttp.open('POST', '/SDFILES', true);
//progress upload event
xmlhttp.upload.addEventListener("progress", updateProgress, false);
//progress function
function updateProgress (oEvent) {
if (oEvent.lengthComputable) {
var percentComplete = (oEvent.loaded / oEvent.total)*100;
document.getElementById('prg').value=percentComplete;
document.getElementById('upload-button').value = "Uploading ..." + percentComplete.toFixed(0)+"%" ;
} else {
// Impossible because size is unknown
}
}
xmlhttp.onload = function () {
if (xmlhttp.status === 200) {
document.getElementById('upload-button').value = 'Upload';
document.getElementById('prg').style.visibility = "hidden";
document.getElementById('file-select').value="";
var jsonresponse = JSON.parse(xmlhttp.responseText);
dispatchfilestatus(jsonresponse);
canrefresh = true;
refreshSDfiles();
} else alert('An error occurred!');
}
xmlhttp.send(formData);
}
window.onload = function() {
refreshSDfiles();
if ($REFRESH_PAGE$){
setInterval(function(){getstatus();},$REFRESH_PAGE$);
document.getElementById('manualstatus').style.display = "none";
}
else {
document.getElementById('autostatus').style.display = "none";
}
}
function printfile(filename){
if (filename.length>0){
Sendcommand("M23 " + currentpath + filename);
delay(100);
Sendcommand("M24");}
}
</script>
$INCLUDE[footer.inc]$

View File

@ -1,27 +0,0 @@
<HTML>
<HEAD>
<title>Restarting...</title>
</HEAD>
<BODY>
<CENTER>Restarting, please wait....
<BR>
<PROGRESS name='prg' id='prg'></PROGRESS>
</CENTER>
<script>
var i = 0;
var interval;
var x = document.getElementById("prg");
x.max=40;
interval = setInterval(function(){
i=i+1;
var x = document.getElementById("prg");
x.value=i;
if (i>40)
{
clearInterval(interval);
window.location.href='/';
}
},1000);
</script>
</BODY>
</HTML>

View File

@ -1,108 +0,0 @@
$INCLUDE[header.inc]$
$INCLUDE[css2.inc]$
<div id='system' class="panel">
<div class="panel-heading">System</div>
<div class="panel-body">
<form method="POST">
<div class="form-group $BAUD_RATE_STATUS$">
<label class="control-label" for="CONFIG1" >Baud rate</label><br>
<select name="BAUD_RATE" id="CONFIG1" class="form-control">
$BAUD_RATE_OPTIONS_LIST$
</select></div>
<div class="form-group $SLEEP_MODE_STATUS$">
<label class="control-label" for="CONFIG2">Sleep Mode</label><br>
<select name="SLEEP_MODE" id="CONFIG2" class="form-control">
$SLEEP_MODE_OPTIONS_LIST$
</select></div>
<div class="form-group $WEB_PORT_STATUS$"><label class="control-label" for="CONFIG3">Web port:</label><br>
<input type="number" class="form-control" id="CONFIG3" name="WEBPORT" min="1" max="65000" step="1" placeholder="1~65000" value="$WEB_PORT$" style="width: auto;"></div>
<div id='dataport' class="form-group $DATA_PORT_STATUS$" style="$DATA_PORT_VISIBILITY$"><label class="control-label" for="CONFIG4">Data port:</label><br>
<input type="number" class="form-control" id="CONFIG4" name="DATAPORT" min="1" max="65000" step="1" placeholder="1~65000" value="$DATA_PORT$" style="width: auto;"></div>
<div class="alert alert-danger" role="alert" style="$ERROR_MSG_VISIBILITY$" >
$ERROR_MSG$
</div>
<hr><input id='btnsubmit' style="$SUBMIT_BUTTON_VISIBILITY$" type="submit" class="btn btn-primary" name="SUBMIT" value="Apply">
</form>
<div class="alert alert-success" role="alert" style="$SUCCESS_MSG_VISIBILITY$" >
$SUCCESS_MSG$
</div>
</div>
</div>
<div class="panel" style="$WEB_UPDATE_VISIBILITY$">
<div class="panel-heading">Firmware Update</div>
<div class="panel-body">
<table><tr>
<td><input type="file" id="file-select" name="myfiles[]" /></td>
<td><input class="btn btn-primary" type="button" id="upload-button" onclick="Sendfile();" value="Update"/></td>
<td><progress style="visibility:hidden;" name='prg' id='prg' max='100'></progress></td>
<td><div id='msg' style='visibility:hidden;'>Restarting, please wait....</div></td></tr></table>
</div>
</div>
<script>
function Sendfile(){
if (!confirm("Confirm Firmware Update ?"))return;
var files = document.getElementById('file-select').files;
if (files.length==0)return;
document.getElementById('upload-button').value = "Uploading...";
document.getElementById('prg').style.visibility = "visible";
var formData = new FormData();
for (var i = 0; i < files.length; i++) {
var file = files[i];
formData.append('myfiles[]', file, "/"+file.name);}
var xmlhttp = new XMLHttpRequest();
xmlhttp.open('POST', '/UPDATE', true);
//progress upload event
xmlhttp.upload.addEventListener("progress", updateProgress, false);
//progress function
function updateProgress (oEvent) {
if (oEvent.lengthComputable) {
var percentComplete = (oEvent.loaded / oEvent.total)*100;
document.getElementById('prg').value=percentComplete;
document.getElementById('upload-button').value = "Uploading ..." + percentComplete.toFixed(0)+"%" ;
} else {
// Impossible because size is unknown
}
}
xmlhttp.onload = function () {
if (xmlhttp.status === 200) {
document.getElementById('upload-button').value = 'Upload';
document.getElementById('upload-button').style.visibility = 'hidden';
document.getElementById('upload-button').style.width = '0px';
document.getElementById('system').style.visibility = 'hidden';
document.getElementById('system').style.height = '0px';
document.getElementById('dataport').style.visibility = 'hidden';
document.getElementById('dataport').style.height = '0px';
document.getElementById('msg').style.visibility = "visible";
document.getElementById('file-select').value="";
document.getElementById('file-select').style.visibility = 'hidden';
document.getElementById('file-select').style.width = '0px';
document.getElementById('btnsubmit').style.visibility = 'hidden';
var jsonresponse = JSON.parse(xmlhttp.responseText);
if (jsonresponse.status=='1' || jsonresponse.status=='4' || jsonresponse.status=='1')alert("Update failed");
if (jsonresponse.status=='2')alert('Update canceled!');
else if (jsonresponse.status=='3')
{
var i = 0;
var interval;
var x = document.getElementById("prg");
x.max=40;
interval = setInterval(function(){
i=i+1;
var x = document.getElementById("prg");
x.value=i;
if (i>40)
{
clearInterval(interval);
location.reload();
}
},1000);
}
else alert('Update failed!');
} else alert('An error occurred!');
}
xmlhttp.send(formData);
}
</script>
$INCLUDE[footer.inc]$

View File

@ -1,4 +0,0 @@
{
"WEB_ADDRESS":"localhost",
"PAGE_TITLE":"Testing things..."
}

View File

@ -23,71 +23,87 @@
Main author: luc lebosse
*/
//be sure correct IDE and settings are used for ESP8266
#ifndef ARDUINO_ARCH_ESP8266
#error Oops! Make sure you have 'ESP8266' compatible board selected from the 'Tools -> Boards' menu.
//be sure correct IDE and settings are used for ESP8266 or ESP32
#if !(defined( ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32))
#error Oops! Make sure you have 'ESP8266 or ESP32' compatible board selected from the 'Tools -> Boards' menu.
#endif
#include <EEPROM.h>
#include "config.h"
#include "wifi.h"
#include "wificonf.h"
#include "bridge.h"
#include "webinterface.h"
#include "command.h"
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#ifdef ARDUINO_ARCH_ESP8266
#include "ESP8266WiFi.h"
#ifdef MDNS_FEATURE
#include <ESP8266mDNS.h>
#endif
#include <ESP8266WebServer.h>
#else
#include <WiFi.h>
#ifdef MDNS_FEATURE
#include <ESPmDNS.h>
#endif
#include "esp_wifi.h"
#include <WebServer.h>
#include "FS.h"
#include "SPIFFS.h"
#include "Update.h"
#endif
#include <WiFiClient.h>
#ifdef CAPTIVE_PORTAL_FEATURE
#include <DNSServer.h>
const byte DNS_PORT = 53;
DNSServer dnsServer;
extern DNSServer dnsServer;
#endif
#ifdef SSDP_FEATURE
#ifdef ARDUINO_ARCH_ESP8266
#include <ESP8266SSDP.h>
#else
//#include <ESPSSDP.h>
#endif
#endif
#ifdef NETBIOS_FEATURE
#ifdef ARDUINO_ARCH_ESP8266
#include <ESP8266NetBIOS.h>
#else
//#include <ESPNetBIOS.h>
#endif
#endif
#ifndef FS_NO_GLOBALS
#define FS_NO_GLOBALS
#endif
#include <FS.h>
void setup()
{
bool breset_config=false;
long baud_rate=0;
bool directsd_check = false;
web_interface = NULL;
#ifdef TCP_IP_DATA_FEATURE
data_server = NULL;
#endif
// init:
#ifdef DEBUG_ESP3D
Serial.begin(DEFAULT_BAUD_RATE);
if (ESP_SERIAL_OUT.baudRate() != DEFAULT_BAUD_RATE)ESP_SERIAL_OUT.begin(DEFAULT_BAUD_RATE);
delay(2000);
LOG("\r\nDebug Serial set\r\n")
#endif
//WiFi.disconnect();
WiFi.mode(WIFI_OFF);
delay(8000);
CONFIG::InitDirectSD();
CONFIG::InitPins();
#ifdef RECOVERY_FEATURE
delay(8000);
//check if reset config is requested
pinMode(RESET_CONFIG_PIN, INPUT);
if (digitalRead(RESET_CONFIG_PIN)==0) {
breset_config=true; //if requested =>reset settings
}
#endif
//check if EEPROM has value
if ( CONFIG::read_buffer(EP_BAUD_RATE, (byte *)&baud_rate , INTEGER_LENGTH)&&CONFIG::read_buffer(EP_WEB_PORT, (byte *)&(wifi_config.iweb_port) , INTEGER_LENGTH)&&CONFIG::read_buffer(EP_DATA_PORT, (byte *)&(wifi_config.idata_port) , INTEGER_LENGTH)) {
//check if baud value is one of allowed ones
if ( ! (baud_rate==9600 || baud_rate==19200 ||baud_rate==38400 ||baud_rate==57600 ||baud_rate==115200 ||baud_rate==230400 ||baud_rate==250000) ) {
LOG("Error for EEPROM baud rate\r\n")
breset_config=true; //baud rate is incorrect =>reset settings
}
if (wifi_config.iweb_port<1 ||wifi_config.iweb_port>65001 || wifi_config.idata_port <1 || wifi_config.idata_port >65001) {
breset_config=true; //out of range =>reset settings
LOG("Error for EEPROM port values\r\n")
}
} else {
if ( !CONFIG::InitBaudrate() || !CONFIG::InitExternalPorts()) {
breset_config=true; //cannot access to config settings=> reset settings
LOG("Error no EEPROM access\r\n")
}
@ -95,114 +111,78 @@ void setup()
//reset is requested
if(breset_config) {
//update EEPROM with default settings
Serial.begin(DEFAULT_BAUD_RATE);
Serial.setRxBufferSize(SERIAL_RX_BUFFER_SIZE);
if (ESP_SERIAL_OUT.baudRate() != DEFAULT_BAUD_RATE)ESP_SERIAL_OUT.begin(DEFAULT_BAUD_RATE);
#ifdef ARDUINO_ARCH_ESP8266
ESP_SERIAL_OUT.setRxBufferSize(SERIAL_RX_BUFFER_SIZE);
#endif
delay(2000);
Serial.println(F("M117 ESP EEPROM reset"));
ESP_SERIAL_OUT.println(F("M117 ESP EEPROM reset"));
#ifdef DEBUG_ESP3D
CONFIG::print_config(DEBUG_PIPE);
CONFIG::print_config(DEBUG_PIPE, true);
delay(1000);
#endif
CONFIG::reset_config();
delay(1000);
//put some default value to a void some exception at first start
WiFi.mode(WIFI_AP);
#ifdef ARDUINO_ARCH_ESP8266
WiFi.setPhyMode(WIFI_PHY_MODE_11G);
#else
esp_wifi_set_protocol(ESP_IF_WIFI_AP, WIFI_PHY_MODE_11G);
#endif
CONFIG::esp_restart();
}
#if defined(DEBUG_ESP3D) && defined(DEBUG_OUTPUT_SERIAL)
LOG("\r\n");
delay(500);
Serial.flush();
ESP_SERIAL_OUT.flush();
#endif
//setup serial
Serial.begin(baud_rate);
Serial.setRxBufferSize(SERIAL_RX_BUFFER_SIZE);
delay(1000);
LOG("Serial Set\r\n");
wifi_config.baud_rate=baud_rate;
//get target FW
CONFIG::InitFirmwareTarget();
//Update is done if any so should be Ok
#ifdef ARDUINO_ARCH_ESP32
SPIFFS.begin(true);
#else
SPIFFS.begin();
#endif
//setup wifi according settings
if (!wifi_config.Setup()) {
Serial.println(F("M117 Safe mode 1"));
ESP_SERIAL_OUT.println(F("M117 Safe mode 1"));
//try again in AP mode
if (!wifi_config.Setup(true)) {
Serial.println(F("M117 Safe mode 2"));
ESP_SERIAL_OUT.println(F("M117 Safe mode 2"));
wifi_config.Safe_Setup();
}
}
delay(1000);
//start web interface
web_interface = new WEBINTERFACE_CLASS(wifi_config.iweb_port);
//here the list of headers to be recorded
const char * headerkeys[] = {"Cookie"} ;
size_t headerkeyssize = sizeof(headerkeys)/sizeof(char*);
//ask server to track these headers
web_interface->WebServer.collectHeaders(headerkeys, headerkeyssize );
#ifdef CAPTIVE_PORTAL_FEATURE
if (WiFi.getMode()!=WIFI_STA ) {
// if DNSServer is started with "*" for domain name, it will reply with
// provided IP to all DNS request
dnsServer.start(DNS_PORT, "*", WiFi.softAPIP());
//setup servers
if (!wifi_config.Enable_servers()) {
ESP_SERIAL_OUT.println(F("M117 Error enabling servers"));
}
#endif
web_interface->WebServer.begin();
#ifdef TCP_IP_DATA_FEATURE
//start TCP/IP interface
data_server = new WiFiServer (wifi_config.idata_port);
data_server->begin();
data_server->setNoDelay(true);
#endif
#ifdef MDNS_FEATURE
// Check for any mDNS queries and send responses
wifi_config.mdns.addService("http", "tcp", wifi_config.iweb_port);
#endif
#if defined(SSDP_FEATURE) || defined(NETBIOS_FEATURE)
String shost;
if (!CONFIG::read_string(EP_HOSTNAME, shost , MAX_HOSTNAME_LENGTH)) {
shost=wifi_config.get_default_hostname();
}
#endif
#ifdef SSDP_FEATURE
String stmp;
SSDP.setSchemaURL("description.xml");
SSDP.setHTTPPort( wifi_config.iweb_port);
SSDP.setName(shost.c_str());
stmp=String(ESP.getChipId());
SSDP.setSerialNumber(stmp.c_str());
SSDP.setURL("/");
SSDP.setModelName("ESP8266 01");
SSDP.setModelNumber("01");
SSDP.setModelURL("http://espressif.com/en/products/esp8266/");
SSDP.setManufacturer("Espressif Systems");
SSDP.setManufacturerURL("http://espressif.com");
SSDP.setDeviceType("upnp:rootdevice");
SSDP.begin();
#endif
#ifdef NETBIOS_FEATURE
NBNS.begin(shost.c_str());
#endif
LOG("Setup Done\r\n");
}
//main loop
void loop()
{
//be sure wifi is on to proceed wifi function
if (WiFi.getMode()!=WIFI_OFF ) {
#ifdef CAPTIVE_PORTAL_FEATURE
if (WiFi.getMode()!=WIFI_STA ) {
dnsServer.processNextRequest();
}
#endif
//web requests
web_interface->WebServer.handleClient();
web_interface->web_server.handleClient();
#ifdef TCP_IP_DATA_FEATURE
BRIDGE::processFromTCP2Serial();
#endif
}
BRIDGE::processFromSerial2TCP();
//in case of restart requested
if (web_interface->restartmodule) {
CONFIG::esp_restart();
}

Binary file not shown.

326
esp3d/nofile.h Normal file
View File

@ -0,0 +1,326 @@
/*
nofile.h - ESP3D data file
Copyright (c) 2014 Luc Lebosse. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
//data generated by https://github.com/AraHaan/bin2c
//bin2c Conversion Tool v0.14.0 - Windows - [FINAL].
#define PAGE_NOFILES_SIZE 4776
const char PAGE_NOFILES [] PROGMEM =
{
0x1F, 0x8B, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0xED, 0x5C, 0x7D, 0x93, 0xDA, 0x46,
0x93, 0xFF, 0x2A, 0xB2, 0x52, 0x36, 0x70, 0x2B, 0x58, 0x49, 0xBC, 0xA3, 0x05, 0x3F, 0x49, 0x1C,
0x5F, 0x7C, 0xE5, 0xC4, 0x2E, 0xEF, 0xFA, 0xEE, 0xAA, 0xE2, 0x94, 0x4B, 0x48, 0x03, 0xE8, 0xB1,
0x90, 0x74, 0xD2, 0xB0, 0xBB, 0x98, 0xF0, 0xDD, 0xAF, 0x7B, 0x5E, 0xA4, 0x91, 0x10, 0x2C, 0xBB,
0x49, 0x9E, 0x3C, 0x7F, 0x24, 0x78, 0x11, 0xCC, 0x4C, 0xF7, 0xF4, 0xF4, 0x74, 0xFF, 0xBA, 0xA7,
0x87, 0xCA, 0xD5, 0x8A, 0xAE, 0xC3, 0xD9, 0xD5, 0x8A, 0xB8, 0xFE, 0xEC, 0x2A, 0xA3, 0xDB, 0x90,
0xCC, 0xB0, 0x65, 0xB7, 0x88, 0x23, 0xDA, 0x5E, 0xB8, 0xEB, 0x20, 0xDC, 0x4E, 0x32, 0x37, 0xCA,
0xDA, 0x19, 0x49, 0x83, 0x85, 0xD3, 0x5E, 0x67, 0x6D, 0x4A, 0xEE, 0x69, 0x3B, 0x0B, 0xBE, 0x92,
0xB6, 0xEB, 0xFF, 0x73, 0x93, 0xD1, 0x89, 0x65, 0x9A, 0xCF, 0x9D, 0xF6, 0x1D, 0x99, 0x7F, 0x09,
0xE8, 0x91, 0x5E, 0xC6, 0x0E, 0x5B, 0xE1, 0x6B, 0x72, 0xBF, 0x9F, 0xC7, 0xFE, 0xB6, 0x34, 0x85,
0xFE, 0x23, 0x09, 0x6F, 0x09, 0x0D, 0x3C, 0x57, 0xFB, 0x99, 0x6C, 0x88, 0x6E, 0xE4, 0xDF, 0x8D,
0x6F, 0xD3, 0xC0, 0x0D, 0x0D, 0x45, 0x06, 0x85, 0x57, 0x2F, 0xB9, 0x77, 0xC2, 0x20, 0x22, 0xED,
0x15, 0x09, 0x96, 0x2B, 0x98, 0xAB, 0xD3, 0xB3, 0x47, 0xFD, 0xA1, 0xD5, 0xEB, 0x3A, 0x5E, 0x1C,
0xC6, 0xE9, 0xE4, 0x9B, 0x6E, 0xB7, 0xEB, 0xCC, 0x5D, 0xEF, 0xCB, 0x32, 0x8D, 0x37, 0x91, 0xDF,
0x16, 0xAD, 0x8B, 0xC5, 0x62, 0xDF, 0xF1, 0x80, 0x8F, 0x0B, 0xC4, 0xE9, 0x6E, 0xED, 0xA6, 0xCB,
0x20, 0x6A, 0xA7, 0x8C, 0x87, 0xBB, 0xA1, 0xB1, 0x23, 0x5A, 0x42, 0xB2, 0x10, 0x0D, 0x89, 0xEB,
0xFB, 0x41, 0xB4, 0xE4, 0x2D, 0x56, 0x1F, 0xE6, 0x95, 0x2D, 0x9C, 0x0A, 0x9B, 0xF6, 0xD4, 0x9D,
0x87, 0x64, 0x37, 0x8F, 0x53, 0x9F, 0xA4, 0x13, 0xD3, 0xE1, 0x1F, 0xDA, 0x59, 0xE2, 0x7A, 0x30,
0x10, 0x1A, 0xD6, 0xEE, 0x7D, 0xFB, 0x2E, 0xF0, 0xE9, 0x8A, 0x29, 0x65, 0xDF, 0x61, 0xE3, 0xDB,
0x7C, 0x18, 0xF1, 0x77, 0x45, 0x97, 0x20, 0x9D, 0x58, 0xC9, 0xBD, 0x96, 0xC5, 0x61, 0xE0, 0x6B,
0xDF, 0xF8, 0xBE, 0x2F, 0xA5, 0x9A, 0xC7, 0x94, 0xC6, 0xEB, 0x89, 0x8D, 0x9A, 0xA4, 0x40, 0xB6,
0x0A, 0x28, 0x61, 0xB3, 0x90, 0x49, 0x14, 0xDF, 0xA5, 0x6E, 0x22, 0x65, 0x9B, 0xD8, 0xEB, 0xF5,
0x9E, 0xAE, 0x76, 0x6C, 0x4F, 0xDC, 0x30, 0x58, 0x46, 0x13, 0x94, 0x5F, 0x4C, 0x3C, 0xA3, 0xB8,
0x0D, 0x33, 0x9A, 0xCE, 0xA8, 0x6F, 0x1C, 0x34, 0xAD, 0xF2, 0x26, 0x66, 0x1B, 0xE5, 0x51, 0x79,
0xD3, 0x6A, 0x27, 0xA7, 0x1A, 0x1D, 0xDF, 0x8A, 0x5B, 0x92, 0xE2, 0x4E, 0x86, 0x42, 0x04, 0x1A,
0x27, 0x52, 0x35, 0xF0, 0xB1, 0xB2, 0xC6, 0xAA, 0x52, 0x6A, 0x84, 0xAC, 0xEB, 0x5B, 0x1D, 0xF6,
0x1D, 0x88, 0x5D, 0xD7, 0xB7, 0xDA, 0xD5, 0x6A, 0xFA, 0x50, 0x8A, 0x47, 0x71, 0x13, 0x3B, 0x24,
0xF6, 0xDA, 0x86, 0x6D, 0x12, 0x34, 0x19, 0x4D, 0x83, 0x44, 0x11, 0x7C, 0x12, 0xD1, 0x55, 0x3B,
0x5E, 0xB4, 0xE9, 0x36, 0x21, 0xCD, 0xD8, 0xF7, 0x5B, 0xBB, 0x1A, 0x5B, 0x1D, 0xE3, 0x6B, 0xFF,
0x8F, 0x35, 0xF1, 0x03, 0x57, 0x6B, 0xAE, 0xC1, 0x00, 0x38, 0xDF, 0xE1, 0x00, 0x74, 0xDE, 0xDA,
0x29, 0x76, 0x2C, 0xDA, 0xFB, 0x68, 0x18, 0x35, 0x04, 0xE3, 0xB1, 0x5D, 0x4B, 0x30, 0x1E, 0x1E,
0x21, 0xB0, 0x6C, 0xD3, 0xAC, 0xA5, 0xB0, 0x2C, 0x4E, 0xD2, 0x89, 0xDC, 0x5B, 0xD5, 0x6C, 0x85,
0xC8, 0x9E, 0xE7, 0x55, 0x1C, 0xC6, 0xAC, 0xBA, 0x8B, 0x09, 0xC6, 0x92, 0x81, 0x1B, 0x23, 0xE2,
0x80, 0xD5, 0x46, 0xA4, 0xC6, 0x4B, 0x99, 0xEF, 0x72, 0x85, 0xA6, 0xAE, 0x1F, 0x6C, 0xB2, 0xC9,
0x00, 0x8C, 0xAC, 0xC6, 0x09, 0xDC, 0x5D, 0x12, 0x67, 0x01, 0x0D, 0xE2, 0x68, 0x92, 0x92, 0xD0,
0xA5, 0xC1, 0x2D, 0x71, 0xFC, 0x20, 0x4B, 0x42, 0x77, 0x3B, 0x99, 0x87, 0xB1, 0xF7, 0x25, 0x77,
0x08, 0x44, 0x1F, 0x8D, 0xB9, 0x2F, 0xF3, 0x09, 0x9F, 0x78, 0x71, 0xEA, 0x32, 0x42, 0x26, 0x43,
0x21, 0xFF, 0xBE, 0xE3, 0x7A, 0xC8, 0x67, 0x57, 0x20, 0x46, 0x8D, 0x84, 0xA6, 0x69, 0xCA, 0x81,
0x9A, 0x6B, 0xB8, 0x93, 0x45, 0xEC, 0x6D, 0x32, 0x78, 0xAE, 0x62, 0xB0, 0xF9, 0x9D, 0x0A, 0x36,
0x89, 0x1B, 0x91, 0x70, 0x77, 0x28, 0x7B, 0x3D, 0x38, 0x1D, 0xF1, 0xFF, 0xB2, 0x32, 0x10, 0xFC,
0x24, 0xEA, 0xCE, 0xE3, 0xFB, 0x76, 0xB6, 0x72, 0xFD, 0xF8, 0x6E, 0x62, 0x6A, 0x48, 0x85, 0x7F,
0xE9, 0x72, 0xEE, 0x36, 0x4D, 0x03, 0x5F, 0x1D, 0xB3, 0xDF, 0x72, 0xCE, 0x19, 0x24, 0x24, 0x6D,
0x33, 0x84, 0xCE, 0xB5, 0x86, 0xE0, 0x26, 0x3A, 0xD0, 0xD8, 0xA1, 0x6D, 0x77, 0xA8, 0xD1, 0xD3,
0x88, 0xDB, 0xC7, 0x97, 0x5C, 0x81, 0x68, 0x54, 0xD6, 0x04, 0x48, 0xC0, 0x4D, 0x43, 0xAE, 0xAE,
0x8B, 0xBA, 0x29, 0xFA, 0xD0, 0x8C, 0x6A, 0xBA, 0x84, 0x26, 0x2B, 0xDE, 0x1B, 0xBA, 0x73, 0x50,
0xB6, 0xB4, 0x80, 0x20, 0x62, 0xB8, 0xC4, 0x0D, 0xA1, 0x0C, 0xC1, 0x15, 0x63, 0xC2, 0x55, 0xB0,
0xE8, 0x72, 0xC7, 0x31, 0x6C, 0x88, 0xDB, 0xCB, 0x0C, 0x25, 0x88, 0x16, 0xB1, 0xDC, 0xCF, 0x2E,
0x18, 0xFF, 0x08, 0xB6, 0x74, 0x11, 0xA7, 0xEB, 0x36, 0x7A, 0x46, 0x1A, 0x17, 0x93, 0xF1, 0x59,
0xF8, 0x0C, 0x2C, 0x70, 0x08, 0x38, 0xEC, 0xF6, 0x8A, 0x90, 0x81, 0x66, 0xAC, 0x59, 0xB6, 0x9C,
0xEC, 0xDC, 0x50, 0xD6, 0xEF, 0xF7, 0x8F, 0x59, 0x4B, 0xD1, 0x1A, 0xAC, 0xDD, 0xA5, 0x74, 0xA8,
0x03, 0x1B, 0x42, 0xBF, 0x3C, 0xCB, 0x86, 0x82, 0x28, 0x23, 0x54, 0x3B, 0x62, 0x24, 0xC3, 0xB2,
0x29, 0x3D, 0x38, 0xB6, 0x1D, 0xB7, 0x69, 0x0A, 0xE1, 0x9B, 0x3B, 0xA8, 0x6A, 0x01, 0x1A, 0x71,
0x33, 0x02, 0xBA, 0x6D, 0xC7, 0x1B, 0xAA, 0x75, 0xAC, 0x7E, 0x66, 0x14, 0x7C, 0x0F, 0xFA, 0xCA,
0x0A, 0xE7, 0xAE, 0xB6, 0x2B, 0xDB, 0xD3, 0x60, 0xE0, 0x2E, 0xC8, 0xD8, 0x01, 0x0A, 0xD4, 0x24,
0x04, 0xDC, 0x27, 0x2C, 0xCD, 0x30, 0xA1, 0x73, 0x24, 0x3B, 0x2C, 0xD3, 0x36, 0xAC, 0x61, 0xDF,
0xB0, 0xBB, 0x5D, 0xA3, 0x33, 0x68, 0x09, 0x19, 0x50, 0xD7, 0x49, 0xC5, 0x99, 0xB9, 0x8F, 0xCC,
0x69, 0x74, 0xCC, 0xEE, 0xD4, 0xC1, 0x66, 0xC9, 0xCC, 0x7A, 0xA6, 0xE9, 0x28, 0x21, 0xDA, 0x23,
0x11, 0x25, 0x69, 0x35, 0x6A, 0xAE, 0x03, 0xDF, 0x0F, 0x09, 0x4F, 0xC0, 0xE2, 0x8D, 0xB7, 0x6A,
0x23, 0xEC, 0x80, 0x3E, 0xD7, 0x6E, 0x14, 0x24, 0x9B, 0x90, 0x81, 0x98, 0x73, 0xBC, 0xC7, 0xDB,
0xA4, 0x19, 0xA8, 0x28, 0x89, 0x03, 0xC6, 0xFC, 0x4C, 0x8B, 0x61, 0xFB, 0x96, 0xB8, 0x29, 0x48,
0xE4, 0x9C, 0x48, 0x33, 0x1E, 0x69, 0xCF, 0x35, 0x26, 0xB8, 0x8E, 0xBF, 0xB6, 0x37, 0x19, 0x26,
0x4B, 0x24, 0x24, 0x1E, 0xE5, 0xE2, 0xE0, 0x5A, 0x0F, 0x1A, 0xAB, 0x0D, 0x4C, 0xE7, 0xED, 0x24,
0x85, 0x65, 0xA4, 0xDB, 0xD3, 0x68, 0xDD, 0xED, 0x0E, 0xDD, 0xF9, 0xB0, 0x82, 0x41, 0x36, 0x19,
0xF8, 0x6E, 0xAF, 0xC4, 0x45, 0x20, 0xBA, 0x51, 0x6A, 0xE3, 0xD0, 0x5E, 0x6A, 0x62, 0x28, 0x5F,
0x6A, 0x9A, 0xD4, 0x50, 0x4E, 0x0E, 0x29, 0x0F, 0xE2, 0x43, 0x8D, 0xB0, 0xF6, 0x68, 0x60, 0x8E,
0xCD, 0x8A, 0xB0, 0x96, 0x6D, 0xCF, 0x7B, 0xE6, 0xDE, 0x73, 0x13, 0xDC, 0x54, 0x89, 0xC1, 0x2C,
0x8D, 0x1A, 0x29, 0x29, 0xA9, 0xB0, 0xB2, 0x51, 0x01, 0xCA, 0xC3, 0xE1, 0xD0, 0x39, 0xC8, 0x02,
0xDD, 0x10, 0x4C, 0xAC, 0x04, 0xF2, 0x35, 0xC1, 0xF5, 0xB4, 0x51, 0x1C, 0x6C, 0xA5, 0xE0, 0xDA,
0xCE, 0x36, 0x9E, 0x47, 0xB2, 0xAC, 0x26, 0x9F, 0xF1, 0x17, 0x0B, 0xD3, 0x1F, 0x55, 0x23, 0xC1,
0x80, 0x8C, 0xBD, 0x41, 0x1E, 0x42, 0xBC, 0xE1, 0xA0, 0xEB, 0x4B, 0x56, 0xBE, 0x1B, 0x2D, 0x41,
0x5B, 0x35, 0xD0, 0x67, 0xFB, 0xC4, 0x27, 0x15, 0x4E, 0x64, 0xEE, 0x79, 0xBE, 0x25, 0x39, 0xB9,
0xE3, 0x5E, 0xAF, 0x67, 0xEF, 0x3B, 0x2B, 0x37, 0x6B, 0x93, 0x34, 0x05, 0xC8, 0x29, 0xC3, 0x76,
0x99, 0x96, 0x8F, 0xFE, 0xB3, 0x01, 0xF1, 0xA8, 0x34, 0xB5, 0x98, 0x36, 0xEA, 0x75, 0xFB, 0xDD,
0xDE, 0x93, 0x91, 0x0C, 0x5D, 0xF3, 0x1B, 0x8F, 0x8C, 0x7A, 0xA3, 0xEE, 0x63, 0x64, 0xAC, 0xD2,
0x96, 0x64, 0x16, 0xE2, 0xB6, 0x79, 0x98, 0xAD, 0xD1, 0xB4, 0xD8, 0xFC, 0x93, 0xBA, 0xE6, 0x7B,
0xFC, 0xAF, 0xD1, 0x75, 0xAD, 0x3C, 0xB5, 0xDA, 0xB6, 0xE7, 0xFD, 0x9E, 0xED, 0xFD, 0x3E, 0x6D,
0x0F, 0x86, 0x73, 0x6B, 0x30, 0x7A, 0x9A, 0xB6, 0x39, 0x6D, 0x45, 0xEA, 0x5A, 0x7D, 0x4B, 0x1F,
0x41, 0x58, 0x11, 0x1E, 0x72, 0x12, 0x4F, 0xFC, 0x31, 0x98, 0xD1, 0xA2, 0xEA, 0x76, 0xBD, 0xEE,
0xA2, 0xEB, 0xAA, 0x4C, 0x4A, 0xD8, 0x27, 0x9A, 0x14, 0x00, 0x13, 0x2D, 0x0A, 0xF2, 0xF1, 0x96,
0xC9, 0x21, 0xD9, 0xE4, 0x80, 0xEC, 0x1C, 0xD8, 0xF3, 0xC6, 0x5D, 0xD3, 0xF6, 0x2A, 0x62, 0x0E,
0x07, 0x96, 0x67, 0x8D, 0x99, 0x98, 0xC1, 0x7A, 0xB9, 0x13, 0xB1, 0x6C, 0xE5, 0x46, 0xD5, 0x94,
0x78, 0x50, 0x87, 0x57, 0x3C, 0x01, 0xE7, 0xB4, 0x42, 0x84, 0x1A, 0x2C, 0x31, 0xF1, 0x55, 0x99,
0xD7, 0x04, 0x11, 0xFF, 0x72, 0xC7, 0x03, 0xC1, 0x99, 0xA4, 0xA7, 0x57, 0xDE, 0x35, 0x45, 0xFA,
0x21, 0xC7, 0x3E, 0xB4, 0xD2, 0xBF, 0x7E, 0x5D, 0x21, 0x88, 0x06, 0x19, 0xC2, 0x17, 0x69, 0x10,
0xEC, 0x30, 0x95, 0xB7, 0x4E, 0x84, 0x8D, 0x2D, 0x82, 0x90, 0xB0, 0xEF, 0xDC, 0x5D, 0xF3, 0xB1,
0xE3, 0x1E, 0xEC, 0x6A, 0x10, 0x25, 0x1B, 0xFA, 0x0B, 0x9E, 0x9E, 0xA7, 0x38, 0xEE, 0xD7, 0xC9,
0x44, 0x2E, 0x0B, 0xBF, 0xB6, 0x37, 0x49, 0x18, 0xBB, 0x7E, 0x7B, 0xBE, 0x81, 0x68, 0xF6, 0x77,
0x5E, 0xF6, 0xAF, 0xCD, 0xCB, 0x9C, 0x93, 0x6E, 0xDE, 0x9F, 0x7B, 0xE6, 0x41, 0xE8, 0xEE, 0x0D,
0xE6, 0x23, 0xDF, 0x7D, 0xD4, 0xA6, 0x0A, 0xAB, 0xF8, 0x7B, 0x6B, 0xFF, 0x7D, 0xB6, 0xB6, 0x6B,
0xCD, 0x4D, 0xBF, 0x7A, 0xD2, 0xB7, 0xE6, 0x03, 0x7F, 0xD4, 0x7F, 0xDC, 0xD6, 0x72, 0x00, 0xFB,
0x7B, 0x6B, 0xFF, 0xCD, 0xB7, 0xD6, 0x1E, 0x8C, 0xDD, 0xB9, 0xB7, 0xCF, 0x81, 0xBA, 0x04, 0xE7,
0x65, 0xF4, 0x56, 0xD0, 0xBC, 0x94, 0x0A, 0x08, 0x34, 0x17, 0x15, 0xA6, 0x45, 0x1C, 0x83, 0x52,
0x4F, 0x14, 0x98, 0x58, 0xFD, 0xE5, 0x69, 0x35, 0xA6, 0x83, 0x3A, 0x2F, 0x1A, 0x1C, 0x86, 0x49,
0xBE, 0x57, 0x3D, 0x25, 0x69, 0xE8, 0xE2, 0x4B, 0x25, 0x55, 0x3A, 0xBB, 0xBD, 0xF1, 0xC8, 0x9F,
0x57, 0x54, 0xDF, 0x37, 0x9F, 0x3B, 0xB2, 0x6E, 0x0A, 0xD2, 0xCA, 0x9D, 0xC2, 0xCF, 0x60, 0x3B,
0x6B, 0x5E, 0x66, 0xCC, 0x92, 0x20, 0xD2, 0xEC, 0x4C, 0xC3, 0xCD, 0x74, 0x53, 0x2D, 0x88, 0x16,
0x41, 0x04, 0x96, 0xB0, 0xFF, 0xC7, 0x17, 0xB2, 0x5D, 0xA4, 0xEE, 0x9A, 0x64, 0x1A, 0x0E, 0xD9,
0x99, 0xCF, 0x77, 0xCC, 0x5C, 0x30, 0x63, 0x9D, 0xA4, 0x31, 0x75, 0x29, 0x69, 0x9A, 0xAD, 0x3D,
0x16, 0xAD, 0x0E, 0x3B, 0xBA, 0x03, 0x00, 0xD3, 0x65, 0x6B, 0xFF, 0x97, 0x68, 0x70, 0x1D, 0xFB,
0x6E, 0x51, 0xFF, 0x62, 0x46, 0x94, 0x57, 0x63, 0x17, 0xC1, 0x3D, 0xF1, 0x9D, 0xAF, 0xED, 0x20,
0xF2, 0xC9, 0x3D, 0x56, 0xDC, 0xCC, 0xA2, 0x10, 0xCC, 0x78, 0x61, 0x7D, 0xD9, 0x61, 0x25, 0x62,
0x70, 0x5A, 0x68, 0x30, 0x1D, 0xA5, 0x38, 0x27, 0x35, 0x88, 0x9F, 0xD1, 0x5C, 0x16, 0x21, 0x24,
0x1A, 0xAC, 0xA8, 0x56, 0x5B, 0x89, 0x3D, 0x6C, 0x55, 0x93, 0x90, 0x5E, 0x4B, 0x88, 0xCA, 0xF2,
0x7F, 0x70, 0xC1, 0x5D, 0xB1, 0xA6, 0x52, 0x75, 0xD1, 0x32, 0xCB, 0x95, 0xC7, 0x52, 0x55, 0x52,
0xED, 0x14, 0x45, 0xFE, 0x63, 0xB4, 0xA2, 0xFB, 0x18, 0x39, 0x5E, 0x0B, 0xE4, 0xE6, 0x24, 0x0B,
0x13, 0x4A, 0x7D, 0x16, 0x4B, 0x50, 0x16, 0x42, 0x81, 0x59, 0xCA, 0xA5, 0xEC, 0x96, 0x73, 0x58,
0xEB, 0xE6, 0x70, 0x58, 0xBA, 0xA8, 0x9A, 0xD4, 0xA8, 0xE3, 0x9B, 0x05, 0xC1, 0x97, 0xD4, 0x03,
0x56, 0x72, 0x15, 0x2B, 0xB1, 0xC5, 0x84, 0x4E, 0x9E, 0xFC, 0xE2, 0xAB, 0x8E, 0x8B, 0x8D, 0xAF,
0x63, 0xC5, 0xD9, 0x47, 0xAA, 0xAF, 0x54, 0x9E, 0x5C, 0xE0, 0x4B, 0x8A, 0x57, 0xAE, 0x40, 0x9B,
0x42, 0x3A, 0xD9, 0x5B, 0x35, 0xF1, 0x81, 0x94, 0x5E, 0x18, 0x4D, 0xAF, 0xD3, 0x27, 0xEB, 0xC7,
0x2F, 0xE5, 0x50, 0x9C, 0xDF, 0xB9, 0xDB, 0x27, 0xEE, 0x6D, 0xCA, 0xD6, 0xC8, 0xFB, 0xFA, 0x23,
0xF5, 0x2A, 0x26, 0xF3, 0x52, 0x42, 0x22, 0x0D, 0xB2, 0x7D, 0xA0, 0xCF, 0x0B, 0xD7, 0xC3, 0xC1,
0xF0, 0x28, 0x3D, 0xBB, 0x57, 0xDC, 0x5F, 0x5D, 0xF2, 0x9B, 0xDC, 0xAB, 0x4B, 0x7E, 0xAF, 0xCB,
0x6E, 0x9B, 0xAE, 0xFC, 0xE0, 0x56, 0x63, 0xED, 0x53, 0x3D, 0x37, 0x21, 0x77, 0x0E, 0x8B, 0xDD,
0x50, 0x22, 0x9C, 0x8F, 0x5F, 0xCE, 0x98, 0xFA, 0xEC, 0xBF, 0xAD, 0x8E, 0xA5, 0xBD, 0x88, 0xE6,
0x59, 0xE2, 0xF0, 0xF7, 0xAB, 0x4B, 0x20, 0x9F, 0x5D, 0xF1, 0x68, 0x3A, 0xBB, 0x5A, 0xD9, 0xB3,
0x37, 0x54, 0xCB, 0x08, 0x59, 0x67, 0xDA, 0x36, 0xDE, 0x68, 0x7E, 0xAC, 0x45, 0x31, 0xD5, 0x56,
0x2E, 0x5E, 0x84, 0x44, 0x5B, 0x8D, 0x39, 0x7C, 0x07, 0x6F, 0x92, 0xB5, 0x88, 0x04, 0x74, 0x45,
0x52, 0xA5, 0xA9, 0xB3, 0xFC, 0x6A, 0x68, 0x49, 0x88, 0x05, 0x5E, 0x8D, 0x87, 0x7C, 0x2D, 0xA0,
0x5A, 0x9C, 0xC2, 0x17, 0x1F, 0xE0, 0x0C, 0x19, 0xA6, 0xDA, 0x22, 0x48, 0xD7, 0x77, 0x10, 0x2B,
0xB5, 0x60, 0x01, 0x2C, 0xF0, 0x20, 0x8C, 0x25, 0x37, 0x58, 0x91, 0x0D, 0xCB, 0x92, 0x62, 0xE0,
0x92, 0xBC, 0xD0, 0xCD, 0x32, 0x58, 0x12, 0x22, 0x9F, 0xAE, 0x05, 0xFE, 0x54, 0xBF, 0x7E, 0xFF,
0xE6, 0xF5, 0xEB, 0x6B, 0xFD, 0xB0, 0x5B, 0x5E, 0x5E, 0xE8, 0xB3, 0xD7, 0xD0, 0xBA, 0xD2, 0x5E,
0x43, 0x34, 0xCA, 0xB6, 0x19, 0x25, 0x6B, 0xB1, 0xBC, 0x03, 0x02, 0xD4, 0x1C, 0x30, 0x62, 0x79,
0x8B, 0xC6, 0xF2, 0x16, 0x1D, 0x43, 0x18, 0x9F, 0x87, 0xE5, 0x2C, 0x3C, 0x78, 0xEA, 0x5A, 0x04,
0xD8, 0x3D, 0xD5, 0xD7, 0x5B, 0x6C, 0xCC, 0x7E, 0xF9, 0x55, 0xD7, 0xD6, 0x9B, 0x90, 0x06, 0x09,
0x6A, 0x5B, 0x7E, 0xD2, 0x67, 0x9A, 0xE0, 0x24, 0xE6, 0x80, 0xE3, 0x9A, 0xA6, 0x94, 0x05, 0x75,
0x31, 0x03, 0xCF, 0x7F, 0xF8, 0x1C, 0xA5, 0x94, 0x48, 0xD7, 0xE2, 0xC8, 0x0B, 0x03, 0xEF, 0x0B,
0xAC, 0x91, 0x44, 0x3E, 0x4E, 0xD5, 0x6C, 0x39, 0xBA, 0x76, 0xEB, 0x86, 0x1B, 0xA0, 0xFB, 0xC8,
0xC6, 0xEA, 0xB3, 0xD2, 0xBE, 0x25, 0x69, 0xBC, 0x4C, 0xB1, 0x8C, 0x20, 0xB6, 0xFE, 0x36, 0xC8,
0x82, 0x79, 0x10, 0x06, 0x74, 0x3B, 0x59, 0x41, 0x12, 0x44, 0x22, 0x29, 0x7A, 0x92, 0x2E, 0xF9,
0x94, 0xEC, 0x03, 0x98, 0xDB, 0x54, 0x07, 0x6B, 0x82, 0xC5, 0x5F, 0x4A, 0x16, 0x60, 0x48, 0x29,
0xFF, 0x3B, 0xD0, 0xFB, 0x71, 0xD5, 0xF1, 0x1B, 0xE3, 0x2B, 0x0A, 0x54, 0xD4, 0xD7, 0x98, 0x95,
0x4E, 0x75, 0xF3, 0x79, 0xAE, 0xD4, 0xF3, 0x54, 0x51, 0x5A, 0xF7, 0xF7, 0xF1, 0x1A, 0xB2, 0x31,
0xBF, 0xD9, 0xC0, 0x2B, 0xC4, 0x86, 0xD1, 0x70, 0xC3, 0xB0, 0xA1, 0xA8, 0xE1, 0x03, 0x59, 0x80,
0xB4, 0x2B, 0x94, 0x9C, 0xFA, 0x07, 0xB3, 0xA2, 0x9C, 0x39, 0xB7, 0xEF, 0x53, 0x02, 0x06, 0xE7,
0x07, 0x69, 0xB3, 0xA5, 0x2B, 0x92, 0xC0, 0xF1, 0x19, 0x46, 0x66, 0xB7, 0x4B, 0x49, 0xD9, 0x33,
0x75, 0x8D, 0x03, 0x0B, 0xFF, 0x7C, 0x1B, 0x90, 0xBB, 0xEF, 0x62, 0xD0, 0x10, 0x9E, 0x6A, 0x7B,
0xF8, 0x0F, 0xC6, 0xA7, 0x60, 0x07, 0x1A, 0xB4, 0xF5, 0x75, 0x6D, 0x8B, 0xBA, 0xD3, 0x25, 0x75,
0x57, 0xA1, 0xB6, 0xE1, 0x73, 0x0A, 0x83, 0x6C, 0x78, 0x6C, 0xD9, 0x03, 0x76, 0x31, 0x9C, 0xEA,
0x22, 0xB7, 0xD2, 0x2F, 0x0B, 0x3E, 0x38, 0x74, 0xCB, 0xD8, 0x09, 0x3E, 0x56, 0xBF, 0xE0, 0x83,
0x9F, 0x1F, 0xE0, 0x83, 0x49, 0x30, 0xF2, 0xB1, 0xB8, 0x40, 0x36, 0x3C, 0xF2, 0x8C, 0x12, 0x5A,
0x47, 0xE2, 0xEB, 0x9D, 0xE0, 0x38, 0x82, 0xCD, 0x16, 0x4C, 0x58, 0x72, 0xAA, 0xCF, 0x2E, 0x40,
0x81, 0xC0, 0x03, 0xF4, 0x08, 0xAA, 0x98, 0x09, 0x17, 0x11, 0x3A, 0xE5, 0x8A, 0x44, 0x73, 0xE1,
0x19, 0x54, 0xAE, 0x3E, 0xF1, 0xB5, 0x32, 0x3C, 0x5F, 0x82, 0x99, 0x6F, 0x02, 0x33, 0x35, 0x97,
0xAE, 0x72, 0x4A, 0xBC, 0x63, 0x93, 0xC6, 0xAB, 0x52, 0x5F, 0xA2, 0xED, 0x5C, 0x4A, 0x3B, 0xC2,
0x87, 0xA4, 0xE0, 0x5F, 0x4A, 0x57, 0xEB, 0xBA, 0xB4, 0xF3, 0x22, 0xFB, 0x46, 0x23, 0xE4, 0x38,
0xC8, 0x8C, 0x70, 0xA5, 0x9A, 0xC3, 0x0D, 0x58, 0x19, 0xF0, 0x5E, 0x61, 0xFB, 0xEC, 0x67, 0xF0,
0x83, 0xFC, 0xCB, 0x35, 0x68, 0x49, 0x7E, 0x29, 0x19, 0x50, 0xA5, 0x4D, 0xAC, 0x88, 0xB5, 0x0A,
0x49, 0xC5, 0x64, 0xE8, 0x00, 0x39, 0x4E, 0x7C, 0x46, 0x5B, 0x65, 0xE3, 0x38, 0x18, 0xCB, 0xF5,
0x1C, 0xC1, 0x1D, 0x1E, 0xDE, 0xB8, 0x3F, 0x66, 0x90, 0xE3, 0x6D, 0xB2, 0x42, 0xA3, 0x07, 0xEF,
0xE7, 0x78, 0x63, 0x81, 0x7C, 0x12, 0x57, 0x3F, 0x32, 0xB0, 0x7D, 0x00, 0xF8, 0x4A, 0xDE, 0x7B,
0x14, 0x07, 0xEF, 0x8E, 0xA2, 0xA0, 0x62, 0x2F, 0x4F, 0x43, 0x3E, 0xE0, 0x7D, 0x80, 0x01, 0x1C,
0xE7, 0x0E, 0xD1, 0x0F, 0xD7, 0xA3, 0xCE, 0xF8, 0x18, 0xE8, 0x5B, 0xDC, 0xE5, 0xE0, 0x87, 0x1F,
0xEB, 0xE1, 0x2F, 0xE7, 0x0C, 0xE7, 0xB6, 0x88, 0x0D, 0x5F, 0x67, 0x4B, 0xFD, 0x38, 0xFB, 0xD9,
0x07, 0x02, 0x9B, 0x07, 0x07, 0xCF, 0x68, 0x99, 0x07, 0xBC, 0x3B, 0x37, 0xA0, 0x1D, 0xF8, 0x0F,
0x9C, 0x0A, 0x98, 0x28, 0xAC, 0x3C, 0x48, 0x4C, 0x28, 0xF7, 0x1C, 0xDE, 0x73, 0x68, 0xFC, 0xD5,
0x4D, 0xE7, 0xEE, 0x07, 0xA9, 0x5F, 0x02, 0x87, 0xCE, 0xDC, 0x8F, 0x58, 0x5E, 0x50, 0xB6, 0x81,
0x52, 0xAA, 0x50, 0xD7, 0xC5, 0x73, 0x41, 0xE8, 0x59, 0x75, 0x67, 0x6F, 0x40, 0x74, 0x1A, 0x2C,
0xE0, 0xB4, 0x8C, 0x29, 0x02, 0x44, 0xDC, 0x6E, 0x8D, 0xA1, 0x15, 0x39, 0x9A, 0xCE, 0xD7, 0x20,
0x56, 0x52, 0xEA, 0x46, 0xF4, 0xD0, 0xF9, 0xB1, 0x17, 0x32, 0x55, 0x32, 0xFB, 0x08, 0xA7, 0xCD,
0x89, 0x58, 0x5E, 0x25, 0x14, 0xAA, 0x95, 0x79, 0x69, 0x0D, 0x9C, 0x3C, 0x5F, 0xE4, 0x67, 0x3C,
0xAC, 0x7E, 0xE6, 0x8D, 0x42, 0xE5, 0xC5, 0x3D, 0x7B, 0xA1, 0xB6, 0x55, 0x7A, 0xBE, 0x40, 0xEF,
0xA1, 0xEF, 0x0E, 0x80, 0xE2, 0x11, 0x42, 0x25, 0x82, 0x44, 0x15, 0x4C, 0xB6, 0x3D, 0x2C, 0x1C,
0x06, 0xCF, 0x23, 0xBA, 0x14, 0x2E, 0x5F, 0x76, 0x32, 0x69, 0xFB, 0xC7, 0xDC, 0xA6, 0x88, 0x8B,
0x9B, 0xF9, 0x3A, 0xA0, 0x1F, 0xC8, 0xFF, 0x6D, 0xC0, 0xE4, 0x30, 0x9A, 0x09, 0xAF, 0xE0, 0xED,
0xB5, 0xE0, 0x01, 0xD9, 0x65, 0x90, 0xD0, 0xD9, 0x62, 0x13, 0xB1, 0x0A, 0x07, 0xF8, 0xC2, 0xED,
0xDC, 0x85, 0x48, 0xB8, 0xBB, 0x85, 0x83, 0x29, 0x90, 0x2A, 0xCE, 0xAF, 0x1B, 0x74, 0xEA, 0x6D,
0x52, 0xAC, 0x5C, 0x20, 0x64, 0x77, 0xE0, 0xAC, 0x17, 0xD0, 0xA6, 0x7E, 0xA9, 0xB7, 0x8C, 0x68,
0x0A, 0x0F, 0x23, 0x98, 0x5A, 0x0E, 0x68, 0xAB, 0x49, 0x2E, 0x90, 0xCE, 0x17, 0xF2, 0x36, 0x78,
0x2C, 0x6D, 0x68, 0xB9, 0x9C, 0x9F, 0x74, 0x85, 0xCD, 0xB4, 0x71, 0xD9, 0x70, 0xB4, 0xE3, 0x11,
0xFD, 0x93, 0x3E, 0xBB, 0x64, 0x5E, 0xA0, 0x3B, 0xC1, 0x15, 0xED, 0x84, 0x24, 0x5A, 0xD2, 0x55,
0xDB, 0x72, 0x5A, 0xD1, 0xC5, 0x94, 0xFE, 0x12, 0xFC, 0x7A, 0x81, 0x33, 0x1F, 0x99, 0xF1, 0xC8,
0x84, 0xFA, 0x45, 0x74, 0xA1, 0x3F, 0x34, 0xA9, 0x7E, 0xC1, 0xB9, 0xE7, 0xFE, 0x2E, 0xA4, 0x30,
0x82, 0x8B, 0x0B, 0x27, 0x25, 0x74, 0x93, 0x46, 0x1A, 0x9B, 0x56, 0x75, 0x4E, 0x7D, 0x9F, 0x2B,
0x12, 0xEC, 0x2B, 0x5B, 0x7D, 0x0E, 0xC0, 0x70, 0x14, 0x65, 0x16, 0xD9, 0x44, 0xC3, 0xEE, 0x35,
0x64, 0x1C, 0x67, 0x9F, 0x65, 0x36, 0xD1, 0xC0, 0x6C, 0xC2, 0xB2, 0x47, 0xF8, 0xD7, 0x80, 0x45,
0xAB, 0x53, 0x89, 0xA4, 0xA0, 0xD1, 0xB7, 0x1B, 0x10, 0xCC, 0x1B, 0x16, 0x3C, 0x20, 0xFC, 0x37,
0x06, 0x0D, 0x0C, 0xFF, 0xF8, 0x90, 0xBC, 0xFB, 0x05, 0xEF, 0x61, 0x43, 0x98, 0x62, 0x03, 0xC3,
0x3A, 0x1C, 0x17, 0x7D, 0xA7, 0xA1, 0x5D, 0xCE, 0x84, 0xCE, 0xAA, 0x1C, 0xEB, 0x79, 0xD8, 0x65,
0x1E, 0x2C, 0x33, 0xA8, 0xE3, 0xD2, 0x35, 0x39, 0x97, 0xD1, 0x11, 0xB9, 0x06, 0xC3, 0x82, 0x27,
0xE0, 0xEA, 0x59, 0x92, 0xD9, 0x65, 0x9E, 0x96, 0xC9, 0x99, 0xE2, 0x53, 0x70, 0x1D, 0xA9, 0x5C,
0x7B, 0x8F, 0x61, 0x6A, 0x8F, 0x6B, 0x99, 0x74, 0xCF, 0x5C, 0x6E, 0x8F, 0x73, 0xE9, 0x75, 0xB9,
0x68, 0x43, 0x2E, 0xD9, 0x30, 0xE7, 0xA9, 0xB0, 0x1C, 0x9C, 0xCB, 0x73, 0xF0, 0x27, 0xF0, 0x1C,
0xFD, 0x11, 0x3C, 0x79, 0xFE, 0xA7, 0x18, 0x38, 0x1E, 0xA4, 0xA5, 0x7D, 0x73, 0x23, 0x3D, 0xDF,
0xBE, 0xED, 0x1E, 0xFC, 0x6B, 0x40, 0x70, 0x06, 0x7F, 0xD4, 0xFC, 0x69, 0xE3, 0xA7, 0xA1, 0xD1,
0xD5, 0xDE, 0xDA, 0xC6, 0x48, 0x7B, 0x3B, 0x34, 0xAC, 0x2E, 0x7B, 0x37, 0xB5, 0xB7, 0x96, 0x78,
0x8C, 0x0C, 0xCB, 0xE2, 0x8F, 0x3E, 0x6F, 0x1C, 0xC0, 0xC3, 0x64, 0x8F, 0xB1, 0x61, 0x0D, 0xD9,
0xFB, 0x98, 0x35, 0xD9, 0x30, 0xDC, 0x16, 0x0F, 0xDB, 0xB0, 0x46, 0xEC, 0x31, 0x62, 0x6D, 0x03,
0xE4, 0x3A, 0xD0, 0xBE, 0xE2, 0x02, 0xD3, 0xF8, 0x0B, 0xAC, 0x90, 0x5D, 0x33, 0x35, 0x78, 0xBA,
0xDB, 0x60, 0x2B, 0xAD, 0x5D, 0x28, 0x4F, 0x6B, 0x3E, 0xE3, 0x01, 0x81, 0xB4, 0x76, 0x0A, 0x92,
0x5C, 0x4C, 0x09, 0x43, 0x1F, 0x15, 0x47, 0x74, 0x96, 0xE2, 0x19, 0x3A, 0xE0, 0x88, 0xDE, 0x2A,
0x78, 0x78, 0xF1, 0x1A, 0x6B, 0xBE, 0xD7, 0x90, 0x9F, 0x46, 0xCB, 0xAC, 0x49, 0x0C, 0x2A, 0x95,
0x06, 0xB8, 0x40, 0x3A, 0x34, 0x7E, 0x1B, 0xDF, 0x91, 0xF4, 0x7B, 0xC8, 0x0D, 0x9A, 0x2D, 0x80,
0x59, 0x5A, 0x69, 0x21, 0x57, 0xF4, 0x65, 0xDB, 0x9A, 0x90, 0x19, 0x7D, 0x69, 0x4D, 0xCC, 0x82,
0x2D, 0x16, 0xDC, 0x5C, 0xEA, 0xAD, 0x58, 0xA6, 0xC5, 0x32, 0x44, 0x14, 0x11, 0xD1, 0x06, 0x72,
0x79, 0x00, 0xCC, 0x45, 0x13, 0x9E, 0xEA, 0x49, 0xF0, 0x9A, 0x0D, 0x9A, 0x68, 0xFA, 0x05, 0xE9,
0x70, 0x02, 0x83, 0x5E, 0x94, 0x87, 0xFC, 0xA6, 0x7E, 0xB9, 0x89, 0xA9, 0x1B, 0x6A, 0xBC, 0x42,
0xCD, 0x88, 0x28, 0x36, 0x9C, 0xA6, 0x81, 0x00, 0xEF, 0xAB, 0x24, 0x10, 0xB1, 0xFD, 0xD3, 0x14,
0xEF, 0x3C, 0x6F, 0x93, 0xF0, 0xD2, 0xA8, 0xA6, 0xB3, 0xA1, 0x57, 0x6B, 0x02, 0x71, 0x50, 0x5B,
0x07, 0x11, 0x18, 0x4C, 0x83, 0x65, 0x62, 0x1C, 0x31, 0x56, 0x60, 0x51, 0xD3, 0xC6, 0x18, 0x3E,
0xF1, 0xD8, 0xD6, 0xC0, 0x19, 0xE2, 0x9C, 0x1E, 0x70, 0x1D, 0x36, 0x90, 0x11, 0x8B, 0x43, 0x44,
0xB5, 0xFF, 0xB9, 0x6E, 0xF8, 0xB1, 0xB7, 0x59, 0xC3, 0x1E, 0x76, 0x96, 0x84, 0xFE, 0x10, 0x12,
0xFC, 0xF8, 0xDD, 0xF6, 0x0D, 0xEC, 0x9D, 0x48, 0xB2, 0x5B, 0x9D, 0x20, 0x8A, 0x48, 0xFA, 0xE3,
0xCD, 0x4F, 0x6F, 0xA7, 0xD4, 0x40, 0x4D, 0x1A, 0xB0, 0xCD, 0xCF, 0xD4, 0xE0, 0xC7, 0x95, 0x1C,
0x95, 0xE2, 0x21, 0xC4, 0x1E, 0xFA, 0x06, 0xEB, 0x1B, 0xEF, 0x16, 0x18, 0x15, 0x8D, 0x52, 0x1F,
0x0F, 0x5B, 0x76, 0xCB, 0x61, 0xAB, 0xA3, 0xA9, 0xF4, 0x32, 0xF5, 0x32, 0xF7, 0x44, 0xC0, 0x2A,
0xC5, 0x5D, 0x18, 0x42, 0x9A, 0xA6, 0x11, 0x5D, 0x58, 0xAD, 0x87, 0xE3, 0x18, 0x86, 0x45, 0x08,
0x66, 0x8A, 0xB7, 0x16, 0x31, 0x0D, 0xCC, 0x32, 0xC4, 0x94, 0x04, 0xB0, 0xA1, 0x31, 0x83, 0xB3,
0x00, 0x26, 0xA4, 0x32, 0xDF, 0xD4, 0xF7, 0x84, 0x15, 0xEA, 0xB3, 0x0E, 0xC8, 0x47, 0x9B, 0xD2,
0xE4, 0x4A, 0xA6, 0x5B, 0xB5, 0xEA, 0x0E, 0x66, 0xD2, 0x06, 0x65, 0x8F, 0xD6, 0xBE, 0xC5, 0x92,
0x01, 0xD4, 0x53, 0x30, 0x35, 0x21, 0x78, 0x4B, 0x7E, 0x5C, 0x17, 0x0E, 0xC4, 0xD1, 0x96, 0xDE,
0xB6, 0x40, 0xAF, 0x9C, 0xBE, 0x29, 0xFA, 0x21, 0xEA, 0x76, 0xF0, 0x74, 0xDA, 0x7A, 0xF1, 0xA2,
0xC9, 0x94, 0x75, 0xF3, 0x61, 0x26, 0x8C, 0x82, 0x65, 0xDD, 0x00, 0x31, 0x2A, 0xAE, 0x28, 0x70,
0x53, 0x03, 0x31, 0xDA, 0xEC, 0x13, 0x2D, 0x50, 0xC6, 0x32, 0x6C, 0x40, 0x09, 0xC3, 0xB6, 0x10,
0x6B, 0x6C, 0xFC, 0x3C, 0xE0, 0x8F, 0x21, 0x6B, 0xB3, 0x10, 0x1F, 0xDE, 0x5A, 0xB6, 0x78, 0xB7,
0x34, 0x1C, 0x66, 0x9D, 0x81, 0x18, 0x58, 0xC7, 0xD7, 0xEE, 0x2D, 0x1E, 0x91, 0xB7, 0xF8, 0x6C,
0x68, 0xF7, 0x36, 0x3C, 0x00, 0x59, 0xB7, 0x36, 0x8B, 0x80, 0x15, 0x0E, 0xFC, 0x6B, 0x5B, 0x08,
0x6F, 0x35, 0x2E, 0xE5, 0x12, 0x73, 0x56, 0x7D, 0xC1, 0xC9, 0x14, 0xAC, 0xBA, 0x9C, 0x95, 0x65,
0x9E, 0xC1, 0x0B, 0xD6, 0x7C, 0xC0, 0xA7, 0x57, 0xE1, 0xD3, 0x7B, 0x22, 0x9F, 0x51, 0x85, 0xCF,
0xE8, 0x0C, 0x3E, 0xB2, 0x76, 0xC0, 0xF2, 0x27, 0x58, 0x66, 0xE3, 0xEA, 0xE6, 0x95, 0xC8, 0xD5,
0x3E, 0x89, 0x64, 0xED, 0x53, 0x23, 0x2F, 0x4B, 0xCA, 0xDA, 0x75, 0x72, 0xEF, 0x80, 0xF9, 0xBA,
0xDA, 0x2A, 0x25, 0x8B, 0xA9, 0xDE, 0x00, 0x3F, 0x66, 0xA0, 0xAB, 0x98, 0x09, 0xDA, 0xD9, 0x45,
0x03, 0x92, 0x72, 0x37, 0x05, 0x57, 0x9E, 0x7E, 0x06, 0x01, 0xA2, 0x2F, 0xA5, 0xAC, 0x3A, 0xFF,
0x25, 0x81, 0x3E, 0x6B, 0xE0, 0xCC, 0x15, 0x62, 0xAE, 0x73, 0x91, 0x0F, 0xBB, 0xF0, 0x77, 0xF3,
0x6A, 0x06, 0xB2, 0x71, 0x29, 0x2B, 0xF6, 0x28, 0xC6, 0xF2, 0x11, 0xD2, 0xEC, 0xCC, 0xE7, 0x0D,
0x75, 0xBE, 0x4F, 0xA2, 0x70, 0xF4, 0x49, 0x57, 0x5C, 0xF9, 0x15, 0xC4, 0x0F, 0x4A, 0x9A, 0x0C,
0xA9, 0xCA, 0xA2, 0xEB, 0x8D, 0x16, 0x66, 0x9A, 0xC8, 0x59, 0x4D, 0x17, 0x4B, 0x52, 0xE1, 0x7C,
0xDC, 0x27, 0xF1, 0x0D, 0x7D, 0xA1, 0xF0, 0xAB, 0x10, 0xFC, 0x2A, 0xAC, 0xFA, 0x55, 0x28, 0xFC,
0x6A, 0x5A, 0xF5, 0xAB, 0xF0, 0x0F, 0xF5, 0x2B, 0xC5, 0xAB, 0xC6, 0x3C, 0x3C, 0x8F, 0x31, 0xD0,
0x42, 0x90, 0x86, 0x78, 0x2C, 0xDE, 0xFA, 0x18, 0x72, 0x7B, 0xE8, 0x45, 0x3D, 0xF4, 0xBB, 0x3E,
0x73, 0x3E, 0x9B, 0x0D, 0xC5, 0x07, 0x06, 0x6A, 0x74, 0xC5, 0x2E, 0xA3, 0xEF, 0xB3, 0x77, 0x9B,
0x7B, 0x22, 0xF4, 0x9F, 0x17, 0xA7, 0x0B, 0xA3, 0xD2, 0x71, 0x5B, 0xCA, 0x27, 0x00, 0x2D, 0xDF,
0xFE, 0x3C, 0xAB, 0x39, 0xBC, 0x0E, 0x53, 0x51, 0x57, 0x09, 0xF5, 0xCA, 0x76, 0x85, 0xC5, 0x76,
0x39, 0x72, 0xBF, 0x2A, 0x7D, 0x25, 0xE3, 0x90, 0xF2, 0x3C, 0xD9, 0x56, 0x8E, 0xCF, 0xFF, 0x24,
0x73, 0x39, 0x1A, 0xEF, 0x8A, 0x9A, 0x54, 0x39, 0xE4, 0x1D, 0x25, 0x60, 0xA5, 0x3A, 0x75, 0xAC,
0x3C, 0x2E, 0x16, 0x29, 0x89, 0xB0, 0x76, 0xCC, 0x94, 0xE2, 0x08, 0xEB, 0xF7, 0x4D, 0xFD, 0x7B,
0xFE, 0x41, 0xF3, 0xB1, 0x0B, 0xC7, 0xC4, 0x0B, 0xDC, 0x4C, 0x9E, 0x1E, 0x80, 0x39, 0x96, 0xF2,
0x27, 0x36, 0x88, 0x40, 0x06, 0x7B, 0xC0, 0x53, 0x26, 0x60, 0xA7, 0xD8, 0xC2, 0x18, 0xD8, 0xC0,
0x38, 0xDD, 0x9E, 0xE0, 0x0D, 0x63, 0xCA, 0xEC, 0x95, 0x02, 0xB0, 0x38, 0xA9, 0x25, 0x29, 0x44,
0x36, 0x38, 0xDB, 0xBE, 0xE7, 0xE5, 0x1A, 0x76, 0xD1, 0x50, 0xF0, 0x66, 0x05, 0x23, 0xC8, 0x09,
0x40, 0xB5, 0xD1, 0x26, 0x0C, 0x9F, 0x4D, 0x49, 0x65, 0x1E, 0x4F, 0x32, 0x84, 0x79, 0x3A, 0xE0,
0x84, 0xEB, 0x66, 0x4B, 0x99, 0x4E, 0x1D, 0xCA, 0xA2, 0x29, 0x4F, 0x25, 0x22, 0x72, 0xA7, 0xFD,
0xEF, 0x4F, 0x6F, 0x7F, 0xA4, 0x34, 0x11, 0x27, 0x78, 0x38, 0x50, 0xEB, 0x97, 0xCC, 0x04, 0x5E,
0xF2, 0xDF, 0x1F, 0x4C, 0x61, 0x4D, 0x10, 0x37, 0x21, 0x93, 0xC2, 0x56, 0x5E, 0xB6, 0xBA, 0x20,
0x91, 0x17, 0xFB, 0xE4, 0xE3, 0x87, 0x37, 0x4D, 0xDA, 0x32, 0x58, 0x27, 0x4B, 0x1A, 0xD4, 0x0E,
0x35, 0x71, 0x39, 0xBE, 0xB9, 0xA2, 0x68, 0xDB, 0xEA, 0x30, 0x57, 0xE9, 0x14, 0x95, 0x2C, 0x51,
0xD5, 0x0A, 0x61, 0xCD, 0x51, 0x27, 0x8E, 0x60, 0x71, 0xFE, 0x16, 0x53, 0x25, 0xE2, 0xAD, 0xF0,
0x27, 0x7D, 0xD3, 0x3C, 0x37, 0x68, 0xED, 0x20, 0xE3, 0xEC, 0x4D, 0xA7, 0x51, 0x87, 0x8D, 0xC1,
0x64, 0x93, 0xB4, 0xA0, 0xC9, 0x36, 0x4D, 0x6C, 0xE4, 0xE9, 0x95, 0xD4, 0xF1, 0x7F, 0x5D, 0xBF,
0xFB, 0x19, 0x10, 0x3D, 0x85, 0x04, 0x17, 0xC7, 0x67, 0x49, 0x1C, 0x65, 0xE4, 0x86, 0xDC, 0xD3,
0x13, 0x06, 0x7B, 0x42, 0x44, 0x51, 0x6D, 0x33, 0x6A, 0x53, 0xE2, 0x3D, 0x09, 0x61, 0x1B, 0x2B,
0xE5, 0x91, 0x3D, 0xAE, 0x26, 0x21, 0x51, 0x53, 0xFF, 0xCF, 0x1F, 0x6E, 0xE0, 0x5C, 0x6F, 0x3C,
0x33, 0x5B, 0xD0, 0x94, 0xC1, 0xF6, 0x34, 0x2B, 0xDB, 0xC5, 0xCB, 0x8C, 0xBB, 0xBC, 0x8D, 0xD9,
0x37, 0x2C, 0x8C, 0x08, 0xD0, 0x85, 0xED, 0x4C, 0x36, 0xAC, 0x04, 0x20, 0xB3, 0x6F, 0xC2, 0x7F,
0x54, 0xE0, 0x5F, 0x8A, 0x94, 0xF9, 0x3F, 0x20, 0x7B, 0x3D, 0xBE, 0x2E, 0xBC, 0x6D, 0x69, 0x75,
0x78, 0x46, 0x7B, 0xC2, 0xFD, 0xCA, 0xF7, 0x40, 0x92, 0x40, 0x94, 0x42, 0x01, 0xD5, 0xB4, 0x4E,
0xA7, 0xA3, 0x5F, 0xE0, 0xE1, 0xE1, 0x35, 0xDE, 0xB9, 0x37, 0xCD, 0x16, 0xE6, 0xBB, 0xFB, 0x3D,
0x17, 0xE9, 0x24, 0x0C, 0xC8, 0xE2, 0x6D, 0x8B, 0x63, 0x0E, 0x1E, 0x1D, 0xCC, 0x67, 0x53, 0x59,
0x6F, 0x69, 0xED, 0x9E, 0x2C, 0x13, 0x8A, 0x74, 0x02, 0x50, 0xD8, 0xC2, 0x8F, 0x1B, 0x9C, 0x53,
0xF8, 0xC6, 0xEB, 0x38, 0x5D, 0xBF, 0x72, 0xA9, 0xEB, 0x44, 0x1D, 0x37, 0x49, 0x70, 0x93, 0x38,
0x1C, 0xA9, 0x79, 0x76, 0x35, 0xE5, 0xA4, 0x6A, 0xB2, 0xB9, 0xE3, 0x21, 0x13, 0xCB, 0x3A, 0x0A,
0x8F, 0xA2, 0x4C, 0x6D, 0x84, 0x2A, 0xAB, 0x8B, 0x50, 0xA4, 0xB1, 0x48, 0xE5, 0xD7, 0x78, 0xA7,
0xE3, 0x0B, 0xEB, 0x79, 0xFF, 0xEE, 0xFA, 0x06, 0x4F, 0x08, 0x8C, 0x8F, 0xCE, 0xAC, 0xC8, 0xEF,
0x70, 0xB5, 0x74, 0x20, 0xDA, 0xFC, 0x70, 0x0B, 0x1C, 0xDF, 0x02, 0xC8, 0x12, 0x00, 0x4D, 0x5C,
0x31, 0x2F, 0x24, 0x03, 0x34, 0x18, 0xCF, 0x2C, 0x1C, 0x1A, 0x47, 0x38, 0xB4, 0xE2, 0x45, 0xCC,
0x65, 0xA6, 0x7E, 0xEE, 0x33, 0x4F, 0xD3, 0xFF, 0x13, 0x34, 0x9F, 0xFB, 0xD1, 0x79, 0xD6, 0x22,
0xA6, 0xD3, 0x9D, 0x1A, 0xBF, 0x53, 0x3C, 0xDC, 0x2F, 0x7B, 0xB8, 0x70, 0x48, 0xF6, 0xD3, 0xFE,
0xA6, 0xFE, 0x2D, 0x38, 0x14, 0xFB, 0x51, 0x39, 0x9E, 0xD3, 0x60, 0x03, 0xFC, 0x67, 0x70, 0x74,
0x06, 0xC5, 0x30, 0x4F, 0x8C, 0x5A, 0xFB, 0xC2, 0x17, 0xD5, 0xA2, 0xFF, 0x9F, 0xEA, 0x8D, 0x8B,
0xBB, 0x73, 0xFC, 0x11, 0x0B, 0xFF, 0x6A, 0x34, 0x7C, 0xD8, 0x13, 0x41, 0xCA, 0x83, 0xF8, 0x55,
0xB9, 0x8C, 0xD1, 0x5E, 0xEA, 0x2D, 0x29, 0xF7, 0xF1, 0x4D, 0xB8, 0xFB, 0x9D, 0x0E, 0x5B, 0x5C,
0xA9, 0x3C, 0xCD, 0x08, 0x94, 0xF9, 0x9F, 0x40, 0xCE, 0x15, 0x77, 0x22, 0xCE, 0x9C, 0xAF, 0xF2,
0x13, 0x63, 0xC5, 0x4F, 0x00, 0xE4, 0x44, 0xE2, 0xD7, 0x49, 0x53, 0x1D, 0x7F, 0x9E, 0x74, 0xDA,
0x33, 0xD8, 0xF6, 0x9F, 0x40, 0x25, 0x89, 0x32, 0x65, 0x64, 0x32, 0x1E, 0x09, 0x3A, 0x1C, 0x73,
0xF4, 0x4B, 0xFD, 0xF1, 0x58, 0xC3, 0x7F, 0x24, 0x01, 0x62, 0xFE, 0x35, 0x70, 0xA3, 0x5A, 0xCF,
0xB9, 0x80, 0x73, 0xB0, 0x73, 0x27, 0xEE, 0xC5, 0x4E, 0xB0, 0x91, 0x37, 0x64, 0x4F, 0x33, 0x9F,
0x3F, 0xC4, 0xF8, 0x8F, 0x31, 0x91, 0x17, 0xC4, 0xC9, 0xFD, 0x99, 0x9E, 0x23, 0xC1, 0xF3, 0x4F,
0xF3, 0xB3, 0x43, 0x72, 0x45, 0x46, 0xE7, 0x20, 0x0F, 0xAB, 0xA0, 0x34, 0x62, 0x8A, 0x8E, 0x05,
0x1A, 0x59, 0x2B, 0x7C, 0xF1, 0x42, 0xEF, 0x95, 0xBF, 0xAA, 0xBD, 0xBF, 0xFD, 0x26, 0xF0, 0x5C,
0xE0, 0xD8, 0xC2, 0x05, 0x1B, 0xF7, 0xF5, 0x96, 0xA1, 0xDB, 0x70, 0x18, 0x95, 0xA3, 0x5A, 0xE5,
0x41, 0x9E, 0x1B, 0x79, 0x20, 0x21, 0x42, 0xBE, 0xC3, 0x62, 0x02, 0xCE, 0xD9, 0x55, 0xC7, 0x73,
0x28, 0x34, 0xA2, 0xA9, 0x09, 0xFE, 0xF5, 0x90, 0xD3, 0x3A, 0x41, 0x07, 0x6B, 0x85, 0x3D, 0xD3,
0xA0, 0xD3, 0x8C, 0xD0, 0x37, 0x68, 0x2A, 0xA0, 0xE5, 0xA6, 0x62, 0xED, 0xD1, 0xC5, 0xD4, 0x7A,
0xD8, 0xF9, 0xF9, 0xD6, 0x44, 0xE7, 0xD8, 0x61, 0x61, 0xD2, 0x3D, 0xAB, 0x1D, 0x19, 0xD1, 0xAC,
0x67, 0xC2, 0x11, 0xDB, 0x03, 0x8B, 0x4E, 0xF3, 0xF9, 0x21, 0x35, 0x0F, 0x63, 0x7E, 0x97, 0x0A,
0x3A, 0x46, 0xF3, 0xC1, 0x23, 0x81, 0x61, 0x91, 0x6E, 0x39, 0x14, 0x96, 0x54, 0x87, 0x61, 0xF0,
0x51, 0x71, 0xB2, 0x08, 0x94, 0x02, 0x37, 0xDE, 0xE2, 0xCD, 0x64, 0xF3, 0x84, 0x3B, 0x9F, 0x93,
0x50, 0x1F, 0xA7, 0x95, 0x97, 0xCE, 0x07, 0x08, 0xCB, 0x7E, 0xA0, 0xAD, 0x94, 0xD0, 0x2B, 0x99,
0xF7, 0x29, 0x79, 0x8E, 0xF2, 0x64, 0xA8, 0x2D, 0x6C, 0xF6, 0x34, 0xBD, 0x72, 0x47, 0x2C, 0x36,
0x52, 0x1C, 0xC2, 0x8C, 0x13, 0x11, 0xB5, 0xEE, 0x12, 0xB7, 0x42, 0x8D, 0xB7, 0x9C, 0x6C, 0xD8,
0xCB, 0x8F, 0xD7, 0x3F, 0x7C, 0x50, 0x4F, 0x58, 0x98, 0x71, 0x80, 0x78, 0x11, 0x85, 0x4C, 0xE4,
0x42, 0x7F, 0xF1, 0xFE, 0xDB, 0xEB, 0xEB, 0xFF, 0x79, 0xF7, 0xE1, 0x55, 0xFD, 0x10, 0x8A, 0x43,
0xAE, 0x3F, 0x7E, 0xF7, 0xD3, 0x9B, 0x9B, 0xE9, 0x16, 0x33, 0xC6, 0xA0, 0x0E, 0xF1, 0x83, 0x07,
0x4E, 0x5A, 0x70, 0xCC, 0x0A, 0x94, 0x63, 0xD6, 0x8B, 0x17, 0x00, 0xE1, 0xCF, 0xA0, 0x49, 0xBA,
0x66, 0xD9, 0x02, 0xF6, 0x46, 0xA0, 0x9E, 0x75, 0x22, 0x16, 0x36, 0x02, 0x79, 0xD6, 0x41, 0xA5,
0xAA, 0x05, 0x67, 0x08, 0x49, 0xCE, 0x5D, 0x10, 0xF9, 0xF1, 0x5D, 0x4D, 0xB4, 0x38, 0x7E, 0xDB,
0xE1, 0x5C, 0x5D, 0x8A, 0xEB, 0xE4, 0xAB, 0x4B, 0xF1, 0x03, 0x17, 0xF6, 0x3F, 0x96, 0xF9, 0x7F,
0x4B, 0x13, 0x97, 0xBB, 0x5F, 0x46, 0x00, 0x00
};

View File

@ -20,7 +20,7 @@
#ifndef STORESTRINGS_h
#define STORESTRINGS_h
#include <ESP8266WiFi.h>
#include <Arduino.h>
#include "GenLinkedList.h"
class STORESTRINGS_CLASS
{

File diff suppressed because it is too large Load Diff

View File

@ -23,26 +23,25 @@
#include <Arduino.h>
#include <WiFiClient.h>
#include <WiFiServer.h>
#include <ESP8266WebServer.h>
#ifdef SDCARD_FEATURE
#ifndef FS_NO_GLOBALS
#define FS_NO_GLOBALS
#endif
#endif
#include <FS.h>
#ifdef ARDUINO_ARCH_ESP8266
#include <ESP8266WebServer.h>
#else
#include <WebServer.h>
#endif
#include "storestrings.h"
#define MAX_EXTRUDERS 4
typedef enum {
LEVEL_GUEST = 0,
LEVEL_USER = 1,
LEVEL_ADMIN = 2
} level_authenticate_type;
struct auth_ip {
IPAddress ip;
level_authenticate_type level;
char userID[17];
char sessionID[17];
uint32_t last_time;
auth_ip * _next;
@ -53,24 +52,12 @@ class WEBINTERFACE_CLASS
public:
WEBINTERFACE_CLASS (int port = 80);
~WEBINTERFACE_CLASS();
ESP8266WebServer WebServer;
FSFILE fsUploadFile;
#ifdef TEMP_MONITORING_FEATURE
String answer4M105;
uint32_t last_temp;
#endif
#ifdef POS_MONITORING_FEATURE
String answer4M114;
#endif
#ifdef SPEED_MONITORING_FEATURE
String answer4M220;
#endif
#ifdef FLOW_MONITORING_FEATURE
String answer4M221;
#endif
#ifndef DIRECT_SDCARD_FEATURE
STORESTRINGS_CLASS fileslist;
#ifdef ARDUINO_ARCH_ESP8266
ESP8266WebServer web_server;
#else
WebServer web_server;
#endif
FS_FILE fsUploadFile;
#ifdef ERROR_MSG_FEATURE
STORESTRINGS_CLASS error_msg;
#endif
@ -81,25 +68,14 @@ public:
STORESTRINGS_CLASS status_msg;
#endif
bool restartmodule;
bool generateJSON(STORESTRINGS_CLASS & KeysList , STORESTRINGS_CLASS & ValuesList );
bool processTemplate(const char * filename, STORESTRINGS_CLASS & KeysList , STORESTRINGS_CLASS & ValuesList );
void GetFreeMem(STORESTRINGS_CLASS & KeysList, STORESTRINGS_CLASS & ValuesList);
void GetLogin(STORESTRINGS_CLASS & KeysList, STORESTRINGS_CLASS & ValuesList,level_authenticate_type auth_level, bool ishtmloutput);
void GetIpWeb(STORESTRINGS_CLASS & KeysList, STORESTRINGS_CLASS & ValuesList);
void GetMode(STORESTRINGS_CLASS & KeysList, STORESTRINGS_CLASS & ValuesList);
void GetPorts(STORESTRINGS_CLASS & KeysList, STORESTRINGS_CLASS & ValuesList, bool ishtmloutput);
void SetPageProp(STORESTRINGS_CLASS & KeysList, STORESTRINGS_CLASS & ValuesList,
const __FlashStringHelper *title, const __FlashStringHelper *filename);
void GetDHCPStatus(STORESTRINGS_CLASS & KeysList, STORESTRINGS_CLASS & ValuesList);
void ProcessAlertError(STORESTRINGS_CLASS & KeysList, STORESTRINGS_CLASS & ValuesList, String & smsg, bool ishtmloutput);
void ProcessAlertSuccess(STORESTRINGS_CLASS & KeysList, STORESTRINGS_CLASS & ValuesList, String & smsg, bool ishtmloutput);
void ProcessNoAlert(STORESTRINGS_CLASS & KeysList, STORESTRINGS_CLASS & ValuesList, bool ishtmloutput);
String getContentType(String filename);
level_authenticate_type is_authenticated();
bool AddAuthIP(auth_ip * item);
bool blockserial;
#ifdef AUTHENTICATION_FEATURE
level_authenticate_type ResetAuthIP(IPAddress ip,const char * sessionID);
auth_ip * GetAuth(IPAddress ip,const char * sessionID);
bool ClearAuthIP(IPAddress ip, const char * sessionID);
char * create_session_ID();
#endif
uint8_t _upload_status;

View File

@ -1,5 +1,5 @@
/*
wifi.cpp - ESP3D configuration class
wificonf.cpp - ESP3D configuration class
Copyright (c) 2014 Luc Lebosse. All rights reserved.
@ -17,21 +17,53 @@
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "wifi.h"
#include "config.h"
#include "wificonf.h"
#include "bridge.h"
#include "webinterface.h"
#ifdef ARDUINO_ARCH_ESP8266
#include "ESP8266WiFi.h"
#include "IPAddress.h"
#ifdef MDNS_FEATURE
#include <ESP8266mDNS.h>
#endif
#else
#include <WiFi.h>
#include "esp_wifi.h"
#ifdef MDNS_FEATURE
#include <ESPmDNS.h>
#endif
#endif
#include "IPAddress.h"
#ifdef CAPTIVE_PORTAL_FEATURE
#include <DNSServer.h>
extern DNSServer dnsServer;
DNSServer dnsServer;
const byte DNS_PORT = 53;
#endif
#ifdef ARDUINO_ARCH_ESP8266
#include <ESP8266WebServer.h>
#else
#include <WebServer.h>
#endif
#ifdef SSDP_FEATURE
#include <ESP8266SSDP.h>
#endif
#ifdef NETBIOS_FEATURE
#ifdef ARDUINO_ARCH_ESP8266
#include <ESP8266NetBIOS.h>
#else
#include <ESPNetBIOS.h>
#endif
#endif
#ifdef ARDUINO_ARCH_ESP8266
extern "C" {
#include "user_interface.h"
}
#endif
#ifdef TIMESTAMP_FEATURE
#include <time.h>
#endif
WIFI_CONFIG::WIFI_CONFIG()
{
iweb_port=DEFAULT_WEB_PORT;
@ -54,12 +86,18 @@ int32_t WIFI_CONFIG::getSignal(int32_t RSSI)
const char * WIFI_CONFIG::get_hostname()
{
if (WiFi.hostname().length()==0) {
String hname;
#ifdef ARDUINO_ARCH_ESP8266
hname = WiFi.hostname();
#else
hname = WiFi.getHostname();
#endif
if (hname.length()==0) {
if (!CONFIG::read_string(EP_HOSTNAME, _hostname, MAX_HOSTNAME_LENGTH)) {
strcpy(_hostname,get_default_hostname());
}
} else {
strcpy(_hostname,WiFi.hostname().c_str());
strcpy(_hostname,hname.c_str());
}
return _hostname;
}
@ -95,7 +133,7 @@ void WIFI_CONFIG::Safe_Setup()
delay(500);
WiFi.softAPConfig( local_ip, gateway, subnet);
delay(1000);
Serial.println(F("M117 Safe mode started"));
ESP_SERIAL_OUT.println(F("M117 Safe mode started"));
}
//Read configuration settings and apply them
@ -114,7 +152,11 @@ bool WIFI_CONFIG::Setup(bool force_ap)
LOG("Error read Sleep mode\r\n")
return false;
}
#ifdef ARDUINO_ARCH_ESP8266
WiFi.setSleepMode ((WiFiSleepType_t)bflag);
#else
esp_wifi_set_ps((wifi_ps_type_t)bflag);
#endif
sleep_mode=bflag;
if (force_ap) {
bmode = AP_MODE;
@ -137,9 +179,9 @@ bool WIFI_CONFIG::Setup(bool force_ap)
if(!CONFIG::read_string(EP_AP_PASSWORD, pwd, MAX_PASSWORD_LENGTH)) {
return false;
}
Serial.print(FPSTR(M117_));
Serial.print(F("SSID "));
Serial.println(sbuf);
ESP_SERIAL_OUT.print(FPSTR(M117_));
ESP_SERIAL_OUT.print(F("SSID "));
ESP_SERIAL_OUT.println(sbuf);
LOG("SSID ")
LOG(sbuf)
LOG("\r\n")
@ -187,46 +229,78 @@ bool WIFI_CONFIG::Setup(bool force_ap)
LOG("Disable STA\r\n")
WiFi.enableSTA(false);
delay(100);
LOG("Set phy mode\r\n")
//setup PHY_MODE
if (!CONFIG::read_byte(EP_AP_PHY_MODE, &bflag )) {
return false;
}
#ifdef ARDUINO_ARCH_ESP32
esp_wifi_set_protocol(ESP_IF_WIFI_AP, bflag);
#endif
LOG("Set AP\r\n")
//setup Soft AP
WiFi.mode(WIFI_AP);
delay(50);
WiFi.softAP(sbuf, pwd);
delay(100);
LOG("Set phy mode\r\n")
//setup PHY_MODE
if (!CONFIG::read_byte(EP_AP_PHY_MODE, &bflag )) {
return false;
}
#ifdef ARDUINO_ARCH_ESP8266
WiFi.setPhyMode((WiFiPhyMode_t)bflag);
#endif
delay(100);
LOG("Get current config\r\n")
//get current config
#ifdef ARDUINO_ARCH_ESP32
wifi_config_t conf;
esp_wifi_get_config(ESP_IF_WIFI_AP, &conf);
#else
struct softap_config apconfig;
wifi_softap_get_config(&apconfig);
#endif
//set the chanel
if (!CONFIG::read_byte(EP_CHANNEL, &bflag )) {
return false;
}
#ifdef ARDUINO_ARCH_ESP32
conf.ap.channel=bflag;
#else
apconfig.channel=bflag;
#endif
//set Authentification type
if (!CONFIG::read_byte(EP_AUTH_TYPE, &bflag )) {
return false;
}
#ifdef ARDUINO_ARCH_ESP32
conf.ap.authmode=(wifi_auth_mode_t)bflag;
#else
apconfig.authmode=(AUTH_MODE)bflag;
#endif
//set the visibility of SSID
if (!CONFIG::read_byte(EP_SSID_VISIBLE, &bflag )) {
return false;
}
#ifdef ARDUINO_ARCH_ESP32
conf.ap.ssid_hidden=!bflag;
#else
apconfig.ssid_hidden=!bflag;
#endif
//no need to add these settings to configuration just use default ones
#ifdef ARDUINO_ARCH_ESP32
conf.ap.max_connection=DEFAULT_MAX_CONNECTIONS;
conf.ap.beacon_interval=DEFAULT_BEACON_INTERVAL;
if (esp_wifi_set_config(ESP_IF_WIFI_AP, &conf)!=ESP_OK){
ESP_SERIAL_OUT.println(F("M117 Error Wifi AP!"));
delay(1000);
}
#else
apconfig.max_connection=DEFAULT_MAX_CONNECTIONS;
apconfig.beacon_interval=DEFAULT_BEACON_INTERVAL;
//apply settings to current and to default
if (!wifi_softap_set_config(&apconfig) || !wifi_softap_set_config_current(&apconfig)) {
Serial.println(F("M117 Error Wifi AP!"));
ESP_SERIAL_OUT.println(F("M117 Error Wifi AP!"));
delay(1000);
}
#endif
} else {
LOG("Set STA mode\r\n")
if(!CONFIG::read_string(EP_STA_SSID, sbuf, MAX_SSID_LENGTH)) {
@ -235,9 +309,9 @@ bool WIFI_CONFIG::Setup(bool force_ap)
if(!CONFIG::read_string(EP_STA_PASSWORD, pwd, MAX_PASSWORD_LENGTH)) {
return false;
}
Serial.print(FPSTR(M117_));
Serial.print(F("SSID "));
Serial.println(sbuf);
ESP_SERIAL_OUT.print(FPSTR(M117_));
ESP_SERIAL_OUT.print(F("SSID "));
ESP_SERIAL_OUT.println(sbuf);
LOG("SSID ")
LOG(sbuf)
LOG("\r\n")
@ -266,62 +340,167 @@ bool WIFI_CONFIG::Setup(bool force_ap)
}
WiFi.enableAP(false);
delay(100);
//setup PHY_MODE
if (!CONFIG::read_byte(EP_STA_PHY_MODE, &bflag )) {
return false;
}
#ifdef ARDUINO_ARCH_ESP32
esp_wifi_set_protocol(ESP_IF_WIFI_STA, bflag);
#endif
//setup station mode
WiFi.mode(WIFI_STA);
delay(100);
WiFi.begin(sbuf, pwd);
delay(100);
//setup PHY_MODE
if (!CONFIG::read_byte(EP_STA_PHY_MODE, &bflag )) {
return false;
}
#ifdef ARDUINO_ARCH_ESP8266
WiFi.setPhyMode((WiFiPhyMode_t)bflag);
#endif
delay(100);
byte i=0;
//try to connect
byte dot = 0;
String msg;
while (WiFi.status() != WL_CONNECTED && i<40) {
switch(WiFi.status()) {
case 1:
Serial.print(FPSTR(M117_));
Serial.println(F("No SSID found!"));
ESP_SERIAL_OUT.print(FPSTR(M117_));
ESP_SERIAL_OUT.println(F("No SSID found!"));
break;
case 4:
Serial.print(FPSTR(M117_));
Serial.println(F("No Connection!"));
ESP_SERIAL_OUT.print(FPSTR(M117_));
ESP_SERIAL_OUT.println(F("No Connection!"));
break;
default:
Serial.print(FPSTR(M117_));
Serial.println(F("Connecting..."));
ESP_SERIAL_OUT.print(FPSTR(M117_));
if (dot == 0)msg = F("Connecting");
dot++;
msg.trim();
msg +=F(".");
//for smoothieware to keep position
for (byte i= 0;i< 4-dot; i++)msg +=F(" ");
if (dot == 4)dot=0;
ESP_SERIAL_OUT.println(msg);
break;
}
delay(500);
i++;
}
if (WiFi.status() != WL_CONNECTED) {
ESP_SERIAL_OUT.print(FPSTR(M117_));
ESP_SERIAL_OUT.println(F("Not Connectied!"));
return false;
}
#ifdef ARDUINO_ARCH_ESP8266
WiFi.hostname(hostname);
}
#ifdef MDNS_FEATURE
// Set up mDNS responder:
if (!mdns.begin(hostname)) {
Serial.print(FPSTR(M117_));
Serial.println(F("Error with mDNS!"));
delay(1000);
}
#else
WiFi.setHostname(hostname);
#endif
}
//Get IP
if (WiFi.getMode()==WIFI_STA) {
currentIP=WiFi.localIP();
} else {
currentIP=WiFi.softAPIP();
}
Serial.print(FPSTR(M117_));
Serial.println(currentIP);
ESP_SERIAL_OUT.print(FPSTR(M117_));
ESP_SERIAL_OUT.println(currentIP);
ESP_SERIAL_OUT.flush();
return true;
}
bool WIFI_CONFIG::Enable_servers()
{
//start web interface
web_interface = new WEBINTERFACE_CLASS(wifi_config.iweb_port);
//here the list of headers to be recorded
const char * headerkeys[] = {"Cookie"} ;
size_t headerkeyssize = sizeof(headerkeys)/sizeof(char*);
//ask server to track these headers
web_interface->web_server.collectHeaders(headerkeys, headerkeyssize );
#ifdef CAPTIVE_PORTAL_FEATURE
if (WiFi.getMode()!=WIFI_STA ) {
// if DNSServer is started with "*" for domain name, it will reply with
// provided IP to all DNS request
dnsServer.setErrorReplyCode(DNSReplyCode::NoError);
dnsServer.start(DNS_PORT, "*", WiFi.softAPIP());
}
#endif
web_interface->web_server.begin();
#ifdef TCP_IP_DATA_FEATURE
//start TCP/IP interface
data_server = new WiFiServer (wifi_config.idata_port);
data_server->begin();
data_server->setNoDelay(true);
#endif
#ifdef MDNS_FEATURE
// Set up mDNS responder:
//useless in AP mode and service consuming
if (WiFi.getMode()!=WIFI_AP ){
char hostname [MAX_HOSTNAME_LENGTH+1];
if (!CONFIG::read_string(EP_HOSTNAME, hostname, MAX_HOSTNAME_LENGTH)) {
strcpy(hostname,get_default_hostname());
}
if (!mdns.begin(hostname)) {
ESP_SERIAL_OUT.print(FPSTR(M117_));
ESP_SERIAL_OUT.println(F("Error with mDNS!"));
delay(1000);
} else {
// Check for any mDNS queries and send responses
delay(100);
wifi_config.mdns.addService("http", "tcp", wifi_config.iweb_port);
}
}
#endif
#if defined(SSDP_FEATURE) || defined(NETBIOS_FEATURE)
String shost;
if (!CONFIG::read_string(EP_HOSTNAME, shost, MAX_HOSTNAME_LENGTH)) {
shost=wifi_config.get_default_hostname();
}
#endif
#ifdef SSDP_FEATURE
String stmp;
SSDP.setSchemaURL("description.xml");
SSDP.setHTTPPort( wifi_config.iweb_port);
SSDP.setName(shost.c_str());
stmp=String(ESP.getChipId());
SSDP.setSerialNumber(stmp.c_str());
SSDP.setURL("/");
SSDP.setModelName("ESP8266 01");
SSDP.setModelNumber("01");
SSDP.setModelURL("http://espressif.com/en/products/esp8266/");
SSDP.setManufacturer("Espressif Systems");
SSDP.setManufacturerURL("http://espressif.com");
SSDP.setDeviceType("upnp:rootdevice");
if (WiFi.getMode()!=WIFI_AP )SSDP.begin();
#endif
#ifdef NETBIOS_FEATURE
//useless in AP mode and service consuming
if (WiFi.getMode()!=WIFI_AP )NBNS.begin(shost.c_str());
#endif
return true;
}
bool WIFI_CONFIG::Disable_servers()
{
#ifdef TCP_IP_DATA_FEATURE
data_server->stop();
#endif
#ifdef CAPTIVE_PORTAL_FEATURE
if (WiFi.getMode()!=WIFI_STA ) {
dnsServer.stop();
}
#endif
#ifdef NETBIOS_FEATURE
//useless in AP mode and service consuming
if (WiFi.getMode()!=WIFI_AP )NBNS.end();
#endif
web_interface->web_server.stop();
return true;
}
WIFI_CONFIG wifi_config;

View File

@ -1,5 +1,5 @@
/*
wifi.h - ESP3D configuration class
wificonf.h - ESP3D configuration class
Copyright (c) 2014 Luc Lebosse. All rights reserved.
@ -18,16 +18,22 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef WIFI_h
#define WIFI_h
#ifndef WIFICONF_H
#define WIFICONF_H
#include <Arduino.h>
#include "config.h"
#include "IPAddress.h"
#include <ESP8266WiFi.h>
#ifdef ARDUINO_ARCH_ESP8266
#include "ESP8266WiFi.h"
#ifdef MDNS_FEATURE
#include <ESP8266mDNS.h>
#endif
#else
#include <WiFi.h>
#ifdef MDNS_FEATURE
#include <ESPmDNS.h>
#endif
#endif
class WIFI_CONFIG
{
@ -44,6 +50,8 @@ public:
int32_t getSignal(int32_t RSSI);
bool Setup(bool force_ap = false);
void Safe_Setup();
bool Enable_servers();
bool Disable_servers();
const char * get_default_hostname();
const char * get_hostname();
private:

BIN
images/docs/embedded.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

32
libraries/DNSServer/.gitignore vendored Normal file
View File

@ -0,0 +1,32 @@
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app

View File

@ -0,0 +1,83 @@
sudo: false
language: python
python:
- "2.7"
os:
- linux
env:
- TESTCASE="1.8.3|esp8266|stable|esp8266:esp8266:nodemcuv2:CpuFrequency=80,FlashSize=4M3M"
- TESTCASE="1.8.3|esp8266|devel|esp8266com:esp8266:nodemcuv2:CpuFrequency=80,FlashSize=4M3M"
- TESTCASE="1.8.3|esp32|devel|espressif:esp32:esp32:FlashFreq=40"
script:
- echo -e "travis_fold:start:sketch_test_env_prepare"
- pip install pyserial
- IFS='|' read -r -a array <<< "${TESTCASE}"
- |
for index in "${!array[@]}"
do
case $index in
0) IDEVER="${array[index]}" ;;
1) PLATFORM="${array[index]}" ;;
2) RELEASE="${array[index]}" ;;
3) BOARD="${array[index]}" ;;
esac
done
- cd $HOME
- wget -O arduino.tar.xz https://downloads.arduino.cc/arduino-${IDEVER}-linux64.tar.xz
- tar xf arduino.tar.xz
- mv arduino-${IDEVER} $HOME/arduino_ide
- mkdir -p $HOME/Arduino/libraries
- IDEDIR="${HOME}/arduino_ide"
- export PATH="$IDEDIR:$PATH"
- cd ${IDEDIR}
- which arduino
- |
if [[ $PLATFORM == "esp8266" ]] && [[ $RELEASE == "stable" ]]
then
arduino --pref "boardsmanager.additional.urls=http://arduino.esp8266.com/stable/package_esp8266com_index.json" --save-prefs
arduino --install-boards "esp8266:esp8266"
fi
- |
if [[ $PLATFORM == "esp8266" ]] && [[ $RELEASE == "devel" ]]
then
mkdir -p hardware/esp8266com
cd hardware/esp8266com
git clone https://github.com/esp8266/Arduino.git esp8266
cd esp8266/tools
python get.py
fi
- |
if [[ $PLATFORM == "esp32" ]] && [[ $RELEASE == "devel" ]]
then
mkdir -p hardware/espressif
cd hardware/espressif
git clone https://github.com/espressif/arduino-esp32.git esp32
cd esp32/tools/
python get.py
fi
- arduino --pref "compiler.warning_level=all" --save-prefs
- echo -e "travis_fold:end:sketch_test_env_prepare"
- echo -e "travis_fold:start:install_library"
- cd $HOME/Arduino/libraries
- git clone https://github.com/bbx10/WebServer_tng ${HOME}/Arduino/libraries/WebServer
- git clone https://github.com/bbx10/DNSServer_tng ${HOME}/Arduino/libraries/DNSServer
- echo -e "travis_fold:end:install_library"
- echo -e "travis_fold:start:CaptivePortalAdvanced"
- cd DNSServer/examples
- arduino --verbose-build --verify --board $BOARD CaptivePortalAdvanced/CaptivePortalAdvanced.ino
- echo -e "travis_fold:end:CaptivePortalAdvanced"
- echo -e "travis_fold:start:CaptivePortal"
- arduino --verbose-build --verify --board $BOARD CaptivePortal/CaptivePortal.ino
- echo -e "travis_fold:end:CaptivePortal"
- echo -e "travis_fold:start:DNSServer"
- arduino --verbose-build --verify --board $BOARD DNSServer/DNSServer.ino
- echo -e "travis_fold:end:DNSServer"
notifications:
email:
on_success: change
on_failure: change

View File

@ -0,0 +1,7 @@
# DNSServer
ESP8266/ESP32 DNSServer library
This is an experimental port of the DNSServer library that should work on
ESP8266 and ESP32. This is NOT an official repo supported by Espressif. Do not
depend on this code for anything important or expect it to be updated. Once the
official repo is created, this repo will be deleted.

View File

@ -0,0 +1,41 @@
#ifdef ESP8266
#include <ESP8266WiFi.h>
#include <DNSServer.h>
#include <ESP8266WebServer.h>
ESP8266WebServer webServer(80);
#else
#include <WiFi.h>
#include <DNSServer.h>
#include <WebServer.h>
WebServer webServer(80);
#endif
const byte DNS_PORT = 53;
IPAddress apIP(192, 168, 1, 1);
DNSServer dnsServer;
String responseHTML = ""
"<!DOCTYPE html><html><head><title>CaptivePortal</title></head><body>"
"<h1>Hello World!</h1><p>This is a captive portal example. All requests will "
"be redirected here.</p></body></html>";
void setup() {
WiFi.mode(WIFI_AP);
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
WiFi.softAP("DNSServer CaptivePortal example");
// if DNSServer is started with "*" for domain name, it will reply with
// provided IP to all DNS request
dnsServer.start(DNS_PORT, "*", apIP);
// replay to all requests with same HTML
webServer.onNotFound([]() {
webServer.send(200, "text/html", responseHTML);
});
webServer.begin();
}
void loop() {
dnsServer.processNextRequest();
webServer.handleClient();
}

View File

@ -0,0 +1,157 @@
#ifdef ESP8266
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <DNSServer.h>
#include <ESP8266mDNS.h>
#include <EEPROM.h>
// Web server
ESP8266WebServer server(80);
#else
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <DNSServer.h>
#include <ESPmDNS.h>
#include <Preferences.h>
// Web server
WebServer server(80);
#endif
/*
* This example serves a "hello world" on a WLAN and a SoftAP at the same time.
* The SoftAP allow you to configure WLAN parameters at run time. They are not setup in the sketch but saved on EEPROM.
*
* Connect your computer or cell phone to wifi network ESP_ap with password 12345678. A popup may appear and it allow you to go to WLAN config. If it does not then navigate to http://192.168.4.1/wifi and config it there.
* Then wait for the module to connect to your wifi and take note of the WLAN IP it got. Then you can disconnect from ESP_ap and return to your regular WLAN.
*
* Now the ESP8266/ESP32 is in your network. You can reach it through http://192.168.x.x/ (the IP you took note of) or maybe at http://esp8266.local too.
*
* This is a captive portal because through the softAP it will redirect any http request to http://192.168.4.1/
*/
/* Set these to your desired softAP credentials. They are not configurable at runtime */
const char *softAP_ssid = "ESP_ap";
const char *softAP_password = "12345678";
/* hostname for mDNS. Should work at least on windows. Try http://esp8266.local */
#ifdef ESP8266
const char *myHostname = "esp8266";
#else
const char *myHostname = "esp32mambo";
#endif
/* Don't set this wifi credentials. They are configurated at runtime and stored on EEPROM */
char ssid[32] = "";
char password[32] = "";
// DNS server
const byte DNS_PORT = 53;
DNSServer dnsServer;
#ifdef ESP32
/* Storage for SSID and password */
Preferences preferences;
#endif
/* Soft AP network parameters */
IPAddress apIP(192, 168, 4, 1);
IPAddress netMsk(255, 255, 255, 0);
/** Should I connect to WLAN asap? */
boolean connect;
/** Last time I tried to connect to WLAN */
long lastConnectTry = 0;
/** Current WLAN status */
int status = WL_IDLE_STATUS;
void setup() {
delay(1000);
Serial.begin(115200);
Serial.println();
#ifdef ESP32
preferences.begin("CapPortAdv", false);
#endif
Serial.print("Configuring access point...");
/* You can remove the password parameter if you want the AP to be open. */
WiFi.softAPConfig(apIP, apIP, netMsk);
WiFi.softAP(softAP_ssid, softAP_password);
delay(500); // Without delay I've seen the IP address blank
Serial.print("AP IP address: ");
Serial.println(WiFi.softAPIP());
/* Setup the DNS server redirecting all the domains to the apIP */
dnsServer.setErrorReplyCode(DNSReplyCode::NoError);
dnsServer.start(DNS_PORT, "*", apIP);
/* Setup web pages: root, wifi config pages, SO captive portal detectors and not found. */
server.on("/", handleRoot);
server.on("/wifi", handleWifi);
server.on("/wifisave", handleWifiSave);
server.on("/generate_204", handleRoot); //Android captive portal. Maybe not needed. Might be handled by notFound handler.
server.on("/fwlink", handleRoot); //Microsoft captive portal. Maybe not needed. Might be handled by notFound handler.
server.onNotFound ( handleNotFound );
server.begin(); // Web server start
Serial.println("HTTP server started");
loadCredentials(); // Load WLAN credentials from network
connect = strlen(ssid) > 0; // Request WLAN connect if there is a SSID
}
void connectWifi() {
Serial.println("Connecting as wifi client...");
WiFi.disconnect();
WiFi.begin ( ssid, password );
int connRes = WiFi.waitForConnectResult();
Serial.print ( "connRes: " );
Serial.println ( connRes );
}
void loop() {
if (connect) {
Serial.println ( "Connect requested" );
connect = false;
connectWifi();
lastConnectTry = millis();
}
{
int s = WiFi.status();
if (s == 0 && millis() > (lastConnectTry + 60000) ) {
/* If WLAN disconnected and idle try to connect */
/* Don't set retry time too low as retry interfere the softAP operation */
connect = true;
}
if (status != s) { // WLAN status change
Serial.print ( "Status: " );
Serial.println ( s );
status = s;
if (s == WL_CONNECTED) {
/* Just connected to WLAN */
Serial.println ( "" );
Serial.print ( "Connected to " );
Serial.println ( ssid );
Serial.print ( "IP address: " );
Serial.println ( WiFi.localIP() );
// Setup MDNS responder
if (!MDNS.begin(myHostname)) {
Serial.println("Error setting up MDNS responder!");
} else {
Serial.println("mDNS responder started");
// Add service to MDNS-SD
MDNS.addService("http", "tcp", 80);
}
} else if (s == WL_NO_SSID_AVAIL) {
WiFi.disconnect();
}
}
}
// Do work:
//DNS
dnsServer.processNextRequest();
//HTTP
server.handleClient();
}

View File

@ -0,0 +1,47 @@
#ifdef ESP8266
/** Load WLAN credentials from EEPROM */
void loadCredentials() {
EEPROM.begin(512);
EEPROM.get(0, ssid);
EEPROM.get(0+sizeof(ssid), password);
char ok[2+1];
EEPROM.get(0+sizeof(ssid)+sizeof(password), ok);
EEPROM.end();
if (String(ok) != String("OK")) {
ssid[0] = 0;
password[0] = 0;
}
Serial.println("Recovered credentials:");
Serial.println(ssid);
Serial.println(strlen(password)>0?"********":"<no password>");
}
/** Store WLAN credentials to EEPROM */
void saveCredentials() {
EEPROM.begin(512);
EEPROM.put(0, ssid);
EEPROM.put(0+sizeof(ssid), password);
char ok[2+1] = "OK";
EEPROM.put(0+sizeof(ssid)+sizeof(password), ok);
EEPROM.commit();
EEPROM.end();
}
#else
/** Load WLAN credentials from Preferences */
void loadCredentials() {
preferences.getString("ssid", ssid, sizeof(ssid));
preferences.getString("password", password, sizeof(password));
Serial.println("Recovered credentials:");
Serial.println(ssid);
Serial.println(strlen(password)>0?"********":"<no password>");
}
/** Store WLAN credentials to Preference */
void saveCredentials() {
preferences.putString("ssid", ssid);
preferences.putString("password", password);
Serial.println("Saved credentials:");
Serial.println(ssid);
Serial.println(strlen(password)>0?"********":"<no password>");
}
#endif

View File

@ -0,0 +1,137 @@
/** Handle root or redirect to captive portal */
void handleRoot() {
if (captivePortal()) { // If caprive portal redirect instead of displaying the page.
return;
}
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "-1");
server.setContentLength(CONTENT_LENGTH_UNKNOWN);
server.send(200, "text/html", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves.
server.sendContent(
"<html><head></head><body>"
"<h1>HELLO WORLD!!</h1>"
);
if (server.client().localIP() == apIP) {
server.sendContent(String("<p>You are connected through the soft AP: ") + softAP_ssid + "</p>");
} else {
server.sendContent(String("<p>You are connected through the wifi network: ") + ssid + "</p>");
}
server.sendContent(
"<p>You may want to <a href='/wifi'>config the wifi connection</a>.</p>"
"</body></html>"
);
server.sendContent("");
server.client().stop(); // Stop is needed because we sent no content length
}
/** Redirect to captive portal if we got a request for another domain. Return true in that case so the page handler do not try to handle the request again. */
boolean captivePortal() {
if (!isIp(server.hostHeader()) && server.hostHeader() != (String(myHostname)+".local")) {
Serial.print("Request redirected to captive portal");
server.sendHeader("Location", String("http://") + toStringIp(server.client().localIP()), true);
server.send ( 302, "text/plain", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves.
server.client().stop(); // Stop is needed because we sent no content length
return true;
}
return false;
}
/** Wifi config page handler */
void handleWifi() {
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "-1");
server.setContentLength(CONTENT_LENGTH_UNKNOWN);
server.send(200, "text/html", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves.
server.sendContent(
"<html><head></head><body>"
"<h1>Wifi config</h1>"
);
if (server.client().localIP() == apIP) {
server.sendContent(String("<p>You are connected through the soft AP: ") + softAP_ssid + "</p>");
} else {
server.sendContent(String("<p>You are connected through the wifi network: ") + ssid + "</p>");
}
server.sendContent(
"\r\n<br />"
"<table><tr><th align='left'>SoftAP config</th></tr>"
);
server.sendContent(String() + "<tr><td>SSID " + String(softAP_ssid) + "</td></tr>");
server.sendContent(String() + "<tr><td>IP " + toStringIp(WiFi.softAPIP()) + "</td></tr>");
server.sendContent(
"</table>"
"\r\n<br />"
"<table><tr><th align='left'>WLAN config</th></tr>"
);
server.sendContent(String() + "<tr><td>SSID " + String(ssid) + "</td></tr>");
server.sendContent(String() + "<tr><td>IP " + toStringIp(WiFi.localIP()) + "</td></tr>");
server.sendContent(
"</table>"
"\r\n<br />"
"<table><tr><th align='left'>WLAN list (refresh if any missing)</th></tr>"
);
Serial.println("scan start");
int n = WiFi.scanNetworks();
Serial.println("scan done");
if (n > 0) {
for (int i = 0; i < n; i++) {
#ifdef ESP8266
server.sendContent(String() + "\r\n<tr><td>SSID " + WiFi.SSID(i) + String((WiFi.encryptionType(i) == ENC_TYPE_NONE)?" ":" *") + " (" + WiFi.RSSI(i) + ")</td></tr>");
#else
server.sendContent(String() + "\r\n<tr><td>SSID " + WiFi.SSID(i) + String((WiFi.encryptionType(i) == WIFI_AUTH_OPEN)?" ":" *") + " (" + WiFi.RSSI(i) + ")</td></tr>");
#endif
}
} else {
server.sendContent(String() + "<tr><td>No WLAN found</td></tr>");
}
server.sendContent(
"</table>"
"\r\n<br /><form method='POST' action='wifisave'><h4>Connect to network:</h4>"
"<input type='text' placeholder='network' name='n'/>"
"<br /><input type='password' placeholder='password' name='p'/>"
"<br /><input type='submit' value='Connect/Disconnect'/></form>"
"<p>You may want to <a href='/'>return to the home page</a>.</p>"
"</body></html>"
);
server.sendContent("");
server.client().stop(); // Stop is needed because we sent no content length
}
/** Handle the WLAN save form and redirect to WLAN config page again */
void handleWifiSave() {
Serial.println("wifi save");
server.arg("n").toCharArray(ssid, sizeof(ssid) - 1);
server.arg("p").toCharArray(password, sizeof(password) - 1);
server.sendHeader("Location", "wifi", true);
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "-1");
server.send ( 302, "text/plain", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves.
server.client().stop(); // Stop is needed because we sent no content length
saveCredentials();
connect = strlen(ssid) > 0; // Request WLAN connect with new credentials if there is a SSID
}
void handleNotFound() {
if (captivePortal()) { // If caprive portal redirect instead of displaying the error page.
return;
}
String message = "File Not Found\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += ( server.method() == HTTP_GET ) ? "GET" : "POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for ( uint8_t i = 0; i < server.args(); i++ ) {
message += " " + server.argName ( i ) + ": " + server.arg ( i ) + "\n";
}
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "-1");
server.send ( 404, "text/plain", message );
}

View File

@ -0,0 +1,21 @@
/** Is this an IP? */
boolean isIp(String str) {
for (int i = 0; i < str.length(); i++) {
int c = str.charAt(i);
if (c != '.' && (c < '0' || c > '9')) {
return false;
}
}
return true;
}
/** IP to String? */
String toStringIp(IPAddress ip) {
String res = "";
for (int i = 0; i < 3; i++) {
res += String((ip >> (8 * i)) & 0xFF) + ".";
}
res += String(((ip >> 8 * 3)) & 0xFF);
return res;
}

View File

@ -0,0 +1,48 @@
#ifdef ESP8266
#include <ESP8266WiFi.h>
#include <DNSServer.h>
#include <ESP8266WebServer.h>
ESP8266WebServer webServer(80);
#else
#include <WiFi.h>
#include <DNSServer.h>
#include <WebServer.h>
WebServer webServer(80);
#endif
const byte DNS_PORT = 53;
IPAddress apIP(192, 168, 1, 1);
DNSServer dnsServer;
void setup() {
WiFi.mode(WIFI_AP);
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
WiFi.softAP("DNSServer example");
// modify TTL associated with the domain name (in seconds)
// default is 60 seconds
dnsServer.setTTL(300);
// set which return code will be used for all other domains (e.g. sending
// ServerFailure instead of NonExistentDomain will reduce number of queries
// sent by clients)
// default is DNSReplyCode::NonExistentDomain
dnsServer.setErrorReplyCode(DNSReplyCode::ServerFailure);
// start DNS server for a specific domain name
dnsServer.start(DNS_PORT, "www.example.com", apIP);
// simple HTTP server to see that DNS server is working
webServer.onNotFound([]() {
String message = "Hello World!\n\n";
message += "URI: ";
message += webServer.uri();
webServer.send(200, "text/plain", message);
});
webServer.begin();
}
void loop() {
dnsServer.processNextRequest();
webServer.handleClient();
}

View File

@ -0,0 +1,9 @@
name=DNSServer
version=1.1.0
author=Kristijan Novoselić
maintainer=Kristijan Novoselić, <kristijan.novoselic@gmail.com>
sentence=A simple DNS server for ESP8266.
paragraph=This library implements a simple DNS server.
category=Communication
url=
architectures=esp8266,esp32

View File

@ -0,0 +1,171 @@
#include "DNSServer.h"
#include <lwip/def.h>
#include <Arduino.h>
DNSServer::DNSServer()
{
_ttl = htonl(60);
_errorReplyCode = DNSReplyCode::NonExistentDomain;
}
bool DNSServer::start(const uint16_t &port, const String &domainName,
const IPAddress &resolvedIP)
{
_port = port;
_buffer = NULL;
_domainName = domainName;
_resolvedIP[0] = resolvedIP[0];
_resolvedIP[1] = resolvedIP[1];
_resolvedIP[2] = resolvedIP[2];
_resolvedIP[3] = resolvedIP[3];
downcaseAndRemoveWwwPrefix(_domainName);
return _udp.begin(_port) == 1;
}
void DNSServer::setErrorReplyCode(const DNSReplyCode &replyCode)
{
_errorReplyCode = replyCode;
}
void DNSServer::setTTL(const uint32_t &ttl)
{
_ttl = htonl(ttl);
}
void DNSServer::stop()
{
_udp.stop();
free(_buffer);
_buffer = NULL;
}
void DNSServer::downcaseAndRemoveWwwPrefix(String &domainName)
{
domainName.toLowerCase();
domainName.replace("www.", "");
}
void DNSServer::processNextRequest()
{
_currentPacketSize = _udp.parsePacket();
if (_currentPacketSize)
{
if (_buffer != NULL) free(_buffer);
_buffer = (unsigned char*)malloc(_currentPacketSize * sizeof(char));
if (_buffer == NULL) return;
_udp.read(_buffer, _currentPacketSize);
_dnsHeader = (DNSHeader*) _buffer;
if (_dnsHeader->QR == DNS_QR_QUERY &&
_dnsHeader->OPCode == DNS_OPCODE_QUERY &&
requestIncludesOnlyOneQuestion() &&
(_domainName == "*" || getDomainNameWithoutWwwPrefix() == _domainName)
)
{
replyWithIP();
}
else if (_dnsHeader->QR == DNS_QR_QUERY)
{
replyWithCustomCode();
}
free(_buffer);
_buffer = NULL;
}
}
bool DNSServer::requestIncludesOnlyOneQuestion()
{
return ntohs(_dnsHeader->QDCount) == 1 &&
_dnsHeader->ANCount == 0 &&
_dnsHeader->NSCount == 0 &&
_dnsHeader->ARCount == 0;
}
String DNSServer::getDomainNameWithoutWwwPrefix()
{
String parsedDomainName = "";
if (_buffer == NULL) return parsedDomainName;
unsigned char *start = _buffer + 12;
if (*start == 0)
{
return parsedDomainName;
}
int pos = 0;
while(true)
{
unsigned char labelLength = *(start + pos);
for(int i = 0; i < labelLength; i++)
{
pos++;
parsedDomainName += (char)*(start + pos);
}
pos++;
if (*(start + pos) == 0)
{
downcaseAndRemoveWwwPrefix(parsedDomainName);
return parsedDomainName;
}
else
{
parsedDomainName += ".";
}
}
}
void DNSServer::replyWithIP()
{
if (_buffer == NULL) return;
_dnsHeader->QR = DNS_QR_RESPONSE;
_dnsHeader->ANCount = _dnsHeader->QDCount;
_dnsHeader->QDCount = _dnsHeader->QDCount;
//_dnsHeader->RA = 1;
_udp.beginPacket(_udp.remoteIP(), _udp.remotePort());
_udp.write(_buffer, _currentPacketSize);
_udp.write((uint8_t)192); // answer name is a pointer
_udp.write((uint8_t)12); // pointer to offset at 0x00c
_udp.write((uint8_t)0); // 0x0001 answer is type A query (host address)
_udp.write((uint8_t)1);
_udp.write((uint8_t)0); //0x0001 answer is class IN (internet address)
_udp.write((uint8_t)1);
_udp.write((unsigned char*)&_ttl, 4);
// Length of RData is 4 bytes (because, in this case, RData is IPv4)
_udp.write((uint8_t)0);
_udp.write((uint8_t)4);
_udp.write(_resolvedIP, sizeof(_resolvedIP));
_udp.endPacket();
#ifdef DEBUG
DEBUG_OUTPUT.print("DNS responds: ");
DEBUG_OUTPUT.print(_resolvedIP[0]);
DEBUG_OUTPUT.print(".");
DEBUG_OUTPUT.print(_resolvedIP[1]);
DEBUG_OUTPUT.print(".");
DEBUG_OUTPUT.print(_resolvedIP[2]);
DEBUG_OUTPUT.print(".");
DEBUG_OUTPUT.print(_resolvedIP[3]);
DEBUG_OUTPUT.print(" for ");
DEBUG_OUTPUT.println(getDomainNameWithoutWwwPrefix());
#endif
}
void DNSServer::replyWithCustomCode()
{
if (_buffer == NULL) return;
_dnsHeader->QR = DNS_QR_RESPONSE;
_dnsHeader->RCode = (unsigned char)_errorReplyCode;
_dnsHeader->QDCount = 0;
_udp.beginPacket(_udp.remoteIP(), _udp.remotePort());
_udp.write(_buffer, sizeof(DNSHeader));
_udp.endPacket();
}

View File

@ -0,0 +1,71 @@
#ifndef DNSServer_h
#define DNSServer_h
#include <WiFiUdp.h>
#define DNS_QR_QUERY 0
#define DNS_QR_RESPONSE 1
#define DNS_OPCODE_QUERY 0
enum class DNSReplyCode
{
NoError = 0,
FormError = 1,
ServerFailure = 2,
NonExistentDomain = 3,
NotImplemented = 4,
Refused = 5,
YXDomain = 6,
YXRRSet = 7,
NXRRSet = 8
};
struct DNSHeader
{
uint16_t ID; // identification number
unsigned char RD : 1; // recursion desired
unsigned char TC : 1; // truncated message
unsigned char AA : 1; // authoritive answer
unsigned char OPCode : 4; // message_type
unsigned char QR : 1; // query/response flag
unsigned char RCode : 4; // response code
unsigned char Z : 3; // its z! reserved
unsigned char RA : 1; // recursion available
uint16_t QDCount; // number of question entries
uint16_t ANCount; // number of answer entries
uint16_t NSCount; // number of authority entries
uint16_t ARCount; // number of resource entries
};
class DNSServer
{
public:
DNSServer();
void processNextRequest();
void setErrorReplyCode(const DNSReplyCode &replyCode);
void setTTL(const uint32_t &ttl);
// Returns true if successful, false if there are no sockets available
bool start(const uint16_t &port,
const String &domainName,
const IPAddress &resolvedIP);
// stops the DNS server
void stop();
private:
WiFiUDP _udp;
uint16_t _port;
String _domainName;
unsigned char _resolvedIP[4];
int _currentPacketSize;
unsigned char* _buffer;
DNSHeader* _dnsHeader;
uint32_t _ttl;
DNSReplyCode _errorReplyCode;
void downcaseAndRemoveWwwPrefix(String &domainName);
String getDomainNameWithoutWwwPrefix();
bool requestIncludesOnlyOneQuestion();
void replyWithIP();
void replyWithCustomCode();
};
#endif

32
libraries/WebServer/.gitignore vendored Normal file
View File

@ -0,0 +1,32 @@
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app

View File

@ -0,0 +1,99 @@
sudo: false
language: python
python:
- "2.7"
os:
- linux
env:
- TESTCASE="1.8.3|esp8266|stable|esp8266:esp8266:nodemcuv2:CpuFrequency=80,FlashSize=4M3M"
- TESTCASE="1.8.3|esp8266|devel|esp8266com:esp8266:nodemcuv2:CpuFrequency=80,FlashSize=4M3M"
- TESTCASE="1.8.3|esp32|devel|espressif:esp32:esp32:FlashFreq=40"
script:
- echo -e "travis_fold:start:sketch_test_env_prepare"
- pip install pyserial
- IFS='|' read -r -a array <<< "${TESTCASE}"
- |
for index in "${!array[@]}"
do
case $index in
0) IDEVER="${array[index]}" ;;
1) PLATFORM="${array[index]}" ;;
2) RELEASE="${array[index]}" ;;
3) BOARD="${array[index]}" ;;
esac
done
- cd $HOME
- wget -O arduino.tar.xz https://downloads.arduino.cc/arduino-${IDEVER}-linux64.tar.xz
- tar xf arduino.tar.xz
- mv arduino-${IDEVER} $HOME/arduino_ide
- mkdir -p $HOME/Arduino/libraries
- IDEDIR="${HOME}/arduino_ide"
- export PATH="$IDEDIR:$PATH"
- cd ${IDEDIR}
- which arduino
- |
if [[ $PLATFORM == "esp8266" ]] && [[ $RELEASE == "stable" ]]
then
arduino --pref "boardsmanager.additional.urls=http://arduino.esp8266.com/stable/package_esp8266com_index.json" --save-prefs
arduino --install-boards "esp8266:esp8266"
fi
- |
if [[ $PLATFORM == "esp8266" ]] && [[ $RELEASE == "devel" ]]
then
mkdir -p hardware/esp8266com
cd hardware/esp8266com
git clone https://github.com/esp8266/Arduino.git esp8266
cd esp8266/tools
python get.py
fi
- |
if [[ $PLATFORM == "esp32" ]] && [[ $RELEASE == "devel" ]]
then
mkdir -p hardware/espressif
cd hardware/espressif
git clone https://github.com/espressif/arduino-esp32.git esp32
cd esp32/tools/
python get.py
fi
- arduino --pref "compiler.warning_level=all" --save-prefs
- echo -e "travis_fold:end:sketch_test_env_prepare"
- echo -e "travis_fold:start:install_library"
- cd $HOME/Arduino/libraries
- git clone https://github.com/bbx10/WebServer_tng ${HOME}/Arduino/libraries/WebServer
- echo -e "travis_fold:end:install_library"
- echo -e "travis_fold:start:AdvancedWebServer"
- cd ${HOME}/Arduino/libraries/WebServer/examples
- arduino --verbose-build --verify --board ${BOARD} AdvancedWebServer/AdvancedWebServer.ino
- echo -e "travis_fold:end:AdvancedWebServer"
- echo -e "travis_fold:start:HelloServer"
- arduino --verbose-build --verify --board ${BOARD} HelloServer/HelloServer.ino
- echo -e "travis_fold:end:HelloServer"
- echo -e "travis_fold:start:HttpBasicAuth"
- arduino --verbose-build --verify --board ${BOARD} HttpBasicAuth/HttpBasicAuth.ino
- echo -e "travis_fold:end:HttpBasicAuth"
- echo -e "travis_fold:start:SDWebServer"
- arduino --verbose-build --verify --board ${BOARD} SDWebServer/SDWebServer.ino
- echo -e "travis_fold:end:SDWebServer"
- echo -e "travis_fold:start:SimpleAuthentification"
- arduino --verbose-build --verify --board ${BOARD} SimpleAuthentification/SimpleAuthentification.ino
- echo -e "travis_fold:end:SimpleAuthentification"
- |
if [[ $PLATFORM == "esp8266" ]]
then
echo -e "travis_fold:start:FSBrowser"
arduino --verbose-build --verify --board ${BOARD} FSBrowser/FSBrowser.ino
echo -e "travis_fold:end:FSBrowser"
echo -e "travis_fold:start:WebUpdate"
arduino --verbose-build --verify --board ${BOARD} WebUpdate/WebUpdate.ino
echo -e "travis_fold:end:WebUpdate"
fi
notifications:
email:
on_success: change
on_failure: change

504
libraries/WebServer/LICENSE Normal file
View File

@ -0,0 +1,504 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
(This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.)
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
{description}
Copyright (C) {year} {fullname}
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random
Hacker.
{signature of Ty Coon}, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

View File

@ -0,0 +1,9 @@
# WebServer
ESP8266/ESP32 WebServer library
This is an experimental port of the ESP8266WebServer library that should work
on ESP8266 and ESP32. This is NOT an official repo supported by Espressif. Do
not depend on this code for anything important or expect it to be updated. Once
the official repo is created, this repo will be deleted.
Added Travis CI

View File

@ -0,0 +1,160 @@
/*
* Copyright (c) 2015, Majenko Technologies
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* * Neither the name of Majenko Technologies nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef ESP8266
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
ESP8266WebServer server ( 80 );
#else
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
WebServer server ( 80 );
#endif
const char *ssid = "YourSSIDHere";
const char *password = "YourPSKHere";
#ifdef LED_BUILTIN
const int led = LED_BUILTIN;
#else
const int led = 13;
#endif
void handleRoot() {
digitalWrite ( led, 1 );
char temp[400];
int sec = millis() / 1000;
int min = sec / 60;
int hr = min / 60;
snprintf ( temp, 400,
"<html>\
<head>\
<meta http-equiv='refresh' content='5'/>\
<title>ESP8266/ESP32 Demo</title>\
<style>\
body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\
</style>\
</head>\
<body>\
<h1>Hello from ESP8266/ESP32!</h1>\
<p>Uptime: %02d:%02d:%02d</p>\
<img src=\"/test.svg\" />\
</body>\
</html>",
hr, min % 60, sec % 60
);
server.send ( 200, "text/html", temp );
digitalWrite ( led, 0 );
}
void handleNotFound() {
digitalWrite ( led, 1 );
String message = "File Not Found\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += ( server.method() == HTTP_GET ) ? "GET" : "POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for ( uint8_t i = 0; i < server.args(); i++ ) {
message += " " + server.argName ( i ) + ": " + server.arg ( i ) + "\n";
}
server.send ( 404, "text/plain", message );
digitalWrite ( led, 0 );
}
void setup ( void ) {
pinMode ( led, OUTPUT );
digitalWrite ( led, 0 );
Serial.begin ( 115200 );
WiFi.begin ( ssid, password );
Serial.println ( "" );
// Wait for connection
while ( WiFi.status() != WL_CONNECTED ) {
delay ( 500 );
Serial.print ( "." );
}
Serial.println ( "" );
Serial.print ( "Connected to " );
Serial.println ( ssid );
Serial.print ( "IP address: " );
Serial.println ( WiFi.localIP() );
#ifdef ESP8266
if ( MDNS.begin ( "esp8266" ) ) {
#else
if ( MDNS.begin ( "esp32" ) ) {
#endif
Serial.println ( "MDNS responder started" );
}
server.on ( "/", handleRoot );
server.on ( "/test.svg", drawGraph );
server.on ( "/inline", []() {
server.send ( 200, "text/plain", "this works as well" );
} );
server.onNotFound ( handleNotFound );
server.begin();
Serial.println ( "HTTP server started" );
}
void loop ( void ) {
server.handleClient();
}
void drawGraph() {
String out = "";
char temp[100];
out += "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" width=\"400\" height=\"150\">\n";
out += "<rect width=\"400\" height=\"150\" fill=\"rgb(250, 230, 210)\" stroke-width=\"1\" stroke=\"rgb(0, 0, 0)\" />\n";
out += "<g stroke=\"black\">\n";
int y = rand() % 130;
for (int x = 10; x < 390; x+= 10) {
int y2 = rand() % 130;
sprintf(temp, "<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" stroke-width=\"1\" />\n", x, 140 - y, x + 10, 140 - y2);
out += temp;
y = y2;
}
out += "</g>\n</svg>\n";
server.send ( 200, "image/svg+xml", out);
}

View File

@ -0,0 +1,345 @@
/*
FSWebServer - Example WebServer with SPIFFS backend for esp8266
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
This file is part of the ESP8266/ESP32 WebServer library for Arduino environment.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
upload the contents of the data folder with MkSPIFFS Tool ("ESP8266 Sketch Data Upload" in Tools menu in Arduino IDE)
or you can upload the contents of a folder if you CD in that folder and run the following command:
for file in `ls -A1`; do curl -F "file=@$PWD/$file" esp8266fs.local/edit; done
access the sample web page at http://esp8266fs.local
edit the page by going to http://esp8266fs.local/edit
*/
/*
* Uploading html, css, javascript, etc.
* Use curl to upload the files from the SPIFFS data directory.
* cd data/
* curl -X POST -F "data=@index.htm" http://<ESP32 IP address>/edit >/dev/null
* curl -X POST -F "data=@graphs.js.gz" http://<ESP32 IP address>/edit >/dev/null
* curl -X POST -F "data=@favicon.ico" http://<ESP32 IP address>/edit >/dev/null
* curl -X POST -F "data=@edit.htm.gz" http://<ESP32 IP address>/edit >/dev/null
*/
#ifdef ESP8266
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <FS.h>
ESP8266WebServer server(80);
#else
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <SPIFFS.h>
WebServer server(80);
#endif
#define DBG_OUTPUT_PORT Serial
const char* ssid = "wifi-ssid";
const char* password = "wifi-password";
const char* host = "esp8266fs";
//holds the current upload
File fsUploadFile;
//format bytes
String formatBytes(size_t bytes){
if (bytes < 1024){
return String(bytes)+"B";
} else if(bytes < (1024 * 1024)){
return String(bytes/1024.0)+"KB";
} else if(bytes < (1024 * 1024 * 1024)){
return String(bytes/1024.0/1024.0)+"MB";
} else {
return String(bytes/1024.0/1024.0/1024.0)+"GB";
}
}
String getContentType(String filename){
if(server.hasArg("download")) return "application/octet-stream";
else if(filename.endsWith(".htm")) return "text/html";
else if(filename.endsWith(".html")) return "text/html";
else if(filename.endsWith(".css")) return "text/css";
else if(filename.endsWith(".js")) return "application/javascript";
else if(filename.endsWith(".png")) return "image/png";
else if(filename.endsWith(".gif")) return "image/gif";
else if(filename.endsWith(".jpg")) return "image/jpeg";
else if(filename.endsWith(".ico")) return "image/x-icon";
else if(filename.endsWith(".xml")) return "text/xml";
else if(filename.endsWith(".pdf")) return "application/x-pdf";
else if(filename.endsWith(".zip")) return "application/x-zip";
else if(filename.endsWith(".gz")) return "application/x-gzip";
return "text/plain";
}
bool handleFileRead(String path){
DBG_OUTPUT_PORT.println("handleFileRead: " + path);
if(path.endsWith("/")) path += "index.htm";
String contentType = getContentType(path);
String pathWithGz = path + ".gz";
if(SPIFFS.exists(pathWithGz) || SPIFFS.exists(path)){
if(SPIFFS.exists(pathWithGz))
path += ".gz";
File file = SPIFFS.open(path, "r");
size_t sent = server.streamFile(file, contentType);
file.close();
return true;
}
return false;
}
void handleFileUpload(){
if(server.uri() != "/edit") return;
HTTPUpload& upload = server.upload();
if(upload.status == UPLOAD_FILE_START){
String filename = upload.filename;
if(!filename.startsWith("/")) filename = "/"+filename;
DBG_OUTPUT_PORT.print("handleFileUpload Name: "); DBG_OUTPUT_PORT.println(filename);
fsUploadFile = SPIFFS.open(filename, "w");
filename = String();
} else if(upload.status == UPLOAD_FILE_WRITE){
//DBG_OUTPUT_PORT.print("handleFileUpload Data: "); DBG_OUTPUT_PORT.println(upload.currentSize);
if(fsUploadFile)
fsUploadFile.write(upload.buf, upload.currentSize);
} else if(upload.status == UPLOAD_FILE_END){
if(fsUploadFile)
fsUploadFile.close();
DBG_OUTPUT_PORT.print("handleFileUpload Size: "); DBG_OUTPUT_PORT.println(upload.totalSize);
}
}
void handleFileDelete(){
if(server.args() == 0) return server.send(500, "text/plain", "BAD ARGS");
String path = server.arg(0);
DBG_OUTPUT_PORT.println("handleFileDelete: " + path);
if(path == "/")
return server.send(500, "text/plain", "BAD PATH");
if(!SPIFFS.exists(path))
return server.send(404, "text/plain", "FileNotFound");
SPIFFS.remove(path);
server.send(200, "text/plain", "");
path = String();
}
void handleFileCreate(){
if(server.args() == 0)
return server.send(500, "text/plain", "BAD ARGS");
String path = server.arg(0);
DBG_OUTPUT_PORT.println("handleFileCreate: " + path);
if(path == "/")
return server.send(500, "text/plain", "BAD PATH");
if(SPIFFS.exists(path))
return server.send(500, "text/plain", "FILE EXISTS");
File file = SPIFFS.open(path, "w");
if(file)
file.close();
else
return server.send(500, "text/plain", "CREATE FAILED");
server.send(200, "text/plain", "");
path = String();
}
void returnFail(String msg) {
server.send(500, "text/plain", msg + "\r\n");
}
#ifdef ESP8266
void handleFileList() {
if(!server.hasArg("dir")) {
returnFail("BAD ARGS");
return;
}
String path = server.arg("dir");
DBG_OUTPUT_PORT.println("handleFileList: " + path);
Dir dir = SPIFFS.openDir(path);
path = String();
String output = "[";
while(dir.next()){
File entry = dir.openFile("r");
if (output != "[") output += ',';
bool isDir = false;
output += "{\"type\":\"";
output += (isDir)?"dir":"file";
output += "\",\"name\":\"";
output += String(entry.name()).substring(1);
output += "\"}";
entry.close();
}
output += "]";
server.send(200, "text/json", output);
}
#else
void handleFileList() {
if(!server.hasArg("dir")) {
returnFail("BAD ARGS");
return;
}
String path = server.arg("dir");
if(path != "/" && !SPIFFS.exists((char *)path.c_str())) {
returnFail("BAD PATH");
return;
}
File dir = SPIFFS.open((char *)path.c_str());
path = String();
if(!dir.isDirectory()){
dir.close();
returnFail("NOT DIR");
return;
}
dir.rewindDirectory();
String output = "[";
for (int cnt = 0; true; ++cnt) {
File entry = dir.openNextFile();
if (!entry)
break;
if (cnt > 0)
output += ',';
output += "{\"type\":\"";
output += (entry.isDirectory()) ? "dir" : "file";
output += "\",\"name\":\"";
// Ignore '/' prefix
output += entry.name()+1;
output += "\"";
output += "}";
entry.close();
}
output += "]";
server.send(200, "text/json", output);
dir.close();
}
void listDir(fs::FS &fs, const char * dirname, uint8_t levels) {
DBG_OUTPUT_PORT.printf("Listing directory: %s\n", dirname);
File root = fs.open(dirname);
if (!root) {
DBG_OUTPUT_PORT.println("Failed to open directory");
return;
}
if (!root.isDirectory()) {
DBG_OUTPUT_PORT.println("Not a directory");
return;
}
File file = root.openNextFile();
while (file) {
if (file.isDirectory()) {
DBG_OUTPUT_PORT.print(" DIR : ");
DBG_OUTPUT_PORT.println(file.name());
if (levels) {
listDir(fs, file.name(), levels - 1);
}
} else {
DBG_OUTPUT_PORT.print(" FILE: ");
DBG_OUTPUT_PORT.print(file.name());
DBG_OUTPUT_PORT.print(" SIZE: ");
DBG_OUTPUT_PORT.println(file.size());
}
file = root.openNextFile();
}
}
#endif
void setup(void){
DBG_OUTPUT_PORT.begin(115200);
DBG_OUTPUT_PORT.print("\n");
DBG_OUTPUT_PORT.setDebugOutput(true);
SPIFFS.begin();
{
#ifdef ESP8266
Dir dir = SPIFFS.openDir("/");
while (dir.next()) {
String fileName = dir.fileName();
size_t fileSize = dir.fileSize();
DBG_OUTPUT_PORT.printf("FS File: %s, size: %s\n", fileName.c_str(), formatBytes(fileSize).c_str());
}
#else
listDir(SPIFFS, "/", 0);
#endif
DBG_OUTPUT_PORT.printf("\n");
}
//WIFI INIT
DBG_OUTPUT_PORT.printf("Connecting to %s\n", ssid);
if (String(WiFi.SSID()) != String(ssid)) {
WiFi.begin(ssid, password);
}
while (WiFi.status() != WL_CONNECTED) {
delay(500);
DBG_OUTPUT_PORT.print(".");
}
DBG_OUTPUT_PORT.println("");
DBG_OUTPUT_PORT.print("Connected! IP address: ");
DBG_OUTPUT_PORT.println(WiFi.localIP());
MDNS.begin(host);
DBG_OUTPUT_PORT.print("Open http://");
DBG_OUTPUT_PORT.print(host);
DBG_OUTPUT_PORT.println(".local/edit to see the file browser");
//SERVER INIT
//list directory
server.on("/list", HTTP_GET, handleFileList);
//load editor
server.on("/edit", HTTP_GET, [](){
if(!handleFileRead("/edit.htm")) server.send(404, "text/plain", "FileNotFound");
});
//create file
server.on("/edit", HTTP_PUT, handleFileCreate);
//delete file
server.on("/edit", HTTP_DELETE, handleFileDelete);
//first callback is called after the request has ended with all parsed arguments
//second callback handles file uploads at that location
server.on("/edit", HTTP_POST, [](){ server.send(200, "text/plain", ""); }, handleFileUpload);
//called when the url is not defined here
//use it to load content from SPIFFS
server.onNotFound([](){
if(!handleFileRead(server.uri()))
server.send(404, "text/plain", "FileNotFound");
});
//get heap status, analog input value and all GPIO statuses in one json call
server.on("/all", HTTP_GET, [](){
String json = "{";
json += "\"heap\":"+String(ESP.getFreeHeap());
json += ", \"analog\":"+String(analogRead(A0));
#ifdef ESP8266
json += ", \"gpio\":"+String((uint32_t)(((GPI | GPO) & 0xFFFF) | ((GP16I & 0x01) << 16)));
#endif
json += "}";
server.send(200, "text/json", json);
json = String();
});
server.begin();
DBG_OUTPUT_PORT.println("HTTP server started");
}
void loop(void){
server.handleClient();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,97 @@
<!--
FSWebServer - Example Index Page
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
This file is part of the ESP8266WebServer library for Arduino environment.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-->
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>ESP Monitor</title>
<script type="text/javascript" src="graphs.js"></script>
<script type="text/javascript">
var heap,temp,digi;
var reloadPeriod = 1000;
var running = false;
function loadValues(){
if(!running) return;
var xh = new XMLHttpRequest();
xh.onreadystatechange = function(){
if (xh.readyState == 4){
if(xh.status == 200) {
var res = JSON.parse(xh.responseText);
heap.add(res.heap);
temp.add(res.analog);
digi.add(res.gpio);
if(running) setTimeout(loadValues, reloadPeriod);
} else running = false;
}
};
xh.open("GET", "/all", true);
xh.send(null);
};
function run(){
if(!running){
running = true;
loadValues();
}
}
function onBodyLoad(){
var refreshInput = document.getElementById("refresh-rate");
refreshInput.value = reloadPeriod;
refreshInput.onchange = function(e){
var value = parseInt(e.target.value);
reloadPeriod = (value > 0)?value:0;
e.target.value = reloadPeriod;
}
var stopButton = document.getElementById("stop-button");
stopButton.onclick = function(e){
running = false;
}
var startButton = document.getElementById("start-button");
startButton.onclick = function(e){
run();
}
// Example with 10K thermistor
//function calcThermistor(v) {
// var t = Math.log(((10230000 / v) - 10000));
// t = (1/(0.001129148+(0.000234125*t)+(0.0000000876741*t*t*t)))-273.15;
// return (t>120)?0:Math.round(t*10)/10;
//}
//temp = createGraph(document.getElementById("analog"), "Temperature", 100, 128, 10, 40, false, "cyan", calcThermistor);
temp = createGraph(document.getElementById("analog"), "Analog Input", 100, 128, 0, 1023, false, "cyan");
heap = createGraph(document.getElementById("heap"), "Current Heap", 100, 125, 0, 30000, true, "orange");
digi = createDigiGraph(document.getElementById("digital"), "GPIO", 100, 146, [0, 4, 5, 16], "gold");
run();
}
</script>
</head>
<body id="index" style="margin:0; padding:0;" onload="onBodyLoad()">
<div id="controls" style="display: block; border: 1px solid rgb(68, 68, 68); padding: 5px; margin: 5px; width: 362px; background-color: rgb(238, 238, 238);">
<label>Period (ms):</label>
<input type="number" id="refresh-rate"/>
<input type="button" id="start-button" value="Start"/>
<input type="button" id="stop-button" value="Stop"/>
</div>
<div id="heap"></div>
<div id="analog"></div>
<div id="digital"></div>
</body>
</html>

View File

@ -0,0 +1,91 @@
#ifdef ESP8266
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
ESP8266WebServer server(80);
#else
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
WebServer server(80);
#endif
const char* ssid = "........";
const char* password = "........";
#ifdef LED_BUILTIN
const int led = LED_BUILTIN;
#else
const int led = 13;
#endif
void handleRoot() {
digitalWrite(led, 1);
#ifdef ESP8266
server.send(200, "text/plain", "hello from esp8266!");
#else
server.send(200, "text/plain", "hello from esp32!");
#endif
digitalWrite(led, 0);
}
void handleNotFound(){
digitalWrite(led, 1);
String message = "File Not Found\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += (server.method() == HTTP_GET)?"GET":"POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for (uint8_t i=0; i<server.args(); i++){
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
}
server.send(404, "text/plain", message);
digitalWrite(led, 0);
}
void setup(void){
pinMode(led, OUTPUT);
digitalWrite(led, 0);
Serial.begin(115200);
WiFi.begin(ssid, password);
Serial.println("");
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
#ifdef ESP8266
if (MDNS.begin("esp8266")) {
#else
if (MDNS.begin("esp32")) {
#endif
Serial.println("MDNS responder started");
}
server.on("/", handleRoot);
server.on("/inline", [](){
server.send(200, "text/plain", "this works as well");
});
server.onNotFound(handleNotFound);
server.begin();
Serial.println("HTTP server started");
}
void loop(void){
server.handleClient();
}

View File

@ -0,0 +1,47 @@
#ifdef ESP8266
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <ArduinoOTA.h>
#include <ESP8266WebServer.h>
ESP8266WebServer server(80);
#else
#include <WiFi.h>
#include <ESPmDNS.h>
#include <ArduinoOTA.h>
#include <WebServer.h>
WebServer server(80);
#endif
const char* ssid = "........";
const char* password = "........";
const char* www_username = "admin";
const char* www_password = "esp8266esp32";
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
if(WiFi.waitForConnectResult() != WL_CONNECTED) {
Serial.println("WiFi Connect Failed! Rebooting...");
delay(1000);
ESP.restart();
}
ArduinoOTA.begin();
server.on("/", [](){
if(!server.authenticate(www_username, www_password))
return server.requestAuthentication();
server.send(200, "text/plain", "Login OK");
});
server.begin();
Serial.print("Open http://");
Serial.print(WiFi.localIP());
Serial.println("/ in your browser to see it working");
}
void loop() {
ArduinoOTA.handle();
server.handleClient();
}

View File

@ -0,0 +1,290 @@
/*
SDWebServer - Example WebServer with SD Card backend for esp8266/esp32
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
This file is part of the ESP8266/ESP32 WebServer library for Arduino environment.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Have a FAT Formatted SD Card connected to the SPI port of the ESP8266
The web root is the SD Card root folder
File extensions with more than 3 charecters are not supported by the SD Library
File Names longer than 8 charecters will be truncated by the SD library, so keep filenames shorter
index.htm is the default index (works on subfolders as well)
upload the contents of SdRoot to the root of the SDcard and access the editor by going to http://esp8266sd.local/edit
*/
#ifdef ESP8266
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <SPI.h>
#include <SD.h>
ESP8266WebServer server(80);
#else
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <SPI.h>
#include <SD.h>
WebServer server(80);
#endif
#define DBG_OUTPUT_PORT Serial
const char* ssid = "**********";
const char* password = "**********";
const char* host = "esp8266sd";
static bool hasSD = false;
File uploadFile;
void returnOK() {
server.send(200, "text/plain", "");
}
void returnFail(String msg) {
server.send(500, "text/plain", msg + "\r\n");
}
bool loadFromSdCard(String path){
String dataType = "text/plain";
if(path.endsWith("/")) path += "index.htm";
if(path.endsWith(".src")) path = path.substring(0, path.lastIndexOf("."));
else if(path.endsWith(".htm")) dataType = "text/html";
else if(path.endsWith(".css")) dataType = "text/css";
else if(path.endsWith(".js")) dataType = "application/javascript";
else if(path.endsWith(".png")) dataType = "image/png";
else if(path.endsWith(".gif")) dataType = "image/gif";
else if(path.endsWith(".jpg")) dataType = "image/jpeg";
else if(path.endsWith(".ico")) dataType = "image/x-icon";
else if(path.endsWith(".xml")) dataType = "text/xml";
else if(path.endsWith(".pdf")) dataType = "application/pdf";
else if(path.endsWith(".zip")) dataType = "application/zip";
File dataFile = SD.open(path.c_str());
if(dataFile.isDirectory()){
path += "/index.htm";
dataType = "text/html";
dataFile = SD.open(path.c_str());
}
if (!dataFile)
return false;
if (server.hasArg("download")) dataType = "application/octet-stream";
if (server.streamFile(dataFile, dataType) != dataFile.size()) {
DBG_OUTPUT_PORT.println("Sent less data than expected!");
}
dataFile.close();
return true;
}
void handleFileUpload(){
if(server.uri() != "/edit") return;
HTTPUpload& upload = server.upload();
if(upload.status == UPLOAD_FILE_START){
if(SD.exists((char *)upload.filename.c_str())) SD.remove((char *)upload.filename.c_str());
uploadFile = SD.open(upload.filename.c_str(), FILE_WRITE);
DBG_OUTPUT_PORT.print("Upload: START, filename: "); DBG_OUTPUT_PORT.println(upload.filename);
} else if(upload.status == UPLOAD_FILE_WRITE){
if(uploadFile) uploadFile.write(upload.buf, upload.currentSize);
DBG_OUTPUT_PORT.print("Upload: WRITE, Bytes: "); DBG_OUTPUT_PORT.println(upload.currentSize);
} else if(upload.status == UPLOAD_FILE_END){
if(uploadFile) uploadFile.close();
DBG_OUTPUT_PORT.print("Upload: END, Size: "); DBG_OUTPUT_PORT.println(upload.totalSize);
}
}
void deleteRecursive(String path){
File file = SD.open((char *)path.c_str());
if(!file.isDirectory()){
file.close();
SD.remove((char *)path.c_str());
return;
}
file.rewindDirectory();
while(true) {
File entry = file.openNextFile();
if (!entry) break;
String entryPath = path + "/" +entry.name();
if(entry.isDirectory()){
entry.close();
deleteRecursive(entryPath);
} else {
entry.close();
SD.remove((char *)entryPath.c_str());
}
yield();
}
SD.rmdir((char *)path.c_str());
file.close();
}
void handleDelete(){
if(server.args() == 0) return returnFail("BAD ARGS");
String path = server.arg(0);
if(path == "/" || !SD.exists((char *)path.c_str())) {
returnFail("BAD PATH");
return;
}
deleteRecursive(path);
returnOK();
}
void handleCreate(){
if(server.args() == 0) return returnFail("BAD ARGS");
String path = server.arg(0);
if(path == "/" || SD.exists((char *)path.c_str())) {
returnFail("BAD PATH");
return;
}
if(path.indexOf('.') > 0){
File file = SD.open((char *)path.c_str(), FILE_WRITE);
if(file){
#ifdef ESP8266
file.write((const char *)0);
#else
// TODO Create file with 0 bytes???
file.write(NULL, 0);
#endif
file.close();
}
} else {
SD.mkdir((char *)path.c_str());
}
returnOK();
}
void printDirectory() {
if(!server.hasArg("dir")) return returnFail("BAD ARGS");
String path = server.arg("dir");
if(path != "/" && !SD.exists((char *)path.c_str())) return returnFail("BAD PATH");
File dir = SD.open((char *)path.c_str());
path = String();
if(!dir.isDirectory()){
dir.close();
return returnFail("NOT DIR");
}
dir.rewindDirectory();
server.setContentLength(CONTENT_LENGTH_UNKNOWN);
server.send(200, "text/json", "");
WiFiClient client = server.client();
server.sendContent("[");
for (int cnt = 0; true; ++cnt) {
File entry = dir.openNextFile();
if (!entry)
break;
String output;
if (cnt > 0)
output = ',';
output += "{\"type\":\"";
output += (entry.isDirectory()) ? "dir" : "file";
output += "\",\"name\":\"";
#ifdef ESP8266
output += entry.name();
#else
// Ignore '/' prefix
output += entry.name()+1;
#endif
output += "\"";
output += "}";
server.sendContent(output);
entry.close();
}
server.sendContent("]");
// Send zero length chunk to terminate the HTTP body
server.sendContent("");
dir.close();
}
void handleNotFound(){
if(hasSD && loadFromSdCard(server.uri())) return;
String message = "SDCARD Not Detected\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += (server.method() == HTTP_GET)?"GET":"POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for (uint8_t i=0; i<server.args(); i++){
message += " NAME:"+server.argName(i) + "\n VALUE:" + server.arg(i) + "\n";
}
server.send(404, "text/plain", message);
DBG_OUTPUT_PORT.print(message);
}
void setup(void){
DBG_OUTPUT_PORT.begin(115200);
DBG_OUTPUT_PORT.setDebugOutput(true);
DBG_OUTPUT_PORT.print("\n");
WiFi.begin(ssid, password);
DBG_OUTPUT_PORT.print("Connecting to ");
DBG_OUTPUT_PORT.println(ssid);
// Wait for connection
uint8_t i = 0;
while (WiFi.status() != WL_CONNECTED && i++ < 20) {//wait 10 seconds
delay(500);
}
if(i == 21){
DBG_OUTPUT_PORT.print("Could not connect to ");
DBG_OUTPUT_PORT.println(ssid);
while(1) delay(500);
}
DBG_OUTPUT_PORT.print("Connected! IP address: ");
DBG_OUTPUT_PORT.println(WiFi.localIP());
if (MDNS.begin(host)) {
MDNS.addService("http", "tcp", 80);
DBG_OUTPUT_PORT.println("MDNS responder started");
DBG_OUTPUT_PORT.print("You can now connect to http://");
DBG_OUTPUT_PORT.print(host);
DBG_OUTPUT_PORT.println(".local");
}
server.on("/list", HTTP_GET, printDirectory);
server.on("/edit", HTTP_DELETE, handleDelete);
server.on("/edit", HTTP_PUT, handleCreate);
server.on("/edit", HTTP_POST, [](){ returnOK(); }, handleFileUpload);
server.onNotFound(handleNotFound);
server.begin();
DBG_OUTPUT_PORT.println("HTTP server started");
if (SD.begin(SS)){
DBG_OUTPUT_PORT.println("SD Card initialized.");
hasSD = true;
}
}
void loop(void){
server.handleClient();
}

View File

@ -0,0 +1,674 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>SD Editor</title>
<style type="text/css" media="screen">
.contextMenu {
z-index: 300;
position: absolute;
left: 5px;
border: 1px solid #444;
background-color: #F5F5F5;
display: none;
box-shadow: 0 0 10px rgba( 0, 0, 0, .4 );
font-size: 12px;
font-family: sans-serif;
font-weight:bold;
}
.contextMenu ul {
list-style: none;
top: 0;
left: 0;
margin: 0;
padding: 0;
}
.contextMenu li {
position: relative;
min-width: 60px;
cursor: pointer;
}
.contextMenu span {
color: #444;
display: inline-block;
padding: 6px;
}
.contextMenu li:hover { background: #444; }
.contextMenu li:hover span { color: #EEE; }
.css-treeview ul, .css-treeview li {
padding: 0;
margin: 0;
list-style: none;
}
.css-treeview input {
position: absolute;
opacity: 0;
}
.css-treeview {
font: normal 11px Verdana, Arial, Sans-serif;
-moz-user-select: none;
-webkit-user-select: none;
user-select: none;
}
.css-treeview span {
color: #00f;
cursor: pointer;
}
.css-treeview span:hover {
text-decoration: underline;
}
.css-treeview input + label + ul {
margin: 0 0 0 22px;
}
.css-treeview input ~ ul {
display: none;
}
.css-treeview label, .css-treeview label::before {
cursor: pointer;
}
.css-treeview input:disabled + label {
cursor: default;
opacity: .6;
}
.css-treeview input:checked:not(:disabled) ~ ul {
display: block;
}
.css-treeview label, .css-treeview label::before {
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAACgCAYAAAAFOewUAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAApxJREFUeNrslM1u00AQgGdthyalFFOK+ClIIKQKyqUVQvTEE3DmAhLwAhU8QZoH4A2Q2gMSFace4MCtJ8SPBFwAkRuiHKpA6sRN/Lu7zG5i14kctaUqRGhGXnu9O/Pt7MzsMiklvF+9t2kWTDvyIrAsA0aKRRi1T0C/hJ4LUbt5/8rNpWVlp8RSr9J40b48fxFaTQ9+ft8EZ6MJYb0Ok+dnYGpmPgXwKIAvLx8vYXc5GdMAQJgQEkpjRTh36TS2U+DWW/D17WuYgm8pwJyY1npZsZKOxImOV1I/h4+O6vEg5GCZBpgmA6hX8wHKUHDRBXQYicQ4rlc3Tf0VMs8DHBS864F2YFspjgUYjKX/Az3gsdQd2eeBHwmdGWXHcgBGSkZXOXohcEXebRoQcAgjqediNY+AVyu3Z3sAKqfKoGMsewBeEIOPgQxxPJIjcGH6qtL/0AdADzKGnuuD+2tLK7Q8DhHHbOBW+KEzcHLuYc82MkEUekLiwuvVH+guQBQzOG4XdAb8EOcRcqQvDkY2iCLuxECJ43JobMXoutqGgDa2T7UqLKwt9KRyuxKVByqVXXqIoCCUCAqhUOioTWC7G4TQEOD0APy2/7G2Xpu1J4+lxeQ4TXBbITDpoVelRN/BVFbwu5oMMJUBhoXy5tmdRcMwymP2OLQaLjx9/vnBo6V3K6izATmSnMa0Dq7ferIohJhr1p01zrlz49rZF4OMs8JkX23vVQzYp+wbYGV/KpXKjvspl8tsIKCrMNAYFxj2GKS5ZWxg4ewKsJfaGMIY5KXqPz8LBBj6+yDvVP79+yDp/9F9oIx3OisHWwe7Oal0HxCAAAQgAAEIQAACEIAABCAAAQhAAAIQgAAEIAABCEAAAhCAAAQgwD8E/BZgAP0qhKj3rXO7AAAAAElFTkSuQmCC") no-repeat;
}
.css-treeview label, .css-treeview span, .css-treeview label::before {
display: inline-block;
height: 16px;
line-height: 16px;
vertical-align: middle;
}
.css-treeview label {
background-position: 18px 0;
}
.css-treeview label::before {
content: "";
width: 16px;
margin: 0 22px 0 0;
vertical-align: middle;
background-position: 0 -32px;
}
.css-treeview input:checked + label::before {
background-position: 0 -16px;
}
/* webkit adjacent element selector bugfix */
@media screen and (-webkit-min-device-pixel-ratio:0)
{
.css-treeview{
-webkit-animation: webkit-adjacent-element-selector-bugfix infinite 1s;
}
@-webkit-keyframes webkit-adjacent-element-selector-bugfix
{
from {
padding: 0;
}
to {
padding: 0;
}
}
}
#uploader {
position: absolute;
top: 0;
right: 0;
left: 0;
height:28px;
line-height: 24px;
padding-left: 10px;
background-color: #444;
color:#EEE;
}
#tree {
position: absolute;
top: 28px;
bottom: 0;
left: 0;
width:200px;
padding: 8px;
}
#editor, #preview {
position: absolute;
top: 28px;
right: 0;
bottom: 0;
left: 200px;
}
#preview {
background-color: #EEE;
padding:5px;
}
</style>
<script>
function createFileUploader(element, tree, editor){
var xmlHttp;
var input = document.createElement("input");
input.type = "file";
input.multiple = false;
input.name = "data";
document.getElementById(element).appendChild(input);
var path = document.createElement("input");
path.id = "upload-path";
path.type = "text";
path.name = "path";
path.defaultValue = "/";
document.getElementById(element).appendChild(path);
var button = document.createElement("button");
button.innerHTML = 'Upload';
document.getElementById(element).appendChild(button);
var mkdir = document.createElement("button");
mkdir.innerHTML = 'MkDir';
document.getElementById(element).appendChild(mkdir);
var mkfile = document.createElement("button");
mkfile.innerHTML = 'MkFile';
document.getElementById(element).appendChild(mkfile);
function httpPostProcessRequest(){
if (xmlHttp.readyState == 4){
if(xmlHttp.status != 200) alert("ERROR["+xmlHttp.status+"]: "+xmlHttp.responseText);
else {
tree.refreshPath(path.value);
}
}
}
function createPath(p){
xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = httpPostProcessRequest;
var formData = new FormData();
formData.append("path", p);
xmlHttp.open("PUT", "/edit");
xmlHttp.send(formData);
}
mkfile.onclick = function(e){
if(path.value.indexOf(".") === -1) return;
createPath(path.value);
editor.loadUrl(path.value);
};
mkdir.onclick = function(e){
if(path.value.length < 2) return;
var dir = path.value
if(dir.indexOf(".") !== -1){
if(dir.lastIndexOf("/") === 0) return;
dir = dir.substring(0, dir.lastIndexOf("/"));
}
createPath(dir);
};
button.onclick = function(e){
if(input.files.length === 0){
return;
}
xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = httpPostProcessRequest;
var formData = new FormData();
formData.append("data", input.files[0], path.value);
xmlHttp.open("POST", "/edit");
xmlHttp.send(formData);
}
input.onchange = function(e){
if(input.files.length === 0) return;
var filename = input.files[0].name;
var ext = /(?:\.([^.]+))?$/.exec(filename)[1];
var name = /(.*)\.[^.]+$/.exec(filename)[1];
if(typeof name !== undefined){
if(name.length > 8) name = name.substring(0, 8);
filename = name;
}
if(typeof ext !== undefined){
if(ext === "html") ext = "htm";
else if(ext === "jpeg") ext = "jpg";
filename = filename + "." + ext;
}
if(path.value === "/" || path.value.lastIndexOf("/") === 0){
path.value = "/"+filename;
} else {
path.value = path.value.substring(0, path.value.lastIndexOf("/")+1)+filename;
}
}
}
function createTree(element, editor){
var preview = document.getElementById("preview");
var treeRoot = document.createElement("div");
treeRoot.className = "css-treeview";
document.getElementById(element).appendChild(treeRoot);
function loadDownload(path){
document.getElementById('download-frame').src = path+"?download=true";
}
function loadPreview(path){
document.getElementById("editor").style.display = "none";
preview.style.display = "block";
preview.innerHTML = '<img src="'+path+'" style="max-width:100%; max-height:100%; margin:auto; display:block;" />';
}
function fillFolderMenu(el, path){
var list = document.createElement("ul");
el.appendChild(list);
var action = document.createElement("li");
list.appendChild(action);
var isChecked = document.getElementById(path).checked;
var expnd = document.createElement("li");
list.appendChild(expnd);
if(isChecked){
expnd.innerHTML = "<span>Collapse</span>";
expnd.onclick = function(e){
document.getElementById(path).checked = false;
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
};
var refrsh = document.createElement("li");
list.appendChild(refrsh);
refrsh.innerHTML = "<span>Refresh</span>";
refrsh.onclick = function(e){
var leaf = document.getElementById(path).parentNode;
if(leaf.childNodes.length == 3) leaf.removeChild(leaf.childNodes[2]);
httpGet(leaf, path);
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
};
} else {
expnd.innerHTML = "<span>Expand</span>";
expnd.onclick = function(e){
document.getElementById(path).checked = true;
var leaf = document.getElementById(path).parentNode;
if(leaf.childNodes.length == 3) leaf.removeChild(leaf.childNodes[2]);
httpGet(leaf, path);
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
};
}
var upload = document.createElement("li");
list.appendChild(upload);
upload.innerHTML = "<span>Upload</span>";
upload.onclick = function(e){
var pathEl = document.getElementById("upload-path");
if(pathEl){
var subPath = pathEl.value;
if(subPath.lastIndexOf("/") < 1) pathEl.value = path+subPath;
else pathEl.value = path.substring(subPath.lastIndexOf("/"))+subPath;
}
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
};
var delFile = document.createElement("li");
list.appendChild(delFile);
delFile.innerHTML = "<span>Delete</span>";
delFile.onclick = function(e){
httpDelete(path);
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
};
}
function fillFileMenu(el, path){
var list = document.createElement("ul");
el.appendChild(list);
var action = document.createElement("li");
list.appendChild(action);
if(isTextFile(path)){
action.innerHTML = "<span>Edit</span>";
action.onclick = function(e){
editor.loadUrl(path);
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
};
} else if(isImageFile(path)){
action.innerHTML = "<span>Preview</span>";
action.onclick = function(e){
loadPreview(path);
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
};
}
var download = document.createElement("li");
list.appendChild(download);
download.innerHTML = "<span>Download</span>";
download.onclick = function(e){
loadDownload(path);
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
};
var delFile = document.createElement("li");
list.appendChild(delFile);
delFile.innerHTML = "<span>Delete</span>";
delFile.onclick = function(e){
httpDelete(path);
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
};
}
function showContextMenu(e, path, isfile){
var divContext = document.createElement("div");
var scrollTop = document.body.scrollTop ? document.body.scrollTop : document.documentElement.scrollTop;
var scrollLeft = document.body.scrollLeft ? document.body.scrollLeft : document.documentElement.scrollLeft;
var left = e.clientX + scrollLeft;
var top = e.clientY + scrollTop;
divContext.className = 'contextMenu';
divContext.style.display = 'block';
divContext.style.left = left + 'px';
divContext.style.top = top + 'px';
if(isfile) fillFileMenu(divContext, path);
else fillFolderMenu(divContext, path);
document.body.appendChild(divContext);
var width = divContext.offsetWidth;
var height = divContext.offsetHeight;
divContext.onmouseout = function(e){
if(e.clientX < left || e.clientX > (left + width) || e.clientY < top || e.clientY > (top + height)){
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(divContext);
}
};
}
function createTreeLeaf(path, name, size){
var leaf = document.createElement("li");
leaf.id = (((path == "/")?"":path)+"/"+name).toLowerCase();
var label = document.createElement("span");
label.textContent = name.toLowerCase();
leaf.appendChild(label);
leaf.onclick = function(e){
if(isTextFile(leaf.id)){
editor.loadUrl(leaf.id);
} else if(isImageFile(leaf.id)){
loadPreview(leaf.id);
}
};
leaf.oncontextmenu = function(e){
e.preventDefault();
e.stopPropagation();
showContextMenu(e, leaf.id, true);
};
return leaf;
}
function createTreeBranch(path, name, disabled){
var leaf = document.createElement("li");
var check = document.createElement("input");
check.type = "checkbox";
check.id = (((path == "/")?"":path)+"/"+name).toLowerCase();
if(typeof disabled !== "undefined" && disabled) check.disabled = "disabled";
leaf.appendChild(check);
var label = document.createElement("label");
label.for = check.id;
label.textContent = name.toLowerCase();
leaf.appendChild(label);
check.onchange = function(e){
if(check.checked){
if(leaf.childNodes.length == 3) leaf.removeChild(leaf.childNodes[2]);
httpGet(leaf, check.id);
}
};
label.onclick = function(e){
if(!check.checked){
check.checked = true;
if(leaf.childNodes.length == 3) leaf.removeChild(leaf.childNodes[2]);
httpGet(leaf, check.id);
} else {
check.checked = false;
}
};
leaf.oncontextmenu = function(e){
e.preventDefault();
e.stopPropagation();
showContextMenu(e, check.id, false);
}
return leaf;
}
function addList(parent, path, items){
var list = document.createElement("ul");
parent.appendChild(list);
var ll = items.length;
for(var i = 0; i < ll; i++){
var item = items[i];
var itemEl;
if(item.type === "file"){
itemEl = createTreeLeaf(path, item.name, item.size);
} else {
itemEl = createTreeBranch(path, item.name);
}
list.appendChild(itemEl);
}
}
function isTextFile(path){
var ext = /(?:\.([^.]+))?$/.exec(path)[1];
if(typeof ext !== undefined){
switch(ext){
case "txt":
case "htm":
case "html":
case "js":
case "json":
case "c":
case "h":
case "cpp":
case "css":
case "xml":
return true;
}
}
return false;
}
function isImageFile(path){
var ext = /(?:\.([^.]+))?$/.exec(path)[1];
if(typeof ext !== undefined){
switch(ext){
case "png":
case "jpg":
case "gif":
case "ico":
return true;
}
}
return false;
}
this.refreshPath = function(path){
if(path.lastIndexOf('/') < 1){
path = '/';
treeRoot.removeChild(treeRoot.childNodes[0]);
httpGet(treeRoot, "/");
} else {
path = path.substring(0, path.lastIndexOf('/'));
var leaf = document.getElementById(path).parentNode;
if(leaf.childNodes.length == 3) leaf.removeChild(leaf.childNodes[2]);
httpGet(leaf, path);
}
};
function delCb(path){
return function(){
if (xmlHttp.readyState == 4){
if(xmlHttp.status != 200){
alert("ERROR["+xmlHttp.status+"]: "+xmlHttp.responseText);
} else {
if(path.lastIndexOf('/') < 1){
path = '/';
treeRoot.removeChild(treeRoot.childNodes[0]);
httpGet(treeRoot, "/");
} else {
path = path.substring(0, path.lastIndexOf('/'));
var leaf = document.getElementById(path).parentNode;
if(leaf.childNodes.length == 3) leaf.removeChild(leaf.childNodes[2]);
httpGet(leaf, path);
}
}
}
}
}
function httpDelete(filename){
xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = delCb(filename);
var formData = new FormData();
formData.append("path", filename);
xmlHttp.open("DELETE", "/edit");
xmlHttp.send(formData);
}
function getCb(parent, path){
return function(){
if (xmlHttp.readyState == 4){
//clear loading
if(xmlHttp.status == 200) addList(parent, path, JSON.parse(xmlHttp.responseText));
}
}
}
function httpGet(parent, path){
xmlHttp = new XMLHttpRequest(parent, path);
xmlHttp.onreadystatechange = getCb(parent, path);
xmlHttp.open("GET", "/list?dir="+path, true);
xmlHttp.send(null);
//start loading
}
httpGet(treeRoot, "/");
return this;
}
function createEditor(element, file, lang, theme, type){
function getLangFromFilename(filename){
var lang = "plain";
var ext = /(?:\.([^.]+))?$/.exec(filename)[1];
if(typeof ext !== undefined){
switch(ext){
case "txt": lang = "plain"; break;
case "htm": lang = "html"; break;
case "js": lang = "javascript"; break;
case "c": lang = "c_cpp"; break;
case "cpp": lang = "c_cpp"; break;
case "css":
case "scss":
case "php":
case "html":
case "json":
case "xml":
lang = ext;
}
}
return lang;
}
if(typeof file === "undefined") file = "/index.htm";
if(typeof lang === "undefined"){
lang = getLangFromFilename(file);
}
if(typeof theme === "undefined") theme = "textmate";
if(typeof type === "undefined"){
type = "text/"+lang;
if(lang === "c_cpp") type = "text/plain";
}
var xmlHttp = null;
var editor = ace.edit(element);
//post
function httpPostProcessRequest(){
if (xmlHttp.readyState == 4){
if(xmlHttp.status != 200) alert("ERROR["+xmlHttp.status+"]: "+xmlHttp.responseText);
}
}
function httpPost(filename, data, type){
xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = httpPostProcessRequest;
var formData = new FormData();
formData.append("data", new Blob([data], { type: type }), filename);
xmlHttp.open("POST", "/edit");
xmlHttp.send(formData);
}
//get
function httpGetProcessRequest(){
if (xmlHttp.readyState == 4){
document.getElementById("preview").style.display = "none";
document.getElementById("editor").style.display = "block";
if(xmlHttp.status == 200) editor.setValue(xmlHttp.responseText);
else editor.setValue("");
editor.clearSelection();
}
}
function httpGet(theUrl){
xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = httpGetProcessRequest;
xmlHttp.open("GET", theUrl, true);
xmlHttp.send(null);
}
if(lang !== "plain") editor.getSession().setMode("ace/mode/"+lang);
editor.setTheme("ace/theme/"+theme);
editor.$blockScrolling = Infinity;
editor.getSession().setUseSoftTabs(true);
editor.getSession().setTabSize(2);
editor.setHighlightActiveLine(true);
editor.setShowPrintMargin(false);
editor.commands.addCommand({
name: 'saveCommand',
bindKey: {win: 'Ctrl-S', mac: 'Command-S'},
exec: function(editor) {
httpPost(file, editor.getValue()+"", type);
},
readOnly: false
});
editor.commands.addCommand({
name: 'undoCommand',
bindKey: {win: 'Ctrl-Z', mac: 'Command-Z'},
exec: function(editor) {
editor.getSession().getUndoManager().undo(false);
},
readOnly: false
});
editor.commands.addCommand({
name: 'redoCommand',
bindKey: {win: 'Ctrl-Shift-Z', mac: 'Command-Shift-Z'},
exec: function(editor) {
editor.getSession().getUndoManager().redo(false);
},
readOnly: false
});
httpGet(file);
editor.loadUrl = function(filename){
file = filename;
lang = getLangFromFilename(file);
type = "text/"+lang;
if(lang !== "plain") editor.getSession().setMode("ace/mode/"+lang);
httpGet(file);
}
return editor;
}
function onBodyLoad(){
var vars = {};
var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) { vars[key] = value; });
var editor = createEditor("editor", vars.file, vars.lang, vars.theme);
var tree = createTree("tree", editor);
createFileUploader("uploader", tree, editor);
};
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.1.9/ace.js" type="text/javascript" charset="utf-8"></script>
</head>
<body onload="onBodyLoad();">
<div id="uploader"></div>
<div id="tree"></div>
<div id="editor"></div>
<div id="preview" style="display:none;"></div>
<iframe id=download-frame style='display:none;'></iframe>
</body>
</html>

View File

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>ESP Index</title>
<style>
body {
background-color:black;
color:white;
}
</style>
<script type="text/javascript">
function onBodyLoad(){
console.log("we are loaded!!");
}
</script>
</head>
<body id="index" onload="onBodyLoad()">
<h1>ESP8266 Pin Functions</h1>
<img src="pins.png" />
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

View File

@ -0,0 +1,137 @@
#ifdef ESP8266
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
ESP8266WebServer server(80);
#else
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
WebServer server(80);
#endif
const char* ssid = "........";
const char* password = "........";
//Check if header is present and correct
bool is_authentified(){
Serial.println("Enter is_authentified");
if (server.hasHeader("Cookie")){
Serial.print("Found cookie: ");
String cookie = server.header("Cookie");
Serial.println(cookie);
if (cookie.indexOf("ESPSESSIONID=1") != -1) {
Serial.println("Authentification Successful");
return true;
}
}
Serial.println("Authentification Failed");
return false;
}
//login page, also called for disconnect
void handleLogin(){
String msg;
if (server.hasHeader("Cookie")){
Serial.print("Found cookie: ");
String cookie = server.header("Cookie");
Serial.println(cookie);
}
if (server.hasArg("DISCONNECT")){
Serial.println("Disconnection");
server.sendHeader("Location","/login");
server.sendHeader("Cache-Control","no-cache");
server.sendHeader("Set-Cookie","ESPSESSIONID=0");
server.send(301);
return;
}
if (server.hasArg("USERNAME") && server.hasArg("PASSWORD")){
if (server.arg("USERNAME") == "admin" && server.arg("PASSWORD") == "admin" ){
server.sendHeader("Location","/");
server.sendHeader("Cache-Control","no-cache");
server.sendHeader("Set-Cookie","ESPSESSIONID=1");
server.send(301);
Serial.println("Log in Successful");
return;
}
msg = "Wrong username/password! try again.";
Serial.println("Log in Failed");
}
String content = "<html><body><form action='/login' method='POST'>To log in, please use : admin/admin<br>";
content += "User:<input type='text' name='USERNAME' placeholder='user name'><br>";
content += "Password:<input type='password' name='PASSWORD' placeholder='password'><br>";
content += "<input type='submit' name='SUBMIT' value='Submit'></form>" + msg + "<br>";
content += "You also can go <a href='/inline'>here</a></body></html>";
server.send(200, "text/html", content);
}
//root page can be accessed only if authentification is ok
void handleRoot(){
Serial.println("Enter handleRoot");
String header;
if (!is_authentified()){
server.sendHeader("Location","/login");
server.sendHeader("Cache-Control","no-cache");
server.send(301);
return;
}
String content = "<html><body><H2>hello, you successfully connected to esp8266/esp32!</H2><br>";
if (server.hasHeader("User-Agent")){
content += "the user agent used is : " + server.header("User-Agent") + "<br><br>";
}
content += "You can access this page until you <a href=\"/login?DISCONNECT=YES\">disconnect</a></body></html>";
server.send(200, "text/html", content);
}
//no need authentification
void handleNotFound(){
String message = "File Not Found\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += (server.method() == HTTP_GET)?"GET":"POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for (uint8_t i=0; i<server.args(); i++){
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
}
server.send(404, "text/plain", message);
}
void setup(void){
Serial.begin(115200);
WiFi.begin(ssid, password);
Serial.println("");
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
server.on("/", handleRoot);
server.on("/login", handleLogin);
server.on("/inline", [](){
server.send(200, "text/plain", "this works without need of authentification");
});
server.onNotFound(handleNotFound);
//here the list of headers to be recorded
const char * headerkeys[] = {"User-Agent","Cookie"} ;
size_t headerkeyssize = sizeof(headerkeys)/sizeof(char*);
//ask server to track these headers
server.collectHeaders(headerkeys, headerkeyssize );
server.begin();
Serial.println("HTTP server started");
}
void loop(void){
server.handleClient();
}

View File

@ -0,0 +1,78 @@
/*
To upload through terminal you can use: curl -F "image=@firmware.bin" esp8266-webupdate.local/update
*/
#ifdef ESP8266
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
ESP8266WebServer server(80);
const char* host = "esp8266-webupdate";
#else
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
WebServer server(80);
const char* host = "esp32-webupdate";
#endif
const char* ssid = "........";
const char* password = "........";
const char* serverIndex = "<form method='POST' action='/update' enctype='multipart/form-data'><input type='file' name='update'><input type='submit' value='Update'></form>";
void setup(void){
Serial.begin(115200);
Serial.println();
Serial.println("Booting Sketch...");
WiFi.mode(WIFI_AP_STA);
WiFi.begin(ssid, password);
if(WiFi.waitForConnectResult() == WL_CONNECTED){
MDNS.begin(host);
server.on("/", HTTP_GET, [](){
server.sendHeader("Connection", "close");
server.send(200, "text/html", serverIndex);
});
server.on("/update", HTTP_POST, [](){
server.sendHeader("Connection", "close");
server.send(200, "text/plain", (Update.hasError())?"FAIL":"OK");
ESP.restart();
},[](){
HTTPUpload& upload = server.upload();
if(upload.status == UPLOAD_FILE_START){
Serial.setDebugOutput(true);
WiFiUDP::stopAll();
Serial.printf("Update: %s\n", upload.filename.c_str());
uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
if(!Update.begin(maxSketchSpace)){//start with max available size
Update.printError(Serial);
}
} else if(upload.status == UPLOAD_FILE_WRITE){
if(Update.write(upload.buf, upload.currentSize) != upload.currentSize){
Update.printError(Serial);
}
} else if(upload.status == UPLOAD_FILE_END){
if(Update.end(true)){ //true to set the size to the current progress
Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
} else {
Update.printError(Serial);
}
Serial.setDebugOutput(false);
}
yield();
});
server.begin();
MDNS.addService("http", "tcp", 80);
Serial.printf("Ready! Open http://%s.local in your browser\n", host);
} else {
Serial.println("WiFi Failed");
}
}
void loop(void){
server.handleClient();
delay(1);
}

View File

@ -0,0 +1,36 @@
#######################################
# Syntax Coloring Map For Ultrasound
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
ESP8266WebServer KEYWORD1
HTTPMethod KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
begin KEYWORD2
handleClient KEYWORD2
on KEYWORD2
addHandler KEYWORD2
uri KEYWORD2
method KEYWORD2
client KEYWORD2
send KEYWORD2
arg KEYWORD2
argName KEYWORD2
args KEYWORD2
hasArg KEYWORD2
onNotFound KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################
HTTP_GET LITERAL1
HTTP_POST LITERAL1
HTTP_ANY LITERAL1

View File

@ -0,0 +1,9 @@
name=WebServer
version=1.0
author=Ivan Grokhotkov
maintainer=Ivan Grokhtkov <ivan@esp8266.com>
sentence=Simple web server library
paragraph=The library supports HTTP GET and POST requests, provides argument parsing, handles one client at a time.
category=Communication
url=
architectures=esp8266,esp32

View File

@ -0,0 +1,29 @@
/*
ESP8266WebServer.h - Dead simple web-server.
Supports only one simultaneous client, knows how to handle GET and POST.
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)
*/
#ifndef ESP8266WEBSERVER_H
#define ESP8266WEBSERVER_H
#include <WebServer.h>
#endif //ESP8266WEBSERVER_H

View File

@ -0,0 +1,610 @@
/*
Parsing.cpp - HTTP request parsing.
Copyright (c) 2015 Ivan Grokhotkov. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)
*/
#include <Arduino.h>
#include "WiFiServer.h"
#include "WiFiClient.h"
#include "WebServer.h"
//#define DEBUG_ESP_HTTP_SERVER
#ifdef DEBUG_ESP_PORT
#define DEBUG_OUTPUT DEBUG_ESP_PORT
#else
#define DEBUG_OUTPUT Serial
#endif
static char* readBytesWithTimeout(WiFiClient& client, size_t maxLength, size_t& dataLength, int timeout_ms)
{
char *buf = nullptr;
dataLength = 0;
while (dataLength < maxLength) {
int tries = timeout_ms;
size_t newLength;
while (!(newLength = client.available()) && tries--) delay(1);
if (!newLength) {
break;
}
if (!buf) {
buf = (char *) malloc(newLength + 1);
if (!buf) {
return nullptr;
}
}
else {
char* newBuf = (char *) realloc(buf, dataLength + newLength + 1);
if (!newBuf) {
free(buf);
return nullptr;
}
buf = newBuf;
}
client.readBytes(buf + dataLength, newLength);
dataLength += newLength;
buf[dataLength] = '\0';
}
return buf;
}
bool WebServer::_parseRequest(WiFiClient& client) {
// Read the first line of HTTP request
String req = client.readStringUntil('\r');
client.readStringUntil('\n');
//reset header value
for (int i = 0; i < _headerKeysCount; ++i) {
_currentHeaders[i].value =String();
}
// First line of HTTP request looks like "GET /path HTTP/1.1"
// Retrieve the "/path" part by finding the spaces
int addr_start = req.indexOf(' ');
int addr_end = req.indexOf(' ', addr_start + 1);
if (addr_start == -1 || addr_end == -1) {
#ifdef DEBUG_ESP_HTTP_SERVER
DEBUG_OUTPUT.print("Invalid request: ");
DEBUG_OUTPUT.println(req);
#endif
return false;
}
String methodStr = req.substring(0, addr_start);
String url = req.substring(addr_start + 1, addr_end);
String versionEnd = req.substring(addr_end + 8);
_currentVersion = atoi(versionEnd.c_str());
String searchStr = "";
int hasSearch = url.indexOf('?');
if (hasSearch != -1){
searchStr = url.substring(hasSearch + 1);
url = url.substring(0, hasSearch);
}
_currentUri = url;
_chunked = false;
HTTPMethod method = HTTP_GET;
if (methodStr == "POST") {
method = HTTP_POST;
} else if (methodStr == "DELETE") {
method = HTTP_DELETE;
} else if (methodStr == "OPTIONS") {
method = HTTP_OPTIONS;
} else if (methodStr == "PUT") {
method = HTTP_PUT;
} else if (methodStr == "PATCH") {
method = HTTP_PATCH;
}
_currentMethod = method;
#ifdef DEBUG_ESP_HTTP_SERVER
DEBUG_OUTPUT.print("method: ");
DEBUG_OUTPUT.print(methodStr);
DEBUG_OUTPUT.print(" url: ");
DEBUG_OUTPUT.print(url);
DEBUG_OUTPUT.print(" search: ");
DEBUG_OUTPUT.println(searchStr);
#endif
//attach handler
RequestHandler* handler;
for (handler = _firstHandler; handler; handler = handler->next()) {
if (handler->canHandle(_currentMethod, _currentUri))
break;
}
_currentHandler = handler;
String formData;
// below is needed only when POST type request
if (method == HTTP_POST || method == HTTP_PUT || method == HTTP_PATCH || method == HTTP_DELETE){
String boundaryStr;
String headerName;
String headerValue;
bool isForm = false;
bool isEncoded = false;
uint32_t contentLength = 0;
//parse headers
while(1){
req = client.readStringUntil('\r');
client.readStringUntil('\n');
if (req == "") break;//no moar headers
int headerDiv = req.indexOf(':');
if (headerDiv == -1){
break;
}
headerName = req.substring(0, headerDiv);
headerValue = req.substring(headerDiv + 1);
headerValue.trim();
_collectHeader(headerName.c_str(),headerValue.c_str());
#ifdef DEBUG_ESP_HTTP_SERVER
DEBUG_OUTPUT.print("headerName: ");
DEBUG_OUTPUT.println(headerName);
DEBUG_OUTPUT.print("headerValue: ");
DEBUG_OUTPUT.println(headerValue);
#endif
if (headerName.equalsIgnoreCase("Content-Type")){
if (headerValue.startsWith("text/plain")){
isForm = false;
} else if (headerValue.startsWith("application/x-www-form-urlencoded")){
isForm = false;
isEncoded = true;
} else if (headerValue.startsWith("multipart/")){
boundaryStr = headerValue.substring(headerValue.indexOf('=')+1);
isForm = true;
}
} else if (headerName.equalsIgnoreCase("Content-Length")){
contentLength = headerValue.toInt();
} else if (headerName.equalsIgnoreCase("Host")){
_hostHeader = headerValue;
}
}
if (!isForm){
size_t plainLength;
char* plainBuf = readBytesWithTimeout(client, contentLength, plainLength, HTTP_MAX_POST_WAIT);
if (plainLength < contentLength) {
free(plainBuf);
return false;
}
if (contentLength > 0) {
if (searchStr != "") searchStr += '&';
if(isEncoded){
//url encoded form
String decoded = urlDecode(plainBuf);
size_t decodedLen = decoded.length();
memcpy(plainBuf, decoded.c_str(), decodedLen);
plainBuf[decodedLen] = 0;
searchStr += plainBuf;
}
_parseArguments(searchStr);
if(!isEncoded){
//plain post json or other data
RequestArgument& arg = _currentArgs[_currentArgCount++];
arg.key = "plain";
arg.value = String(plainBuf);
}
#ifdef DEBUG_ESP_HTTP_SERVER
DEBUG_OUTPUT.print("Plain: ");
DEBUG_OUTPUT.println(plainBuf);
#endif
free(plainBuf);
} else {
// No content - but we can still have arguments in the URL.
_parseArguments(searchStr);
}
}
if (isForm){
_parseArguments(searchStr);
if (!_parseForm(client, boundaryStr, contentLength)) {
return false;
}
}
} else {
String headerName;
String headerValue;
//parse headers
while(1){
req = client.readStringUntil('\r');
client.readStringUntil('\n');
if (req == "") break;//no moar headers
int headerDiv = req.indexOf(':');
if (headerDiv == -1){
break;
}
headerName = req.substring(0, headerDiv);
headerValue = req.substring(headerDiv + 2);
_collectHeader(headerName.c_str(),headerValue.c_str());
#ifdef DEBUG_ESP_HTTP_SERVER
DEBUG_OUTPUT.print("headerName: ");
DEBUG_OUTPUT.println(headerName);
DEBUG_OUTPUT.print("headerValue: ");
DEBUG_OUTPUT.println(headerValue);
#endif
if (headerName.equalsIgnoreCase("Host")){
_hostHeader = headerValue;
}
}
_parseArguments(searchStr);
}
client.flush();
#ifdef DEBUG_ESP_HTTP_SERVER
DEBUG_OUTPUT.print("Request: ");
DEBUG_OUTPUT.println(url);
DEBUG_OUTPUT.print(" Arguments: ");
DEBUG_OUTPUT.println(searchStr);
#endif
return true;
}
bool WebServer::_collectHeader(const char* headerName, const char* headerValue) {
for (int i = 0; i < _headerKeysCount; i++) {
if (_currentHeaders[i].key.equalsIgnoreCase(headerName)) {
_currentHeaders[i].value=headerValue;
return true;
}
}
return false;
}
void WebServer::_parseArguments(String data) {
#ifdef DEBUG_ESP_HTTP_SERVER
DEBUG_OUTPUT.print("args: ");
DEBUG_OUTPUT.println(data);
#endif
if (_currentArgs)
delete[] _currentArgs;
_currentArgs = 0;
if (data.length() == 0) {
_currentArgCount = 0;
_currentArgs = new RequestArgument[1];
return;
}
_currentArgCount = 1;
for (int i = 0; i < (int)data.length(); ) {
i = data.indexOf('&', i);
if (i == -1)
break;
++i;
++_currentArgCount;
}
#ifdef DEBUG_ESP_HTTP_SERVER
DEBUG_OUTPUT.print("args count: ");
DEBUG_OUTPUT.println(_currentArgCount);
#endif
_currentArgs = new RequestArgument[_currentArgCount+1];
int pos = 0;
int iarg;
for (iarg = 0; iarg < _currentArgCount;) {
int equal_sign_index = data.indexOf('=', pos);
int next_arg_index = data.indexOf('&', pos);
#ifdef DEBUG_ESP_HTTP_SERVER
DEBUG_OUTPUT.print("pos ");
DEBUG_OUTPUT.print(pos);
DEBUG_OUTPUT.print("=@ ");
DEBUG_OUTPUT.print(equal_sign_index);
DEBUG_OUTPUT.print(" &@ ");
DEBUG_OUTPUT.println(next_arg_index);
#endif
if ((equal_sign_index == -1) || ((equal_sign_index > next_arg_index) && (next_arg_index != -1))) {
#ifdef DEBUG_ESP_HTTP_SERVER
DEBUG_OUTPUT.print("arg missing value: ");
DEBUG_OUTPUT.println(iarg);
#endif
if (next_arg_index == -1)
break;
pos = next_arg_index + 1;
continue;
}
RequestArgument& arg = _currentArgs[iarg];
arg.key = data.substring(pos, equal_sign_index);
arg.value = urlDecode(data.substring(equal_sign_index + 1, next_arg_index));
#ifdef DEBUG_ESP_HTTP_SERVER
DEBUG_OUTPUT.print("arg ");
DEBUG_OUTPUT.print(iarg);
DEBUG_OUTPUT.print(" key: ");
DEBUG_OUTPUT.print(arg.key);
DEBUG_OUTPUT.print(" value: ");
DEBUG_OUTPUT.println(arg.value);
#endif
++iarg;
if (next_arg_index == -1)
break;
pos = next_arg_index + 1;
}
_currentArgCount = iarg;
#ifdef DEBUG_ESP_HTTP_SERVER
DEBUG_OUTPUT.print("args count: ");
DEBUG_OUTPUT.println(_currentArgCount);
#endif
}
void WebServer::_uploadWriteByte(uint8_t b){
if (_currentUpload.currentSize == HTTP_UPLOAD_BUFLEN){
if(_currentHandler && _currentHandler->canUpload(_currentUri))
_currentHandler->upload(*this, _currentUri, _currentUpload);
_currentUpload.totalSize += _currentUpload.currentSize;
_currentUpload.currentSize = 0;
}
_currentUpload.buf[_currentUpload.currentSize++] = b;
}
uint8_t WebServer::_uploadReadByte(WiFiClient& client){
int res = client.read();
if(res == -1){
while(!client.available() && client.connected())
yield();
res = client.read();
}
return (uint8_t)res;
}
bool WebServer::_parseForm(WiFiClient& client, String boundary, uint32_t len){
(void) len;
#ifdef DEBUG_ESP_HTTP_SERVER
DEBUG_OUTPUT.print("Parse Form: Boundary: ");
DEBUG_OUTPUT.print(boundary);
DEBUG_OUTPUT.print(" Length: ");
DEBUG_OUTPUT.println(len);
#endif
String line;
int retry = 0;
do {
line = client.readStringUntil('\r');
++retry;
} while (line.length() == 0 && retry < 3);
client.readStringUntil('\n');
//start reading the form
if (line == ("--"+boundary)){
RequestArgument* postArgs = new RequestArgument[32];
int postArgsLen = 0;
while(1){
String argName;
String argValue;
String argType;
String argFilename;
bool argIsFile = false;
line = client.readStringUntil('\r');
client.readStringUntil('\n');
if (line.length() > 19 && line.substring(0, 19).equalsIgnoreCase("Content-Disposition")){
int nameStart = line.indexOf('=');
if (nameStart != -1){
argName = line.substring(nameStart+2);
nameStart = argName.indexOf('=');
if (nameStart == -1){
argName = argName.substring(0, argName.length() - 1);
} else {
argFilename = argName.substring(nameStart+2, argName.length() - 1);
argName = argName.substring(0, argName.indexOf('"'));
argIsFile = true;
#ifdef DEBUG_ESP_HTTP_SERVER
DEBUG_OUTPUT.print("PostArg FileName: ");
DEBUG_OUTPUT.println(argFilename);
#endif
//use GET to set the filename if uploading using blob
if (argFilename == "blob" && hasArg("filename")) argFilename = arg("filename");
}
#ifdef DEBUG_ESP_HTTP_SERVER
DEBUG_OUTPUT.print("PostArg Name: ");
DEBUG_OUTPUT.println(argName);
#endif
argType = "text/plain";
line = client.readStringUntil('\r');
client.readStringUntil('\n');
if (line.length() > 12 && line.substring(0, 12).equalsIgnoreCase("Content-Type")){
argType = line.substring(line.indexOf(':')+2);
//skip next line
client.readStringUntil('\r');
client.readStringUntil('\n');
}
#ifdef DEBUG_ESP_HTTP_SERVER
DEBUG_OUTPUT.print("PostArg Type: ");
DEBUG_OUTPUT.println(argType);
#endif
if (!argIsFile){
while(1){
line = client.readStringUntil('\r');
client.readStringUntil('\n');
if (line.startsWith("--"+boundary)) break;
if (argValue.length() > 0) argValue += "\n";
argValue += line;
}
#ifdef DEBUG_ESP_HTTP_SERVER
DEBUG_OUTPUT.print("PostArg Value: ");
DEBUG_OUTPUT.println(argValue);
DEBUG_OUTPUT.println();
#endif
RequestArgument& arg = postArgs[postArgsLen++];
arg.key = argName;
arg.value = argValue;
if (line == ("--"+boundary+"--")){
#ifdef DEBUG_ESP_HTTP_SERVER
DEBUG_OUTPUT.println("Done Parsing POST");
#endif
break;
}
} else {
_currentUpload.status = UPLOAD_FILE_START;
_currentUpload.name = argName;
_currentUpload.filename = argFilename;
_currentUpload.type = argType;
_currentUpload.totalSize = 0;
_currentUpload.currentSize = 0;
#ifdef DEBUG_ESP_HTTP_SERVER
DEBUG_OUTPUT.print("Start File: ");
DEBUG_OUTPUT.print(_currentUpload.filename);
DEBUG_OUTPUT.print(" Type: ");
DEBUG_OUTPUT.println(_currentUpload.type);
#endif
if(_currentHandler && _currentHandler->canUpload(_currentUri))
_currentHandler->upload(*this, _currentUri, _currentUpload);
_currentUpload.status = UPLOAD_FILE_WRITE;
uint8_t argByte = _uploadReadByte(client);
readfile:
while(argByte != 0x0D){
if (!client.connected()) return _parseFormUploadAborted();
_uploadWriteByte(argByte);
argByte = _uploadReadByte(client);
}
argByte = _uploadReadByte(client);
if (!client.connected()) return _parseFormUploadAborted();
if (argByte == 0x0A){
argByte = _uploadReadByte(client);
if (!client.connected()) return _parseFormUploadAborted();
if ((char)argByte != '-'){
//continue reading the file
_uploadWriteByte(0x0D);
_uploadWriteByte(0x0A);
goto readfile;
} else {
argByte = _uploadReadByte(client);
if (!client.connected()) return _parseFormUploadAborted();
if ((char)argByte != '-'){
//continue reading the file
_uploadWriteByte(0x0D);
_uploadWriteByte(0x0A);
_uploadWriteByte((uint8_t)('-'));
goto readfile;
}
}
uint8_t endBuf[boundary.length()];
client.readBytes(endBuf, boundary.length());
if (strstr((const char*)endBuf, boundary.c_str()) != NULL){
if(_currentHandler && _currentHandler->canUpload(_currentUri))
_currentHandler->upload(*this, _currentUri, _currentUpload);
_currentUpload.totalSize += _currentUpload.currentSize;
_currentUpload.status = UPLOAD_FILE_END;
if(_currentHandler && _currentHandler->canUpload(_currentUri))
_currentHandler->upload(*this, _currentUri, _currentUpload);
#ifdef DEBUG_ESP_HTTP_SERVER
DEBUG_OUTPUT.print("End File: ");
DEBUG_OUTPUT.print(_currentUpload.filename);
DEBUG_OUTPUT.print(" Type: ");
DEBUG_OUTPUT.print(_currentUpload.type);
DEBUG_OUTPUT.print(" Size: ");
DEBUG_OUTPUT.println(_currentUpload.totalSize);
#endif
line = client.readStringUntil(0x0D);
client.readStringUntil(0x0A);
if (line == "--"){
#ifdef DEBUG_ESP_HTTP_SERVER
DEBUG_OUTPUT.println("Done Parsing POST");
#endif
break;
}
continue;
} else {
_uploadWriteByte(0x0D);
_uploadWriteByte(0x0A);
_uploadWriteByte((uint8_t)('-'));
_uploadWriteByte((uint8_t)('-'));
uint32_t i = 0;
while(i < boundary.length()){
_uploadWriteByte(endBuf[i++]);
}
argByte = _uploadReadByte(client);
goto readfile;
}
} else {
_uploadWriteByte(0x0D);
goto readfile;
}
break;
}
}
}
}
int iarg;
int totalArgs = ((32 - postArgsLen) < _currentArgCount)?(32 - postArgsLen):_currentArgCount;
for (iarg = 0; iarg < totalArgs; iarg++){
RequestArgument& arg = postArgs[postArgsLen++];
arg.key = _currentArgs[iarg].key;
arg.value = _currentArgs[iarg].value;
}
if (_currentArgs) delete[] _currentArgs;
_currentArgs = new RequestArgument[postArgsLen];
for (iarg = 0; iarg < postArgsLen; iarg++){
RequestArgument& arg = _currentArgs[iarg];
arg.key = postArgs[iarg].key;
arg.value = postArgs[iarg].value;
}
_currentArgCount = iarg;
if (postArgs) delete[] postArgs;
return true;
}
#ifdef DEBUG_ESP_HTTP_SERVER
DEBUG_OUTPUT.print("Error: line: ");
DEBUG_OUTPUT.println(line);
#endif
return false;
}
String WebServer::urlDecode(const String& text)
{
String decoded = "";
char temp[] = "0x00";
unsigned int len = text.length();
unsigned int i = 0;
while (i < len)
{
char decodedChar;
char encodedChar = text.charAt(i++);
if ((encodedChar == '%') && (i + 1 < len))
{
temp[2] = text.charAt(i++);
temp[3] = text.charAt(i++);
decodedChar = strtol(temp, NULL, 16);
}
else {
if (encodedChar == '+')
{
decodedChar = ' ';
}
else {
decodedChar = encodedChar; // normal ascii char
}
}
decoded += decodedChar;
}
return decoded;
}
bool WebServer::_parseFormUploadAborted(){
_currentUpload.status = UPLOAD_FILE_ABORTED;
if(_currentHandler && _currentHandler->canUpload(_currentUri))
_currentHandler->upload(*this, _currentUri, _currentUpload);
return false;
}

View File

@ -0,0 +1,530 @@
/*
WebServer.cpp - Dead simple web-server.
Supports only one simultaneous client, knows how to handle GET and POST.
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)
*/
#include <Arduino.h>
#include <libb64/cencode.h>
#include "WiFiServer.h"
#include "WiFiClient.h"
#include "WebServer.h"
#include "FS.h"
#include "detail/RequestHandlersImpl.h"
//#define DEBUG_ESP_HTTP_SERVER
#ifdef DEBUG_ESP_PORT
#define DEBUG_OUTPUT DEBUG_ESP_PORT
#else
#define DEBUG_OUTPUT Serial
#endif
const char * AUTHORIZATION_HEADER = "Authorization";
WebServer::WebServer(IPAddress addr, int port)
: _server(addr, port)
, _currentMethod(HTTP_ANY)
, _currentVersion(0)
, _currentStatus(HC_NONE)
, _statusChange(0)
, _currentHandler(0)
, _firstHandler(0)
, _lastHandler(0)
, _currentArgCount(0)
, _currentArgs(0)
, _headerKeysCount(0)
, _currentHeaders(0)
, _contentLength(0)
, _chunked(false)
{
}
WebServer::WebServer(int port)
: _server(port)
, _currentMethod(HTTP_ANY)
, _currentVersion(0)
, _currentStatus(HC_NONE)
, _statusChange(0)
, _currentHandler(0)
, _firstHandler(0)
, _lastHandler(0)
, _currentArgCount(0)
, _currentArgs(0)
, _headerKeysCount(0)
, _currentHeaders(0)
, _contentLength(0)
, _chunked(false)
{
}
WebServer::~WebServer() {
if (_currentHeaders)
delete[]_currentHeaders;
_headerKeysCount = 0;
RequestHandler* handler = _firstHandler;
while (handler) {
RequestHandler* next = handler->next();
delete handler;
handler = next;
}
close();
}
void WebServer::begin() {
_currentStatus = HC_NONE;
_server.begin();
if(!_headerKeysCount)
collectHeaders(0, 0);
}
bool WebServer::authenticate(const char * username, const char * password){
if(hasHeader(AUTHORIZATION_HEADER)){
String authReq = header(AUTHORIZATION_HEADER);
if(authReq.startsWith("Basic")){
authReq = authReq.substring(6);
authReq.trim();
char toencodeLen = strlen(username)+strlen(password)+1;
char *toencode = new char[toencodeLen + 1];
if(toencode == NULL){
authReq = String();
return false;
}
char *encoded = new char[base64_encode_expected_len(toencodeLen)+1];
if(encoded == NULL){
authReq = String();
delete[] toencode;
return false;
}
sprintf(toencode, "%s:%s", username, password);
if(base64_encode_chars(toencode, toencodeLen, encoded) > 0 && authReq.equals(encoded)){
authReq = String();
delete[] toencode;
delete[] encoded;
return true;
}
delete[] toencode;
delete[] encoded;
}
authReq = String();
}
return false;
}
void WebServer::requestAuthentication(){
sendHeader("WWW-Authenticate", "Basic realm=\"Login Required\"");
send(401);
}
void WebServer::on(const String &uri, WebServer::THandlerFunction handler) {
on(uri, HTTP_ANY, handler);
}
void WebServer::on(const String &uri, HTTPMethod method, WebServer::THandlerFunction fn) {
on(uri, method, fn, _fileUploadHandler);
}
void WebServer::on(const String &uri, HTTPMethod method, WebServer::THandlerFunction fn, WebServer::THandlerFunction ufn) {
_addRequestHandler(new FunctionRequestHandler(fn, ufn, uri, method));
}
void WebServer::addHandler(RequestHandler* handler) {
_addRequestHandler(handler);
}
void WebServer::_addRequestHandler(RequestHandler* handler) {
if (!_lastHandler) {
_firstHandler = handler;
_lastHandler = handler;
}
else {
_lastHandler->next(handler);
_lastHandler = handler;
}
}
void WebServer::serveStatic(const char* uri, FS& fs, const char* path, const char* cache_header) {
_addRequestHandler(new StaticRequestHandler(fs, path, uri, cache_header));
}
void WebServer::handleClient() {
if (_currentStatus == HC_NONE) {
WiFiClient client = _server.available();
if (!client) {
return;
}
#ifdef DEBUG_ESP_HTTP_SERVER
DEBUG_OUTPUT.println("New client");
#endif
_currentClient = client;
_currentStatus = HC_WAIT_READ;
_statusChange = millis();
}
if (!_currentClient.connected()) {
_currentClient = WiFiClient();
_currentStatus = HC_NONE;
return;
}
// Wait for data from client to become available
if (_currentStatus == HC_WAIT_READ) {
if (!_currentClient.available()) {
if (millis() - _statusChange > HTTP_MAX_DATA_WAIT) {
_currentClient = WiFiClient();
_currentStatus = HC_NONE;
}
yield();
return;
}
if (!_parseRequest(_currentClient)) {
_currentClient = WiFiClient();
_currentStatus = HC_NONE;
return;
}
_currentClient.setTimeout(HTTP_MAX_SEND_WAIT);
_contentLength = CONTENT_LENGTH_NOT_SET;
_handleRequest();
if (!_currentClient.connected()) {
_currentClient = WiFiClient();
_currentStatus = HC_NONE;
return;
} else {
_currentStatus = HC_WAIT_CLOSE;
_statusChange = millis();
return;
}
}
if (_currentStatus == HC_WAIT_CLOSE) {
if (millis() - _statusChange > HTTP_MAX_CLOSE_WAIT) {
_currentClient = WiFiClient();
_currentStatus = HC_NONE;
} else {
yield();
return;
}
}
}
void WebServer::close() {
#ifdef ESP8266
_server.stop();
#else
// TODO add ESP32 WiFiServer::stop()
_server.end();
#endif
}
void WebServer::stop() {
close();
}
void WebServer::sendHeader(const String& name, const String& value, bool first) {
String headerLine = name;
headerLine += ": ";
headerLine += value;
headerLine += "\r\n";
if (first) {
_responseHeaders = headerLine + _responseHeaders;
}
else {
_responseHeaders += headerLine;
}
}
void WebServer::setContentLength(size_t contentLength) {
_contentLength = contentLength;
}
void WebServer::_prepareHeader(String& response, int code, const char* content_type, size_t contentLength) {
response = "HTTP/1."+String(_currentVersion)+" ";
response += String(code);
response += " ";
response += _responseCodeToString(code);
response += "\r\n";
if (!content_type)
content_type = "text/html";
sendHeader("Content-Type", content_type, true);
if (_contentLength == CONTENT_LENGTH_NOT_SET) {
sendHeader("Content-Length", String(contentLength));
} else if (_contentLength != CONTENT_LENGTH_UNKNOWN) {
sendHeader("Content-Length", String(_contentLength));
} else if(_contentLength == CONTENT_LENGTH_UNKNOWN && _currentVersion){ //HTTP/1.1 or above client
//let's do chunked
_chunked = true;
sendHeader("Accept-Ranges","none");
sendHeader("Transfer-Encoding","chunked");
}
sendHeader("Connection", "close");
response += _responseHeaders;
response += "\r\n";
_responseHeaders = String();
}
void WebServer::send(int code, const char* content_type, const String& content) {
String header;
// Can we asume the following?
//if(code == 200 && content.length() == 0 && _contentLength == CONTENT_LENGTH_NOT_SET)
// _contentLength = CONTENT_LENGTH_UNKNOWN;
_prepareHeader(header, code, content_type, content.length());
_currentClient.write(header.c_str(), header.length());
if(content.length())
sendContent(content);
}
void WebServer::send_P(int code, PGM_P content_type, PGM_P content) {
size_t contentLength = 0;
if (content != NULL) {
contentLength = strlen_P(content);
}
String header;
char type[64];
memccpy_P((void*)type, (PGM_VOID_P)content_type, 0, sizeof(type));
_prepareHeader(header, code, (const char* )type, contentLength);
_currentClient.write(header.c_str(), header.length());
sendContent_P(content);
}
void WebServer::send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength) {
String header;
char type[64];
memccpy_P((void*)type, (PGM_VOID_P)content_type, 0, sizeof(type));
_prepareHeader(header, code, (const char* )type, contentLength);
sendContent(header);
sendContent_P(content, contentLength);
}
void WebServer::send(int code, char* content_type, const String& content) {
send(code, (const char*)content_type, content);
}
void WebServer::send(int code, const String& content_type, const String& content) {
send(code, (const char*)content_type.c_str(), content);
}
void WebServer::sendContent(const String& content) {
const char * footer = "\r\n";
size_t len = content.length();
if(_chunked) {
char * chunkSize = (char *)malloc(11);
if(chunkSize){
sprintf(chunkSize, "%x%s", len, footer);
_currentClient.write(chunkSize, strlen(chunkSize));
free(chunkSize);
}
}
_currentClient.write(content.c_str(), len);
if(_chunked){
_currentClient.write(footer, 2);
}
}
void WebServer::sendContent_P(PGM_P content) {
sendContent_P(content, strlen_P(content));
}
void WebServer::sendContent_P(PGM_P content, size_t size) {
const char * footer = "\r\n";
if(_chunked) {
char * chunkSize = (char *)malloc(11);
if(chunkSize){
sprintf(chunkSize, "%x%s", size, footer);
_currentClient.write(chunkSize, strlen(chunkSize));
free(chunkSize);
}
}
_currentClient.write_P(content, size);
if(_chunked){
_currentClient.write(footer, 2);
}
}
String WebServer::arg(String name) {
for (int i = 0; i < _currentArgCount; ++i) {
if ( _currentArgs[i].key == name )
return _currentArgs[i].value;
}
return String();
}
String WebServer::arg(int i) {
if (i < _currentArgCount)
return _currentArgs[i].value;
return String();
}
String WebServer::argName(int i) {
if (i < _currentArgCount)
return _currentArgs[i].key;
return String();
}
int WebServer::args() {
return _currentArgCount;
}
bool WebServer::hasArg(String name) {
for (int i = 0; i < _currentArgCount; ++i) {
if (_currentArgs[i].key == name)
return true;
}
return false;
}
String WebServer::header(String name) {
for (int i = 0; i < _headerKeysCount; ++i) {
if (_currentHeaders[i].key.equalsIgnoreCase(name))
return _currentHeaders[i].value;
}
return String();
}
void WebServer::collectHeaders(const char* headerKeys[], const size_t headerKeysCount) {
_headerKeysCount = headerKeysCount + 1;
if (_currentHeaders)
delete[]_currentHeaders;
_currentHeaders = new RequestArgument[_headerKeysCount];
_currentHeaders[0].key = AUTHORIZATION_HEADER;
for (int i = 1; i < _headerKeysCount; i++){
_currentHeaders[i].key = headerKeys[i-1];
}
}
String WebServer::header(int i) {
if (i < _headerKeysCount)
return _currentHeaders[i].value;
return String();
}
String WebServer::headerName(int i) {
if (i < _headerKeysCount)
return _currentHeaders[i].key;
return String();
}
int WebServer::headers() {
return _headerKeysCount;
}
bool WebServer::hasHeader(String name) {
for (int i = 0; i < _headerKeysCount; ++i) {
if ((_currentHeaders[i].key.equalsIgnoreCase(name)) && (_currentHeaders[i].value.length() > 0))
return true;
}
return false;
}
String WebServer::hostHeader() {
return _hostHeader;
}
void WebServer::onFileUpload(THandlerFunction fn) {
_fileUploadHandler = fn;
}
void WebServer::onNotFound(THandlerFunction fn) {
_notFoundHandler = fn;
}
void WebServer::_handleRequest() {
bool handled = false;
if (!_currentHandler){
#ifdef DEBUG_ESP_HTTP_SERVER
DEBUG_OUTPUT.println("request handler not found");
#endif
}
else {
handled = _currentHandler->handle(*this, _currentMethod, _currentUri);
#ifdef DEBUG_ESP_HTTP_SERVER
if (!handled) {
DEBUG_OUTPUT.println("request handler failed to handle request");
}
#endif
}
if (!handled) {
if(_notFoundHandler) {
_notFoundHandler();
}
else {
send(404, "text/plain", String("Not found: ") + _currentUri);
}
}
_currentUri = String();
}
String WebServer::_responseCodeToString(int code) {
switch (code) {
case 100: return F("Continue");
case 101: return F("Switching Protocols");
case 200: return F("OK");
case 201: return F("Created");
case 202: return F("Accepted");
case 203: return F("Non-Authoritative Information");
case 204: return F("No Content");
case 205: return F("Reset Content");
case 206: return F("Partial Content");
case 300: return F("Multiple Choices");
case 301: return F("Moved Permanently");
case 302: return F("Found");
case 303: return F("See Other");
case 304: return F("Not Modified");
case 305: return F("Use Proxy");
case 307: return F("Temporary Redirect");
case 400: return F("Bad Request");
case 401: return F("Unauthorized");
case 402: return F("Payment Required");
case 403: return F("Forbidden");
case 404: return F("Not Found");
case 405: return F("Method Not Allowed");
case 406: return F("Not Acceptable");
case 407: return F("Proxy Authentication Required");
case 408: return F("Request Time-out");
case 409: return F("Conflict");
case 410: return F("Gone");
case 411: return F("Length Required");
case 412: return F("Precondition Failed");
case 413: return F("Request Entity Too Large");
case 414: return F("Request-URI Too Large");
case 415: return F("Unsupported Media Type");
case 416: return F("Requested range not satisfiable");
case 417: return F("Expectation Failed");
case 500: return F("Internal Server Error");
case 501: return F("Not Implemented");
case 502: return F("Bad Gateway");
case 503: return F("Service Unavailable");
case 504: return F("Gateway Time-out");
case 505: return F("HTTP Version not supported");
default: return "";
}
}

View File

@ -0,0 +1,236 @@
/*
WebServer.h - Dead simple web-server.
Supports only one simultaneous client, knows how to handle GET and POST.
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)
*/
#ifndef WEBSERVER_H
#define WEBSERVER_H
#include <functional>
#ifdef ESP8266
#define WebServer ESP8266WebServer
#include <ESP8266WiFi.h>
#else
#include <WiFi.h>
#define write_P write
#endif
enum HTTPMethod { HTTP_ANY, HTTP_GET, HTTP_POST, HTTP_PUT, HTTP_PATCH, HTTP_DELETE, HTTP_OPTIONS };
enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END,
UPLOAD_FILE_ABORTED };
enum HTTPClientStatus { HC_NONE, HC_WAIT_READ, HC_WAIT_CLOSE };
#define HTTP_DOWNLOAD_UNIT_SIZE 1460
#ifndef HTTP_UPLOAD_BUFLEN
#define HTTP_UPLOAD_BUFLEN 2048
#endif
#define HTTP_MAX_DATA_WAIT 1000 //ms to wait for the client to send the request
#define HTTP_MAX_POST_WAIT 1000 //ms to wait for POST data to arrive
#define HTTP_MAX_SEND_WAIT 5000 //ms to wait for data chunk to be ACKed
#define HTTP_MAX_CLOSE_WAIT 2000 //ms to wait for the client to close the connection
#define CONTENT_LENGTH_UNKNOWN ((size_t) -1)
#define CONTENT_LENGTH_NOT_SET ((size_t) -2)
class WebServer;
typedef struct {
HTTPUploadStatus status;
String filename;
String name;
String type;
size_t totalSize; // file size
size_t currentSize; // size of data currently in buf
uint8_t buf[HTTP_UPLOAD_BUFLEN];
} HTTPUpload;
#include "detail/RequestHandler.h"
namespace fs {
class FS;
}
class WebServer
{
public:
WebServer(IPAddress addr, int port = 80);
WebServer(int port = 80);
~WebServer();
void begin();
void handleClient();
void close();
void stop();
bool authenticate(const char * username, const char * password);
void requestAuthentication();
typedef std::function<void(void)> THandlerFunction;
void on(const String &uri, THandlerFunction handler);
void on(const String &uri, HTTPMethod method, THandlerFunction fn);
void on(const String &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn);
void addHandler(RequestHandler* handler);
void serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_header = NULL );
void onNotFound(THandlerFunction fn); //called when handler is not assigned
void onFileUpload(THandlerFunction fn); //handle file uploads
String uri() { return _currentUri; }
HTTPMethod method() { return _currentMethod; }
WiFiClient client() { return _currentClient; }
HTTPUpload& upload() { return _currentUpload; }
String arg(String name); // get request argument value by name
String arg(int i); // get request argument value by number
String argName(int i); // get request argument name by number
int args(); // get arguments count
bool hasArg(String name); // check if argument exists
void collectHeaders(const char* headerKeys[], const size_t headerKeysCount); // set the request headers to collect
String header(String name); // get request header value by name
String header(int i); // get request header value by number
String headerName(int i); // get request header name by number
int headers(); // get header count
bool hasHeader(String name); // check if header exists
String hostHeader(); // get request host header if available or empty String if not
// send response to the client
// code - HTTP response code, can be 200 or 404
// content_type - HTTP content type, like "text/plain" or "image/png"
// content - actual content body
void send(int code, const char* content_type = NULL, const String& content = String(""));
void send(int code, char* content_type, const String& content);
void send(int code, const String& content_type, const String& content);
void send_P(int code, PGM_P content_type, PGM_P content);
void send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength);
void setContentLength(size_t contentLength);
void sendHeader(const String& name, const String& value, bool first = false);
void sendContent(const String& content);
void sendContent_P(PGM_P content);
void sendContent_P(PGM_P content, size_t size);
static String urlDecode(const String& text);
#ifdef ESP8266
template<typename T> size_t streamFile(T &file, const String& contentType){
setContentLength(file.size());
if (String(file.name()).endsWith(".gz") &&
contentType != "application/x-gzip" &&
contentType != "application/octet-stream"){
sendHeader("Content-Encoding", "gzip");
}
send(200, contentType, "");
return _currentClient.write(file);
}
#else
template<typename T> size_t streamFile(T &file, const String& contentType){
#define STREAMFILE_BUFSIZE 2*1460
setContentLength(file.size());
if (String(file.name()).endsWith(".gz") &&
contentType != "application/x-gzip" &&
contentType != "application/octet-stream") {
sendHeader("Content-Encoding", "gzip");
}
send(200, contentType, "");
uint8_t *buf = (uint8_t *)malloc(STREAMFILE_BUFSIZE);
if (buf == NULL) {
//DBG_OUTPUT_PORT.printf("streamFile malloc failed");
return 0;
}
size_t totalBytesOut = 0;
while (client().connected() && (file.available() > 0)) {
int bytesOut;
int bytesIn = file.read(buf, STREAMFILE_BUFSIZE);
if (bytesIn <= 0) break;
while (1) {
bytesOut = 0;
if (!client().connected()) break;
bytesOut = client().write(buf, bytesIn);
if (bytesIn == bytesOut) break;
//DBG_OUTPUT_PORT.printf("bytesIn %d != bytesOut %d\r\n",
//bytesIn, bytesOut);
delay(1);
}
totalBytesOut += bytesOut;
yield();
}
if (totalBytesOut != file.size()) {
//DBG_OUTPUT_PORT.printf("file size %d bytes out %d\r\n",
// file.size(), totalBytesOut);
}
free(buf);
return totalBytesOut;
}
#endif
protected:
void _addRequestHandler(RequestHandler* handler);
void _handleRequest();
bool _parseRequest(WiFiClient& client);
void _parseArguments(String data);
static String _responseCodeToString(int code);
bool _parseForm(WiFiClient& client, String boundary, uint32_t len);
bool _parseFormUploadAborted();
void _uploadWriteByte(uint8_t b);
uint8_t _uploadReadByte(WiFiClient& client);
void _prepareHeader(String& response, int code, const char* content_type, size_t contentLength);
bool _collectHeader(const char* headerName, const char* headerValue);
struct RequestArgument {
String key;
String value;
};
WiFiServer _server;
WiFiClient _currentClient;
HTTPMethod _currentMethod;
String _currentUri;
uint8_t _currentVersion;
HTTPClientStatus _currentStatus;
unsigned long _statusChange;
RequestHandler* _currentHandler;
RequestHandler* _firstHandler;
RequestHandler* _lastHandler;
THandlerFunction _notFoundHandler;
THandlerFunction _fileUploadHandler;
int _currentArgCount;
RequestArgument* _currentArgs;
HTTPUpload _currentUpload;
int _headerKeysCount;
RequestArgument* _currentHeaders;
size_t _contentLength;
String _responseHeaders;
String _hostHeader;
bool _chunked;
};
#endif //WEBSERVER_H

View File

@ -0,0 +1,19 @@
#ifndef REQUESTHANDLER_H
#define REQUESTHANDLER_H
class RequestHandler {
public:
virtual ~RequestHandler() { }
virtual bool canHandle(HTTPMethod method, String uri) { (void) method; (void) uri; return false; }
virtual bool canUpload(String uri) { (void) uri; return false; }
virtual bool handle(WebServer& server, HTTPMethod requestMethod, String requestUri) { (void) server; (void) requestMethod; (void) requestUri; return false; }
virtual void upload(WebServer& server, String requestUri, HTTPUpload& upload) { (void) server; (void) requestUri; (void) upload; }
RequestHandler* next() { return _next; }
void next(RequestHandler* r) { _next = r; }
private:
RequestHandler* _next = nullptr;
};
#endif //REQUESTHANDLER_H

View File

@ -0,0 +1,191 @@
#ifndef REQUESTHANDLERSIMPL_H
#define REQUESTHANDLERSIMPL_H
#include "RequestHandler.h"
#ifdef ESP8266
// Table of extension->MIME strings stored in PROGMEM, needs to be global due to GCC section typing rules
static const struct {const char endsWith[16]; const char mimeType[32];} mimeTable[] ICACHE_RODATA_ATTR = {
#else
static const struct {const char endsWith[16]; const char mimeType[32];} mimeTable[] = {
#endif
{ ".html", "text/html" },
{ ".htm", "text/html" },
{ ".css", "text/css" },
{ ".txt", "text/plain" },
{ ".js", "application/javascript" },
{ ".json", "application/json" },
{ ".png", "image/png" },
{ ".gif", "image/gif" },
{ ".jpg", "image/jpeg" },
{ ".ico", "image/x-icon" },
{ ".svg", "image/svg+xml" },
{ ".ttf", "application/x-font-ttf" },
{ ".otf", "application/x-font-opentype" },
{ ".woff", "application/font-woff" },
{ ".woff2", "application/font-woff2" },
{ ".eot", "application/vnd.ms-fontobject" },
{ ".sfnt", "application/font-sfnt" },
{ ".xml", "text/xml" },
{ ".pdf", "application/pdf" },
{ ".zip", "application/zip" },
{ ".gz", "application/x-gzip" },
{ ".appcache", "text/cache-manifest" },
{ "", "application/octet-stream" } };
class FunctionRequestHandler : public RequestHandler {
public:
FunctionRequestHandler(WebServer::THandlerFunction fn, WebServer::THandlerFunction ufn, const String &uri, HTTPMethod method)
: _fn(fn)
, _ufn(ufn)
, _uri(uri)
, _method(method)
{
}
bool canHandle(HTTPMethod requestMethod, String requestUri) override {
if (_method != HTTP_ANY && _method != requestMethod)
return false;
if (requestUri != _uri)
return false;
return true;
}
bool canUpload(String requestUri) override {
if (!_ufn || !canHandle(HTTP_POST, requestUri))
return false;
return true;
}
bool handle(WebServer& server, HTTPMethod requestMethod, String requestUri) override {
(void) server;
if (!canHandle(requestMethod, requestUri))
return false;
_fn();
return true;
}
void upload(WebServer& server, String requestUri, HTTPUpload& upload) override {
(void) server;
(void) upload;
if (canUpload(requestUri))
_ufn();
}
protected:
WebServer::THandlerFunction _fn;
WebServer::THandlerFunction _ufn;
String _uri;
HTTPMethod _method;
};
class StaticRequestHandler : public RequestHandler {
public:
StaticRequestHandler(FS& fs, const char* path, const char* uri, const char* cache_header)
: _fs(fs)
, _uri(uri)
, _path(path)
, _cache_header(cache_header)
{
_isFile = fs.exists(path);
#ifdef ESP8266
DEBUGV("StaticRequestHandler: path=%s uri=%s isFile=%d, cache_header=%s\r\n", path, uri, _isFile, cache_header);
#else
#ifdef DEBUG_ESP_HTTP_SERVER
DEBUG_OUTPUT.printf("StaticRequestHandler: path=%s uri=%s isFile=%d, cache_header=%s\r\n", path, uri, _isFile, cache_header);
#endif
#endif
_baseUriLength = _uri.length();
}
bool canHandle(HTTPMethod requestMethod, String requestUri) override {
if (requestMethod != HTTP_GET)
return false;
if ((_isFile && requestUri != _uri) || !requestUri.startsWith(_uri))
return false;
return true;
}
bool handle(WebServer& server, HTTPMethod requestMethod, String requestUri) override {
if (!canHandle(requestMethod, requestUri))
return false;
#ifdef ESP8266
DEBUGV("StaticRequestHandler::handle: request=%s _uri=%s\r\n", requestUri.c_str(), _uri.c_str());
#else
#ifdef DEBUG_ESP_HTTP_SERVER
DEBUG_OUTPUT.printf("StaticRequestHandler::handle: request=%s _uri=%s\r\n", requestUri.c_str(), _uri.c_str());
#endif
#endif
String path(_path);
if (!_isFile) {
// Base URI doesn't point to a file.
// If a directory is requested, look for index file.
if (requestUri.endsWith("/")) requestUri += "index.htm";
// Append whatever follows this URI in request to get the file path.
path += requestUri.substring(_baseUriLength);
}
#ifdef ESP8266
DEBUGV("StaticRequestHandler::handle: path=%s, isFile=%d\r\n", path.c_str(), _isFile);
#else
#ifdef DEBUG_ESP_HTTP_SERVER
DEBUG_OUTPUT.printf("StaticRequestHandler::handle: path=%s, isFile=%d\r\n", path.c_str(), _isFile);
#endif
#endif
String contentType = getContentType(path);
// look for gz file, only if the original specified path is not a gz. So part only works to send gzip via content encoding when a non compressed is asked for
// if you point the the path to gzip you will serve the gzip as content type "application/x-gzip", not text or javascript etc...
if (!path.endsWith(".gz") && !_fs.exists(path)) {
String pathWithGz = path + ".gz";
if(_fs.exists(pathWithGz))
path += ".gz";
}
File f = _fs.open(path, "r");
if (!f)
return false;
if (_cache_header.length() != 0)
server.sendHeader("Cache-Control", _cache_header);
server.streamFile(f, contentType);
return true;
}
static String getContentType(const String& path) {
char buff[sizeof(mimeTable[0].mimeType)];
// Check all entries but last one for match, return if found
for (size_t i=0; i < sizeof(mimeTable)/sizeof(mimeTable[0])-1; i++) {
strcpy_P(buff, mimeTable[i].endsWith);
if (path.endsWith(buff)) {
strcpy_P(buff, mimeTable[i].mimeType);
return String(buff);
}
}
// Fall-through and just return default type
strcpy_P(buff, mimeTable[sizeof(mimeTable)/sizeof(mimeTable[0])-1].mimeType);
return String(buff);
}
protected:
FS _fs;
String _uri;
String _path;
String _cache_header;
bool _isFile;
size_t _baseUriLength;
};
#endif //REQUESTHANDLERSIMPL_H

View File

@ -1,105 +0,0 @@
#!/usr/bin/env python
#original file and idea come from https://github.com/j0hnlittle
import SimpleHTTPServer, SocketServer
import sys
import os
import json
import re
#Replace this with a different path if you need to...
base_path = os.path.join(os.getcwd(),"..","esp3d","data")
tools_path = os.getcwd();
server_port=8080
class MyHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
def do_GET(self):
is_tpl, s = self.process_path(self.path)
if is_tpl:
self.send_response(301)
self.send_header("Content-type", "text/html")
self.end_headers()
data = self.process_tpl(s)
self.wfile.write(data)
self.wfile.close()
return
SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
def process_path(self,s):
#A template link is all caps and is associated to a lower().tpl file in base_path
if s == "/":
return True,"home.tpl"
ret = False,""
s = s.replace("/","")
if s.endswith(".tpl"):
return True,s
s = s.lower()+".tpl"
#these do not exactly match, so let's make them!
s = s.replace("configsta","config_sta")
s = s.replace("configap","config_ap")
s = s.replace("configsys","system")
if os.path.exists(os.path.join(base_path,s)):
ret = True,s
return ret
def process_tpl(self,fn):
p = re.compile('\$(.*?)\$')
if fn.startswith("/") or fn.startswith("\\"):
fn = fn[1:]
fn = os.path.join(base_path,fn)
data = open(fn).read()
fn_json = os.path.join(tools_path,"tags.json")
if os.path.exists(fn_json):
json_dic = json.loads(open(fn_json).read())
else:
json_dic = {}
tags = p.findall(data)
i = 0
n_tags = len(tags)
while i < n_tags:
dd = self.process_tag(data,tags[i],json_dic)
if dd != data:
data = dd
tags = p.findall(data)
n_tags = len(tags)
else:
i = i+1
return data
def process_tag(self, data, tag, json_dic={}):
print " processing $%s$" % tag
if tag in json_dic.keys():
data = data.replace("$"+tag+"$",json_dic[tag])
elif tag.startswith("INCLUDE[") and tag.endswith("]"):
fn = tag[8:-1]
fn = os.path.join(base_path,fn)
d = open(fn).read()
p0 = data.find("$"+tag)
p1 = data.find("]",p0)+2
data = data[:p0]+d+data[p1:]
return data
if __name__ == '__main__':
print "="*60
print "Serving files from:"
print base_path
print "\ntags.json is located at:"
print tools_path
print "\nOpen your browser at http://localhost:" + str(server_port)
os.chdir(base_path)
print "="*60
handler = MyHandler
server = SocketServer.TCPServer(("",server_port), handler)
server.serve_forever()

View File

@ -1,4 +0,0 @@
{
"WEB_ADDRESS":"localhost:8080",
"PAGE_TITLE":"Testing things..."
}